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:
parent
464869cf57
commit
fdf26d6a1f
@ -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.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user