JCHub

  • Home
  • Category
    • A/V
    • WebRTC
    • Beauty of Programming
    • Linux
    • Windows
    • Moments of Life
    • Campus Life
  • Reference
    • API Reference
    • Utilities
    • AV Test
    • Doc
  • Message Board
  • About
JCHub
Code as My Sword, Lost in Obsession
  1. Main page
  2. WebRTC
  3. Main content

WebRTC研究:Simulcast层数变化

2020年12月27日 9164hotness 12likes 20comments

当我们上行推Simulcast流的时候,会发现Simulcast层数会时不时变化,本来是大小流(大小两个分辨率)两层,但是变为只有一层小流了,后面又恢复为两层了,如果我们服务端支持Simulcast的话,这就成为一个棘手问题。

例如我们团队的WebRTC产品,客户端默认会启用Simulcast,上行推送多个不同分辨率的流到服务端,服务端下行会依据带宽估计值,自动选择某个分辨率的流给订阅客户端。假设此时下行带宽足够,服务端会推送大流。但是对于上行客户端来说,因为某种原因,Simulcast层数减少为一层,只剩一路小流了,如果没有相应处理,此时订阅客户端就接收不到视频流了。

Simulcast层数为何变化

WebRTC中如果送到编码器的采集视频帧分辨率发生变化,会触发ReconfigureEncoder也就是重置编码器操作,然后Simulcast层数也会重新计算。在WebRTC中,Simulcast层数与采集视频帧分辨率的关系由一张表决定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
constexpr const SimulcastFormat kSimulcastFormats[] = {
    {1920, 1080, 3, webrtc::DataRate::KilobitsPerSec(5000),
     webrtc::DataRate::KilobitsPerSec(4000),
     webrtc::DataRate::KilobitsPerSec(800)},
    {1280, 720, 3, webrtc::DataRate::KilobitsPerSec(2500),
     webrtc::DataRate::KilobitsPerSec(2500),
     webrtc::DataRate::KilobitsPerSec(600)},
    {960, 540, 3, webrtc::DataRate::KilobitsPerSec(1200),
     webrtc::DataRate::KilobitsPerSec(1200),
     webrtc::DataRate::KilobitsPerSec(350)},
    {640, 360, 2, webrtc::DataRate::KilobitsPerSec(700),
     webrtc::DataRate::KilobitsPerSec(500),
     webrtc::DataRate::KilobitsPerSec(150)},
    {480, 270, 2, webrtc::DataRate::KilobitsPerSec(450),
     webrtc::DataRate::KilobitsPerSec(350),
     webrtc::DataRate::KilobitsPerSec(150)},
    {320, 180, 1, webrtc::DataRate::KilobitsPerSec(200),
     webrtc::DataRate::KilobitsPerSec(150),
     webrtc::DataRate::KilobitsPerSec(30)},
    {0, 0, 1, webrtc::DataRate::KilobitsPerSec(200),
     webrtc::DataRate::KilobitsPerSec(150),
     webrtc::DataRate::KilobitsPerSec(30)}};

对于1920x1080的采集分辨率,允许的最大Simulcast层数为3层,对于640x360的采集分辨率,允许的最大层数为2层。所以当采集视频分辨率从1920x1080变为640x360时,Simulcast层数就会发生变化。

下面简单说下几种造成采集视频帧分辨率变化的几种情况:
1)带宽估计值不够;
2)编码视频的QP值在设定的阈值范围外;
3)通过编码延时计算得到的CPU负载发生变化。

发生如上情况时,WebRTC会调整采集的原始视频帧分辨率或者帧率,至于是调整采集分辨率还是帧率,依据我们采取的策略,WebRTC中目前存在四种策略:
1)BALANCED,平衡模式;
2)MAINTAIN_RESOLUTION,保分辨率模式,也就是清晰模式;
3)MAINTAIN_FRAMERATE,保帧率模式,也就是流畅模式;
4)DISABLED,禁用模式。

例如对于摄像头视频流,默认是流畅模式,我们举两个例子说明下。
1)如果分配的带宽估计值满足不了当前编码器分辨率的码率设置时,就会触发降低采集视频帧分辨率的操作;
2)如果检测到CPU负载较高,也会触发降低采集视频帧分辨率的操作。这种现象在移动端设备较为明显,会看到视频刚开始很清晰,后面就模糊了(假设带宽足够情况下)。

当然了,对于屏幕共享流,默认是清晰模式,只会触发降低帧率的操作,看起来视频会变卡。

如何防止Simulcast层数变化

这里我们先看下启用Simulcast时,采集分辨率改变后,重置编码器的相关代码流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
VideoStreamEncoder::OnFrame
           ↓
VideoStreamEncoder::MaybeEncodeVideoFrame
           ↓
VideoStreamEncoder::ReconfigureEncoder
           ↓
EncoderStreamFactory::CreateEncoderStreams
           ↓
EncoderStreamFactory::CreateSimulcastOrConfereceModeScreenshareStreams
           ↓
GetSimulcastConfig
           ↓
LimitSimulcastLayerCount

在LimitSimulcastLayerCount中会根据前面提到的表内容,改变Simulcast层数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int LimitSimulcastLayerCount(int width, int height, int layer_count) {
  if (!webrtc::field_trial::IsDisabled(
          kUseLegacySimulcastLayerLimitFieldTrial)) {
    // 根据table,得到当前分辨率允许的simulcast最大层数
    int adaptive_layer_count =
        kSimulcastFormats[FindSimulcastFormatIndex(width, height)].max_layers;
    if (layer_count > adaptive_layer_count) {
      RTC_LOG(LS_WARNING) << "Reducing simulcast layer count from "
                          << layer_count << " to " << adaptive_layer_count;
      layer_count = adaptive_layer_count;
    }
  }
  return layer_count;
}

可知,通过在webrtc::field_trial设置禁用kUseLegacySimulcastLayerLimitFieldTrial即可保持当前Simulcast层数,避免了大流消失的麻烦。

总结

本文分析了Simulcast层数变化的原因,以及如何防止变化。其中介绍了WebRTC的清晰模式,流畅模式等概念,后续文章会对这些进行详细分析。

This article is licensed with Creative Commons Attribution-NonCommercial-No Derivatives 4.0 International License
Tag: WebRTC
Last updated:2022年3月27日

Jeff

管理员——代码为剑,如痴如醉

Tip the author Like
< Last article
Next article >

Comments

  • 悠哉嗑瓜子

    博主好,感谢分享! :smile:
    2)MAINTAIN_FRAMERATE,保分辨率模式,也就是清晰模式;
    3)MAINTAIN_RESOLUTION,保帧率模式,也就是流畅模式;
    这两个从字面意思看,貌似是写反了?

    2021年3月17日
    Reply
    • Jeff

      @悠哉嗑瓜子 感谢纠正,已修改

      2021年3月17日
      Reply
  • Jason

    博主您好,请问WebRTC启用simulcast后,
    VideoStreamEncoder::OnEncodedImage(video_stream_encoder.cc)是不是应该收到多个编码流呀?我应该成功配置了两个Simulcast流,但是只收到了1280*720的编码流,博主您知道可能是什么原因导致的吗?
    以下是我的配置信息:
    video_stream_encoder.cc: (line 685): ReconfigureEncoder:
    Simulcast streams:
    0: 640x360 fps: 60 min_kbps: 150 target_kbps: 500 max_kbps: 700 max_fps: 60 max_qp: 56 num_tl: 3 active: true
    1: 1280x720 fps: 60 min_kbps: 600 target_kbps: 2500 max_kbps: 2500 max_fps: 60 max_qp: 56 num_tl: 3 active: true
    最后感谢博主的分享!

    2021年3月29日
    Reply
    • Jeff

      @Jason 只编码出一路流,你把日志都打开,应该能看出问题

      2021年3月29日
      Reply
  • Jason

    @Jeff,感谢博主的回复。我看了一下日志,应该是硬编码器不支持。
    【video_stream_encoder.cc: (line 1278): Encoder settings 】
    这里的EncoderInfo::supports_simulcast = 0.
    博主您觉得是这个原因吗?

    2)另外,请问目前WebRTC如果要支持H264的Simulcast功能,是不是必须得使用OpenH264进行软编?
    博主之前也是使用软编实现的吗?(我现在在安卓平台测试,发现WebRTC目前不支持H264软编)

    2021年3月30日
    Reply
    • Jeff

      @Jason 恩,你要相信WebRTC日志。安卓上,WebRTC支持H264软编,你照着VP8/VP9的调用接口,自己实现jni接口调用,调用openH264库进行软编:
      src\sdk\android\api\org\webrtc\LibvpxVp8Encoder.java
      src\sdk\android\src\jni\vp8_codec.cc

      你模仿这个VP8接口调用实现openH264编码器调用即可

      2021年3月30日
      Reply
  • Jason

    @Jeff,我用VP8软编试了一下,simulcast是正常的。之前的H264、VP8都是使用的硬编。后面的工作就是实现以及选择支持的硬/软编码器了。感谢!

    2021年3月31日
    Reply
  • jwkuang

    请问webrtc上如何才能开启simulcast呢

    2021年5月11日
    Reply
    • Jeff

      @jwkuang 已邮件回复

      2021年5月12日
      Reply
      • walle

        @Jeff 你好博主,我也在学习simulcast的过程,读了你的文档大受启发,我发现在UnifiedPlan标准下没办法直接通过设置num_simulcast_layers启用,所以也请帮忙将开启simulcast的逻辑发我一份,谢谢

        2021年9月28日
        Reply
      • kk

        @Jeff 请问iOS端webrtc上如何才能开启simulcast呢

        2022年6月22日
        Reply
    • tony

      @jwkuang 能不能把simulcast分享的方法,分享一下。

      2022年7月12日
      Reply
  • xjm

    刚接触这一块,可能问的问题比较小白。想请教的就是simulcast我的理解是多个不同分辨率的流,但是设置里面为什么会有num_temporal_layers设置时域层数,这个不是SVC的概念吗?这是simulcast和svc混用吗?另外如果时域层数大于1,解码端解码h264使用的是ffmpeg能正常解码吗?

    2021年5月12日
    Reply
    • Jeff

      @xjm WebRTC里Simulcast主要指不同分辨率的流,WebRTC内部代码是叫做空域分层(跟SVC里的不一样),是通过创建多个编码器实现的。对于每个分辨率的流,如果支持SVC里的时域分层,也会配置每个分辨率各自的时域层数,只能说simulcast用到了SVC技术

      2021年5月13日
      Reply
      • xjm

        @Jeff 非常感谢博主的回复,多谢

        2021年5月13日
        Reply
    • xjm

      @xjm 非常感谢博主的回复,多谢

      2021年5月13日
      Reply
  • lh

    博主你好,1. 请问web端有什么方法,可以保证编码层数。
    2. 通过设置每层的编码maxbitrate能起到什么作用呢

    2021年12月10日
    Reply
  • 11

    你好,请教一下,我下了个web的simulcast的例子
    pc1.addTransceiver(
    localStream.getVideoTracks()[0],
    {
    sendEncodings: [{rid: 'l', active: true}, {rid: 'm', active: true}, {rid: 'h', active: true}],
    streams: [localStream]
    });
    在webrtc-internals里看确实有了3个RTCOutboundRTPVideoStream,但是只有一个在发送数据,怎么才能让3个都发数据呢

    2022年2月19日
    Reply
  • weixiao

    你好,博主,能不能将开启simulcast的逻辑发我一份,我发现我无论怎么设置num_simulcast_layers的值,对结果都没有影响

    2022年2月28日
    Reply
  • Nicklhk

    请问下博主,webrtc使用的openh264默认支持svc吗,我看源码里面好像只有vp8和vp9支持开启

    2023年5月8日
    Reply
  • razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
    Cancel

    This site uses Akismet to reduce spam. Learn how your comment data is processed.

    文章目录
    • Simulcast层数为何变化
    • 如何防止Simulcast层数变化
    • 总结
    Related Posts
    • 浅谈基于SFU实现一对一效果
    • WebRTC资讯:H265支持进展
    • Protected: WebRTC硬件编解码器出错无缝切换软编软解
    • WebRTC研究:Audio level
    • Mac平台WebRTC编译
    Categories

    COPYRIGHT © 2026 jianchihu.net. ALL RIGHTS RESERVED.

    Theme Kratos Made By Seaton Jiang