文章原创
废话不多说,直接上代码
// 测试合成是否可用的(此方法可以无视)
+ (void)SyntheticRecordAndFinishBlock:(void(^)(BOOL success))block{
NSString *main = [[NSBundle mainBundle] pathForResource:@"107785182" ofType:@"m4a"];
NSString *change = [[NSBundle mainBundle] pathForResource:@"audio" ofType:@"m4a"];
[self SyntheticRecordWithMainRecordFile:main AndChangeRecordFile:change AndChangeStarTime:10.0f AndFinishBlock:^(BOOL success) {
if(success){
//文件转移
//删除文件
block(success);
}
}];
}
/**
* 第一段音频 0~30 秒 star 0 end 30
* 第二段音频 10~15 秒 star 0 end 5
* 合成: 第一段音频0~10 + 第二段音频 0~5 + 第一段音频 15~30
*
*/
+ (BOOL)SyntheticRecordWithMainRecordFile:(NSString *)mainFilePathString AndChangeRecordFile:(NSString *)recordFilePathString AndChangeStarTime:(NSTimeInterval)starTime AndFinishBlock:(void(^)(BOOL success))block{
AVMutableComposition *composition = [[AVMutableComposition alloc] init];
AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
NSURL *mainFileURL = [NSURL fileURLWithPath:mainFilePathString];
NSURL *changeFileURL = [NSURL fileURLWithPath:recordFilePathString];
AVAsset *mainAVAsset = [AVURLAsset URLAssetWithURL:mainFileURL options:nil];
AVAsset *changeAVAsset = [AVURLAsset URLAssetWithURL:changeFileURL options:nil];
if (!([self AVAssetCanUseing:mainAVAsset] && [self AVAssetCanUseing:changeAVAsset])) return NO;
//几个CMTime
CMTime mainStar = kCMTimeZero;
CMTime mainEnd = [mainAVAsset duration];
CMTime changeStar = kCMTimeZero;
CMTime changeEnd = [changeAVAsset duration];
CMTime syntheticStar = CMTimeMake((int)(floor(starTime * 100)), 100);
/**
* CMTimeRange 用于剪切音频(三个或二个)针对一段完整的音频
* CMTime 用于合成音频(三个或二个) 针对合成的音频
----***--- 第一种 3段 1 2 1
***------- 第二种 3段 1 2 1 第一段的 CMTimeRange = 0
---******* 第三种 2段 1 2
***/
//计算出三个 CMTimeRange
CMTime firstStarTime = kCMTimeZero;
CMTime twoStarTime = syntheticStar;
CMTime threeStarTime = CMTimeAdd(syntheticStar,changeEnd);
//拼接
BOOL success = YES;
CMTimeRange firstRangeInAsset = CMTimeRangeMake(mainStar, syntheticStar);
if(starTime > 0){
//非第二种情况才拼接第一段
success = [self CompositionAudioTrackInsertWithCompositionAudioTrack:compositionAudioTrack AndStarTime:firstStarTime AndCMTimeRange:firstRangeInAsset AndAVAsset:mainAVAsset];
}
CMTimeRange twoRangeInAsset = CMTimeRangeMake(changeStar, changeEnd);//这一段就是第二个完整的音频
success = [self CompositionAudioTrackInsertWithCompositionAudioTrack:compositionAudioTrack AndStarTime:twoStarTime AndCMTimeRange:twoRangeInAsset AndAVAsset:changeAVAsset];
if(CMTimeGetSeconds(threeStarTime)<CMTimeGetSeconds(mainEnd)){
//非第三种情况才拼接第三段
CMTimeRange threeRangeInAsset = CMTimeRangeMake(threeStarTime, mainEnd);
success = [self CompositionAudioTrackInsertWithCompositionAudioTrack:compositionAudioTrack AndStarTime:threeStarTime AndCMTimeRange:threeRangeInAsset AndAVAsset:mainAVAsset];
}
//生成音频文件
success = [self SyntheticFileWith:composition AndFilePath:self.writePath AndFinishBlock:block];
return success;
}
/**
* 判断是否可用
*/
+ (BOOL)AVAssetCanUseing:(AVAsset *)avAsset{
NSArray *tracks = [avAsset tracksWithMediaType:AVMediaTypeAudio];
if ([tracks count] == 0) return NO;
return YES;
}
/**
* compositionAudioTrack 拼接
*
*/
+ (BOOL)CompositionAudioTrackInsertWithCompositionAudioTrack:(AVMutableCompositionTrack *)compositionAudioTrack
AndStarTime:(CMTime)starTime
AndCMTimeRange:(CMTimeRange)timeRangeInAsset
AndAVAsset:(AVAsset *)avAsset{
AVAssetTrack *clipAudioTrack = [[avAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
NSError *error = nil;
BOOL ok = [compositionAudioTrack insertTimeRange:timeRangeInAsset ofTrack:clipAudioTrack atTime:starTime error:&error];
if (!ok) {
NSLog(@"Current Video Track Error: %@",error);
}
return ok;
}
/**
* 合成 ???
* 不会 覆盖 原文件 (存在原文件导致失败)
*/
+ (BOOL)SyntheticFileWith:(AVMutableComposition *)composition AndFilePath:(NSString *)mainFilePathString AndFinishBlock:(void(^)(BOOL success))block{
// create the export session
// no need for a retain here, the session will be retained by the
// completion handler since it is referenced there
AVAssetExportSession *exportSession = [AVAssetExportSession
exportSessionWithAsset:composition
presetName:AVAssetExportPresetAppleM4A];
if (nil == exportSession) return NO;
//NSLog(@"Output file path - %@",soundOneNew);
// configure export session output with all our parameters
exportSession.outputURL = [NSURL fileURLWithPath:mainFilePathString]; // output path
exportSession.outputFileType = AVFileTypeAppleM4A; // output file type
// perform the export
[exportSession exportAsynchronouslyWithCompletionHandler:^{
if (AVAssetExportSessionStatusCompleted == exportSession.status) {
NSLog(@"AVAssetExportSessionStatusCompleted");
block(YES);
} else if (AVAssetExportSessionStatusFailed == exportSession.status) {
// a failure may happen because of an event out of your control
// for example, an interruption like a phone call comming in
// make sure and handle this case appropriately
NSLog(@"AVAssetExportSessionStatusFailed");
block(NO);
} else {
NSLog(@"Export Session Status: %d", exportSession.status);
block(NO);
}
}];
return YES;
}
辅助方法
//文件路径
+ (NSString *)getAppDocumentDir
{
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES) objectAtIndex:0];
}