WebRTC手記之WebRtcVideoEngine2模塊

終於講到視頻數據的編碼發送模塊了,不容易。總體來說也看了不少時間WebRTC的源碼了,最大的感觸就是各個模塊在開發的時候非常獨立,每個模塊都定義了自己的一套接口,最後串起來的時候添加各種適配對象來轉接。這給我們這些剛開始源碼閱讀的人帶來非常大的苦惱,不過WebRTC的模塊內的結構設計還是很不錯的,不然我也沒有看下去的動力。

注意命名,WebRtcVideoEngine2帶了個2字,不用想,這肯定是個升級版本的VideoEngine,還有個WebRtcVideoEngine類。從目前我的理解來看,WebRtcVideoEngine2比WebRtcVideoEngine改進之處在於將視頻流一分爲二:發送流(WebRtcVideoSendStream)和接收流(WebRtcVideoReceiveStream),從而結構上更合理,源碼更清晰。這個部分等下會細說。在介紹WebRtcVideoEngine2之前,先簡單地分析一下WebRTC的Media Engine結構,說實話,我真不會表達Engine是個怎樣的概念,但既然這樣命名,核心模塊肯定是錯不了的。結構很簡單:

 

  • MediaEngineInterface:抽象Media Engine的邏輯接口,負責創建用於視頻傳輸的VideoMediaChannel、用於音頻傳輸的VoiceMediaChannel、註冊音頻數據處理接口等。
  • CompositeMediaEngine:實現MediaEngineInterface接口,本身也是個模板類,兩個模板參數分別是視頻Engine和音頻Engine。其派生類WebRtcMediaEngine依賴的模板參數是WebRtcVoiceEngine和WebRtcVideoEngine,而用於Chromium的WebRtcMediaEngine2則依賴WebRtcVoiceEngine和WebRtcVideoEngine2。

WebRtcVideoEngine2主要作用在於創建視頻channel對象WebRtcVideoChannel2。結構如下:

當調用WebRtcVideoChannel2的AddSendStream方法時,會創建一個WebRtcVideoSendStream對象,同樣,調用AddRecvStream成員方法,會創建一個WebRtcVideoReceiveStream對象。

當外部調用WebRtcVideoChannel2的SetCapturer方法時,會轉給WebRtcVideoSendStream來響應,WebRtcVideoSendStream內部將InputFrame成員方法掛接VideoCapturer的SignalVideoFrame信號來接收視頻採集器傳輸過來的視頻幀數據。

WebRtcVideoChannel2的AddSendStream和SetCapturer的調用時機這裏暫時不考慮,這些涉及到網絡連接,等每個節點的內容分析完後,再探討整個流程。

如圖所示,WebRtcVideoSendStream和WebRtcVideoReceiveStream也只是個包裝類,內部依賴Call接口創建對應的VideoSendStream接口實現類和VideoReceiveStream接口實現類。在internal命名空間內,分別有一個Call類、VideoSendStream類、VideoReceiveStream類來實現這三個接口,Call類創建關鍵的VideoEngine對象來管理視頻數據發送過程中的一系列處理邏輯。從代碼結構上看,VideoEngine是一個相對獨立的模塊,它封裝視頻數據採集後的處理、編碼等邏輯,下面仔細分析一下VideoEngine的結構:

 

VideoEngine模塊裏有ViEBase、ViECodec、ViECapture、ViEImageProcess、ViENetwork、ViERender、ViERTP_RTCP、ViEExternalCodec接口,注意,這些都是功能性的接口,它們相應的實現分別對應於上圖中的XXXImpl類,VideoEngineImpl類從所有的XXXImpl接口派生,因此外部有了VideoEngine接口,都可以通過強轉的方式獲取ViEBase、ViECapture等之類的接口(根據VideoEngine強轉成相應的接口的邏輯封裝在目標接口的GetInterface靜態方法中),外界可以通過這些接口來完成視頻數據做相應的設置,而這些設置最終都反映到一個名叫ViESharedData的類對象裏。該對象由ViEBaseImpl創建並在各接口的實現之間共享,XXXImpl可以通過ViEBaseImpl的shared_data方法來訪問,用於共享的數據有三類:ViEInputManager、ViEChannelManager和ViERenderManager。下面分別介紹一下這關鍵的三個對象。

  • ViEInputManager:封裝了視頻採集/輸入邏輯(哈哈,又是一套視頻輸入邏輯),結構:

ViEInputManager爲每個通道分配一個ViECapturer對象來做爲視頻源,ViECapturer可以傳入也可以自己創建一個VideoCaptureModule視頻採集模塊,並通過VideoCaptureDataCallback接口從其接收數據,也可以直接通過ViEExternalCapture接口接收從外部直接傳入的視頻幀數據(調用ViEExternalCapture接口的IncomingFrame方法)。VideoSendStream就是通過ViEInputManager創建一個ViEExternalCapture對象來傳入外界傳來的視頻幀數據(通過WebRtcVideoSendStream的InputFrame傳來)。這裏要注意,ViEInputManager爲創建的ViECapturer對象分配一個capture_id,外界可以通過這個capture_id來操作其對應的ViECapturer。視頻源傳入邏輯已經明瞭,接下來分析一下視頻是怎麼傳出去的。無論通過哪種視頻數據接收方法,ViECapturer都不會立即將數據傳遞出去,因爲它內部需要對這些視頻數據做相關的處理。數據處理必然耗時,如果採用同步的方式,必將阻塞視頻傳入的流程。因此,在創建ViECapturer的時候,會啓動一採集線程,該線程調用ViECaptureProcess處理函數,在該處理函數裏,先調用VideoProcessingModule對視頻數據進行處理(燈光加亮、去閃爍),如果在ViEImageProcessImpl裏註冊了ViEEffectFilter處理對象,這裏也會調用該對象來處理視頻幀數據,最後通過DeliverFrame方法分發到註冊進來的所有ViEFrameCallback接口。

  • ViEChannelManager:封裝了視頻編碼和傳輸邏輯,這塊結構比較複雜,總體如下:

ViEChannelManager維護了ViEEncoder和ViEChannel對象,ViEEncoder實現了ViEFrameCallback接口從ViECapturer對象中接收視頻幀數據,ViEEncoder對接收到的視頻幀數據進行編碼,然後將編碼後的數據傳給ViEChannel(通過兩者之間共享的PayloadRouter對象),ViEChannel將編碼後的視頻數據通過RTP/RTCP協議發送出去。下面分別分析一下ViEEncoder和ViEChannel。

    1) ViEEncoder類:封裝了視頻編碼流程。

視頻編碼由VideoCodingModule模塊統一管理,視頻幀傳入接口是通過VideoCodingModule的的AddVideoFrame方法,編碼後的視頻傳出接口是藉助VCMPacketizationCallback接口來回調。具體選取哪種視頻編碼的邏輯位於VCMCodecDataBase類,當前支持VP8編碼、VP9編碼和視頻格式到I420格式的轉換。

2)ViEChannel類:封裝了編碼後的視頻數據發送邏輯和視頻數據接收解碼邏輯。

視頻數據發送邏輯是通過PayloadRouter對象委託給RtpRtcp模塊做RTP協議的封裝,具體的網絡發送操作還是回託給ViESender做數據的網絡發送操作。ViESender的邏輯相對簡單,限於篇幅,圖中無法做詳細的標註。ViESender的發送操作依賴外部設置給它的Transport接口(通過VideoEngine模塊的ViENetwork接口來完成設置)。

當WebRtcVideoChannel2接收到網路數據包後(通過OnPacketReceived或OnRtcpReceived方法響應),會在VideoReceiveStream對象中通過VideoEngine模塊暴露出去的ViENetwork接口來響應數據包處理,最終觸發到ViEChannel的ReceivedRTPPacket或ReceivedRTCPPacket方法。ViEChannel中將接收並解碼網絡視頻數據的任務分配給ViEReceiver對象。ViEReceiver先調用RTP/RTCP模塊做協議的解析(圖中限於篇幅未標註出來),解析完成後調用VideoCodingModule模塊進行數據的解碼操作(參見ViEReceiver的OnReceivedPayloadData方法),VideoCodingModule模塊內部維護了一個與VideoSender對應的VideoReceiver來完成解碼邏輯,這塊與VideoSender的編碼邏輯完全對稱,這裏不再表述。

  • ViERenderManager:這個類封裝了視頻渲染邏輯,結構如下:

當ViEChannel接收到網絡數據解包並解碼後,就會開啓觸發渲染流程(參見FrameToRender方法),ViEChannel會調用向其註冊的ViEFrameCallback接口來派發視頻幀數據。ViERenderManager維護了一個ViERenderer對象來實現ViEFrameCallback接口,它將數據進一步派發,最終通過ExternalRenderer接口派發給WebRtcVideoChannel2的VideoReceiveStream對象。VideoReceiveStream通過VideoSource設置進來的VideoRenderer接口將數據派發給VideoTrack,用戶可以掛接VideoRendererInterface接口來接收視頻幀數據。真夠繞的,而且那麼多命名的相似性(比如VideoRender/VideoRenderer),感覺各模塊開發期間,都實現了自己的一套接口規範,最後強行串在一起了。

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