fix(reverse): software-codec fallback when a HW codec rejects start (47233ad7)#78
Merged
Merged
Conversation
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes Crashlytics 47233ad7 —
Non-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"isMediaCodec.native_start()'s JNI message; aBAD_VALUEnative status surfaces it asIllegalArgumentException. On this low-end non-Samsung device the hardware AVC codec rejectsstart(). The hand-rolled reverse pipeline (which, unlike the Media3 Transformer path, doesn't getsetEnableDecoderFallback) 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
isMediaCodecInitializationFailure+shouldRetryReverseWithSoftwareCodec(MediaCodecLifecycle.kt), distinct from benign teardown.VideoReverserretries once on the software encoder AND decoder after an init failure (threadedpreferSoftwareDecoderthrough the pipeline; the platform-default decoder path previously had no fallback). Samsung / zero-frame paths unchanged.BoomerangRenderWorkernow reports the real render cause to Crashlytics in itsIOException/ExportException/RuntimeExceptionbranches; previously the actual exception lived only in on-device logcat and never reached Firebase.Tests
MediaCodecLifecycleTest— predicate + retry-decision coverage, incl. the exactstart failedsignature and a regression guard vs. the old contention path.VideoReverserTest.reverse_recoversFromCodecStartFailure_viaSoftwareFallback— on-device fault-injection via a newVideoReverser.attemptHooktest seam: injectsstart failedon 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).DeviceMediaHintsTest/SamsungReversePreviewRegressionTestreferenced removed*_MSconstants;OpenLoopViewModelTestasserted a RAW the render flow now intentionally deletes after a successful save.Verification (Definition of Done)
assembleDebug+assembleRelease—BUILD SUCCESSFUL, exit 0lintDebug0 errors (13 warnings, none new);lintVitalReleasecleandocs/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
DEFINITION_OF_DONE.md+CLAUDE.md(no PR on a red baseline, pre-existing failures included).Manual QA checklist
FileProviderpermission-denial warnings noted in the E2E report).Made with Cursor