https://github.com/WarBand/AVFoundationOCDemo
編輯
AVFoundation支持視頻的編輯和導出墩蔓。
使用AVAssetExportSession進行簡單的編輯
AVAssetExportSession可以將特定AVAsset中的內(nèi)容導出到磁盤中恨闪”棵伲看一段最簡單的導出代碼充活。
//載入視頻源
NSURL *url = [[NSBundle mainBundle] URLForResource:@"ElephantSeals" withExtension:@"mov"];
AVAsset *videoAsset = [AVAsset assetWithURL:url];
//導出
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:videoAsset presetName:AVAssetExportPresetHighestQuality];
NSString *outputPath = [NSString stringWithFormat:@"%@/tmp/test.%@", NSHomeDirectory(),CFBridgingRelease(UTTypeCopyPreferredTagWithClass((CFStringRef)AVFileTypeQuickTimeMovie, kUTTagClassFilenameExtension))];
NSURL *outputUrl = [NSURL fileURLWithPath:outputPath];
if ([[NSFileManager defaultManager] fileExistsAtPath:outputPath]) {
[[NSFileManager defaultManager] removeItemAtURL:outputUrl error:nil];
}
NSLog(@"輸出至: %@", outputUrl);
//設置輸出路徑
exporter.outputURL = outputUrl;
//設置輸出文件類型
exporter.outputFileType = AVFileTypeQuickTimeMovie;
[exporter exportAsynchronouslyWithCompletionHandler:^{
NSLog(@"status: %ld; error: %@;", (long)exporter.status, exporter.error);
}];
timeRange
以上的代碼完成了視頻導出導出磁盤,AVAssetExportSession有一個屬性timeRange的触菜,在以上的代碼中如果加入
exporter.timeRange = exporter.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMake(2, 1));
導出的視頻就是原視頻的前兩秒逻澳。
這樣的裁剪只是將視頻的一部分導出,這似乎不能算是一個完全的視頻編輯得滤。這樣的功能似乎不能使我們滿意
使用AVComposition
視頻編輯的過程是將已有視頻文件中的視頻流數(shù)據(jù)和音頻流數(shù)據(jù)拼合到一個新的容器中,然后導出盒犹。AVFoundation中懂更,AVAsset就是這樣一個扮演容器的角色,但是AVAsset的設計并不是為了修改視頻和音頻數(shù)據(jù)急膀。這里要使用到一個子類AVComposition沮协。
AVComposition是繼承自AVAsset的一個子類,根據(jù)蘋果命名API的習慣推測卓嫂,應該還有一個可編輯的子類慷暂。那就是AVMutableComposition。
開始前的準備工作
為了使用AVMutableComposition晨雳。首先創(chuàng)建一個工具方法行瑞。
- (void)exportAvasset:(AVAsset *)asset
{
//導出
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetHighestQuality];
NSString *outputPath = [NSString stringWithFormat:@"%@/tmp/test.%@", NSHomeDirectory(),CFBridgingRelease(UTTypeCopyPreferredTagWithClass((CFStringRef)AVFileTypeQuickTimeMovie, kUTTagClassFilenameExtension))];
NSURL *outputUrl = [NSURL fileURLWithPath:outputPath];
if ([[NSFileManager defaultManager] fileExistsAtPath:outputPath]) {
[[NSFileManager defaultManager] removeItemAtURL:outputUrl error:nil];
}
NSLog(@"輸出至: %@", outputUrl);
exporter.outputURL = outputUrl;
exporter.outputFileType = AVFileTypeQuickTimeMovie;
[exporter exportAsynchronouslyWithCompletionHandler:^{
NSLog(@"status: %ld; error: %@;", (long)exporter.status, exporter.error);
}];
}
接下來,還需要一個包含視頻流數(shù)據(jù)和音頻數(shù)據(jù)的集合對象餐禁,并且將其中的視頻流數(shù)據(jù)和音頻數(shù)據(jù)提去出來血久。
/** 載入視頻源 */
NSURL *url = [[NSBundle mainBundle] URLForResource:@"ElephantSeals" withExtension:@"mov"];
AVAsset *videoAsset = [AVAsset assetWithURL:url];
AVAssetTrack *videoTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] firstObject];
AVAssetTrack *audioTrack = [[videoAsset tracksWithMediaType:AVMediaTypeAudio] firstObject];
這個方法將一個AVAsset對象導出到沙盒tmp文件夾下,并命名為test.mov帮非。
這個是我工程中的一個視頻文件氧吐,時長為7秒讹蘑。
創(chuàng)建導出的視頻源
/** 創(chuàng)建待輸出的視頻源 */
AVMutableComposition *mutableComposition = [AVMutableComposition composition];
AVComposition繼承自AVAsset,本身就是視頻流和音頻流的一個集合筑舅,AVMutableComposition就是一個視頻流和音頻流都可以編輯的集合座慰。使用addMutableTrackWithMediaType方法可以向AVMutableComposition中添加一個視頻流
如下
/** 向待輸出的視頻源添加視頻流*/
AVMutableCompositionTrack *videoCompositionTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
此處添加的是一條視頻流track。當然這條track里面什么也沒有翠拣,只是一個可以編輯的track版仔。接下來就要提取已有視頻的track插入到其中。
/** 將提取出的視頻流插入到待輸出的視頻流上 */
[videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, CMTimeMake(2, 1)) ofTrack:videoAssetTrack atTime:CMTimeMake(2, 1) error:nil];
通過導出文件的方法導出文件
[self exportAvasset:mutableComposition];
這一句代碼將提取的視頻數(shù)據(jù)插入到了從兩秒開始误墓,也就將要導出的視頻變成了4秒蛮粮,前兩秒是空白啥也沒有。但是你發(fā)現(xiàn)這個文件沒有聲音优烧。因為我們還沒有插入音頻數(shù)據(jù)蝉揍。
插入音頻數(shù)據(jù)
插入音頻的過程和插入視頻類似
AVMutableCompositionTrack *audioCompositionTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
[audioCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, CMTimeMake(2, 1)) ofTrack:audioTrack atTime:kCMTimeZero error:nil];
這一段將準備好的音頻流的前兩秒插到了開頭链峭。將導出方法移動到最后畦娄,導出文件,可以發(fā)現(xiàn)導出的視頻文件里面有了聲音弊仪,但是只前兩秒熙卡。
有了上面兩種編輯功能,視頻的拼接已經(jīng)可以實現(xiàn)了励饵。但是AVFoundation能做的還不止這些驳癌。
AVMutableVideoComposition和AVMutableAudioMix
回到導出方法中的AVAssetExportSession,AVAssetExportSession還有兩個屬性
@property (nonatomic, copy, nullable) AVAudioMix *audioMix;
@property (nonatomic, copy, nullable) AVVideoComposition *videoComposition;
使用AVMutableVideoComposition控制視頻
將導出方法改成下面的樣子役听。
- (void)exportAvasset:(AVAsset *)asset videoComposition:(AVVideoComposition *)videoComposition
并在方法內(nèi)部添加
exporter.videoComposition = videoComposition;
接下來創(chuàng)建AVMutableVideoComposition并導出
/** 創(chuàng)建視頻流輸出控制 */
AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition];
videoComposition.renderSize = videoAssetTrack.naturalSize;
videoComposition.frameDuration = CMTimeMake(1, 30);
運行發(fā)現(xiàn)颓鲜,這樣是不可以的,還需要AVMutableVideoCompositionInstruction
/** 創(chuàng)建視頻流命令控制器 */
AVMutableVideoCompositionInstruction *mutableVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
/** 設置視頻流命令控制器有效時間區(qū)域 */
mutableVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, mutableComposition.duration);
mutableVideoCompositionInstruction.backgroundColor = [UIColor redColor].CGColor;
videoComposition.instructions = @[mutableVideoCompositionInstruction];
導出文件后會發(fā)現(xiàn)典予,導出的視頻在本來空白的地方多了一個紅色的背景甜滨。然而也發(fā)生一個嚴重的問題,視頻圖像不見了A鲂洹R履Α!而且無論你怎么設置mutableVideoCompositionInstruction.timeRange都無濟于事N娴小0纭!導出的視頻只有一個有聲音的紅色圖像U纪瘛E葑臁!
查看一遍頭文件逆济,你會發(fā)現(xiàn)這一句
/* Provides an array of instances of AVVideoCompositionLayerInstruction that specify how video frames from source tracks should be layered and composed.
Tracks are layered in the composition according to the top-to-bottom order of the layerInstructions array; the track with trackID of the first instruction
in the array will be layered on top, with the track with the trackID of the second instruction immediately underneath, etc.
If this key is nil, the output will be a fill of the background color. */
@property (nonatomic, copy) NSArray<AVVideoCompositionLayerInstruction *> *layerInstructions;
似乎一下在找到了希望磕诊,那就趕緊設置AVVideoCompositionLayerInstruction試試填物,當然我們使用的還是它的mutable的子類
/** 設置視頻流命令控制器圖層控制命令(操作待輸出視頻源的視頻流) */
AVMutableVideoCompositionLayerInstruction *passThroughLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:[mutableComposition tracksWithMediaType:AVMediaTypeVideo][0]];
/** 設置命令 */
videoComposition.instructions = @[mutableVideoCompositionInstruction];
導出文件你會發(fā)現(xiàn),視頻出來了霎终,而且長度也是4秒滞磺,前面的空白也是紅色。
不過這樣看起來莱褒,貌似也沒什么击困,除了比開始的代碼多了一個紅色的空白背景,也沒什么特別的嘛广凸,而且黑色的背景可能還更好看呢阅茶。那么繼續(xù)往下設置。
[passThroughLayer setTransform:CGAffineTransformMakeTranslation(0, 250) atTime:CMTimeMake(2, 1)];
[passThroughLayer setTransform:CGAffineTransformMakeTranslation(0, 500) atTime:CMTimeMake(3, 1)];
導出以后谅海,畫面在設置的時間點脸哀,發(fā)生了偏移,繼續(xù)設置
[passThroughLayer setOpacityRampFromStartOpacity:0.5 toEndOpacity:1 timeRange:CMTimeRangeMake(CMTimeMake(2, 1), CMTimeMake(3, 1))];
視頻發(fā)生了一個透明變化的動畫扭吁。
AVMutableVideoComposition除了instructions這個屬性以外還有這樣一個屬性
/* indicates a special video composition tool for use of Core Animation; may be nil */
@property (nonatomic, retain, nullable) AVVideoCompositionCoreAnimationTool *animationTool;
官方文檔中給出的是一段加水印的代碼撞蜂,我在這里做了點兒修改,現(xiàn)在加上去感受下
/** 水印 */
CGSize videoSize = CGSizeMake(videoTrack.naturalSize.width, videoTrack.naturalSize.height);
CATextLayer *textLayer = [CATextLayer layer];
textLayer.backgroundColor = [UIColor redColor].CGColor;
textLayer.string = @"123456";
textLayer.bounds = CGRectMake(0, 0, videoSize.width * 0.5, videoSize.height * 0.5);
CALayer *baseLayer = [CALayer layer];
[baseLayer addSublayer:textLayer];
baseLayer.position = CGPointMake(videoComposition.renderSize.width/2, videoComposition.renderSize.height/2);
CALayer *videoLayer = [CALayer layer];
videoLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height);
CALayer *parentLayer = [CALayer layer];
parentLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height);
[parentLayer addSublayer:videoLayer];
[parentLayer addSublayer:baseLayer];
AVVideoCompositionCoreAnimationTool *animalTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
videoComposition.animationTool = animalTool;
有點長侥袜,導出后蝌诡,畫面中,出現(xiàn)了一個白色的123456枫吧,我自己改的代碼浦旱,畫面當然也是慘不忍睹,這里不做探究九杂,我在使用過程中颁湖,發(fā)現(xiàn)設置這個屬性,會出現(xiàn)導出的視頻畫面被斜拉的現(xiàn)象例隆,最后通過調整layer的屬性才把畫面正過來甥捺,這里感覺有坑啊。
瞅了幾遍這個類的名字裳擎,你會發(fā)現(xiàn)名字里面有CoreAnimation涎永,你竟然只能打水印。這也太對不起你的名字了吧鹿响。經(jīng)過我的實驗羡微,下面在加水印的后面繼續(xù)添加
CABasicAnimation *baseAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
baseAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
baseAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(200, 200)];
baseAnimation.repeatCount = 5;
baseAnimation.beginTime = AVCoreAnimationBeginTimeAtZero;
baseAnimation.duration = 1;
baseAnimation.removedOnCompletion = NO;
[textLayer addAnimation:baseAnimation forKey:@"hehe"];
那個水印竟然有了動畫。
到這里惶我,視頻的編輯基本就告一段落了妈倔,下面是音頻的編輯。
AVMutableAudioMix
我們將導出方法繼續(xù)修改一下
- (void)exportAvasset:(AVAsset *)asset videoComposition:(AVVideoComposition *)videoComposition audioMix:(AVAudioMix *)audioMix
并在方法內(nèi)部添加
exporter.audioMix = audioMix;
現(xiàn)在開始音頻的編輯, AVMutableAudioMix 重要的屬性只有一個inputParameters绸贡,是一個存放AVMutableAudioMixInputParameters的數(shù)組
//音頻
//音頻
AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
AVMutableAudioMixInputParameters *inputParameter = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:audioCompositionTrack];
audioMix.inputParameters = @[inputParameter];
[inputParameter setVolumeRampFromStartVolume:0 toEndVolume:1 timeRange:CMTimeRangeMake(kCMTimeZero, CMTimeMake(2, 1))];
導出使用
[self exportAvasset:mutableComposition videoComposition:videoComposition audioMix:audioMix];
聲音會有一個從高到低的變化過程盯蝴。
*********************************遇到的問題************************************
使用AVMutableCompositionTrack的insertTimeRange方法毅哗,編輯時,會遇到如下錯誤捧挺。
Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSLocalizedDescription=The operation could not be completed, NSUnderlyingError=0x166833a0 {Error Domain=NSOSStatusErrorDomain Code=-12780 "(null)"}, NSLocalizedFailureReason=An unknown error occurred (-12780)}
基本這個錯誤都是由于數(shù)據(jù)源都是造成的虑绵,即使是在AVMutableCompositionTrack和AVAssetTrack對象都存在的情況下,也要注意AVAssetTrack所在的AVAsset是否存在闽烙。如果AVAsset被釋放了翅睛,源數(shù)據(jù)都是,插入自然也是失敗的黑竞。