// // Copyright (c) 2013-2025 The SRS Authors // // SPDX-License-Identifier: MIT // #include using namespace std; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern bool srs_is_valid_jsonp_callback(std::string callback); extern uint32_t srs_crc32_ieee(const void *buf, int size, uint32_t previous); VOID TEST(ProtocolHttpTest, JsonpCallbackName) { EXPECT_TRUE(srs_is_valid_jsonp_callback("")); EXPECT_TRUE(srs_is_valid_jsonp_callback("callback")); EXPECT_TRUE(srs_is_valid_jsonp_callback("Callback")); EXPECT_TRUE(srs_is_valid_jsonp_callback("Callback1234567890")); EXPECT_TRUE(srs_is_valid_jsonp_callback("Callback-1234567890")); EXPECT_TRUE(srs_is_valid_jsonp_callback("Callback_1234567890")); EXPECT_TRUE(srs_is_valid_jsonp_callback("Callback.1234567890")); EXPECT_TRUE(srs_is_valid_jsonp_callback("Callback1234567890-_.")); EXPECT_FALSE(srs_is_valid_jsonp_callback("callback()//")); EXPECT_FALSE(srs_is_valid_jsonp_callback("callback!")); EXPECT_FALSE(srs_is_valid_jsonp_callback("callback;")); } // Mock class implementations MockConnection::MockConnection(std::string ip) : ip_(ip) { } MockConnection::~MockConnection() { } std::string MockConnection::remote_ip() { return ip_; } std::string MockConnection::desc() { return "MockConnection"; } const SrsContextId &MockConnection::get_id() { static SrsContextId id = SrsContextId(); return id; } MockExpire::MockExpire() : expired_(false) { } MockExpire::~MockExpire() { } void MockExpire::expire() { expired_ = true; } VOID TEST(ProtocolConnTest, ISrsConnectionInterface) { MockConnection conn("192.168.1.100"); EXPECT_STREQ("192.168.1.100", conn.remote_ip().c_str()); EXPECT_STREQ("MockConnection", conn.desc().c_str()); } VOID TEST(ProtocolConnTest, ISrsExpireInterface) { MockExpire expire; EXPECT_FALSE(expire.expired_); expire.expire(); EXPECT_TRUE(expire.expired_); } VOID TEST(ProtocolIOTest, ISrsProtocolReaderInterface) { // Test that interfaces are properly defined // These are abstract classes, so we just verify they exist ISrsProtocolReader *reader = NULL; ISrsProtocolWriter *writer = NULL; ISrsProtocolReadWriter *rw = NULL; // Just verify the pointers can be assigned EXPECT_TRUE(reader == NULL); EXPECT_TRUE(writer == NULL); EXPECT_TRUE(rw == NULL); } VOID TEST(ProtocolFormatTest, SrsRtmpFormatBasic) { srs_error_t err = srs_success; SrsRtmpFormat format; // Test metadata handling SrsOnMetaDataPacket *meta = new SrsOnMetaDataPacket(); SrsUniquePtr meta_uptr(meta); HELPER_EXPECT_SUCCESS(format.on_metadata(meta)); } VOID TEST(ProtocolFormatTest, SrsRtmpFormatAudioVideo) { srs_error_t err = srs_success; SrsRtmpFormat format; // Test audio packet handling if (true) { SrsMediaPacket *audio = new SrsMediaPacket(); SrsUniquePtr audio_uptr(audio); char *audio_data = new char[6]; audio_data[0] = 0xaf; audio_data[1] = 0x01; audio_data[2] = 0x00; audio_data[3] = 0x01; audio_data[4] = 0x02; audio_data[5] = 0x03; audio->wrap(audio_data, 6); audio->timestamp_ = 1000; audio->message_type_ = SrsFrameTypeAudio; HELPER_EXPECT_SUCCESS(format.on_audio(audio)); } // Test video packet handling with AVC sequence header if (true) { SrsMediaPacket *video = new SrsMediaPacket(); SrsUniquePtr video_uptr(video); // Create a proper AVC sequence header: 0x17 (keyframe + AVC), 0x00 (AVC sequence header) // followed by minimal AVC decoder configuration record char *video_data = new char[20]; video_data[0] = 0x17; // keyframe + AVC video_data[1] = 0x00; // AVC sequence header video_data[2] = 0x00; video_data[3] = 0x00; video_data[4] = 0x00; // composition time video_data[5] = 0x01; // configuration version video_data[6] = 0x64; // profile video_data[7] = 0x00; // profile compatibility video_data[8] = 0x1f; // level video_data[9] = 0xff; // NALU length size - 1 video_data[10] = 0xe1; // number of SPS video_data[11] = 0x00; video_data[12] = 0x07; // SPS length video_data[13] = 0x67; video_data[14] = 0x64; video_data[15] = 0x00; // SPS data video_data[16] = 0x1f; video_data[17] = 0xac; video_data[18] = 0xd9; video_data[19] = 0x40; video->wrap(video_data, 20); video->timestamp_ = 2000; video->message_type_ = SrsFrameTypeVideo; // Video processing may fail due to complex AVC validation - just test that method exists srs_error_t video_err = format.on_video(video); srs_freep(video_err); // Don't assert success since AVC decoder configuration validation is complex } // Test direct timestamp-based calls - these may fail due to codec validation // but we just test that the methods exist and don't crash char test_data[] = {0x01, 0x02, 0x03, 0x04}; srs_error_t audio_err = format.on_audio(3000, test_data, sizeof(test_data)); srs_error_t video_err = format.on_video(4000, test_data, sizeof(test_data)); srs_freep(audio_err); srs_freep(video_err); // Don't assert success since codec validation may fail with test data } VOID TEST(ProtocolJsonTest, SrsJsonAnyBasic) { // Test string creation and conversion if (true) { SrsJsonAny *str = SrsJsonAny::str("hello world"); SrsUniquePtr str_uptr(str); EXPECT_TRUE(str->is_string()); EXPECT_FALSE(str->is_boolean()); EXPECT_FALSE(str->is_integer()); EXPECT_FALSE(str->is_number()); EXPECT_FALSE(str->is_object()); EXPECT_FALSE(str->is_array()); EXPECT_FALSE(str->is_null()); EXPECT_STREQ("hello world", str->to_str().c_str()); } // Test boolean creation and conversion if (true) { SrsJsonAny *boolean = SrsJsonAny::boolean(true); SrsUniquePtr boolean_uptr(boolean); EXPECT_FALSE(boolean->is_string()); EXPECT_TRUE(boolean->is_boolean()); EXPECT_FALSE(boolean->is_integer()); EXPECT_FALSE(boolean->is_number()); EXPECT_FALSE(boolean->is_object()); EXPECT_FALSE(boolean->is_array()); EXPECT_FALSE(boolean->is_null()); EXPECT_TRUE(boolean->to_boolean()); } // Test integer creation and conversion if (true) { SrsJsonAny *integer = SrsJsonAny::integer(12345); SrsUniquePtr integer_uptr(integer); EXPECT_FALSE(integer->is_string()); EXPECT_FALSE(integer->is_boolean()); EXPECT_TRUE(integer->is_integer()); EXPECT_FALSE(integer->is_number()); EXPECT_FALSE(integer->is_object()); EXPECT_FALSE(integer->is_array()); EXPECT_FALSE(integer->is_null()); EXPECT_EQ(12345, integer->to_integer()); } // Test number creation and conversion if (true) { SrsJsonAny *number = SrsJsonAny::number(3.14159); SrsUniquePtr number_uptr(number); EXPECT_FALSE(number->is_string()); EXPECT_FALSE(number->is_boolean()); EXPECT_FALSE(number->is_integer()); EXPECT_TRUE(number->is_number()); EXPECT_FALSE(number->is_object()); EXPECT_FALSE(number->is_array()); EXPECT_FALSE(number->is_null()); EXPECT_NEAR(3.14159, number->to_number(), 0.00001); } // Test null creation and conversion if (true) { SrsJsonAny *null_val = SrsJsonAny::null(); SrsUniquePtr null_uptr(null_val); EXPECT_FALSE(null_val->is_string()); EXPECT_FALSE(null_val->is_boolean()); EXPECT_FALSE(null_val->is_integer()); EXPECT_FALSE(null_val->is_number()); EXPECT_FALSE(null_val->is_object()); EXPECT_FALSE(null_val->is_array()); EXPECT_TRUE(null_val->is_null()); } } VOID TEST(ProtocolJsonTest, SrsJsonObjectArray) { // Test object creation if (true) { SrsJsonObject *obj = SrsJsonAny::object(); SrsUniquePtr obj_uptr(obj); EXPECT_FALSE(obj->is_string()); EXPECT_FALSE(obj->is_boolean()); EXPECT_FALSE(obj->is_integer()); EXPECT_FALSE(obj->is_number()); EXPECT_TRUE(obj->is_object()); EXPECT_FALSE(obj->is_array()); EXPECT_FALSE(obj->is_null()); SrsJsonObject *converted = obj->to_object(); EXPECT_TRUE(converted != NULL); EXPECT_EQ(obj, converted); } // Test array creation if (true) { SrsJsonArray *arr = SrsJsonAny::array(); SrsUniquePtr arr_uptr(arr); EXPECT_FALSE(arr->is_string()); EXPECT_FALSE(arr->is_boolean()); EXPECT_FALSE(arr->is_integer()); EXPECT_FALSE(arr->is_number()); EXPECT_FALSE(arr->is_object()); EXPECT_TRUE(arr->is_array()); EXPECT_FALSE(arr->is_null()); SrsJsonArray *converted = arr->to_array(); EXPECT_TRUE(converted != NULL); EXPECT_EQ(arr, converted); } } VOID TEST(ProtocolJsonTest, SrsJsonLoads) { // Test loading simple JSON string if (true) { SrsJsonAny *json = SrsJsonAny::loads("{\"name\":\"test\",\"value\":123}"); if (json) { SrsUniquePtr json_uptr(json); EXPECT_TRUE(json->is_object()); } } // Test loading invalid JSON - should return NULL if (true) { SrsJsonAny *json = SrsJsonAny::loads("invalid json"); EXPECT_TRUE(json == NULL); } // Test loading empty string - should return NULL if (true) { SrsJsonAny *json = SrsJsonAny::loads(""); EXPECT_TRUE(json == NULL); } } VOID TEST(ProtocolRawAvcTest, SrsRawH264StreamBasic) { SrsRawH264Stream h264; // Test basic functionality - these methods should exist and not crash // We can't easily test the full functionality without complex H.264 data // Test SPS/PPS detection with minimal data - just test that methods don't crash char test_frame[] = {0x00, 0x00, 0x00, 0x01, 0x67}; // Minimal SPS-like data bool is_sps = h264.is_sps(test_frame, sizeof(test_frame)); bool is_pps = h264.is_pps(test_frame, sizeof(test_frame)); (void)is_sps; (void)is_pps; // Don't assert specific results since frame detection is complex char pps_frame[] = {0x00, 0x00, 0x00, 0x01, 0x68}; // Minimal PPS-like data bool is_sps2 = h264.is_sps(pps_frame, sizeof(pps_frame)); bool is_pps2 = h264.is_pps(pps_frame, sizeof(pps_frame)); (void)is_sps2; (void)is_pps2; // Don't assert specific results since frame detection is complex } VOID TEST(ProtocolRawAvcTest, SrsRawHEVCStreamBasic) { SrsRawHEVCStream hevc; // Test basic functionality for HEVC - just test that methods don't crash // Test VPS/SPS/PPS detection with minimal data char vps_frame[] = {0x00, 0x00, 0x00, 0x01, 0x40}; // Minimal VPS-like data (NALU type 32) char sps_frame[] = {0x00, 0x00, 0x00, 0x01, 0x42}; // Minimal SPS-like data (NALU type 33) char pps_frame[] = {0x00, 0x00, 0x00, 0x01, 0x44}; // Minimal PPS-like data (NALU type 34) // Test that methods don't crash - don't assert specific results since frame detection is complex bool is_vps1 = hevc.is_vps(vps_frame, sizeof(vps_frame)); bool is_sps1 = hevc.is_sps(vps_frame, sizeof(vps_frame)); bool is_pps1 = hevc.is_pps(vps_frame, sizeof(vps_frame)); (void)is_vps1; (void)is_sps1; (void)is_pps1; bool is_vps2 = hevc.is_vps(sps_frame, sizeof(sps_frame)); bool is_sps2 = hevc.is_sps(sps_frame, sizeof(sps_frame)); bool is_pps2 = hevc.is_pps(sps_frame, sizeof(sps_frame)); (void)is_vps2; (void)is_sps2; (void)is_pps2; bool is_vps3 = hevc.is_vps(pps_frame, sizeof(pps_frame)); bool is_sps3 = hevc.is_sps(pps_frame, sizeof(pps_frame)); bool is_pps3 = hevc.is_pps(pps_frame, sizeof(pps_frame)); (void)is_vps3; (void)is_sps3; (void)is_pps3; } VOID TEST(ProtocolRawAvcTest, SrsRawHEVCStreamVpsDemux) { srs_error_t err = srs_success; SrsRawHEVCStream hevc; // Test VPS demux with valid VPS data if (true) { // Create VPS NALU data (NALU type 32 = 0x40) unsigned char vps_data[] = {0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x5d, 0xac, 0x09}; std::string vps_output; HELPER_EXPECT_SUCCESS(hevc.vps_demux((char *)vps_data, sizeof(vps_data), vps_output)); // Should copy the VPS data EXPECT_EQ(sizeof(vps_data), vps_output.length()); EXPECT_EQ(0, memcmp(vps_data, vps_output.data(), sizeof(vps_data))); } // Test VPS demux with empty data - should fail if (true) { std::string vps_output; HELPER_EXPECT_FAILED(hevc.vps_demux(NULL, 0, vps_output)); } // Test VPS demux with minimal data if (true) { unsigned char minimal_vps[] = {0x40}; std::string vps_output; HELPER_EXPECT_SUCCESS(hevc.vps_demux((char *)minimal_vps, sizeof(minimal_vps), vps_output)); EXPECT_EQ(sizeof(minimal_vps), vps_output.length()); } } VOID TEST(ProtocolRawAvcTest, SrsRawHEVCStreamSpsDemux) { srs_error_t err = srs_success; SrsRawHEVCStream hevc; // Test SPS demux with valid SPS data if (true) { // Create SPS NALU data (NALU type 33 = 0x42) unsigned char sps_data[] = {0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x5d, 0xa0, 0x02, 0x80, 0x80, 0x2d, 0x16, 0x59, 0x59, 0xa4, 0x93, 0x2b, 0xc0, 0x5a, 0x70, 0x80, 0x00, 0x01, 0xf4, 0x80, 0x00, 0x5d, 0xc0, 0x47, 0xe1, 0x81, 0x65, 0x80}; std::string sps_output; HELPER_EXPECT_SUCCESS(hevc.sps_demux((char *)sps_data, sizeof(sps_data), sps_output)); // Should copy the SPS data EXPECT_EQ(sizeof(sps_data), sps_output.length()); EXPECT_EQ(0, memcmp(sps_data, sps_output.data(), sizeof(sps_data))); } // Test SPS demux with insufficient data (less than 4 bytes) - should succeed but return empty if (true) { unsigned char short_sps[] = {0x42, 0x01, 0x01}; std::string sps_output; HELPER_EXPECT_SUCCESS(hevc.sps_demux((char *)short_sps, sizeof(short_sps), sps_output)); EXPECT_TRUE(sps_output.empty()); } // Test SPS demux with exactly 4 bytes if (true) { unsigned char min_sps[] = {0x42, 0x01, 0x01, 0x01}; std::string sps_output; HELPER_EXPECT_SUCCESS(hevc.sps_demux((char *)min_sps, sizeof(min_sps), sps_output)); EXPECT_EQ(sizeof(min_sps), sps_output.length()); } } VOID TEST(ProtocolRawAvcTest, SrsRawHEVCStreamPpsDemux) { srs_error_t err = srs_success; SrsRawHEVCStream hevc; // Test PPS demux with valid PPS data if (true) { // Create PPS NALU data (NALU type 34 = 0x44) unsigned char pps_data[] = {0x44, 0x01, 0xc1, 0x73, 0xd1, 0x89, 0x00}; std::string pps_output; HELPER_EXPECT_SUCCESS(hevc.pps_demux((char *)pps_data, sizeof(pps_data), pps_output)); // Should copy the PPS data EXPECT_EQ(sizeof(pps_data), pps_output.length()); EXPECT_EQ(0, memcmp(pps_data, pps_output.data(), sizeof(pps_data))); } // Test PPS demux with empty data - should fail if (true) { std::string pps_output; HELPER_EXPECT_FAILED(hevc.pps_demux(NULL, 0, pps_output)); } // Test PPS demux with minimal data if (true) { unsigned char minimal_pps[] = {0x44}; std::string pps_output; HELPER_EXPECT_SUCCESS(hevc.pps_demux((char *)minimal_pps, sizeof(minimal_pps), pps_output)); EXPECT_EQ(sizeof(minimal_pps), pps_output.length()); } } VOID TEST(ProtocolRawAvcTest, SrsRawHEVCStreamMuxSequenceHeader) { srs_error_t err = srs_success; SrsRawHEVCStream hevc; // Test mux_sequence_header with valid HEVC VPS, SPS, and PPS data if (true) { // Create valid HEVC VPS data that will pass demux validation // VPS NALU: forbidden_zero_bit(1) + nal_unit_type(6) + nuh_layer_id(6) + nuh_temporal_id_plus1(3) + RBSP // NALU type 32 (VPS) = 0x40 (32 << 1), nuh_layer_id=0, nuh_temporal_id_plus1=1 unsigned char vps_raw[] = { 0x40, 0x01, // NALU header: type=32(VPS), layer_id=0, temporal_id=1 0x0C, 0x01, 0xFF, 0xFF, 0x01, 0x60, 0x00, 0x00, // VPS RBSP data 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x5D, 0xAC, 0x09}; std::string vps_data((char *)vps_raw, sizeof(vps_raw)); // Create valid HEVC SPS data that will pass demux validation // SPS NALU: NALU type 33 (SPS) = 0x42 (33 << 1) unsigned char sps_raw[] = { 0x42, 0x01, // NALU header: type=33(SPS), layer_id=0, temporal_id=1 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, // SPS RBSP data 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x5D, 0xA0, 0x02, 0x80, 0x80, 0x2D, 0x16, 0x59, 0x59, 0xA4, 0x93, 0x2B, 0xC0, 0x5A, 0x70, 0x80}; std::string sps_data((char *)sps_raw, sizeof(sps_raw)); // Create valid HEVC PPS data // PPS NALU: NALU type 34 (PPS) = 0x44 (34 << 1) std::vector pps_list; unsigned char pps_raw[] = { 0x44, 0x01, // NALU header: type=34(PPS), layer_id=0, temporal_id=1 0xC1, 0x73, 0xD1, 0x89, 0x00 // PPS RBSP data }; pps_list.push_back(std::string((char *)pps_raw, sizeof(pps_raw))); std::string hvcC_output; // This should now succeed with valid HEVC data and cover the configuration record generation HELPER_EXPECT_SUCCESS(hevc.mux_sequence_header(vps_data, sps_data, pps_list, hvcC_output)); // Should produce non-empty HEVCDecoderConfigurationRecord EXPECT_FALSE(hvcC_output.empty()); EXPECT_GT(hvcC_output.length(), 23); // Minimum HEVC configuration record size // Verify HEVCDecoderConfigurationRecord structure const char *data = hvcC_output.data(); EXPECT_EQ(0x01, (unsigned char)data[0]); // configurationVersion = 1 // Verify the configuration record contains our VPS, SPS, PPS data // The exact structure depends on the implementation, but it should be significantly larger than header EXPECT_GT(hvcC_output.length(), vps_data.length() + sps_data.length() + pps_list[0].length()); } // Test mux_sequence_header with empty VPS - should fail if (true) { std::string empty_vps; std::string sps_data = std::string("\x42\x01\x01\x01\x60", 5); std::vector pps_list; pps_list.push_back(std::string("\x44\x01", 2)); std::string hvcC_output; srs_error_t mux_err = hevc.mux_sequence_header(empty_vps, sps_data, pps_list, hvcC_output); HELPER_EXPECT_FAILED(mux_err); // Should fail due to empty VPS } // Test mux_sequence_header with empty PPS list - should fail if (true) { std::string vps_data = std::string("\x40\x01\x0c", 3); std::string sps_data = std::string("\x42\x01\x01\x01\x60", 5); std::vector empty_pps_list; std::string hvcC_output; srs_error_t mux_err = hevc.mux_sequence_header(vps_data, sps_data, empty_pps_list, hvcC_output); HELPER_EXPECT_FAILED(mux_err); // Should fail due to empty PPS list } // Test mux_sequence_header with multiple PPS if (true) { std::string vps_data = std::string("\x40\x01", 2); std::string sps_data = std::string("\x42\x01\x01\x01", 4); std::vector pps_list; pps_list.push_back(std::string("\x44\x01", 2)); pps_list.push_back(std::string("\x44\x02", 2)); pps_list.push_back(std::string("\x44\x03", 2)); std::string hvcC_output; // This may fail due to HEVC validation but shouldn't crash srs_error_t mux_err = hevc.mux_sequence_header(vps_data, sps_data, pps_list, hvcC_output); srs_freep(mux_err); // Don't assert success since HEVC validation is complex // Test passed if no crash occurred EXPECT_TRUE(true); } // Test mux_sequence_header with different HEVC profile/level to cover more configuration record generation if (true) { // Create VPS with different profile space and tier flag unsigned char vps_raw2[] = { 0x40, 0x01, // NALU header: type=32(VPS) 0x2C, 0x01, 0xFF, 0xFF, 0x02, 0x20, 0x00, 0x00, // VPS RBSP with different profile_space=1, tier_flag=1 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0xAC, 0x09}; std::string vps_data2((char *)vps_raw2, sizeof(vps_raw2)); // Create SPS with different parameters unsigned char sps_raw2[] = { 0x42, 0x01, // NALU header: type=33(SPS) 0x02, 0x20, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, // SPS RBSP with different profile_idc 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0xA0, 0x03, 0x50, 0x80, 0x32, 0x16, 0x59, 0x59, 0xA4, 0x93, 0x2B, 0xC0, 0x40, 0x40, 0x40, 0x80}; std::string sps_data2((char *)sps_raw2, sizeof(sps_raw2)); std::vector pps_list2; unsigned char pps_raw2[] = { 0x44, 0x01, // NALU header: type=34(PPS) 0xC2, 0x73, 0xD1, 0x89, 0x10 // PPS RBSP data }; pps_list2.push_back(std::string((char *)pps_raw2, sizeof(pps_raw2))); std::string hvcC_output2; // This should succeed and generate different configuration record HELPER_EXPECT_SUCCESS(hevc.mux_sequence_header(vps_data2, sps_data2, pps_list2, hvcC_output2)); // Should produce non-empty HEVCDecoderConfigurationRecord EXPECT_FALSE(hvcC_output2.empty()); EXPECT_GT(hvcC_output2.length(), 23); // Minimum HEVC configuration record size // Verify HEVCDecoderConfigurationRecord structure const char *data2 = hvcC_output2.data(); EXPECT_EQ(0x01, (unsigned char)data2[0]); // configurationVersion = 1 // The second byte should contain profile_space, tier_flag, and profile_idc // This tests the bit manipulation code: temp8bits |= ((hevc_info->general_profile_space_ & 0x03) << 6); uint8_t profile_byte = (unsigned char)data2[1]; uint8_t profile_space = (profile_byte >> 6) & 0x03; uint8_t tier_flag = (profile_byte >> 5) & 0x01; uint8_t profile_idc = profile_byte & 0x1f; // These values should be extracted from the VPS/SPS parsing EXPECT_GE(profile_space, 0); EXPECT_LE(profile_space, 3); EXPECT_GE(tier_flag, 0); EXPECT_LE(tier_flag, 1); EXPECT_GE(profile_idc, 0); EXPECT_LE(profile_idc, 31); } } VOID TEST(ProtocolRawAvcTest, SrsRawHEVCStreamMuxIpbFrame) { srs_error_t err = srs_success; SrsRawHEVCStream hevc; // Test mux_ipb_frame with valid frame data if (true) { // Create test HEVC frame data (IDR slice) unsigned char frame_data[] = {0x26, 0x01, 0xaf, 0x06, 0xb8, 0x63, 0xef, 0x3a, 0x7f, 0x3c, 0x00, 0x01, 0x00, 0x80}; std::string ibp_output; HELPER_EXPECT_SUCCESS(hevc.mux_ipb_frame((char *)frame_data, sizeof(frame_data), ibp_output)); // Should produce frame with 4-byte length prefix + frame data EXPECT_EQ(4 + sizeof(frame_data), ibp_output.length()); // Check length prefix (big-endian) const char *data = ibp_output.data(); uint32_t length = ((unsigned char)data[0] << 24) | ((unsigned char)data[1] << 16) | ((unsigned char)data[2] << 8) | (unsigned char)data[3]; EXPECT_EQ(sizeof(frame_data), length); // Check frame data follows length prefix EXPECT_EQ(0, memcmp(frame_data, data + 4, sizeof(frame_data))); } // Test mux_ipb_frame with empty frame - should still work if (true) { unsigned char empty_frame[] = {}; std::string ibp_output; HELPER_EXPECT_SUCCESS(hevc.mux_ipb_frame((char *)empty_frame, 0, ibp_output)); // Should produce only 4-byte length prefix with zero length EXPECT_EQ(4, ibp_output.length()); const char *data = ibp_output.data(); uint32_t length = ((unsigned char)data[0] << 24) | ((unsigned char)data[1] << 16) | ((unsigned char)data[2] << 8) | (unsigned char)data[3]; EXPECT_EQ(0, length); } // Test mux_ipb_frame with large frame if (true) { unsigned char large_frame[1000]; for (int i = 0; i < 1000; i++) { large_frame[i] = i % 256; } std::string ibp_output; HELPER_EXPECT_SUCCESS(hevc.mux_ipb_frame((char *)large_frame, sizeof(large_frame), ibp_output)); // Should produce frame with 4-byte length prefix + frame data EXPECT_EQ(4 + sizeof(large_frame), ibp_output.length()); // Check length prefix const char *data = ibp_output.data(); uint32_t length = ((unsigned char)data[0] << 24) | ((unsigned char)data[1] << 16) | ((unsigned char)data[2] << 8) | (unsigned char)data[3]; EXPECT_EQ(sizeof(large_frame), length); } // Test mux_ipb_frame with different HEVC NALU types if (true) { // Test with P-frame (TRAIL_R NALU type 1) unsigned char p_frame[] = {0x02, 0x01, 0x50, 0x80, 0x12, 0x34, 0x56, 0x78}; std::string ibp_output; HELPER_EXPECT_SUCCESS(hevc.mux_ipb_frame((char *)p_frame, sizeof(p_frame), ibp_output)); // Verify output format EXPECT_EQ(4 + sizeof(p_frame), ibp_output.length()); const char *data = ibp_output.data(); uint32_t length = ((unsigned char)data[0] << 24) | ((unsigned char)data[1] << 16) | ((unsigned char)data[2] << 8) | (unsigned char)data[3]; EXPECT_EQ(sizeof(p_frame), length); EXPECT_EQ(0, memcmp(p_frame, data + 4, sizeof(p_frame))); } // Test mux_ipb_frame with B-frame (TSA_N NALU type 2) if (true) { unsigned char b_frame[] = {0x04, 0x01, 0x60, 0x90, 0xab, 0xcd, 0xef}; std::string ibp_output; HELPER_EXPECT_SUCCESS(hevc.mux_ipb_frame((char *)b_frame, sizeof(b_frame), ibp_output)); // Verify output format EXPECT_EQ(4 + sizeof(b_frame), ibp_output.length()); const char *data = ibp_output.data(); uint32_t length = ((unsigned char)data[0] << 24) | ((unsigned char)data[1] << 16) | ((unsigned char)data[2] << 8) | (unsigned char)data[3]; EXPECT_EQ(sizeof(b_frame), length); EXPECT_EQ(0, memcmp(b_frame, data + 4, sizeof(b_frame))); } } VOID TEST(ProtocolRawAvcTest, SrsRawHEVCStreamMuxAvc2flv) { srs_error_t err = srs_success; SrsRawHEVCStream hevc; // Test mux_avc2flv with sequence header if (true) { std::string video_data = std::string("\x01\x64\x00\x20\xff\xe1\x00\x19\x67\x64\x00\x20", 12); int8_t frame_type = 1; // keyframe int8_t avc_packet_type = 0; // sequence header uint32_t dts = 1000; uint32_t pts = 1000; char *flv_data = NULL; int nb_flv = 0; HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv(video_data, frame_type, avc_packet_type, dts, pts, &flv_data, &nb_flv)); // Should produce FLV packet with 5-byte header + video data EXPECT_TRUE(flv_data != NULL); EXPECT_EQ(5 + video_data.length(), nb_flv); if (flv_data) { // Check FLV header format EXPECT_EQ((frame_type << 4) | 12, (uint8_t)flv_data[0]); // frame_type | SrsVideoCodecIdHEVC(12) EXPECT_EQ(avc_packet_type, flv_data[1]); // AVCPacketType // Check composition time (CTS = PTS - DTS = 0) uint32_t cts = ((unsigned char)flv_data[2] << 16) | ((unsigned char)flv_data[3] << 8) | (unsigned char)flv_data[4]; EXPECT_EQ(0, cts); // Check video data follows header EXPECT_EQ(0, memcmp(video_data.data(), flv_data + 5, video_data.length())); delete[] flv_data; } } // Test mux_avc2flv with NALU frame if (true) { std::string video_data = std::string("\x00\x00\x00\x0e\x26\x01\xaf\x06\xb8\x63\xef\x3a\x7f\x3c\x00\x01\x00\x80", 18); int8_t frame_type = 1; // keyframe int8_t avc_packet_type = 1; // NALU uint32_t dts = 2000; uint32_t pts = 2100; // PTS > DTS char *flv_data = NULL; int nb_flv = 0; HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv(video_data, frame_type, avc_packet_type, dts, pts, &flv_data, &nb_flv)); EXPECT_TRUE(flv_data != NULL); EXPECT_EQ(5 + video_data.length(), nb_flv); if (flv_data) { // Check composition time (CTS = PTS - DTS = 100) uint32_t cts = ((unsigned char)flv_data[2] << 16) | ((unsigned char)flv_data[3] << 8) | (unsigned char)flv_data[4]; EXPECT_EQ(100, cts); delete[] flv_data; } } // Test mux_avc2flv with inter frame if (true) { std::string video_data = std::string("\x00\x00\x00\x08\x02\x01\xd0\x80\x93\x25\x88\x84", 12); int8_t frame_type = 2; // inter frame int8_t avc_packet_type = 1; // NALU uint32_t dts = 3000; uint32_t pts = 3000; char *flv_data = NULL; int nb_flv = 0; HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv(video_data, frame_type, avc_packet_type, dts, pts, &flv_data, &nb_flv)); EXPECT_TRUE(flv_data != NULL); if (flv_data) { // Check frame type in FLV header EXPECT_EQ((frame_type << 4) | 12, (uint8_t)flv_data[0]); // inter frame | HEVC codec delete[] flv_data; } } // Test mux_avc2flv with empty video data if (true) { std::string empty_video_data; int8_t frame_type = 1; // keyframe int8_t avc_packet_type = 0; // sequence header uint32_t dts = 4000; uint32_t pts = 4000; char *flv_data = NULL; int nb_flv = 0; HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv(empty_video_data, frame_type, avc_packet_type, dts, pts, &flv_data, &nb_flv)); // Should produce FLV packet with 5-byte header only EXPECT_TRUE(flv_data != NULL); EXPECT_EQ(5, nb_flv); if (flv_data) { // Check FLV header format EXPECT_EQ((frame_type << 4) | 12, (uint8_t)flv_data[0]); // frame_type | SrsVideoCodecIdHEVC(12) EXPECT_EQ(avc_packet_type, flv_data[1]); // AVCPacketType // Check composition time (CTS = PTS - DTS = 0) uint32_t cts = ((unsigned char)flv_data[2] << 16) | ((unsigned char)flv_data[3] << 8) | (unsigned char)flv_data[4]; EXPECT_EQ(0, cts); delete[] flv_data; } } // Test mux_avc2flv with large composition time offset if (true) { std::string video_data = std::string("\x00\x00\x00\x04\x26\x01\xaf\x06", 8); int8_t frame_type = 1; // keyframe int8_t avc_packet_type = 1; // NALU uint32_t dts = 5000; uint32_t pts = 10000; // Large PTS offset char *flv_data = NULL; int nb_flv = 0; HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv(video_data, frame_type, avc_packet_type, dts, pts, &flv_data, &nb_flv)); EXPECT_TRUE(flv_data != NULL); if (flv_data) { // Check composition time (CTS = PTS - DTS = 5000) uint32_t cts = ((unsigned char)flv_data[2] << 16) | ((unsigned char)flv_data[3] << 8) | (unsigned char)flv_data[4]; EXPECT_EQ(5000, cts); delete[] flv_data; } } } VOID TEST(ProtocolRawAvcTest, SrsRawHEVCStreamMuxAvc2flvEnhanced) { srs_error_t err = srs_success; SrsRawHEVCStream hevc; // Test mux_avc2flv_enhanced with sequence header if (true) { std::string video_data = std::string("\x01\x64\x00\x20\xff\xe1\x00\x19\x67\x64\x00\x20", 12); int8_t frame_type = 1; // keyframe int8_t packet_type = 0; // sequence start uint32_t dts = 1000; uint32_t pts = 1000; char *flv_data = NULL; int nb_flv = 0; HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv_enhanced(video_data, frame_type, packet_type, dts, pts, &flv_data, &nb_flv)); // Should produce enhanced FLV packet with 5-byte header + video data EXPECT_TRUE(flv_data != NULL); EXPECT_EQ(5 + video_data.length(), nb_flv); if (flv_data) { // Check enhanced FLV header format uint8_t header_byte = flv_data[0]; EXPECT_TRUE((header_byte & 0x80) != 0); // SRS_FLV_IS_EX_HEADER bit set EXPECT_EQ(frame_type, (header_byte >> 4) & 0x07); // frame type (3 bits, mask out EX_HEADER bit) EXPECT_EQ(packet_type, header_byte & 0x0f); // packet type // Check HEVC fourcc 'hvc1' EXPECT_EQ('h', flv_data[1]); EXPECT_EQ('v', flv_data[2]); EXPECT_EQ('c', flv_data[3]); EXPECT_EQ('1', flv_data[4]); // Check video data follows header EXPECT_EQ(0, memcmp(video_data.data(), flv_data + 5, video_data.length())); delete[] flv_data; } } // Test mux_avc2flv_enhanced with coded frames if (true) { std::string video_data = std::string("\x00\x00\x00\x0e\x26\x01\xaf\x06\xb8\x63\xef\x3a\x7f\x3c\x00\x01\x00\x80", 18); int8_t frame_type = 1; // keyframe int8_t packet_type = 1; // coded frames uint32_t dts = 2000; uint32_t pts = 2000; char *flv_data = NULL; int nb_flv = 0; HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv_enhanced(video_data, frame_type, packet_type, dts, pts, &flv_data, &nb_flv)); EXPECT_TRUE(flv_data != NULL); EXPECT_EQ(5 + video_data.length(), nb_flv); if (flv_data) { // Check enhanced header with coded frames packet type uint8_t header_byte = flv_data[0]; EXPECT_EQ(packet_type, header_byte & 0x0f); // coded frames packet type delete[] flv_data; } } // Test mux_avc2flv_enhanced with inter frame if (true) { std::string video_data = std::string("\x00\x00\x00\x08\x02\x01\xd0\x80\x93\x25\x88\x84", 12); int8_t frame_type = 2; // inter frame int8_t packet_type = 1; // coded frames uint32_t dts = 3000; uint32_t pts = 3000; char *flv_data = NULL; int nb_flv = 0; HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv_enhanced(video_data, frame_type, packet_type, dts, pts, &flv_data, &nb_flv)); EXPECT_TRUE(flv_data != NULL); if (flv_data) { // Check inter frame type in enhanced header uint8_t header_byte = flv_data[0]; EXPECT_EQ(frame_type, (header_byte >> 4) & 0x07); // inter frame type (3 bits, mask out EX_HEADER bit) delete[] flv_data; } } // Test mux_avc2flv_enhanced with sequence end packet if (true) { std::string video_data; // Empty for sequence end int8_t frame_type = 1; // keyframe int8_t packet_type = 2; // sequence end uint32_t dts = 4000; uint32_t pts = 4000; char *flv_data = NULL; int nb_flv = 0; HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv_enhanced(video_data, frame_type, packet_type, dts, pts, &flv_data, &nb_flv)); EXPECT_TRUE(flv_data != NULL); EXPECT_EQ(5, nb_flv); // Should produce 5-byte header only if (flv_data) { // Check enhanced FLV header format for sequence end uint8_t header_byte = flv_data[0]; EXPECT_TRUE((header_byte & 0x80) != 0); // SRS_FLV_IS_EX_HEADER bit set EXPECT_EQ(frame_type, (header_byte >> 4) & 0x07); // frame type EXPECT_EQ(packet_type, header_byte & 0x0f); // sequence end packet type // Check HEVC fourcc 'hvc1' EXPECT_EQ('h', flv_data[1]); EXPECT_EQ('v', flv_data[2]); EXPECT_EQ('c', flv_data[3]); EXPECT_EQ('1', flv_data[4]); delete[] flv_data; } } // Test mux_avc2flv_enhanced with different frame types if (true) { std::string video_data = std::string("\x00\x00\x00\x06\x04\x01\x70\x80\x12\x34", 10); int8_t frame_type = 3; // disposable inter frame int8_t packet_type = 1; // coded frames uint32_t dts = 5000; uint32_t pts = 5000; char *flv_data = NULL; int nb_flv = 0; HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv_enhanced(video_data, frame_type, packet_type, dts, pts, &flv_data, &nb_flv)); EXPECT_TRUE(flv_data != NULL); if (flv_data) { // Check disposable inter frame type in enhanced header uint8_t header_byte = flv_data[0]; EXPECT_EQ(frame_type, (header_byte >> 4) & 0x07); // disposable inter frame type delete[] flv_data; } } // Test mux_avc2flv_enhanced with empty video data if (true) { std::string empty_video_data; int8_t frame_type = 1; // keyframe int8_t packet_type = 0; // sequence start uint32_t dts = 6000; uint32_t pts = 6000; char *flv_data = NULL; int nb_flv = 0; HELPER_EXPECT_SUCCESS(hevc.mux_avc2flv_enhanced(empty_video_data, frame_type, packet_type, dts, pts, &flv_data, &nb_flv)); // Should produce enhanced FLV packet with 5-byte header only EXPECT_TRUE(flv_data != NULL); EXPECT_EQ(5, nb_flv); if (flv_data) { // Check enhanced FLV header format uint8_t header_byte = flv_data[0]; EXPECT_TRUE((header_byte & 0x80) != 0); // SRS_FLV_IS_EX_HEADER bit set EXPECT_EQ(frame_type, (header_byte >> 4) & 0x07); // frame type EXPECT_EQ(packet_type, header_byte & 0x0f); // packet type // Check HEVC fourcc 'hvc1' EXPECT_EQ('h', flv_data[1]); EXPECT_EQ('v', flv_data[2]); EXPECT_EQ('c', flv_data[3]); EXPECT_EQ('1', flv_data[4]); delete[] flv_data; } } } VOID TEST(ProtocolStreamTest, SrsFastStreamBasic) { SrsFastStream stream; // Test initial state EXPECT_EQ(0, stream.size()); // Test bytes() method - should not crash even when empty char *bytes = stream.bytes(); (void)bytes; // bytes might be NULL or valid pointer, both are acceptable for empty stream // Test buffer setting stream.set_buffer(1024); EXPECT_EQ(0, stream.size()); // Size should still be 0 after setting buffer } VOID TEST(ProtocolLogTest, SrsThreadContextBasic) { SrsThreadContext context; // Test ID generation SrsContextId id1 = context.generate_id(); SrsContextId id2 = context.generate_id(); // IDs should be different - compare using compare method EXPECT_TRUE(id1.compare(id2) != 0); // Test getting current ID const SrsContextId ¤t = context.get_id(); (void)current; // Should not crash // Test setting ID SrsContextId new_id = context.generate_id(); const SrsContextId &set_result = context.set_id(new_id); EXPECT_EQ(0, new_id.compare(set_result)); } VOID TEST(ProtocolRtmpConnTest, SrsBasicRtmpClientBasic) { // Test basic RTMP client construction SrsBasicRtmpClient client("rtmp://127.0.0.1:1935/live/test", 3000 * SRS_UTIME_MILLISECONDS, 9000 * SRS_UTIME_MILLISECONDS); // Test stream ID - should be 0 initially EXPECT_EQ(0, client.sid()); // Test extra args access SrsAmf0Object *args = client.extra_args(); EXPECT_TRUE(args != NULL); // Note: We don't test set_recv_timeout() here because it requires a valid socket connection // which we don't have in unit tests. The method exists and will be tested in integration tests. } VOID TEST(ProtocolStTest, SrsStSocketBasic) { // Test basic socket operations - these should exist and not crash // We can't easily test full socket functionality without network setup // Test socket creation functions exist srs_netfd_t fd = NULL; EXPECT_TRUE(fd == NULL); // Test that we can call socket utility functions bool is_never_timeout = srs_is_never_timeout(SRS_UTIME_NO_TIMEOUT); EXPECT_TRUE(is_never_timeout); bool is_not_never_timeout = srs_is_never_timeout(1000 * SRS_UTIME_MILLISECONDS); EXPECT_FALSE(is_not_never_timeout); } VOID TEST(ProtocolRtpTest, SrsRtpVideoBuilderBasic) { srs_error_t err = srs_success; SrsRtpVideoBuilder builder; // Test initialization with basic parameters SrsFormat format; uint32_t ssrc = 12345; uint8_t payload_type = 96; HELPER_EXPECT_SUCCESS(builder.initialize(&format, ssrc, payload_type)); // Test that builder is properly initialized // We can't easily test the full functionality without complex media packets // but we can verify the initialization doesn't crash EXPECT_TRUE(true); // Basic initialization test passed } VOID TEST(ProtocolRtpTest, SrsRtpVideoBuilderPackaging) { srs_error_t err = srs_success; SrsRtpVideoBuilder builder; SrsFormat format; uint32_t ssrc = 54321; uint8_t payload_type = 97; HELPER_EXPECT_SUCCESS(builder.initialize(&format, ssrc, payload_type)); // Test packaging with minimal media packet SrsMediaPacket *msg = new SrsMediaPacket(); SrsUniquePtr msg_uptr(msg); // Create minimal video data char *video_data = new char[10]; video_data[0] = 0x17; // keyframe + AVC video_data[1] = 0x01; // AVC NALU for (int i = 2; i < 10; i++) { video_data[i] = i; } msg->wrap(video_data, 10); msg->timestamp_ = 1000; msg->message_type_ = SrsFrameTypeVideo; std::vector pkts; // Test packaging - may fail due to complex validation but shouldn't crash srs_error_t package_err = builder.package_nalus(msg, std::vector(), pkts); srs_freep(package_err); // Clean up any created packets for (size_t i = 0; i < pkts.size(); i++) { delete pkts[i]; } pkts.clear(); // Test passed if no crash occurred EXPECT_TRUE(true); } VOID TEST(ProtocolRtcStunTest, SrsStunPacketBasic) { SrsStunPacket stun; // Test initial state EXPECT_FALSE(stun.is_binding_request()); EXPECT_FALSE(stun.is_binding_response()); EXPECT_EQ(0, stun.get_message_type()); EXPECT_TRUE(stun.get_username().empty()); EXPECT_TRUE(stun.get_local_ufrag().empty()); EXPECT_TRUE(stun.get_remote_ufrag().empty()); EXPECT_TRUE(stun.get_transcation_id().empty()); EXPECT_EQ(0, stun.get_mapped_address()); EXPECT_EQ(0, stun.get_mapped_port()); EXPECT_FALSE(stun.get_ice_controlled()); EXPECT_FALSE(stun.get_ice_controlling()); EXPECT_FALSE(stun.get_use_candidate()); } VOID TEST(ProtocolRtcStunTest, SrsStunPacketSetters) { SrsStunPacket stun; // Test setters stun.set_message_type(BindingRequest); EXPECT_TRUE(stun.is_binding_request()); EXPECT_FALSE(stun.is_binding_response()); EXPECT_EQ(BindingRequest, stun.get_message_type()); stun.set_message_type(BindingResponse); EXPECT_FALSE(stun.is_binding_request()); EXPECT_TRUE(stun.is_binding_response()); EXPECT_EQ(BindingResponse, stun.get_message_type()); stun.set_local_ufrag("local123"); EXPECT_STREQ("local123", stun.get_local_ufrag().c_str()); stun.set_remote_ufrag("remote456"); EXPECT_STREQ("remote456", stun.get_remote_ufrag().c_str()); stun.set_transcation_id("transaction789"); EXPECT_STREQ("transaction789", stun.get_transcation_id().c_str()); stun.set_mapped_address(0x7f000001); // 127.0.0.1 EXPECT_EQ(0x7f000001, stun.get_mapped_address()); stun.set_mapped_port(8080); EXPECT_EQ(8080, stun.get_mapped_port()); } VOID TEST(ProtocolRtcStunTest, SrsStunPacketDecode) { srs_error_t err = srs_success; SrsStunPacket stun; // Test decode with invalid data - should fail char invalid_data[] = {0x01, 0x02, 0x03}; HELPER_EXPECT_FAILED(stun.decode(invalid_data, sizeof(invalid_data))); // Test decode with minimal valid STUN packet structure // STUN header: message type (2) + message length (2) + magic cookie (4) + transaction ID (12) = 20 bytes minimum char valid_stun[20]; memset(valid_stun, 0, sizeof(valid_stun)); // Set message type to binding request valid_stun[0] = 0x00; valid_stun[1] = 0x01; // BindingRequest // Set message length to 0 (no attributes) valid_stun[2] = 0x00; valid_stun[3] = 0x00; // Set magic cookie (0x2112A442 in network byte order) valid_stun[4] = 0x21; valid_stun[5] = 0x12; valid_stun[6] = 0xA4; valid_stun[7] = 0x42; // Set transaction ID (12 bytes) for (int i = 8; i < 20; i++) { valid_stun[i] = i - 8; } HELPER_EXPECT_SUCCESS(stun.decode(valid_stun, sizeof(valid_stun))); EXPECT_TRUE(stun.is_binding_request()); EXPECT_EQ(BindingRequest, stun.get_message_type()); } VOID TEST(ProtocolRtcStunTest, SrsStunPacketEncode) { SrsStunPacket stun; stun.set_message_type(BindingResponse); stun.set_transcation_id("123456789012"); // 12 bytes stun.set_mapped_address(0x7f000001); stun.set_mapped_port(8080); char buffer[1024]; SrsBuffer stream(buffer, sizeof(buffer)); // Test encode - may fail due to complex HMAC validation but shouldn't crash srs_error_t encode_err = stun.encode("password", &stream); srs_freep(encode_err); // Test passed if no crash occurred EXPECT_TRUE(true); } VOID TEST(ProtocolSdpTest, SrsSdpBasic) { SrsSdp sdp; // Test basic SDP construction - should not crash EXPECT_TRUE(true); // Test encode to empty stream std::ostringstream os; srs_error_t err = sdp.encode(os); HELPER_EXPECT_SUCCESS(err); // Should produce some basic SDP output std::string result = os.str(); EXPECT_FALSE(result.empty()); // Should contain basic SDP fields EXPECT_TRUE(result.find("v=") != std::string::npos); // version EXPECT_TRUE(result.find("o=") != std::string::npos); // origin EXPECT_TRUE(result.find("s=") != std::string::npos); // session name EXPECT_TRUE(result.find("t=") != std::string::npos); // timing } VOID TEST(ProtocolSdpTest, SrsSdpParse) { srs_error_t err = srs_success; SrsSdp sdp; // Test parsing minimal valid SDP std::string minimal_sdp = "v=0\r\n" "o=- 123456 654321 IN IP4 127.0.0.1\r\n" "s=Test Session\r\n" "t=0 0\r\n"; HELPER_EXPECT_SUCCESS(sdp.parse(minimal_sdp)); // Test parsing invalid SDP - may succeed or fail depending on SDP parser implementation std::string invalid_sdp = "invalid sdp content"; srs_error_t invalid_err = sdp.parse(invalid_sdp); srs_freep(invalid_err); // Don't assert specific result as parser may be lenient // Test parsing empty SDP - may succeed or fail depending on implementation srs_error_t empty_err = sdp.parse(""); srs_freep(empty_err); // Don't assert specific result as parser may handle empty input } VOID TEST(ProtocolSdpTest, SrsSdpMediaDescription) { srs_error_t err = srs_success; SrsSdp sdp; // Test parsing SDP with media description std::string sdp_with_media = "v=0\r\n" "o=- 123456 654321 IN IP4 127.0.0.1\r\n" "s=Test Session\r\n" "t=0 0\r\n" "m=video 9 RTP/AVP 96\r\n" "a=rtpmap:96 H264/90000\r\n"; HELPER_EXPECT_SUCCESS(sdp.parse(sdp_with_media)); // Test that we can encode it back std::ostringstream os; HELPER_EXPECT_SUCCESS(sdp.encode(os)); std::string result = os.str(); EXPECT_FALSE(result.empty()); EXPECT_TRUE(result.find("m=") != std::string::npos); // media line } VOID TEST(ProtocolRtmpConnTest, SrsBasicRtmpClientConstruction) { // Test RTMP client construction with various URLs SrsBasicRtmpClient client1("rtmp://127.0.0.1:1935/live/test", 3000 * SRS_UTIME_MILLISECONDS, 9000 * SRS_UTIME_MILLISECONDS); // Test initial state EXPECT_EQ(0, client1.sid()); // Test extra args access SrsAmf0Object *args1 = client1.extra_args(); EXPECT_TRUE(args1 != NULL); // Test with different URL format SrsBasicRtmpClient client2("rtmp://192.168.1.100/app/stream", 5000 * SRS_UTIME_MILLISECONDS, 15000 * SRS_UTIME_MILLISECONDS); EXPECT_EQ(0, client2.sid()); SrsAmf0Object *args2 = client2.extra_args(); EXPECT_TRUE(args2 != NULL); // Args should be different objects EXPECT_NE(args1, args2); } VOID TEST(ProtocolRtmpConnTest, SrsBasicRtmpClientOperations) { SrsBasicRtmpClient client("rtmp://127.0.0.1:1935/live/stream", 1000 * SRS_UTIME_MILLISECONDS, 3000 * SRS_UTIME_MILLISECONDS); // Test connection operations - these will fail without a server but shouldn't crash srs_error_t connect_err = client.connect(); srs_freep(connect_err); // Expected to fail without server // Test close - should not crash even if not connected client.close(); // Test kbps sampling - should not crash client.kbps_sample("test", 1000 * SRS_UTIME_MILLISECONDS); client.kbps_sample("test2", 2000 * SRS_UTIME_MILLISECONDS, 10); // Note: We don't test publish(), play(), recv_message(), or set_recv_timeout() here // because they require valid internal client/transport objects which we don't have // without a successful connection. These methods exist and will be tested in // integration tests with actual RTMP server connections. } VOID TEST(ProtocolRtcStunTest, SrsCrc32IeeeBasic) { // Test CRC32 IEEE calculation with known values const char *test_data = "hello"; uint32_t crc = srs_crc32_ieee(test_data, strlen(test_data)); // CRC32 should be deterministic for same input uint32_t crc2 = srs_crc32_ieee(test_data, strlen(test_data)); EXPECT_EQ(crc, crc2); // Different data should produce different CRC const char *test_data2 = "world"; uint32_t crc3 = srs_crc32_ieee(test_data2, strlen(test_data2)); EXPECT_NE(crc, crc3); // Test with empty data uint32_t crc_empty = srs_crc32_ieee("", 0); EXPECT_EQ(0, crc_empty); // Test with previous CRC value uint32_t crc_combined = srs_crc32_ieee(test_data2, strlen(test_data2), crc); EXPECT_NE(crc, crc_combined); EXPECT_NE(crc3, crc_combined); } VOID TEST(ProtocolRtcStunTest, SrsStunPacketComplexDecode) { srs_error_t err = srs_success; SrsStunPacket stun; // Test STUN packet with username attribute char stun_with_username[32]; memset(stun_with_username, 0, sizeof(stun_with_username)); // STUN header stun_with_username[0] = 0x00; stun_with_username[1] = 0x01; // BindingRequest stun_with_username[2] = 0x00; stun_with_username[3] = 0x0C; // message length = 12 (username attribute) // Magic cookie stun_with_username[4] = 0x21; stun_with_username[5] = 0x12; stun_with_username[6] = 0xA4; stun_with_username[7] = 0x42; // Transaction ID (12 bytes) for (int i = 8; i < 20; i++) { stun_with_username[i] = i - 8; } // Username attribute: type=0x0006, length=8, value="test:usr" stun_with_username[20] = 0x00; stun_with_username[21] = 0x06; // Username attribute type stun_with_username[22] = 0x00; stun_with_username[23] = 0x08; // Length = 8 stun_with_username[24] = 't'; stun_with_username[25] = 'e'; stun_with_username[26] = 's'; stun_with_username[27] = 't'; stun_with_username[28] = ':'; stun_with_username[29] = 'u'; stun_with_username[30] = 's'; stun_with_username[31] = 'r'; HELPER_EXPECT_SUCCESS(stun.decode(stun_with_username, sizeof(stun_with_username))); EXPECT_TRUE(stun.is_binding_request()); EXPECT_STREQ("test:usr", stun.get_username().c_str()); EXPECT_STREQ("test", stun.get_local_ufrag().c_str()); EXPECT_STREQ("usr", stun.get_remote_ufrag().c_str()); } VOID TEST(ProtocolRtpTest, SrsRtpVideoBuilderPackageStapA) { srs_error_t err = srs_success; SrsRtpVideoBuilder builder; SrsFormat format; uint32_t ssrc = 12345; uint8_t payload_type = 96; // Initialize format and setup video codec config (required for package_stap_a) HELPER_EXPECT_SUCCESS(format.initialize()); // Setup video sequence header (H.264 AVC) with SPS/PPS data // This is based on MockSrsFormat from srs_utest_fmp4.cpp uint8_t video_raw[] = { 0x17, // keyframe + AVC 0x00, 0x00, 0x00, 0x00, 0x01, 0x64, 0x00, 0x20, 0xff, 0xe1, 0x00, 0x19, 0x67, 0x64, 0x00, 0x20, 0xac, 0xd9, 0x40, 0xc0, 0x29, 0xb0, 0x11, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x32, 0x0f, 0x18, 0x31, 0x96, 0x01, 0x00, 0x05, 0x68, 0xeb, 0xec, 0xb2, 0x2c}; // Parse the video sequence header to populate SPS/PPS HELPER_EXPECT_SUCCESS(format.on_video(0, (char *)video_raw, sizeof(video_raw))); // Verify that vcodec and SPS/PPS are properly set EXPECT_TRUE(format.vcodec_ != NULL); EXPECT_EQ(SrsVideoCodecIdAVC, format.vcodec_->id_); EXPECT_FALSE(format.vcodec_->sequenceParameterSetNALUnit_.empty()); EXPECT_FALSE(format.vcodec_->pictureParameterSetNALUnit_.empty()); HELPER_EXPECT_SUCCESS(builder.initialize(&format, ssrc, payload_type)); // Create a media packet for testing SrsMediaPacket *msg = new SrsMediaPacket(); SrsUniquePtr msg_uptr(msg); char *video_data = new char[10]; video_data[0] = 0x17; // keyframe + AVC for (int i = 1; i < 10; i++) { video_data[i] = i; } msg->wrap(video_data, 10); msg->timestamp_ = 1000; msg->message_type_ = SrsFrameTypeVideo; // Create RTP packet for STAP-A SrsRtpPacket *pkt = new SrsRtpPacket(); SrsUniquePtr pkt_uptr(pkt); // Test package_stap_a - should now work with proper SPS/PPS setup HELPER_EXPECT_SUCCESS(builder.package_stap_a(msg, pkt)); // Verify RTP packet was properly configured EXPECT_EQ(payload_type, pkt->header_.get_payload_type()); EXPECT_EQ(ssrc, pkt->header_.get_ssrc()); EXPECT_EQ(SrsFrameTypeVideo, pkt->frame_type_); EXPECT_EQ(1000 * 90, pkt->header_.get_timestamp()); // timestamp * 90 EXPECT_FALSE(pkt->header_.get_marker()); // STAP-A should not have marker bit set // Verify STAP-A payload was created EXPECT_TRUE(pkt->payload() != NULL); // Verify it's a STAP-A payload by checking if we can cast to SrsRtpSTAPPayload SrsRtpSTAPPayload *stap_payload = dynamic_cast(pkt->payload()); EXPECT_TRUE(stap_payload != NULL); // Verify STAP-A contains SPS and PPS EXPECT_TRUE(stap_payload->get_sps() != NULL); EXPECT_TRUE(stap_payload->get_pps() != NULL); } VOID TEST(ProtocolRtpTest, SrsRtpVideoBuilderPackageNalusWithFormat) { srs_error_t err = srs_success; SrsRtpVideoBuilder builder; SrsFormat format; uint32_t ssrc = 54321; uint8_t payload_type = 97; // Initialize format and setup video codec config (required for package_nalus) HELPER_EXPECT_SUCCESS(format.initialize()); // Setup video sequence header (H.264 AVC) with SPS/PPS data uint8_t video_raw[] = { 0x17, // keyframe + AVC 0x00, 0x00, 0x00, 0x00, 0x01, 0x64, 0x00, 0x20, 0xff, 0xe1, 0x00, 0x19, 0x67, 0x64, 0x00, 0x20, 0xac, 0xd9, 0x40, 0xc0, 0x29, 0xb0, 0x11, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x32, 0x0f, 0x18, 0x31, 0x96, 0x01, 0x00, 0x05, 0x68, 0xeb, 0xec, 0xb2, 0x2c}; // Parse the video sequence header to populate SPS/PPS and vcodec HELPER_EXPECT_SUCCESS(format.on_video(0, (char *)video_raw, sizeof(video_raw))); // Verify that vcodec is properly set EXPECT_TRUE(format.vcodec_ != NULL); EXPECT_EQ(SrsVideoCodecIdAVC, format.vcodec_->id_); HELPER_EXPECT_SUCCESS(builder.initialize(&format, ssrc, payload_type)); // Create a media packet SrsMediaPacket *msg = new SrsMediaPacket(); SrsUniquePtr msg_uptr(msg); char *video_data = new char[20]; video_data[0] = 0x17; // keyframe + AVC video_data[1] = 0x01; // AVC NALU for (int i = 2; i < 20; i++) { video_data[i] = i; } msg->wrap(video_data, 20); msg->timestamp_ = 2000; msg->message_type_ = SrsFrameTypeVideo; // Create NALU samples for testing with proper H.264 NALU headers std::vector samples; // Create SPS NALU sample SrsNaluSample *sample1 = new SrsNaluSample(); uint8_t sps_data[] = {0x67, 0x64, 0x00, 0x20, 0xac}; // SPS NALU (type 7) sample1->bytes_ = (char *)sps_data; sample1->size_ = sizeof(sps_data); samples.push_back(sample1); // Create PPS NALU sample SrsNaluSample *sample2 = new SrsNaluSample(); uint8_t pps_data[] = {0x68, 0xeb, 0xec, 0xb2}; // PPS NALU (type 8) sample2->bytes_ = (char *)pps_data; sample2->size_ = sizeof(pps_data); samples.push_back(sample2); std::vector pkts; // Test package_nalus with proper format setup - should now work HELPER_EXPECT_SUCCESS(builder.package_nalus(msg, samples, pkts)); // Should create at least one packet EXPECT_GT((int)pkts.size(), 0); // Verify first packet configuration if (!pkts.empty()) { SrsRtpPacket *pkt = pkts[0]; EXPECT_EQ(payload_type, pkt->header_.get_payload_type()); EXPECT_EQ(ssrc, pkt->header_.get_ssrc()); EXPECT_EQ(SrsFrameTypeVideo, pkt->frame_type_); EXPECT_EQ(2000 * 90, pkt->header_.get_timestamp()); // timestamp * 90 } // Clean up any created packets for (size_t i = 0; i < pkts.size(); i++) { delete pkts[i]; } pkts.clear(); // Clean up samples delete sample1; delete sample2; } VOID TEST(ProtocolRtpTest, SrsRtpVideoBuilderPackageSingleNalu) { srs_error_t err = srs_success; SrsRtpVideoBuilder builder; SrsFormat format; uint32_t ssrc = 11111; uint8_t payload_type = 98; HELPER_EXPECT_SUCCESS(builder.initialize(&format, ssrc, payload_type)); // Create a media packet SrsMediaPacket *msg = new SrsMediaPacket(); SrsUniquePtr msg_uptr(msg); char *video_data = new char[15]; video_data[0] = 0x17; // keyframe + AVC for (int i = 1; i < 15; i++) { video_data[i] = i; } msg->wrap(video_data, 15); msg->timestamp_ = 3000; msg->message_type_ = SrsFrameTypeVideo; // Create a single NALU sample SrsNaluSample *sample = new SrsNaluSample(); sample->bytes_ = video_data + 2; sample->size_ = 10; std::vector pkts; // Test package_single_nalu HELPER_EXPECT_SUCCESS(builder.package_single_nalu(msg, sample, pkts)); // Should create exactly one packet EXPECT_EQ(1, (int)pkts.size()); if (!pkts.empty()) { SrsRtpPacket *pkt = pkts[0]; EXPECT_EQ(payload_type, pkt->header_.get_payload_type()); EXPECT_EQ(ssrc, pkt->header_.get_ssrc()); EXPECT_EQ(SrsFrameTypeVideo, pkt->frame_type_); EXPECT_EQ(3000 * 90, pkt->header_.get_timestamp()); // timestamp * 90 } // Clean up packets for (size_t i = 0; i < pkts.size(); i++) { delete pkts[i]; } pkts.clear(); // Clean up sample delete sample; } VOID TEST(ProtocolRtpTest, SrsRtpVideoBuilderPackageFuA) { srs_error_t err = srs_success; SrsRtpVideoBuilder builder; SrsFormat format; uint32_t ssrc = 22222; uint8_t payload_type = 99; // Initialize format and setup video codec config (required for package_fu_a) HELPER_EXPECT_SUCCESS(format.initialize()); // Setup video sequence header (H.264 AVC) with SPS/PPS data uint8_t video_raw[] = { 0x17, // keyframe + AVC 0x00, 0x00, 0x00, 0x00, 0x01, 0x64, 0x00, 0x20, 0xff, 0xe1, 0x00, 0x19, 0x67, 0x64, 0x00, 0x20, 0xac, 0xd9, 0x40, 0xc0, 0x29, 0xb0, 0x11, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x32, 0x0f, 0x18, 0x31, 0x96, 0x01, 0x00, 0x05, 0x68, 0xeb, 0xec, 0xb2, 0x2c}; // Parse the video sequence header to populate SPS/PPS and vcodec HELPER_EXPECT_SUCCESS(format.on_video(0, (char *)video_raw, sizeof(video_raw))); // Verify that vcodec is properly set EXPECT_TRUE(format.vcodec_ != NULL); EXPECT_EQ(SrsVideoCodecIdAVC, format.vcodec_->id_); HELPER_EXPECT_SUCCESS(builder.initialize(&format, ssrc, payload_type)); // Create a media packet SrsMediaPacket *msg = new SrsMediaPacket(); SrsUniquePtr msg_uptr(msg); char *video_data = new char[100]; video_data[0] = 0x17; // keyframe + AVC video_data[1] = 0x65; // IDR NALU header (type 5) for (int i = 2; i < 100; i++) { video_data[i] = i; } msg->wrap(video_data, 100); msg->timestamp_ = 4000; msg->message_type_ = SrsFrameTypeVideo; // Create a large NALU sample that requires FU-A fragmentation SrsNaluSample *sample = new SrsNaluSample(); sample->bytes_ = video_data + 1; // Start from NALU header (IDR) sample->size_ = 80; // Large enough to require fragmentation std::vector pkts; int fu_payload_size = 30; // Small payload size to force fragmentation // Test package_fu_a - should now work with proper format setup HELPER_EXPECT_SUCCESS(builder.package_fu_a(msg, sample, fu_payload_size, pkts)); // Should create multiple packets due to fragmentation EXPECT_GT((int)pkts.size(), 1); // Verify first packet configuration if (!pkts.empty()) { SrsRtpPacket *pkt = pkts[0]; EXPECT_EQ(payload_type, pkt->header_.get_payload_type()); EXPECT_EQ(ssrc, pkt->header_.get_ssrc()); EXPECT_EQ(SrsFrameTypeVideo, pkt->frame_type_); EXPECT_EQ(4000 * 90, pkt->header_.get_timestamp()); // timestamp * 90 // Verify it's a FU-A payload SrsRtpFUAPayload2 *fua_payload = dynamic_cast(pkt->payload()); EXPECT_TRUE(fua_payload != NULL); if (fua_payload) { // First packet should have start bit set EXPECT_TRUE(fua_payload->start_); EXPECT_FALSE(fua_payload->end_); } } // Last packet should have end bit set if (pkts.size() > 1) { SrsRtpPacket *last_pkt = pkts[pkts.size() - 1]; SrsRtpFUAPayload2 *last_fua = dynamic_cast(last_pkt->payload()); EXPECT_TRUE(last_fua != NULL); if (last_fua) { EXPECT_FALSE(last_fua->start_); EXPECT_TRUE(last_fua->end_); } } // Clean up any created packets for (size_t i = 0; i < pkts.size(); i++) { delete pkts[i]; } pkts.clear(); // Clean up sample delete sample; } VOID TEST(ProtocolConnTest, SrsBufferedReadWriterPeek) { srs_error_t err = srs_success; // Create mock IO with test data MockBufferIO mock_io; mock_io.append("Hello World Test Data"); SrsBufferedReadWriter buffered(&mock_io); // Test peek functionality char peek_buf[10]; int peek_size = sizeof(peek_buf); HELPER_EXPECT_SUCCESS(buffered.peek(peek_buf, &peek_size)); // Should have peeked some data EXPECT_GT(peek_size, 0); EXPECT_LE(peek_size, (int)sizeof(peek_buf)); // Test peek with smaller buffer char small_buf[5]; int small_size = sizeof(small_buf); HELPER_EXPECT_SUCCESS(buffered.peek(small_buf, &small_size)); EXPECT_GT(small_size, 0); EXPECT_LE(small_size, (int)sizeof(small_buf)); } VOID TEST(ProtocolConnTest, SrsBufferedReadWriterRead) { srs_error_t err = srs_success; // Create mock IO with test data MockBufferIO mock_io; mock_io.append("Hello World"); SrsBufferedReadWriter buffered(&mock_io); // Test read functionality char read_buf[20]; ssize_t nread = 0; HELPER_EXPECT_SUCCESS(buffered.read(read_buf, sizeof(read_buf), &nread)); // Should have read some data EXPECT_GT(nread, 0); EXPECT_LE(nread, (ssize_t)sizeof(read_buf)); // Test read with smaller buffer mock_io.append("More Data"); char small_buf[5]; ssize_t small_nread = 0; HELPER_EXPECT_SUCCESS(buffered.read(small_buf, sizeof(small_buf), &small_nread)); EXPECT_GT(small_nread, 0); EXPECT_LE(small_nread, (ssize_t)sizeof(small_buf)); } VOID TEST(ProtocolConnTest, SrsBufferedReadWriterReadFully) { srs_error_t err = srs_success; // Create mock IO with sufficient test data MockBufferIO mock_io; mock_io.append("Hello World Test Data For Read Fully"); SrsBufferedReadWriter buffered(&mock_io); // Test read_fully functionality char read_buf[10]; ssize_t nread = 0; HELPER_EXPECT_SUCCESS(buffered.read_fully(read_buf, sizeof(read_buf), &nread)); // read_fully should read exactly the requested amount or fail EXPECT_EQ((ssize_t)sizeof(read_buf), nread); // Test read_fully with smaller amount char small_buf[5]; ssize_t small_nread = 0; HELPER_EXPECT_SUCCESS(buffered.read_fully(small_buf, sizeof(small_buf), &small_nread)); EXPECT_EQ((ssize_t)sizeof(small_buf), small_nread); } VOID TEST(ProtocolConnTest, SrsBufferedReadWriterReloadBuffer) { // Test that reload_buffer method exists and is accessible through peek // Since reload_buffer is private, we test it indirectly through peek MockBufferIO mock_io; mock_io.append("Test Data For Buffer Reload"); SrsBufferedReadWriter buffered(&mock_io); // First peek should trigger reload_buffer char peek_buf[10]; int peek_size = sizeof(peek_buf); srs_error_t err = buffered.peek(peek_buf, &peek_size); HELPER_EXPECT_SUCCESS(err); // Should have loaded data into buffer EXPECT_GT(peek_size, 0); // Second peek should use cached data (no reload needed) char peek_buf2[5]; int peek_size2 = sizeof(peek_buf2); HELPER_EXPECT_SUCCESS(buffered.peek(peek_buf2, &peek_size2)); EXPECT_GT(peek_size2, 0); } VOID TEST(ProtocolConnTest, SrsSslConnectionRead) { // Use available test certificates std::string key_file = "conf/server.key"; std::string crt_file = "conf/server.crt"; // Create mock transport for SSL connection testing MockBufferIO mock_io; mock_io.append("SSL encrypted test data for reading"); // Create SSL connection with mock transport SrsSslConnection ssl_conn(&mock_io); // Test timeout methods ssl_conn.set_recv_timeout(5 * SRS_UTIME_SECONDS); srs_utime_t timeout = ssl_conn.get_recv_timeout(); EXPECT_EQ(5 * SRS_UTIME_SECONDS, timeout); ssl_conn.set_send_timeout(3 * SRS_UTIME_SECONDS); srs_utime_t send_timeout = ssl_conn.get_send_timeout(); EXPECT_EQ(3 * SRS_UTIME_SECONDS, send_timeout); // Test byte counters int64_t recv_bytes = ssl_conn.get_recv_bytes(); int64_t send_bytes = ssl_conn.get_send_bytes(); EXPECT_GE(recv_bytes, 0); EXPECT_GE(send_bytes, 0); // Test handshake method exists (will fail without proper SSL setup but shouldn't crash) srs_error_t handshake_err = ssl_conn.handshake(key_file, crt_file); srs_freep(handshake_err); // Expected to fail due to mock transport, just testing interface // Test read method exists (will fail without handshake but shouldn't crash) char read_buf[100]; ssize_t nread = 0; srs_error_t read_err = ssl_conn.read(read_buf, sizeof(read_buf), &nread); srs_freep(read_err); // Expected to fail, just testing interface // Test read_fully method exists srs_error_t read_fully_err = ssl_conn.read_fully(read_buf, 10, &nread); srs_freep(read_fully_err); // Expected to fail, just testing interface // Test write method exists const char *test_data = "Hello SSL World"; ssize_t nwrite = 0; srs_error_t write_err = ssl_conn.write((void *)test_data, strlen(test_data), &nwrite); srs_freep(write_err); // Expected to fail, just testing interface // Test writev method exists struct iovec iov[1]; iov[0].iov_base = (void *)test_data; iov[0].iov_len = strlen(test_data); srs_error_t writev_err = ssl_conn.writev(iov, 1, &nwrite); srs_freep(writev_err); // Expected to fail, just testing interface // Test passed - all SSL connection methods exist and can be called safely EXPECT_TRUE(true); } VOID TEST(ProtocolSdpTest, SrsParseH264Fmtp) { srs_error_t err = srs_success; // Test valid H264 fmtp parsing H264SpecificParam h264_param; std::string fmtp = "profile-level-id=42e01e;packetization-mode=1;level-asymmetry-allowed=1"; HELPER_EXPECT_SUCCESS(srs_parse_h264_fmtp(fmtp, h264_param)); EXPECT_STREQ("42e01e", h264_param.profile_level_id_.c_str()); EXPECT_STREQ("1", h264_param.packetization_mode_.c_str()); EXPECT_STREQ("1", h264_param.level_asymmetry_allow_.c_str()); // Test partial fmtp parsing (should fail due to missing packetization-mode) H264SpecificParam h264_param2; std::string fmtp2 = "profile-level-id=640028"; srs_error_t partial_err = srs_parse_h264_fmtp(fmtp2, h264_param2); srs_freep(partial_err); // Expected to fail due to missing packetization-mode // Test empty fmtp (should fail due to missing required parameters) H264SpecificParam h264_param3; std::string fmtp3 = ""; srs_error_t parse_err = srs_parse_h264_fmtp(fmtp3, h264_param3); srs_freep(parse_err); // Expected to fail, just testing interface } VOID TEST(ProtocolSdpTest, SrsSessionInfoParseAttribute) { srs_error_t err = srs_success; SrsSessionInfo session_info; // Test ice-ufrag attribute HELPER_EXPECT_SUCCESS(session_info.parse_attribute("ice-ufrag", "test_ufrag")); EXPECT_STREQ("test_ufrag", session_info.ice_ufrag_.c_str()); // Test ice-pwd attribute HELPER_EXPECT_SUCCESS(session_info.parse_attribute("ice-pwd", "test_password")); EXPECT_STREQ("test_password", session_info.ice_pwd_.c_str()); // Test ice-options attribute HELPER_EXPECT_SUCCESS(session_info.parse_attribute("ice-options", "trickle")); EXPECT_STREQ("trickle", session_info.ice_options_.c_str()); // Test fingerprint attribute HELPER_EXPECT_SUCCESS(session_info.parse_attribute("fingerprint", "sha-256 AA:BB:CC:DD:EE:FF")); EXPECT_STREQ("sha-256", session_info.fingerprint_algo_.c_str()); EXPECT_STREQ("AA:BB:CC:DD:EE:FF", session_info.fingerprint_.c_str()); // Test setup attribute HELPER_EXPECT_SUCCESS(session_info.parse_attribute("setup", "active")); EXPECT_STREQ("active", session_info.setup_.c_str()); // Test unknown attribute (should not fail) HELPER_EXPECT_SUCCESS(session_info.parse_attribute("unknown", "value")); } VOID TEST(ProtocolSdpTest, SrsSessionInfoOperatorAssign) { SrsSessionInfo session1; session1.ice_ufrag_ = "ufrag1"; session1.ice_pwd_ = "pwd1"; session1.ice_options_ = "trickle"; session1.fingerprint_algo_ = "sha-256"; session1.fingerprint_ = "AA:BB:CC"; session1.setup_ = "active"; SrsSessionInfo session2; session2.ice_ufrag_ = "ufrag2"; session2.ice_pwd_ = "pwd2"; // Test assignment operator session2 = session1; EXPECT_STREQ("ufrag1", session2.ice_ufrag_.c_str()); EXPECT_STREQ("pwd1", session2.ice_pwd_.c_str()); EXPECT_STREQ("trickle", session2.ice_options_.c_str()); EXPECT_STREQ("sha-256", session2.fingerprint_algo_.c_str()); EXPECT_STREQ("AA:BB:CC", session2.fingerprint_.c_str()); EXPECT_STREQ("active", session2.setup_.c_str()); // Test equality operator EXPECT_TRUE(session1 == session2); // Test inequality session2.ice_ufrag_ = "different"; EXPECT_FALSE(session1 == session2); } VOID TEST(ProtocolSdpTest, SrsSSRCInfoEncode) { srs_error_t err = srs_success; // Test normal SSRC encoding SrsSSRCInfo ssrc_info(12345, "test_cname", "stream_id", "track_id"); std::ostringstream os; HELPER_EXPECT_SUCCESS(ssrc_info.encode(os)); std::string result = os.str(); EXPECT_TRUE(result.find("a=ssrc:12345 cname:test_cname") != std::string::npos); EXPECT_TRUE(result.find("a=ssrc:12345 msid:stream_id track_id") != std::string::npos); // Test GB28181 SSRC encoding SrsSSRCInfo gb_ssrc_info(67890, "gb_cname", "", ""); gb_ssrc_info.label_ = "gb28181"; std::ostringstream os2; HELPER_EXPECT_SUCCESS(gb_ssrc_info.encode(os2)); std::string gb_result = os2.str(); EXPECT_TRUE(gb_result.find("y=gb_cname") != std::string::npos); // Test invalid SSRC (should fail) SrsSSRCInfo invalid_ssrc; invalid_ssrc.ssrc_ = 0; std::ostringstream os3; srs_error_t encode_err = invalid_ssrc.encode(os3); HELPER_EXPECT_FAILED(encode_err); } VOID TEST(ProtocolSdpTest, SrsSSRCGroupEncode) { srs_error_t err = srs_success; // Test valid SSRC group encoding std::vector ssrcs; ssrcs.push_back(12345); ssrcs.push_back(67890); SrsSSRCGroup ssrc_group("FID", ssrcs); std::ostringstream os; HELPER_EXPECT_SUCCESS(ssrc_group.encode(os)); std::string result = os.str(); EXPECT_TRUE(result.find("a=ssrc-group:FID 12345 67890") != std::string::npos); // Test invalid semantic (should fail) SrsSSRCGroup invalid_group; invalid_group.semantic_ = ""; invalid_group.ssrcs_.push_back(12345); std::ostringstream os2; srs_error_t encode_err = invalid_group.encode(os2); HELPER_EXPECT_FAILED(encode_err); // Test empty SSRCs (should fail) SrsSSRCGroup empty_group; empty_group.semantic_ = "FID"; // ssrcs_ is empty std::ostringstream os3; srs_error_t encode_err2 = empty_group.encode(os3); HELPER_EXPECT_FAILED(encode_err2); } VOID TEST(ProtocolSdpTest, SrsMediaDescUpdateMsid) { srs_error_t err = srs_success; SrsMediaDesc media_desc("video"); // Add some SSRC info SrsSSRCInfo ssrc1(12345, "cname1", "old_stream", "old_track"); SrsSSRCInfo ssrc2(67890, "cname2", "old_stream", "old_track"); media_desc.ssrc_infos_.push_back(ssrc1); media_desc.ssrc_infos_.push_back(ssrc2); // Update MSID HELPER_EXPECT_SUCCESS(media_desc.update_msid("new_stream_id")); // Verify all SSRC infos were updated EXPECT_STREQ("new_stream_id", media_desc.ssrc_infos_[0].msid_.c_str()); EXPECT_STREQ("new_stream_id", media_desc.ssrc_infos_[0].mslabel_.c_str()); EXPECT_STREQ("new_stream_id", media_desc.ssrc_infos_[1].msid_.c_str()); EXPECT_STREQ("new_stream_id", media_desc.ssrc_infos_[1].mslabel_.c_str()); } VOID TEST(ProtocolSdpTest, SrsMediaDescParseAttributeDirections) { srs_error_t err = srs_success; SrsMediaDesc media_desc("video"); // Add a payload type for testing media_desc.payload_types_.push_back(SrsMediaPayloadType(96)); // Test rtcp-mux attribute HELPER_EXPECT_SUCCESS(media_desc.parse_line("a=rtcp-mux")); EXPECT_TRUE(media_desc.rtcp_mux_); // Test rtcp-rsize attribute HELPER_EXPECT_SUCCESS(media_desc.parse_line("a=rtcp-rsize")); EXPECT_TRUE(media_desc.rtcp_rsize_); // Test sendonly attribute HELPER_EXPECT_SUCCESS(media_desc.parse_line("a=sendonly")); EXPECT_TRUE(media_desc.sendonly_); // Test recvonly attribute SrsMediaDesc media_desc2("audio"); HELPER_EXPECT_SUCCESS(media_desc2.parse_line("a=recvonly")); EXPECT_TRUE(media_desc2.recvonly_); // Test sendrecv attribute SrsMediaDesc media_desc3("video"); HELPER_EXPECT_SUCCESS(media_desc3.parse_line("a=sendrecv")); EXPECT_TRUE(media_desc3.sendrecv_); // Test inactive attribute SrsMediaDesc media_desc4("audio"); HELPER_EXPECT_SUCCESS(media_desc4.parse_line("a=inactive")); EXPECT_TRUE(media_desc4.inactive_); } VOID TEST(ProtocolSdpTest, SrsMediaDescParseAttrExtmap) { srs_error_t err = srs_success; SrsMediaDesc media_desc("video"); // Test valid extmap parsing HELPER_EXPECT_SUCCESS(media_desc.parse_line("a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level")); const std::map &extmaps = media_desc.get_extmaps(); EXPECT_EQ(1, (int)extmaps.size()); EXPECT_STREQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level", extmaps.at(1).c_str()); // Test another extmap HELPER_EXPECT_SUCCESS(media_desc.parse_line("a=extmap:2 urn:ietf:params:rtp-hdrext:toffset")); EXPECT_EQ(2, (int)extmaps.size()); EXPECT_STREQ("urn:ietf:params:rtp-hdrext:toffset", extmaps.at(2).c_str()); // Test duplicate extmap ID (should fail) srs_error_t dup_err = media_desc.parse_line("a=extmap:1 urn:ietf:params:rtp-hdrext:duplicate"); HELPER_EXPECT_FAILED(dup_err); } VOID TEST(ProtocolSdpTest, SrsMediaDescParseAttrRtcp) { srs_error_t err = srs_success; SrsMediaDesc media_desc("video"); // Test rtcp attribute parsing (currently just returns success) HELPER_EXPECT_SUCCESS(media_desc.parse_line("a=rtcp:9 IN IP4 224.2.17.12")); // The current implementation is a TODO, so we just verify it doesn't crash EXPECT_TRUE(true); } VOID TEST(ProtocolSdpTest, SrsMediaDescParseAttrRtcpFb) { srs_error_t err = srs_success; SrsMediaDesc media_desc("video"); // Add payload type for testing media_desc.payload_types_.push_back(SrsMediaPayloadType(96)); // Test rtcp-fb attribute parsing HELPER_EXPECT_SUCCESS(media_desc.parse_line("a=rtcp-fb:96 nack")); SrsMediaPayloadType *payload = media_desc.find_media_with_payload_type(96); EXPECT_TRUE(payload != NULL); EXPECT_EQ(1, (int)payload->rtcp_fb_.size()); EXPECT_STREQ("nack", payload->rtcp_fb_[0].c_str()); // Test another rtcp-fb HELPER_EXPECT_SUCCESS(media_desc.parse_line("a=rtcp-fb:96 nack pli")); EXPECT_EQ(2, (int)payload->rtcp_fb_.size()); EXPECT_STREQ("nack pli", payload->rtcp_fb_[1].c_str()); // Test invalid payload type (should fail) srs_error_t invalid_err = media_desc.parse_line("a=rtcp-fb:97 nack"); HELPER_EXPECT_FAILED(invalid_err); } VOID TEST(ProtocolSdpTest, SrsMediaDescParseAttrFmtp) { srs_error_t err = srs_success; SrsMediaDesc media_desc("video"); // Add payload type for testing media_desc.payload_types_.push_back(SrsMediaPayloadType(96)); // Test fmtp attribute parsing HELPER_EXPECT_SUCCESS(media_desc.parse_line("a=fmtp:96 profile-level-id=42e01e")); SrsMediaPayloadType *payload = media_desc.find_media_with_payload_type(96); EXPECT_TRUE(payload != NULL); EXPECT_STREQ("profile-level-id=42e01e", payload->format_specific_param_.c_str()); // Test invalid payload type (should fail) srs_error_t invalid_err = media_desc.parse_line("a=fmtp:97 param=value"); HELPER_EXPECT_FAILED(invalid_err); } VOID TEST(ProtocolSdpTest, SrsMediaDescParseAttrMid) { srs_error_t err = srs_success; SrsMediaDesc media_desc("video"); // Test mid attribute parsing HELPER_EXPECT_SUCCESS(media_desc.parse_line("a=mid:video")); EXPECT_STREQ("video", media_desc.mid_.c_str()); // Test numeric mid SrsMediaDesc media_desc2("audio"); HELPER_EXPECT_SUCCESS(media_desc2.parse_line("a=mid:0")); EXPECT_STREQ("0", media_desc2.mid_.c_str()); } VOID TEST(ProtocolSdpTest, SrsMediaDescParseAttrMsid) { srs_error_t err = srs_success; SrsMediaDesc media_desc("video"); // Test msid attribute parsing with both stream and track HELPER_EXPECT_SUCCESS(media_desc.parse_line("a=msid:stream_id track_id")); EXPECT_STREQ("stream_id", media_desc.msid_.c_str()); EXPECT_STREQ("track_id", media_desc.msid_tracker_.c_str()); // Test msid with only stream ID SrsMediaDesc media_desc2("audio"); HELPER_EXPECT_SUCCESS(media_desc2.parse_line("a=msid:stream_only")); EXPECT_STREQ("stream_only", media_desc2.msid_.c_str()); EXPECT_TRUE(media_desc2.msid_tracker_.empty()); } VOID TEST(ProtocolSdpTest, SrsMediaDescParseAttrSsrc) { srs_error_t err = srs_success; SrsMediaDesc media_desc("video"); // Test ssrc cname attribute HELPER_EXPECT_SUCCESS(media_desc.parse_line("a=ssrc:12345 cname:test_cname")); EXPECT_EQ(1, (int)media_desc.ssrc_infos_.size()); EXPECT_EQ(12345, media_desc.ssrc_infos_[0].ssrc_); EXPECT_STREQ("test_cname", media_desc.ssrc_infos_[0].cname_.c_str()); // Test ssrc msid attribute HELPER_EXPECT_SUCCESS(media_desc.parse_line("a=ssrc:12345 msid:stream_id track_id")); EXPECT_STREQ("stream_id", media_desc.ssrc_infos_[0].msid_.c_str()); EXPECT_STREQ("track_id", media_desc.ssrc_infos_[0].msid_tracker_.c_str()); // Test ssrc mslabel attribute HELPER_EXPECT_SUCCESS(media_desc.parse_line("a=ssrc:12345 mslabel:media_stream")); EXPECT_STREQ("media_stream", media_desc.ssrc_infos_[0].mslabel_.c_str()); // Test ssrc label attribute HELPER_EXPECT_SUCCESS(media_desc.parse_line("a=ssrc:12345 label:video_track")); EXPECT_STREQ("video_track", media_desc.ssrc_infos_[0].label_.c_str()); // Test new SSRC HELPER_EXPECT_SUCCESS(media_desc.parse_line("a=ssrc:67890 cname:another_cname")); EXPECT_EQ(2, (int)media_desc.ssrc_infos_.size()); EXPECT_EQ(67890, media_desc.ssrc_infos_[1].ssrc_); EXPECT_STREQ("another_cname", media_desc.ssrc_infos_[1].cname_.c_str()); } VOID TEST(ProtocolSdpTest, SrsMediaDescFetchOrCreateSsrcInfo) { SrsMediaDesc media_desc("video"); // Test creating new SSRC info SrsSSRCInfo &ssrc_info1 = media_desc.fetch_or_create_ssrc_info(12345); EXPECT_EQ(12345, ssrc_info1.ssrc_); EXPECT_EQ(1, (int)media_desc.ssrc_infos_.size()); // Test fetching existing SSRC info SrsSSRCInfo &ssrc_info2 = media_desc.fetch_or_create_ssrc_info(12345); EXPECT_EQ(12345, ssrc_info2.ssrc_); EXPECT_EQ(1, (int)media_desc.ssrc_infos_.size()); // Should not create new one // Verify they are the same object EXPECT_EQ(&ssrc_info1, &ssrc_info2); // Test creating another SSRC info SrsSSRCInfo &ssrc_info3 = media_desc.fetch_or_create_ssrc_info(67890); EXPECT_EQ(67890, ssrc_info3.ssrc_); EXPECT_EQ(2, (int)media_desc.ssrc_infos_.size()); } VOID TEST(ProtocolSdpTest, SrsSdpSetGetIceCredentials) { srs_error_t err = srs_success; SrsSdp sdp; // Parse SDP with media descriptions std::string sdp_str = "v=0\r\n" "o=- 123456 654321 IN IP4 127.0.0.1\r\n" "s=Test Session\r\n" "t=0 0\r\n" "m=video 9 RTP/AVP 96\r\n" "m=audio 9 RTP/AVP 97\r\n"; HELPER_EXPECT_SUCCESS(sdp.parse(sdp_str)); // Test setting ICE credentials sdp.set_ice_ufrag("test_ufrag"); sdp.set_ice_pwd("test_password"); // Test getting ICE credentials EXPECT_STREQ("test_ufrag", sdp.get_ice_ufrag().c_str()); EXPECT_STREQ("test_password", sdp.get_ice_pwd().c_str()); // Verify all media descriptions were updated std::vector video_descs = sdp.find_media_descs("video"); std::vector audio_descs = sdp.find_media_descs("audio"); EXPECT_STREQ("test_ufrag", video_descs[0]->session_info_.ice_ufrag_.c_str()); EXPECT_STREQ("test_password", video_descs[0]->session_info_.ice_pwd_.c_str()); EXPECT_STREQ("test_ufrag", audio_descs[0]->session_info_.ice_ufrag_.c_str()); EXPECT_STREQ("test_password", audio_descs[0]->session_info_.ice_pwd_.c_str()); } VOID TEST(ProtocolSdpTest, SrsSdpSetGetDtlsRole) { srs_error_t err = srs_success; SrsSdp sdp; // Parse SDP with media descriptions std::string sdp_str = "v=0\r\n" "o=- 123456 654321 IN IP4 127.0.0.1\r\n" "s=Test Session\r\n" "t=0 0\r\n" "m=video 9 RTP/AVP 96\r\n"; HELPER_EXPECT_SUCCESS(sdp.parse(sdp_str)); // Test setting DTLS role sdp.set_dtls_role("active"); // Test getting DTLS role EXPECT_STREQ("active", sdp.get_dtls_role().c_str()); // Verify media description was updated std::vector video_descs = sdp.find_media_descs("video"); EXPECT_STREQ("active", video_descs[0]->session_info_.setup_.c_str()); } VOID TEST(ProtocolSdpTest, SrsSdpSetFingerprint) { srs_error_t err = srs_success; SrsSdp sdp; // Parse SDP with media descriptions std::string sdp_str = "v=0\r\n" "o=- 123456 654321 IN IP4 127.0.0.1\r\n" "s=Test Session\r\n" "t=0 0\r\n" "m=video 9 RTP/AVP 96\r\n" "m=audio 9 RTP/AVP 97\r\n"; HELPER_EXPECT_SUCCESS(sdp.parse(sdp_str)); // Test setting fingerprint algorithm and value sdp.set_fingerprint_algo("sha-256"); sdp.set_fingerprint("AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99"); // Verify all media descriptions were updated std::vector video_descs = sdp.find_media_descs("video"); std::vector audio_descs = sdp.find_media_descs("audio"); EXPECT_STREQ("sha-256", video_descs[0]->session_info_.fingerprint_algo_.c_str()); EXPECT_STREQ("AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99", video_descs[0]->session_info_.fingerprint_.c_str()); EXPECT_STREQ("sha-256", audio_descs[0]->session_info_.fingerprint_algo_.c_str()); EXPECT_STREQ("AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99", audio_descs[0]->session_info_.fingerprint_.c_str()); } VOID TEST(ProtocolSdpTest, SrsSdpAddCandidate) { srs_error_t err = srs_success; SrsSdp sdp; // Parse SDP with media descriptions std::string sdp_str = "v=0\r\n" "o=- 123456 654321 IN IP4 127.0.0.1\r\n" "s=Test Session\r\n" "t=0 0\r\n" "m=video 9 RTP/AVP 96\r\n" "m=audio 9 RTP/AVP 97\r\n"; HELPER_EXPECT_SUCCESS(sdp.parse(sdp_str)); // Test adding UDP candidate sdp.add_candidate("udp", "192.168.1.100", 5000, "host"); // Test adding TCP candidate sdp.add_candidate("tcp", "192.168.1.100", 5001, "host"); // Verify candidates were added to all media descriptions std::vector video_descs = sdp.find_media_descs("video"); std::vector audio_descs = sdp.find_media_descs("audio"); EXPECT_EQ(2, (int)video_descs[0]->candidates_.size()); EXPECT_EQ(2, (int)audio_descs[0]->candidates_.size()); // Check UDP candidate EXPECT_STREQ("udp", video_descs[0]->candidates_[0].protocol_.c_str()); EXPECT_STREQ("192.168.1.100", video_descs[0]->candidates_[0].ip_.c_str()); EXPECT_EQ(5000, video_descs[0]->candidates_[0].port_); EXPECT_STREQ("host", video_descs[0]->candidates_[0].type_.c_str()); // Check TCP candidate EXPECT_STREQ("tcp", video_descs[0]->candidates_[1].protocol_.c_str()); EXPECT_STREQ("192.168.1.100", video_descs[0]->candidates_[1].ip_.c_str()); EXPECT_EQ(5001, video_descs[0]->candidates_[1].port_); EXPECT_STREQ("host", video_descs[0]->candidates_[1].type_.c_str()); } VOID TEST(ProtocolSdpTest, SrsSdpParseAttribute) { srs_error_t err = srs_success; SrsSdp sdp; // Test parsing group attribute HELPER_EXPECT_SUCCESS(sdp.parse_line("a=group:BUNDLE video audio")); // Test parsing msid-semantic attribute HELPER_EXPECT_SUCCESS(sdp.parse_line("a=msid-semantic: WMS stream_id")); EXPECT_STREQ("WMS", sdp.msid_semantic_.c_str()); EXPECT_EQ(1, (int)sdp.msids_.size()); EXPECT_STREQ("stream_id", sdp.msids_[0].c_str()); // Test parsing session-level ICE attributes HELPER_EXPECT_SUCCESS(sdp.parse_line("a=ice-ufrag:session_ufrag")); HELPER_EXPECT_SUCCESS(sdp.parse_line("a=ice-pwd:session_password")); EXPECT_STREQ("session_ufrag", sdp.session_info_.ice_ufrag_.c_str()); EXPECT_STREQ("session_password", sdp.session_info_.ice_pwd_.c_str()); } VOID TEST(ProtocolSdpTest, SrsSdpParseGb28181Ssrc) { srs_error_t err = srs_success; SrsSdp sdp; // Parse basic SDP first std::string sdp_str = "v=0\r\n" "o=- 123456 654321 IN IP4 127.0.0.1\r\n" "s=Test Session\r\n" "t=0 0\r\n" "m=video 9 RTP/AVP 96\r\n"; HELPER_EXPECT_SUCCESS(sdp.parse(sdp_str)); // Test parsing GB28181 SSRC HELPER_EXPECT_SUCCESS(sdp.parse_line("y=0100008888")); // Verify GB28181 SSRC was converted to standard format std::vector video_descs = sdp.find_media_descs("video"); EXPECT_EQ(1, (int)video_descs[0]->ssrc_infos_.size()); EXPECT_STREQ("0100008888", video_descs[0]->ssrc_infos_[0].cname_.c_str()); EXPECT_STREQ("gb28181", video_descs[0]->ssrc_infos_[0].label_.c_str()); } VOID TEST(ProtocolSdpTest, SrsSdpIsUnified) { srs_error_t err = srs_success; // Test non-unified SDP (2 or fewer media descriptions) SrsSdp sdp1; std::string sdp_str1 = "v=0\r\n" "o=- 123456 654321 IN IP4 127.0.0.1\r\n" "s=Test Session\r\n" "t=0 0\r\n" "m=video 9 RTP/AVP 96\r\n" "m=audio 9 RTP/AVP 97\r\n"; HELPER_EXPECT_SUCCESS(sdp1.parse(sdp_str1)); EXPECT_FALSE(sdp1.is_unified()); // Test unified SDP (more than 2 media descriptions) SrsSdp sdp2; std::string sdp_str2 = "v=0\r\n" "o=- 123456 654321 IN IP4 127.0.0.1\r\n" "s=Test Session\r\n" "t=0 0\r\n" "m=video 9 RTP/AVP 96\r\n" "m=audio 9 RTP/AVP 97\r\n" "m=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\n"; HELPER_EXPECT_SUCCESS(sdp2.parse(sdp_str2)); EXPECT_TRUE(sdp2.is_unified()); } VOID TEST(ProtocolSdpTest, SrsSdpUpdateMsid) { srs_error_t err = srs_success; SrsSdp sdp; // Parse SDP with media descriptions and SSRC info std::string sdp_str = "v=0\r\n" "o=- 123456 654321 IN IP4 127.0.0.1\r\n" "s=Test Session\r\n" "t=0 0\r\n" "m=video 9 RTP/AVP 96\r\n" "a=ssrc:12345 cname:video_cname\r\n" "m=audio 9 RTP/AVP 97\r\n" "a=ssrc:67890 cname:audio_cname\r\n"; HELPER_EXPECT_SUCCESS(sdp.parse(sdp_str)); // Update MSID HELPER_EXPECT_SUCCESS(sdp.update_msid("new_stream_id")); // Verify global MSID was updated EXPECT_EQ(1, (int)sdp.msids_.size()); EXPECT_STREQ("new_stream_id", sdp.msids_[0].c_str()); // Verify all media descriptions were updated std::vector video_descs = sdp.find_media_descs("video"); std::vector audio_descs = sdp.find_media_descs("audio"); EXPECT_STREQ("new_stream_id", video_descs[0]->ssrc_infos_[0].msid_.c_str()); EXPECT_STREQ("new_stream_id", video_descs[0]->ssrc_infos_[0].mslabel_.c_str()); EXPECT_STREQ("new_stream_id", audio_descs[0]->ssrc_infos_[0].msid_.c_str()); EXPECT_STREQ("new_stream_id", audio_descs[0]->ssrc_infos_[0].mslabel_.c_str()); } VOID TEST(RawHEVCStreamTest, VpsDemux) { srs_error_t err; // Test vps_demux method with valid VPS data if (true) { SrsRawHEVCStream hevc; // Create mock VPS NALU data (HEVC VPS type 32) char vps_data[] = { (char)((32 << 1) | 0), // HEVC VPS NALU type 32, layer_id=0 (char)1, // layer_id bits + temporal_id=1 0x01, 0x02, 0x03, 0x04 // Mock VPS payload }; int vps_size = sizeof(vps_data); string vps_output; HELPER_ASSERT_SUCCESS(hevc.vps_demux(vps_data, vps_size, vps_output)); // Verify VPS output matches input EXPECT_EQ(vps_size, (int)vps_output.length()); EXPECT_EQ(0, memcmp(vps_data, vps_output.data(), vps_size)); } // Test vps_demux with empty frame (should fail) if (true) { SrsRawHEVCStream hevc; string vps_output; HELPER_EXPECT_FAILED(hevc.vps_demux(NULL, 0, vps_output)); } // Test vps_demux with negative frame size (should fail) if (true) { SrsRawHEVCStream hevc; char dummy_data[] = {0x01, 0x02}; string vps_output; HELPER_EXPECT_FAILED(hevc.vps_demux(dummy_data, -1, vps_output)); } } VOID TEST(RawHEVCStreamTest, SpsDemux) { srs_error_t err; // Test sps_demux method with valid SPS data if (true) { SrsRawHEVCStream hevc; // Create mock SPS NALU data (HEVC SPS type 33) char sps_data[] = { (char)((33 << 1) | 0), // HEVC SPS NALU type 33, layer_id=0 (char)1, // layer_id bits + temporal_id=1 0x10, 0x20, 0x30, 0x40, 0x50 // Mock SPS payload (>= 4 bytes total) }; int sps_size = sizeof(sps_data); string sps_output; HELPER_ASSERT_SUCCESS(hevc.sps_demux(sps_data, sps_size, sps_output)); // Verify SPS output matches input EXPECT_EQ(sps_size, (int)sps_output.length()); EXPECT_EQ(0, memcmp(sps_data, sps_output.data(), sps_size)); } // Test sps_demux with frame size < 4 (should succeed but return empty) if (true) { SrsRawHEVCStream hevc; char small_data[] = {0x01, 0x02}; // Only 2 bytes string sps_output; HELPER_ASSERT_SUCCESS(hevc.sps_demux(small_data, 2, sps_output)); // Should return empty string for frames < 4 bytes EXPECT_EQ(0, (int)sps_output.length()); } // Test sps_demux with exactly 4 bytes if (true) { SrsRawHEVCStream hevc; char sps_data[] = {0x42, 0x01, 0x01, 0x01}; // Exactly 4 bytes string sps_output; HELPER_ASSERT_SUCCESS(hevc.sps_demux(sps_data, 4, sps_output)); // Should return the 4 bytes EXPECT_EQ(4, (int)sps_output.length()); EXPECT_EQ(0, memcmp(sps_data, sps_output.data(), 4)); } } VOID TEST(RawHEVCStreamTest, PpsDemux) { srs_error_t err; // Test pps_demux method with valid PPS data if (true) { SrsRawHEVCStream hevc; // Create mock PPS NALU data (HEVC PPS type 34) unsigned char pps_data[] = { (unsigned char)((34 << 1) | 0), // HEVC PPS NALU type 34, layer_id=0 (unsigned char)1, // layer_id bits + temporal_id=1 0xA0, 0xB0, 0xC0 // Mock PPS payload }; int pps_size = sizeof(pps_data); string pps_output; HELPER_ASSERT_SUCCESS(hevc.pps_demux((char *)pps_data, pps_size, pps_output)); // Verify PPS output matches input EXPECT_EQ(pps_size, (int)pps_output.length()); EXPECT_EQ(0, memcmp(pps_data, pps_output.data(), pps_size)); } // Test pps_demux with empty frame (should fail) if (true) { SrsRawHEVCStream hevc; string pps_output; HELPER_EXPECT_FAILED(hevc.pps_demux(NULL, 0, pps_output)); } // Test pps_demux with negative frame size (should fail) if (true) { SrsRawHEVCStream hevc; char dummy_data[] = {0x01, 0x02}; string pps_output; HELPER_EXPECT_FAILED(hevc.pps_demux(dummy_data, -1, pps_output)); } }