Android Multimedia Codecs - H264编解码分析

目录

(一)、从零了解H264结构(概览)
1.0、前言
1.0、前言
1.1、原理
1.1.1. NAL Header
1.1.2. RBSP
1.2、从NALU出发了解H.264里面的专业词语
1.2.1. Slice(片)
1.2.2. 宏块(Macroblock)
1.2.3. 图像,场和帧
1.2.4. I,P,B帧与pts/dts
1.2.5. GOP
1.2.6 . IDR
1.3. 帧内预测和帧间预测
1.3. 1. 帧内预测(也叫帧内压缩)
1.3. 2. 帧间预测(也叫帧间压缩)

(二)、Android H264编码
2.1、SoftAVC::initEncoder()
2.2、SoftAVC::onQueueFilled()
2.3、ih264e_encode()

(三)、Android H264解码
3.1、SoftAVC::initDecoder()
3.2、SoftAVC::onQueueFilled()
3.3、WORD32 ih264d_video_decode()

(四)、libavc库分析
(五)、Others编解码
(六)、参考资料(特别感谢各位前辈的分析和图示):


(一)、从零了解H264结构(概览)

1.0、前言

为什么需要编码呢?比如当前屏幕是1280*720.一秒24张图片.那么我们一秒的视频数据是
1280 * 720(位像素) * 24(张) / 8(1字节8位)(结果:B) / 1024(结果:KB) / 1024 (结果:MB) = 2.64MB

一秒的数据有2.64MB数据量。1分钟就会有100多MB。这对用户来说真心是灾难。所以现在我们需要一种压缩方式减小数据的大小.在更低 比特率(bps)的情况下依然提供清晰的视频。

H264: H264/AVC是广泛采用的一种编码方式。
我们这边会带大家了解。从大到小排序依次是 序列,图像,片组,片,NALU,宏块,亚宏块,块,像素。


1.1、原理

H.264原始码流(裸流)是由一个接一个NALU组成,它的功能分为两层,VCL(视频编码层)和 NAL(网络提取层).

VCL(Video Coding Layer) + NAL(Network Abstraction Layer)
  1. VCL
    包括核心压缩引擎和块,宏块和片的语法级别定义,设计目标是尽可能地独立于网络进行高效的编码;
  2. NAL
    负责将VCL产生的比特字符串适配到各种各样的网络和多元环境中,覆盖了所有片级以上的语法级别。

在VCL进行数据传输或存储之前,这些编码的VCL数据,被映射或封装进NAL单元。(NALU)。

一个NALU = 一组对应于视频编码的NALU头部信息 + 一个原始字节序列负荷(RBSP,Raw Byte Sequence Payload).

如图所示,下图中的NALU的头 + RBSP 就相当于一个NALU(Nal Unit),每个单元都按独立的NALU传送。
H.264的结构全部都是以NALU为主,理解了NALU,就理解了H.264的结构。
一个原始的H.264 NALU 单元常由 [StartCode] [NALU Header] [NALU Payload] 三部分组成,其中 Start Code 用于标示这是一个NALU 单元的开始,必须是”00 00 00 01” 或”00 00 01”

在这里插入图片描述

1.1.1. NAL Header

由三部分组成,forbidden_bit(1bit),nal_reference_bit(2bits)(优先级),nal_unit_type(5bits)(类型)。
在这里插入图片描述
举例来说:

00 00 00 01 06: SEI信息
00 00 00 01 67: 0x67&0x1f = 0x07 :SPS
00 00 00 01 68: 0x68&0x1f = 0x08 :PPS
00 00 00 01 65: 0x65&0x1f = 0x05: IDR Slice

1.1.2. RBSP

在这里插入图片描述
图 6.69 RBSP 序列举例

在这里插入图片描述

SODB 与 RBSP
SODB 数据比特串 -> 是编码后的原始数据.
RBSP 原始字节序列载荷 -> 在原始编码数据的后面添加了 结尾比特。一个 bit“1”和若干比特“0”,以便字节对齐。

在这里插入图片描述

1.2、从NALU出发了解H.264里面的专业词语

在这里插入图片描述

1= n个片
1= n个宏块
1宏块 = 16x16yuv数据

1.2.1. Slice(片)

如图所示,NALU的主体中包含了Slice(片).
一个片 = Slice Header + Slice Data

片是H.264提出的新概念,通过编码图片后切分通过高效的方式整合出来的概念。
一张图片有一个或者多个片,而片由NALU装载并进行网络传输的。
但是NALU不一定是切片,这是充分不必要条件,因为 NALU 还有可能装载着其他用作描述视频的信息.

那么为什么要设置片呢?
设置片的目的是为了限制误码的扩散和传输,应使编码片相互间是独立的。某片的预测不能以其他片中的宏块为参考图像,这样某一片中的预测误差才不会传播到其他片中。

可以看到上图中,每个图像中,若干宏块(Macroblock)被排列成片。
一个视频图像可编程一个或更多个片,每片包含整数个宏块 (MB),每片至少包含一个宏块。

片有一下五种类型:
在这里插入图片描述

1.2.2. 宏块(Macroblock)

刚才在片中提到了宏块.那么什么是宏块呢?
宏块是视频信息的主要承载者。一个编码图像通常划分为多个宏块组成.包含着每一个像素的亮度和色度信息。
视频解码最主要的工作则是提供高效的方式从码流中获得宏块中像素阵列。

一个宏块 = 一个16*16的亮度像素 + 一个8×8Cb + 一个8×8Cr彩色像素块组成。
(YCbCr 是属于 YUV家族的一员,在YCbCr 中 Y 是指亮度分量,Cb 指蓝色色度分量,而 Cr 指红色色度分量)

在这里插入图片描述
分层结构,在 H.264 中,句法元素共被组织成 序列、图像、片、宏块、子宏块五个层次。
句法元素的分层结构有助于更有效地节省码流。

例如,再一个图像中,经常会在各个片之间有相同的数据,如果每个片都同时携带这些数据,势必会造成码流的浪费。更为有效的做法是将该图像的公共信息抽取出来,形成图像一级的句法元素,而在片级只携带该片自身独有的句法元素。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.2.3. 图像,场和帧

图像是个集合概念,顶 场、底场、帧都可以称为图像。
对于H.264 协议来说,我们平常所熟悉的那些称呼,例如: I 帧、P 帧、B帧等等,实际上都是我们把图像这个概念具体化和细小化了。

我们 在 H.264里提到的“帧”通常就是指不分场的图像;
视频的一场或一帧可用来产生一个编码图像。
一帧通常是一个完整的图像。当采集视频信号时,如果采用隔行扫描(奇.偶数行),则扫描下来的一帧图像就被分为了两个部分,这每一部分就被称为 [场],根据次序氛围: [顶场] 和 [底场]。
在这里插入图片描述


1.2.4. I,P,B帧与pts/dts

在这里插入图片描述
DTS与PTS的不同:
DTS主要用户视频的解码,在解码阶段使用。
PTS主要用于视频的同步和输出,在display的时候使用。再没有B frame的时候输出顺序一样


1.2.5. GOP

在这里插入图片描述
GOP是画面组,一个GOP是一组连续的画面。
GOP一般有两个数字,如M=3,N=12.
M制定I帧与P帧之间的距离,
N指定两个I帧之间的距离。

那么现在的GOP结构是 I BBP BBP BBP BB I ----> (M=3,N=12)

增大图片组能有效的减少编码后的视频体积,但是也会降低视频质量,至于怎么取舍,得看需求了


1.2.6 . IDR

一个序列的第一个图像叫做 IDR 图像(立即刷新图像),IDR 图像都是 I 帧图像。
I和IDR帧都使用帧内预测。I帧不用参考任何帧,但是之后的P帧和B帧是有可能参考这个I帧之前的帧的。IDR就不允许这样。

比如这种情况:
IDR1 P4 B2 B3 P7 B5 B6 I10 B8 B9 P13 B11 B12 P16 B14 B15 这里的B8可以跨过I10去参考P7

核心作用:
H.264 引入 IDR 图像是为了解码的重同步,当解码器解码到 IDR 图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样,如果前一个序列出现重大错误,在这里可以获得重新同步的机会。IDR图像之后的图像永远不会使用IDR之前的图像的数据来解码。


1.3. 帧内预测和帧间预测
1.3. 1. 帧内预测(也叫帧内压缩)

在这里插入图片描述
我们可以通过第 1、2、3、4、5 块的编码来推测和计算第 6 块的编码,因此就不需要对第 6 块进行编码了,从而压缩了第 6 块,节省了空间。


1.3. 2. 帧间预测(也叫帧间压缩)

在这里插入图片描述
可以看到前后两帧的差异其实是很小的,这时候用帧间压缩就很有意义。
这里涉及到几个重要的概念:块匹配,残差,运动搜索(运动估计), 运动补偿.

帧间压缩最常用的方式就是块匹配(Block Matching)。找找看前面已经编码的几帧里面,和我当前这个块最类似的一个块,这样我不用编码当前块的内容了,只需要编码当前块和我找到的块的差异(残差)即可。

找最像的块的过程叫运动搜索(Motion Search),又叫运动估计。
用残差和原来的块就能推算出当前块的过程叫运动补偿(Motion Compensation).



(二)、Android H264编码

在这里插入图片描述

由于博主的手机为Qcom平台,支持h264编解码,
博主保留了Qcom h264 Encoder ,将Decoder使用Google的,但分析主要还是以Google的H264 Encoder/Decoder 为主。

## \hardware\qcom\media\conf_files\msm8937\media_codecs_8937.xml

<!--
  Decoder capabilities for thorium
   _________________________________________________________________________
  | Codec    | W       H       fps     Mbps    MB/s    | Encode Secure-dec |
  |__________|_________________________________________|___________________|
  | h264     | 1920    1088    30      20      244800  |  Y       Y        |
  | hevc     | 1920    1088    30      20      244800  |  N       N        |
  | mpeg4    | 1920    1088    30      6       244800  |  Y       N        |
  | vp8      | 1920    1088    30      20      244800  |  N       N        |
  | div4/5/6 | 1920    1088    30      6       244800  |  N       N        |
  | h263     |  864     480    30      2       48600   |  Y       N        |
  |__________|_________________________________________|___________________|

-->

<!--
  Encoder capabilities for thorium
   ____________________________________________________
  | Codec    | W       H       fps     Mbps    MB/s    |
  |__________|_________________________________________|
  | h264     | 1920    1088    30      20      244800  |
  | mpeg4    | 864      480    30       2       48600  |
  | h263     | 864      480    30       2       48600  |
  |____________________________________________________|
-->

<MediaCodecs>
    <Include href="media_codecs_google_audio.xml" />
    <Include href="media_codecs_google_telephony.xml" />
    <Settings>
        <Setting name="max-video-encoder-input-buffers" value="9" />
    </Settings>
    <Encoders>
        <!-- Video Hardware  -->
        <MediaCodec name="OMX.qcom.video.encoder.avc" type="video/avc" >
            <Quirk name="requires-allocate-on-input-ports" />
            <Quirk name="requires-allocate-on-output-ports" />
            <Quirk name="requires-loaded-to-idle-after-allocation" />
            <Limit name="size" min="96x64" max="1920x1088" />
            <Limit name="alignment" value="2x2" />
            <Limit name="block-size" value="16x16" />
            <Limit name="blocks-per-second" min="1" max="244800" />
            <Limit name="bitrate" range="1-20000000" />
            <Limit name="concurrent-instances" max="8" />
        </MediaCodec>
        <!-- Video Software  -->
        <MediaCodec name="OMX.qcom.video.encoder.mpeg4sw" type="video/mp4v-es" >
            <Quirk name="requires-allocate-on-input-ports" />
            <Quirk name="requires-allocate-on-output-ports" />
            <Quirk name="requires-loaded-to-idle-after-allocation" />
            <Limit name="size" min="32x32" max="864x480" />
            <Limit name="alignment" value="2x2" />
            <Limit name="block-size" value="16x16" />
            <Limit name="blocks-per-second" min="1" max="48600" />
            <Limit name="bitrate" range="1-2000000" />
            <Limit name="concurrent-instances" max="1" />
        </MediaCodec>
        <MediaCodec name="OMX.qcom.video.encoder.h263sw" type="video/3gpp" >
            <Quirk name="requires-allocate-on-input-ports" />
            <Quirk name="requires-allocate-on-output-ports" />
            <Quirk name="requires-loaded-to-idle-after-allocation" />
            <Limit name="size" min="32x32" max="864x480" />
            <Limit name="alignment" value="4x4" />
            <Limit name="block-size" value="16x16" />
            <Limit name="blocks-per-second" min="1" max="48600" />
            <Limit name="bitrate" range="1-2000000" />
            <Limit name="concurrent-instances" max="1" />
        </MediaCodec>
    </Encoders>
    <Decoders>
        <!-- Audio Software  -->
        <MediaCodec name="OMX.qti.audio.decoder.flac" type="audio/flac" />
        <!-- Video Hardware  -->
        <MediaCodec name="OMX.qcom.video.decoder.avc" type="video/avc" >
            <Quirk name="requires-allocate-on-input-ports" />
            <Quirk name="requires-allocate-on-output-ports" />
            <Quirk name="defers-output-buffer-allocation" />
            <Limit name="size" min="64x64" max="1920x1088" />
            <Limit name="alignment" value="2x2" />
            <Limit name="block-size" value="16x16" />
            <Limit name="blocks-per-second" min="1" max="244800" />
            <Limit name="bitrate" range="1-20000000" />
            <Feature name="adaptive-playback" />
            <Limit name="concurrent-instances" max="8" />
        </MediaCodec>
        <MediaCodec name="OMX.qcom.video.decoder.vp8" type="video/x-vnd.on2.vp8" >
            <Quirk name="requires-allocate-on-input-ports" />
            <Quirk name="requires-allocate-on-output-ports" />
            <Quirk name="defers-output-buffer-allocation" />
            <Limit name="size" min="64x64" max="1920x1088" />
            <Limit name="alignment" value="2x2" />
            <Limit name="block-size" value="16x16" />
            <Limit name="blocks-per-second" min="1" max="244800" />
            <Limit name="bitrate" range="1-20000000" />
            <Feature name="adaptive-playback" />
            <Limit name="concurrent-instances" max="8" />
        </MediaCodec>
        <MediaCodec name="OMX.qcom.video.decoder.hevc" type="video/hevc" >
            <Quirk name="requires-allocate-on-input-ports" />
            <Quirk name="requires-allocate-on-output-ports" />
            <Quirk name="defers-output-buffer-allocation" />
            <Limit name="size" min="64x64" max="1920x1088" />
            <Limit name="alignment" value="2x2" />
            <Limit name="block-size" value="16x16" />
            <Limit name="blocks-per-second" min="1" max="244800" />
            <Limit name="bitrate" range="1-20000000" />
            <Feature name="adaptive-playback" />
            <Limit name="concurrent-instances" max="8" />
        </MediaCodec>
        <MediaCodec name="OMX.qti.video.decoder.divxsw" type="video/divx" >
            <Quirk name="requires-allocate-on-input-ports" />
            <Quirk name="requires-allocate-on-output-ports" />
            <Limit name="size" min="16x16" max="1920x1088" />
            <Limit name="alignment" value="2x2" />
            <Limit name="block-size" value="16x16" />
            <Limit name="blocks-per-second" min="1" max="244800" />
            <Limit name="bitrate" range="1-6000000" />
            <Limit name="concurrent-instances" max="1" />
        </MediaCodec>
        <MediaCodec name="OMX.qti.video.decoder.divx4sw" type="video/divx4" >
            <Quirk name="requires-allocate-on-input-ports" />
            <Quirk name="requires-allocate-on-output-ports" />
            <Limit name="size" min="16x16" max="1920x1088" />
            <Limit name="alignment" value="2x2" />
            <Limit name="block-size" value="16x16" />
            <Limit name="blocks-per-second" min="1" max="244800" />
            <Limit name="bitrate" range="1-6000000" />
            <Limit name="concurrent-instances" max="1" />
        </MediaCodec>
        <MediaCodec name="OMX.qti.video.decoder.mpeg4sw">
            <Type name="video/mp4v-es" />
            <Type name="video/mp4v-esdp" />
            <Quirk name="requires-allocate-on-input-ports" />
            <Quirk name="requires-allocate-on-output-ports" />
            <Limit name="size" min="16x16" max="1920x1088" />
            <Limit name="alignment" value="2x2" />
            <Limit name="block-size" value="16x16" />
            <Limit name="blocks-per-second" min="1" max="244800" />
            <Limit name="bitrate" range="1-6000000" />
            <Limit name="concurrent-instances" max="1" />
        </MediaCodec>
        <MediaCodec name="OMX.qti.video.decoder.h263sw" type="video/3gpp" >
            <Quirk name="requires-allocate-on-input-ports" />
            <Quirk name="requires-allocate-on-output-ports" />
            <Limit name="size" min="16x16" max="864x480" />
            <Limit name="alignment" value="4x4" />
            <Limit name="block-size" value="16x16" />
            <Limit name="blocks-per-second" min="1" max="48600" />
            <Limit name="bitrate" range="1-2000000" />
            <Limit name="concurrent-instances" max="1" />
        </MediaCodec>
    </Decoders>
    <Include href="media_codecs_google_video.xml" />
</MediaCodecs>

可以看出支持很多种格式,接下来我们主要以 “OMX.qcom.video.decoder.avc” 为主:


2.1、SoftAVC::initEncoder()

参考:
从零了解H264结构
Android Multimedia Codecs - H264编解码分析

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值