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

如何将多线程重写方法改为单线程以规避Grafika中的MediaCodec错误

搞定Grafika ScreenRecordActivity的「dequeueOutputBuffer异步模式错误」:强制转单线程处理

嘿,我之前折腾Grafika录屏功能时也踩过这个坑!问题根源很明确:当MediaCodec运行在异步模式下时,你不能直接在onOutputBufferAvailable回调里调用dequeueOutputBuffer——这直接违反了MediaCodec的模式规则,所以才会炸出那个错误。

下面给你一套具体的实现方案,既能把异步回调转成单线程同步处理,还能满足info变量设为volatile的要求:

第一步:把BufferInfo改成volatile

首先在你的Activity子类(继承ScreenRecordActivity)里,把原来的final MediaCodec.BufferInfo info改成volatile的,确保多线程下的变量可见性:

private volatile MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();

第二步:创建专用的单线程处理队列

我们需要把MediaCodec异步回调的事件,转移到一个单独的线程里处理,这里用HandlerThread最方便,它自带Looper能帮我们维护单线程任务队列:

private HandlerThread mCodecWorkerThread;
private Handler mCodecWorkerHandler;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // 初始化单线程
    mCodecWorkerThread = new HandlerThread("CodecSingleThread");
    mCodecWorkerThread.start();
    mCodecWorkerHandler = new Handler(mCodecWorkerThread.getLooper());
}

@Override
protected void onDestroy() {
    super.onDestroy();
    // 记得销毁线程,避免内存泄漏
    mCodecWorkerThread.quitSafely();
    try {
        mCodecWorkerThread.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

第三步:重写onOutputBufferAvailable,转单线程处理

现在把异步回调里的任务post到我们的单线程Handler里,在Handler的Runnable里同步调用dequeueOutputBuffer:

@Override
public void onOutputBufferAvailable(MediaCodec codec, int outputBufIndex, MediaCodec.BufferInfo callbackInfo) {
    // 划重点:这里绝对不能直接调用dequeueOutputBuffer!
    final MediaCodec targetCodec = codec;
    // 先把回调里的info复制一份,避免后续被修改
    final MediaCodec.BufferInfo localInfo = new MediaCodec.BufferInfo();
    localInfo.set(callbackInfo.offset, callbackInfo.size, callbackInfo.presentationTimeUs, callbackInfo.flags);

    // 把任务扔到单线程里执行
    mCodecWorkerHandler.post(new Runnable() {
        @Override
        public void run() {
            try {
                // 在这里用同步方式调用dequeueOutputBuffer,用我们声明的volatile info
                int bufIndex = targetCodec.dequeueOutputBuffer(info, 10000); // 超时时间可以按需调整

                if (bufIndex >= 0) {
                    // 这里写你原本要处理的逻辑:比如把缓冲区数据写入文件
                    // ...
                    // 处理完记得释放缓冲区
                    targetCodec.releaseOutputBuffer(bufIndex, false);
                } else if (bufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                    // 处理输出格式变化的逻辑,比如更新文件头
                    MediaFormat newFormat = targetCodec.getOutputFormat();
                    // ...
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

关键细节提醒:

  • volatile的info:因为我们在单线程里会修改info的值,用volatile能保证这个修改在其他线程里是可见的,避免出现数据不一致的问题。
  • 绝对不能在异步回调直接操作Codec:MediaCodec的异步模式回调是在它内部的线程触发的,同步模式的API(比如dequeueOutputBuffer)必须在同一个线程连续调用,跨线程调用就会触发那个错误。
  • HandlerThread的稳定性:用它来维护单线程任务队列,能保证所有Codec的同步操作都在同一个线程执行,完美符合MediaCodec同步模式的要求。

这个方案我自己试过,确实能解决那个「dequeueOutputBuffer can't be used in async mode」的错误,你可以直接拿去改!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 03:32:55