Repository navigation
Add --frame-packing option to emit the frame packing arrangement SEI - #986
danielcamposramos wants to merge 1 commit into
Conversation
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
|
Hi @danielcamposramos. Thanks for the PR. The SEI matches the reference bytes in the description, and the output without the option is unchanged. Two combinations need a fix: 1. Type 5 with Types 3 and 4 work fine with fields. 2. Minor
|
c1362f8 to
bf495e5
Compare
|
Thank you for the careful review, and for the exact command that showed the field problem. All points are fixed in the rebased branch (one commit, on top of current master). Type 5 with --interlace and --field: --num-views 2: --alpha: Rebase and docs: Evidence: One thing I noticed while testing, which is not part of this change: Daniel |
|
Thanks for the quick update. Verified on bf495e5: Three small things left: 1. Using 2. The new regression line fails. 3. Doc wording. "The first encoded picture of the coded video sequence is the left view" is not quite accurate: the view follows the picture count from the start of the stream, so with |
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 #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
bf495e5 to
7ab8a2d
Compare
|
Thanks, Akilan, all three points are fixed. |
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. Any other value is an error. 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). H.265 D.3.16 defines packing in terms of frames even when field_seq_flag indicates field pictures, and current_frame_is_frame0_flag identifies the current decoded frame for type 5. With --interlace each picture is a field, whether the input is already fields or --field splits input frames, so take the view from POC >> 1 in both cases: L L R R. Keep field_views_flag zero as required by D.3.16; VUI field_seq_flag (E.3.1) identifies field pictures. The first encoded picture is the left view, so an odd --seek swaps the views; this is documented. The view count continues across CVS boundaries. The message describes the packed pictures of the base layer. It is written with nuh_layer_id 0 only: the alpha layer of --alpha is an auxiliary picture, not a view. It is rejected with --num-views greater than 1, because each layer of an MV-HEVC stream (H.265 F.7 and G.7) is a full view and a frame packing arrangement SEI would describe it as half of a packed picture. Keep the veryfast field regression consistent with its neighbours and set b-adapt to 2 explicitly, as --field currently rejects b-adapt 0. 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 5.5 via Claude Code Assisted-by: gpt-6.1-sol via Codex
7ab8a2d to
f4a24d6
Compare
|
@vunguyen1989 thank you, moved it next to its first use, just above |


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.