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

Android平台使用ExtractMpegFramesTest提取视频帧并执行BoofCV目标跟踪时出现死锁问题求助

解决Android MediaCodec解码+BoofCV/OpenCV跟踪时的卡死问题

我来帮你拆解并解决这个问题——从你的代码和日志来看,核心矛盾是跟踪处理阻塞了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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 20:02:51