iOS 中合并音视频如何实现?
在 iOS 中,合并音视频通常涉及将多个音频文件、视频文件或音频与视频轨道组合成一个完整的媒体文件。以下是使用 AVFoundation 框架的详细实现方案,涵盖音频合并、视频合并以及音视频合并的完整代码示例。
1. 音频合并(多段音频拼接)
将多个音频文件(如 .mp3
、.m4a
)合并为一个音频文件。
代码示例
// 合并音频文件(支持 .mp3/.m4a 等格式)
- (void)mergeAudioFiles:(NSArray<NSURL *> *)audioURLs completion:(void (^)(NSURL *outputURL, NSError *error))completion {
// 1. 创建 AVMutableComposition 对象
AVMutableComposition *composition = [AVMutableComposition composition];
// 2. 添加音频轨道
AVMutableCompositionTrack *audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
// 3. 插入每个音频文件到轨道中
CMTime currentTime = kCMTimeZero;
for (NSURL *url in audioURLs) {
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
AVAssetTrack *assetTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] firstObject];
[audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration)
ofTrack:assetTrack
atTime:currentTime
error:nil];
currentTime = CMTimeAdd(currentTime, asset.duration);
}
// 4. 导出合并后的音频
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetAppleM4A];
NSString *outputPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"mergedAudio.m4a"];
exportSession.outputURL = [NSURL fileURLWithPath:outputPath];
exportSession.outputFileType = AVFileTypeAppleM4A;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
if (exportSession.status == AVAssetExportSessionStatusCompleted) {
NSLog(@"音频合并成功: %@", outputPath);
if (completion) completion([NSURL fileURLWithPath:outputPath], nil);
} else {
NSError *error = exportSession.error;
NSLog(@"音频合并失败: %@", error.localizedDescription);
if (completion) completion(nil, error);
}
}];
}
使用方法
// 示例:合并两个音频文件
NSArray<NSURL *> *audioURLs = @[
[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"audio1" ofType:@"mp3"]],
[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"audio2" ofType:@"mp3"]]
];
[self mergeAudioFiles:audioURLs completion:^(NSURL *outputURL, NSError *error) {
if (outputURL) {
NSLog(@"合并后的音频路径: %@", outputURL.path);
}
}];
2. 视频合并(多段视频拼接)
将多个视频文件(如 .mp4
)合并为一个视频文件。
代码示例
// 合并视频文件(支持 .mp4 等格式)
- (void)mergeVideoFiles:(NSArray<NSURL *> *)videoURLs completion:(void (^)(NSURL *outputURL, NSError *error))completion {
// 1. 创建 AVMutableComposition 对象
AVMutableComposition *composition = [AVMutableComposition composition];
// 2. 添加视频轨道和音频轨道
AVMutableCompositionTrack *videoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack *audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
// 3. 插入每个视频文件到轨道中
CMTime currentTime = kCMTimeZero;
for (NSURL *url in videoURLs) {
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
AVAssetTrack *videoAssetTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
AVAssetTrack *audioAssetTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] firstObject];
[videoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration)
ofTrack:videoAssetTrack
atTime:currentTime
error:nil];
[audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration)
ofTrack:audioAssetTrack
atTime:currentTime
error:nil];
currentTime = CMTimeAdd(currentTime, asset.duration);
}
// 4. 导出合并后的视频
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetHighestQuality];
NSString *outputPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"mergedVideo.mp4"];
exportSession.outputURL = [NSURL fileURLWithPath:outputPath];
exportSession.outputFileType = AVFileTypeMPEG4;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
if (exportSession.status == AVAssetExportSessionStatusCompleted) {
NSLog(@"视频合并成功: %@", outputPath);
if (completion) completion([NSURL fileURLWithPath:outputPath], nil);
} else {
NSError *error = exportSession.error;
NSLog(@"视频合并失败: %@", error.localizedDescription);
if (completion) completion(nil, error);
}
}];
}
使用方法
// 示例:合并两个视频文件
NSArray<NSURL *> *videoURLs = @[
[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"video1" ofType:@"mp4"]],
[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"video2" ofType:@"mp4"]]
];
[self mergeVideoFiles:videoURLs completion:^(NSURL *outputURL, NSError *error) {
if (outputURL) {
NSLog(@"合并后的视频路径: %@", outputURL.path);
}
}];
3. 音视频合并(将音频与视频组合)
将独立的音频文件和视频文件合并为一个包含音视频的媒体文件。
代码示例
// 合并音频与视频
- (void)mergeAudio:(NSURL *)audioURL withVideo:(NSURL *)videoURL completion:(void (^)(NSURL *outputURL, NSError *error))completion {
// 1. 创建 AVMutableComposition 对象
AVMutableComposition *composition = [AVMutableComposition composition];
// 2. 添加视频轨道
AVMutableCompositionTrack *videoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVURLAsset *videoAsset = [AVURLAsset URLAssetWithURL:videoURL options:nil];
AVAssetTrack *videoAssetTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] firstObject];
[videoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration)
ofTrack:videoAssetTrack
atTime:kCMTimeZero
error:nil];
// 3. 添加音频轨道
AVMutableCompositionTrack *audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
AVURLAsset *audioAsset = [AVURLAsset URLAssetWithURL:audioURL options:nil];
AVAssetTrack *audioAssetTrack = [[audioAsset tracksWithMediaType:AVMediaTypeAudio] firstObject];
[audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioAsset.duration)
ofTrack:audioAssetTrack
atTime:kCMTimeZero
error:nil];
// 4. 导出合并后的音视频
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetHighestQuality];
NSString *outputPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"mergedMedia.mp4"];
exportSession.outputURL = [NSURL fileURLWithPath:outputPath];
exportSession.outputFileType = AVFileTypeMPEG4;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
if (exportSession.status == AVAssetExportSessionStatusCompleted) {
NSLog(@"音视频合并成功: %@", outputPath);
if (completion) completion([NSURL fileURLWithPath:outputPath], nil);
} else {
NSError *error = exportSession.error;
NSLog(@"音视频合并失败: %@", error.localizedDescription);
if (completion) completion(nil, error);
}
}];
}
使用方法
// 示例:合并一个音频和一个视频文件
NSURL *audioURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"backgroundMusic" ofType:@"mp3"]];
NSURL *videoURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"video" ofType:@"mp4"]];
[self mergeAudio:audioURL withVideo:videoURL completion:^(NSURL *outputURL, NSError *error) {
if (outputURL) {
NSLog(@"合并后的音视频路径: %@", outputURL.path);
}
}];
4. 注意事项
-
采样率与编码格式
- 合并音频时,确保所有音频文件的采样率(如 44.1kHz)一致,否则需要先进行重采样。
- 合并视频时,确保所有视频的分辨率、帧率一致,否则需调整为统一参数。
-
性能优化
- 使用
AVAssetExportSession
的AVAssetExportPresetLowQuality
或AVAssetExportPresetMediumQuality
降低导出质量以加快处理速度。 - 大文件合并时,建议分段处理或使用后台线程。
- 使用
-
错误处理
- 检查
AVAssetExportSession.status
和error
信息,确保合并过程稳定。
- 检查
-
资源释放
- 合并完成后,删除临时文件以释放存储空间。
5. 第三方工具推荐
如果需要更复杂的音视频处理(如裁剪、滤镜、转码),可以结合以下工具:
-
FFmpeg:通过
FFmpeg-iOS
实现强大的音视频处理功能。 - GPUImage:用于实时视频滤镜和图像处理。
- Lame:用于音频编码(如 MP3)。
通过以上方案,你可以高效地实现 iOS 平台上的音视频合并功能,适用于短视频拼接、音乐创作、播客制作等场景。