AVFoundation视频添加背景音乐(六)

前言

从本文开始逐渐学习iOS自带的多媒体处理框架,例如AVFoundation,VideoToolbox,CoreMedia,CoreVideo实现多媒体的处理,并且将实现方式以及效果和ffmpeg的方式做对比

给一个视频添加音乐,将多段音视频文件合并为一个文件是很常见的需求,AVFoundation就提供了这样的接口。

本文的目的:
为一段视频添加背景音乐

音视频合并相关流程

image.png

上图介绍了AVFoundation框架中关于合并音视频文件的相关的对象关系图,可以看到使用AVFoundation合并音视频还是相对比较简单的。

相关对象及函数介绍

  • 1、AVURLAsset
    容器对象,代表了要操作的容器。封装,解封装,音视频播放,以及音视频合并等等操作的基础都涉及到这个对象。

  • 2、AVAssetTrack
    音视频轨道对象,代表了文件中的一路音频流或者一路视频流,它作为每一个要被合并的音频或者视频流被添加到组合对象中最终进行合并

  • 3、AVMutableCompositionTrack
    组合轨道对象,它作为音视频合并的基础,通过它添加要合并的音频流或者视频流,分为两种类型:音频组合轨道对象和视频组合轨道对象,音频组合轨道对象只能添加音频流,视频组合轨道对象只能添加视频流

  • 4、AVMutableComposition
    组合对象,通过它构建组合轨道对象

  • 5、AVAssetExportSession
    执行合并操作并导出为文件对象,该对象内部应该是封装了合并多个音频流或者视频流的操作和封装操作

实现代码

#import <Foundation/Foundation.h>

@interface AVMYComposition : NSObject

/** 实现音视频合并功能
 *  1、要合并的视频时长大于任何一个音频的时长,有可能小于两段音频的时长
 *  2、以视频的时长为基准,如果两段音频的时长之和大于视频时长,则截取掉第二个音频的部分时间
 */
- (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对象
     * 组合对象,它是AVAsset的子类,通过它来实现音视频的合并。它就相当于一个编辑容器,每一个要被合并的
     * 音频或者视频轨道被组装为AVMutableCompositionTrack然后进行合并
     *
     *  AVMutableCompositionTrack组合对象轨道,他是AVAssetTrack的子类。代表了每一个要被合并的音频或者视频轨道
     */
    AVMutableComposition *mixComposition = [AVMutableComposition composition];
    
    // 添加一个组合对象轨道,用于添加视频轨道
    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;
    }
    
    // 添加一个组合对象轨道,第二个参数为kCMPersistentTrackID_Invalid代表由系统自动生成ID
    AVMutableCompositionTrack *audioComTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    AVURLAsset *audioAsset1 = [AVURLAsset assetWithURL:audioUrl1];
    // 将同步解析,会阻塞当前线程
    CMTime duration1 = audioAsset1.duration;
    AVAssetTrack *audioTrack1 = [[audioAsset1 tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
    CMTimeRange firstTimeRange = CMTimeRangeMake(kCMTimeZero,duration1);
    // 往组合对象轨道中添加轨道对象
    [audioComTrack insertTimeRange:firstTimeRange ofTrack:audioTrack1 atTime:kCMTimeZero error:&error];
    if (error) {
        NSLog(@"audio track %@",error);
        return;
    }
   
    AVURLAsset *audioAsset2 = [AVURLAsset assetWithURL:audioUrl2];
    // 阻塞当前线程
    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];
    /** 参数解释:
     *  timeRange:代表截取track的时间范围内容然后插入这个组合对象的轨道中
     *  startTime:代表将这段内容按组对象轨道时间轴的指定位置插入
     */
    [audioComTrack insertTimeRange:secondTimeRange ofTrack:audioTrack2 atTime:duration1 error:&error];
    
    // 执行合并
    if ([[NSFileManager defaultManager] fileExistsAtPath:dsturl.path]) {
        [[NSFileManager defaultManager] removeItemAtURL:dsturl error:nil];
    }
    
    // 合并导出会话
    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(@"结束了");
}
@end

遇到问题

项目地址

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

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,884评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,212评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,351评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,412评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,438评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,127评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,714评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,636评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,173评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,264评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,402评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,073评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,763评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,253评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,382评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,749评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,403评论 2 358