diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index de3f595976..a2ba72dbc0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -363,6 +363,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { @Nullable private DrmSession sourceDrmSession; @Nullable private MediaCrypto mediaCrypto; private boolean mediaCryptoRequiresSecureDecoder; + private long renderTimeLimitMs; private float operatingRate; @Nullable private MediaCodec codec; @Nullable private MediaCodecAdapter codecAdapter; @@ -442,6 +443,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { outputBufferInfo = new MediaCodec.BufferInfo(); operatingRate = 1f; mediaCodecOperationMode = OPERATION_MODE_SYNCHRONOUS; + renderTimeLimitMs = C.TIME_UNSET; pendingOutputStreamStartPositionsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT]; pendingOutputStreamOffsetsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT]; pendingOutputStreamSwitchTimesUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT]; @@ -451,6 +453,19 @@ public abstract class MediaCodecRenderer extends BaseRenderer { resetCodecStateForRelease(); } + /** + * Set a limit on the time a single {@link #render(long, long)} call can spend draining and + * filling the decoder. + * + *
This method should be called right after creating an instance of this class. + * + * @param renderTimeLimitMs The render time limit in milliseconds, or {@link C#TIME_UNSET} for no + * limit. + */ + public void setRenderTimeLimitMs(long renderTimeLimitMs) { + this.renderTimeLimitMs = renderTimeLimitMs; + } + /** * Set the mode of operation of the underlying {@link MediaCodec}. * @@ -837,9 +852,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer { while (bypassRender(positionUs, elapsedRealtimeUs)) {} TraceUtil.endSection(); } else if (codec != null) { + long renderStartTimeMs = SystemClock.elapsedRealtime(); TraceUtil.beginSection("drainAndFeed"); - while (drainOutputBuffer(positionUs, elapsedRealtimeUs)) {} - while (feedInputBuffer()) {} + while (drainOutputBuffer(positionUs, elapsedRealtimeUs) + && shouldContinueRendering(renderStartTimeMs)) {} + while (feedInputBuffer() && shouldContinueRendering(renderStartTimeMs)) {} TraceUtil.endSection(); } else { decoderCounters.skippedInputBufferCount += skipSource(positionUs); @@ -1171,6 +1188,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer { onCodecInitialized(codecName, codecInitializedTimestamp, elapsed); } + private boolean shouldContinueRendering(long renderStartTimeMs) { + return renderTimeLimitMs == C.TIME_UNSET + || SystemClock.elapsedRealtime() - renderStartTimeMs < renderTimeLimitMs; + } + private void getCodecBuffers(MediaCodec codec) { if (Util.SDK_INT < 21) { inputBuffers = codec.getInputBuffers();