【问题标题】:Short circuiting of audio in VOIP app with CallKit使用 CallKit 使 VOIP 应用程序中的音频短路
【发布时间】:2017-03-10 04:03:47
【问题描述】:

我使用SpeakerBox 应用程序作为我的VOIP 应用程序的基础。我已经设法让一切正常,但我似乎无法摆脱从麦克风到设备扬声器的音频“短路”。

换句话说,当我拨打电话时,我可以在扬声器中听到自己以及对方的声音。我该如何更改?

AVAudioSession 设置:

    AVAudioSession *sessionInstance = [AVAudioSession sharedInstance];

    NSError *error = nil;
    [sessionInstance setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
    XThrowIfError((OSStatus)error.code, "couldn't set session's audio category");

    [sessionInstance setMode:AVAudioSessionModeVoiceChat error:&error];
    XThrowIfError((OSStatus)error.code, "couldn't set session's audio mode");

    NSTimeInterval bufferDuration = .005;
    [sessionInstance setPreferredIOBufferDuration:bufferDuration error:&error];
    XThrowIfError((OSStatus)error.code, "couldn't set session's I/O buffer duration");

    [sessionInstance setPreferredSampleRate:44100 error:&error];
    XThrowIfError((OSStatus)error.code, "couldn't set session's preferred sample rate");

IO 单元的设置:

- (void)setupIOUnit
{
try {
    // Create a new instance of Apple Voice Processing IO

    AudioComponentDescription desc;
    desc.componentType = kAudioUnitType_Output;
    desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
    desc.componentFlags = 0;
    desc.componentFlagsMask = 0;

    AudioComponent comp = AudioComponentFindNext(NULL, &desc);
    XThrowIfError(AudioComponentInstanceNew(comp, &_rioUnit), "couldn't create a new instance of Apple Voice Processing IO");

    //  Enable input and output on Apple Voice Processing IO
    //  Input is enabled on the input scope of the input element
    //  Output is enabled on the output scope of the output element

    UInt32 one = 1;
    XThrowIfError(AudioUnitSetProperty(_rioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &one, sizeof(one)), "could not enable input on Apple Voice Processing IO");
    XThrowIfError(AudioUnitSetProperty(_rioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &one, sizeof(one)), "could not enable output on Apple Voice Processing IO");

    // Explicitly set the input and output client formats
    // sample rate = 44100, num channels = 1, format = 32 bit floating point

    CAStreamBasicDescription ioFormat = CAStreamBasicDescription(44100, 1, CAStreamBasicDescription::kPCMFormatFloat32, false);
    XThrowIfError(AudioUnitSetProperty(_rioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &ioFormat, sizeof(ioFormat)), "couldn't set the input client format on Apple Voice Processing IO");
    XThrowIfError(AudioUnitSetProperty(_rioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &ioFormat, sizeof(ioFormat)), "couldn't set the output client format on Apple Voice Processing IO");

    // Set the MaximumFramesPerSlice property. This property is used to describe to an audio unit the maximum number
    // of samples it will be asked to produce on any single given call to AudioUnitRender
    UInt32 maxFramesPerSlice = 4096;
    XThrowIfError(AudioUnitSetProperty(_rioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFramesPerSlice, sizeof(UInt32)), "couldn't set max frames per slice on Apple Voice Processing IO");

    // Get the property value back from Apple Voice Processing IO. We are going to use this value to allocate buffers accordingly
    UInt32 propSize = sizeof(UInt32);
    XThrowIfError(AudioUnitGetProperty(_rioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFramesPerSlice, &propSize), "couldn't get max frames per slice on Apple Voice Processing IO");

    // We need references to certain data in the render callback
    // This simple struct is used to hold that information

    cd.rioUnit = _rioUnit;
    cd.muteAudio = &_muteAudio;
    cd.audioChainIsBeingReconstructed = &_audioChainIsBeingReconstructed;

    // Set the render callback on Apple Voice Processing IO
    AURenderCallbackStruct renderCallback;
    renderCallback.inputProc = performRender;
    renderCallback.inputProcRefCon = NULL;
    XThrowIfError(AudioUnitSetProperty(_rioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &renderCallback, sizeof(renderCallback)), "couldn't set render callback on Apple Voice Processing IO");

    // Initialize the Apple Voice Processing IO instance
    XThrowIfError(AudioUnitInitialize(_rioUnit), "couldn't initialize Apple Voice Processing IO instance");
}

catch (CAXException &e) {
    NSLog(@"Error returned from setupIOUnit: %d: %s", (int)e.mError, e.mOperation);
}
catch (...) {
    NSLog(@"Unknown error returned from setupIOUnit");
}

return;
}

启动 IOUnit:

NSError *error = nil;
[[AVAudioSession sharedInstance] setActive:YES error:&error];
if (nil != error) NSLog(@"AVAudioSession set active (TRUE) failed with error: %@", error);

OSStatus err = AudioOutputUnitStart(_rioUnit);
if (err) NSLog(@"couldn't start Apple Voice Processing IO: %d", (int)err);
return err;

停止 IOUnit

NSError *error = nil;
[[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error];
if (nil != error) NSLog(@"AVAudioSession set active (FALSE) failed with error: %@", error);

OSStatus err = AudioOutputUnitStop(_rioUnit);
if (err) NSLog(@"couldn't stop Apple Voice Processing IO: %d", (int)err);
return err;

我使用 PJSIP 作为我的 SIP 堆栈并且有一个 Asterisk 服务器。问题必须在客户端,因为我们也有一个基于 Android 的 PJSIP 实现,没有这个问题。

【问题讨论】:

  • 我也在调查我的应用程序几乎相同的问题。如果我正确理解扬声器盒中的配置,扬声器盒代码会将输入流式传输到扬声器。所以我没有使用那个示例代码。我正在使用 pjsua_set_no_snd_dev() 和 pjsua_set_snd_dev()。就我而言,另一方受到此短路问题的影响。顺便说一句,如果我不使用 CallKit,我的植入效果很好。
  • 好吧,我的问题也存在于较旧的 iOS 版本上,对方可以听到他自己的声音,我不确定是什么原因导致了这个问题。在你的情况下,我会说,使用 pjsip 函数。有关 pjsip 和 CallKit 的更多详细信息,请查看trac.pjsip.org/repos/ticket/1941

标签: avfoundation core-audio voip ios10 callkit


【解决方案1】:

我在使用 WebRTC 时遇到了同样的问题。我最终得出的结论是,您不应在 AudioController.mm 中设置 IOUnit,而应将其留给 PJSIP(在我的情况下为 WebRTC)。

快速修复如下: 注释掉 AudioController.mm 的 setupAudioChain 中的 [self setupIOUnit]; 以及 ProviderDelegate.swiftdidActivate audioSession 中的 startAudio()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-02
    相关资源
    最近更新 更多