2. WebRTC基础
做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 本章知识体系
下面这张图把本章的核心概念串起来了。你可以看到音频和视频从采集到编码的完整路径。
从图上你可以看到,音频和视频在进入编码器之前,都经历了各自的「数字化」过程。而色彩空间转换是视频特有的步骤,它把RGB转成YUV 4:2:0,这是编码器能接受的格式。