Repository navigation
Add --frame-packing option to emit the frame packing arrangement SEI - #986
danielcamposramos wants to merge 1 commit into
Conversation
x265 defined the SEI payload type FRAME_PACKING but had no way to write it, so an HEVC encode of side-by-side or top-and-bottom 3D video could not carry the in-stream mark that players and 3D televisions read (see Multicorewareinc#970). --frame-packing <integer> takes the values H.265 defines in Table D.8: 3 side by side, 4 top and bottom, 5 temporal interleaving. It is disabled by default and the bitstream does not change without it. As x264 does, frame 0 is signalled as the left view (content_interpretation_type 1). The message follows H.265 D.2.16 and is written as a prefix SEI with each IRAP picture (types 3 and 4, persistence flag set) or with every picture (type 5, where current_frame_is_frame0_flag alternates and the persistence flag is clear). X265_BUILD is incremented as x265_param gets a new member. SEIFramePacking::writeSEI() carries an inline cppcheck suppression for missingOverride, like the other SEI classes it has no override specifier: the default build is -std=gnu++98, where the specifier is not available. Assisted-by: Claude Sonnet via Claude Code
fe6d28e to
c1362f8
Compare
|
Hi @danielcamposramos , I noticed something unexpected with frame-packing type 3 when using --single-sei. I tested commit c1362f8 with an SBS Y4M input: With For example, before POC 12, the prefix SEI bytes excluding the start code are: Expected: 4E 01 2D 06 81 81 00 00 03 00 02 80 The extra five bytes match the beginning of the preceding picture’s slice-header payload. Could you take a look at whether the buffer is being reset correctly before collecting the prefix SEI messages? |
With --single-sei the prefix SEI messages of an access unit are written into m_bs and serialized as one NAL unit. m_bs is reset before them only by the access unit delimiter or by the repeated stream headers, so with --no-aud, on a picture without repeated headers, it still holds the previous picture's slice header and those bytes are written in front of the first SEI message. FFmpeg's trace_headers then reports "Invalid SEI message: payload_size too large". On master this happens with --hrd --single-sei --no-aud --no-repeat-headers (47 of 48 SEI NAL units in a 48 picture encode), and with --frame-packing as reported on Multicorewareinc#986. Reset m_bs in that case. Streams that were already correct do not change: with --aud, without --single-sei, and on keyframes with --repeat-headers the output is byte-identical. Assisted-by: Claude Opus via Claude Code
|
Thank you @vunguyen1989, good catch, and thank you for the exact command and bytes. You were right: This is older than The fix is a separate commit, 4712634, which resets What I checked, 8 and 10 bit:
Our earlier tests ran If you prefer the fix as its own PR, since it also affects master, I can split it out. |
|
Thanks for the detailed follow-up and for verifying all those combinations. Since the Glad the test case helped uncover it. |
4712634 to
c1362f8
Compare

x265 defines the SEI payload type FRAME_PACKING but had no way to write it.
An HEVC encode of side-by-side or top-and-bottom 3D video could not carry the in-stream mark that players and 3D televisions read.
This adds the option, as discussed in #970.
What it adds
--frame-packing <integer>with the values H.265 defines in Table D.8.3is side by side,4is top and bottom,5is temporal interleaving.It is off by default, and the bitstream does not change when it is not given.
Other values are rejected with an error, because H.265 does not define them.
Frame 0 is always signalled as the left view, as x264 does.
Where the SEI is written
It is a prefix SEI NAL unit written in
FrameEncoder::compressFrame(), next to the alternative transfer characteristics SEI.Types 3 and 4 write it with each IRAP picture, with the persistence flag set.
Type 5 writes it with every picture, because
current_frame_is_frame0_flagalternates, with the persistence flag clear.The syntax is H.265 D.2.16 and the semantics are D.3.16.
The new
SEIFramePackingclass is insei.h.API
The new member
framePackingis at the end ofx265_param, withX265_BUILDraised to 218.Parsing, default, copy, the parameter string, the CLI help,
cli.rstand the test lists are updated.How it was tested
The SEI NAL units were compared byte for byte with an independent writer that follows the D.2.16 syntax table field by field.
They matched for types 3 and 4 on every IRAP picture, and for type 5 on every picture with the flag alternating in output order.
The same writer, in its H.264 form, reproduces the 16-byte message x264 writes.
Over 60-frame clips with several GOPs, with and without B-frames, open and closed GOP, there is one SEI with each IRAP picture and none elsewhere for types 3 and 4, and one on every picture for type 5.
Without the option the output is byte-identical to the unpatched build (
--frame-threads 1 --pools 1).The 8-bit and 10-bit builds have no new warnings.
FFmpeg 9.0.2 reads the message and reports Stereo 3D side data with the right type on the pictures (and the left and right view alternating for type 5).
A stock FFmpeg with libx264 carries the layout through a transcode to H.264.
The clips were also played on a Linux stereo desktop: the player reads the SEI from the stream and declares the video as stereo, and each eye gets its own view.
The captures are attached (
hevc-sbs-capture.pngfor--frame-packing 3,hevc-tb-capture.pngfor--frame-packing 4): LEFT in the left eye, RIGHT in the right eye, the same frame number in both.Reference SEI
The prefix SEI NAL units the option writes, without the start code, for the byte-for-byte comparison:
--frame-packing 34E 01 2D 06 81 81 00 00 03 00 02 80--frame-packing 44E 01 2D 06 82 01 00 00 03 00 02 80--frame-packing 5, frame 04E 01 2D 04 82 81 10 00 80--frame-packing 5, frame 14E 01 2D 04 82 81 00 00 80Notes
I did not run TestBench, as no primitives are touched.
The three lines in
regression-tests.txtand the one insmoke-tests.txtusebig_buck_bunny_360p24.y4m, which I have not run through the CI harness.