Prefetch input buffers while waiting for the next frame to arrive

This commit is contained in:
Cameron Gutman
2022-09-06 00:59:45 -05:00
parent 7734de6465
commit 49051a5095

View File

@@ -48,6 +48,9 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
private boolean submittedCsd; private boolean submittedCsd;
private boolean submitCsdNextCall; private boolean submitCsdNextCall;
private int nextInputBufferIndex = -1;
private ByteBuffer nextInputBuffer;
private Context context; private Context context;
private MediaCodec videoDecoder; private MediaCodec videoDecoder;
private Thread rendererThread; private Thread rendererThread;
@@ -374,6 +377,8 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
legacyInputBuffers = videoDecoder.getInputBuffers(); legacyInputBuffers = videoDecoder.getInputBuffers();
} }
fetchNextInputBuffer();
return true; return true;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -682,19 +687,37 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
rendererThread.start(); rendererThread.start();
} }
private int dequeueInputBuffer() { private boolean fetchNextInputBuffer() {
int index = -1;
long startTime; long startTime;
if (nextInputBufferIndex >= 0) {
// We already have an input buffer
return true;
}
startTime = SystemClock.uptimeMillis(); startTime = SystemClock.uptimeMillis();
try { try {
while (index < 0 && !stopping) { while (nextInputBufferIndex < 0 && !stopping) {
index = videoDecoder.dequeueInputBuffer(10000); nextInputBufferIndex = videoDecoder.dequeueInputBuffer(10000);
}
if (nextInputBufferIndex >= 0) {
// Using the new getInputBuffer() API on Lollipop allows
// the framework to do some performance optimizations for us
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
nextInputBuffer = videoDecoder.getInputBuffer(nextInputBufferIndex);
}
else {
nextInputBuffer = legacyInputBuffers[nextInputBufferIndex];
// Clear old input data pre-Lollipop
nextInputBuffer.clear();
}
} }
} catch (Exception e) { } catch (Exception e) {
handleDecoderException(e, null, 0, true); handleDecoderException(e, null, 0, true);
return MediaCodec.INFO_TRY_AGAIN_LATER; return false;
} }
int deltaMs = (int)(SystemClock.uptimeMillis() - startTime); int deltaMs = (int)(SystemClock.uptimeMillis() - startTime);
@@ -703,7 +726,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
LimeLog.warning("Dequeue input buffer ran long: " + deltaMs + " ms"); LimeLog.warning("Dequeue input buffer ran long: " + deltaMs + " ms");
} }
if (index < 0) { if (nextInputBufferIndex < 0) {
// We've been hung for 5 seconds and no other exception was reported, // We've been hung for 5 seconds and no other exception was reported,
// so generate a decoder hung exception // so generate a decoder hung exception
if (deltaMs >= 5000 && initialException == null) { if (deltaMs >= 5000 && initialException == null) {
@@ -714,10 +737,11 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
} }
throw new RendererException(this, decoderHungException); throw new RendererException(this, decoderHungException);
} }
return index;
return false;
} }
return index; return true;
} }
@Override @Override
@@ -793,39 +817,24 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
// TODO: Set HDR metadata? // TODO: Set HDR metadata?
} }
private boolean queueInputBuffer(int inputBufferIndex, int offset, int length, long timestampUs, int codecFlags) { private boolean queueNextInputBuffer(long timestampUs, int codecFlags) {
try { try {
videoDecoder.queueInputBuffer(inputBufferIndex, videoDecoder.queueInputBuffer(nextInputBufferIndex,
offset, length, 0, nextInputBuffer.position(),
timestampUs, codecFlags); timestampUs, codecFlags);
return true;
} catch (Exception e) { } catch (Exception e) {
handleDecoderException(e, null, codecFlags, true); handleDecoderException(e, null, codecFlags, true);
return false; return false;
} } finally {
} nextInputBufferIndex = -1;
nextInputBuffer = null;
// Using the new getInputBuffer() API on Lollipop allows
// the framework to do some performance optimizations for us
private ByteBuffer getEmptyInputBuffer(int inputBufferIndex) {
ByteBuffer buf;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
try {
buf = videoDecoder.getInputBuffer(inputBufferIndex);
} catch (Exception e) {
handleDecoderException(e, null, 0, true);
return null;
}
}
else {
buf = legacyInputBuffers[inputBufferIndex];
// Clear old input data pre-Lollipop
buf.clear();
} }
return buf; // Fetch a new input buffer now while we have some time between frames
// to have it ready immediately when the next frame arrives.
fetchNextInputBuffer();
return true;
} }
private void doProfileSpecificSpsPatching(SeqParameterSet sps) { private void doProfileSpecificSpsPatching(SeqParameterSet sps) {
@@ -904,8 +913,6 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
activeWindowVideoStats.measurementStartTimestamp = SystemClock.uptimeMillis(); activeWindowVideoStats.measurementStartTimestamp = SystemClock.uptimeMillis();
} }
int inputBufferIndex;
ByteBuffer buf;
long timestampUs; long timestampUs;
int codecFlags = 0; int codecFlags = 0;
@@ -1050,25 +1057,17 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
// fused IDR frames, we will submit the CSD blob in a // fused IDR frames, we will submit the CSD blob in a
// separate input buffer. // separate input buffer.
if (!submittedCsd || !fusedIdrFrame) { if (!submittedCsd || !fusedIdrFrame) {
inputBufferIndex = dequeueInputBuffer(); if (!fetchNextInputBuffer()) {
if (inputBufferIndex < 0) {
// We're being torn down now
return MoonBridge.DR_NEED_IDR;
}
buf = getEmptyInputBuffer(inputBufferIndex);
if (buf == null) {
// We're being torn down now
return MoonBridge.DR_NEED_IDR; return MoonBridge.DR_NEED_IDR;
} }
// When we get the PPS, submit the VPS and SPS together with // When we get the PPS, submit the VPS and SPS together with
// the PPS, as required by AOSP docs on use of MediaCodec. // the PPS, as required by AOSP docs on use of MediaCodec.
if (vpsBuffer != null) { if (vpsBuffer != null) {
buf.put(vpsBuffer); nextInputBuffer.put(vpsBuffer);
} }
if (spsBuffer != null) { if (spsBuffer != null) {
buf.put(spsBuffer); nextInputBuffer.put(spsBuffer);
} }
// This is the CSD blob // This is the CSD blob
@@ -1097,27 +1096,19 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
activeWindowVideoStats.totalTimeMs += enqueueTimeMs - receiveTimeMs; activeWindowVideoStats.totalTimeMs += enqueueTimeMs - receiveTimeMs;
} }
inputBufferIndex = dequeueInputBuffer(); if (!fetchNextInputBuffer()) {
if (inputBufferIndex < 0) {
// We're being torn down now
return MoonBridge.DR_NEED_IDR;
}
buf = getEmptyInputBuffer(inputBufferIndex);
if (buf == null) {
// We're being torn down now
return MoonBridge.DR_NEED_IDR; return MoonBridge.DR_NEED_IDR;
} }
if (submitCsdNextCall) { if (submitCsdNextCall) {
if (vpsBuffer != null) { if (vpsBuffer != null) {
buf.put(vpsBuffer); nextInputBuffer.put(vpsBuffer);
} }
if (spsBuffer != null) { if (spsBuffer != null) {
buf.put(spsBuffer); nextInputBuffer.put(spsBuffer);
} }
if (ppsBuffer != null) { if (ppsBuffer != null) {
buf.put(ppsBuffer); nextInputBuffer.put(ppsBuffer);
} }
submitCsdNextCall = false; submitCsdNextCall = false;
@@ -1140,9 +1131,9 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
numFramesIn++; numFramesIn++;
} }
if (decodeUnitLength > buf.limit() - buf.position()) { if (decodeUnitLength > nextInputBuffer.limit() - nextInputBuffer.position()) {
IllegalArgumentException exception = new IllegalArgumentException( IllegalArgumentException exception = new IllegalArgumentException(
"Decode unit length "+decodeUnitLength+" too large for input buffer "+buf.limit()); "Decode unit length "+decodeUnitLength+" too large for input buffer "+nextInputBuffer.limit());
if (!reportedCrash) { if (!reportedCrash) {
reportedCrash = true; reportedCrash = true;
crashListener.notifyCrash(exception); crashListener.notifyCrash(exception);
@@ -1151,11 +1142,9 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
} }
// Copy data from our buffer list into the input buffer // Copy data from our buffer list into the input buffer
buf.put(decodeUnitData, 0, decodeUnitLength); nextInputBuffer.put(decodeUnitData, 0, decodeUnitLength);
if (!queueInputBuffer(inputBufferIndex, if (!queueNextInputBuffer(timestampUs, codecFlags)) {
0, buf.position(),
timestampUs, codecFlags)) {
return MoonBridge.DR_NEED_IDR; return MoonBridge.DR_NEED_IDR;
} }
@@ -1177,18 +1166,12 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
} }
private boolean replaySps() { private boolean replaySps() {
int inputIndex = dequeueInputBuffer(); if (!fetchNextInputBuffer()) {
if (inputIndex < 0) {
return false;
}
ByteBuffer inputBuffer = getEmptyInputBuffer(inputIndex);
if (inputBuffer == null) {
return false; return false;
} }
// Write the Annex B header // Write the Annex B header
inputBuffer.put(new byte[]{0x00, 0x00, 0x00, 0x01, 0x67}); nextInputBuffer.put(new byte[]{0x00, 0x00, 0x00, 0x01, 0x67});
// Switch the H264 profile back to high // Switch the H264 profile back to high
savedSps.profileIdc = 100; savedSps.profileIdc = 100;
@@ -1199,15 +1182,13 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
// The H264Utils.writeSPS function safely handles // The H264Utils.writeSPS function safely handles
// Annex B NALUs (including NALUs with escape sequences) // Annex B NALUs (including NALUs with escape sequences)
ByteBuffer escapedNalu = H264Utils.writeSPS(savedSps, 128); ByteBuffer escapedNalu = H264Utils.writeSPS(savedSps, 128);
inputBuffer.put(escapedNalu); nextInputBuffer.put(escapedNalu);
// No need for the SPS anymore // No need for the SPS anymore
savedSps = null; savedSps = null;
// Queue the new SPS // Queue the new SPS
return queueInputBuffer(inputIndex, return queueNextInputBuffer(0, MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
0, inputBuffer.position(),
0, MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
} }
@Override @Override