前言
从本文开始逐渐学习iOS自带的多媒体处理框架,例如AVFoundation,VideoToolbox,CoreMedia,CoreVideo实现多媒体的处理,并且将实现方式以及效果和ffmpeg的方式做对比
截取一个音视频文件中的某个时间段的音视频然后保存是很常见的需求,AVFoundation就提供了这样的接口,它其实也是利用AVMutableComposition来实现的
本文的目的:
截取一个音视频文件中的某个时间段的音视频然后保存到本地
音视频截取相关流程
上图介绍了AVFoundation框架中关于音视频截取的相关的对象关系图,可以看到使用AVFoundation截取音视频中某一段还是相对比较简单的。
相关对象及函数介绍
1、AVURLAsset
容器对象,代表了要操作的容器。封装,解封装,音视频播放,以及音视频合并等等操作的基础都涉及到这个对象。2、AVAssetTrack
音视频轨道对象,代表了文件中的一路音频流或者一路视频流,它作为每一个要被合并的音频或者视频流被添加到组合对象中最终进行合并3、AVMutableCompositionTrack
组合轨道对象,它作为音视频合并的基础,通过它添加要合并的音频流或者视频流,分为两种类型:音频组合轨道对象和视频组合轨道对象,音频组合轨道对象只能添加音频流,视频组合轨道对象只能添加视频流
通过此对象定义要截取的时间段即可实现音视频内容的截取4、AVMutableComposition
组合对象,通过它构建组合轨道对象5、AVAssetExportSession
执行合并操作并导出为文件对象,该对象内部应该是封装了合并多个音频流或者视频流的操作和封装操作
实现代码
这里例子中为截取源音视频中中5-15秒的内容然后保存为MP4
#import <Foundation/Foundation.h>
@interface AVVideoCut : NSObject
/** 实现功能:截取音视频文件中指定范围段的内容
*/
- (void)cutVideo:(NSURL*)srcURL dst:(NSURL*)dstURL;
@end
#import "AVVideoCut.h"
#import <AVFoundation/AVFoundation.h>
@implementation AVVideoCut
{
dispatch_semaphore_t processSemaphore;
}
- (void)cutVideo:(NSURL*)srcURL dst:(NSURL*)dstURL
{
processSemaphore = dispatch_semaphore_create(0);
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:srcURL options:nil];
[asset loadValuesAsynchronouslyForKeys:@[@"tracks"] completionHandler:^{
NSError *error = nil;
AVKeyValueStatus status = [asset statusOfValueForKey:@"tracks" error:&error];
if (status != AVKeyValueStatusLoaded) {
NSLog(@"loaded error %@",error);
return;
}
[self processAsset:asset dst:dstURL];
}];
dispatch_semaphore_wait(processSemaphore, DISPATCH_TIME_FOREVER);
NSLog(@"结束了");
}
- (void)processAsset:(AVAsset*)asset dst:(NSURL*)dstURL
{
// 获取容器中的音视频轨道对象
NSArray *videotracks = [asset tracksWithMediaType:AVMediaTypeVideo];
NSArray *audiotracks = [asset tracksWithMediaType:AVMediaTypeAudio];
AVAssetTrack *videoTrack = videotracks?videotracks[0]:nil;
AVAssetTrack *audioTrack = audiotracks?audiotracks[0]:nil;
// 划定要截取的时间;这里选择的时间为5-15秒的视频
CMTime start = CMTimeMake(5*1000, 1000);
CMTime time = CMTimeMake(10*1000, 1000);
CMTimeRange range = CMTimeRangeMake(start, time);
// 创建组合对象
AVMutableComposition *compostion = [AVMutableComposition composition];
if (audioTrack) {
// 添加组合音频轨道
AVMutableCompositionTrack *audiocomtrack = [compostion addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
NSError *error = nil;
// 在音频轨道中选取指定的时间范围的音频插入到组合音频轨道中
[audiocomtrack insertTimeRange:range ofTrack:audioTrack atTime:kCMTimeZero error:&error];
}
if (videoTrack) {
AVMutableCompositionTrack *videocomtrack = [compostion addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
NSError *error = nil;
[videocomtrack insertTimeRange:range ofTrack:videoTrack atTime:kCMTimeZero error:&error];
}
// 执行合并
if ([[NSFileManager defaultManager] fileExistsAtPath:dstURL.path]) {
[[NSFileManager defaultManager] removeItemAtURL:dstURL error:nil];
}
// 执行组合对象中组合轨道的编辑任务
AVAssetExportSession *extSession = [[AVAssetExportSession alloc] initWithAsset:compostion presetName:AVAssetExportPresetHighestQuality];
extSession.outputURL = dstURL;
extSession.outputFileType = AVFileTypeMPEG4;
NSLog(@"开始编辑");
[extSession exportAsynchronouslyWithCompletionHandler:^{
if (extSession.status != AVAssetExportSessionStatusCompleted) {
NSLog(@"编辑 error %@",extSession.error);
}
NSLog(@"编辑完毕");
dispatch_semaphore_signal(self->processSemaphore);
}];
}
@end
遇到问题
项目地址
https://github.com/nldzsz/ffmpeg-demo
位于AVFoundation目录下文件AVVideoCut.h/AVVideoCut.m中