Improve handling of transient CodecExceptions

This commit is contained in:
Cameron Gutman
2022-09-15 00:08:06 -05:00
parent 245a9f2751
commit ef7ac62f97

View File

@@ -478,15 +478,16 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
return 0; return 0;
} }
private void handleDecoderException(Exception e, ByteBuffer buf, int codecFlags, boolean throwOnTransient) { // Returns true if the exception is transient
private boolean handleDecoderException(Exception e, ByteBuffer buf, int codecFlags) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (e instanceof CodecException) { if (e instanceof CodecException) {
CodecException codecExc = (CodecException) e; CodecException codecExc = (CodecException) e;
if (codecExc.isTransient() && !throwOnTransient) { if (codecExc.isTransient()) {
// We'll let transient exceptions go // We'll let transient exceptions go
LimeLog.warning(codecExc.getDiagnosticInfo()); LimeLog.warning(codecExc.getDiagnosticInfo());
return; return true;
} }
LimeLog.severe(codecExc.getDiagnosticInfo()); LimeLog.severe(codecExc.getDiagnosticInfo());
@@ -524,6 +525,9 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
initialExceptionTimestamp = SystemClock.uptimeMillis(); initialExceptionTimestamp = SystemClock.uptimeMillis();
} }
} }
// Not transient
return false;
} }
@Override @Override
@@ -555,9 +559,14 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
lastRenderedFrameTimeNanos = frameTimeNanos; lastRenderedFrameTimeNanos = frameTimeNanos;
activeWindowVideoStats.totalFramesRendered++; activeWindowVideoStats.totalFramesRendered++;
} catch (Exception e) { } catch (Exception ignored) {
// This will leak nextOutputBuffer, but there's really nothing else we can do try {
handleDecoderException(e, null, 0, false); // Try to avoid leaking the output buffer by releasing it without rendering
videoDecoder.releaseOutputBuffer(nextOutputBuffer, false);
} catch (Exception e) {
// This will leak nextOutputBuffer, but there's really nothing else we can do
handleDecoderException(e, null, 0);
}
} }
} }
} }
@@ -677,7 +686,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
} }
} }
} catch (Exception e) { } catch (Exception e) {
handleDecoderException(e, null, 0, false); handleDecoderException(e, null, 0);
} }
} }
} }
@@ -724,7 +733,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
} }
} }
} catch (Exception e) { } catch (Exception e) {
handleDecoderException(e, null, 0, true); handleDecoderException(e, null, 0);
return false; return false;
} }
@@ -830,12 +839,24 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
videoDecoder.queueInputBuffer(nextInputBufferIndex, videoDecoder.queueInputBuffer(nextInputBufferIndex,
0, nextInputBuffer.position(), 0, nextInputBuffer.position(),
timestampUs, codecFlags); timestampUs, codecFlags);
} catch (Exception e) {
handleDecoderException(e, null, codecFlags, true); // We need a new buffer now
return false;
} finally {
nextInputBufferIndex = -1; nextInputBufferIndex = -1;
nextInputBuffer = null; nextInputBuffer = null;
} catch (Exception e) {
if (handleDecoderException(e, null, codecFlags)) {
// We encountered a transient error. In this case, just hold onto the buffer
// (to avoid leaking it), clear it, and keep it for the next frame. We'll return
// false to trigger an IDR frame to recover.
nextInputBuffer.clear();
}
else {
// We encountered a non-transient error. In this case, we will simply leak the
// buffer because we cannot be sure we will ever succeed in queuing it.
nextInputBufferIndex = -1;
nextInputBuffer = null;
}
return false;
} }
// Fetch a new input buffer now while we have some time between frames // Fetch a new input buffer now while we have some time between frames