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());
}123

当接收到第一帧视频数据后,首先会进入 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();
}12345678910111213

在执行 VideoReceiveStream 对象的构造函数时,生成了一个 PlatformThread 类型的对象 decode_thread_,这就是视频解码线程。视频解码线程 decode_thread_ 的入口函数为 DecodeThreadFunction() ,传入的参数为一个 VideoReceiveStream  对象指针。

 

void VideoReceiveStream::DecodeThreadFunction(void* ptr) {
  while (static_cast<VideoReceiveStream*>(ptr)->Decode()) {
  }
}1234

执行 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());
}12345678910

在 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);
...
}12345678910

在 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);
}1234567

在 VCMDecodedFrameCallback  的 Decoded() 成员函数中, 执行了以下代码:


  _receiveCallback->FrameToRender(decodedImage, qp, frameInfo->content_type);


receiveCallback 的类型为 VCMReceiveCallback,其实际指向为子类对象 VideoStreamDecoder,在子类中有 ReceiveStatisticsProxy  类型的数据成员 receive_stats_callback,主要功能是实现接收数据后的参数统计。
--------------------- 
作者:zhuiyuanqingya 
来源:CSDN 
原文:https://blog.csdn.net/zhuiyuanqingya/article/details/81639007 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值