点击上方“LiveVideoStack”关注我们
作者 | 王朋闯
本文为王朋闯老师创作的系列ion文章,LiveVideoStack已获得授权发布,未来将持续更新。
七、Simulcast流程
1. Simulcast概念
先介绍WebRTC的一个概念——Simulcast(联播,俗称大小流):
推流端===f/h/q==>SFU--f--->收流端A
|---q--->收流端B
|---h--->收流端C
上行一般是三路流,按分辨率和码率,一般分为fhq(大中小)三层
下行可以分给不同的用户不同的流,比如网不好时分发个小流q,网变好了再切回大流f
三层的streamId、trackId是一样的,但是rid和ssrc是不同的,rid一般是f、h、q
对应的SDP部分
.........
a=rid:f send
a=rid:h send
a=rid:q send
a=simulcast:send f;h;q
2.收发流程
看本章之前,最好看一下前一章,熟悉一下收发流程,本文只重点介绍其中的Simulcast部分。
收发包逻辑打通步骤:
SDK推流---->OnTrack---->router.AddReceiver(设置Buffer和上行Track)------>SessionLocal.Publish(设置下行Track)---->收发包逻辑打通
3.Simulcast上行流程
非Simulcast情况,OnTrack一般会触发两次:一个audioTrack+一个videoTrack。
Simulcast下,OnTrack一般会触发四次:一个audioTrack+三个videoTrack(rid分别为fhq)。
这个流程会触发四次:
OnTrack--->router.AddReceiver--->WebRTCReceiver.AddUpTrack
三个videoTrack,共用同一个WebRTCReceiver。
type WebRTCReceiver struct {
。。。
receiver *webrtc.RTPReceiver
codec webrtc.RTPCodecParameters
rtcpCh chan []rtcp.Packet
buffers [3]*buffer.Buffer//需要三个buffer
upTracks [3]*webrtc.TrackRemote//三个TrackRemote
。。。
pendingTracks [3][]*DownTrack//三个层,每层来订阅的downtrack
。。。
}
接下来看一下AddUpTrack是如何工作的:
func (w *WebRTCReceiver) AddUpTrack(track *webrtc.TrackRemote,buff *buffer.Buffer, bestQualityFirst bool) {
if w.closed.get() {
return
}
//根据RID来区分layer
var layer int
switch track.RID() {//如果没开simulcast,为""
case fullResolution:
layer = 2
case halfResolution:
layer = 1
default:
layer = 0//如果没开simulcast,为0
}
w.Lock()
//设置空域层layer的track
w.upTracks[layer] = track
//设置空域层layer的buff
w.buffers[layer] = buff
w.available[layer].set(true)
//设置空域层layer的downtrack
w.downTracks[layer].Store(make([]*DownTrack,0, 10))
w.pendingTracks[layer] = make([]*DownTrack,0, 10)
w.Unlock()
//闭包函数,按最佳质量订阅,切到f层
subBestQuality := func(targetLayerint) {
for l := 0; l <targetLayer; l++ {
dts :=w.downTracks[l].Load()
if dts == nil{
continue
}
for _, dt :=range dts.([]*DownTrack) {
_ = dt.SwitchSpatialLayer(int32(targetLayer), false)
}
}
}
//闭包函数,按最差质量订阅,切到q层
subLowestQuality := func(targetLayerint) {
for l := 2; l !=targetLayer; l-- {
dts :=w.downTracks[l].Load()
if dts == nil{
continue
}
for _, dt :=range dts.([]*DownTrack) {