webrtc中视频采集实现分析(二) 视频帧的分发

需求

在上一篇介绍了webrtc中的视频采集模块的接口。视频采集模块抛出视频数据后,对视频帧的处理需求一般包括如下几种:

  1. 视频帧送到编码模块进行编码
  2. 视频帧送到渲染模块进行本地回显
  3. 视频帧先进行预处理(比如添加水印,字幕),再送去回显或编码
  4. 视频帧会送到多个编码器,产生多路不同分辨率,码率的码流

相当于一个“源头”有多个“支流”,通过回调拿到的视频帧数据就是“源头”,如上的各种需求就是“支流”。视频帧数据要按需分发,满足各个“支流”的需要。那么在代码中的体现就是一个source,多个sink。

分发框架涉及到类

webrtc中有一个对视频帧的分发框架,可以直接复用,包括videoSourceInterfacevideoSinkInterfaceVideoBroadcaster

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代表视频采集模块,它到代表了总的数据“源头”,通过回调将视频帧数据给VideoBroadcasterVideoBroadcaster再将视频帧分发到各个sink。

前一篇文章中 VideoCaptureModule有个回调注册接口void RegisterCaptureDataCallback(rtc::VideoSinkInterface<VideoFrame>* dataCallback),可以将VideoBroadcaster作为sink注册。

需要注意的是,VideoBoradcaster并没包含VideoAdpater类,它没有适配功能,只是单纯的分发。如果要对视频帧数据进行适配就需要在VideoBoradcaster得前面或后面加上VideoAdpater

变化

webrtc中的VideoBoradcaster的用途是有局限的,它并不能实现不同帧率,不同分辨率的分发(上面提到它会对所有的sink wants做合并,取sink wants中的帧率,分辨率的最小值)。它适用于对帧的用途的分发,比如有的被送去编码,有的被送去本地回显,都是同一种分辨率和帧率。
所以上面的这个组合图,是并不能满足产生多路不同分辨率,帧率的码流场景。

考虑这样一个需求:

  1. 能产生三路码流,它们有不同的分辨率,帧率
  2. 能本地回显

所以需要像下面这种方式来进行组合了
在这里插入图片描述
videocatpure采集的视频是30fps,720P。需要产生三路码流(有三个编码器对象)和本地回显,VideoAapter不能放在VideoBroadcaster前面,因为VideoBroadcaster只能输出同样的帧率和分辨率。需要在video encoder前面放一个VideoAapter,满足帧率,分辨率的要求。

VideoAapter在满足帧率时,会进行丢帧处理,所以这三个VideoAapter+Video encoder应该是分别在一个线程中。

webrtc中的例子

在例子peer_conection_client的实现中,是通过TestVideoCapturer类集成了VideoAdapterVideoBroadcaster,可以参考下如何实现将它们连接起来的。但是在浏览器的实现中应该不通过TestVideoCapturer实现的,因为它的实现是满足不了需求多路不同分辨率,帧率的码流的。

猜你喜欢

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