當前位置:編程學習大全網 - 編程語言 - 第六章 音視頻的采集與編碼

第六章 音視頻的采集與編碼

iOS平臺提供了多套API采集音頻,如果開發者想要直接指定壹個路徑,則可以將錄制的音頻編碼到文件中,可以使用 AVAudioRecorder 這套API。

iOS平臺提供了兩個層次的API來協助實現,第壹種方式是使用 AudioQueue ,第二種方式是使用 AudioUnit ,實際上AudioQueue是AudioUnit更高級的封裝。

使用場景

1. AVAudioRecorder 簡單易用

2. AudioQueue 僅僅是要獲取內存中的錄音數據,然後再進行編碼輸出(有可能是輸出到本地磁盤,也有可能 是網絡)

3. AudioUnit 要使用更多的音效處理,以及實時的監聽。

ExtAudioFile ,iOS提供的這個API只需要設置好輸入格式、輸出格式以及輸出文件路徑和文件格式即可。

視頻畫面的采集主要是使用各個平臺提供的 攝像頭API 來實現的, 在為攝像頭設置了合適的參數之後,將攝像頭實時采集的視頻幀渲染到屏幕上提供給用戶預覽,然後將該視頻幀 編碼 到壹個視頻文件中,其使用的編碼格式壹般是 H264 。

本節會設計並實現壹個基於攝像頭采集,最終用 OpenGL ES 渲染到 UIView 上,並且可以支持後期視頻特效處理,以及編碼視頻幀的架構。

首先來看壹下整體架構圖

接下來分析壹下該架構

我們就可以 抽象出以下兩個規則。

基於上面的分析,我們可以畫出節點的類圖關系

由於我們要獲取攝像頭采集的數據,所以這裏需重寫該Protocol裏面約定的方法,也就是攝像頭用來輸出數據的方法,簽名如下:

最重要的是 CMSample-Buffer 類型的 sampleBuffer ,其中實際存儲著攝像頭采集到的圖像, CMSampleBuffer 結構體由以下三個部分組成。

iOS平臺不允許App進入 後臺 的時候還進行 OpenGL 的渲染操作,如果App依然進行 渲染操作 的話,那麽系統就會強制殺掉該App。

在iOS平臺上的 CoreVideo 這個 framework 中提供了

CVOpenGLESTextureCacheCreateTextureFromImage 方法,可以使得整個交換過程更加高效,因為 CVPixelBuffer 是 YUV 數據格式的,所以可以分配以下兩個紋理對象。

為什麽非要轉換為RGBA格式呢?

因為在 OpenGL 中紋理的默認格式都是 RGBA 格式的,並且也要為後續的紋理處理以及渲染到屏幕上打下基礎,最終編碼器也是以 RGBA 格式為基礎進行轉換和處理的。

YUV轉RGBA

在 FragmentShader 中將 YUV 轉換為 RGBA 格式。

無論是單獨的音頻編碼,還是視頻編碼中的音頻流部分,使用得最廣泛的都是 AAC 的編碼格式。

首先是比特率,也就是最終編碼出來的文件的碼率,接著是聲道 數、采樣率,這兩個將不再贅述,然後是最終編碼的文件路徑,最後是編碼器的名字。

銷毀前面所分配的資源以及打開的連接通道。

可使用 AudioToolbox 下的 Audio Converter Services 來完成硬件編碼。

AudioToolbox 中編碼出來的AAC數據也是裸數據,在寫入文件之前 也需要添加上 ADTS 頭信息,最終寫出來的文件才可以被系統播放器播放。

類似於軟件編碼提供的三個接口方法,這裏也提供了三個接口方法,分別用於完成 初始化 、 編碼數據 和 銷毀編碼器 的操作。

iOS平臺提供了音視頻的API,如果需要用到硬件Device相關的API,就需要配置各種 Session ;如果要用到與提供的軟件相關的API,就需要配置各種 Description 以描述配置的信息,而在這裏需要配置的Description就是前面介紹的AudioUnit部分所配置的 Description 。

軟件編碼實際使用的庫是 libx264 庫,但是開發是基於FFmpeg的API進行的。

而編碼的輸入就是本文前面攝像頭捕捉的紋理圖像(顯存中的表示),輸出是 H264 的 Annexb 封裝格式的流。

由於輸入是壹張 紋理 ,輸出是 H264 的裸流。

VideoEncoderAdapter 。為壹個類命名其實就是根據該類的職責而確定的,上面這個類實際上就是將輸入的 紋理ID 做壹個轉換,使得轉換之後的數據可以作為具體 編碼器 的輸入。

從全局來看壹下軟件編碼器的整體結 構,如下圖所示。

從上圖中可以看到整個軟件編碼器模塊的整體結構,其實, 紋理拷貝線程 是壹個生產者,它生產的視頻幀會放入 VideoFrameQueue 中; 而 編碼線程 則是壹個消費者,其可從 VideoFrameQueue 中取出視頻幀, 再進行 編碼 ,編碼好的 H264 數據將輸出到目標文件中。

Video-FrameQueue ,這是壹個我們自己實現的 保證線程安全 的隊列,實際上就是壹個 鏈表 ,鏈表中每個 Node 節點內部的元素均是壹個 VideoFrame 的結構體。

編碼線程 ,在編碼線程中首先需要實例化編碼器,然後進入壹個循環,不斷從 VideoFrameQueue 裏面取出視頻幀元素,調用編碼器進行編碼,如果從 VideoFrameQueue 中獲取元素的返回值是 -1 ,則跳出循環,最後銷毀編碼器。

紋理拷貝線程 ,該線程首先需要初始化OpenGL ES的上下文環境,然後 綁定到新建立的這個紋理拷貝線程之上。

幀緩存對象 是任何壹個 OpenGL Program 渲染的目標。

在iOS8.0以後,系統提供了 VideoToolbox 編碼API,該API可以充分 使用硬件來做編碼工作以提升性能和編碼速度。

首先來介紹 VideoToolbox 如何將壹幀視頻幀數據編碼為 H264 的壓縮數據,並把它封裝到 H264HWEncoderImpl 類中,然後再將封裝好的這個類集成進前面的預覽系統中,集成進去之後,對於原來僅僅是預覽的項目,也可以將其保存到壹個 H264 文件中了。

使用 VideoToolbox 可以為系統帶來以下幾個優點,

而VideoToolbox是iOS 8.0 以後才公開的API,既可以做編碼又可以做解碼工作。

VideoToolbox的編碼原理如下圖所示

左邊的三幀視頻幀是發送給編碼器之前的數據,開發者必須將原始圖像數據封裝為 CVPixelBuffer 的數據結構,該數據結構是使用 VideoToolbox 編解碼的核心。

iOS的 CoreVideo 這個 framework 提供的方法 CVOpenGLESTextureCacheCreateTextureFromImage 就是專門用來將 紋理對象 關聯到 CVPixelBuffer 表示視頻幀的方法。

下面來看這個編碼器輸出的對象, Camera 預覽返回的 CMSampleBuffer 中存儲的數據是壹個 CVPixelBuffer ,而經過 VideoToolbox 編碼輸出的 CMSampleBuffer 中存儲的數據是壹個 CMBlockBuffer 的引用,如下圖所示。

如何構建編碼器,使用 Camera 的時候使用的是 AVCaptureSession ,而這裏使用的會話就是 VTCompressionSession ,這個會話就代表要 使用編碼器 ,等後續講到 硬件解碼場景 時將要使用的會話就是 VTDecompressionSessionRef 。

為什麽要判斷關鍵幀呢?因為 VideoToolbox 編碼器在每壹個關鍵幀前面都會輸出 SPS 和 PPS 信息,所以如果本幀是關鍵幀,則取出對應的 SPS 和 PPS 信息。

那麽如何取出對應的SPS和PPS信息呢?前面提到 CMSampleBuffer 中有壹個成員是 CMVideoFormatDesc ,而 SPS 和 PPS 信息就存在於這個對於視頻格式的描述裏面。

Video-Encoder 也是壹個輸出節點,該輸出節點是編碼並寫到磁盤中的。

有兩點需要註意。

第壹點,由於要將紋理對象渲染之後再放到編碼器中。

第二點,由於渲染到的目標紋理對象需要交給編碼器進行編碼。

如上圖所示,iOS平臺提供的多媒體接口是從底層到上層的結 構,之前都是直接使用 VideoToolbox ,而 AVFoundation 是基於 VideoToolbox 進行的封裝。它們的關註點不壹樣。

重點來看壹下 AVFoundation 這個層次提供的幾個主要API。

為了寫入本地文件而提供的API,該類可 以方便地將圖像和音頻寫成壹個完整的本地視頻文件。

該類可以 方便地將本地文件中的音頻和視頻解碼出來。

這個類的使用場景比較多,比如拼接視頻、合並音頻與視頻、轉換格式,以及壓縮視頻等多種場景,其實是壹個更高層次的封裝。

項目鏈接地址如下:

iOS-FDKAACEncoder

iOS-AudioToolboxEncoder

Android-CameraPreview

iOS-VideoToolboxEncoder

  • 上一篇:大眾汽車為什麽不放棄雙離合?
  • 下一篇:北京理工大學附屬小學的學校簡介。
  • copyright 2024編程學習大全網