mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2026-04-19 06:50:10 +00:00
Prefetch input buffers while waiting for the next frame to arrive
This commit is contained in:
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user