Android 音视频编解码(三) -- 视频编码和H264格式原理讲解

news2024/11/6 3:12:27

Android 音视频编解码(一) – MediaCodec 初探
Android 音视频编解码(二) – MediaCodec 解码(同步和异步)

前面学习了 MediaCodec 的基本原理,以及如何解码,在学习MediaCodec 编码之前,先来学习视频是如何编码的,以及最常用的 H264。
这一章偏文字理论,但非常重要,希望沉下心来慢慢看。

说到视频,第一印象就是占内存,我们知道视频是由一连串图像组成的,假设我们现在有一个视频,1080p(1920x1080) ,帧率是25帧,时长是2个小时,如果不进行压缩的话,它的大小为 1920x1080x25x2x60x60x1.5≈260.7G 。如果我们不对视频进行压缩的话,任何存储设备都存储不了几部电影,更别说在线电影了,带宽根本撑不住。

一. 视频编码原理

前面说到,视频是一帧帧的图像,因此编码也是对图像的编码,而图像一般是 RGB,即红蓝绿三个分量来组合成所有颜色。但是 RGB 对编码不太友好,因此通常会使用 YUV 的图像格式来进行编码,Y 表示亮度,UV表示色彩空间。人眼对亮度信息敏感,对色度信息稍弱,因此我们对图像进行不同的编码。具体YUV与RGB的信息,可以查看Android OpenGL ES 学习(十一) –渲染YUV视频以及视频抖音特效

对于每一帧图像,又可以划分成一个个块来编码,不同编码类型对块的解释不通,但基本相同,如 H264 中图像块被叫做宏块,宏块的大小一般为 16x16(H264),32x32(H265,VP9)等。

二. 编码模式

图像一般具备数据冗余的,因此我们可以根据此特性去做一些操作,减少图像的数据量,比如 Bitmap 压缩,我们可以修改它的缩放因此,适配空间大小,达到压缩空间省内存的效果,视频同样如此,去除冗余的方式有:

  • 空间冗余:相邻像素具备相似性
  • 时间冗余:相邻帧的内容往往很相似,两张图像变化很小,相似性很高
  • 视觉冗余:人眼睛对高频信息敏感度低于信息,比如眼睫毛,因此可以去除高频信息
  • 信息熵冗余:我们一般会使用 Zip 等压缩工具去压缩文件,将文件大小减小,这个对于
    图像来说也是可以做的。

视频编码就是通过上述4中冗余来达到视频压缩的目的。更多视频压缩信息,可以参考极客学院-视频怎么编码的
而 H264 就是针对以上冗余信息进行算法编程,达到视频压缩的。

三. H264 简介

视频编码标准其实有很多,而大名鼎鼎的就是 H264 了,可以说是最常用,最普遍的视频编码格式。其实除了H264,还有H265,H264和H265都是国际标准化组织(ISO)和国际电信联盟(ITU)开发的编码标准,而VP8、VP9 和 AV1是谷歌开发的编码标准,H264 和 H265 是需要专利费的,所以VP8、VP9 和 AV1(都是免费)也是谷歌为了对抗他们高昂专利费而开发出来的。

上面讲到了视频编码的原理,这里我们通过H264来了解视频编码中的码流结构,以及H264 是如何解决4中冗余信息的。它的主要步骤为:

  1. 帧内预测:解决空间冗余问题
  2. 帧间预测:解决时间冗余问题
  3. DCT变化和量化:解决视觉冗余问题

3.1 帧内预测 - 如何减少空间冗余

前面说到,帧内预测是为了解决空间冗余问题,我们知道,一幅图像中相邻像素的亮度和色度信息是比较接近的,并且亮度和色度信息也是逐渐变化的,不太会出现突变。也就是说,图像具有空间相关性,帧内预测就是按照这个特点来进行的。

比如这张蓝色,它相邻的像素的亮度和色彩就是相似。
在这里插入图片描述
所以帧内预测就是利用已经编码后的相邻像素的值来预测待编码的像素值,从而达到减少空间冗余的目的。

你可能会奇怪,已经编码过的像素,不是变成码流了吗,怎么还能去预测待编码的像素的。其实这个已经编码的像素是会重建成重建像素,用做之后待编码快的参考像素的。

不同块大小的帧内预测模式

这里不打算深入讲,因为要深入讲的话,得重新开一章才行,这里我们简单了解一下即可。
前面说到,视频编码是以块为单位的。在H264的标准里面,块可以分为宏块和子块,宏块的大小是 16x16 (YUV 4:2:0 ,Y 为16x16,UV 为 8x8),在帧内预测中,图像除了相似的地方,还有更细节的部分,如蓝色天空的一朵云,因此,Y 宏块还可以分成16个 4x4 的子块
在这里插入图片描述
Y 与 UV 的预测是分开进行的,因此,我们可以总结三个点:

  1. 宏块大小为 16x16,Y 为16x16,UV 为 8x8
  2. Y 和 UV 块是分开独立进行预测的,即Y会参考已编码Y块的像素,UV 会参考已编码的UV块像素
  3. 16 x 16 的Y块可以继续划分成 16 个 4 x 4 的子块。

所以,我们在实际帧内预测的时候就会分为:4 x 4 Y块的预测、16 x 16 Y块的预
测、8 x 8 UV块的预测.

实际上,4x4的字块中,帧内预测的模式最多,共有8个,其中有8个方向模式和一种DC模式,且方向模式指的是预测是有方向角度的。这里涉及到具体的编码算法,就不展开,若感兴趣,可参考极客学院-帧内预测

3.2 帧间预测 - 如何减少时间冗余

这里也不深入,主要连接P帧,B 帧的概念即可

前面说到,时间冗余是指相邻两帧画面相似的地方比较多,前后两帧图像变化很小,比如帧率30,1秒内有30张图像,如果是连续变化,两帧的图像其实变化往往很小,这就是相关性,帧间预测,就是利用这个特点来进行算法编码的。即:

通过在已经编码的帧里面找到一个块来预测等待编码块的像素,从而达到减少时间冗余的目的。

注意这里和帧内预测的区别:

  1. 帧内预测是同张图像,等待编码的块去参考已编码的块像素;而帧间预测,则是等待编码的图像去参考已经编码的图像像素块
  2. 帧间预测可以在多个已经编码的图像里,去寻找参考像素块,也可以是单个。
  3. 帧间预测,即可以参考前面的图像,也可以参考后面的图像(如果参考后面的图像,后面的图像也需要提前编码)。只参考前面的帧,我们成为前向参考帧 - P帧了;参考后面的帧或者前后两帧图像的帧,我们称之双向参考帧 - B帧。

这里以 H264 的标准来讲讲 P 帧的帧间预测过程,当然也是了解个大概,更详细的,参考极客学院 - 帧间预测

其实阵间预测也是通过块作为参考的,我们会在已经编码的帧里面找到一个块来作为预测块,这个已经编码的帧称之为参考帧。

那问题来了,前面说到帧间预测是图像与图像之间的参考,如果两张图像一模一样,还好理解一点,如果是有变化的呢?比如下面这张图:
在这里插入图片描述

可以看到,树木是不变的,但是汽车移动了,这里是怎么计算的?

从编码的角度来看,汽车是不变的,变的是运动轨迹,因此,为了表示这种变化,可以使用运动矢量来表示编码帧中编码块和参考帧中,预测块之间的位置的差值

比如说上面两幅图像中,小车从前一幅图像中的(32,80)的坐标位置,变化到当前图像
(80,80)的位置,向前行驶了 48 个像素。很明显,如果我们选用(32,80)这个块作
为当前(80,80)这个编码块的预测块的话,是不是就可以得到全为 0 像素的残差块了?
这是因为小车本身是没有变化的,变化的只是小车的位置。

而这里的难点,在于如何找到这个最佳的参考帧,这里就涉及到运动算法。你可能会箱单,既然都分成块了,那逐行对比即可,即全局搜索,好处就是啥都能对比到,但是缺点就是一帧算下来,耗时过长,因此还需要更快速的算法。这里就不细讲,参考极客学院 - 帧间预测

3.2 变换量化 – 如何减少视觉冗余

通过前面的知识,已经知道 ,通过帧内编码可以去除空间冗余,通过帧间编码可以去除时间冗余,而为了分离图像块的高频和低频信息从而去除视觉冗余,我们需要做 DCT 变换和量化。

其实DCT 变换,就是离散余弦变化,它可以将你扛到的图像转到成数据,并能很好的去除相关性。
经过 DCT 变化之后,低频信息会集中出现在左上角,高频信息则分散到其他地方,而人又对高频信息不敏感,因此图像在经过 DCT 变化后,再去除一些高频信息,就可以达到压缩的目的。

而高频信息幅度值比较小,因此还可以使用量化的方式,即除法操作,将高频信息幅度变小,达到压缩的目的。

在这里插入图片描述
更多细节,参考极客学院-变化量化

四. H264

前面学习了视频编码的原理,现在来看H264,估计会简单很多。先来看看结构:
在这里插入图片描述
在上面的结构中,一个视频编码后的数据叫做帧,一帧由1个片 (slice)或多个片组成,一个片 又可以分成多个宏块 (MB),一个宏块又可以由多个子块组成。而宏块是H264的基本单位,前面也说到,编码基本是基于块去做参考的。

4.1 帧类型

可能平时你也接触过,H264的帧类型,可以分为 I帧,P帧,和B帧。前面的视频编码原理讲到,为了减少空间冗余和时间冗余,有涉及到帧内预测和帧间预测。
再回顾一下,帧内预测,是参考自身的宏块,帧间预测是不同帧之间的参考,即:

  • 帧内预测不需要参考已编码帧,对已编码帧是没有依赖的,并可以自行完成编码
    和解码。
  • 帧间预测是需要参考已编码帧的,并对已编码帧具有依赖性。帧间预测需要参
    考已经编码好了的帧内编码帧或者帧间编码帧。并且,帧间编码帧又可以分为只参考前面
    帧的前向编码帧,和既可以参考前面帧又可以参考后面帧的双向编码帧。

因此,我们可以得出两者之间的关系:

帧类型预测方式参考帧优点缺点
I帧帧内编码帧只帧内预测能独立编码压缩率小
P帧前向编码帧可以进行帧内预测和帧内预测参考前面的I帧或P帧压缩率比I帧高必需参考正确的帧才能正确解码
B帧双向编码帧可以进行帧内预测和帧内预测参考前面或者后面已经编码的I帧或者P帧压缩率最高需要缓存帧,参考后面的帧,延时高

可以看到,如果编解码中,如果前面一个帧出现了错误,那么P帧和B帧肯定也会出现错误,虽然B帧也可以参考后面的帧,但一般很少用到B帧,比如只有P帧,错误就会一直传递,为了避免这种情况,就有一种特殊的I帧,叫 IDR 帧,也叫立即刷新帧

4.2 IDR 帧

在H264的标砖中,IDR 后的帧不再参考前面的帧,这样如果一帧编码错误后,如果此时有IDR帧过来,后面的帧就只会参考这个IDR帧,就截断了错误,后面就可以正常编解码了。
所以,一般都会使用 IDR帧,而不是普通I帧。

4.3 GOP - 关键帧间隔

从一个 IDR 帧开始到下一个 IDR 帧的前一帧为止,这里面包含的 IDR 帧、普通 I 帧、P 帧和 B 帧,我们称为一个 GOP(图像组)。
在这里插入图片描述
所以,一个GOP的大小,是由IDR的间隔决定的,这个间隔,也叫做 关键帧间隔,关键帧越大,则IDR相关越远,GOP越大,反之亦然。
当然GOP不是越大越好,也不是越小越好,比如直播场景,由于网络等因素,你可能GOP小一些比较好,而局域网投屏,网络基本稳定,所以GOP可以设置大一些。

4.4 码流结构

前面也说到一帧图像可以划分成一个或多个 Slice,而一个 Slice 包含多个宏块,且一个宏块又可以划分成多个不同尺寸的子块。这里我们一层层去剥开它。

4.4.1 码流格式

H264 有两种格式,一种是Annexb,另一种是MP4,相同是他们都有起始码,而它们的不同是:

  • Annexb: 起始码是三个字节,即 00 00 01
  • MP4: 起始码是四个字节,即 00 00 00 01

因此,我们在解析H264文件时,要注意头部信息的起始码。

4.4.2 SPS/ PPS

在H264码流中,有两个很重要的信息,即sps和pps:

  • sps:主要包含图像的宽高,YUV 格式和位深等基本信息
  • pps:主要包含熵编码类型,基础QP和最大参考帧等编码信息

如果一段码流中,缺失了这两个部分,之后的I帧,P帧,B帧都无办法解码。

4.4.3 NALU

现在我们知道了,H264 的码流中, SPS、PPS、I 帧、P帧和 B 帧。由于帧又可以划分成一个或多个 Slice。因此,帧在码流中实际上是以 Slice 的形式呈现的。

所以,H264 的码流主要是由 SPS、PPS、I Slice、P Slice和B Slice 组成
的。如下图所示:
在这里插入图片描述
那如何在码流中区分这些数据呢?
为了解决这个问题,H264设计了NALU (网络抽象层单元),sps 是一个 NALU,pps也是一个 NALU单元,每一个片(Slice) 也是一个 NALU 。
它的结构组成是:

  • NALU : 一个字节的 Nalu Header 加若干个字节的Nalu data 组成吗
    – 如果这个NALU 是 Slice,它的data ,有可以拆分成 slice Header 和 Slice Data
    – 而Slice data 由可以拆分成 MB Data,如下图:
    在这里插入图片描述
    这里主要看 NALU Header ,它是一个字节,结构如下:
    在这里插入图片描述
    这里关注 type类型,它占5个bit,表示 NALU 的类型,取值如下:
    在这里插入图片描述
    有了 NALU Type 表示,我们就可以知道了几个重要的类型,如sps 是7,pps 是8,而IDR 帧是5等等:
    在这里插入图片描述
    通过解析一段H264的码流,我们也可以看到这些信息:
    SPS:
    在这里插入图片描述
    PPS:
    在这里插入图片描述
    IDR:
    在这里插入图片描述
    因此,我们解析H264 文件时,就可以通过起始码,00 00 01 x 或00 00 00 001 x 的方式去取出每一帧的数据,再喂给解码器,实现解码的效果。

参考:
极客学院-帧内预测
极客学院 - 帧间预测
极客学院-变化量化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/128286.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

亚马逊vs Starday :做跨境电商生意,从哪里开始?

据有关数据统计,中国跨境电商进出口五年增长近十倍,在一众行业面前脱颖而出,成为我国对外贸易新的增长极,然而也正是这样的趋势,使得许多原本从事电商行业的卖家和资本纷纷闻风而动,想要进入市场分一杯羹&a…

3d打印的翘边问题

如何解决3D打印翘边问题 翘边是3D打印中常见的问题之一。为什么在打印的过程中会遇到翘边呢?主要是因为塑料的热胀冷缩,从喷嘴挤出来的塑料在冷却时候会收缩,进而导致模型边缘或者两头翘了起来与平台出现分离。那么如何避免或解决翘边问题呢…

“消费盲返”爆火,一个月能赚1000w?

寒冬已至,疫情还是在断断续续的复发,很多城市也受到严重的影响,封城的通告一出,无疑是给不少的实体企业增添了相当大的噩耗打击,这时候更为磨炼实体企业和创业人看待事情的立场,有些人会觉得疫情的袭来什么…

SSM框架学习记录-SpringBoot_day01

1.SpringBoot简介 SpringBoot是用来简化Spring应用的初始搭建以及开发过程 先回顾一下SpringMVC的开发过程&#xff1a; 创建工程&#xff0c;并在pom.xml配置文件中配置所依赖的坐标&#xff1a; <dependencies><dependency><groupId>javax.servlet</gro…

阶段性回顾(3)

1. 学习指针必须得了解清楚内存&#xff0c;而内存到底是什么东西呢&#xff1f;内存就是电脑上的存储设备&#xff08;除了内存之外&#xff0c;还有硬盘&#xff0c;寄存器等等&#xff09;&#xff0c;那内存到底是来干啥的呢&#xff1f;程序运行的时候会载入到内存当中&am…

Fast Report .NET 2023.1.7-2022-最后版本

通过使用 Fast Report .NET&#xff0c;用户可以构建和创建本质上独立的应用程序以及报表。网。换句话说&#xff0c;这意味着 Fast Report .NET 可以作为所有用户的独立报告工具独立使用。它可以包括一个强大的可视化报告&#xff0c;用于创建和修改报告的过程。用户应用程序可…

Selenium Webdriver 实现原理详解-手工用Postman调用webdriver执行UI测试

目录 1. Selenium 概述 2. 术语解释&#xff1a; 3. Selenium WebDriver 实现原理 4. 安装selenium 客户端&#xff0c;浏览器&#xff0c;驱动 4.1 安装selenium client lib 4.2 安装浏览器和浏览器驱动 4.3 例子代码 4.4 省略浏览器驱动的方法 4.5 测试代码与Webdr…

Linux近期补充

Linux近期补充Linux命令的近期补充Linux命令的近期补充 1.本地服务器链接远端服务器 命令 ssh 远端服务器ip 如 ssh 121.5.151.236 会弹出 登录框 自己输入密码即可2.当前位置 pwd3.查看网络设备 ifconfig4.查看服务器内存 free -h可以看到还有2.3G内存可以用 5.查看磁盘…

ES学习1~23(ECMAcript相关介绍+ECMASript 6新特性)

1 ECMAcript相关介绍 1.1 什么是ECMA ECMA(European Computer Manufacturers Association)中文名称为欧洲计算机制造商协会&#xff0c;这个组织的目标是评估、开发和认可电信和计算机标准。1994年后该组织改名为Ecma国际。 1.2 什么是ECMScript ECMAScript是由Ecma国际通过…

外网远程访问本地MySQL数据库【cpolar内网穿透】

作为网站运行必备组件之一的数据库&#xff0c;免不了随时对其进行管理维护。若我们没有在安装数据库的电脑旁&#xff0c;但又需要立即对数据库进行管理时&#xff0c;应该如何处理&#xff1f;这时我们可以使用cpolar对内网进行穿透&#xff0c;远程管理和操作MySQL数据库。现…

三叠云甘特图新亮点,可翻页查看数据啦

表单管理 路径 表单 >> 表单设计 功能简介 1.「甘特视图」新增“翻页”功能&#xff0c;用户可以通过翻页查阅更多的数据。 2. 滑动超过显示区域时显示“标记点”&#xff0c;用户可以通过点击标记点快速定位到相应的数据。 3.「列表视图」条件着色功能,修复“系统字…

Linux进程管理

1.什么是程序&#xff1f;具有执行代码和执行权限的文本文件 2.什么是进程&#xff1f;是已启动的可执行程序的运行实例 3.进程的生命周期&#xff1a;由系统程序fork出来的子程序&#xff0c;具备一定的父资源&#xff0c;直到运行完毕 4.进程有哪些组成部分&#xff1f; …

操作系统真相还原_第3章:实模式下跳转指令补充

文章目录数据类型伪指令ret指令call指令jmp指令标志寄存器flags与条件转移数据类型伪指令 byte&#xff1a;字节 word&#xff1a;字 dword&#xff1a;双字 qword&#xff1a;四字 跳转指令指定目标操作数大小 short&#xff1a;字节 near&#xff1a;字 far&#xff1a;双字…

Linux环境下内存泄露检测

linux下内存泄漏检测工具valgrind 该工具可以检测下列与内存相关的问题 : 未释放内存的使用对释放后内存的读/写对已分配内存块尾部的读/写内存泄露不匹配的使用malloc/new/new[] 和 free/delete/delete[]重复释放内存Memcheck。这是valgrind应用最广泛的工具&#xff0c;一个…

vscode插件(个人正在用的)

插件目录any-ruleAuto Close TagAuto Rename Tagbackground-coverChinese (Simplified) (简体中文) Language Pack for Visual Studio CodeDebugger for JavaError LensESLintExtension Pack for JavaImage previewIntelliCodeIntelliCode API Usage ExamplesLanguage Support …

对耳朵伤害最小的耳机类型有哪些,对耳朵伤害小的骨传导耳机

现在耳机的佩戴方式越来越多样化&#xff0c;完美最常见的入耳式耳机&#xff0c;长时间佩戴会导致耳朵肿胀、不适&#xff0c;如果换成骨传导耳机就不会有这个困扰了&#xff0c;骨传导耳机利用开放式听声音的方式&#xff0c;可以在极大程度上减少对听力的损害&#xff0c;从…

Docsify保姆级教程

官网 Docsify官网地址 docsify.js.org/ 前言 动态生成文档网站的一款热门工具&#xff01;网上很多关于动态生成文档的工具&#xff01;今天我们的主角是Docsify&#xff01; Docsify的优点 无需构建&#xff0c;写完文档直接发布容易使用并且轻量智能的全文搜索丰富的 AP…

很好用的Unity编辑器扩展工具 Odin Inspector教程

Odin Inspector是什么&#xff1f; Odin Inspector是Unity的一个插件&#xff0c;让您可以享受拥有强大&#xff0c;自定义和用户友好编辑器的所有工作流程优势&#xff0c;而无需编写任何自定义编辑器代码。 Odin包含许多功能&#xff0c;例如Static Inspector&#xff0c;Pr…

Mybatis框架(一)初识Mybatis之Mybatis简单使用

本文是本人专栏【Java开发后端系列框架】里的文章&#xff0c;文章根据各框架官网与网上资料加上本人工作经验&#xff0c;进行修改总结发布在这个专栏&#xff0c;主要目的是用于自我提升&#xff0c;不用于获取利益。如果系列文章能到帮到您本人将感到荣幸&#xff0c;如果有…

修改配置文件解决matplotlib中文与正负号乱码问题

步骤如下&#xff1a; 1、 找到配置文件matplotlibrc 不管是啥系统&#xff0c;都可以通过以下方式查找matplotlibrc所在的文件夹&#xff08;可以在终端或者编译器中运行以下代码&#xff09; import matplotlib# 查找字体路径 print(matplotlib.matplotlib_fname())结果&am…