Rename SamplePipeline to SampleExporter

PiperOrigin-RevId: 545974776
This commit is contained in:
claincly 2023-07-06 14:58:09 +01:00 committed by Rohit Singh
parent 99a143a74e
commit 50c6efe95d
10 changed files with 67 additions and 66 deletions

View File

@ -186,7 +186,8 @@ import java.util.concurrent.atomic.AtomicReference;
/**
* Attempts to feed input data to the {@link AudioProcessingPipeline}.
*
* @return Whether the {@link AudioSamplePipeline} may be able to continue processing data.
* @return Whether it may be possible to process more data immediately by calling this method
* again.
*/
private boolean feedProcessingPipelineFromInput() {
if (shouldGenerateSilence()) {

View File

@ -32,8 +32,8 @@ import androidx.media3.decoder.DecoderInputBuffer;
import java.nio.ByteBuffer;
import org.checkerframework.dataflow.qual.Pure;
/** Pipeline to process, re-encode and mux raw audio samples. */
/* package */ final class AudioSamplePipeline extends SamplePipeline {
/** Processes, re-encodes and muxes raw audio samples. */
/* package */ final class AudioSampleExporter extends SampleExporter {
private static final int DEFAULT_ENCODER_BITRATE = 128 * 1024;
private final Codec encoder;
@ -44,9 +44,9 @@ import org.checkerframework.dataflow.qual.Pure;
private long encoderTotalInputBytes;
public AudioSamplePipeline(
public AudioSampleExporter(
Format firstAssetLoaderInputFormat,
Format firstPipelineInputFormat,
Format firstExporterInputFormat,
TransformationRequest transformationRequest,
EditedMediaItem firstEditedMediaItem,
Codec.EncoderFactory encoderFactory,
@ -54,10 +54,10 @@ import org.checkerframework.dataflow.qual.Pure;
FallbackListener fallbackListener)
throws ExportException {
super(firstAssetLoaderInputFormat, muxerWrapper);
checkArgument(firstPipelineInputFormat.pcmEncoding != Format.NO_VALUE);
checkArgument(firstExporterInputFormat.pcmEncoding != Format.NO_VALUE);
try {
audioGraph = new AudioGraph(firstPipelineInputFormat, firstEditedMediaItem);
audioGraph = new AudioGraph(firstExporterInputFormat, firstEditedMediaItem);
} catch (AudioProcessor.UnhandledAudioFormatException e) {
throw ExportException.createForAudioProcessing(e, e.inputAudioFormat);
}

View File

@ -27,8 +27,8 @@ import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicLong;
/** Pipeline that muxes encoded samples without any transcoding or transformation. */
/* package */ final class EncodedSamplePipeline extends SamplePipeline {
/** Muxes encoded samples without any transcoding or transformation. */
/* package */ final class EncodedSampleExporter extends SampleExporter {
private static final int MAX_INPUT_BUFFER_COUNT = 10;
@ -41,7 +41,7 @@ import java.util.concurrent.atomic.AtomicLong;
private volatile boolean inputEnded;
public EncodedSamplePipeline(
public EncodedSampleExporter(
Format format,
TransformationRequest transformationRequest,
MuxerWrapper muxerWrapper,

View File

@ -164,7 +164,7 @@ public final class ExoPlayerAssetLoader implements AssetLoader {
.setForceHighestSupportedBitrate(true)
.build());
// Arbitrarily decrease buffers for playback so that samples start being sent earlier to the
// pipelines (rebuffers are less problematic for the export use case).
// exporters (rebuffers are less problematic for the export use case).
DefaultLoadControl loadControl =
new DefaultLoadControl.Builder()
.setBufferDurationsMs(

View File

@ -19,20 +19,20 @@ import androidx.annotation.Nullable;
import androidx.media3.common.Format;
import androidx.media3.common.MediaItem;
/** A listener for {@link MediaItem} changes in the {@linkplain SamplePipeline sample pipelines}. */
/** A listener for {@link MediaItem} changes in the {@linkplain SampleExporter sample exporters}. */
/* package */ interface OnMediaItemChangedListener {
/**
* Called when the {@link MediaItem} whose samples are passed to the {@link SamplePipeline}
* Called when the {@link MediaItem} whose samples are passed to the {@link SampleExporter}
* changes.
*
* @param editedMediaItem The {@link MediaItem} with the transformations to apply to it.
* @param durationUs The duration of the {@link MediaItem}, in microseconds.
* @param trackFormat The {@link Format} extracted (and possibly decoded) from the {@link
* MediaItem} track, which represents the samples input to the {@link SamplePipeline}. {@code
* MediaItem} track, which represents the samples input to the {@link SampleExporter}. {@code
* null} if no such track was extracted.
* @param isLast Whether the {@link MediaItem} is the last one passed to the {@link
* SamplePipeline}.
* SampleExporter}.
*/
void onMediaItemChanged(
EditedMediaItem editedMediaItem,

View File

@ -35,17 +35,17 @@ import com.google.common.collect.ImmutableSet;
import java.util.List;
/**
* Pipeline for processing media data.
* Exporter that processes media data.
*
* <p>This pipeline can be used to implement transformations of audio or video samples.
* <p>This exporter can be used to implement transformations of audio or video samples.
*
* <p>The {@link SampleConsumer} and {@link OnMediaItemChangedListener} methods must be called from
* the same thread. This thread can change when the {@link
* OnMediaItemChangedListener#onMediaItemChanged(EditedMediaItem, long, Format, boolean) MediaItem}
* changes, and can be different from the thread used to call the other {@code SamplePipeline}
* changes, and can be different from the thread used to call the other {@code SampleExporter}
* methods.
*/
/* package */ abstract class SamplePipeline implements SampleConsumer, OnMediaItemChangedListener {
/* package */ abstract class SampleExporter implements SampleConsumer, OnMediaItemChangedListener {
private final MuxerWrapper muxerWrapper;
private final @C.TrackType int outputTrackType;
@ -53,7 +53,7 @@ import java.util.List;
private boolean muxerWrapperTrackAdded;
public SamplePipeline(Format firstInputFormat, MuxerWrapper muxerWrapper) {
public SampleExporter(Format firstInputFormat, MuxerWrapper muxerWrapper) {
this.muxerWrapper = muxerWrapper;
this.metadata = firstInputFormat.metadata;
outputTrackType = getProcessedTrackType(firstInputFormat.sampleMimeType);
@ -67,7 +67,7 @@ import java.util.List;
return feedMuxer() || (!isMuxerInputEnded() && processDataUpToMuxer());
}
/** Releases all resources held by the pipeline. */
/** Releases all resources held by the exporter. */
public abstract void release();
protected boolean processDataUpToMuxer() throws ExportException {

View File

@ -93,13 +93,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
// Internal messages.
private static final int MSG_START = 0;
private static final int MSG_REGISTER_SAMPLE_PIPELINE = 1;
private static final int MSG_DRAIN_PIPELINES = 2;
private static final int MSG_REGISTER_SAMPLE_EXPORTER = 1;
private static final int MSG_DRAIN_EXPORTERS = 2;
private static final int MSG_END = 3;
private static final int MSG_UPDATE_PROGRESS = 4;
private static final String TAG = "TransformerInternal";
private static final int DRAIN_PIPELINES_DELAY_MS = 10;
private static final int DRAIN_EXPORTERS_DELAY_MS = 10;
private final Context context;
private final Composition composition;
@ -115,13 +115,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private final AtomicInteger tracksToAdd;
private final AtomicBoolean outputHasAudio;
private final AtomicBoolean outputHasVideo;
private final List<SamplePipeline> samplePipelines;
private final List<SampleExporter> sampleExporters;
private final Object setMaxSequenceDurationUsLock;
private final MuxerWrapper muxerWrapper;
private final ConditionVariable transformerConditionVariable;
private final ExportResult.Builder exportResultBuilder;
private boolean isDrainingPipelines;
private boolean isDrainingExporters;
private long currentMaxSequenceDurationUs;
private int nonLoopingSequencesWithNonFinalDuration;
private @Transformer.ProgressState int progressState;
@ -185,7 +185,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
tracksToAdd = new AtomicInteger();
outputHasAudio = new AtomicBoolean();
outputHasVideo = new AtomicBoolean();
samplePipelines = new ArrayList<>();
sampleExporters = new ArrayList<>();
setMaxSequenceDurationUsLock = new Object();
transformerConditionVariable = new ConditionVariable();
exportResultBuilder = new ExportResult.Builder();
@ -288,11 +288,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
case MSG_START:
startInternal();
break;
case MSG_REGISTER_SAMPLE_PIPELINE:
registerSamplePipelineInternal((SamplePipeline) msg.obj);
case MSG_REGISTER_SAMPLE_EXPORTER:
registerSampleExporterInternal((SampleExporter) msg.obj);
break;
case MSG_DRAIN_PIPELINES:
drainPipelinesInternal();
case MSG_DRAIN_EXPORTERS:
drainExportersInternal();
break;
case MSG_END:
endInternal(/* endReason= */ msg.arg1, /* exportException= */ (ExportException) msg.obj);
@ -317,21 +317,21 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
}
private void registerSamplePipelineInternal(SamplePipeline samplePipeline) {
samplePipelines.add(samplePipeline);
if (!isDrainingPipelines) {
internalHandler.sendEmptyMessage(MSG_DRAIN_PIPELINES);
isDrainingPipelines = true;
private void registerSampleExporterInternal(SampleExporter sampleExporter) {
sampleExporters.add(sampleExporter);
if (!isDrainingExporters) {
internalHandler.sendEmptyMessage(MSG_DRAIN_EXPORTERS);
isDrainingExporters = true;
}
}
private void drainPipelinesInternal() throws ExportException {
for (int i = 0; i < samplePipelines.size(); i++) {
while (samplePipelines.get(i).processData()) {}
private void drainExportersInternal() throws ExportException {
for (int i = 0; i < sampleExporters.size(); i++) {
while (sampleExporters.get(i).processData()) {}
}
if (!muxerWrapper.isEnded()) {
internalHandler.sendEmptyMessageDelayed(MSG_DRAIN_PIPELINES, DRAIN_PIPELINES_DELAY_MS);
internalHandler.sendEmptyMessageDelayed(MSG_DRAIN_EXPORTERS, DRAIN_EXPORTERS_DELAY_MS);
}
}
@ -351,12 +351,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
boolean releasedPreviously = released;
if (!released) {
released = true;
// The video sample pipeline can hold buffers from the asset loader's decoder in a surface
// texture, so we release the video sample pipeline first to avoid releasing the codec while
// its buffers are pending processing.
for (int i = 0; i < samplePipelines.size(); i++) {
// VideoSampleExporter can hold buffers from the asset loader's decoder in a surface texture,
// so we release the VideoSampleExporter first to avoid releasing the codec while its buffers
// are pending processing.
for (int i = 0; i < sampleExporters.size(); i++) {
try {
samplePipelines.get(i).release();
sampleExporters.get(i).release();
} catch (RuntimeException e) {
if (releaseExportException == null) {
releaseExportException = ExportException.createForUnexpected(e);
@ -525,19 +525,19 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@C.TrackType int trackType = getProcessedTrackType(assetLoaderOutputFormat.sampleMimeType);
AddedTrackInfo trackInfo = checkStateNotNull(addedTrackInfoByTrackType.get(trackType));
SamplePipeline samplePipeline = getSamplePipeline(assetLoaderOutputFormat, trackInfo);
SampleExporter sampleExporter = getSampleExporter(assetLoaderOutputFormat, trackInfo);
OnMediaItemChangedListener onMediaItemChangedListener =
(editedMediaItem, durationUs, trackFormat, isLast) -> {
onMediaItemChanged(trackType, durationUs, isLast);
samplePipeline.onMediaItemChanged(editedMediaItem, durationUs, trackFormat, isLast);
sampleExporter.onMediaItemChanged(editedMediaItem, durationUs, trackFormat, isLast);
};
sequenceAssetLoaders
.get(sequenceIndex)
.addOnMediaItemChangedListener(onMediaItemChangedListener, trackType);
internalHandler.obtainMessage(MSG_REGISTER_SAMPLE_PIPELINE, samplePipeline).sendToTarget();
return samplePipeline;
internalHandler.obtainMessage(MSG_REGISTER_SAMPLE_EXPORTER, sampleExporter).sendToTarget();
return sampleExporter;
}
@Override
@ -547,14 +547,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
// Private methods.
private SamplePipeline getSamplePipeline(
private SampleExporter getSampleExporter(
Format firstAssetLoaderOutputFormat, AddedTrackInfo addedTrackInfo) throws ExportException {
if (addedTrackInfo.shouldTranscode) {
EditedMediaItem firstEditedMediaItem = editedMediaItems.get(0);
if (MimeTypes.isAudio(firstAssetLoaderOutputFormat.sampleMimeType)) {
return new AudioSamplePipeline(
return new AudioSampleExporter(
addedTrackInfo.firstAssetLoaderInputFormat,
/* firstPipelineInputFormat= */ firstAssetLoaderOutputFormat,
/* firstExporterInputFormat= */ firstAssetLoaderOutputFormat,
transformationRequest,
firstEditedMediaItem,
encoderFactory,
@ -569,7 +569,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
: (Presentation) compositionVideoEffects.get(0);
// TODO(b/267301878): Pass firstAssetLoaderOutputFormat once surface creation not in VSP.
return new VideoSamplePipeline(
return new VideoSampleExporter(
context,
addedTrackInfo.firstAssetLoaderInputFormat,
transformationRequest,
@ -583,7 +583,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
}
return new EncodedSamplePipeline(
return new EncodedSampleExporter(
firstAssetLoaderOutputFormat, transformationRequest, muxerWrapper, fallbackListener);
}

View File

@ -64,10 +64,10 @@ import java.util.concurrent.atomic.AtomicLong;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.dataflow.qual.Pure;
/** Pipeline to process, re-encode and mux raw video frames. */
/* package */ final class VideoSamplePipeline extends SamplePipeline {
/** Processes, re-encodes and muxes raw video frames. */
/* package */ final class VideoSampleExporter extends SampleExporter {
private static final String TAG = "VideoSamplePipeline";
private static final String TAG = "VideoSampleExporter";
private final AtomicLong mediaItemOffsetUs;
private final VideoFrameProcessor videoFrameProcessor;
private final ColorInfo videoFrameProcessorInputColor;
@ -83,7 +83,7 @@ import org.checkerframework.dataflow.qual.Pure;
*/
private volatile long finalFramePresentationTimeUs;
public VideoSamplePipeline(
public VideoSampleExporter(
Context context,
Format firstInputFormat,
TransformationRequest transformationRequest,
@ -96,8 +96,8 @@ import org.checkerframework.dataflow.qual.Pure;
DebugViewProvider debugViewProvider)
throws ExportException {
// TODO(b/262693177) Add tests for input format change.
// TODO(b/278259383) Consider delaying configuration of VideoSamplePipeline to use the decoder
// output format instead of the extractor output format, to match AudioSamplePipeline behavior.
// TODO(b/278259383) Consider delaying configuration of VideoSampleExporter to use the decoder
// output format instead of the extractor output format, to match AudioSampleExporter behavior.
super(firstInputFormat, muxerWrapper);
mediaItemOffsetUs = new AtomicLong();
@ -189,7 +189,7 @@ import org.checkerframework.dataflow.qual.Pure;
@Override
public void onEnded() {
VideoSamplePipeline.this.finalFramePresentationTimeUs =
VideoSampleExporter.this.finalFramePresentationTimeUs =
lastProcessedFramePresentationTimeUs;
try {
encoderWrapper.signalEndOfInputStream();

View File

@ -905,7 +905,7 @@ public final class MediaItemExportTest {
}
@Test
public void start_withAssetLoaderAlwaysDecoding_pipelineExpectsDecoded() throws Exception {
public void start_withAssetLoaderAlwaysDecoding_exporterExpectsDecoded() throws Exception {
AtomicReference<SampleConsumer> sampleConsumerRef = new AtomicReference<>();
Transformer transformer =
createTransformerBuilder(testMuxerHolder, /* enableFallback= */ false)
@ -917,7 +917,7 @@ public final class MediaItemExportTest {
transformer.start(mediaItem, outputPath);
runLooperUntil(transformer.getApplicationLooper(), () -> sampleConsumerRef.get() != null);
assertThat(sampleConsumerRef.get()).isNotInstanceOf(EncodedSamplePipeline.class);
assertThat(sampleConsumerRef.get()).isNotInstanceOf(EncodedSampleExporter.class);
}
@Test

View File

@ -41,7 +41,7 @@ import org.robolectric.shadows.MediaCodecInfoBuilder;
import org.robolectric.shadows.ShadowMediaCodec;
import org.robolectric.shadows.ShadowMediaCodecList;
/** Unit tests for {@link VideoSamplePipeline.EncoderWrapper}. */
/** Unit tests for {@link VideoSampleExporter.EncoderWrapper}. */
@RunWith(AndroidJUnit4.class)
public final class VideoEncoderWrapperTest {
private static final Composition FAKE_COMPOSITION =
@ -61,8 +61,8 @@ public final class VideoEncoderWrapperTest {
new ListenerSet<>(Looper.myLooper(), Clock.DEFAULT, (listener, flags) -> {}),
Clock.DEFAULT.createHandler(Looper.myLooper(), /* callback= */ null),
emptyTransformationRequest);
private final VideoSamplePipeline.EncoderWrapper encoderWrapper =
new VideoSamplePipeline.EncoderWrapper(
private final VideoSampleExporter.EncoderWrapper encoderWrapper =
new VideoSampleExporter.EncoderWrapper(
fakeEncoderFactory,
/* inputFormat= */ new Format.Builder()
.setSampleMimeType(MimeTypes.VIDEO_H264)