备注:本文的 webrtc 代码为 chromium (64) 代码中的第三方库。
webrtc 默认采用的编码格式为 vp8,所以解码对应的解码方法也是 vp8.
从视频 channel 接收到视频数据到执行解码算法的类图如图所示:
图中绿色类代表类图分析的起点,红色类代表分析类的终点。
类 BaseChannel 有一个 MediaChannel 的数据成员 media_channel_ , 在子类 VideoChannel 中,这个数据成员被转换为 VideoMediaChannel 对象。
而 VideoMediaChannel 为纯虚基类, WebRtcVideoChannel 为其子类,所以最终 media_channel_ 指向了 WebRtcVideoChannel 的对象。
VideoMediaChannel* media_channel() const override {
return static_cast(BaseChannel::media_channel());
}
当接收到第一帧视频数据后,首先会进入 VideoChannel 类,执行 SetRemoteContent_w() 函数,这个函数会调用 BaseChannel 类的 UpdateRemoteStreams_w() 函数,这个函数内部调用 BaseChannel 类的 AddRecvStream_w() 函数,在函数 AddRecvStream_w() 内部,调用 WebRtcVideoChannel 类的 AddRecvStream() 函数。
在 AddRecvStream() 函数中,创建 WebRtcVideoReceiveStream 对象,在执行 WebRtcVideoReceiveStream 的构造函数时,执行 RecreateWebRtcVideoStream() 函数,在该函数内部通过调用创建代码,生成了一个 VideoReceiveStream 类型的对象 stream_ 。
void WebRtcVideoChannel::WebRtcVideoReceiveStream::
RecreateWebRtcVideoStream() {
if (stream_) {
MaybeDissociateFlexfecFromVideo();
call_->DestroyVideoReceiveStream(stream_);
stream_ = nullptr;
}
webrtc::VideoReceiveStream::Config config = config_.Copy();
config.rtp.protected_by_flexfec = (flexfec_stream_ != nullptr);
stream_ = call_->CreateVideoReceiveStream(std::move(config));
MaybeAssociateFlexfecWithVideo();
stream_->Start();
}
在执行 VideoReceiveStream 对象的构造函数时,生成了一个 PlatformThread 类型的对象 decode_thread_,这就是视频解码线程。视频解码线程 decode_thread_ 的入口函数为 DecodeThreadFunction() ,传入的参数为一个 VideoReceiveStream 对象指针。
void VideoReceiveStream::DecodeThreadFunction(void* ptr) {
while (static_cast(ptr)->Decode()) {
}
}
执行 stream_ 的 Start() 函数时,经过一系列准备动作,会启动 decode_thread_ 线程,在视频解码线程内部循环执行 VideoReceiveStream 的 Decode() 函数。在该函数内部,具体执行了 VideoReceiver 类型的对象 video_receiver_ 的 Decode() 函数,函数实现如下。
int32_t VideoReceiver::Decode(const VCMEncodedFrame& frame) {
TRACE_EVENT0("webrtc", "VideoReceiver::Decode");
// Change decoder if payload type has changed
VCMGenericDecoder* decoder =
_codecDataBase.GetDecoder(frame, &_decodedFrameCallback);
if (decoder == nullptr) {
return VCM_NO_CODEC_REGISTERED;
}
return decoder->Decode(frame, clock_->TimeInMilliseconds());
}
在 VideoReceiver 内部,有一个 VCMCodecDataBase 类型的对象 _codecDataBase ,_codecDataBase 会根据接收到的视频帧的内容选择对应的解码器,最后根据选择的解码器执行对应的 Decode() 函数。
在 VCMCodecDataBase 内部,会根据视频帧的内容创建一个 VCMGenericDecoder 对象,在 VCMGenericDecoder 内部有一个 VideoDecoder 类型的数据成员 decoder_,实际指向的是 VP8DecoderImpl 类型对象。
最终,调用的解码算法是 VP8DecoderImpl 里面的 Decode() 函数。
在解码完毕之后,会执行一些回调操作,根据类名以及函数名字分析,大概是渲染以及数据统计相关的操作。
VideoDecoder 类有一个成员函数 RegisterDecodeCompleteCallback(DecodedImageCallback* callback) ,在解码完毕后会调用这个回调函数。
int VP8DecoderImpl::Decode(const EncodedImage& input_image,
bool missing_frames,
const RTPFragmentationHeader* fragmentation,
const CodecSpecificInfo* codec_specific_info,
int64_t /*render_time_ms*/) {
// 解码过程
...
ret = ReturnFrame(img, input_image._timeStamp, input_image.ntp_time_ms_, qp);
...
}
在 ReturnFrame() 函数中,执行了 DecodedImageCallback 类型的对象 decode_complete_callback_ 回调函数, decode_complete_callback_ 实际指向的是 VCMDecodedFrameCallback 类型的对象。
int VP8DecoderImpl::ReturnFrame(const vpx_image_t* img,
uint32_t timestamp,
int64_t ntp_time_ms,
int qp) {
...
decode_complete_callback_->Decoded(decoded_image, rtc::nullopt, qp);
}
在 VCMDecodedFrameCallback 的 Decoded() 成员函数中, 执行了以下代码:
_receiveCallback->FrameToRender(decodedImage, qp, frameInfo->content_type);
receiveCallback 的类型为 VCMReceiveCallback,其实际指向为子类对象 VideoStreamDecoder,在子类中有 ReceiveStatisticsProxy 类型的数据成员 receive_stats_callback,主要功能是实现接收数据后的参数统计。