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 <suzp1984@gmail.com>
Co-authored-by: winlin <winlinvip@gmail.com>
This commit is contained in:
Haibo Chen(陈海博) 2025-07-10 21:08:05 +08:00 committed by GitHub
parent 1d91da1989
commit 6208b6fe61
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 298 additions and 44 deletions

View File

@ -7,6 +7,7 @@ The changelog for SRS.
<a name="v7-changes"></a>
## 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.
<a name="v6-changes"></a>
## 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.
<a name="v5-changes"></a>
## 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)

View File

@ -9,6 +9,6 @@
#define VERSION_MAJOR 5
#define VERSION_MINOR 0
#define VERSION_REVISION 223
#define VERSION_REVISION 224
#endif

View File

@ -9,6 +9,6 @@
#define VERSION_MAJOR 6
#define VERSION_MINOR 0
#define VERSION_REVISION 168
#define VERSION_REVISION 169
#endif

View File

@ -9,6 +9,6 @@
#define VERSION_MAJOR 7
#define VERSION_MINOR 0
#define VERSION_REVISION 45
#define VERSION_REVISION 46
#endif

View File

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

View File

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