srs/trunk/src/utest/srs_utest_protocol3.cpp
2025-09-21 15:39:53 -04:00

2770 lines
94 KiB
C++

//
// Copyright (c) 2013-2025 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#include <srs_utest_protocol3.hpp>
using namespace std;
#include <srs_app_listener.hpp>
#include <srs_app_st.hpp>
#include <srs_core_autofree.hpp>
#include <srs_kernel_buffer.hpp>
#include <srs_kernel_codec.hpp>
#include <srs_kernel_error.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_protocol_amf0.hpp>
#include <srs_protocol_conn.hpp>
#include <srs_protocol_format.hpp>
#include <srs_protocol_http_client.hpp>
#include <srs_protocol_http_conn.hpp>
#include <srs_protocol_io.hpp>
#include <srs_protocol_json.hpp>
#include <srs_protocol_log.hpp>
#include <srs_protocol_protobuf.hpp>
#include <srs_protocol_raw_avc.hpp>
#include <srs_protocol_rtc_stun.hpp>
#include <srs_protocol_rtmp_conn.hpp>
#include <srs_protocol_rtmp_msg_array.hpp>
#include <srs_protocol_rtmp_stack.hpp>
#include <srs_protocol_rtp.hpp>
#include <srs_protocol_sdp.hpp>
#include <srs_protocol_st.hpp>
#include <srs_protocol_stream.hpp>
#include <srs_protocol_utility.hpp>
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<SrsOnMetaDataPacket> 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<SrsMediaPacket> 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<SrsMediaPacket> 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<SrsJsonAny> 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<SrsJsonAny> 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<SrsJsonAny> 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<SrsJsonAny> 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<SrsJsonAny> 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<SrsJsonObject> 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<SrsJsonArray> 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<SrsJsonAny> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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 &current = 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<SrsMediaPacket> 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<SrsRtpPacket *> pkts;
// Test packaging - may fail due to complex validation but shouldn't crash
srs_error_t package_err = builder.package_nalus(msg, std::vector<SrsNaluSample *>(), 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<SrsMediaPacket> 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<SrsRtpPacket> 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<SrsRtpSTAPPayload *>(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<SrsMediaPacket> 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<SrsNaluSample *> 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<SrsRtpPacket *> 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<SrsMediaPacket> 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<SrsRtpPacket *> 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<SrsMediaPacket> 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<SrsRtpPacket *> 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<SrsRtpFUAPayload2 *>(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<SrsRtpFUAPayload2 *>(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<uint32_t> 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<int, std::string> &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<SrsMediaDesc *> video_descs = sdp.find_media_descs("video");
std::vector<SrsMediaDesc *> 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<SrsMediaDesc *> 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<SrsMediaDesc *> video_descs = sdp.find_media_descs("video");
std::vector<SrsMediaDesc *> 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<SrsMediaDesc *> video_descs = sdp.find_media_descs("video");
std::vector<SrsMediaDesc *> 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<SrsMediaDesc *> 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<SrsMediaDesc *> video_descs = sdp.find_media_descs("video");
std::vector<SrsMediaDesc *> 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));
}
}