edimuj / cordova-plugin-audioinput

This iOS/Android Cordova/PhoneGap plugin enables audio capture from the device microphone, by in near real-time forwarding audio to the web layer of your application. A typical usage scenario for this plugin would be to use the captured audio as source for a web audio node chain, where it then can be analyzed, manipulated and/or played.

Home Page:https://github.com/edimuj/app-audioinput-demo

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Crashing in iOS after returning from background

KrisHedges opened this issue · comments

I'm getting a crash when my app returns from background. I'm able to record and stop recording all day long when the app first comes up.. but hitting record after coming back from background the app crashes.

It throws EXC_BAD_ACCESS(code=1, adress=0xWtver123) at line 89 in
cordova-plugin-audioinput/src/ios/CDVAudioInputCapture.m

Here:
https://github.com/edimuj/cordova-plugin-audioinput/blob/master/src/ios/CDVAudioInputCapture.m#L89

I don't ObjC from a hole in the ground so any help would be much appreciated. Love the plugin it does exactly what I needed. I was also a little confused as to how to connect it up to my WebAudio lib. I'm using Recorder.js (https://github.com/mattdiamond/Recorderjs) but this is how I got it to work... perhaps there is a better way of connecting the 2 and I'm not sure if it's related?

Any advice would be much appreciated.

This is what my code essentially looks like minus some DOM stuff.

startRecording: function(){
        // Start audioinput streaming to WebAudio        
        audioinput.start({ streamToWebAudio: true});

        if(this.recorder){
            this.recorder.clear();
            this.recorder.record();
        } else {
            // Here I use audioinput._micGainNode as the source for a new recorder.js instance
            this.recorder = new Recorder(audioinput._micGainNode, {numChannels:1});
            this.recorder.record();
        }
},

stopRecording: function(){
        this.recorder.stop();
        // I stop audioinput after I stop recording
        audioinput.stop();
        this.recorder.exportWAV(this.setRecordedFile);
}
commented

Hi, Kris! Thanks for registering this issue. Sadly I haven't had the time to analyze the problem yet, but my guess is that iOS deactivates the AVAudioSession when the app is pushed to the background and when coming back to the foreground the AVAudioSession isn't activated again which leads to the crash.

I have only used the plugin using the backgroundmode for audio in iOS:


audio

Which means that iOS will allow the app to continue playing/recording when in the background. The Apple reviewers will only allow this if recording is a critical part of the app functionality.

Of course, this is just a workaround and I will try to implement a fix where the foreground event is handled correctly.

commented

And for the other part of your issue. It looks like your code is handling things correctly.

The micGainNode is actually private (which the prefix "" indicates), so it is a bit risky to use it, since the internal code of the plugin may change. But since the current version of the plugin doesn't enable you to get hold of the node in any other way, it is still ok to do it that way. Will add support for getting the mic node through a function in the next version of the plugin.

If you want to, you can also create a gain node yourself and the connect the audioinput plugin to your node:

startRecording: function(){
        // Start audioinput streaming to WebAudio        
        audioinput.start({ streamToWebAudio: true});

        if(this.recorder){
            this.recorder.clear();
            this.recorder.record();
        } else {
            this.microphoneGainNode = audioinput.getAudioContext().createGain(); // NEW
            audioinput.connect(this.microphoneGainNode); // NEW
            this.recorder = new Recorder(this.microphoneGainNode, {numChannels:1}); // UPDATED
            this.recorder.record();
        }
},

This would also allow you to adjust the volume of the sound that is being recorded, or you could even use a more advanced audio route chain (using a ChannelSplitterNode instead of a GainNode) to route the sound to both the recorder and an analyser node which can analyse the sound in realtime, to be able to draw some kind of graphical recording indicator (VU meters, frequency spectrum etc).

Thank you so much for this plugin and your extremely well thought responses to my issues. I managed to minimize the crashes by reinitialuzing the plugin after returning from background. It still happens on occasion but is mostly under control in normal usage. I also managed to get my app in the stores. I think by limiting the sound recording to a short time period (20s with a timer) it was allowed to slide... Thank goodness. Anyway thank you for all your help.

Thank you so much for this plugin and your extremely well thought responses to my issues. I managed to minimize the crashes by reinitialuzing the plugin after returning from background. It still happens on occasion but is mostly under control in normal usage. I also managed to get my app in the stores. I think by limiting the sound recording to a short time period (20s with a timer) it was allowed to slide... Thank goodness. Anyway thank you for all your help.

@edimuj I have the same problem: after returning from background and starting the recording (which starts the audioinput recording) the app crashes. This still happens when I enable the audio background mode (in XCode 8.0: capabilities -> background modes -> Audio, Airplay and Picture in Picture).

@KrisHedges You mentioned you solved the issue by reinitializing the plugin after returning from background. How exactly did you implement this?

@KrisHedges As far as I understand (after some logging) the plugin will actually completely reinitialize the AVAudioSession when audioinput.start is called, right?

@edimuj Is there a reason why you call [self.audioReceiver start]; when app returns to the foreground? This immediately seems to kick of the HandleInputBuffer callback (even when not having started the recording)

Yes this is what I saw as well... In my original approach I was hanging onto the audioinput object thinking it would just be there whenever I needed to tap into it with recorder it was foolish and crashed every time on returning from background but surprisingly worked pretty well otherwise. But clearly that was naive. Calling stop and start every time made it crash less often... But it does still happen just intermittently... Sorry for the lateness on my reply

Thanks for you reply! BTW: I now changed the plugin to not start when it enters the foreground (commented out this line: https://github.com/edimuj/cordova-plugin-audioinput/blob/master/src/ios/CDVAudioInputCapture.m#L165).
I haven't seen any crashes so far (without background audio mode on), but I will test further.

@edimuj There is one other thing that I couldn't fully understand: the number of samples in the handleInoutBuffer is calculated from the buffer: https://github.com/edimuj/cordova-plugin-audioinput/blob/master/src/ios/AudioReceiver.m#L36; but the value actually seems to be already available: inNumPackets. I now use that as the return value for dataLength. No idea whether this could have caused a crash through.

commented

That is interesting, I thought that recording was stopped if the [self.audioReceiver start]; isn't executed when the app returns from background, since it gets paused when the app is pushed to background. Can you please confirm if this is the case? I would guess that your crash issue has something to do with this though, but I don't know why. The length value probably doesn't have anything to do with it, but I'll look into it when I have some time over.

commented

Doing some housekeeping: Since there hasn't been any action regarding this issue for a time now, I'm closing it.