chromium中 webrtc 视频解码过程分析

备注:本文的 webrtc 代码为 chromium (64) 代码中的第三方库。

1.解码

webrtc 默认采用的编码格式为 vp8,所以解码对应的解码方法也是 vp8.
从视频 channel 接收到视频数据到执行解码算法的类图如图所示:
这里写图片描述
图中绿色类代表类图分析的起点,红色类代表分析类的终点。

类 BaseChannel 有一个 MediaChannel 的数据成员 media_channel_ , 在子类 VideoChannel 中,这个数据成员被转换为 VideoMediaChannel 对象。
而 VideoMediaChannel 为纯虚基类, WebRtcVideoChannel 为其子类,所以最终 media_channel_ 指向了 WebRtcVideoChannel 的对象。

VideoMediaChannel* media_channel() const override {
    return static_cast<VideoMediaChannel*>(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<VideoReceiveStream*>(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() 函数。

2.解码后的回调

在解码完毕之后,会执行一些回调操作,根据类名以及函数名字分析,大概是渲染以及数据统计相关的操作。

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,主要功能是实现接收数据后的参数统计。

猜你喜欢

转载自blog.csdn.net/zhuiyuanqingya/article/details/81639007