Skip to content

fix: Android recorderController.stop() never completes#486

Open
rubenvde wants to merge 2 commits into
SimformSolutionsPvtLtd:mainfrom
rubenvde:main
Open

fix: Android recorderController.stop() never completes#486
rubenvde wants to merge 2 commits into
SimformSolutionsPvtLtd:mainfrom
rubenvde:main

Conversation

@rubenvde
Copy link
Copy Markdown

@rubenvde rubenvde commented Apr 3, 2026

Description

Fixes a hang where await recorderController.stop() on Android never completes, especially for recordings >30 seconds. This was introduced in the 2.0.x rewrite that replaced MediaRecorder with AudioRecord +
MediaCodec.

Root causes fixed:

  1. Deadlock in stopEncoder()stopEncoder() is called from onOutputBufferAvailable on the handler thread, but it called handlerThread.join(), waiting for itself to finish. Removed the join() and
    use quitSafely() only.

  2. Race condition in signalToStop() — EOS buffer queuing now runs on the handler thread via handler.post {}, avoiding races with onInputBufferAvailable that caused "MediaCodec discarded an unknown
    buffer" errors.

  3. release() called before async completion — For the non-WAV path, release() was called synchronously after signalToStop(), before the completion callback fired. Moved release() into the completion
    callback.

  4. result.success() called from wrong threadsendRecordingResult() now posts to the main looper, since Flutter's MethodChannel.Result must be called on the platform thread.

  5. completionCallback swallowed on error — Moved completionCallback?.invoke() to a finally block in stopEncoder() so the Flutter result is always resolved.

  6. Missing handler in setCallback()mediaCodec.setCallback() now passes the handler so callbacks run on the intended HandlerThread.

Checklist

  • The title of my PR starts with a [Conventional Commit] prefix (fix:, feat:, docs: etc).
  • I have followed the [Contributor Guide] when preparing my PR.
  • I have updated/added tests for ALL new/updated/fixed functionality.
  • I have updated/added relevant documentation in docs and added dartdoc comments with ///.
  • I have updated/added relevant examples in examples or docs.

There's no test infrastructure in the project and the bug is a native Android threading issue that's difficult to unit test from Dart

Breaking Change?

  • Yes, this PR is a breaking change.
  • No, this PR is not a breaking change.

Related Issues

#470

rubenvde added 2 commits April 3, 2026 10:19
Prevent encoder hang and ensure proper cleanup when stopping recordings. Add queueEosBuffer helper and proactively queue EOS in signalToStop if an input buffer is available, run MediaCodec callback on the handler, move completionCallback to finally, call release() after sending results or on error, and post result.success on the main looper for thread-safety.
Post the EOS check to the handler thread so EOS queuing runs on the same thread as onInputBufferAvailable, preventing race conditions when an input buffer is already available. Add an early return if the encoder is stopped. Move handlerThread.quitSafely() into the finally block and remove join() to avoid deadlocking when stopEncoder is invoked from callbacks; ensure completionCallback is invoked before quitting the thread.
@yuanhoujun
Copy link
Copy Markdown
Contributor

Same issue!

_playerControler?.dispose();

@DorraY
Copy link
Copy Markdown

DorraY commented Apr 13, 2026

This does not work for me it actually crashes the app when I call stop().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants