Work around volantis adaptation issue.

The volantis H.264/AVC decoder could get stuck when adapting between certain
stream formats where there was no change in resolution.

Queue a small Baseline profile SPS, PPS and IDR slice during adaptation on this
device, to force reallocation of reference frames.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=127091330
This commit is contained in:
andrewlewis 2016-07-11 08:55:59 -07:00 committed by Oliver Woodman
parent 464869cf57
commit fdf26d6a1f

View File

@ -29,6 +29,7 @@ import android.media.MediaCodec;
import android.media.MediaCodec.CodecException; import android.media.MediaCodec.CodecException;
import android.media.MediaCodec.CryptoException; import android.media.MediaCodec.CryptoException;
import android.media.MediaCrypto; import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.os.Looper; import android.os.Looper;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Log; import android.util.Log;
@ -148,6 +149,16 @@ public abstract class MediaCodecRenderer extends Renderer {
*/ */
private static final int REINITIALIZATION_STATE_WAIT_END_OF_STREAM = 2; private static final int REINITIALIZATION_STATE_WAIT_END_OF_STREAM = 2;
/**
* H.264/AVC buffer to queue when using the adaptation workaround (see
* {@link #codecNeedsAdaptationWorkaround(String)}. Consists of three NAL units with start codes:
* Baseline sequence/picture parameter sets and a 32 * 32 pixel IDR slice. This stream can be
* queued to force a resolution change when adapting to a new format.
*/
private static final byte[] ADAPTATION_WORKAROUND_BUFFER = Util.getBytesFromHexString(
"0000016742C00BDA259000000168CE0F13200000016588840DCE7118A0002FBF1C31C3275D78");
private static final int ADAPTATION_WORKAROUND_SLICE_WIDTH_HEIGHT = 32;
private final MediaCodecSelector mediaCodecSelector; private final MediaCodecSelector mediaCodecSelector;
private final DrmSessionManager drmSessionManager; private final DrmSessionManager drmSessionManager;
private final boolean playClearSamplesWithoutKeys; private final boolean playClearSamplesWithoutKeys;
@ -162,9 +173,12 @@ public abstract class MediaCodecRenderer extends Renderer {
private boolean codecIsAdaptive; private boolean codecIsAdaptive;
private boolean codecNeedsDiscardToSpsWorkaround; private boolean codecNeedsDiscardToSpsWorkaround;
private boolean codecNeedsFlushWorkaround; private boolean codecNeedsFlushWorkaround;
private boolean codecNeedsAdaptationWorkaround;
private boolean codecNeedsEosPropagationWorkaround; private boolean codecNeedsEosPropagationWorkaround;
private boolean codecNeedsEosFlushWorkaround; private boolean codecNeedsEosFlushWorkaround;
private boolean codecNeedsMonoChannelCountWorkaround; private boolean codecNeedsMonoChannelCountWorkaround;
private boolean codecNeedsAdaptationWorkaroundBuffer;
private boolean shouldSkipAdaptationWorkaroundOutputBuffer;
private ByteBuffer[] inputBuffers; private ByteBuffer[] inputBuffers;
private ByteBuffer[] outputBuffers; private ByteBuffer[] outputBuffers;
private long codecHotswapDeadlineMs; private long codecHotswapDeadlineMs;
@ -316,6 +330,7 @@ public abstract class MediaCodecRenderer extends Renderer {
codecIsAdaptive = decoderInfo.adaptive; codecIsAdaptive = decoderInfo.adaptive;
codecNeedsDiscardToSpsWorkaround = codecNeedsDiscardToSpsWorkaround(codecName, format); codecNeedsDiscardToSpsWorkaround = codecNeedsDiscardToSpsWorkaround(codecName, format);
codecNeedsFlushWorkaround = codecNeedsFlushWorkaround(codecName); codecNeedsFlushWorkaround = codecNeedsFlushWorkaround(codecName);
codecNeedsAdaptationWorkaround = codecNeedsAdaptationWorkaround(codecName);
codecNeedsEosPropagationWorkaround = codecNeedsEosPropagationWorkaround(codecName); codecNeedsEosPropagationWorkaround = codecNeedsEosPropagationWorkaround(codecName);
codecNeedsEosFlushWorkaround = codecNeedsEosFlushWorkaround(codecName); codecNeedsEosFlushWorkaround = codecNeedsEosFlushWorkaround(codecName);
codecNeedsMonoChannelCountWorkaround = codecNeedsMonoChannelCountWorkaround(codecName, format); codecNeedsMonoChannelCountWorkaround = codecNeedsMonoChannelCountWorkaround(codecName, format);
@ -397,9 +412,12 @@ public abstract class MediaCodecRenderer extends Renderer {
codecIsAdaptive = false; codecIsAdaptive = false;
codecNeedsDiscardToSpsWorkaround = false; codecNeedsDiscardToSpsWorkaround = false;
codecNeedsFlushWorkaround = false; codecNeedsFlushWorkaround = false;
codecNeedsAdaptationWorkaround = false;
codecNeedsEosPropagationWorkaround = false; codecNeedsEosPropagationWorkaround = false;
codecNeedsEosFlushWorkaround = false; codecNeedsEosFlushWorkaround = false;
codecNeedsMonoChannelCountWorkaround = false; codecNeedsMonoChannelCountWorkaround = false;
codecNeedsAdaptationWorkaroundBuffer = false;
shouldSkipAdaptationWorkaroundOutputBuffer = false;
codecReceivedEos = false; codecReceivedEos = false;
codecReconfigurationState = RECONFIGURATION_STATE_NONE; codecReconfigurationState = RECONFIGURATION_STATE_NONE;
codecReinitializationState = REINITIALIZATION_STATE_NONE; codecReinitializationState = REINITIALIZATION_STATE_NONE;
@ -455,6 +473,8 @@ public abstract class MediaCodecRenderer extends Renderer {
waitingForKeys = false; waitingForKeys = false;
shouldSkipOutputBuffer = false; shouldSkipOutputBuffer = false;
decodeOnlyPresentationTimestamps.clear(); decodeOnlyPresentationTimestamps.clear();
codecNeedsAdaptationWorkaroundBuffer = false;
shouldSkipAdaptationWorkaroundOutputBuffer = false;
if (codecNeedsFlushWorkaround || (codecNeedsEosFlushWorkaround && codecReceivedEos)) { if (codecNeedsFlushWorkaround || (codecNeedsEosFlushWorkaround && codecReceivedEos)) {
// Workaround framework bugs. See [Internal: b/8347958, b/8578467, b/8543366, b/23361053]. // Workaround framework bugs. See [Internal: b/8347958, b/8578467, b/8543366, b/23361053].
releaseCodec(); releaseCodec();
@ -511,6 +531,15 @@ public abstract class MediaCodecRenderer extends Renderer {
return false; return false;
} }
if (codecNeedsAdaptationWorkaroundBuffer) {
codecNeedsAdaptationWorkaroundBuffer = false;
buffer.data.put(ADAPTATION_WORKAROUND_BUFFER);
codec.queueInputBuffer(inputIndex, 0, ADAPTATION_WORKAROUND_BUFFER.length, 0, 0);
inputIndex = -1;
codecReceivedBuffers = true;
return true;
}
int result; int result;
int adaptiveReconfigurationBytes = 0; int adaptiveReconfigurationBytes = 0;
if (waitingForKeys) { if (waitingForKeys) {
@ -664,6 +693,7 @@ public abstract class MediaCodecRenderer extends Renderer {
if (codec != null && canReconfigureCodec(codec, codecIsAdaptive, oldFormat, format)) { if (codec != null && canReconfigureCodec(codec, codecIsAdaptive, oldFormat, format)) {
codecReconfigured = true; codecReconfigured = true;
codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING; codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING;
codecNeedsAdaptationWorkaroundBuffer = codecNeedsAdaptationWorkaround;
} else { } else {
if (codecReceivedBuffers) { if (codecReceivedBuffers) {
// Signal end of stream and wait for any final output buffers before re-initialization. // Signal end of stream and wait for any final output buffers before re-initialization.
@ -684,7 +714,7 @@ public abstract class MediaCodecRenderer extends Renderer {
* @param codec The {@link MediaCodec} instance. * @param codec The {@link MediaCodec} instance.
* @param outputFormat The new output format. * @param outputFormat The new output format.
*/ */
protected void onOutputFormatChanged(MediaCodec codec, android.media.MediaFormat outputFormat) { protected void onOutputFormatChanged(MediaCodec codec, MediaFormat outputFormat) {
// Do nothing. // Do nothing.
} }
@ -776,6 +806,12 @@ public abstract class MediaCodecRenderer extends Renderer {
outputIndex = codec.dequeueOutputBuffer(outputBufferInfo, getDequeueOutputBufferTimeoutUs()); outputIndex = codec.dequeueOutputBuffer(outputBufferInfo, getDequeueOutputBufferTimeoutUs());
if (outputIndex >= 0) { if (outputIndex >= 0) {
// We've dequeued a buffer. // We've dequeued a buffer.
if (shouldSkipAdaptationWorkaroundOutputBuffer) {
shouldSkipAdaptationWorkaroundOutputBuffer = false;
codec.releaseOutputBuffer(outputIndex, false);
outputIndex = -1;
return true;
}
if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
// The dequeued buffer indicates the end of the stream. Process it immediately. // The dequeued buffer indicates the end of the stream. Process it immediately.
processEndOfStream(); processEndOfStream();
@ -820,9 +856,16 @@ public abstract class MediaCodecRenderer extends Renderer {
* Processes a new output format. * Processes a new output format.
*/ */
private void processOutputFormat() { private void processOutputFormat() {
android.media.MediaFormat format = codec.getOutputFormat(); MediaFormat format = codec.getOutputFormat();
if (codecNeedsAdaptationWorkaround
&& format.getInteger(MediaFormat.KEY_WIDTH) == ADAPTATION_WORKAROUND_SLICE_WIDTH_HEIGHT
&& format.getInteger(MediaFormat.KEY_HEIGHT) == ADAPTATION_WORKAROUND_SLICE_WIDTH_HEIGHT) {
// We assume this format changed event was caused by the adaptation workaround.
shouldSkipAdaptationWorkaroundOutputBuffer = true;
return;
}
if (codecNeedsMonoChannelCountWorkaround) { if (codecNeedsMonoChannelCountWorkaround) {
format.setInteger(android.media.MediaFormat.KEY_CHANNEL_COUNT, 1); format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
} }
onOutputFormatChanged(codec, format); onOutputFormatChanged(codec, format);
} }
@ -913,6 +956,22 @@ public abstract class MediaCodecRenderer extends Renderer {
&& ("OMX.Exynos.avc.dec".equals(name) || "OMX.Exynos.avc.dec.secure".equals(name))); && ("OMX.Exynos.avc.dec".equals(name) || "OMX.Exynos.avc.dec.secure".equals(name)));
} }
/**
* Returns whether the decoder is known to get stuck during some adaptations.
* <p>
* If true is returned, the renderer will work around the issue by queueing and discarding a blank
* frame at a different resolution, which resets the codec's internal state.
* <p>
* See [Internal: b/27807182].
*
* @param name The name of the decoder.
* @return True if the decoder is known to get stuck during some adaptations.
*/
private static boolean codecNeedsAdaptationWorkaround(String name) {
return Util.SDK_INT < 24 && Util.DEVICE.startsWith("flounder")
&& ("OMX.Nvidia.h264.decode".equals(name) || "OMX.Nvidia.h264.decode.secure".equals(name));
}
/** /**
* Returns whether the decoder is an H.264/AVC decoder known to fail if NAL units are queued * Returns whether the decoder is an H.264/AVC decoder known to fail if NAL units are queued
* before the codec specific data. * before the codec specific data.