From 6fcbfc0288829916e03001983c2fb3380adfed23 Mon Sep 17 00:00:00 2001 From: antono2 Date: Fri, 2 Oct 2026 03:11:36 +0200 Subject: [PATCH] Fix scaling-list storage and V3 flag pointer handling --- .github/workflows/test.yml | 42 ++++++++++++++++++++++++++++++----- README.md | 8 +++++++ h264.v | 27 ++++++++++++++++++----- h264_test.v | 45 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 892df53..092a89c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,27 +14,59 @@ jobs: linux: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v7 - - uses: prantlf/setup-v-action@v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: prantlf/setup-v-action@92f20bea50d2c9931a28c96bb4997a3c7357baf0 # v4 + with: + version: 0.5.2 - run: v test . windows: runs-on: windows-2022 steps: - - uses: actions/checkout@v7 - - uses: prantlf/setup-v-action@v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: prantlf/setup-v-action@92f20bea50d2c9931a28c96bb4997a3c7357baf0 # v4 + with: + version: 0.5.2 - name: Test with the Microsoft C compiler run: v -cc msvc test . + v3: + name: Pinned V3 / Linux / TinyCC + runs-on: ubuntu-24.04 + timeout-minutes: 20 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + path: source + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + repository: vlang/v + ref: 0dc6a692ed5333cbe3f05ff63ae0766f0333038a + path: toolchain/v + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + repository: vlang/vc + ref: 8af812feb76c678abd86a8e682fd9ab2790e519c + path: toolchain/v/vc + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + repository: vlang/tccbin + ref: d6e7ac1b1bcc98aed734a6ecbfa8509f24606c74 + path: toolchain/v/thirdparty/tcc + - run: make -C toolchain/v local=1 + - run: toolchain/v/v -new-compiler -cc tcc test source + test: if: always() - needs: [linux, windows] + needs: [linux, windows, v3] runs-on: ubuntu-24.04 steps: - name: Verify required platforms env: LINUX_RESULT: ${{ needs.linux.result }} WINDOWS_RESULT: ${{ needs.windows.result }} + V3_RESULT: ${{ needs.v3.result }} run: | test "$LINUX_RESULT" = success test "$WINDOWS_RESULT" = success + test "$V3_RESULT" = success diff --git a/README.md b/README.md index 97dfdd9..b0544d6 100644 --- a/README.md +++ b/README.md @@ -68,3 +68,11 @@ v -cc msvc test . They cover fixed-width and Exp-Golomb bit reading, truncated input behavior, NAL headers, RBSP look-ahead, integer bit widths, and representative High-profile SPS/PPS parsing used by the player. + +CI also runs strict V3 with TinyCC on Linux, using a pinned compiler snapshot. +The regression samples cover custom SPS/PPS scaling lists and the default-list +flag. Scaling lists are copied explicitly into their fixed storage arrays. + +Callers of `Bitstream.read_scaling_list` should pass the flag as `&flag`, not +`mut &flag`. The pointer itself is not reassigned; only its pointed-to value is +written. This avoids a pointer-to-pointer mismatch in V3-generated C. diff --git a/h264.v b/h264.v index 46a2866..e15febc 100644 --- a/h264.v +++ b/h264.v @@ -371,7 +371,7 @@ pub fn (mut nal NetworkAbstractionLayerHeader) read_nal_header(mut b Bitstream) nal.type = unsafe { NAL_UNIT_TYPE(b.u(5)) } } -pub fn (mut b Bitstream) read_scaling_list(mut scaling_list []i32, size_of_scaling_list i32, mut use_default_scaling_matrix_flag &u32) { +pub fn (mut b Bitstream) read_scaling_list(mut scaling_list []i32, size_of_scaling_list i32, use_default_scaling_matrix_flag &u32) { mut last_scale := i32(8) mut next_scale := i32(8) mut delta_scale := i32(0) @@ -577,11 +577,19 @@ pub fn (mut sps SequenceParameterSet) read_sps(mut b Bitstream) { sps.seq_scaling_list_present_flag[i] = b.u1() if sps.seq_scaling_list_present_flag[i] != 0 { if i < 6 { - b.read_scaling_list(mut sps.scaling_list_4x4[i][0..], 16, mut + mut list := []i32{len: 16} + b.read_scaling_list(mut list, 16, &sps.use_default_scaling_matrix_4x4_flag[i]) + for j in 0 .. 16 { + sps.scaling_list_4x4[i][j] = list[j] + } } else { - b.read_scaling_list(mut sps.scaling_list_8x8[i - 6][0..], 64, mut + mut list := []i32{len: 64} + b.read_scaling_list(mut list, 64, &sps.use_default_scaling_matrix_8x8_flag[i - 6]) + for j in 0 .. 64 { + sps.scaling_list_8x8[i - 6][j] = list[j] + } } } } @@ -676,12 +684,19 @@ pub fn (mut pps PictureParameterSet) read_pps(mut b Bitstream) { pps.pic_scaling_list_present_flag[i] = b.u1() if pps.pic_scaling_list_present_flag[i] != 0 { if i < 6 { - // Make it a slice to get a dynamic array - b.read_scaling_list(mut pps.scaling_list_4x4[i][0..], 16, mut + mut list := []i32{len: 16} + b.read_scaling_list(mut list, 16, &pps.use_default_scaling_matrix_4x4_flag[i]) + for j in 0 .. 16 { + pps.scaling_list_4x4[i][j] = list[j] + } } else { - b.read_scaling_list(mut pps.scaling_list_8x8[i - 6][0..], 64, mut + mut list := []i32{len: 64} + b.read_scaling_list(mut list, 64, &pps.use_default_scaling_matrix_8x8_flag[i - 6]) + for j in 0 .. 64 { + pps.scaling_list_8x8[i - 6][j] = list[j] + } } } } diff --git a/h264_test.v b/h264_test.v index 094ed6f..0e59a18 100644 --- a/h264_test.v +++ b/h264_test.v @@ -1,5 +1,50 @@ module h264 +import encoding.hex + +fn test_custom_sps_scaling_lists_are_stored() { + // FRExt_MMCO4_Sony_B conformance SPS, excluding its NAL header. + data := + hex.decode('64001fad9464763b8ac4444a323b1dc5622225191d8ee2b11114222b373669a844566e6cd35088acdcd9a69444cd1b9bc57c9f93f9bf27c9e4e4cd251a4689c9ebe4fd7f27ebe4f5c9a906c694160964')! + mut stream := Bitstream{} + stream.init(data) + mut sps := SequenceParameterSet{} + sps.read_sps(mut stream) + assert sps.seq_parameter_set_id == 0 + assert sps.scaling_list_4x4[0][..4] == [i32(6), 12, 12, 19] + assert sps.scaling_list_8x8[0][..4] == [i32(6), 10, 10, 13] + for i in 0 .. 6 { + assert sps.scaling_list_4x4[i][15] != 0 + } + for i in 0 .. 2 { + assert sps.scaling_list_8x8[i][63] != 0 + } +} + +fn test_custom_pps_scaling_list_is_stored() { + mut stream := Bitstream{} + stream.init(hex.decode('ce3c7fffe0c0')!) + mut pps := PictureParameterSet{} + pps.read_pps(mut stream) + assert pps.pic_scaling_matrix_present_flag == 1 + for value in pps.scaling_list_4x4[0] { + assert value == 8 + } +} + +fn test_scaling_list_default_flag_points_to_value_not_pointer() { + mut stream := Bitstream{} + // signed Exp-Golomb -8 makes the first nextScale zero. + stream.init([u8(0x08), 0x80]) + mut list := []i32{len: 16} + mut use_default := u32(0) + stream.read_scaling_list(mut list, 16, &use_default) + assert use_default == 1 + for value in list { + assert value == 8 + } +} + fn test_fixed_width_reads_cross_byte_boundaries() { mut stream := Bitstream{} stream.init([u8(0xa5), 0xc0])