一、H264数据结构

  一个原始的H.264 NALU 由一个接一个的 NALU 组成的,而它的功能分为两层,VCL(视频编码层)和 NAL(网络提取层).

  VCL:包括核心压缩引擎和块,宏块和片的语法级别定义,设计目标是尽可能地独立于网络进行高效的编码。
  NAL:负责将VCL产生的比特字符串适配到各种各样的网络和多元环境中,覆盖了所有片级以上的语法级别。

  

  组成:NALU (Nal Unit) = NALU头 + RBSP 在 VCL 数据传输或存储之前,这些编码的 VCL 数据,先被映射或封装进 NAL 单元(以下简称 NALU,Nal Unit) 中。每个 NALU 包括一个原始字节序列负荷(RBSP, Raw Byte Sequence Payload)、一组 对应于视频编码的 NALU 头部信息。RBSP 的基本结构是:在原始编码数据的后面填加了结尾 比特。一个 bit“1”若干比特“0”,以便字节对齐。

  一个原始的H.264 NALU 单元常由 [StartCode] [NALU Header] [NALU Payload] 三部分组成
  如下所示:

  

  iOS的硬编码器输出的就是一个一个NALU + pts + dts

  StartCode : Start Code 用于标示这是一个NALU 单元的开始,必须是”00 00 00 01” 或”00 00 01”

  NALU Header 下表为 NAL Header Type

  

  

  RBSP :NAL包将其负载数据存储在 RBSP(Raw Byte Sequence Payload) 中,RBSP 是一系列的 SODB(String Of Data Bits)。

  一帧图片跟NALU的关联:
  一帧图片经过 H.264 编码器之后,就被编码为一个或多个片(slice),而装载着这些片(slice)的载体,就是 NALU 了。
  注意:片(slice)的概念不同与帧(frame),帧(frame)是用作描述一张图片的,一帧(frame)对应一张图片,而片(slice),是 H.264 中提出的新概念,是通过编码图片后切分通过高效的方式整合出来的概念,一张图片至少有一个或多个片(slice)。

  

  片的类型:

  

  •   I片:只包 I宏块,I 宏块利用从当前片中已解码的像素作为参考进行帧内预测(不能取其它片中的已解码像素作为参考进行帧内预测)。
  •   P片:可包 P和I宏块,P 宏块利用前面已编码图象作为参考图象进行帧内预测,一个帧内编码的宏块可进一步作宏块的分割:即 16×16、16×8、8×16 或 8×8 亮度像素块(以及附带的彩色像素);如果选了 8×8 的子宏块,则可再分成各种子宏块的分割,其尺寸为 8×8、8×4、4×8 或 4×4 亮度像素块(以及附带的彩色像素)。
  •   B片:可包 B和I宏块,B 宏块则利用双向的参考图象(当前和 来的已编码图象帧)进行帧内预测。
  •   SP片(切换P):用于不同编码流之间的切换,包含 P 和/或 I 宏块
  • SI片:扩展档次中必须具有的切换,它包含了一种特殊类型的编码宏块,叫做 SI 宏块,SI 也是扩展档次中的必备功能。

  以上为H264流的数据结构定义,下面阐述I帧、P帧、B帧的关系

二、I帧、P帧、B帧

  • I帧:帧内编码帧(intra picture),I帧通常是每个GOP(MPEG所使用的一种视频压缩技术)的第一帧,经过适度地压缩,作为随机访问的参考点可以当成静态图像。I帧可以看做一个图像经过压缩后觉得产物,I帧压缩可以得6:1的压缩比而不会产生任何可觉察的模糊现象。I帧压缩可去掉视频的空间冗余信息,下面即将介绍P帧和B帧是为了去掉时间冗余信息。
  • P帧:前向预测编码在帧(predictive-frame),通过将图像序列中前面已编码帧的时间冗余信息去充分去除压缩传输数据量的编码图像,也成为预测帧。
  • B帧:双向预测内插编码帧(bi-directionalinterpolated prediction frame),既考虑源图像序列前面的已编码帧,又估计源图像序列后面的已编码帧之间的时间冗余信息,来压缩传输数据量的编码图像,也成为双向预测帧。

 基于上面的定义,我们可以从解码的角度来理解IBP帧。

  • I帧自身可以通过视频解压算法解压成一行单独的完善的完整视频画面,所以I帧去掉视频帧在空间维度上的冗余信息。
  • P帧需要参考其前面一个I帧或者P帧来解码成一张完整的视频画面。
  • B帧则需要参考前一个I帧或者P帧及其后面一个P帧来生成后面一张完整的视频画面,所以P帧与B帧去掉是视频在时间维度上的冗余信息。

 I帧并不是IDR帧,H264中的IDR帧(instantaneous decoding refresh picture),I帧之后P帧有可能会参考之前的I帧之前的帧,这就使得在随机访问的时候不能以找到I帧作为参考条件,因为即使找到I帧,I帧之后的帧还是有可能解析不出来,而IDR就是一个特殊的I帧,即这一帧之后的所有参考帧只会参考到这个IDR帧,而不会参考前面的帧。在解码器中,一旦接收到一个IDR帧,就会立即清理参考帧缓存区,并将IDR帧作为被参考的帧。 

三、GOP、pts、dts 、帧的排列顺序

  

  一个Gop一般由一个IDR帧和多个B帧和P帧组成。DTS是数据准备解码的时间、PTS是这一帧图像呈现的时间。

  因为B帧存在的关系,会导致B帧的pts和dts不一致的情况,也就是解码器为了解码出一个B帧,必须等待到下一个P帧或者I帧出现。

  比如:

  

  上面的图中,从左到右为时间序输入的图像,最终展示也应当按照这个顺序进行渲染,IBBBPBBBPBBBP...

  因为中间的B帧依赖后续的P帧才能解码,所以第2个B帧必须等到第5个P帧出现才能解码,因此会导致解码的延时,这也是大部分直播中的H264流关闭了B帧的原因。

  那如果RTMP中真实存在B帧会是什么情况呢?

  首先,直播中不断按照时间序列向编码器输入单帧图像,如下图所示:

  

  也就是,最终编码器输出的顺序是按照dts输出的,不仅如此,所有视频的文件存放顺序都是按照dts的顺序存放的。

  实际解码之后,在输入IPBBP之后,第一个P帧输入之后,解码器不会输出,输入第二个P帧之后,会先输出中间两个B帧,然后再输出P帧

  这也是某些编码器解码器编码解码API是异步的原因,ffmpeg是同步的API,因此会出现在合格问题:

  

 四、RTMP中如何兼容B帧

  

  因为RTMP发送的是H264的NALU单元,实际上RTMP的帧类型中,只是区分了IDR帧和非IDR帧,非IDR帧中包含P帧和B帧

  那怎么区分P帧和B帧呢,需要进入到NALU中SLICE的header中进行区分,所以RTMP协议可以不用关系到底是否包含B帧

  但是,B帧的存在影响到RTMP中的cts时间,因为dts在RTMP中单调递增,pts一定大于dts,因此RTMP中用一个字段表示pts(H264本身没有标记pts)

  

  rtmp视频包在h264的包前边再添加了9个Byte.具体内容填写参考srs的代码 SrsRawH264Stream::mux_ipb_frame 和 SrsRawH264Stream::mux_avc2flv

  这个是ipb贞的编码.

  buf[0] = 贞类型和编码. ( (1|2)<<4 | 7 ) 1是关键帧 2是P/B贞 7代表h264

  buf[1] = 数据类型 ( 1 = NALU )

  buf[2-4] = cts = pts – dts;

  buf[5-8] = NAL的长度,不包括贞分隔符

  buf[9-] = H264的RAW数据. 不包含分隔符,00 00 01 或者 00 00 00 01

  buf[9+NAL_length- +4byte] = 下一个NAL长度 这个很重要, 一个rtmp的视频包可能包括多个NAL的, 切记转换TS的时候把它分割并添加NAL的分隔符,例如{0×00,0×00,0×01}等。

  buf[9+NAL_length+4 -] = 下一个H264的RAW数据.

  ……

  这里需要注意的是cts这个值,如果视频流没有B侦的话,这个cts值=0 如果,这个cts值是一个动态数字,你需要根据这个值去计算pts。

五、参考

  https://juejin.im/post/5a8fe66b6fb9a0633e51eadc

  https://blog.csdn.net/wzw88486969/article/details/62229133

RTMP推流与B帧的关系的更多相关文章

  1. 安卓直播开源: RTMP 推流SDK

    前些日子在github上提交了基于GPUImage的IOS直播推流SDK(https://github.com/runner365/GPUImageRtmpPush) 最近整理了android直播推流 ...

  2. C++ 实现的netstat -an 的功能<转>-目的为获取rtmp推流地址如果是域名的话查看1935的ip

    目的可能是为了获取rtmp真正的推流ip 如果rtmp推流地址是域名,往CDN推流的话,需要nslookup  的那种DNS解析,然后获取的几个ip 可以使用netstat -n 等命令查看 1935 ...

  3. ffmpeg+EasyDSS流媒体服务器实现稳定的rtmp推流直播

    本文转自EasyDarwin团队成员Alex的博客:http://blog.csdn.net/cai6811376/article/details/74783269 需求 在做EasyDSS开发时,总 ...

  4. 前端多媒体(7)—— 在浏览器中实现rtmp推流

    示例:https://young-cowboy.github.io/gallery/rtmp_client/index.html 在国内的直播场景中通常使用,rtmp协议作为推流协议.RTMP是Rea ...

  5. win10下一分钟快速搭建rtmp推流服务器

    为了让大家少踩笔者踩过的坑,目前将工作中搭建rtmp推流服务器的步骤总结如下: 步骤1: 下载 nginx 1.7.11.3 Gryphon 下载链接: http://nginx-win.ecsds. ...

  6. Android 安卓直播开源: RTMP 推流SDK

    前些日子在github上提交了基于GPUImage的iOS直播推流SDK(https://github.com/runner365/GPUImageRtmpPush) 最近整理了Android直播推流 ...

  7. 命令行利用ffmpeg实现rtmp推流《转》

    ffmpeg在以前介绍过,是一个相当强大的工具,我们这次利用它实现rtmp推流(最终推流地址统一为rtmp://127.0.0.1:1935/live/123). 1.首先下载ffmpeg和ffpla ...

  8. rtsp向rtmp推流

    package com.awifi.video.media.test; import org.bytedeco.javacpp.avcodec; import org.bytedeco.javacv. ...

  9. 3款知名RTMP推流模块比较:OBS VS SmartPublisher VS Flash Media Live Encoder

    OBS 功能强大,几乎所有你想要的场景它都有,用起来很顺手.可以将桌面.摄像头.程序窗口通过rtmp推送到流媒体服务器上. 当然如果你是开发者,想基于OBS做二次开发,实现二次产品化的化,难度比较大, ...

  10. 安卓平台RTMP推流或轻量级RTSP服务(摄像头或同屏)编码前数据接入类型总结

    很多开发者在做Android平台RTMP推流或轻量级RTSP服务(摄像头或同屏)时,总感觉接口不够用,以大牛直播SDK为例 (Github) 我们来总结下,我们常规需要支持的编码前音视频数据有哪些类型 ...

随机推荐

  1. sql 语句系列(字符串之裂开)[八百章之第十三章]

    创建分割列表 一张表: 先查询出来的效果是这样的: mysql: select emp_copy.deptno,GROUP_CONCAT(emp_copy.emps SEPARATOR ',') fr ...

  2. windows server 出现.net 3.5安装不上去

    前言 有人说为什么到了现在还有人用.net 3.5呢?其实我也不用,主要是mmsql安装的时候要用到. 正文 一般我们不同电脑在程序和功能中添加,如果添加不上会弹出一个自动获取的一个过程,这时我们点击 ...

  3. C内存操作API的实现原理

    我们在编写C代码时,会使用两种类型的内存,一种是栈内存,另外一种是堆内存,其中栈内存的申请和释放是由编译器来隐式管理的,我们也称为自动内存,这种变量是最简单而且最常用的,然后就是堆内存,堆的申请和释放 ...

  4. Django框架——csrf跨站请求伪造、csrf校验、csrf相关装饰器、auth认证、auth认证相关模块及操作

    csrf跨站请求伪造 钓鱼网站:模仿一个正规的网站 让用户在该网站上做操作 但操作的结果会影响到用户正常的网站账户 但是其中有一些猫腻 eg:英语四六级考试需要网上先缴费 但是你会发现卡里的钱扣了但是 ...

  5. 来电科技:基于Flink+Hologres的实时数仓演进之路

    简介: 本文将会讲述共享充电宝开创企业来电科技如何基于Flink+Hologres构建统一数据服务加速的实时数仓 作者:陈健新,来电科技数据仓库开发工程师,目前专注于负责来电科技大数据平台离线和实时架 ...

  6. [FAQ] edge debug栏的网络里 没有见到 All Fetch/XHR JS CSS 这些东西

      一种方式是 打开调试器的设置,重置默认并刷新即可. 另一种方式是把这个 "筛选" 点掉. Tool:揭开网站所用的技术 Link:https://www.cnblogs.com ...

  7. [FAQ] Phpstorm 代码提示功能失效问题

    如果是之前有代码提示,中间突然不出现提示了,那么考虑重建一下项目索引. 示例: Refer:Phpstorm代码提示 Link:https://www.cnblogs.com/farwish/p/13 ...

  8. github 解决推拉代码提示 REMOTE HOST IDENTIFICATION HAS CHANGED 失败

    本文记录最近 github 推送或拉取代码时提示 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! 而失败的解决方法 报错提示如下 @@@@@@@@@@ ...

  9. dotnet 通过 DockerfileContext 解决项目放在里层文件夹导致 VisualStudio 构建失败

    本文告诉大家,如何解决 csproj 项目文件放入到里层的文件夹,不放在 sln 所在文件夹的第一层子文件夹,导致 VisualStudio 2022 在构建 docker 映像提示找不到文件的问题 ...

  10. jqGrid--设置单元格字体颜色

    colModel: [ { name: '列名称', index: '列名称', width: 65, sortable: true, resizable: false, cellattr: addC ...