Move generateSilentAudio to Composition
Also rename to forceAudioTrack PiperOrigin-RevId: 510394620
This commit is contained in:
parent
0c17605ff0
commit
5806414fba
@ -62,7 +62,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
public static final String SHOULD_REMOVE_AUDIO = "should_remove_audio";
|
public static final String SHOULD_REMOVE_AUDIO = "should_remove_audio";
|
||||||
public static final String SHOULD_REMOVE_VIDEO = "should_remove_video";
|
public static final String SHOULD_REMOVE_VIDEO = "should_remove_video";
|
||||||
public static final String SHOULD_FLATTEN_FOR_SLOW_MOTION = "should_flatten_for_slow_motion";
|
public static final String SHOULD_FLATTEN_FOR_SLOW_MOTION = "should_flatten_for_slow_motion";
|
||||||
public static final String GENERATE_SILENT_AUDIO = "generate_silent_audio";
|
public static final String FORCE_AUDIO_TRACK = "force_audio_track";
|
||||||
public static final String AUDIO_MIME_TYPE = "audio_mime_type";
|
public static final String AUDIO_MIME_TYPE = "audio_mime_type";
|
||||||
public static final String VIDEO_MIME_TYPE = "video_mime_type";
|
public static final String VIDEO_MIME_TYPE = "video_mime_type";
|
||||||
public static final String RESOLUTION_HEIGHT = "resolution_height";
|
public static final String RESOLUTION_HEIGHT = "resolution_height";
|
||||||
@ -213,7 +213,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
private @MonotonicNonNull CheckBox removeAudioCheckbox;
|
private @MonotonicNonNull CheckBox removeAudioCheckbox;
|
||||||
private @MonotonicNonNull CheckBox removeVideoCheckbox;
|
private @MonotonicNonNull CheckBox removeVideoCheckbox;
|
||||||
private @MonotonicNonNull CheckBox flattenForSlowMotionCheckbox;
|
private @MonotonicNonNull CheckBox flattenForSlowMotionCheckbox;
|
||||||
private @MonotonicNonNull CheckBox generateSilentAudioCheckbox;
|
private @MonotonicNonNull CheckBox forceAudioTrackCheckbox;
|
||||||
private @MonotonicNonNull Spinner audioMimeSpinner;
|
private @MonotonicNonNull Spinner audioMimeSpinner;
|
||||||
private @MonotonicNonNull Spinner videoMimeSpinner;
|
private @MonotonicNonNull Spinner videoMimeSpinner;
|
||||||
private @MonotonicNonNull Spinner resolutionHeightSpinner;
|
private @MonotonicNonNull Spinner resolutionHeightSpinner;
|
||||||
@ -274,7 +274,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
flattenForSlowMotionCheckbox = findViewById(R.id.flatten_for_slow_motion_checkbox);
|
flattenForSlowMotionCheckbox = findViewById(R.id.flatten_for_slow_motion_checkbox);
|
||||||
|
|
||||||
generateSilentAudioCheckbox = findViewById(R.id.generate_silent_audio_checkbox);
|
forceAudioTrackCheckbox = findViewById(R.id.force_audio_track_checkbox);
|
||||||
|
|
||||||
ArrayAdapter<String> audioMimeAdapter =
|
ArrayAdapter<String> audioMimeAdapter =
|
||||||
new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item);
|
new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item);
|
||||||
@ -385,7 +385,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
"removeAudioCheckbox",
|
"removeAudioCheckbox",
|
||||||
"removeVideoCheckbox",
|
"removeVideoCheckbox",
|
||||||
"flattenForSlowMotionCheckbox",
|
"flattenForSlowMotionCheckbox",
|
||||||
"generateSilentAudioCheckbox",
|
"forceAudioTrackCheckbox",
|
||||||
"audioMimeSpinner",
|
"audioMimeSpinner",
|
||||||
"videoMimeSpinner",
|
"videoMimeSpinner",
|
||||||
"resolutionHeightSpinner",
|
"resolutionHeightSpinner",
|
||||||
@ -405,7 +405,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
bundle.putBoolean(SHOULD_REMOVE_AUDIO, removeAudioCheckbox.isChecked());
|
bundle.putBoolean(SHOULD_REMOVE_AUDIO, removeAudioCheckbox.isChecked());
|
||||||
bundle.putBoolean(SHOULD_REMOVE_VIDEO, removeVideoCheckbox.isChecked());
|
bundle.putBoolean(SHOULD_REMOVE_VIDEO, removeVideoCheckbox.isChecked());
|
||||||
bundle.putBoolean(SHOULD_FLATTEN_FOR_SLOW_MOTION, flattenForSlowMotionCheckbox.isChecked());
|
bundle.putBoolean(SHOULD_FLATTEN_FOR_SLOW_MOTION, flattenForSlowMotionCheckbox.isChecked());
|
||||||
bundle.putBoolean(GENERATE_SILENT_AUDIO, generateSilentAudioCheckbox.isChecked());
|
bundle.putBoolean(FORCE_AUDIO_TRACK, forceAudioTrackCheckbox.isChecked());
|
||||||
String selectedAudioMimeType = String.valueOf(audioMimeSpinner.getSelectedItem());
|
String selectedAudioMimeType = String.valueOf(audioMimeSpinner.getSelectedItem());
|
||||||
if (!SAME_AS_INPUT_OPTION.equals(selectedAudioMimeType)) {
|
if (!SAME_AS_INPUT_OPTION.equals(selectedAudioMimeType)) {
|
||||||
bundle.putString(AUDIO_MIME_TYPE, selectedAudioMimeType);
|
bundle.putString(AUDIO_MIME_TYPE, selectedAudioMimeType);
|
||||||
@ -739,7 +739,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
@RequiresNonNull({
|
@RequiresNonNull({
|
||||||
"removeVideoCheckbox",
|
"removeVideoCheckbox",
|
||||||
"generateSilentAudioCheckbox",
|
"forceAudioTrackCheckbox",
|
||||||
"audioMimeSpinner",
|
"audioMimeSpinner",
|
||||||
"videoMimeSpinner",
|
"videoMimeSpinner",
|
||||||
"resolutionHeightSpinner",
|
"resolutionHeightSpinner",
|
||||||
@ -761,7 +761,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
@RequiresNonNull({
|
@RequiresNonNull({
|
||||||
"removeAudioCheckbox",
|
"removeAudioCheckbox",
|
||||||
"generateSilentAudioCheckbox",
|
"forceAudioTrackCheckbox",
|
||||||
"audioMimeSpinner",
|
"audioMimeSpinner",
|
||||||
"videoMimeSpinner",
|
"videoMimeSpinner",
|
||||||
"resolutionHeightSpinner",
|
"resolutionHeightSpinner",
|
||||||
@ -782,7 +782,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RequiresNonNull({
|
@RequiresNonNull({
|
||||||
"generateSilentAudioCheckbox",
|
"forceAudioTrackCheckbox",
|
||||||
"audioMimeSpinner",
|
"audioMimeSpinner",
|
||||||
"videoMimeSpinner",
|
"videoMimeSpinner",
|
||||||
"resolutionHeightSpinner",
|
"resolutionHeightSpinner",
|
||||||
@ -794,7 +794,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
"selectVideoEffectsButton"
|
"selectVideoEffectsButton"
|
||||||
})
|
})
|
||||||
private void enableTrackSpecificOptions(boolean isAudioEnabled, boolean isVideoEnabled) {
|
private void enableTrackSpecificOptions(boolean isAudioEnabled, boolean isVideoEnabled) {
|
||||||
generateSilentAudioCheckbox.setEnabled(isVideoEnabled);
|
forceAudioTrackCheckbox.setEnabled(isVideoEnabled);
|
||||||
audioMimeSpinner.setEnabled(isAudioEnabled);
|
audioMimeSpinner.setEnabled(isAudioEnabled);
|
||||||
videoMimeSpinner.setEnabled(isVideoEnabled);
|
videoMimeSpinner.setEnabled(isVideoEnabled);
|
||||||
resolutionHeightSpinner.setEnabled(isVideoEnabled);
|
resolutionHeightSpinner.setEnabled(isVideoEnabled);
|
||||||
|
@ -82,6 +82,7 @@ import androidx.media3.transformer.Composition;
|
|||||||
import androidx.media3.transformer.DefaultEncoderFactory;
|
import androidx.media3.transformer.DefaultEncoderFactory;
|
||||||
import androidx.media3.transformer.DefaultMuxer;
|
import androidx.media3.transformer.DefaultMuxer;
|
||||||
import androidx.media3.transformer.EditedMediaItem;
|
import androidx.media3.transformer.EditedMediaItem;
|
||||||
|
import androidx.media3.transformer.EditedMediaItemSequence;
|
||||||
import androidx.media3.transformer.Effects;
|
import androidx.media3.transformer.Effects;
|
||||||
import androidx.media3.transformer.ExportException;
|
import androidx.media3.transformer.ExportException;
|
||||||
import androidx.media3.transformer.ExportResult;
|
import androidx.media3.transformer.ExportResult;
|
||||||
@ -99,6 +100,8 @@ import com.google.common.util.concurrent.ListenableFuture;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -226,9 +229,9 @@ public final class TransformerActivity extends AppCompatActivity {
|
|||||||
MediaItem mediaItem = createMediaItem(bundle, uri);
|
MediaItem mediaItem = createMediaItem(bundle, uri);
|
||||||
try {
|
try {
|
||||||
Transformer transformer = createTransformer(bundle, filePath);
|
Transformer transformer = createTransformer(bundle, filePath);
|
||||||
EditedMediaItem editedMediaItem = createEditedMediaItem(mediaItem, bundle);
|
Composition composition = createComposition(mediaItem, bundle);
|
||||||
exportStopwatch.start();
|
exportStopwatch.start();
|
||||||
transformer.start(editedMediaItem, filePath);
|
transformer.start(composition, filePath);
|
||||||
this.transformer = transformer;
|
this.transformer = transformer;
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
@ -300,13 +303,10 @@ public final class TransformerActivity extends AppCompatActivity {
|
|||||||
requestBuilder.setHdrMode(bundle.getInt(ConfigurationActivity.HDR_MODE));
|
requestBuilder.setHdrMode(bundle.getInt(ConfigurationActivity.HDR_MODE));
|
||||||
transformerBuilder.setTransformationRequest(requestBuilder.build());
|
transformerBuilder.setTransformationRequest(requestBuilder.build());
|
||||||
|
|
||||||
transformerBuilder
|
transformerBuilder.setEncoderFactory(
|
||||||
.experimentalSetGenerateSilentAudio(
|
new DefaultEncoderFactory.Builder(this.getApplicationContext())
|
||||||
bundle.getBoolean(ConfigurationActivity.GENERATE_SILENT_AUDIO))
|
.setEnableFallback(bundle.getBoolean(ConfigurationActivity.ENABLE_FALLBACK))
|
||||||
.setEncoderFactory(
|
.build());
|
||||||
new DefaultEncoderFactory.Builder(this.getApplicationContext())
|
|
||||||
.setEnableFallback(bundle.getBoolean(ConfigurationActivity.ENABLE_FALLBACK))
|
|
||||||
.build());
|
|
||||||
|
|
||||||
if (!bundle.getBoolean(ConfigurationActivity.ABORT_SLOW_EXPORT)) {
|
if (!bundle.getBoolean(ConfigurationActivity.ABORT_SLOW_EXPORT)) {
|
||||||
transformerBuilder.setMuxerFactory(
|
transformerBuilder.setMuxerFactory(
|
||||||
@ -357,23 +357,28 @@ public final class TransformerActivity extends AppCompatActivity {
|
|||||||
"exportStopwatch",
|
"exportStopwatch",
|
||||||
"progressViewGroup",
|
"progressViewGroup",
|
||||||
})
|
})
|
||||||
private EditedMediaItem createEditedMediaItem(MediaItem mediaItem, @Nullable Bundle bundle)
|
private Composition createComposition(MediaItem mediaItem, @Nullable Bundle bundle)
|
||||||
throws PackageManager.NameNotFoundException {
|
throws PackageManager.NameNotFoundException {
|
||||||
EditedMediaItem.Builder editedMediaItemBuilder = new EditedMediaItem.Builder(mediaItem);
|
EditedMediaItem.Builder editedMediaItemBuilder = new EditedMediaItem.Builder(mediaItem);
|
||||||
if (bundle == null) {
|
|
||||||
return editedMediaItemBuilder.build();
|
|
||||||
}
|
|
||||||
// For image inputs. Automatically ignored if input is audio/video.
|
// For image inputs. Automatically ignored if input is audio/video.
|
||||||
editedMediaItemBuilder.setDurationUs(5_000_000).setFrameRate(30);
|
editedMediaItemBuilder.setDurationUs(5_000_000).setFrameRate(30);
|
||||||
ImmutableList<AudioProcessor> audioProcessors = createAudioProcessorsFromBundle(bundle);
|
boolean forceAudioTrack = false;
|
||||||
ImmutableList<Effect> videoEffects = createVideoEffectsFromBundle(bundle);
|
if (bundle != null) {
|
||||||
return editedMediaItemBuilder
|
ImmutableList<AudioProcessor> audioProcessors = createAudioProcessorsFromBundle(bundle);
|
||||||
.setRemoveAudio(bundle.getBoolean(ConfigurationActivity.SHOULD_REMOVE_AUDIO))
|
ImmutableList<Effect> videoEffects = createVideoEffectsFromBundle(bundle);
|
||||||
.setRemoveVideo(bundle.getBoolean(ConfigurationActivity.SHOULD_REMOVE_VIDEO))
|
editedMediaItemBuilder
|
||||||
.setFlattenForSlowMotion(
|
.setRemoveAudio(bundle.getBoolean(ConfigurationActivity.SHOULD_REMOVE_AUDIO))
|
||||||
bundle.getBoolean(ConfigurationActivity.SHOULD_FLATTEN_FOR_SLOW_MOTION))
|
.setRemoveVideo(bundle.getBoolean(ConfigurationActivity.SHOULD_REMOVE_VIDEO))
|
||||||
.setEffects(new Effects(audioProcessors, videoEffects))
|
.setFlattenForSlowMotion(
|
||||||
.build();
|
bundle.getBoolean(ConfigurationActivity.SHOULD_FLATTEN_FOR_SLOW_MOTION))
|
||||||
|
.setEffects(new Effects(audioProcessors, videoEffects));
|
||||||
|
forceAudioTrack = bundle.getBoolean(ConfigurationActivity.FORCE_AUDIO_TRACK);
|
||||||
|
}
|
||||||
|
List<EditedMediaItem> editedMediaItems = new ArrayList<>();
|
||||||
|
editedMediaItems.add(editedMediaItemBuilder.build());
|
||||||
|
List<EditedMediaItemSequence> sequences = new ArrayList<>();
|
||||||
|
sequences.add(new EditedMediaItemSequence(editedMediaItems));
|
||||||
|
return new Composition(sequences, Effects.EMPTY, forceAudioTrack);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImmutableList<AudioProcessor> createAudioProcessorsFromBundle(Bundle bundle) {
|
private ImmutableList<AudioProcessor> createAudioProcessorsFromBundle(Bundle bundle) {
|
||||||
|
@ -116,9 +116,9 @@
|
|||||||
android:layout_weight="1">
|
android:layout_weight="1">
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:text="@string/generate_silent_audio" />
|
android:text="@string/force_audio_track" />
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:id="@+id/generate_silent_audio_checkbox"
|
android:id="@+id/force_audio_track_checkbox"
|
||||||
android:layout_gravity="end" />
|
android:layout_gravity="end" />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<TableRow
|
<TableRow
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
<string name="permission_denied">Permission Denied</string>
|
<string name="permission_denied">Permission Denied</string>
|
||||||
<string name="hide_input_video">Hide input video</string>
|
<string name="hide_input_video">Hide input video</string>
|
||||||
<string name="show_input_video">Show input video</string>
|
<string name="show_input_video">Show input video</string>
|
||||||
<string name="generate_silent_audio">Generate silent audio</string>
|
<string name="force_audio_track">Force audio track</string>
|
||||||
<string name="overlay_alpha">Alpha</string>
|
<string name="overlay_alpha">Alpha</string>
|
||||||
<string name="overlay_uri">Uri</string>
|
<string name="overlay_uri">Uri</string>
|
||||||
<string name="bitmap_overlay_settings">Specify bitmap overlay settings</string>
|
<string name="bitmap_overlay_settings">Specify bitmap overlay settings</string>
|
||||||
|
@ -66,16 +66,15 @@ import org.checkerframework.dataflow.qual.Pure;
|
|||||||
TransformationRequest transformationRequest,
|
TransformationRequest transformationRequest,
|
||||||
boolean flattenForSlowMotion,
|
boolean flattenForSlowMotion,
|
||||||
ImmutableList<AudioProcessor> audioProcessors,
|
ImmutableList<AudioProcessor> audioProcessors,
|
||||||
long generateSilentAudioDurationUs,
|
long forceAudioTrackDurationUs,
|
||||||
Codec.EncoderFactory encoderFactory,
|
Codec.EncoderFactory encoderFactory,
|
||||||
MuxerWrapper muxerWrapper,
|
MuxerWrapper muxerWrapper,
|
||||||
FallbackListener fallbackListener)
|
FallbackListener fallbackListener)
|
||||||
throws ExportException {
|
throws ExportException {
|
||||||
super(firstInputFormat, streamStartPositionUs, muxerWrapper);
|
super(firstInputFormat, streamStartPositionUs, muxerWrapper);
|
||||||
|
|
||||||
if (generateSilentAudioDurationUs != C.TIME_UNSET) {
|
if (forceAudioTrackDurationUs != C.TIME_UNSET) {
|
||||||
silentAudioGenerator =
|
silentAudioGenerator = new SilentAudioGenerator(firstInputFormat, forceAudioTrackDurationUs);
|
||||||
new SilentAudioGenerator(firstInputFormat, generateSilentAudioDurationUs);
|
|
||||||
} else {
|
} else {
|
||||||
silentAudioGenerator = null;
|
silentAudioGenerator = null;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ package androidx.media3.transformer;
|
|||||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||||
|
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
|
import androidx.media3.common.audio.AudioProcessor;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -40,16 +41,57 @@ public final class Composition {
|
|||||||
public final ImmutableList<EditedMediaItemSequence> sequences;
|
public final ImmutableList<EditedMediaItemSequence> sequences;
|
||||||
/** The {@link Effects} to apply to the composition. */
|
/** The {@link Effects} to apply to the composition. */
|
||||||
public final Effects effects;
|
public final Effects effects;
|
||||||
|
/**
|
||||||
|
* Whether the output file should always contain an audio track.
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>If {@code false}:
|
||||||
|
* <ul>
|
||||||
|
* <li>If the {@link Composition} export doesn't produce any audio at timestamp 0, the
|
||||||
|
* output won't contain any audio, and audio tracks from the {@link MediaItem}
|
||||||
|
* instances in the {@link Composition} will be ignored.
|
||||||
|
* <li>If the {@link Composition} export produces audio at timestamp 0, the output will
|
||||||
|
* contain an audio track.
|
||||||
|
* </ul>
|
||||||
|
* <li>If {@code true}, the output will always contain an audio track.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* If the output contains an audio track, silent audio will be generated for the segments where
|
||||||
|
* the {@link Composition} export doesn't produce any audio.
|
||||||
|
*
|
||||||
|
* <p>The MIME type of the output's audio track can be set using {@link
|
||||||
|
* TransformationRequest.Builder#setAudioMimeType(String)}. The sample rate and channel count can
|
||||||
|
* be set by passing relevant {@link AudioProcessor} instances to the {@link Composition}.
|
||||||
|
*
|
||||||
|
* <p>This parameter is experimental and may be removed or changed without warning.
|
||||||
|
*/
|
||||||
|
public final boolean experimentalForceAudioTrack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance.
|
||||||
|
*
|
||||||
|
* <p>This is equivalent to calling {@link Composition#Composition(List, Effects, boolean)} with
|
||||||
|
* {@link #experimentalForceAudioTrack} set to {@code false}.
|
||||||
|
*/
|
||||||
|
public Composition(List<EditedMediaItemSequence> sequences, Effects effects) {
|
||||||
|
this(sequences, effects, /* experimentalForceAudioTrack= */ false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
*
|
*
|
||||||
* @param sequences The {@link #sequences}.
|
* @param sequences The {@link #sequences}.
|
||||||
* @param effects The {@link #effects}.
|
* @param effects The {@link #effects}.
|
||||||
|
* @param experimentalForceAudioTrack Whether to {@linkplain #experimentalForceAudioTrack always
|
||||||
|
* add an audio track in the output}.
|
||||||
*/
|
*/
|
||||||
public Composition(List<EditedMediaItemSequence> sequences, Effects effects) {
|
public Composition(
|
||||||
|
List<EditedMediaItemSequence> sequences,
|
||||||
|
Effects effects,
|
||||||
|
boolean experimentalForceAudioTrack) {
|
||||||
checkArgument(!sequences.isEmpty());
|
checkArgument(!sequences.isEmpty());
|
||||||
this.sequences = ImmutableList.copyOf(sequences);
|
this.sequences = ImmutableList.copyOf(sequences);
|
||||||
this.effects = effects;
|
this.effects = effects;
|
||||||
|
this.experimentalForceAudioTrack = experimentalForceAudioTrack;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,6 @@ import androidx.media3.common.MediaLibraryInfo;
|
|||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.VideoFrameProcessor;
|
import androidx.media3.common.VideoFrameProcessor;
|
||||||
import androidx.media3.common.audio.AudioProcessor;
|
import androidx.media3.common.audio.AudioProcessor;
|
||||||
import androidx.media3.common.audio.SonicAudioProcessor;
|
|
||||||
import androidx.media3.common.util.Clock;
|
import androidx.media3.common.util.Clock;
|
||||||
import androidx.media3.common.util.HandlerWrapper;
|
import androidx.media3.common.util.HandlerWrapper;
|
||||||
import androidx.media3.common.util.ListenerSet;
|
import androidx.media3.common.util.ListenerSet;
|
||||||
@ -86,7 +85,6 @@ public final class Transformer {
|
|||||||
private boolean removeVideo;
|
private boolean removeVideo;
|
||||||
private boolean flattenForSlowMotion;
|
private boolean flattenForSlowMotion;
|
||||||
private boolean transmux;
|
private boolean transmux;
|
||||||
private boolean generateSilentAudio;
|
|
||||||
private ListenerSet<Transformer.Listener> listeners;
|
private ListenerSet<Transformer.Listener> listeners;
|
||||||
private AssetLoader.@MonotonicNonNull Factory assetLoaderFactory;
|
private AssetLoader.@MonotonicNonNull Factory assetLoaderFactory;
|
||||||
private VideoFrameProcessor.Factory videoFrameProcessorFactory;
|
private VideoFrameProcessor.Factory videoFrameProcessorFactory;
|
||||||
@ -123,7 +121,6 @@ public final class Transformer {
|
|||||||
this.videoEffects = transformer.videoEffects;
|
this.videoEffects = transformer.videoEffects;
|
||||||
this.removeAudio = transformer.removeAudio;
|
this.removeAudio = transformer.removeAudio;
|
||||||
this.removeVideo = transformer.removeVideo;
|
this.removeVideo = transformer.removeVideo;
|
||||||
this.generateSilentAudio = transformer.generateSilentAudio;
|
|
||||||
this.listeners = transformer.listeners;
|
this.listeners = transformer.listeners;
|
||||||
this.assetLoaderFactory = transformer.assetLoaderFactory;
|
this.assetLoaderFactory = transformer.assetLoaderFactory;
|
||||||
this.videoFrameProcessorFactory = transformer.videoFrameProcessorFactory;
|
this.videoFrameProcessorFactory = transformer.videoFrameProcessorFactory;
|
||||||
@ -395,39 +392,6 @@ public final class Transformer {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether to generate silent audio for the output file, if there is no audio available.
|
|
||||||
*
|
|
||||||
* <p>This method is experimental and may be removed or changed without warning.
|
|
||||||
*
|
|
||||||
* <p>To replace existing audio with silence, {@linkplain
|
|
||||||
* EditedMediaItem.Builder#setRemoveAudio(boolean) remove the audio} from the {@link
|
|
||||||
* EditedMediaItem} to export.
|
|
||||||
*
|
|
||||||
* <p>Audio properties/format:
|
|
||||||
*
|
|
||||||
* <ul>
|
|
||||||
* <li>Duration will match duration of the input media.
|
|
||||||
* <li>Sample mime type will match {@link TransformationRequest#audioMimeType}, or {@link
|
|
||||||
* MimeTypes#AUDIO_AAC} if {@code null}.
|
|
||||||
* <li>Sample rate will be {@code 44100} Hz. This can be modified by creating a {@link
|
|
||||||
* SonicAudioProcessor}, setting its {@linkplain
|
|
||||||
* SonicAudioProcessor#setOutputSampleRateHz(int) sample rate}, and passing it to the
|
|
||||||
* {@link EditedMediaItem} used to start the export.
|
|
||||||
* <li>Channel count will be {@code 2}. This can be modified by implementing a custom {@link
|
|
||||||
* AudioProcessor} and passing it to the {@link EditedMediaItem} used to start the export.
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @param generateSilentAudio Whether to generate silent audio for the output file if there is
|
|
||||||
* no audio track.
|
|
||||||
* @return This builder.
|
|
||||||
*/
|
|
||||||
@CanIgnoreReturnValue
|
|
||||||
public Builder experimentalSetGenerateSilentAudio(boolean generateSilentAudio) {
|
|
||||||
this.generateSilentAudio = generateSilentAudio;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a {@link Transformer} instance.
|
* Builds a {@link Transformer} instance.
|
||||||
*
|
*
|
||||||
@ -461,7 +425,6 @@ public final class Transformer {
|
|||||||
removeVideo,
|
removeVideo,
|
||||||
flattenForSlowMotion,
|
flattenForSlowMotion,
|
||||||
transmux,
|
transmux,
|
||||||
generateSilentAudio,
|
|
||||||
listeners,
|
listeners,
|
||||||
assetLoaderFactory,
|
assetLoaderFactory,
|
||||||
videoFrameProcessorFactory,
|
videoFrameProcessorFactory,
|
||||||
@ -620,7 +583,6 @@ public final class Transformer {
|
|||||||
private final boolean removeVideo;
|
private final boolean removeVideo;
|
||||||
private final boolean flattenForSlowMotion;
|
private final boolean flattenForSlowMotion;
|
||||||
private final boolean transmux;
|
private final boolean transmux;
|
||||||
private final boolean generateSilentAudio;
|
|
||||||
private final ListenerSet<Transformer.Listener> listeners;
|
private final ListenerSet<Transformer.Listener> listeners;
|
||||||
private final AssetLoader.Factory assetLoaderFactory;
|
private final AssetLoader.Factory assetLoaderFactory;
|
||||||
private final VideoFrameProcessor.Factory videoFrameProcessorFactory;
|
private final VideoFrameProcessor.Factory videoFrameProcessorFactory;
|
||||||
@ -641,7 +603,6 @@ public final class Transformer {
|
|||||||
boolean removeVideo,
|
boolean removeVideo,
|
||||||
boolean flattenForSlowMotion,
|
boolean flattenForSlowMotion,
|
||||||
boolean transmux,
|
boolean transmux,
|
||||||
boolean generateSilentAudio,
|
|
||||||
ListenerSet<Listener> listeners,
|
ListenerSet<Listener> listeners,
|
||||||
AssetLoader.Factory assetLoaderFactory,
|
AssetLoader.Factory assetLoaderFactory,
|
||||||
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
||||||
@ -659,7 +620,6 @@ public final class Transformer {
|
|||||||
this.removeVideo = removeVideo;
|
this.removeVideo = removeVideo;
|
||||||
this.flattenForSlowMotion = flattenForSlowMotion;
|
this.flattenForSlowMotion = flattenForSlowMotion;
|
||||||
this.transmux = transmux;
|
this.transmux = transmux;
|
||||||
this.generateSilentAudio = generateSilentAudio;
|
|
||||||
this.listeners = listeners;
|
this.listeners = listeners;
|
||||||
this.assetLoaderFactory = assetLoaderFactory;
|
this.assetLoaderFactory = assetLoaderFactory;
|
||||||
this.videoFrameProcessorFactory = videoFrameProcessorFactory;
|
this.videoFrameProcessorFactory = videoFrameProcessorFactory;
|
||||||
@ -779,7 +739,6 @@ public final class Transformer {
|
|||||||
path,
|
path,
|
||||||
transformationRequest,
|
transformationRequest,
|
||||||
transmux,
|
transmux,
|
||||||
generateSilentAudio,
|
|
||||||
assetLoaderFactory,
|
assetLoaderFactory,
|
||||||
encoderFactory,
|
encoderFactory,
|
||||||
muxerFactory,
|
muxerFactory,
|
||||||
|
@ -100,7 +100,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
private final ConditionVariable transformerConditionVariable;
|
private final ConditionVariable transformerConditionVariable;
|
||||||
private final ExportResult.Builder exportResultBuilder;
|
private final ExportResult.Builder exportResultBuilder;
|
||||||
|
|
||||||
private boolean generateSilentAudio;
|
private boolean forceAudioTrack;
|
||||||
private boolean isDrainingPipelines;
|
private boolean isDrainingPipelines;
|
||||||
private @Transformer.ProgressState int progressState;
|
private @Transformer.ProgressState int progressState;
|
||||||
private @MonotonicNonNull RuntimeException cancelException;
|
private @MonotonicNonNull RuntimeException cancelException;
|
||||||
@ -113,7 +113,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
String outputPath,
|
String outputPath,
|
||||||
TransformationRequest transformationRequest,
|
TransformationRequest transformationRequest,
|
||||||
boolean transmux,
|
boolean transmux,
|
||||||
boolean generateSilentAudio,
|
|
||||||
AssetLoader.Factory assetLoaderFactory,
|
AssetLoader.Factory assetLoaderFactory,
|
||||||
Codec.EncoderFactory encoderFactory,
|
Codec.EncoderFactory encoderFactory,
|
||||||
Muxer.Factory muxerFactory,
|
Muxer.Factory muxerFactory,
|
||||||
@ -124,7 +123,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
Clock clock) {
|
Clock clock) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.transformationRequest = transformationRequest;
|
this.transformationRequest = transformationRequest;
|
||||||
this.generateSilentAudio = generateSilentAudio;
|
this.forceAudioTrack = composition.experimentalForceAudioTrack;
|
||||||
this.encoderFactory = new CapturingEncoderFactory(encoderFactory);
|
this.encoderFactory = new CapturingEncoderFactory(encoderFactory);
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.applicationHandler = applicationHandler;
|
this.applicationHandler = applicationHandler;
|
||||||
@ -366,11 +365,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
throws ExportException {
|
throws ExportException {
|
||||||
int trackType = MimeTypes.getTrackType(firstInputFormat.sampleMimeType);
|
int trackType = MimeTypes.getTrackType(firstInputFormat.sampleMimeType);
|
||||||
if (!trackAdded) {
|
if (!trackAdded) {
|
||||||
if (generateSilentAudio) {
|
if (forceAudioTrack) {
|
||||||
if (trackCount.get() == 1 && trackType == C.TRACK_TYPE_VIDEO) {
|
if (trackCount.get() == 1 && trackType == C.TRACK_TYPE_VIDEO) {
|
||||||
trackCount.incrementAndGet();
|
trackCount.incrementAndGet();
|
||||||
} else {
|
} else {
|
||||||
generateSilentAudio = false;
|
forceAudioTrack = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,7 +386,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
compositeAssetLoader.addOnMediaItemChangedListener(samplePipeline, trackType);
|
compositeAssetLoader.addOnMediaItemChangedListener(samplePipeline, trackType);
|
||||||
internalHandler.obtainMessage(MSG_REGISTER_SAMPLE_PIPELINE, samplePipeline).sendToTarget();
|
internalHandler.obtainMessage(MSG_REGISTER_SAMPLE_PIPELINE, samplePipeline).sendToTarget();
|
||||||
|
|
||||||
if (generateSilentAudio) {
|
if (forceAudioTrack) {
|
||||||
Format silentAudioFormat =
|
Format silentAudioFormat =
|
||||||
new Format.Builder()
|
new Format.Builder()
|
||||||
.setSampleMimeType(MimeTypes.AUDIO_AAC)
|
.setSampleMimeType(MimeTypes.AUDIO_AAC)
|
||||||
@ -479,7 +478,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
transformationRequest,
|
transformationRequest,
|
||||||
firstEditedMediaItem.flattenForSlowMotion,
|
firstEditedMediaItem.flattenForSlowMotion,
|
||||||
firstEditedMediaItem.effects.audioProcessors,
|
firstEditedMediaItem.effects.audioProcessors,
|
||||||
generateSilentAudio ? durationUs : C.TIME_UNSET,
|
forceAudioTrack ? durationUs : C.TIME_UNSET,
|
||||||
encoderFactory,
|
encoderFactory,
|
||||||
muxerWrapper,
|
muxerWrapper,
|
||||||
fallbackListener);
|
fallbackListener);
|
||||||
@ -525,7 +524,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
if (!firstEditedMediaItem.effects.audioProcessors.isEmpty()) {
|
if (!firstEditedMediaItem.effects.audioProcessors.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (generateSilentAudio) {
|
if (forceAudioTrack) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,14 +279,17 @@ public final class TransformerEndToEndTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void start_silentAudioOnAudioOnly_isIgnored() throws Exception {
|
public void start_forceAudioTrackOnAudioOnly_isIgnored() throws Exception {
|
||||||
Transformer transformer =
|
Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
|
||||||
createTransformerBuilder(/* enableFallback= */ false)
|
|
||||||
.experimentalSetGenerateSilentAudio(true)
|
|
||||||
.build();
|
|
||||||
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_ENCODER);
|
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_ENCODER);
|
||||||
|
EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(mediaItem).build();
|
||||||
|
EditedMediaItemSequence sequence =
|
||||||
|
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
|
||||||
|
Composition composition =
|
||||||
|
new Composition(
|
||||||
|
ImmutableList.of(sequence), Effects.EMPTY, /* experimentalForceAudioTrack= */ true);
|
||||||
|
|
||||||
transformer.start(mediaItem, outputPath);
|
transformer.start(composition, outputPath);
|
||||||
TransformerTestRunner.runLooper(transformer);
|
TransformerTestRunner.runLooper(transformer);
|
||||||
|
|
||||||
DumpFileAsserts.assertOutput(
|
DumpFileAsserts.assertOutput(
|
||||||
@ -294,31 +297,36 @@ public final class TransformerEndToEndTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void start_silentAudioOnAudioVideo_isIgnored() throws Exception {
|
public void start_forceAudioTrackOnAudioVideo_isIgnored() throws Exception {
|
||||||
Transformer transformer =
|
Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
|
||||||
createTransformerBuilder(/* enableFallback= */ false)
|
|
||||||
.experimentalSetGenerateSilentAudio(true)
|
|
||||||
.build();
|
|
||||||
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
|
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
|
||||||
|
EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(mediaItem).build();
|
||||||
|
EditedMediaItemSequence sequence =
|
||||||
|
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
|
||||||
|
Composition composition =
|
||||||
|
new Composition(
|
||||||
|
ImmutableList.of(sequence), Effects.EMPTY, /* experimentalForceAudioTrack= */ true);
|
||||||
|
|
||||||
transformer.start(mediaItem, outputPath);
|
transformer.start(composition, outputPath);
|
||||||
TransformerTestRunner.runLooper(transformer);
|
TransformerTestRunner.runLooper(transformer);
|
||||||
|
|
||||||
DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO));
|
DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void start_silentAudioRemoveAudio_completesSuccessfully() throws Exception {
|
public void start_forceAudioTrackAndRemoveAudio_generatesSilentAudio() throws Exception {
|
||||||
Transformer transformer =
|
Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
|
||||||
createTransformerBuilder(/* enableFallback= */ false)
|
|
||||||
.experimentalSetGenerateSilentAudio(true)
|
|
||||||
.build();
|
|
||||||
EditedMediaItem editedMediaItem =
|
EditedMediaItem editedMediaItem =
|
||||||
new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO))
|
new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO))
|
||||||
.setRemoveAudio(true)
|
.setRemoveAudio(true)
|
||||||
.build();
|
.build();
|
||||||
|
EditedMediaItemSequence sequence =
|
||||||
|
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
|
||||||
|
Composition composition =
|
||||||
|
new Composition(
|
||||||
|
ImmutableList.of(sequence), Effects.EMPTY, /* experimentalForceAudioTrack= */ true);
|
||||||
|
|
||||||
transformer.start(editedMediaItem, outputPath);
|
transformer.start(composition, outputPath);
|
||||||
TransformerTestRunner.runLooper(transformer);
|
TransformerTestRunner.runLooper(transformer);
|
||||||
|
|
||||||
DumpFileAsserts.assertOutput(
|
DumpFileAsserts.assertOutput(
|
||||||
@ -326,31 +334,36 @@ public final class TransformerEndToEndTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void start_silentAudioRemoveVideo_isIgnored() throws Exception {
|
public void start_forceAudioTrackAndRemoveVideo_isIgnored() throws Exception {
|
||||||
Transformer transformer =
|
Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
|
||||||
createTransformerBuilder(/* enableFallback= */ false)
|
|
||||||
.experimentalSetGenerateSilentAudio(true)
|
|
||||||
.build();
|
|
||||||
EditedMediaItem editedMediaItem =
|
EditedMediaItem editedMediaItem =
|
||||||
new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO))
|
new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO))
|
||||||
.setRemoveVideo(true)
|
.setRemoveVideo(true)
|
||||||
.build();
|
.build();
|
||||||
|
EditedMediaItemSequence sequence =
|
||||||
|
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
|
||||||
|
Composition composition =
|
||||||
|
new Composition(
|
||||||
|
ImmutableList.of(sequence), Effects.EMPTY, /* experimentalForceAudioTrack= */ true);
|
||||||
|
|
||||||
transformer.start(editedMediaItem, outputPath);
|
transformer.start(composition, outputPath);
|
||||||
TransformerTestRunner.runLooper(transformer);
|
TransformerTestRunner.runLooper(transformer);
|
||||||
DumpFileAsserts.assertOutput(
|
DumpFileAsserts.assertOutput(
|
||||||
context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".novideo"));
|
context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".novideo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void start_silentAudioOnVideoOnly_completesSuccessfully() throws Exception {
|
public void start_forceAudioTrackOnVideoOnly_generatesSilentAudio() throws Exception {
|
||||||
Transformer transformer =
|
Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
|
||||||
createTransformerBuilder(/* enableFallback= */ false)
|
|
||||||
.experimentalSetGenerateSilentAudio(true)
|
|
||||||
.build();
|
|
||||||
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_VIDEO_ONLY);
|
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_VIDEO_ONLY);
|
||||||
|
EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(mediaItem).build();
|
||||||
|
EditedMediaItemSequence sequence =
|
||||||
|
new EditedMediaItemSequence(ImmutableList.of(editedMediaItem));
|
||||||
|
Composition composition =
|
||||||
|
new Composition(
|
||||||
|
ImmutableList.of(sequence), Effects.EMPTY, /* experimentalForceAudioTrack= */ true);
|
||||||
|
|
||||||
transformer.start(mediaItem, outputPath);
|
transformer.start(composition, outputPath);
|
||||||
TransformerTestRunner.runLooper(transformer);
|
TransformerTestRunner.runLooper(transformer);
|
||||||
|
|
||||||
DumpFileAsserts.assertOutput(
|
DumpFileAsserts.assertOutput(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user