音视频(一)基本开发概念
播放流程
简单理解封装格式
相当与将媒体文件是一个容器封装->音频流,视频流,字幕流
可以有多个视频流,也可以有多个音频流。通常有两种形式:
- 单独的字幕流,单独的音视频流
- 字幕流+视频流 合成流
简单理解解封装(解复用,打开封装格式)
-> 视频压缩数据(标准格式等,代表性H.264,H.265)
-> 视频解压播放(视频解码)
-> 原始数据YUV等
-》 YUV转换RGBA
-> 音频压缩数据(AAC,mp3,AC-3)
-> 音频解压播放(音频解码)
-> 原始数据PCM等
-》音频重新采样(双声道转单声道等声道转换)
一般需要考虑问题 -> 音视频同步
编码流程
采集图像数据
摄像头->采集RGBA-> 转换YUV-> 编码器编码YUV数据->H.264等格式压缩数据
↓
封包xxx.mp4 封装格式,封装操作
↑
采集音频数据
喇叭音频输入->音频采集->PCM数据-》AAC等压缩数据-》封包
编码的目的:去除冗余信息。
去掉冗余信息(可以理解成数据格式压缩)视频太大,完整不处理200GB的视频,编码后可能1G左右
编码的目的,就是为了压缩。各种视频编码方式,都是为了让视频变得体积更小,有利于存储和传输。编码的 核心思想就是去除冗余信息。
了解冗余信息分类
- 空间冗余=相邻像素重复
空间冗余:图像内部相邻像素之间存在较强的相关性多造成的冗余。
如:20x20 个像素,重复一样,那么只保留1个就好,因为剩下都是相似
- 时间冗余=帧之间的差值
时间冗余:视频图像序列中的不同帧之间的相关性所造成的冗余。
如:两张人像图片过度,只有眼睛发生变化,第二帧只要保留眼睛信息,参考第一帧。得到完整的第二帧图像。
- 视觉冗余=人类不敏感的颜色:(轻微的偏差和饱和度不敏感)
视觉冗余:是指人眼不能感知或不敏感的那部分图像信息。
如:深红色和浅红色还能分辨,会容易察觉,对亮度敏感。红色偏一点点橘红色,人眼就不容易察觉
- 信息熵冗余= 熵编码-哈夫曼算法等
信息熵冗余:也称编码冗余,人们用于表达某一信息所需要的比特数总比理论上表示该信息所需要的最少比特 数要大,它们之间的差距就是信息熵冗余,或称编码冗余。
- 知识冗余=人类(头,身体,腿),汽车,房子不需要记录
知识冗余:是指在有些图像中还包含与某些验证知识有关的信息。
I 帧,B帧,P帧
I帧: 帧内编码帧,关键帧,I帧可以看作一个图像经过压缩之后的产物,可以单独解码出一个完整的图像; (压缩率最低)
P帧:前向预测/参考 编码帧,记录了本帧跟之前的一个关键帧(或P帧)的差别,解码时需要用之前缓存的画 面叠加上本帧定义的差别,生成最终画面。 (压缩率比I帧高,比B帧低 属于 适中情况)
B帧:双向预测/参考 编码帧,记录了本帧与前后帧的差别,解码需要参考前面一个I帧或者P帧,同时也需要 后面的P帧才能解码一张完整的图像。 (参考前后的预测得到的,压缩率是最高,但是耗时))
图形学中基础经典的展示
所以作为直播的话,虽然B帧的压缩率最高,延时也比较高,在不考虑宽带费用的情况下会尽量减少B帧。为了观看的流畅度和体验。
编码统一名称
H.264 == MPEG-4 - AVC
H.265 == HEVC [H.264基础上 加入了更多的算法 == 压缩的更小]
H.264分层结构(VCL与NAL)
VCL :
VCL(Video Coding Layer,视频编码层):负责高效的视频内容表示, VCL数据即编码处理的输出,它表示被压缩编码后的视频数据序列。
NAL :
NAL(Network Abstraction Layer,网络提取层):负责以网络所要求的恰当的方式对数据进行打包和传送
是传输层。不管在本地播放还是网络播放,都要通过这一层来传输。
VCL就是被压缩编码后原始数据,在VCL数据封装到NAL单元中之后,才可以用于传输或存储。
数据传输:
无论是后端还是前端,数据传输过程都是遵循一个流程:
数据 → JavaBean → 序列化(Json)→ HTTP →
↓
TCP 传输
↓
数据 ← JavaBean ←序列化(Json)← HTTP ←
数据封装到NAL单元中之后传输类似
图片 → 编码(VCL) → 打包(NAL)→ HTTP →
↓
TCP 传输
↓
图片 ← 解码(VCL) ← 解包(NAL))← HTTP ←
NAL片(宏块)
一系列NAL每个NAL之间有规则的间隔,序列开头有起始码
一帧图片经过 H.264 编码器之后,NAL单元就是装载着这些片(被编码为一个或多个片 slice), 每片包 含整数个宏块(至少一个宏块,最多包含整个图像宏块)。
- 一般H.264编码器的默认输出为:起始码+NALU(Nal单元)。起始码为:0x00000001或者0x000001。
每一个NALU都需要分隔,要分隔帧操作,就相对于写文章断句一样,这样才方便解析操作
- 其中每个NALU之间通过startcode(起始码)进行分隔,起始码分成两种:
0x00|0001(3Byte)
或者0x0000|0001(4Byte)
。
0x00000001 起始码代表:一个NALU里面有很多片。
0x000001 起始码代表: 一个NALU里面一个片可以搞定。
NALU 包含:起始码(两种)+ 1个字节Nal Header + 其他字节负荷数据EBSP数据集
nal_unit_type. 这个NALU单元的类型,1~12由H.264使用,24~31由H.264以外的应用使用,简述如下:
0 没有定义
1-23 NAL单元 单个 NAL 单元包
1 不分区,非IDR图像的片
2 片分区A
3 片分区B
4 片分区C
5 IDR图像中的片
6 补充增强信息单元(SEI)
7 SPS
8 PPS
9 序列结束
10 序列结束
11 码流借宿
12 填充
13-23 保留
24 STAP-A 单一时间的组合包
25 STAP-B 单一时间的组合包
26 MTAP16 多个时间的组合包
27 MTAP24 多个时间的组合包
28 FU-A 分片的单元
29 FU-B 分片的单元
30-31 没有定义
文件头部分析类型:低五位的16进制的值,参照表可以查询数据类型
1. SPS 序列参数集(记录有多少I,B,P,如何排列),打开一个264文件分析(可以用iHex或者010 Editor)
00 00 00 01 67 → 二进制01100111 → 取低五位 000 00111 → 16进制 0x07 → 查表 :SPS
67 就是head
一个字节八位
0 1 1 0 0 1 1 1
分组
0(第一组,这个帧可以用0,不可以用1)丨 11(第二组,11 代表这个帧很重要)丨00111 (第三组低五位,类型)
第一组:1代表这个帧不可以用,0代表可以用
第二组:11 代表很重要
第三组:00111 查表 SPS
PPS 图像参数集(记录图像宽高信息)
00 00 00 01 68 → 二进制0x68 0110 1000 → 取低五位 01000 → 16进制0x08 → 查表:PPS
SEI 这种是短的,补充信息单元。记录坐标信息,人员信息等
00 00 01 06 → 0x06 00000110 → 低五位0x06 →查表:补充增强信息单元(SEI)可有可无
00 00 00 65 → 0110 0101 → 00101 → 0x05 → 查表:(IDR图像中的片)
00 00 01 41 → 0x41 :0100 0001 (不重要P帧) → 0x01 → 查表: (NAL单元 单个 NAL 单元包)
0x01 == 普通P帧 和 重要P帧 ,还有B帧
00 00 01 61 → (重要P帧)→ 0x61:0110 0001 → 0x01(多了一个1 ---0110)
00 00 01 01 → (B帧)→ 0000 0001→ 0x01 0x01 (前面全是0---0000)
开发中,SPS,PPS ,I帧 分析这三个比较多
查询无法播放等原因,可以考虑查看头是否确实信息,SPS,PPS ,I帧
PTS 与 DTS
DTS 解码时间戳,在什么时解码这一帧
PTS表示显示时间戳,什么时候显示这一帧
没有B帧的情况,DTS和PTS输出顺序是一样的。
因为B帧打乱了解码和显示的顺序(要解码B帧需要先解码后面的P帧),所以一旦存在B帧,PTS和DTS就会不 同。
I帧 p b p b p b.... I帧 p b p b p b ...
SPS+PPS+两个I帧之间所有帧分(包括前面的I帧)为一组GOP
解析B帧的时候,B帧后面的p帧要是没有是不是就莫法解析了, B 必须保证 前后帧解码了,我才能参考
开发参考模型
一个线程:解码
一个队列 解码数据加入队列, 从队列取出数据 (缓冲数据的作用)
一个线程:播放
41 61 1 基本上都是P帧
01 1 基本上都是B帧
B,P帧数的字节小,B帧是最小的
高2-3位:11代表重要的P帧,10表示适中不重要的P帧,00表示不重要的帧