Skip to content

fix(reverse): software-codec fallback when a HW codec rejects start (47233ad7)#78

Merged
stozo04 merged 1 commit into
mainfrom
fix/reverse-codec-software-fallback
Jun 22, 2026
Merged

fix(reverse): software-codec fallback when a HW codec rejects start (47233ad7)#78
stozo04 merged 1 commit into
mainfrom
fix/reverse-codec-software-fallback

Conversation

@stozo04

@stozo04 stozo04 commented Jun 22, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes Crashlytics 47233ad7Non-fatal he: Render worker reported failure, repeating on an LG LM-X540 (Android 10). The real cause was in the breadcrumbs: reverse_preview_failure: IllegalArgumentException: start failed.

"start failed" is MediaCodec.native_start()'s JNI message; a BAD_VALUE native status surfaces it as IllegalArgumentException. On this low-end non-Samsung device the hardware AVC codec rejects start(). The hand-rolled reverse pipeline (which, unlike the Media3 Transformer path, doesn't get setEnableDecoderFallback) only retried Samsung lifecycle churn / "surface released", so this failure escaped on the first attempt with no software fallback — failing both the editor reverse preview and the boomerang save.

What changed

  • Detect codec-init failuresisMediaCodecInitializationFailure + shouldRetryReverseWithSoftwareCodec (MediaCodecLifecycle.kt), distinct from benign teardown.
  • Recover on any deviceVideoReverser retries once on the software encoder AND decoder after an init failure (threaded preferSoftwareDecoder through the pipeline; the platform-default decoder path previously had no fallback). Samsung / zero-frame paths unchanged.
  • DiagnosabilityBoomerangRenderWorker now reports the real render cause to Crashlytics in its IOException/ExportException/RuntimeException branches; previously the actual exception lived only in on-device logcat and never reached Firebase.

Tests

  • MediaCodecLifecycleTest — predicate + retry-decision coverage, incl. the exact start failed signature and a regression guard vs. the old contention path.
  • VideoReverserTest.reverse_recoversFromCodecStartFailure_viaSoftwareFallback — on-device fault-injection via a new VideoReverser.attemptHook test seam: injects start failed on attempt 0, asserts the reverse recovers on the software encoder+decoder and produces a valid, playable clip. ✅ Passed on Pixel 6 (tests=1 failures=0 errors=0).
  • Unblocked the test source set (pre-existing breakage unrelated to the bug): DeviceMediaHintsTest / SamsungReversePreviewRegressionTest referenced removed *_MS constants; OpenLoopViewModelTest asserted a RAW the render flow now intentionally deletes after a successful save.

Verification (Definition of Done)

  • assembleDebug + assembleReleaseBUILD SUCCESSFUL, exit 0
  • lintDebug 0 errors (13 warnings, none new); lintVitalRelease clean
  • ✅ Unit tests: 0 failures
  • ✅ Instrumented recovery test: passed on Pixel 6
  • ✅ 3-device E2E happy-path sweep (Pixel 6 / 8 / 10 Pro Fold) — record → edit all 4 tabs → reverse → save → share → gallery → playback, 0 crashes / 0 timeouts (docs/e2e/2026-06-21_150054.md)

Honest gap

The fault-injection test exercises the recovery branch on a real device, but the literal LG LM-X540 hardware could not be tested — Firebase Test Lab no longer carries any LG device (only a physical phone would give 100% certainty on that exact chip).

Also included

  • Production zero-error rule recorded in DEFINITION_OF_DONE.md + CLAUDE.md (no PR on a red baseline, pre-existing failures included).

Manual QA checklist

  • On a physical low-end / older device, save a reverse-containing boomerang and confirm it renders + plays.
  • Confirm share-sheet target still receives the file (benign chooser-preview FileProvider permission-denial warnings noted in the E2E report).

Made with Cursor

…47233ad7)

On a non-Samsung low-end device (LG LM-X540, Android 10) the hardware AVC
codec rejects start with `IllegalArgumentException: start failed`. The reverse
pipeline only retried Samsung lifecycle churn / "surface released", so this
failure escaped on the first attempt with no fallback — both the editor reverse
preview and the boomerang save failed.

Fix:
- Add isMediaCodecInitializationFailure + shouldRetryReverseWithSoftwareCodec
  (MediaCodecLifecycle) to detect a codec that fails to initialize, distinct
  from benign teardown.
- VideoReverser now retries once on the software encoder AND decoder on ANY
  manufacturer after an init failure (threaded preferSoftwareDecoder through the
  pipeline; the platform-default decoder path previously had no fallback).
- BoomerangRenderWorker now reports the real render cause to Crashlytics in its
  IOException/ExportException/RuntimeException branches — previously the actual
  codec exception lived only in logcat ("details in BoomerangRenderWorker log")
  and never reached Firebase.

Tests:
- MediaCodecLifecycleTest: predicate + retry-decision coverage incl. the exact
  "start failed" signature and a regression guard vs. the old contention path.
- VideoReverserTest.reverse_recoversFromCodecStartFailure_viaSoftwareFallback:
  on-device fault-injection (new VideoReverser.attemptHook test seam) injects
  "start failed" on attempt 0 and asserts the reverse recovers via the software
  encoder+decoder and produces a valid, playable clip. Passed on Pixel 6.
- Fix pre-existing test-source-set breakage that blocked the whole suite:
  DeviceMediaHintsTest / SamsungReversePreviewRegressionTest referenced removed
  *_MS constants; OpenLoopViewModelTest asserted a RAW that the render flow now
  intentionally deletes after a successful save.

Docs: record the production zero-error rule (DEFINITION_OF_DONE.md + CLAUDE.md)
and the 3-device E2E sweep report (docs/e2e/).

Co-authored-by: Cursor <cursoragent@cursor.com>
@stozo04 stozo04 merged commit 82f3dca into main Jun 22, 2026
1 check passed
@stozo04 stozo04 deleted the fix/reverse-codec-software-fallback branch June 22, 2026 02:13
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.

1 participant