学习目标:



Android音视频
H265和H264官方文档下载 视频分析软件,记得下载,下文分析的视频软件就靠它了码流查看器ffmpeg 提取视频命令:ffmpeg.exe -i input1.mp4 -c:v copy -bsf:v h264_mp4toannexb -an out1.h264 播放h264:ffplay -stats -f h264 out1.h264 提取音频命令:ffmpeg.exe -i input1.mp4 -acodec copy -vn out1.aac

学习内容:

大致思路

一般视频文件里不光有视频,还有音频(而且视频和音频的轨道可以有多个,没有限制。),封装格式的作用就是把视频和音频打包起来。 所以我们先要了解解封装格式,然后有哪些视频编码和哪些 音频编码,此时的音频流和视频流都还是压缩数据,不能直接用于显示,所以最后就需要解码
这张图就是我们H264的格式了。

Android 获取所有视频 android 获取所有视频教程_数据



全文总结:


NALU = NALU_HEADER + NALU_CONTENT


NALU_CONTENT (根据 NALU_HEADER里的不同的TYPE,它可能是 I帧(里面就是Slice 数组) 可能是SPS 也可能是其他,有32种不同的类型)

DSP芯片视频编码器工作流程

(Digital Signal Processing)即数字信号处理技术,DSP芯片即指能够实现数字信号处理技术的芯片

Android 获取所有视频 android 获取所有视频教程_数据_02

封裝格式

封装格式(也叫容器),就是将已经编码压缩好的视频轨和音频轨按照一定的格式放到一个文件中,也就是说仅仅是一个外壳,或者大家把它当成一个放视频轨和音频轨的文件夹也可以。 下面是几种常用的 视频文件后缀类型 与其相对应的 封装格式

视频文件格式

视频封装格式

.avi

AVI(Audio Video Interleaved)

.wmv、.asf

WMV(Windows Media Video)

.mpg、.mpeg、.vob、.dat、.3gp、.mp4

MPEG(Moving Picture Experts Group)

.mkv

Matroska

.rm、.rmvb

Real Video

.mov

QuickTime File Format

.flv

Flash Video

视频编码方式

将视频像素数据(RGB,YUV等)压缩成视频码流,从而降低视频的数据量

名称

推出机构

HEVC(H.265)

MPEG/ITU-T

H.264

MPEG/ITU-T

MPEG4

MPEG

MPEG2

MPEG

VP9

Google

VP8

Google

VC-1

Microsoft Inc.

音频编码方式

将音频采样数据(PCM 等)压缩成音频码流,从而降低音频的数据量。 常用的音频编码方式有以下几种

名称

推出机构

AAC(Advanced Audio Coding)

MPEG

MP3

MPEG

WMV(Windows Media Audio)

Microsoft Inc

AC-3

Dolby Inc.

  • 宏块: 一个视频会有很多帧,每一帧就是一个画面,每一个画面都可以划分成很多宏块,类似下面的图,每一个格子就是一个宏块-

    利用上面的软件你可以分析出宏块的个数

    从上图可以分析出宏块 16 * 16 有549个,这个16 * 16一般来说是最大的宏块了,没有比这更大的了,有一个结论:宏块大的个数越多,视频压缩比越高

H264压缩技术主要采用了以下几种方法对视频数据进行压缩

  • 帧内编码
    I帧因为不参考其他帧,所以是帧内编码 第一次 根据帧内的像素趋于统一 而采用帧内预测编码技术帧内预测编码技术
    解决的是空域数据冗余问题,
    由当前帧中,已编码的部分来推测当前待编码的这一部分数据是什么,这里的推测模式也有好几种方法(如下图/下表),简称帧内预测
  • Android 获取所有视频 android 获取所有视频教程_码流_03


模式

描 述

模式0(垂直)

由A、B、C、D 垂直推出相应像素值

模式1(水平)

由I、J、K、L 水平推出相应像素值

模式2(DC)

由A~D 及I~L 平均值推出所有像素值

模式3(下左对角线)

由45°方向像素内插得出相应像素值

模式4(下右对角线)

由45°方向像素内插得出相应像素值

模式5(右垂直)

由26.6°方向像素值内插得出相应像素值

模式6(下水平)

由26.6°方向像素值内插得出相应像素值

模式7(左垂直)

由26.6° 方向像素值内插得出相应像素值

模式8(上水平)

由26.6° 方向像素值内插得出相应像素值

  • 帧间编码
    P帧,要前向参考,而B帧,则要进行双向参考,这两种,都属于帧间编码 使用以宏块为基础的运动补偿预测编码技术,从当前宏块从参考帧中产兆最佳匹配宏块
    运动补偿预测编码技术
    (运动估计与补偿),解决的是时域数据冗徐问题
    由这一帧的前(或后)一帧(或几帧)来推测当前待压缩的这一部分数据是什么。意思就是一个视频序列中连续的两帧,视屏中肯定大部分的宏块位置都相对固定,只有小部分的宏块会发生位移,前后两帧的差别很小,这时用帧间压缩的效果会比帧内压缩的效果好,即寻找前面的几帧里和当前宏块最相似的(这个过程叫运动搜索/运动估计),不需要重新编码,然后计算前宏块和寻找到的前几帧最相似宏块的差值就行了(简称残差),这样就只要编码残差就行了。在还原的时候,我们通过残差和原来的宏块推算出当前的块的过程叫做(运动补偿。算法有全搜索,菱形搜索法,三步搜索算法,新三步搜索算法,梯度下降搜索算法,运动矢量场自适应搜索算法等算法)
  • 整数离散余弦变换(DCT),将空间上的相关性变为频域上无关的数据然后进行量化。
  • CABAC压缩。

经过压缩后的帧分为:I帧,P帧和B帧:

  I帧:关键帧,采用帧内压缩技术。主要存储编码数据(top(上边界)+left(左边界)数据)+预测方向

  P帧:向前参考帧,在压缩时,只参考前面已经处理的帧。采用帧间压缩技术。主要存储运动矢量+差异数据   B帧:双向参考帧,在压缩时,它即参考前而的帧,又参考它后面的帧。采用帧间压缩技术,主要存储运动矢量

除了I/P/B帧外,还有图像序列GOP

  GOP:两个I帧之间是一个图像序列,在一个图像序列中只有一个I帧。GOP越长越好,这样I帧个数少,所以占用空间小。短视频,没有及时性的要求,一般GOP序列较长。直播,GOP较短,因为直播讲究及时性,秒开,所以I帧多,用户可以在短时间内收到关键I帧,更早的看到画面(但是这样会导致I帧过多,视频文件大,可以通过减少帧率来,每秒绘制的画面个数减少I帧的数量)。一般来说gop>200,已经算久的时间序列了。下图帧数一列,两个I帧相距的帧数插值就是对应的GOP序列长度。

  

Android 获取所有视频 android 获取所有视频教程_Androd_04

  • 环路滤波
    实际上是一个数字低筒滤波器,滤除不必要的高频信息
  • 块结构混合编码
    采用 “块结构的混合编码” 方案的编码标准。

H264编码分层

  • NAL层: (Network Abstraction Layer,视频数据网络抽象层)
    它的作用是H264只要在网络上传输,在传输的过程每个包以太网是1500字节. 而H264的帧往往会大于1500字节的.所以就要进行拆包. 将一个帧拆成多个包进行传输.所有的拆包或者组包都是通过NAL层去处理的。
  • VCL层:(Video Coding Layer,视频数据编码层)
    它的作用就是对视频原始数据进行压缩.
  • H264码流分层结构图
    注意:NALU主体里存放的数据类型根据NALU头里存放的type来定义的。列如type为5的时候,NALU主体里是一个slice数组(就是下图的情况),多个slice组成一帧图像
  • Android 获取所有视频 android 获取所有视频教程_码流_05


  1. A Annex格式数据,就是起始码 + Nal Unit 数据
  2. NAL Unit: NALU 头+NALU数据
  3. NALU 主体,是由切片组成.切片包括切片头+切片数据

看到这儿,你可能想问,宏块里的到底放的是啥数据?

存放的是YUV数据,但是并不是所有的,他只会存放第一行第一列的数据,其他的地方它会通过预测的方法来推算出来。

Android 获取所有视频 android 获取所有视频教程_Android 获取所有视频_06


那什么是YUV?

首先你了解图像的常用表示方法。通常我们表示颜色的时候,我们需要三原色(RGB)才可以渲染一个颜色,这三种都是一定要的,这样渲染一个颜色(像素)必须要有3个字节。这样占用空间就会很大,所以才产生了YUV编码YUV颜色编码采用的是明亮度和色度来指定像素的颜色。其中,Y表示明亮度(LUminance、Luma),而U和V表示色度(Chrominance、Chroma)。而色度又定义了颜色的两个方面:色调和饱和度。,下图(2*2区域内)就完全可以看出YUV减少了一般,大大的减少了带宽。

Android 获取所有视频 android 获取所有视频教程_数据_07


YUV的采样有许多种,常用的有444,422,420,411,上图的采样就是411,YUV的出现就是为了减少传输的数据量带宽的。

下面表格来源 简书 - 水木年华1987 - H264码流分析NALU Header说明

字段名

字段大小

字段说明

forbidden_bit

1bit

如果有语法冲突,则为 1。当网络识别此单元存在比特错误时,可将其设为 1,以便接收方丢掉该单元

nal_reference_bit

2bit

用来指示该NALU 的重要性等级。值越大,表示当前NALU越重要。具体大于0 时取何值,没有具体规定。

type

5bit

NAL类型

我们视频的第一帧一定是I帧,接下来与I帧相似程度极高 达到95%以上 编码成B帧,相似程度70%编码成P帧。首先输出I帧,当画面差异程度小于%5 时生成B帧但是他不是马上输出,会暂时在传输缓冲器里等待,等到画面差异程度大于30%时,会生成P帧,这时会通知传输缓冲器里的缓存的B帧一起输出(I P B B ...).接下来通过上面的软件来分析一下。

Android 获取所有视频 android 获取所有视频教程_Androd_04


从上图中,可以发现,从码流顺序一列,我们的I帧之后绝对是P帧然后才是B帧


首先,上面的编码出来的帧顺序是不能直接用来直接渲染的,不然会出现,后面的画面提前出现,会很奇怪。那我们需要怎么做?我们可以看到那张视频码流分析图(图片),我们看到 PTS(显示时间戳,单位纳秒),我们看到视频的第一帧并不是0,所以任何视频的第一帧并不是为0,而一定会有一定的时延。PTS的顺序是我们的播放顺序,所以可以根据PTS顺序解码播放。 还有一个点,每个帧之间是有分隔符的 0x 00 00 00 01 或者是 0x 00 00 01,但是我们单单只有这些I、B、P帧数据信息,我们还不能够进行画面的绘制,我们还需要宽高,编码等级,编码方式,帧率、码率等信息,所以在每一个I帧前,都会有SPS来存放这些配置信息,还会有一个PPS(存放宽高)或者等等,结构如下所示。

Android 获取所有视频 android 获取所有视频教程_Androd_09


但是,我们怎么来区分当前字段是SPS还是PPS还是I帧还是P帧呢?这是后我们就需要划分帧类型了。下面图可以发现 我们的帧类型其实有好多种,一共有32种,所以我们可以通过5位二进制来存放。

Android 获取所有视频 android 获取所有视频教程_Android 获取所有视频_10


接下来可以看下面一段H264 16进制码流

从之前的总结来看这个 67 是帧类型 ,那它是那种帧类型呢?我们来计算一下

公式:
(帧优先级) h->nal_ref_idc= 帧值 >> 5;
(帧类型) h->nal_unit_type= 帧值 & 0x1F;

看着公式就很容易计算了。

6:0110
7:0111
67 :0110 0111
又因为我们上面说帧类型只占五位,所以
0110 0111 & 0001 1111 = 0110 0111 & 0x1F = 0000 0111 = 7
从表里查出来 帧类型是序列参数集合,所以这就是SPS标志

数据的含义都标出来了,大家可以算算看。

Android 获取所有视频 android 获取所有视频教程_码流_11