FFmpegCommandBuilder emitted -c:a libvorbis, and libvorbis was not in the AAR this app
ships. The arm was not merely unreached — it could not have succeeded; the first user to pick Ogg
Vorbis would have got Unknown encoder 'libvorbis'. This rebuilds the bundled FFmpeg with
libvorbis and implements Ogg Vorbis on it properly: -c:a libvorbis -q:a 5, no -strict experimental, no forced -ac 2, mono preserved.
The configure flag, and how it was confirmed
--enable-libvorbis — not the --enable-vorbis the ticket predicted. ffmpeg-kit's --enable-* names come from its own get_library_name() (--enable-lame for libmp3lame, --enable-opus for libopus), so the name was read rather than extrapolated, out of the
checked-out scripts/function.sh in the build container before any compile started:
It went into COMMON_LIBS beside --enable-lame and --enable-opus, for the same reason those
are there: Android has no Vorbis encoder at any API level either.
Evidence that libvorbis is really in the binary
Checked against the artifact beforebin/README.md was touched, because ffmpeg-kit ignores an
unrecognised --enable-* silently and a dropped library looks exactly like a successful build.
== libvorbis present? (before -> after)
jni/arm64-v8a/libavcodec.so : MISSING -> libvorbis
jni/x86_64/libavcodec.so : MISSING -> libvorbis
== external lib* encoder names, x86_64 libavcodec.so
libdav1d libjxl libjxl_anim libmp3lame libopus libopusdec libsvtav1
libvorbis libvpx libx264 libx264rgb libx265 <- libvorbis is the only new one
== configure line out of libavutil.so (both ABIs)
... --enable-libsvtav1 --enable-libvorbis --enable-libvpx ... <- one flag added, nothing moved
FFmpeg version n8.1.2 <- unchanged
== shape
20 shared libraries, 10 per ABI (unchanged)
77 LOAD segments, all 0x4000 (16 KB page alignment intact)
sha256 ae188c9aec3c89a1c87a169589253c85438d57cfdcc3ce8b40fb3e87de368ff2 (old)
sha256 c8f4491d2c626566cbf18d5035513c1a5d8049e6696531342ea030c5427df507 (new)
bin/README.md carries the new checksum, the new configure line, a Rebuilt row, and the two ways
this flag can be got wrong. tools/ffmpeg/README.md's "easy to get wrong" list gained a third
entry for the same reason.
How the build was run — disclosed, because it was incremental
podman commit of ffmpeg-full, the container that produced the currently shipped AAR, then a full build in it with the new flag. The starting point is provably the shipped binary: that
container's AAR is ae188c9a…, byte for byte what bin/ held. Only libogg and libvorbis
built fresh; everything else reported already built, and ffmpeg was reconfigured and relinked for
both ABIs. 14 minutes, not the 40 the ticket budgeted. The configure-line delta above is the check
that this produced the intended binary rather than a stale one.
The mutations, run rather than predicted (API 34 emulator)
The ticket's acceptance criterion — point AudioCodec.VORBIS at libopus:
FFmpegEngineTest.encodesOggVorbisThroughAnEncoderTheBundledBinaryActuallyHas FAILED
java.lang.AssertionError: expected a lone Vorbis track -- an Opus one would carry the same
OggS magic expected:<[audio/vorbis]> but was:<[audio/opus]>
Note which assertion caught it: the OggS magic assertion passed, because the mutant writes a
perfectly good Ogg. A magic-only test would have been vacuous here, which is why the track MIME is
asserted.
And the mono one — add -ac 2 back, the compromise the in-tree encoder would force:
java.lang.AssertionError: the fixture is mono and libvorbis takes any channel count, so
nothing may upmix it expected:<[1]> but was:<[2]>
Restored after each; the whole FFmpegEngineTest class (12 tests) is green against the new AAR.
Mono
Confirmed, and pinned rather than asserted in prose. sample_h264.mp4 — the class's own fixture —
is mono (ffprobe … channels=1), so the passing test's channelCounts(out) == [1] is a real
check, as the second mutation above shows. That was the user-visible compromise the in-tree encoder
forced, and it is gone.
Did the emulator sweep run?
Yes.pre-commit printed app/src changed -- sweeping API 33, 34, 35, 36 (this takes tens of minutes, by design) and ran all four against the rebuilt AAR:
API 33: tests=72 failures=0 errors=0 skipped=3
API 34: tests=72 failures=0 errors=0 skipped=3
API 35: tests=72 failures=0 errors=0 skipped=3
API 36: tests=72 failures=0 errors=0 skipped=3
[local-gate] NOT COVERED LOCALLY: API 37. No API 37 device is attached ...
[local-gate] green on API 33, 34, 35, 36; pre-commit allowed
pre-push then read the cache — app/src (8a9b316e...) already swept and green — as designed. The Starting 72 tests line is also the cross-check CLAUDE.md asks for on the derived count.
Worth knowing regardless: the gate classifies by path, matching app/src/main/* and app/src/{test,androidTest}/* and nothing else. bin/ is invisible to it, so an AAR rebuilt on
its own — a different native binary under every instrumented test — would invalidate no cache and
sweep nothing, while the JVM gate that does run cannot execute FFmpeg at all. This commit is not
affected (the AAR and the app/src change ship together), and CLAUDE.md now records it.
Three things that contradicted the ticket
The flag is --enable-libvorbis, not the --enable-vorbis the ticket expected the get_library_name() rule to produce. The rule is real; libvorbis is simply one of the names that
agrees with FFmpeg's.
The rebuild took 14 minutes, not 40, because the container that built the shipped AAR still
had every other library built.
There is no separate mono fixture. sample_h264.mp4, which FFmpegEngineTest already uses for
everything, is mono — so mono needed one more assertion in the existing test, not a new test.
What is not covered
API 37 was not swept locally (no Pixel attached; the API 37 emulator cannot install the APK on this
host). The new test carries no @FailsOnEmulatorApi37, so CI's gating leg runs it — the premise
holds there: the job encodes entirely inside FFmpeg and then asks the platform's software OggExtractor, with none of the c2.goldfish codecs that break the Media3 tests involved. The
baseline stays at 7; the derived counts move to 72 instrumented tests, 65 on the gating leg, and CLAUDE.md is updated with both.
Closes #254.
`FFmpegCommandBuilder` emitted `-c:a libvorbis`, and **libvorbis was not in the AAR this app
ships**. The arm was not merely unreached — it could not have succeeded; the first user to pick Ogg
Vorbis would have got `Unknown encoder 'libvorbis'`. This rebuilds the bundled FFmpeg with
libvorbis and implements Ogg Vorbis on it properly: `-c:a libvorbis -q:a 5`, no `-strict
experimental`, no forced `-ac 2`, mono preserved.
## The configure flag, and how it was confirmed
**`--enable-libvorbis`** — *not* the `--enable-vorbis` the ticket predicted. ffmpeg-kit's
`--enable-*` names come from its own `get_library_name()` (`--enable-lame` for libmp3lame,
`--enable-opus` for libopus), so the name was read rather than extrapolated, out of the
checked-out `scripts/function.sh` in the build container before any compile started:
```
62: 9) echo "libvorbis" ;; # get_library_name
248: libvorbis) echo 9 ;;
977: echo -e " --enable-libvorbis\t\tbuild with libvorbis [no]"
1283: libvorbis)
1284: ENABLED_LIBRARIES[LIBRARY_LIBVORBIS]=$2
1285: ENABLED_LIBRARIES[LIBRARY_LIBOGG]=$2 # libogg comes with it, unasked
```
It went into `COMMON_LIBS` beside `--enable-lame` and `--enable-opus`, for the same reason those
are there: Android has no Vorbis encoder at any API level either.
## Evidence that libvorbis is really in the binary
Checked against the artifact **before** `bin/README.md` was touched, because ffmpeg-kit ignores an
unrecognised `--enable-*` silently and a dropped library looks exactly like a successful build.
```
== libvorbis present? (before -> after)
jni/arm64-v8a/libavcodec.so : MISSING -> libvorbis
jni/x86_64/libavcodec.so : MISSING -> libvorbis
== external lib* encoder names, x86_64 libavcodec.so
libdav1d libjxl libjxl_anim libmp3lame libopus libopusdec libsvtav1
libvorbis libvpx libx264 libx264rgb libx265 <- libvorbis is the only new one
== configure line out of libavutil.so (both ABIs)
... --enable-libsvtav1 --enable-libvorbis --enable-libvpx ... <- one flag added, nothing moved
FFmpeg version n8.1.2 <- unchanged
== shape
20 shared libraries, 10 per ABI (unchanged)
77 LOAD segments, all 0x4000 (16 KB page alignment intact)
sha256 ae188c9aec3c89a1c87a169589253c85438d57cfdcc3ce8b40fb3e87de368ff2 (old)
sha256 c8f4491d2c626566cbf18d5035513c1a5d8049e6696531342ea030c5427df507 (new)
```
`bin/README.md` carries the new checksum, the new configure line, a `Rebuilt` row, and the two ways
this flag can be got wrong. `tools/ffmpeg/README.md`'s "easy to get wrong" list gained a third
entry for the same reason.
### How the build was run — disclosed, because it was incremental
`podman commit` of `ffmpeg-full`, the container that produced the currently shipped AAR, then a
`full` build in it with the new flag. The starting point is provably the shipped binary: that
container's AAR is `ae188c9a…`, byte for byte what `bin/` held. Only `libogg` and `libvorbis`
built fresh; everything else reported `already built`, and ffmpeg was reconfigured and relinked for
both ABIs. 14 minutes, not the 40 the ticket budgeted. The configure-line delta above is the check
that this produced the intended binary rather than a stale one.
## The mutations, run rather than predicted (API 34 emulator)
**The ticket's acceptance criterion** — point `AudioCodec.VORBIS` at `libopus`:
```
FFmpegEngineTest.encodesOggVorbisThroughAnEncoderTheBundledBinaryActuallyHas FAILED
java.lang.AssertionError: expected a lone Vorbis track -- an Opus one would carry the same
OggS magic expected:<[audio/vorbis]> but was:<[audio/opus]>
```
Note *which* assertion caught it: the `OggS` magic assertion passed, because the mutant writes a
perfectly good Ogg. A magic-only test would have been vacuous here, which is why the track MIME is
asserted.
**And the mono one** — add `-ac 2` back, the compromise the in-tree encoder would force:
```
java.lang.AssertionError: the fixture is mono and libvorbis takes any channel count, so
nothing may upmix it expected:<[1]> but was:<[2]>
```
Restored after each; the whole `FFmpegEngineTest` class (12 tests) is green against the new AAR.
## Mono
Confirmed, and pinned rather than asserted in prose. `sample_h264.mp4` — the class's own fixture —
is **mono** (`ffprobe … channels=1`), so the passing test's `channelCounts(out) == [1]` is a real
check, as the second mutation above shows. That was the user-visible compromise the in-tree encoder
forced, and it is gone.
## Did the emulator sweep run?
**Yes.** `pre-commit` printed `app/src changed -- sweeping API 33, 34, 35, 36 (this takes tens of
minutes, by design)` and ran all four against the rebuilt AAR:
```
API 33: tests=72 failures=0 errors=0 skipped=3
API 34: tests=72 failures=0 errors=0 skipped=3
API 35: tests=72 failures=0 errors=0 skipped=3
API 36: tests=72 failures=0 errors=0 skipped=3
[local-gate] NOT COVERED LOCALLY: API 37. No API 37 device is attached ...
[local-gate] green on API 33, 34, 35, 36; pre-commit allowed
```
`pre-push` then read the cache — `app/src (8a9b316e...) already swept and green` — as designed. The
`Starting 72 tests` line is also the cross-check `CLAUDE.md` asks for on the derived count.
**Worth knowing regardless:** the gate classifies by path, matching `app/src/main/*` and
`app/src/{test,androidTest}/*` and nothing else. **`bin/` is invisible to it**, so an AAR rebuilt on
its own — a different native binary under every instrumented test — would invalidate no cache and
sweep nothing, while the JVM gate that does run cannot execute FFmpeg at all. This commit is not
affected (the AAR and the `app/src` change ship together), and `CLAUDE.md` now records it.
## Three things that contradicted the ticket
- The flag is `--enable-libvorbis`, not the `--enable-vorbis` the ticket expected the
`get_library_name()` rule to produce. The rule is real; libvorbis is simply one of the names that
agrees with FFmpeg's.
- The rebuild took **14 minutes**, not 40, because the container that built the shipped AAR still
had every other library built.
- There is no separate mono fixture. `sample_h264.mp4`, which `FFmpegEngineTest` already uses for
everything, is mono — so mono needed one more assertion in the existing test, not a new test.
## What is not covered
API 37 was not swept locally (no Pixel attached; the API 37 emulator cannot install the APK on this
host). The new test carries no `@FailsOnEmulatorApi37`, so CI's gating leg runs it — the premise
holds there: the job encodes entirely inside FFmpeg and then asks the platform's software
`OggExtractor`, with none of the `c2.goldfish` codecs that break the Media3 tests involved. The
baseline stays at 7; the derived counts move to **72 instrumented tests, 65 on the gating leg**, and
`CLAUDE.md` is updated with both.
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.
Closes #254.
FFmpegCommandBuilderemitted-c:a libvorbis, and libvorbis was not in the AAR this appships. The arm was not merely unreached — it could not have succeeded; the first user to pick Ogg
Vorbis would have got
Unknown encoder 'libvorbis'. This rebuilds the bundled FFmpeg withlibvorbis and implements Ogg Vorbis on it properly:
-c:a libvorbis -q:a 5, no-strict experimental, no forced-ac 2, mono preserved.The configure flag, and how it was confirmed
--enable-libvorbis— not the--enable-vorbisthe ticket predicted. ffmpeg-kit's--enable-*names come from its ownget_library_name()(--enable-lamefor libmp3lame,--enable-opusfor libopus), so the name was read rather than extrapolated, out of thechecked-out
scripts/function.shin the build container before any compile started:It went into
COMMON_LIBSbeside--enable-lameand--enable-opus, for the same reason thoseare there: Android has no Vorbis encoder at any API level either.
Evidence that libvorbis is really in the binary
Checked against the artifact before
bin/README.mdwas touched, because ffmpeg-kit ignores anunrecognised
--enable-*silently and a dropped library looks exactly like a successful build.bin/README.mdcarries the new checksum, the new configure line, aRebuiltrow, and the two waysthis flag can be got wrong.
tools/ffmpeg/README.md's "easy to get wrong" list gained a thirdentry for the same reason.
How the build was run — disclosed, because it was incremental
podman commitofffmpeg-full, the container that produced the currently shipped AAR, then afullbuild in it with the new flag. The starting point is provably the shipped binary: thatcontainer's AAR is
ae188c9a…, byte for byte whatbin/held. Onlyliboggandlibvorbisbuilt fresh; everything else reported
already built, and ffmpeg was reconfigured and relinked forboth ABIs. 14 minutes, not the 40 the ticket budgeted. The configure-line delta above is the check
that this produced the intended binary rather than a stale one.
The mutations, run rather than predicted (API 34 emulator)
The ticket's acceptance criterion — point
AudioCodec.VORBISatlibopus:Note which assertion caught it: the
OggSmagic assertion passed, because the mutant writes aperfectly good Ogg. A magic-only test would have been vacuous here, which is why the track MIME is
asserted.
And the mono one — add
-ac 2back, the compromise the in-tree encoder would force:Restored after each; the whole
FFmpegEngineTestclass (12 tests) is green against the new AAR.Mono
Confirmed, and pinned rather than asserted in prose.
sample_h264.mp4— the class's own fixture —is mono (
ffprobe … channels=1), so the passing test'schannelCounts(out) == [1]is a realcheck, as the second mutation above shows. That was the user-visible compromise the in-tree encoder
forced, and it is gone.
Did the emulator sweep run?
Yes.
pre-commitprintedapp/src changed -- sweeping API 33, 34, 35, 36 (this takes tens of minutes, by design)and ran all four against the rebuilt AAR:pre-pushthen read the cache —app/src (8a9b316e...) already swept and green— as designed. TheStarting 72 testsline is also the cross-checkCLAUDE.mdasks for on the derived count.Worth knowing regardless: the gate classifies by path, matching
app/src/main/*andapp/src/{test,androidTest}/*and nothing else.bin/is invisible to it, so an AAR rebuilt onits own — a different native binary under every instrumented test — would invalidate no cache and
sweep nothing, while the JVM gate that does run cannot execute FFmpeg at all. This commit is not
affected (the AAR and the
app/srcchange ship together), andCLAUDE.mdnow records it.Three things that contradicted the ticket
--enable-libvorbis, not the--enable-vorbisthe ticket expected theget_library_name()rule to produce. The rule is real; libvorbis is simply one of the names thatagrees with FFmpeg's.
had every other library built.
sample_h264.mp4, whichFFmpegEngineTestalready uses foreverything, is mono — so mono needed one more assertion in the existing test, not a new test.
What is not covered
API 37 was not swept locally (no Pixel attached; the API 37 emulator cannot install the APK on this
host). The new test carries no
@FailsOnEmulatorApi37, so CI's gating leg runs it — the premiseholds there: the job encodes entirely inside FFmpeg and then asks the platform's software
OggExtractor, with none of thec2.goldfishcodecs that break the Media3 tests involved. Thebaseline stays at 7; the derived counts move to 72 instrumented tests, 65 on the gating leg, and
CLAUDE.mdis updated with both.