zoukankan      html  css  js  c++  java
  • webrtc-SVC+simulcast改造

    1.simulcast+SVC打开

        std::string str[] = {"low", "medium", "high"};
        std::string msid[] = {"l", "m", "h"};
        //double pri = 0.8;
        //添加初始化参数,在此处设置时域层数,push 多少webrtc::RtpEncodingParameters就是多少层simulcast
        webrtc::RtpTransceiverInit rtpTI;
         for (int i = 3; i >= 1; i--) {
          webrtc::RtpEncodingParameters videoEncoding;
          videoEncoding.rid = str[i-1];
          //videoEncoding.max_bitrate_bps = 3 * i * 100 * 1000;
          //videoEncoding.bitrate_priority = pri;
          videoEncoding.num_temporal_layers = 3;
          rtpTI.send_encodings.push_back(videoEncoding);
          //pri -= 0.2;
          rtpTI.stream_ids.push_back(msid[i-1]); //这个是SDP中msid参数的名字
        }
        //单层simulcast的时候设置时域层数
        //rtpTI.stream_ids.push_back("cam");
        //webrtc::RtpEncodingParameters videoEncoding;
        //videoEncoding.rid = str[2];
        //videoEncoding.num_temporal_layers = 3;
        //rtpTI.send_encodings.push_back(videoEncoding);
    
        auto ret = peer_connection_->AddTransceiver(video_track_,rtpTI);
        //这个可以获取当前设置的参数
        webrtc::RtpParameters para =
            peer_connection_->GetSenders()[1]->GetParameters();
    

    2.设置编码优先顺序(编码选择)

    src/media/engine/internal_encoder_factory.cc

    std::vector<SdpVideoFormat> InternalEncoderFactory::GetSupportedFormats()
        const {
      std::vector<SdpVideoFormat> supported_codecs;
      //for (const webrtc::SdpVideoFormat& format : webrtc::SupportedH264Codecs())
      //  supported_codecs.push_back(format);
      // for (const webrtc::SdpVideoFormat& format : webrtc::SupportedVP9Codecs())
      //   supported_codecs.push_back(format);
      supported_codecs.push_back(SdpVideoFormat(cricket::kVp8CodecName));
      return supported_codecs;
    }
    

    3.设置H264可通过AddTransceiver参数初始化支持SVC

    增加对H264的支持:
    src/media/engine/webrtc_video_engine.cc

    bool IsTemporalLayersSupported(const std::string& codec_name) {
      return absl::EqualsIgnoreCase(codec_name, kVp8CodecName) ||
             absl::EqualsIgnoreCase(codec_name, kVp9CodecName) ||
             absl::EqualsIgnoreCase(codec_name, kH264CodecName);
    }
    

    4.关于simulcast和svc的常量参数设置

    src/api/video/video_codec_constans.h

    enum : int { kMaxEncoderBuffers = 8 };
    enum : int { kMaxSimulcastStreams = 3 };
    enum : int { kMaxSpatialLayers = 5 };
    enum : int { kMaxTemporalStreams = 4 };
    

    5.simulcast

    	media_session.cc
    	
    	static bool AddStreamParams(){
    	...
    	  //这是对应的流的信息 
    	  StreamParams stream_param =
    	          sender.rids.empty()
    	              ?
    	              // Signal SSRCs and legacy simulcast (if requested).
    	              //老版本planb. rids为空,使用num_simulcast_layer来创建,内部调用GenerateSsrcs		              
    	              CreateStreamParamsForNewSenderWithSsrcs(
    	                  sender, rtcp_cname, include_rtx_streams,
    	                  include_flexfec_stream, ssrc_generator)
    	              :
    	              // Signal RIDs and spec-compliant simulcast (if requested).
    	              CreateStreamParamsForNewSenderWithRids(sender, rtcp_cname);
    	
    	...
    	}
    
    

    AddTransceiver只能在webrtc::SdpSemantics::kUnifiedPlan模式下,这个在CreatePeerConnection时设置进去,目前设置完simulcast参数后,数据未推上去,应该是服务端暂未支持。

    webrtc::SdpSemantics::kPlanB模式下对应的是AddTrack,但是设置simulcast层数是在CreateOffer设置进去,webrtc::PeerConnectionInterface::RTCOfferAnswerOptions.num_simulcast_layers,但是此时根据抓包和断点看到的是只有2层,RTCOfferAnswerOptions里面默认是两层。
    注:webrtc会根据当前视频的分辨率,以及预设的常量来决定实际的simulcast层数,例如640x480-位于(960,540)和(640,320),所以参数是设置为(640,320),最多2层simulcast

    void Conductor::ConnectToPeer(int peer_id) {
      //RTC_DCHECK(peer_id_ == -1);
      //RTC_DCHECK(peer_id != -1);
    
      if (peer_connection_.get()) {
        main_wnd_->MessageBox(
            "Error", "We only support connecting to one peer at a time", true);
        return;
      }
    
      if (InitializePeerConnection()) {
        webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options =
            webrtc::PeerConnectionInterface::RTCOfferAnswerOptions();
        options.num_simulcast_layers = 3;
        peer_id_ = peer_id;
        peer_connection_->CreateOffer(
            this, options);
      } else {
        main_wnd_->MessageBox("Error", "Failed to initialize PeerConnection", true);
      }
    }
    

    src/media/engine/simulcast.cc

    // These tables describe from which resolution we can use how many
    // simulcast layers at what bitrates (maximum, target, and minimum).
    // Important!! Keep this table from high resolution to low resolution.
    // clang-format off
    const SimulcastFormat kSimulcastFormats[] = {
      {1920, 1080, 3, 5000, 4000, 800},
      {1280, 720, 3, 2500, 2500, 600},
      {960, 540, 3, 1200, 1200, 350},
      {640, 360, 2, 700, 500, 150},
      {480, 270, 2, 450, 350, 150},
      {320, 180, 1, 200, 150, 30},
      {0, 0, 1, 200, 150, 30}
    };
    
    FindSimulcastFormatIndex:
    {
      ...
      for (uint32_t i = 0; i < arraysize(kSimulcastFormats); ++i) {
        if (width * height >=
            kSimulcastFormats[i].width * kSimulcastFormats[i].height) {
          return i;
        }
      }
      ...
    }
    
    ReconfigureEncoder
              ↓
    CreateEncoderStreams
              ↓
    GetSimulcastConfig
              ↓
    LimitSimulcastLayerCount
              ↓
    FindSimulcastFormatIndex
    

    6.simulcast码率设置

    编码相关的设置在src/video/video_stream_encoder.cc-ReconfigureEncoder

    ReconfigureEncoder
              ↓
    CreateEncoderStreams
              ↓
    GetSimulcastConfig
              ↓
    GetNormalSimulcastLayers 设置宽高码率,同时会设置默认时域层
        DefaultNumberOfTemporalLayers
        FindSimulcastMaxBitrateBps
        FindSimulcastTargetBitrateBps
        FindSimulcastMinBitrateBps
        kDefaultVideoMaxFramerate = 60
    

    计算simulcast码率的时候使用双线性插值:

      const int total_pixels_up =
          kSimulcastFormats[index - 1].width * kSimulcastFormats[index - 1].height;
      const int total_pixels_down =
          kSimulcastFormats[index].width * kSimulcastFormats[index].height;
      const int total_pixels = width * height;
      const float rate = (total_pixels_up - total_pixels) /
                         static_cast<float>(total_pixels_up - total_pixels_down);
      SimulcastFormat res;
      res.width = width;
      res.height = height;
      res.max_layers = kSimulcastFormats[index].max_layers;
      res.max_bitrate_kbps =
          kSimulcastFormats[index - 1].max_bitrate_kbps * (1.0 - rate) +
          kSimulcastFormats[index].max_bitrate_kbps * rate;
    

    在设置0层simulcast时,如果打开了kUseBaseHeavyVP8TL3RateAllocationFieldTrial("WebRTC-UseBaseHeavyVP8TL3RateAllocation"),最大码率和目标码率将会乘以系数以适应0时域层

          // If alternative temporal rate allocation is selected, adjust the
          // bitrate of the lowest simulcast stream so that absolute bitrate for
          // the base temporal layer matches the bitrate for the base temporal
          // layer with the default 3 simulcast streams. Otherwise we risk a
          // higher threshold for receiving a feed at all.
          
        if (num_temporal_layers == 3) {
            if (webrtc::field_trial::IsEnabled(
                    kUseBaseHeavyVP8TL3RateAllocationFieldTrial)) {
              // Base heavy allocation increases TL0 bitrate from 40% to 60%.
              rate_factor = 0.4 / 0.6;
            }
          } else {
            rate_factor =
                webrtc::SimulcastRateAllocator::GetTemporalRateAllocation(3, 0) /
                webrtc::SimulcastRateAllocator::GetTemporalRateAllocation(
                    num_temporal_layers, 0);
          }
    
    static const float
        kLayerRateAllocation[kMaxTemporalStreams][kMaxTemporalStreams] = {
            {1.0f, 1.0f, 1.0f, 1.0f},  // 1 layer
            {0.6f, 1.0f, 1.0f, 1.0f},  // 2 layers {60%, 40%}
            {0.4f, 0.6f, 1.0f, 1.0f},  // 3 layers {40%, 20%, 40%}
            {0.25f, 0.4f, 0.6f, 1.0f}  // 4 layers {25%, 15%, 20%, 40%}
    };
    
    static const float kBaseHeavy3TlRateAllocation[kMaxTemporalStreams] = {
        0.6f, 0.8f, 1.0f, 1.0f  // 3 layers {60%, 20%, 20%}
    };
    

    7.时域层码率设置

    ReconfigureEncoder
              ↓
    EncoderSimulcastProxy::InitEncode
              ↓
    LibvpxVp8Encoder::InitEncode
              ↓
    SimulcastRateAllocator::Allocate
              ↓
    SimulcastRateAllocator::GetTemporalRateAllocation 获取对应simulcast层数的时域层比率乘以目标码率
    

    时域层的所有码率总和是当前simulcast层的目标码率

    std::vector<uint32_t> SimulcastRateAllocator::DefaultTemporalLayerAllocation(
        int bitrate_kbps,
        int max_bitrate_kbps,
        int simulcast_id) const {
      const size_t num_temporal_layers = NumTemporalStreams(simulcast_id);
      std::vector<uint32_t> bitrates;
      for (size_t i = 0; i < num_temporal_layers; ++i) {
        float layer_bitrate =
            bitrate_kbps * GetTemporalRateAllocation(num_temporal_layers, i);
        bitrates.push_back(static_cast<uint32_t>(layer_bitrate + 0.5));
      }
    
      // Allocation table is of aggregates, transform to individual rates.
      uint32_t sum = 0;
      for (size_t i = 0; i < num_temporal_layers; ++i) {
        uint32_t layer_bitrate = bitrates[i];
        RTC_DCHECK_LE(sum, bitrates[i]);
        bitrates[i] -= sum;
        sum = layer_bitrate;
    
        if (sum >= static_cast<uint32_t>(bitrate_kbps)) {
          // Sum adds up; any subsequent layers will be 0.
          bitrates.resize(i + 1);
          break;
        }
      }
    
      return bitrates;
    }
    
  • 相关阅读:
    Stream中的map
    项目中的process.bpmn的读-过程
    windows10打开switchHost,提示无修改权限
    Windows10安装node.js
    工作中的小发现
    启动redis
    call apply bin 的区别
    利用promise 让 函数按序执行
    uni-app 小程序
    插件 Generate css tree
  • 原文地址:https://www.cnblogs.com/bloglearning/p/12128501.html
Copyright © 2011-2022 走看看