iOS音频合并的多种方式

最近在做音频合并这块需求,然后遇见了几种形式,这里做一个总结。

第一种 可网络可本地 ,可调整音量,范围等 定制化还比较高

1.基本类介绍
refer:https://docs.microsoft.com/en-us/dotnet/api/avfoundation.avcomposition?view=xamarin-ios-sdk-12

  • AVAsset 媒体信息
  • AVURLAsset 根据URL路径创建的媒体信息
  • AVAssetTrack 资源轨道,包括音频轨道和视频轨道
  • AVMutableAudioMixInputParameters音频操作参数
  • AVMutableComposition 继承自AVComposition,用于从现有Asset创建新合成的可变对象。包含多个轨道的媒体信息,可以添加、删除轨道
  • AVAssetExportSession 导出

具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#pragma mark - 音频与音频的合并
+ (void)mixOriginalAudio:(NSURL *)originalAudioPath
originalAudioVolume:(float)originalAudioVolume
bgAudioPath:(NSURL *)bgAudioPath
bgAudioVolume:(float)bgAudioVolume
outPutFileName:(NSString *)fileName
completionBlock:(CompletionBlock)completionBlock
{
if (originalAudioPath == nil) {
return;
}
if (bgAudioPath == nil) {
return;
}
if (originalAudioVolume > 1.0) {
originalAudioVolume = 1.0f;
}
if (originalAudioVolume < 0) {
originalAudioVolume = 0.0f;
}
if (bgAudioVolume > 1.0) {
bgAudioVolume = 1.0f;
}
if (bgAudioVolume < 0) {
bgAudioVolume = 0.0f;
}

dispatch_async(dispatch_get_global_queue(0, 0), ^{

AVURLAsset *originalAudioAsset = [AVURLAsset assetWithURL:originalAudioPath];
AVURLAsset *bgAudioAsset = [AVURLAsset assetWithURL:bgAudioPath];

AVMutableComposition *compostion = [AVMutableComposition composition];

AVMutableCompositionTrack *originalAudio = [compostion addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:0];
[originalAudio insertTimeRange:CMTimeRangeMake(kCMTimeZero, originalAudioAsset.duration) ofTrack:[originalAudioAsset tracksWithMediaType:AVMediaTypeAudio].firstObject atTime:kCMTimeZero error:nil];

AVMutableCompositionTrack *bgAudio = [compostion addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:0];

[bgAudio insertTimeRange:CMTimeRangeMake(kCMTimeZero, originalAudioAsset.duration) ofTrack:[bgAudioAsset tracksWithMediaType:AVMediaTypeAudio].firstObject atTime:kCMTimeZero error:nil];
//播放速率会受影响
NSUInteger originalAudioAssetTotalSeconds = CMTimeGetSeconds(originalAudioAsset.duration);
NSUInteger bgAudioAssetTotalSeconds = CMTimeGetSeconds(bgAudioAsset.duration);
if (originalAudioAssetTotalSeconds>bgAudioAssetTotalSeconds) {
[bgAudio scaleTimeRange:CMTimeRangeMake(kCMTimeZero, bgAudioAsset.duration) toDuration:originalAudioAsset.duration];
}
/** 得到对应轨道中的音频声音信息,并更改 */
AVMutableAudioMixInputParameters *originalAudioParameters = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:originalAudio];
[originalAudioParameters setVolume:originalAudioVolume atTime:kCMTimeZero];

AVMutableAudioMixInputParameters *bgAudioParameters = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:bgAudio];
[bgAudioParameters setVolume:bgAudioVolume atTime:kCMTimeZero];

/** 赋给对应的类 */
AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
audioMix.inputParameters = @[ originalAudioParameters, bgAudioParameters ];

AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:compostion presetName:AVAssetExportPresetAppleM4A];

/** 设置输出路径 */
NSURL *outputPath = [self exporterAudioPathWithFileName:fileName];

session.audioMix = audioMix;
session.outputURL = outputPath;
session.outputFileType = AVFileTypeAppleM4A;
session.shouldOptimizeForNetworkUse = YES;

[session exportAsynchronouslyWithCompletionHandler:^{

dispatch_async(dispatch_get_main_queue(), ^{

switch ([session status]) {
case AVAssetExportSessionStatusFailed: {
NSLog(@"合成失败:%@", [[session error] description]);
completionBlock(NO, outputPath);
} break;

case AVAssetExportSessionStatusCancelled: {
completionBlock(NO, outputPath);
} break;

case AVAssetExportSessionStatusCompleted: {
completionBlock(YES, outputPath);

} break;

default: {
completionBlock(NO, outputPath);
} break;
}

});


}];

});
}
⚠️压缩时长
  • 更改给定时间范围内所有轨道的时长。
    1
    2
    3
    4
    5
    6
    - (void)scaleTimeRange:(CMTimeRange)timeRange
    toDuration:(CMTime)duration;
    参数timeRange:要缩放的合成轨道的时间范围。
    参数duration:新的时长。

    受缩放操作影响的每个轨道段将以等于其结果时间映射的source.duration/target.duration的速率呈现。
⚠️设置背景音乐时长和插入时间
1
2
[bgAudio insertTimeRange:CMTimeRangeMake(kCMTimeZero, originalAudioAsset.duration) ofTrack:[bgAudioAsset tracksWithMediaType:AVMediaTypeAudio].firstObject atTime:kCMTimeZero error:nil];
因为我的需求是加入背景音乐给录音,所以将背景音乐从头插入kCMTimeZero 插入时长为录音的时长 originalAudioAsset.duration
⚠️注意导出格式

首先是输出路径,切记切记这里的后缀一定要和上面选择的呼应,比如我写的格式为AVAssetExportPresetAppleM4A那么我的文件后缀就需要为.m4a,如果用.MP3这种的话是无法正确生成出文件的,有可能会出现空文件的存在.一定要注意!!!

⚠️传入网络链接的时候 需要将网络链接下载下来转成本地路径进行传入。

refer: https://github.com/Anny-github/AudioPlayerManager

第二种 本地音频合并,可以调整音量

https://github.com/daybreak1024/ZLMMixAudio

第三种 可以网络或者本地合并,但是无法定制化,因为用的别人的静态库😢

https://github.com/QuintGao/GKDubbingDemo