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

使用FFmpeg解码H.265格式IP摄像头流时出现‘First slice in a frame missing’错误的技术求助

解决H.265多切片IDR帧解码“First slice in a frame missing”问题

我之前处理过类似的H.265 + Live555 + FFmpeg解码场景,你的问题核心是H.265的单帧多切片需要在应用层手动重组——Live555只会按RTP包传递单个NALU,不会帮你合并同属一个帧的切片,而FFmpeg对H.265的帧完整性要求极高,收到不完整的切片就会抛出“First slice in a frame missing”错误。H.264能正常工作是因为FFmpeg对它的部分切片容错性更强,但H.265必须传入完整帧数据才能解码。

下面是具体的解决步骤:

1. 用RTP时间戳分组同帧切片

同一个帧的所有H.265切片会共享相同的RTP时间戳,这是最可靠的分组依据。你可以从Live555的FramedSource中拿到presentationTime(对应RTP时间戳),用这个值判断当前NALU是否属于正在组装的帧:

  • 维护一个缓冲区暂存当前帧的所有NALU
  • 收到新NALU时,对比其时间戳与缓冲区中帧的时间戳:
    • 相同则追加NALU到缓冲区
    • 不同则先把缓冲区里的完整帧传给FFmpeg,再清空缓冲区开始组装新帧

2. 确保每个NALU都带正确的起始码

不管Live555传递的NALU是否自带起始码,都要保证每个NALU前有0x00000001(FFmpeg优先识别4字节起始码),这样FFmpeg才能正确分割帧内的各个NALU。

3. 处理IDR帧的特殊情况

IDR帧的所有切片必须完整收集后再传给FFmpeg,不能单独发送单个切片。因为IDR帧是帧内刷新帧,FFmpeg需要完整的IDR数据重置解码上下文,缺失任何一个切片都会导致解码失败。

伪代码示例

这里给你一个简化的C++风格实现逻辑:

// 类成员或全局变量:暂存当前帧和对应时间戳
std::vector<uint8_t> m_current_frame;
uint64_t m_current_frame_ts = 0;

void onFrameReceived(uint8_t* data, size_t data_size, uint64_t presentation_ts) {
    std::vector<uint8_t> nalu_data;

    // 步骤1:添加起始码(如果没有)
    bool has_start_code = (data_size >= 4 && data[0] == 0 && data[1] == 0 && data[2] == 0 && data[3] == 1) 
                        || (data_size >= 3 && data[0] == 0 && data[1] == 0 && data[2] == 1);
    if (!has_start_code) {
        nalu_data.insert(nalu_data.end(), {0, 0, 0, 1});
    }
    nalu_data.insert(nalu_data.end(), data, data + data_size);

    // 步骤2:按时间戳分组帧
    if (presentation_ts == m_current_frame_ts) {
        // 同帧切片,追加到缓冲区
        m_current_frame.insert(m_current_frame.end(), nalu_data.begin(), nalu_data.end());
    } else {
        // 上一帧组装完成,传给FFmpeg解码
        if (!m_current_frame.empty()) {
            // 构造AVPacket并发送给FFmpeg解码器
            AVPacket pkt = {0};
            av_init_packet(&pkt);
            pkt.data = m_current_frame.data();
            pkt.size = m_current_frame.size();
            pkt.pts = pkt.dts = presentation_ts; // 可根据实际情况调整时间戳
            
            int ret = avcodec_send_packet(m_avctx, &pkt);
            if (ret >= 0) {
                AVFrame* frame = av_frame_alloc();
                while (avcodec_receive_frame(m_avctx, frame) == 0) {
                    // 处理解码后的帧(渲染、存储等)
                    av_frame_unref(frame);
                }
                av_frame_free(&frame);
            }
            m_current_frame.clear();
        }
        // 开始组装新帧
        m_current_frame_ts = presentation_ts;
        m_current_frame = std::move(nalu_data);
    }

    // 可选:添加超时逻辑,防止缓冲区因丢包一直挂起
}

额外注意事项

  • 参数集(VPS/SPS/PPS)处理:确保在解码开始前(或参数集更新时)把VPS/SPS/PPS正确传给FFmpeg,这些参数是解码的基础,缺失或错误会导致后续帧解码失败。
  • FU类型NALU(类型49):你之前尝试的FU组装逻辑只适用于被分片的单个NALU,而你的问题是单帧多切片(每个切片是独立的NALU),所以FU逻辑不适用,时间戳分组才是正确方向。
  • 丢包处理:如果遇到丢包导致帧不完整,建议丢弃整个帧,避免传给FFmpeg引发连锁错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 15:47:45