剑痴乎

  • 首页
  • 文章分类
    • 音视频
    • WebRTC
    • 编程之美
    • Linux
    • Windows
    • 生活点滴
    • 校园生活
  • 参考
    • API参考
    • 实用工具
    • 测试音视频
    • 文档
  • 留言板
  • 关于
剑痴乎
代码为剑,如痴如醉
  1. 首页
  2. WebRTC
  3. 正文

WebRTC研究:Simulcast层数变化

2020年12月27日 7822点热度 12人点赞 20条评论

当我们上行推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的清晰模式,流畅模式等概念,后续文章会对这些进行详细分析。

本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可
标签: WebRTC
最后更新:2022年3月27日

Jeff

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

打赏 点赞
< 上一篇
下一篇 >

文章评论

  • 悠哉嗑瓜子

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

    2021年3月17日
    回复
    • Jeff

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

      2021年3月17日
      回复
  • 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日
    回复
    • Jeff

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

      2021年3月29日
      回复
  • Jason

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

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

    2021年3月30日
    回复
    • 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日
      回复
  • Jason

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

    2021年3月31日
    回复
  • jwkuang

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

    2021年5月11日
    回复
    • Jeff

      @jwkuang 已邮件回复

      2021年5月12日
      回复
      • walle

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

        2021年9月28日
        回复
      • kk

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

        2022年6月22日
        回复
    • tony

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

      2022年7月12日
      回复
  • xjm

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

    2021年5月12日
    回复
    • Jeff

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

      2021年5月13日
      回复
      • xjm

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

        2021年5月13日
        回复
    • xjm

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

      2021年5月13日
      回复
  • lh

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

    2021年12月10日
    回复
  • 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日
    回复
  • weixiao

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

    2022年2月28日
    回复
  • Nicklhk

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

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

    这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理。

    版权声明

    为支持原创,创作更好的文章,未经许可,禁止任何形式的转载与抄袭,如需转载请邮件私信!本人保留所有法定权利。违者必究!

    文章目录
    • Simulcast层数为何变化
    • 如何防止Simulcast层数变化
    • 总结
    最近评论
    ztt 发布于 1 个月前(04月05日) 你好,想看里面的视频和图片为什么没有显示呢?需要下flash吗还是什么。
    huowa222 发布于 1 个月前(03月26日) 同问
    邱国禄 发布于 3 个月前(02月17日) Receive Delta以0.25ms为单位,reference time以64ms为单位,kDe...
    啊非 发布于 4 个月前(12月30日) 大神,请教一个问题: constexpr int kBaseScaleFactor = Tran...
    啊非 发布于 4 个月前(12月30日) reference time:3字节,表示参考时间,以64ms为单位,但是 代码里面是 Trans...
    相关文章
    • WebRTC资讯:H265支持进展
    • WebRTC研究:Audio level
    • Mac平台WebRTC编译
    • WebRTC研究:RTP时间戳的产生
    • WebRTC研究:统计参数之丢包率

    COPYRIGHT © 2024 jianchihu.net. ALL RIGHTS RESERVED.

    Theme Kratos Made By Seaton Jiang