WebRTC之視頻採集

基於WebRTC的實時音視頻會議中對於視頻處理流水,第一級就是視頻採集,視頻內容可以攝像頭、屏幕和視頻文件,視頻來源的操作系統可以是Linux、Windows、Mac,IOS以及Android,不同的平臺由不同的公司開發設計,因而他們從camera獲取視頻的底層框架並不一樣,Linux系統使用V4L2(Video for Linux Version 2),Mac和IOS都是蘋果公司開發的,都使用AVFoundation框架,Windows使用的是微軟開發的DS(Direct Show)框架,Android使用camera2.0接口(Camera2Capturer)採集視頻。

視頻設備封裝

視頻採集的通用代碼位於modules/video_capture目錄,該目錄下的文件組織結構如下圖:

視頻採集使用c++的類和對象的封裝思想,不論什麼平臺都有啓動採集(StartCapture)和停止採集(StopCapture)等類似的視頻採集控制功能需求,這樣上層在調用啓動採集時可以只調用StartCapture方法,而忽略具體的平臺細節,圖中的video_capture.h頭文件定義了視頻採集的虛基類VideoCaptureModule。linux和windows目錄定義了調用操作系統API進行視頻採集平臺相關的代碼,由於Android和評估平臺的提供系統API分別是基於Java和Object C所以沒有放在這個目錄,WebRTC提供了sdk目錄用於存放和APPLE和Android平臺相關的代碼,sdk/objc/components/capturer和sdk/android/api/org/webrtc兩個目錄。爲了簡便性,這裏僅分析視頻採集的架構,具體平臺細節這裏不再討論。

虛基類中的Start/StopCapture用於開始和結束數據採集,Register/DeCaptureDataCallback用於註冊和註銷數據回調模塊,數據回調模塊用於把視頻數據推送給上層,而不需要上層輪訓底層有沒有視頻數據,這樣效率上會高。

VideoCaptureImpl是虛基類VideoCaptureModule實現的子類,其實現了子類的各種方法,並且也定義了通用平臺無關的一些成員變量,對於平臺相關在平臺相關的類中實現,也就是VideoCaptureImpl會根據不同的平臺泛化成對應的具體實現,它們之間的UML關係如下圖:

視頻採集過程

各個平臺採集到數據之後,會調用VideoCaptureImpl類中的IncomingFrame方法將數據通過DeliverCapturedFrame傳遞給上層。控制流程上視頻採集模塊在初始階段由上層創建並StartCapture,結束階段由上層調用StopCapture停止並銷燬改模塊,數據流階段採集到的數據通過回調的(高效方式)傳遞給上層回顯和編碼環節以進一步處理。其控制路和數據流如下:

WebRTC自帶的PeerConnection例程採集視頻過程如下:

1.VideoCaptureFactory類可用於創建VideoCaptureModule實例,當然也可自己include相應頭文件直接創建;

2.一個系統可以同時連接多個camera,這可以通過NumberOfDevices獲取攝像頭數目,並循環調用GetDeviceName或得每個攝像頭的信息,可以通過將攝像頭信息(通常是名字或和UUID相關的字段)回顯到UI上供用戶自己選擇;

3.WebRtcVideoDeviceCapturerFactory和create創建VideoCaptureModule實例,這調用了對應平臺的Create方法,如Linux平臺創建的是VideoCaptureModuleV4L2對象,代碼如下:

rtc::scoped_refptr<VideoCaptureModule> VideoCaptureFactory::Create(
    const char* deviceUniqueIdUTF8) {
#if defined(WEBRTC_ANDROID) || defined(WEBRTC_MAC)
  return nullptr;
#else
  return videocapturemodule::VideoCaptureImpl::Create(deviceUniqueIdUTF8);
#endif
}

4.根據視頻幀率、分辨率等選擇最合適的一個,這可以通過UI回顯給用戶選擇,當然也會根據系統的負載和網絡情況綜合選擇最優的視頻參數;

5.調用RegisterCaptureDataCallback註冊採集到視頻數據幀的回調函數,這裏的this就是其本身,所以這個函數裏重寫了OnFrame函數。

void VcmCapturer::OnFrame(const VideoFrame& frame) {
  TestVideoCapturer::OnFrame(frame);
}

採集之後的回顯和編碼在後續文章再敘述,這裏不做展開。

6.啓動視頻傳輸,這裏調用了StartCapture方法,該方法最終調用到具體的平臺代碼。

視頻捕獲過程

上一節得到了一幀視頻數據,爲了便於管理,如編碼,回顯等,需要在應用層和底層一幀數據做一個橋接層,以隔離上下層。橋接層的代碼主要位於media/base 和media/engine,WebRTC中抽象了VideoSourceInterface和VideoSinkInterface這兩個模板類分別表示產生和吸收視頻流。

// api/video/video_source_interface.h
template <typename VideoFrameT>
class VideoSourceInterface {
 public:
  virtual ~VideoSourceInterface() = default;

  virtual void AddOrUpdateSink(VideoSinkInterface<VideoFrameT>* sink,
                               const VideoSinkWants& wants) = 0;
  // RemoveSink must guarantee that at the time the method returns,
  // there is no current and no future calls to VideoSinkInterface::OnFrame.
  virtual void RemoveSink(VideoSinkInterface<VideoFrameT>* sink) = 0;
};

// api/video/video_sink_interface.h
template <typename VideoFrameT>
class VideoSinkInterface {
 public:
  virtual ~VideoSinkInterface() = default;

  virtual void OnFrame(const VideoFrameT& frame) = 0;

  // Should be called by the source when it discards the frame due to rate
  // limiting.
  virtual void OnDiscardedFrame() {}
};

如果能提供視頻數據,需要實現 VideoSourceInterface,此接口類暴露了 AddOrUpdateSink 函數,可以將 Sink 註冊給 Source。

如果想要接收視頻數據,需要實現 VideoSinkInterface,此接口類暴露了 OnFrame 函數。只要將 Sink 通過 AddOrUpdateSink 函數註冊給 Source,那麼 Source 就會通過 OnFrame 接口將數據傳給 Sink。對於WebRTC的PeerConnection例子,其VcmCapturer類繼承了VideoSinkInterface。

class VcmCapturer : public TestVideoCapturer,
                    public rtc::VideoSinkInterface<VideoFrame> {
 public:
  static VcmCapturer* Create(size_t width,
                             size_t height,
                             size_t target_fps,
                             size_t capture_device_index);
  virtual ~VcmCapturer();

  void OnFrame(const VideoFrame& frame) override;

 private:
  VcmCapturer();
  bool Init(size_t width,
            size_t height,
            size_t target_fps,
            size_t capture_device_index);
  void Destroy();

  rtc::scoped_refptr<VideoCaptureModule> vcm_;
  VideoCaptureCapability capability_;
};

VcmCapturer實現了實現了 VideoSinkInterface 接口,在初始化該對象時,會調用RegisterCaptureDataCallback將自身註冊給視頻設備管理層,當視頻設備採集到一幀數據時,會調用註冊對象(VcmCapturer)的OnFrame方法。

VideoSourceBase保存了註冊給VcmCapturer的所有sink,因此其實現了VideoSourceInterface作爲一個視頻源,VideoBroadCaster繼承了VideoSourceBase類,同時實現了VideoSinkInterface,其作用是VcmCapturer接受數據,然後分發給所有向其註冊的Sink。

CaptureTrackSource作爲視頻源添加到VideoTrack組合到MediaStream中,MediaStream最終會添加到PeerConnection中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章