Enable seamless rejoing for Vp9 extension.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=123982330
This commit is contained in:
olly 2016-06-03 10:39:39 -07:00 committed by Oliver Woodman
parent afa1ad1a6a
commit c622483f79
3 changed files with 60 additions and 24 deletions

View file

@ -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);

View file

@ -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();
} }

View file

@ -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);
} }
} }