Repository navigation
Parse frame packing SEI in MP4 and fragmented MP4, right-eye-first stereo modes, StereoMode in WebmMuxer - #3439
Open
danielcamposramos wants to merge 3 commits into
Open
danielcamposramos wants to merge 3 commits into
danielcamposramos wants to merge 3 commits into
Conversation
MP4 files from common encoders (x264, FFmpeg, HandBrake) carry the layout of frame-packed H.264 and H.265 video only in the frame_packing_arrangement SEI message, so Format.stereoMode stayed unset for them. Read it with NalUnitUtil.parseSeiStereoMode, added for MPEG-TS in 58f396c: - Mp4Extractor reads the SEI NAL units while copying the sample, and FragmentedMp4Extractor through its existing SEI path for CEA-608/708, now also taken for these tracks. - Only for H.264 and H.265 tracks whose container declares no stereo mode, so a container value still wins. - The refined format is emitted ahead of the sample whose SEI carried it. As in the MPEG-TS readers, a cancellation or 2D arrangement only ends a stereo layout read earlier. The test assets were made with FFmpeg 9.0.1 and libx264 (-x264-params frame-packing=3 or 4). Issue: androidx#3419
WebmConstants defines StereoMode, but WebmMuxer never wrote it, so a transcode to WebM lost the stereo layout. Write 1 (side by side) and 3 (top-bottom), the values MatroskaExtractor maps to C.STEREO_MODE_LEFT_RIGHT and C.STEREO_MODE_TOP_BOTTOM (RFC 9559, section 5.1.4.1.28.3). Mono is the element's default and isn't written. Issue: androidx#3419
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
A frame packing arrangement SEI says which eye comes first: with content_interpretation_type 2, constituent frame 0 (the left or top half) is the right view. Matroska has StereoMode 11 (side by side) and 2 (top-bottom) for the same layouts (RFC 9559, section 5.1.4.1.28.3). Media3 had no constants for them, so these layouts were read as left eye first. - Add C.STEREO_MODE_RIGHT_LEFT and C.STEREO_MODE_BOTTOM_TOP. - NalUnitUtil.parseSeiStereoMode reads content_interpretation_type. - MatroskaExtractor maps StereoMode 11 and 2, and WebmMuxer writes them. - ProjectionRenderer swaps the eyes for them. Issue: androidx#3419
Author
|
@microkatz, would you review this one? It builds on your 58f396c, for the MP4 side of #3419. |
This was referenced Oct 7, 2026
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.
Builds on 58f396c (thank you, @microkatz), for the MP4 case in #3419.
What
Mp4ExtractorandFragmentedMp4Extractorread the stereo mode from H.264 and H.265frame_packing_arrangementSEI messages, through theNalUnitUtil.parseSeiStereoModeadded in 58f396c, unchanged. Only for H.264/H.265 tracks whose container declares no stereo mode, so a container value still wins.FragmentedMp4Extractoralready does for CEA-608/708 (it now also buffers them for these tracks, and feeds the caption path only when caption outputs exist). No extra peeking.FragmentedMp4Extractor. As in the MPEG-TS readers, a cancellation or 2D arrangement only ends a stereo layout read earlier.FLAG_OMIT_TRACK_SAMPLE_TABLEand init-segment-only reads are unaffected: track formats are output when the header is parsed, as before.content_interpretation_type2 means constituent frame 0 (the left or top half) is the right view, and Matroska hasStereoMode11 (side by side) and 2 (top-bottom) for the same layouts. Media3 had no constants for them, so these layouts were read as left eye first. This addsC.STEREO_MODE_RIGHT_LEFTandC.STEREO_MODE_BOTTOM_TOP:NalUnitUtil.parseSeiStereoModereadscontent_interpretation_type,MatroskaExtractormaps 11 and 2, and the spherical video renderer (ProjectionRenderer) swaps the eyes for them.WebmMuxerwrites the MatroskaStereoModeelement: 1 and 11 for side by side, 3 and 2 for top-bottom (left and right eye first), the inverse ofMatroskaExtractor's mapping, so a transcode keeps the layout. Mono is the element's default and isn't written.Why MP4
The case measured in #3419 is an MP4: a file whose only stereo signal is this SEI switches two 2011/2012 Sony televisions into 3D, and the same file with the SEI removed (791 bytes, picture data untouched) plays flat. MP4 files from common encoders carry the frame-packing layout only in the SEI: x264 and FFmpeg write it there (
-x264-params frame-packing=…), and HandBrake does since HandBrake#8100. Measurement method and files: 3d-signalling-ecosystem.md.Sources
frame_packing_arrangement(payloadType 45), as parsed by 58f396c.StereoMode: RFC 9559, section 5.1.4.1.28.3, table 5 (0x53B8; 1 and 11 side by side, 3 and 2 top-bottom, left and right eye first).content_interpretation_typesemantics in the same Annex D clause (1: frame 0 is the left view; 2: frame 0 is the right view).Tests
Mp4ExtractorParameterizedTest(sample_h264_frame_packing_side_by_side.mp4,sample_h264_frame_packing_top_bottom.mp4) andFragmentedMp4ExtractorParameterizedTest(sample_fragmented_h264_frame_packing_side_by_side.mp4), withsetDeduplicateConsecutiveFormats(true)for the refined format.DumpableFormatdoesn't printstereoMode, soMp4ExtractorNonParameterizedTestandFragmentedMp4ExtractorNonParameterizedTestassert it directly (side by side, top-bottom, and an H.264 file without the SEI staying unset).NalUnitUtilTest:content_interpretation_type2 for side by side and top-bottom, and 1 staying left eye first.WebmMuxerEndToEndTest: all four layouts muxed and read back withMatroskaExtractor.lib-common,lib-container,lib-extractorandlib-muxerunit tests pass.The three assets (about 8 KB each, 128x64, 5 frames) were made with FFmpeg 9.0.1 and libx264, for example:
(
frame-packing=4for top-bottom;-movflags frag_keyframe+empty_moov+default_base_mooffor the fragmented file.)Prepared with AI assistance (Claude), directed, reviewed and tested by me.