隨著遠端辦公與異地協作越來越頻繁,視訊會議系統的使用也是越來越普遍。同時,使用者對視訊會議系統的功能也提出了更高的要求,比如,其中之一就是希望可以將整個視訊會議的過程錄製下來,以備之後可以查閱觀看。
我們可以選擇在視訊會議系統的服務端或客戶端來錄製整個視訊會議過程,在服務端錄製與在客戶端錄製各有優劣。比如,在服務端錄製對伺服器配置要求更高,因為同時可能有很多個會議同時錄製;而在客戶端錄製,錄製的檔案存放在客戶端本地電腦上,只能在本地播放,如果其他人需要觀看,則需要在錄製完成後將該檔案再上傳到伺服器。
無論是在服務端錄製,還是在客戶端錄製,其技術原理是一樣的。這裡我就 傲瑞視訊會議(OrayMeeting)在服務端(Windows、Linux、信創國產OS、銀河麒麟、統信UOS)錄製會議過程的技術原理和實現介紹給大家。
如下圖就是某會議錄製檔案使用QQ影音播放的效果:
接下來,我們將具體介紹傲瑞視訊會議是怎麼實現會議錄製的。
一. 錄製流程說明
(1)會議資訊中心包含了一個欄位,用於指示該會議是否開啟錄製。
(2)當第一個人進入會議時,啟動錄製。
(3)當到會議結束時間:若會議室沒有人,這立即結束錄製;若會議室還有人,等最後一個人退出時,結束錄製。
(3)任何時候,主持人結束會議,則將同時結束錄製。
(4)中途若會議室沒有人時,則暫停錄製;當再有人進入會議時,則繼續錄製。
二. 傲瑞會議錄製的畫面佈局
(1)錄製畫面最上面有一行高為30px標題欄,將實時顯示如下資訊:會議名稱、系統時間、參會人數。
(2)標題欄下面的剩下區域為內容區,用來渲染使用者影片/頭像,或者是渲染使用者分享螢幕的桌面影像。
(3)當參會人數不超過4個人時,採用2x2四宮格;當參會人數多於4個人時,採用3x3九宮格。
(4)錄製時最多渲染9個人的影片或頭像(3x3),開啟了影片的使用者排在最前面,其次是開啟了麥克風的使用者。
(5)如果使用者開啟了影片,錄製時就渲染其影片影像,否則,就渲染該使用者的預設頭像。
(6)如果參會人員中有人開啟了螢幕分享,這錄製畫面的內容區將不再渲染使用者影片/頭像,而是改為渲染被分享螢幕的桌面影像。
三. 程式實現技術要點
(1)獲取參會人員的PCM聲音資料和RGB影像資料。
使用 OMCS 提供的 MicrophoneConnector 和 DynamicCameraConnector 就可以獲取每個參會人員的聲音資料以及影像資料。
(2)拼接並渲染要錄製的影片影像幀
在Windows可使用GDI+技術、在Linux上則可使用Skia技術來完成錄製影像幀的拼接渲染。
if (desktopShare) //如果有人分享桌面,這主體內容區就是桌面影像 { //獲取螢幕分享的最新桌面影像幀 Image image = this.connectorManager.GetCurrentImage(null); canvas.SetDesktopImage(image); } else { for (int i = 0; i < recordMembers.Count; i++) { string userID = recordMembers[i]; Image image = null; if (!meeting.CamClosedMemberList.Contains(userID)) { //獲取參會成員的最新影片影像幀 image = this.connectorManager.GetCurrentImage(userID); } DrawInfo renderModel = this.drawInfoManagers.Get(userID); renderModel.SetCameraImage(image); renderModel.SetMicState(!meeting.MuteMemberList.Contains(userID)); } //繪製主體內容區 canvas.SetCameraImage(this.drawInfoManagers.GetAll()); } byte[] bytes = canvas.RenderImage(); //準備好要錄製的影像 if (bytes != null) { this.videoFileMaker?.AddVideoFrame(bytes); //提交給錄製器 }
(3)混音
使用 OMCS 提供的 MicrophoneConnectorMixer 可以將參會人員的聲音混成一路。
IConnectorManager connectorManager = new OMCSConnectorManager(globalCache); connectorManager.AudioMixed += ConnectorManager_AudioMixed; private void ConnectorManager_AudioMixed(string userID, byte[] data) { if (recording && data != null) { this.videoFileMaker?.AddAudioFrame(data); //將混音資料提交給錄製器 } }
(4)定時器
使用定時器來排程聲音資料和影像資料。比如:每隔10毫秒就從各個MicrophoneConnector獲取一幀語音資料,並將它們混音。每隔40毫秒就從各個 DynamicCameraConnector 獲取相應的影像資料,並將它們拼接,並按照前面描述的畫面佈局進行渲染。
//開啟錄製執行緒,定時呼叫 internal void StartRecord() { recording = true; Task.Factory.StartNew(() => { RecordThread(); }); } private void RecordThread() { int sleepSeconds = 1000 / frameRate; while (true) { System.Threading.Thread.Sleep(sleepSeconds); //record ... } }
(5)將影像幀和聲音幀編碼並生成MP4檔案。
將混音好的聲音資料、拼接好的渲染影像提交給 MFile,MFile會將它們編碼並寫入到MP4檔案中。
會議結束時,將結束錄製,並釋放相關的資源。
internal void FinishRecord() { recording = false; //釋放麥克風、攝像頭、桌面裝置聯結器 connectorManager.DisconnectAllConnect(); //釋放錄製器 videoFileMaker?.Close(true); }
四. 結語
在將錄製會議的流程、畫面佈局、技術要點做了簡單介紹後,相信大家對視訊會議服務端在程式上是如何實現會議錄製功能的,已經初步瞭解了。
本文只是粗略地介紹了視訊會議錄製的原理與技術實現,如果你有更具體的實現細節需要了解的,歡迎與我討論。