AVFoundation視頻編輯

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ù)都是,插入自然也是失敗的黑竞。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捕发,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子很魂,更是在濱河造成了極大的恐慌扎酷,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遏匆,死亡現(xiàn)場離奇詭異法挨,居然都是意外死亡,警方通過查閱死者的電腦和手機拉岁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門坷剧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惰爬,“玉大人喊暖,你說我怎么就攤上這事∷呵疲” “怎么了陵叽?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長丛版。 經(jīng)常有香客問我巩掺,道長,這世上最難降的妖魔是什么页畦? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任胖替,我火速辦了婚禮,結果婚禮上豫缨,老公的妹妹穿的比我還像新娘独令。我一直安慰自己,他們只是感情好好芭,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布燃箭。 她就那樣靜靜地躺著,像睡著了一般舍败。 火紅的嫁衣襯著肌膚如雪招狸。 梳的紋絲不亂的頭發(fā)上敬拓,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音裙戏,去河邊找鬼乘凸。 笑死,一個胖子當著我的面吹牛累榜,可吹牛的內(nèi)容都是我干的翰意。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼信柿,長吁一口氣:“原來是場噩夢啊……” “哼冀偶!你這毒婦竟也來了?” 一聲冷哼從身側響起渔嚷,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤进鸠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后形病,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體客年,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年漠吻,在試婚紗的時候發(fā)現(xiàn)自己被綠了量瓜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡途乃,死狀恐怖绍傲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情耍共,我是刑警寧澤烫饼,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站试读,受9級特大地震影響杠纵,放射性物質發(fā)生泄漏。R本人自食惡果不足惜钩骇,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一比藻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧倘屹,春花似錦银亲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至哄辣,卻和暖如春请梢,著一層夾襖步出監(jiān)牢的瞬間赠尾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工毅弧, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留气嫁,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓够坐,卻偏偏與公主長得像寸宵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子元咙,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫梯影、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,024評論 4 62
  • 1 AVFoundation簡介 1.1 Apple的媒體處理體系 Apple的媒體處理體系分為高庶香、中甲棍、低三層,i...
    RichardJieChen閱讀 2,259評論 1 2
  • 原文:AVFoundation Programming Guide 寫在前面 簡單翻譯一下AVFoundation...
    朦朧1919閱讀 1,597評論 0 1
  • 在有機會看到玫瑰花后赶掖,便看不上月季花了感猛,首先覺得名字土,沒有玫瑰洋氣奢赂,沒有玫瑰的嬌艷陪白,代表愛情的美好。在看過牡丹后...
    琴韻2竹語閱讀 375評論 0 1
  • 老胡生病了!腰纏萬貫的富家翁生病了膳灶! 老胡的病古怪得很咱士,身體其他部分·沒啥毛病,但就是覺得心臟不舒...
    風微云搖閱讀 239評論 0 1