12、Simulcast与SVC:Simulcast的SDP表示(a=simulcast)、SVC的SDP表示(a=scms)、码率分配策略

聊到WebRTC的媒体协商,有个话题绕不开——那就是Simulcast和SVC。说白了,这两个技术都是为了解决一个核心问题:不同网络条件下,怎么让视频流畅不掉帧

我最早接触Simulcast是在做大型视频会议项目的时候。那时候用户反馈说,有人用5G,有人还在3G边缘,结果要么高清卡死,要么模糊到看不清人脸。嗯,这就是典型的“一刀切”编码带来的问题。

Simulcast:多路并行,各取所需

Simulcast的思路很直接——发送端同时编码多路不同分辨率和码率的视频流。接收端根据自身网络状况,选择最合适的一路来接收。

举个例子,我同时推三路流:

  • 高清流(720p,2Mbps)
  • 标清流(360p,500Kbps)
  • 低清流(180p,150Kbps)

接收端A网络好,就订阅高清流;接收端B网络差,就订阅低清流。互不影响。

核心优势:接收端无需解码再编码,直接选择即可。延迟低,CPU开销小。

核心代价:发送端带宽和编码压力大。三路流就是三倍的计算量。

Simulcast的SDP表示:a=simulcast

在SDP里,Simulcast通过a=simulcast属性来描述。我习惯把它拆成两部分看:发送方向(send)接收方向(recv)

先看一个典型的例子:

a=simulcast:send 1,2,3 recv 4,5,6

这里的数字是RID(RTP Stream ID)的标识符。每个数字对应一个独立的RTP流。

更完整的写法会配合a=rid一起使用:

a=rid:1 send pt=96;max-width=1280;max-height=720
a=rid:2 send pt=96;max-width=640;max-height=360
a=rid:3 send pt=96;max-width=320;max-height=180
a=simulcast:send 1,2,3

你看,这里定义了三个RID:

  • RID 1:最大1280x720,高清流
  • RID 2:最大640x360,标清流
  • RID 3:最大320x180,低清流

每个RID还可以指定不同的编码参数,比如帧率、码率上限等。

我的经验:在实际项目中,我建议RID的命名用数字或简短字符串,不要用中文或特殊字符。有些SFU实现会对RID做字符串匹配,搞复杂了容易出bug。

SVC:分层编码,灵活适配

SVC(Scalable Video Coding)的思路和Simulcast完全不同。它不是多路独立流,而是一层基础层 + 多层增强层

打个比方:基础层是黑白素描,增强层一层层往上加颜色、加细节。接收端可以只收基础层,也可以收基础层+几层增强层,画质逐步提升。

SVC有三种伸缩维度:

  • 时间伸缩(Temporal):帧率变化。比如基础层15fps,增强层到30fps
  • 空间伸缩(Spatial):分辨率变化。比如基础层360p,增强层到720p
  • 质量伸缩(Quality):码率/信噪比变化。比如基础层低码率,增强层高码率

核心优势:编码器只编一次,解码器只解一次。带宽利用率高。

核心代价:编码复杂度高,解码也需要支持SVC。浏览器支持度不如Simulcast好。

SVC的SDP表示:a=scms

SVC在SDP里用a=scms(Scalable Codec Media Stream)来描述。不过说实话,这个字段在实际项目中用得并不多。更常见的是通过a=extmap扩展头来传递SVC的层级信息。

一个典型的SVC SDP片段:

a=scms:1 2 3
a=extmap:5 urn:ietf:params:rtp-hdrext:scms

这里的数字表示SVC的层级ID。接收端可以通过RTP扩展头动态切换层级。

但我要提醒你——实际项目中,SVC的SDP协商往往更复杂。不同厂商的实现差异很大。我曾经遇到过两个SFU对a=scms的解析方式完全不同,结果媒体流一直协商失败。

避坑指南:如果你在做跨厂商的SVC互通,建议先用Simulcast兜底。SVC的兼容性目前还是不如Simulcast成熟。

码率分配策略

不管是Simulcast还是SVC,码率分配都是关键。分配不好,要么浪费带宽,要么画质崩盘。

我总结了几种常见的分配策略:

策略 描述 适用场景
平均分配 每路流/每层分配相同码率 网络稳定,用户终端能力相近
优先级分配 高清流分配更多码率,低清流保底 大部分用户网络较好,少数差
动态调整 根据接收端反馈实时调整码率 网络波动大,需要自适应
目标码率分配 为每路流设定目标码率,允许波动 有固定带宽预算的场景

我个人比较推荐动态调整策略。为什么呢?因为网络状况是实时变化的。你想想看,用户可能从WiFi切换到4G,或者突然有人开始下载大文件。这时候如果码率分配是静态的,体验就会很差。

具体实现上,我会这样做:

  1. 发送端定期上报每路流的实际码率
  2. 接收端反馈接收质量(丢包率、抖动、RTT)
  3. SFU或发送端根据反馈动态调整码率分配

举个例子,假设总带宽是3Mbps,三路Simulcast流:

  • 初始分配:高清2Mbps,标清700Kbps,低清300Kbps
  • 如果高清流丢包率上升,就降低高清到1.5Mbps,把省出来的500Kbps给标清
  • 如果网络恢复,再逐步回调

我的习惯:我会给低清流设置一个最低保障码率,比如150Kbps。这样即使网络再差,用户至少能看到画面,不至于黑屏。这个经验是从一次线上事故里学到的——那次低清流码率被压到80Kbps,结果画面全是马赛克。

Simulcast vs SVC:怎么选?

这个问题我经常被问到。我的建议是:

  • 浏览器端为主:选Simulcast。Chrome、Firefox对Simulcast支持很好,SVC支持有限
  • 服务端转码场景:可以考虑SVC。编码一次,分发多种质量,节省服务端资源
  • 移动端为主:Simulcast更稳妥。移动端硬件编码器对SVC支持参差不齐
  • 极端网络环境:SVC更灵活。可以精细控制每一层的码率,适应窄带场景

嗯,其实没有绝对的好坏。我见过不少项目是Simulcast和SVC混用的——发送端用Simulcast推几路流,每路流内部再用SVC分层。这样既有Simulcast的兼容性,又有SVC的灵活性。当然,复杂度也上去了。

知识体系图

下面这张图梳理了本章的核心逻辑,你可以对照着回顾一下:

Simulcast与SVC知识体系 Simulcast(多路独立流) SVC(分层编码) SDP表示:a=simulcast + a=rid 多路编码,接收端选择 优点:兼容性好,延迟低 缺点:编码开销大 SDP表示:a=scms + extmap 单路编码,分层解码 优点:带宽利用率高 缺点:浏览器支持有限 码率分配策略:动态调整 > 优先级 > 平均

这张图把Simulcast和SVC的核心差异、SDP表示、优缺点以及码率分配策略串在了一起。你可以把它当作一个快速参考。


好了,这一章的内容就到这里。Simulcast和SVC的选择没有标准答案,关键看你的业务场景。我个人建议先从Simulcast入手,等遇到性能瓶颈了再考虑SVC。毕竟,能跑起来的方案才是好方案。