AVAudioRecorder не будет записывать, когда фильм был ранее записан и воспроизведен

В моем приложении iPhone используется "AVAudioRecorder" для записи голоса. Он также использует "UIImagePickerController" для записи фильмов и "MPMoviePlayerController" для воспроизведения фильмов.

Все работает нормально, пока я не буду делать все три строки подряд:

  • Записать фильм с помощью UIImagePickerController
  • Воспроизведение записанного фильма с помощью MPMoviePlayerController
  • Попробуйте сделать запись голоса с помощью AVAudioRecorder

Когда я вызываю метод записи "AVAudioRecorder" на шаге 3, он возвращает NO, указывающий на сбой, но не дает никаких подсказок о том, почему (приходят на Apple!). Метод передачи данных AVAudioRecorder audioRecorderEncodeErrorDidOccur никогда не вызывается и я не получаю никаких других ошибок при настройке рекордер.

Мое первое предположение заключалось в том, что запись/воспроизведение видео изменяли общий экземпляр "AVAudioSession" таким образом, что он не позволял звукозаписывающему устройству работать. Тем не менее, я вручную устанавливаю свойство категории AVAudioSession в "AVAudioSessionCategoryRecord", и я делаю сессию аудио активнее, прежде чем пытаться записать.

Здесь мой метод создания рекордера:

- (void)createAudioRecorder
{
 NSError *error = nil;
 AVAudioSession *audioSession = [AVAudioSession sharedInstance];
 [audioSession setCategory:AVAudioSessionCategoryRecord error:&error];
 if (error)
  ...
 [audioSession setActive:YES error:&error];
 if (error)
  ...

 NSMutableDictionary *settings = [[NSMutableDictionary alloc] init];

 // General Audio Format Settings 
 [settings setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
 [settings setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey]; 
 [settings setValue:[NSNumber numberWithInt:1] forKey:AVNumberOfChannelsKey];

 // Encoder Settings 
 [settings setValue:[NSNumber numberWithInt:AVAudioQualityMin] forKey:AVEncoderAudioQualityKey]; 
 [settings setValue:[NSNumber numberWithInt:96] forKey:AVEncoderBitRateKey]; 
 [settings setValue:[NSNumber numberWithInt:16] forKey:AVEncoderBitDepthHintKey];

 // Write the audio to a temporary file
 NSURL *tempURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"Recording.m4a"]];

 audioRecorder = [[AVAudioRecorder alloc] initWithURL:tempURL settings:settings error:&error];
 if (error)
  ...

 audioRecorder.delegate = self;
 if ([audioRecorder prepareToRecord] == NO)
  NSLog(@"Recorder fails to prepare!");

 [settings release];
}

И вот мой способ начать запись:

- (void)startRecording
{
 if (!audioRecorder)
  [self createAudioRecorder];

 NSError *error = nil;
 [[AVAudioSession sharedInstance] setActive:YES error:&error];
 if (error)
  ...

 BOOL recording = [audioRecorder record];
 if (!recording)
  NSLog(@"Recording won't start!");
}

Кто-нибудь сталкивался с этой проблемой раньше?

Ответ 1

У меня была такая же проблема. Прежде чем я исправил проблему, мой код записи/воспроизведения выглядел следующим образом:

Начать запись

- (BOOL) startRecording {   
@try {
    NSDictionary *recordSetting = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey, [NSNumber numberWithFloat: 44100.0], AVSampleRateKey, [NSNumber numberWithInt: 1], AVNumberOfChannelsKey,  [NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey, nil];

        NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
        NSString *soundFilePath = [documentsPath stringByAppendingPathComponent:@"recording.caf"];

        if(audioRecorder != nil) {
            [audioRecorder stop];
            [audioRecorder release];
            audioRecorder = nil;
        }

        NSError *err = nil;
        audioRecorder = [[AVAudioRecorder alloc] initWithURL:soundFileURL settings:recordSetting error:&err];
        [soundFileURL release];
        [recordSetting release];
        if(!audioRecorder || err){
            NSLog(@"recorder initWithURL: %@ %d %@", [err domain], [err code], [[err userInfo] description]);
            return NO;
        }

        [audioRecorder peakPowerForChannel:8];
        [audioRecorder updateMeters];
        audioRecorder.meteringEnabled = YES;
        [audioRecorder record];
    }
    @catch (NSException * e) {
        return NO;
    }

    recording = YES;
    return YES;
}

Остановить запись

- (BOOL) stopRecording {
    @try {
        [audioRecorder stop];
        [audioRecorder release];
        audioRecorder = nil;

        recording = NO;
    }
    @catch (NSException * e) {
        return NO;
    }

    return YES;
}

Начать воспроизведение

- (BOOL) startPlaying {
    @try {
        NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
        NSString *soundFilePath = [documentsPath stringByAppendingPathComponent:@"recording.caf"];      NSURL * soundFileURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];
        NSError *err = nil;

        if (audioPlayer) {
            [audioPlayer release];
            audioPlayer = nil;
        }

        audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error: &err];
        [soundFileURL release];
        if (!audioPlayer || err) {
            NSLog(@"recorder: %@ %d %@", [err domain], [err code], [[err userInfo] description]);
            return NO;
        }

        [audioPlayer prepareToPlay];
        [audioPlayer setDelegate: self];
        [audioPlayer play];

        playing = YES;
    }
    @catch (NSException * e) {
        return NO;
    }

    return YES;
}

Остановить воспроизведение

- (BOOL) stopPlaying {
    @try {
        [audioPlayer stop];
        [audioPlayer release];
        audioPlayer = nil;

        playing = NO;
    }
    @catch (NSException * e) {
        return NO;
    }

    return YES;
}

Я зафиксировал проблему записи после воспроизведения захваченного видео, код выглядит следующим образом:

- (BOOL) startRecording {   
    @try {
        AVAudioSession *session = [AVAudioSession sharedInstance];
        [session setCategory:AVAudioSessionCategoryRecord error:nil];

        // rest of the recording code is the same .
}

- (BOOL) stopRecording {
    @try {
        AVAudioSession *session = [AVAudioSession sharedInstance];
        [session setCategory:AVAudioSessionCategoryPlayback error:nil];

    // rest of the code is the same
}

- (BOOL) startPlaying {
    @try {
        AVAudioSession *session = [AVAudioSession sharedInstance];
        [session setCategory:AVAudioSessionCategoryPlayback error:nil];

    // rest of the code is the same. 
}

- (BOOL) stopPlaying {
    // There is no change in this function
}

Ответ 2

Была та же проблема. Мое решение состояло в том, чтобы остановить фильм перед началом сеанса записи. Мой код был похож на код rmomin.

Ответ 3

У меня такая же проблема. Я использую AVPlayer для воспроизведения композиций (предыдущие записи, которые я использовал AVAudioRecord для). Тем не менее, я обнаружил, что, как только я использовал AVPlayer, я больше не мог использовать AVAudioRecorder. После некоторого поиска я обнаружил, что до тех пор, пока AVPlayer создается в памяти и воспроизводится хотя бы один раз (что обычно происходит сразу после его создания), AVAudioRecorder не будет записывать. Однако, как только AVPlayer будет отменен, AVAudioRecorder затем сможет снова записать. Похоже, что AVPlayer поддерживает какое-то соединение, которое требуется AVAudioRecorder, и оно жадное... оно не отпустит его, пока вы не вытащите его из холодных мертвых рук.

Это решение, которое я нашел. Некоторые люди утверждают, что создание экземпляра AVPlayer занимает слишком много времени, чтобы продолжать ломать и настраивать резервные копии. Однако это не так. Активация AVPlayer на самом деле довольно тривиальна. Так же создается экземпляр AVPlayerItem. Что не является тривиальным, это загрузка AVAsset (или любого из его подклассов). Вы действительно хотите это сделать только один раз. Они должны использовать эту последовательность:

  • Загрузите AVAsset (например, если вы загружаетесь из файла, используйте AVURLAsset напрямую или добавьте его в AVMutableComposition и используйте это) и сохраните ссылку на него. Не позволяйте этому идти, пока вы не закончите с ним. Загрузка - это то, что занимает все время.
  • Как только вы будете готовы к игре: создайте AVPlayerItem с вашим активом, затем AVPlayer с AVPlayerItem и сыграйте его. Не держите ссылку на AVPlayerItem, AVPlayer сохранит ссылку на нее, и вы не сможете повторно использовать ее с другим игроком.
  • Как только это произойдет, немедленно уничтожьте AVPlayer... отпустите его, установите его var в ноль, что бы вы ни делали. **
  • Теперь вы можете записывать. AVPlayer не существует, поэтому AVAudioRecorder может делать все возможное.
  • Когда вы будете готовы снова играть, повторно создайте экземпляр AVPlayerItem с уже загруженным вами ресурсом и AVPlayer. Опять же, это тривиально. Актив уже загружен, поэтому задержки не должно быть.

** Обратите внимание, что уничтожение AVPlayer может занять больше, чем просто отпустить его и установить для var значение nil. Скорее всего, вы также добавили периодического наблюдателя времени, чтобы отслеживать ход воспроизведения. Когда вы это сделаете, вы получите обратно непрозрачный объект, на который вы должны удержать. Если вы не удалите этот элемент из плеера И отпустите его/установите его на ноль, AVPlayer не удалит. Похоже, что Apple создает умышленный цикл сохранения, который вы должны рубить вручную. Поэтому, прежде чем уничтожить AVPlayer, вам нужно (пример):

[_player removeTimeObserver:_playerObserver];
[_playerObserver release]; //Only if you're not using ARC
 _playerObserver = nil;

В качестве дополнительной заметки вы также можете настроить NSNotifications (я использую один, чтобы определить, когда игрок завершил игру)... не забудьте также удалить их.

Ответ 4

У меня была такая же проблема в Monotouch и скорректирована rmomins ответ для Monotouch.

изменено

avrecorder.Record();

to

NSError error;
var avsession = AVAudioSession.SharedInstance();
avsession.SetCategory(AVAudioSession.CategoryRecord,out error);

avrecorder.Record();

Работает как шарм.

Ответ 5

У меня была такая же проблема для записи и воспроизведения. Чтобы устранить проблему, я вспоминаю AVAudioSession в функции "запись" и "воспроизведение".

Это может быть помощь человеку, у которого есть эта проблема на устройстве, а не на симуляторе!

Ответ 6

У меня такая же проблема. Наконец, я узнал, что ARC выпустила мой рекордер. Итак, вы должны объявить рекордер в файле .h, т.е. AVAudioRecord *recorder;. Положите другие вещи на .m, будет работать как обычно.