Add support for codec flush recovery

This commit is contained in:
Cameron Gutman
2022-11-04 01:20:00 -05:00
parent 2433ce8d24
commit a2b2131beb

View File

@@ -76,8 +76,9 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
private static final int CR_TIMEOUT_MS = 5000; private static final int CR_TIMEOUT_MS = 5000;
private static final int CR_MAX_TRIES = 10; private static final int CR_MAX_TRIES = 10;
private static final int CR_RECOVERY_TYPE_NONE = 0; private static final int CR_RECOVERY_TYPE_NONE = 0;
private static final int CR_RECOVERY_TYPE_RESTART = 1; private static final int CR_RECOVERY_TYPE_FLUSH = 1;
private static final int CR_RECOVERY_TYPE_RESET = 2; private static final int CR_RECOVERY_TYPE_RESTART = 2;
private static final int CR_RECOVERY_TYPE_RESET = 3;
private AtomicInteger codecRecoveryType = new AtomicInteger(CR_RECOVERY_TYPE_NONE); private AtomicInteger codecRecoveryType = new AtomicInteger(CR_RECOVERY_TYPE_NONE);
private final Object codecRecoveryMonitor = new Object(); private final Object codecRecoveryMonitor = new Object();
@@ -548,16 +549,34 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
codecRecoveryThreadQuiescedFlags |= quiescenceFlag; codecRecoveryThreadQuiescedFlags |= quiescenceFlag;
// This is the final thread to quiesce, so let's perform the codec recovery now.
if (codecRecoveryThreadQuiescedFlags == CR_FLAG_ALL) { if (codecRecoveryThreadQuiescedFlags == CR_FLAG_ALL) {
// This is the final thread to quiesce, so let's perform the codec recovery now.
codecRecoveryAttempts++;
LimeLog.info("Codec recovery attempt: "+codecRecoveryAttempts);
// Input and output buffers are invalidated by stop() and reset(). // Input and output buffers are invalidated by stop() and reset().
nextInputBuffer = null; nextInputBuffer = null;
nextInputBufferIndex = -1; nextInputBufferIndex = -1;
outputBufferQueue.clear(); outputBufferQueue.clear();
// If we just need a flush, do so now with all threads quiesced.
if (codecRecoveryType.get() == CR_RECOVERY_TYPE_FLUSH) {
LimeLog.warning("Flushing decoder");
try {
videoDecoder.flush();
codecRecoveryType.set(CR_RECOVERY_TYPE_NONE);
} catch (IllegalStateException e) {
e.printStackTrace();
// Something went wrong during the restart, let's use a bigger hammer
// and try a reset instead.
codecRecoveryType.set(CR_RECOVERY_TYPE_RESTART);
}
}
// We don't count flushes as codec recovery attempts
if (codecRecoveryType.get() != CR_RECOVERY_TYPE_NONE) {
codecRecoveryAttempts++;
LimeLog.info("Codec recovery attempt: "+codecRecoveryAttempts);
}
// For "recoverable" exceptions, we can just stop, reconfigure, and restart. // For "recoverable" exceptions, we can just stop, reconfigure, and restart.
if (codecRecoveryType.get() == CR_RECOVERY_TYPE_RESTART) { if (codecRecoveryType.get() == CR_RECOVERY_TYPE_RESTART) {
LimeLog.warning("Trying to restart decoder after CodecException"); LimeLog.warning("Trying to restart decoder after CodecException");
@@ -684,21 +703,34 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
if (codecRecoveryAttempts < CR_MAX_TRIES) { if (codecRecoveryAttempts < CR_MAX_TRIES) {
// If the exception is non-recoverable or we already require a reset, perform a reset. // If the exception is non-recoverable or we already require a reset, perform a reset.
// If we have no prior unrecoverable failure, we will try a restart instead. // If we have no prior unrecoverable failure, we will try a restart instead.
if (codecExc.isRecoverable() && codecRecoveryType.compareAndSet(CR_RECOVERY_TYPE_NONE, CR_RECOVERY_TYPE_RESTART)) { if (codecExc.isRecoverable()) {
LimeLog.info("Decoder requires restart for recoverable CodecException"); if (codecRecoveryType.compareAndSet(CR_RECOVERY_TYPE_NONE, CR_RECOVERY_TYPE_RESTART)) {
e.printStackTrace(); LimeLog.info("Decoder requires restart for recoverable CodecException");
e.printStackTrace();
}
else if (codecRecoveryType.compareAndSet(CR_RECOVERY_TYPE_FLUSH, CR_RECOVERY_TYPE_RESTART)) {
LimeLog.info("Decoder flush promoted to restart for recoverable CodecException");
e.printStackTrace();
}
else if (codecRecoveryType.get() != CR_RECOVERY_TYPE_RESET || codecRecoveryType.get() != CR_RECOVERY_TYPE_RESTART) {
throw new IllegalStateException("Unexpected codec recovery type: " + codecRecoveryType.get());
}
} }
else if (!codecExc.isRecoverable()) { else if (!codecExc.isRecoverable()) {
if (codecRecoveryType.compareAndSet(CR_RECOVERY_TYPE_NONE, CR_RECOVERY_TYPE_RESET)) { if (codecRecoveryType.compareAndSet(CR_RECOVERY_TYPE_NONE, CR_RECOVERY_TYPE_RESET)) {
LimeLog.info("Decoder requires reset for non-recoverable CodecException"); LimeLog.info("Decoder requires reset for non-recoverable CodecException");
e.printStackTrace(); e.printStackTrace();
} }
else if (codecRecoveryType.compareAndSet(CR_RECOVERY_TYPE_FLUSH, CR_RECOVERY_TYPE_RESET)) {
LimeLog.info("Decoder flush promoted to reset for non-recoverable CodecException");
e.printStackTrace();
}
else if (codecRecoveryType.compareAndSet(CR_RECOVERY_TYPE_RESTART, CR_RECOVERY_TYPE_RESET)) { else if (codecRecoveryType.compareAndSet(CR_RECOVERY_TYPE_RESTART, CR_RECOVERY_TYPE_RESET)) {
LimeLog.info("Decoder restart promoted to reset for non-recoverable CodecException"); LimeLog.info("Decoder restart promoted to reset for non-recoverable CodecException");
e.printStackTrace(); e.printStackTrace();
} }
else if (codecRecoveryType.get() != CR_RECOVERY_TYPE_RESET) { else if (codecRecoveryType.get() != CR_RECOVERY_TYPE_RESET) {
throw new IllegalStateException("Unexpected codec recovery type" + codecRecoveryType.get()); throw new IllegalStateException("Unexpected codec recovery type: " + codecRecoveryType.get());
} }
} }
@@ -716,6 +748,10 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
LimeLog.info("Decoder requires reset for IllegalStateException"); LimeLog.info("Decoder requires reset for IllegalStateException");
e.printStackTrace(); e.printStackTrace();
} }
else if (codecRecoveryType.compareAndSet(CR_RECOVERY_TYPE_FLUSH, CR_RECOVERY_TYPE_RESET)) {
LimeLog.info("Decoder flush promoted to reset for IllegalStateException");
e.printStackTrace();
}
else if (codecRecoveryType.compareAndSet(CR_RECOVERY_TYPE_RESTART, CR_RECOVERY_TYPE_RESET)) { else if (codecRecoveryType.compareAndSet(CR_RECOVERY_TYPE_RESTART, CR_RECOVERY_TYPE_RESET)) {
LimeLog.info("Decoder restart promoted to reset for IllegalStateException"); LimeLog.info("Decoder restart promoted to reset for IllegalStateException");
e.printStackTrace(); e.printStackTrace();