一、常用的類
1、AVVideoComposition
對兩個或多個視頻軌道組合在一起的方法給出了總體描述甜刻。由一組時間范圍和描述組合行為的介紹內容組成绍撞,這些信息出現(xiàn)在組合資源內的任意時間點。
除了包含描述輸入視頻層組合的信息之外得院,還提供了配置視頻組合的渲染尺寸傻铣、縮放和幀時長等屬性。視頻組合配置確定了委托對象處理時AVComposition
的呈現(xiàn)方式尿招。這里的委托對象比如AVPlayer或AVAssetImageGenerator矾柜。
AVVideoComposition
并不是AVComposition
的子類,沒有直接關聯(lián)就谜。在視頻播放、淡出或處理時會用AVVideoComposition
來控制資源視頻軌道的視頻組合行為里覆。
2丧荐、AVVideoCompositionInstruction
AVVideoComposition
是由一組AVVideoCompositionInstruction
對象格式定義的指令組成的。這個對象所提供的最關鍵的一段數據是組合對象時間軸內的時間范圍信息喧枷,這一時間范圍是在某一組合形式出現(xiàn)時的時間范文虹统,要執(zhí)行的組合特質是通過其layerInstructions
集合定義的。
3隧甚、AVVideoCompositionLayerInstruction
用于定義對給定視頻軌道應用的模糊车荔、變形和裁剪效果。
它提供了一些方法用于在特定的時間點火災一個時間范圍內對這些值進修修改戚扳。在一段時間內對這些值應用漸變操作可以讓開發(fā)者創(chuàng)建出動態(tài)的過渡效果忧便,比如溶解和漸淡效果。
與所有AVFoundation媒體編輯類一樣帽借,視頻組合API具有不可變和可變兩種形式珠增。不可變超類形式使用于客戶端對象,比如AVAssetExportSession砍艾,不過當創(chuàng)建自己的視頻組合應用程序時蒂教,應該使用不可變子類。
AVVideoComposition并不直接和AVComposition相關脆荷。相反凝垛,這些對象和雷系AVPlayerItem的客戶端相關聯(lián),在播放組合或進行其他處理時使用這些對象蜓谋。
不將AVComposition與輸出行為強耦合梦皮,可以再播放、導出或處理視頻時更靈活地確定如何使用這些行為孤澎。
二届氢、概念理解
1、部署視頻布局
要在剪輯間添加過過渡覆旭,首先需要將兩個軌道間的視頻片段重新部署退子。大多數情況下兩個軌道就足夠岖妄,為滿足一些特殊需求加入更多軌道也是可以的,同時添加過多的軌道會對性能產生負面影響寂祥。
// 創(chuàng)建新的可變組合荐虐,添加兩個AVMediaTypeVideo類型的組合軌道
AVMutableComposition *composition = [AVMutableComposition composition];
AVMutableCompositionTrack *trackA = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack *trackB = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
// 將組合軌道添加到一個NSArray中備用。
NSArray *videoTracks = @[trackA,trackB];
安排好所需軌道之后丸凭,需要錯開部署兩個軌道的視頻布局(A-B交錯模式)福扬。當視頻按照這種方式排列后,視頻片段前面或后面的空間都會添加一個空片段惜犀。都是普通的AVCompositionTrackSegment實例铛碑,與視頻一樣,不過不包含任何媒體虽界。
// 視頻剪輯
NSArray *videoAssets ;
CMTime cursorTime = kCMTimeZero;
for (NSUInteger i = 0; i < videoAssets.count; i ++) {
//通過i%2來計算A-B模式下目標軌道的索引號
NSUInteger trackIndex = i % 2;
AVMutableCompositionTrack *currentTrack = videoTracks[trackIndex];
AVAsset *asset = videoAssets[i];
AVAssetTrack *assetTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);
// 將視頻軌道從當前資源中提取出來汽烦,插入到currentTrack中
[currentTrack insertTimeRange:timeRange ofTrack:assetTrack atTime:cursorTime error:nil];
cursorTime = CMTimeAdd(cursorTime, timeRange.duration);
}
這個時候已經采用交錯方式排列這些軌道,但是每個片段都還緊接著上一篇段的片尾莉御,兩個可見剪輯間沒有任何空間撇吞,還無法將他們組合在一起。
2礁叔、定義重疊區(qū)域
要在兩個片段中應用視頻過度牍颈,需要根據期望的過度持續(xù)時長來確定片段的重疊情況。
設置添加一個重疊時間來處理琅关。
NSArray *videoAssets ;
CMTime cursorTime = kCMTimeZero;
// 定義一個2秒的CMTIme 作為期望的過渡持續(xù)時長煮岁。
CMTime transitionDuration = CMTimeMake(2, 1);
for (NSUInteger i = 0; i < videoAssets.count; i ++) {
//通過i%2來計算A-B模式下目標軌道的索引號
NSUInteger trackIndex = i % 2;
AVMutableCompositionTrack *currentTrack = videoTracks[trackIndex];
AVAsset *asset = videoAssets[i];
AVAssetTrack *assetTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);
// 將視頻軌道從當前資源中提取出來,插入到currentTrack中
[currentTrack insertTimeRange:timeRange ofTrack:assetTrack atTime:cursorTime error:nil];
cursorTime = CMTimeAdd(cursorTime, timeRange.duration);
// 減去過渡時長死姚,過渡時長適度偏移下一個插入點
cursorTime = CMTimeSubtract(cursorTime, transitionDuration);
}
此時的組合可以播放人乓,但是視頻軌道怒又內在的z索引行為,第一個視頻軌道和第二個之前都毒,第二個在第三個之前色罚,一次類推。
在這種情況下播放組合账劲,會發(fā)現(xiàn)第一戳护、第二段視頻什么都沒有,最后會看到最前面軌道中所包含的第三個片段瀑焦。在看到第二個軌道的內容前腌且,需要定義時間范圍并向組合方法解釋這兩個軌道應該如何組合。
創(chuàng)建重疊區(qū)域時榛瓮,已經將視頻時間軸減少了(videoCount-1)*transitionDuration的大小铺董。如果組合中有額外的軌道,則需要將它們的時長相應的縮短以滿足新的視頻時間軸。
3精续、計算通過和過渡的時間范圍
AVVideoComposition由一組AVVideoCompositionInstruction對象組成坝锰。其中最重要的數據是時間范圍,用來表示某種出現(xiàn)的組合方式持續(xù)的時長重付。在開始創(chuàng)建AVVideoCompositionInstruction實例前顷级,首先需要為組合對象計算一系列時間范圍。
需要計算兩個類型的時間范圍确垫。第一個通常被認為是通過(pass-through)時間范圍弓颈,在這個時間范圍內希望一個軌道的所有幀序列都在不與其他軌道進行混合的情況下通過某一區(qū)域。
第二個時間范圍類型是過渡(transition)時間范圍删掀。定義了在組合中視頻片段重疊的區(qū)域翔冀,并在時間軸上標記處應用過渡效果的區(qū)域。
可以通過多種方式計算這些時間范圍披泪,一種很好的通用方法橘蜜。
NSArray *videoAssets ;
CMTime cursorTime = kCMTimeZero;
// 定義一個2秒的CMTIme 作為期望的過渡持續(xù)時長。
CMTime transitionDuration = CMTimeMake(2, 1);
NSMutableArray *passThroughTiemRanges = [NSMutableArray array];
NSMutableArray *transitionTimeRanges = [NSMutableArray array];
NSUInteger videoCount = [videoAssets count];
for (NSUInteger i = 0; i < videoCount; i ++) {
AVAsset *asset = videoAssets[i];
CMTimeRange timeRange = CMTimeRangeMake(cursorTime, asset.duration);
if (i > 0) {
timeRange.start = CMTimeAdd(timeRange.start, transitionDuration);
timeRange.duration = CMTimeSubtract(timeRange.duration, transitionDuration);
}
if (i + 1 < videoCount) {
timeRange.duration = CMTimeSubtract(timeRange.duration, transitionDuration);
}
[passThroughTiemRanges addObject:[NSValue valueWithCMTimeRange:timeRange]];
cursorTime = CMTimeAdd(cursorTime, asset.duration);
cursorTime = CMTimeSubtract(cursorTime, transitionDuration);
if (i + 1 < videoCount) {
timeRange = CMTimeRangeMake(cursorTime, transitionDuration);
NSValue *timeRangeValue = [NSValue valueWithCMTimeRange:timeRange];
[transitionTimeRanges addObject:timeRangeValue];
}
}
創(chuàng)建所需要的通過和過渡時間范圍被認為是整個處理過程中最重要的一步操作付呕,他并不是最難解決的問題,不過容易出錯跌捆。進行這一步處理的時候徽职,有兩個關鍵點:
- 1、計算的時間范圍必須沒有任何空隙或重疊佩厚。必須是緊接上一個片段之后按需排列的最新時間范圍姆钉。
- 2、計算必須考慮組合對象持續(xù)時間抄瓦。如果組合中還包含額外的軌道潮瓶,就需要使它們遵循目前的視頻時間軸,或者根據他們的持續(xù)時長擴展最終的時間范圍钙姊。
如果沒有注意到這兩點毯辅,組合對象仍可播放,但視頻內容不會被渲染煞额,只會顯示黑屏思恐。
Apple Developer Center中AVCompositionDebugViewer可以幫助呈現(xiàn)組合。
4膊毁、創(chuàng)建組合和層指令
創(chuàng)建AVVideoCompositionInstruction
和AVVideoCompositionLayerInstruction
示例胀莹,提供視頻組合方法所執(zhí)行的指令。
NSMutableArray *compositionInstructions = [NSMutableArray array];
//遍歷所有的通過時間范圍
for (NSUInteger i = 0; i > passThroughTiemRanges.count; i ++) {
// 循環(huán)在連個需要創(chuàng)建所需指令的視頻軌道間前后切換婚温。
NSInteger trackIndex = i % 2;
AVMutableCompositionTrack *currentTrack = videoTracks[trackIndex];
// 創(chuàng)建AVMutableVideoCompositionInstruction實例描焰,設置當前通過時間范圍作為timeRange
AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
instruction.timeRange = [passThroughTiemRanges[i] CMTimeRangeValue];
// 為活動組合創(chuàng)建一個新的AVMutableVideoCompositionLayerInstruction
AVMutableVideoCompositionLayerInstruction *layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:currentTrack];
// 添加到數組
instruction.layerInstructions = @[layerInstruction];
// 設置為組合指令的layerInstructions屬性。
[compositionInstructions addObject:instruction];
//組合的通過時間范圍區(qū)域只需要一個與要呈現(xiàn)視頻幀的軌道相關的單獨層指令栅螟。
if (i < transitionTimeRanges.count) {
//要創(chuàng)建過渡時間的指令荆秦,需要得到前一個軌道(過渡前的軌道)的引用和后一個軌道(過渡后的軌道)的引用
//確保軌道的引用始終順序正確篱竭。
AVCompositionTrack *foregroundTrack = videoTracks[trackIndex];
AVCompositionTrack *backgroundTrack = videoTracks[1-trackIndex];
//創(chuàng)建一個AVMutableVideoCompositionInstruction實例
AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
//設置當前過渡時間范圍為它的timeRange屬性。
CMTimeRange timeRange = [transitionTimeRanges[i] CMTimeRangeValue];
instruction.timeRange = timeRange;
// 為每個軌道創(chuàng)建一個AVMutableVideoCompositionLayerInstruction實例
// 在這些層指令上定義從一個場景到另一個場景的過渡效果
AVMutableVideoCompositionLayerInstruction *fromLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:foregroundTrack];
AVMutableVideoCompositionLayerInstruction *toLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:backgroundTrack];
// 將兩個層指令添加到數組萄凤,并設置他們作為當前組合指令的layerInstructions屬性
// 這一數組中的元素進行排序非常重要室抽,因為它定義了組合輸出中視頻圖層的z軸順序。
instruction.layerInstructions = @[fromLayerInstruction,toLayerInstruction];
[compositionInstructions addObject:instruction];
}
}
5靡努、創(chuàng)建和配置AVVideoComposition
AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition];
videoComposition.instructions = compositionInstructions;
// 定義組合應該被渲染尺寸坪圾。對應于組合中視頻原始大小720p-1280*720,1080p-1920*1080
videoComposition.renderSize = CGSizeMake(1280.f, 720.f);
// 設置有效幀率
videoComposition.frameDuration = CMTimeMake(1, 30);
// 視頻組合應用的縮放
videoComposition.renderScale = 1.0f;
6惑朦、創(chuàng)建視頻組合的捷徑
// 創(chuàng)建一個基礎的AVMutableVideoComposition
AVMutableVideoComposition *co = [AVMutableVideoComposition videoCompositionWithPropertiesOfAsset:composition];
包含基礎配置:
- instructions 屬性包含一組完整的基于組合視頻軌道(以及其中包含的片段空間布局)的組合和層指令
- renderSize 屬性被設置為AVComposition對象的naturalSize兽泄,如果沒有設置,則使用能夠滿足組合視頻軌道中最大視頻緯度的尺寸值漾月。
- frameDuration 設置為組合視頻軌道中最大nominalFrameRate的值病梢。如果所有軌道的nominalFrameRate值都為0,則frameDuration設置層默認值1/30秒(30FPS)梁肿。
- renderScale 始終設置為1.0
三蜓陌、過渡效果
1、溶解
[fromLayer setOpacityRampFromStartOpacity:1.0 toEndOpacity:0.0 timeRange:timeRange];
// 對fromLayer對象設置一個模糊漸變
2吩蔑、推出
//定義個對于輸入視頻層的變換钮热。
//CGAffineTransForm可以修改層的轉化、旋轉烛芬、縮放隧期。對層應用一個漸變的變化可以衍生出很多效果。
CGAffineTransform identityTransform = CGAffineTransformIdentity;
CGFloat videoWidth = videoCompostion.renderSize.width;
// 這里通過轉化(translation)轉換赘娄,修改圖層的x仆潮,y坐標。
CGAffineTransform fromDestTransform = CGAffineTransformMakeTranslation(-videoWidth, 0);
CGAffineTransform toStartTransform = CGAffineTransformMakeTranslation(videoWidth, 0);
// 設置fromLayer的漸變效果遣臼,初始變換設置為identityTransform性置,終點變換為fromDestTransform
[fromLayer setTransformRampFromStartTransform:identityTransform toEndTransform:fromDestTransform timeRange:timeRange];
[toLayer setTransformRampFromStartTransform:toStartTransform toEndTransform:identityTransform timeRange:timeRange];
3、擦除
CGFloat videoWidth = videoCompostion.renderSize.width;
CGFloat videoHeight = videoCompostion.renderSize.height;
CGRect startRect = CGRectMake(0, 0, videoWidth, videoHeight);
CGRect endRect = CGRectMake(0, videoHeight, videoWidth, 0);
[fromLayer setCropRectangleRampFromStartCropRectangle:startRect toEndCropRectangle:endRect timeRange:timeRange];
完整代碼.m
#import "THTransitionCompositionBuilder.h"
#import "THVideoItem.h"
#import "THAudioItem.h"
#import "THVolumeAutomation.h"
#import "THTransitionComposition.h"
#import "THTransitionInstructions.h"
#import "THFunctions.h"
@interface THTransitionCompositionBuilder ()
@property (strong, nonatomic) THTimeline *timeline;
@property (strong, nonatomic) AVMutableComposition *composition;
@property (weak, nonatomic) AVMutableCompositionTrack *musicTrack;
@end
@implementation THTransitionCompositionBuilder
- (id)initWithTimeline:(THTimeline *)timeline {
self = [super init];
if (self) {
_timeline = timeline;
}
return self;
}
- (id <THComposition>)buildComposition {
self.composition = [AVMutableComposition composition];
[self buildCompositionTracks];
AVVideoComposition *videoComposition = [self buildVideoComposition];
AVAudioMix *audioMix = [self buildAudioMix];
return [[THTransitionComposition alloc] initWithComposition:self.composition
videoComposition:videoComposition
audioMix:audioMix];
}
- (void)buildCompositionTracks {
CMPersistentTrackID trackID = kCMPersistentTrackID_Invalid;
// 創(chuàng)建兩個AVMediaTypeVideo類型的軌道暑诸,提供所需的A-B軌道排列
AVMutableCompositionTrack *compositionTrackA = [self.composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:trackID];
AVMutableCompositionTrack *compositionTrackB = [self.composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:trackID];
NSArray *videoTracks = @[compositionTrackA,compositionTrackB];
CMTime cursorTime = kCMTimeZero;
CMTime transitionDuration = kCMTimeZero;
if (!THIsEmpty(self.timeline.transitions)) {
//判斷transitions是否被填充蚌讼,即視頻過渡是否被設置為激活。
//如果激活過渡效果个榕,設置常量值
transitionDuration = THDefaultTransitionDuration;
}
NSArray *videos = self.timeline.videos;
for (NSUInteger i = 0; i < videos.count; i ++) {
NSUInteger trackIndex = i%2;
THVideoItem *item = videos[i];
// 確定要插入的目標軌道
AVMutableCompositionTrack *currentTrack = videoTracks[trackIndex];
AVAssetTrack *assetTrack = [[item.asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
[currentTrack insertTimeRange:item.timeRange ofTrack:assetTrack atTime:cursorTime error:nil];
cursorTime = CMTimeAdd(cursorTime, item.timeRange.duration);
cursorTime = CMTimeSubtract(cursorTime, transitionDuration);
}
//添加畫外音和音樂軌道
[self addCompositionTrackOfType:AVMediaTypeAudio withMediaItems:self.timeline.voiceOvers];
NSArray *musicItems = self.timeline.musicItems;
self.musicTrack = [self addCompositionTrackOfType:AVMediaTypeVideo withMediaItems:musicItems];
}
- (AVVideoComposition *)buildVideoComposition {
// 創(chuàng)建一個新的AVVideoComposition實例篡石,自動創(chuàng)建所需的組合對象和層指令
// 設置renderSize,renderScale和frameDuration屬性為相應的值西采。
AVVideoComposition *videoCompostion = [AVMutableVideoComposition videoCompositionWithPropertiesOfAsset:self.composition];
// 從AVVideoComposition中提取相關的層指令凰萨,可以應用視頻過渡效果。
// 返回一個THTransitionInstructions對象數組
NSArray *transitionInstructions = [self transitionInstructionsInVideoComposition:videoCompostion];
for (THTransitionInstructions *instructions in transitionInstructions) {
CMTimeRange timeRange = instructions.compositionInstruction.timeRange;
AVMutableVideoCompositionLayerInstruction *fromLayer = instructions.fromLayerInstruction;
AVMutableVideoCompositionLayerInstruction *toLayer = instructions.toLayerInstruction;
THVideoTransitionType type = instructions.transition.type;
if (type == THVideoTransitionTypeDissolve) { // 溶解過渡效果
[fromLayer setOpacityRampFromStartOpacity:1.0 toEndOpacity:0.0 timeRange:timeRange];
// 對fromLayer對象設置一個模糊漸變
} else if (type == THVideoTransitionTypePush) { //推入過渡效果
//定義個對于輸入視頻層的變換。
//CGAffineTransForm可以修改層的轉化胖眷、旋轉武通、縮放。對層應用一個漸變的變化可以衍生出很多效果珊搀。
CGAffineTransform identityTransform = CGAffineTransformIdentity;
CGFloat videoWidth = videoCompostion.renderSize.width;
// 這里通過轉化(translation)轉換冶忱,修改圖層的x,y坐標境析。
CGAffineTransform fromDestTransform = CGAffineTransformMakeTranslation(-videoWidth, 0);
CGAffineTransform toStartTransform = CGAffineTransformMakeTranslation(videoWidth, 0);
// 設置fromLayer的漸變效果囚枪,初始變換設置為identityTransform,終點變換為fromDestTransform
[fromLayer setTransformRampFromStartTransform:identityTransform toEndTransform:fromDestTransform timeRange:timeRange];
[toLayer setTransformRampFromStartTransform:toStartTransform toEndTransform:identityTransform timeRange:timeRange];
} else if (type == THVideoTransitionTypeWipe) { //擦除過渡效果
CGFloat videoWidth = videoCompostion.renderSize.width;
CGFloat videoHeight = videoCompostion.renderSize.height;
CGRect startRect = CGRectMake(0, 0, videoWidth, videoHeight);
CGRect endRect = CGRectMake(0, videoHeight, videoWidth, 0);
[fromLayer setCropRectangleRampFromStartCropRectangle:startRect toEndCropRectangle:endRect timeRange:timeRange];
}
// 配合組合指令的layerInstructions劳淆,按顯示的順序傳遞指令链沼,確保正確應用視頻過渡效果
instructions.compositionInstruction.layerInstructions = @[fromLayer,toLayer];
}
return videoCompostion;
}
// Extract the composition and layer instructions out of the
// prebuilt AVVideoComposition. Make the association between the instructions
// and the THVideoTransition the user configured in the timeline.
- (NSArray *)transitionInstructionsInVideoComposition:(AVVideoComposition *)vc {
NSMutableArray *transitionInstructions = [NSMutableArray array];
int layerInstructionIndex = 1;
NSArray *compositionInstructions = vc.instructions;
// 遍歷從AVVideoComposition中得到的AVVideoCompositionInstruction對象
for (AVMutableVideoCompositionInstruction *vci in compositionInstructions) {
//只關心包含兩個層指令的組合指令,表示這個指令定義了組合中的過渡區(qū)域沛鸵。
if (vci.layerInstructions.count == 2) {
THTransitionInstructions *instructions = [[THTransitionInstructions alloc] init];
instructions.compositionInstruction = vci;
// 自動創(chuàng)建的層指令通常保存于layerInstructions數組
//第一個軌道的層指令作為數組的第一個元素括勺,接下來是第二個軌道的層指令
//
instructions.fromLayerInstruction = [vci.layerInstructions[1-layerInstructionIndex] mutableCopy];
instructions.toLayerInstruction = [vci.layerInstructions[layerInstructionIndex] mutableCopy];
[transitionInstructions addObject:instructions];
layerInstructionIndex = layerInstructionIndex == 1 ? 0:1;
}
}
NSArray *transitions = self.timeline.transitions;
if (THIsEmpty(transitions)) {
//禁用了過渡
return transitionInstructions;
}
// 如果過渡已啟用,遍歷transitionInstructions曲掰,并將用戶選定的THVideoTransition對象和它進行關聯(lián)疾捍。
for (NSUInteger i = 0; i < transitionInstructions.count; i ++) {
THTransitionInstructions *tis = transitionInstructions[i];
tis.transition = self.timeline.transitions[i];
}
return transitionInstructions;
}
- (AVMutableCompositionTrack *)addCompositionTrackOfType:(NSString *)mediaType
withMediaItems:(NSArray *)mediaItems {
AVMutableCompositionTrack *compositionTrack = nil;
if (!THIsEmpty(mediaItems)) {
compositionTrack =
[self.composition addMutableTrackWithMediaType:mediaType
preferredTrackID:kCMPersistentTrackID_Invalid];
CMTime cursorTime = kCMTimeZero;
for (THMediaItem *item in mediaItems) {
if (CMTIME_COMPARE_INLINE(item.startTimeInTimeline, !=, kCMTimeInvalid)) {
cursorTime = item.startTimeInTimeline;
}
AVAssetTrack *assetTrack = [[item.asset tracksWithMediaType:mediaType] firstObject];
[compositionTrack insertTimeRange:item.timeRange ofTrack:assetTrack atTime:cursorTime error:nil];
// Move cursor to next item time
cursorTime = CMTimeAdd(cursorTime, item.timeRange.duration);
}
}
return compositionTrack;
}
- (AVAudioMix *)buildAudioMix {
NSArray *items = self.timeline.musicItems;
// Only one allowed
if (items.count == 1) {
THAudioItem *item = self.timeline.musicItems[0];
AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
AVMutableAudioMixInputParameters *parameters =
[AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:self.musicTrack];
for (THVolumeAutomation *automation in item.volumeAutomation) {
[parameters setVolumeRampFromStartVolume:automation.startVolume
toEndVolume:automation.endVolume
timeRange:automation.timeRange];
}
audioMix.inputParameters = @[parameters];
return audioMix;
}
return nil;
}
@end