Five codec names resolve in CodecNames but not in AndroidDeviceCodecs, so hardware is attempted blind #87

Closed
opened 2026-08-25 03:22:06 +00:00 by JMR-dev · 0 comments
JMR-dev commented 2026-08-25 03:22:06 +00:00 (Migrated from github.com)

Found while triaging coverage gaps on ad28293. This is a defect, not a coverage ticket — it happens to be why those tables need tests.

Two tables read the same vocabulary and disagree

Both take an FFprobe codec_name and both lowercase it first:

  • model/CodecNames.kt -> videoFromName(name): VideoCodec?
  • codec/AndroidDeviceCodecs.kt -> mimeForCodecName(name): String?
codec_name CodecNames.videoFromName AndroidDeviceCodecs.mimeForCodecName
h264, avc, avc1 H264 AVC
x264 H264 null
hevc, h265, hvc1 H265 HEVC
hev1, x265 H265 null
vp9 VP9 VP9
vp09 VP9 null
av1, av01 AV1 AV1
mpeg4 null MPEG4

Five names are handled by one and not the other, in both directions.

What it costs

For x264, hev1, x265, vp09 — names FFmpeg genuinely emits — the app recognises the codec for display and routing but the device-capability check does not recognise it, so mimeForCodecName returns null and takes its documented fallthrough:

Unknown to us: assume the platform can handle it and let a failed export trigger the FFmpeg fallback, rather than pre-emptively refusing hardware.

So the app attempts a hardware path it could have known to skip, fails, and falls back. Not data loss — the fallback is real and tested — but a wasted transcode attempt on exactly the inputs where the codec was already identified. The policy is sound for genuinely unknown names; these are not unknown, they are known one file over.

mpeg4 runs the other way: the capability check knows it, CodecNames does not, so describeVideo falls through to the raw name and the card reads mpeg4 instead of a label.

Why it happened, and the shape to fix

The codec vocabulary is written out in at least four places — CodecNames.videoFromName/audioFromName, AndroidDeviceCodecs.mimeForCodecName/mimeFor, Media3Engine.videoMimeTypeFor/audioMimeTypeFor, and MediaProbe.shortName. None of the four has a test. Nothing makes them agree and nothing notices when they stop.

Whether the fix is one shared table or four tables plus a test asserting they agree is a design call. A test that enumerates the names and asserts both directions is cheaper and less disruptive than a refactor, and it catches the next divergence too.

Done means

  • The five divergent names resolve consistently, or each exception is documented where it lives.
  • A test that fails when the tables disagree — enumerate the alias set and assert every name either resolves in both or is deliberately excluded in both.
  • Mutation: add "x265" to one table only, and that test must go red. That is the bite; per-table arm tests would not catch it.

Related

#74 (describeAudio lacks describeVideo's unparseable arm) is the same family — two functions over one vocabulary that stopped matching. Worth fixing together.

_Found while triaging coverage gaps on `ad28293`. This is a defect, not a coverage ticket — it happens to be **why** those tables need tests._ ### Two tables read the same vocabulary and disagree Both take an FFprobe `codec_name` and both lowercase it first: - `model/CodecNames.kt` -> `videoFromName(name): VideoCodec?` - `codec/AndroidDeviceCodecs.kt` -> `mimeForCodecName(name): String?` | `codec_name` | `CodecNames.videoFromName` | `AndroidDeviceCodecs.mimeForCodecName` | |---|---|---| | `h264`, `avc`, `avc1` | H264 | AVC | | **`x264`** | **H264** | **null** | | `hevc`, `h265`, `hvc1` | H265 | HEVC | | **`hev1`**, **`x265`** | **H265** | **null** | | `vp9` | VP9 | VP9 | | **`vp09`** | **VP9** | **null** | | `av1`, `av01` | AV1 | AV1 | | **`mpeg4`** | **null** | **MPEG4** | Five names are handled by one and not the other, in both directions. ### What it costs For `x264`, `hev1`, `x265`, `vp09` — names FFmpeg genuinely emits — the app **recognises the codec for display and routing** but the device-capability check does not recognise it, so `mimeForCodecName` returns `null` and takes its documented fallthrough: > Unknown to us: assume the platform can handle it and let a failed export trigger the FFmpeg fallback, rather than pre-emptively refusing hardware. So the app attempts a hardware path it could have known to skip, fails, and falls back. **Not data loss** — the fallback is real and tested — but a wasted transcode attempt on exactly the inputs where the codec was already identified. The policy is sound for genuinely unknown names; these are not unknown, they are known one file over. `mpeg4` runs the other way: the capability check knows it, `CodecNames` does not, so `describeVideo` falls through to the raw name and the card reads `mpeg4` instead of a label. ### Why it happened, and the shape to fix **The codec vocabulary is written out in at least four places** — `CodecNames.videoFromName`/`audioFromName`, `AndroidDeviceCodecs.mimeForCodecName`/`mimeFor`, `Media3Engine.videoMimeTypeFor`/`audioMimeTypeFor`, and `MediaProbe.shortName`. **None of the four has a test.** Nothing makes them agree and nothing notices when they stop. Whether the fix is one shared table or four tables plus a test asserting they agree is a design call. A test that enumerates the names and asserts both directions is cheaper and less disruptive than a refactor, and it catches the next divergence too. ### Done means - The five divergent names resolve consistently, or each exception is documented where it lives. - **A test that fails when the tables disagree** — enumerate the alias set and assert every name either resolves in both or is deliberately excluded in both. - **Mutation:** add `"x265"` to one table only, and that test must go red. That is the bite; per-table arm tests would not catch it. ### Related #74 (`describeAudio` lacks `describeVideo`'s unparseable arm) is the same family — two functions over one vocabulary that stopped matching. Worth fixing together.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: JMR-dev/LibreMediaConverter#87