回顧
在上一篇GPUImage詳細(xì)解析(八)視頻合并混音介紹了如何使用GPUImage進(jìn)行視頻的合并偎窘,以及混音译隘。這次使用AVFoundation框架來實(shí)現(xiàn)這個(gè)功能好啰。
概念
-
AVPlayer
視頻播放類赡磅,本身不顯示視頻涡驮,需創(chuàng)建一個(gè)AVPlayerLayer層暗甥,添加到視圖 -
AVAssetTrack
資源軌道,包括音頻軌道和視頻軌道 -
AVAsset
媒體信息 -
AVURLAsset
根據(jù)URL路徑創(chuàng)建的媒體信息 -
AVPlayerItem
媒體資源管理對(duì)象捉捅,管理視頻的基本信息和狀態(tài) -
AVMutableVideoCompositionInstruction
視頻操作指令 -
AVMutableVideoCompositionLayerInstruction
視頻軌道操作指令撤防,需要添加到AVMutableVideoCompositionInstruction
-
AVMutableAudioMixInputParameters
音頻操作參數(shù) -
AVMutableComposition
包含多個(gè)軌道的媒體信息,可以添加棒口、刪除軌道 -
AVMutableVideoComposition
視頻操作指令集合
效果
視頻效果如下寄月,音頻效果可運(yùn)行demo。
核心思路
分別加載多個(gè)AVURLAsset无牵,用GCD保證異步加載完成后回調(diào)漾肮,調(diào)用Editor類配置軌道信息、視頻操作指令和音頻指令參數(shù)合敦。
具體細(xì)節(jié)
流程圖如下
a初橘、配置軌道信息
- 1,計(jì)算變化的長(zhǎng)度充岛,確保變換的長(zhǎng)度不大于最小的視頻的長(zhǎng)度的一半保檐;
思考1:demo中是如何計(jì)算小于一半,為何要小于一半崔梗?
- 2夜只,添加兩個(gè)視頻軌道,兩個(gè)音頻軌道蒜魄;
- 3扔亥,在視頻索引對(duì)應(yīng)的軌道(%2),插入視頻軌道信息和音頻軌道信息谈为;
思考2:當(dāng)多個(gè)視頻在同一個(gè)音軌插入多個(gè)信息旅挤,如何保證不重疊?
- 4伞鲫,計(jì)算直接播放和變換的時(shí)間粘茄;
// 確保最后合并后的視頻,變換長(zhǎng)度不會(huì)超過最小長(zhǎng)度的一半
CMTime transitionDuration = self.transitionDuration;
for (i = 0; i < clipsCount; i++ ) {
NSValue *clipTimeRange = [self.clipTimeRanges objectAtIndex:i];
if (clipTimeRange) {
CMTime halfClipDuration = [clipTimeRange CMTimeRangeValue].duration;
halfClipDuration.timescale *= 2;
transitionDuration = CMTimeMinimum(transitionDuration, halfClipDuration);
}
}
AVMutableCompositionTrack *compositionVideoTracks[2];
AVMutableCompositionTrack *compositionAudioTracks[2];
compositionVideoTracks[0] = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; // 添加視頻軌道0
compositionVideoTracks[1] = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; // 添加視頻軌道1
compositionAudioTracks[0] = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; // 添加音頻軌道0
compositionAudioTracks[1] = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; // 添加音頻軌道1
b秕脓、配置視頻操作指令
- 1柒瓣,新建視頻操作指令集合;
- 2吠架,根據(jù)視頻所在對(duì)應(yīng)的軌道(%2)芙贫,新建視頻操作指令passThroughInstruction,長(zhǎng)度為passThroughTimeRanges傍药,同時(shí)定義passThroughLayer直接播放的視頻軌道操作指令磺平,并設(shè)置passThroughLayer為passThroughInstruction的視頻軌道操作指令集合魂仍;
- 3,根據(jù)視頻所在對(duì)應(yīng)軌道拣挪,新建視頻操作指令transitionInstruction蓄诽,長(zhǎng)度為transitionTimeRanges,同時(shí)根據(jù)軌道定義視頻軌道操作指令fromLayer和toLayer媒吗,并設(shè)置fromLayer和toLayer的變換方式與時(shí)間仑氛;
- 4,把passThroughInstruction和transitionInstruction添加到視頻指令集合闸英;
AVMutableVideoCompositionInstruction *passThroughInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; // 新建指令
passThroughInstruction.timeRange = passThroughTimeRanges[i]; // 直接播放
AVMutableVideoCompositionLayerInstruction *passThroughLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTracks[alternatingIndex]]; // 視頻軌道操作指令
passThroughInstruction.layerInstructions = [NSArray arrayWithObject:passThroughLayer];
[instructions addObject:passThroughInstruction]; // 添加到指令集合
if (i+1 < clipsCount) { // 不是最后一個(gè)
AVMutableVideoCompositionInstruction *transitionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; // 新建指令
transitionInstruction.timeRange = transitionTimeRanges[i]; // 變換時(shí)間
AVMutableVideoCompositionLayerInstruction *fromLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTracks[alternatingIndex]]; // 視頻軌道操作指令
AVMutableVideoCompositionLayerInstruction *toLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTracks[1-alternatingIndex]]; // 新的軌道指令
// 1 dao 0
[fromLayer setOpacityRampFromStartOpacity:1.0 toEndOpacity:0.0 timeRange:transitionTimeRanges[i]];
// 目的軌道锯岖,從0到1
[toLayer setOpacityRampFromStartOpacity:0.0 toEndOpacity:1.0 timeRange:transitionTimeRanges[i]];
transitionInstruction.layerInstructions = [NSArray arrayWithObjects:toLayer, fromLayer, nil];
[instructions addObject:transitionInstruction];
}
c、配置音頻軌道參數(shù)
- 1甫何,新建音頻軌道參數(shù)集合出吹;
- 2,根據(jù)視頻所在索引辙喂,新建當(dāng)前音軌的參數(shù)trackMix1捶牢,設(shè)置變換時(shí)間內(nèi)音量從1.0到0.0;
- 3巍耗,根據(jù)視頻所在索引秋麸,新建另外一條音軌的參數(shù)trackMix2,設(shè)置變換時(shí)間內(nèi)音量從0.0到1.0炬太;設(shè)置直接播放時(shí)間內(nèi)音量一直為1.0灸蟆;
- 4,把參數(shù)trackMix1和trackMix2添加到音頻軌道參數(shù)集合亲族;
AVMutableAudioMixInputParameters *trackMix1 = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:compositionAudioTracks[alternatingIndex]]; // 音軌0的參數(shù)
[trackMix1 setVolumeRampFromStartVolume:1.0 toEndVolume:0.0 timeRange:transitionTimeRanges[i]]; // 音軌0炒考,變換期間音量從1.0到0.0
[trackMixArray addObject:trackMix1];
AVMutableAudioMixInputParameters *trackMix2 = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:compositionAudioTracks[1 - alternatingIndex]]; // 音軌1的參數(shù)
[trackMix2 setVolumeRampFromStartVolume:0.0 toEndVolume:1.0 timeRange:transitionTimeRanges[i]]; // 變換期間音量從0.0到1.0
[trackMixArray addObject:trackMix2];
總結(jié)
AVPlayer通過KVO監(jiān)聽rate屬性,status屬性霎迫,用notification來監(jiān)聽播放完成斋枢;
AVPlayer和AVPlayerItem的使用不復(fù)雜,解析集中在SimpleEditor類如何配置軌道信息和音視頻操作指令知给。
代碼地址可以點(diǎn)這里瓤帚。
思考
思考1
通過timescale*2,再用CMTimeMinimum炼鞠;處于中間的視頻要經(jīng)歷兩次變換缘滥,故而變換的長(zhǎng)度不能大于最小視頻長(zhǎng)度的一半轰胁;
思考2
音軌插入的函數(shù)有開始點(diǎn)和持續(xù)時(shí)間谒主,只要保證區(qū)間不重疊,音頻就不會(huì)重疊赃阀;