Repository navigation
Parse the H.264/HEVC frame_packing_arrangement SEI so frame-packed 3D is detected without a container tag #3419
Description
[REQUIRED] Use case description
Media3 detects stereoscopic video from container-level signalling only. Frame-packed 3D that carries its layout in the elementary stream is invisible to it.
What is present today:
MatroskaExtractorreads the MatroskaStereoModeelement (private static final int ID_STEREO_MODE = 0x53B8;).BoxParserreads Apple'svexubox, andHevcConfigparses the 3D Reference Display Information SEI, for MV-HEVC spatial video.H264Reader's SEI handling is documented as "An SEI reader for consuming closed caption channels", and wires up only CEA-608/708.
What is absent: the classic frame_packing_arrangement SEI — ITU-T H.264 Annex D, payload type 45, and its HEVC equivalent. Searching this repository for frame_packing, FramePacking or frame packing returns no hits.
The consequence is narrow but real: an H.264 stream whose only stereoscopic signal is that SEI plays as a flat double-width (or double-height) picture, with no way for an app to know it is 3D. That is the normal case for:
- MPEG-TS delivery, where container-level stereoscopic tags do not exist at all. In the DVB frame-compatible 3DTV specification (ETSI TS 101 547-2) the in-stream SEI is the signal, and it takes precedence over container signalling.
- Any remux from Matroska to TS, which drops
StereoModesilently. - Broadcast-era and disc-derived material generally, which is where most frame-packed content in existence comes from.
This was requested once before, on the ExoPlayer tracker: google/ExoPlayer#7869 (September 2020), answered with "as you are the first one to ask for it, we will probably not look into it in the near future" and labelled low priority. This is the second ask, five years on, with measurements and with the rest of the ecosystem having moved.
Where the ecosystem now stands, in case it helps weigh the cost:
- GStreamer already does the whole loop:
gst_h264_parser_parse_frame_packing()decodes payload type 45,h264parsemaps it toGstVideoMultiviewModeand publishes it on caps, andx264encderives its write-side parameter from those caps automatically. - FFmpeg exposes the same signal as
AV_FRAME_DATA_STEREO3D, so anything built on libavcodec can read it in a few lines. - VLC parses payload type 45 into
multiview_modein its H.264 packetizer and uses it for stereo rendering. - HandBrake merged a patch to write the SEI when encoding (PR #8100, September 2026).
- Universal Media Server has an approved PR doing the same for its transcodes (PR #6330).
Why it is worth doing now rather than in 2020. Stereoscopic playback stopped being a niche the moment headsets became ordinary again, and Android is the platform most of them run on. Media3 already grew MV-HEVC support for Apple spatial video (#2011); this is the same class of feature for the far larger body of existing content, which is frame-packed rather than multi-view.
The behaviour is measurable on hardware, not theoretical. On two 2011/2012 televisions I own, an MP4 whose only stereoscopic signal is this SEI switches the set into 3D automatically; the identical file with the SEI removed — a difference of 791 bytes, picture data untouched — plays flat; and a Matroska carrying only the container StereoMode tag is ignored. Method and files: https://github.com/danielcamposramos/sony-bravia-linux/blob/main/docs/3d-signalling-ecosystem.md
Proposed solution
Parse SEI payload type 45 where the elementary stream is already being walked (H264Reader/SeiReader, and the HEVC equivalent), and map it onto the Format.stereoMode values that already exist:
frame_packing_arrangement_type |
Suggested C.STEREO_MODE_* |
|---|---|
| 3 (side-by-side) | STEREO_MODE_LEFT_RIGHT |
| 4 (top-bottom) | STEREO_MODE_TOP_BOTTOM |
| 0 (checkerboard), 1 (column), 2 (row), 5 (temporal) | no existing constant; ignoring them is reasonable for a first pass |
content_interpretation_type carries eye order and would select between left-first and right-first. A container tag, where one exists, should presumably still win, so this would only fill in stereoMode when the container did not state it.
One adjacent observation, since it is in the same area and may be a separate small fix: WebmConstants defines STEREO_MODE = 0x53B8 and lists it in the element table, but WebmWriter, WebmMuxer and WebmElements contain no reference to it, so Media3's own muxer does not appear to write the tag back out on transcode.
Alternatives considered
- Remux to add a container tag. Fails for the MPEG-TS and streaming cases, which is where this signal is most common, and requires knowing the layout in the first place.
- Leave it to the app. An app can only guess from resolution and aspect, or ask the user to pick a layout by hand, which is what players without this support do today.
Happy to help with the implementation if that would be useful — I understand a code contribution would need the Google CLA, and I am willing to do that if you would take the patch.
Activity
Two merges: UMS #6330 landed, and the player survey that follows it
Parse H.264 and H.265 frame packing arrangement SEI in MPEG-TS
microkatz commentedon Oct 1, 2026
@danielcamposramos
Thank you for reporting the issue! I have submitted a fix for the MPEG-TS in-stream processing, 58f396c, into the main branch. Ideally containers, MP4/fMP4, would contain the correct stereo info themselves.
Does this fulfill your immediate requirements? If yes, then we can close the ticket.
Add right-eye-first stereo modes
danielcamposramos commentedon Oct 1, 2026
Thank you, Michael. That covers the MPEG-TS case, H.264 and H.265 both.
But, not yet for MP4, though, and that is the case I measured.
In MP4, common encoders (x264, FFmpeg, HandBrake) write the frame-packed layout only in the SEI.
So in MP4 and fMP4 the SEI is the stereo signal there is.
That is the 791-byte difference in the report: the same file with the SEI switches the TV into 3D, and without it plays flat.
I've enhanced your code for MP4 in #3439, building on 58f396c and your
NalUnitUtil.parseSeiStereoMode:Mp4ExtractorandFragmentedMp4Extractorread the stereo mode from the SEI, only when the container declares none.WebmMuxernow writesStereoMode, so a transcode keeps the layout.content_interpretation_type2 means frame 0 is the right view.Media3 had no right-first constant for side by side or top-bottom, so I added them in the same PR:
C.STEREO_MODE_RIGHT_LEFTandC.STEREO_MODE_BOTTOM_TOP, read from the SEI and from MatroskaStereoMode11 and 2, written byWebmMuxer, and rendered with the eyes swapped by the spherical renderer.microkatz commentedon Oct 20, 2026
@danielcamposramos
I have seen your update and the additional PR that you supplied to extend the feature for MP4 processing. It is still on our radar and we will get to reviewing the issue and PR. We appreciate your patience!
danielcamposramos commentedon Oct 20, 2026
@microkatz No worries, is just that as I progress and discover more nuances, I like to share upstream so everyone is in the same page. I am working on something with KDE that also touches Android, if you have couple minutes, just take a peek here:
flat-headset