RGB 色彩空间
我们平时接触最多的色彩空间 是 RGB
,上学的时候就学过三原色,所有的颜色可以通过三原色产生,这 三原色 就是 Red (红),Green(绿),Blue(蓝)。
做 web 前端的同学也会经常用过 RGB 来指定 页面元素的 颜色。RGB 格式目前主要有两类:
像素格式,这是我们比较常用的格式,R,G,B 分别分开,用N个位来表示。例如 RGB24格式 中 R 占 8位,G 占 8位,B 占8 位,所以一个像素占 24位。这种格式可以混合生成 256 * 256 * 256 = 16,777,216 种颜色,但缺点是占用空间大。
索引格式,RGB 的值是一个索引,不是真正的颜色值,例如 RGB 的值 占 1位,那只有两个值 0 跟 1,通常用于黑白颜色,这种情况下 一个像素只占 1位,大大节省了空间。0 跟 1 到底是什么颜色,是通过 索引表(也叫调色板)来定位的,不一定是 黑白,也可以是其他的颜色。所以叫索引格式。
只占 1 位的 RGB 成为 RGB1
,还有 RGB4
占 4 位,索引表有 16 种颜色, RGB8
占 8 位,索引表有 256 种颜色。
扩展知识:不需要 索引表/调色板 的 RGB 模式 称为 真彩色。
YUV 色彩空间
虽然日常生活中接触得比较多的是 RGB
色彩空间,但是还有另一种比较常用的色彩空间 是 YUV
。
RGB
色彩空间更适合图像采集和显示, YUV
空间用于编码和存储则比较好。
在存储和编码之前,RGB
图像要转换为 YUV
图像,而 YUV
图像在 显示之前通常有必要转换回 RGB
。
这里显示的时候 YUV
转成 RGB
通常是硬件或者软件内部做了,我们写代码开发的时候 这个 YUV
转 RGB
显示到屏幕这个过程通常是透明的。
无论是 RGB 还是 YUV ,他们都是 表达 色彩信息的一种方式。
可以把 RGB 跟 YUV 色彩空间看成是 一个 xyz轴 的立方形,一个三维的空间。
- 首先,讲一下为什么 YUV 色彩空间 在音视频开发,编码压缩领域更加常用,这个涉及到 HVS (Human Visual System)人类视觉系统 对 色彩空间的感知能力。
视觉心理学研究表明,人的视觉系统对光的感知程度可以用两个属性来描述:亮度(luminance)跟 色度(chrominance),这里的色度也叫做 饱和度或彩度,总之 色度的叫法有很多,要注意上下文来区分语义。
- 然后 色度感知 包含两个维度:色调(Hue)和 色饱和度(saturation)。色调是由光波的峰值定义的,描述的是光的颜色。色饱和度是由光波的谱宽定义的,描述的是光的纯度。
因此 HVS 对色彩的感知主要有 3个属性:亮度(luminance),色调(Hue)和 色饱和度(saturation)。也就是 YUV 色彩空间,Y 代表 亮度,U代表色调,V代表色饱和度。
- 经过大量研究实验表明,视觉系统 对 色度 的敏感度 是远小于 亮度的。所以可以对 色度 采用更小的采样率来压缩数据,对亮度采用正常的采样率即可,这样压缩数据不会对视觉体验产生太大的影响。简单来说就是用更少的数据/信息来表达 色度(chroma),用更多的数据/信息来表达 亮度(luminance)。
YUV 其实目前有 三种分类
YIQ 适用于NTSC彩色电视制式
YUV 适用于PAL和SECAM彩色电视制式
YCbCr 适用于计算机用的显示器
我们做互联网音视频开发, 一般说的 YUV
是 指 YCbCr
,U 就是 Cb,V 就是 Cr。
大家经常在一些音视频书籍看到 YCbCr
,把它当成是 YUV
就行。实际上 YCbCr
才是比较准确的术语,JPEG、MPEG 标准 用的也是 YCbCr 。
后面说讲的 YUV 也是指 YCbCr ,不是指 用于PAL和SECAM彩色电视 的 YUV。、
YUV数据分析
在 前文中,已经知道 YUV 色彩空间非常适合存储跟传输,本文就用实战的方式来演示一下,YUV 在存储跟传输领域有哪些优点。
- 首先 YUV 有 3 种采样模式:
4:4:4 ,一个像素 占 3 个字节。
4:2:2,平均一个像素占 2 个字节。
4:2:0,平均一个像素占 1.5 个字节。
Yuv 图片可用以下 FFmpeg 命令转出来的,
ffmpeg -i juren.jpg -s 1920*1080 -pix_fmt yuvj444p juren_yuv_444.yuv
ffmpeg -i juren.jpg -s 1920*1080 -pix_fmt yuvj422p juren_yuv_422.yuv
ffmpeg -i juren.jpg -s 1920*1080 -pix_fmt yuvj420p juren_yuv_420.yuv
2
3
重点:
YUV420
比YUV444
少了一半数据,视觉体验几乎没有变化。
由于大多数视频编码使用的是 YUV420
,所以我们经常说的 视频分辨率,是指他的亮度分辨率,因为只有亮度才是铺满的,UV 不是。
YUV 格式有3大类
- planner:平面格式, 先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。
注意:这里的连续存储,不是一行像素里面连续存储,是整张图片的连续存储,例如 本文的 juren_yuv_444.yuv 图片是 6075kb 大小,那第 1~ 2025kb 都是 Y 的数据, 2026 ~ 4050kb 都是 像素点的 U 数据,以此类推。
semi-Planar:半平面的YUV格式,Y分量单独存储,但是UV分量交叉存储。
packed:每个像素点的Y,U,V是连续交错存储的。
上面 FFmpeg 命令把 jpeg 转 yuv 的时候,使用的格式是 yuvj444p,后面的 p 就代表 planner,所以 本文只关注 planner格式,其他格式不讲。
yuv422p 的存储格式:422 比 444 采样格式 少了 三分之一的数据量 所以,422 格式,是第一第二个像素共享 一组UV,第三第四像素共享 一组 UV,以此类推
数据量最少的 YUV420 格式:这个格式也是应用最广泛的的,视频会议,数字电视,DVD,都用的 YUV420 格式
编码压缩介绍
从前文我们知道,YUV420
已经比 YUV444
数据少了一半,而且体验几乎感受不到差别。
动画看起来流畅,需要一秒播放 24 张图片,1个小时的电影大小如下: 3x24x60x60=259200
因此,1小时的YUV420 的电影,需要 253G 空间来存储,所以 即使 YUV420 这种 HVS 技巧,已经让数据量少了一半,但是还是不能满足当前硬件环境的存储跟传输的需求,所以需要进一步压缩。
在音视频领域,压缩被称为 encode(编码)
,因为 压缩这个术语比较通用,字符串文本也能压缩,其他的二进制文件也能压缩。音视频里面的压缩算法 比较有行业自身的特点,所以叫 编码
。
常见的压缩标准有以下:
JPEG 标准,用于单张图片压缩。标准文档 ISO/IEC 10918-1
H.262 标准,用于视频编解码,标准文档 ISO/IEC 13818-2
H.263 标准,用于视频编解码。
H.264 标准,在 2022年 目前是应用非常广泛的标准。
VP9,Google 出的视频编解码标准。
AVS,中国的视频压缩标准。
视频编码系统中,用到的主流技术有以下:
预测
变换
量化
熵编码
环路滤波
YUV420 这些数据,进入 编码系统之后,从编码系统出了的数据会少很多,具体少多少,H264 的压缩比 可以是 102:1
,也就是上面那个 253G 的YUV420电影,压缩之后只需 2.5G 存储空间。
封装格式介绍
在讲封装格式之前,先讲一下什么是文件格式,计算机存储任何东西都是二进制的,ASCII码字符串也是二进制的,一个 ASCII 字符占1个字节。int 类型存储在文件也是二进制的,一个 int 占 4个字节。
大家经常使用的文本编辑器,如果打开 ASCII码文件是显示正常的,但是如果打开存储 int 的二进制文件,就会显示很奇怪,因为文本编辑器把 int 4字节认为是 4 个 ASCII码了。
不只是 int ,还有很多数据类型也可以存储在文件里面。因此在这里,我把文件格式分为两种:
字符串存储,直接用普通的文本编辑器,notepad++,sublime,打开文件即可查看内容。
二进制存储,需要专门的软件分析文件的内容。
实际上,音视频封装格式 是 属于 文件格式的二进制存储的。音视频封装格式的特殊之处在于他的英文是 mux(复用)。
学过 TCP 的同学应该听过 多路复用,音视频封装格式也是同理,可以把多个流数据合并到一个文件里面,这就是封装格式,这就是 mux。
因为编码压缩系统 输出 的是单路流,视频编码系统输出视频流,音频编码系统输出 音频流,封装格式就是可以把多个流合在一个文件里面。
FLV,MP4,MKV
这些都是封装格式,这些文件存储的内容都是二进制的。
封装格式 实际上 就是各个 组织或者公司 之间,定一个标准,这种格式的文件,都有哪些字段,各个字段是什么意思,占多少字节,在文件的什么位置,各个字段之间的嵌套关系是怎么样的。封装格式定义的就是这些东西。
封装格式实际上就是定义一种标准,例如MP4标准,大家都按照 MP4 标准文档这种方式去读写数据内容,就不会有问题。举个例子,我跟别人约定好,面包放在抽屉的第一层,第二层放药品。这样别人就知道去第一层拿面包。如果我不按照约定把面包放在第一层,别人就会找不到面包,他的解析就会报错,找不到面包。
封装格式主要的作用是提供一个约定的规则。封装格式可以把 音视频流合在一起。
封装格式是多种多样的,为了解决不同的场景问题,大家会定义出来不同的格式。举个例子。
点播场景,什么是点播,就是视频已经录好了,放在服务器,客户端按需拉取一小端内容播放,不需要下载全部的视频内容。点播场景比较适合用 MP4 格式,因为 MP4 格式定义了 stts 索引表以及一些相关的数据结构,可以很快的跳转,例如 跳转 某个时间点播放,MP4 格式会比 FLV 快很多。(补充:FLV 可以额外添加 keyframeindex 加快跳转速度)
直播场景,MP4 格式的box结构要全部视频录完才能生成,而直播是不知道什么时候结束的。而 FLV 是一种渐进式的格式,非常适合用于直播。
所以不同的封装格式,是解决不同场景的问题。不过封装格式还会解决一些共同的问题,就是音视频同步。封装格式会给每个视频帧跟音频帧打上一个时间戳 PTS。
播放器单独播放视频流,或者音频流的时候,是不需要这个 PTS 时间戳的,视频流按帧率播放,音频流按采样率播放即可。这个 PTS 可以帮助音视频同步。
下面来讲解为什么,因为我们使用的操作系统大多都是分时系统,也就是每个任务分配一定的CPU时间片。
假设 Windows 系统正在播放一个视频(没有音频流),帧率是 1秒 24帧,现在是晚上 8点00分00秒。第一帧视频是从 8:00:00:00 开始播放,按帧率播放,41 毫秒就需要显示第二帧,所以第二帧应该在 8:00:00:41 的时候播放,第三帧在 8:00:00:82 的时候播放,这样画面看起来才是流畅的。
但是由于是分时系统,在 8:00:00:41 的时候,CPU 在忙着干其他的任务,无法切换回来播放器线程。
所以第二帧有可能是在 在 8:00:00:61 的时候才开始播放,慢了 20 毫秒。
这种情况我们能怎么办?即使播放器是我们开发的,我们也什么都干不了,在某个时刻 CPU 就是忙不过来。第二帧慢了 20 毫秒,第三帧也慢20毫秒就行了。
这样后面CPU不忙碌,看起来也会很流畅。这是单个视频流的情况。
假设有 一个音频流 与 一个视频流 同时播放,本来画面声音是需要同步的,例如第二帧视频播放的时候,第二帧音频也要播放。
但是因为视频流慢了 20 毫秒,如果音频流的播放线程不理会 视频播放线程的卡顿情况,音频流还是按自己的采样率播放,就会导致音频流播放快于 视频流 20 毫秒,这样不断累计,音视频流就会逐渐差距很大,画面不同步。
所以需要封装格式给 每个 音视频帧打上一个 PTS。音视频流 播放线程 通过观察对方 已经播放的 PTS 来决定自己要继续播放还是休眠,还是丢弃帧。
mp4封装格式
MP4 的封装格式是 基于 ISO/IEC 14496-12 标准实现的。先来说一下 上面 ISO/IEC 14496-12 这个名字是啥意思。
ISO 全称是 the International Organization for Standardization,国际标准化组织。
IEC 全称是 the International Electrotechnical Commission,国际电工委员会。
这是两个国际组织,流媒体是他们共同的领域,他们会在共同感兴趣的领域进行合作。所以两个组织有时候一起合作,制定一些标准。
14496 其实是 MPEG-4 标准的一组协议族,大概有30多个文档,定义了一些编解码标准,容器格式。而 MP4 所用的文档,就是第 12 个文档,标题是 “ISO base media format” (基础媒体格式)。
其实标准文档定义的东西,都是比较宽泛的,他提到的东西,你一定要按照那样实现,没提到的其实可以自由发挥。ISO/IEC 14496-12 是一个基础容器格式,你也可以基于这个标准,开发个 MP5 ,MP6 容器格式自己内部使用。都是可以的。
MPEG-TS封装格式
先讲一下 MPEG 是什么,MPEG 全称 Moving Picture Experts Group (动态影像专家小组),该专家组是联合技术委员会(Joint Technical Committee, JTC1)的一部分,JTC1 是由 ISO(国际标准化组织)和 IEC(国际电工委员会)建立的。JTC1 负责信息技术,在 JTC1 中,下设有负责“音频、图像编码以及多媒体和 超媒体信息”的子组 SG29。在 SG29 子组中,又设有 多个工作小组,其中就包括 JPEG(联合图片专家组)和 负责活动图像压缩的工作组 WG11 。因此,可以认为 MPEG 是 ISO/IEC JTC1/SG29/WG11,成立于1988年。
把 MPEG 理解成一个组织就行。 MPEG 主要制定影音压缩及传输的规格标准。MPEG 组织制定的标准目前一共有五个 ,MPEG-1、MPEG-2、MPEG-4、MPEG-7及MPEG-21,而 MPEG-TS 封装格式 的定义在 MPEG-2 标准 里面。
下面简单介绍一下 MPEG-2 标准,MPEG-2 目前广泛用于互联网传输协议,以及早期的有线数字电视,无线数字电视,卫星电视, DVB,DVD,等等。
MPEG-2 标准目前分为10个部分,统称为 ISO/IEC 13818 国际标准,标题 是 “GENERIC CODING OF MOVING PICTURES AND ASSOCIATED AUDIO”。
MPEG 的背景介绍完了,下面来讲一下 MPEG-TS 的封装格式,后缀名以 .ts 结尾的文件 就是 MPEG-TS 封装格式,ts 的全称是 Transport Stream (传输流)。
其实还有一个封装格式 MPEG-PS ,ps 的 全称是 Program Stream (节目流)。注意 Program 翻译成中文是 节目 的意思,不是程序。PS 封装格式主要用在 不容易发生错误的环境,例如 DVD 光盘。
TS流的包结构是固定长度188字节的,而PS流的包结构是可变长度的。TS码流的固定包结构具有较强的抵抗传输误码的能力。
什么是传输误码?
误码的产生是由于在信号传输中,衰变改变了信号的电压,致使信号在传输中遭到破坏,产生误码。噪音、交流电或闪电造成的脉冲、传输设备故障及其他因素都会导致误码(比如传送的信号是1,而接收到的是0;反之亦然)。由于种种原因,数字信号在传输过程中不可避免地会产生差错。-- 百度百科
简单来说,传输误码是比较底层的情况,数字信号都是二进制流 111000 这种,发生误码就是 1 在传输过程中 变成了 0。这也是我们经常使用的 TCP ,UDP 协议为什么要有一个 checksum (校验和)字段 ,发生误码,底层直接丢弃数据包,不会丢上去给应用层。
实际上,对于 UDP 或者 TCP 来说,用 TS 封装格式还是 PS 封装格式 ,抵抗传输误码的能力是一样的,因为UDP ,TCP 这些协议帮你处理了误码情况。但是 TS 跟 PS 的标准不只是给 UDP 跟 TCP 用,录像机,DVD机,这些数字视频领域的设备,也会用到 PS 跟TS。
无论是 TS 跟 PS, 早期主要是用在 数字电视 领域,我国数字电视标准用的是 DVB,全称是 Digital Video Broadcasting(数字视频广播)。
我们以前看电视机的时候,是有很多频道的,接受天线可以切换频道,接受不同频道。一个频道里面是有很多节目的,注意节目这个概念,英文是 Program ,这个术语在 MPEG-2 文档经常出现。
例如,CCTV频道下面,在同一时刻就有CCTV1-CCTV14这些节目,每个节目都有一个音频流跟视频流。
这些 频道、节目、音视频码流如何在TS里面进行区分呢?这就是 TS 封装格式复杂的地方。早期电视机(接受设备)性能比较差,跟基站无法交互,只能被动接受基站广播的数字信号。
我们互联网 TCP/UDP 传输的 是数字信号,数字电视基站也是广播的数字信号,001001 之类的二进制流。但是 互联网里面使用 TS 封装,通常是传了 一路视频和一路音频,所以 TS 里面很多字段我们用不着,互联网的 TS 封装是一个比较简单的应用场景。这种单节目的TS 封装 叫做 Single Program Transport Stream (SPTS)。