SurfaceView复用关联BufferQueueProducer与MediaCodec异常求助
Android MediaCodec复用SurfaceView后绿屏闪烁问题解决方案
无需重建SurfaceView的刷新方法
- 清空解码器缓冲区再释放:在调用
mediaCodec.release()前,先执行flush(),循环取出所有输出缓冲区直到流结束,确保解码器的所有帧都已输出到Surface,避免残留Buffer占用导致的slot异常:
mediaCodec.flush(); MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); while (true) { int outputIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0); if (outputIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { break; } else if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { continue; } else if (outputIndex >= 0) { mediaCodec.releaseOutputBuffer(outputIndex, false); if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { break; } } } mediaCodec.release();
- 强制刷新Surface内容:释放解码器后,通过SurfaceHolder锁定画布并填充背景色,覆盖残留的绿屏帧:
SurfaceHolder holder = surfaceView.getHolder(); Canvas canvas = holder.lockCanvas(); if (canvas != null) { // 使用应用背景色或黑色填充 canvas.drawColor(Color.BLACK); holder.unlockCanvasAndPost(canvas); }
- 延迟复用Surface:释放解码器后延迟100-200ms再初始化新的MediaCodec,给系统足够时间清理BufferQueue的残留资源:
mediaCodec.release(); new Handler(Looper.getMainLooper()).postDelayed(() -> { // 重新初始化并配置MediaCodec initAndConfigureCodec(surfaceView.getHolder().getSurface()); }, 150);
其他可行解决方案
- 优先使用谷歌软件解码器:针对有问题的硬件解码器,直接指定使用谷歌开源软解,避免底层BufferQueue兼容性问题:
MediaCodec codec = null; // H264解码器初始化 try { codec = MediaCodec.createByCodecName("OMX.google.h264.decoder"); } catch (IOException e) { codec = MediaCodec.createDecoderByType("video/avc"); } // HEVC解码器初始化 try { codec = MediaCodec.createByCodecName("OMX.google.hevc.decoder"); } catch (IOException e) { codec = MediaCodec.createDecoderByType("video/hevc"); }
- 替换为TextureView:TextureView的Buffer管理机制更灵活,不会出现SurfaceView底层BufferQueue残留的问题,复用稳定性更高,仅需将布局中的SurfaceView替换为TextureView,解码流程基本一致。
- 复用解码器实例:避免频繁释放和重建MediaCodec,而是调用
mediaCodec.flush()重置解码器状态,复用同一个解码器处理不同视频,减少Surface与解码器绑定/解绑的次数,从根源避免BufferQueue异常。
内容的提问来源于stack exchange,提问作者Hong
相关产品推荐
相关产品推荐

