jsierles / react-native-audio

Audio recorder library for React Native
MIT License
1.16k stars 539 forks source link

iOS stops playing audio after stop recording #85

Closed benlinton closed 7 years ago

benlinton commented 8 years ago

When I stop a recording on my iPad Air device, I get the error below:

ERROR: [0x16e247000] AVAudioSession.mm:692: -[AVAudioSession setActive:withOptions:error:]: Deactivating an audio session that has running I/O. All I/O should be stopped or paused prior to deactivating the audio session.

And after that all audio played is silent.

When I'm in the iOS simulator I have no problems, recordings stop as expected and everything seems to work perfectly.

I'm playing sound effects within an React Native iOS WebView (using Howler.js v2.0.0) immediately before and after recording, so maybe that's part of the problem?

jsierles commented 8 years ago

I'll take a look at this today.

benlinton commented 8 years ago

I did some more testing. Here's what I found so far:

This works

It makes no sense to me why Howler.js is part of the problem since it's a javascript library being ran inside a UIWebView. Howler.js is supposed to utilize either HTML5 audio or the Audio Web API depending on the browser/device it's running on. Does the UIWebview then use AVAudioSession under the covers somehow? Or maybe Howler.js uses additional resources which exposes a race condition (my first guess).

This does not help

benlinton commented 8 years ago

Could part of the problem be that the AudioRecorderManager.m doesn't set a sessionCategory when it creates _audioPlayer so the session defaults to AVAudioSessionCategorySoloAmbient instead of using AVAudioSessionCategoryPlayAndRecord or AVAudioSessionCategoryPlayback?

AudioRecorderManager.m

_audioPlayer = [[AVAudioPlayer alloc]
  initWithContentsOfURL:_audioRecorder.url
  error:&error];

Update: I set the audio player session to use an AVAudioSessionCategoryPlayback session category. And created both sessions with options withOptions:AVAudioSessionCategoryOptionMixWithOthers. It did not help.

jsierles commented 8 years ago

I looked at this a bit, but I'm not really sure what's causing it. I'm not using this plugin now and don't have a lot of time to work on it. I'd like to focus on releasing android support, then come back around to this issue. Let me know meanwhile if you find a solution!

benlinton commented 8 years ago

Success!

It's gross but adding a delay in between stopping and deactivating the session fixes the problem.

Original code, not working

RCT_EXPORT_METHOD(stopRecording)
{
  [_audioRecorder stop];
  [_recordSession setActive:NO error:nil];
  _prevProgressUpdateTime = nil;
}

Newly delayed code, it works!

RCT_EXPORT_METHOD(stopRecording)
{
    [_audioRecorder stop];
    _prevProgressUpdateTime = nil;
    [self performSelector:@selector(stopSession) withObject:nil afterDelay:1000];
}

-(void)stopSession
{
  [_recordSession setActive:NO error:nil];
}

It seems that stop happens asynchronously. And deactivating the audio session before stop is finished can result in a race condition issue on slower devices.

I tried to deactivate the audio session after the audioRecorderDidFinishRecording event so we don't have to use a gross delay. But recorder stop seems to still be doing it's thing even after the audioRecorderDidFinishRecording event has been triggered.

Possibly, it's is still closing/encoding the recorded audio file? Unfortunately, the developer docs aren't much help in this regard.

We may need to file a bug to Apple in order to eventually remove the delay.

jsierles commented 7 years ago

Is this still happening?

johnbryant commented 7 years ago

I have the same problem too. After I stop the record, I can not play all the media file (video and audio)

jsierles commented 7 years ago

@benlinton if you're still seeing this, can you submit a PR with this change? Ideally the delay could be configurable.

benlinton commented 7 years ago

I imagine so, but I haven't been working with this library for the last few months so I can't say for sure.

VansonLeung commented 7 years ago

Just encountered this problem when developing a chat app

HermitCarb commented 7 years ago

I have the same problem too. I read library react-native-audio and react-native-sound native code. I find this:

So, After added this code in my javascript file. It worked :

if (Platform.OS == 'ios') {SoundPlayer.enable(true);}    // <---- this line
this.soundPlayer = new Sound(filename, SoundPlayer.DOCUMENT, (error) => {
  ...
}

Hope to be helpful.

johnbryant commented 7 years ago

@HermitCarb it worked for me, thank u~

jsierles commented 7 years ago

Looks like this is solved, so closing.