MediaCodec.Callback 处理来自 MediaExtractor 的 IllegalStateException
MediaCodec.Callback handle IllegalStateException from MediaExtractor
我正在异步模式下使用 MediaCodec
根据 mstorsjo 的示例对视频进行转码。对于某些视频文件,当我调用 MediaExtractor.advance()
或 MediaExtractor.getSampleTime()
时,我会抛出 IllegalStateException
。例如。在我的音频解码器中:
MediaCodec decoder = MediaCodec.createDecoderByType(type);
decoder.setCallback(new MediaCodec.Callback() {
public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) {
ByteBuffer decoderInputBuffer = codec.getInputBuffer(index);
while (!mExtractorDone) {
int size = mExtractor.readSampleData(decoderInputBuffer, 0);
long presentationTimeUs = mExtractor.getSampleTime();
boolean queuedInputBuffer = false;
if (size >= 0) {
codec.queueInputBuffer(
index,
0,
size,
presentationTimeUs,
mExtractor.getSampleFlags());
queuedInputBuffer = true;
}
mExtractorDone = !mExtractor.advance();
if (mExtractorDone) {
queueEOS();
}
if (queuedInputBuffer) {
break;
}
}
}
...
});
decoder.configure(inputFormat, null, null, 0);
decoder.start();
在我的视频解码器中也是如此,它是 运行 在一个单独的 HandlerThread 中。
有没有办法捕获 MediaCodec.Callback
中抛出的所有异常并将它们传递回主 awaitEncode 函数,这样我就可以关闭所有内容并顺利退出?我应该在每个回调周围放置 try catch
s 然后 notify
主处理线程吗?
如果能找出导致原始问题的原因 IllegalStateException
会很棒,但如果知道我的视频转码器的所有问题都已被捕获并向用户解释,我也会感到更自在。
到目前为止,我最好的解决方案是将所有内容都包装在一个 try catch
块中。我使用扩展 MediaCodec.Callback
:
的实用程序 class
public abstract class SafeMediaCodecCallback extends MediaCodec.Callback {
private final OnExceptionListener exceptionListener;
public SafeMediaCodecCallback(OnExceptionListener exceptionListener) {
this.exceptionListener = exceptionListener;
}
@Override
public final void onInputBufferAvailable(@NonNull MediaCodec mediaCodec, int i) {
try {
onInputBufferAvailableSafe(mediaCodec, i);
} catch (Exception exception) {
handleException(exception);
}
}
@Override
public final void onOutputBufferAvailable(@NonNull MediaCodec mediaCodec, int i, @NonNull MediaCodec.BufferInfo bufferInfo) {
try {
onOutputBufferAvailableSafe(mediaCodec, i, bufferInfo);
} catch (Exception exception) {
handleException(exception);
}
}
@Override
public final void onError(@NonNull MediaCodec mediaCodec, @NonNull MediaCodec.CodecException e) {
handleException(e);
}
@Override
public final void onOutputFormatChanged(@NonNull MediaCodec mediaCodec, @NonNull MediaFormat mediaFormat) {
try {
onOutputFormatChangedSafe(mediaCodec, mediaFormat);
} catch (Exception exception) {
handleException(exception);
}
}
private void handleException(Exception e) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (e instanceof CodecException) {
CodecException codecExc = (CodecException) e;
if (codecExc.isTransient()) {
// We'll let transient exceptions go
return;
}
}
}
exceptionListener.onException(e);
}
public abstract void onInputBufferAvailableSafe(@NonNull MediaCodec mediaCodec, int i);
public abstract void onOutputBufferAvailableSafe(@NonNull MediaCodec mediaCodec, int i, @NonNull MediaCodec.BufferInfo bufferInfo);
public abstract void onOutputFormatChangedSafe(@NonNull MediaCodec mediaCodec, @NonNull MediaFormat mediaFormat);
}
然后我可以将异常冒泡回顶部线程,如果我们无法恢复,则杀死所有线程,整理并抛出原来的 Exception
。
我正在异步模式下使用 MediaCodec
根据 mstorsjo 的示例对视频进行转码。对于某些视频文件,当我调用 MediaExtractor.advance()
或 MediaExtractor.getSampleTime()
时,我会抛出 IllegalStateException
。例如。在我的音频解码器中:
MediaCodec decoder = MediaCodec.createDecoderByType(type);
decoder.setCallback(new MediaCodec.Callback() {
public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) {
ByteBuffer decoderInputBuffer = codec.getInputBuffer(index);
while (!mExtractorDone) {
int size = mExtractor.readSampleData(decoderInputBuffer, 0);
long presentationTimeUs = mExtractor.getSampleTime();
boolean queuedInputBuffer = false;
if (size >= 0) {
codec.queueInputBuffer(
index,
0,
size,
presentationTimeUs,
mExtractor.getSampleFlags());
queuedInputBuffer = true;
}
mExtractorDone = !mExtractor.advance();
if (mExtractorDone) {
queueEOS();
}
if (queuedInputBuffer) {
break;
}
}
}
...
});
decoder.configure(inputFormat, null, null, 0);
decoder.start();
在我的视频解码器中也是如此,它是 运行 在一个单独的 HandlerThread 中。
有没有办法捕获 MediaCodec.Callback
中抛出的所有异常并将它们传递回主 awaitEncode 函数,这样我就可以关闭所有内容并顺利退出?我应该在每个回调周围放置 try catch
s 然后 notify
主处理线程吗?
如果能找出导致原始问题的原因 IllegalStateException
会很棒,但如果知道我的视频转码器的所有问题都已被捕获并向用户解释,我也会感到更自在。
到目前为止,我最好的解决方案是将所有内容都包装在一个 try catch
块中。我使用扩展 MediaCodec.Callback
:
public abstract class SafeMediaCodecCallback extends MediaCodec.Callback {
private final OnExceptionListener exceptionListener;
public SafeMediaCodecCallback(OnExceptionListener exceptionListener) {
this.exceptionListener = exceptionListener;
}
@Override
public final void onInputBufferAvailable(@NonNull MediaCodec mediaCodec, int i) {
try {
onInputBufferAvailableSafe(mediaCodec, i);
} catch (Exception exception) {
handleException(exception);
}
}
@Override
public final void onOutputBufferAvailable(@NonNull MediaCodec mediaCodec, int i, @NonNull MediaCodec.BufferInfo bufferInfo) {
try {
onOutputBufferAvailableSafe(mediaCodec, i, bufferInfo);
} catch (Exception exception) {
handleException(exception);
}
}
@Override
public final void onError(@NonNull MediaCodec mediaCodec, @NonNull MediaCodec.CodecException e) {
handleException(e);
}
@Override
public final void onOutputFormatChanged(@NonNull MediaCodec mediaCodec, @NonNull MediaFormat mediaFormat) {
try {
onOutputFormatChangedSafe(mediaCodec, mediaFormat);
} catch (Exception exception) {
handleException(exception);
}
}
private void handleException(Exception e) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (e instanceof CodecException) {
CodecException codecExc = (CodecException) e;
if (codecExc.isTransient()) {
// We'll let transient exceptions go
return;
}
}
}
exceptionListener.onException(e);
}
public abstract void onInputBufferAvailableSafe(@NonNull MediaCodec mediaCodec, int i);
public abstract void onOutputBufferAvailableSafe(@NonNull MediaCodec mediaCodec, int i, @NonNull MediaCodec.BufferInfo bufferInfo);
public abstract void onOutputFormatChangedSafe(@NonNull MediaCodec mediaCodec, @NonNull MediaFormat mediaFormat);
}
然后我可以将异常冒泡回顶部线程,如果我们无法恢复,则杀死所有线程,整理并抛出原来的 Exception
。