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创建

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,060评论 25 707
  • 在保证视频图像质量的前提下,HEVC通过增加一定的计算复杂度,可以实现码流在H.264/AVC的基础上降低50%。...
    加刘景长阅读 7,865评论 0 6
  • 道路交通与网络交通有很相似之处。就像道路上的车辆一样,网络分包也可能转错了弯,或者因为堵塞导致延迟。但是,网络分包...
    befoio阅读 2,317评论 0 4
  • 第一天跑1000米,第二天4000米,第三天6000米,这样的事情,我一步一步做到了!
    思思培阅读 213评论 0 0
  • 第一集里冰原狼与鹿的横死惨剧是一个不祥之兆,一错再错的奈德已经将史塔克家族逼入绝境。这集的标题是“剑之尖端”,这句...
    李邦斯阅读 297评论 0 0