使用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
相关产品推荐
相关产品推荐

