认识音视频
常见的Mp4等格式,不是视频的编码格式,只是一种压缩格式(封装格式)。相当于.zip
各种视频格式的本质都是:一个视频流:(H264码流) + 一个音频流(ACC音频流)组成的(也可能是多个流)
大部分视频都是H264/H265(H264升级版)格式,也有一部分是mpeg-4编码。(因为H264同等质量带宽更小)
(备注:H264 = mpeg-4 AVC 名称不一样,不同组织不同叫法)
摄像头捕捉到画面后,会生成视频源Yuv,进行编码后就变为H264码流(视频流)。
麦克风捕捉到声音后,会生成音频源PCM,进行编码后就变为ACC码流(音频流)。
视频流+音频流=视频
编码压缩流程
解码播放流程
音频重采样:把双声道转为单声道
安卓屏幕都是RGBA 所以需要将YUV转为RGBA
硬编与软编
每个手机都有Dsp芯片,这个芯片专门负责音视频的编码、解码。
用DSP芯片进行编码(解码)就是硬编
用CPU进行编码(解码)就是软编
因为cpu很忙,所以用软编会导致cpu资源的占用,并且手机发热很严重。
但是手机种类有很多,需要完全兼容dsp芯片很难。
所以最优解就是:能硬编就硬编,无法硬编再走软编
硬解:mediacodec
软解:ijkplayer默认是基于FFmpeg的软解(所以发热比较严重)
编码的本质就是压缩数据!!!
为什么要编码?
因为视频不进行压缩是很大的,网络带宽又很珍贵。
假设1帧图片1M 那么1分钟视频 = 1m * 60s * 30fps = 1800M
怎么编码?
压缩,去除冗余信息
1.空间冗余 == 相邻像素重复
2.时间冗余 == 帧之间的差值
3.视觉冗余 == 人类不敏感的信息(亮度敏感,色度不敏感)
4.信息熵冗余 == 熵编码-哈夫曼算法
5.知识冗余 == 某些验证信息等
视频源Yuv
视频源为什么是yuv?yuv是什么意思?为什么不是RGB?
RGB存储
如果用RGB来表示,每一个像素用rgb来表示
1.传输的时候需要3个通道(R/G/B)
2.存储4个像素的时候需要3 * 4=12个字节。
yuv存储
解释yuv: y=亮度 U与V存储色度(颜色)
聪明的科学家就根据了人眼的特点:对亮度敏感,对色值不敏感的特点。设计了YUV。
yuv传输只需要一个通道
yuv存储4个像素:4个y + 1个u+1个v=6个字节(以前黑白电视就是仅有Y,彩色电视YUV,uv混上颜色,看起来就像rgb颜色)
将视频源Yuv编码成H264流
编码分为帧内编码和帧间编码。
帧内编码
视频随便截一帧,其实就是一张图片。所以帧内编码就是怎么将图片编码(压缩)
图片放大后其实就是无数个像素块,怎么将这些像素块进行压缩?
编码:将图片打乱分为N个块,为了方便压缩,这些块都是颜色比较相近的。或者说将附近颜色趋于一致的不同像素分为大大小小不同的宏块
帧内像素预测:1帧图像打成n个宏块
根据附近颜色会打成不同宏块。
如:大面积颜色比较相近, 宏块大小:16 * 16
颜色差别较大会打成8 * 8、16 * 8、8 * 16等
比如:
压缩宏块
比如16 * 16的块,只需要记录 16+16-1个像素就可以了,不需要记录完整的16*16=256像素
但是仅记录两边的话,那么右下角如果颜色有变化,无法表示,所以引入了
预测方向
预测方向
还原的时候有一个预测方向,根据这个方向去生成一个渐变色 来代表这个块本来的颜色(大差不差的还原)
比如这个块,还原的时候就会从坐上渐变到右下,来还原这个块的颜色
帧间编码
因为视频前后一帧。画面大概率不会相差特别大,(走路)最多就是人往前运动了0.12s,放到这本质就是一部分宏块移动了一段距离。所以不必全部的记录这个画面,只需要记录宏块的位移就行。
帧间运动矢量:(运动补偿)
这时候,就将视频帧分为了:
I帧(关键帧、帧内编码帧) B帧(双向预测内插编码帧) P帧(前向预测编码帧)
I帧:原始图像
P帧:与I帧的差别信息
B帧:与前面I帧和后面P帧对比后的差别信息(直播少,因为解码慢)
先编码I帧,然后后面的帧跟I帧对比,画面差距过大时,输出生成P帧,I与P帧之间的帧为B帧。B帧是参考前后I/P帧在输出生成H264码流的
视频第一帧就是I帧,I帧会直接输出成H264码流
后面帧会跟I帧做对比,画面差距有没有30%,没有就存到传输缓冲器中,然后找下一帧做对比,直到找到差距30%的帧,此帧为P帧直接输出
P帧输出完了,中间的第2帧、第三帧(存在传输缓冲器中)会跟第1帧I帧,第4帧P帧做对比,然后计算后再输出,H264码流,中间帧为B帧。
解码的时候也反向解,先解I帧,在解P帧,再解B帧,然后播放B帧,播放P帧(播放根据时间戳播放,解码根据编码顺序解I - P - B)
(备注:B帧的前后也可以都是P帧,因为B帧叫双向预测内插编码帧)
H264码流传输
H264码流传输(不可能一个一个块传输太无序了):H264是不见头不见尾的序列(直播)
传输:视频序列 -> 分为N个图像 -> 1个图像又分为N个片silence -> 一个片分为片头+n个宏块
NAL(网络抽象层面):类似于json,在传输时,传输的是NAL
VCL(视频编码层面):类似于javaBean,将数据编码成NAL
传输的时候就是 -NAL -NAL -NAL,其中-表示起始码(0x000001或0x00000001)
H264分析
NAL单元的各种类型如下表所示:
使用UItraEdit工具,打开一个h264,查看16进制源码
可以看到起始码 00 00 01,起始码后面的第一位表示NAL的类型。
可以看到第一个值是0x67,类型的值,
第1位表示该帧是否可用
第23位表示该帧重要程度
后5位表示帧的类型(查上表)
0x67 -> 01100111 ->0 11 00111 ->可用 重要 0x07
0x07根据上表可查出是序列参数集(SPS)(记录有多少I帧,多少B帧,多少P帧,帧是如何排列)
继续查第二个NAL
0x68 -> 0110 1000 ->0 11 01000 ->可用 重要 0x08
0x08根据上表可查出是图像参数集(PPS)(图像宽高信息等)
继续查下一个NAL
0x06 -> 0000 0110 ->0 00 00110 ->可用 不重要 0x06
0x06根据上表可查出是补充增强信息(SEI)(备注信息,解码时可以获取使用)
继续查下一个NAL
0x65 -> 0110 0101 ->0 11 00101 ->可用 重要 0x05
0x05 IDR图像的Slice,也就是I帧
继续查下一个NAL
0x41 -> 0100 0001 ->0 10 00001 ->可用 一般重要 0x01
0x01 非IDR图像,也就是非I帧,P帧 (还有一种情况0x61)
继续查下一个NAL
0x01 -> 0000 0001 ->0 00 00001 ->可用 不重要 0x01
0x01 非IDR图像,也就是非I帧,B帧(因为不重要00)
GOP
两个I帧之间
I BB P B P *** I 分为一组,名字叫GOP