iOS 音視訊學習-視訊錄製
前言
伴隨著大火的短視訊應用,正好自己也有點時間,就稍微學習了一下視訊相關的內容。
這種多媒體技術並沒有想象的那麼簡單,這算是一個技術方向了。我把這些視訊相關的技術分為了兩部分,暫且叫做應用層面和底層技術層面(自己取得名字並不準確)。
應用層面可以理解為呼叫一些系統的api或者使用一些三方的框架,完成專案中的需求。從一個視訊類app的流程來說,可能就要包括視訊的錄製,視訊的處理(多段合成一段,新增背景音樂等),視訊的播放等,當然了還包括一些濾鏡效果,美顏效果,甚至是一些特效。
底層技術呢,就包括視訊如何編碼解碼,相機濾鏡美顏特效的一些實現。
坦白的說,從這兩個方面來說,我都很菜,這段時間的學習就算是這方面一個進階的過程吧。首先先從應用的角度來入手,畢竟有專案驅動的話,首先咱得先把某些效果給實現了,然後再考慮他們底層一些的技術。
視訊入門
視訊實質:
純粹的視訊(不包括音訊)實質上就是一組幀圖片,經過視訊編碼成為視訊(video)檔案再把音訊(audio)檔案有些還有字幕檔案組裝在一起成為我們看到的視訊(movie)檔案。1秒內出現的圖片數就是幀率,圖片間隔越小畫面就越流暢,所以幀率越高效果就越好,需要的儲存空間也就越多。視訊格式:
MP4、MOV、AVI、RMVB這些播放格式其實都是封裝格式,除了RMVB比較特殊外,其他格式內封裝的視訊編碼格式都是H264,H264以高壓縮率聞名於世,壓縮效率比MEPG-2提升一倍多,但是世上沒有兩全其美的事,H264的解碼難度提高了3倍多。
這兩個概念就足以為我自己掃盲了。我們之前接觸到的視訊檔案,其實我們不能單一把它當做一個檔案,其實他是一種封裝。它包括了純粹的視訊,也就是一連串的圖片,包括音訊,還可能包括了字幕。
視訊錄製
我上網看了很多的文章,總結起來實現視訊的錄製有三種方法可以用。
- UIImagePickerController
- AVCaptureSession + AVCaptureMovieFileOutput
- AVCaptureSession + AVAssetWriter
下面我們來分別說一下這三種方式的。
第一種:UIImagePickerController是使用起來最簡單的,當然可定製化也是最低的,只能設定一些簡單的引數來實現基本的視訊錄製的效果。如果你想自定義錄製介面的UI,那你就只能拋棄這個簡單的方法了。
第二、三兩種方式要使用AVFoundation框架。
在AVFoundation框架中,關於視訊錄製的是要的類是AVCaptureSession,他負責調配輸入和輸出,算是總的管理。單獨管理輸入的是AVCaptureDeviceInput這個類。
AVFoundation中類很多,一個類會有各種屬性,用起來比較麻煩。我們第一次使用就先著重瞭解這個整體流程和主要的那幾個類。
第二種方法中使用了AVCaptureMovieFileOutput來作為輸出,這是一個需要很少配置就可以直接輸出視訊的類,什麼意思呢,也就是使用第二種AVCaptureSession + AVCaptureMovieFileOutput的方式錄製視訊,在你結束錄製之後,AVCaptureMovieFileOutput會幫你直接生成一個視訊檔案到你指定的路徑下,好處就是便捷,直接輸出了視訊檔案。
第三種方法我個人覺得是最麻煩的,因為它處理的最原始的資料,而且視訊資料和音訊資料是分開處理,同時這也提高了這種方法的可定製性。
這種方法是通過AVCaptureVideoDataOutput和AVCaptureAudioDataOutput 分別拿到原始的視訊和音訊的資料,再進行處理。我們拿到這些原始的資料流可以來為設定很多引數,也可以新增背景音樂水印等。然後通過AVAssetWriter把這些資料流處理合成視訊檔案。
當然了,第二和三兩種方法中,我們還需要AVCaptureVideoPreviewLayer來實時預覽攝像頭的畫面。(也就是說我們攝像頭捕獲的畫面,是通過這個類來管理展示的)
程式碼示例和DEMO
這部分具體的操作邏輯就是上面描述的這個樣子,主要的東西都在程式碼上。每個功能類的初始化,各種屬性配置,使用AVAssetWriter時資料時如何寫入的,這都是一個個麻煩的點。還好我從網上發現了一份特別棒的程式碼,然後照著大神的程式碼敲了一遍,然後針對我想實現的功能做了一點點修改,我把改後的程式碼放到百度網盤了。
下面是第二種方法裡面的一段程式碼,從中可以看出整個流程來
#pragma mark - 主要過程
- (void)setUpWithType:(VideoViewType)type {
/// -1. 提前非同步建立儲存路徑
dispatch_async(dispatch_get_main_queue(), ^{
[self videoFold];
});
///0. 初始化捕捉會話,資料的採集都在會話中處理
[self setUpInit];
///1. 設定視訊的輸入
[self setUpVideo];
///2. 設定音訊的輸入
[self setUpAudio];
///3.新增寫入檔案的fileoutput
[self setUpFileOut];
///4. 視訊的預覽層
[self setUpPreviewLayerWithType:type];
///5. 開始採集畫面
[self.session startRunning];
}
這個地方要宣告一下,那個第-1步提前非同步建立儲存路徑是我加上的,之前程式碼裡面沒有,為什麼要加這一步呢?
再點選了開始錄製按鈕之後呢,會出現短暫的卡頓然後才開始錄製,我分析了一下可能是在第3步的時候有建立路徑的操作,大家都知道,都檔案的操作是比較耗時的,所以我才在最開始就先非同步把路徑提前建立好了,然後正真開始錄製的時候就會免去這一步耗時的操作,體驗好一些,當然這是我的想法。
視訊錄製的細節功能
我們從專案開始講起吧 ~ 
這是專案結構,我提前宣告一下,那個RecordVideo檔案中我是照著大神的FileOut檔案中的程式碼敲得(也就是視訊錄製的第二種方法),裡面有一些小改動。然後針對第三種方法的程式碼修改我都是直接在AVAssetWriter這個檔案中直接改的。
下面是重點
原來這份程式碼只是從三個方面實現了視訊錄製的功能,三種方法實現的效果都是一樣的,就是簡單的視訊錄製。
但是在實際的專案中,我們有時候會遇到這麼一個需求,那就是在錄製的過程中暫停,然後恢復錄製。
我從網上看了一下,找到了兩種實現方法。一種是通過暫停時候的時間偏移量計算來實現,第二種是多段視訊拼接。第一種我暫時還沒能深入的瞭解,所以我通過修改AVAssetWriter這個檔案中的程式碼,來實現一下多段視訊拼接的思路。
我的修改是這樣的:
- 點選開始錄製是開始錄製第一段視訊,點選停止的時候就停止錄製並生成第一段視訊 (之前是點選了停止生成視訊直接跳到下一級頁面播放了)
- 再一次點選開始錄製的時候,開始錄製第二段,點選停止時停止錄製並生成第二段。
- 以此類推
- 在view上我添加了一個錄製完成的button,點選這個button就開始把之前的多段視訊合成一段。(點選button之後注意看xcode的列印臺)
- 合成完成之後。直接跳轉到下一個頁面預覽我們生成的視訊檔案。
視訊合成的程式碼可以去FMWVideoView這個類中的下面方法中看,我給了很多註釋。
//////////////// 視訊合成////////////
- (void)showFiles{}
上面方法的程式碼也有很多不足的地方,比如說如果給整個視訊錄製規定一個最大時間長度,上面實現的分段錄製並沒有收到這個最大時間的限制,這都是要進一步完善的地方,重點在於這個分段錄製合成的思路。
兩點補充
使用AVAssetWriter完成視訊錄製
在用AVAssetWriter實現錄製的時候,視訊資料和音訊資料分別是使用AVCaptureVideoDataOutput和AVCaptureAudioDataOutput來進行輸出,他與之前的AVCaptureMovieFileOutput輸出不同的是,AVCaptureMovieFileOutput會直接輸出來視訊檔案,但是AVCaptureVideoDataOutput和AVCaptureAudioDataOutput輸出的是原始的資料,再結合AVAssetWriter的把原始資料寫成檔案的能力來實現整個錄製過程。
AVAssetWriter實現錄製視訊相對來說麻煩一點,具體麻煩在把採集到的資料寫成檔案的這個過程。其實具體的實現流程並沒有什麼難懂,關鍵是這寫類的用法問題。還有在寫入檔案時,視訊和音訊是分開處理的。具體的用法,程式碼寫的很清楚,大家可以看一下。
關於視訊更改背景音樂的思考
上面說到兩個視訊合成一個視訊的例子,其實更改背景音樂用到的就是這種方法。
舉個例子說,就像我們說過的,我們要把A和B兩個視訊合成一個新的視訊,我們會建立一個視訊軌道一個音訊軌道,然後把A和B的視訊部分提取出來加到視訊軌道中,音訊部分提取出來加到音訊軌道中,然後用這兩個軌道合成一個檔案。我們在更改背景音樂的時候,就是要處理這個音訊軌道,在這個音訊軌道中,我們不再是新增之前檔案的音訊部分,而且把我們需要的那個背景音樂新增到音訊軌道中,用添加了背景音樂的音訊軌道和視訊軌道合成視訊檔案。
當然了,這裡面還涉及一些是否保留原音,或者或者跟範圍有關的一些問題,大家可以自己再研究一下。
接下來學習
濾鏡效果。
特別感謝
參考文章
音視訊相關的東西太多太多,我們一邊學習,一邊領悟吧。