diff --git a/CHANGELOG.md b/CHANGELOG.md index 3beb9051..3b14c05d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## 2.1.0 (#unreleased) - Feature [#468](https://github.com/SimformSolutionsPvtLtd/audio_waveforms/pull/468) - Add macOS support +- Fixed [#482](https://github.com/SimformSolutionsPvtLtd/audio_waveforms/issues/482) - Prevent Android crash on stop by checking muxer state before stopping ## 2.0.2 diff --git a/android/src/main/kotlin/com/simform/audio_waveforms/AudioRecorder.kt b/android/src/main/kotlin/com/simform/audio_waveforms/AudioRecorder.kt index 24fd73bd..3b74c146 100644 --- a/android/src/main/kotlin/com/simform/audio_waveforms/AudioRecorder.kt +++ b/android/src/main/kotlin/com/simform/audio_waveforms/AudioRecorder.kt @@ -135,9 +135,7 @@ class AudioRecorder : PluginRegistry.RequestPermissionsResultListener { ) wavEncoder?.start(result) } else { - commonEncoder.initCodec(recorderSettings = recorderSettings!!, result = result) { - recordingThread?.join() - } + commonEncoder.initCodec(recorderSettings = recorderSettings!!, result = result) } val buffer = ByteArray(bufferSize!!) recordingThread = Thread { @@ -145,6 +143,14 @@ class AudioRecorder : PluginRegistry.RequestPermissionsResultListener { if (recorderState == RecorderState.Recording) { val read = audioRecord?.read(buffer, 0, buffer.size) ?: 0 + if (read < 0) { + // Negative return is an AudioRecord error (e.g. ERROR_INVALID_OPERATION, + // ERROR_DEAD_OBJECT). Reading again would keep failing, so stop cleanly. + Log.e(LOG_TAG, "AudioRecord.read() error: $read") + recorderState = RecorderState.Stopped + break + } + if (read > 0) { val audioData = buffer.copyOf(read) if (encoder?.encodeForWav == true) { @@ -169,12 +175,15 @@ class AudioRecorder : PluginRegistry.RequestPermissionsResultListener { fun stop(result: Result) { try { + // Stop the read loop first, then unblock and join the recording thread so it + // can never call audioRecord.read() after the AudioRecord is released below. + recorderState = RecorderState.Stopped audioRecord?.stop() + recordingThread?.join() + recordingThread = null totalSamples = 0L - recorderState = RecorderState.Stopped if (encoder?.encodeForWav == true) { wavEncoder?.stop(result) - recordingThread?.join() sendRecordingResult(result) } else { commonEncoder.setOnEncodingCompleted { diff --git a/android/src/main/kotlin/com/simform/audio_waveforms/encoders/CommonEncoder.kt b/android/src/main/kotlin/com/simform/audio_waveforms/encoders/CommonEncoder.kt index 182a7fa8..10bef28f 100644 --- a/android/src/main/kotlin/com/simform/audio_waveforms/encoders/CommonEncoder.kt +++ b/android/src/main/kotlin/com/simform/audio_waveforms/encoders/CommonEncoder.kt @@ -406,7 +406,11 @@ class CommonEncoder { try { mediaCodec.stop() mediaCodec.release() - mediaMuxer?.stop() + // Only stop the muxer if it was actually started; stopping an unstarted muxer + // throws and triggers the native MPEG4Writer "track not started" crash. + if (isMuxerStarted) { + mediaMuxer?.stop() + } mediaMuxer?.release() outputStream.close() handlerThread.quitSafely()