webrtc中的视频编码(一) 视频编码模块轮廓

关于webrtc视频编码的分析,这将是一系列文章,主要从代码结构和设计思路两个方面去分析视频编码模块,不会进入代码细节,目的是在自己实现视频编码时可以从中借鉴。这篇文章是这个系列的第一篇,主要是介绍视频编码模块轮廓。


在webrtc中视频编码功能是一列功能类相互协作实现的,因为编码功能不只涉及到将raw video数据编码,至少有如下几个功能:

  1. 根据配置或视频协商的结果初始化编码参数
  2. 根据带宽或配置,动态改变编码参数,比如动态改变编码码率,分辨率,帧率等
  3. 同时产生多码流

整个视频编码功能包括下面几个功能类:

  • 视频编码接口类及相关工厂类

VideoEncoder视频编码接口类,是一个抽象接口类。它有多个实例,编码h264 encoder,vp8/9 encoder
VideoEncoderFactory工厂类,用于创建VideoEncoder的具体实例

  • 具体的编码类

H264Encoder h264编码接口类,有个Create方法用于创建h264的编码类实例(好绕-_-!)
H264EncoderImp h264编码的具体实现,H264Encoder的实例

  • 编码器参数配置相关类

包括VideoStreamVideoCodeViCodecInitializer 它们之间的关系在文章中再会讲解。

创建编码器

VideoEncoderFactory

创建编码器的工厂类,它是个抽象类,定义了创建编码器的方法

std::unique_ptr<VideoEncoder> CreateVideoEncoder(
      const SdpVideoFormat& format);

BuiltinVideoEncoderFactoryInternalEncoderFactoryVideoEncoderFactory的实现子类

在这里插入图片描述

BuiltinVideoEncoderFactory类中有一个InternalEncoderFactory的成员变量,具体的创建编码器的工作在InternalEncoderFactory`中完成,如下代码:

std::unique_ptr<VideoEncoder> InternalEncoderFactory::CreateVideoEncoder(
    const SdpVideoFormat& format) {
    
    
  if (absl::EqualsIgnoreCase(format.name, cricket::kVp8CodecName))
    return VP8Encoder::Create();
  if (absl::EqualsIgnoreCase(format.name, cricket::kVp9CodecName))
    return VP9Encoder::Create(cricket::VideoCodec(format));
  if (absl::EqualsIgnoreCase(format.name, cricket::kH264CodecName))
    return H264Encoder::Create(cricket::VideoCodec(format));
  if (kIsLibaomAv1EncoderSupported &&
      absl::EqualsIgnoreCase(format.name, cricket::kAv1CodecName))
    return CreateLibaomAv1Encoder();
  RTC_LOG(LS_ERROR) << "Trying to created encoder of unsupported format "
                    << format.name;
  return nullptr;
}

VideoEncoderFactory的接口是面向业务,通过转调到InternalEncoderFactoryCreateVideoEncoder方法来创建具体的编码器。

创建编码器的调用堆栈

如下堆栈,创建编码器

peerconnection_client.exe!webrtc::anonymous namespace'::BuiltinVideoEncoderFactory::CreateVideoEncoder() 行 59 C++ peerconnection_client.exe!webrtc::VideoStreamEncoder::ReconfigureEncoder() 行 633 C++ peerconnection_client.exe!webrtc::VideoStreamEncoder::MaybeEncodeVideoFrame() 行 1264 C++ peerconnection_client.exe!webrtc::VideoStreamEncoder::OnFrame(const webrtc::VideoFrame &)::(anonymous class)::operator()() 行 1046 C++ peerconnection_client.exe!webrtc::webrtc_new_closure_impl::ClosureTask<lambda at …/…/video/video_stream_encoder.cc:1032:7’>::Run() 行 33 C++
peerconnection_client.exe!webrtc::anonymous namespace'::TaskQueueWin::RunPendingTasks() 行 272 C++ peerconnection_client.exe!webrtc::anonymous namespace’::TaskQueueWin::RunThreadMain() 行 285 C++
peerconnection_client.exe!webrtc::`anonymous namespace’::TaskQueueWin::ThreadMain() 行 280 C++
peerconnection_client.exe!rtc::PlatformThread::Run() 行 130 C++
peerconnection_client.exe!rtc::PlatformThread::StartThread() 行 62 C++

VideoStreamEncoderReconfigureEncoder方法中创建编码器(这里的VideoStreamEncoder类在video目录下video_stream_encoder.h文件中)

VideoEncoder

webrtc中包括多种编码器,比如h264vp8等,各个编码器的功能都类似,所以抽象出一个VideoEncoder的基类,各个不同格式的编码器是它的具体子类。如下类图(这里只列出了H264Encoder子类)

在这里插入图片描述

编码器的配置体系

前面提到过,编码器会有编码参数,这些参数信息可能是业务配置产生,也可能是媒体能力协商时产生,最终都需要设置到编码器中,所以通常会有一套配置功能类,来管理这些参数。

VideoEncoderConfig

与业务直接打交道的配置类VideoEncoderConfig

VideoStream

The |VideoStream| struct describes a simulcast layer, or “stream”

按照它的注释描述,代表了一条码流(的配置)。它包含了码流的一些基本参数,是VideoEncoderConfig中的配置信息的进一步细化。

struct VideoStream {
    
    
  VideoStream();
  ~VideoStream();
  VideoStream(const VideoStream& other);
  std::string ToString() const;
  // Width in pixels.
  size_t width;
  // Height in pixels.
  size_t height;
  // Frame rate in fps.
  int max_framerate;
  // Bitrate, in bps, for the stream.
  int min_bitrate_bps;
  int target_bitrate_bps;
  int max_bitrate_bps;
  double scale_resolution_down_by;
  // Maximum Quantization Parameter to use when encoding the stream.
  int max_qp;
  absl::optional<size_t> num_temporal_layers;
  absl::optional<double> bitrate_priority;
  // If this stream is enabled by the user, or not.
  bool active;
};

包含分辨率,最大/小码率,qp值等,num_temporal_layers代表时间域的层数,就是帧率,在支持temporal layer的编码器中(比如vp8/9),就是一条码流包含不同的帧率。
simulcast 是指同时产生多条不同分辨率,帧率,码率的码流,一个VideoStream代表了其中一条码流的配置。

VideoCodec

Common video codec properties

VideoCodec是编码器的配置信息,在InitEncode方法中需要VideoCodec类型的形参。将VideoStreamVideoEncoderConfig中配置信息转换成VideoCodec后,再去设置到编码器中。

VideoStream只是VideoCodec的子集。如果是**simulcast **模式,VideoCodec中包含多个VideoStream的配置信息。

//这就是对应的videoStream的配置
SpatialLayer simulcastStream[kMaxSimulcastStreams];

VideoCodecInitalizer

VideoEncoderConfigVideoStream转换为VideoCodec

关系

这几个编码器参数配置相关的类,关系如下:
编码器参数设置.png

业务层根据配置或媒体协商的结果生成编码器参数VideoEncoderConfig,再将VideoEncoderConfig中的simulcast信息取出生成VideoStreamsimulcast每条码流对应一个VideoSteramVideoCodeceInitalizerVideoEncoderConfigVideoStream参数配置信息转换成VideoCodec中配置信息,VideoCodec会用于初始化VideoEncoder
具体的参数转换策略可以看看,VideoStreamFactoryInterface类中的CreateEncoderStreams方法

std::vector<VideoStream> CreateEncoderStreams(
        int width,
        int height,
        const VideoEncoderConfig& encoder_config)

VideoCodecInitializer类中SetupCodec方法

bool SetupCodec(const VideoEncoderConfig& config,
                         const std::vector<VideoStream>& streams,
                         VideoCodec* codec)

VideoStreamEncoder类中的ReconfigureEncoder方法中,有从创建编码器到转换编码器配置的整个流程,可以看看具体调用方法。

总结

  • 不同编码格式的编码器功能基本一致,通常是会抽象出一个基类,比如VideoEncoder
  • 编码器支持的特性不同,所以编码器的参数配置往往是分层的,从抽象到具体,从粗到细。比如VideoEncoderConfigVideoStreamVideoCodec,就是这种层次递进的情况
  • webrtc中的编码器配置体系比较复杂,繁琐,这是因为它的功能特性决定的,它要支持不同编码器,支持simulcastTemporalLayer,并且对simulcast中每条码流的帧率,码率,分辨率是根据带宽,cpu性能来动态适用,所以肯定需要额外的参数配置。如果我们自己实现视频编码功能,不需要这些特性,可以简化这种配置体系

猜你喜欢

转载自blog.csdn.net/mo4776/article/details/125675528