多媒體

本章將結合Web前端的發展歷程和未來的發展前景詳解現在HTML5中引入的多媒體技術

HTML5的多媒體支持

在HTML5規範出來之前,網頁對視頻和音頻播放的支持基本上是靠Flash插件來實現,在HTML5之後,同文字和圖片一樣,音頻和視頻直接變成HTML一系列規範中第一等公民,首先是JavaScript接口的支持,開發者可以使用JavaScript接口來方便的控制音視頻的播放,實現例如播放、停止和記錄等功能,其次HTML5中多媒體與圖片一樣可以用其他技術來進行操作,例如使用CSS技術來修改它的樣式,例如3D變形,Web開發者可以將視頻同Canvas2D或者WebGL結合在一起,而Flash插件中的視頻是不能做到的。在HTML5中,對於多媒體的支持大致可以包括一下幾個部分:第一是HTML的元素“video”,他用於音視頻的播放;第二是“audio”,它用於單純的音頻播放;第三是可以將多個聲音合成處理的WebAudio技術;第四是將照相機、麥克分與視頻、音頻和通信結合起來使用的最新技術WebRTC(網絡實時通信),這使得Web領域使用視頻對話和視頻網絡會議成爲了現實。

視頻

HTML5視頻

在HTML5規範定義中,Web開發者可以使用“video”元素來播放視頻資源,其中視頻涉及到視頻編碼格式,目前標準中包含了三種編碼格式:Ogg、MPEG4和WebM,其中Ogg使用Theora作爲視頻編碼格式和Vorbis作爲音頻編碼格式,MPEG4使用H.264作爲視頻編碼格式和AAC作爲音頻編碼格式,WebM是有Google研發的標準,使用VP8作爲視頻編碼格式和Vorbis作爲音頻編碼格式。HTML提供了一些屬性讓開發者來使用JavaScript代碼檢查和操作視頻,HTML5在“video”和“audio”元素之間抽象了一個基類元素“media”,結合它提供的能力,大致有一下幾個方面的JavaScript編程接口,首先是資源加載和信息方面的接口,開發者可以通過特定接口檢查遊覽器支持什麼格式,如Metadata和海報(Poster)等,其次是緩衝(Buffering)處理,包括緩衝區域、進度等信息,然後是播放方面的狀態,包括播放、暫停、終止等。再次是搜尋(Seeking)方面的信息,包括設置當前時間、“Timeupdate”事件,以及兩個狀態“Seeking”和“seeked”,最後是音量方面的設置,包括獲取和設置音量、靜音和音量變換等事件。

WebKit基礎設施

WebKit提供了支持多媒體規範的基礎框架,如音視頻元素、JavaScript接口和視頻播放等,根據WebKit的一般設計思想,它主要是提供標準的實現框架,而具體的實現有各個移植類來完成,因爲音視頻需要平臺的支持,下圖顯示了各個類和它們之間的關係,也包括了Chromium移動的幾個基礎類:
這裏寫圖片描述
首先WebKit是支持規範定義的編程接口,圖中左側的HTMLMediaElement和HTMLVideoElement類是DOM樹中的節點類,包括衆多的DOM接口,這些接口可以被JavaScript代碼訪問;其次是MediaPlayer和MediaPlayerClient兩個類,MediaPlayer類是一個公共標準累,被HTMLMediaElement類使用來播放音頻和視頻,它本身支持提供抽象接口,具體實現依賴於不同的WebKit移植,同時一些播放器中的狀態信息需要通知到HTMLMediaElement類,這裏使用MediaPlayerClient類來定義這些有關狀態信息的接口,HTMLMediaElement類需要繼承MediaPlayerClient類並接收這些狀態信息,根據前面的描述,規範要求將事件派發到JavaScript代碼中,而這個實現在HTMLMediaElement類完成,然後是不同移植對MediaPlayer類的實現,其中包括MediaPlayerPrivateInterface類和WebMediaPlayerClientImpl類,前者是除了Chromium移植之外使用的標準接口也是一個抽象接口,由不同移植來實現,後者是Chromium移植的實現類,因爲Chromium將WebKit複製出Blink之後就將MediaPlayerPrivateInterface類直接移除了,而在MediaPlayer類中直接調用它,WebMediaPlayerClientImpl類會使用Chromium移植自己定義的WebMediaPlayer接口類來作爲實際的播放器,而真正的播放器則是在Chromium項目的代碼中來實現,最後同渲染有關,這裏就是之前介紹的RenderObject樹和RenderLayer樹,圖中的RenderMedia類和RenderVideo類是RenderObject的子類,用於表示Media節點和Video節點。

Chromium視頻機制
資源獲取

由於視頻資源相對其他資源而言比較大,當用戶播放視頻的時候需要連續性播放以獲得較好的體驗,但是網絡可能並不是移植都穩定和告訴,所以資源的獲取對用戶體驗很重要,需要使用緩存機制或者其他機制來預先獲取視頻資源。下圖是Chromium中的緩存資源類,BufferedDataSource類表示資源數據,它是一個簡單的數據表示類,內存包含一個較小的內存空間(32K),實際的緩衝機制由BufferedResourceLoader類完成,在Chromium的設置中,最小的緩存空間是2M內存,最大的緩存空間是20M,並沒有使用磁盤來緩存視頻資源:
這裏寫圖片描述

基礎設施

下圖是chromium中支持硬件加速機制的視頻播放所需基礎設施的總體架構圖,基於Chromium的多進程結構:
這裏寫圖片描述
根據多進程架構的設計原則,Chromium的媒體播放器的實現應該在Renderer進程,而對於資源的獲取則是在Browser進程,其中WebKit基礎設施需要每個移植的具體實現,因此WebKit的Chromium移植部分提供了橋接接口,並且實現則是在Chromium代碼中來完成,Chromium支持媒體播放器的具體實現涉及到不同的操作系統,目前Chromium在不同操作系統上實現的媒體播放器也不一樣,下圖顯示了Chromium的基礎類:
這裏寫圖片描述
上半部分是WebKit和WebKit的Chromium移植中的相關類,下半部分是Chromium中使用硬件加速機制來實現視頻播放的基礎設施類,從做到分開來看,左邊部分是播放器的具體實現類,右邊部分是支持視頻在合成器中工作的相關類。
首先看下這些類和對象的創建過程,WebMediaPlayerClientImpl類是WebKit在創建HTMLMediaElement對象之後創建MediaPlayer對象的時候有MediaPlayer對象來創建的,當視頻資源開始加載時,WebKit創建一個WebMediaPlayer對象,當然就是Chromium中的具體實現類WebMediaPlayerImpl對象,同時WebMediaPlayerClientImpl類也實現了WebMediaPlayerClient類,所以WebMediaPlayerImpl在播放視頻的過程中需要向該WebMediaPlayerClient類更新各種狀態,這些狀態信息最終會傳遞到HTMLMediaElement類中,最終可能成爲JavaScript事件,之後WebMediaPlayerImpl對象會創建一個WebLayerImpl對象,海湖同時創建VideoLayer對象,根據合成器的設計,Chromium還有一個LayerImpl樹,在同步的時候,VideoLayer對象對應的VideoLayerImpl對象會被創建,之後Chromium需要創建VideoFrameProviderClientImpl對象,該對象將合成器的Video層同視頻播放器聯繫起來並將合成器繪製一幀的請求轉給提供視頻內容的VideoFrameProvider類,這實際上是調用Chromium的媒體播放器WebMediaPlayerImpl,因爲它是一個VideoFrameProvider類的實現子類,然後是Chromium如何使用這些類來生成和顯示每一幀,當合成器調用每一層來繪製下一幀的時候,VideoFrameProviderClientImpl::AcquireLockAndCurrentFrame()函數會被調用,然後該函數調用WebMediaPlayerImpl類的GetCurrentFrame函數返回當前一幀的數據,VideoLayerImpl類根據需要會將這一幀數據上傳到GPU的紋理對象中,當繪製完這一幀之後,VideoLayerImpl調用VidelFrameProviderClientImpl::PutCurrentFrame來通知播放器這一幀已繪製完成,並釋放掉相應的資源,同時,媒體播放器也可以通知合成器有一些新幀生成,需要繪製出來,它會首先調用播放器的VideoFrameProvider::DidReceiveFrame()函數,該函數用來檢查當前有沒有一個VideoLayerImpl對象,如果有對象存在,需要設置它的SetNeedsRedraw標記位,這樣合成器就知道需要重新生成新的一幀,最後是有關視頻播放對象的銷燬過程,有多種情況使Chromium需要銷燬媒體播放器和相關的資源,如“video”元素被移除或者設置爲隱藏等,這樣視頻元素對應的各種層對象以及WebKit和Chromium中的這些設施都會被銷燬,WebMediaPalyerImpl類是多媒體播放器的具體實現類,在Chromium項目中,隨着對Android系統的支持,Chromium既能支持左面系統也能支持移動系統,而這兩者對視頻和音頻的支持很不一樣,所以在不同系統上WebMediaPlayerImpl是如何實現和工作的也很不一樣。

桌面系統

在桌面系統中,Chroimum使用了一套多媒體播放框架,而不是直接使用系統或者第三方庫的完整解決方案,下圖是Chromium在桌面系統上採用的多媒體播放引擎的工作模塊和過程,這一框架稱爲多媒體管線化引擎,圖中主要的模塊四號多路分配器、音視頻解碼器、音視頻渲染器,這些部分主要被WebMediaPlayerImpl類調用:
這裏寫圖片描述
在處理音視頻的管線化過程中,需要解碼器和渲染其來分別處理視頻和音頻數據,它們均採用一種叫做“拉”而不是“推”的方式進行,也就是說有視頻或者音頻渲染器根據聲卡或者時鐘控制器,按需求來請求解碼器解碼數據,然後解碼器和渲染器又向前請求“拉”數據,直到請求從視頻資源文件讀入數據,根據之前的多進程架構和Chromium的安全機制,整個管線化引擎雖然在Render進程中,但是由於Render進程不能訪問聲卡,所以渲染器需要通過IPC將數據或者消息同Browser進程通信,由Browser進程來訪問聲卡。雖然FFmpeg多媒體庫擁有上述管線化的能力,但Chromium並不是將其作爲一個黑盒來使用,而是分別使用FFmpeg的不同模塊來實現自己的管線化引擎,目的是由自身來控制這一整個過程。Chromum使用並行FFmpeg解碼技術,也就是說FFmpeg能夠在幀這個層面上並行解碼,當然不是針對所有格式的視頻文件,目前主要針對H.264這個格式的視頻。

Android系統

Chromium使用的是Android系統所提供的android.media.MediaPlayer類,也就是使用系統提供的音視頻的渲染框架,在減少了管線化引擎帶來複雜性的同時,也引入了一些額外的複雜問題。Android中的Chromium徹底拋棄了FFmpeg,直接使用系統自帶的多媒體功能,因而,Android系統支持什麼樣的音視頻格式,Chromium就只能支持什麼樣的相應格式,同時由於Android多媒體框架的優點使得視頻元素仍然能夠同HTML5中的其他技術一起工作。
Ⅰ Android媒體播放框架
Android中使用一個名爲“MediaService”的服務進程來爲應用程序提供音頻和視頻的播放功能,對於每一個使用多媒體播發功能的應用程序來說,“MediaService”服務是透明的,因爲Android系統提供了“MediaService”的封裝接口,這些接口隱藏了“MediaService”服務內部的細節,應用程序只是使用了簡單的播放接口。MediaService能夠爲多個播放器提供服務,對於播放器來說,它的主要設置爲兩個參數,其一是輸入的URL,第二是輸出結果的繪製目標,下圖描述了Android的播放器類和相關類:
這裏寫圖片描述
當應用程序使用播放器的時候,Chromium可以創建MediaPlayer類的對象,調用setDataSource函數來設置待播放視頻文件,並調用setSurface來設置視頻結果繪製的目標-SurfaceTexture對象,這是一個GL的紋理對象,實際的解碼和繪製是在MediaService進程中完成,這需要該紋理對象能夠被多個不同的GL上下文對象所訪問,支持多個GL上下文對象訪問的GL紋理對象的類型GL_TEXTURE_EXTERNAL_OES,由此可以看到Chromium使用Android系統提供的音視頻播放功能,表示Chromium使用Android系統的音視頻解碼器,所以Chromium是依賴與Android系統支持的音視頻編碼格式,而不像Chromium桌面版獨立與操作系統的音視頻編碼格式。
Ⅱ Chromium的視頻解決方案
在Android系統上,因爲Chromium使用系統的多媒體框架,所以沒有自己的管線化引擎,主要的工作是將Chromium的架構同Android多媒體框架結合起來以完成對網頁中視頻和音頻的播放。下圖是Chromium的Android系統上支持音頻和視頻播放的播放器主要類,因爲Chromium的多進程架構,所以這裏麪包括兩大部分,首先是右側的Render進程的相關類,根據前面Chromium的桌面版上支持多媒體的相關類,可以看到WebKit::WebMediaPlayer類和WebMediaPlayerClient類來自於WebKit的Chromium移植,這兩個類在所有平臺上的定義都是一樣的:
這裏寫圖片描述
上圖中右側的Render進程,從上向下首先是WebMediaPlayerAndroid類,它同之前的WebMediaPlayerImpl類相似,表示的是Android系統上網頁中的播放器,同video元素是一一對應的,與桌面系統不一樣的是,Android系統使用RendeerMediaPlayerManager類來管理所有的WebMediaPlayerAndroid對象,因爲一個網頁中可能包含多個播放器實例,而WebMediaPlayerProxyAndroid則是同Browser進程來通信的,因爲真正的Android播放器是在Browser進程中,主要請求Browser進程創建實際的Android的MediaPlayer類並設置播放文件的信息,左側則是實際的播放器,在JNI(Java Native Interface)之上的是Java類,該播放器就是使用Android系統的android.media.MediaPlayer及其相關類來工作的,從下向上看首先是BrowserMediaPlayerManager類,該類不僅負責同Render進程的播放器類進行通信,而且自身又是一個播放器的管理類,它包含當前全部網頁中的所有播放器對象,因爲可能會有多個Render進程,所以只能通過播放器的唯一標記來區分這些播放器,BrowserMediaPlayerManager類管理稱爲MediaPlayerAndroid類的多個對象,而MediaPlayerAndroid的子類MediaPlayerBridge則是具體實現類,該子類能夠與Java層中相同名字類通過JNI調用來控制Android系統的播放器類,以上的基本過程就是如何在網頁中創建一個播放器,從右向左直到android.media.MediaPlayer對象被創建,同時Chromium獲取網頁中設置的視頻文件的URL字符串然後傳遞並設置該URL字符串到Android的媒體播放器作爲輸入,對於輸出Chrome使用SurfaceTexture對象作爲輸出目標,當Chromium調用WebMediaPlayerAndroid類的play函數時,該函數發起請求從Render進程到Browser進程來創建輸出目標,也就是SurfeceTexture對象,下圖描述了這個過程中使用到的主要類和之間的關係:
這裏寫圖片描述
右側的Render進程,最上面的StreamTexureFactoryImpl是創建目標結果存儲空間的類,它被WebMediaPlayerAndroid類使用來創建所需要的結果存儲對象StreamTexture,由於實際的對象是在Browser進程中創建的,所以Render進程中的StreamTextureProxy類就是一個代理類,最後的請求是通過GPUChannelHost類傳遞給Browser進程。在Browser進程中,負責處理上述請求的是GPU線程,該線程有StreamTextureManagerAndroid類處理所有創建StreamTexture對象的請求,StreamTexture對象的直接使用者是GPU線程,Render進程需要區分和標識這些StreamTexture對象,具體的方法是使用整形標記符來表示Browser進程中的各個StreamTexture對象,StreamTexture和StreamTextureManager是基礎抽象類,在Android系統上,StreamTextureAndroid和StreamTextureManagerAndroid是實際的實現類,StreamTextureAndroid表示的是C++端的橋接類,它包含一個SurfaceTexture對象,該對象會在Java端創建一個android.graphics.SurfaceTexture對象,Chromium設置該對象到MediaPlayer對象作爲播放器的輸出目標,當視頻播放器將解碼後的結果寫入到SurfaceTexture中後,播放器需要告訴Chromium遊覽器這一信息,Chroimum遊覽器需要執行合成操作而合成器在Render進程中,同之前創建SurfaceTexture對象的調用過程正好相反,這裏需要使用回調機制,這就是Java層SurfaceTextureListener類的作用,該回調類註冊Java層的回調對象到創建好的SurfaceTexture對象,當該對象被寫入新的幀的時候,Chromium首先從Browse進程中的Java層將這一回調動作通過JNI到C++層的SurfaceTextureListener類的FrameAvailable函數,該函數經過StreamTextureAndroid和StreamTextureManagerAndroid類最後發送到Render進程,Render進程的調用過程如下:
這裏寫圖片描述
網頁中的視頻播放有兩種模式,其一是嵌入式模式,其二是全屏模式,這兩種模式在解碼後結果的處理上是不一樣的,下圖描述了全屏模式創建視頻結果的繪製目標的相關類和過程:
這裏寫圖片描述
當播放器進入全屏模式的時候,Chromium使用ContentVideoView類來管理,該類會創建一個SurfaceView對象並將對象傳遞給C++端的ContentVideoView類,因爲統一時刻只有一個播放器是全屏模式,而且BrowserMediaPlayerManager管理所有的MediaPlayer對象,該管理類能夠知道哪個對象是全屏模式,並將該SurfaceView對象設置到相應的MediaPlayer對象中去。

字幕

視頻需要字母的支持,W3C組織已經開始定義支持字幕的“trace”元素,而字幕文件採用的格式是WebVTT格式,該格式看起來比較直觀,簡單的例子就是時間戳區間加上相應的字母文字,一下是使用字母的視頻元素,因爲語言的問題,每個“video”元素可以有多個“trace”元素,每個“trace”元素可以用來表示一個語言:

<video controls="controls">
  <source src="video.mp4" type="video/mp4">
  <trace src="trace.vtt" kind="subtitles" srclang="en" label="English"></trace>
</video>

字幕文件的解釋工作不依賴與各個WebKit移植,WebCore模塊支持“track”元素解析、字幕文件解析等功能,下圖是WebKit支持字幕功能的主要類:
這裏寫圖片描述
“track”本身是一個HTML元素,因此它在DOM中有相應的節點元素,這就是HTMLTrackElement類,根據規範,“track“元素有一個重要的屬性”src“,該屬性指定了字幕文件的URL,WebKit使用LoadableTextTrack類來負責解析字幕文件並使用TextTrack類來存儲解析後的結果,目前WebKit只支持WebVTT格式的字幕,使用WebVTTParser解析器來解釋它們。
下面一部分是提供接口,這裏的接口是WebKit的Chromium移植所定義的接口不同額移植所定義的接口可能不一樣,接口有兩個類,WebInbandTextTrack和WebInbandTextTrackClient類,且是公開接口,WebInbandTextTrack類是有Chromium實現由WebKit調用,而WebInbandTextTrackClient則是有WebKit實現,實現類就是InbandTextTrackPrivateImpl,它實現WebInbandTextTrackClient的接口,然後後調用解析後的字幕並返回給Chromium。上述需要將一些消息傳遞給JavaScript代碼,因爲規範提供了JavaScript接口,開發者可以讓JavaScript代碼控制或者獲取字幕信息,下面是Chromium中支持框架,下圖描述了Chromium是如何將WebKit中的字幕信息橋接到多媒體管線化引擎中的:
這裏寫圖片描述
在Chromium中,WebMediaPlayerImpl類創建繼承類的對象,並設置WebInbandTextTrackClient對戲那個到該對象,該對象實際上是InTextTrack,它包含解析後的字幕內容,這樣TextTrackImpl就可以獲取字幕的內容,而TextTrack對象會被多媒體的管線化引擎多調用並渲染在視頻的結果中。

音頻

音頻元素

音頻支持不僅指對聲音的播放,還包括對音頻的編輯和合成以及對樂器數字接口等的支持。

HTML5 Audio元素

在HTML5中使用”audio“元素來表示,同視頻類似,HTML5標準中定義了三種格式:Ogg、MP3和Wav,因爲視頻內容通常包含音頻數據,所以不僅僅是”audio“元素纔會使用音頻播放,同時,音頻的字幕同視頻一樣,”track“元素也可以用在”audio“元素的字母中,用來顯示字幕。

基礎設施

音頻的支持方面還是從輸入和輸出兩個方面着手,對於輸入,同視頻類似,WebKit使用資源加載器先加載音頻文件,之後建立音頻元素、管線話引擎相關類,如MediaPlayer類,HTMLAudioElement和WebMediaPlayer類等,同視頻不一樣的是,視頻的輸出是GPU中的紋理對象,而音頻需要輸出到聲卡,因此需要打開聲卡設備,由於Chromium的沙箱模型機制,所以只能靠Browser進程來打開和關閉聲卡設備,下圖描述了多進程中如何將音頻從Render進程傳輸到Browser進程,以及WebKit和Chromium中相應的基礎設施:
這裏寫圖片描述
首先看Render進程,從上玩下介紹如下:

  • WebKit::WebAudioSourceProvider和WebKit::WebAudioSourceProviderClient:最上面兩個類是WebKit的Chromium移植接口類,前者提供音頻原數據,也就是音頻文件中的數據,這裏採用“拉”的方式,也就是在ResourceLoader加載數據之後,當且僅當渲染引擎需要新的數據的時候,主動從加載後的數據中拉出數據來進行解碼,“provideInput”函數由Chromium實現,由WebKit引擎調用,WebKit::WebAudioSourceProviderClient提供“setFormat”函數,用於讓Chromium的媒體播放器設置頻道數量、採樣率等信息,WebAudioSourceProviderImpl是WebKit::WebAudioSourceProvider的實現類
  • AudioRendererImpl:該類是音頻渲染器的實現,並使用AudioRenderSink將音頻解碼的結果輸出到音頻設備
  • AudioRendererSink:一個抽象類,用於表示一個音頻終端店,能夠接收解碼後的音頻信息,典型的例子就是音頻設備
  • AudioRendererMixer:渲染器中的調用類
  • AudioOutputDevice:音頻的輸出設備,當然只是一個橋接層,因爲實際的調用請求是通過下面兩個類傳送給Browser進程的
  • AudioOutputIPCImpl和AudioMessageFilter:前者將數據和指令通過IPC發送給Browser進程,後者就是執行消息發送機制的類

下面是Browser進程,從下向上一次介紹:

  • AudioRendererHost:Browser進程端同Renderer進程通信並調度管理輸出視頻流的功能,對於每個輸出流,有相應的AudioOutputStream對象對應,並且通過AudioOutputController類處理和優化輸出
  • AudioOutputController:該類控制一個AudioOutputStream對象並提供數據給該對象,提供play、pause、stop等功能,因爲它控制這音頻的輸出結果
  • AudiOutputStream和AudioOutputProxy:音頻的輸出流類和其子類,AudioOutputProxy是一個使用優化算法的類,它僅在Start()和Stop()函數之間打開音頻設備,其他情況下音頻設備都是關閉的,AudioOutputProxy使用AudioOutputDispatcher打開和關閉實際的物理音頻設備
  • AudioOutputDispatcher和AudioOutputDispatcherImpl:控制音頻設備的接口類和實際實現類

由此可以得出當WebKit和Chromium需要輸出解碼後的音頻數據是,通過從側自上向下、左側自下向上的過程,然後使用共享內存的方式將解碼後的數據輸出到實際的物理設備中。

Web Audio

Audio元素能夠用來播放各種格式的音頻,但是HTML5還擁有更強大的能力來處理聲音,這就是Web Audio,該規範提供了搞層次的JavaScript接口,用來處理和合成聲音,整個思路就是提供一張圖,giant圖中的每個節點稱爲AudioNode,這些節點構成處理的整個過程,雖然實際的處理是使用C/C++來完成,但是Web Audio也提供了一些接口來讓Web前端開發者使用JavaScript代碼來調用C/C++的實現,WebAudio對於很多Web應用很有幫助,例如它呢能夠幫助開發者設計和實時合成出各種音效,根據W3C的Web Audio規範的定義,整個處理過程可以看成一個拓撲圖,該圖有一個或多個輸入源,稱爲Source節點,中間的所有點都可以看成各種處理過程,它們組成複雜的網,圖中有一個最終節點稱爲“Destination”,它可以表示實際的音頻設備,每個圖只能有一個該類型的節點,上述圖中的所有節點都是工作在一個上下文中,稱爲AudioContext:
這裏寫圖片描述
對於Source1節點,它沒有輸入節點,hi有輸出節點,對於中間的這些節點,它們既包含輸入節點也包含輸出節點,而對應Destination節點,它只有輸入節點,沒有輸出節點,圖中的其他節點都是可以任意定義的,這些即誒但每一個都可以代表一種處理算法,開發者可以根據需要設置不同的節點以處理出不同效果的音頻輸出,中間這些節點有很多類型,它們的作用也不一樣,這些節點的實現通常由C或者C++代碼來完成以達到高性能,當然這裏提供的接口都是JavaScript接口。Web Audio的絕大多數處理都是在WebKi中完成的,而不需要Chromium過多的參與,除了輸入源和輸出結果到實際設備,其他同前面的多媒體數據源是一致的。下圖的上半部分主要是支持規範中的標準接口,例如AudioBufferSourceNode、AudioContext、DestinationNode和OscillatorNode等類,它們對應上圖中規範定義的接口,還有衆多類沒有繪出,以下重點關注OsicllatorNode類,它需要對音頻數據進行大量計算,包括向量的加法、乘法等,同時該節點類需要使用PeriodicWave來計算週期性波形,這裏面需要使用到FFT(快速傅立葉變換)算法,因爲音頻的及時性,網頁對性能有非常高的要求,對於Chromium移植,不同平臺採用不同的加速算法,在Windows和Linux中使用FFMpeg中的高性能算法,在Android上使用OpenMax DL提供的接口來加速,而在Mac上又是不同的算法:
這裏寫圖片描述

MIDI和Web MIDI

MIDI是一個通信標準,它是電子樂器之間,以及電子樂器與電腦之間的統一交流協議,用以確定電腦音樂程序、合成器和其他電子音響設備互相交換信息與控制信息的方法,同其他的聲音格式不同,MIDI不是記錄採樣信息,而是記錄樂器的演奏指令。音頻也可以以MIDI格式來存儲,但是該格式不是HTML5的標準,所以遊覽器並沒有內置支持它們,爲了讓MIDI格式的音樂播放出來,可以使用JavaScript代碼,這就是MIDI.js,它使用上面提到的WebAudio技術和Audio元素來實現音樂的播放。Web MIDI規範中定義了輸入和輸出的MIDI設備,如MIDIInput和MIDIOutput,通過MIDIAccess接口分那會到所有枚舉的輸入和輸出設備,MIDIInput主要包含一個接收指令的函數“onMessage”,而MIDIOutput包含一個發送指令的函數“send”,而發送的數據指令就是MIDIEvent,該指令包含一個時間戳和數據,WebKIt和Chromium對於Web MIDI的支持主要包括三個部分,第一是加入JavaScript的綁定,第二是將對MIDI接口的支持從Redner進程橋接到Browser進程,第三是Chromium的具體實現。

WebRTC

WebRTC(Web Real Time Communication)技術,中文全稱爲Web實時通信技術,是一種提供實時視頻通信的規範,目前是W3C推薦的規範。

原理和規範

構建網絡視頻通信需要三種類型的技術,其一是視頻,其二是音頻,其三是網絡傳輸

  • 音視頻輸入和輸出設備:同音視頻播放不同,因爲它們只是需要輸出設備,這裏需要輸入和輸出設備,同時輸入使用getUserMedia技術,而輸出基本上可以採用音視頻播放的基本框架
  • 網絡連接的建立:因爲視頻通信需要不停的傳送大量數據,所以需要建立一種可靠的網絡連接來讓各個參與方傳輸數據
  • 數據捕獲、比那嗎和發送:當用戶打開設備之後,需要捕獲這些數據並對它們進行編碼,因爲原始數據的數據量太大,然後需要將編碼後的數據通過連接傳輸出去
  • 數據接收、解碼和顯示:接收來自其他方的數據流並進行解碼,然後顯示出來,這個跟播放媒體文件的需求比較類似

下圖結合主要組成部分構建一個比較完整的音視頻通信過程:
這裏寫圖片描述
下面瞭解一下規範中如何針對上面的描述來定義相應的JavaScript接口,根據W3C推薦的規範草案,主要包括一下幾個部分:

  • Media Capture and Streams 規範和WebRTC對它的擴展,這個主要是依賴攝像頭和麥克風來捕獲多媒體流,WebRTC對它進行擴展,使得多媒體流可以滿足網絡傳輸用途,也就是“video”元素可以來源與多媒體流而不僅僅是資源文件
  • 點到點的連接,也就是規範中的RTCPeerConnection接口,它能夠建立端到端的連接,兩者直接通過某種方式傳輸控制信息,至於方式並沒有進行規定
  • RTCDataChannel接口,通過該接口,通信雙方可以發哦是那個任何類型的信息,例如文本或者二進制數據,這個不是必須的,不過這個功能極大的方便了開發者,其主要思想來源與WebSocket
WebKit和Chromium的實現

首先了解下WebRTC技術的內部框架和功能模塊,下圖主要包括三大方面,即語言、視頻和傳輸,它們三個構成WebRTC的主要組成部分,其中iSAC(internet Speech Audio Codec)和iLBC(internet Low Bitrate Codec)是兩種不同的音頻編碼格式,是爲了適應互聯網的語言傳輸要求而存在的,前者是針對帶寬比較大的情況,後者是針對帶寬較小的情況,其中VP8同樣是Google提供免費視頻格式,傳輸部分主要是加入了對前面協議的支持模塊,在會話管理中,主要使用一個開源項目libjingle來管理,下面部分主要是WebRTC工作時依賴的下層功能的接口,在Chromium遊覽器中,它會提供相應接口和功能給WebRTC使用:
這裏寫圖片描述
上圖是WebRTC開源項目的架構圖,在Chromium中,通常使用WebRTC項目來完成WebRTC規範的功能,並使用libjingle項目來建立點到點的連接,所有Chromium主要的目的是將WebRTC和libjingle的能力橋接到遊覽器中來,先看WebRTC規範中建立連接所需要的相關的技術設備,下圖是WebKit、Chromium及Chromium中使用libjingle的類的層次圖:
這裏寫圖片描述
基礎設備分成三個層次,首先是WebKit,該部分最上面的類是RTCPeerConnection,該類是對WebRTC連接的接口類,實際上它是從規範中定義的RTCPeerConnection接口文件生成的基本框架,實際真正和JavaScript引擎打交道還需要一個橋接類,該橋接類包含一個實際實現的連接類句柄m_peerHandler,它是這個連接所包含的本地多媒體流和遠端對方的多媒體流,場景大致是首先WebKit需要將本地的多媒體流收集起來,通過連接傳輸給對方,本地可以選擇是否通過“video”播放,同時需要接收從對方傳輸過來的多媒體流,接下來是WebKit的實現類,該類能夠滿足RTCPeerConnection的功能要求,但是它需要通過不同移植的實現才能完成,因爲本身WebKit的WebCore並沒有這樣的能力,在WebKit的Chromium中同樣定義了兩個類WebRTCPeerConnectionHandler和WebRTCPeerCOnnectionHandlerClient,根據WebKit的類名定義方式,前者是需要Chroimum來實現,後者則由Chromium調用,並由WebKit來實現,這裏主要是應用連接事件的監聽函數,所以WebKit能夠將它們傳遞給JavaScript引擎,之後是Chromium的實現類,RTCPeerCOnnectionHandler類繼承自WebKit的Chromium移植的接口類,並做了具體的實現,也就是content::RTCPeerConnectionHandler,它同時集成自PeerConnectionHandleBase類,而該類擁有了支持建立連接所需的能力,當然它是依賴於libjingle項目提供的連接能力,libjingle提供了建立和管理連接的能力,支持透過NAT和防火牆設備、代理等建立連接,libjingle不僅支持點到點的連接,也支持多用戶連接,同時還包含了連接所使用的MediaStream接口,這是因爲Chromium本身不直接使用WebRTC項目提供的接口,而是調用libjingle來建立連接,並使用libjingle提供的MediaStream接口,而libjingle本身則會使用WebRTC項目的音視頻處理引擎。
下面總結下多媒體流,先看下WebKit是怎樣支持getUserMedia這個接口的,下圖描述了WebKit,以及WebKit的Chroimum移植中所定義的接口:
這裏寫圖片描述
最上層的是WebKit支持多媒體流編程接口提供的具體實現類,如NavigatorMediaStream類,而直接同V8JavaScript引擎橋接的類是V8NavigatorUserMediaSuccessCallback,它是一個綁定類,因爲getUserMedia接口主要是返回一個MediaStream對象,而MediaStream類可以提供衆多訪問數據流的接口,而連接的目的就是需要將MediaStream對應的多媒體流傳輸出去,UserMediaRequest類負責請求創建一個MediaStream對象,在WebKit的Chromium移植中,定義WebUserMediaClient爲一個接口類,Chromium需要新建子類來實現這一功能,這就是Chromium中的MediaStreamImpl類。WebKit使用MediaStreamRegistry類來註冊和管理對應的類,管理類根據ID信息來識別各個多媒體數據流,在接口層中,Chromium移植使用WebMediaStream類來表示多媒體流,使用WebMediaStreamRegistry類來表示註冊管理類,下面的問題是MediaStream接口需要提供各種事件給網頁,因爲很多實際的工作是在Chromium中來完成的,所以MediaStreamImpl會將這些事件從Chromium傳遞給WebKit,同時因爲Chromium的多進程和沙箱模型,一些工作需要在Browser進程中完成,下圖是所描述的跨進程的基礎設施:
這裏寫圖片描述
IPC左側部分是Browser進程中的兩個主要類,分別是消息處理類和MediaSrteam的管理類,該管理類知道MediaStream對應的網頁是什麼,並將事件(如創建和銷燬等)傳回Render進程,右側是消息派發類,主要幫助MediaStreamImpl類來完成與Browser進程相關的MediaStream消息的傳遞,MediaStream可以表示本地的多媒體流也可以表示遠端的多媒體流,對於本地的多媒體流,需要音頻和視頻的捕獲機制,同時使用上面建立的連接傳輸給遠端,對於遠端的多媒體流,需要使用連接來接收數據,並使用到音頻和視頻的解碼能力,下面分成四個部分來分別介紹,首先是音頻的捕獲機制,下圖描述了該機制使用的主要類,當我那工業需要創建多媒體流的時候,MediaStreamImpl會創建音頻捕獲類WebRtcAudioCapturer類,因爲捕獲音頻需要音頻輸入設備,所以使用AudioDeviceFactory工廠類創建一個邏輯上的AudioInputDevice對象,另外一個重要的類是WebRtcAudioDeviceImpl,用來表示音頻的設備,該類繼承自WebRtcAudioDeviceNotImpl類,這其實是繼承自libjingle和WebRTC項目中的抽象接口的一個橋接類,用來表示它們需要的音頻設備,包括輸入鼠輩,同樣因爲Render進程不能訪問音頻輸入設備,所以需要IPC來完成一個功能,Browser進程的AudioInputController會控制和訪問設備,而AudioInputDeviceManager可以管理和控制所有輸入設備:
這裏寫圖片描述
其次是處理遠程多媒體流中的音頻解碼和播放功能,下圖是Chromium處理遠端音頻流所需要的一些主要類及關係圖,這裏不涉及連接如何接收傳輸的數據,因爲Chromium是使用libjingle和WebRTC項目來完成連接的功能,Chromium使用WebRtcAudioRender類來完成音頻渲染,該橋接類會被WebMediaPlayer作爲渲染音頻的實現類,其作用主要是將MediaStream的數據同實際的音頻渲染類結合起來:
這裏寫圖片描述
再次是從視頻輸入設備請求捕獲本地視頻流,下圖是該功能依賴的一些主要類:
這裏寫圖片描述
實線右側Render進程中的設施,同樣是MediaStreamImpl類發起,有輔助工廠類MediaStreamDependencyFactory幫助創建一個RtcVideoCapaturer,用來獲取視頻,該類有兩個作用,其一是是吸納linjingle和WebRTC項目中的接口類,因爲需要薯片輸入的實現,這個同音頻部分類似,另外就是將調用請求交割一個代理類來完成(RtcVideoCaptureDelegate類),分別是管理類VideoCaptureImplManager和視頻捕獲類VideoCaptureImpl,幷包括一個發送消息到Browser進程的輔助類,在Browser進程使用控制類VideoCaptureController來獲取VideoCaptureDevice,該類會使用攝像頭等視頻輸入設備。最後是處理遠端多媒體流中的視頻解碼和播放功能,當MediaStreamImpl對象接收到遠端的多媒體流之後,它會使用WebRTC來會視頻數據進行解碼,因爲可以使用硬件來解碼,提供了處理的性能:
這裏寫圖片描述
把WebRTC整個過程綜合起來分析如下圖,可以有一種更爲整體和直觀的感受:
這裏寫圖片描述

發佈了39 篇原創文章 · 獲贊 11 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章