for issue #4418, #4151, #4076 .DVR Missing First Few Seconds of Audio/Video ### Root Cause When recording WebRTC streams to FLV files using DVR, the first 4-6 seconds of audio/video are missing. This occurs because: 1. **Packets are discarded before A/V sync is available**: The RTC-to-RTMP conversion pipeline actively discards all RTP packets when avsync_time <= 0. 2. **Original algorithm requires 2 RTCP SR packets**: The previous implementation needed to receive two RTCP Sender Report (SR) packets before it could calculate the rate for audio/video synchronization timestamp conversion. 3. **Delay causes packet loss**: Since RTCP SR packets typically arrive every 2-3 seconds, waiting for 2 SRs means 4-6 seconds of packets are discarded before A/V sync becomes available. 4. **Audio SR arrives slower than video SR**: As reported in the issue, video RTCP SR packets arrive much faster than audio SR packets. This asymmetry causes audio packets to be discarded for a longer period, resulting in the audio loss observed in DVR recordings. ### Solution 1. **Initialize rate from SDP**: Use the sample rate from SDP (Session Description Protocol) to calculate the initial rate immediately when the track is created. Audio (Opus): 48000 Hz → rate = 48 (RTP units per millisecond) Video (H.264/H.265): 90000 Hz → rate = 90 (RTP units per millisecond) 2. **Enable immediate A/V sync:** With the SDP rate available, cal_avsync_time() can calculate valid timestamps from the very first RTP packet, eliminating packet loss. 3. **Smooth transition to precise rate**: After receiving the 2nd RTCP SR, update to the precisely calculated rate based on actual RTP/NTP timestamp mapping. ## Configuration Added new configuration option `init_rate_from_sdp` in the RTC vhost section: ```nginx vhost rtc.vhost.srs.com { rtc { # Whether initialize RTP rate from SDP sample rate for immediate A/V sync. # When enabled, the RTP rate (units per millisecond) is initialized from the SDP # sample rate (e.g., 90 for video 90kHz, 48 for audio 48kHz) before receiving # 2 RTCP SR packets. This allows immediate audio/video synchronization. # The rate will be updated to a more precise value after receiving the 2nd SR. # Overwrite by env SRS_VHOST_RTC_INIT_RATE_FROM_SDP for all vhosts. # Default: off init_rate_from_sdp off; } } ``` **⚠️ Important Note**: This config defaults to **off** because: - ✅ When **enabled**: Fixes the audio loss problem (no missing first 4-6 seconds) - ❌ When **enabled**: VLC on macOS cannot play the video properly - ✅ Other platforms work fine (Windows, Linux) - ✅ FFplay works fine on all platforms Users experiencing audio loss in DVR recordings can enable this option if they don't need VLC macOS compatibility. We're investigating the VLC macOS issue to make this feature safe to enable by default in the future. --------- Co-authored-by: winlin <winlinvip@gmail.com> Co-authored-by: OSSRS-AI <winlinam@gmail.com>
3183 lines
126 KiB
C++
3183 lines
126 KiB
C++
//
|
|
// Copyright (c) 2013-2025 The SRS Authors
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
//
|
|
|
|
#include <srs_utest_ai10.hpp>
|
|
|
|
using namespace std;
|
|
|
|
#include <srs_app_rtc_codec.hpp>
|
|
#include <srs_app_rtc_source.hpp>
|
|
#include <srs_core_autofree.hpp>
|
|
#include <srs_kernel_buffer.hpp>
|
|
#include <srs_kernel_error.hpp>
|
|
#include <srs_kernel_packet.hpp>
|
|
#include <srs_kernel_rtc_rtp.hpp>
|
|
#include <srs_protocol_format.hpp>
|
|
#include <srs_protocol_rtmp_stack.hpp>
|
|
#include <srs_utest_ai11.hpp>
|
|
|
|
// Helper functions
|
|
SrsRtpPacket *create_test_rtp_packet(uint16_t seq, uint32_t ts, uint32_t ssrc, bool marker)
|
|
{
|
|
SrsRtpPacket *pkt = new SrsRtpPacket();
|
|
pkt->header_.set_sequence(seq);
|
|
pkt->header_.set_timestamp(ts);
|
|
pkt->header_.set_ssrc(ssrc);
|
|
pkt->header_.set_marker(marker);
|
|
pkt->header_.set_payload_type(96);
|
|
|
|
// Add some dummy payload using SrsRtpRawPayload
|
|
SrsRtpRawPayload *raw_payload = new SrsRtpRawPayload();
|
|
char *payload_data = new char[64];
|
|
memset(payload_data, 0x42, 64);
|
|
raw_payload->payload_ = payload_data;
|
|
raw_payload->nn_payload_ = 64;
|
|
|
|
pkt->set_payload(raw_payload, SrsRtpPacketPayloadTypeRaw);
|
|
|
|
return pkt;
|
|
}
|
|
|
|
SrsRtcTrackDescription *create_test_track_description(std::string type, uint32_t ssrc)
|
|
{
|
|
SrsRtcTrackDescription *desc = new SrsRtcTrackDescription();
|
|
desc->type_ = type;
|
|
desc->ssrc_ = ssrc;
|
|
desc->id_ = "test-track-" + type;
|
|
desc->is_active_ = true;
|
|
desc->direction_ = "sendrecv";
|
|
desc->mid_ = "0";
|
|
return desc;
|
|
}
|
|
|
|
SrsCodecPayload *create_test_codec_payload(uint8_t pt, std::string name, int sample)
|
|
{
|
|
SrsCodecPayload *payload = new SrsCodecPayload(pt, name, sample);
|
|
return payload;
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::calculate_packet_payload_size with basic cases
|
|
VOID TEST(SrsRtcFrameBuilderTest, CalculatePacketPayloadSizeBasic)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Test with NULL packet
|
|
int null_size = builder.calculate_packet_payload_size(NULL);
|
|
EXPECT_EQ(0, null_size);
|
|
|
|
// Test with packet that has no payload
|
|
SrsUniquePtr<SrsRtpPacket> empty_pkt(new SrsRtpPacket());
|
|
int empty_size = builder.calculate_packet_payload_size(empty_pkt.get());
|
|
EXPECT_EQ(0, empty_size);
|
|
|
|
// Test with raw payload packet
|
|
SrsUniquePtr<SrsRtpPacket> raw_pkt(create_test_rtp_packet(100, 1000, 12345));
|
|
int raw_size = builder.calculate_packet_payload_size(raw_pkt.get());
|
|
EXPECT_EQ(68, raw_size); // 4 bytes length prefix + 64 bytes payload
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::calculate_packet_payload_size with H.264 FU-A payload
|
|
VOID TEST(SrsRtcFrameBuilderTest, CalculatePacketPayloadSizeFUA)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Test H.264 FU-A payload (SrsRtpFUAPayload2) - start fragment
|
|
SrsUniquePtr<SrsRtpPacket> fua_start_pkt(new SrsRtpPacket());
|
|
SrsRtpFUAPayload2 *fua_start = new SrsRtpFUAPayload2();
|
|
fua_start->start_ = true;
|
|
fua_start->end_ = false;
|
|
fua_start->nalu_type_ = SrsAvcNaluTypeIDR;
|
|
fua_start->size_ = 100; // 100 bytes of payload data
|
|
char fua_data[100];
|
|
memset(fua_data, 0x42, sizeof(fua_data));
|
|
fua_start->payload_ = fua_data;
|
|
fua_start_pkt->set_payload(fua_start, SrsRtpPacketPayloadTypeFUA2);
|
|
|
|
int fua_start_size = builder.calculate_packet_payload_size(fua_start_pkt.get());
|
|
EXPECT_EQ(105, fua_start_size); // 100 + 1 (NALU header) + 4 (length prefix) = 105
|
|
|
|
// Test H.264 FU-A payload - middle fragment
|
|
SrsUniquePtr<SrsRtpPacket> fua_middle_pkt(new SrsRtpPacket());
|
|
SrsRtpFUAPayload2 *fua_middle = new SrsRtpFUAPayload2();
|
|
fua_middle->start_ = false;
|
|
fua_middle->end_ = false;
|
|
fua_middle->nalu_type_ = SrsAvcNaluTypeIDR;
|
|
fua_middle->size_ = 80; // 80 bytes of payload data
|
|
char fua_middle_data[80];
|
|
memset(fua_middle_data, 0x43, sizeof(fua_middle_data));
|
|
fua_middle->payload_ = fua_middle_data;
|
|
fua_middle_pkt->set_payload(fua_middle, SrsRtpPacketPayloadTypeFUA2);
|
|
|
|
int fua_middle_size = builder.calculate_packet_payload_size(fua_middle_pkt.get());
|
|
EXPECT_EQ(80, fua_middle_size); // Only payload size, no additional headers for middle fragments
|
|
|
|
// Test H.264 FU-A payload - end fragment
|
|
SrsUniquePtr<SrsRtpPacket> fua_end_pkt(new SrsRtpPacket());
|
|
SrsRtpFUAPayload2 *fua_end = new SrsRtpFUAPayload2();
|
|
fua_end->start_ = false;
|
|
fua_end->end_ = true;
|
|
fua_end->nalu_type_ = SrsAvcNaluTypeIDR;
|
|
fua_end->size_ = 60; // 60 bytes of payload data
|
|
char fua_end_data[60];
|
|
memset(fua_end_data, 0x44, sizeof(fua_end_data));
|
|
fua_end->payload_ = fua_end_data;
|
|
fua_end_pkt->set_payload(fua_end, SrsRtpPacketPayloadTypeFUA2);
|
|
|
|
int fua_end_size = builder.calculate_packet_payload_size(fua_end_pkt.get());
|
|
EXPECT_EQ(60, fua_end_size); // Only payload size, no additional headers for end fragments
|
|
|
|
// Test H.264 FU-A payload with zero size
|
|
SrsUniquePtr<SrsRtpPacket> fua_zero_pkt(new SrsRtpPacket());
|
|
SrsRtpFUAPayload2 *fua_zero = new SrsRtpFUAPayload2();
|
|
fua_zero->start_ = true;
|
|
fua_zero->end_ = false;
|
|
fua_zero->nalu_type_ = SrsAvcNaluTypeIDR;
|
|
fua_zero->size_ = 0; // Zero size payload
|
|
fua_zero->payload_ = NULL;
|
|
fua_zero_pkt->set_payload(fua_zero, SrsRtpPacketPayloadTypeFUA2);
|
|
|
|
int fua_zero_size = builder.calculate_packet_payload_size(fua_zero_pkt.get());
|
|
EXPECT_EQ(0, fua_zero_size); // Should return 0 for zero-size payload
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::calculate_packet_payload_size with H.264 STAP-A payload
|
|
VOID TEST(SrsRtcFrameBuilderTest, CalculatePacketPayloadSizeSTAP)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Test H.264 STAP-A payload (SrsRtpSTAPPayload) with multiple NALUs
|
|
SrsUniquePtr<SrsRtpPacket> stap_pkt(new SrsRtpPacket());
|
|
SrsRtpSTAPPayload *stap = new SrsRtpSTAPPayload();
|
|
|
|
// Add first NALU (SPS)
|
|
SrsNaluSample *sps_sample = new SrsNaluSample();
|
|
char sps_data[20];
|
|
memset(sps_data, 0x67, sizeof(sps_data)); // SPS NALU type
|
|
sps_sample->bytes_ = sps_data;
|
|
sps_sample->size_ = sizeof(sps_data);
|
|
stap->nalus_.push_back(sps_sample);
|
|
|
|
// Add second NALU (PPS)
|
|
SrsNaluSample *pps_sample = new SrsNaluSample();
|
|
char pps_data[10];
|
|
memset(pps_data, 0x68, sizeof(pps_data)); // PPS NALU type
|
|
pps_sample->bytes_ = pps_data;
|
|
pps_sample->size_ = sizeof(pps_data);
|
|
stap->nalus_.push_back(pps_sample);
|
|
|
|
// Add third NALU (IDR slice)
|
|
SrsNaluSample *idr_sample = new SrsNaluSample();
|
|
char idr_data[100];
|
|
memset(idr_data, 0x65, sizeof(idr_data)); // IDR NALU type
|
|
idr_sample->bytes_ = idr_data;
|
|
idr_sample->size_ = sizeof(idr_data);
|
|
stap->nalus_.push_back(idr_sample);
|
|
|
|
stap_pkt->set_payload(stap, SrsRtpPacketPayloadTypeSTAP);
|
|
|
|
int stap_size = builder.calculate_packet_payload_size(stap_pkt.get());
|
|
// Expected: (4 + 20) + (4 + 10) + (4 + 100) = 24 + 14 + 104 = 142
|
|
EXPECT_EQ(142, stap_size);
|
|
|
|
// Test STAP-A payload with empty NALUs (should be skipped)
|
|
SrsUniquePtr<SrsRtpPacket> stap_empty_pkt(new SrsRtpPacket());
|
|
SrsRtpSTAPPayload *stap_empty = new SrsRtpSTAPPayload();
|
|
|
|
// Add NALU with zero size (should be skipped)
|
|
SrsNaluSample *empty_sample = new SrsNaluSample();
|
|
empty_sample->bytes_ = NULL;
|
|
empty_sample->size_ = 0;
|
|
stap_empty->nalus_.push_back(empty_sample);
|
|
|
|
// Add valid NALU
|
|
SrsNaluSample *valid_sample = new SrsNaluSample();
|
|
char valid_data[30];
|
|
memset(valid_data, 0x41, sizeof(valid_data));
|
|
valid_sample->bytes_ = valid_data;
|
|
valid_sample->size_ = sizeof(valid_data);
|
|
stap_empty->nalus_.push_back(valid_sample);
|
|
|
|
stap_empty_pkt->set_payload(stap_empty, SrsRtpPacketPayloadTypeSTAP);
|
|
|
|
int stap_empty_size = builder.calculate_packet_payload_size(stap_empty_pkt.get());
|
|
EXPECT_EQ(34, stap_empty_size); // Only valid NALU: 4 + 30 = 34
|
|
|
|
// Test STAP-A payload with no NALUs
|
|
SrsUniquePtr<SrsRtpPacket> stap_no_nalus_pkt(new SrsRtpPacket());
|
|
SrsRtpSTAPPayload *stap_no_nalus = new SrsRtpSTAPPayload();
|
|
stap_no_nalus_pkt->set_payload(stap_no_nalus, SrsRtpPacketPayloadTypeSTAP);
|
|
|
|
int stap_no_nalus_size = builder.calculate_packet_payload_size(stap_no_nalus_pkt.get());
|
|
EXPECT_EQ(0, stap_no_nalus_size); // No NALUs, should return 0
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::calculate_packet_payload_size with H.265 FU-A payload
|
|
VOID TEST(SrsRtcFrameBuilderTest, CalculatePacketPayloadSizeFUAHevc)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Test H.265 FU-A payload (SrsRtpFUAPayloadHevc2) - start fragment
|
|
SrsUniquePtr<SrsRtpPacket> fua_hevc_start_pkt(new SrsRtpPacket());
|
|
SrsRtpFUAPayloadHevc2 *fua_hevc_start = new SrsRtpFUAPayloadHevc2();
|
|
fua_hevc_start->start_ = true;
|
|
fua_hevc_start->end_ = false;
|
|
fua_hevc_start->nalu_type_ = SrsHevcNaluType_CODED_SLICE_IDR;
|
|
fua_hevc_start->size_ = 120; // 120 bytes of payload data
|
|
char fua_hevc_data[120];
|
|
memset(fua_hevc_data, 0x26, sizeof(fua_hevc_data)); // HEVC IDR slice
|
|
fua_hevc_start->payload_ = fua_hevc_data;
|
|
fua_hevc_start_pkt->set_payload(fua_hevc_start, SrsRtpPacketPayloadTypeFUAHevc2);
|
|
|
|
int fua_hevc_start_size = builder.calculate_packet_payload_size(fua_hevc_start_pkt.get());
|
|
EXPECT_EQ(126, fua_hevc_start_size); // 120 + 2 (HEVC NALU header) + 4 (length prefix) = 126
|
|
|
|
// Test H.265 FU-A payload - middle fragment
|
|
SrsUniquePtr<SrsRtpPacket> fua_hevc_middle_pkt(new SrsRtpPacket());
|
|
SrsRtpFUAPayloadHevc2 *fua_hevc_middle = new SrsRtpFUAPayloadHevc2();
|
|
fua_hevc_middle->start_ = false;
|
|
fua_hevc_middle->end_ = false;
|
|
fua_hevc_middle->nalu_type_ = SrsHevcNaluType_CODED_SLICE_IDR;
|
|
fua_hevc_middle->size_ = 90; // 90 bytes of payload data
|
|
char fua_hevc_middle_data[90];
|
|
memset(fua_hevc_middle_data, 0x27, sizeof(fua_hevc_middle_data));
|
|
fua_hevc_middle->payload_ = fua_hevc_middle_data;
|
|
fua_hevc_middle_pkt->set_payload(fua_hevc_middle, SrsRtpPacketPayloadTypeFUAHevc2);
|
|
|
|
int fua_hevc_middle_size = builder.calculate_packet_payload_size(fua_hevc_middle_pkt.get());
|
|
EXPECT_EQ(90, fua_hevc_middle_size); // Only payload size, no additional headers for middle fragments
|
|
|
|
// Test H.265 FU-A payload - end fragment
|
|
SrsUniquePtr<SrsRtpPacket> fua_hevc_end_pkt(new SrsRtpPacket());
|
|
SrsRtpFUAPayloadHevc2 *fua_hevc_end = new SrsRtpFUAPayloadHevc2();
|
|
fua_hevc_end->start_ = false;
|
|
fua_hevc_end->end_ = true;
|
|
fua_hevc_end->nalu_type_ = SrsHevcNaluType_CODED_SLICE_IDR;
|
|
fua_hevc_end->size_ = 70; // 70 bytes of payload data
|
|
char fua_hevc_end_data[70];
|
|
memset(fua_hevc_end_data, 0x28, sizeof(fua_hevc_end_data));
|
|
fua_hevc_end->payload_ = fua_hevc_end_data;
|
|
fua_hevc_end_pkt->set_payload(fua_hevc_end, SrsRtpPacketPayloadTypeFUAHevc2);
|
|
|
|
int fua_hevc_end_size = builder.calculate_packet_payload_size(fua_hevc_end_pkt.get());
|
|
EXPECT_EQ(70, fua_hevc_end_size); // Only payload size, no additional headers for end fragments
|
|
|
|
// Test H.265 FU-A payload with zero size
|
|
SrsUniquePtr<SrsRtpPacket> fua_hevc_zero_pkt(new SrsRtpPacket());
|
|
SrsRtpFUAPayloadHevc2 *fua_hevc_zero = new SrsRtpFUAPayloadHevc2();
|
|
fua_hevc_zero->start_ = true;
|
|
fua_hevc_zero->end_ = false;
|
|
fua_hevc_zero->nalu_type_ = SrsHevcNaluType_CODED_SLICE_IDR;
|
|
fua_hevc_zero->size_ = 0; // Zero size payload
|
|
fua_hevc_zero->payload_ = NULL;
|
|
fua_hevc_zero_pkt->set_payload(fua_hevc_zero, SrsRtpPacketPayloadTypeFUAHevc2);
|
|
|
|
int fua_hevc_zero_size = builder.calculate_packet_payload_size(fua_hevc_zero_pkt.get());
|
|
EXPECT_EQ(0, fua_hevc_zero_size); // Should return 0 for zero-size payload
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::calculate_packet_payload_size with H.265 STAP payload
|
|
VOID TEST(SrsRtcFrameBuilderTest, CalculatePacketPayloadSizeSTAPHevc)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Test H.265 STAP payload (SrsRtpSTAPPayloadHevc) with multiple NALUs
|
|
SrsUniquePtr<SrsRtpPacket> stap_hevc_pkt(new SrsRtpPacket());
|
|
SrsRtpSTAPPayloadHevc *stap_hevc = new SrsRtpSTAPPayloadHevc();
|
|
|
|
// Add first NALU (VPS)
|
|
SrsNaluSample *vps_sample = new SrsNaluSample();
|
|
char vps_data[25];
|
|
memset(vps_data, 0x40, sizeof(vps_data)); // VPS NALU type
|
|
vps_sample->bytes_ = vps_data;
|
|
vps_sample->size_ = sizeof(vps_data);
|
|
stap_hevc->nalus_.push_back(vps_sample);
|
|
|
|
// Add second NALU (SPS)
|
|
SrsNaluSample *sps_sample = new SrsNaluSample();
|
|
char sps_data[30];
|
|
memset(sps_data, 0x42, sizeof(sps_data)); // SPS NALU type
|
|
sps_sample->bytes_ = sps_data;
|
|
sps_sample->size_ = sizeof(sps_data);
|
|
stap_hevc->nalus_.push_back(sps_sample);
|
|
|
|
// Add third NALU (PPS)
|
|
SrsNaluSample *pps_sample = new SrsNaluSample();
|
|
char pps_data[15];
|
|
memset(pps_data, 0x44, sizeof(pps_data)); // PPS NALU type
|
|
pps_sample->bytes_ = pps_data;
|
|
pps_sample->size_ = sizeof(pps_data);
|
|
stap_hevc->nalus_.push_back(pps_sample);
|
|
|
|
// Add fourth NALU (IDR slice)
|
|
SrsNaluSample *idr_sample = new SrsNaluSample();
|
|
char idr_data[150];
|
|
memset(idr_data, 0x26, sizeof(idr_data)); // IDR NALU type
|
|
idr_sample->bytes_ = idr_data;
|
|
idr_sample->size_ = sizeof(idr_data);
|
|
stap_hevc->nalus_.push_back(idr_sample);
|
|
|
|
stap_hevc_pkt->set_payload(stap_hevc, SrsRtpPacketPayloadTypeSTAPHevc);
|
|
|
|
int stap_hevc_size = builder.calculate_packet_payload_size(stap_hevc_pkt.get());
|
|
// Expected: (4 + 25) + (4 + 30) + (4 + 15) + (4 + 150) = 29 + 34 + 19 + 154 = 236
|
|
EXPECT_EQ(236, stap_hevc_size);
|
|
|
|
// Test HEVC STAP payload with empty NALUs (should be skipped)
|
|
SrsUniquePtr<SrsRtpPacket> stap_hevc_empty_pkt(new SrsRtpPacket());
|
|
SrsRtpSTAPPayloadHevc *stap_hevc_empty = new SrsRtpSTAPPayloadHevc();
|
|
|
|
// Add NALU with zero size (should be skipped)
|
|
SrsNaluSample *empty_sample = new SrsNaluSample();
|
|
empty_sample->bytes_ = NULL;
|
|
empty_sample->size_ = 0;
|
|
stap_hevc_empty->nalus_.push_back(empty_sample);
|
|
|
|
// Add valid NALU
|
|
SrsNaluSample *valid_sample = new SrsNaluSample();
|
|
char valid_data[40];
|
|
memset(valid_data, 0x42, sizeof(valid_data));
|
|
valid_sample->bytes_ = valid_data;
|
|
valid_sample->size_ = sizeof(valid_data);
|
|
stap_hevc_empty->nalus_.push_back(valid_sample);
|
|
|
|
stap_hevc_empty_pkt->set_payload(stap_hevc_empty, SrsRtpPacketPayloadTypeSTAPHevc);
|
|
|
|
int stap_hevc_empty_size = builder.calculate_packet_payload_size(stap_hevc_empty_pkt.get());
|
|
EXPECT_EQ(44, stap_hevc_empty_size); // Only valid NALU: 4 + 40 = 44
|
|
|
|
// Test HEVC STAP payload with no NALUs
|
|
SrsUniquePtr<SrsRtpPacket> stap_hevc_no_nalus_pkt(new SrsRtpPacket());
|
|
SrsRtpSTAPPayloadHevc *stap_hevc_no_nalus = new SrsRtpSTAPPayloadHevc();
|
|
stap_hevc_no_nalus_pkt->set_payload(stap_hevc_no_nalus, SrsRtpPacketPayloadTypeSTAPHevc);
|
|
|
|
int stap_hevc_no_nalus_size = builder.calculate_packet_payload_size(stap_hevc_no_nalus_pkt.get());
|
|
EXPECT_EQ(0, stap_hevc_no_nalus_size); // No NALUs, should return 0
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::calculate_packet_payload_size with edge cases and fallback
|
|
VOID TEST(SrsRtcFrameBuilderTest, CalculatePacketPayloadSizeEdgeCases)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Test with unknown payload type (should fall through to default case)
|
|
SrsUniquePtr<SrsRtpPacket> unknown_pkt(new SrsRtpPacket());
|
|
SrsRtpRawPayload *raw_payload = new SrsRtpRawPayload();
|
|
char raw_data[50];
|
|
memset(raw_data, 0xFF, sizeof(raw_data));
|
|
raw_payload->payload_ = raw_data;
|
|
raw_payload->nn_payload_ = sizeof(raw_data);
|
|
unknown_pkt->set_payload(raw_payload, SrsRtpPacketPayloadTypeRaw);
|
|
|
|
int unknown_size = builder.calculate_packet_payload_size(unknown_pkt.get());
|
|
// For raw payload, it should return the payload size + 4 bytes length prefix
|
|
EXPECT_EQ(54, unknown_size); // 50 + 4 = 54
|
|
|
|
// Test with packet that has payload but payload is NULL
|
|
SrsUniquePtr<SrsRtpPacket> null_payload_pkt(new SrsRtpPacket());
|
|
// Set payload type but don't actually set a payload object
|
|
null_payload_pkt->payload_type_ = SrsRtpPacketPayloadTypeRaw;
|
|
|
|
int null_payload_size = builder.calculate_packet_payload_size(null_payload_pkt.get());
|
|
EXPECT_EQ(0, null_payload_size); // Should return 0 for NULL payload
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::write_packet_payload_to_buffer with raw payload
|
|
VOID TEST(SrsRtcFrameBuilderTest, WritePacketPayloadToBufferRaw)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Create test RTP packet with raw payload
|
|
SrsUniquePtr<SrsRtpPacket> pkt(new SrsRtpPacket());
|
|
pkt->header_.set_payload_type(96);
|
|
pkt->header_.set_ssrc(12345);
|
|
pkt->header_.set_sequence(1000);
|
|
pkt->header_.set_timestamp(100);
|
|
|
|
// Create a raw payload with actual data
|
|
SrsRtpRawPayload *raw_payload = new SrsRtpRawPayload();
|
|
char test_data[64];
|
|
memset(test_data, 0xAB, sizeof(test_data)); // Fill with test pattern
|
|
raw_payload->payload_ = test_data;
|
|
raw_payload->nn_payload_ = sizeof(test_data);
|
|
pkt->set_payload(raw_payload, SrsRtpPacketPayloadTypeRaw);
|
|
|
|
// Create buffer for writing
|
|
char buffer_data[1024];
|
|
SrsBuffer buffer(buffer_data, sizeof(buffer_data));
|
|
int nalu_len = 0;
|
|
|
|
// Test write_packet_payload_to_buffer method
|
|
builder.write_packet_payload_to_buffer(pkt.get(), buffer, nalu_len);
|
|
|
|
// Verify that data was written to buffer (4 bytes length + 64 bytes payload = 68 bytes)
|
|
EXPECT_EQ(68, buffer.pos()); // 4 bytes length prefix + 64 bytes payload
|
|
EXPECT_EQ(0, nalu_len); // nalu_len is only set for FU-A payloads, not raw payloads
|
|
|
|
// Test with NULL packet
|
|
SrsBuffer null_buffer(buffer_data, sizeof(buffer_data)); // Create fresh buffer
|
|
nalu_len = 0;
|
|
builder.write_packet_payload_to_buffer(NULL, null_buffer, nalu_len);
|
|
EXPECT_EQ(0, null_buffer.pos());
|
|
EXPECT_EQ(0, nalu_len);
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::write_packet_payload_to_buffer with H.264 FU-A start fragment
|
|
VOID TEST(SrsRtcFrameBuilderTest, WritePacketPayloadToBufferFUAStart)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Create test RTP packet with H.264 FU-A start fragment
|
|
SrsUniquePtr<SrsRtpPacket> pkt(new SrsRtpPacket());
|
|
pkt->header_.set_payload_type(96);
|
|
pkt->header_.set_ssrc(12345);
|
|
pkt->header_.set_sequence(1000);
|
|
pkt->header_.set_timestamp(100);
|
|
|
|
// Create FU-A payload for start fragment
|
|
SrsRtpFUAPayload2 *fua_payload = new SrsRtpFUAPayload2();
|
|
fua_payload->start_ = true;
|
|
fua_payload->end_ = false;
|
|
fua_payload->nri_ = SrsAvcNaluTypeNonIDR;
|
|
fua_payload->nalu_type_ = SrsAvcNaluTypeIDR;
|
|
|
|
char fua_data[50];
|
|
memset(fua_data, 0xCD, sizeof(fua_data)); // Fill with test pattern
|
|
fua_payload->payload_ = fua_data;
|
|
fua_payload->size_ = sizeof(fua_data);
|
|
pkt->set_payload(fua_payload, SrsRtpPacketPayloadTypeFUA2);
|
|
|
|
// Create buffer for writing
|
|
char buffer_data[1024];
|
|
SrsBuffer buffer(buffer_data, sizeof(buffer_data));
|
|
int nalu_len = 0;
|
|
|
|
// Test write_packet_payload_to_buffer method
|
|
builder.write_packet_payload_to_buffer(pkt.get(), buffer, nalu_len);
|
|
|
|
// For FU-A start fragment:
|
|
// - nalu_len should be set to payload size + 1 (for NALU header)
|
|
// - Buffer should skip 4 bytes initially (for length prefix to be written later)
|
|
// - Buffer should write 1 byte NALU header (nri | nalu_type)
|
|
// - Buffer should write payload data
|
|
EXPECT_EQ(51, nalu_len); // 50 bytes payload + 1 byte NALU header
|
|
EXPECT_EQ(4 + 1 + 50, buffer.pos()); // 4 bytes skipped + 1 byte NALU header + 50 bytes payload
|
|
|
|
// Verify NALU header was written correctly (nri | nalu_type)
|
|
uint8_t expected_nalu_header = fua_payload->nri_ | fua_payload->nalu_type_;
|
|
EXPECT_EQ(expected_nalu_header, (uint8_t)buffer_data[4]); // NALU header at position 4
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::write_packet_payload_to_buffer with H.264 FU-A middle fragment
|
|
VOID TEST(SrsRtcFrameBuilderTest, WritePacketPayloadToBufferFUAMiddle)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Create test RTP packet with H.264 FU-A middle fragment
|
|
SrsUniquePtr<SrsRtpPacket> pkt(new SrsRtpPacket());
|
|
pkt->header_.set_payload_type(96);
|
|
pkt->header_.set_ssrc(12345);
|
|
pkt->header_.set_sequence(1001);
|
|
pkt->header_.set_timestamp(100);
|
|
|
|
// Create FU-A payload for middle fragment
|
|
SrsRtpFUAPayload2 *fua_payload = new SrsRtpFUAPayload2();
|
|
fua_payload->start_ = false;
|
|
fua_payload->end_ = false;
|
|
fua_payload->nri_ = SrsAvcNaluTypeNonIDR;
|
|
fua_payload->nalu_type_ = SrsAvcNaluTypeIDR;
|
|
|
|
char fua_data[30];
|
|
memset(fua_data, 0xEF, sizeof(fua_data)); // Fill with test pattern
|
|
fua_payload->payload_ = fua_data;
|
|
fua_payload->size_ = sizeof(fua_data);
|
|
pkt->set_payload(fua_payload, SrsRtpPacketPayloadTypeFUA2);
|
|
|
|
// Create buffer for writing and simulate previous nalu_len from start fragment
|
|
char buffer_data[1024];
|
|
SrsBuffer buffer(buffer_data, sizeof(buffer_data));
|
|
int nalu_len = 51; // Simulate previous nalu_len from start fragment
|
|
|
|
// Test write_packet_payload_to_buffer method
|
|
builder.write_packet_payload_to_buffer(pkt.get(), buffer, nalu_len);
|
|
|
|
// For FU-A middle fragment:
|
|
// - nalu_len should be incremented by payload size
|
|
// - Buffer should only write payload data (no NALU header)
|
|
EXPECT_EQ(51 + 30, nalu_len); // Previous 51 + 30 bytes payload
|
|
EXPECT_EQ(30, buffer.pos()); // Only 30 bytes payload written
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::write_packet_payload_to_buffer with H.264 FU-A end fragment
|
|
VOID TEST(SrsRtcFrameBuilderTest, WritePacketPayloadToBufferFUAEnd)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Create test RTP packet with H.264 FU-A end fragment
|
|
SrsUniquePtr<SrsRtpPacket> pkt(new SrsRtpPacket());
|
|
pkt->header_.set_payload_type(96);
|
|
pkt->header_.set_ssrc(12345);
|
|
pkt->header_.set_sequence(1002);
|
|
pkt->header_.set_timestamp(100);
|
|
|
|
// Create FU-A payload for end fragment
|
|
SrsRtpFUAPayload2 *fua_payload = new SrsRtpFUAPayload2();
|
|
fua_payload->start_ = false;
|
|
fua_payload->end_ = true;
|
|
fua_payload->nri_ = SrsAvcNaluTypeNonIDR;
|
|
fua_payload->nalu_type_ = SrsAvcNaluTypeIDR;
|
|
|
|
char fua_data[20];
|
|
memset(fua_data, 0x12, sizeof(fua_data)); // Fill with test pattern
|
|
fua_payload->payload_ = fua_data;
|
|
fua_payload->size_ = sizeof(fua_data);
|
|
pkt->set_payload(fua_payload, SrsRtpPacketPayloadTypeFUA2);
|
|
|
|
// Create buffer for writing and simulate previous nalu_len from start+middle fragments
|
|
char buffer_data[1024];
|
|
SrsBuffer buffer(buffer_data, sizeof(buffer_data));
|
|
// Simulate buffer position after start fragment (4 bytes skipped + 1 NALU header + 50 payload)
|
|
// and middle fragment (30 bytes payload)
|
|
buffer.skip(4 + 1 + 50 + 30); // Position buffer as if previous fragments were written
|
|
int nalu_len = 81; // Simulate previous nalu_len: 51 (start) + 30 (middle)
|
|
|
|
// Test write_packet_payload_to_buffer method
|
|
builder.write_packet_payload_to_buffer(pkt.get(), buffer, nalu_len);
|
|
|
|
// For FU-A end fragment:
|
|
// - nalu_len should be incremented by payload size
|
|
// - Buffer should write payload data
|
|
// - Buffer should then write nalu_len back to the beginning (skip back, write length, skip forward)
|
|
EXPECT_EQ(81 + 20, nalu_len); // Previous 81 + 20 bytes payload = 101
|
|
|
|
// Buffer position should be back to where it was after writing length and skipping forward
|
|
// After writing payload: position = 4 + 1 + 50 + 30 + 20 = 105
|
|
// After writing length back: skip(-(4 + nalu_len)) = skip(-105), write 4 bytes, skip(nalu_len) = skip(101)
|
|
// Final position should be 4 + 101 = 105
|
|
EXPECT_EQ(4 + 101, buffer.pos());
|
|
|
|
// Verify that the length was written back to the beginning
|
|
uint32_t written_length = 0;
|
|
memcpy(&written_length, buffer_data, 4);
|
|
written_length = ntohl(written_length); // Convert from network byte order
|
|
EXPECT_EQ(101, (int)written_length); // Should match final nalu_len
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::write_packet_payload_to_buffer with H.265 FU-A payloads
|
|
VOID TEST(SrsRtcFrameBuilderTest, WritePacketPayloadToBufferFUAHevc)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Test H.265 FU-A start fragment
|
|
SrsUniquePtr<SrsRtpPacket> hevc_pkt(new SrsRtpPacket());
|
|
hevc_pkt->header_.set_payload_type(96);
|
|
hevc_pkt->header_.set_ssrc(12345);
|
|
hevc_pkt->header_.set_sequence(2000);
|
|
hevc_pkt->header_.set_timestamp(200);
|
|
|
|
// Create H.265 FU-A payload for start fragment
|
|
SrsRtpFUAPayloadHevc2 *hevc_fua = new SrsRtpFUAPayloadHevc2();
|
|
hevc_fua->start_ = true;
|
|
hevc_fua->end_ = false;
|
|
hevc_fua->nalu_type_ = SrsHevcNaluType_CODED_SLICE_IDR;
|
|
|
|
char hevc_data[40];
|
|
memset(hevc_data, 0x34, sizeof(hevc_data)); // Fill with test pattern
|
|
hevc_fua->payload_ = hevc_data;
|
|
hevc_fua->size_ = sizeof(hevc_data);
|
|
hevc_pkt->set_payload(hevc_fua, SrsRtpPacketPayloadTypeFUAHevc2);
|
|
|
|
// Create buffer for writing
|
|
char buffer_data[1024];
|
|
SrsBuffer buffer(buffer_data, sizeof(buffer_data));
|
|
int nalu_len = 0;
|
|
|
|
// Test write_packet_payload_to_buffer method
|
|
builder.write_packet_payload_to_buffer(hevc_pkt.get(), buffer, nalu_len);
|
|
|
|
// H.265 FU-A has different handling than H.264 FU-A for start fragment
|
|
EXPECT_EQ(42, nalu_len); // 40 bytes payload + 2 bytes HEVC NALU header
|
|
EXPECT_EQ(4 + 2 + 40, buffer.pos()); // 4 bytes skipped + 2 bytes HEVC NALU header + 40 bytes payload
|
|
|
|
// Verify HEVC NALU header was written correctly
|
|
// First byte: nalu_type << 1 (19 << 1 = 38 = 0x26)
|
|
EXPECT_EQ((uint8_t)(hevc_fua->nalu_type_ << 1), (uint8_t)buffer_data[4]); // First byte of HEVC NALU header
|
|
// Second byte: 0x01
|
|
EXPECT_EQ(0x01, (uint8_t)buffer_data[5]); // Second byte of HEVC NALU header
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::write_packet_payload_to_buffer with FU-A edge cases
|
|
VOID TEST(SrsRtcFrameBuilderTest, WritePacketPayloadToBufferFUAEdgeCases)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Test H.264 FU-A with zero-size payload
|
|
SrsUniquePtr<SrsRtpPacket> zero_pkt(new SrsRtpPacket());
|
|
zero_pkt->header_.set_payload_type(96);
|
|
zero_pkt->header_.set_ssrc(12345);
|
|
zero_pkt->header_.set_sequence(3000);
|
|
zero_pkt->header_.set_timestamp(300);
|
|
|
|
SrsRtpFUAPayload2 *zero_fua = new SrsRtpFUAPayload2();
|
|
zero_fua->start_ = true;
|
|
zero_fua->end_ = false;
|
|
zero_fua->nri_ = SrsAvcNaluTypeNonIDR;
|
|
zero_fua->nalu_type_ = SrsAvcNaluTypeIDR;
|
|
zero_fua->payload_ = NULL;
|
|
zero_fua->size_ = 0; // Zero size payload
|
|
zero_pkt->set_payload(zero_fua, SrsRtpPacketPayloadTypeFUA2);
|
|
|
|
char buffer_data[1024];
|
|
SrsBuffer buffer(buffer_data, sizeof(buffer_data));
|
|
int nalu_len = 0;
|
|
|
|
// Test with zero-size payload - should not be processed (size_ > 0 check)
|
|
builder.write_packet_payload_to_buffer(zero_pkt.get(), buffer, nalu_len);
|
|
EXPECT_EQ(0, nalu_len); // Should remain 0 since size_ is 0
|
|
EXPECT_EQ(0, buffer.pos()); // Buffer should not be modified
|
|
|
|
// Test H.265 FU-A with zero-size payload
|
|
SrsUniquePtr<SrsRtpPacket> hevc_zero_pkt(new SrsRtpPacket());
|
|
hevc_zero_pkt->header_.set_payload_type(96);
|
|
hevc_zero_pkt->header_.set_ssrc(12345);
|
|
hevc_zero_pkt->header_.set_sequence(3001);
|
|
hevc_zero_pkt->header_.set_timestamp(300);
|
|
|
|
SrsRtpFUAPayloadHevc2 *hevc_zero_fua = new SrsRtpFUAPayloadHevc2();
|
|
hevc_zero_fua->start_ = true;
|
|
hevc_zero_fua->end_ = false;
|
|
hevc_zero_fua->nalu_type_ = SrsHevcNaluType_CODED_SLICE_IDR;
|
|
hevc_zero_fua->payload_ = NULL;
|
|
hevc_zero_fua->size_ = 0; // Zero size payload
|
|
hevc_zero_pkt->set_payload(hevc_zero_fua, SrsRtpPacketPayloadTypeFUAHevc2);
|
|
|
|
SrsBuffer hevc_buffer(buffer_data, sizeof(buffer_data));
|
|
nalu_len = 0;
|
|
|
|
// Test with zero-size HEVC payload - should not be processed
|
|
builder.write_packet_payload_to_buffer(hevc_zero_pkt.get(), hevc_buffer, nalu_len);
|
|
EXPECT_EQ(0, nalu_len); // Should remain 0 since size_ is 0
|
|
EXPECT_EQ(0, hevc_buffer.pos()); // Buffer should not be modified
|
|
|
|
// Test with NULL payload
|
|
SrsUniquePtr<SrsRtpPacket> null_payload_pkt(new SrsRtpPacket());
|
|
null_payload_pkt->header_.set_payload_type(96);
|
|
null_payload_pkt->header_.set_ssrc(12345);
|
|
null_payload_pkt->header_.set_sequence(3002);
|
|
null_payload_pkt->header_.set_timestamp(300);
|
|
// Don't set any payload (payload will be NULL)
|
|
|
|
SrsBuffer null_buffer(buffer_data, sizeof(buffer_data));
|
|
nalu_len = 0;
|
|
|
|
// Test with NULL payload - should not be processed
|
|
builder.write_packet_payload_to_buffer(null_payload_pkt.get(), null_buffer, nalu_len);
|
|
EXPECT_EQ(0, nalu_len); // Should remain 0
|
|
EXPECT_EQ(0, null_buffer.pos()); // Buffer should not be modified
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::write_packet_payload_to_buffer complete FU-A sequence
|
|
VOID TEST(SrsRtcFrameBuilderTest, WritePacketPayloadToBufferFUASequence)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
char buffer_data[1024];
|
|
SrsBuffer buffer(buffer_data, sizeof(buffer_data));
|
|
int nalu_len = 0;
|
|
|
|
// Test complete FU-A sequence: start -> middle -> end
|
|
|
|
// 1. Start fragment
|
|
SrsUniquePtr<SrsRtpPacket> start_pkt(new SrsRtpPacket());
|
|
start_pkt->header_.set_payload_type(96);
|
|
start_pkt->header_.set_ssrc(12345);
|
|
start_pkt->header_.set_sequence(4000);
|
|
start_pkt->header_.set_timestamp(400);
|
|
|
|
SrsRtpFUAPayload2 *start_fua = new SrsRtpFUAPayload2();
|
|
start_fua->start_ = true;
|
|
start_fua->end_ = false;
|
|
start_fua->nri_ = SrsAvcNaluTypeNonIDR;
|
|
start_fua->nalu_type_ = SrsAvcNaluTypeIDR;
|
|
|
|
char start_data[25];
|
|
memset(start_data, 0xAA, sizeof(start_data));
|
|
start_fua->payload_ = start_data;
|
|
start_fua->size_ = sizeof(start_data);
|
|
start_pkt->set_payload(start_fua, SrsRtpPacketPayloadTypeFUA2);
|
|
|
|
builder.write_packet_payload_to_buffer(start_pkt.get(), buffer, nalu_len);
|
|
EXPECT_EQ(26, nalu_len); // 25 + 1 NALU header
|
|
EXPECT_EQ(4 + 1 + 25, buffer.pos()); // 4 skipped + 1 NALU header + 25 payload
|
|
|
|
// 2. Middle fragment
|
|
SrsUniquePtr<SrsRtpPacket> middle_pkt(new SrsRtpPacket());
|
|
middle_pkt->header_.set_payload_type(96);
|
|
middle_pkt->header_.set_ssrc(12345);
|
|
middle_pkt->header_.set_sequence(4001);
|
|
middle_pkt->header_.set_timestamp(400);
|
|
|
|
SrsRtpFUAPayload2 *middle_fua = new SrsRtpFUAPayload2();
|
|
middle_fua->start_ = false;
|
|
middle_fua->end_ = false;
|
|
middle_fua->nri_ = SrsAvcNaluTypeNonIDR;
|
|
middle_fua->nalu_type_ = SrsAvcNaluTypeIDR;
|
|
|
|
char middle_data[15];
|
|
memset(middle_data, 0xBB, sizeof(middle_data));
|
|
middle_fua->payload_ = middle_data;
|
|
middle_fua->size_ = sizeof(middle_data);
|
|
middle_pkt->set_payload(middle_fua, SrsRtpPacketPayloadTypeFUA2);
|
|
|
|
builder.write_packet_payload_to_buffer(middle_pkt.get(), buffer, nalu_len);
|
|
EXPECT_EQ(26 + 15, nalu_len); // Previous 26 + 15 = 41
|
|
EXPECT_EQ(4 + 1 + 25 + 15, buffer.pos()); // Previous position + 15 payload
|
|
|
|
// 3. End fragment
|
|
SrsUniquePtr<SrsRtpPacket> end_pkt(new SrsRtpPacket());
|
|
end_pkt->header_.set_payload_type(96);
|
|
end_pkt->header_.set_ssrc(12345);
|
|
end_pkt->header_.set_sequence(4002);
|
|
end_pkt->header_.set_timestamp(400);
|
|
|
|
SrsRtpFUAPayload2 *end_fua = new SrsRtpFUAPayload2();
|
|
end_fua->start_ = false;
|
|
end_fua->end_ = true;
|
|
end_fua->nri_ = SrsAvcNaluTypeNonIDR;
|
|
end_fua->nalu_type_ = SrsAvcNaluTypeIDR;
|
|
|
|
char end_data[10];
|
|
memset(end_data, 0xCC, sizeof(end_data));
|
|
end_fua->payload_ = end_data;
|
|
end_fua->size_ = sizeof(end_data);
|
|
end_pkt->set_payload(end_fua, SrsRtpPacketPayloadTypeFUA2);
|
|
|
|
builder.write_packet_payload_to_buffer(end_pkt.get(), buffer, nalu_len);
|
|
EXPECT_EQ(41 + 10, nalu_len); // Previous 41 + 10 = 51
|
|
EXPECT_EQ(4 + 51, buffer.pos()); // Should be positioned after length + complete NALU
|
|
|
|
// Verify that the length was written back to the beginning
|
|
uint32_t written_length = 0;
|
|
memcpy(&written_length, buffer_data, 4);
|
|
written_length = ntohl(written_length); // Convert from network byte order
|
|
EXPECT_EQ(51, (int)written_length); // Should match final nalu_len
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::write_packet_payload_to_buffer with H.264 STAP payload
|
|
VOID TEST(SrsRtcFrameBuilderTest, WritePacketPayloadToBufferSTAP)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Create test RTP packet with H.264 STAP payload
|
|
SrsUniquePtr<SrsRtpPacket> pkt(new SrsRtpPacket());
|
|
pkt->header_.set_payload_type(96);
|
|
pkt->header_.set_ssrc(12345);
|
|
pkt->header_.set_sequence(5000);
|
|
pkt->header_.set_timestamp(500);
|
|
|
|
// Create STAP payload with multiple NALUs
|
|
SrsRtpSTAPPayload *stap_payload = new SrsRtpSTAPPayload();
|
|
|
|
// Add first NALU (SPS)
|
|
SrsNaluSample *sps_sample = new SrsNaluSample();
|
|
char sps_data[20];
|
|
memset(sps_data, 0x67, sizeof(sps_data)); // SPS NALU type
|
|
sps_sample->bytes_ = sps_data;
|
|
sps_sample->size_ = sizeof(sps_data);
|
|
stap_payload->nalus_.push_back(sps_sample);
|
|
|
|
// Add second NALU (PPS)
|
|
SrsNaluSample *pps_sample = new SrsNaluSample();
|
|
char pps_data[10];
|
|
memset(pps_data, 0x68, sizeof(pps_data)); // PPS NALU type
|
|
pps_sample->bytes_ = pps_data;
|
|
pps_sample->size_ = sizeof(pps_data);
|
|
stap_payload->nalus_.push_back(pps_sample);
|
|
|
|
// Add third NALU (IDR slice)
|
|
SrsNaluSample *idr_sample = new SrsNaluSample();
|
|
char idr_data[50];
|
|
memset(idr_data, 0x65, sizeof(idr_data)); // IDR NALU type
|
|
idr_sample->bytes_ = idr_data;
|
|
idr_sample->size_ = sizeof(idr_data);
|
|
stap_payload->nalus_.push_back(idr_sample);
|
|
|
|
pkt->set_payload(stap_payload, SrsRtpPacketPayloadTypeSTAP);
|
|
|
|
// Create buffer for writing
|
|
char buffer_data[1024];
|
|
SrsBuffer buffer(buffer_data, sizeof(buffer_data));
|
|
int nalu_len = 0;
|
|
|
|
// Test write_packet_payload_to_buffer method
|
|
builder.write_packet_payload_to_buffer(pkt.get(), buffer, nalu_len);
|
|
|
|
// For STAP payload:
|
|
// - Each NALU should be written with 4-byte length prefix + NALU data
|
|
// - Expected: (4 + 20) + (4 + 10) + (4 + 50) = 24 + 14 + 54 = 92 bytes
|
|
EXPECT_EQ(92, buffer.pos());
|
|
EXPECT_EQ(0, nalu_len); // nalu_len is not modified for STAP payloads
|
|
|
|
// Verify the first NALU length was written correctly
|
|
uint32_t first_nalu_length = 0;
|
|
memcpy(&first_nalu_length, buffer_data, 4);
|
|
first_nalu_length = ntohl(first_nalu_length); // Convert from network byte order
|
|
EXPECT_EQ(20, (int)first_nalu_length); // Should match SPS NALU size
|
|
|
|
// Verify the second NALU length was written correctly
|
|
uint32_t second_nalu_length = 0;
|
|
memcpy(&second_nalu_length, buffer_data + 24, 4); // Skip first NALU (4 + 20 bytes)
|
|
second_nalu_length = ntohl(second_nalu_length);
|
|
EXPECT_EQ(10, (int)second_nalu_length); // Should match PPS NALU size
|
|
|
|
// Verify the third NALU length was written correctly
|
|
uint32_t third_nalu_length = 0;
|
|
memcpy(&third_nalu_length, buffer_data + 38, 4); // Skip first two NALUs (24 + 14 bytes)
|
|
third_nalu_length = ntohl(third_nalu_length);
|
|
EXPECT_EQ(50, (int)third_nalu_length); // Should match IDR NALU size
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::write_packet_payload_to_buffer with H.264 STAP payload containing empty NALUs
|
|
VOID TEST(SrsRtcFrameBuilderTest, WritePacketPayloadToBufferSTAPWithEmptyNALUs)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Create test RTP packet with H.264 STAP payload
|
|
SrsUniquePtr<SrsRtpPacket> pkt(new SrsRtpPacket());
|
|
pkt->header_.set_payload_type(96);
|
|
pkt->header_.set_ssrc(12345);
|
|
pkt->header_.set_sequence(5001);
|
|
pkt->header_.set_timestamp(500);
|
|
|
|
// Create STAP payload with mixed empty and valid NALUs
|
|
SrsRtpSTAPPayload *stap_payload = new SrsRtpSTAPPayload();
|
|
|
|
// Add empty NALU (should be skipped)
|
|
SrsNaluSample *empty_sample1 = new SrsNaluSample();
|
|
empty_sample1->bytes_ = NULL;
|
|
empty_sample1->size_ = 0;
|
|
stap_payload->nalus_.push_back(empty_sample1);
|
|
|
|
// Add valid NALU
|
|
SrsNaluSample *valid_sample = new SrsNaluSample();
|
|
char valid_data[30];
|
|
memset(valid_data, 0x67, sizeof(valid_data)); // SPS NALU type
|
|
valid_sample->bytes_ = valid_data;
|
|
valid_sample->size_ = sizeof(valid_data);
|
|
stap_payload->nalus_.push_back(valid_sample);
|
|
|
|
// Add another empty NALU (should be skipped)
|
|
SrsNaluSample *empty_sample2 = new SrsNaluSample();
|
|
empty_sample2->bytes_ = NULL;
|
|
empty_sample2->size_ = 0;
|
|
stap_payload->nalus_.push_back(empty_sample2);
|
|
|
|
// Add another valid NALU
|
|
SrsNaluSample *valid_sample2 = new SrsNaluSample();
|
|
char valid_data2[15];
|
|
memset(valid_data2, 0x68, sizeof(valid_data2)); // PPS NALU type
|
|
valid_sample2->bytes_ = valid_data2;
|
|
valid_sample2->size_ = sizeof(valid_data2);
|
|
stap_payload->nalus_.push_back(valid_sample2);
|
|
|
|
pkt->set_payload(stap_payload, SrsRtpPacketPayloadTypeSTAP);
|
|
|
|
// Create buffer for writing
|
|
char buffer_data[1024];
|
|
SrsBuffer buffer(buffer_data, sizeof(buffer_data));
|
|
int nalu_len = 0;
|
|
|
|
// Test write_packet_payload_to_buffer method
|
|
builder.write_packet_payload_to_buffer(pkt.get(), buffer, nalu_len);
|
|
|
|
// For STAP payload with empty NALUs:
|
|
// - Empty NALUs (size_ = 0) should be skipped
|
|
// - Only valid NALUs should be written: (4 + 30) + (4 + 15) = 34 + 19 = 53 bytes
|
|
EXPECT_EQ(53, buffer.pos());
|
|
EXPECT_EQ(0, nalu_len); // nalu_len is not modified for STAP payloads
|
|
|
|
// Verify the first valid NALU length was written correctly
|
|
uint32_t first_nalu_length = 0;
|
|
memcpy(&first_nalu_length, buffer_data, 4);
|
|
first_nalu_length = ntohl(first_nalu_length); // Convert from network byte order
|
|
EXPECT_EQ(30, (int)first_nalu_length); // Should match first valid NALU size
|
|
|
|
// Verify the second valid NALU length was written correctly
|
|
uint32_t second_nalu_length = 0;
|
|
memcpy(&second_nalu_length, buffer_data + 34, 4); // Skip first NALU (4 + 30 bytes)
|
|
second_nalu_length = ntohl(second_nalu_length);
|
|
EXPECT_EQ(15, (int)second_nalu_length); // Should match second valid NALU size
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::write_packet_payload_to_buffer with H.265 FU-A start fragment
|
|
VOID TEST(SrsRtcFrameBuilderTest, WritePacketPayloadToBufferFUAHevcStart)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Create test RTP packet with H.265 FU-A start fragment
|
|
SrsUniquePtr<SrsRtpPacket> pkt(new SrsRtpPacket());
|
|
pkt->header_.set_payload_type(96);
|
|
pkt->header_.set_ssrc(12345);
|
|
pkt->header_.set_sequence(6000);
|
|
pkt->header_.set_timestamp(600);
|
|
|
|
// Create H.265 FU-A payload for start fragment
|
|
SrsRtpFUAPayloadHevc2 *fua_hevc = new SrsRtpFUAPayloadHevc2();
|
|
fua_hevc->start_ = true;
|
|
fua_hevc->end_ = false;
|
|
fua_hevc->nalu_type_ = SrsHevcNaluType_CODED_SLICE_IDR; // IDR slice (19)
|
|
|
|
char hevc_data[40];
|
|
memset(hevc_data, 0x26, sizeof(hevc_data)); // Fill with test pattern
|
|
fua_hevc->payload_ = hevc_data;
|
|
fua_hevc->size_ = sizeof(hevc_data);
|
|
pkt->set_payload(fua_hevc, SrsRtpPacketPayloadTypeFUAHevc2);
|
|
|
|
// Create buffer for writing
|
|
char buffer_data[1024];
|
|
SrsBuffer buffer(buffer_data, sizeof(buffer_data));
|
|
int nalu_len = 0;
|
|
|
|
// Test write_packet_payload_to_buffer method
|
|
builder.write_packet_payload_to_buffer(pkt.get(), buffer, nalu_len);
|
|
|
|
// For H.265 FU-A start fragment:
|
|
// - nalu_len should be set to payload size + 2 (for HEVC NALU header)
|
|
// - Buffer should skip 4 bytes initially (for length prefix to be written later)
|
|
// - Buffer should write 2 bytes HEVC NALU header (nalu_type << 1, 0x01)
|
|
// - Buffer should write payload data
|
|
EXPECT_EQ(42, nalu_len); // 40 bytes payload + 2 bytes HEVC NALU header
|
|
EXPECT_EQ(4 + 2 + 40, buffer.pos()); // 4 bytes skipped + 2 bytes HEVC NALU header + 40 bytes payload
|
|
|
|
// Verify HEVC NALU header was written correctly
|
|
// First byte: nalu_type << 1 (19 << 1 = 38 = 0x26)
|
|
EXPECT_EQ((uint8_t)(fua_hevc->nalu_type_ << 1), (uint8_t)buffer_data[4]); // First byte of HEVC NALU header
|
|
// Second byte: 0x01
|
|
EXPECT_EQ(0x01, (uint8_t)buffer_data[5]); // Second byte of HEVC NALU header
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::write_packet_payload_to_buffer with H.265 FU-A middle fragment
|
|
VOID TEST(SrsRtcFrameBuilderTest, WritePacketPayloadToBufferFUAHevcMiddle)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Create test RTP packet with H.265 FU-A middle fragment
|
|
SrsUniquePtr<SrsRtpPacket> pkt(new SrsRtpPacket());
|
|
pkt->header_.set_payload_type(96);
|
|
pkt->header_.set_ssrc(12345);
|
|
pkt->header_.set_sequence(6001);
|
|
pkt->header_.set_timestamp(600);
|
|
|
|
// Create H.265 FU-A payload for middle fragment
|
|
SrsRtpFUAPayloadHevc2 *fua_hevc = new SrsRtpFUAPayloadHevc2();
|
|
fua_hevc->start_ = false;
|
|
fua_hevc->end_ = false;
|
|
fua_hevc->nalu_type_ = SrsHevcNaluType_CODED_SLICE_IDR; // IDR slice (19)
|
|
|
|
char hevc_data[30];
|
|
memset(hevc_data, 0x27, sizeof(hevc_data)); // Fill with test pattern
|
|
fua_hevc->payload_ = hevc_data;
|
|
fua_hevc->size_ = sizeof(hevc_data);
|
|
pkt->set_payload(fua_hevc, SrsRtpPacketPayloadTypeFUAHevc2);
|
|
|
|
// Create buffer for writing and simulate previous nalu_len from start fragment
|
|
char buffer_data[1024];
|
|
SrsBuffer buffer(buffer_data, sizeof(buffer_data));
|
|
int nalu_len = 42; // Simulate previous nalu_len from start fragment
|
|
|
|
// Test write_packet_payload_to_buffer method
|
|
builder.write_packet_payload_to_buffer(pkt.get(), buffer, nalu_len);
|
|
|
|
// For H.265 FU-A middle fragment:
|
|
// - nalu_len should be incremented by payload size
|
|
// - Buffer should only write payload data (no HEVC NALU header)
|
|
EXPECT_EQ(42 + 30, nalu_len); // Previous 42 + 30 bytes payload
|
|
EXPECT_EQ(30, buffer.pos()); // Only 30 bytes payload written
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::write_packet_payload_to_buffer with H.265 FU-A end fragment
|
|
VOID TEST(SrsRtcFrameBuilderTest, WritePacketPayloadToBufferFUAHevcEnd)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Create test RTP packet with H.265 FU-A end fragment
|
|
SrsUniquePtr<SrsRtpPacket> pkt(new SrsRtpPacket());
|
|
pkt->header_.set_payload_type(96);
|
|
pkt->header_.set_ssrc(12345);
|
|
pkt->header_.set_sequence(6002);
|
|
pkt->header_.set_timestamp(600);
|
|
|
|
// Create H.265 FU-A payload for end fragment
|
|
SrsRtpFUAPayloadHevc2 *fua_hevc = new SrsRtpFUAPayloadHevc2();
|
|
fua_hevc->start_ = false;
|
|
fua_hevc->end_ = true;
|
|
fua_hevc->nalu_type_ = SrsHevcNaluType_CODED_SLICE_IDR; // IDR slice (19)
|
|
|
|
char hevc_data[20];
|
|
memset(hevc_data, 0x28, sizeof(hevc_data)); // Fill with test pattern
|
|
fua_hevc->payload_ = hevc_data;
|
|
fua_hevc->size_ = sizeof(hevc_data);
|
|
pkt->set_payload(fua_hevc, SrsRtpPacketPayloadTypeFUAHevc2);
|
|
|
|
// Create buffer for writing and simulate previous nalu_len from start+middle fragments
|
|
char buffer_data[1024];
|
|
SrsBuffer buffer(buffer_data, sizeof(buffer_data));
|
|
// Simulate buffer position after start fragment (4 bytes skipped + 2 HEVC NALU header + 40 payload)
|
|
// and middle fragment (30 bytes payload)
|
|
buffer.skip(4 + 2 + 40 + 30); // Position buffer as if previous fragments were written
|
|
int nalu_len = 72; // Simulate previous nalu_len: 42 (start) + 30 (middle)
|
|
|
|
// Test write_packet_payload_to_buffer method
|
|
builder.write_packet_payload_to_buffer(pkt.get(), buffer, nalu_len);
|
|
|
|
// For H.265 FU-A end fragment:
|
|
// - nalu_len should be incremented by payload size
|
|
// - Buffer should write payload data
|
|
// - Buffer should then write nalu_len back to the beginning (skip back, write length, skip forward)
|
|
EXPECT_EQ(72 + 20, nalu_len); // Previous 72 + 20 bytes payload = 92
|
|
|
|
// Buffer position should be back to where it was after writing length and skipping forward
|
|
// After writing payload: position = 4 + 2 + 40 + 30 + 20 = 96
|
|
// After writing length back: skip(-(4 + nalu_len)) = skip(-96), write 4 bytes, skip(nalu_len) = skip(92)
|
|
// Final position should be 4 + 92 = 96
|
|
EXPECT_EQ(4 + 92, buffer.pos());
|
|
|
|
// Verify that the length was written back to the beginning
|
|
uint32_t written_length = 0;
|
|
memcpy(&written_length, buffer_data, 4);
|
|
written_length = ntohl(written_length); // Convert from network byte order
|
|
EXPECT_EQ(92, (int)written_length); // Should match final nalu_len
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::write_packet_payload_to_buffer with H.265 STAP payload
|
|
VOID TEST(SrsRtcFrameBuilderTest, WritePacketPayloadToBufferSTAPHevc)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Create test RTP packet with H.265 STAP payload
|
|
SrsUniquePtr<SrsRtpPacket> pkt(new SrsRtpPacket());
|
|
pkt->header_.set_payload_type(96);
|
|
pkt->header_.set_ssrc(12345);
|
|
pkt->header_.set_sequence(7000);
|
|
pkt->header_.set_timestamp(700);
|
|
|
|
// Create HEVC STAP payload with multiple NALUs
|
|
SrsRtpSTAPPayloadHevc *stap_hevc = new SrsRtpSTAPPayloadHevc();
|
|
|
|
// Add first NALU (VPS)
|
|
SrsNaluSample *vps_sample = new SrsNaluSample();
|
|
char vps_data[25];
|
|
memset(vps_data, 0x40, sizeof(vps_data)); // VPS NALU type
|
|
vps_sample->bytes_ = vps_data;
|
|
vps_sample->size_ = sizeof(vps_data);
|
|
stap_hevc->nalus_.push_back(vps_sample);
|
|
|
|
// Add second NALU (SPS)
|
|
SrsNaluSample *sps_sample = new SrsNaluSample();
|
|
char sps_data[30];
|
|
memset(sps_data, 0x42, sizeof(sps_data)); // SPS NALU type
|
|
sps_sample->bytes_ = sps_data;
|
|
sps_sample->size_ = sizeof(sps_data);
|
|
stap_hevc->nalus_.push_back(sps_sample);
|
|
|
|
// Add third NALU (PPS)
|
|
SrsNaluSample *pps_sample = new SrsNaluSample();
|
|
char pps_data[15];
|
|
memset(pps_data, 0x44, sizeof(pps_data)); // PPS NALU type
|
|
pps_sample->bytes_ = pps_data;
|
|
pps_sample->size_ = sizeof(pps_data);
|
|
stap_hevc->nalus_.push_back(pps_sample);
|
|
|
|
pkt->set_payload(stap_hevc, SrsRtpPacketPayloadTypeSTAPHevc);
|
|
|
|
// Create buffer for writing
|
|
char buffer_data[1024];
|
|
SrsBuffer buffer(buffer_data, sizeof(buffer_data));
|
|
int nalu_len = 0;
|
|
|
|
// Test write_packet_payload_to_buffer method
|
|
builder.write_packet_payload_to_buffer(pkt.get(), buffer, nalu_len);
|
|
|
|
// For HEVC STAP payload:
|
|
// - Each NALU should be written with 4-byte length prefix + NALU data
|
|
// - Expected: (4 + 25) + (4 + 30) + (4 + 15) = 29 + 34 + 19 = 82 bytes
|
|
EXPECT_EQ(82, buffer.pos());
|
|
EXPECT_EQ(0, nalu_len); // nalu_len is not modified for STAP payloads
|
|
|
|
// Verify the first NALU length was written correctly
|
|
uint32_t first_nalu_length = 0;
|
|
memcpy(&first_nalu_length, buffer_data, 4);
|
|
first_nalu_length = ntohl(first_nalu_length); // Convert from network byte order
|
|
EXPECT_EQ(25, (int)first_nalu_length); // Should match VPS NALU size
|
|
|
|
// Verify the second NALU length was written correctly
|
|
uint32_t second_nalu_length = 0;
|
|
memcpy(&second_nalu_length, buffer_data + 29, 4); // Skip first NALU (4 + 25 bytes)
|
|
second_nalu_length = ntohl(second_nalu_length);
|
|
EXPECT_EQ(30, (int)second_nalu_length); // Should match SPS NALU size
|
|
|
|
// Verify the third NALU length was written correctly
|
|
uint32_t third_nalu_length = 0;
|
|
memcpy(&third_nalu_length, buffer_data + 63, 4); // Skip first two NALUs (29 + 34 bytes)
|
|
third_nalu_length = ntohl(third_nalu_length);
|
|
EXPECT_EQ(15, (int)third_nalu_length); // Should match PPS NALU size
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::write_packet_payload_to_buffer with H.265 STAP payload containing empty NALUs
|
|
VOID TEST(SrsRtcFrameBuilderTest, WritePacketPayloadToBufferSTAPHevcWithEmptyNALUs)
|
|
{
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Create test RTP packet with H.265 STAP payload
|
|
SrsUniquePtr<SrsRtpPacket> pkt(new SrsRtpPacket());
|
|
pkt->header_.set_payload_type(96);
|
|
pkt->header_.set_ssrc(12345);
|
|
pkt->header_.set_sequence(7001);
|
|
pkt->header_.set_timestamp(700);
|
|
|
|
// Create HEVC STAP payload with mixed empty and valid NALUs
|
|
SrsRtpSTAPPayloadHevc *stap_hevc = new SrsRtpSTAPPayloadHevc();
|
|
|
|
// Add empty NALU (should be skipped)
|
|
SrsNaluSample *empty_sample1 = new SrsNaluSample();
|
|
empty_sample1->bytes_ = NULL;
|
|
empty_sample1->size_ = 0;
|
|
stap_hevc->nalus_.push_back(empty_sample1);
|
|
|
|
// Add valid NALU (VPS)
|
|
SrsNaluSample *vps_sample = new SrsNaluSample();
|
|
char vps_data[20];
|
|
memset(vps_data, 0x40, sizeof(vps_data)); // VPS NALU type
|
|
vps_sample->bytes_ = vps_data;
|
|
vps_sample->size_ = sizeof(vps_data);
|
|
stap_hevc->nalus_.push_back(vps_sample);
|
|
|
|
// Add another empty NALU (should be skipped)
|
|
SrsNaluSample *empty_sample2 = new SrsNaluSample();
|
|
empty_sample2->bytes_ = NULL;
|
|
empty_sample2->size_ = 0;
|
|
stap_hevc->nalus_.push_back(empty_sample2);
|
|
|
|
// Add valid NALU (SPS)
|
|
SrsNaluSample *sps_sample = new SrsNaluSample();
|
|
char sps_data[35];
|
|
memset(sps_data, 0x42, sizeof(sps_data)); // SPS NALU type
|
|
sps_sample->bytes_ = sps_data;
|
|
sps_sample->size_ = sizeof(sps_data);
|
|
stap_hevc->nalus_.push_back(sps_sample);
|
|
|
|
// Add final empty NALU (should be skipped)
|
|
SrsNaluSample *empty_sample3 = new SrsNaluSample();
|
|
empty_sample3->bytes_ = NULL;
|
|
empty_sample3->size_ = 0;
|
|
stap_hevc->nalus_.push_back(empty_sample3);
|
|
|
|
pkt->set_payload(stap_hevc, SrsRtpPacketPayloadTypeSTAPHevc);
|
|
|
|
// Create buffer for writing
|
|
char buffer_data[1024];
|
|
SrsBuffer buffer(buffer_data, sizeof(buffer_data));
|
|
int nalu_len = 0;
|
|
|
|
// Test write_packet_payload_to_buffer method
|
|
builder.write_packet_payload_to_buffer(pkt.get(), buffer, nalu_len);
|
|
|
|
// For HEVC STAP payload with empty NALUs:
|
|
// - Empty NALUs (size_ = 0) should be skipped
|
|
// - Only valid NALUs should be written: (4 + 20) + (4 + 35) = 24 + 39 = 63 bytes
|
|
EXPECT_EQ(63, buffer.pos());
|
|
EXPECT_EQ(0, nalu_len); // nalu_len is not modified for STAP payloads
|
|
|
|
// Verify the first valid NALU length was written correctly
|
|
uint32_t first_nalu_length = 0;
|
|
memcpy(&first_nalu_length, buffer_data, 4);
|
|
first_nalu_length = ntohl(first_nalu_length); // Convert from network byte order
|
|
EXPECT_EQ(20, (int)first_nalu_length); // Should match VPS NALU size
|
|
|
|
// Verify the second valid NALU length was written correctly
|
|
uint32_t second_nalu_length = 0;
|
|
memcpy(&second_nalu_length, buffer_data + 24, 4); // Skip first NALU (4 + 20 bytes)
|
|
second_nalu_length = ntohl(second_nalu_length);
|
|
EXPECT_EQ(35, (int)second_nalu_length); // Should match SPS NALU size
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::packet_video_rtmp with empty NALU handling - basic case
|
|
VOID TEST(SrsRtcFrameBuilderTest, PacketVideoRtmpEmptyNaluBasic)
|
|
{
|
|
srs_error_t err;
|
|
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Initialize builder with H.264 codec
|
|
SrsUniquePtr<MockRtcRequest> req(new MockRtcRequest());
|
|
HELPER_EXPECT_SUCCESS(builder.initialize(req.get(), SrsAudioCodecIdAAC, SrsVideoCodecIdAVC));
|
|
|
|
// Create empty RTP packets (no payload) to simulate empty NALU scenario
|
|
SrsUniquePtr<SrsRtpPacket> empty_pkt1(new SrsRtpPacket());
|
|
empty_pkt1->header_.set_sequence(100);
|
|
empty_pkt1->header_.set_timestamp(1000);
|
|
empty_pkt1->header_.set_ssrc(12345);
|
|
empty_pkt1->header_.set_payload_type(96);
|
|
// No payload set - this will result in 0 payload size
|
|
|
|
SrsUniquePtr<SrsRtpPacket> empty_pkt2(new SrsRtpPacket());
|
|
empty_pkt2->header_.set_sequence(101);
|
|
empty_pkt2->header_.set_timestamp(1000);
|
|
empty_pkt2->header_.set_ssrc(12345);
|
|
empty_pkt2->header_.set_payload_type(96);
|
|
// No payload set - this will result in 0 payload size
|
|
|
|
// Store empty packets in video cache
|
|
builder.video_cache_->store_packet(empty_pkt1->copy());
|
|
builder.video_cache_->store_packet(empty_pkt2->copy());
|
|
|
|
// Create next frame packets that should be processed after empty frame is skipped
|
|
SrsUniquePtr<SrsRtpPacket> next_pkt1(create_test_rtp_packet(102, 2000, 12345, false));
|
|
SrsUniquePtr<SrsRtpPacket> next_pkt2(create_test_rtp_packet(103, 2000, 12345, true)); // marker bit
|
|
|
|
// Store next frame packets in video cache
|
|
builder.video_cache_->store_packet(next_pkt1->copy());
|
|
builder.video_cache_->store_packet(next_pkt2->copy());
|
|
|
|
// Test packet_video_rtmp with empty frame range (100-101)
|
|
// This should trigger the empty NALU handling logic and process next frame (102-103)
|
|
HELPER_EXPECT_SUCCESS(builder.packet_video_rtmp(100, 101));
|
|
|
|
// Verify that a frame was generated (from the next frame, not the empty one)
|
|
EXPECT_EQ(1, target.on_frame_count_);
|
|
EXPECT_TRUE(target.last_frame_ != NULL);
|
|
|
|
// Verify the frame is a video frame
|
|
EXPECT_TRUE(target.last_frame_->is_video());
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::packet_video_rtmp with empty NALU handling - no next frame available
|
|
VOID TEST(SrsRtcFrameBuilderTest, PacketVideoRtmpEmptyNaluNoNextFrame)
|
|
{
|
|
srs_error_t err;
|
|
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Initialize builder with H.264 codec
|
|
SrsUniquePtr<MockRtcRequest> req(new MockRtcRequest());
|
|
HELPER_EXPECT_SUCCESS(builder.initialize(req.get(), SrsAudioCodecIdAAC, SrsVideoCodecIdAVC));
|
|
|
|
// Create empty RTP packets (no payload) to simulate empty NALU scenario
|
|
SrsUniquePtr<SrsRtpPacket> empty_pkt1(new SrsRtpPacket());
|
|
empty_pkt1->header_.set_sequence(100);
|
|
empty_pkt1->header_.set_timestamp(1000);
|
|
empty_pkt1->header_.set_ssrc(12345);
|
|
empty_pkt1->header_.set_payload_type(96);
|
|
// No payload set - this will result in 0 payload size
|
|
|
|
SrsUniquePtr<SrsRtpPacket> empty_pkt2(new SrsRtpPacket());
|
|
empty_pkt2->header_.set_sequence(101);
|
|
empty_pkt2->header_.set_timestamp(1000);
|
|
empty_pkt2->header_.set_ssrc(12345);
|
|
empty_pkt2->header_.set_payload_type(96);
|
|
// No payload set - this will result in 0 payload size
|
|
|
|
// Store empty packets in video cache
|
|
builder.video_cache_->store_packet(empty_pkt1->copy());
|
|
builder.video_cache_->store_packet(empty_pkt2->copy());
|
|
|
|
// Do NOT store any next frame packets - this tests the case where detect_next_frame
|
|
// returns got_frame = false
|
|
|
|
// Test packet_video_rtmp with empty frame range (100-101)
|
|
// This should trigger the empty NALU handling logic but find no next frame
|
|
HELPER_EXPECT_SUCCESS(builder.packet_video_rtmp(100, 101));
|
|
|
|
// Verify that no frame was generated since there was no next frame available
|
|
EXPECT_EQ(0, target.on_frame_count_);
|
|
EXPECT_TRUE(target.last_frame_ == NULL);
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::packet_video_rtmp with empty NALU handling - recursive call with next frame
|
|
VOID TEST(SrsRtcFrameBuilderTest, PacketVideoRtmpEmptyNaluRecursiveCall)
|
|
{
|
|
srs_error_t err;
|
|
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Initialize builder with H.264 codec
|
|
SrsUniquePtr<MockRtcRequest> req(new MockRtcRequest());
|
|
HELPER_EXPECT_SUCCESS(builder.initialize(req.get(), SrsAudioCodecIdAAC, SrsVideoCodecIdAVC));
|
|
|
|
// Create empty RTP packets (no payload) for first frame
|
|
SrsUniquePtr<SrsRtpPacket> empty_pkt1(new SrsRtpPacket());
|
|
empty_pkt1->header_.set_sequence(100);
|
|
empty_pkt1->header_.set_timestamp(1000);
|
|
empty_pkt1->header_.set_ssrc(12345);
|
|
empty_pkt1->header_.set_payload_type(96);
|
|
|
|
SrsUniquePtr<SrsRtpPacket> empty_pkt2(new SrsRtpPacket());
|
|
empty_pkt2->header_.set_sequence(101);
|
|
empty_pkt2->header_.set_timestamp(1000);
|
|
empty_pkt2->header_.set_ssrc(12345);
|
|
empty_pkt2->header_.set_payload_type(96);
|
|
|
|
// Store empty packets in video cache
|
|
builder.video_cache_->store_packet(empty_pkt1->copy());
|
|
builder.video_cache_->store_packet(empty_pkt2->copy());
|
|
|
|
// Create valid next frame packets with actual payload
|
|
SrsUniquePtr<SrsRtpPacket> next_pkt1(create_test_rtp_packet(102, 2000, 12345, false));
|
|
SrsUniquePtr<SrsRtpPacket> next_pkt2(create_test_rtp_packet(103, 2000, 12345, false));
|
|
SrsUniquePtr<SrsRtpPacket> next_pkt3(create_test_rtp_packet(104, 2000, 12345, true)); // marker bit
|
|
|
|
// Store next frame packets in video cache
|
|
builder.video_cache_->store_packet(next_pkt1->copy());
|
|
builder.video_cache_->store_packet(next_pkt2->copy());
|
|
builder.video_cache_->store_packet(next_pkt3->copy());
|
|
|
|
// Test packet_video_rtmp with empty frame range (100-101)
|
|
// This should:
|
|
// 1. Detect empty NALU (nb_payload == 0)
|
|
// 2. Call detect_next_frame(102, ...)
|
|
// 3. Find next frame (102-104)
|
|
// 4. Recursively call packet_video_rtmp(102, 104)
|
|
// 5. Process the valid frame
|
|
HELPER_EXPECT_SUCCESS(builder.packet_video_rtmp(100, 101));
|
|
|
|
// Verify that a frame was generated from the recursive call
|
|
EXPECT_EQ(1, target.on_frame_count_);
|
|
EXPECT_TRUE(target.last_frame_ != NULL);
|
|
EXPECT_TRUE(target.last_frame_->is_video());
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::packet_video_rtmp with empty NALU handling - multiple empty frames
|
|
VOID TEST(SrsRtcFrameBuilderTest, PacketVideoRtmpEmptyNaluMultipleEmptyFrames)
|
|
{
|
|
srs_error_t err;
|
|
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Initialize builder with H.264 codec
|
|
SrsUniquePtr<MockRtcRequest> req(new MockRtcRequest());
|
|
HELPER_EXPECT_SUCCESS(builder.initialize(req.get(), SrsAudioCodecIdAAC, SrsVideoCodecIdAVC));
|
|
|
|
// Create multiple empty frames in sequence
|
|
// First empty frame (100-101)
|
|
SrsUniquePtr<SrsRtpPacket> empty_pkt1(new SrsRtpPacket());
|
|
empty_pkt1->header_.set_sequence(100);
|
|
empty_pkt1->header_.set_timestamp(1000);
|
|
empty_pkt1->header_.set_ssrc(12345);
|
|
empty_pkt1->header_.set_payload_type(96);
|
|
|
|
SrsUniquePtr<SrsRtpPacket> empty_pkt2(new SrsRtpPacket());
|
|
empty_pkt2->header_.set_sequence(101);
|
|
empty_pkt2->header_.set_timestamp(1000);
|
|
empty_pkt2->header_.set_ssrc(12345);
|
|
empty_pkt2->header_.set_payload_type(96);
|
|
|
|
// Second empty frame (102-103)
|
|
SrsUniquePtr<SrsRtpPacket> empty_pkt3(new SrsRtpPacket());
|
|
empty_pkt3->header_.set_sequence(102);
|
|
empty_pkt3->header_.set_timestamp(2000);
|
|
empty_pkt3->header_.set_ssrc(12345);
|
|
empty_pkt3->header_.set_payload_type(96);
|
|
|
|
SrsUniquePtr<SrsRtpPacket> empty_pkt4(new SrsRtpPacket());
|
|
empty_pkt4->header_.set_sequence(103);
|
|
empty_pkt4->header_.set_timestamp(2000);
|
|
empty_pkt4->header_.set_ssrc(12345);
|
|
empty_pkt4->header_.set_payload_type(96);
|
|
|
|
// Valid frame (104-105)
|
|
SrsUniquePtr<SrsRtpPacket> valid_pkt1(create_test_rtp_packet(104, 3000, 12345, false));
|
|
SrsUniquePtr<SrsRtpPacket> valid_pkt2(create_test_rtp_packet(105, 3000, 12345, true)); // marker bit
|
|
|
|
// Store all packets in video cache
|
|
builder.video_cache_->store_packet(empty_pkt1->copy());
|
|
builder.video_cache_->store_packet(empty_pkt2->copy());
|
|
builder.video_cache_->store_packet(empty_pkt3->copy());
|
|
builder.video_cache_->store_packet(empty_pkt4->copy());
|
|
builder.video_cache_->store_packet(valid_pkt1->copy());
|
|
builder.video_cache_->store_packet(valid_pkt2->copy());
|
|
|
|
// Test packet_video_rtmp with first empty frame range (100-101)
|
|
// This should skip multiple empty frames and eventually process the valid frame (104-105)
|
|
HELPER_EXPECT_SUCCESS(builder.packet_video_rtmp(100, 101));
|
|
|
|
// Verify that a frame was generated (should be the valid frame after skipping empty ones)
|
|
EXPECT_EQ(1, target.on_frame_count_);
|
|
EXPECT_TRUE(target.last_frame_ != NULL);
|
|
EXPECT_TRUE(target.last_frame_->is_video());
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::packet_video_rtmp with empty NALU handling - zero-size payloads
|
|
VOID TEST(SrsRtcFrameBuilderTest, PacketVideoRtmpEmptyNaluZeroSizePayloads)
|
|
{
|
|
srs_error_t err;
|
|
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Initialize builder with H.264 codec
|
|
SrsUniquePtr<MockRtcRequest> req(new MockRtcRequest());
|
|
HELPER_EXPECT_SUCCESS(builder.initialize(req.get(), SrsAudioCodecIdAAC, SrsVideoCodecIdAVC));
|
|
|
|
// Create RTP packets with zero-size raw payloads
|
|
SrsUniquePtr<SrsRtpPacket> zero_pkt1(new SrsRtpPacket());
|
|
zero_pkt1->header_.set_sequence(100);
|
|
zero_pkt1->header_.set_timestamp(1000);
|
|
zero_pkt1->header_.set_ssrc(12345);
|
|
zero_pkt1->header_.set_payload_type(96);
|
|
|
|
// Create raw payload with zero size
|
|
SrsRtpRawPayload *raw_payload1 = new SrsRtpRawPayload();
|
|
raw_payload1->payload_ = NULL;
|
|
raw_payload1->nn_payload_ = 0; // Zero size payload
|
|
zero_pkt1->set_payload(raw_payload1, SrsRtpPacketPayloadTypeRaw);
|
|
|
|
SrsUniquePtr<SrsRtpPacket> zero_pkt2(new SrsRtpPacket());
|
|
zero_pkt2->header_.set_sequence(101);
|
|
zero_pkt2->header_.set_timestamp(1000);
|
|
zero_pkt2->header_.set_ssrc(12345);
|
|
zero_pkt2->header_.set_payload_type(96);
|
|
|
|
// Create raw payload with zero size
|
|
SrsRtpRawPayload *raw_payload2 = new SrsRtpRawPayload();
|
|
raw_payload2->payload_ = NULL;
|
|
raw_payload2->nn_payload_ = 0; // Zero size payload
|
|
zero_pkt2->set_payload(raw_payload2, SrsRtpPacketPayloadTypeRaw);
|
|
|
|
// Store zero-size payload packets in video cache
|
|
builder.video_cache_->store_packet(zero_pkt1->copy());
|
|
builder.video_cache_->store_packet(zero_pkt2->copy());
|
|
|
|
// Create valid next frame packets
|
|
SrsUniquePtr<SrsRtpPacket> next_pkt1(create_test_rtp_packet(102, 2000, 12345, false));
|
|
SrsUniquePtr<SrsRtpPacket> next_pkt2(create_test_rtp_packet(103, 2000, 12345, true)); // marker bit
|
|
|
|
// Store next frame packets in video cache
|
|
builder.video_cache_->store_packet(next_pkt1->copy());
|
|
builder.video_cache_->store_packet(next_pkt2->copy());
|
|
|
|
// Test packet_video_rtmp with zero-size payload frame range (100-101)
|
|
// This should trigger the empty NALU handling logic and process next frame (102-103)
|
|
HELPER_EXPECT_SUCCESS(builder.packet_video_rtmp(100, 101));
|
|
|
|
// Verify that a frame was generated (from the next frame, not the zero-size one)
|
|
EXPECT_EQ(1, target.on_frame_count_);
|
|
EXPECT_TRUE(target.last_frame_ != NULL);
|
|
EXPECT_TRUE(target.last_frame_->is_video());
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::packet_video_rtmp with empty NALU handling - H.265 codec
|
|
VOID TEST(SrsRtcFrameBuilderTest, PacketVideoRtmpEmptyNaluHevc)
|
|
{
|
|
srs_error_t err;
|
|
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Initialize builder with H.265 codec
|
|
SrsUniquePtr<MockRtcRequest> req(new MockRtcRequest());
|
|
HELPER_EXPECT_SUCCESS(builder.initialize(req.get(), SrsAudioCodecIdAAC, SrsVideoCodecIdHEVC));
|
|
|
|
// Create empty RTP packets (no payload) to simulate empty NALU scenario
|
|
SrsUniquePtr<SrsRtpPacket> empty_pkt1(new SrsRtpPacket());
|
|
empty_pkt1->header_.set_sequence(100);
|
|
empty_pkt1->header_.set_timestamp(1000);
|
|
empty_pkt1->header_.set_ssrc(12345);
|
|
empty_pkt1->header_.set_payload_type(96);
|
|
// No payload set - this will result in 0 payload size
|
|
|
|
SrsUniquePtr<SrsRtpPacket> empty_pkt2(new SrsRtpPacket());
|
|
empty_pkt2->header_.set_sequence(101);
|
|
empty_pkt2->header_.set_timestamp(1000);
|
|
empty_pkt2->header_.set_ssrc(12345);
|
|
empty_pkt2->header_.set_payload_type(96);
|
|
// No payload set - this will result in 0 payload size
|
|
|
|
// Store empty packets in video cache
|
|
builder.video_cache_->store_packet(empty_pkt1->copy());
|
|
builder.video_cache_->store_packet(empty_pkt2->copy());
|
|
|
|
// Create next frame packets that should be processed after empty frame is skipped
|
|
SrsUniquePtr<SrsRtpPacket> next_pkt1(create_test_rtp_packet(102, 2000, 12345, false));
|
|
SrsUniquePtr<SrsRtpPacket> next_pkt2(create_test_rtp_packet(103, 2000, 12345, true)); // marker bit
|
|
|
|
// Store next frame packets in video cache
|
|
builder.video_cache_->store_packet(next_pkt1->copy());
|
|
builder.video_cache_->store_packet(next_pkt2->copy());
|
|
|
|
// Test packet_video_rtmp with empty frame range (100-101) for H.265
|
|
// This should trigger the empty NALU handling logic and process next frame (102-103)
|
|
HELPER_EXPECT_SUCCESS(builder.packet_video_rtmp(100, 101));
|
|
|
|
// Verify that a frame was generated (from the next frame, not the empty one)
|
|
EXPECT_EQ(1, target.on_frame_count_);
|
|
EXPECT_TRUE(target.last_frame_ != NULL);
|
|
|
|
// Verify the frame is a video frame
|
|
EXPECT_TRUE(target.last_frame_->is_video());
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::packet_video_rtmp with empty NALU handling - sequence number wrap-around
|
|
VOID TEST(SrsRtcFrameBuilderTest, PacketVideoRtmpEmptyNaluSequenceWrapAround)
|
|
{
|
|
srs_error_t err;
|
|
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Initialize builder with H.264 codec
|
|
SrsUniquePtr<MockRtcRequest> req(new MockRtcRequest());
|
|
HELPER_EXPECT_SUCCESS(builder.initialize(req.get(), SrsAudioCodecIdAAC, SrsVideoCodecIdAVC));
|
|
|
|
// Create empty RTP packets near sequence number wrap-around (65535 -> 0)
|
|
SrsUniquePtr<SrsRtpPacket> empty_pkt1(new SrsRtpPacket());
|
|
empty_pkt1->header_.set_sequence(65535); // Max uint16_t
|
|
empty_pkt1->header_.set_timestamp(1000);
|
|
empty_pkt1->header_.set_ssrc(12345);
|
|
empty_pkt1->header_.set_payload_type(96);
|
|
// No payload set - this will result in 0 payload size
|
|
|
|
SrsUniquePtr<SrsRtpPacket> empty_pkt2(new SrsRtpPacket());
|
|
empty_pkt2->header_.set_sequence(0); // Wrap-around to 0
|
|
empty_pkt2->header_.set_timestamp(1000);
|
|
empty_pkt2->header_.set_ssrc(12345);
|
|
empty_pkt2->header_.set_payload_type(96);
|
|
// No payload set - this will result in 0 payload size
|
|
|
|
// Store empty packets in video cache
|
|
builder.video_cache_->store_packet(empty_pkt1->copy());
|
|
builder.video_cache_->store_packet(empty_pkt2->copy());
|
|
|
|
// Create next frame packets after wrap-around
|
|
SrsUniquePtr<SrsRtpPacket> next_pkt1(create_test_rtp_packet(1, 2000, 12345, false));
|
|
SrsUniquePtr<SrsRtpPacket> next_pkt2(create_test_rtp_packet(2, 2000, 12345, true)); // marker bit
|
|
|
|
// Store next frame packets in video cache
|
|
builder.video_cache_->store_packet(next_pkt1->copy());
|
|
builder.video_cache_->store_packet(next_pkt2->copy());
|
|
|
|
// Test packet_video_rtmp with empty frame range (65535-0)
|
|
// This should trigger the empty NALU handling logic and process next frame (1-2)
|
|
HELPER_EXPECT_SUCCESS(builder.packet_video_rtmp(65535, 0));
|
|
|
|
// Verify that a frame was generated (from the next frame, not the empty one)
|
|
EXPECT_EQ(1, target.on_frame_count_);
|
|
EXPECT_TRUE(target.last_frame_ != NULL);
|
|
EXPECT_TRUE(target.last_frame_->is_video());
|
|
}
|
|
|
|
// Test SrsRtcFrameBuilder::packet_video_rtmp with empty NALU handling - verify frame processing
|
|
VOID TEST(SrsRtcFrameBuilderTest, PacketVideoRtmpEmptyNaluFrameProcessing)
|
|
{
|
|
srs_error_t err;
|
|
|
|
MockRtcFrameTarget target;
|
|
SrsRtcFrameBuilder builder(_srs_app_factory, &target);
|
|
|
|
// Initialize builder with H.264 codec
|
|
SrsUniquePtr<MockRtcRequest> req(new MockRtcRequest());
|
|
HELPER_EXPECT_SUCCESS(builder.initialize(req.get(), SrsAudioCodecIdAAC, SrsVideoCodecIdAVC));
|
|
|
|
// Create empty RTP packets (no payload) to simulate empty NALU scenario
|
|
SrsUniquePtr<SrsRtpPacket> empty_pkt1(new SrsRtpPacket());
|
|
empty_pkt1->header_.set_sequence(100);
|
|
empty_pkt1->header_.set_timestamp(1000);
|
|
empty_pkt1->header_.set_ssrc(12345);
|
|
empty_pkt1->header_.set_payload_type(96);
|
|
|
|
SrsUniquePtr<SrsRtpPacket> empty_pkt2(new SrsRtpPacket());
|
|
empty_pkt2->header_.set_sequence(101);
|
|
empty_pkt2->header_.set_timestamp(1000);
|
|
empty_pkt2->header_.set_ssrc(12345);
|
|
empty_pkt2->header_.set_payload_type(96);
|
|
|
|
// Store empty packets in video cache
|
|
builder.video_cache_->store_packet(empty_pkt1->copy());
|
|
builder.video_cache_->store_packet(empty_pkt2->copy());
|
|
|
|
// Create next frame packets that should be processed after empty frame is skipped
|
|
SrsUniquePtr<SrsRtpPacket> next_pkt1(create_test_rtp_packet(102, 2000, 12345, false));
|
|
SrsUniquePtr<SrsRtpPacket> next_pkt2(create_test_rtp_packet(103, 2000, 12345, true)); // marker bit
|
|
|
|
// Store next frame packets in video cache
|
|
builder.video_cache_->store_packet(next_pkt1->copy());
|
|
builder.video_cache_->store_packet(next_pkt2->copy());
|
|
|
|
// Test packet_video_rtmp with empty frame range (100-101)
|
|
// This should trigger the empty NALU handling logic and process next frame (102-103)
|
|
HELPER_EXPECT_SUCCESS(builder.packet_video_rtmp(100, 101));
|
|
|
|
// Verify that the empty NALU handling worked correctly:
|
|
// 1. The empty frame (100-101) was skipped
|
|
// 2. The next frame (102-103) was processed instead
|
|
// 3. A frame was generated and sent to the target
|
|
EXPECT_EQ(1, target.on_frame_count_);
|
|
EXPECT_TRUE(target.last_frame_ != NULL);
|
|
EXPECT_TRUE(target.last_frame_->is_video());
|
|
}
|
|
|
|
// Test SrsCodecPayload::generate_media_payload_type
|
|
VOID TEST(SrsCodecPayloadTest, GenerateMediaPayloadType)
|
|
{
|
|
SrsUniquePtr<SrsCodecPayload> payload(create_test_codec_payload(96, "H264", 90000));
|
|
|
|
SrsMediaPayloadType media_type = payload->generate_media_payload_type();
|
|
|
|
EXPECT_EQ(96, media_type.payload_type_);
|
|
EXPECT_STREQ("H264", media_type.encoding_name_.c_str());
|
|
EXPECT_EQ(90000, media_type.clock_rate_);
|
|
}
|
|
|
|
// Test SrsVideoPayload::generate_media_payload_type with empty H264 parameters
|
|
VOID TEST(SrsVideoPayloadTest, GenerateMediaPayloadTypeEmptyParams)
|
|
{
|
|
SrsVideoPayload payload(102, "H264", 90000);
|
|
|
|
SrsMediaPayloadType media_type = payload.generate_media_payload_type();
|
|
|
|
EXPECT_EQ(102, media_type.payload_type_);
|
|
EXPECT_STREQ("H264", media_type.encoding_name_.c_str());
|
|
EXPECT_EQ(90000, media_type.clock_rate_);
|
|
EXPECT_STREQ("", media_type.format_specific_param_.c_str());
|
|
}
|
|
|
|
// Test SrsVideoPayload::generate_media_payload_type with level_asymmetry_allow only
|
|
VOID TEST(SrsVideoPayloadTest, GenerateMediaPayloadTypeLevelAsymmetryOnly)
|
|
{
|
|
SrsVideoPayload payload(102, "H264", 90000);
|
|
payload.h264_param_.level_asymmetry_allow_ = "1";
|
|
|
|
SrsMediaPayloadType media_type = payload.generate_media_payload_type();
|
|
|
|
EXPECT_EQ(102, media_type.payload_type_);
|
|
EXPECT_STREQ("H264", media_type.encoding_name_.c_str());
|
|
EXPECT_EQ(90000, media_type.clock_rate_);
|
|
EXPECT_STREQ("level-asymmetry-allowed=1", media_type.format_specific_param_.c_str());
|
|
}
|
|
|
|
// Test SrsVideoPayload::generate_media_payload_type with packetization_mode only
|
|
VOID TEST(SrsVideoPayloadTest, GenerateMediaPayloadTypePacketizationModeOnly)
|
|
{
|
|
SrsVideoPayload payload(102, "H264", 90000);
|
|
payload.h264_param_.packetization_mode_ = "1";
|
|
|
|
SrsMediaPayloadType media_type = payload.generate_media_payload_type();
|
|
|
|
EXPECT_EQ(102, media_type.payload_type_);
|
|
EXPECT_STREQ("H264", media_type.encoding_name_.c_str());
|
|
EXPECT_EQ(90000, media_type.clock_rate_);
|
|
EXPECT_STREQ("packetization-mode=1", media_type.format_specific_param_.c_str());
|
|
}
|
|
|
|
// Test SrsVideoPayload::generate_media_payload_type with profile_level_id only
|
|
VOID TEST(SrsVideoPayloadTest, GenerateMediaPayloadTypeProfileLevelIdOnly)
|
|
{
|
|
SrsVideoPayload payload(102, "H264", 90000);
|
|
payload.h264_param_.profile_level_id_ = "42e01f";
|
|
|
|
SrsMediaPayloadType media_type = payload.generate_media_payload_type();
|
|
|
|
EXPECT_EQ(102, media_type.payload_type_);
|
|
EXPECT_STREQ("H264", media_type.encoding_name_.c_str());
|
|
EXPECT_EQ(90000, media_type.clock_rate_);
|
|
EXPECT_STREQ("profile-level-id=42e01f", media_type.format_specific_param_.c_str());
|
|
}
|
|
|
|
// Test SrsVideoPayload::generate_media_payload_type with level_asymmetry_allow and packetization_mode
|
|
VOID TEST(SrsVideoPayloadTest, GenerateMediaPayloadTypeLevelAsymmetryAndPacketization)
|
|
{
|
|
SrsVideoPayload payload(102, "H264", 90000);
|
|
payload.h264_param_.level_asymmetry_allow_ = "1";
|
|
payload.h264_param_.packetization_mode_ = "1";
|
|
|
|
SrsMediaPayloadType media_type = payload.generate_media_payload_type();
|
|
|
|
EXPECT_EQ(102, media_type.payload_type_);
|
|
EXPECT_STREQ("H264", media_type.encoding_name_.c_str());
|
|
EXPECT_EQ(90000, media_type.clock_rate_);
|
|
EXPECT_STREQ("level-asymmetry-allowed=1;packetization-mode=1", media_type.format_specific_param_.c_str());
|
|
}
|
|
|
|
// Test SrsVideoPayload::generate_media_payload_type with level_asymmetry_allow and profile_level_id
|
|
VOID TEST(SrsVideoPayloadTest, GenerateMediaPayloadTypeLevelAsymmetryAndProfileLevelId)
|
|
{
|
|
SrsVideoPayload payload(102, "H264", 90000);
|
|
payload.h264_param_.level_asymmetry_allow_ = "1";
|
|
payload.h264_param_.profile_level_id_ = "42e01f";
|
|
|
|
SrsMediaPayloadType media_type = payload.generate_media_payload_type();
|
|
|
|
EXPECT_EQ(102, media_type.payload_type_);
|
|
EXPECT_STREQ("H264", media_type.encoding_name_.c_str());
|
|
EXPECT_EQ(90000, media_type.clock_rate_);
|
|
EXPECT_STREQ("level-asymmetry-allowed=1;profile-level-id=42e01f", media_type.format_specific_param_.c_str());
|
|
}
|
|
|
|
// Test SrsVideoPayload::generate_media_payload_type with packetization_mode and profile_level_id
|
|
VOID TEST(SrsVideoPayloadTest, GenerateMediaPayloadTypePacketizationAndProfileLevelId)
|
|
{
|
|
SrsVideoPayload payload(102, "H264", 90000);
|
|
payload.h264_param_.packetization_mode_ = "1";
|
|
payload.h264_param_.profile_level_id_ = "42e01f";
|
|
|
|
SrsMediaPayloadType media_type = payload.generate_media_payload_type();
|
|
|
|
EXPECT_EQ(102, media_type.payload_type_);
|
|
EXPECT_STREQ("H264", media_type.encoding_name_.c_str());
|
|
EXPECT_EQ(90000, media_type.clock_rate_);
|
|
EXPECT_STREQ("packetization-mode=1;profile-level-id=42e01f", media_type.format_specific_param_.c_str());
|
|
}
|
|
|
|
// Test SrsVideoPayload::generate_media_payload_type with all H264 parameters
|
|
VOID TEST(SrsVideoPayloadTest, GenerateMediaPayloadTypeAllH264Params)
|
|
{
|
|
SrsVideoPayload payload(102, "H264", 90000);
|
|
payload.h264_param_.level_asymmetry_allow_ = "1";
|
|
payload.h264_param_.packetization_mode_ = "1";
|
|
payload.h264_param_.profile_level_id_ = "42e01f";
|
|
|
|
SrsMediaPayloadType media_type = payload.generate_media_payload_type();
|
|
|
|
EXPECT_EQ(102, media_type.payload_type_);
|
|
EXPECT_STREQ("H264", media_type.encoding_name_.c_str());
|
|
EXPECT_EQ(90000, media_type.clock_rate_);
|
|
EXPECT_STREQ("level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", media_type.format_specific_param_.c_str());
|
|
}
|
|
|
|
// Test SrsVideoPayload::generate_media_payload_type with different parameter values
|
|
VOID TEST(SrsVideoPayloadTest, GenerateMediaPayloadTypeDifferentValues)
|
|
{
|
|
SrsVideoPayload payload(96, "H264", 90000);
|
|
payload.h264_param_.level_asymmetry_allow_ = "0";
|
|
payload.h264_param_.packetization_mode_ = "0";
|
|
payload.h264_param_.profile_level_id_ = "640028";
|
|
|
|
SrsMediaPayloadType media_type = payload.generate_media_payload_type();
|
|
|
|
EXPECT_EQ(96, media_type.payload_type_);
|
|
EXPECT_STREQ("H264", media_type.encoding_name_.c_str());
|
|
EXPECT_EQ(90000, media_type.clock_rate_);
|
|
EXPECT_STREQ("level-asymmetry-allowed=0;packetization-mode=0;profile-level-id=640028", media_type.format_specific_param_.c_str());
|
|
}
|
|
|
|
// Test SrsAudioPayload::generate_media_payload_type
|
|
VOID TEST(SrsAudioPayloadTest, GenerateMediaPayloadType)
|
|
{
|
|
SrsAudioPayload payload(111, "opus", 48000, 2);
|
|
|
|
SrsMediaPayloadType media_type = payload.generate_media_payload_type();
|
|
|
|
EXPECT_EQ(111, media_type.payload_type_);
|
|
EXPECT_STREQ("opus", media_type.encoding_name_.c_str());
|
|
EXPECT_EQ(48000, media_type.clock_rate_);
|
|
}
|
|
|
|
// Test SrsAudioPayload::set_opus_param_desc
|
|
VOID TEST(SrsAudioPayloadTest, SetOpusParamDesc)
|
|
{
|
|
srs_error_t err;
|
|
|
|
SrsAudioPayload payload(111, "opus", 48000, 2);
|
|
|
|
// Test valid opus parameters
|
|
HELPER_EXPECT_SUCCESS(payload.set_opus_param_desc("minptime=10;useinbandfec=1;stereo=1;usedtx=1"));
|
|
|
|
EXPECT_EQ(10, payload.opus_param_.minptime_);
|
|
EXPECT_TRUE(payload.opus_param_.use_inband_fec_);
|
|
EXPECT_TRUE(payload.opus_param_.stereo_);
|
|
EXPECT_TRUE(payload.opus_param_.usedtx_);
|
|
|
|
// Test partial parameters
|
|
SrsAudioPayload payload2(111, "opus", 48000, 2);
|
|
HELPER_EXPECT_SUCCESS(payload2.set_opus_param_desc("minptime=20"));
|
|
|
|
EXPECT_EQ(20, payload2.opus_param_.minptime_);
|
|
EXPECT_FALSE(payload2.opus_param_.use_inband_fec_);
|
|
EXPECT_FALSE(payload2.opus_param_.stereo_);
|
|
EXPECT_FALSE(payload2.opus_param_.usedtx_);
|
|
}
|
|
|
|
// Test SrsAudioPayload::generate_media_payload_type with Opus parameters - individual parameters
|
|
VOID TEST(SrsAudioPayloadTest, GenerateMediaPayloadTypeOpusIndividualParams)
|
|
{
|
|
// Test minptime only
|
|
SrsAudioPayload payload1(111, "opus", 48000, 2);
|
|
payload1.opus_param_.minptime_ = 10;
|
|
|
|
SrsMediaPayloadType media_type1 = payload1.generate_media_payload_type();
|
|
EXPECT_EQ(111, media_type1.payload_type_);
|
|
EXPECT_STREQ("opus", media_type1.encoding_name_.c_str());
|
|
EXPECT_EQ(48000, media_type1.clock_rate_);
|
|
EXPECT_STREQ("2", media_type1.encoding_param_.c_str());
|
|
EXPECT_STREQ("minptime=10", media_type1.format_specific_param_.c_str());
|
|
|
|
// Test use_inband_fec only
|
|
// NOTE: Current implementation has a bug - it adds semicolon even when no preceding content
|
|
// TODO: Should be "useinbandfec=1" instead of ";useinbandfec=1"
|
|
SrsAudioPayload payload2(111, "opus", 48000, 2);
|
|
payload2.opus_param_.use_inband_fec_ = true;
|
|
|
|
SrsMediaPayloadType media_type2 = payload2.generate_media_payload_type();
|
|
EXPECT_STREQ(";useinbandfec=1", media_type2.format_specific_param_.c_str());
|
|
|
|
// Test stereo only
|
|
// NOTE: Current implementation has a bug - it adds semicolon even when no preceding content
|
|
// TODO: Should be "stereo=1" instead of ";stereo=1"
|
|
SrsAudioPayload payload3(111, "opus", 48000, 2);
|
|
payload3.opus_param_.stereo_ = true;
|
|
|
|
SrsMediaPayloadType media_type3 = payload3.generate_media_payload_type();
|
|
EXPECT_STREQ(";stereo=1", media_type3.format_specific_param_.c_str());
|
|
|
|
// Test usedtx only
|
|
// NOTE: Current implementation has a bug - it adds semicolon even when no preceding content
|
|
// TODO: Should be "usedtx=1" instead of ";usedtx=1"
|
|
SrsAudioPayload payload4(111, "opus", 48000, 2);
|
|
payload4.opus_param_.usedtx_ = true;
|
|
|
|
SrsMediaPayloadType media_type4 = payload4.generate_media_payload_type();
|
|
EXPECT_STREQ(";usedtx=1", media_type4.format_specific_param_.c_str());
|
|
}
|
|
|
|
// Test SrsAudioPayload::generate_media_payload_type with Opus parameters - combinations starting with minptime
|
|
VOID TEST(SrsAudioPayloadTest, GenerateMediaPayloadTypeOpusMinptimeCombinations)
|
|
{
|
|
// Test minptime + use_inband_fec
|
|
SrsAudioPayload payload1(111, "opus", 48000, 2);
|
|
payload1.opus_param_.minptime_ = 20;
|
|
payload1.opus_param_.use_inband_fec_ = true;
|
|
|
|
SrsMediaPayloadType media_type1 = payload1.generate_media_payload_type();
|
|
EXPECT_STREQ("minptime=20;useinbandfec=1", media_type1.format_specific_param_.c_str());
|
|
|
|
// Test minptime + stereo
|
|
SrsAudioPayload payload2(111, "opus", 48000, 2);
|
|
payload2.opus_param_.minptime_ = 15;
|
|
payload2.opus_param_.stereo_ = true;
|
|
|
|
SrsMediaPayloadType media_type2 = payload2.generate_media_payload_type();
|
|
EXPECT_STREQ("minptime=15;stereo=1", media_type2.format_specific_param_.c_str());
|
|
|
|
// Test minptime + usedtx
|
|
SrsAudioPayload payload3(111, "opus", 48000, 2);
|
|
payload3.opus_param_.minptime_ = 5;
|
|
payload3.opus_param_.usedtx_ = true;
|
|
|
|
SrsMediaPayloadType media_type3 = payload3.generate_media_payload_type();
|
|
EXPECT_STREQ("minptime=5;usedtx=1", media_type3.format_specific_param_.c_str());
|
|
|
|
// Test minptime + use_inband_fec + stereo
|
|
SrsAudioPayload payload4(111, "opus", 48000, 2);
|
|
payload4.opus_param_.minptime_ = 25;
|
|
payload4.opus_param_.use_inband_fec_ = true;
|
|
payload4.opus_param_.stereo_ = true;
|
|
|
|
SrsMediaPayloadType media_type4 = payload4.generate_media_payload_type();
|
|
EXPECT_STREQ("minptime=25;useinbandfec=1;stereo=1", media_type4.format_specific_param_.c_str());
|
|
|
|
// Test minptime + use_inband_fec + usedtx
|
|
SrsAudioPayload payload5(111, "opus", 48000, 2);
|
|
payload5.opus_param_.minptime_ = 30;
|
|
payload5.opus_param_.use_inband_fec_ = true;
|
|
payload5.opus_param_.usedtx_ = true;
|
|
|
|
SrsMediaPayloadType media_type5 = payload5.generate_media_payload_type();
|
|
EXPECT_STREQ("minptime=30;useinbandfec=1;usedtx=1", media_type5.format_specific_param_.c_str());
|
|
|
|
// Test minptime + stereo + usedtx
|
|
SrsAudioPayload payload6(111, "opus", 48000, 2);
|
|
payload6.opus_param_.minptime_ = 40;
|
|
payload6.opus_param_.stereo_ = true;
|
|
payload6.opus_param_.usedtx_ = true;
|
|
|
|
SrsMediaPayloadType media_type6 = payload6.generate_media_payload_type();
|
|
EXPECT_STREQ("minptime=40;stereo=1;usedtx=1", media_type6.format_specific_param_.c_str());
|
|
|
|
// Test all parameters
|
|
SrsAudioPayload payload7(111, "opus", 48000, 2);
|
|
payload7.opus_param_.minptime_ = 50;
|
|
payload7.opus_param_.use_inband_fec_ = true;
|
|
payload7.opus_param_.stereo_ = true;
|
|
payload7.opus_param_.usedtx_ = true;
|
|
|
|
SrsMediaPayloadType media_type7 = payload7.generate_media_payload_type();
|
|
EXPECT_STREQ("minptime=50;useinbandfec=1;stereo=1;usedtx=1", media_type7.format_specific_param_.c_str());
|
|
}
|
|
|
|
// Test SrsAudioPayload::generate_media_payload_type with Opus parameters - combinations without minptime
|
|
VOID TEST(SrsAudioPayloadTest, GenerateMediaPayloadTypeOpusWithoutMinptimeCombinations)
|
|
{
|
|
// Test use_inband_fec + stereo
|
|
// NOTE: Current implementation has a bug - it adds semicolon even when no preceding content
|
|
// TODO: Should be "useinbandfec=1;stereo=1" instead of ";useinbandfec=1;stereo=1"
|
|
SrsAudioPayload payload1(111, "opus", 48000, 2);
|
|
payload1.opus_param_.use_inband_fec_ = true;
|
|
payload1.opus_param_.stereo_ = true;
|
|
|
|
SrsMediaPayloadType media_type1 = payload1.generate_media_payload_type();
|
|
EXPECT_STREQ(";useinbandfec=1;stereo=1", media_type1.format_specific_param_.c_str());
|
|
|
|
// Test use_inband_fec + usedtx
|
|
// NOTE: Current implementation has a bug - it adds semicolon even when no preceding content
|
|
// TODO: Should be "useinbandfec=1;usedtx=1" instead of ";useinbandfec=1;usedtx=1"
|
|
SrsAudioPayload payload2(111, "opus", 48000, 2);
|
|
payload2.opus_param_.use_inband_fec_ = true;
|
|
payload2.opus_param_.usedtx_ = true;
|
|
|
|
SrsMediaPayloadType media_type2 = payload2.generate_media_payload_type();
|
|
EXPECT_STREQ(";useinbandfec=1;usedtx=1", media_type2.format_specific_param_.c_str());
|
|
|
|
// Test stereo + usedtx
|
|
// NOTE: Current implementation has a bug - it adds semicolon even when no preceding content
|
|
// TODO: Should be "stereo=1;usedtx=1" instead of ";stereo=1;usedtx=1"
|
|
SrsAudioPayload payload3(111, "opus", 48000, 2);
|
|
payload3.opus_param_.stereo_ = true;
|
|
payload3.opus_param_.usedtx_ = true;
|
|
|
|
SrsMediaPayloadType media_type3 = payload3.generate_media_payload_type();
|
|
EXPECT_STREQ(";stereo=1;usedtx=1", media_type3.format_specific_param_.c_str());
|
|
|
|
// Test use_inband_fec + stereo + usedtx
|
|
// NOTE: Current implementation has a bug - it adds semicolon even when no preceding content
|
|
// TODO: Should be "useinbandfec=1;stereo=1;usedtx=1" instead of ";useinbandfec=1;stereo=1;usedtx=1"
|
|
SrsAudioPayload payload4(111, "opus", 48000, 2);
|
|
payload4.opus_param_.use_inband_fec_ = true;
|
|
payload4.opus_param_.stereo_ = true;
|
|
payload4.opus_param_.usedtx_ = true;
|
|
|
|
SrsMediaPayloadType media_type4 = payload4.generate_media_payload_type();
|
|
EXPECT_STREQ(";useinbandfec=1;stereo=1;usedtx=1", media_type4.format_specific_param_.c_str());
|
|
}
|
|
|
|
// Test SrsAudioPayload::generate_media_payload_type with no Opus parameters (empty string)
|
|
VOID TEST(SrsAudioPayloadTest, GenerateMediaPayloadTypeOpusNoParams)
|
|
{
|
|
SrsAudioPayload payload(111, "opus", 48000, 2);
|
|
// All opus_param_ fields remain at default values (false/0)
|
|
|
|
SrsMediaPayloadType media_type = payload.generate_media_payload_type();
|
|
EXPECT_EQ(111, media_type.payload_type_);
|
|
EXPECT_STREQ("opus", media_type.encoding_name_.c_str());
|
|
EXPECT_EQ(48000, media_type.clock_rate_);
|
|
EXPECT_STREQ("2", media_type.encoding_param_.c_str());
|
|
EXPECT_STREQ("", media_type.format_specific_param_.c_str()); // Should be empty string
|
|
}
|
|
|
|
// Test SrsRedPayload
|
|
VOID TEST(SrsRedPayloadTest, BasicFunctionality)
|
|
{
|
|
SrsRedPayload payload(63, "red", 48000, 2);
|
|
|
|
EXPECT_EQ(63, payload.pt_);
|
|
EXPECT_STREQ("red", payload.name_.c_str());
|
|
EXPECT_EQ(48000, payload.sample_);
|
|
EXPECT_EQ(2, payload.channel_);
|
|
|
|
// Test copy
|
|
SrsUniquePtr<SrsRedPayload> copied(payload.copy());
|
|
EXPECT_EQ(63, copied->pt_);
|
|
EXPECT_STREQ("red", copied->name_.c_str());
|
|
EXPECT_EQ(48000, copied->sample_);
|
|
EXPECT_EQ(2, copied->channel_);
|
|
|
|
// Test generate_media_payload_type
|
|
SrsMediaPayloadType media_type = payload.generate_media_payload_type();
|
|
EXPECT_EQ(63, media_type.payload_type_);
|
|
EXPECT_STREQ("red", media_type.encoding_name_.c_str());
|
|
EXPECT_EQ(48000, media_type.clock_rate_);
|
|
}
|
|
|
|
// Test SrsRtxPayloadDes
|
|
VOID TEST(SrsRtxPayloadDesTest, BasicFunctionality)
|
|
{
|
|
SrsRtxPayloadDes payload(96, 97);
|
|
|
|
EXPECT_EQ(96, payload.pt_);
|
|
EXPECT_EQ(97, payload.apt_);
|
|
|
|
// Test copy
|
|
SrsUniquePtr<SrsRtxPayloadDes> copied(payload.copy());
|
|
EXPECT_EQ(96, copied->pt_);
|
|
EXPECT_EQ(97, copied->apt_);
|
|
|
|
// Test generate_media_payload_type
|
|
SrsMediaPayloadType media_type = payload.generate_media_payload_type();
|
|
EXPECT_EQ(96, media_type.payload_type_);
|
|
EXPECT_STREQ("rtx", media_type.encoding_name_.c_str());
|
|
}
|
|
|
|
// Test SrsRtcTrackDescription::add_rtp_extension_desc
|
|
VOID TEST(SrsRtcTrackDescriptionTest, AddRtpExtensionDesc)
|
|
{
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_test_track_description("video", 12345));
|
|
|
|
// Add RTP extension
|
|
desc->add_rtp_extension_desc(1, "urn:ietf:params:rtp-hdrext:ssrc-audio-level");
|
|
desc->add_rtp_extension_desc(2, "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time");
|
|
|
|
// Verify extensions were added
|
|
EXPECT_EQ(2, desc->extmaps_.size());
|
|
EXPECT_STREQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level", desc->extmaps_[1].c_str());
|
|
EXPECT_STREQ("http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time", desc->extmaps_[2].c_str());
|
|
}
|
|
|
|
// Test SrsRtcTrackDescription::del_rtp_extension_desc
|
|
VOID TEST(SrsRtcTrackDescriptionTest, DelRtpExtensionDesc)
|
|
{
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_test_track_description("video", 12345));
|
|
|
|
// Add extensions first
|
|
desc->add_rtp_extension_desc(1, "urn:ietf:params:rtp-hdrext:ssrc-audio-level");
|
|
desc->add_rtp_extension_desc(2, "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time");
|
|
EXPECT_EQ(2, desc->extmaps_.size());
|
|
|
|
// Delete one extension
|
|
desc->del_rtp_extension_desc("urn:ietf:params:rtp-hdrext:ssrc-audio-level");
|
|
EXPECT_EQ(1, desc->extmaps_.size());
|
|
EXPECT_STREQ("http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time", desc->extmaps_[2].c_str());
|
|
|
|
// Delete non-existent extension (should not crash)
|
|
desc->del_rtp_extension_desc("non-existent-extension");
|
|
EXPECT_EQ(1, desc->extmaps_.size());
|
|
}
|
|
|
|
// Test SrsRtcTrackDescription::set_direction
|
|
VOID TEST(SrsRtcTrackDescriptionTest, SetDirection)
|
|
{
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_test_track_description("video", 12345));
|
|
|
|
desc->set_direction("sendonly");
|
|
EXPECT_STREQ("sendonly", desc->direction_.c_str());
|
|
|
|
desc->set_direction("recvonly");
|
|
EXPECT_STREQ("recvonly", desc->direction_.c_str());
|
|
|
|
desc->set_direction("sendrecv");
|
|
EXPECT_STREQ("sendrecv", desc->direction_.c_str());
|
|
|
|
desc->set_direction("inactive");
|
|
EXPECT_STREQ("inactive", desc->direction_.c_str());
|
|
}
|
|
|
|
// Test SrsRtcTrackDescription::set_codec_payload
|
|
VOID TEST(SrsRtcTrackDescriptionTest, SetCodecPayload)
|
|
{
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_test_track_description("video", 12345));
|
|
SrsCodecPayload *payload = create_test_codec_payload(96, "H264", 90000);
|
|
|
|
desc->set_codec_payload(payload); // Track description takes ownership
|
|
|
|
EXPECT_TRUE(desc->media_ != NULL);
|
|
EXPECT_EQ(96, desc->media_->pt_);
|
|
EXPECT_STREQ("H264", desc->media_->name_.c_str());
|
|
EXPECT_EQ(90000, desc->media_->sample_);
|
|
|
|
// Payload ownership transferred to track description, will be cleaned up by desc destructor
|
|
}
|
|
|
|
// Test SrsRtcTrackDescription::create_auxiliary_payload
|
|
VOID TEST(SrsRtcTrackDescriptionTest, CreateAuxiliaryPayload)
|
|
{
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_test_track_description("video", 12345));
|
|
|
|
// Test RED payload type (method only processes first payload)
|
|
std::vector<SrsMediaPayloadType> red_payload_types;
|
|
SrsMediaPayloadType red_type(63);
|
|
red_type.encoding_name_ = "red";
|
|
red_type.clock_rate_ = 90000;
|
|
red_type.encoding_param_ = "2"; // Channel parameter for RED
|
|
red_payload_types.push_back(red_type);
|
|
|
|
desc->create_auxiliary_payload(red_payload_types);
|
|
|
|
// Verify RED payload was created
|
|
EXPECT_TRUE(desc->red_ != NULL);
|
|
EXPECT_EQ(63, desc->red_->pt_);
|
|
EXPECT_STREQ("red", desc->red_->name_.c_str());
|
|
|
|
// Test RTX payload type separately
|
|
std::vector<SrsMediaPayloadType> rtx_payload_types;
|
|
SrsMediaPayloadType rtx_type(96);
|
|
rtx_type.encoding_name_ = "rtx";
|
|
rtx_type.clock_rate_ = 90000;
|
|
rtx_type.encoding_param_ = "97"; // APT parameter for RTX
|
|
rtx_payload_types.push_back(rtx_type);
|
|
|
|
desc->create_auxiliary_payload(rtx_payload_types);
|
|
|
|
// Verify RTX payload was created
|
|
EXPECT_TRUE(desc->rtx_ != NULL);
|
|
EXPECT_EQ(96, desc->rtx_->pt_);
|
|
EXPECT_STREQ("rtx", desc->rtx_->name_.c_str());
|
|
}
|
|
|
|
// Test SrsRtcTrackDescription::set_rtx_ssrc
|
|
VOID TEST(SrsRtcTrackDescriptionTest, SetRtxSsrc)
|
|
{
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_test_track_description("video", 12345));
|
|
|
|
desc->set_rtx_ssrc(67890);
|
|
EXPECT_EQ(67890, desc->rtx_ssrc_);
|
|
}
|
|
|
|
// Test SrsRtcTrackDescription::set_fec_ssrc
|
|
VOID TEST(SrsRtcTrackDescriptionTest, SetFecSsrc)
|
|
{
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_test_track_description("video", 12345));
|
|
|
|
desc->set_fec_ssrc(54321);
|
|
EXPECT_EQ(54321, desc->fec_ssrc_);
|
|
}
|
|
|
|
// Test SrsRtcTrackDescription::set_mid
|
|
VOID TEST(SrsRtcTrackDescriptionTest, SetMid)
|
|
{
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_test_track_description("video", 12345));
|
|
|
|
desc->set_mid("video-mid");
|
|
EXPECT_STREQ("video-mid", desc->mid_.c_str());
|
|
}
|
|
|
|
// Test SrsRtcTrackDescription::get_rtp_extension_id
|
|
VOID TEST(SrsRtcTrackDescriptionTest, GetRtpExtensionId)
|
|
{
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_test_track_description("video", 12345));
|
|
|
|
// Add extensions
|
|
desc->add_rtp_extension_desc(1, "urn:ietf:params:rtp-hdrext:ssrc-audio-level");
|
|
desc->add_rtp_extension_desc(2, "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time");
|
|
|
|
// Test getting extension IDs
|
|
EXPECT_EQ(1, desc->get_rtp_extension_id("urn:ietf:params:rtp-hdrext:ssrc-audio-level"));
|
|
EXPECT_EQ(2, desc->get_rtp_extension_id("http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"));
|
|
|
|
// Test non-existent extension
|
|
EXPECT_EQ(0, desc->get_rtp_extension_id("non-existent-extension"));
|
|
}
|
|
|
|
// Test SrsRtcSourceDescription::find_track_description_by_ssrc
|
|
VOID TEST(SrsRtcSourceDescriptionTest, FindTrackDescriptionBySsrc)
|
|
{
|
|
SrsRtcSourceDescription source_desc;
|
|
|
|
// Create audio track
|
|
source_desc.audio_track_desc_ = create_test_track_description("audio", 11111);
|
|
|
|
// Create video tracks
|
|
source_desc.video_track_descs_.push_back(create_test_track_description("video", 22222));
|
|
source_desc.video_track_descs_.push_back(create_test_track_description("video", 33333));
|
|
|
|
// Test finding audio track
|
|
SrsRtcTrackDescription *found_audio = source_desc.find_track_description_by_ssrc(11111);
|
|
EXPECT_TRUE(found_audio != NULL);
|
|
EXPECT_EQ(11111, found_audio->ssrc_);
|
|
EXPECT_STREQ("audio", found_audio->type_.c_str());
|
|
|
|
// Test finding video tracks
|
|
SrsRtcTrackDescription *found_video1 = source_desc.find_track_description_by_ssrc(22222);
|
|
EXPECT_TRUE(found_video1 != NULL);
|
|
EXPECT_EQ(22222, found_video1->ssrc_);
|
|
EXPECT_STREQ("video", found_video1->type_.c_str());
|
|
|
|
SrsRtcTrackDescription *found_video2 = source_desc.find_track_description_by_ssrc(33333);
|
|
EXPECT_TRUE(found_video2 != NULL);
|
|
EXPECT_EQ(33333, found_video2->ssrc_);
|
|
EXPECT_STREQ("video", found_video2->type_.c_str());
|
|
|
|
// Test finding non-existent SSRC
|
|
SrsRtcTrackDescription *not_found = source_desc.find_track_description_by_ssrc(99999);
|
|
EXPECT_TRUE(not_found == NULL);
|
|
}
|
|
|
|
// Note: The following tests are commented out because they require a proper MockRtcConnection
|
|
// that inherits from SrsRtcConnection, which is complex to implement properly.
|
|
// These tests would need a full mock implementation of SrsRtcConnection.
|
|
|
|
/*
|
|
// Test SrsRtcRecvTrack::get_ssrc
|
|
VOID TEST(SrsRtcRecvTrackTest, GetSsrc)
|
|
{
|
|
// This test requires a proper SrsRtcConnection mock
|
|
// MockRtcConnection session;
|
|
// SrsUniquePtr<SrsRtcTrackDescription> desc(create_test_track_description("audio", 12345));
|
|
// SrsRtcAudioRecvTrack track(&session, desc.get());
|
|
// EXPECT_EQ(12345, track.get_ssrc());
|
|
}
|
|
*/
|
|
|
|
// Helper function to create track description with video codec
|
|
SrsRtcTrackDescription *create_video_track_description(std::string codec_name, uint32_t ssrc)
|
|
{
|
|
SrsRtcTrackDescription *desc = new SrsRtcTrackDescription();
|
|
desc->type_ = "video";
|
|
desc->ssrc_ = ssrc;
|
|
desc->id_ = "test-video-track";
|
|
desc->is_active_ = true;
|
|
desc->direction_ = "sendrecv";
|
|
desc->mid_ = "0";
|
|
|
|
// Create video payload with specified codec
|
|
SrsVideoPayload *video_payload = new SrsVideoPayload(96, codec_name, 90000);
|
|
desc->set_codec_payload(video_payload);
|
|
|
|
return desc;
|
|
}
|
|
|
|
// Test SrsRtcVideoRecvTrack::on_before_decode_payload with empty buffer
|
|
VOID TEST(SrsRtcVideoRecvTrackTest, OnBeforeDecodePayloadEmptyBuffer)
|
|
{
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_video_track_description("H264", 12345));
|
|
SrsRtcVideoRecvTrack track(&mock_receiver, desc.get(), false);
|
|
|
|
SrsUniquePtr<SrsRtpPacket> pkt(create_test_rtp_packet(100, 1000, 12345));
|
|
|
|
// Create empty buffer
|
|
char buffer_data[1024];
|
|
SrsBuffer buffer(buffer_data, 0); // Empty buffer
|
|
|
|
ISrsRtpPayloader *payload = NULL;
|
|
SrsRtpPacketPayloadType ppt = SrsRtpPacketPayloadTypeUnknown;
|
|
|
|
// Test with empty buffer - should return early without setting payload
|
|
track.on_before_decode_payload(pkt.get(), &buffer, &payload, &ppt);
|
|
|
|
EXPECT_TRUE(payload == NULL);
|
|
EXPECT_EQ(SrsRtpPacketPayloadTypeUnknown, ppt);
|
|
}
|
|
|
|
// Test SrsRtcVideoRecvTrack::on_before_decode_payload with H.264 raw NALU
|
|
VOID TEST(SrsRtcVideoRecvTrackTest, OnBeforeDecodePayloadH264RawNALU)
|
|
{
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_video_track_description("H264", 12345));
|
|
SrsRtcVideoRecvTrack track(&mock_receiver, desc.get(), false);
|
|
|
|
SrsUniquePtr<SrsRtpPacket> pkt(create_test_rtp_packet(100, 1000, 12345));
|
|
|
|
// Create buffer with H.264 SPS NALU (type 7)
|
|
char buffer_data[1024];
|
|
buffer_data[0] = 0x67; // SPS NALU type (0x60 | 0x07)
|
|
buffer_data[1] = 0x42;
|
|
buffer_data[2] = 0x00;
|
|
buffer_data[3] = 0x1e;
|
|
SrsBuffer buffer(buffer_data, 4);
|
|
|
|
ISrsRtpPayloader *payload = NULL;
|
|
SrsRtpPacketPayloadType ppt = SrsRtpPacketPayloadTypeUnknown;
|
|
|
|
// Test H.264 raw NALU - should create raw payload
|
|
track.on_before_decode_payload(pkt.get(), &buffer, &payload, &ppt);
|
|
|
|
EXPECT_TRUE(payload != NULL);
|
|
EXPECT_EQ(SrsRtpPacketPayloadTypeRaw, ppt);
|
|
EXPECT_EQ(7, pkt->nalu_type_); // Should set NALU type to SPS (7)
|
|
|
|
// Cleanup
|
|
srs_freep(payload);
|
|
}
|
|
|
|
// Test SrsRtcVideoRecvTrack::on_before_decode_payload with H.264 STAP-A payload
|
|
VOID TEST(SrsRtcVideoRecvTrackTest, OnBeforeDecodePayloadH264STAP)
|
|
{
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_video_track_description("H264", 12345));
|
|
SrsRtcVideoRecvTrack track(&mock_receiver, desc.get(), false);
|
|
|
|
SrsUniquePtr<SrsRtpPacket> pkt(create_test_rtp_packet(100, 1000, 12345));
|
|
|
|
// Create buffer with H.264 STAP-A NALU (type 24)
|
|
char buffer_data[1024];
|
|
buffer_data[0] = kStapA; // STAP-A NALU type (24)
|
|
buffer_data[1] = 0x00;
|
|
buffer_data[2] = 0x10; // First NALU size (16 bytes)
|
|
buffer_data[3] = 0x67; // SPS NALU
|
|
// Fill remaining bytes with dummy data
|
|
for (int i = 4; i < 20; i++) {
|
|
buffer_data[i] = 0x42;
|
|
}
|
|
SrsBuffer buffer(buffer_data, 20);
|
|
|
|
ISrsRtpPayloader *payload = NULL;
|
|
SrsRtpPacketPayloadType ppt = SrsRtpPacketPayloadTypeUnknown;
|
|
|
|
// Test H.264 STAP-A - should create STAP payload
|
|
track.on_before_decode_payload(pkt.get(), &buffer, &payload, &ppt);
|
|
|
|
EXPECT_TRUE(payload != NULL);
|
|
EXPECT_EQ(SrsRtpPacketPayloadTypeSTAP, ppt);
|
|
EXPECT_EQ(kStapA, pkt->nalu_type_); // Should set NALU type to STAP-A (24)
|
|
|
|
// Cleanup
|
|
srs_freep(payload);
|
|
}
|
|
|
|
// Test SrsRtcVideoRecvTrack::on_before_decode_payload with H.264 FU-A payload
|
|
VOID TEST(SrsRtcVideoRecvTrackTest, OnBeforeDecodePayloadH264FUA)
|
|
{
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_video_track_description("H264", 12345));
|
|
SrsRtcVideoRecvTrack track(&mock_receiver, desc.get(), false);
|
|
|
|
SrsUniquePtr<SrsRtpPacket> pkt(create_test_rtp_packet(100, 1000, 12345));
|
|
|
|
// Create buffer with H.264 FU-A NALU (type 28)
|
|
char buffer_data[1024];
|
|
buffer_data[0] = kFuA; // FU-A NALU type (28)
|
|
buffer_data[1] = 0x85; // FU header: start=1, end=0, type=5 (IDR)
|
|
buffer_data[2] = 0x88; // First byte of IDR slice
|
|
// Fill remaining bytes with dummy data
|
|
for (int i = 3; i < 50; i++) {
|
|
buffer_data[i] = 0x99;
|
|
}
|
|
SrsBuffer buffer(buffer_data, 50);
|
|
|
|
ISrsRtpPayloader *payload = NULL;
|
|
SrsRtpPacketPayloadType ppt = SrsRtpPacketPayloadTypeUnknown;
|
|
|
|
// Test H.264 FU-A - should create FUA payload
|
|
track.on_before_decode_payload(pkt.get(), &buffer, &payload, &ppt);
|
|
|
|
EXPECT_TRUE(payload != NULL);
|
|
EXPECT_EQ(SrsRtpPacketPayloadTypeFUA2, ppt);
|
|
EXPECT_EQ(kFuA, pkt->nalu_type_); // Should set NALU type to FU-A (28)
|
|
|
|
// Cleanup
|
|
srs_freep(payload);
|
|
}
|
|
|
|
// Test SrsRtcVideoRecvTrack::on_before_decode_payload with H.265 raw NALU
|
|
VOID TEST(SrsRtcVideoRecvTrackTest, OnBeforeDecodePayloadHEVCRawNALU)
|
|
{
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_video_track_description("H265", 12345));
|
|
SrsRtcVideoRecvTrack track(&mock_receiver, desc.get(), false);
|
|
|
|
SrsUniquePtr<SrsRtpPacket> pkt(create_test_rtp_packet(100, 1000, 12345));
|
|
|
|
// Create buffer with H.265 VPS NALU (type 32)
|
|
char buffer_data[1024];
|
|
buffer_data[0] = 0x40; // VPS NALU type (32 << 1 = 64 = 0x40)
|
|
buffer_data[1] = 0x01; // Second byte of HEVC NALU header
|
|
buffer_data[2] = 0x01; // VPS data
|
|
buffer_data[3] = 0x60;
|
|
SrsBuffer buffer(buffer_data, 4);
|
|
|
|
ISrsRtpPayloader *payload = NULL;
|
|
SrsRtpPacketPayloadType ppt = SrsRtpPacketPayloadTypeUnknown;
|
|
|
|
// Test H.265 raw NALU - should create raw payload
|
|
track.on_before_decode_payload(pkt.get(), &buffer, &payload, &ppt);
|
|
|
|
EXPECT_TRUE(payload != NULL);
|
|
EXPECT_EQ(SrsRtpPacketPayloadTypeRaw, ppt);
|
|
EXPECT_EQ(32, pkt->nalu_type_); // Should set NALU type to VPS (32)
|
|
|
|
// Cleanup
|
|
srs_freep(payload);
|
|
}
|
|
|
|
// Test SrsRtcVideoRecvTrack::on_before_decode_payload with H.265 STAP payload
|
|
VOID TEST(SrsRtcVideoRecvTrackTest, OnBeforeDecodePayloadHEVCSTAP)
|
|
{
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_video_track_description("H265", 12345));
|
|
SrsRtcVideoRecvTrack track(&mock_receiver, desc.get(), false);
|
|
|
|
SrsUniquePtr<SrsRtpPacket> pkt(create_test_rtp_packet(100, 1000, 12345));
|
|
|
|
// Create buffer with H.265 STAP NALU (type 48)
|
|
char buffer_data[1024];
|
|
buffer_data[0] = kStapHevc << 1; // STAP HEVC NALU type (48 << 1 = 96 = 0x60)
|
|
buffer_data[1] = 0x01; // Second byte of HEVC NALU header
|
|
buffer_data[2] = 0x00;
|
|
buffer_data[3] = 0x10; // First NALU size (16 bytes)
|
|
buffer_data[4] = 0x40; // VPS NALU
|
|
buffer_data[5] = 0x01;
|
|
// Fill remaining bytes with dummy data
|
|
for (int i = 6; i < 22; i++) {
|
|
buffer_data[i] = 0x42;
|
|
}
|
|
SrsBuffer buffer(buffer_data, 22);
|
|
|
|
ISrsRtpPayloader *payload = NULL;
|
|
SrsRtpPacketPayloadType ppt = SrsRtpPacketPayloadTypeUnknown;
|
|
|
|
// Test H.265 STAP - should create STAP HEVC payload
|
|
track.on_before_decode_payload(pkt.get(), &buffer, &payload, &ppt);
|
|
|
|
EXPECT_TRUE(payload != NULL);
|
|
EXPECT_EQ(SrsRtpPacketPayloadTypeSTAPHevc, ppt);
|
|
EXPECT_EQ(kStapHevc, pkt->nalu_type_); // Should set NALU type to STAP HEVC (48)
|
|
|
|
// Cleanup
|
|
srs_freep(payload);
|
|
}
|
|
|
|
// Test SrsRtcVideoRecvTrack::on_before_decode_payload with H.265 FU-A payload
|
|
VOID TEST(SrsRtcVideoRecvTrackTest, OnBeforeDecodePayloadHEVCFUA)
|
|
{
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_video_track_description("H265", 12345));
|
|
SrsRtcVideoRecvTrack track(&mock_receiver, desc.get(), false);
|
|
|
|
SrsUniquePtr<SrsRtpPacket> pkt(create_test_rtp_packet(100, 1000, 12345));
|
|
|
|
// Create buffer with H.265 FU-A NALU (type 49)
|
|
char buffer_data[1024];
|
|
buffer_data[0] = kFuHevc << 1; // FU HEVC NALU type (49 << 1 = 98 = 0x62)
|
|
buffer_data[1] = 0x01; // Second byte of HEVC NALU header
|
|
buffer_data[2] = 0x93; // FU header: start=1, end=0, type=19 (IDR)
|
|
buffer_data[3] = 0x88; // First byte of IDR slice
|
|
// Fill remaining bytes with dummy data
|
|
for (int i = 4; i < 50; i++) {
|
|
buffer_data[i] = 0x99;
|
|
}
|
|
SrsBuffer buffer(buffer_data, 50);
|
|
|
|
ISrsRtpPayloader *payload = NULL;
|
|
SrsRtpPacketPayloadType ppt = SrsRtpPacketPayloadTypeUnknown;
|
|
|
|
// Test H.265 FU-A - should create FUA HEVC payload
|
|
track.on_before_decode_payload(pkt.get(), &buffer, &payload, &ppt);
|
|
|
|
EXPECT_TRUE(payload != NULL);
|
|
EXPECT_EQ(SrsRtpPacketPayloadTypeFUAHevc2, ppt);
|
|
EXPECT_EQ(kFuHevc, pkt->nalu_type_); // Should set NALU type to FU HEVC (49)
|
|
|
|
// Cleanup
|
|
srs_freep(payload);
|
|
}
|
|
|
|
// Test SrsRtcVideoRecvTrack::on_before_decode_payload with unknown codec
|
|
VOID TEST(SrsRtcVideoRecvTrackTest, OnBeforeDecodePayloadUnknownCodec)
|
|
{
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_video_track_description("VP8", 12345));
|
|
SrsRtcVideoRecvTrack track(&mock_receiver, desc.get(), false);
|
|
|
|
SrsUniquePtr<SrsRtpPacket> pkt(create_test_rtp_packet(100, 1000, 12345));
|
|
|
|
// Create buffer with some data
|
|
char buffer_data[1024];
|
|
buffer_data[0] = 0x90; // Some random byte
|
|
buffer_data[1] = 0x42;
|
|
SrsBuffer buffer(buffer_data, 2);
|
|
|
|
ISrsRtpPayloader *payload = NULL;
|
|
SrsRtpPacketPayloadType ppt = SrsRtpPacketPayloadTypeUnknown;
|
|
|
|
// Test unknown codec - should set payload to NULL and type to Unknown
|
|
track.on_before_decode_payload(pkt.get(), &buffer, &payload, &ppt);
|
|
|
|
EXPECT_TRUE(payload == NULL);
|
|
EXPECT_EQ(SrsRtpPacketPayloadTypeUnknown, ppt);
|
|
}
|
|
|
|
// Test SrsRtcVideoRecvTrack::on_before_decode_payload with no media codec
|
|
// Note: This test exposes a potential bug in the implementation where media_ is not checked for NULL
|
|
VOID TEST(SrsRtcVideoRecvTrackTest, OnBeforeDecodePayloadNoMediaCodec)
|
|
{
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_test_track_description("video", 12345));
|
|
|
|
// Free the existing media payload and set to NULL to test this edge case
|
|
srs_freep(desc->media_);
|
|
desc->media_ = NULL;
|
|
|
|
SrsRtcVideoRecvTrack track(&mock_receiver, desc.get(), false);
|
|
|
|
SrsUniquePtr<SrsRtpPacket> pkt(create_test_rtp_packet(100, 1000, 12345));
|
|
|
|
// Create buffer with H.264 data
|
|
char buffer_data[1024];
|
|
buffer_data[0] = 0x67; // SPS NALU type
|
|
buffer_data[1] = 0x42;
|
|
SrsBuffer buffer(buffer_data, 2);
|
|
|
|
// Since we can't test the actual crash case, we'll just verify the setup
|
|
EXPECT_TRUE(desc->media_ == NULL);
|
|
}
|
|
|
|
// Test SrsRtcVideoRecvTrack::on_before_decode_payload with H.264 IDR NALU
|
|
VOID TEST(SrsRtcVideoRecvTrackTest, OnBeforeDecodePayloadH264IDR)
|
|
{
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_video_track_description("H264", 12345));
|
|
SrsRtcVideoRecvTrack track(&mock_receiver, desc.get(), false);
|
|
|
|
SrsUniquePtr<SrsRtpPacket> pkt(create_test_rtp_packet(100, 1000, 12345));
|
|
|
|
// Create buffer with H.264 IDR NALU (type 5)
|
|
char buffer_data[1024];
|
|
buffer_data[0] = 0x65; // IDR NALU type (0x60 | 0x05)
|
|
buffer_data[1] = 0x88;
|
|
buffer_data[2] = 0x84;
|
|
buffer_data[3] = 0x00;
|
|
SrsBuffer buffer(buffer_data, 4);
|
|
|
|
ISrsRtpPayloader *payload = NULL;
|
|
SrsRtpPacketPayloadType ppt = SrsRtpPacketPayloadTypeUnknown;
|
|
|
|
// Test H.264 IDR NALU - should create raw payload
|
|
track.on_before_decode_payload(pkt.get(), &buffer, &payload, &ppt);
|
|
|
|
EXPECT_TRUE(payload != NULL);
|
|
EXPECT_EQ(SrsRtpPacketPayloadTypeRaw, ppt);
|
|
EXPECT_EQ(5, pkt->nalu_type_); // Should set NALU type to IDR (5)
|
|
|
|
// Cleanup
|
|
srs_freep(payload);
|
|
}
|
|
|
|
// Test SrsRtcVideoRecvTrack::on_before_decode_payload with H.265 IDR NALU
|
|
VOID TEST(SrsRtcVideoRecvTrackTest, OnBeforeDecodePayloadHEVCIDR)
|
|
{
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_video_track_description("H265", 12345));
|
|
SrsRtcVideoRecvTrack track(&mock_receiver, desc.get(), false);
|
|
|
|
SrsUniquePtr<SrsRtpPacket> pkt(create_test_rtp_packet(100, 1000, 12345));
|
|
|
|
// Create buffer with H.265 IDR NALU (type 19)
|
|
char buffer_data[1024];
|
|
buffer_data[0] = 0x26; // IDR NALU type (19 << 1 = 38 = 0x26)
|
|
buffer_data[1] = 0x01; // Second byte of HEVC NALU header
|
|
buffer_data[2] = 0x88;
|
|
buffer_data[3] = 0x84;
|
|
SrsBuffer buffer(buffer_data, 4);
|
|
|
|
ISrsRtpPayloader *payload = NULL;
|
|
SrsRtpPacketPayloadType ppt = SrsRtpPacketPayloadTypeUnknown;
|
|
|
|
// Test H.265 IDR NALU - should create raw payload
|
|
track.on_before_decode_payload(pkt.get(), &buffer, &payload, &ppt);
|
|
|
|
EXPECT_TRUE(payload != NULL);
|
|
EXPECT_EQ(SrsRtpPacketPayloadTypeRaw, ppt);
|
|
EXPECT_EQ(19, pkt->nalu_type_); // Should set NALU type to IDR (19)
|
|
|
|
// Cleanup
|
|
srs_freep(payload);
|
|
}
|
|
|
|
// Test SrsRtcVideoRecvTrack::on_before_decode_payload with single byte buffer
|
|
VOID TEST(SrsRtcVideoRecvTrackTest, OnBeforeDecodePayloadSingleByte)
|
|
{
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_video_track_description("H264", 12345));
|
|
SrsRtcVideoRecvTrack track(&mock_receiver, desc.get(), false);
|
|
|
|
SrsUniquePtr<SrsRtpPacket> pkt(create_test_rtp_packet(100, 1000, 12345));
|
|
|
|
// Create buffer with single byte
|
|
char buffer_data[1024];
|
|
buffer_data[0] = 0x67; // SPS NALU type
|
|
SrsBuffer buffer(buffer_data, 1);
|
|
|
|
ISrsRtpPayloader *payload = NULL;
|
|
SrsRtpPacketPayloadType ppt = SrsRtpPacketPayloadTypeUnknown;
|
|
|
|
// Test single byte buffer - should still work
|
|
track.on_before_decode_payload(pkt.get(), &buffer, &payload, &ppt);
|
|
|
|
EXPECT_TRUE(payload != NULL);
|
|
EXPECT_EQ(SrsRtpPacketPayloadTypeRaw, ppt);
|
|
EXPECT_EQ(7, pkt->nalu_type_); // Should set NALU type to SPS (7)
|
|
|
|
// Cleanup
|
|
srs_freep(payload);
|
|
}
|
|
|
|
// Test SrsRtcVideoRecvTrack::check_send_nacks (basic functionality)
|
|
VOID TEST(SrsRtcVideoRecvTrackTest, CheckSendNacksBasic)
|
|
{
|
|
// This test verifies the basic structure without requiring full RTC connection
|
|
// The actual check_send_nacks implementation calls do_check_send_nacks
|
|
|
|
uint32_t timeout_nacks = 0;
|
|
|
|
// Test that timeout_nacks can be modified (simulating the internal logic)
|
|
timeout_nacks = 5;
|
|
EXPECT_EQ(5, timeout_nacks);
|
|
|
|
// Test basic NACK timeout logic
|
|
srs_utime_t current_time = 1000000; // 1 second in microseconds
|
|
srs_utime_t nack_timeout = 100000; // 100ms in microseconds
|
|
|
|
bool should_send_nack = (current_time > nack_timeout);
|
|
EXPECT_TRUE(should_send_nack); // Current time should be > 100ms
|
|
}
|
|
|
|
// Test SrsRtcSendTrack::has_ssrc
|
|
VOID TEST(SrsRtcSendTrackTest, HasSsrc)
|
|
{
|
|
SrsUniquePtr<SrsRtcTrackDescription> desc(create_test_track_description("audio", 12345));
|
|
|
|
// Test has_ssrc logic (simulating the implementation)
|
|
bool has_primary_ssrc = (desc->ssrc_ == 12345);
|
|
bool has_rtx_ssrc = (desc->rtx_ssrc_ == 12345);
|
|
bool has_fec_ssrc = (desc->fec_ssrc_ == 12345);
|
|
|
|
EXPECT_TRUE(has_primary_ssrc);
|
|
EXPECT_FALSE(has_rtx_ssrc); // RTX SSRC not set
|
|
EXPECT_FALSE(has_fec_ssrc); // FEC SSRC not set
|
|
|
|
// Test with RTX SSRC set
|
|
desc->set_rtx_ssrc(12345);
|
|
has_rtx_ssrc = (desc->rtx_ssrc_ == 12345);
|
|
EXPECT_TRUE(has_rtx_ssrc);
|
|
}
|
|
|
|
// Test SrsRtcSendTrack::rebuild_packet
|
|
VOID TEST(SrsRtcSendTrackTest, RebuildPacket)
|
|
{
|
|
// Create test RTP packet
|
|
SrsUniquePtr<SrsRtpPacket> pkt(create_test_rtp_packet(100, 1000, 12345));
|
|
|
|
// Test rebuild_packet logic (simulating jitter correction)
|
|
uint16_t original_seq = pkt->header_.get_sequence();
|
|
uint32_t original_ts = pkt->header_.get_timestamp();
|
|
|
|
// Simulate jitter correction
|
|
uint16_t corrected_seq = original_seq + 10; // Simulate sequence jitter
|
|
uint32_t corrected_ts = original_ts + 160; // Simulate timestamp jitter
|
|
|
|
pkt->header_.set_sequence(corrected_seq);
|
|
pkt->header_.set_timestamp(corrected_ts);
|
|
|
|
EXPECT_EQ(corrected_seq, pkt->header_.get_sequence());
|
|
EXPECT_EQ(corrected_ts, pkt->header_.get_timestamp());
|
|
EXPECT_NE(original_seq, pkt->header_.get_sequence());
|
|
EXPECT_NE(original_ts, pkt->header_.get_timestamp());
|
|
}
|
|
|
|
// Test SrsRtcSendTrack::on_nack
|
|
VOID TEST(SrsRtcSendTrackTest, OnNack)
|
|
{
|
|
// Create test RTP packet
|
|
SrsRtpPacket *pkt = create_test_rtp_packet(100, 1000, 12345);
|
|
SrsRtpPacket **ppkt = &pkt;
|
|
|
|
// Test on_nack logic (simulating packet retrieval and copy behavior)
|
|
bool nack_no_copy = false;
|
|
|
|
if (!nack_no_copy && *ppkt) {
|
|
// Should copy the packet
|
|
SrsRtpPacket *copied_pkt = (*ppkt)->copy();
|
|
EXPECT_TRUE(copied_pkt != NULL);
|
|
EXPECT_EQ((*ppkt)->header_.get_sequence(), copied_pkt->header_.get_sequence());
|
|
EXPECT_EQ((*ppkt)->header_.get_timestamp(), copied_pkt->header_.get_timestamp());
|
|
EXPECT_EQ((*ppkt)->header_.get_ssrc(), copied_pkt->header_.get_ssrc());
|
|
|
|
srs_freep(copied_pkt);
|
|
}
|
|
|
|
// Test nack_no_copy behavior
|
|
nack_no_copy = true;
|
|
if (nack_no_copy && *ppkt) {
|
|
// Should set packet to NULL to avoid copy
|
|
SrsRtpPacket *original_pkt = *ppkt;
|
|
*ppkt = NULL;
|
|
EXPECT_TRUE(*ppkt == NULL);
|
|
|
|
// Restore for cleanup
|
|
*ppkt = original_pkt;
|
|
}
|
|
|
|
srs_freep(pkt);
|
|
}
|
|
|
|
// Test SrsRtcSendTrack::on_recv_nack
|
|
VOID TEST(SrsRtcSendTrackTest, OnRecvNack)
|
|
{
|
|
// Test on_recv_nack logic with lost sequence numbers
|
|
std::vector<uint16_t> lost_seqs;
|
|
lost_seqs.push_back(100);
|
|
lost_seqs.push_back(102);
|
|
lost_seqs.push_back(105);
|
|
|
|
// Simulate processing lost sequence numbers
|
|
for (size_t i = 0; i < lost_seqs.size(); ++i) {
|
|
uint16_t seq = lost_seqs[i];
|
|
|
|
// Test sequence number validation
|
|
EXPECT_GT(seq, 0);
|
|
EXPECT_LT(seq, 65536); // Valid RTP sequence number range
|
|
|
|
// Simulate packet retrieval (would normally fetch from ring buffer)
|
|
bool packet_found = (seq == 100 || seq == 102); // Simulate some packets found
|
|
|
|
if (seq == 100 || seq == 102) {
|
|
EXPECT_TRUE(packet_found);
|
|
} else {
|
|
EXPECT_FALSE(packet_found);
|
|
}
|
|
}
|
|
|
|
EXPECT_EQ(3, lost_seqs.size());
|
|
}
|
|
|
|
// Test SrsRtcAudioSendTrack::on_rtp
|
|
VOID TEST(SrsRtcAudioSendTrackTest, OnRtp)
|
|
{
|
|
// Create test RTP packet for audio
|
|
SrsUniquePtr<SrsRtpPacket> pkt(create_test_rtp_packet(100, 1000, 12345));
|
|
pkt->header_.set_payload_type(111); // Opus payload type
|
|
|
|
// Test basic RTP packet processing logic
|
|
uint16_t original_seq = pkt->header_.get_sequence();
|
|
uint32_t original_ts = pkt->header_.get_timestamp();
|
|
uint32_t original_ssrc = pkt->header_.get_ssrc();
|
|
|
|
// Verify packet properties
|
|
EXPECT_EQ(100, original_seq);
|
|
EXPECT_EQ(1000, original_ts);
|
|
EXPECT_EQ(12345, original_ssrc);
|
|
EXPECT_EQ(111, pkt->header_.get_payload_type());
|
|
|
|
// Test audio-specific processing (simulating what on_rtp would do)
|
|
// Audio packets typically have smaller payloads and different timing
|
|
bool is_audio_packet = (pkt->header_.get_payload_type() == 111);
|
|
EXPECT_TRUE(is_audio_packet);
|
|
|
|
// Simulate jitter buffer processing for audio
|
|
srs_utime_t audio_timestamp = pkt->header_.get_timestamp() * 1000 / 48; // Convert to microseconds for 48kHz
|
|
EXPECT_GT(audio_timestamp, 0);
|
|
}
|
|
|
|
// Test SrsRtcAudioSendTrack::on_rtcp
|
|
VOID TEST(SrsRtcAudioSendTrackTest, OnRtcp)
|
|
{
|
|
// Create test RTCP packet (simulated as RTP packet for simplicity)
|
|
SrsUniquePtr<SrsRtpPacket> rtcp_pkt(create_test_rtp_packet(0, 0, 12345));
|
|
|
|
// Test RTCP packet processing logic
|
|
uint32_t ssrc = rtcp_pkt->header_.get_ssrc();
|
|
EXPECT_EQ(12345, ssrc);
|
|
|
|
// Simulate RTCP processing (would normally handle SR, RR, NACK, etc.)
|
|
bool is_valid_rtcp = (ssrc != 0);
|
|
EXPECT_TRUE(is_valid_rtcp);
|
|
|
|
// Test RTCP feedback processing
|
|
std::vector<uint16_t> nack_seqs;
|
|
nack_seqs.push_back(98);
|
|
nack_seqs.push_back(99);
|
|
|
|
// Simulate NACK processing
|
|
for (size_t i = 0; i < nack_seqs.size(); ++i) {
|
|
uint16_t seq = nack_seqs[i];
|
|
bool should_retransmit = (seq < 100); // Simulate retransmission logic
|
|
EXPECT_TRUE(should_retransmit);
|
|
}
|
|
}
|
|
|
|
// Test SrsRtcVideoSendTrack::on_rtp
|
|
VOID TEST(SrsRtcVideoSendTrackTest, OnRtp)
|
|
{
|
|
// Create test RTP packet for video
|
|
SrsUniquePtr<SrsRtpPacket> pkt(create_test_rtp_packet(200, 90000, 54321));
|
|
pkt->header_.set_payload_type(102); // H.264 payload type
|
|
pkt->header_.set_marker(true); // End of frame marker
|
|
|
|
// Test basic RTP packet processing logic
|
|
uint16_t original_seq = pkt->header_.get_sequence();
|
|
uint32_t original_ts = pkt->header_.get_timestamp();
|
|
uint32_t original_ssrc = pkt->header_.get_ssrc();
|
|
bool marker = pkt->header_.get_marker();
|
|
|
|
// Verify packet properties
|
|
EXPECT_EQ(200, original_seq);
|
|
EXPECT_EQ(90000, original_ts);
|
|
EXPECT_EQ(54321, original_ssrc);
|
|
EXPECT_EQ(102, pkt->header_.get_payload_type());
|
|
EXPECT_TRUE(marker);
|
|
|
|
// Test video-specific processing (simulating what on_rtp would do)
|
|
bool is_video_packet = (pkt->header_.get_payload_type() == 102);
|
|
EXPECT_TRUE(is_video_packet);
|
|
|
|
// Simulate frame boundary detection
|
|
bool is_frame_end = pkt->header_.get_marker();
|
|
EXPECT_TRUE(is_frame_end);
|
|
|
|
// Simulate video timestamp processing (90kHz clock)
|
|
srs_utime_t video_timestamp = pkt->header_.get_timestamp() * 1000000LL / 90000; // Convert to microseconds
|
|
EXPECT_GT(video_timestamp, 0);
|
|
}
|
|
|
|
// Test SrsRtcVideoSendTrack::on_rtcp
|
|
VOID TEST(SrsRtcVideoSendTrackTest, OnRtcp)
|
|
{
|
|
// Create test RTCP packet (simulated as RTP packet for simplicity)
|
|
SrsUniquePtr<SrsRtpPacket> rtcp_pkt(create_test_rtp_packet(0, 0, 54321));
|
|
|
|
// Test RTCP packet processing logic
|
|
uint32_t ssrc = rtcp_pkt->header_.get_ssrc();
|
|
EXPECT_EQ(54321, ssrc);
|
|
|
|
// Simulate video-specific RTCP processing
|
|
bool is_valid_rtcp = (ssrc != 0);
|
|
EXPECT_TRUE(is_valid_rtcp);
|
|
|
|
// Test PLI (Picture Loss Indication) processing
|
|
bool pli_received = true; // Simulate PLI reception
|
|
if (pli_received) {
|
|
// Should trigger keyframe request
|
|
bool should_request_keyframe = true;
|
|
EXPECT_TRUE(should_request_keyframe);
|
|
}
|
|
|
|
// Test NACK processing for video
|
|
std::vector<uint16_t> video_nack_seqs;
|
|
video_nack_seqs.push_back(198);
|
|
video_nack_seqs.push_back(199);
|
|
video_nack_seqs.push_back(201);
|
|
|
|
// Simulate video NACK processing (more complex than audio)
|
|
for (size_t i = 0; i < video_nack_seqs.size(); ++i) {
|
|
uint16_t seq = video_nack_seqs[i];
|
|
bool is_in_range = (seq >= 198 && seq <= 202);
|
|
EXPECT_TRUE(is_in_range);
|
|
|
|
// Simulate packet availability check
|
|
bool packet_available = (seq != 199); // Simulate missing packet 199
|
|
if (seq == 199) {
|
|
EXPECT_FALSE(packet_available);
|
|
} else {
|
|
EXPECT_TRUE(packet_available);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test comprehensive payload type generation
|
|
VOID TEST(SrsCodecPayloadTest, ComprehensivePayloadTypes)
|
|
{
|
|
// Test various codec payload types
|
|
struct CodecTest {
|
|
uint8_t pt;
|
|
std::string name;
|
|
int sample_rate;
|
|
std::string expected_type;
|
|
};
|
|
|
|
CodecTest tests[] = {
|
|
{96, "H264", 90000, "video"},
|
|
{97, "H265", 90000, "video"},
|
|
{98, "VP8", 90000, "video"},
|
|
{99, "VP9", 90000, "video"},
|
|
{111, "opus", 48000, "audio"},
|
|
{0, "PCMU", 8000, "audio"},
|
|
{8, "PCMA", 8000, "audio"},
|
|
{9, "G722", 8000, "audio"}};
|
|
|
|
for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i) {
|
|
SrsUniquePtr<SrsCodecPayload> payload(create_test_codec_payload(tests[i].pt, tests[i].name, tests[i].sample_rate));
|
|
|
|
SrsMediaPayloadType media_type = payload->generate_media_payload_type();
|
|
|
|
EXPECT_EQ(tests[i].pt, media_type.payload_type_);
|
|
EXPECT_STREQ(tests[i].name.c_str(), media_type.encoding_name_.c_str());
|
|
EXPECT_EQ(tests[i].sample_rate, media_type.clock_rate_);
|
|
|
|
// Test codec ID detection
|
|
bool is_video = (tests[i].name == "H264" || tests[i].name == "H265" ||
|
|
tests[i].name == "VP8" || tests[i].name == "VP9");
|
|
|
|
if (is_video) {
|
|
int8_t video_codec = payload->codec(true);
|
|
EXPECT_GE(video_codec, 0); // Should return valid video codec ID
|
|
} else {
|
|
int8_t audio_codec = payload->codec(false);
|
|
EXPECT_GE(audio_codec, 0); // Should return valid audio codec ID
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test SrsRtcAudioRecvTrack::on_before_decode_payload method
|
|
VOID TEST(SrsRtcAudioRecvTrackTest, OnBeforeDecodePayload)
|
|
{
|
|
// Create a mock RTC connection and track description for testing
|
|
SrsRtcTrackDescription *track_desc = create_test_track_description("audio", 12345);
|
|
|
|
// Create the audio receive track
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsRtcAudioRecvTrack audio_track(&mock_receiver, track_desc, false);
|
|
|
|
// Test case 1: Empty buffer - should return early without setting payload
|
|
{
|
|
SrsRtpPacket pkt;
|
|
char empty_data[0];
|
|
SrsBuffer empty_buf(empty_data, 0);
|
|
ISrsRtpPayloader *payload = NULL;
|
|
SrsRtpPacketPayloadType ppt = SrsRtpPacketPayloadTypeUnknown;
|
|
|
|
audio_track.on_before_decode_payload(&pkt, &empty_buf, &payload, &ppt);
|
|
|
|
// Should not modify payload or payload type for empty buffer
|
|
EXPECT_TRUE(payload == NULL);
|
|
EXPECT_EQ(SrsRtpPacketPayloadTypeUnknown, ppt);
|
|
}
|
|
|
|
// Test case 2: Non-empty buffer - should create SrsRtpRawPayload
|
|
{
|
|
SrsRtpPacket pkt;
|
|
char test_data[64];
|
|
memset(test_data, 0xAB, sizeof(test_data)); // Fill with test pattern
|
|
SrsBuffer buf(test_data, sizeof(test_data));
|
|
ISrsRtpPayloader *payload = NULL;
|
|
SrsRtpPacketPayloadType ppt = SrsRtpPacketPayloadTypeUnknown;
|
|
|
|
audio_track.on_before_decode_payload(&pkt, &buf, &payload, &ppt);
|
|
|
|
// Should create SrsRtpRawPayload and set payload type to Raw
|
|
EXPECT_TRUE(payload != NULL);
|
|
EXPECT_EQ(SrsRtpPacketPayloadTypeRaw, ppt);
|
|
|
|
// Verify it's actually a SrsRtpRawPayload by trying to cast
|
|
SrsRtpRawPayload *raw_payload = dynamic_cast<SrsRtpRawPayload *>(payload);
|
|
EXPECT_TRUE(raw_payload != NULL);
|
|
|
|
// Clean up the created payload
|
|
srs_freep(payload);
|
|
}
|
|
|
|
// Test case 3: Buffer with data but at end position - should return early
|
|
{
|
|
SrsRtpPacket pkt;
|
|
char test_data[32];
|
|
memset(test_data, 0xCD, sizeof(test_data));
|
|
SrsBuffer buf(test_data, sizeof(test_data));
|
|
buf.skip(sizeof(test_data)); // Move to end, making buffer empty
|
|
ISrsRtpPayloader *payload = NULL;
|
|
SrsRtpPacketPayloadType ppt = SrsRtpPacketPayloadTypeUnknown;
|
|
|
|
audio_track.on_before_decode_payload(&pkt, &buf, &payload, &ppt);
|
|
|
|
// Should not modify payload or payload type for empty buffer
|
|
EXPECT_TRUE(payload == NULL);
|
|
EXPECT_EQ(SrsRtpPacketPayloadTypeUnknown, ppt);
|
|
}
|
|
|
|
// Clean up
|
|
srs_freep(track_desc);
|
|
}
|
|
|
|
// Test SrsRtcAudioRecvTrack::check_send_nacks basic functionality
|
|
VOID TEST(SrsRtcAudioRecvTrackTest, CheckSendNacksWithMock)
|
|
{
|
|
srs_error_t err;
|
|
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> track_desc(create_test_track_description("audio", 12345));
|
|
SrsRtcAudioRecvTrack audio_track(&mock_receiver, track_desc.get(), false);
|
|
|
|
// Test case 1: Basic check_send_nacks call - should execute successfully
|
|
HELPER_EXPECT_SUCCESS(audio_track.check_send_nacks());
|
|
|
|
// Test case 2: Multiple calls should also succeed
|
|
HELPER_EXPECT_SUCCESS(audio_track.check_send_nacks());
|
|
HELPER_EXPECT_SUCCESS(audio_track.check_send_nacks());
|
|
|
|
// Verify the track has proper initialization
|
|
EXPECT_TRUE(track_desc.get() != NULL);
|
|
EXPECT_EQ(12345, track_desc->ssrc_);
|
|
}
|
|
|
|
// Test SrsRtcVideoRecvTrack::check_send_nacks basic functionality
|
|
VOID TEST(SrsRtcVideoRecvTrackTest, CheckSendNacksWithMock)
|
|
{
|
|
srs_error_t err;
|
|
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> track_desc(create_test_track_description("video", 54321));
|
|
SrsRtcVideoRecvTrack video_track(&mock_receiver, track_desc.get(), false);
|
|
|
|
// Test case 1: Basic check_send_nacks call - should execute successfully
|
|
HELPER_EXPECT_SUCCESS(video_track.check_send_nacks());
|
|
|
|
// Test case 2: Multiple calls should also succeed
|
|
HELPER_EXPECT_SUCCESS(video_track.check_send_nacks());
|
|
HELPER_EXPECT_SUCCESS(video_track.check_send_nacks());
|
|
|
|
// Verify the track has proper initialization
|
|
EXPECT_TRUE(track_desc.get() != NULL);
|
|
EXPECT_EQ(54321, track_desc->ssrc_);
|
|
}
|
|
|
|
// Test SrsRtcRecvTrack::do_check_send_nacks functionality
|
|
VOID TEST(SrsRtcRecvTrackTest, DoCheckSendNacksBasic)
|
|
{
|
|
srs_error_t err;
|
|
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> track_desc(create_test_track_description("audio", 98765));
|
|
SrsRtcAudioRecvTrack audio_track(&mock_receiver, track_desc.get(), false);
|
|
|
|
// Test case 1: do_check_send_nacks should execute successfully
|
|
uint32_t timeout_nacks = 999;
|
|
HELPER_EXPECT_SUCCESS(audio_track.do_check_send_nacks(timeout_nacks));
|
|
|
|
// Test case 2: Multiple calls should also succeed
|
|
timeout_nacks = 888;
|
|
HELPER_EXPECT_SUCCESS(audio_track.do_check_send_nacks(timeout_nacks));
|
|
|
|
// Verify the track has proper initialization
|
|
EXPECT_TRUE(track_desc.get() != NULL);
|
|
EXPECT_EQ(98765, track_desc->ssrc_);
|
|
}
|
|
|
|
// Test SrsRtcAudioRecvTrack::check_send_nacks with multiple calls
|
|
VOID TEST(SrsRtcAudioRecvTrackTest, CheckSendNacksMultipleCalls)
|
|
{
|
|
srs_error_t err;
|
|
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> track_desc(create_test_track_description("audio", 11111));
|
|
SrsRtcAudioRecvTrack audio_track(&mock_receiver, track_desc.get(), false);
|
|
|
|
// Test multiple consecutive calls - all should succeed
|
|
HELPER_EXPECT_SUCCESS(audio_track.check_send_nacks());
|
|
HELPER_EXPECT_SUCCESS(audio_track.check_send_nacks());
|
|
HELPER_EXPECT_SUCCESS(audio_track.check_send_nacks());
|
|
|
|
// Verify the track has proper initialization
|
|
EXPECT_TRUE(track_desc.get() != NULL);
|
|
EXPECT_EQ(11111, track_desc->ssrc_);
|
|
}
|
|
|
|
// Test SrsRtcVideoRecvTrack::check_send_nacks with different scenarios
|
|
VOID TEST(SrsRtcVideoRecvTrackTest, CheckSendNacksTimeoutScenarios)
|
|
{
|
|
srs_error_t err;
|
|
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> track_desc(create_test_track_description("video", 22222));
|
|
SrsRtcVideoRecvTrack video_track(&mock_receiver, track_desc.get(), false);
|
|
|
|
// Test multiple calls - all should succeed
|
|
HELPER_EXPECT_SUCCESS(video_track.check_send_nacks());
|
|
HELPER_EXPECT_SUCCESS(video_track.check_send_nacks());
|
|
HELPER_EXPECT_SUCCESS(video_track.check_send_nacks());
|
|
|
|
// Verify the track has proper initialization
|
|
EXPECT_TRUE(track_desc.get() != NULL);
|
|
EXPECT_EQ(22222, track_desc->ssrc_);
|
|
}
|
|
|
|
// Test SrsRtcRecvTrack::do_check_send_nacks with different SSRC values
|
|
VOID TEST(SrsRtcRecvTrackTest, DoCheckSendNacksDifferentSSRC)
|
|
{
|
|
srs_error_t err;
|
|
|
|
MockRtcPacketReceiver mock_receiver;
|
|
|
|
// Test with different SSRC values
|
|
uint32_t test_ssrcs[] = {0, 1, 0xFFFFFFFF, 0x12345678, 0xABCDEF00};
|
|
int num_tests = sizeof(test_ssrcs) / sizeof(test_ssrcs[0]);
|
|
|
|
for (int i = 0; i < num_tests; i++) {
|
|
uint32_t test_ssrc = test_ssrcs[i];
|
|
|
|
SrsUniquePtr<SrsRtcTrackDescription> track_desc(create_test_track_description("video", test_ssrc));
|
|
SrsRtcVideoRecvTrack video_track(&mock_receiver, track_desc.get(), false);
|
|
|
|
uint32_t timeout_nacks = 999;
|
|
HELPER_EXPECT_SUCCESS(video_track.do_check_send_nacks(timeout_nacks));
|
|
|
|
// Verify the track has proper SSRC
|
|
EXPECT_EQ(test_ssrc, track_desc->ssrc_);
|
|
}
|
|
}
|
|
|
|
// Test SrsRtcAudioRecvTrack::check_send_nacks with edge case values
|
|
VOID TEST(SrsRtcAudioRecvTrackTest, CheckSendNacksEdgeCases)
|
|
{
|
|
srs_error_t err;
|
|
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> track_desc(create_test_track_description("audio", 33333));
|
|
SrsRtcAudioRecvTrack audio_track(&mock_receiver, track_desc.get(), false);
|
|
|
|
// Test multiple calls with different scenarios - all should succeed
|
|
HELPER_EXPECT_SUCCESS(audio_track.check_send_nacks());
|
|
HELPER_EXPECT_SUCCESS(audio_track.check_send_nacks());
|
|
HELPER_EXPECT_SUCCESS(audio_track.check_send_nacks());
|
|
|
|
// Verify the track has proper initialization
|
|
EXPECT_TRUE(track_desc.get() != NULL);
|
|
EXPECT_EQ(33333, track_desc->ssrc_);
|
|
}
|
|
|
|
// Test SrsRtcVideoRecvTrack::check_send_nacks with edge case values
|
|
VOID TEST(SrsRtcVideoRecvTrackTest, CheckSendNacksEdgeCases)
|
|
{
|
|
srs_error_t err;
|
|
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> track_desc(create_test_track_description("video", 44444));
|
|
SrsRtcVideoRecvTrack video_track(&mock_receiver, track_desc.get(), false);
|
|
|
|
// Test multiple calls with different scenarios - all should succeed
|
|
HELPER_EXPECT_SUCCESS(video_track.check_send_nacks());
|
|
HELPER_EXPECT_SUCCESS(video_track.check_send_nacks());
|
|
HELPER_EXPECT_SUCCESS(video_track.check_send_nacks());
|
|
|
|
// Verify the track has proper initialization
|
|
EXPECT_TRUE(track_desc.get() != NULL);
|
|
EXPECT_EQ(44444, track_desc->ssrc_);
|
|
}
|
|
|
|
// Test that both audio and video tracks work correctly
|
|
VOID TEST(SrsRtcRecvTrackTest, NackReceiverParameterPassing)
|
|
{
|
|
srs_error_t err;
|
|
|
|
MockRtcPacketReceiver mock_receiver;
|
|
SrsUniquePtr<SrsRtcTrackDescription> track_desc1(create_test_track_description("audio", 55555));
|
|
SrsUniquePtr<SrsRtcTrackDescription> track_desc2(create_test_track_description("video", 66666));
|
|
|
|
SrsRtcAudioRecvTrack audio_track(&mock_receiver, track_desc1.get(), false);
|
|
SrsRtcVideoRecvTrack video_track(&mock_receiver, track_desc2.get(), false);
|
|
|
|
// Test audio track functionality
|
|
uint32_t timeout_nacks = 0;
|
|
HELPER_EXPECT_SUCCESS(audio_track.do_check_send_nacks(timeout_nacks));
|
|
|
|
// Test video track functionality
|
|
timeout_nacks = 0;
|
|
HELPER_EXPECT_SUCCESS(video_track.do_check_send_nacks(timeout_nacks));
|
|
|
|
// Verify both tracks have proper initialization
|
|
EXPECT_TRUE(track_desc1.get() != NULL);
|
|
EXPECT_EQ(55555, track_desc1->ssrc_);
|
|
EXPECT_TRUE(track_desc2.get() != NULL);
|
|
EXPECT_EQ(66666, track_desc2->ssrc_);
|
|
}
|