Let the user pick a container and codecs independently, and remux without re-encoding #3
Closed
JMR-dev wants to merge 1 commits from
feat/remux-and-codec-matrix into fix/media3-honours-output-format
pull from: feat/remux-and-codec-matrix
merge into: :fix/media3-honours-output-format
:main
:fix/102-picker-back-press-overshoot
:fix/268-saf-picker-determinism
:feat/ogg-vorbis-libvorbis
:feat/expedited-conversion-work
:fix/gate-cache-in-worktrees
:chore/gate-runs-shellcheck
:test/publish-delete-arm-real-provider
:docs/e8-instrumented-coverage
:docs/api37-carrier-count-drift
:docs/e7-second-constraint
:test/publish-to-a-real-saf-destination
:fix/api37-report-match-line
:fix/api37-task-snapshot-crash
:test/join-failure-message-on-device
:docs/e2e-read-findings-e7
:test/cancelling-a-running-export
:test/reattach-to-a-running-job
:test/content-uri-reaches-ffmpeg
:fix/launcher-wiring-waits-for-the-pick
:fix/cancel-tests-need-a-slower-encode
:test/cancelling-a-running-join
:test/cancelling-a-running-session
:test/notification-cancel-action
:test/ffmpeg-progress-is-observed
:test/fallback-asserts-the-path
:test/flac-and-opus-assert-their-format
:docs/e2e-read-findings
:docs/wave4-coverage-numbers
:fix/injectable-startup-sweep-scope
:test/session-outcome-seam
:test/launcher-callback-identity
:test/theme-follows-system-dark
:test/audio-drop-arm
:fix/rotation-waits-for-recreation
:fix/convert-guards-on-ready
:test/retry-save-mime
:test/hardware-progress-reaches-workmanager
:test/ffprobe-mapping-seam
:test/device-codec-enumeration-seam
:test/unknown-container-row
:test/null-message-fallbacks
:test/cancel-reaches-workmanager
:docs/coverage-wave3-recovery
:test/concat-engine-seam
:docs/coverage-wave3
:test/mediaprobe-merge-seam
:test/adaptive-shell-wiring
:test/aac-audio-args
:test/notification-progress-text
:test/media3-muxer-guard
:test/hardware-fallback-and-cancellation
:test/unprobeable-join-clip
:test/one-branch-outcomes
:test/foreground-type-regimes
:fix/bound-wedge-diagnostics
:docs/coverage-wave2
:test/screen-wiring
:test/viewmodel-setters
:test/join-state-mapping
:test/conversion-state-mapping
:test/dedupe-user-messages
:fix/restore-stack-merges
:test/refused-jobs
:test/concatworker-failure-arms
:test/container-capabilities-audio
:test/readspec-enum-fallbacks
:test/outputpublisher-seams
:test/mediaprobe-track-seam
:test/outputpublisher-partial-branches
:test/fake-provider-scaffolding
:docs/coverage-read-findings
:chore/gitignore-kotlin
:test/bound-the-hangs
:docs/coverage-remeasure
:ci/baseline-counter-precision
:fix/invalid-suggestion-chip
:ci/wedged-leg-report
:fix/reattachment-overwrites-pick
:test/theme-live-branches
:fix/failed-save-retry
:fix/empty-composition-crash
:ci/advisory-failure-report
:docs/seven-run-counts
:test/release-permission-guard
:ci/build-workflow-permissions
:docs/api37-point-release
:docs/benchmark-populate-path
:fix/dead-assertion-probe-test
:ci/actionlint
:test/device-codecs-encode-consequence
:fix/sdkmanager-pipefail
:docs/readme-restart-claim
:fix/saf-picker-root-discovery
:fix/probe-dispatcher-seam
:test/media3engine-mime-tables
:test/mediaprobe-pure-helpers
:fix/codec-vocabulary-drift
:docs/robolectric-rationale-correction
:docs/api37-advisory-counts
:test/r38-8-saf-e2e
:test/r38-7-join-states
:test/r38-6-conversion-states
:test/r38-5-state-seam
:fix/jacoco-robolectric-coverage
:docs/instrumented-tests-correction
:test/r38-2-filecard
:test/r38-4-advanced-picker
:test/r38-3-pickers
:tools/file-issue-script
:tools/api-37-emulator
:fix/review-app-gaps
:docs/review-corrections
No Reviewers
Labels
Clear labels
above-cut
accessibility
backlog
bug
confirmed
documentation
duplicate
enhancement
good first issue
help wanted
invalid
plausible
question
sev:high
sev:low
sev:medium
wontfix
Worked autonomously overnight: local, JVM-verifiable, no product decision
Barrier affecting people with disabilities
Held for manual review: product/UX call, CI/workflow, hardware, or unverifiable here
Something isn't working
Reviewer demonstrated the defect
Improvements or additions to documentation
This issue or pull request already exists
New feature or request
Good for newcomers
Extra attention is needed
This doesn't seem right
Reviewer could not fully demonstrate it; treat as unproven
Further information is requested
High severity
Low severity
Medium severity
This will not be worked on
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: JMR-dev/LibreMediaConverter#3
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
OutputFormatwas a closed enum of twelve(container, videoCodec, audioCodec)triples, defended on the grounds that a closed set was what made routing decidable. Two things it could not express: changing the container while copying the streams, and choosing codecs per track.OutputSpecreplaces it as the vocabulary;OutputFormatstays as presets over it. Decidability moves toContainerCapabilities, which is explicit and unit-tested rather than implicit in whichever combinations somebody enumerated.The matrix has a mode axis
Indexed by
(container, codec, trackType, mode), not one boolean. "Can MP4 carry AV1" and "can this app encode AV1" have different answers, and copy is where the difference shows: a single flag would either refuse a legitimate remux or promise an encode neither engine can deliver.COPYis a codec value rather than a flag, so every exhaustivewhenin the codebase had to say what it does about copying.CopyPlannerresolves it before anything else reads the request, and inheritsConcatPlanner's rule that an unproven match is never a copy — a needless re-encode costs time, a wrong stream copy costs a file that will not play.A matching codec is upgraded to a copy only when the container is changing. If container and codec both already match, the only reason to run the job is to re-encode it, most likely to compress — silently copying would hand back a byte-identical file and call it done. That assumption needs revisiting if bitrate or resolution controls are ever added.
Routing asks the plan, never the request
COPYbelongs to none of the capability sets, so testing the request directly sends every remux to FFmpeg on the first check — and nothing notices, because-c copyproduces a correct file, just on the CPU.ConversionRouterTestasserts the engine for that reason.The router also learns what Media3 can carry as opposed to encode: its MP4 muxer takes AAC, Opus, Vorbis and PCM but neither MP3 nor FLAC, so remuxing an MP3 track into MP4 leaves the hardware path. Before remuxing existed nothing could reach that combination.
Containers became load-bearing
Containernow drives-f, the extension and the SAF MIME type, so Matroska without video is.mkaand MP4 without video is.m4awithout a preset for each.FLACwas declared asContainer.MKVwith a.flacextension — inert only while nothing read the container; it now has its own. Six added: MOV, MKA, MPEG-TS, AVI, FLV, WMV/ASF.Probing
MediaProbeseparates "no video track" from "could not parse" and reports the source container, whichMediaExtractorcannot supply at all. FFprobe runs on every pick for that reason, not as a fallback. Matroska and WebM share a demuxer and report identical format names, so the codec is used to disambiguate — documented, with the residual ambiguity shown to be safe for the planner.UI
Presets stay the one-tap path. The Advanced drawer shows the whole matrix and lets an impossible combination be selected on purpose, then explains it and offers one-tap alternatives; Convert is what blocks the job.
ConversionWorkervalidates too, so a stale queued spec fails with the reason rather than being coerced into something else.Verification
:app:testDebugUnitTest— 133 tests green, up from 71.ContainerCapabilitiesTest,CopyPlannerTest,CodecNamesTest,MediaProbeFormatTest; extended router, builder and format tests.RemuxTestplus four committed fixtures (sample_h264.mkv,sample_aac.m4a,sample_vp9.webm,sample_still.png), recipes recorded in KDoc per theHardwareFallbackTestconvention.Three bugs were caught by tests written for this change: suggestions repaired only the failing axis (offering VP9-in-WebM with AAC still attached); the MP3-in-MP4 muxer gap above, where a test asserted the opposite of the truth; and
FFmpegCommandBuilder'selse -> libx264, which would silently hand back H.264 for a VP8/AV1 request — structurally the same defect #2 fixes.⚠️ The instrumented tests have not been executed locally — the emulator segfaults on my machine. CI is the first real exercise of
RemuxTest. Unproven until it runs: MKV→MP4 landing onMEDIA3, WAV/Ogg output, andInputKindclassification against the real fixtures.🤖 Generated with Claude Code
Pull request closed