文章目录
需求
在上一篇介绍了webrtc中的视频采集模块的接口。视频采集模块抛出视频数据后,对视频帧的处理需求一般包括如下几种:
- 视频帧送到编码模块进行编码
- 视频帧送到渲染模块进行本地回显
- 视频帧先进行预处理(比如添加水印,字幕),再送去回显或编码
- 视频帧会送到多个编码器,产生多路不同分辨率,码率的码流
相当于一个“源头”有多个“支流”,通过回调拿到的视频帧数据就是“源头”,如上的各种需求就是“支流”。视频帧数据要按需分发,满足各个“支流”的需要。那么在代码中的体现就是一个source,多个sink。
分发框架涉及到类
webrtc中有一个对视频帧的分发框架,可以直接复用,包括videoSourceInterface
,videoSinkInterface
,VideoBroadcaster
videoSourceInterface
作为source的基类,指“源头”
- 主要接口是
AddOrUpdateSink()
和RemoveSink()
,videoSourceInterface
可以添加多个sink AddOrUpdateSink
接口的第二个参数是VideoSinkWants
结构体,下面是它的定义
struct VideoSinkWants {
VideoSinkWants();
VideoSinkWants(const VideoSinkWants&);
~VideoSinkWants();
// Tells the source whether the sink wants frames with rotation applied.
// By default, any rotation must be applied by the sink.
bool rotation_applied = false;
// Tells the source that the sink only wants black frames.
bool black_frames = false;
// Tells the source the maximum number of pixels the sink wants.
int max_pixel_count = std::numeric_limits<int>::max();
// Tells the source the desired number of pixels the sinks wants. This will
// typically be used when stepping the resolution up again when conditions
// have improved after an earlier downgrade. The source should select the
// closest resolution to this pixel count, but if max_pixel_count is set, it
// still sets the absolute upper bound.
absl::optional<int> target_pixel_count;
// Tells the source the maximum framerate the sink wants.
int max_framerate_fps = std::numeric_limits<int>::max();
// Tells the source that the sink wants width and height of the video frames
// to be divisible by |resolution_alignment|.
// For example: With I420, this value would be a multiple of 2.
// Note that this field is unrelated to any horizontal or vertical stride
// requirements the encoder has on the incoming video frame buffers.
int resolution_alignment = 1;
};
主要就是描述sink期望的分辨率,帧率等,上面的注释也写的很明白了。
videoSinkInterface
作为sink的基类
主要接口是OnFrame()
和OnDiscardeFrame()
这两个类就代表着source和sink,一个source中可以添加多个sink,source通过调用sink中的OnFrame方法,将视频帧数据传给sink
VideoBroadcaster 视频帧分发类
- VideoBroadcaster
类,看名字就知道是视频帧分发类。上面的类图可以看到,VideoSourceBase
既是一个source也是一个sink。
-
VideoBroadcaster
可以放入对个sink,但它会对所有的sink集合的want做个合并,取的帧率帧率值是sink want中的最小值,分辨率的值也是sink want中的最小值 -
但是source提供的视频帧不满足sink时就需要对视频帧数据做适配了,那么
VideoAdpater
就是这样的作用了
VideoAdpater
VideoAdpater
相当于一个filter,对source中的视频帧进行过滤,来满足sink的要求
VideoAdpater
可以实现对帧率的过滤和对分辨率的缩放
基本框架
通过上面的这几个类的关系,就拼出了一个视频帧分发的框架
videocaputre
代表视频采集模块,它到代表了总的数据“源头”,通过回调将视频帧数据给VideoBroadcaster
,VideoBroadcaster
再将视频帧分发到各个sink。
前一篇文章中 VideoCaptureModule
有个回调注册接口void RegisterCaptureDataCallback(rtc::VideoSinkInterface<VideoFrame>* dataCallback)
,可以将VideoBroadcaster
作为sink注册。
需要注意的是,VideoBoradcaster
并没包含VideoAdpater
类,它没有适配功能,只是单纯的分发。如果要对视频帧数据进行适配就需要在VideoBoradcaster
得前面或后面加上VideoAdpater
。
变化
webrtc中的VideoBoradcaster
的用途是有局限的,它并不能实现不同帧率,不同分辨率的分发(上面提到它会对所有的sink wants做合并,取sink wants中的帧率,分辨率的最小值)。它适用于对帧的用途的分发,比如有的被送去编码,有的被送去本地回显,都是同一种分辨率和帧率。
所以上面的这个组合图,是并不能满足产生多路不同分辨率,帧率的码流场景。
考虑这样一个需求:
- 能产生三路码流,它们有不同的分辨率,帧率
- 能本地回显
所以需要像下面这种方式来进行组合了
videocatpure采集的视频是30fps,720P。需要产生三路码流(有三个编码器对象)和本地回显,VideoAapter
不能放在VideoBroadcaster
前面,因为VideoBroadcaster
只能输出同样的帧率和分辨率。需要在video encoder前面放一个VideoAapter
,满足帧率,分辨率的要求。
VideoAapter
在满足帧率时,会进行丢帧处理,所以这三个VideoAapter+Video encoder
应该是分别在一个线程中。
webrtc中的例子
在例子peer_conection_client
的实现中,是通过TestVideoCapturer
类集成了VideoAdapter
和VideoBroadcaster
,可以参考下如何实现将它们连接起来的。但是在浏览器的实现中应该不通过TestVideoCapturer
实现的,因为它的实现是满足不了需求多路不同分辨率,帧率的码流的。