RTMP推流与B帧的关系
一、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帧的关系的更多相关文章
- 安卓直播开源: RTMP 推流SDK
前些日子在github上提交了基于GPUImage的IOS直播推流SDK(https://github.com/runner365/GPUImageRtmpPush) 最近整理了android直播推流 ...
- C++ 实现的netstat -an 的功能<转>-目的为获取rtmp推流地址如果是域名的话查看1935的ip
目的可能是为了获取rtmp真正的推流ip 如果rtmp推流地址是域名,往CDN推流的话,需要nslookup 的那种DNS解析,然后获取的几个ip 可以使用netstat -n 等命令查看 1935 ...
- ffmpeg+EasyDSS流媒体服务器实现稳定的rtmp推流直播
本文转自EasyDarwin团队成员Alex的博客:http://blog.csdn.net/cai6811376/article/details/74783269 需求 在做EasyDSS开发时,总 ...
- 前端多媒体(7)—— 在浏览器中实现rtmp推流
示例:https://young-cowboy.github.io/gallery/rtmp_client/index.html 在国内的直播场景中通常使用,rtmp协议作为推流协议.RTMP是Rea ...
- win10下一分钟快速搭建rtmp推流服务器
为了让大家少踩笔者踩过的坑,目前将工作中搭建rtmp推流服务器的步骤总结如下: 步骤1: 下载 nginx 1.7.11.3 Gryphon 下载链接: http://nginx-win.ecsds. ...
- Android 安卓直播开源: RTMP 推流SDK
前些日子在github上提交了基于GPUImage的iOS直播推流SDK(https://github.com/runner365/GPUImageRtmpPush) 最近整理了Android直播推流 ...
- 命令行利用ffmpeg实现rtmp推流《转》
ffmpeg在以前介绍过,是一个相当强大的工具,我们这次利用它实现rtmp推流(最终推流地址统一为rtmp://127.0.0.1:1935/live/123). 1.首先下载ffmpeg和ffpla ...
- rtsp向rtmp推流
package com.awifi.video.media.test; import org.bytedeco.javacpp.avcodec; import org.bytedeco.javacv. ...
- 3款知名RTMP推流模块比较:OBS VS SmartPublisher VS Flash Media Live Encoder
OBS 功能强大,几乎所有你想要的场景它都有,用起来很顺手.可以将桌面.摄像头.程序窗口通过rtmp推送到流媒体服务器上. 当然如果你是开发者,想基于OBS做二次开发,实现二次产品化的化,难度比较大, ...
- 安卓平台RTMP推流或轻量级RTSP服务(摄像头或同屏)编码前数据接入类型总结
很多开发者在做Android平台RTMP推流或轻量级RTSP服务(摄像头或同屏)时,总感觉接口不够用,以大牛直播SDK为例 (Github) 我们来总结下,我们常规需要支持的编码前音视频数据有哪些类型 ...
随机推荐
- leetcode - 子数组最大平均值
给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大平均数. 示例: 输入:[1,12,-5,-6,50,3], k = 4 输出:12.75 解释:最大平均数 (12-5-6+5 ...
- 还原数据库从远端环境到本地开发环境,数据SINGLE_USER问题
要还原到本地开发环境,首先要detach本地数据库或者是删除本地数据库,不然提示在使用中还原不了,但是搞过几次去做detach和drop数据库时,总是不成功,最后还出现个单用户模式,莫名奇妙 为此网上 ...
- 飞桨PaddlePaddle的安装
飞桨PaddlePaddle的安装 MacOS 下的 PIP 安装 一.环境准备 1.1 如何查看您的环境 可以使用以下命令查看本机的操作系统和位数信息: uname -m && ca ...
- 暑期集训 Day10 —— 模拟赛复盘
${\color{Green} \mathrm{Problem\ 0 :water }} $ 题如其名,可以用单调队列做,但是数据范围直接暴力枚举每一高度就行. 最不会打错的,还是暴力,所以用暴力. ...
- 龙湖千丁基于 ACK@Edge 的云原生智慧停车系统架构实践
简介: 结合龙湖千丁自研的新版停车云系统以及 ACK@Edge 提供的标准 Kubernetes 服务以及云边一体化协同解决方案,整体来着,边缘部署时间成本由 1 天缩短到 3 小时,将之前的手动升级 ...
- Apache ShenYu 网关正式支持 Dubbo3 服务代理
简介: 本文介绍了如何通过 Apache ShenYu 网关访问 Dubbo 服务,主要内容包括从简单示例到核心调用流程分析,并对设计原理进行了总结. 作者:刘良 Apache Dubbo 在去年发布 ...
- 从 RxJS 到 Flink:如何处理数据流?
简介: 前端开发的本质是什么?响应式编程相对于 MVVM 或者 Redux 有什么优点?响应式编程的思想是否可以应用到后端开发中?本文以一个新闻网站为例,阐述在前端开发中如何使用响应式编程思想:再以计 ...
- 阿里云云效技术专家:一文详解kubernetes下5种常见发布模式如何选择
简介: Kubernetes下5场场景应用发布方式的选择,每种发布模式适合什么样的场景,以及如何在阿里云云效上高效落地. 作者:郑云龙,阿里云云效技术专家 Kubernetes面向通用场景提供了非常灵 ...
- Yurt-Tunnel 详解|如何解决 K8s 在云边协同下的运维监控挑战
简介: 伴随着 5G.IoT 等技术的快速发展,边缘计算被越来越广泛地应用于电信.媒体.运输.物流.农业.零售等行业和场景中,成为解决这些领域数据传输效率的关键方式.与此同时,边缘计算形态.规模.复杂 ...
- [PHP] 如何让 php-fpm 的循环 echo 实时输出到浏览器
PHP 里开启实时输出方法是 ob_implicit_flush() , 但它大部分情况下都不管用, 因为 php.ini 配置里 output_buffering 输出缓冲大部分是 On 开启的 ...