ios - 如何在 iOS 11 中正确停止和删除音频处理器

标签 ios objective-c avfoundation

我正在使用类似于audioTap example from apple的audioTapProcessor .

基本上,audioTapProcessor 被创建并添加到audioMix 中:

MTAudioProcessingTapRef audioProcessingTap;
if (noErr == MTAudioProcessingTapCreate(kCFAllocatorDefault, &callbacks,     kMTAudioProcessingTapCreationFlag_PreEffects, &audioProcessingTap))
{
    audioMixInputParameters.audioTapProcessor = audioProcessingTap;

    CFRelease(audioProcessingTap);

    audioMix.inputParameters = @[audioMixInputParameters];

    _audioMix = audioMix;
}

当创建audioMix时,它被添加到avPlayer的currentItem中:

self.avPlayer.currentItem.audioMix = audioMix;

当我完成audioTapProcessor 后,我会清除对audioTapProcessor 和audioMix 的引用:

if (self.avPlayer.currentItem.audioMix){

    for (AVMutableAudioMixInputParameters *inputParameter in player.currentItem.audioMix.inputParameters) {
        inputParameter.audioTapProcessor = nil;

    }

    player.currentItem.audioMix = nil;
}

这会触发tap_UnprepareCallbacktap_FinalizeCallback函数,并且audioTapProcessor对象被释放。

在 iOS11 几天前发布之前,这一切都没有任何问题。

在 iOS11 设备上,我有时会在 ClientProcessingTapManager 线程上收到 EXC_BAD_ACCESS 错误。错误发生在AVFoundation的其中一个类中,所以我无法调试。该错误发生在 ClientProcessingTap::PerformTap 中的某处。

我已向audioTap 回调添加了日志记录,看起来只有tap_ProcessCallBack 在ClientProcessingTapManager 线程上执行。

我认为有时audioTapProcessor的tap_ProcessCallback方法会在此线程上触发,而实际的audioTapProcessor已在另一个线程上释放。

我的问题是:如何正确停止 audioTapProcessor 并将其从内存中删除?


这是一些输出日志记录示例:

2017-09-22 12:49:26:171 xx[1261:780894] cleaning up audioMix of AvPlayer
2017-09-22 12:49:26:213 xx[1261:781173] into tap_ProcessCallback. thread: ClientProcessingTapManager
2017-09-22 12:49:26:213 xx[1261:781173] end of tap_ProcessCallback
2017-09-22 12:49:26:217 xx[1261:781127] into tap_UnprepareCallback. 
2017-09-22 12:49:26:218 xx[1261:781127] end of tap_UnprepareCallback
2017-09-22 12:49:26:218 xx[1261:781127] into tap_FinalizeCallback. 
2017-09-22 12:49:26:218 Tunify[1261:781127] end of tap_FinalizeCallback
2017-09-22 12:49:26:218 Tunify[1261:781127] into MYAudioTapProcessor dealloc.
(lldb) 

很高兴知道:

我正在使用 avPlayers 池,因此我正在重用这些对象。当我不清除 avPlayers 的 audioMix 时,我不会遇到此错误。如果不清理 audioMix 对象,只有当我重用 avPlayer 并将新的 audioMix 分配给 avPlayer 时,audioTapProcessors 才会被释放。


我找到了related question ,但我已经触发了清理功能。那里的解决方案在 iOS11 设备上给出了相同的错误

最佳答案

这是 iOS 11 下 AVFoundation 的一个错误。已在 iOS 12 中修复。 针对该问题的雷达rdar://34977000

一个可能的解决方法是在释放 AVPlayer/AVPlayerItem 之前调用prepareReleaseAudioMix:

+ (void)prepareReleaseAudioMix:(AVAudioMix*)audioMix {
    if ( @available(iOS 11.0, *) ) {
        // Starting with iOS 11, it is not required to manually nil audioTapProcessor,
        // but we need to retain the audioMix for a bit to make sure the processing callback
        // will not be called after we release (this is due to a bug in iOS 11 which calls the release
        // callback but still calls the processing callback afterwards - it also releases internal data
        // on release, so simply checking for release in the processing block is not enough)
        // rdar://34977000
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [self _prepareReleaseAudioMixNow:audioMix];
        });
    } else {
        // Prior to iOS 11 we need to manually nil the audioTapProcessor
        [self _prepareReleaseAudioMixNow:audioMix];
    }
}

+ (void)_prepareReleaseAudioMixNow:(AVAudioMix*)audioMix {
    for ( AVMutableAudioMixInputParameters *parameters in audioMix.inputParameters ) {
        if ( ![parameters isKindOfClass:[AVMutableAudioMixInputParameters class]] )
            continue;
        parameters.audioTapProcessor = nil;
    }
}

关于ios - 如何在 iOS 11 中正确停止和删除音频处理器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46362859/

相关文章:

ios - React Native 复选框显示为红点

ios - 无法将 Cordova/Ionic App 的二进制文件提交到 Appstore

ios - 为什么允许在实现内部的@interface中重写ivar,但是具有这种奇怪的行为?

ios - maxRecordedDuration 过期后无法检查录制是否成功

ios - 为什么 audioRecorderDidFinishRecording 没有被调用?能够成功录制和播放音频

ios - 如何将 .caf 文件转换为 .mp3 文件?

iOS,如何将应用程序本地化为中文,带编号?

iphone - Parse.com 关系查询问题

ios - 如何在 UITableView 中创建刷新按钮

objective-c - 在 iOS 中向服务器发送 GET 请求并从中获取生成的 html 页面数据