将AudioSession类别从SoloAmbient更改为PlayAndRecord后,为什么无法从RemoteIOUnit录制?

时间:2014-06-30 20:03:04

标签: ios audio core-audio avaudiosession

我的应用程序具有音频播放和录制功能,我想在用户启动录制时仅将音频会话的类别设置为PlayAndRecord,因此静音开关等将标准音频播放静音。

我遇到了一个问题,在我将音频会话类别更改为AudioUnitRender后,我errParam录制音频输入的调用失败了PlayAndRecord( - 50)。如果我使用PlayAndRecord类别启动我的应用,则录制工作正常。

@implementation MyAudioSession
- (instancetype)init {
  NSError *error = nil;
  AVAudioSession *session = [AVAudioSession sharedInstance];
  [session setCategory:AVAudioSessionCategorySoloAmbient];
  [session setActive:YES error:&error];
}

- (void)enableRecording {
  void (^setCategory)(void) = ^{
    NSError *error;
    AVAudioSession *session = [AVAudioSession sharedInstance];
    [session setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
  };
  // Do I need to set the category from the main thread?
  if ([NSThread isMainThread]) {
    setCategory();
  } else {
    dispatch_sync(dispatch_get_main_queue(), ^{
      setCategory();
    });
  }
}
@end

@interface MyRecorder {
  AudioUnit ioUnit_;
  AudioBufferList *tmpRecordListPtr_
@end

@implementation MyRecorder
- (instancetype)init {
  // Sets up AUGraph with just a RemoteIOUnit node, recording enabled, callback, etc.
  // Set up audio buffers
  tmpRecordListPtr_ = malloc(sizeof(AudioBufferList) + 64 * sizeof(AudioBuffer));
}

- (OSStatus)doRecordCallback:(AudioUnitRenderActionFlags *)ioActionFlags
                   timeStamp:(AudioTimeStamp *)inTimeStamp
                   busNumber:(UInt32)busNumber
                   numFrames:(UInt32)numFrames
                   bufferOut:(AudioBufferList *)ioData {
  // Set up buffers... All this works fine if I initialize the audio session to
  // PlayAndRecord in -[MyAudioSession init]
  OSStatus status = AudioUnitRender(ioUnit_, ioActionFlags, inTimeStamp, busNumber,
                                    numFrames, tmpRecordListPtr_);
  // This fails with errParam, but only if I start my app in SoloAmbient and then
  // later change it to PlayAndRecord
}
@end

OSStatus MyRecorderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
                            AudioTimeStamp *inTimestamp, UInt32 inBusNumber,
                            UInt32 inNumberFrames, AudioBufferList *ioData) {
  MyRecorder *recorder = (MyRecorder *)inRefCon;
  return [recorder doRecordCallback:ioActionFlags
                          timeStamp:inTimestamp
                          busNumber:inBusNumber
                          numFrames:inNumberFrames
                          bufferOut:ioData];
}

我正在测试运行iOS 7.1.2的iPod touch(第5代)。

还有其他人遇到过此问题吗?我可以发布任何修复建议或更多信息吗?

编辑:对象生命周期类似于:

- (void)startRecording {
  [mySession enableRecording];
  [myRecorder release];
  myRecorder = [[MyRecorder alloc] init];
  [myRecorder start]; // starts the AUGraph
}

2 个答案:

答案 0 :(得分:0)

不看你的代码就很难评论。但是我在我的应用程序中做了类似的事情,我发现只有在音频会话处于非活动状态时才要注意哪些音频会话设置可以更改是很重要的。

// Get the app's audioSession singleton object
AVAudioSession* session = [AVAudioSession sharedInstance];

//error handling
NSError* audioSessionError = nil;

SDR_DEBUGPRINT(("Setting session not active!\n"));
[session setActive:NO error:&audioSessionError]; // shut down the audio session if it is active

例如,在更改会话类别之前,将setActive设置为“NO”非常重要。如果不这样做,可能会在配置会话时发生回调。

观察生命周期流程,我试图在设置音频会话进行录制之前查看停止AUGraph的位置。我用来停止AUGraph的代码如下。我在尝试重新配置音频会话之前先调用它。

- (void)stopAUGraph {
if(mAudioGraph != nil)  {
    Boolean isRunning = FALSE;

    OSStatus result = AUGraphIsRunning(mAudioGraph, &isRunning);
    if(result) {
        DEBUGPRINT(("AUGraphIsRunning result %d %08X %4.4s\n", (int)result, (int)result, (char*)&result));
        return;
    }

    if(isRunning) {
        result = AUGraphStop(mAudioGraph);

        if(result) {
            DEBUGPRINT(("AUGraphStop result %d %08X %4.4s\n", (int)result, (int)result, (char*)&result));
            return;
        } else {
            DEBUGPRINT(("mAudioGraph has been stopped!\n"));
        }
    } else {
        DEBUGPRINT(("mAudioGraph is already stopped!\n"));
    }
}

答案 1 :(得分:0)

在停用和/或更改音频会话类型之前,您需要确保RemoteIO音频单元(音频图形)已停止。然后(重新)在设置新会话类型之后和(重新)启动图表之前初始化RemoteIO音频单元,因为新会话类型或选项可能会更改某些允许的设置。此外,在任何图形(重新)开始之前检查所有先前的音频单元和音频会话调用错误代码是有帮助的。