前言
从本文开始逐渐学习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中
网友评论