Android平台使用ExtractMpegFramesTest提取视频帧并执行BoofCV目标跟踪时出现死锁问题求助
我来帮你拆解并解决这个问题——从你的代码和日志来看,核心矛盾是跟踪处理阻塞了MediaCodec的解码线程循环,导致整个流程卡住。咱们一步步来分析和解决:
问题根源
你的processFrame是在MediaCodec输出帧的回调链路里执行的,虽然单帧跟踪耗时只有30-40ms,但如果和MediaCodec的解码线程绑定在同一个执行路径上,会导致解码循环无法及时回收输出缓冲区,进而让整个解码流程停滞。从日志能看到,执行tracker.process后MediaCodec的Surface连接被断开,这就是缓冲区无法正常回收的信号。
另外,你之前用MediaMetadataRetriever能正常运行,是因为它的帧提取是独立于解码线程的,不会占用MediaCodec的缓冲区资源。
可行解决方案
1. 异步处理跟踪逻辑,不阻塞解码线程
不要在处理Surface帧的同步流程里执行跟踪操作,而是把帧数据复制后交给独立的后台线程池处理,让MediaCodec能立刻释放缓冲区继续解码:
首先初始化一个单线程线程池(保证跟踪器线程安全,因为BoofCV/OpenCV跟踪器通常不是线程安全的):
// 类成员变量,初始化一次即可 private ExecutorService trackerWorker = Executors.newSingleThreadExecutor();
然后修改你的processFrame方法,只做帧数据复制和提交:
public void processFrame() { mPixelBuf.rewind(); GLES20.glReadPixels(0, 0, mWidth, mHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mPixelBuf); // 复制缓冲区数据,避免原缓冲区被MediaCodec立即回收 ByteBuffer frameCopy = ByteBuffer.allocate(mPixelBuf.capacity()); frameCopy.put(mPixelBuf); frameCopy.rewind(); // 保存当前帧的宽高,避免匿名内部类引用外部变量的问题 final int frameWidth = mWidth; final int frameHeight = mHeight; // 提交到后台线程处理 trackerWorker.submit(() -> { Bitmap bmp = Bitmap.createBitmap(frameWidth, frameHeight, Bitmap.Config.ARGB_8888); bmp.copyPixelsFromBuffer(frameCopy); GrayU8 img = ConvertBitmap.bitmapToGray(bmp, null, null); Log.d(TAG, "Running tracker..."); tracker.process(img, location); barPath.add(getCenterOf(location)); Log.d(TAG, "Finished running tracker."); bmp.recycle(); }); }
2. 优化帧数据转换,减少内存拷贝开销
你当前的Surface -> ByteBuffer -> Bitmap -> GrayU8流程有多次内存拷贝,既耗时又占内存。可以直接从ByteBuffer转换为BoofCV的灰度图像,跳过Bitmap环节:
// 后台线程内的处理逻辑替换成这个 GrayU8 img = new GrayU8(frameWidth, frameHeight); // 直接把RGBA格式的ByteBuffer转成灰度图,省去Bitmap的创建和回收 ConvertRgb.byteRgbaToGray(frameCopy, img); tracker.process(img, location); barPath.add(getCenterOf(location));
这一步能大幅降低单帧处理的内存开销和耗时,进一步减少线程阻塞的风险。
3. 确保跟踪器的线程安全
BoofCV和OpenCV的跟踪器都不是线程安全的,上面用单线程线程池就能保证所有tracker.process调用都在同一个线程里执行,避免跨线程操作导致的死锁或状态异常。
4. 调整MediaCodec缓冲区配置(可选)
如果还是出现缓冲区不足的情况,可以在初始化MediaCodec时调整缓冲区参数:
MediaFormat videoFormat = MediaFormat.createVideoFormat(videoMimeType, videoWidth, videoHeight); // 设置最大输入缓冲区大小,根据你的视频分辨率调整 videoFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, videoWidth * videoHeight * 2); MediaCodec decoder = MediaCodec.createDecoderByType(videoMimeType); decoder.configure(videoFormat, outputSurface, null, 0);
为什么之前的尝试会有不同结果
- 注释掉
tracker.process后,processFrame只做轻量的内存操作,不会阻塞解码线程,所以流程正常。 - 替换成OpenCV跟踪器时,因为跟踪逻辑同样阻塞了解码线程,所以还是会卡死。
- 模板匹配能运行但速度极慢,是因为它单帧耗时更长,但还没到完全耗尽缓冲区的程度,只是解码线程被长时间阻塞,导致整体速度暴跌。
内容的提问来源于stack exchange,提问作者John Doe

