mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Enable seamless rejoing for Vp9 extension.
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=123982330
This commit is contained in:
parent
afa1ad1a6a
commit
c622483f79
3 changed files with 60 additions and 24 deletions
|
|
@ -88,7 +88,7 @@ public class VpxPlaybackTest extends InstrumentationTestCase {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Looper.prepare();
|
Looper.prepare();
|
||||||
LibvpxVideoTrackRenderer videoRenderer = new LibvpxVideoTrackRenderer(true);
|
LibvpxVideoTrackRenderer videoRenderer = new LibvpxVideoTrackRenderer(true, 0);
|
||||||
DefaultTrackSelector trackSelector = new DefaultTrackSelector(
|
DefaultTrackSelector trackSelector = new DefaultTrackSelector(
|
||||||
new DefaultTrackSelectionPolicy(), null);
|
new DefaultTrackSelectionPolicy(), null);
|
||||||
player = ExoPlayerFactory.newInstance(new TrackRenderer[] {videoRenderer}, trackSelector);
|
player = ExoPlayerFactory.newInstance(new TrackRenderer[] {videoRenderer}, trackSelector);
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,9 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
|
||||||
public final CodecCounters codecCounters = new CodecCounters();
|
public final CodecCounters codecCounters = new CodecCounters();
|
||||||
|
|
||||||
private final boolean scaleToFit;
|
private final boolean scaleToFit;
|
||||||
private final EventDispatcher eventDispatcher;
|
private final long allowedJoiningTimeMs;
|
||||||
private final int maxDroppedFrameCountToNotify;
|
private final int maxDroppedFrameCountToNotify;
|
||||||
|
private final EventDispatcher eventDispatcher;
|
||||||
private final FormatHolder formatHolder;
|
private final FormatHolder formatHolder;
|
||||||
|
|
||||||
private Format format;
|
private Format format;
|
||||||
|
|
@ -71,6 +72,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
|
||||||
private Bitmap bitmap;
|
private Bitmap bitmap;
|
||||||
private boolean drawnToSurface;
|
private boolean drawnToSurface;
|
||||||
private boolean renderedFirstFrame;
|
private boolean renderedFirstFrame;
|
||||||
|
private long joiningDeadlineMs;
|
||||||
private Surface surface;
|
private Surface surface;
|
||||||
private VpxOutputBufferRenderer outputBufferRenderer;
|
private VpxOutputBufferRenderer outputBufferRenderer;
|
||||||
private int outputMode;
|
private int outputMode;
|
||||||
|
|
@ -85,26 +87,31 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
|
||||||
private int consecutiveDroppedFrameCount;
|
private int consecutiveDroppedFrameCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param scaleToFit Boolean that indicates if video frames should be scaled to fit when
|
* @param scaleToFit Whether video frames should be scaled to fit when rendering.
|
||||||
* rendering.
|
* @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer
|
||||||
|
* can attempt to seamlessly join an ongoing playback.
|
||||||
*/
|
*/
|
||||||
public LibvpxVideoTrackRenderer(boolean scaleToFit) {
|
public LibvpxVideoTrackRenderer(boolean scaleToFit, long allowedJoiningTimeMs) {
|
||||||
this(scaleToFit, null, null, 0);
|
this(scaleToFit, allowedJoiningTimeMs, null, null, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param scaleToFit Boolean that indicates if video frames should be scaled to fit when
|
* @param scaleToFit Whether video frames should be scaled to fit when rendering.
|
||||||
* rendering.
|
* @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer
|
||||||
|
* can attempt to seamlessly join an ongoing playback.
|
||||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||||
* null if delivery of events is not required.
|
* null if delivery of events is not required.
|
||||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||||
* @param maxDroppedFrameCountToNotify The maximum number of frames that can be dropped between
|
* @param maxDroppedFrameCountToNotify The maximum number of frames that can be dropped between
|
||||||
* invocations of {@link VideoTrackRendererEventListener#onDroppedFrames(int, long)}.
|
* invocations of {@link VideoTrackRendererEventListener#onDroppedFrames(int, long)}.
|
||||||
*/
|
*/
|
||||||
public LibvpxVideoTrackRenderer(boolean scaleToFit, Handler eventHandler,
|
public LibvpxVideoTrackRenderer(boolean scaleToFit, long allowedJoiningTimeMs,
|
||||||
VideoTrackRendererEventListener eventListener, int maxDroppedFrameCountToNotify) {
|
Handler eventHandler, VideoTrackRendererEventListener eventListener,
|
||||||
|
int maxDroppedFrameCountToNotify) {
|
||||||
this.scaleToFit = scaleToFit;
|
this.scaleToFit = scaleToFit;
|
||||||
|
this.allowedJoiningTimeMs = allowedJoiningTimeMs;
|
||||||
this.maxDroppedFrameCountToNotify = maxDroppedFrameCountToNotify;
|
this.maxDroppedFrameCountToNotify = maxDroppedFrameCountToNotify;
|
||||||
|
joiningDeadlineMs = -1;
|
||||||
previousWidth = -1;
|
previousWidth = -1;
|
||||||
previousHeight = -1;
|
previousHeight = -1;
|
||||||
formatHolder = new FormatHolder();
|
formatHolder = new FormatHolder();
|
||||||
|
|
@ -202,10 +209,11 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop frame only if we have the next frame and that's also late, otherwise render whatever we
|
// Drop the frame if we're joining and are more than 30ms late, or if we have the next frame
|
||||||
// have.
|
// and that's also late. Else we'll render what we have.
|
||||||
if (nextOutputBuffer != null && nextOutputBuffer.timestampUs < positionUs) {
|
if ((joiningDeadlineMs != -1 && outputBuffer.timestampUs < positionUs - 30000)
|
||||||
// Drop frame if we are too late.
|
|| (nextOutputBuffer != null && !nextOutputBuffer.isEndOfStream()
|
||||||
|
&& nextOutputBuffer.timestampUs < positionUs)) {
|
||||||
codecCounters.droppedOutputBufferCount++;
|
codecCounters.droppedOutputBufferCount++;
|
||||||
droppedFrameCount++;
|
droppedFrameCount++;
|
||||||
consecutiveDroppedFrameCount++;
|
consecutiveDroppedFrameCount++;
|
||||||
|
|
@ -322,7 +330,21 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isReady() {
|
protected boolean isReady() {
|
||||||
return format != null && (isSourceReady() || outputBuffer != null) && renderedFirstFrame;
|
if (format != null && (isSourceReady() || outputBuffer != null) && renderedFirstFrame) {
|
||||||
|
// Ready. If we were joining then we've now joined, so clear the joining deadline.
|
||||||
|
joiningDeadlineMs = -1;
|
||||||
|
return true;
|
||||||
|
} else if (joiningDeadlineMs == -1) {
|
||||||
|
// Not joining.
|
||||||
|
return false;
|
||||||
|
} else if (SystemClock.elapsedRealtime() < joiningDeadlineMs) {
|
||||||
|
// Joining and still within the joining deadline.
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// The joining deadline has been exceeded. Give up and clear the deadline.
|
||||||
|
joiningDeadlineMs = -1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -340,6 +362,8 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
|
||||||
if (decoder != null) {
|
if (decoder != null) {
|
||||||
flushDecoder();
|
flushDecoder();
|
||||||
}
|
}
|
||||||
|
joiningDeadlineMs = joining && allowedJoiningTimeMs > 0
|
||||||
|
? (SystemClock.elapsedRealtime() + allowedJoiningTimeMs) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -350,6 +374,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStopped() {
|
protected void onStopped() {
|
||||||
|
joiningDeadlineMs = -1;
|
||||||
maybeNotifyDroppedFrameCount();
|
maybeNotifyDroppedFrameCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,8 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String TAG = "SimpleExoPlayer";
|
private static final String TAG = "SimpleExoPlayer";
|
||||||
|
private static final long ALLOWED_VIDEO_JOINING_TIME_MS = 5000;
|
||||||
|
private static final int MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY = 50;
|
||||||
|
|
||||||
private final ExoPlayer player;
|
private final ExoPlayer player;
|
||||||
private final BandwidthMeter bandwidthMeter;
|
private final BandwidthMeter bandwidthMeter;
|
||||||
|
|
@ -381,8 +383,9 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
||||||
private void buildRenderers(Context context, DrmSessionManager drmSessionManager,
|
private void buildRenderers(Context context, DrmSessionManager drmSessionManager,
|
||||||
ArrayList<TrackRenderer> renderersList) {
|
ArrayList<TrackRenderer> renderersList) {
|
||||||
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context,
|
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context,
|
||||||
MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000,
|
MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT,
|
||||||
drmSessionManager, false, mainHandler, componentListener, 50);
|
ALLOWED_VIDEO_JOINING_TIME_MS, drmSessionManager, false, mainHandler, componentListener,
|
||||||
|
MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY);
|
||||||
renderersList.add(videoRenderer);
|
renderersList.add(videoRenderer);
|
||||||
|
|
||||||
TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(MediaCodecSelector.DEFAULT,
|
TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(MediaCodecSelector.DEFAULT,
|
||||||
|
|
@ -405,13 +408,15 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
||||||
try {
|
try {
|
||||||
Class<?> clazz =
|
Class<?> clazz =
|
||||||
Class.forName("com.google.android.exoplayer.ext.vp9.LibvpxVideoTrackRenderer");
|
Class.forName("com.google.android.exoplayer.ext.vp9.LibvpxVideoTrackRenderer");
|
||||||
Constructor<?> constructor = clazz.getConstructor(boolean.class, Handler.class,
|
Constructor<?> constructor = clazz.getConstructor(boolean.class, long.class, Handler.class,
|
||||||
VideoTrackRendererEventListener.class, int.class);
|
VideoTrackRendererEventListener.class, int.class);
|
||||||
renderersList.add((TrackRenderer) constructor.newInstance(true, mainHandler,
|
renderersList.add((TrackRenderer) constructor.newInstance(true, ALLOWED_VIDEO_JOINING_TIME_MS,
|
||||||
componentListener, 50));
|
mainHandler, componentListener, MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY));
|
||||||
Log.i(TAG, "Loaded LibvpxVideoTrackRenderer.");
|
Log.i(TAG, "Loaded LibvpxVideoTrackRenderer.");
|
||||||
} catch (Exception e) {
|
} catch (ClassNotFoundException e) {
|
||||||
// Expected if the app was built without the extension.
|
// Expected if the app was built without the extension.
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -421,8 +426,10 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
||||||
AudioTrackRendererEventListener.class);
|
AudioTrackRendererEventListener.class);
|
||||||
renderersList.add((TrackRenderer) constructor.newInstance(mainHandler, componentListener));
|
renderersList.add((TrackRenderer) constructor.newInstance(mainHandler, componentListener));
|
||||||
Log.i(TAG, "Loaded LibopusAudioTrackRenderer.");
|
Log.i(TAG, "Loaded LibopusAudioTrackRenderer.");
|
||||||
} catch (Exception e) {
|
} catch (ClassNotFoundException e) {
|
||||||
// Expected if the app was built without the extension.
|
// Expected if the app was built without the extension.
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -432,8 +439,10 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
||||||
AudioTrackRendererEventListener.class);
|
AudioTrackRendererEventListener.class);
|
||||||
renderersList.add((TrackRenderer) constructor.newInstance(mainHandler, componentListener));
|
renderersList.add((TrackRenderer) constructor.newInstance(mainHandler, componentListener));
|
||||||
Log.i(TAG, "Loaded LibflacAudioTrackRenderer.");
|
Log.i(TAG, "Loaded LibflacAudioTrackRenderer.");
|
||||||
} catch (Exception e) {
|
} catch (ClassNotFoundException e) {
|
||||||
// Expected if the app was built without the extension.
|
// Expected if the app was built without the extension.
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -443,8 +452,10 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
||||||
AudioTrackRendererEventListener.class);
|
AudioTrackRendererEventListener.class);
|
||||||
renderersList.add((TrackRenderer) constructor.newInstance(mainHandler, componentListener));
|
renderersList.add((TrackRenderer) constructor.newInstance(mainHandler, componentListener));
|
||||||
Log.i(TAG, "Loaded FfmpegAudioTrackRenderer.");
|
Log.i(TAG, "Loaded FfmpegAudioTrackRenderer.");
|
||||||
} catch (Exception e) {
|
} catch (ClassNotFoundException e) {
|
||||||
// Expected if the app was built without the extension.
|
// Expected if the app was built without the extension.
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue