關(guān)鍵字:AVFoundation,AVAsset,AVComposition,AVVideoComposition,AVVideoCompositionInstruction,AVVideoCompositionLayerInstruction
最近做了一個關(guān)于視頻處理的項(xiàng)目,剛開始的需求大概只有:
- 對視頻制定區(qū)域進(jìn)行裁剪
- 對幾段視頻進(jìn)行裁剪拼接
- 對視頻及音頻進(jìn)行拼接
遇到不會做的需求,很習(xí)慣就是Google一下別人的Demo是怎么做的,然后大概看一下是如何實(shí)現(xiàn)的,然后拿過來修改一下。嗯,按照我后來學(xué)習(xí)了一下AVFoundation這個庫之后,感覺我第一階段的工作自己應(yīng)該是幾乎沒弄懂什么。好了,工作又有進(jìn)一步的需求了:
- 實(shí)現(xiàn)視頻間的過渡效果
- 實(shí)現(xiàn)多段視頻分屏顯示的效果
- 實(shí)現(xiàn)視頻添加字幕,以及字幕動畫效果
這些內(nèi)容Google出來的內(nèi)容,很難明白到底是怎么實(shí)現(xiàn)的,已經(jīng)其實(shí)什么都沒說出來,很多的都是這個復(fù)制另一個的代碼,修改一下,這完全不能做到讓你學(xué)會使用,大多都是讓你學(xué)會搬磚。
還好我找到一本書AVFoundation秘籍
對于媒體的編輯,主要看熟里面的第九章以及第十一章,進(jìn)階的是在第十二章。
媒體的組合和編輯
在iOS中,所有的音視頻都被抽象成AVAsset對象,這樣一來就可以簡化了不同格式的媒體,并且可以將所有的音視頻都是用統(tǒng)一的接口進(jìn)行處理,這很符合面向?qū)ο蟮牧?xí)慣。
如果我們有使用過一些音視頻剪切的工具,其實(shí)可以看到在處理剪切合成的時候,你的每一段素材,在進(jìn)行剪切以及合并之后,其實(shí)仍然是可以進(jìn)行單獨(dú)播放已經(jīng)繼續(xù)編輯的一個實(shí)體,這個原理跟iOS的媒體組合和編輯是差不多的。

其實(shí)可以簡單的理解成:
- 我們平時播放的
AVAsset,是由多段完整的AVAssetTrack,其中最簡單的就是包含一段完整AVMediaTypeVideo類型的AVAssetTrack,以及一段完整AVMediaTypeAudio類型的AVAssetTrack。 -
AVAssetTrack用于提供接口對媒體的軌道進(jìn)行操作。 -
AVComposition這個組合的類,顧名思義是用于負(fù)責(zé)描述多個AVAssetTrack組合的關(guān)系,由于他是繼承于AVAsset的,因此他也是可以單獨(dú)作為AVAsset來使用,這里非常重要。 -
AVCompositionTrackSegment,軌道的片段,不過目前我還沒有需要使用。
于是使用上面類圖,我們能做什么呢?我們能簡單的將幾個視頻首尾拼接成一個視頻進(jìn)行播放導(dǎo)出,以及將音頻視頻進(jìn)行簡單的拼接處理(ps:這里的視頻拼接不能在同一時刻有另外的視頻在播放,不然會出現(xiàn)黑屏的現(xiàn)象,解決方法會在后面出現(xiàn))。
//首先定義兩個需要操作的資源
AVAsset *assetA = [AVAsset assetWithURL:[NSURL URLWithString:@"A"]];
AVAsset *assetB = [AVAsset assetWithURL:[NSURL URLWithString:@"B"]];
//定義兩個資源的軌道
AVAssetTrack *trackA = [assetA tracksWithMediaType:AVMediaTypeVideo].firstObject;
AVAssetTrack *trackB = [assetB tracksWithMediaType:AVMediaTypeVideo].firstObject;
//定義一個用于操作資源組合的可變資源
AVMutableComposition *composition = [AVMutableComposition composition];
//定義兩個資源的組合軌道
AVMutableCompositionTrack *compositionTrackA = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack *compositionTrackB = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
//為兩個組合軌道分別插入0s-3s段的資源,a在0-3s播放,b在3-6s播放
[compositionTrackA insertTimeRange:CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(3, NSEC_PER_SEC)) ofTrack:trackA atTime:kCMTimeZero error:nil];
[compositionTrackB insertTimeRange:CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(3, NSEC_PER_SEC)) ofTrack:trackB atTime:CMTimeMakeWithSeconds(3, NSEC_PER_SEC) error:nil];
//到這里就可以直接使用composition進(jìn)行播放
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:composition];
AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];
創(chuàng)建視頻的過渡效果
上面的AVComposition是以資源軌道為單位的對不同媒體資源進(jìn)行組合,然后對于音頻和視頻還有更進(jìn)一步的細(xì)分,這也為創(chuàng)建更多視頻效果提供了方便。下面先說對于視頻的組合。
以視頻添加過渡效果為例,還記得上面說的兩個視頻,不能重疊在一起播放的問題嗎?這里就以這個為例。不過需要先引入一些概念。

-
AVVideoComposition,視頻組合類,類似上面的AVComposition概念,不過這里是對一個媒體資源中,負(fù)責(zé)所有視頻資源組合關(guān)系的一個類,跟AVComposition是一點(diǎn)關(guān)系都沒有。這個類除了可以包含視頻組合信息之外,還能配置視頻的渲染尺寸,縮放,和幀時長等屬性。 -
AVVideoCompositionInstruction,AVVideoCompostion是由一組AVVideoCompositionInstruction對象格式定義的指令組成的。這個對象提供的最關(guān)鍵的信息是組合對象時間軸內(nèi)的時間范圍信息,其實(shí)就是表明這個視頻組合的開始時間和持續(xù)時間。要執(zhí)行的組合特質(zhì)是通過AVVideoCompositionLayerInstruction -
AVVideoCompositionLayerInstruction,用于定義給定視頻軌道應(yīng)用的模糊,變形和裁剪效果。
PS:AVVideoComposition并不直接與AVCompostion相關(guān),相反看上面視頻組合類.png,這些對象是和類似AVPlayItem等客戶端相關(guān)聯(lián),在播放組合或進(jìn)行其他處理時使用這些對象。
下面的例子,其實(shí)我們可以對于每一段AVVideoCompositionInstrction都自行定義,但是有更簡便的方法,為何不用呢?
AVMutableVideoComposition *videoCompostion = [AVMutableVideoComposition videoCompositionWithPropertiesOfAsset:composition];
當(dāng)你使用這段代碼的時候,會自動生成一個默認(rèn)的AVVideoComposition,并且會自動生成默認(rèn)的AVVideoCompositionInstruction,AVVideoCompostionLayerInstruction。那么它這個自動生成的規(guī)則是怎么樣的呢?多嘗試幾次不一樣的視頻組合,不難發(fā)現(xiàn):
- 在時間軸延伸的方向,只要某一時刻的視頻組合數(shù)量發(fā)生變化,那么就會產(chǎn)生一個新的
AVVideoCompositionInstruction對象
例如:現(xiàn)在有三個視頻軌道,A,B,C(有數(shù)字的時刻代表視頻在該時間段有內(nèi)容,#代表空)
軌道A:1 2 3 4 5 6 7 8 9
軌道B:1 2 3 # # # # # #
軌道C:1 2 3 4 5 6 # # #
- 軌道A,B,C在1~3s內(nèi)都是有內(nèi)容的,因此這會生成第一個
AVVideoCompositionInstruction - 軌道A,C在3~6s內(nèi)有內(nèi)容,由于這個時間段內(nèi),視頻組合數(shù)量發(fā)生了改變,因此這里是第二個
AVVideoCompositionInstruction - 最后在6-9s,只有軌道A有視頻,因此這里是第三個
AVVideoCompositionInstruction
在我們生成了AVVideoCompositionInstruction之后,我們需要對里面的AVVideoCompositionLayerInstruction進(jìn)行自定義,具體這個過渡效果要什么樣子,靠你自己組合視頻的數(shù)量,以及改變視頻的模糊,變形和裁剪效果,這里就不一一實(shí)現(xiàn)了。
以上面3個軌道例子中的第3-6s作為演示
最終的效果是軌道A在3-6s播放的時候會漸漸模糊
AVMutableVideoComposition *videoCompostion = [AVMutableVideoComposition videoCompositionWithPropertiesOfAsset:composition];
for (AVMutableVideoCompositionInstruction *vci in videoCompostion.instructions) {
if (vci.layerInstructions.count == 2) {
AVMutableVideoCompositionLayerInstruction *fromLayerInstruction = (AVMutableVideoCompositionLayerInstruction *)vci.layerInstructions.firstObject;
// AVVideoCompositionLayerInstruction *toLayerInstruction = vci.layerInstructions.firstObject;
CMTimeRange timeRange = vci.timeRange;
//漸變效果
[fromLayerInstruction setOpacityRampFromStartOpacity:1.0 toEndOpacity:0 timeRange:timeRange];
}
}