Skip to content

feat(libSceVideodec2): implement H.264 picture info in sceVideodec2GetPictureInfo and sceVideodec2GetAvcPictureInfo - #12633

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
Lindamea1:feat/videodec2-avc-picture-info
Oct 29, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
Lindamea1:feat/videodec2-avc-picture-info

Conversation

@Lindamea1

Copy link
Copy Markdown
Contributor

What

sceVideodec2GetPictureInfo (NtXRa3dRzU0) and sceVideodec2GetAvcPictureInfo (kjrLbcyhEiw) now fill the 120-byte (0x78) SceVideodec2AvcPictureInfo of an H.264 picture. Since #10959 removed the 56-byte fill, both getters threw NotImplemented for an H.264 picture with an accepted destination size. The bot listed both under "went back to stubs" on #10959.

Getter behaviour

  • The 13.42 argument prologue (PictureArguments) is unchanged. Argument errors and the early validity clears still come first.
  • When pictureCount is 1, an H.264 decoder owns the frame buffer and the destination thisSize is 0x78, the getter copies that picture's record under a GuestArena::HostWrite over the 0x78 bytes. It keeps the caller's thisSize and returns 0.
  • The branch is in the routine that all four named getters share, like the HEVC and VP9 records. BunnyFl's 13.42 reading found the getters to be thin wrappers around one routine, whose argument checks do not look at codecType.
  • These cases still throw after the clears: other accepted sizes (0x58, 0x68, 0x88, 0xa8, 0xb8), pictureCount 2, and records marked unsupported.

Parsing in sceVideodec2Decode

  • sceVideodec2Decode parses every H.264 unit with Videodec2Avc::Parse_nid_no_patch in the new AvcMetadata.cpp. It mirrors HevcMetadata.cpp: a bit reader, RBSP extraction and one catch per NAL, so metadata never stops decoding. Only the first 32 bytes after a slice's NAL header are unescaped.
  • The record is matched to its picture in display order through decoder.inputs, keyed by packet pts, as for HEVC.
  • A unit without slices that Decode holds (fix(libSceVideodec2): hold an AVC unit of parameter sets for the next slice #5803) updates the SPS and PPS tables. It is not counted in presence flags.

Record fields

  • isValid is 1. ptsData, dtsData and attachedData come from the unit.
  • idrPictureFlag is set when the unit's slices are nal_unit_type 5. It does not come from FFmpeg's key flag.
  • These fields come from the SPS that the first slice's PPS selects (7.3.2.1.1): profile_idc, level_idc, constraint_set0..5, pic_width_in_mbs_minus1, pic_height_in_map_units_minus1, frame_mbs_only_flag, frame_cropping_flag and the raw frame_crop_*_offset in SPS units.
  • These come from the VUI (E.1.1):
    • The aspect-ratio flag, idc and SAR: explicit for idc 255, from Table E-1 for 1 to 16, and 0:0 for 0 and for the reserved 17 to 254. These are the values BunnyFl's 13.42 reading of the H.264 path describes (see References). The merged HEVC record throws for a reserved idc instead; that reading did not cover HEVC, so this PR leaves HEVC alone.
    • The video signal type and colour description. When absent they take the E.2.1 values 5/0/0/2/2/2.
    • Timing: num_units_in_tick, time_scale and fixed_frame_rate_flag.
    • bitstream_restriction_flag and max_dec_frame_buffering.
  • When there is no bitstream restriction, max_dec_frame_buffering takes the E.2.1 inferred value:
    • 0 for profiles 44, 86, 100, 110, 122 and 244 with constraint_set3_flag.
    • Otherwise MaxDpbFrames = Min(MaxDpbMbs / (PicWidthInMbs * FrameHeightInMbs), 16) from A.3.1 and A.3.2, with MaxDpbMbs from Table A-1. Level 1b is level_idc 9, or 11 with constraint_set3_flag for profiles 66, 77 and 88.
  • pic_struct_present_flag comes from the VUI. pic_struct comes from the unit's picture-timing SEI (D.1.3).
    • The SEI is parsed after the slices select the SPS.
    • It skips cpb_removal_delay and dpb_output_delay, whose lengths come from that SPS's NAL or VCL hrd_parameters (E.1.2).
  • field_pic_flag and bottom_field_flag come from the first slice header (7.3.3). They are 0 when frame_mbs_only_flag is 1.
  • The presence flags come only from the unit that carries the slices. They cover NAL types 7 (SPS), 8 (PPS), 9 (AUD), 10 (end of sequence), 11 (end of stream) and 12 (filler), and SEI payload types 0 (buffering period) and 1 (picture timing). Other SEI payload types are skipped.

Unsupported cases

These mark the record unsupported: the query throws and decoding continues.

  • Parameter sets:
    • A slice that refers to a PPS or SPS that had not been received.
    • An SPS or PPS id sent again with other bytes, or a malformed parameter set. These stay unsupported until the decoder is recreated, because FFmpeg keeps parameter sets across Reset (the HEVC rule).
  • Field pictures, which SharpProspero documents as one record per field.
  • pic_struct_present_flag without a picture-timing SEI, or pic_struct above 8.
  • Several primary pictures in one unit. A later slice starts a new picture when its first_mb_in_slice is 0, or when its PPS id, frame_num, field flags, IDR type or nal_ref_idc being zero differ from the first slice's.
  • Several AUDs or picture-timing SEIs in one unit.
  • An SEI, SPS, PPS or AUD after the slices.
  • NAL types other than 1, 5 and 6 to 12.
  • A level outside Table A-1 when the DPB size must be inferred.
  • A signalled max_dec_frame_buffering above 16.
  • Differing NAL and VCL HRD delay lengths.
  • Truncated or malformed syntax: an empty NAL unit or one with forbidden_zero_bit set, an emulation prevention pattern that 7.4.1 forbids, or a value outside its range.

ue(v) values are limited to 32 bits. Every count that drives a loop is bounded: num_ref_frames_in_pic_order_cnt_cycle at most 255 and cpb_cnt_minus1 at most 31.

Layout, tests and debt

  • SceVideodec2::AvcPictureInfo in Api.hpp becomes the 120-byte layout. It has the size of the in-tree SceVdecsw::AvcPictureInfo and the same named fields up to frameCropBottomOffset at 0x44. The fields from 0x48 to 0x74, which that struct keeps as vui[44] and the first byte of its tail padding, take their names and order from SharpProspero, shadPS4 and KytyPS5. Offset static_asserts in AvcMetadata.hpp cover 0x2c, 0x35, 0x38, 0x48, 0x4a, 0x55, 0x58, 0x60, 0x67, 0x6f, 0x74 and size 0x78.
  • sizeof(AvcPictureInfo) is now 0x78, an accepted size. testDecode's two 0x811D0101 checks, through both getters on every H.264 picture, become the record checks. testClip (HEVC and VP9 pictures) and testFailures keep a 0x38 destination for their size-error checks. GuestHevcMetadata.cpp still checks 0x811D0101 through all four getters.
  • Debt:
    • The "AVC picture-info payload" entry is replaced by the gaps still open: unmeasured values and the unsupported cases.
    • The stale clause "sceVideodec2GetPictureInfo reports no frame cropping" is removed.

References:

  • Layout:
  • Call site: prosper's notes on PPSA06367's call sites in videodec2.cpp (L221-L224, L1302-L1311).
    • The title calls sceVideodec2GetPictureInfo(&outputInfo, &pictureInfo, 0, 0) on H.264 pictures with thisSize 0x78.
    • It then reads +0x20, +0x35 with +0x38 to +0x44, +0x48/+0x49/+0x4a/+0x4c, and +0x55 with +0x58/+0x5c. These match the layout above.
  • Values: ITU-T H.264 (08/2021). Sections used:
    • 7.3.1, 7.3.2.1.1 (with 7.3.2.1.1.1 scaling_list), 7.3.2.2, 7.3.2.3.1, 7.3.3, 7.4.1, 7.4.1.2.3 and 7.4.1.2.4.
    • D.1.2, D.1.3 and D.2.3 Table D-1.
    • E.1.1, E.1.2, E.2.1 and Table E-1.
    • A.3.1, A.3.2 and Table A-1.
  • SAR: BunnyFl's 13.42 reading of the H.264 path (#9937 comment; emulator runs and code reading, not a console). It says libSceAvPlayer takes the frame SAR from sceVideodec2GetPictureInfo's sar_width and sar_height, and that idc 1 to 16 give the standard table, 255 gives the explicit pair, and 0, 17 to 254 or a picture without aspect information give 0:0.
  • Accepted sizes and the order of argument errors: BunnyFl's 13.42 report and clarification. Field values were not read there, so only 0x78 is filled.
  • Conventions this follows: HevcMetadata.cpp L202-L264 (Table E-1 SAR, inferred VUI defaults) and L352-L505 (per-unit presence flags, replacement policy, one catch per NAL).

Where the sources disagree:

  • KytyPS5 fills the same layout with approximations from FFmpeg frame data (videoDec2Decoder.cpp L432-L468).
    • Crop offsets are in pixels.
    • aspect_ratio_idc is 255 whenever FFmpeg reports a SAR, and 0 otherwise. frame_mbs_only_flag and video_signal_type_present_flag are forced to 1.
    • Timing, presence and constraint flags are left at 0.
    • It also accepts thisSize 0x68 and fills its first 0x68 bytes (libVideoDec2.cpp L535-L543).
    • This PR follows the SPS and VUI meaning of the field names in SharpProspero. 0x68 keeps throwing because its layout is not known.
  • The merged HEVC record throws when a conformance or default display window is present (HevcMetadata.cpp L277-L306), and its debt entry says crop units are not guessed. This PR fills the raw frame_crop_*_offset values in SPS units, which SharpProspero documents as "from the SPS" (L284-L294).
  • SharpProspero documents PicStructPresentFlag as "Whether picture-timing SEI is present" (L347-L348), next to a separate PictureTimingSeiPresentFlag (L377-L378). This PR fills it from the VUI's pic_struct_present_flag, as its name says. The two readings differ for a stream that carries picture-timing SEIs for its HRD delays while pic_struct_present_flag is 0.
  • prosper maps aspect_ratio_idc 0 and the reserved 17 to 254 to 1:1 (h264_sps.cpp L86-L98). This PR gives 0:0, as BunnyFl's reading does.
  • prosper reads +0x20 as a decoder-side record. Its own notes say the title frees that pointer through its own allocator (videodec2.cpp L1375-L1380). That fits +0x20 being the caller's attachedData, as in SharpProspero, KytyPS5 and shadPS4.

No console measured the field values. The debt entry lists the choices that are not verified:

  • SAR from Table E-1, and 0:0 for idc 0 and 17 to 254. BunnyFl's reading gives the same values, but it is not a console measurement.
  • The E.2.1 defaults, including the inferred max_dec_frame_buffering.
  • Crop offsets in SPS units.
  • pic_struct_present_flag from the VUI.
  • Presence flags that leave out held units that carry only parameter sets.
  • +0x20 as attachedData.

Overlaps: tools/pr_overlap.py reports no overlap with open pull requests. One open PR edits the same files:

Relevance: in prosper's notes on PPSA06367, the title calls sceVideodec2GetPictureInfo with thisSize 0x78 on its H.264 pictures. Since #10959 that call throws on main. I did not run a title for this PR, so Unblocks is none.

Tested

Gentoo Linux, kernel 7.2.8, GCC 16.2.1, AMD Ryzen 7 9800X3D. Release build of this branch on main 319621b, with BUILD_TESTING=ON.

  • ctest --test-dir build -R 'guest_videodec2|guest_hevc_metadata|guest_videodec$|guest_vdecsw|public_headers' --output-on-failure -j8 --timeout 300: 70/70 pass. These are guest_videodec2, guest_videodec2_unimplemented, guest_hevc_metadata, guest_videodec, guest_vdecsw, public_headers and the 64 public_headers_compile_* tests.
  • New checks in GuestVideodec2.cpp. Records are compared field by field through both getters, with a guard word after the 0x78 record.
    • Every picture of H264Fixture::Stream, Annex-B and length-prefixed:
      • Values: profile 100, level 10, MBs 3/2, crop (0,0,0,4), SAR 1:1, inferred 5/0/0/2/2/2, timing 1/60, max_dec_frame_buffering 4.
      • AUD is present on every picture. SPS, PPS and IDR are set only for unit 0.
      • pts, dts and attachedData match the unit, in display order.
      • With the parameter sets sent alone first, the first picture reports SPS, PPS and AUD absent.
    • croppedStream() gives crop (0,0,4,4).
    • The fixture with a test SPS and added SEI and NAL units:
      • The SPS carries overscan info, a video signal type with colour description (format 2, full range, 9/16/9), chroma location, fixed timing 1001/60000, NAL HRD (delay lengths 10 and 14), pic_struct_present_flag and SAR idc 13 (160:99).
      • Unit 0 has a buffering-period SEI, and picture-timing SEIs give pic_struct 0, 3, 4, 7, 8 and 6.
      • Unit 2 has filler, and unit 5 has end-of-sequence and end-of-stream NALs.
      • Every field and presence flag matches per picture, and the pixels match the reference hashes.
    • The same stream with the picture-timing SEI of unit 3 removed:
      • FFmpeg decodes all six pictures with the reference hashes, and the other five records are unchanged.
      • The query on unit 3's picture, which lacks the SEI its pic_struct_present_flag calls for, either completes or changes only the validity word and nothing past thisSize. This is the try-and-canary pattern of GuestHevcMetadata.cpp.
    • The IPPP stream with an SPS that has no timing or bitstream restriction and SAR 255 (7:5):
      • Profile 66 with constraint_set0 and constraint_set1.
      • max_dec_frame_buffering is inferred as 16 (MaxDpbMbs 396 / 12 MBs, capped at 16).
      • The pixels match a decode with the original SPS.
    • An SPS with aspect_ratio_idc 100 gives the Stream records with idc 100 and SAR 0:0, and FFmpeg decodes all six pictures with the reference hashes.
    • Queries with thisSize 0x58 and 0x68 on every Stream picture use the same try-and-canary check.
    • testClip keeps 0x811D0101 for a 0x38 destination. That is the error BunnyFl's 13.42 emulator run reports for sizes other than the six accepted ones; 0x38 itself was not among the sizes tried. testFailures keeps 0x811D010F for a missing picture count ahead of that size error, in the order of the same run.
    • Control, with only Export.cpp reverted to main's: ctest --test-dir build -R '^guest_videodec2$' fails with sceVideodec2GetAvcPictureInfo_nid_postfix not implemented.
  • A host harness outside the repository, built with -fsanitize=address,undefined -fno-sanitize-recover=all, ran the parser on 39,999 seeds, 479,988 parses in all.
    • Each seed XORs four pseudo-random bytes of one unit of the fixture stream or of the test-SPS stream above, and parses all six units. It then cuts that unit to a random length and parses all six again.
    • It exited 0 with no sanitizer report, and no exception left Parse_nid_no_patch.
    • The records it printed for the test streams before fuzzing match the values the tests expect.
  • python3 tools/check_test_registration.py build passes: 1515 test executables, none new.
  • CI test selection (tools/select_tests.py): 491 of 2745 tests selected. All 491 pass, none skipped.
  • python3 tools/check_conventions.py --base 319621ba2 passes.
  • MinGW-w64 syntax check of the changed files only, not a Windows build or run.
    • x86_64-w64-mingw32-g++ 16.2.0 with -std=gnu++20 -fsyntax-only -Wall -Wextra -D_WIN32_WINNT=0x0602 compiles AvcMetadata.cpp, Export.cpp and GuestVideodec2.cpp without errors.
    • AvcMetadata.cpp gives no warnings.
    • The other two give only -Wmissing-field-initializers, from the partial aggregate initializers both files already use on main. Export.cpp gives 2 against 1 on main, because the Picture{pts, dts, attached} initializer now also leaves out avc. GuestVideodec2.cpp gives 772 against 492.

Not tested: no Windows or macOS build or run, no console, and no title.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

🤖 Generated with Claude Code

…tPictureInfo and sceVideodec2GetAvcPictureInfo

Fill the 120-byte SceVideodec2AvcPictureInfo for a single owned H.264
picture queried with thisSize 0x78: SPS fields, VUI, pic_struct from the
picture-timing SEI and per-unit presence flags, parsed per ITU-T H.264
(08/2021) from the access unit that produced the picture. Layout from
SharpProspero Videodec2.cs and shadPS4 videodec2.h. Other accepted
sizes, two records and unsupported syntax still throw after the
validity clears.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JJenkx

JJenkx commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

Windows run of this head (bcf8e9be44), merged onto main edf6804cf5 (clean merge, local only). Windows 11 Enterprise 10.0.22621, Ryzen 9 5950X, WinLibs GCC 15.2.0 UCRT r7, Ninja, Release, BUILD_TESTING=ON, no Vulkan driver visible.

  • This PR: guest_videodec2, guest_videodec2_unimplemented pass 3 of 3 (ctest --repeat until-fail:3).
  • Negative control: not possible. With this PR's source files (core/libs/prx/libSceVideodec2/AvcMetadata.cpp, core/libs/prx/libSceVideodec2/AvcMetadata.hpp, core/libs/prx/libSceVideodec2/Export.cpp, core/libs/prx/libSceVideodec2/include/Api.hpp) put back to main's version, and the ones it adds removed, the build fails against the rest of the PR, so there is no source-only control.

AI-assisted: yes (Claude Code).

@adriadam10
adriadam10 merged commit d608626 into boykopovar:main Oct 29, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants