一、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. k8s 深入篇———— Job与CronJob[十]

    开篇 简要演练一下job 和 cronjob 正文 实际上,它们主要编排的对象,都是"在线业务",即:Long Running Task(长作业).比如,我在前面举例时常用的 Ng ...

  2. 重新点亮shell————变量[三]

    前言 简单介绍一下shell的变量. 正文 变量的定义 变量名的命名规则 字母.数字.下划线 不以数字开头 变量的赋值 在赋值的时候不能出现空格 a =123,在等号前面有一个空格,那么会报错. 这是 ...

  3. Nginx 简介、安装与配置文件详解

    〇.前言 在日常工作中,Nginx 的重要性当然不言而喻. 经常用,但并不意味着精通,还会有很多不清楚的方式和技巧,那么本文就简单汇总下,帮助自己理解. 一.Nginx 简介 1.1 关于 Nginx ...

  4. Django框架——路由分发、名称空间、虚拟环境、视图层三板斧、JsonResponse对象、request获取文件、FBV与CBV、CBV源码剖析、模版层

    路由分发 # Django支持每个应用都可以有自己独立的路由层.静态文件.模版层.基于该特性多人开发项目就可以完全解耦合,之后利用路由分发还可以整合到一起 多个应用都有很多路由与视图函数的对应关系 这 ...

  5. 精通中间件测试:Asp.Net Core实战指南,提升应用稳定性和可靠性

    引言 在上一章节我们实战了在Asp.Net Core中的项目实战,这一章节讲解一下如何测试Asp.Net Core的中间件. TestServer 还记得我们在集成测试中提供的TestServer吗? ...

  6. iLogtail社区版使用入门 - 采集MySQL Binlog

    简介: MySQL Binlog记录了MySQL的变更日志,业界也有一些方案来同步Binlog的数据,如Canal.MaxWell.DTS等.不同的工具可以实现不同的目标,iLogtail也提供了便捷 ...

  7. 云原生事件驱动引擎(RocketMQ-EventBridge)应用场景与技术解析

    简介: RocketMQ 给人最大的印象一直是一个消息引擎.那什么是事件驱动引擎?为什么我们这次要推出事件驱动引擎这个产品?他有哪些应用场景,以及对应的技术方案是什么?本文我们就一起来看下. 作者:罗 ...

  8. 为 Serverless Devs 插上 Terraform 的翅膀,实现企业级多环境部署(上)

    简介: Serverless Devs 离不开对云资源的操作,但支持新资源时需要开发相应的组件代码:​如果将环境模板的定义通过 Terraform IaC 来完成,在 Serverless Devs ...

  9. Flagger on ASM——基于Mixerless Telemetry实现渐进式灰度发布系列 2 应用级扩缩容

    简介: 应用级扩缩容是相对于运维级而言的.像监控CPU/内存的利用率就属于应用无关的纯运维指标,针对这种指标进行扩缩容的HPA配置就是运维级扩缩容.而像请求数量.请求延迟.P99分布等指标就属于应用相 ...

  10. [Linux] 启动管理: 运行级别

    Link:https://www.cnblogs.com/farwish/p/14983932.html