From 6208b6fe618eed6e3cf8319430335c746c33cc4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Haibo=20Chen=28=E9=99=88=E6=B5=B7=E5=8D=9A=29?= <495810242@qq.com> Date: Thu, 10 Jul 2025 21:08:05 +0800 Subject: [PATCH] Fix H.264 B-frame detection logic to comply with specification. v5.0.224 v6.0.169 v7.0.46 (#4414) For H.264, only when the NAL Type is 1, 2, 3, or 4 is it possible for B-frames to be present; that is, non-IDR pictures and slice data. The current `SrsVideoFrame::parse_avc_bframe()` function uses incorrect logic to determine if a NALU can contain B-frames. The original implementation only checked for specific NALU types (IDR, SPS, PPS) to mark as non-B-frames, but this approach misses many other NALU types that cannot contain B-frames according to the H.264 specification. According to H.264 specification (ISO_IEC_14496-10-AVC-2012.pdf, Table 7-1), B-frames can **only** exist in these specific NALU types: - Type 1: Non-IDR coded slice (`SrsAvcNaluTypeNonIDR`) - Type 2: Coded slice data partition A (`SrsAvcNaluTypeDataPartitionA`) - Type 3: Coded slice data partition B (`SrsAvcNaluTypeDataPartitionB`) - Type 4: Coded slice data partition C (`SrsAvcNaluTypeDataPartitionC`) All other NALU types (IDR=5, SEI=6, SPS=7, PPS=8, AUD=9, etc.) cannot contain B-frames by definition. --------- Co-authored-by: Jacob Su Co-authored-by: winlin --- trunk/doc/CHANGELOG.md | 3 + trunk/src/core/srs_core_version5.hpp | 2 +- trunk/src/core/srs_core_version6.hpp | 2 +- trunk/src/core/srs_core_version7.hpp | 2 +- trunk/src/kernel/srs_kernel_codec.cpp | 3 +- trunk/src/utest/srs_utest_kernel.cpp | 330 ++++++++++++++++++++++---- 6 files changed, 298 insertions(+), 44 deletions(-) diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md index 01144bb74..58c11e4f4 100644 --- a/trunk/doc/CHANGELOG.md +++ b/trunk/doc/CHANGELOG.md @@ -7,6 +7,7 @@ The changelog for SRS. ## SRS 7.0 Changelog +* v7.0, 2025-07-10, Merge [#4414](https://github.com/ossrs/srs/pull/4414): Fix H.264 B-frame detection logic to comply with specification. v7.0.46 (#4414) * v7.0, 2025-07-04, Merge [#4412](https://github.com/ossrs/srs/pull/4412): Refine code and add tests for #4289. v7.0.45 (#4412) * v7.0, 2025-07-04, Merge [#4413](https://github.com/ossrs/srs/pull/4413): RTMP2RTC: Support dual video track for bridge. v7.0.44 (#4413) * v7.0, 2025-07-03, Merge [#4349](https://github.com/ossrs/srs/pull/4349): rtc2rtmp: Support WebRTC-to-RTMP conversion with HEVC. v7.0.43 (#4349) @@ -56,6 +57,7 @@ The changelog for SRS. ## SRS 6.0 Changelog +* v6.0, 2025-07-10, Merge [#4414](https://github.com/ossrs/srs/pull/4414): Fix H.264 B-frame detection logic to comply with specification. v6.0.169 (#4414) * v6.0, 2025-06-04, Merge [#4325](https://github.com/ossrs/srs/pull/4325): fix bug: loop transcoding #3516. v6.0.168 (#4325) * v6.0, 2025-05-29, Merge [#4356](https://github.com/ossrs/srs/pull/4356): RTMP: Use extended timestamp as delta when chunk fmt=1/2. v6.0.167 (#4356) * v6.0, 2025-03-21, Merge [#4303](https://github.com/ossrs/srs/pull/4303): replace values with enums. v6.0.165 (#4303) @@ -227,6 +229,7 @@ The changelog for SRS. ## SRS 5.0 Changelog +* v5.0, 2025-07-10, Merge [#4414](https://github.com/ossrs/srs/pull/4414): Fix H.264 B-frame detection logic to comply with specification. v5.0.224 (#4414) * v5.0, 2025-03-21, Merge [#4303](https://github.com/ossrs/srs/pull/4303): replace values with enums. v5.0.223 (#4303) * v5.0, 2025-03-20, Merge [#4305](https://github.com/ossrs/srs/pull/4305): free sample to prevent memory leak. v5.0.222 (#4305) * v5.0, 2025-03-18, Merge [#4302](https://github.com/ossrs/srs/pull/4302): update geekyeggo/delete-artifact to 5.0.0. v5.0.221 (#4302) diff --git a/trunk/src/core/srs_core_version5.hpp b/trunk/src/core/srs_core_version5.hpp index 489043499..219653f3a 100644 --- a/trunk/src/core/srs_core_version5.hpp +++ b/trunk/src/core/srs_core_version5.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 5 #define VERSION_MINOR 0 -#define VERSION_REVISION 223 +#define VERSION_REVISION 224 #endif diff --git a/trunk/src/core/srs_core_version6.hpp b/trunk/src/core/srs_core_version6.hpp index cc7d160e2..d7db085e5 100644 --- a/trunk/src/core/srs_core_version6.hpp +++ b/trunk/src/core/srs_core_version6.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 6 #define VERSION_MINOR 0 -#define VERSION_REVISION 168 +#define VERSION_REVISION 169 #endif diff --git a/trunk/src/core/srs_core_version7.hpp b/trunk/src/core/srs_core_version7.hpp index 5a48c9b2f..74c774f4f 100644 --- a/trunk/src/core/srs_core_version7.hpp +++ b/trunk/src/core/srs_core_version7.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 7 #define VERSION_MINOR 0 -#define VERSION_REVISION 45 +#define VERSION_REVISION 46 #endif \ No newline at end of file diff --git a/trunk/src/kernel/srs_kernel_codec.cpp b/trunk/src/kernel/srs_kernel_codec.cpp index 22a178c4e..5ac35c6a9 100644 --- a/trunk/src/kernel/srs_kernel_codec.cpp +++ b/trunk/src/kernel/srs_kernel_codec.cpp @@ -749,7 +749,8 @@ srs_error_t SrsVideoFrame::parse_avc_bframe(const SrsSample* sample, bool& is_b_ return srs_error_wrap(err, "parse avc nalu type error"); } - if (nalu_type == SrsAvcNaluTypeIDR || nalu_type == SrsAvcNaluTypeSPS || nalu_type == SrsAvcNaluTypePPS) { + if (nalu_type != SrsAvcNaluTypeNonIDR && nalu_type != SrsAvcNaluTypeDataPartitionA + && nalu_type != SrsAvcNaluTypeDataPartitionB && nalu_type != SrsAvcNaluTypeDataPartitionC) { is_b_frame = false; return err; } diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index 9caebc1df..7d0c3814a 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -3567,92 +3567,342 @@ VOID TEST(KernelCodecTest, AVFrameNoConfig) HELPER_EXPECT_SUCCESS(f.add_sample((char*)"\x05", 1)); } } -VOID TEST(KernelCodecTest, VideoFrameH264) + +VOID TEST(KernelCodecTest, VideoFrameH264_ParseNaluType) { srs_error_t err; - + if (true) { // I Frame uint8_t data[] = {0x05, 0x00, 0x00, 0x00}; SrsSample sample((char*)data, sizeof(data)); - + SrsAvcNaluType nalu_type = SrsAvcNaluTypeForbidden; HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_nalu_type(&sample, nalu_type)); EXPECT_EQ(nalu_type, SrsAvcNaluTypeIDR); - + // P Frame uint8_t data2[] = {0x01, 0x00, 0x00, 0x00}; SrsSample sample2((char*)data2, sizeof(data2)); - + nalu_type = SrsAvcNaluTypeForbidden; HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_nalu_type(&sample2, nalu_type)); EXPECT_EQ(nalu_type, SrsAvcNaluTypeNonIDR); - + // SPS uint8_t data3[] = {0x07, 0x00, 0x00, 0x00}; SrsSample sample3((char*)data3, sizeof(data3)); - + nalu_type = SrsAvcNaluTypeForbidden; HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_nalu_type(&sample3, nalu_type)); EXPECT_EQ(nalu_type, SrsAvcNaluTypeSPS); - + // PPS uint8_t data4[] = {0x08, 0x00, 0x00, 0x00}; SrsSample sample4((char*)data4, sizeof(data4)); - + nalu_type = SrsAvcNaluTypeForbidden; HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_nalu_type(&sample4, nalu_type)); EXPECT_EQ(nalu_type, SrsAvcNaluTypePPS); - + // Empty Sample SrsSample empty_sample(NULL, 0); HELPER_EXPECT_FAILED(SrsVideoFrame::parse_avc_nalu_type(&empty_sample, nalu_type)); } - +} + +VOID TEST(KernelCodecTest, VideoFrameH264_BFrameDetection_AllowedNaluTypes) +{ + srs_error_t err; + if (true) { - // B Frame, slice_type=1(B Frame) - uint8_t data[] = {0x01, 0xA8, 0x00, 0x00}; - SrsSample sample((char*)data, sizeof(data)); - + // Test NALU types that CAN contain B-frames (1, 2, 3, 4) + + // NonIDR NALU (type 1) with B-frame slice_type=1 + uint8_t data_b1[] = {0x01, 0xA8, 0x00, 0x00}; // NALU type 1, slice_type=1 (B) + SrsSample sample_b1((char*)data_b1, sizeof(data_b1)); bool is_b_frame = false; - HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample, is_b_frame)); + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_b1, is_b_frame)); EXPECT_TRUE(is_b_frame); - - // Non-B Frame, slice_type=0(P Frame) - uint8_t data2[] = {0x01, 0x88, 0x00, 0x00}; - SrsSample sample2((char*)data2, sizeof(data2)); - + + // Test that the function correctly processes NALU type 1 (the main case) + + // NonIDR NALU (type 1) with P-frame slice_type=0 + uint8_t data_p1[] = {0x01, 0x88, 0x00, 0x00}; // NALU type 1, slice_type=0 (P) + SrsSample sample_p1((char*)data_p1, sizeof(data_p1)); is_b_frame = true; - HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample2, is_b_frame)); + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_p1, is_b_frame)); EXPECT_FALSE(is_b_frame); - // SPS - uint8_t data3[] = {0x07, 0xA8, 0x00, 0x00}; - SrsSample sample3((char*)data3, sizeof(data3)); - + // NonIDR NALU (type 1) with I-frame slice_type=2 + uint8_t data_i1[] = {0x01, 0x98, 0x00, 0x00}; // NALU type 1, slice_type=2 (I) + SrsSample sample_i1((char*)data_i1, sizeof(data_i1)); is_b_frame = true; - HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample3, is_b_frame)); + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_i1, is_b_frame)); EXPECT_FALSE(is_b_frame); - // PPS - uint8_t data4[] = {0x08, 0xA8, 0x00, 0x00}; - SrsSample sample4((char*)data4, sizeof(data4)); - + // DataPartitionA NALU (type 2) with B-frame slice_type=1 + uint8_t data_dpa_b[] = {0x02, 0xA8, 0x00, 0x00}; // NALU type 2, slice_type=1 (B) + SrsSample sample_dpa_b((char*)data_dpa_b, sizeof(data_dpa_b)); + is_b_frame = false; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_dpa_b, is_b_frame)); + EXPECT_TRUE(is_b_frame); + + // DataPartitionA NALU (type 2) with P-frame slice_type=0 + uint8_t data_dpa_p[] = {0x02, 0x88, 0x00, 0x00}; // NALU type 2, slice_type=0 (P) + SrsSample sample_dpa_p((char*)data_dpa_p, sizeof(data_dpa_p)); is_b_frame = true; - HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample4, is_b_frame)); + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_dpa_p, is_b_frame)); EXPECT_FALSE(is_b_frame); - // IDR - uint8_t data5[] = {0x05, 0xA8, 0x00, 0x00}; - SrsSample sample5((char*)data5, sizeof(data5)); - - is_b_frame = true; - HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample5, is_b_frame)); + // DataPartitionB NALU (type 3) with B-frame slice_type=1 + uint8_t data_dpb_b[] = {0x03, 0xA8, 0x00, 0x00}; // NALU type 3, slice_type=1 (B) + SrsSample sample_dpb_b((char*)data_dpb_b, sizeof(data_dpb_b)); + is_b_frame = false; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_dpb_b, is_b_frame)); + EXPECT_TRUE(is_b_frame); + + // DataPartitionC NALU (type 4) with B-frame slice_type=1 + uint8_t data_dpc_b[] = {0x04, 0xA8, 0x00, 0x00}; // NALU type 4, slice_type=1 (B) + SrsSample sample_dpc_b((char*)data_dpc_b, sizeof(data_dpc_b)); + is_b_frame = false; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_dpc_b, is_b_frame)); + EXPECT_TRUE(is_b_frame); + } +} + +VOID TEST(KernelCodecTest, VideoFrameH264_BFrameDetection_ForbiddenNaluTypes) +{ + srs_error_t err; + + if (true) { + // Test NALU types that CANNOT contain B-frames (should return false immediately) + + // IDR NALU (type 5) - cannot contain B-frames by definition + uint8_t data_idr[] = {0x05, 0xA8, 0x00, 0x00}; // NALU type 5, any slice data + SrsSample sample_idr((char*)data_idr, sizeof(data_idr)); + bool is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_idr, is_b_frame)); EXPECT_FALSE(is_b_frame); - - // Empty Sample + + // SEI NALU (type 6) - cannot contain B-frames + uint8_t data_sei[] = {0x06, 0xA8, 0x00, 0x00}; // NALU type 6 + SrsSample sample_sei((char*)data_sei, sizeof(data_sei)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_sei, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // SPS NALU (type 7) - cannot contain B-frames + uint8_t data_sps[] = {0x07, 0xA8, 0x00, 0x00}; // NALU type 7 + SrsSample sample_sps((char*)data_sps, sizeof(data_sps)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_sps, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // PPS NALU (type 8) - cannot contain B-frames + uint8_t data_pps[] = {0x08, 0xA8, 0x00, 0x00}; // NALU type 8 + SrsSample sample_pps((char*)data_pps, sizeof(data_pps)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_pps, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // AUD NALU (type 9) - cannot contain B-frames + uint8_t data_aud[] = {0x09, 0xA8, 0x00, 0x00}; // NALU type 9 + SrsSample sample_aud((char*)data_aud, sizeof(data_aud)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_aud, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // End of Sequence NALU (type 10) - cannot contain B-frames + uint8_t data_eos[] = {0x0A, 0xA8, 0x00, 0x00}; // NALU type 10 + SrsSample sample_eos((char*)data_eos, sizeof(data_eos)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_eos, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // End of Stream NALU (type 11) - cannot contain B-frames + uint8_t data_eost[] = {0x0B, 0xA8, 0x00, 0x00}; // NALU type 11 + SrsSample sample_eost((char*)data_eost, sizeof(data_eost)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_eost, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // Filler Data NALU (type 12) - cannot contain B-frames + uint8_t data_filler[] = {0x0C, 0xA8, 0x00, 0x00}; // NALU type 12 + SrsSample sample_filler((char*)data_filler, sizeof(data_filler)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_filler, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // SPS Extension NALU (type 13) - cannot contain B-frames + uint8_t data_sps_ext[] = {0x0D, 0xA8, 0x00, 0x00}; // NALU type 13 + SrsSample sample_sps_ext((char*)data_sps_ext, sizeof(data_sps_ext)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_sps_ext, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // Prefix NALU (type 14) - cannot contain B-frames + uint8_t data_prefix[] = {0x0E, 0xA8, 0x00, 0x00}; // NALU type 14 + SrsSample sample_prefix((char*)data_prefix, sizeof(data_prefix)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_prefix, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // Subset SPS NALU (type 15) - cannot contain B-frames + uint8_t data_subset_sps[] = {0x0F, 0xA8, 0x00, 0x00}; // NALU type 15 + SrsSample sample_subset_sps((char*)data_subset_sps, sizeof(data_subset_sps)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_subset_sps, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // Layer Without Partition NALU (type 19) - cannot contain B-frames + uint8_t data_layer[] = {0x13, 0xA8, 0x00, 0x00}; // NALU type 19 + SrsSample sample_layer((char*)data_layer, sizeof(data_layer)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_layer, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // Coded Slice Extension NALU (type 20) - cannot contain B-frames + uint8_t data_slice_ext[] = {0x14, 0xA8, 0x00, 0x00}; // NALU type 20 + SrsSample sample_slice_ext((char*)data_slice_ext, sizeof(data_slice_ext)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_slice_ext, is_b_frame)); + EXPECT_FALSE(is_b_frame); + } +} + +VOID TEST(KernelCodecTest, VideoFrameH264_BFrameDetection_EdgeCases) +{ + srs_error_t err; + + // Test NALU types that CANNOT contain B-frames (should return false immediately) + if (true) { + // IDR NALU (type 5) - cannot contain B-frames by definition + uint8_t data_idr[] = {0x05, 0xA8, 0x00, 0x00}; // NALU type 5, any slice data + SrsSample sample_idr((char*)data_idr, sizeof(data_idr)); + bool is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_idr, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // SEI NALU (type 6) - cannot contain B-frames + uint8_t data_sei[] = {0x06, 0xA8, 0x00, 0x00}; // NALU type 6 + SrsSample sample_sei((char*)data_sei, sizeof(data_sei)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_sei, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // SPS NALU (type 7) - cannot contain B-frames + uint8_t data_sps[] = {0x07, 0xA8, 0x00, 0x00}; // NALU type 7 + SrsSample sample_sps((char*)data_sps, sizeof(data_sps)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_sps, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // PPS NALU (type 8) - cannot contain B-frames + uint8_t data_pps[] = {0x08, 0xA8, 0x00, 0x00}; // NALU type 8 + SrsSample sample_pps((char*)data_pps, sizeof(data_pps)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_pps, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // AUD NALU (type 9) - cannot contain B-frames + uint8_t data_aud[] = {0x09, 0xA8, 0x00, 0x00}; // NALU type 9 + SrsSample sample_aud((char*)data_aud, sizeof(data_aud)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_aud, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // End of Sequence NALU (type 10) - cannot contain B-frames + uint8_t data_eos[] = {0x0A, 0xA8, 0x00, 0x00}; // NALU type 10 + SrsSample sample_eos((char*)data_eos, sizeof(data_eos)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_eos, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // End of Stream NALU (type 11) - cannot contain B-frames + uint8_t data_eost[] = {0x0B, 0xA8, 0x00, 0x00}; // NALU type 11 + SrsSample sample_eost((char*)data_eost, sizeof(data_eost)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_eost, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // Filler Data NALU (type 12) - cannot contain B-frames + uint8_t data_filler[] = {0x0C, 0xA8, 0x00, 0x00}; // NALU type 12 + SrsSample sample_filler((char*)data_filler, sizeof(data_filler)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_filler, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // SPS Extension NALU (type 13) - cannot contain B-frames + uint8_t data_sps_ext[] = {0x0D, 0xA8, 0x00, 0x00}; // NALU type 13 + SrsSample sample_sps_ext((char*)data_sps_ext, sizeof(data_sps_ext)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_sps_ext, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // Prefix NALU (type 14) - cannot contain B-frames + uint8_t data_prefix[] = {0x0E, 0xA8, 0x00, 0x00}; // NALU type 14 + SrsSample sample_prefix((char*)data_prefix, sizeof(data_prefix)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_prefix, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // Subset SPS NALU (type 15) - cannot contain B-frames + uint8_t data_subset_sps[] = {0x0F, 0xA8, 0x00, 0x00}; // NALU type 15 + SrsSample sample_subset_sps((char*)data_subset_sps, sizeof(data_subset_sps)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_subset_sps, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // Layer Without Partition NALU (type 19) - cannot contain B-frames + uint8_t data_layer[] = {0x13, 0xA8, 0x00, 0x00}; // NALU type 19 + SrsSample sample_layer((char*)data_layer, sizeof(data_layer)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_layer, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // Coded Slice Extension NALU (type 20) - cannot contain B-frames + uint8_t data_slice_ext[] = {0x14, 0xA8, 0x00, 0x00}; // NALU type 20 + SrsSample sample_slice_ext((char*)data_slice_ext, sizeof(data_slice_ext)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_slice_ext, is_b_frame)); + EXPECT_FALSE(is_b_frame); + } + + // Test edge cases and error conditions + if (true) { + // Empty Sample - should fail SrsSample empty_sample(NULL, 0); + bool is_b_frame = false; HELPER_EXPECT_FAILED(SrsVideoFrame::parse_avc_bframe(&empty_sample, is_b_frame)); + + // Sample too small for slice parsing (only NALU header) - should fail for slice types + uint8_t data_small[] = {0x01}; // NALU type 1, but no slice data + SrsSample sample_small((char*)data_small, sizeof(data_small)); + is_b_frame = false; + HELPER_EXPECT_FAILED(SrsVideoFrame::parse_avc_bframe(&sample_small, is_b_frame)); + + // Test basic slice types for NonIDR NALU using known working patterns + // P frame (slice_type=0) + uint8_t data_p[] = {0x01, 0x88, 0x00, 0x00}; // slice_type=0 + SrsSample sample_p((char*)data_p, sizeof(data_p)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_p, is_b_frame)); + EXPECT_FALSE(is_b_frame); + + // B frame (slice_type=1) + uint8_t data_b[] = {0x01, 0xA8, 0x00, 0x00}; // slice_type=1 + SrsSample sample_b((char*)data_b, sizeof(data_b)); + is_b_frame = false; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_b, is_b_frame)); + EXPECT_TRUE(is_b_frame); + + // I frame (slice_type=2) + uint8_t data_i[] = {0x01, 0x98, 0x00, 0x00}; // slice_type=2 + SrsSample sample_i((char*)data_i, sizeof(data_i)); + is_b_frame = true; + HELPER_EXPECT_SUCCESS(SrsVideoFrame::parse_avc_bframe(&sample_i, is_b_frame)); + EXPECT_FALSE(is_b_frame); } }