前言
從本文開始逐漸學習iOS自帶的多媒體處理框架断楷,例如AVFoundation,VideoToolbox,CoreMedia衣厘,CoreVideo實現多媒體的處理,并且將實現方式以及效果和ffmpeg的方式做對比
給一個視頻添加音樂,將多段音視頻文件合并為一個文件是很常見的需求影暴,AVFoundation就提供了這樣的接口错邦。
本文的目的:
合并兩個MP4文件為一個
音視頻合并相關流程
上圖介紹了AVFoundation框架中關于合并音視頻文件的相關的對象關系圖,可以看到使用AVFoundation合并音視頻還是相對比較簡單的型宙。
相關對象及函數介紹
1撬呢、AVURLAsset
容器對象,代表了要操作的容器妆兑。封裝魂拦,解封裝,音視頻播放搁嗓,以及音視頻合并等等操作的基礎都涉及到這個對象芯勘。2、AVAssetTrack
音視頻軌道對象腺逛,代表了文件中的一路音頻流或者一路視頻流荷愕,它作為每一個要被合并的音頻或者視頻流被添加到組合對象中最終進行合并3、AVMutableCompositionTrack
組合軌道對象棍矛,它作為音視頻合并的基礎安疗,通過它添加要合并的音頻流或者視頻流,分為兩種類型:音頻組合軌道對象和視頻組合軌道對象够委,音頻組合軌道對象只能添加音頻流荐类,視頻組合軌道對象只能添加視頻流4、AVMutableComposition
組合對象慨绳,通過它構建組合軌道對象5掉冶、AVAssetExportSession
執(zhí)行合并操作并導出為文件對象,該對象內部應該是封裝了合并多個音頻流或者視頻流的操作和封裝操作
實現代碼
#import <Foundation/Foundation.h>
@interface AVMYComposition : NSObject
/** 合并任意兩個相同容器類型容器文件的功能脐雪;合并后的文件分辨率取最低的文件分辨率厌小,像素格式及顏色范圍取第一文件的。編碼方式則
*/
- (void)mergeFile:(NSURL*)filstURL twoURL:(NSURL*)twoURL dst:(NSURL*)dsturl;
@end
import "AVMYComposition.h"
#import <AVFoundation/AVFoundation.h>
@implementation AVMYComposition
{
dispatch_semaphore_t processSemaphore;
}
- (void)mergeFile:(NSURL*)filstURL twoURL:(NSURL*)twoURL dst:(NSURL*)dsturl
{
NSLog(@"開始");
processSemaphore = dispatch_semaphore_create(0);
// 創(chuàng)建組合對象
AVMutableComposition *composition = [AVMutableComposition composition];
// 為組合對象添加組合對象音頻軌道战秋,用于合并所有音頻
AVMutableCompositionTrack *audioComTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
// 為組合對象添加組合對象視頻軌道璧亚,用于合并所有視頻
AVMutableCompositionTrack *videoComTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
// 準備第一個容器文件的所有音頻和視頻軌道
AVURLAsset *asset1 = [[AVURLAsset alloc] initWithURL:filstURL options:nil];
NSArray *audioTracks1 = [asset1 tracksWithMediaType:AVMediaTypeAudio];
NSArray *videoTracks1 = [asset1 tracksWithMediaType:AVMediaTypeVideo];
AVAssetTrack *audioTrack1 = audioTracks1?audioTracks1[0]:nil;
AVAssetTrack *videoTrack1 = videoTracks1?videoTracks1[0]:nil;
// 準備第二個文件的所有音頻和視頻軌道
AVURLAsset *asset2 = [[AVURLAsset alloc] initWithURL:twoURL options:nil];
NSArray *audioTracks2 = [asset2 tracksWithMediaType:AVMediaTypeAudio];
NSArray *videoTracks2 = [asset2 tracksWithMediaType:AVMediaTypeVideo];
AVAssetTrack *audioTrack2 = audioTracks2?audioTracks2[0]:nil;
AVAssetTrack *videoTrack2 = videoTracks2?videoTracks2[0]:nil;
// 將解析出的每個文件的各個軌道添加到組合對象的對應的用于編輯的音視頻軌道對象中
CMTimeRange audioTmeRange1 = kCMTimeRangeZero;
CMTimeRange videoTmeRange1 = kCMTimeRangeZero;
NSError *error = nil;
if (audioTrack1) {
audioTmeRange1 = CMTimeRangeMake(kCMTimeZero, asset1.duration);
// 組合對象音頻軌道添加音頻軌道
[audioComTrack insertTimeRange:audioTmeRange1 ofTrack:audioTrack1 atTime:kCMTimeZero error:&error];
if (error) {
NSLog(@"audio1 error %@",error);
return;;
}
}
if (videoTrack1) {
videoTmeRange1 = CMTimeRangeMake(kCMTimeZero, asset1.duration);
// 組合對象音頻軌道添加音頻軌道
[videoComTrack insertTimeRange:videoTmeRange1 ofTrack:videoTrack1 atTime:kCMTimeZero error:&error];
if (error) {
NSLog(@"video1 error %@",error);
return;;
}
}
// 處理第二個文件
CMTimeRange audioTmeRange2 = kCMTimeRangeZero;
CMTimeRange videoTmeRange2 = kCMTimeRangeZero;
if (audioTrack2) {
audioTmeRange2 = CMTimeRangeMake(kCMTimeZero, asset2.duration);
[audioComTrack insertTimeRange:audioTmeRange2 ofTrack:audioTrack2 atTime:asset1.duration error:&error];
if (error) {
NSLog(@"audio2 error %@",error);
return;;
}
}
if (videoTrack2) {
videoTmeRange2 = CMTimeRangeMake(kCMTimeZero, asset2.duration);
[videoComTrack insertTimeRange:videoTmeRange2 ofTrack:videoTrack2 atTime:asset1.duration error:&error];
if (error) {
NSLog(@"video2 error %@",error);
return;;
}
}
// 執(zhí)行合并
if ([[NSFileManager defaultManager] fileExistsAtPath:dsturl.path]) {
[[NSFileManager defaultManager] removeItemAtURL:dsturl error:nil];
}
// 執(zhí)行合并軌道對象會話
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetHighestQuality];
exportSession.outputURL = dsturl;
exportSession.outputFileType = AVFileTypeMPEG4;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
NSLog(@"over");
if (exportSession.status != AVAssetExportSessionStatusCompleted) {
NSLog(@"error %@",exportSession.error);
}
dispatch_semaphore_signal(self->processSemaphore);
}];
dispatch_semaphore_wait(self->processSemaphore, DISPATCH_TIME_FOREVER);
NSLog(@"結束了");
}
遇到問題
項目地址
https://github.com/nldzsz/ffmpeg-demo
位于AVFoundation目錄下文件AVMYComposition.h/AVMYComposition.m中