C++ 使用ffmpeg(libavcodec)通过RTP解码H264视频时出现问题
我使用SDP的profile level id et sprop参数集设置AVCodeContext的profile_idc、level_idc、extradata et extradata_大小 我分离编码切片、SPS、PPS和NAL_IDR_切片数据包的解码: 初始化: uint8_t start_序列[]={0,0,1}; int size=recv(id_de_la_socket,(char*)rtprective,65535,0) 编码片: 结果:ConsumedBytes>0和GotPicture>0(通常) SPS和PPS: 相同的代码。 结果:ConsumedBytes>0且GotPicture=0 我想这很正常 当我找到一对新的SPS/PP时,我会使用此数据包的有效负载及其大小更新extradata和extrada_大小 NAL_IDR_切片: Nal单元类型为28=>idr帧被分段,因此我尝试了两种方法进行解码 1) 我用序列0x000001作为第一个片段(不带RTP头)的前缀,并将其发送到avcodec\U decode\U video。然后我将剩余的片段发送到此函数 2) 我用序列0x000001作为第一个片段(不带RTP头)的前缀,并将其余片段连接到它。我将这个缓冲区发送到解码器 在这两种情况下,我都没有错误(ConsumedBytes>0),但我没有检测到帧(GotPicture=0)C++ 使用ffmpeg(libavcodec)通过RTP解码H264视频时出现问题,c++,h.264,rtp,libavcodec,C++,H.264,Rtp,Libavcodec,我使用SDP的profile level id et sprop参数集设置AVCodeContext的profile_idc、level_idc、extradata et extradata_大小 我分离编码切片、SPS、PPS和NAL_IDR_切片数据包的解码: 初始化: uint8_t start_序列[]={0,0,1}; int size=recv(id_de_la_socket,(char*)rtprective,65535,0) 编码片: 结果:ConsumedBytes>0和Got
问题出在哪里?我不知道您实现的其余部分,但您收到的“片段”似乎是NAL单元。因此,在将比特流发送到ffmpeg之前重建比特流时,每个都可能需要附加NALU开始代码(
00 00 01
或00 00 00 01
)
无论如何,您可能会发现H264 RTP打包的RFC很有用:
希望这有帮助 在RTP中,所有H264 I帧(IDR)通常都是分段的。当您收到RTP时,首先必须跳过报头(通常是前12个字节),然后到达NAL单元(第一个有效负载字节)。如果NAL为28(1C),则表示以下有效负载表示一个H264 IDR(I-Frame)片段,您需要收集所有这些片段以重建H264 IDR(I-Frame)
发生碎片是因为MTU有限,IDR大得多。一个片段可以如下所示:
起始位为1的片段:
First byte: [ 3 NAL UNIT BITS | 5 FRAGMENT TYPE BITS]
Second byte: [ START BIT | END BIT | RESERVED BIT | 5 NAL UNIT BITS]
Other bytes: [... IDR FRAGMENT DATA...]
First byte: [ 3 NAL UNIT BITS | 5 FRAGMENT TYPE BITS]
Other bytes: [... IDR FRAGMENT DATA...]
其他片段:
First byte: [ 3 NAL UNIT BITS | 5 FRAGMENT TYPE BITS]
Second byte: [ START BIT | END BIT | RESERVED BIT | 5 NAL UNIT BITS]
Other bytes: [... IDR FRAGMENT DATA...]
First byte: [ 3 NAL UNIT BITS | 5 FRAGMENT TYPE BITS]
Other bytes: [... IDR FRAGMENT DATA...]
要重建IDR,您必须收集以下信息:
int fragment_type = Data[0] & 0x1F;
int nal_type = Data[1] & 0x1F;
int start_bit = Data[1] & 0x80;
int end_bit = Data[1] & 0x40;
如果fragment\u type==28
,则其后面的有效负载是一个IDR片段。下一个检查是start\u bit
set,如果是,则该片段是序列中的第一个片段。通过从第一个有效负载字节(3个NAL单位位)
中提取前3位,并将其与第二个有效负载字节(5个NAL单位位)
中的最后5位组合,可以使用它来重构IDR的NAL字节,这样就可以得到这样的字节[3个NAL单位位| 5个NAL单位位]
。然后首先将该NAL字节与该片段中的所有其他后续字节一起写入一个清除缓冲区。请记住跳过序列中的第一个字节,因为它不是IDR的一部分,而只是标识片段
如果start\u位
和end\u位
为0,则只需将有效负载(跳过标识片段的第一个有效负载字节)写入缓冲区
如果start_位为0,end_位为1,这意味着它是最后一个片段,您只需将其有效负载(跳过标识该片段的第一个字节)写入缓冲区,现在就可以重建IDR
如果您需要一些代码,只需在评论中询问,我会发布它,但我认为这非常清楚如何做…=)
关于解码
今天我想起了为什么你在解码IDR时出错(我认为你重建得很好)。您如何建立AVC解码器配置记录?您使用的库是否实现了自动化?如果没有,而且你还没有听说过,继续阅读
AVCDCR被指定为允许解码器快速解析解码H264(AVC)视频流所需的所有数据。数据如下:
- 简介IDC
- 轮廓眼压
- LevelIDC
- SPS(序列参数集)
- PPS(图片参数集)
profile level id
和sprop参数集
解码配置文件级别ID
Prifile级别ID字符串分为3个子字符串,每个子字符串长度为2个字符:
[PROFILE IDC][PROFILE IOP][LEVEL IDC]
每个子字符串表示base16中的一个字节!所以,如果Profile IDC是28,这意味着在base10中实际上是40。稍后,您将使用base10值来构造AVC解码器配置记录
解码SPROP-PARAMETER-set
存储过程通常是两个逗号分隔的字符串(可能更多),并且base64编码!你可以同时解码它们,但没有必要。您在这里的工作只是将它们从base64字符串转换为字节数组以供以后使用。现在有了2个字节的数组,第一个数组是us SP,第二个是PPS
构建AVCDCR
现在,您已经具备了构建AVCDCR所需的一切,您可以从创建新的干净缓冲区开始,现在按照此处解释的顺序在其中写入以下内容:
1-具有值1并表示版本的字节
2-配置文件IDC字节
3-文件IOP字节
4级IDC字节
5字节,值为0xFF(通过谷歌搜索AVC解码器配置记录来查看这是什么)
6-值为0xE1的字节
7-短接SPS阵列长度的值
8-SPS字节数组
9字节,包含PPS数组的数量(sprop参数集中可以有更多PPS数组)
10-短,长度为以下PPS阵列
11-PPS阵列
解码视频流
现在有了字节数组,它告诉解码器如何解码H264视频流。我相信如果你的lib不需要的话,你需要这个