AVFoundation詳細(xì)解析(一)視頻合并與混音

回顧

在上一篇GPUImage詳細(xì)解析(八)視頻合并混音介紹了如何使用GPUImage進(jìn)行視頻的合并偎窘,以及混音译隘。這次使用AVFoundation框架來實(shí)現(xiàn)這個(gè)功能好啰。

概念

  • AVPlayer 視頻播放類赡磅,本身不顯示視頻涡驮,需創(chuàng)建一個(gè)AVPlayerLayer層暗甥,添加到視圖
  • AVAssetTrack 資源軌道,包括音頻軌道和視頻軌道
  • AVAsset 媒體信息
  • AVURLAsset 根據(jù)URL路徑創(chuàng)建的媒體信息
  • AVPlayerItem媒體資源管理對(duì)象捉捅,管理視頻的基本信息和狀態(tài)
  • AVMutableVideoCompositionInstruction 視頻操作指令
  • AVMutableVideoCompositionLayerInstruction視頻軌道操作指令撤防,需要添加到AVMutableVideoCompositionInstruction
  • AVMutableAudioMixInputParameters音頻操作參數(shù)
  • AVMutableComposition 包含多個(gè)軌道的媒體信息,可以添加棒口、刪除軌道
  • AVMutableVideoComposition視頻操作指令集合

效果

視頻效果如下寄月,音頻效果可運(yùn)行demo

核心思路

分別加載多個(gè)AVURLAsset无牵,用GCD保證異步加載完成后回調(diào)漾肮,調(diào)用Editor類配置軌道信息、視頻操作指令和音頻指令參數(shù)合敦。


具體細(xì)節(jié)

流程圖如下


a初橘、配置軌道信息

  • 1,計(jì)算變化的長(zhǎng)度充岛,確保變換的長(zhǎng)度不大于最小的視頻的長(zhǎng)度的一半保檐;

思考1:demo中是如何計(jì)算小于一半,為何要小于一半崔梗?

  • 2夜只,添加兩個(gè)視頻軌道,兩個(gè)音頻軌道蒜魄;
  • 3扔亥,在視頻索引對(duì)應(yīng)的軌道(%2),插入視頻軌道信息和音頻軌道信息谈为;

思考2:當(dāng)多個(gè)視頻在同一個(gè)音軌插入多個(gè)信息旅挤,如何保證不重疊?

  • 4伞鲫,計(jì)算直接播放和變換的時(shí)間粘茄;
    // 確保最后合并后的視頻,變換長(zhǎng)度不會(huì)超過最小長(zhǎng)度的一半
    CMTime transitionDuration = self.transitionDuration;
    for (i = 0; i < clipsCount; i++ ) {
        NSValue *clipTimeRange = [self.clipTimeRanges objectAtIndex:i];
        if (clipTimeRange) {
            CMTime halfClipDuration = [clipTimeRange CMTimeRangeValue].duration;
            halfClipDuration.timescale *= 2;
            transitionDuration = CMTimeMinimum(transitionDuration, halfClipDuration);
        }
    }
    AVMutableCompositionTrack *compositionVideoTracks[2];
    AVMutableCompositionTrack *compositionAudioTracks[2];
    compositionVideoTracks[0] = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; // 添加視頻軌道0
    compositionVideoTracks[1] = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; // 添加視頻軌道1
    compositionAudioTracks[0] = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; // 添加音頻軌道0
    compositionAudioTracks[1] = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; // 添加音頻軌道1

b秕脓、配置視頻操作指令

  • 1柒瓣,新建視頻操作指令集合;
  • 2吠架,根據(jù)視頻所在對(duì)應(yīng)的軌道(%2)芙贫,新建視頻操作指令passThroughInstruction,長(zhǎng)度為passThroughTimeRanges傍药,同時(shí)定義passThroughLayer直接播放的視頻軌道操作指令磺平,并設(shè)置passThroughLayer為passThroughInstruction的視頻軌道操作指令集合魂仍;
  • 3,根據(jù)視頻所在對(duì)應(yīng)軌道拣挪,新建視頻操作指令transitionInstruction蓄诽,長(zhǎng)度為transitionTimeRanges,同時(shí)根據(jù)軌道定義視頻軌道操作指令fromLayer和toLayer媒吗,并設(shè)置fromLayer和toLayer的變換方式與時(shí)間仑氛;
  • 4,把passThroughInstruction和transitionInstruction添加到視頻指令集合闸英;
        AVMutableVideoCompositionInstruction *passThroughInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; // 新建指令
        passThroughInstruction.timeRange = passThroughTimeRanges[i]; // 直接播放
        AVMutableVideoCompositionLayerInstruction *passThroughLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTracks[alternatingIndex]]; // 視頻軌道操作指令
        
        passThroughInstruction.layerInstructions = [NSArray arrayWithObject:passThroughLayer];
        [instructions addObject:passThroughInstruction]; // 添加到指令集合
        
        if (i+1 < clipsCount) { // 不是最后一個(gè)
            AVMutableVideoCompositionInstruction *transitionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; // 新建指令
            transitionInstruction.timeRange = transitionTimeRanges[i]; // 變換時(shí)間
            AVMutableVideoCompositionLayerInstruction *fromLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTracks[alternatingIndex]]; // 視頻軌道操作指令
            AVMutableVideoCompositionLayerInstruction *toLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTracks[1-alternatingIndex]]; // 新的軌道指令
            // 1 dao 0
            [fromLayer setOpacityRampFromStartOpacity:1.0 toEndOpacity:0.0 timeRange:transitionTimeRanges[i]];
            // 目的軌道锯岖,從0到1
            [toLayer setOpacityRampFromStartOpacity:0.0 toEndOpacity:1.0 timeRange:transitionTimeRanges[i]];
            
            transitionInstruction.layerInstructions = [NSArray arrayWithObjects:toLayer, fromLayer, nil];
            [instructions addObject:transitionInstruction];
        }

c、配置音頻軌道參數(shù)

  • 1甫何,新建音頻軌道參數(shù)集合出吹;
  • 2,根據(jù)視頻所在索引辙喂,新建當(dāng)前音軌的參數(shù)trackMix1捶牢,設(shè)置變換時(shí)間內(nèi)音量從1.0到0.0;
  • 3巍耗,根據(jù)視頻所在索引秋麸,新建另外一條音軌的參數(shù)trackMix2,設(shè)置變換時(shí)間內(nèi)音量從0.0到1.0炬太;設(shè)置直接播放時(shí)間內(nèi)音量一直為1.0灸蟆;
  • 4,把參數(shù)trackMix1和trackMix2添加到音頻軌道參數(shù)集合亲族;
AVMutableAudioMixInputParameters *trackMix1 = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:compositionAudioTracks[alternatingIndex]]; // 音軌0的參數(shù)
[trackMix1 setVolumeRampFromStartVolume:1.0 toEndVolume:0.0 timeRange:transitionTimeRanges[i]]; // 音軌0炒考,變換期間音量從1.0到0.0
[trackMixArray addObject:trackMix1];
AVMutableAudioMixInputParameters *trackMix2 = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:compositionAudioTracks[1 - alternatingIndex]]; // 音軌1的參數(shù)
[trackMix2 setVolumeRampFromStartVolume:0.0 toEndVolume:1.0 timeRange:transitionTimeRanges[i]]; // 變換期間音量從0.0到1.0           
[trackMixArray addObject:trackMix2];

總結(jié)

AVPlayer通過KVO監(jiān)聽rate屬性,status屬性霎迫,用notification來監(jiān)聽播放完成斋枢;
AVPlayer和AVPlayerItem的使用不復(fù)雜,解析集中在SimpleEditor類如何配置軌道信息和音視頻操作指令知给。
代碼地址可以點(diǎn)這里瓤帚。

思考

思考1

通過timescale*2,再用CMTimeMinimum炼鞠;處于中間的視頻要經(jīng)歷兩次變換缘滥,故而變換的長(zhǎng)度不能大于最小視頻長(zhǎng)度的一半轰胁;

思考2

音軌插入的函數(shù)有開始點(diǎn)和持續(xù)時(shí)間谒主,只要保證區(qū)間不重疊,音頻就不會(huì)重疊赃阀;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末霎肯,一起剝皮案震驚了整個(gè)濱河市擎颖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌观游,老刑警劉巖泽篮,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件输玷,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)莽使,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來薄腻,“玉大人编振,你說我怎么就攤上這事」つ耄” “怎么了弱睦?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)渊额。 經(jīng)常有香客問我况木,道長(zhǎng),這世上最難降的妖魔是什么旬迹? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任火惊,我火速辦了婚禮,結(jié)果婚禮上奔垦,老公的妹妹穿的比我還像新娘矗晃。我一直安慰自己,他們只是感情好宴倍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布张症。 她就那樣靜靜地躺著,像睡著了一般鸵贬。 火紅的嫁衣襯著肌膚如雪俗他。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天阔逼,我揣著相機(jī)與錄音兆衅,去河邊找鬼。 笑死嗜浮,一個(gè)胖子當(dāng)著我的面吹牛羡亩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播危融,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼畏铆,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了吉殃?” 一聲冷哼從身側(cè)響起辞居,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤楷怒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后瓦灶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸠删,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年贼陶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了刃泡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡碉怔,死狀恐怖捅僵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情眨层,我是刑警寧澤庙楚,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站趴樱,受9級(jí)特大地震影響馒闷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜叁征,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一纳账、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧捺疼,春花似錦疏虫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至官扣,卻和暖如春翅敌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背惕蹄。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工蚯涮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人卖陵。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓遭顶,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親泪蔫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子棒旗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

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