SpeakHere 的 AudioQueue 代码在 iPad 上失败

Posted

技术标签:

【中文标题】SpeakHere 的 AudioQueue 代码在 iPad 上失败【英文标题】:AudioQueue code from SpeakHere fails on iPad 【发布时间】:2010-07-10 16:15:30 【问题描述】:

我正在创建的应用程序中使用 SpeakHere 音频类,该应用程序必须同时播放和录制。

我在通用应用构建(针对 iPad 和 iPhone)中使用带有 3.2 设备目标的最新 SDK。

该应用使用 MPMoviePlayerController 播放流媒体电影并同时录制音频。

这在 iPhone 上 100% 完美运行。

但是,它在我的客户端 iPad 上 100% 失败。 日志显示 !act 错误,Audiosession 只是拒绝激活!我从他那里收到的每个日志文件都包含许多被返回给回调函数的中断和路由更改(即类别)。 **在 iPhone 上,我根本看不到这样的东西。日志仅显示创建记录并记录到指定文件。没有中断,没有路线变化,没有废话。

以下是相关日志:

Jul 10 07:15:21 iPad mediaserverd[15502] <Error>: [07:15:21.464 <0x1207000>] AudioSessionSetClientPlayState: Error adding running client - session not active
Sat Jul 10 07:15:21 iPad mediaserverd[15502] <Error>: [07:15:21.464 <AudioQueueServer>] AudioQueue: Error '!act' from AudioSessionSetClientPlayState(15642)

我已经删除了两个回调函数,仅记录中断和路由更改的发生(有原因)。所以我不会费心发布代码,因为它实际上什么也没做。不过,在一次尝试开始在 iPad 上录制时,我多次看到这些日志。

我几乎已经阅读了在 Apple Dev 论坛和 *** 中可以找到的所有帖子,但似乎无法在 Apple Docs 中找到有相同问题的人或任何解释 iPad 行为差异的相关注释。 --注意:iPad 确实显示了其他一些已修复的缺陷行为,例如永不结束的不匹配的 Begin Interruption 调用(因此我从未停用会话)。

我从未收到任何日志,表明来自 AudioQueue 或 AudioSession 代码的任何失败的初始化或激活调用。当我尝试开始录制时,它只是失败了。 --我什至试图强制 AudioSessionSetActive(true);在每次尝试使用音响系统之前调用,我仍然收到这些错误。

以下是初始化调用的相关代码:

//Initialize the Sound System
    OSStatus error = AudioSessionInitialize(NULL, NULL, interruptionListener, self);
    if (error) printf("ERROR INITIALIZING AUDIO SESSION! %d\n", (int)error); 
    else 
        //must set the session active first according to devs talking about some defect....         
        error = AudioSessionSetActive(true); 
        if (error) NSLog(@"AudioSessionSetActive (true) failed");

        UInt32 category = kAudioSessionCategory_PlayAndRecord;  
        error = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category);
        if (error) printf("couldn't set audio category!\n");

        error = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, propListener, self);
        if (error) printf("ERROR ADDING AUDIO SESSION PROP LISTENER! %d\n", (int)error);

        //Force mixing!
        UInt32 allowMixing = true;
        error = AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof (allowMixing), &allowMixing );
        if (error) printf("ERROR ENABLING MIXING PROPS! %d\n", (int)error);

        UInt32 inputAvailable = 0;
        UInt32 size = sizeof(inputAvailable);           
        // we do not want to allow recording if input is not available
        error = AudioSessionGetProperty(kAudioSessionProperty_AudioInputAvailable, &size, &inputAvailable);
        if (error) printf("ERROR GETTING INPUT AVAILABILITY! %d\n", (int)error);
        isInputAvailable = (inputAvailable) ? YES : NO;


        //iPad doesn't require the routing changes, branched to help isolate iPad behavioral issues
        if(! [Utils GetMainVC].usingiPad)
            //redirect to speaker?  //this only resets on a category change!
            UInt32 doChangeDefaultRoute = 1;        
            error = AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, sizeof (doChangeDefaultRoute), &doChangeDefaultRoute);
            if (error) printf("ERROR CHANGING DEFAULT ROUTE PROPS! %d\n", (int)error);

            //this resets with interruption and/or route changes
            UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker; 
            error = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute,sizeof (audioRouteOverride),&audioRouteOverride);
            if (error) printf("ERROR SPEAKER ROUTE PROPS! %d\n", (int)error);
        

        // we also need to listen to see if input availability changes
        error = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioInputAvailable, propListener, self);
        if (error) printf("ERROR ADDING AUDIO SESSION PROP LISTENER! %d\n", (int)error);

        error = AudioSessionSetActive(true); 
        if (error) NSLog(@"AudioSessionSetActive (true) failed");
    

    // Allocate our singleton instance for the recorder & player object
    myRecorder = new AQRecorder();
    myPlayer = new AQPlayer();

稍后在视频的 loadstate 回调中,我只是尝试开始录制到预定的文件路径:

myRecorder->StartRecord((CFStringRef)myPathStr);

而且录音完全失败。

感谢您的时间和帮助。

【问题讨论】:

【参考方案1】:

原来这是一个奇怪的问题。

1) 仅使用录音和回放,代码在 iPad 上完美运行。

2) 添加电影播放,不要调用任何路由更改,并且在 iPad 上一切正常。

Movie Player 播放的存在以某种方式足以以某种方式更改 AudioSession,从而强制任何路由更改(例如使用设备扬声器而不是耳机)导致 AudioSession 变为非活动状态。

【讨论】:

以上是关于SpeakHere 的 AudioQueue 代码在 iPad 上失败的主要内容,如果未能解决你的问题,请参考以下文章

iPhone dev - AudioQueue 服务在后台录制

计算音频功率峰值 iOS

音频队列 - 没有用于调用“AudioSessionInitialize”的匹配函数

修改 SpeakHere 以录制设备上播放的音频

使用 AudioQueue 播放断断续续的音频

如何使用 Speakhere 示例中的 AQRecorder