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

Android端MediaCodec解码H264流超时求助:dequeueOutputBuffer返回-1

哇,卡三周真的太闹心了!我帮你梳理下MediaCodec解码H264时dequeueOutputBuffer一直返回-1的常见坑,一步步排查解决:

排查MediaCodec输出超时问题

1. 确认Surface与MediaCodec的绑定时机是否正确

你用的是TextureView,必须等它的SurfaceTexture完全可用后,再初始化MediaCodec并绑定Surface——也就是要在SurfaceTextureListener的onSurfaceTextureAvailable回调里做初始化操作,不能在onCreate里提前搞!

比如正确的初始化逻辑应该是这样:

@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
    Surface surface = new Surface(surfaceTexture);
    try {
        m_codec = MediaCodec.createDecoderByType("video/avc");
        MediaFormat format = MediaFormat.createVideoFormat("video/avc", width, height);
        
        // 重点!拆分SPS和PPS,分别放到csd-0和csd-1参数里
        // 假设你的byte_SPSPPS是「起始码+SPS+起始码+PPS」的格式
        int spsEnd = findNextStartCodeIndex(byte_SPSPPS, 4); // 跳过第一个起始码
        byte[] sps = Arrays.copyOfRange(byte_SPSPPS, 4, spsEnd);
        byte[] pps = Arrays.copyOfRange(byte_SPSPPS, spsEnd + 4, byte_SPSPPS.length);
        
        format.setByteBuffer("csd-0", ByteBuffer.wrap(sps));
        format.setByteBuffer("csd-1", ByteBuffer.wrap(pps));
        
        m_codec.configure(format, surface, null, 0);
        m_codec.start();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

// 辅助方法:找到下一个00 00 00 01起始码的位置
private int findNextStartCodeIndex(byte[] data, int start) {
    for (int i = start; i < data.length - 3; i++) {
        if (data[i] == 0 && data[i+1] == 0 && data[i+2] == 0 && data[i+3] == 1) {
            return i;
        }
    }
    return data.length;
}

这里最容易踩的坑:把SPS和PPS混在一起塞给csd参数,解码器根本识别不了,直接罢工不输出。

2. 检查输入帧的格式与时间戳

MediaCodec对输入的H264帧要求很严格:

  • 必须是完整的NAL单元,带正确的起始码(00 00 00 01或00 00 01)
  • 一定要设置合理的时间戳(presentationTimeUs)!如果时间戳为0、乱序或者间隔不合理,解码器会卡住不输出

正确的输入缓冲区处理逻辑:

// 在接收帧的线程里处理
if (byte_Frame != null && m_codec != null) {
    int inputBufferIndex = m_codec.dequeueInputBuffer(1000);
    if (inputBufferIndex >= 0) {
        ByteBuffer inputBuffer = m_codec.getInputBuffer(inputBufferIndex);
        inputBuffer.clear();
        inputBuffer.put(byte_Frame);
        
        // 用当前时间转成微秒作为时间戳,或者用服务器传来的帧时间戳更准确
        long pts = System.currentTimeMillis() * 1000;
        m_codec.queueInputBuffer(inputBufferIndex, 0, byte_Frame.length, pts, 0);
        byte_Frame = null; // 处理完清空,避免重复解码
    }
}

3. 确保输出缓冲区的正确处理

就算解码器有输出,如果你忘了释放缓冲区,也会导致后续无法获取新的输出。处理输出的逻辑要完整:

MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
int outputBufferIndex = m_codec.dequeueOutputBuffer(info, 100000);
switch (outputBufferIndex) {
    case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
        // 第一次输出前会回调这个,正常现象
        Log.d("H264Player", "输出格式更新:" + m_codec.getOutputFormat());
        break;
    case MediaCodec.INFO_TRY_AGAIN_LATER:
        // 超时了,先检查输入是否正常(有没有传对SPS/PPS、帧数据是否完整)
        Log.d("H264Player", "等待输出超时,检查输入数据");
        break;
    default:
        // 渲染到Surface并释放缓冲区,第二个参数必须为true
        m_codec.releaseOutputBuffer(outputBufferIndex, true);
        // 如果收到EOS标记,停止解码
        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
            // 做停止解码的逻辑
        }
        break;
}

4. 线程安全问题要注意

你用ZMQ接收数据是在单独线程吧?要确保:

  • MediaCodec的所有操作(初始化、输入、输出)要么在同一个线程,要么通过Handler切换到同一线程执行,不能多线程同时调用MediaCodec的方法
  • 接收线程要等MediaCodec完全初始化完成后,再开始传帧数据,避免空指针或者无效操作

5. 验证SPS/PPS的有效性

如果SPS或PPS本身是错的,解码器根本无法正常初始化。你可以把分离出的SPS/PPS数据保存成文件,用ffprobe工具验证:

ffprobe -show_streams -i sps_pps.bin

如果工具识别不出H264参数,说明你的SPS/PPS分离有问题,得重新检查数据解析逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 03:52:18