Editing(編輯)
AVFoundation框架提供了一組功能豐富的類,以便于編輯視聽資料。AVFoundation的編輯API的核心是組合。組合僅僅是來自一個或多個不同媒體asset的曲目的集合。的AVMutableComposition類提供用于插入和移除軌道,以及管理他們的時間排序的接口。圖3-1顯示了新組合如何從現有asset的組合中拼接成一個新的asset。如果您想要做的是將多個asset合并到一個文件中,那就是您需要的細節。如果您想在作曲中的軌道上執行任何自定義音頻或視頻處理,則需要分別包含音頻混合或視頻構圖。
使用AVMutableAudioMix該類,您可以對構圖中的音軌執行自定義音頻處理,如圖3-2所示。目前,您可以為音軌指定最大音量或設置音量斜坡。
您可以使用AVMutableVideoComposition課程直接與組合中的視頻軌道一起工作,以進行編輯,如圖3-3所示。使用單個視頻構圖,您可以為輸出視頻指定所需的渲染大小和縮放以及幀持續時間。通過視頻作品的指示(由AVMutableVideoCompositionInstruction類表示),您可以修改視頻的背景顏色并應用圖層說明。這些層指令(由AVMutableVideoCompositionLayerInstruction類表示)可用于將轉換,變換斜坡,不透明度和不透明度斜坡應用于組合中的視頻軌道。視頻構圖類還使您能夠使用該animationTool屬性將核心動畫框架的效果引入到您的視頻中。
要將您的作品與音頻混合和視頻合成相結合,可以使用AVAssetExportSession對象,如圖3-4所示。您可以使用您的作品初始化導出會話,然后分別將音頻混合和視頻組合分配給該屬性audioMix和videoComposition屬性。
Creating a Composition
要創建自己的構圖,您可以使用AVMutableComposition該類。要將媒體數據添加到組合中,您必須添加一個或多個由AVMutableCompositionTrack類表示的組合曲目。最簡單的情況是創建一個具有一個視頻軌道和一個音軌的可變組合:
AVMutableComposition * mutableComposition = [AVMutableComposition composition];
//創建視頻構圖軌道。
AVMutableCompositionTrack * mutableCompositionVideoTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
//創建音頻合成音軌。
AVMutableCompositionTrack * mutableCompositionAudioTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
Options for Initializing a Composition Track
添加新曲目時,您必須同時提供媒體類型和曲目ID。雖然音頻和視頻是最常用的媒體類型,但您也可以指定其他媒體類型,例如AVMediaTypeSubtitle或AVMediaTypeText。
與某些視聽數據相關聯的每個軌道具有稱為軌跡ID的唯一標識符。如果您指定kCMPersistentTrackID_Invalid為首選軌道ID,則會為您自動生成并與軌道相關聯的唯一標識符。
Adding Audiovisual Data to a Composition(將視聽數據添加到組合)
一旦您有一個或多個曲目的作品,您可以開始將媒體數據添加到相應的曲目。要將媒體數據添加到合成曲目,您需要訪問AVAsset媒體數據所在的對象。您可以使用可變成分跟蹤界面將同一底層媒體類型的多個軌道放在同一軌道上。以下示例說明如何依次添加兩個不同的視頻資源軌道到同一個組合軌道:
// You can retrieve AVAssets from a number of places, like the camera roll for example.
AVAsset *videoAsset = <#AVAsset with at least one video track#>;
AVAsset *anotherVideoAsset = <#another AVAsset with at least one video track#>;
// Get the first video track from each asset.
AVAssetTrack *videoAssetTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
AVAssetTrack *anotherVideoAssetTrack = [[anotherVideoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
// Add them both to the composition.
[mutableCompositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,videoAssetTrack.timeRange.duration) ofTrack:videoAssetTrack atTime:kCMTimeZero error:nil];
[mutableCompositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,anotherVideoAssetTrack.timeRange.duration) ofTrack:anotherVideoAssetTrack atTime:videoAssetTrack.timeRange.duration error:nil];
Retrieving Compatible Composition Tracks(檢索兼容的構圖軌跡)
在可能的情況下,每個媒體類型應該只有一個組合軌。兼容的asset軌道的統一導致最少的資源使用量。當連續呈現媒體數據時,您應該將相同類型的任何媒體數據放在同一作曲軌道上。您可以查詢可變組合來確定是否有與所需asset軌道兼容的組合曲目:
AVMutableCompositionTrack * compatibleCompositionTrack = [mutableComposition mutableTrackCompatibleWithTrack:<#the AVAssetTrack you want to insert#>];
if(compatibleCompositionTrack){
//執行繼續。
}
注意 將多個視頻片段放置在同一作曲軌道上可能會導致在視頻片段(特別是嵌入式設備)之間的轉換時放棄播放幀。為視頻片段選擇合成曲目的數量完全取決于您的應用程序的設計及其預期平臺。 |
---|
Generating a Volume Ramp(生成音量斜坡)
單個AVMutableAudioMix對象可以單獨對組合中的所有音軌進行自定義音頻處理。您可以使用audioMix類方法創建音頻混合,并使用AVMutableAudioMixInputParameters該類的實例將音頻混合與組合中的特定曲目相關聯。可以使用音頻混合來改變音軌的音量。以下示例顯示如何在特定音軌上設置音量斜坡,以在組合的持續時間內緩慢淡出音頻:
AVMutableAudioMix * mutableAudioMix = [AVMutableAudioMix audioMix];
//創建音頻混合輸入參數對象。
音頻混合輸入參數
//設置音量斜坡在組合的持續時間內慢慢淡出音頻輸出。
[mixParameters setVolumeRampFromStartVolume:1.f toEndVolume:0.f timeRange:CMTimeRangeMake(kCMTimeZero,mutableComposition.duration)];
//將輸入參數附加到音頻混合。
mutableAudioMix.inputParameters = @ [mixParameters];
Performing Custom Video Processing(執行自定義視頻處理)
與音頻組合一樣,您只需要一個AVMutableVideoComposition
對象來對您的作品的視頻軌道執行所有自定義視頻處理。使用視頻構圖,您可以直接設置合成視頻軌道的相應渲染大小,縮放比例和幀速率。有關為這些屬性設置適當值的詳細示例,請參閱設置渲染大小和幀持續時間。
Changing the Composition’s Background Color(改變作品的背景顏色)
所有視頻構圖也必須AVVideoCompositionInstruction包含至少包含一個視頻構圖指令的對象數組。您可以使用AVMutableVideoCompositionInstruction該類創建自己的視頻構圖說明。使用視頻合成指令,您可以修改組合的背景顏色,指定是否需要后處理或應用圖層指令。
以下示例說明如何創建將整個構圖的背景顏色更改為紅色的視頻合成指令
AVMutableVideoCompositionInstruction * mutableVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
mutableVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,mutableComposition.duration);
mutableVideoCompositionInstruction.backgroundColor = [[UIColor redColor] CGColor];
Applying Opacity Ramps(應用不透明度斜坡)
視頻合成指令也可用于應用視頻合成圖層指令。一個AVMutableVideoCompositionLayerInstruction對象可以應用變換,組合物內的變換坡道,不透明度和不透明度坡道到一定視頻道。在視頻合成指令的layerInstructions陣列中的層指令的順序決定了在構圖指令的持續時間內,來自源軌道的視頻幀應如何分層和組合。以下代碼片段顯示了如何設置不透明度斜坡以在轉換到第二個視頻之前緩慢淡出組合中的第一個視頻:
AVAsset * firstVideoAssetTrack = <#AVAssetTrack表示組合#中播放的第一個視頻段;
AVAsset * secondVideoAssetTrack = <#AVAssetTrack表示組合#中播放的第二個視頻段;
//創建第一個視頻構圖指令。
AVMutableVideoCompositionInstruction * firstVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
//設置其時間范圍以跨越第一個視頻軌道的持續時間。
firstVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,firstVideoAssetTrack.timeRange.duration);
//創建圖層指令并將其與合成視頻軌道相關聯。
AVMutableVideoCompositionLayerInstruction * firstVideoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:mutableCompositionVideoTrack];
//創建不透明度斜坡以在整個持續時間內淡出第一個視頻軌道。
[firstVideoLayerInstruction setOpacityRampFromStartOpacity:1.f toEndOpacity:0.f timeRange:CMTimeRangeMake(kCMTimeZero,firstVideoAssetTrack.timeRange.duration)];
//創建第二個視頻合成指令,使第二個視頻軌道不透明。
AVMutableVideoCompositionInstruction * secondVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
//設置其時間范圍以跨越第二個視頻軌道的持續時間。
secondVideoCompositionInstruction.timeRange = CMTimeRangeMake(firstVideoAssetTrack.timeRange.duration,CMTimeAdd(firstVideoAssetTrack.timeRange.duration,secondVideoAssetTrack.timeRange.duration));
//創建第二層指令并將其與合成視頻軌道相關聯。
AVMutableVideoCompositionLayerInstruction * secondVideoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:mutableCompositionVideoTrack];
//將第一層指令附加到第一個視頻構圖指令。
firstVideoCompositionInstruction.layerInstructions = @ [firstVideoLayerInstruction];
//將第二層指令附加到第二個視頻構圖指令。
secondVideoCompositionInstruction.layerInstructions = @ [secondVideoLayerInstruction];
//將兩個視頻構圖指令附加到視頻構圖。
AVMutableVideoComposition * mutableVideoComposition = [AVMutableVideoComposition videoComposition];
mutableVideoComposition.instructions = @ [firstVideoCompositionInstruction,secondVideoCompositionInstruction];
Incorporating Core Animation Effects(結合核心動畫效果)
視頻構圖可以通過animationTool屬性添加Core Animation的功能。通過這個動畫工具,您可以完成諸如水印視頻和添加標題或動畫疊加等任務。核心動畫可以以兩種不同的方式與視頻作品一起使用:您可以添加一個核心動畫圖層作為自己的個人作品軌跡,或者您可以將核心動畫效果(使用核心動畫圖層)直接渲染到構圖中的視頻幀中。以下代碼通過向視頻的中心添加水印來顯示后一個選項:
CALayer * watermarkLayer = <#CALayer表示所需的水印圖像#>;
CALayer * parentLayer = [CALayer layer];
CALayer * videoLayer = [CALayer layer];
parentLayer.frame = CGRectMake(0,0,mutableVideoComposition.renderSize.width,mutableVideoComposition.renderSize.height);
videoLayer.frame = CGRectMake(0,0,mutableVideoComposition.renderSize.width,mutableVideoComposition.renderSize.height);
[parentLayer addSublayer:videoLayer];
watermarkLayer.position = CGPointMake(mutableVideoComposition.renderSize.width / 2,mutableVideoComposition.renderSize.height / 4);
[parentLayer addSublayer:watermarkLayer];
mutableVideoComposition.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
Putting It All Together: Combining Multiple Assets and Saving the Result to the Camera Roll(放在一起:組合多個asset并將結果保存到相機卷)
這個簡短的代碼示例說明了如何組合兩個視頻asset軌道和音頻asset軌道來創建單個視頻文件。它顯示如何:
- 創建一個AVMutableComposition對象并添加多個AVMutableCompositionTrack對象
- 將AVAssetTrack對象的時間范圍添加到兼容的合成軌道
- 檢查preferredTransform視頻asset軌道的屬性以確定視頻的方向
- 使用AVMutableVideoCompositionLayerInstruction對象將變換應用于組合中的視頻軌道
- 設置適當的值用于renderSize和frameDuration一個視頻組合物的性質
- 導出到視頻文件時,使用組合結合視頻合成
- 將視頻文件保存到相機膠卷
注意: 為了專注于最相關的代碼,本示例省略了一個完整的應用程序的幾個方面,如內存管理和錯誤處理。要使用AVFoundation,您將有足夠的經驗與可可推斷丟失的部分。 |
---|
Creating the Composition(創建構圖)
要使用單獨的資源組合曲目,您可以使用AVMutableComposition對象。創建構圖并添加一個音頻和一個視頻軌道。
AVMutableComposition * mutableComposition = [AVMutableComposition composition];
AVMutableCompositionTrack * videoCompositionTrack = [mutableComposition addMutableTrackWithMediaType:
AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack * audioCompositionTrack = [mutableComposition addMutableTrackWithMediaType:
AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
Adding the Assets(添加Asset)
一個空的作文你沒有好處。將兩個視頻asset軌道和音頻asset軌道添加到組合。
AVAssetTrack * firstVideoAssetTrack = [[firstVideoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
AVAssetTrack * secondVideoAssetTrack = [[secondVideoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
[videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,firstVideoAssetTrack.timeRange.duration)ofTrack:firstVideoAssetTrack atTime:kCMTimeZero error:nil];
[videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,secondVideoAssetTrack.timeRange.duration)ofTrack:secondVideoAssetTrack atTime:firstVideoAssetTrack.timeRange.duration error:nil];
[audioCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,CMTimeAdd(firstVideoAssetTrack.timeRange.duration,secondVideoAssetTrack.timeRange.duration))ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:kCMTimeZero error:nil];
注意: 這假設您有兩個asset每個至少包含一個視頻軌道,第三個asset至少包含一個音軌。可以從相機膠卷中檢索視頻,并且可以從音樂庫或視頻本身檢索音軌。 |
---|
Checking the Video Orientations(檢查視頻方向)
將視頻和音軌添加到組合中后,您需要確保兩個視頻軌道的方向正確。默認情況下,所有視頻軌道都假定為橫向模式。如果您的視頻軌道是以縱向模式拍攝的,導出時視頻將不會正確定向。同樣,如果您嘗試將以縱向模式拍攝的視頻與風景模式下的視頻拍攝相結合,導出會話將無法完成。
BOOL isFirstVideoPortrait = NO;
CGAffineTransform firstTransform = firstVideoAssetTrack.preferredTransform;
// Check the first video track's preferred transform to determine if it was recorded in portrait mode.
if (firstTransform.a == 0 && firstTransform.d == 0 && (firstTransform.b == 1.0 || firstTransform.b == -1.0) && (firstTransform.c == 1.0 || firstTransform.c == -1.0)) {
isFirstVideoPortrait = YES;
}
BOOL isSecondVideoPortrait = NO;
CGAffineTransform secondTransform = secondVideoAssetTrack.preferredTransform;
// Check the second video track's preferred transform to determine if it was recorded in portrait mode.
if (secondTransform.a == 0 && secondTransform.d == 0 && (secondTransform.b == 1.0 || secondTransform.b == -1.0) && (secondTransform.c == 1.0 || secondTransform.c == -1.0)) {
isSecondVideoPortrait = YES;
}
if ((isFirstVideoAssetPortrait && !isSecondVideoAssetPortrait) || (!isFirstVideoAssetPortrait && isSecondVideoAssetPortrait)) {
UIAlertView *incompatibleVideoOrientationAlert = [[UIAlertView alloc] initWithTitle:@"Error!" message:@"Cannot combine a video shot in portrait mode with a video shot in landscape mode." delegate:self cancelButtonTitle:@"Dismiss" otherButtonTitles:nil];
[incompatibleVideoOrientationAlert show];
return;
}
Applying the Video Composition Layer Instructions(應用視頻組合層說明)
一旦知道視頻片段具有兼容的方向,您可以對每個視頻片段應用必要的圖層指令,并將這些圖層指令添加到視頻構圖。
AVMutableVideoCompositionInstruction * firstVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
//設置第一個指令的時間范圍,以跨越第一個視頻軌道的持續時間。
firstVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,firstVideoAssetTrack.timeRange.duration);
AVMutableVideoCompositionInstruction * secondVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
//設置第二條指令的時間范圍,以跨越第二個視頻軌道的持續時間。
secondVideoCompositionInstruction.timeRange = CMTimeRangeMake(firstVideoAssetTrack.timeRange.duration,CMTimeAdd(firstVideoAssetTrack.timeRange.duration,secondVideoAssetTrack.timeRange.duration));
AVMutableVideoCompositionLayerInstruction * firstVideoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoCompositionTrack];
//將第一層指令的變換設置為第一個視頻軌道的首選變換。
[firstVideoLayerInstruction setTransform:firstTransform atTime:kCMTimeZero];
AVMutableVideoCompositionLayerInstruction * secondVideoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoCompositionTrack];
//將第二層指令的變換設置為第二視頻軌道的首選變換。
[secondVideoLayerInstruction setTransform:secondTransform atTime:
firstVideoAssetTrack.timeRange.duration];
firstVideoCompositionInstruction.layerInstructions = @ [firstVideoLayerInstruction];
secondVideoCompositionInstruction.layerInstructions = @ [secondVideoLayerInstruction];
AVMutableVideoComposition * mutableVideoComposition = [AVMutableVideoComposition videoComposition];
mutableVideoComposition.instructions = @ [firstVideoCompositionInstruction,secondVideoCompositionInstruction];
所有AVAssetTrack對象都具有preferredTransform包含該asset軌道的方向信息的屬性。每當asset軌跡顯示在屏幕上時,就會應用此轉換。
在之前的代碼中,層指令的變換設置為asset軌道的變換,以便在調整渲染大小后,新構圖中的視頻將正確顯示。
Setting the Render Size and Frame Duration(設置渲染大小和幀持續時間)
要完成視頻導向修復,您必須相應地調整renderSize屬性。您還應該為該frameDuration屬性選擇合適的值,例如1/30秒(或30幀/秒)。默認情況下,renderScale屬性設置為1.0,適用于此組合。
CGSize naturalSizeFirst,naturalSizeSecond;
//如果第一個視頻asset以縱向模式拍攝,那么第二個視頻資源如果我們在這里拍攝,
if(isFirstVideoAssetPortrait){
//反轉視頻軌道的寬度和高度,以確保它們正確顯示。
naturalSizeFirst = CGSizeMake(firstVideoAssetTrack.naturalSize.height,firstVideoAssetTrack.naturalSize.width);
naturalSizeSecond = CGSizeMake(secondVideoAssetTrack.naturalSize.height,secondVideoAssetTrack.naturalSize.width);
}
else {
//如果視頻沒有以縱向模式拍攝,我們只能使用自然尺寸。
naturalSizeFirst = firstVideoAssetTrack.naturalSize;
naturalSizeSecond = secondVideoAssetTrack.naturalSize;
}
float renderWidth,renderHeight;
//將renderWidth和renderHeight設置為兩個視頻寬度和高度的最大值。
if(naturalSizeFirst.width> naturalSizeSecond.width){
renderWidth = naturalSizeFirst.width;
}
else {
renderWidth = naturalSizeSecond.width;
}
if(naturalSizeFirst.height> naturalSizeSecond.height){
renderHeight = naturalSizeFirst.height;
}
else {
renderHeight = naturalSizeSecond.height;
}
mutableVideoComposition.renderSize = CGSizeMake(renderWidth,renderHeight);
//將幀持續時間設置為適當的值(即視頻為30幀/秒)。
mutableVideoComposition.frameDuration = CMTimeMake(1,30);
Exporting the Composition and Saving it to the Camera Roll(導出構圖并將其保存到相機膠卷)
此過程的最后一步是將整個組合導出到單個視頻文件中,并將該視頻保存到相機卷。您可以使用AVAssetExportSession對象創建新的視頻文件,并將其傳送給輸出文件所需的URL。然后,您可以使用ALAssetsLibrary該類將生成的視頻文件保存到相機膠卷。
// Create a static date formatter so we only have to initialize it once.
static NSDateFormatter *kDateFormatter;
if (!kDateFormatter) {
kDateFormatter = [[NSDateFormatter alloc] init];
kDateFormatter.dateStyle = NSDateFormatterMediumStyle;
kDateFormatter.timeStyle = NSDateFormatterShortStyle;
}
// Create the export session with the composition and set the preset to the highest quality.
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mutableComposition presetName:AVAssetExportPresetHighestQuality];
// Set the desired output URL for the file created by the export process.
exporter.outputURL = [[[[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:@YES error:nil] URLByAppendingPathComponent:[kDateFormatter stringFromDate:[NSDate date]]] URLByAppendingPathExtension:CFBridgingRelease(UTTypeCopyPreferredTagWithClass((CFStringRef)AVFileTypeQuickTimeMovie, kUTTagClassFilenameExtension))];
// Set the output file type to be a QuickTime movie.
exporter.outputFileType = AVFileTypeQuickTimeMovie;
exporter.shouldOptimizeForNetworkUse = YES;
exporter.videoComposition = mutableVideoComposition;
// Asynchronously export the composition to a video file and save this file to the camera roll once export completes.
[exporter exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
if (exporter.status == AVAssetExportSessionStatusCompleted) {
ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init];
if ([assetsLibrary videoAtPathIsCompatibleWithSavedPhotosAlbum:exporter.outputURL]) {
[assetsLibrary writeVideoAtPathToSavedPhotosAlbum:exporter.outputURL completionBlock:NULL];
}
}
});
}];