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);
}
}