Аудио система iOS. Начать и остановить или просто начать?

У меня есть приложение, в котором аудиозапись является основной и самой важной частью. Однако пользователь может переключиться на контроллер табличного представления, где отображаются все записи и запись не производится.

Вопрос в том, какой подход лучше: "запустить и остановить аудио систему или просто запустить ее". Может показаться очевидным, что первый вариант является более правильным, например, "выделять, когда вам это нужно, освобождать, когда он используется". Я покажу свои мысли по этому вопросу, и я надеюсь найти одобрение или неодобрение с аргументами среди опытных людей.

Когда я построил AudioController.m в первый раз, я реализовал методы, чтобы открыть / закрыть аудио-сессию и запустить / остановить аудиоустройство. Я хотел остановить аудио систему, когда запись не активна. Я использовал следующий код:

- (BOOL)startAudioSystem {

    // open audio session
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    NSError *err = nil;
    if (![audioSession setActive:YES error:&err] ) {
        NSLog(@"Couldn't activate audio session: %@", err);
    }
    // start audio unit
    OSStatus status;
    status = AudioOutputUnitStart([self audioUnit]);
    BOOL noErrors = err == nil && status == noErr;    
    return noErrors;
}

а также

- (BOOL)stopAudioSystem {
    // stop audio unit
    BOOL result;
    result = AudioOutputUnitStop([self audioUnit]) == noErr;
    HANDLE_RESULT(result);
    // close audio session
    NSError *err;
    HANDLE_RESULT([[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&err]);
    HANDLE_ERROR(err);
    BOOL noErrors = err == nil && result;
    return noErrors;
}

Я нашел такой подход проблематичным по следующим причинам:

  1. Аудиосистема запускается с задержкой. Это означает, что recording_callback() не вызывается в течение некоторого времени. Я подозреваю, что это AudioOutputUnitStart, который отвечает за это. Я попытался закомментировать строку с этим вызовом функции и переместить ее в инициализацию. задержка прошла
  2. Если пользователь выполняет переключение между просмотром записи и просмотром таблицы очень-очень быстро (запуск и остановка аудиосистемы тоже очень быстрая), это вызывает гибель медиа-службы (я знаю, что наблюдение AVAudioSessionMediaServicesWereResetNotification может помочь здесь, но это не главное).

Чтобы решить эти проблемы, я изменил AudioController.m с другим подходом, который мне удалось обнаружить: запускать аудиосистему, когда приложение становится активным, и не останавливать ее до завершения работы приложения. В этом случае есть также несколько проблем:

  1. использование процессора
  2. Если для категории аудио задана только запись, то при просмотре пользователем контроллера табличного представления невозможно воспроизвести другой звук.

Первый, что удивительно, не имеет большого значения, если отменить любой вид обработки в recording_callback(), например так:

    static OSStatus recordingCallback(void *inRefCon,
                                  AudioUnitRenderActionFlags *ioActionFlags,
                                  const AudioTimeStamp *inTimeStamp,
                                  UInt32 inBusNumber,
                                  UInt32 inNumberFrames,
                                  AudioBufferList *ioData) {

    AudioController *input = (__bridge AudioController*)inRefCon;

    if(!input->shouldPerformProcessing)
        return noErr;

    // processing
    // ...
    //

    return noErr;
}

При этом использование ЦП равно 0% на реальном устройстве, когда запись не требуется и другие действия не выполняются.

И вторая проблема может быть решена путем переключения категории аудио на RecordAndPlay и включения микширования или просто игнорирования проблемы. Например, в моем случае приложение требует, чтобы мини-джек использовался внешним устройством, поэтому нельзя использовать наушники параллельно.

Несмотря на все это, первый подход мне ближе, так как мне нравится закрывать / очищать каждый поток / ресурс, когда он больше не нужен. И я хочу быть уверен, что действительно нет другого выбора, кроме как просто запустить аудиосистему. Пожалуйста, убедитесь, что я не единственный, кто пришел к этому решению, и оно правильное.

1 ответ

Решение

Ключ к решению этой проблемы заключается в том, что аудиосистема фактически работает в другом потоке (в реальном времени). И вы не можете реально остановить и освободить что-то, выполняющееся в другом потоке, именно тогда, когда вы (или основной поток пользовательского интерфейса приложения) "не нуждаетесь в этом", но вынуждены откладывать, чтобы позволить другому потоку понять, что он должен сделать что-то, а затем закончить и очистить себя. Это может потенциально занять до многих сотен миллисекунд для аудио.

Учитывая это, стратегия 2 (только начало) безопаснее и реалистичнее.

Или, возможно, установите задержку в много-много секунд неиспользования перед попыткой остановить звук, и, возможно, еще одну короткую задержку после этого перед попыткой любого перезапуска.

Другие вопросы по тегам