AV Foundation系列(五)媒體組合

生活中我們經(jīng)常可能碰到這么一種需求摘仅,你有兩段視頻靶庙,你想將兩段視頻組合成一個(gè)視頻,你還想為組合的這段新視頻添加背景音樂(lè)实檀,這就要用到媒體組合技術(shù)惶洲,AVMutableComposition是這個(gè)技術(shù)的一個(gè)核心的類(lèi)按声,他繼承于AVComposition類(lèi),AVComposition類(lèi)又繼承于AVAsset資源類(lèi)恬吕。

現(xiàn)在有四段視頻签则,比如視頻1,視頻2铐料,視頻3渐裂,視頻4,實(shí)現(xiàn)功能钠惩,將視頻1柒凉,視頻2,視頻3組合一段視頻篓跛,視頻3要保證視頻3的視頻數(shù)據(jù)和音頻數(shù)據(jù)保持一致膝捞,提取視頻4的音頻數(shù)據(jù)作為新視頻的前段的背景音樂(lè)。

@property(nonatomic,strong)AVMutableComposition *mutableComposition;
@property(nonatomic,strong)AVAsset *nebual1Asset;
@property(nonatomic,strong)AVAsset *nebual3Asset;
@property(nonatomic,strong)AVAsset *backhole2Asset;
@property(nonatomic,strong)AVAsset *vidAsset;

@property(nonatomic,strong)AVMutableCompositionTrack *mutableVideoTrack;
@property(nonatomic,strong)AVMutableCompositionTrack *mutableAudioTrack;

@property(nonatomic,strong)AVAsset *compositionAsset;
@property(nonatomic,strong)AVPlayerItem *playerItem;
@property(nonatomic,strong)AVPlayer *player;
@property(nonatomic,strong)AVPlayerLayer *playerLayer;
@property(nonatomic,strong)NSString *storePath;

初始化相關(guān)數(shù)據(jù)

-(void)setAssetInfo
{
    NSURL *nebula1Url = [[NSBundle mainBundle] URLForResource:@"01_nebula" withExtension:@"mp4"];
    NSURL *nebula3Url = [[NSBundle mainBundle] URLForResource:@"03_nebula" withExtension:@"mp4"];
    NSURL *backholeUrl = [[NSBundle mainBundle] URLForResource:@"02_blackhole" withExtension:@"mp4"];
    NSURL *vidUrl = [[NSBundle mainBundle] URLForResource:@"video" withExtension:@"mp4"];
    
    self.storePath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    self.storePath = [self.storePath stringByAppendingPathComponent:@"cm.mp4"];
    
    self.nebual1Asset = [AVURLAsset URLAssetWithURL:nebula1Url options:nil];
    self.nebual3Asset = [AVURLAsset URLAssetWithURL:nebula3Url options:nil];
    self.backhole2Asset = [AVURLAsset URLAssetWithURL:backholeUrl options:nil];
    self.vidAsset = [AVURLAsset URLAssetWithURL:vidUrl options:nil];
    
    self.mutableComposition = [AVMutableComposition composition];
    
     //添加音頻軌道和視頻軌道
    self.mutableVideoTrack = [self.mutableComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    self.mutableAudioTrack = [self.mutableComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];

}

組合各視頻和音頻

- (IBAction)btnClicked:(id)sender
{
    CMTime startTime = kCMTimeZero;
    CMTime duration = self.nebual1Asset.duration;
    
    AVAssetTrack *video1Track = [[self.nebual1Asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
    // 向視頻軌道中添加媒體片段
    [self.mutableVideoTrack insertTimeRange:CMTimeRangeMake(startTime, duration) ofTrack:video1Track atTime:kCMTimeZero error:nil];
    
    AVAssetTrack *video2Track = [[self.nebual3Asset tracksWithMediaType:AVMediaTypeVideo]firstObject];
    // 向視頻軌道中添加媒體片段
    [self.mutableVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, self.nebual3Asset.duration) ofTrack:video2Track atTime:CMTimeAdd(startTime, duration) error:nil];
    
    AVAssetTrack *vidVideoTrack = [[self.vidAsset tracksWithMediaType:AVMediaTypeVideo] firstObject];
    // 向視頻軌道中添加媒體片段
    [self.mutableVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, self.vidAsset.duration) ofTrack:vidVideoTrack atTime:CMTimeAdd(self.nebual3Asset.duration, self.nebual1Asset.duration) error:nil];
    
    AVAssetTrack *audioTrack = [[self.backhole2Asset tracksWithMediaType:AVMediaTypeAudio] firstObject];
    // 向音頻軌道中添加媒體片段
    [self.mutableAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, CMTimeAdd(self.nebual3Asset.duration, self.nebual1Asset.duration)) ofTrack:audioTrack atTime:kCMTimeZero error:nil];
    
    AVAssetTrack *vidAudioTrack = [[self.vidAsset tracksWithMediaType:AVMediaTypeAudio] firstObject];
    // 向音頻軌道中添加媒體片段
    [self.mutableAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, self.vidAsset.duration) ofTrack:vidAudioTrack atTime:CMTimeAdd(self.nebual1Asset.duration, self.nebual3Asset.duration) error:nil];

    //導(dǎo)出
    AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]initWithAsset:self.mutableComposition presetName:AVAssetExportPresetMediumQuality];
    exportSession.outputFileType = AVFileTypeQuickTimeMovie;
    exportSession.outputURL = [NSURL fileURLWithPath:self.storePath];
    [exportSession exportAsynchronouslyWithCompletionHandler:^{
        //導(dǎo)出完成
        if (exportSession.status == AVAssetExportSessionStatusCompleted ) {         //橫屏播放
            [self switchScreen];
            //簡(jiǎn)單播放
            [self playerCompostionVideo];
        }
    }];    
}

橫屏實(shí)現(xiàn)愧沟,強(qiáng)制橫屏主要用當(dāng)前的設(shè)備的setOrientation方法蔬咬,當(dāng)然首先需要判斷是否能夠響應(yīng)該方法,如何可以就調(diào)用沐寺,這里使用了NSInvocation調(diào)用對(duì)象林艘,其實(shí)NSInvocation封裝了調(diào)用函數(shù)所需的所有的信息,比如那個(gè)對(duì)象發(fā)起調(diào)用-target混坞,調(diào)用哪個(gè)方法-selector狐援,調(diào)用的參數(shù)setArgument設(shè)置,如何有返回值也可以通過(guò)getReturn方法得到究孕,這里需要注意的是使用setArgument設(shè)置參數(shù)時(shí)參數(shù)的索引之從2開(kāi)始啥酱,其實(shí)這一點(diǎn)一點(diǎn)都不奇怪,我們要知道IMP函數(shù)指針指向的函數(shù)前兩個(gè)參數(shù)就是隱藏的target蚊俺,selector懈涛,所有從2開(kāi)始。

-(void)switchScreen
{
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)])
            
        {
            SEL selector=NSSelectorFromString(@"setOrientation:");
            
            NSInvocation *invocation =[NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
            
            [invocation setSelector:selector];
            
            [invocation setTarget:[UIDevice currentDevice]];
            
            int val =UIInterfaceOrientationLandscapeRight;
            
            [invocation setArgument:&val atIndex:2];
            [invocation invoke];
        }
    });
 
}

playerCompostionVideo簡(jiǎn)單的播放

-(void)playerCompostionVideo
{
    NSFileManager *fileMange = [NSFileManager defaultManager];
    if ([fileMange fileExistsAtPath:self.storePath]) {
        
        NSURL *videoUrl = [NSURL fileURLWithPath:self.storePath];
        self.compositionAsset = [AVAsset assetWithURL:videoUrl];
        self.playerItem = [[AVPlayerItem alloc] initWithAsset:self.compositionAsset];
        //kvo跟蹤playerItem的status狀態(tài)
        [self.playerItem addObserver:self forKeyPath:@"status" options:0 context:nil];
        self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
        self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
        
        dispatch_async(dispatch_get_main_queue(), ^{
            self.playerLayer.frame = self.view.bounds;
            [self.view.layer addSublayer:self.playerLayer];
        });
    }
}

使用KVO觀察AVPlayerItem的status狀態(tài)泳猬,當(dāng)AVPlayerItemStatusReadyToPlay處于準(zhǔn)備播放狀態(tài)時(shí),開(kāi)始播放

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    AVPlayerItem *playerItem = (AVPlayerItem*)object;
    if (playerItem.status == AVPlayerItemStatusReadyToPlay) {
        //播放
        [self.player play];
    }
}

至此宇植,媒體組合的功能就是實(shí)現(xiàn)了得封,媒體組合技術(shù)還是蠻使用的,很多媒體編輯的軟件都用得到指郁。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末忙上,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子闲坎,更是在濱河造成了極大的恐慌疫粥,老刑警劉巖茬斧,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異梗逮,居然都是意外死亡项秉,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)慷彤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)娄蔼,“玉大人,你說(shuō)我怎么就攤上這事底哗∷晁撸” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵跋选,是天一觀的道長(zhǎng)涕癣。 經(jīng)常有香客問(wèn)我,道長(zhǎng)前标,這世上最難降的妖魔是什么属划? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮候生,結(jié)果婚禮上同眯,老公的妹妹穿的比我還像新娘。我一直安慰自己唯鸭,他們只是感情好须蜗,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著目溉,像睡著了一般明肮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上缭付,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天柿估,我揣著相機(jī)與錄音,去河邊找鬼陷猫。 笑死秫舌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的绣檬。 我是一名探鬼主播足陨,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼娇未!你這毒婦竟也來(lái)了墨缘?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎镊讼,沒(méi)想到半個(gè)月后宽涌,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蝶棋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年卸亮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嚼松。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嫡良,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出献酗,到底是詐尸還是另有隱情寝受,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布罕偎,位于F島的核電站很澄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏颜及。R本人自食惡果不足惜甩苛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望俏站。 院中可真熱鬧讯蒲,春花似錦、人聲如沸肄扎。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)犯祠。三九已至旭等,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間衡载,已是汗流浹背搔耕。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留痰娱,地道東北人弃榨。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像猜揪,于是被迫代替她去往敵國(guó)和親惭墓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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