2、音视频基础理论:数字音频原理、数字视频原理、色彩空间与YUV格式

做WebRTC开发,说白了就是在跟「数字信号」打交道。你传的不是声音和画面,而是一串串0和1。我见过不少新手,上来就调SDP、搞ICE,结果音频卡顿、视频偏色,根本不知道问题出在哪。嗯,根子就在基础理论上。

这一章,我们把音视频的「数字底子」捋一遍。不扯太深,但该有的概念一个都不能少。

2.1 数字音频原理

声音是连续的模拟信号。计算机要处理它,就得「离散化」。这个过程分三步:采样、量化、编码。

2.1.1 采样

采样就是每隔一段时间,抓一个声音的瞬时值。抓多快?这就是采样率。

  • 8kHz:电话语音,够用但音质差。
  • 16kHz:WebRTC语音通话的标配。我个人习惯用这个,带宽和音质平衡得最好。
  • 44.1kHz:CD音质,音乐流媒体常用。
  • 48kHz:视频制作、专业音频的标准。WebRTC里很多场景也支持。

奈奎斯特采样定理:采样率必须大于信号最高频率的2倍,否则就会混叠。比如人声最高约4kHz,那8kHz采样就刚好够。

我在项目中遇到过一个问题:用48kHz采样录了一段语音,结果对方听到高频嘶嘶声。查了半天,原来是麦克风硬件只支持到22kHz,但软件没做抗混叠滤波。嗯,从那以后我每次选采样率,都会先确认硬件的有效带宽。

2.1.2 量化

采样拿到的是连续值,量化就是把这些值「四舍五入」到有限个等级。等级数由位深决定。

位深 动态范围 典型场景
8-bit 48 dB 早期电话、低端对讲
16-bit 96 dB CD、WebRTC默认
24-bit 144 dB 录音棚、专业音频

量化会引入误差,也就是量化噪声。16-bit的噪声水平已经很低,人耳基本听不出来。但如果你做混音或者多次编解码,噪声会累积。我曾经调试一个回声消除算法,发现底噪越消越大,最后定位到是量化精度不够,中间计算用了太多截断。

避坑指南:WebRTC内部默认用16-bit线性PCM。如果你从外部输入8-bit或24-bit数据,记得做格式转换,否则AudioProcessing模块会报错。

2.1.3 编码

原始PCM数据太大了。1分钟16-bit/48kHz的双声道音频,体积是:

48,000 × 16 × 2 × 60 = 92,160,000 bits ≈ 11 MB

所以必须压缩。WebRTC里最常用的编码器是Opus。它支持从6 kbps到510 kbps的动态码率,而且能自适应网络状况。我个人非常喜欢Opus,它几乎成了实时通信的标配。

其他编码器比如G.711(PCMA/PCMU)还在一些老旧系统里用,但WebRTC已经逐渐淘汰它们了。

2.2 数字视频原理

视频比音频复杂得多。三个核心参数:分辨率、帧率、码率。它们互相制约,你不可能同时要高清、流畅、低带宽。

2.2.1 分辨率

分辨率就是一张图像有多少像素。常见的有:

  • QVGA (320×240):老式手机摄像头,现在基本不用。
  • VGA (640×480):标清,WebRTC里做降级备用。
  • HD (1280×720):720p,WebRTC视频通话的常用分辨率。
  • Full HD (1920×1080):1080p,需要较高带宽。
  • 4K (3840×2160):目前WebRTC还不太实用,带宽要求太高。

分辨率越高,细节越丰富,但数据量也越大。我建议你根据实际场景选:如果是屏幕共享,1080p很有必要;如果是移动端视频通话,720p就够了,再高反而容易卡顿。

2.2.2 帧率

帧率就是每秒显示多少帧图像。单位fps。

  • 15 fps:勉强能动,适合低带宽场景。
  • 24 fps:电影标准。
  • 30 fps:WebRTC视频通话的默认值。
  • 60 fps:游戏、体育直播,但WebRTC里很少用。

帧率影响流畅度,但不影响清晰度。你想想看,30fps和60fps在静态场景下几乎没区别,但快速运动时差距就出来了。我在做远程驾驶项目时,帧率必须保证30fps以上,否则操作延迟感太强。

2.2.3 码率

码率是视频流每秒的数据量。单位bps。码率决定了视频质量的上限。

分辨率 推荐码率(H.264) 推荐码率(VP8/VP9)
720p @ 30fps 1.5 - 2.5 Mbps 1.0 - 2.0 Mbps
1080p @ 30fps 3.0 - 5.0 Mbps 2.0 - 4.0 Mbps
4K @ 30fps 15 - 25 Mbps 10 - 20 Mbps

码率不是越高越好。超过某个阈值后,画质提升微乎其微,但带宽浪费严重。WebRTC有内置的码率控制算法,会根据网络状况动态调整。我曾经遇到过一个问题:明明带宽足够,但视频还是模糊。后来发现是编码器的码率上限被设死了,没有给WebRTC的码率自适应留空间。

注意:分辨率、帧率、码率三者是「不可能三角」。你只能优化其中两个,第三个必然妥协。比如你要高清(高分辨率)且流畅(高帧率),那码率就必须高;如果带宽受限,就得降分辨率或降帧率。

2.3 色彩空间与YUV格式

计算机显示颜色,最直观的方式是RGB。但视频编码几乎不用RGB,而是用YUV。为什么?

因为人眼对亮度(Y)比对色度(U、V)更敏感。YUV可以把亮度和色度分开,然后对色度做「降采样」,大幅减少数据量,而人眼几乎察觉不到。

2.3.1 YUV采样格式

常见的YUV采样格式有三种:

  • YUV 4:4:4:每个像素都有完整的Y、U、V。数据量最大,画质无损。
  • YUV 4:2:2:每两个像素共享一组U、V。水平分辨率减半。
  • YUV 4:2:0:每四个像素(2×2)共享一组U、V。水平和垂直分辨率都减半。WebRTC里最常用。

举个例子:一张1920×1080的图像,如果用RGB存储,每个像素3字节,总大小约6.2 MB。如果用YUV 4:2:0,Y分量1920×1080,U和V各960×540,总大小约3.1 MB。直接省了一半。

核心结论:WebRTC视频编码器(VP8、VP9、H.264)内部都使用YUV 4:2:0作为输入格式。你从摄像头采集到的数据,必须先转换成YUV 4:2:0,才能送入编码器。

2.3.2 色彩空间转换

RGB到YUV的转换有标准公式。以BT.601标准为例:

Y  = 0.299 * R + 0.587 * G + 0.114 * B
U  = -0.169 * R - 0.331 * G + 0.500 * B + 128
V  = 0.500 * R - 0.419 * G - 0.081 * B + 128

注意U和V加了128的偏移,这是为了把负数映射到0-255范围。我在项目中遇到过色彩偏绿的问题,排查后发现是转换时忘了加这个偏移量。嗯,这种细节最容易坑人。

2.3.3 WebRTC中的YUV处理

WebRTC提供了libyuv库,专门做YUV的转换、缩放、旋转。你不需要自己写公式。常用的API有:

  • I420ToNV12:I420(YUV 4:2:0平面格式)转NV12(半平面格式)。
  • ARGBToI420:RGB转YUV 4:2:0。
  • I420Scale:YUV图像缩放。
  • I420Rotate:YUV图像旋转。

我曾经调试过一个bug:Android端采集的视频在iOS端显示是倒的。原因就是Android摄像头默认是横屏,但YUV数据没有做旋转。用I420Rotate旋转90度后,问题解决。

个人建议:如果你要手动处理视频帧,尽量用libyuv,不要自己写转换逻辑。它经过了SIMD优化,性能比手写快好几倍。

2.4 本章知识体系

下面这张图把本章的核心概念串起来了。你可以看到音频和视频从采集到编码的完整路径。

音视频数字处理流程 音视频数字处理流程 模拟音频信号 采样 (8/16/48kHz) 量化 (8/16/24-bit) 编码 (Opus/G.711) 模拟视频信号 分辨率 (720p/1080p) 帧率 (15/30/60fps) 编码 (VP8/VP9/H.264) RGB原始数据 YUV 4:4:4 YUV 4:2:0 libyuv转换 WebRTC 编码器输入 音频:采样→量化→编码 视频:分辨率→帧率→码率 色彩:RGB→YUV 4:2:0

从图上你可以看到,音频和视频在进入编码器之前,都经历了各自的「数字化」过程。而色彩空间转换是视频特有的步骤,它把RGB转成YUV 4:2:0,这是编码器能接受的格式。