mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-20 19:42:45 +00:00
Throw a special RendererException when we have a MediaCodec crash so we have much more info for debugging
This commit is contained in:
parent
ae298fbc51
commit
b5e585834d
@ -33,6 +33,11 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
private long decoderTimeMs;
|
private long decoderTimeMs;
|
||||||
private int totalFrames;
|
private int totalFrames;
|
||||||
|
|
||||||
|
private String decoderName;
|
||||||
|
private int numSpsIn;
|
||||||
|
private int numPpsIn;
|
||||||
|
private int numIframeIn;
|
||||||
|
|
||||||
private final static byte[] BITSTREAM_RESTRICTIONS = new byte[] {(byte) 0xF1, (byte) 0x83, 0x2A, 0x00};
|
private final static byte[] BITSTREAM_RESTRICTIONS = new byte[] {(byte) 0xF1, (byte) 0x83, 0x2A, 0x00};
|
||||||
|
|
||||||
public static final List<String> blacklistedDecoderPrefixes;
|
public static final List<String> blacklistedDecoderPrefixes;
|
||||||
@ -186,10 +191,12 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
decoderName = decoder.getName();
|
||||||
|
|
||||||
// Codecs have been known to throw all sorts of crazy runtime exceptions
|
// Codecs have been known to throw all sorts of crazy runtime exceptions
|
||||||
// due to implementation problems
|
// due to implementation problems
|
||||||
try {
|
try {
|
||||||
videoDecoder = MediaCodec.createByCodecName(decoder.getName());
|
videoDecoder = MediaCodec.createByCodecName(decoderName);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -233,43 +240,48 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int outIndex = videoDecoder.dequeueOutputBuffer(info, 0);
|
try {
|
||||||
if (outIndex >= 0) {
|
int outIndex = videoDecoder.dequeueOutputBuffer(info, 0);
|
||||||
long presentationTimeUs = info.presentationTimeUs;
|
|
||||||
int lastIndex = outIndex;
|
|
||||||
|
|
||||||
// Get the last output buffer in the queue
|
if (outIndex >= 0) {
|
||||||
while ((outIndex = videoDecoder.dequeueOutputBuffer(info, 0)) >= 0) {
|
long presentationTimeUs = info.presentationTimeUs;
|
||||||
videoDecoder.releaseOutputBuffer(lastIndex, false);
|
int lastIndex = outIndex;
|
||||||
lastIndex = outIndex;
|
|
||||||
presentationTimeUs = info.presentationTimeUs;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render the last buffer
|
// Get the last output buffer in the queue
|
||||||
videoDecoder.releaseOutputBuffer(lastIndex, true);
|
while ((outIndex = videoDecoder.dequeueOutputBuffer(info, 0)) >= 0) {
|
||||||
|
videoDecoder.releaseOutputBuffer(lastIndex, false);
|
||||||
|
lastIndex = outIndex;
|
||||||
|
presentationTimeUs = info.presentationTimeUs;
|
||||||
|
}
|
||||||
|
|
||||||
// Add delta time to the totals (excluding probable outliers)
|
// Render the last buffer
|
||||||
long delta = System.currentTimeMillis()-(presentationTimeUs/1000);
|
videoDecoder.releaseOutputBuffer(lastIndex, true);
|
||||||
if (delta > 5 && delta < 300) {
|
|
||||||
decoderTimeMs += delta;
|
// Add delta time to the totals (excluding probable outliers)
|
||||||
totalTimeMs += delta;
|
long delta = System.currentTimeMillis()-(presentationTimeUs/1000);
|
||||||
|
if (delta > 5 && delta < 300) {
|
||||||
|
decoderTimeMs += delta;
|
||||||
|
totalTimeMs += delta;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (outIndex) {
|
||||||
|
case MediaCodec.INFO_TRY_AGAIN_LATER:
|
||||||
|
LockSupport.parkNanos(1);
|
||||||
|
break;
|
||||||
|
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
|
||||||
|
LimeLog.info("Output buffers changed");
|
||||||
|
break;
|
||||||
|
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
|
||||||
|
LimeLog.info("Output format changed");
|
||||||
|
LimeLog.info("New output Format: " + videoDecoder.getOutputFormat());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} catch (Exception e) {
|
||||||
switch (outIndex) {
|
throw new RendererException(MediaCodecDecoderRenderer.this, e);
|
||||||
case MediaCodec.INFO_TRY_AGAIN_LATER:
|
}
|
||||||
LockSupport.parkNanos(1);
|
|
||||||
break;
|
|
||||||
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
|
|
||||||
LimeLog.info("Output buffers changed");
|
|
||||||
break;
|
|
||||||
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
|
|
||||||
LimeLog.info("Output format changed");
|
|
||||||
LimeLog.info("New output Format: " + videoDecoder.getOutputFormat());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -318,7 +330,11 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inputIndex = videoDecoder.dequeueInputBuffer(100000);
|
try {
|
||||||
|
inputIndex = videoDecoder.dequeueInputBuffer(100000);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RendererException(this, e);
|
||||||
|
}
|
||||||
} while (inputIndex < 0);
|
} while (inputIndex < 0);
|
||||||
|
|
||||||
ByteBuffer buf = videoDecoderInputBuffers[inputIndex];
|
ByteBuffer buf = videoDecoderInputBuffers[inputIndex];
|
||||||
@ -340,70 +356,79 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
}
|
}
|
||||||
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_SYNC_FRAME) != 0) {
|
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_SYNC_FRAME) != 0) {
|
||||||
codecFlags |= MediaCodec.BUFFER_FLAG_SYNC_FRAME;
|
codecFlags |= MediaCodec.BUFFER_FLAG_SYNC_FRAME;
|
||||||
|
numIframeIn++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_CODEC_CONFIG) != 0 &&
|
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_CODEC_CONFIG) != 0) {
|
||||||
(needsSpsBitstreamFixup || needsSpsNumRefFixup)) {
|
|
||||||
ByteBufferDescriptor header = decodeUnit.getBufferList().get(0);
|
ByteBufferDescriptor header = decodeUnit.getBufferList().get(0);
|
||||||
if (header.data[header.offset+4] == 0x67) {
|
if (header.data[header.offset+4] == 0x67) {
|
||||||
byte last = header.data[header.length+header.offset-1];
|
numSpsIn++;
|
||||||
|
|
||||||
// TI OMAP4 requires a reference frame count of 1 to decode successfully
|
if ((needsSpsBitstreamFixup || needsSpsNumRefFixup)) {
|
||||||
if (needsSpsNumRefFixup) {
|
byte last = header.data[header.length+header.offset-1];
|
||||||
LimeLog.info("Fixing up num ref frames");
|
|
||||||
this.replace(header, 80, 9, new byte[] {0x40}, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The SPS that comes in the current H264 bytestream doesn't set bitstream_restriction_flag
|
// TI OMAP4 requires a reference frame count of 1 to decode successfully
|
||||||
// or max_dec_frame_buffering which increases decoding latency on Tegra.
|
if (needsSpsNumRefFixup) {
|
||||||
// We manually modify the SPS here to speed-up decoding if the decoder was flagged as needing it.
|
LimeLog.info("Fixing up num ref frames");
|
||||||
int spsLength;
|
this.replace(header, 80, 9, new byte[] {0x40}, 3);
|
||||||
if (needsSpsBitstreamFixup) {
|
}
|
||||||
if (!needsSpsNumRefFixup) {
|
|
||||||
switch (header.length) {
|
// The SPS that comes in the current H264 bytestream doesn't set bitstream_restriction_flag
|
||||||
case 26:
|
// or max_dec_frame_buffering which increases decoding latency on Tegra.
|
||||||
LimeLog.info("Adding bitstream restrictions to SPS (26)");
|
// We manually modify the SPS here to speed-up decoding if the decoder was flagged as needing it.
|
||||||
buf.put(header.data, header.offset, 24);
|
int spsLength;
|
||||||
buf.put((byte) 0x11);
|
if (needsSpsBitstreamFixup) {
|
||||||
buf.put((byte) 0xe3);
|
if (!needsSpsNumRefFixup) {
|
||||||
buf.put((byte) 0x06);
|
switch (header.length) {
|
||||||
buf.put((byte) 0x50);
|
case 26:
|
||||||
spsLength = header.length + 2;
|
LimeLog.info("Adding bitstream restrictions to SPS (26)");
|
||||||
break;
|
buf.put(header.data, header.offset, 24);
|
||||||
case 27:
|
buf.put((byte) 0x11);
|
||||||
LimeLog.info("Adding bitstream restrictions to SPS (27)");
|
buf.put((byte) 0xe3);
|
||||||
buf.put(header.data, header.offset, 25);
|
buf.put((byte) 0x06);
|
||||||
buf.put((byte) 0x04);
|
buf.put((byte) 0x50);
|
||||||
buf.put((byte) 0x78);
|
spsLength = header.length + 2;
|
||||||
buf.put((byte) 0xc1);
|
break;
|
||||||
buf.put((byte) 0x94);
|
case 27:
|
||||||
spsLength = header.length + 2;
|
LimeLog.info("Adding bitstream restrictions to SPS (27)");
|
||||||
break;
|
buf.put(header.data, header.offset, 25);
|
||||||
default:
|
buf.put((byte) 0x04);
|
||||||
LimeLog.warning("Unknown SPS of length "+header.length);
|
buf.put((byte) 0x78);
|
||||||
|
buf.put((byte) 0xc1);
|
||||||
|
buf.put((byte) 0x94);
|
||||||
|
spsLength = header.length + 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LimeLog.warning("Unknown SPS of length "+header.length);
|
||||||
|
buf.put(header.data, header.offset, header.length);
|
||||||
|
spsLength = header.length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Set bitstream restrictions to only buffer single frame
|
||||||
|
// (starts 9 bits before stop bit and 6 bits earlier because of the shortening above)
|
||||||
|
this.replace(header, header.length*8+Integer.numberOfLeadingZeros(last & - last)%8-9-6, 2, BITSTREAM_RESTRICTIONS, 3*8);
|
||||||
buf.put(header.data, header.offset, header.length);
|
buf.put(header.data, header.offset, header.length);
|
||||||
spsLength = header.length;
|
spsLength = header.length;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Set bitstream restrictions to only buffer single frame
|
|
||||||
// (starts 9 bits before stop bit and 6 bits earlier because of the shortening above)
|
|
||||||
this.replace(header, header.length*8+Integer.numberOfLeadingZeros(last & - last)%8-9-6, 2, BITSTREAM_RESTRICTIONS, 3*8);
|
|
||||||
buf.put(header.data, header.offset, header.length);
|
buf.put(header.data, header.offset, header.length);
|
||||||
spsLength = header.length;
|
spsLength = header.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
videoDecoder.queueInputBuffer(inputIndex,
|
||||||
|
0, spsLength,
|
||||||
|
currentTime * 1000, codecFlags);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RendererException(this, e, buf, codecFlags);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
} else if (header.data[header.offset+4] == 0x68) {
|
||||||
buf.put(header.data, header.offset, header.length);
|
numPpsIn++;
|
||||||
spsLength = header.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
videoDecoder.queueInputBuffer(inputIndex,
|
|
||||||
0, spsLength,
|
|
||||||
currentTime * 1000, codecFlags);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,9 +438,13 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
buf.put(desc.data, desc.offset, desc.length);
|
buf.put(desc.data, desc.offset, desc.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
videoDecoder.queueInputBuffer(inputIndex,
|
try {
|
||||||
0, decodeUnit.getDataLength(),
|
videoDecoder.queueInputBuffer(inputIndex,
|
||||||
currentTime * 1000, codecFlags);
|
0, decodeUnit.getDataLength(),
|
||||||
|
currentTime * 1000, codecFlags);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RendererException(this, e, buf, codecFlags);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -519,4 +548,47 @@ public class MediaCodecDecoderRenderer implements VideoDecoderRenderer {
|
|||||||
}
|
}
|
||||||
return (int)(totalTimeMs / totalFrames);
|
return (int)(totalTimeMs / totalFrames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class RendererException extends RuntimeException {
|
||||||
|
private static final long serialVersionUID = 8985937536997012406L;
|
||||||
|
|
||||||
|
private Exception originalException;
|
||||||
|
private MediaCodecDecoderRenderer renderer;
|
||||||
|
private ByteBuffer currentBuffer;
|
||||||
|
private int currentCodecFlags;
|
||||||
|
|
||||||
|
public RendererException(MediaCodecDecoderRenderer renderer, Exception e) {
|
||||||
|
this.renderer = renderer;
|
||||||
|
this.originalException = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RendererException(MediaCodecDecoderRenderer renderer, Exception e, ByteBuffer currentBuffer, int currentCodecFlags) {
|
||||||
|
this.renderer = renderer;
|
||||||
|
this.originalException = e;
|
||||||
|
this.currentBuffer = currentBuffer;
|
||||||
|
this.currentCodecFlags = currentCodecFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
String str = "";
|
||||||
|
|
||||||
|
str += "Decoder: "+renderer.decoderName+"\n";
|
||||||
|
str += "In stats: "+renderer.numSpsIn+", "+renderer.numPpsIn+", "+renderer.numIframeIn+"\n";
|
||||||
|
str += "Total frames: "+renderer.totalFrames+"\n";
|
||||||
|
|
||||||
|
if (currentBuffer != null) {
|
||||||
|
str += "Current buffer: ";
|
||||||
|
currentBuffer.flip();
|
||||||
|
while (currentBuffer.hasRemaining() && currentBuffer.position() < 10) {
|
||||||
|
str += String.format("%02x ", currentBuffer.get());
|
||||||
|
}
|
||||||
|
str += "\n";
|
||||||
|
str += "Buffer codec flags: "+currentCodecFlags+"\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
str += originalException.toString();
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user