在 IOS 上使用 AVComposition 混合两个音频文件

Posted

技术标签:

【中文标题】在 IOS 上使用 AVComposition 混合两个音频文件【英文标题】:Mixing two Audio Files using AVComposition on IOS 【发布时间】:2014-09-16 15:41:50 【问题描述】:

我正在尝试混合两个音频文件(将一个音频文件放在另一个文件之上 - 没有拼接在一起),但我正在努力学习 ios 上的 AVFoundation。我在这里关注了这个答案:How to merge Audio and video using AVMutableCompositionTrack

这就是我所拥有的:

//NSURL *audioFilePath = [NSURL fileURLWithPath:@"var/mobile/Applications/822732B6-67B9-485F-BA44-FAACAB34C4FD/Documents/Coisir Cheoil10_09_2014_1429.m4a"];
NSURL *audioUrl = [NSURL fileURLWithPath:@"var/mobile/Applications/822732B6-67B9-485F-BA44-FAACAB34C4FD/Documents/Robot R-3-311_09_2014_2252.m4a"];
// need to fix link to backing Track
NSURL *backingTrackURL = [NSURL fileURLWithPath:@"var/mobile/Applications/822732B6-67B9-485F-BA44-FAACAB34C4FD/Documents/Robot R-3-316_09_2014_1559.m4a"];// need ot fix the link to this
AVURLAsset* backingTrack = [[AVURLAsset alloc] initWithURL:audioUrl options:nil];
AVURLAsset* voiceTrack = [[AVURLAsset alloc] initWithURL:backingTrackURL options:nil];

AVMutableComposition* mixComposition = [AVMutableComposition composition];

AVMutableCompositionTrack *compositionCommentaryTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];


[compositionCommentaryTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, backingTrack.duration)
                                    ofTrack:[[backingTrack tracksWithMediaType:AVMediaTypeAudio]objectAtIndex:0]
                                     atTime:kCMTimeZero
                                      error:nil];

AVMutableCompositionTrack *compositionVoiceTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];

[compositionVoiceTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, voiceTrack.duration)
                               ofTrack:[[voiceTrack tracksWithMediaType:AVMediaTypeAudio]objectAtIndex:0] atTime:kCMTimeZero error:nil];

AVAssetExportSession* _assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetHighestQuality];

NSString* mixedAudio = @"mixedAudio.m4a";

NSString *exportPath = [NSTemporaryDirectory() stringByAppendingString:mixedAudio];
NSURL *exportURL = [NSURL fileURLWithPath:exportPath];

if ([[NSFileManager defaultManager]fileExistsAtPath:exportPath]) 
    [[NSFileManager defaultManager]removeItemAtPath:exportPath error:nil];

_assetExport.outputFileType = @"m4a";
_assetExport.outputURL = exportURL;
_assetExport.shouldOptimizeForNetworkUse = YES;

[_assetExport exportAsynchronouslyWithCompletionHandler:^
    NSLog(@"Completed Sucessfully");
];

The code fails when I try to set the timeranges with an error index 0 beyond bounds for empty array.  I take it that backing track does not include tracksWithMediaTye:AvMediaTypeAudio and that is why that fails.  

我怀疑这是因为我正在加载 .m4a 音频文件,而不是像 *** 上的原始答案那样的视频文件。所以我的问题是如何混合两个单独的音频文件并将它们保存为一个新的组合文件。用途是我有一个背景音轨,用户录制自己的人声,然后可以将他们的人声与背景音轨混合,并将混合后的音频发送给自己。

感谢您的任何建议。我发现 AVFoundation 非常令人生畏。

【问题讨论】:

运行它时发生了什么? 2 个音轨的长度是否相同? 发生的事情是当我插入时间范围时,它正在寻找轨道的 objectAtIndex:0 并且当时崩溃了。此后我重新编写了代码(我将在下面发布)并且它正在工作。我认为上面的代码实际上并没有加载任何文件。我给的filePath一定是错的。 【参考方案1】:

我发布了我最终开始工作的代码,以防其他人尝试做同样的事情并想要一些代码示例(我上面的问题是我怀疑音频文件没有正确加载)

 [self showActivityIndicator]; // This code takes a while so show the user an activity Indicator
AVMutableComposition *composition = [AVMutableComposition composition];
NSArray* tracks = [NSArray arrayWithObjects:@"backingTrack", @"RobotR33", nil];
NSString* audioFileType = @"wav";

for (NSString* trackName in tracks) 
    AVURLAsset* audioAsset = [[AVURLAsset alloc]initWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:trackName ofType:audioFileType]]options:nil];

    AVMutableCompositionTrack* audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio
                                                                     preferredTrackID:kCMPersistentTrackID_Invalid];

    NSError* error;
    [audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioAsset.duration) ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio]objectAtIndex:0] atTime:kCMTimeZero error:&error];
    if (error)
    
        NSLog(@"%@", [error localizedDescription]);
    

AVAssetExportSession* _assetExport = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetAppleM4A];

NSString* mixedAudio = @"mixedAudio.m4a";

NSString *exportPath = [NSTemporaryDirectory() stringByAppendingString:mixedAudio];
NSURL *exportURL = [NSURL fileURLWithPath:exportPath];

if ([[NSFileManager defaultManager]fileExistsAtPath:exportPath]) 
    [[NSFileManager defaultManager]removeItemAtPath:exportPath error:nil];

_assetExport.outputFileType = AVFileTypeAppleM4A;
_assetExport.outputURL = exportURL;
_assetExport.shouldOptimizeForNetworkUse = YES;

[_assetExport exportAsynchronouslyWithCompletionHandler:^
    [self hideActivityIndicator];
    NSLog(@"Completed Sucessfully");
];

【讨论】:

嗨,谢谢你的回答..但是如果我将我的录音机.m4a格式和卡拉OK歌曲.mp3格式混合在一起。它合并成功。但是如果听到输出合并文件....我的声音很低,音乐很高。如何增加我的录音机声音和减少歌曲声音????? 还有一个问题:合并两首歌曲后...我得到大量数据。如何在合并两个音频后减小大小【参考方案2】:

对于 Swift 3

    showActivityIndicator()  
    var composition = AVMutableComposition()
    var tracks: [Any] = ["backingTrack", "RobotR33"]
    var audioFileType: String = "wav"
    for trackName: String in tracks 
        var audioAsset = AVURLAsset(url: URL(fileURLWithPath: Bundle.main.path(forResource: trackName, ofType: audioFileType)), options: nil)
        var audioTrack: AVMutableCompositionTrack? = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid)
        var error: Error?
        try? audioTrack?.insertTimeRange(CMTimeRangeMake(kCMTimeZero, audioAsset.duration), of: audioAsset.tracks(withMediaType: AVMediaTypeAudio)[0], atTime: kCMTimeZero)
        if error != nil 
            print("\(error?.localizedDescription)")
        
    
    var _assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A)
//  Converted with Swiftify v1.0.6355 - https://objectivec2swift.com/
var mixedAudio: String = "mixedAudio.m4a"
var exportPath: String = NSTemporaryDirectory() + (mixedAudio)
var exportURL = URL(fileURLWithPath: exportPath)
if FileManager.default.fileExists(atPath: exportPath) 
    try? FileManager.default.removeItem(atPath: exportPath)

assetExport.outputFileType = AVFileTypeAppleM4A
assetExport.outputURL = exportURL
assetExport.shouldOptimizeForNetworkUse = true
assetExport.exportAsynchronously(withCompletionHandler: () -> Void in
    self.hideActivityIndicator()
    print("Completed Sucessfully")
)

【讨论】:

以上是关于在 IOS 上使用 AVComposition 混合两个音频文件的主要内容,如果未能解决你的问题,请参考以下文章

AVComposition 在 Airplay 上中断

在 AVComposition 中编辑视频时序

使用 CMTimeMapping 寻求 AVComposition 会导致 AVPlayerLayer 冻结

AVComposition 的 AVPlayer 播放速率

高性能图文混排框架,构架顺滑的iOS应用-b

IOS开发之使用UIWebView实现图文混排