公司动态

h264 extradata解析,提取参数集SPS/PPS

📅 2026/8/4 20:49:30
h264 extradata解析,提取参数集SPS/PPS
1 背景在音视频处理中AVCC 格式转换为 Annex-B 格式的典型场景包括以下六种情况一、Android/Harmony等平台硬件解码场景Android/Harmony 系统硬解码器通常仅支持 Annex-B 格式的码流输入。当从 MP4/MKV/MOV等容器默认使用 AVCC 格式中提取 H.264码流进行解码时需通过 SPS/PPS 提取、起始码替换等操作完成格式转换。例如 MP4 文件头部的 AVCDecoderConfigurationRecord 数据结构需被解析后重组为 Annex-B 的头部结构。二、实时流传输协议封装在 RTP/RTSP 协议传输或 TS 流封装场景中Annex-B 格式作为标准要求存在。由于 Annex-B 使用 0x000001 或 0x00000001 起始码标识 NALU 边界便于流媒体服务端进行码流分割和实时传输。三、容器格式转换场景将 MP4 文件重新封装为 FLV、TS 等格式时需遵循目标容器的编码规范。例如 FLV 容器要求视频编码信息通过 AVCDecoderConfigurationRecord 传递而 TS 流通常直接使用 Annex-B 格式的起始码分割 NALU。四、硬件解码兼容性修复某些硬件解码器如部分 GPU 加速方案对 Annex-B 格式有强依赖性。若使用 FFmpeg 的 h264_mp4toannexb 等 Bitstream Filter 处理码流时未正确处理 SPS/PPS 插入逻辑可能引发硬解异常此时需规范转换流程。五、防竞争字节处理需求AVCC 格式使用长度前缀标识 NALU 大小而 Annex-B 需在码流中插入防竞争字节Emulation Prevention Bytes避免起始码冲突。当需要兼容老式解码设备或特定流媒体服务器时必须进行格式转换以实现防竞争处理。六、低延迟直播场景优化Annex-B 格式允许在关键帧前周期性插入 SPS/PPS使解码器可从视频流任意位置开始解码。这种特性使其更适合低延迟直播场景而 AVCC 格式因依赖容器头部的集中式参数存储难以满足实时流随机接入需求。AVCC转 Annex-B 场景中当视频帧是IDR帧时需要将参数集SPS/PPS在IDR前拷贝到解码器输入缓存中才能正常解码。本文讲解H.264 extradata语法以及如何从mp4/mkv/mov等容器中提取参数集SPS/PPS。2 AVCC和Annex-B的定义和区别AVCCAVCC是一种用于封装H.264码流的格式它将SPSSequence Parameter Set和PPSPicture Parameter Set封装为ExtraData通常用于MP4/MKV/MOV等封装格式的文件头中播放器通过这些信息初始化解码器‌。Annex-B‌这是一种对NALU层进行包装的格式每个NALU前面加上起始码如0x000001或0x00000001用于分隔不同的NALU。为了防止原始码流中出现相同的起始码Annex-B引入了防竞争字节Emulation Prevention Bytes的概念即在出现0x0001之前插入一个字节内容为0x3‌。 AVCC以长度信息替代起始码其NALU内部的原始数据无需额外处理即可避免数据与起始码冲突的问题因此没有防竞争码。AVCC 结构AVCC封装的H.264码流中最前面的是AVCDecoderConfigurationRecord或称为extradata。AVCDecoderConfigurationRecord语法如下所示(H.264-AVC-ISO_IEC_14496-15)字段解释 configurationVersion 占1字节版本号固定为1。 AVCProfileIndication 占1字节SPS的profile_idc即(H.264标准规定的)SPS的第一个字节。 profile_compatibility占1字节即(H.264标准规定的)SPS的第二个字节。 AVCLevelIndication 占1字节SPS的level_idc即(H.264标准规定的)SPS的第三个字节。 lengthSizeMinusOne取该字节低2位lengthSizeMinusOne为存贮NALUnitLength所需的空间减1 单位为字节。比如当lengthSizeMinusOne为2那么存贮该路H.264码流的每个NALUnitLength所需 的空间为(21)个字节。我们在读取每个NALU之前先读(21)个字节把读取到的数据转成整数 这就是该NALU的总长度了。 numOfSequenceParameterSets取该字节低5位该extradata中SPS个数。 sequenceParameterSetLength占2字节SPS长度以字节为单位。 sequenceParameterSetNALUnitSPS二进制数据长度为sequenceParameterSetLength字节。 numOfPictureParameterSets占1字节该extradata中PPS个数。 pictureParameterSetLength占2字节PPS长度以字节为单位。 pictureParameterSetNALUnitPPS二进制数据长度为pictureParameterSetLength字节。 chroma_format取低2位包含由chroma_format_idc参数定义的chroma_format指示符。 bit_depth_luma_minus8取低3位表示Luma数组中样本的位深度。例如位深度为8用0 表示 位深度8bit_depth_luma_minus8。此字段的值应在0到4的范围内包括0和4。 bit_depth_chroma_minus8取低3位表示Chroma阵列中样本的位深度。例如位深度为8用0 表示 位深度8bit_depth_luma_minus8。此字段的值应在0到4的范围内包括0和4。 numOfSequenceParameterSetExt占1字节表示用于解码AVC基本流的序列参数集扩展的数量。 sequenceParameterSetExtLength占2字节表示SPS 扩展的长度。 sequenceParameterSetExtNALUnit表示SPS 扩展二进制数据长度为sequenceParameterSetExtLength字节。AVCProfileIndicationprofile_compatibilityAVCLevelIndication三个字段对应H.264标准(44 Rec. ITU-T H.264 (08/2021))定义的前三个字节如下图所示。Annex-B‌ 结构(Recommendation ITU-T H.264 08/2021)3 实例分析01 :configurationVersion固定为164:AVCProfileIndication 1字节00:profile_compatibility1字节28:AVCLevelIndication1字节ff:低2位是 “11”,即3 说明存储 “NALUnitLength”所需空间是31 即4.e1:numOfSequenceParameterSets取低5位“00001” ,即100 1e:这两个字节是 numOfSequenceParameterSets的长度二进制“11110”, 十进制30SPS nal 长30个字节跳过sps nal 30个字节。01pps个数 100 06:pps二进制数据长度6个字节跳过6个字节fd:chroma_format,取低2位为0f8:bit_depth_luma_minus8取低三位0f8:bit_depth_chroma_minus8取低三位04 获取avc extradatah264, extra data存在于trak-mdia-minf-stbl-stsd-avc1-avcc 通常使用ffmpeg解封装后得到。// 解析文件intretavformat_open_input(fmtCtx_,filepath_.toStdString().c_str(),nullptr,nullptr);if(ret0){qDebug()avformat_open_input failed;return;}retavformat_find_stream_info(fmtCtx_,nullptr);if(ret0){qDebug()avformat_find_stream_info failed;return;}for(inti0;ifmtCtx_-nb_streams;i){AVStream*streamfmtCtx_-streams[i];if(stream-codecpar-codec_id!AV_CODEC_ID_H264){qDebug()please input h264 file;return;}constAVCodec*decavcodec_find_decoder(stream-codecpar-codec_id);AVCodecContext*codec_ctx;if(!dec){qDebug()Failed to find decoder for stream:i;return;}codec_ctxavcodec_alloc_context3(dec);if(!codec_ctx){qDebug()Failed to allocate the decoder context for stream:i;return;}retavcodec_parameters_to_context(codec_ctx,stream-codecpar);if(ret0){qDebug()Failed to copy decoder parameters to input decoder context for stream:i;return;}if(codec_ctx-codec_typeAVMEDIA_TYPE_VIDEO||codec_ctx-codec_typeAVMEDIA_TYPE_AUDIO){if(codec_ctx-codec_typeAVMEDIA_TYPE_VIDEO){codec_ctx-framerateav_guess_frame_rate(fmtCtx_,stream,NULL);videoCodecCtx_codec_ctx;videoDec_(AVCodec*)dec;videoStreamIndex_i;if(videoCodecCtx_-extradata_size0){QString exepathQCoreApplication::applicationDirPath()/extradata.bin;FILE*fpfopen(exepath.toStdString().c_str(),w);if(fp){fwrite(videoCodecCtx_-extradata,videoCodecCtx_-extradata_size,1,fp);fclose(fp);}}}elseif(codec_ctx-codec_typeAVMEDIA_TYPE_AUDIO){audioCodecCtx_codec_ctx;audioDec_(AVCodec*)dec;audioStreamIndex_i;}retavcodec_open2(codec_ctx,dec,NULL);if(ret0){qDebug()Failed to open decoder for stream:i;return;}}}5 解析avc extradatavoidMainWindow::parseAvcExtradata(constuint8_t*extra_data){intpos1;// AVCProfileIndication1字节intavcprofileidcextra_data[pos];// profile_compatibility1字节intavcprofilecompatextra_data[pos];// AVCLevelIndication1字节intavclevelidcextra_data[pos];// lengthSizeMinusOne低2位intlensizeextra_data[pos]0x03;// numOfSequenceParameterSets低5位intnumSPSextra_data[pos]0x1F;// aways 1std::vectoruint8_tsps;for(inti0;inumSPS;i){// sequenceParameterSetLength2字节intspsLen(extra_data[pos]8)|extra_data[pos1];pos2;sps.assign(extra_datapos,extra_dataposspsLen);posspsLen;}std::vectoruint8_tpps;// numOfPictureParameterSets低5位intnumPPSextra_data[pos];for(inti0;inumPPS;i){// pictureParameterSetLength2字节intppsLen(extra_data[pos]8)|extra_data[pos1];pos2;pps.assign(extra_datapos,extra_dataposppsLen);posppsLen;}// chroma_format, 取低2位intchroma_formatextra_data[pos]0x03;// bit_depth_luma_minus8, 取低3位intbit_depth_luma_minus8(extra_data[pos]0x07);// bit_depth_chroma_minus8, 取低3位intbit_depth_chroma_minus8(extra_data[pos]0x07);// numOfSequenceParameterSetExt,占1字节intspsextnumextra_data[pos];std::vectoruint8_tspsext;for(inti0;ispsextnum;i){// sequenceParameterSetExtLength,2字节intspsextLen(extra_data[pos]8)|extra_data[pos1];pos2;spsext.assign(extra_datapos,extra_dataposspsextLen);posspsextLen;}}6 代码连接avc extradata提取和解析代码运行界面如下C 音视频开发问题音视频同步花屏卡顿crashCPU占用过高延时问题解决代码跨平台移植/国产平台适配 / 资源优化等一系列音视频痛点问题