AVFoundation視頻添加背景音樂(六)

前言

從本文開始逐漸學(xué)習(xí)iOS自帶的多媒體處理框架池充,例如AVFoundation躯保,VideoToolbox,CoreMedia贩猎,CoreVideo實(shí)現(xiàn)多媒體的處理熊户,并且將實(shí)現(xiàn)方式以及效果和ffmpeg的方式做對(duì)比

給一個(gè)視頻添加音樂,將多段音視頻文件合并為一個(gè)文件是很常見的需求融欧,AVFoundation就提供了這樣的接口敏弃。

本文的目的:
為一段視頻添加背景音樂

音視頻合并相關(guān)流程

image.png

上圖介紹了AVFoundation框架中關(guān)于合并音視頻文件的相關(guān)的對(duì)象關(guān)系圖,可以看到使用AVFoundation合并音視頻還是相對(duì)比較簡(jiǎn)單的噪馏。

相關(guān)對(duì)象及函數(shù)介紹

  • 1麦到、AVURLAsset
    容器對(duì)象,代表了要操作的容器欠肾。封裝瓶颠,解封裝,音視頻播放刺桃,以及音視頻合并等等操作的基礎(chǔ)都涉及到這個(gè)對(duì)象粹淋。

  • 2、AVAssetTrack
    音視頻軌道對(duì)象瑟慈,代表了文件中的一路音頻流或者一路視頻流桃移,它作為每一個(gè)要被合并的音頻或者視頻流被添加到組合對(duì)象中最終進(jìn)行合并

  • 3、AVMutableCompositionTrack
    組合軌道對(duì)象葛碧,它作為音視頻合并的基礎(chǔ)借杰,通過它添加要合并的音頻流或者視頻流,分為兩種類型:音頻組合軌道對(duì)象和視頻組合軌道對(duì)象进泼,音頻組合軌道對(duì)象只能添加音頻流蔗衡,視頻組合軌道對(duì)象只能添加視頻流

  • 4、AVMutableComposition
    組合對(duì)象乳绕,通過它構(gòu)建組合軌道對(duì)象

  • 5绞惦、AVAssetExportSession
    執(zhí)行合并操作并導(dǎo)出為文件對(duì)象,該對(duì)象內(nèi)部應(yīng)該是封裝了合并多個(gè)音頻流或者視頻流的操作和封裝操作

實(shí)現(xiàn)代碼

#import <Foundation/Foundation.h>

@interface AVMYComposition : NSObject

/** 實(shí)現(xiàn)音視頻合并功能
 *  1洋措、要合并的視頻時(shí)長(zhǎng)大于任何一個(gè)音頻的時(shí)長(zhǎng)济蝉,有可能小于兩段音頻的時(shí)長(zhǎng)
 *  2、以視頻的時(shí)長(zhǎng)為基準(zhǔn)菠发,如果兩段音頻的時(shí)長(zhǎng)之和大于視頻時(shí)長(zhǎng)王滤,則截取掉第二個(gè)音頻的部分時(shí)間
 */
- (void)startMerge:(NSURL*)audioUrl audio2:(NSURL*)audioUrl2 videoUrl:(NSURL*)videoUrl dst:(NSURL*)dsturl;

@end
import "AVMYComposition.h"
#import <AVFoundation/AVFoundation.h>

@implementation AVMYComposition
{
    dispatch_semaphore_t processSemaphore;
}
- (void)startMerge:(NSURL*)audioUrl1 audio2:(NSURL*)audioUrl2 videoUrl:(NSURL*)videoUrl dst:(NSURL*)dsturl
{
    processSemaphore = dispatch_semaphore_create(0);
    
    /** AVMutableComposition對(duì)象
     * 組合對(duì)象,它是AVAsset的子類雷酪,通過它來實(shí)現(xiàn)音視頻的合并淑仆。它就相當(dāng)于一個(gè)編輯容器,每一個(gè)要被合并的
     * 音頻或者視頻軌道被組裝為AVMutableCompositionTrack然后進(jìn)行合并
     *
     *  AVMutableCompositionTrack組合對(duì)象軌道哥力,他是AVAssetTrack的子類蔗怠。代表了每一個(gè)要被合并的音頻或者視頻軌道
     */
    AVMutableComposition *mixComposition = [AVMutableComposition composition];
    
    // 添加一個(gè)組合對(duì)象軌道,用于添加視頻軌道
    AVMutableCompositionTrack *videoCompostioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    AVURLAsset *videoAsset = [[AVURLAsset alloc] initWithURL:videoUrl options:nil];
    CMTime videoDuration = videoAsset.duration;
    AVAssetTrack *vdeotrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
    CMTimeRange videoTiemRange = CMTimeRangeMake(kCMTimeZero, videoDuration);
    NSError *error = nil;
    [videoCompostioTrack insertTimeRange:videoTiemRange ofTrack:vdeotrack atTime:kCMTimeZero error:&error];
    if (error) {
        NSLog(@"video insert error %@",error);
        return;
    }
    
    // 添加一個(gè)組合對(duì)象軌道吩跋,第二個(gè)參數(shù)為kCMPersistentTrackID_Invalid代表由系統(tǒng)自動(dòng)生成ID
    AVMutableCompositionTrack *audioComTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    AVURLAsset *audioAsset1 = [AVURLAsset assetWithURL:audioUrl1];
    // 將同步解析寞射,會(huì)阻塞當(dāng)前線程
    CMTime duration1 = audioAsset1.duration;
    AVAssetTrack *audioTrack1 = [[audioAsset1 tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
    CMTimeRange firstTimeRange = CMTimeRangeMake(kCMTimeZero,duration1);
    // 往組合對(duì)象軌道中添加軌道對(duì)象
    [audioComTrack insertTimeRange:firstTimeRange ofTrack:audioTrack1 atTime:kCMTimeZero error:&error];
    if (error) {
        NSLog(@"audio track %@",error);
        return;
    }
   
    AVURLAsset *audioAsset2 = [AVURLAsset assetWithURL:audioUrl2];
    // 阻塞當(dāng)前線程
    CMTime duration2 = audioAsset2.duration;
    CMTime newDuration2 = duration2;
    if (CMTimeGetSeconds(duration1)+CMTimeGetSeconds(duration2) > CMTimeGetSeconds(videoDuration) && CMTimeGetSeconds(duration1) < CMTimeGetSeconds(duration2)) {
        newDuration2 = CMTimeSubtract(videoDuration, duration1);
    }
    CMTimeRange secondTimeRange = CMTimeRangeMake(kCMTimeZero, newDuration2);
    NSLog(@" tt %f tt %f",CMTimeGetSeconds(duration1),CMTimeGetSeconds(newDuration2));
    AVAssetTrack *audioTrack2 = [[audioAsset2 tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
    /** 參數(shù)解釋:
     *  timeRange:代表截取track的時(shí)間范圍內(nèi)容然后插入這個(gè)組合對(duì)象的軌道中
     *  startTime:代表將這段內(nèi)容按組對(duì)象軌道時(shí)間軸的指定位置插入
     */
    [audioComTrack insertTimeRange:secondTimeRange ofTrack:audioTrack2 atTime:duration1 error:&error];
    
    // 執(zhí)行合并
    if ([[NSFileManager defaultManager] fileExistsAtPath:dsturl.path]) {
        [[NSFileManager defaultManager] removeItemAtURL:dsturl error:nil];
    }
    
    // 合并導(dǎo)出會(huì)話
    AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetHighestQuality];
    exportSession.outputURL = dsturl;
    exportSession.outputFileType = AVFileTypeQuickTimeMovie;
    
    [exportSession exportAsynchronouslyWithCompletionHandler:^{
        NSLog(@"over");
        dispatch_semaphore_signal(self->processSemaphore);
    }];
    
    dispatch_semaphore_wait(processSemaphore, DISPATCH_TIME_FOREVER);
    
    NSLog(@"結(jié)束了");
}
@end

遇到問題

項(xiàng)目地址

https://github.com/nldzsz/ffmpeg-demo

位于AVFoundation目錄下文件AVMYComposition.h/AVMYComposition.m中

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市锌钮,隨后出現(xiàn)的幾起案子桥温,更是在濱河造成了極大的恐慌,老刑警劉巖梁丘,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侵浸,死亡現(xiàn)場(chǎng)離奇詭異旺韭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)掏觉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門区端,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人澳腹,你說我怎么就攤上這事织盼。” “怎么了酱塔?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵沥邻,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我羊娃,道長(zhǎng)唐全,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任迁沫,我火速辦了婚禮芦瘾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘集畅。我一直安慰自己近弟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布挺智。 她就那樣靜靜地躺著祷愉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪赦颇。 梳的紋絲不亂的頭發(fā)上二鳄,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音媒怯,去河邊找鬼订讼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛扇苞,可吹牛的內(nèi)容都是我干的欺殿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鳖敷,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼脖苏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起定踱,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤棍潘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體亦歉,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡恤浪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鳍徽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片资锰。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡敢课,死狀恐怖阶祭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情直秆,我是刑警寧澤濒募,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站圾结,受9級(jí)特大地震影響瑰剃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜筝野,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一晌姚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧歇竟,春花似錦挥唠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至盅安,卻和暖如春唤锉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背别瞭。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工窿祥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蝙寨。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓晒衩,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親籽慢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浸遗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355