Skip to content

Commit

Permalink
Move progress computation to TransformerInternal
Browse files Browse the repository at this point in the history
AssetLoader will have multiple implementations and be customizable. We
want to remove the responsibility of computing the progress from this
class and centralize the logic in TransformerInternal.

PiperOrigin-RevId: 488608890
  • Loading branch information
kim-vde authored and microkatz committed Nov 16, 2022
1 parent 59aedcf commit 66dc6b3
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 36 deletions.
Expand Up @@ -18,11 +18,13 @@

import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkStateNotNull;
import static java.lang.Math.max;

import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.Util;
import androidx.media3.decoder.DecoderInputBuffer;
import java.nio.ByteBuffer;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
Expand All @@ -38,6 +40,7 @@

@Nullable private DecoderInputBuffer inputBuffer;
private boolean muxerWrapperTrackAdded;
private long currentPositionMs;
private boolean isEnded;

public BaseSamplePipeline(
Expand Down Expand Up @@ -65,9 +68,11 @@ public DecoderInputBuffer dequeueInputBuffer() throws TransformationException {

@Override
public void queueInputBuffer() throws TransformationException {
checkNotNull(inputBuffer);
DecoderInputBuffer inputBuffer = checkNotNull(this.inputBuffer);
currentPositionMs =
max(currentPositionMs, Util.usToMs(inputBuffer.timeUs - streamStartPositionUs));
checkNotNull(inputBuffer.data);
if (!shouldDropInputBuffer()) {
if (!shouldDropInputBuffer(inputBuffer)) {
queueInputBufferInternal();
}
}
Expand All @@ -82,6 +87,11 @@ public boolean isEnded() {
return isEnded;
}

@Override
public long getCurrentPositionMs() {
return currentPositionMs;
}

@Nullable
protected abstract DecoderInputBuffer dequeueInputBufferInternal() throws TransformationException;

Expand All @@ -103,16 +113,15 @@ public boolean isEnded() {
* Preprocesses an {@linkplain DecoderInputBuffer input buffer} queued to the pipeline and returns
* whether it should be dropped.
*/
@RequiresNonNull({"inputBuffer", "inputBuffer.data"})
private boolean shouldDropInputBuffer() {
@RequiresNonNull("#1.data")
private boolean shouldDropInputBuffer(DecoderInputBuffer inputBuffer) {
ByteBuffer inputBytes = inputBuffer.data;

if (sefVideoSlowMotionFlattener == null || inputBuffer.isEndOfStream()) {
return false;
}

long presentationTimeUs = inputBuffer.timeUs - streamOffsetUs;
DecoderInputBuffer inputBuffer = this.inputBuffer;
boolean shouldDropInputBuffer =
sefVideoSlowMotionFlattener.dropOrTransformSample(inputBytes, presentationTimeUs);
if (shouldDropInputBuffer) {
Expand Down
Expand Up @@ -21,11 +21,6 @@
import static androidx.media3.exoplayer.DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS;
import static androidx.media3.exoplayer.DefaultLoadControl.DEFAULT_MAX_BUFFER_MS;
import static androidx.media3.exoplayer.DefaultLoadControl.DEFAULT_MIN_BUFFER_MS;
import static androidx.media3.transformer.Transformer.PROGRESS_STATE_AVAILABLE;
import static androidx.media3.transformer.Transformer.PROGRESS_STATE_NO_TRANSFORMATION;
import static androidx.media3.transformer.Transformer.PROGRESS_STATE_UNAVAILABLE;
import static androidx.media3.transformer.Transformer.PROGRESS_STATE_WAITING_FOR_AVAILABILITY;
import static java.lang.Math.min;

import android.content.Context;
import android.os.Handler;
Expand All @@ -39,6 +34,7 @@
import androidx.media3.common.Timeline;
import androidx.media3.common.Tracks;
import androidx.media3.common.util.Clock;
import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.DefaultLoadControl;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.Renderer;
Expand All @@ -54,6 +50,8 @@

public interface Listener {

void onDurationMs(long durationMs);

void onTrackRegistered();

void onAllTracksRegistered();
Expand All @@ -74,7 +72,6 @@ SamplePipeline onTrackAdded(Format format, long streamStartPositionUs, long stre
private final Clock clock;

@Nullable private ExoPlayer player;
private @Transformer.ProgressState int progressState;

public ExoPlayerAssetLoader(
Context context,
Expand All @@ -89,7 +86,6 @@ public ExoPlayerAssetLoader(
this.mediaSourceFactory = mediaSourceFactory;
this.looper = looper;
this.clock = clock;
progressState = PROGRESS_STATE_NO_TRANSFORMATION;
}

public void start(MediaItem mediaItem, Listener listener) {
Expand Down Expand Up @@ -125,22 +121,9 @@ public void start(MediaItem mediaItem, Listener listener) {
player.setMediaItem(mediaItem);
player.addListener(new PlayerListener(listener));
player.prepare();

progressState = PROGRESS_STATE_WAITING_FOR_AVAILABILITY;
}

public @Transformer.ProgressState int getProgress(ProgressHolder progressHolder) {
if (progressState == PROGRESS_STATE_AVAILABLE) {
Player player = checkNotNull(this.player);
long durationMs = player.getDuration();
long positionMs = player.getCurrentPosition();
progressHolder.progress = min((int) (positionMs * 100 / durationMs), 99);
}
return progressState;
}

public void release() {
progressState = PROGRESS_STATE_NO_TRANSFORMATION;
if (player != null) {
player.release();
player = null;
Expand Down Expand Up @@ -191,6 +174,7 @@ public Renderer[] createRenderers(
private final class PlayerListener implements Player.Listener {

private final Listener listener;
private boolean hasSentDuration;

public PlayerListener(Listener listener) {
this.listener = listener;
Expand All @@ -205,20 +189,14 @@ public void onPlaybackStateChanged(int state) {

@Override
public void onTimelineChanged(Timeline timeline, int reason) {
if (progressState != PROGRESS_STATE_WAITING_FOR_AVAILABILITY) {
if (hasSentDuration) {
return;
}
Timeline.Window window = new Timeline.Window();
timeline.getWindow(/* windowIndex= */ 0, window);
if (!window.isPlaceholder) {
long durationUs = window.durationUs;
// Make progress permanently unavailable if the duration is unknown, so that it doesn't jump
// to a high value at the end of the transformation if the duration is set once the media is
// entirely loaded.
progressState =
durationUs <= 0 || durationUs == C.TIME_UNSET
? PROGRESS_STATE_UNAVAILABLE
: PROGRESS_STATE_AVAILABLE;
listener.onDurationMs(Util.usToMs(window.durationUs));
hasSentDuration = true;
checkNotNull(player).play();
}
}
Expand Down
Expand Up @@ -49,4 +49,10 @@

/** Releases all resources held by the pipeline. */
void release();

/**
* Returns the current timestamp being processed in the track, in milliseconds. This is the
* largest timestamp queued minus the stream start time, or 0 if no input has been queued.
*/
long getCurrentPositionMs();
}
Expand Up @@ -16,6 +16,12 @@

package androidx.media3.transformer;

import static androidx.media3.transformer.Transformer.PROGRESS_STATE_AVAILABLE;
import static androidx.media3.transformer.Transformer.PROGRESS_STATE_NO_TRANSFORMATION;
import static androidx.media3.transformer.Transformer.PROGRESS_STATE_UNAVAILABLE;
import static androidx.media3.transformer.Transformer.PROGRESS_STATE_WAITING_FOR_AVAILABILITY;
import static java.lang.Math.min;

import android.content.Context;
import android.os.Looper;
import androidx.annotation.Nullable;
Expand All @@ -32,6 +38,8 @@
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.extractor.metadata.mp4.SlowMotionData;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;

/* package */ final class TransformerInternal {

Expand All @@ -50,6 +58,10 @@ public interface Listener {
private final FrameProcessor.Factory frameProcessorFactory;
private final DebugViewProvider debugViewProvider;
private final ExoPlayerAssetLoader exoPlayerAssetLoader;
private final List<SamplePipeline> samplePipelines;

private @Transformer.ProgressState int progressState;
private long durationMs;

public TransformerInternal(
Context context,
Expand All @@ -74,6 +86,8 @@ public TransformerInternal(
exoPlayerAssetLoader =
new ExoPlayerAssetLoader(
context, removeAudio, removeVideo, mediaSourceFactory, looper, clock);
samplePipelines = new ArrayList<>(/* initialCapacity= */ 2);
progressState = PROGRESS_STATE_NO_TRANSFORMATION;
}

public void start(
Expand All @@ -84,16 +98,34 @@ public void start(
AssetLoaderListener assetLoaderListener =
new AssetLoaderListener(mediaItem, muxerWrapper, listener, fallbackListener);
exoPlayerAssetLoader.start(mediaItem, assetLoaderListener);
progressState = PROGRESS_STATE_WAITING_FOR_AVAILABILITY;
}

public @Transformer.ProgressState int getProgress(ProgressHolder progressHolder) {
return exoPlayerAssetLoader.getProgress(progressHolder);
if (progressState == PROGRESS_STATE_AVAILABLE) {
long positionMs = getCurrentPositionMs();
progressHolder.progress = min((int) (positionMs * 100 / durationMs), 99);
}
return progressState;
}

public void release() {
samplePipelines.clear();
progressState = PROGRESS_STATE_NO_TRANSFORMATION;
exoPlayerAssetLoader.release();
}

private long getCurrentPositionMs() {
if (samplePipelines.isEmpty()) {
return 0;
}
long positionMsSum = 0;
for (int i = 0; i < samplePipelines.size(); i++) {
positionMsSum += samplePipelines.get(i).getCurrentPositionMs();
}
return positionMsSum / samplePipelines.size();
}

private class AssetLoaderListener implements ExoPlayerAssetLoader.Listener {

private final MediaItem mediaItem;
Expand All @@ -114,6 +146,18 @@ public AssetLoaderListener(
this.fallbackListener = fallbackListener;
}

@Override
public void onDurationMs(long durationMs) {
// Make progress permanently unavailable if the duration is unknown, so that it doesn't jump
// to a high value at the end of the transformation if the duration is set once the media is
// entirely loaded.
progressState =
durationMs <= 0 || durationMs == C.TIME_UNSET
? PROGRESS_STATE_UNAVAILABLE
: PROGRESS_STATE_AVAILABLE;
TransformerInternal.this.durationMs = durationMs;
}

@Override
public void onTrackRegistered() {
trackRegistered = true;
Expand All @@ -132,7 +176,10 @@ public void onAllTracksRegistered() {
public SamplePipeline onTrackAdded(
Format format, long streamStartPositionUs, long streamOffsetUs)
throws TransformationException {
return getSamplePipeline(format, streamStartPositionUs, streamOffsetUs);
SamplePipeline samplePipeline =
getSamplePipeline(format, streamStartPositionUs, streamOffsetUs);
samplePipelines.add(samplePipeline);
return samplePipeline;
}

@Override
Expand Down

0 comments on commit 66dc6b3

Please sign in to comment.