You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MediaCodec异步转码:捕获Callback中MediaExtractor异常的方法

当然可以实现!

你遇到的这个问题在MediaCodec异步转码场景里很常见——子线程中抛出的异常如果不妥善捕获,很容易导致资源泄漏或者主线程无法感知错误状态。下面是一套完整的方案,能帮你把Callback中的异常传递到主线程的awaitEncode函数,进而安全关闭所有资源:

核心思路

我们需要一套线程安全的异常存储机制,配合同步等待工具,把HandlerThread中Callback抛出的异常传递到主线程;同时在Callback的所有回调方法中统一捕获异常,触发资源清理并通知主线程。

具体实现步骤

1. 准备线程安全的异常存储与同步工具

在你的转码类中定义两个核心变量:

  • AtomicReference<Throwable>:用来存储第一个发生的异常(保证线程安全,避免多线程覆盖)
  • CountDownLatch:让主线程的awaitEncode等待转码完成或异常发生
private final AtomicReference<Throwable> mTranscodeException = new AtomicReference<>();
// 如果是音视频双解码,这里计数设为2
private final CountDownLatch mTranscodeLatch = new CountDownLatch(1);

2. 在MediaCodec.Callback中统一捕获异常

给Callback的所有回调方法(onInputBufferAvailable、onOutputBufferAvailable、onError、onOutputFormatChanged)都加上try-catch块,捕获所有Throwable(不要只抓Exception,避免漏掉Error类的异常),然后统一处理:

private MediaCodec.Callback mVideoCodecCallback = new MediaCodec.Callback() {
    @Override
    public void onInputBufferAvailable(MediaCodec codec, int index) {
        try {
            // 你的正常处理逻辑:读取Extractor样本、输入到Codec等
            if (!mVideoExtractor.advance()) {
                codec.signalEndOfInputStream();
                return;
            }
            long sampleTime = mVideoExtractor.getSampleTime();
            ByteBuffer inputBuffer = codec.getInputBuffer(index);
            // 写入数据到inputBuffer...
            codec.queueInputBuffer(index, 0, inputBuffer.limit(), sampleTime, 0);
        } catch (Throwable t) {
            handleTranscodeException(t, codec);
        }
    }

    @Override
    public void onOutputBufferAvailable(MediaCodec codec, int index, MediaCodec.BufferInfo info) {
        try {
            // 你的输出处理逻辑:写入Muxer等
            ByteBuffer outputBuffer = codec.getOutputBuffer(index);
            mMuxer.writeSampleData(mVideoTrackIndex, outputBuffer, info);
            codec.releaseOutputBuffer(index, false);
            // 处理结束逻辑...
        } catch (Throwable t) {
            handleTranscodeException(t, codec);
        }
    }

    // 其他回调方法(onError、onOutputFormatChanged)同样加try-catch

    private void handleTranscodeException(Throwable t, MediaCodec codec) {
        // 只存储第一个异常,避免后续异常覆盖
        if (mTranscodeException.compareAndSet(null, t)) {
            // 立即停止并释放当前Codec
            try {
                codec.stop();
                codec.release();
            } catch (Exception e) {
                // 释放时的异常可以记录日志,不影响主异常处理
                Log.e("Transcode", "Failed to release codec", e);
            }
            // 通知主线程转码异常终止
            mTranscodeLatch.countDown();
        }
    }
};

音频解码器的Callback也需要完全一样的异常处理逻辑,确保任何一条轨道出问题都能及时通知主线程。

3. 主线程awaitEncode函数处理异常与资源清理

在awaitEncode中,先等待Latch触发,然后检查是否有异常,最后统一关闭所有资源:

public void awaitEncode() throws InterruptedException, Throwable {
    // 等待转码完成或异常发生
    mTranscodeLatch.await();
    
    // 检查是否有异常
    Throwable exception = mTranscodeException.get();
    if (exception != null) {
        // 可以在这里重新抛出异常,让上层处理,或者直接处理
        throw exception;
    }
    
    // 正常完成时的资源清理
    cleanUpAllResources();
}

private void cleanUpAllResources() {
    // 统一关闭所有资源:Extractor、Muxer、剩余Codec等
    try {
        if (mVideoExtractor != null) {
            mVideoExtractor.release();
        }
        if (mAudioExtractor != null) {
            mAudioExtractor.release();
        }
        if (mMuxer != null) {
            mMuxer.stop();
            mMuxer.release();
        }
        // 其他资源如HandlerThread也可以在这里quit
    } catch (Exception e) {
        Log.e("Transcode", "Failed to clean up resources", e);
    }
}

关键注意事项

  • 线程安全优先:必须用线程安全的容器存储异常(比如AtomicReference),避免多线程下的异常覆盖问题。
  • 捕获所有Throwable:不要只捕获Exception,因为MediaCodec或Extractor可能抛出Error类的异常(比如OOM导致的OutOfMemoryError)。
  • 资源清理顺序:先停止Codec,再关闭Extractor和Muxer,避免资源占用或文件损坏。
  • 音视频双轨处理:如果是同时处理音视频,CountDownLatch的计数要设为2,每个Codec完成或异常时都调用countDown()。

另外,你遇到的IllegalStateException大概率是某些异常文件导致Extractor状态异常(比如文件损坏、格式不兼容),捕获异常后及时清理资源就能避免程序崩溃或资源泄漏。

内容的提问来源于stack exchange,提问作者Jon G

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:42:46