如何将多线程重写方法改为单线程以规避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
相关产品推荐
相关产品推荐

