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/TMMBNTMMBR是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)