Fix spurious failures due to late decoding.

By default, if a codec is instantiated during an ongoing
playback, ExoPlayer will render the first frame that it
receives (so that there's "something other than black"
drawn to the surface). This frame is the key-frame before
the current playback position, and may be as much as 5
seconds behind the current position. ExoPlayer then drops
subsequent frames that are late until it's caught up to
the current position again.

For GTS tests that are counting dropped frames, this is
not desirable behavior, since it will cause spurious test
failures in cases where DummySurface is not supported.
This change overrides the default behavior so that the
player instead skips (rather than drops) frames until it's
caught up to the current playback position, and only then
renders the first frame.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=170175944
This commit is contained in:
olly 2017-09-27 03:14:20 -07:00 committed by Oliver Woodman
parent a3be937650
commit 55f696e11d

View File

@ -17,6 +17,8 @@ package com.google.android.exoplayer2.testutil;
import android.annotation.TargetApi;
import android.content.Context;
import android.media.MediaCodec;
import android.media.MediaCrypto;
import android.os.Handler;
import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlaybackException;
@ -25,9 +27,12 @@ import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
import com.google.android.exoplayer2.video.VideoRendererEventListener;
import java.nio.ByteBuffer;
import java.util.ArrayList;
/**
@ -66,6 +71,7 @@ public class DebugRenderersFactory extends DefaultRenderersFactory {
private int queueSize;
private int bufferCount;
private int minimumInsertIndex;
private boolean skipToPositionBeforeRenderingFirstFrame;
public DebugMediaCodecVideoRenderer(Context context, MediaCodecSelector mediaCodecSelector,
long allowedJoiningTimeMs, DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
@ -75,10 +81,23 @@ public class DebugRenderersFactory extends DefaultRenderersFactory {
eventHandler, eventListener, maxDroppedFrameCountToNotify);
}
@Override
protected void configureCodec(MediaCodecInfo codecInfo, MediaCodec codec, Format format,
MediaCrypto crypto) throws DecoderQueryException {
// If the codec is being initialized whilst the renderer is started, default behavior is to
// render the first frame (i.e. the keyframe before the current position), then drop frames up
// to the current playback position. For test runs that place a maximum limit on the number of
// dropped frames allowed, this is not desired behavior. Hence we skip (rather than drop)
// frames up to the current playback position [Internal: b/66494991].
skipToPositionBeforeRenderingFirstFrame = getState() == Renderer.STATE_STARTED;
super.configureCodec(codecInfo, codec, format, crypto);
}
@Override
protected void releaseCodec() {
super.releaseCodec();
clearTimestamps();
skipToPositionBeforeRenderingFirstFrame = false;
}
@Override
@ -102,6 +121,34 @@ public class DebugRenderersFactory extends DefaultRenderersFactory {
maybeShiftTimestampsList();
}
@Override
protected boolean processOutputBuffer(long positionUs, long elapsedRealtimeUs, MediaCodec codec,
ByteBuffer buffer, int bufferIndex, int bufferFlags, long bufferPresentationTimeUs,
boolean shouldSkip) {
if (skipToPositionBeforeRenderingFirstFrame && bufferPresentationTimeUs < positionUs) {
// After the codec has been initialized, don't render the first frame until we've caught up
// to the playback position. Else test runs on devices that do not support dummy surface
// will drop frames between rendering the first one and catching up [Internal: b/66494991].
shouldSkip = true;
}
return super.processOutputBuffer(positionUs, elapsedRealtimeUs, codec, buffer, bufferIndex,
bufferFlags, bufferPresentationTimeUs, shouldSkip);
}
@Override
protected void renderOutputBuffer(MediaCodec codec, int index, long presentationTimeUs) {
skipToPositionBeforeRenderingFirstFrame = false;
super.renderOutputBuffer(codec, index, presentationTimeUs);
}
@TargetApi(21)
@Override
protected void renderOutputBufferV21(MediaCodec codec, int index, long presentationTimeUs,
long releaseTimeNs) {
skipToPositionBeforeRenderingFirstFrame = false;
super.renderOutputBufferV21(codec, index, presentationTimeUs, releaseTimeNs);
}
@Override
protected void onProcessedOutputBuffer(long presentationTimeUs) {
super.onProcessedOutputBuffer(presentationTimeUs);