Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/154.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用ffmpeg(libavcodec)通过RTP解码H264视频时出现问题_C++_H.264_Rtp_Libavcodec - Fatal编程技术网

C++ 使用ffmpeg(libavcodec)通过RTP解码H264视频时出现问题

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

我使用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)


问题出在哪里?

我不知道您实现的其余部分,但您收到的“片段”似乎是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(图片参数集)
所有这些数据在SDP中的RTSP会话中,在以下字段下发送:
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不需要的话,你需要这个