Mark the final Ogg page as the end of the stream - #922
Merged
Conversation
AOSP's OggWriter copies its mReachedEOS field into every packet's e_o_s field, but only sets that field to true after the write loop has already exited. No page in the output file ever has the end-of-stream bit set in its header type flags, so the logical bitstream is never terminated. Demuxers that seek to the end of the file to find the last granule position reject the result. Firefox is one, and refuses to play BCR's recordings with NS_ERROR_DOM_MEDIA_METADATA_ERR. Add an OggContainer that wraps MediaMuxer and, once the muxer has finished writing, sets the end-of-stream bit in the last page's header and recomputes that page's CRC. As with FlacContainer, this only runs after the encoder signals EOF, so aborted recordings are untouched. The page structure and CRC logic is split into OggPage, which has no Android dependencies and is covered by JVM unit tests. Signed-off-by: Django Eijgensteijn <djangoboy@gmail.com>
Owner
|
Thanks for the PR! That's quite a dumb bug on the AOSP side. I'm able to reproduce this in libstagefright with the latest AOSP I will review this after work today. |
MediaMuxer.OutputFormat.MUXER_OUTPUT_OGG is not present in earlier SDK versions. Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
chenxiaolong
approved these changes
Aug 7, 2026
chenxiaolong
left a comment
Owner
There was a problem hiding this comment.
Thanks! This looks good to me. I only added 7f7e7e3 to annotate OggContainer with @RequiresApi(Build.VERSION_CODES.Q).
chenxiaolong
added a commit
that referenced
this pull request
Aug 7, 2026
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.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.
TL;DR: BCR's
.ogarecordings are missing the marker that says "this is where the file ends". Most players do not care, but stricter ones refuse to play the file at all, and in Firefox none of them work. This adds the marker when a recording finishes. Nothing about the audio itself changes, and existing recordings are unaffected.Technical version
The OGG/Opus files BCR writes never terminate the logical bitstream: no page in the output has the end-of-stream bit set in its header type flags. The Ogg spec requires the last page of a stream to carry that bit, so every
.ogarecording BCR has produced is malformed.The cause is in AOSP's
OggWriter. It sets each packet's flag from its own member inside the write loop:but
mReachedEOSonly becomes true at the end ofthreadFunc(), after the loop has finished. It is false for every packet actually written, and neitherstop()norreset()writes a terminating page afterwards.OggContainerwrapsMediaMuxerand, once it has stopped, sets the bit in the last page's header and recomputes that page's CRC. Audio data is untouched. LikeFlacContainer, this is a finalization step guarded on encoder EOF, so aborted recordings behave exactly as they do today.The practical benefit is that demuxers which seek to the end of the file to find the last granule position will accept the recordings. Firefox is one, and currently refuses to play them with
NS_ERROR_DOM_MEDIA_METADATA_ERR.The page and CRC logic lives in
OggPage, free of Android dependencies so it can be unit tested on the JVM.OggPageTestcovers the backwards page search, theOggSmagic appearing inside page data, the flag and CRC update, and that nothing outside the header type byte and CRC field is touched. The CRC is checked against a bit-by-bit reference implementation that shares no code with the table driven one.assembleDebugand the unit tests pass.