当前位置:网站首页>webrtc中的视频编码(一) 视频编码模块轮廓
webrtc中的视频编码(一) 视频编码模块轮廓
2022-08-04 05:25:00 【mo4776】
关于webrtc视频编码的分析,这将是一系列文章,主要从代码结构和设计思路两个方面去分析视频编码模块,不会进入代码细节,目的是在自己实现视频编码时可以从中借鉴。这篇文章是这个系列的第一篇,主要是介绍视频编码模块轮廓。
文章目录
在webrtc中视频编码功能是一列功能类相互协作实现的,因为编码功能不只涉及到将raw video数据编码,至少有如下几个功能:
- 根据配置或视频协商的结果初始化编码参数
- 根据带宽或配置,动态改变编码参数,比如动态改变编码码率,分辨率,帧率等
- 同时产生多码流
整个视频编码功能包括下面几个功能类:
- 视频编码接口类及相关工厂类
VideoEncoder
视频编码接口类,是一个抽象接口类。它有多个实例,编码h264 encoder,vp8/9 encoderVideoEncoderFactory
工厂类,用于创建VideoEncoder
的具体实例
- 具体的编码类
H264Encoder
h264编码接口类,有个Create
方法用于创建h264的编码类实例(好绕-_-!)H264EncoderImp
h264编码的具体实现,H264Encoder
的实例
- 编码器参数配置相关类
包括VideoStream
,VideoCode
,ViCodecInitializer
它们之间的关系在文章中再会讲解。
创建编码器
VideoEncoderFactory
创建编码器的工厂类,它是个抽象类,定义了创建编码器的方法
std::unique_ptr<VideoEncoder> CreateVideoEncoder(
const SdpVideoFormat& format);
BuiltinVideoEncoderFactory
和InternalEncoderFactory
是VideoEncoderFactory
的实现子类
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
的接口是面向业务,通过转调到InternalEncoderFactory
的CreateVideoEncoder
方法来创建具体的编码器。
创建编码器的调用堆栈
如下堆栈,创建编码器
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++
在VideoStreamEncoder
的ReconfigureEncoder
方法中创建编码器(这里的VideoStreamEncoder
类在video目录下video_stream_encoder.h文件中)
VideoEncoder
webrtc
中包括多种编码器,比如h264
,vp8
等,各个编码器的功能都类似,所以抽象出一个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
类型的形参。将VideoStream
和 VideoEncoderConfig
中配置信息转换成VideoCodec
后,再去设置到编码器中。
VideoStream
只是VideoCodec
的子集。如果是**simulcast **模式,VideoCodec
中包含多个VideoStream
的配置信息。
//这就是对应的videoStream的配置
SpatialLayer simulcastStream[kMaxSimulcastStreams];
VideoCodecInitalizer
将VideoEncoderConfig
和VideoStream
转换为VideoCodec
关系
这几个编码器参数配置相关的类,关系如下:
业务层根据配置或媒体协商的结果生成编码器参数VideoEncoderConfig
,再将VideoEncoderConfig
中的simulcast
信息取出生成VideoStream
,simulcast
每条码流对应一个VideoSteram
。VideoCodeceInitalizer
将VideoEncoderConfig
和VideoStream
参数配置信息转换成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
- 编码器支持的特性不同,所以编码器的参数配置往往是分层的,从抽象到具体,从粗到细。比如
VideoEncoderConfig
到VideoStream
到VideoCodec
,就是这种层次递进的情况 - webrtc中的编码器配置体系比较复杂,繁琐,这是因为它的功能特性决定的,它要支持不同编码器,支持simulcast和TemporalLayer,并且对simulcast中每条码流的帧率,码率,分辨率是根据带宽,cpu性能来动态适用,所以肯定需要额外的参数配置。如果我们自己实现视频编码功能,不需要这些特性,可以简化这种配置体系
边栏推荐
- 实现登录密码混合动态因子,且动态因子隐式
- 自动化测试的成本高效果差,那么自动化测试的意义在哪呢?
- Embedded system driver primary [4] - under the basis of character device driver _ concurrency control
- Typora 使用保姆级教程 | 看这一篇就够了 | 历史版本已被禁用
- MySQL log articles, binlog log of MySQL log, detailed explanation of binlog log
- Canal mysql data synchronization
- The cost of automated testing is high and the effect is poor, so what is the significance of automated testing?
- TensorRT例程解读之语义分割demo
- 触觉智能分享-SSD20X实现升级显示进度条
- 4.1 声明式事务之JdbcTemplate
猜你喜欢
随机推荐
Canal mysql data synchronization
JS basics - forced type conversion (error-prone, self-use)
动态规划总括
Unity自动生成阻挡Collider的GameObject工具
【论文阅读笔记】无监督行人重识别中的采样策略
3面头条,花7天整理了面试题和学习笔记,已正式入职半个月
7.16 Day22---MYSQL (Dao mode encapsulates JDBC)
TensorRT例程解读之语义分割demo
LCP 17. Quick Calculation Robot
Camera2 闪光灯梳理
npm init [email protected] 构建项目报错SyntaxError: Unexpected token ‘.‘解决办法
代码重构:面向单元测试
MySql data recovery method personal summary
MySQL date functions
Sublime Text 3 2021.8.3 个人配置
力扣:746. 使用最小花费爬楼梯
OpenRefine开源数据清洗软件的GREL语言
想好了吗?
7.18 Day23 - the markup language
(Kettle) pdi-ce-8.2 连接MySQL8.x数据库时驱动问题之终极探讨及解决方法分析