这一段从三光吊舱接收数据时,因为对方外协了一个转换盒子,将同步422出来的h264编码的数据转成了RTP进行发送。我们能拿到的就是这个RTP数据。使用wireshark抓了一堆包,然后使用rtp_h264_extractor插件将里面的h264码流dump出来了,并使用ffplay进行播放,没问题。

但是我前面使用libjrtplib3监听了转换盒子发送出来的rtp数据流。然后使用getPayloadData()将所有的h264负载保存了下来。但是播放不了。对比了一下发现。我保存下来的rtp封包的h264数据。准确来说,就是对于单Nalu帧,它把前面的00 00 00 01这个start code给去掉了;对于FU-A这种分片帧,它把帧头的 00 00 00 01这个start code去掉了,同时把原来的一个字节的nalu_header_type给换成了2个字节,分别是FU indicator和FU header。这里需要把它们还原回去。至于怎么还原,可以看我昨天写的博文。

还原回去之后,发现还是不能播放。又去对比我转换后的文件和wireshark插件拖出来的h264数据。发现还少了SPS和PPS以及SEI帧。这些帧都在IDR帧之前。SPS里面存储了序列参数集合,而PPS存储了图像参数集合。没有这些参数,是无法解析h264的。

上图中,

  • 00 00 00 01是NALU头,是序列的标识符的开头;
  • 0x27转成二进制是100111,00111转成十进制是7,那么7对应NALU type=sps;
  • 0x28转成二进制是101000,转成十进制是8,8对应NALU type=pps;
  • 0x25转成二进制是100101,00101转成十进制是5,5对应的NALU type=IDR帧;

使用FFMPEG解码时,sps和pps是保存在AVCodecContext的extradata.data中,在解码提取sps和pps时,判断NALU type可以用extradata.data[ 4 ]&0x1f(结果是7是sps,8是pps,计算方式是先转成二进制,0x27&0x1f=11111&00111=00111=7,pps计算类似);

NALU type=1是splice,splice有三种编码模式,I_slice、P_slice、B_slice,I帧在编码时就分割保存在splice中。

  1. NALU type值对应表如下:

实际上是因为我前面写的程序有问题。我是基于已经落盘的payload构成的数据集进行测试的(因为吊舱被拉走了,我没法直接测了)。

通过分析和查找资料。我确认了IDR是必须的。IDR一定是I帧,但是I帧不一定是IDR帧。这两东西在编码中是一样的。只是作用不同,所以起了个不同的名字罢了。

IDR是强制刷新帧,也就是说只要解码器碰到了IDR帧,就会把它前面保留的解码参考序列清空,后续的解码以此IDR帧为参考帧。这也是为什么对于没有IDR帧的视频无法随意拖动播放,而又IDR帧的视频可以随意拖动播放的原因。因为有IDR帧的,在拖动后,会从最近的地方找到一个IDR,然后解码后面的图像。没有IDR的,则需要依赖前面的I帧或者P帧,拖动后,无法找到前面的I帧或者P帧。

而SPS和PPS也是一个图像序列必须的。在我们wireshark得到的序列中,就存在很多的SPS和PPS帧,都在IDR之前。甚至还有SEI帧,也在IDR之前。但是这些并不是都需要的。在文件开始的时候只要有一套SPS、PPS、[SEI]、IDR、序列即可对整个h264序列解码(如果中间图像分辨率、大小之类的都没有变化的情况下),h264序列中的IDR前面没有SPS、PPS、[SEI]也是可以的。不过如果在使用过程中如果分辨率等发生了变化,则SPS\PPS是必须保留的。

后续的代码改进的地方是在getPayloadData()之后,

1. 判断是单NAL还是FU类型帧;

2. 如果是单NAL帧,则判断

2.1 当前帧k是不是SPS帧,k+1帧是不是PPS,k+2帧是不是IDR帧或者SEI帧;如果k+2帧是SEI,则k+3是不是IDR帧;如果都满足,则将它们都存下来(一般来说,SPS、PPS、SEI都非常小,在一个MTU中足以容纳;对于这种较小的情况,对于SPS,PPS,SEI直接在前面加上00 00 00 01,然后保存;对于IDR帧,则根据它是单NAL还是FU来判断如何处理);

2.2 如果是IDR帧或者P帧,则在前面直接加上00 00 00 01然后存储(一般IDR帧较大,所以这种情况出现的概率非常低);

3. 如果不是单NAL帧,而是FU-A帧

3.1 I和P帧的处理方式相同,首先找到首包,也就是FU header的最左位为1,然后计算fu indicator和fu header得到nal_header_type这个1个字节,然后使用00 00 00 01 nal_header_type加上后续数据构成首包,放到缓冲区;

3.2 接收后续的中间包(fu header的左边两位都是0),判断和前面的那个包类型是否一致(都是I或者P),然后将fu indicator和fu header两个字节删掉,追加到上述缓冲区后面;

3.3 接收到尾包后(fu header的左边第二位为1),说明该帧的数据接收完了,将该包的前fu indicator和fu header去掉,剩余部分追加到上述缓冲区。

4. 将上述完成的帧写入文件。

上述过程是指的写入文件的过程。如果是使用ffmpeg解码,则也是将相关完整帧序列传给ffmpeg进行解码和播放。

这里没有讨论聚合包和FU-B的情况。

如果有不正确的地方,请读者联系我指出来。

H264 over RTP中存在多个SPS、PPS、[SEI]、 IDR序列是否都需要相关推荐

  1. h264 sei信息 解码_关于H264编码数据中SPS,PPS,SEI,IDR等内容的问题

    群内的朋友们好! 我使用的平台是ipnc rdk 3.8 , 我在h264编码的有关NALU参数设置如下: staticParams->nalUnitControlParams.naluCont ...

  2. SPS PPS SEI

    转载自https://www.jianshu.com/p/6c532568be1c 海思不是有demo,存h264文件吗?你可以把数据导出来看看啊,只需要做个地址偏移就可以得到sps.pps了 nal ...

  3. 图像编码中的小白问题sps ,pps ,nalu ,frame ,silce ect....

    图像编码中的小白问题sps ,pps ,nalu ,frame ,silce ect.... 转载于:https://www.cnblogs.com/jingzhishen/p/5401222.htm ...

  4. RTP中H264封装NALU(SPS,PPS等)

    NAL的英文全称为Network  Abstract Layer,即网络抽象层,在H264/AVC视频编解码标准中,整个系统框架分为两个层面,视频编解码层面(VCL)和网络抽象层面(NAL).VCL负 ...

  5. RTP中H264封装NALU格式详细解析

    名词解释:NAL NAL的英文全称为Network Abstract Layer,即网络抽象层,在H264/AVC视频编解码标准中,整个系统框架分为两个层面,视频编解码层面(VCL)和网络抽象层面(N ...

  6. H264帧的分析sps pps

    帧格式 H264帧由NALU头和NALU主体组成. NALU头由一个字节组成,它的语法如下: +---------------+       |0|1|2|3|4|5|6|7|       +-+-+ ...

  7. H264--NALU/SPS/PPS

    H264结构中,一个视频图像编码后的数据叫做一帧,一帧由一个片(slice)或多个片组成,一个片由一个或多个宏块(MB)组成. H264编码过程中的三种不同的数据形式: SODB        数据比 ...

  8. h265、h264的RTP包封装区别

    h265.h264的RTP包封装区别 一.NAL单元 1.h264 NAL单元 /** h264 nal头部(1字节)* 0 1 2 3 4 5 6 7* +-+-+-+-+-+-+-+-+-+-+- ...

  9. 视频【编码】原理(H.264 librtmp推流),图像编码中sps ,pps ,nalu ,frame ,silce ect

    视频编码格式:H264, VC-1, MPEG-2, MPEG4-ASP (Divx/Xvid), VP8, MJPEG 等.  音频编码格式:AAC, AC3, DTS(-HD), TrueHD, ...

最新文章

  1. 排序:冒泡排序与选择排序
  2. Linux shell脚本的字符串截取
  3. ArcMAP获取要素的外包多边形
  4. mysql哨兵机制_Redis 哨兵机制以及底层原理深入解析,这次终于搞清楚了
  5. 1.8 centos7网络排错
  6. 什么是AWT_Swing_Vector?居然这么高频使用
  7. Shell脚本编程之(六)循环
  8. 13. Memcache 安装,启动,基本命令,URL
  9. JWT、OAuth 2.0、session 用户授权实战
  10. 高等数学在计算机的应用论文,(高等数学论文计算机软件及应用.doc
  11. LoRaWAN网络协议与LoRa私有协议相比有哪些优势
  12. 罗马音平假名中文可复制_200个中文常见姓氏的日语发音,再也不愁起日文名字啦!...
  13. android在线音乐播放器教程,简单实现Android本地音乐播放器
  14. SAP MM-MB52 物料库存查询简单操作
  15. 基于实物的智能化仓储管理-InStock
  16. 护理管理学复习题及参考答案
  17. 2015-2016 ACM-ICPC, NEERC, Moscow Subregional Contest G题: Garden Gathering [线段树/最小曼哈顿距离生成树]
  18. 如何删除Mac下载PS后莫名其妙多出来的几个程序
  19. w7设置双显示器_Win7系统双屏显示设置的方法
  20. iOS 图片捏合放大缩小 点击放大缩小

热门文章

  1. Python实现显著性检验delong
  2. Ansible安装及模块管理
  3. 广义积分中值定理的证明
  4. 零基础JavaScript入门教程(6)–JS之使用开发者工具
  5. 简单制作扑克牌和洗牌
  6. smokeping图表解释
  7. MoveWindow() 与SetWindowPos()
  8. python时间序列预测不连续怎么办_Python建模:预测周期性时间序列的正确姿势
  9. python 实现四则混合运算计算器
  10. 什么是变量,变量的本质