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) 我们来总结下,我们常规需要支持的编码前音视频数据有哪些类型 ...
随机推荐
- 重新点亮linux 命令树————文件特殊权限[十一]
前言 简单介绍一下文件特殊权限. 正文 SUID 用于二进制可执行文件,执行命令时取得文件属组权限 如 /usr/bin/passwd 当我们使用passwd 修改密码的时候其实是以root用户身份进 ...
- 重走py 之路 ——列表(一)
前言 因为最近公司有python项目维护,所以把python的基础入门的书整理一遍,因为有些忘记了,同时在看<<python编程>>这本书的时候觉得对有基础的有很多的赘余,打算 ...
- Pytorch-卷积神经网络CNN之ResNet的Pytorch代码实现
先说一个小知识,助于理解代码中各个层之间维度是怎么变换的. 卷积函数:一般只用来改变输入数据的维度,例如3维到16维. Conv2d() Conv2d(in_channels:int,out_chan ...
- 快速获取使用dblink的视图的全部字段
快速获取使用dblink的视图的全部字段 默认情况下,使用dblink连接的视图在PL/SQL是没法通过代码助手获取全部的字段的 如果要获取全部字段的话,可以使用一个奇怪的方法 对要获取的使用dbli ...
- 力扣372(java)-超级次方(中等)
题目: 你的任务是计算 ab 对 1337 取模,a 是一个正整数,b 是一个非常大的正整数且会以数组形式给出. 示例 1: 输入:a = 2, b = [3]输出:8示例 2: 输入:a = 2, ...
- 大屏小程序探索实践 | Cube 技术解读
简介: 支付宝客户端有极强的动态化诉求,不论 iOS 还是 Android 平台,重新分发软件包从时间上,效率上难以满足产品运营的要求,因此客户端动态化技术应运而生. Cube 起源于 Native ...
- MaxCompute项目子账号做超级管理员
场景 主账号不是大数据团队管理,使用MaxCompute员工都只持有子账号,而project的owner只能为主账号,但是很多MaxCompute的权限管理还需要owner才可以操作(如项目级别的fl ...
- 工商银行分布式服务C10K场景的解决方案
简介: 未来,中国工商银行将持续致力于 Dubbo 的金融级规模化应用. 作者:颜高飞,微服务领域架构师,主要从事服务发现.高性能网络通信等研发工作,擅长 ZooKeeper.Dubbo.RPC 协议 ...
- 【ESSD技术解读-01】 云原生时代,阿里云 ESSD 快照服务 助力企业级数据保护
简介:本文以云原生为时代背景,介绍了阿里云块存储快照服务如何基于高性能 ESSD 云盘提升快照服务性能,提供轻量.实时的用户体验及揭秘背后的技术原理.依据行业发展及云上数据保护场景,为企业用户及备份 ...
- [Go] Colly 使用 POST 提交 application/x-www-form-urlencoded 示范
Colly 提供了 Post 和 PostRaw 方法,它们的参数类型不一样,需要注意. 目标地址接受指定的 Content-Type,可以通过设置 request Header. 局部代码: // ...