webrtc視頻流程

1. 創建視頻引擎VideoEngine

函數:VideoEngine::Create()

構建VideoEngineImpl對象,該類繼承了ViEBaseImpl、ViECodecImpl、ViECaptureImpl、
ViEFileImpl、ViEImageProcessImpl、ViENetworkImpl、ViERenderImpl、
ViERTP_RTCPImpl、ViEExternalCodecImpl、VideoEngine類,
同時ViEBaseImpl實例化視頻共享數據單元ViEShareData,將該共享數據對象分發給各Impl類。
graph TD
A[VideoEngine::Create] --> |vie_impl.cc|B(new VideoEngineImpl:new Config, true)
B --> B1(ViENetworkImpl)
B --> B2(ViECodecImpl)
B --> B3(ViECaptureImpl)
B --> B4(ViEFileImpl)
B --> B5(ViEImageProcessImpl)
B --> B6(ViEBaseImpl)
B --> B7(ViERenderImpl)
B --> B8(ViERTP_RTCPImpl)
B --> B9(ViEExternalCodecImpl)
B --> B10(VideoEngine)
B6--> |vie_shared_data.cc| B6A(ViESharedData::ViESharedData)
B6A --> |創建日志跟蹤對象|B6A1(Trace::CreateTrace)
B6A --> |獲取CPU核心數 number_cores_|B6A2(CpuInfo::DetectNumberOfCores)
B6A --> |創建channel管理對象 channel_manager_|B6A3(new ViEChannelManager)
B6A --> |創建視頻輸入設備管理對象 input_manager_|B6A4(new ViEInputManager)
B6A --> |創建視頻渲染管理對象 render_manager_| B6A5(new ViERenderManager)
B6A --> |創建并啟動模塊運行線程 module_process_thread_: ProcessThread| B6A6(ProcessThread::Create)
其中Impl類都為引擎API接口類,
用戶通過各API類GetInterface()獲取到的實際都是該引擎對象對API父類的轉換,比如:

ViEBase* ViEBase::GetInterface(VideoEngine* video_engine) {
  if (!video_engine) {
    return NULL;
  }
  VideoEngineImpl* vie_impl = static_cast<VideoEngineImpl*>(video_engine);
  ViEBaseImpl* vie_base_impl = vie_impl;
  (*vie_base_impl)++;  // Increase ref count.

  return vie_base_impl;
}

2. 創建視頻通道

函數:int ViEBaseImpl::CreateChannel(int& video_channel)
視頻通道創建涉及到各個視頻相關模塊創建及觀察者注冊等。
graph TD
A[ViEBaseImpl::CreateChannel] --> |vie_channel_manager.cc| B(ViEChannelManager::CreateChannel)
B --> |vie_channel_group.cc 創建通道組對象,管理remb,bitrateControler等| C(new ChannelGroup)
C --> D(ChannelGroup::CreateSendChannel)
D --> |創建視頻編碼管理對象| E(new ViEEncoder)
E --> |初始化ViEEncoder| F(vie_encoder->Init)
F --> |創建視頻通道| G(new ViEChannel)
G --> |初始化視頻通道| H(channel->Init)

2.1 通道組ChannelGroup

ChannelGroup內包含了以下模塊的創建:

1. VieRemb

Remb包發送模塊:負責將RemoteBitrateEstimator模塊的碼率預測值通過RTCP模塊反饋給遠端發送方以便調整遠端的發送碼率

2. BitrateAllocator

碼率變化分配模塊:作為BitrateController與ViEEncoder之間的橋梁,當BitrateController模塊碼率變化時,通過ViEEncoder在該模塊注冊的觀察者對象將變化的碼率值告訴ViEEncoder。具體函數為OnNetworkChanged()

ChannelGroup::ChannelGroup(ProcessThread* process_thread, const Config* config)
--> bitrate_allocator_(new BitrateAllocator())

調用流程

graph TD
A[BitrateControllerImpl::MaybeTriggerOnNetworkChanged] --> |observer_對象為ChannelGroup| B(observer_->OnNetworkChanged)
B --> |vie_channel_group.cc| C(ChannelGroup::OnNetworkChanged)
C --> D(bitrate_allocator_->OnNetworkChanged)
D --> |bitrate_allocator.cc| E(BitrateAllocator::OnNetworkChanged)
E --> |vie_encoder.cc| F(BitrateObserver::OnNetworkChanged)
F --> G(ViEEncoder::OnNetworkChanged)

BitrateAllocator中的BitrateObserver為ViEEncoder在執行ViEEncoder::SetEncoder()時調用bitrate_allocator_->AddBitrateObserver(bitrate_observer_.get(),...)時添加進去。

3. BitrateController

發送端碼率控制模塊:負責發送端碼率控制,綜合遠端Remb反饋的接收碼率與發送端根據丟包率等評估的碼率調整最終發送碼率。
該模塊繼承Module類,會被注冊到ProcessThread線程內運行,默認輪詢時間為25ms

ChannelGroup::ChannelGroup(ProcessThread* process_thread, const Config* config)
    // this即為ChannelGroup,該類繼承了BitrateObserver類,
    --> bitrate_controller_(
       BitrateController::CreateBitrateController(GetRealTimeClock(), this))
            // observer即為傳入的this
            --> new BitrateControllerImpl(clock, observer)
                --> observer_(observer)
    // 注冊模塊到ProcessThread線程內
    --> process_thread->RegisterModule(bitrate_controller_.get()) 

故BitrateController內observer_對象為ChannelGroup所繼承的BitrateObserver,
在碼率預測變化時會用到。
4. EncoderStateFeedback

編碼器狀態反饋模塊:反饋關鍵幀請求給ViEEncoder

// 構建
ChannelGroup::ChannelGroup(ProcessThread* process_thread, const Config* config)
    --> encoder_state_feedback_(new EncoderStateFeedback())

// 添加視頻編碼器
ChannelGroup::CreateSendChannel()
    --> encoder_state_feedback_->AddEncoder(ssrc, encoder)
    
// 將EncoderStateFeedback內部RtcpIntraFrameObserver觀察對象給
// 到ViEChannel,依次傳給RtpRtcp模塊
ChannelGroup::CreateChannel()
    --> new ViEChannel(..., encoder_state_feedback_->GetRtcpIntraFrameObserver(), ...)
        --> intra_frame_observer_(intra_frame_observer)
        --> ViEChannel::CreateRtpRtcpConfiguration()
            --> configuration.intra_frame_callback = intra_frame_observer_;
        //將RtcpIntraFrameObserver傳給RtpRtcp模塊
        --> rtp_rtcp_.reset(RtpRtcp::CreateRtpRtcp(configuration)); 
            --> ModuleRtpRtcpImpl::ModuleRtpRtcpImpl(const Configuration& configuration)
                 // 將RtcpIntraFrameObserver傳給RtcpReceiver模塊
                --> rtcp_receiver_(configuration.intra_frame_callback)
                    --> RTCPReceiver::RTCPReceiver
                        --> _cbRtcpIntraFrameObserver(rtcp_intra_frame_observer)
                        
因此,ViEChannel和RTCPReceiver模塊內都會有RtcpIntraFrameObserver對象
ViEChannel 內由解碼線程DecodingThread調用,更新解碼速度。目前該功能沒實現
graph TD
A[ViEChannel::ChannelDecodeProcess] --> |vie_channel.cc| B(intra_frame_observer_->SetVideoDecodingSpeed)
B --> |encoder_state_feedback.cc| C(EncoderStateFeedbackObserver::SetVideoDecodingSpeed)
本地SSRC變化
graph TD
A[RTCPReceiver::SetSsrcs] --> |rtcp_receiver.cc| B(_cbRtcpIntraFrameObserver->OnLocalSsrcChanged)
B --> |encoder_state_feedback.cc| C(EncoderStateFeedbackObserver::OnLocalSsrcChanged)
C --> D(EncoderStateFeedback::OnLocalSsrcChanged)
D --> E(encoder->OnLocalSsrcChanged)
E --> |vie_encoder.cc| F(ViEEncoder::OnLocalSsrcChanged)
視頻中的RTCP包

第一類:關鍵幀請求

主要包括SLI/PLI/FIR,作用是在關鍵幀丟失無法解碼時,請求發送方重新生成并發送一個關鍵幀。
這本質是一種重傳,但是跟傳輸層的重傳的區別是,它重傳是最新生成的幀。
PLI 是Picture Loss Indication,SLI 是Slice Loss Indication。
發送方接收到接收方反饋的PLI或SLI需要重新讓編碼器生成關鍵幀并發送給接收端。

FIR 是Full Intra Request,這里面Intra的含義可能很多人不知道。
Intra的含義是圖像內編碼,不需要其他圖像信息即可解碼;Inter指圖像間編碼,解碼需要參考幀。
故Intra Frame其實就是指I幀,Inter Frame指P幀或B幀。

那么為什么在PLI和SLI之外還需要一個FIR呢?
原因是使用場景不同,FIR更多是在一個中心化的Video Conference中,新的參與者加入,就需要發送一個FIR,其他的參與者給他發送一個關鍵幀這樣才能解碼,
而PLI和SLI的含義更多是在發生丟包或解碼錯誤時使用。

第二類:重傳請求
主要包括RTX/NACK/RPSI

這個重傳跟關鍵幀請求的區別是它可以要求任意幀進行重傳

第三類:碼率控制
主要包括REMB/TMMBR/TMMBN

TMMBR是Temporal Max Media Bitrate Request,表示臨時最大碼率請求。表明接收端當前帶寬受限,告訴發送端控制碼率。

REMB是ReceiverEstimated Max Bitrate,接收端估計的最大碼率。

TMMBN是Temporal Max Media Bitrate Notification

graph TD
A[RTCPReceiver::TriggerCallbacksFromRTCPPacket] --> |收到關鍵幀請求| A1(_cbRtcpIntraFrameObserver->OnReceivedIntraFrameRequest)
A1 --> |encoder_state_feedback.cc| A1A(EncoderStateFeedbackObserver::OnReceivedIntraFrameRequest)
A1A --> A1B(EncoderStateFeedback::OnReceivedIntraFrameRequest)
A1B --> |vie_encoder.cc| A1C(ViEEncoder::OnReceivedIntraFrameRequest)
A1C --> |video_coding_impl.cc| A1D(VideoCodingModuleImpl::IntraFrameRequest)
A1D --> |video_sender.cc| A1E(VideoSender::IntraFrameRequest)
A1E --> |generic_encoder.cc| A1F(VCMGenericEncoder::RequestFrame)
A1F --> A1G(H264EncoderImpl::Encode)

A --> |收到SLI幀請求| A2(_cbRtcpIntraFrameObserver->OnReceivedSLI)
A2 --> |encoder_state_feedback.cc| A2A(EncoderStateFeedbackObserver::OnReceivedSLI)
A2A --> A2B(EncoderStateFeedback::OnReceivedSLI)
A2B --> |vie_encoder.cc| A2C(ViEEncoder::OnReceivedSLI)

A --> |收到RPSI幀請求| A3(_cbRtcpIntraFrameObserver->OnReceivedRPSI)
A3 --> A3A(EncoderStateFeedbackObserver::OnReceivedRPSI)
A3A --> A3B(EncoderStateFeedback::OnReceivedRPSI)
A3B --> |vie_encoder.cc| A3C(ViEEncoder::OnReceivedRPSI)
5. RemoteBitrateEstimator

接收端碼率預測模塊:使用卡爾曼算法等進行碼率預測。
該模塊繼承Module類,會被注冊到ProcessThread線程內運行,默認輪詢時間為2000ms

6. CallStats

RTT值反饋模塊:將RTT值反饋到視頻JitterBuffer及RemoteBitrateEstimator模塊做相關調整。
該模塊繼承Module類,會被注冊到ProcessThread線程內運行,默認輪詢時間為1000ms,
即每1000ms會反饋一次RTT值給JitterBuffer和RemoteBitrateEstimator模塊。

ChannelGroup::ChannelGroup(ProcessThread* process_thread, const Config* config)
    --> call_stats_(new CallStats())
    // 添加RemoteBitrateEstimator為觀察者
    --> call_stats_->RegisterStatsObserver(remote_bitrate_estimator_.get());

    //注冊到ProcessThread線程內運行
    --> process_thread->RegisterModule(call_stats_.get());

傳遞到 RtpRtcp 模塊
ChannelGroup::CreateChannel()
    --> new ViEChannel(..., call_stats_->rtcp_rtt_stats(), ...)
        --> rtt_stats_(rtt_stats) // channel內rtt_stats賦值為CallStats內 RtcpRttStats 對象
        --> ViEChannel::CreateRtpRtcpConfiguration()
            --> configuration.rtt_stats = rtt_stats_
        --> rtp_rtcp_.reset(RtpRtcp::CreateRtpRtcp(configuration)) //該 rtt_stats_ 傳給 RtpRtcp 模塊
            --> ModuleRtpRtcpImpl::ModuleRtpRtcpImpl(const Configuration& configuration)
                --> rtt_stats_(configuration.rtt_stats) // RtpRtcp 模塊會擁有 CallStats 模塊的 RtcpRttStats 對象
    --> call_stats_->RegisterStatsObserver(channel->GetStatsObserver()) // 注冊channel內CallStatsObserver為觀察者
    
最終,CallStats模塊會有兩個觀察者,分別為 RemoteBitrateEstimator 和 ViEChannel 內 CallStatsObserver。

調用流程,首先由RtpRtcp模塊Process()輪詢執行上報RTT更新給CallStats,后由CallStats模塊在Process()輪詢時將更新的RTT分發給兩個觀察者。

graph TD
A[ModuleRtpRtcpImpl::Process] --> |rtp_rtcp_impl.cc| B(rtt_stats_->OnRttUpdate)
B --> |call_stats.cc| C(RtcpObserver::OnRttUpdate)
C --> D(CallStats::OnRttUpdate)
D --> E(CallStats::Process)
E --> |vie_channel.cc| E1(ChannelStatsObserver::OnRttUpdate)
E1 --> E1A(ViEChannel::OnRttUpdate)
E1A --> |video_coding_impl.cc| E1B(VideoCodingModuleImpl::SetReceiveChannelParameters)
E1B --> |video_receiver.cc| E1C(VideoReceiver::SetReceiveChannelParameters)
E1C --> |receiver.cc| E1D(VCMReceiver::UpdateRtt)
E1D --> |jitter_buffer.cc| E1E(VCMJitterBuffer::UpdateRtt)
E --> |vie_channel_group.cc| E2(WrappingBitrateEstimator::OnRttUpdate)
E2 --> |remote_bitrate_estimator_single_stream.cc| E2A(RemoteBitrateEstimatorImpl::OnRttUpdate)
E2A --> |aimd_rate_control.cc| E2B(AimdRateControl::SetRtt)

2.2 ViEEncoder視頻編碼模塊創建

ViEEncoder 負責創建維護VCM、VPM、PacedSender模塊。

VCM(VideoCodingModule), 視頻編碼器相關管理模塊。
VPM(VideoProcessingModule), 視頻前處理模塊。

PacedSender,負責視頻包有規律的發送,不至于因視頻幀太大,拆分的報文數量多導致網絡擁堵。

==注:PacedSender作為一個Module,并沒有注冊到ViEShareData啟動的
ModuleProcessThread處理隊列,而是在ViEEncoder里面獨自創建
PacedProcesssThread線程處理==

ChannelGroup::CreateSendChannel()
    // 創建ViEEncoder對象
    -->new ViEEncoder(..., process_thread_, bitrate_allocator_, bitrate_controller_, ...)
        --> ViEEncoder::ViEEncoder
            // VCM 模塊
            --> VideoCodingModule::Create(this)
            // VPM 模塊
            --> VideoProcessingModule::Create()
            // PacedProcesssThread處理線程,專門處理視頻有序發送模塊
            --> pacer_thread_(ProcessThread::Create())
            --> bitrate_observer_.reset(new ViEBitrateObserver(this))
            // 將BitrateController對象保存到ViEEncoder,主要用來在更新
            // BitrateController的最大最小及起始碼率
            --> bitrate_controller_(bitrate_controller)
            // this為ViEEncoder對象
            --> pacing_callback_.reset(new ViEPacedSenderCallback(this))
            --> paced_sender_.reset(new PacedSender(..., pacing_callback_ , ...))
1 VCM(VideoCodingModule)
2 VPM(VideoProcessingModule)
3 PacedSender

PacedSender模塊會被線程PacedProcessThread定時執行,需要發送視頻包時通過回調pacing_callback_將發包流程轉到ViEEncoder內執行。

ViEEncoder::ViEEncoder()
    --> pacer_thread_(ProcessThread::Create("PacerProcessThread"))
    --> pacing_callback_.reset(new ViEPacedSenderCallback(this));
    --> paced_sender_.reset(new PacedSender(
      Clock::GetRealTimeClock(),
      pacing_callback_.get(),
      kDefaultStartBitrateKbps,
      PacedSender::kDefaultPaceMultiplier * kDefaultStartBitrateKbps,
      0));
        --> PacedSender::PacedSender(..., callback, ...)
            --> callback_(callback)
graph TD
A[PacedSender::Process] --> |paced_sender.cc| A1(PacedSender::SendPacket)
A1 --> A1A(callback_->TimeToSendPacket)
A1A --> |vie_encoder.cc| A1B(ViEPacedSenderCallback::TimeToSendPacket)
A1B --> A1C(ViEEncoder::TimeToSendPacket)
A1C --> A1D(send_payload_router_->TimeToSendPacket)
A1D --> |payload_router.cc| A1E(PayloadRouter::TimeToSendPacket)
A1E --> A1F(rtp_module->TimeToSendPacket)
A1F --> |rtp_rtcp_impl.cc| A1G(ModuleRtpRtcpImpl::TimeToSendPacket)
A1G --> A1H(rtp_sender_.TimeToSendPacket)
A1H --> |rtp_sender.cc| A1I(RTPSender::TimeToSendPacket)
A1I --> A1J(packet_history_.GetPacketAndSetSendTime)
A1J --> A1K(RTPSender::PrepareAndSendPacket)
A1K --> A1L(RTPSender::SendPacketToNetwork)
A1L --> A1M(transport_->SendPacket)
A1M --> |vie_sender.cc| B(ViESender::SendPacket)

A[PacedSender::Process] --> |paced_sender.cc| A2(PacedSender::SendPadding)
A2 --> A2A(callback_->TimeToSendPadding)
A2A --> |vie_encoder.cc| A2B(ViEPacedSenderCallback::TimeToSendPadding)
A2B --> A2C(ViEEncoder::TimeToSendPadding)
A2C --> A2D(send_payload_router_->TimeToSendPadding)
A2D --> |payload_router.cc| A2E(PayloadRouter::TimeToSendPadding)
A2E --> A2F(rtp_module->TimeToSendPadding)
A2F --> |rtp_rtcp_impl.cc| A2G(ModuleRtpRtcpImpl::TimeToSendPadding)
A2G --> A2H(rtp_sender_.TimeToSendPadding)
A2H --> |rtp_sender.cc| A2I(RTPSender::TimeToSendPadding)
A2I --> A2J(RTPSender::TrySendPadData)
A2J --> A2K(RTPSender::SendPadData)
A2K --> A2L(RTPSender::SendPacketToNetwork)
A2L --> A2M(transport_->SendPacket)
A2M --> |vie_sender.cc| B

B --> |udp_transport_impl.cc| C(UdpTransportImpl::SendPacket)

2.3 ViEChannel創建

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,243評論 25 708
  • 在保證視頻圖像質量的前提下,HEVC通過增加一定的計算復雜度,可以實現碼流在H.264/AVC的基礎上降低50%。...
    加劉景長閱讀 7,993評論 0 6
  • 道路交通與網絡交通有很相似之處。就像道路上的車輛一樣,網絡分包也可能轉錯了彎,或者因為堵塞導致延遲。但是,網絡分包...
    befoio閱讀 2,393評論 0 4
  • 第一天跑1000米,第二天4000米,第三天6000米,這樣的事情,我一步一步做到了!
    思思培閱讀 222評論 0 0
  • 第一集里冰原狼與鹿的橫死慘劇是一個不祥之兆,一錯再錯的奈德已經將史塔克家族逼入絕境。這集的標題是“劍之尖端”,這句...
    李邦斯閱讀 315評論 0 0