Аудио система 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;
}
Я нашел такой подход проблематичным по следующим причинам:
- Аудиосистема запускается с задержкой. Это означает, что recording_callback() не вызывается в течение некоторого времени. Я подозреваю, что это AudioOutputUnitStart, который отвечает за это. Я попытался закомментировать строку с этим вызовом функции и переместить ее в инициализацию. задержка прошла
- Если пользователь выполняет переключение между просмотром записи и просмотром таблицы очень-очень быстро (запуск и остановка аудиосистемы тоже очень быстрая), это вызывает гибель медиа-службы (я знаю, что наблюдение AVAudioSessionMediaServicesWereResetNotification может помочь здесь, но это не главное).
Чтобы решить эти проблемы, я изменил AudioController.m с другим подходом, который мне удалось обнаружить: запускать аудиосистему, когда приложение становится активным, и не останавливать ее до завершения работы приложения. В этом случае есть также несколько проблем:
- использование процессора
- Если для категории аудио задана только запись, то при просмотре пользователем контроллера табличного представления невозможно воспроизвести другой звук.
Первый, что удивительно, не имеет большого значения, если отменить любой вид обработки в 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 (только начало) безопаснее и реалистичнее.
Или, возможно, установите задержку в много-много секунд неиспользования перед попыткой остановить звук, и, возможно, еще одну короткую задержку после этого перед попыткой любого перезапуска.