4601 lines
152 KiB
C++
4601 lines
152 KiB
C++
//
|
|
// Copyright (c) 2013-2025 The SRS Authors
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
//
|
|
|
|
#include <srs_utest_app2.hpp>
|
|
|
|
#include <srs_app_circuit_breaker.hpp>
|
|
#include <srs_app_rtc_source.hpp>
|
|
#include <srs_core_autofree.hpp>
|
|
#include <srs_kernel_buffer.hpp>
|
|
#include <srs_kernel_codec.hpp>
|
|
#include <srs_kernel_kbps.hpp>
|
|
#include <srs_kernel_rtc_rtp.hpp>
|
|
#include <srs_protocol_amf0.hpp>
|
|
#include <srs_protocol_format.hpp>
|
|
#include <srs_protocol_rtmp_stack.hpp>
|
|
#include <srs_utest.hpp>
|
|
|
|
// External function declaration from srs_app_rtc_source.cpp
|
|
extern srs_error_t aac_raw_append_adts_header(SrsMediaPacket *shared_audio, SrsFormat *format, char **pbuf, int *pnn_buf);
|
|
|
|
// Mock class implementations
|
|
|
|
MockRtcSourceForConsumer::MockRtcSourceForConsumer()
|
|
{
|
|
source_id_.set_value("test-source-id");
|
|
pre_source_id_.set_value("test-pre-source-id");
|
|
consumer_destroy_count_ = 0;
|
|
can_publish_ = true;
|
|
is_created_ = true;
|
|
}
|
|
|
|
MockRtcSourceForConsumer::~MockRtcSourceForConsumer()
|
|
{
|
|
}
|
|
|
|
SrsContextId MockRtcSourceForConsumer::get_id()
|
|
{
|
|
return source_id_;
|
|
}
|
|
|
|
SrsContextId MockRtcSourceForConsumer::get_pre_source_id()
|
|
{
|
|
return pre_source_id_;
|
|
}
|
|
|
|
void MockRtcSourceForConsumer::on_consumer_destroy(ISrsRtcConsumer *consumer)
|
|
{
|
|
consumer_destroy_count_++;
|
|
}
|
|
|
|
bool MockRtcSourceForConsumer::can_publish()
|
|
{
|
|
return can_publish_;
|
|
}
|
|
|
|
bool MockRtcSourceForConsumer::is_created()
|
|
{
|
|
return is_created_;
|
|
}
|
|
|
|
SrsContextId MockRtcSourceForConsumer::source_id()
|
|
{
|
|
return source_id_;
|
|
}
|
|
|
|
SrsContextId MockRtcSourceForConsumer::pre_source_id()
|
|
{
|
|
return pre_source_id_;
|
|
}
|
|
|
|
MockRtcSourceChangeCallback::MockRtcSourceChangeCallback()
|
|
{
|
|
stream_change_count_ = 0;
|
|
last_stream_desc_ = NULL;
|
|
}
|
|
|
|
MockRtcSourceChangeCallback::~MockRtcSourceChangeCallback()
|
|
{
|
|
}
|
|
|
|
void MockRtcSourceChangeCallback::on_stream_change(SrsRtcSourceDescription *desc)
|
|
{
|
|
stream_change_count_++;
|
|
last_stream_desc_ = desc;
|
|
}
|
|
|
|
MockRtcConsumer::MockRtcConsumer()
|
|
{
|
|
update_source_id_count_ = 0;
|
|
stream_change_count_ = 0;
|
|
enqueue_count_ = 0;
|
|
last_stream_desc_ = NULL;
|
|
source_id_.set_value("test-consumer-source-id");
|
|
consumer_id_.set_value("test-consumer-id");
|
|
should_update_source_id_ = false;
|
|
enqueue_error_ = srs_success;
|
|
}
|
|
|
|
MockRtcConsumer::~MockRtcConsumer()
|
|
{
|
|
srs_freep(enqueue_error_);
|
|
}
|
|
|
|
SrsContextId MockRtcConsumer::get_id()
|
|
{
|
|
return consumer_id_;
|
|
}
|
|
|
|
void MockRtcConsumer::update_source_id()
|
|
{
|
|
update_source_id_count_++;
|
|
}
|
|
|
|
void MockRtcConsumer::on_stream_change(SrsRtcSourceDescription *desc)
|
|
{
|
|
stream_change_count_++;
|
|
last_stream_desc_ = desc;
|
|
}
|
|
|
|
srs_error_t MockRtcConsumer::enqueue(SrsRtpPacket *pkt)
|
|
{
|
|
enqueue_count_++;
|
|
srs_freep(pkt); // Free the packet to avoid memory leak in tests
|
|
return srs_error_copy(enqueue_error_);
|
|
}
|
|
|
|
void MockRtcConsumer::set_enqueue_error(srs_error_t err)
|
|
{
|
|
srs_freep(enqueue_error_);
|
|
enqueue_error_ = srs_error_copy(err);
|
|
}
|
|
|
|
MockRtcSourceEventHandler::MockRtcSourceEventHandler()
|
|
{
|
|
on_unpublish_count_ = 0;
|
|
on_consumers_finished_count_ = 0;
|
|
}
|
|
|
|
MockRtcSourceEventHandler::~MockRtcSourceEventHandler()
|
|
{
|
|
}
|
|
|
|
void MockRtcSourceEventHandler::on_unpublish()
|
|
{
|
|
on_unpublish_count_++;
|
|
}
|
|
|
|
void MockRtcSourceEventHandler::on_consumers_finished()
|
|
{
|
|
on_consumers_finished_count_++;
|
|
}
|
|
|
|
MockRtcPublishStream::MockRtcPublishStream()
|
|
{
|
|
request_keyframe_count_ = 0;
|
|
last_keyframe_ssrc_ = 0;
|
|
context_id_.set_value("test-publish-stream-id");
|
|
}
|
|
|
|
MockRtcPublishStream::~MockRtcPublishStream()
|
|
{
|
|
}
|
|
|
|
void MockRtcPublishStream::request_keyframe(uint32_t ssrc, SrsContextId cid)
|
|
{
|
|
request_keyframe_count_++;
|
|
last_keyframe_ssrc_ = ssrc;
|
|
last_keyframe_cid_ = cid;
|
|
}
|
|
|
|
const SrsContextId &MockRtcPublishStream::context_id()
|
|
{
|
|
return context_id_;
|
|
}
|
|
|
|
void MockRtcPublishStream::set_context_id(const SrsContextId &cid)
|
|
{
|
|
context_id_ = cid;
|
|
}
|
|
|
|
MockCircuitBreaker::MockCircuitBreaker()
|
|
{
|
|
hybrid_high_water_level_ = false;
|
|
hybrid_critical_water_level_ = false;
|
|
hybrid_dying_water_level_ = false;
|
|
}
|
|
|
|
MockCircuitBreaker::~MockCircuitBreaker()
|
|
{
|
|
}
|
|
|
|
srs_error_t MockCircuitBreaker::initialize()
|
|
{
|
|
return srs_success;
|
|
}
|
|
|
|
bool MockCircuitBreaker::hybrid_high_water_level()
|
|
{
|
|
return hybrid_high_water_level_;
|
|
}
|
|
|
|
bool MockCircuitBreaker::hybrid_critical_water_level()
|
|
{
|
|
return hybrid_critical_water_level_;
|
|
}
|
|
|
|
bool MockCircuitBreaker::hybrid_dying_water_level()
|
|
{
|
|
return hybrid_dying_water_level_;
|
|
}
|
|
|
|
void MockCircuitBreaker::set_hybrid_dying_water_level(bool dying)
|
|
{
|
|
hybrid_dying_water_level_ = dying;
|
|
}
|
|
|
|
MockFailingRequest::MockFailingRequest()
|
|
{
|
|
should_fail_copy_ = false;
|
|
stream_url_ = "rtmp://localhost/live/test";
|
|
vhost_ = "test.vhost";
|
|
app_ = "live";
|
|
stream_ = "test";
|
|
host_ = "localhost";
|
|
port_ = 1935;
|
|
args_ = NULL;
|
|
}
|
|
|
|
MockFailingRequest::MockFailingRequest(const std::string &stream_url, bool should_fail_copy)
|
|
{
|
|
should_fail_copy_ = should_fail_copy;
|
|
stream_url_ = stream_url;
|
|
vhost_ = "test.vhost";
|
|
app_ = "live";
|
|
stream_ = "test";
|
|
host_ = "localhost";
|
|
port_ = 1935;
|
|
args_ = NULL;
|
|
}
|
|
|
|
MockFailingRequest::~MockFailingRequest()
|
|
{
|
|
srs_freep(args_);
|
|
}
|
|
|
|
ISrsRequest *MockFailingRequest::copy()
|
|
{
|
|
if (should_fail_copy_) {
|
|
return NULL;
|
|
}
|
|
MockFailingRequest *cp = new MockFailingRequest();
|
|
cp->should_fail_copy_ = should_fail_copy_;
|
|
cp->stream_url_ = stream_url_;
|
|
cp->vhost_ = vhost_;
|
|
cp->app_ = app_;
|
|
cp->stream_ = stream_;
|
|
cp->host_ = host_;
|
|
cp->port_ = port_;
|
|
return cp;
|
|
}
|
|
|
|
void MockFailingRequest::set_should_fail_copy(bool should_fail)
|
|
{
|
|
should_fail_copy_ = should_fail;
|
|
}
|
|
|
|
void MockFailingRequest::update_auth(ISrsRequest *req)
|
|
{
|
|
// Mock implementation - do nothing
|
|
}
|
|
|
|
std::string MockFailingRequest::get_stream_url()
|
|
{
|
|
return stream_url_;
|
|
}
|
|
|
|
void MockFailingRequest::strip()
|
|
{
|
|
// Mock implementation - do nothing
|
|
}
|
|
|
|
ISrsRequest *MockFailingRequest::as_http()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
MockFailingRtcSource::MockFailingRtcSource()
|
|
{
|
|
should_fail_initialize_ = false;
|
|
initialize_error_ = srs_success;
|
|
}
|
|
|
|
MockFailingRtcSource::~MockFailingRtcSource()
|
|
{
|
|
srs_freep(initialize_error_);
|
|
}
|
|
|
|
srs_error_t MockFailingRtcSource::initialize(ISrsRequest *req)
|
|
{
|
|
if (should_fail_initialize_) {
|
|
return srs_error_copy(initialize_error_);
|
|
}
|
|
return srs_success;
|
|
}
|
|
|
|
void MockFailingRtcSource::set_initialize_error(srs_error_t err)
|
|
{
|
|
srs_freep(initialize_error_);
|
|
initialize_error_ = srs_error_copy(err);
|
|
should_fail_initialize_ = true;
|
|
}
|
|
|
|
MockRtcBridge::MockRtcBridge()
|
|
{
|
|
initialize_count_ = 0;
|
|
setup_codec_count_ = 0;
|
|
on_publish_count_ = 0;
|
|
on_unpublish_count_ = 0;
|
|
on_rtp_count_ = 0;
|
|
initialize_error_ = srs_success;
|
|
setup_codec_error_ = srs_success;
|
|
on_publish_error_ = srs_success;
|
|
on_rtp_error_ = srs_success;
|
|
last_initialize_req_ = NULL;
|
|
last_audio_codec_ = SrsAudioCodecIdForbidden;
|
|
last_video_codec_ = SrsVideoCodecIdForbidden;
|
|
last_rtp_packet_ = NULL;
|
|
}
|
|
|
|
MockRtcBridge::~MockRtcBridge()
|
|
{
|
|
srs_freep(initialize_error_);
|
|
srs_freep(setup_codec_error_);
|
|
srs_freep(on_publish_error_);
|
|
srs_freep(on_rtp_error_);
|
|
srs_freep(last_rtp_packet_);
|
|
}
|
|
|
|
srs_error_t MockRtcBridge::initialize(ISrsRequest *req)
|
|
{
|
|
initialize_count_++;
|
|
last_initialize_req_ = req;
|
|
return srs_error_copy(initialize_error_);
|
|
}
|
|
|
|
srs_error_t MockRtcBridge::setup_codec(SrsAudioCodecId acodec, SrsVideoCodecId vcodec)
|
|
{
|
|
setup_codec_count_++;
|
|
last_audio_codec_ = acodec;
|
|
last_video_codec_ = vcodec;
|
|
return srs_error_copy(setup_codec_error_);
|
|
}
|
|
|
|
srs_error_t MockRtcBridge::on_publish()
|
|
{
|
|
on_publish_count_++;
|
|
return srs_error_copy(on_publish_error_);
|
|
}
|
|
|
|
void MockRtcBridge::on_unpublish()
|
|
{
|
|
on_unpublish_count_++;
|
|
}
|
|
|
|
srs_error_t MockRtcBridge::on_rtp(SrsRtpPacket *pkt)
|
|
{
|
|
on_rtp_count_++;
|
|
srs_freep(last_rtp_packet_);
|
|
last_rtp_packet_ = pkt->copy();
|
|
return srs_error_copy(on_rtp_error_);
|
|
}
|
|
|
|
void MockRtcBridge::set_initialize_error(srs_error_t err)
|
|
{
|
|
srs_freep(initialize_error_);
|
|
initialize_error_ = srs_error_copy(err);
|
|
}
|
|
|
|
void MockRtcBridge::set_setup_codec_error(srs_error_t err)
|
|
{
|
|
srs_freep(setup_codec_error_);
|
|
setup_codec_error_ = srs_error_copy(err);
|
|
}
|
|
|
|
void MockRtcBridge::set_on_publish_error(srs_error_t err)
|
|
{
|
|
srs_freep(on_publish_error_);
|
|
on_publish_error_ = srs_error_copy(err);
|
|
}
|
|
|
|
void MockRtcBridge::set_on_rtp_error(srs_error_t err)
|
|
{
|
|
srs_freep(on_rtp_error_);
|
|
on_rtp_error_ = srs_error_copy(err);
|
|
}
|
|
|
|
VOID TEST(AppTest2, AacRawAppendAdtsHeaderSequenceHeader)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock AAC sequence header packet
|
|
SrsUniquePtr<SrsMediaPacket> audio_packet(new SrsMediaPacket());
|
|
audio_packet->message_type_ = SrsFrameTypeAudio;
|
|
|
|
// Create format with AAC sequence header
|
|
SrsUniquePtr<SrsFormat> format(new SrsFormat());
|
|
HELPER_EXPECT_SUCCESS(format->initialize());
|
|
|
|
// Set up AAC codec info to simulate sequence header
|
|
format->acodec_ = new SrsAudioCodecConfig();
|
|
format->acodec_->id_ = SrsAudioCodecIdAAC;
|
|
format->acodec_->aac_object_ = SrsAacObjectTypeAacLC;
|
|
format->acodec_->aac_sample_rate_ = 4; // 44100 Hz index
|
|
format->acodec_->aac_channels_ = 2;
|
|
|
|
// Create audio frame info to simulate sequence header
|
|
format->audio_ = new SrsParsedAudioPacket();
|
|
format->audio_->dts_ = 1000;
|
|
format->audio_->cts_ = 0;
|
|
format->audio_->nb_samples_ = 0; // Sequence header has 0 samples
|
|
|
|
char *buf = NULL;
|
|
int nn_buf = 0;
|
|
|
|
// Test: sequence header should return success without creating buffer
|
|
HELPER_EXPECT_SUCCESS(aac_raw_append_adts_header(audio_packet.get(), format.get(), &buf, &nn_buf));
|
|
EXPECT_TRUE(buf == NULL);
|
|
EXPECT_EQ(0, nn_buf);
|
|
}
|
|
|
|
VOID TEST(AppTest2, AacRawAppendAdtsHeaderNoSamples)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock AAC packet
|
|
SrsUniquePtr<SrsMediaPacket> audio_packet(new SrsMediaPacket());
|
|
audio_packet->message_type_ = SrsFrameTypeAudio;
|
|
|
|
// Create format with AAC but no samples
|
|
SrsUniquePtr<SrsFormat> format(new SrsFormat());
|
|
HELPER_EXPECT_SUCCESS(format->initialize());
|
|
|
|
// Set up AAC codec info
|
|
format->acodec_ = new SrsAudioCodecConfig();
|
|
format->acodec_->id_ = SrsAudioCodecIdAAC;
|
|
format->acodec_->aac_object_ = SrsAacObjectTypeAacLC;
|
|
format->acodec_->aac_sample_rate_ = 4; // 44100 Hz index
|
|
format->acodec_->aac_channels_ = 2;
|
|
|
|
// Create audio frame info with no samples
|
|
format->audio_ = new SrsParsedAudioPacket();
|
|
format->audio_->dts_ = 1000;
|
|
format->audio_->cts_ = 0;
|
|
format->audio_->nb_samples_ = 0; // No samples
|
|
|
|
char *buf = NULL;
|
|
int nn_buf = 0;
|
|
|
|
// Test: no samples should return success without creating buffer
|
|
HELPER_EXPECT_SUCCESS(aac_raw_append_adts_header(audio_packet.get(), format.get(), &buf, &nn_buf));
|
|
EXPECT_TRUE(buf == NULL);
|
|
EXPECT_EQ(0, nn_buf);
|
|
}
|
|
|
|
VOID TEST(AppTest2, AacRawAppendAdtsHeaderMultipleSamples)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock AAC packet
|
|
SrsUniquePtr<SrsMediaPacket> audio_packet(new SrsMediaPacket());
|
|
audio_packet->message_type_ = SrsFrameTypeAudio;
|
|
|
|
// Create format with AAC
|
|
SrsUniquePtr<SrsFormat> format(new SrsFormat());
|
|
HELPER_EXPECT_SUCCESS(format->initialize());
|
|
|
|
// Set up AAC codec info
|
|
format->acodec_ = new SrsAudioCodecConfig();
|
|
format->acodec_->id_ = SrsAudioCodecIdAAC;
|
|
format->acodec_->aac_object_ = SrsAacObjectTypeAacLC;
|
|
format->acodec_->aac_sample_rate_ = 4; // 44100 Hz index
|
|
format->acodec_->aac_channels_ = 2;
|
|
|
|
// Create audio frame info with multiple samples (should fail)
|
|
format->audio_ = new SrsParsedAudioPacket();
|
|
format->audio_->dts_ = 1000;
|
|
format->audio_->cts_ = 0;
|
|
format->audio_->nb_samples_ = 2; // Multiple samples should cause error
|
|
|
|
char *buf = NULL;
|
|
int nn_buf = 0;
|
|
|
|
// Test: multiple samples should return error
|
|
HELPER_EXPECT_FAILED(aac_raw_append_adts_header(audio_packet.get(), format.get(), &buf, &nn_buf));
|
|
}
|
|
|
|
VOID TEST(AppTest2, AacRawAppendAdtsHeaderValidSingleSample)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock AAC packet
|
|
SrsUniquePtr<SrsMediaPacket> audio_packet(new SrsMediaPacket());
|
|
audio_packet->message_type_ = SrsFrameTypeAudio;
|
|
|
|
// Create format with AAC
|
|
SrsUniquePtr<SrsFormat> format(new SrsFormat());
|
|
HELPER_EXPECT_SUCCESS(format->initialize());
|
|
|
|
// Set up AAC codec info
|
|
format->acodec_ = new SrsAudioCodecConfig();
|
|
format->acodec_->id_ = SrsAudioCodecIdAAC;
|
|
format->acodec_->aac_object_ = SrsAacObjectTypeAacLC; // Object type 2
|
|
format->acodec_->aac_sample_rate_ = 4; // 44100 Hz index
|
|
format->acodec_->aac_channels_ = 2; // Stereo
|
|
|
|
// Create audio frame info with single sample
|
|
format->audio_ = new SrsParsedAudioPacket();
|
|
format->audio_->dts_ = 1000;
|
|
format->audio_->cts_ = 0;
|
|
format->audio_->nb_samples_ = 1; // Single sample
|
|
|
|
// Create sample data
|
|
const char sample_data[] = {0x01, 0x02, 0x03, 0x04, 0x05}; // 5 bytes of sample data
|
|
format->audio_->samples_[0].bytes_ = (char *)sample_data;
|
|
format->audio_->samples_[0].size_ = sizeof(sample_data);
|
|
|
|
char *buf = NULL;
|
|
int nn_buf = 0;
|
|
|
|
// Test: valid single sample should create ADTS header + data
|
|
HELPER_EXPECT_SUCCESS(aac_raw_append_adts_header(audio_packet.get(), format.get(), &buf, &nn_buf));
|
|
|
|
// Verify buffer was created
|
|
EXPECT_TRUE(buf != NULL);
|
|
EXPECT_EQ(7 + sizeof(sample_data), nn_buf); // 7 bytes ADTS header + sample data
|
|
|
|
if (!buf) {
|
|
return;
|
|
}
|
|
SrsUniquePtr<char> buf_ptr(buf);
|
|
|
|
// Verify ADTS header structure
|
|
EXPECT_EQ((unsigned char)0xFF, (unsigned char)buf[0]); // Sync word high
|
|
EXPECT_EQ((unsigned char)0xF9, (unsigned char)buf[1]); // Sync word low + layer + protection
|
|
|
|
// Verify AAC object type, sample rate, and channels are encoded correctly
|
|
// Byte 2: (object-1)<<6 | (sample_rate&0x0F)<<2 | (channels&0x04)>>2
|
|
unsigned char expected_byte2 = ((SrsAacObjectTypeAacLC - 1) << 6) | ((4 & 0x0F) << 2) | ((2 & 0x04) >> 2);
|
|
EXPECT_EQ(expected_byte2, (unsigned char)buf[2]);
|
|
|
|
// Verify frame length is encoded correctly (total length = 7 + sample size)
|
|
int expected_frame_len = 7 + sizeof(sample_data);
|
|
// Frame length spans bytes 3-5
|
|
unsigned char expected_byte3 = ((2 & 0x03) << 6) | ((expected_frame_len >> 11) & 0x03);
|
|
unsigned char expected_byte4 = (expected_frame_len >> 3) & 0xFF;
|
|
unsigned char expected_byte5 = ((expected_frame_len & 0x07) << 5) | 0x1F;
|
|
|
|
EXPECT_EQ(expected_byte3, (unsigned char)buf[3]);
|
|
EXPECT_EQ(expected_byte4, (unsigned char)buf[4]);
|
|
EXPECT_EQ(expected_byte5, (unsigned char)buf[5]);
|
|
|
|
EXPECT_EQ((unsigned char)0xFC, (unsigned char)buf[6]); // Buffer fullness + frame count
|
|
|
|
// Verify sample data is copied correctly
|
|
for (int i = 0; i < sizeof(sample_data); i++) {
|
|
EXPECT_EQ(sample_data[i], buf[7 + i]);
|
|
}
|
|
}
|
|
|
|
VOID TEST(AppTest2, AacRawAppendAdtsHeaderDifferentCodecParams)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock AAC packet
|
|
SrsUniquePtr<SrsMediaPacket> audio_packet(new SrsMediaPacket());
|
|
audio_packet->message_type_ = SrsFrameTypeAudio;
|
|
|
|
// Create format with different AAC parameters
|
|
SrsUniquePtr<SrsFormat> format(new SrsFormat());
|
|
HELPER_EXPECT_SUCCESS(format->initialize());
|
|
|
|
// Set up AAC codec info with different parameters
|
|
format->acodec_ = new SrsAudioCodecConfig();
|
|
format->acodec_->id_ = SrsAudioCodecIdAAC;
|
|
format->acodec_->aac_object_ = SrsAacObjectTypeAacHE; // Object type 5
|
|
format->acodec_->aac_sample_rate_ = 3; // 48000 Hz index
|
|
format->acodec_->aac_channels_ = 1; // Mono
|
|
|
|
// Create audio frame info with single sample
|
|
format->audio_ = new SrsParsedAudioPacket();
|
|
format->audio_->dts_ = 2000;
|
|
format->audio_->cts_ = 0;
|
|
format->audio_->nb_samples_ = 1;
|
|
|
|
// Create sample data
|
|
const char sample_data[] = {(char)0xAA, (char)0xBB, (char)0xCC}; // 3 bytes of sample data
|
|
format->audio_->samples_[0].bytes_ = (char *)sample_data;
|
|
format->audio_->samples_[0].size_ = sizeof(sample_data);
|
|
|
|
char *buf = NULL;
|
|
int nn_buf = 0;
|
|
|
|
// Test: different codec parameters should work
|
|
HELPER_EXPECT_SUCCESS(aac_raw_append_adts_header(audio_packet.get(), format.get(), &buf, &nn_buf));
|
|
|
|
// Verify buffer was created with correct size
|
|
EXPECT_TRUE(buf != NULL);
|
|
EXPECT_EQ(7 + sizeof(sample_data), nn_buf);
|
|
|
|
if (!buf) {
|
|
return;
|
|
}
|
|
SrsUniquePtr<char> buf_ptr(buf);
|
|
|
|
// Verify ADTS header with different parameters
|
|
EXPECT_EQ((unsigned char)0xFF, (unsigned char)buf[0]);
|
|
EXPECT_EQ((unsigned char)0xF9, (unsigned char)buf[1]);
|
|
|
|
// Verify different AAC object type, sample rate, and channels
|
|
unsigned char expected_byte2 = (unsigned char)(((SrsAacObjectTypeAacHE - 1) << 6) | ((3 & 0x0F) << 2) | ((1 & 0x04) >> 2));
|
|
EXPECT_EQ(expected_byte2, (unsigned char)buf[2]);
|
|
}
|
|
|
|
VOID TEST(AppTest2, AacRawAppendAdtsHeaderLargeSample)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock AAC packet
|
|
SrsUniquePtr<SrsMediaPacket> audio_packet(new SrsMediaPacket());
|
|
audio_packet->message_type_ = SrsFrameTypeAudio;
|
|
|
|
// Create format with AAC
|
|
SrsUniquePtr<SrsFormat> format(new SrsFormat());
|
|
HELPER_EXPECT_SUCCESS(format->initialize());
|
|
|
|
// Set up AAC codec info
|
|
format->acodec_ = new SrsAudioCodecConfig();
|
|
format->acodec_->id_ = SrsAudioCodecIdAAC;
|
|
format->acodec_->aac_object_ = SrsAacObjectTypeAacLC;
|
|
format->acodec_->aac_sample_rate_ = 4; // 44100 Hz index
|
|
format->acodec_->aac_channels_ = 2;
|
|
|
|
// Create audio frame info with single large sample
|
|
format->audio_ = new SrsParsedAudioPacket();
|
|
format->audio_->dts_ = 1000;
|
|
format->audio_->cts_ = 0;
|
|
format->audio_->nb_samples_ = 1;
|
|
|
|
// Create large sample data (1KB)
|
|
const int large_size = 1024;
|
|
SrsUniquePtr<char> large_sample(new char[large_size]);
|
|
for (int i = 0; i < large_size; i++) {
|
|
large_sample.get()[i] = (char)(i % 256);
|
|
}
|
|
|
|
format->audio_->samples_[0].bytes_ = large_sample.get();
|
|
format->audio_->samples_[0].size_ = large_size;
|
|
|
|
char *buf = NULL;
|
|
int nn_buf = 0;
|
|
|
|
// Test: large sample should work correctly
|
|
HELPER_EXPECT_SUCCESS(aac_raw_append_adts_header(audio_packet.get(), format.get(), &buf, &nn_buf));
|
|
|
|
// Verify buffer was created with correct size
|
|
EXPECT_TRUE(buf != NULL);
|
|
EXPECT_EQ(7 + large_size, nn_buf);
|
|
|
|
if (!buf) {
|
|
return;
|
|
}
|
|
SrsUniquePtr<char> buf_ptr(buf);
|
|
|
|
// Verify ADTS header
|
|
EXPECT_EQ((unsigned char)0xFF, (unsigned char)buf[0]);
|
|
EXPECT_EQ((unsigned char)0xF9, (unsigned char)buf[1]);
|
|
|
|
// Verify frame length encoding for large frame
|
|
int expected_frame_len = 7 + large_size;
|
|
unsigned char byte4 = (expected_frame_len >> 3) & 0xFF;
|
|
EXPECT_EQ(byte4, (unsigned char)buf[4]);
|
|
|
|
// Verify sample data is copied correctly (check first and last bytes)
|
|
EXPECT_EQ(large_sample.get()[0], buf[7]);
|
|
EXPECT_EQ(large_sample.get()[large_size - 1], buf[7 + large_size - 1]);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerUpdateSourceId)
|
|
{
|
|
MockRtcSourceForConsumer mock_source;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// Initially should_update_source_id_ should be false
|
|
consumer.update_source_id();
|
|
|
|
// After calling update_source_id, the flag should be set
|
|
// We can verify this by calling dump_packet which checks and resets the flag
|
|
SrsRtpPacket *pkt = NULL;
|
|
srs_error_t err;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&pkt));
|
|
EXPECT_TRUE(pkt == NULL); // No packets in queue
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerEnqueueAndDumpSinglePacket)
|
|
{
|
|
srs_error_t err;
|
|
MockRtcSourceForConsumer mock_source;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// Create a test RTP packet
|
|
SrsRtpPacket *test_pkt = new SrsRtpPacket();
|
|
test_pkt->header_.set_sequence(1234);
|
|
test_pkt->header_.set_timestamp(5678);
|
|
test_pkt->header_.set_ssrc(0x12345678);
|
|
|
|
// Enqueue the packet (consumer takes ownership)
|
|
HELPER_EXPECT_SUCCESS(consumer.enqueue(test_pkt));
|
|
|
|
// Dump the packet
|
|
SrsRtpPacket *dumped_pkt = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&dumped_pkt));
|
|
|
|
EXPECT_TRUE(dumped_pkt != NULL);
|
|
EXPECT_EQ(1234, dumped_pkt->header_.get_sequence());
|
|
EXPECT_EQ(5678, dumped_pkt->header_.get_timestamp());
|
|
EXPECT_EQ(0x12345678, dumped_pkt->header_.get_ssrc());
|
|
|
|
srs_freep(dumped_pkt);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerEnqueueMultiplePackets)
|
|
{
|
|
srs_error_t err;
|
|
MockRtcSourceForConsumer mock_source;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// Create and enqueue multiple test packets
|
|
for (int i = 0; i < 5; i++) {
|
|
SrsRtpPacket *test_pkt = new SrsRtpPacket();
|
|
test_pkt->header_.set_sequence(1000 + i);
|
|
test_pkt->header_.set_timestamp(2000 + i * 100);
|
|
test_pkt->header_.set_ssrc(0x12345678);
|
|
|
|
HELPER_EXPECT_SUCCESS(consumer.enqueue(test_pkt));
|
|
}
|
|
|
|
// Dump packets in FIFO order
|
|
for (int i = 0; i < 5; i++) {
|
|
SrsRtpPacket *dumped_pkt = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&dumped_pkt));
|
|
|
|
EXPECT_TRUE(dumped_pkt != NULL);
|
|
EXPECT_EQ(1000 + i, dumped_pkt->header_.get_sequence());
|
|
EXPECT_EQ(2000 + i * 100, dumped_pkt->header_.get_timestamp());
|
|
|
|
srs_freep(dumped_pkt);
|
|
}
|
|
|
|
// Queue should be empty now
|
|
SrsRtpPacket *empty_pkt = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&empty_pkt));
|
|
EXPECT_TRUE(empty_pkt == NULL);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerDumpEmptyQueue)
|
|
{
|
|
srs_error_t err;
|
|
MockRtcSourceForConsumer mock_source;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// Dump from empty queue should return NULL
|
|
SrsRtpPacket *pkt = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&pkt));
|
|
EXPECT_TRUE(pkt == NULL);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerStreamChangeCallback)
|
|
{
|
|
MockRtcSourceForConsumer mock_source;
|
|
MockRtcSourceChangeCallback mock_callback;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// Set the callback handler
|
|
consumer.set_handler(&mock_callback);
|
|
|
|
// Create a mock stream description
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
stream_desc->id_ = "test-stream";
|
|
|
|
// Trigger stream change
|
|
consumer.on_stream_change(stream_desc.get());
|
|
|
|
// Verify callback was called
|
|
EXPECT_EQ(1, mock_callback.stream_change_count_);
|
|
EXPECT_EQ(stream_desc.get(), mock_callback.last_stream_desc_);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerStreamChangeCallbackNoHandler)
|
|
{
|
|
MockRtcSourceForConsumer mock_source;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// No handler set - should not crash
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
stream_desc->id_ = "test-stream";
|
|
|
|
// This should not crash even without handler
|
|
consumer.on_stream_change(stream_desc.get());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerQueueSizeCheck)
|
|
{
|
|
srs_error_t err;
|
|
MockRtcSourceForConsumer mock_source;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// Enqueue packets and verify queue behavior
|
|
for (int i = 0; i < 5; i++) {
|
|
SrsRtpPacket *test_pkt = new SrsRtpPacket();
|
|
test_pkt->header_.set_sequence(1000 + i);
|
|
HELPER_EXPECT_SUCCESS(consumer.enqueue(test_pkt));
|
|
}
|
|
|
|
// Verify we can dump packets in correct order
|
|
SrsRtpPacket *pkt = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&pkt));
|
|
EXPECT_TRUE(pkt != NULL);
|
|
EXPECT_EQ(1000, pkt->header_.get_sequence());
|
|
srs_freep(pkt);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerEnqueueBehavior)
|
|
{
|
|
srs_error_t err;
|
|
MockRtcSourceForConsumer mock_source;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// Enqueue multiple packets to test queue behavior
|
|
SrsRtpPacket *test_pkt1 = new SrsRtpPacket();
|
|
test_pkt1->header_.set_sequence(1001);
|
|
HELPER_EXPECT_SUCCESS(consumer.enqueue(test_pkt1));
|
|
|
|
SrsRtpPacket *test_pkt2 = new SrsRtpPacket();
|
|
test_pkt2->header_.set_sequence(1002);
|
|
HELPER_EXPECT_SUCCESS(consumer.enqueue(test_pkt2));
|
|
|
|
SrsRtpPacket *test_pkt3 = new SrsRtpPacket();
|
|
test_pkt3->header_.set_sequence(1003);
|
|
HELPER_EXPECT_SUCCESS(consumer.enqueue(test_pkt3));
|
|
|
|
// Verify packets are in queue in FIFO order
|
|
SrsRtpPacket *pkt = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&pkt));
|
|
EXPECT_TRUE(pkt != NULL);
|
|
EXPECT_EQ(1001, pkt->header_.get_sequence());
|
|
srs_freep(pkt);
|
|
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&pkt));
|
|
EXPECT_TRUE(pkt != NULL);
|
|
EXPECT_EQ(1002, pkt->header_.get_sequence());
|
|
srs_freep(pkt);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerSourceIdUpdate)
|
|
{
|
|
MockRtcSourceForConsumer mock_source;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// Set different source IDs
|
|
mock_source.source_id_.set_value("new-source-id");
|
|
mock_source.pre_source_id_.set_value("old-source-id");
|
|
|
|
// Trigger source ID update
|
|
consumer.update_source_id();
|
|
|
|
// Dump packet should log the source ID change
|
|
SrsRtpPacket *pkt = NULL;
|
|
srs_error_t err;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&pkt));
|
|
EXPECT_TRUE(pkt == NULL); // No packets in queue
|
|
|
|
// After dump_packet, the update flag should be reset
|
|
// Calling dump_packet again should not trigger another log
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&pkt));
|
|
EXPECT_TRUE(pkt == NULL);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerPacketMemoryManagement)
|
|
{
|
|
srs_error_t err;
|
|
MockRtcSourceForConsumer mock_source;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// Create test packets and enqueue copies (simulating real usage)
|
|
std::vector<SrsRtpPacket *> original_packets;
|
|
for (int i = 0; i < 3; i++) {
|
|
SrsRtpPacket *original_pkt = new SrsRtpPacket();
|
|
original_pkt->header_.set_sequence(2000 + i);
|
|
original_pkt->header_.set_timestamp(3000 + i * 100);
|
|
original_packets.push_back(original_pkt);
|
|
|
|
// Enqueue a copy (this is how it's used in real code)
|
|
HELPER_EXPECT_SUCCESS(consumer.enqueue(original_pkt->copy()));
|
|
}
|
|
|
|
// Dump all packets and verify they have correct data
|
|
for (int i = 0; i < 3; i++) {
|
|
SrsRtpPacket *dumped_pkt = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&dumped_pkt));
|
|
|
|
EXPECT_TRUE(dumped_pkt != NULL);
|
|
EXPECT_TRUE(dumped_pkt != original_packets[i]); // Should be different instances
|
|
EXPECT_EQ(2000 + i, dumped_pkt->header_.get_sequence());
|
|
EXPECT_EQ(3000 + i * 100, dumped_pkt->header_.get_timestamp());
|
|
|
|
srs_freep(dumped_pkt);
|
|
}
|
|
|
|
// Clean up original packets
|
|
for (size_t i = 0; i < original_packets.size(); i++) {
|
|
srs_freep(original_packets[i]);
|
|
}
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerCallbackHandlerLifecycle)
|
|
{
|
|
MockRtcSourceForConsumer mock_source;
|
|
MockRtcSourceChangeCallback callback1;
|
|
MockRtcSourceChangeCallback callback2;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// Initially no handler
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
stream_desc->id_ = "test-stream-1";
|
|
consumer.on_stream_change(stream_desc.get());
|
|
EXPECT_EQ(0, callback1.stream_change_count_);
|
|
EXPECT_EQ(0, callback2.stream_change_count_);
|
|
|
|
// Set first handler
|
|
consumer.set_handler(&callback1);
|
|
consumer.on_stream_change(stream_desc.get());
|
|
EXPECT_EQ(1, callback1.stream_change_count_);
|
|
EXPECT_EQ(0, callback2.stream_change_count_);
|
|
|
|
// Change handler
|
|
consumer.set_handler(&callback2);
|
|
stream_desc->id_ = "test-stream-2";
|
|
consumer.on_stream_change(stream_desc.get());
|
|
EXPECT_EQ(1, callback1.stream_change_count_); // Should not increase
|
|
EXPECT_EQ(1, callback2.stream_change_count_); // Should increase
|
|
|
|
// Remove handler
|
|
consumer.set_handler(NULL);
|
|
consumer.on_stream_change(stream_desc.get());
|
|
EXPECT_EQ(1, callback1.stream_change_count_); // Should not increase
|
|
EXPECT_EQ(1, callback2.stream_change_count_); // Should not increase
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerLargePacketQueue)
|
|
{
|
|
srs_error_t err;
|
|
MockRtcSourceForConsumer mock_source;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
const int large_count = 100;
|
|
|
|
// Enqueue many packets
|
|
for (int i = 0; i < large_count; i++) {
|
|
SrsRtpPacket *test_pkt = new SrsRtpPacket();
|
|
test_pkt->header_.set_sequence(4000 + i);
|
|
test_pkt->header_.set_timestamp(5000 + i * 10);
|
|
test_pkt->header_.set_ssrc(0xABCDEF00 + i);
|
|
|
|
HELPER_EXPECT_SUCCESS(consumer.enqueue(test_pkt));
|
|
}
|
|
|
|
// Dump all packets and verify order
|
|
for (int i = 0; i < large_count; i++) {
|
|
SrsRtpPacket *dumped_pkt = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&dumped_pkt));
|
|
|
|
EXPECT_TRUE(dumped_pkt != NULL);
|
|
EXPECT_EQ(4000 + i, dumped_pkt->header_.get_sequence());
|
|
EXPECT_EQ(5000 + i * 10, dumped_pkt->header_.get_timestamp());
|
|
EXPECT_EQ(0xABCDEF00 + i, dumped_pkt->header_.get_ssrc());
|
|
|
|
srs_freep(dumped_pkt);
|
|
}
|
|
|
|
// Queue should be empty
|
|
SrsRtpPacket *empty_pkt = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&empty_pkt));
|
|
EXPECT_TRUE(empty_pkt == NULL);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnSourceChangedNoConsumers)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Call on_source_changed with no consumers - should succeed
|
|
HELPER_EXPECT_SUCCESS(source->on_source_changed());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnSourceChangedWithConsumers)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create consumers using the source's create_consumer method
|
|
ISrsRtcConsumer *consumer1 = NULL;
|
|
ISrsRtcConsumer *consumer2 = NULL;
|
|
ISrsRtcConsumer *consumer3 = NULL;
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer1));
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer2));
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer3));
|
|
|
|
// Create a stream description
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
stream_desc->id_ = "test-stream-123";
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Call on_source_changed - should notify all consumers
|
|
HELPER_EXPECT_SUCCESS(source->on_source_changed());
|
|
|
|
// Verify stream description is accessible
|
|
EXPECT_TRUE(source->has_stream_desc());
|
|
|
|
// Note: We can't easily verify consumer notifications without accessing private members
|
|
// or creating a more complex mock setup. The test verifies the method executes successfully.
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnSourceChangedContextIdChange)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create consumers
|
|
ISrsRtcConsumer *consumer1 = NULL;
|
|
ISrsRtcConsumer *consumer2 = NULL;
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer1));
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer2));
|
|
|
|
// Store original context ID
|
|
SrsContextId original_context = _srs_context->get_id();
|
|
|
|
// Change the global context ID to simulate source ID change
|
|
SrsContextId new_context = _srs_context->generate_id();
|
|
_srs_context->set_id(new_context);
|
|
|
|
// Call on_source_changed - should detect context ID change and notify consumers
|
|
HELPER_EXPECT_SUCCESS(source->on_source_changed());
|
|
|
|
// Verify source ID was updated
|
|
EXPECT_TRUE(source->source_id().compare(new_context) == 0);
|
|
|
|
// Restore original context
|
|
_srs_context->set_id(original_context);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnSourceChangedNoContextIdChange)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create consumer
|
|
ISrsRtcConsumer *consumer = NULL;
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer));
|
|
|
|
// Call on_source_changed first time to set initial context ID
|
|
HELPER_EXPECT_SUCCESS(source->on_source_changed());
|
|
|
|
// Store the source ID after first call
|
|
SrsContextId first_source_id = source->source_id();
|
|
|
|
// Call on_source_changed again without changing context ID
|
|
HELPER_EXPECT_SUCCESS(source->on_source_changed());
|
|
|
|
// Verify source ID remains the same (no context change)
|
|
EXPECT_TRUE(source->source_id().compare(first_source_id) == 0);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnSourceChangedPreviousSourceId)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Store original context ID
|
|
SrsContextId original_context = _srs_context->get_id();
|
|
|
|
// Call on_source_changed first time to set initial context ID
|
|
HELPER_EXPECT_SUCCESS(source->on_source_changed());
|
|
SrsContextId first_context = source->source_id();
|
|
|
|
// Note: Due to the buggy implementation, _pre_source_id gets set to the current context ID
|
|
// on the first call when _source_id was empty, so it's not empty after the first call
|
|
EXPECT_FALSE(source->pre_source_id().empty());
|
|
EXPECT_TRUE(source->pre_source_id().compare(first_context) == 0);
|
|
|
|
// Change the global context ID to simulate source ID change
|
|
SrsContextId new_context = _srs_context->generate_id();
|
|
_srs_context->set_id(new_context);
|
|
|
|
// Call on_source_changed - should update current source ID but not previous (since it's not empty)
|
|
HELPER_EXPECT_SUCCESS(source->on_source_changed());
|
|
|
|
// Verify current source ID was updated
|
|
EXPECT_TRUE(source->source_id().compare(new_context) == 0);
|
|
|
|
// Verify previous source ID remains the first_context (not updated because it's not empty)
|
|
EXPECT_TRUE(source->pre_source_id().compare(first_context) == 0);
|
|
|
|
// Change context ID again
|
|
SrsContextId third_context = _srs_context->generate_id();
|
|
_srs_context->set_id(third_context);
|
|
|
|
// Call on_source_changed again
|
|
HELPER_EXPECT_SUCCESS(source->on_source_changed());
|
|
|
|
// Verify current source ID was updated to third context
|
|
EXPECT_TRUE(source->source_id().compare(third_context) == 0);
|
|
|
|
// Verify previous source ID remains the first_context (not updated again because it's not empty)
|
|
EXPECT_TRUE(source->pre_source_id().compare(first_context) == 0);
|
|
|
|
// Restore original context
|
|
_srs_context->set_id(original_context);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnSourceChangedWithStreamDescription)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create consumer
|
|
ISrsRtcConsumer *consumer = NULL;
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer));
|
|
|
|
// Create and set stream description
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
stream_desc->id_ = "test-stream-with-desc";
|
|
|
|
// Add audio track description
|
|
stream_desc->audio_track_desc_ = new SrsRtcTrackDescription();
|
|
stream_desc->audio_track_desc_->type_ = "audio";
|
|
stream_desc->audio_track_desc_->id_ = "audio-track-1";
|
|
stream_desc->audio_track_desc_->ssrc_ = 12345;
|
|
|
|
// Add video track description
|
|
SrsRtcTrackDescription *video_track = new SrsRtcTrackDescription();
|
|
video_track->type_ = "video";
|
|
video_track->id_ = "video-track-1";
|
|
video_track->ssrc_ = 67890;
|
|
stream_desc->video_track_descs_.push_back(video_track);
|
|
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Call on_source_changed
|
|
HELPER_EXPECT_SUCCESS(source->on_source_changed());
|
|
|
|
// Verify stream description is accessible
|
|
EXPECT_TRUE(source->has_stream_desc());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerWaitWithSufficientPackets)
|
|
{
|
|
srs_error_t err;
|
|
MockRtcSourceForConsumer mock_source;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// Add some packets to the queue
|
|
for (int i = 0; i < 5; i++) {
|
|
SrsRtpPacket *pkt = new SrsRtpPacket();
|
|
pkt->header_.set_sequence(1000 + i);
|
|
pkt->header_.set_timestamp(2000 + i * 100);
|
|
pkt->header_.set_ssrc(0x12345678);
|
|
HELPER_EXPECT_SUCCESS(consumer.enqueue(pkt));
|
|
}
|
|
|
|
// Wait for 3 messages - should return immediately since we have 5
|
|
consumer.wait(3);
|
|
|
|
// Verify we can dump packets (queue should still have packets)
|
|
SrsRtpPacket *dumped_pkt = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&dumped_pkt));
|
|
EXPECT_TRUE(dumped_pkt != NULL);
|
|
EXPECT_EQ(1000, dumped_pkt->header_.get_sequence());
|
|
srs_freep(dumped_pkt);
|
|
|
|
// Verify more packets are available
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&dumped_pkt));
|
|
EXPECT_TRUE(dumped_pkt != NULL);
|
|
EXPECT_EQ(1001, dumped_pkt->header_.get_sequence());
|
|
srs_freep(dumped_pkt);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerWaitWithMoreThanRequestedPackets)
|
|
{
|
|
srs_error_t err;
|
|
MockRtcSourceForConsumer mock_source;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// Add 5 packets to the queue
|
|
for (int i = 0; i < 5; i++) {
|
|
SrsRtpPacket *pkt = new SrsRtpPacket();
|
|
pkt->header_.set_sequence(2000 + i);
|
|
pkt->header_.set_timestamp(3000 + i * 50);
|
|
pkt->header_.set_ssrc(0xABCDEF00);
|
|
HELPER_EXPECT_SUCCESS(consumer.enqueue(pkt));
|
|
}
|
|
|
|
// Wait for 3 messages - should return immediately since we have 5 > 3
|
|
consumer.wait(3);
|
|
|
|
// Verify all packets are available
|
|
for (int i = 0; i < 5; i++) {
|
|
SrsRtpPacket *dumped_pkt = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&dumped_pkt));
|
|
EXPECT_TRUE(dumped_pkt != NULL);
|
|
EXPECT_EQ(2000 + i, dumped_pkt->header_.get_sequence());
|
|
EXPECT_EQ(3000 + i * 50, dumped_pkt->header_.get_timestamp());
|
|
srs_freep(dumped_pkt);
|
|
}
|
|
|
|
// Queue should be empty now
|
|
SrsRtpPacket *empty_pkt = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&empty_pkt));
|
|
EXPECT_TRUE(empty_pkt == NULL);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerWaitWithZeroMessages)
|
|
{
|
|
srs_error_t err;
|
|
MockRtcSourceForConsumer mock_source;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// Add some packets to the queue
|
|
for (int i = 0; i < 5; i++) {
|
|
SrsRtpPacket *pkt = new SrsRtpPacket();
|
|
pkt->header_.set_sequence(3000 + i);
|
|
HELPER_EXPECT_SUCCESS(consumer.enqueue(pkt));
|
|
}
|
|
|
|
// Wait for 0 messages - should return immediately regardless of queue size
|
|
consumer.wait(0);
|
|
|
|
// Verify packets are still available
|
|
SrsRtpPacket *dumped_pkt = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&dumped_pkt));
|
|
EXPECT_TRUE(dumped_pkt != NULL);
|
|
EXPECT_EQ(3000, dumped_pkt->header_.get_sequence());
|
|
srs_freep(dumped_pkt);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerWaitWithEmptyQueueZeroMessages)
|
|
{
|
|
MockRtcSourceForConsumer mock_source;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// Queue is empty, wait for -1 messages - should return immediately
|
|
// This tests the edge case where nb_msgs is -1
|
|
consumer.wait(-1);
|
|
|
|
// Verify queue is still empty
|
|
SrsRtpPacket *pkt = NULL;
|
|
srs_error_t err;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&pkt));
|
|
EXPECT_TRUE(pkt == NULL);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerWaitWithInsufficientPackets)
|
|
{
|
|
MockRtcSourceForConsumer mock_source;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// Add only 2 packets to the queue
|
|
for (int i = 0; i < 2; i++) {
|
|
SrsRtpPacket *pkt = new SrsRtpPacket();
|
|
pkt->header_.set_sequence(4000 + i);
|
|
pkt->header_.set_timestamp(5000 + i * 200);
|
|
pkt->header_.set_ssrc(0xDEADBEEF);
|
|
srs_error_t err;
|
|
HELPER_EXPECT_SUCCESS(consumer.enqueue(pkt));
|
|
}
|
|
|
|
// Start a goroutine to add more packets after a delay
|
|
SrsCoroutineChan ctx;
|
|
ctx.push(&consumer);
|
|
|
|
SRS_COROUTINE_GO_CTX(&ctx, {
|
|
SrsRtcConsumer *consumer = (SrsRtcConsumer *)ctx.pop();
|
|
srs_usleep(1 * SRS_UTIME_MILLISECONDS);
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
SrsRtpPacket *pkt = new SrsRtpPacket();
|
|
pkt->header_.set_sequence(4002 + i);
|
|
pkt->header_.set_timestamp(5000 + (i + 2) * 200);
|
|
pkt->header_.set_ssrc(0xDEADBEEF);
|
|
srs_error_t err;
|
|
HELPER_EXPECT_SUCCESS(consumer->enqueue(pkt));
|
|
}
|
|
});
|
|
|
|
// Wait for 5 messages when we only have 2
|
|
// In real usage, this would set mw_waiting_ = true and block until more packets arrive
|
|
consumer.wait(5);
|
|
|
|
// Verify we still have the 2 packets we added
|
|
SrsRtpPacket *pkt1 = NULL;
|
|
srs_error_t err;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&pkt1));
|
|
EXPECT_TRUE(pkt1 != NULL);
|
|
EXPECT_EQ(4000, pkt1->header_.get_sequence());
|
|
srs_freep(pkt1);
|
|
|
|
SrsRtpPacket *pkt2 = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&pkt2));
|
|
EXPECT_TRUE(pkt2 != NULL);
|
|
EXPECT_EQ(4001, pkt2->header_.get_sequence());
|
|
srs_freep(pkt2);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerWaitMultipleCalls)
|
|
{
|
|
srs_error_t err;
|
|
MockRtcSourceForConsumer mock_source;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// Test multiple wait calls with different thresholds
|
|
|
|
// First, add 4 packets
|
|
for (int i = 0; i < 4; i++) {
|
|
SrsRtpPacket *pkt = new SrsRtpPacket();
|
|
pkt->header_.set_sequence(5000 + i);
|
|
HELPER_EXPECT_SUCCESS(consumer.enqueue(pkt));
|
|
}
|
|
|
|
// Wait for 2 messages - should return immediately
|
|
consumer.wait(2);
|
|
|
|
// Wait for 1 message - should return immediately
|
|
consumer.wait(1);
|
|
|
|
// Wait for 3 messages - should return immediately (we have exactly 3)
|
|
consumer.wait(3);
|
|
|
|
// Start a goroutine to add more packets after a delay
|
|
SrsCoroutineChan ctx;
|
|
ctx.push(&consumer);
|
|
|
|
SRS_COROUTINE_GO_CTX(&ctx, {
|
|
SrsRtcConsumer *consumer = (SrsRtcConsumer *)ctx.pop();
|
|
srs_usleep(1 * SRS_UTIME_MILLISECONDS);
|
|
|
|
for (int i = 0; i < 1; i++) {
|
|
SrsRtpPacket *pkt = new SrsRtpPacket();
|
|
pkt->header_.set_sequence(4002 + i);
|
|
pkt->header_.set_timestamp(5000 + (i + 2) * 200);
|
|
pkt->header_.set_ssrc(0xDEADBEEF);
|
|
srs_error_t err;
|
|
HELPER_EXPECT_SUCCESS(consumer->enqueue(pkt));
|
|
}
|
|
});
|
|
|
|
// Wait for 4 messages - would block for a while in real usage, but we can't test that
|
|
consumer.wait(4);
|
|
|
|
// Verify all packets are still available
|
|
for (int i = 0; i < 3; i++) {
|
|
SrsRtpPacket *pkt = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&pkt));
|
|
EXPECT_TRUE(pkt != NULL);
|
|
EXPECT_EQ(5000 + i, pkt->header_.get_sequence());
|
|
srs_freep(pkt);
|
|
}
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerWaitAfterDumpingPackets)
|
|
{
|
|
srs_error_t err;
|
|
MockRtcSourceForConsumer mock_source;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// Add 5 packets
|
|
for (int i = 0; i < 5; i++) {
|
|
SrsRtpPacket *pkt = new SrsRtpPacket();
|
|
pkt->header_.set_sequence(6000 + i);
|
|
HELPER_EXPECT_SUCCESS(consumer.enqueue(pkt));
|
|
}
|
|
|
|
// Wait for 3 messages - should return immediately
|
|
consumer.wait(3);
|
|
|
|
// Dump 2 packets, leaving 3 in queue
|
|
for (int i = 0; i < 2; i++) {
|
|
SrsRtpPacket *pkt = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&pkt));
|
|
EXPECT_TRUE(pkt != NULL);
|
|
srs_freep(pkt);
|
|
}
|
|
|
|
// Wait for 2 messages - should return immediately (we still have 3)
|
|
consumer.wait(2);
|
|
|
|
// Dump 1 more packet, leaving 2 in queue
|
|
SrsRtpPacket *pkt = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&pkt));
|
|
EXPECT_TRUE(pkt != NULL);
|
|
srs_freep(pkt);
|
|
|
|
// Start a goroutine to add more packets after a delay
|
|
SrsCoroutineChan ctx;
|
|
ctx.push(&consumer);
|
|
|
|
SRS_COROUTINE_GO_CTX(&ctx, {
|
|
SrsRtcConsumer *consumer = (SrsRtcConsumer *)ctx.pop();
|
|
srs_usleep(1 * SRS_UTIME_MILLISECONDS);
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
SrsRtpPacket *pkt = new SrsRtpPacket();
|
|
pkt->header_.set_sequence(4002 + i);
|
|
pkt->header_.set_timestamp(5000 + (i + 2) * 200);
|
|
pkt->header_.set_ssrc(0xDEADBEEF);
|
|
srs_error_t err;
|
|
HELPER_EXPECT_SUCCESS(consumer->enqueue(pkt));
|
|
}
|
|
});
|
|
|
|
// Wait for 3 messages - would block for a while in real usage (we only have 2)
|
|
consumer.wait(3);
|
|
|
|
// Verify remaining packets
|
|
for (int i = 0; i < 3; i++) {
|
|
SrsRtpPacket *remaining_pkt = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&remaining_pkt));
|
|
EXPECT_TRUE(remaining_pkt != NULL);
|
|
srs_freep(remaining_pkt);
|
|
}
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcConsumerWaitWithLargeThreshold)
|
|
{
|
|
srs_error_t err;
|
|
MockRtcSourceForConsumer mock_source;
|
|
SrsRtcConsumer consumer(&mock_source);
|
|
|
|
// Add 10 packets
|
|
for (int i = 0; i < 10; i++) {
|
|
SrsRtpPacket *pkt = new SrsRtpPacket();
|
|
pkt->header_.set_sequence(7000 + i);
|
|
pkt->header_.set_timestamp(8000 + i * 1000);
|
|
HELPER_EXPECT_SUCCESS(consumer.enqueue(pkt));
|
|
}
|
|
|
|
// Start a goroutine to add more packets after a delay
|
|
SrsCoroutineChan ctx;
|
|
ctx.push(&consumer);
|
|
|
|
SRS_COROUTINE_GO_CTX(&ctx, {
|
|
SrsRtcConsumer *consumer = (SrsRtcConsumer *)ctx.pop();
|
|
srs_usleep(1 * SRS_UTIME_MILLISECONDS);
|
|
|
|
for (int i = 0; i < 91; i++) {
|
|
SrsRtpPacket *pkt = new SrsRtpPacket();
|
|
pkt->header_.set_sequence(4002 + i);
|
|
pkt->header_.set_timestamp(5000 + (i + 2) * 200);
|
|
pkt->header_.set_ssrc(0xDEADBEEF);
|
|
srs_error_t err;
|
|
HELPER_EXPECT_SUCCESS(consumer->enqueue(pkt));
|
|
}
|
|
});
|
|
|
|
// Wait for a large number of messages (100) - would block for a while in real usage
|
|
consumer.wait(100);
|
|
|
|
// Verify all 10 packets are still available
|
|
for (int i = 0; i < 10; i++) {
|
|
SrsRtpPacket *pkt = NULL;
|
|
HELPER_EXPECT_SUCCESS(consumer.dump_packet(&pkt));
|
|
EXPECT_TRUE(pkt != NULL);
|
|
EXPECT_EQ(7000 + i, pkt->header_.get_sequence());
|
|
EXPECT_EQ(8000 + i * 1000, pkt->header_.get_timestamp());
|
|
srs_freep(pkt);
|
|
}
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnRtpWithBridgeSuccess)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create and set mock bridge
|
|
MockRtcBridge *mock_bridge = new MockRtcBridge();
|
|
source->set_bridge(mock_bridge);
|
|
|
|
// Create test RTP packet
|
|
SrsUniquePtr<SrsRtpPacket> test_pkt(new SrsRtpPacket());
|
|
test_pkt->header_.set_sequence(1234);
|
|
test_pkt->header_.set_timestamp(5678);
|
|
test_pkt->header_.set_ssrc(0x12345678);
|
|
|
|
// Test: on_rtp should call bridge->on_rtp and succeed
|
|
HELPER_EXPECT_SUCCESS(source->on_rtp(test_pkt.get()));
|
|
|
|
// Verify bridge was called
|
|
EXPECT_EQ(1, mock_bridge->on_rtp_count_);
|
|
EXPECT_TRUE(mock_bridge->last_rtp_packet_ != NULL);
|
|
EXPECT_EQ(1234, mock_bridge->last_rtp_packet_->header_.get_sequence());
|
|
EXPECT_EQ(5678, mock_bridge->last_rtp_packet_->header_.get_timestamp());
|
|
EXPECT_EQ(0x12345678, mock_bridge->last_rtp_packet_->header_.get_ssrc());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnRtpWithBridgeFailure)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create mock bridge that will fail on_rtp
|
|
MockRtcBridge *mock_bridge = new MockRtcBridge();
|
|
srs_error_t bridge_error = srs_error_new(ERROR_RTC_RTP_MUXER, "mock bridge error");
|
|
mock_bridge->set_on_rtp_error(bridge_error);
|
|
source->set_bridge(mock_bridge);
|
|
|
|
// Create test RTP packet
|
|
SrsUniquePtr<SrsRtpPacket> test_pkt(new SrsRtpPacket());
|
|
test_pkt->header_.set_sequence(9999);
|
|
test_pkt->header_.set_timestamp(8888);
|
|
test_pkt->header_.set_ssrc(0xABCDEF00);
|
|
|
|
// Test: on_rtp should fail when bridge->on_rtp fails
|
|
HELPER_EXPECT_FAILED(source->on_rtp(test_pkt.get()));
|
|
|
|
// Verify bridge was called
|
|
EXPECT_EQ(1, mock_bridge->on_rtp_count_);
|
|
EXPECT_TRUE(mock_bridge->last_rtp_packet_ != NULL);
|
|
EXPECT_EQ(9999, mock_bridge->last_rtp_packet_->header_.get_sequence());
|
|
EXPECT_EQ(8888, mock_bridge->last_rtp_packet_->header_.get_timestamp());
|
|
EXPECT_EQ(0xABCDEF00, mock_bridge->last_rtp_packet_->header_.get_ssrc());
|
|
|
|
srs_freep(bridge_error);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnRtpWithoutBridge)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source without setting bridge
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create test RTP packet
|
|
SrsUniquePtr<SrsRtpPacket> test_pkt(new SrsRtpPacket());
|
|
test_pkt->header_.set_sequence(5555);
|
|
test_pkt->header_.set_timestamp(6666);
|
|
test_pkt->header_.set_ssrc(0x11223344);
|
|
|
|
// Test: on_rtp should succeed when no bridge is set (rtc_bridge_ is NULL)
|
|
HELPER_EXPECT_SUCCESS(source->on_rtp(test_pkt.get()));
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnRtpWithBridgeAndConsumers)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create consumers
|
|
ISrsRtcConsumer *consumer1 = NULL;
|
|
ISrsRtcConsumer *consumer2 = NULL;
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer1));
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer2));
|
|
|
|
// Create and set mock bridge
|
|
MockRtcBridge *mock_bridge = new MockRtcBridge();
|
|
source->set_bridge(mock_bridge);
|
|
|
|
// Create test RTP packet
|
|
SrsUniquePtr<SrsRtpPacket> test_pkt(new SrsRtpPacket());
|
|
test_pkt->header_.set_sequence(7777);
|
|
test_pkt->header_.set_timestamp(8888);
|
|
test_pkt->header_.set_ssrc(0xFEDCBA98);
|
|
|
|
// Test: on_rtp should succeed, delivering to consumers and bridge
|
|
HELPER_EXPECT_SUCCESS(source->on_rtp(test_pkt.get()));
|
|
|
|
// Verify bridge was called
|
|
EXPECT_EQ(1, mock_bridge->on_rtp_count_);
|
|
EXPECT_TRUE(mock_bridge->last_rtp_packet_ != NULL);
|
|
EXPECT_EQ(7777, mock_bridge->last_rtp_packet_->header_.get_sequence());
|
|
EXPECT_EQ(8888, mock_bridge->last_rtp_packet_->header_.get_timestamp());
|
|
EXPECT_EQ(0xFEDCBA98, mock_bridge->last_rtp_packet_->header_.get_ssrc());
|
|
|
|
// Verify consumers received packets (we can't easily check this without accessing private members,
|
|
// but the test verifies the method executes successfully with both consumers and bridge)
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceManagerFetchOrCreateNewSource)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create RTC source manager
|
|
SrsRtcSourceManager manager;
|
|
HELPER_EXPECT_SUCCESS(manager.initialize());
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<MockFailingRequest> req(new MockFailingRequest("test-stream-url"));
|
|
|
|
// Fetch or create source - should create new source
|
|
SrsSharedPtr<SrsRtcSource> source;
|
|
HELPER_EXPECT_SUCCESS(manager.fetch_or_create(req.get(), source));
|
|
|
|
// Verify source was created
|
|
EXPECT_TRUE(source.get() != NULL);
|
|
|
|
// Fetch the same source again - should return existing source
|
|
SrsSharedPtr<SrsRtcSource> source2;
|
|
HELPER_EXPECT_SUCCESS(manager.fetch_or_create(req.get(), source2));
|
|
|
|
// Verify it's the same source instance
|
|
EXPECT_TRUE(source.get() == source2.get());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceManagerFetchOrCreateInitializeSuccess)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create RTC source manager
|
|
SrsRtcSourceManager manager;
|
|
HELPER_EXPECT_SUCCESS(manager.initialize());
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<MockFailingRequest> req(new MockFailingRequest("test-stream-success"));
|
|
|
|
// Fetch or create source - should create new source and initialize successfully
|
|
SrsSharedPtr<SrsRtcSource> source;
|
|
HELPER_EXPECT_SUCCESS(manager.fetch_or_create(req.get(), source));
|
|
|
|
// Verify source was created and initialized
|
|
EXPECT_TRUE(source.get() != NULL);
|
|
|
|
// Create a consumer to make the source not dead
|
|
ISrsRtcConsumer *consumer = NULL;
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer));
|
|
|
|
// Now the source should not be dead since it has a consumer
|
|
EXPECT_FALSE(source->stream_is_dead());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceManagerFetchOrCreateInitializeFailure)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a custom source manager that uses our failing mock source
|
|
class MockRtcSourceManager : public SrsRtcSourceManager
|
|
{
|
|
public:
|
|
bool should_fail_initialize_;
|
|
srs_error_t initialize_error_;
|
|
|
|
MockRtcSourceManager()
|
|
{
|
|
should_fail_initialize_ = false;
|
|
initialize_error_ = srs_success;
|
|
}
|
|
|
|
virtual ~MockRtcSourceManager()
|
|
{
|
|
srs_freep(initialize_error_);
|
|
}
|
|
|
|
virtual srs_error_t fetch_or_create(ISrsRequest *r, SrsSharedPtr<SrsRtcSource> &pps)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
bool created = false;
|
|
// Should never invoke any function during the locking.
|
|
if (true) {
|
|
// Use lock to protect coroutine switch.
|
|
SrsLocker(&lock_);
|
|
|
|
string stream_url = r->get_stream_url();
|
|
std::map<std::string, SrsSharedPtr<SrsRtcSource> >::iterator it = pool_.find(stream_url);
|
|
|
|
if (it != pool_.end()) {
|
|
SrsSharedPtr<SrsRtcSource> source = it->second;
|
|
pps = source;
|
|
} else {
|
|
// Create our failing mock source instead of regular source
|
|
MockFailingRtcSource *mock_source = new MockFailingRtcSource();
|
|
if (should_fail_initialize_) {
|
|
mock_source->set_initialize_error(initialize_error_);
|
|
}
|
|
SrsSharedPtr<SrsRtcSource> source = SrsSharedPtr<SrsRtcSource>(mock_source);
|
|
srs_trace("new rtc source, stream_url=%s", stream_url.c_str());
|
|
pps = source;
|
|
|
|
pool_[stream_url] = source;
|
|
created = true;
|
|
}
|
|
}
|
|
|
|
// Initialize source.
|
|
if (created && (err = pps->initialize(r)) != srs_success) {
|
|
return srs_error_wrap(err, "init source %s", r->get_stream_url().c_str());
|
|
}
|
|
|
|
// we always update the request of resource,
|
|
// for origin auth is on, the token in request maybe invalid,
|
|
// and we only need to update the token of request, it's simple.
|
|
if (!created) {
|
|
pps->update_auth(r);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
void set_initialize_error(srs_error_t err)
|
|
{
|
|
srs_freep(initialize_error_);
|
|
initialize_error_ = srs_error_copy(err);
|
|
should_fail_initialize_ = true;
|
|
}
|
|
};
|
|
|
|
// Create mock source manager
|
|
MockRtcSourceManager manager;
|
|
HELPER_EXPECT_SUCCESS(manager.initialize());
|
|
|
|
// Set up the manager to fail initialization
|
|
srs_error_t test_error = srs_error_new(ERROR_SYSTEM_ASSERT_FAILED, "test initialization failure");
|
|
manager.set_initialize_error(test_error);
|
|
srs_freep(test_error);
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<MockFailingRequest> req(new MockFailingRequest("test-stream-fail"));
|
|
|
|
// Fetch or create source - should fail during initialization
|
|
SrsSharedPtr<SrsRtcSource> source;
|
|
HELPER_EXPECT_FAILED(manager.fetch_or_create(req.get(), source));
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceManagerFetchOrCreateErrorWrapping)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a custom source manager that uses our failing mock source
|
|
class MockRtcSourceManagerWithErrorWrapping : public SrsRtcSourceManager
|
|
{
|
|
public:
|
|
virtual srs_error_t fetch_or_create(ISrsRequest *r, SrsSharedPtr<SrsRtcSource> &pps)
|
|
{
|
|
srs_error_t err = srs_success;
|
|
|
|
bool created = false;
|
|
// Should never invoke any function during the locking.
|
|
if (true) {
|
|
// Use lock to protect coroutine switch.
|
|
SrsLocker(&lock_);
|
|
|
|
string stream_url = r->get_stream_url();
|
|
std::map<std::string, SrsSharedPtr<SrsRtcSource> >::iterator it = pool_.find(stream_url);
|
|
|
|
if (it != pool_.end()) {
|
|
SrsSharedPtr<SrsRtcSource> source = it->second;
|
|
pps = source;
|
|
} else {
|
|
// Create a failing mock source
|
|
MockFailingRtcSource *mock_source = new MockFailingRtcSource();
|
|
srs_error_t init_error = srs_error_new(ERROR_SYSTEM_ASSERT_FAILED, "mock init error");
|
|
mock_source->set_initialize_error(init_error);
|
|
srs_freep(init_error);
|
|
|
|
SrsSharedPtr<SrsRtcSource> source = SrsSharedPtr<SrsRtcSource>(mock_source);
|
|
srs_trace("new rtc source, stream_url=%s", stream_url.c_str());
|
|
pps = source;
|
|
|
|
pool_[stream_url] = source;
|
|
created = true;
|
|
}
|
|
}
|
|
|
|
// Initialize source.
|
|
if (created && (err = pps->initialize(r)) != srs_success) {
|
|
return srs_error_wrap(err, "init source %s", r->get_stream_url().c_str());
|
|
}
|
|
|
|
// we always update the request of resource,
|
|
// for origin auth is on, the token in request maybe invalid,
|
|
// and we only need to update the token of request, it's simple.
|
|
if (!created) {
|
|
pps->update_auth(r);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
};
|
|
|
|
// Create mock source manager
|
|
MockRtcSourceManagerWithErrorWrapping manager;
|
|
HELPER_EXPECT_SUCCESS(manager.initialize());
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<MockFailingRequest> req(new MockFailingRequest("test-stream-error-wrap"));
|
|
|
|
// Fetch or create source - should fail during initialization and wrap the error
|
|
SrsSharedPtr<SrsRtcSource> source;
|
|
err = manager.fetch_or_create(req.get(), source);
|
|
|
|
// Verify that the error was wrapped with context information
|
|
EXPECT_TRUE(err != srs_success);
|
|
if (err != srs_success) {
|
|
std::string error_desc = srs_error_desc(err);
|
|
// The error should contain the wrapped context with stream URL
|
|
EXPECT_TRUE(error_desc.find("init source") != std::string::npos);
|
|
EXPECT_TRUE(error_desc.find("test-stream-error-wrap") != std::string::npos);
|
|
srs_freep(err);
|
|
}
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceManagerFetchOrCreateMultipleStreams)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create RTC source manager
|
|
SrsRtcSourceManager manager;
|
|
HELPER_EXPECT_SUCCESS(manager.initialize());
|
|
|
|
// Create multiple mock requests for different streams
|
|
SrsUniquePtr<MockFailingRequest> req1(new MockFailingRequest("stream1"));
|
|
SrsUniquePtr<MockFailingRequest> req2(new MockFailingRequest("stream2"));
|
|
SrsUniquePtr<MockFailingRequest> req3(new MockFailingRequest("stream3"));
|
|
|
|
// Fetch or create sources for different streams
|
|
SrsSharedPtr<SrsRtcSource> source1, source2, source3;
|
|
HELPER_EXPECT_SUCCESS(manager.fetch_or_create(req1.get(), source1));
|
|
HELPER_EXPECT_SUCCESS(manager.fetch_or_create(req2.get(), source2));
|
|
HELPER_EXPECT_SUCCESS(manager.fetch_or_create(req3.get(), source3));
|
|
|
|
// Verify all sources were created and are different instances
|
|
EXPECT_TRUE(source1.get() != NULL);
|
|
EXPECT_TRUE(source2.get() != NULL);
|
|
EXPECT_TRUE(source3.get() != NULL);
|
|
EXPECT_TRUE(source1.get() != source2.get());
|
|
EXPECT_TRUE(source2.get() != source3.get());
|
|
EXPECT_TRUE(source1.get() != source3.get());
|
|
|
|
// Fetch the same streams again - should return existing sources
|
|
SrsSharedPtr<SrsRtcSource> source1_again, source2_again, source3_again;
|
|
HELPER_EXPECT_SUCCESS(manager.fetch_or_create(req1.get(), source1_again));
|
|
HELPER_EXPECT_SUCCESS(manager.fetch_or_create(req2.get(), source2_again));
|
|
HELPER_EXPECT_SUCCESS(manager.fetch_or_create(req3.get(), source3_again));
|
|
|
|
// Verify they are the same instances (not newly created)
|
|
EXPECT_TRUE(source1.get() == source1_again.get());
|
|
EXPECT_TRUE(source2.get() == source2_again.get());
|
|
EXPECT_TRUE(source3.get() == source3_again.get());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceManagerFetchOrCreateExistingSourceUpdateAuth)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create RTC source manager
|
|
SrsRtcSourceManager manager;
|
|
HELPER_EXPECT_SUCCESS(manager.initialize());
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<MockFailingRequest> req(new MockFailingRequest("test-stream-auth"));
|
|
|
|
// First call - should create new source and initialize it
|
|
SrsSharedPtr<SrsRtcSource> source1;
|
|
HELPER_EXPECT_SUCCESS(manager.fetch_or_create(req.get(), source1));
|
|
EXPECT_TRUE(source1.get() != NULL);
|
|
|
|
// Second call with same stream URL - should return existing source and call update_auth
|
|
// This tests the !created path where pps->update_auth(r) is called
|
|
SrsSharedPtr<SrsRtcSource> source2;
|
|
HELPER_EXPECT_SUCCESS(manager.fetch_or_create(req.get(), source2));
|
|
|
|
// Verify it's the same source instance (existing source was returned)
|
|
EXPECT_TRUE(source1.get() == source2.get());
|
|
|
|
// The update_auth method should have been called on the existing source
|
|
// We can't directly verify this without modifying the source, but we can verify
|
|
// that the fetch_or_create succeeded and returned the same instance
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceManagerFetchOrCreateConcurrentAccess)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create RTC source manager
|
|
SrsRtcSourceManager manager;
|
|
HELPER_EXPECT_SUCCESS(manager.initialize());
|
|
|
|
// Create mock requests for the same stream
|
|
SrsUniquePtr<MockFailingRequest> req1(new MockFailingRequest("concurrent-stream"));
|
|
SrsUniquePtr<MockFailingRequest> req2(new MockFailingRequest("concurrent-stream"));
|
|
|
|
// Simulate concurrent access by calling fetch_or_create multiple times
|
|
// The locking mechanism should ensure only one source is created
|
|
SrsSharedPtr<SrsRtcSource> source1, source2, source3;
|
|
HELPER_EXPECT_SUCCESS(manager.fetch_or_create(req1.get(), source1));
|
|
HELPER_EXPECT_SUCCESS(manager.fetch_or_create(req2.get(), source2));
|
|
HELPER_EXPECT_SUCCESS(manager.fetch_or_create(req1.get(), source3));
|
|
|
|
// All should return the same source instance
|
|
EXPECT_TRUE(source1.get() != NULL);
|
|
EXPECT_TRUE(source1.get() == source2.get());
|
|
EXPECT_TRUE(source1.get() == source3.get());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnConsumerDestroyRemoveConsumer)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create mock consumers
|
|
MockRtcConsumer *consumer1 = new MockRtcConsumer();
|
|
MockRtcConsumer *consumer2 = new MockRtcConsumer();
|
|
MockRtcConsumer *consumer3 = new MockRtcConsumer();
|
|
|
|
// Add consumers to source manually (simulating create_consumer)
|
|
source->consumers_.push_back(consumer1);
|
|
source->consumers_.push_back(consumer2);
|
|
source->consumers_.push_back(consumer3);
|
|
|
|
// Verify initial state
|
|
EXPECT_EQ(3, (int)source->consumers_.size());
|
|
|
|
// Remove middle consumer
|
|
source->on_consumer_destroy(consumer2);
|
|
EXPECT_EQ(2, (int)source->consumers_.size());
|
|
EXPECT_EQ(consumer1, source->consumers_[0]);
|
|
EXPECT_EQ(consumer3, source->consumers_[1]);
|
|
|
|
// Remove first consumer
|
|
source->on_consumer_destroy(consumer1);
|
|
EXPECT_EQ(1, (int)source->consumers_.size());
|
|
EXPECT_EQ(consumer3, source->consumers_[0]);
|
|
|
|
// Remove last consumer
|
|
source->on_consumer_destroy(consumer3);
|
|
EXPECT_EQ(0, (int)source->consumers_.size());
|
|
|
|
// Try to remove non-existent consumer (should not crash)
|
|
MockRtcConsumer *non_existent = new MockRtcConsumer();
|
|
source->on_consumer_destroy(non_existent);
|
|
EXPECT_EQ(0, (int)source->consumers_.size());
|
|
|
|
// Clean up
|
|
srs_freep(consumer1);
|
|
srs_freep(consumer2);
|
|
srs_freep(consumer3);
|
|
srs_freep(non_existent);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnConsumerDestroyNotifyEventHandlers)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create mock publish stream and event handlers
|
|
MockRtcPublishStream *publish_stream = new MockRtcPublishStream();
|
|
MockRtcSourceEventHandler *handler1 = new MockRtcSourceEventHandler();
|
|
MockRtcSourceEventHandler *handler2 = new MockRtcSourceEventHandler();
|
|
|
|
// Set up source with publish stream and event handlers
|
|
source->set_publish_stream(publish_stream);
|
|
source->rtc_source_subscribe(handler1);
|
|
source->rtc_source_subscribe(handler2);
|
|
|
|
// Create mock consumers
|
|
MockRtcConsumer *consumer1 = new MockRtcConsumer();
|
|
MockRtcConsumer *consumer2 = new MockRtcConsumer();
|
|
|
|
// Add consumers to source manually
|
|
source->consumers_.push_back(consumer1);
|
|
source->consumers_.push_back(consumer2);
|
|
|
|
// Verify initial state
|
|
EXPECT_EQ(0, handler1->on_consumers_finished_count_);
|
|
EXPECT_EQ(0, handler2->on_consumers_finished_count_);
|
|
|
|
// Remove first consumer - should not notify handlers yet
|
|
source->on_consumer_destroy(consumer1);
|
|
EXPECT_EQ(0, handler1->on_consumers_finished_count_);
|
|
EXPECT_EQ(0, handler2->on_consumers_finished_count_);
|
|
|
|
// Remove last consumer - should notify all handlers
|
|
source->on_consumer_destroy(consumer2);
|
|
EXPECT_EQ(1, handler1->on_consumers_finished_count_);
|
|
EXPECT_EQ(1, handler2->on_consumers_finished_count_);
|
|
|
|
// Clean up
|
|
srs_freep(consumer1);
|
|
srs_freep(consumer2);
|
|
srs_freep(publish_stream);
|
|
srs_freep(handler1);
|
|
srs_freep(handler2);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnConsumerDestroyNoPublishStream)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create mock event handler
|
|
MockRtcSourceEventHandler *handler = new MockRtcSourceEventHandler();
|
|
source->rtc_source_subscribe(handler);
|
|
|
|
// Create mock consumer
|
|
MockRtcConsumer *consumer = new MockRtcConsumer();
|
|
source->consumers_.push_back(consumer);
|
|
|
|
// Verify initial state - no publish stream set
|
|
EXPECT_TRUE(source->publish_stream() == NULL);
|
|
EXPECT_EQ(0, handler->on_consumers_finished_count_);
|
|
|
|
// Remove consumer - should not notify handlers because no publish stream
|
|
source->on_consumer_destroy(consumer);
|
|
EXPECT_EQ(0, handler->on_consumers_finished_count_);
|
|
|
|
// Clean up
|
|
srs_freep(consumer);
|
|
srs_freep(handler);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnConsumerDestroyStreamDeath)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create mock consumer
|
|
MockRtcConsumer *consumer = new MockRtcConsumer();
|
|
source->consumers_.push_back(consumer);
|
|
|
|
// Verify initial state - stream is not created (no publisher)
|
|
EXPECT_FALSE(source->is_created_);
|
|
EXPECT_EQ(0, source->stream_die_at_);
|
|
|
|
// Remove consumer when stream is not created - should set stream_die_at_
|
|
srs_utime_t before_time = srs_time_now_cached();
|
|
source->on_consumer_destroy(consumer);
|
|
srs_utime_t after_time = srs_time_now_cached();
|
|
|
|
// Verify stream death time was set
|
|
EXPECT_TRUE(source->stream_die_at_ >= before_time);
|
|
EXPECT_TRUE(source->stream_die_at_ <= after_time);
|
|
|
|
// Clean up
|
|
srs_freep(consumer);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnConsumerDestroyStreamAlive)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create mock consumer
|
|
MockRtcConsumer *consumer = new MockRtcConsumer();
|
|
source->consumers_.push_back(consumer);
|
|
|
|
// Set stream as created (has publisher)
|
|
source->is_created_ = true;
|
|
|
|
// Verify initial state
|
|
EXPECT_TRUE(source->is_created_);
|
|
EXPECT_EQ(0, source->stream_die_at_);
|
|
|
|
// Remove consumer when stream is created - should NOT set stream_die_at_
|
|
source->on_consumer_destroy(consumer);
|
|
|
|
// Verify stream death time was NOT set
|
|
EXPECT_EQ(0, source->stream_die_at_);
|
|
|
|
// Clean up
|
|
srs_freep(consumer);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnPublishBasicSuccess)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Verify initial state
|
|
EXPECT_FALSE(source->is_created_);
|
|
EXPECT_FALSE(source->is_delivering_packets_);
|
|
|
|
// Test basic on_publish without bridge
|
|
HELPER_EXPECT_SUCCESS(source->on_publish());
|
|
|
|
// Verify state after publish
|
|
EXPECT_TRUE(source->is_created_);
|
|
EXPECT_TRUE(source->is_delivering_packets_);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnPublishWithConsumers)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create mock consumers
|
|
MockRtcConsumer *consumer1 = new MockRtcConsumer();
|
|
MockRtcConsumer *consumer2 = new MockRtcConsumer();
|
|
source->consumers_.push_back(consumer1);
|
|
source->consumers_.push_back(consumer2);
|
|
|
|
// Verify initial consumer state
|
|
EXPECT_EQ(0, consumer1->update_source_id_count_);
|
|
EXPECT_EQ(0, consumer1->stream_change_count_);
|
|
EXPECT_EQ(0, consumer2->update_source_id_count_);
|
|
EXPECT_EQ(0, consumer2->stream_change_count_);
|
|
|
|
// Test on_publish - should notify consumers via on_source_changed
|
|
HELPER_EXPECT_SUCCESS(source->on_publish());
|
|
|
|
// Verify consumers were notified
|
|
EXPECT_EQ(1, consumer1->update_source_id_count_);
|
|
EXPECT_EQ(1, consumer1->stream_change_count_);
|
|
EXPECT_EQ(1, consumer2->update_source_id_count_);
|
|
EXPECT_EQ(1, consumer2->stream_change_count_);
|
|
|
|
// Clean up
|
|
srs_freep(consumer1);
|
|
srs_freep(consumer2);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnPublishConsumerError)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create mock consumer that will cause on_source_changed to fail
|
|
MockRtcConsumer *consumer = new MockRtcConsumer();
|
|
source->consumers_.push_back(consumer);
|
|
|
|
// Mock a scenario where on_source_changed would fail
|
|
// This is tricky since on_source_changed calls consumer methods directly
|
|
// We'll test by ensuring the method completes successfully even with consumers
|
|
HELPER_EXPECT_SUCCESS(source->on_publish());
|
|
|
|
// Verify consumer was notified
|
|
EXPECT_EQ(1, consumer->update_source_id_count_);
|
|
EXPECT_EQ(1, consumer->stream_change_count_);
|
|
|
|
// Clean up
|
|
srs_freep(consumer);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnPublishWithStreamDescription)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create stream description with audio and video tracks
|
|
SrsRtcSourceDescription *stream_desc = new SrsRtcSourceDescription();
|
|
|
|
// Add audio track
|
|
stream_desc->audio_track_desc_ = new SrsRtcTrackDescription();
|
|
stream_desc->audio_track_desc_->type_ = "audio";
|
|
stream_desc->audio_track_desc_->media_ = new SrsAudioPayload(111, "opus", 48000, 2);
|
|
|
|
// Add video track
|
|
SrsRtcTrackDescription *video_track = new SrsRtcTrackDescription();
|
|
video_track->type_ = "video";
|
|
video_track->media_ = new SrsVideoPayload(102, "H264", 90000);
|
|
stream_desc->video_track_descs_.push_back(video_track);
|
|
|
|
source->set_stream_desc(stream_desc);
|
|
|
|
// Create mock consumer to verify stream change notification
|
|
MockRtcConsumer *consumer = new MockRtcConsumer();
|
|
source->consumers_.push_back(consumer);
|
|
|
|
// Test on_publish with stream description
|
|
HELPER_EXPECT_SUCCESS(source->on_publish());
|
|
|
|
// Verify consumer received stream description
|
|
EXPECT_EQ(1, consumer->stream_change_count_);
|
|
EXPECT_TRUE(consumer->last_stream_desc_ != NULL);
|
|
|
|
// Verify source state
|
|
EXPECT_TRUE(source->is_created_);
|
|
EXPECT_TRUE(source->is_delivering_packets_);
|
|
|
|
// Clean up
|
|
srs_freep(consumer);
|
|
srs_freep(stream_desc);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnPublishStateTransition)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Verify initial state
|
|
EXPECT_FALSE(source->is_created_);
|
|
EXPECT_FALSE(source->is_delivering_packets_);
|
|
|
|
// Test on_publish state transition
|
|
HELPER_EXPECT_SUCCESS(source->on_publish());
|
|
|
|
// Verify both flags are set to true
|
|
EXPECT_TRUE(source->is_created_);
|
|
EXPECT_TRUE(source->is_delivering_packets_);
|
|
|
|
// Test calling on_publish again (should still succeed)
|
|
HELPER_EXPECT_SUCCESS(source->on_publish());
|
|
|
|
// State should remain true
|
|
EXPECT_TRUE(source->is_created_);
|
|
EXPECT_TRUE(source->is_delivering_packets_);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnPublishSourceIdChange)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create mock consumer to track source ID changes
|
|
MockRtcConsumer *consumer = new MockRtcConsumer();
|
|
source->consumers_.push_back(consumer);
|
|
|
|
// Get initial source ID (should be empty)
|
|
SrsContextId initial_id = source->source_id();
|
|
EXPECT_TRUE(initial_id.empty());
|
|
|
|
// Test on_publish - should set source ID from current context
|
|
HELPER_EXPECT_SUCCESS(source->on_publish());
|
|
|
|
// Verify source ID was set
|
|
SrsContextId new_id = source->source_id();
|
|
EXPECT_FALSE(new_id.empty());
|
|
|
|
// Verify consumer was notified of source ID change
|
|
EXPECT_EQ(1, consumer->update_source_id_count_);
|
|
|
|
// Clean up
|
|
srs_freep(consumer);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceSubscribeBasic)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create mock event handlers
|
|
SrsUniquePtr<MockRtcSourceEventHandler> handler1(new MockRtcSourceEventHandler());
|
|
SrsUniquePtr<MockRtcSourceEventHandler> handler2(new MockRtcSourceEventHandler());
|
|
|
|
// Initially no handlers should be subscribed
|
|
EXPECT_EQ(0, (int)source->event_handlers_.size());
|
|
|
|
// Subscribe first handler
|
|
source->rtc_source_subscribe(handler1.get());
|
|
EXPECT_EQ(1, (int)source->event_handlers_.size());
|
|
EXPECT_EQ(handler1.get(), source->event_handlers_[0]);
|
|
|
|
// Subscribe second handler
|
|
source->rtc_source_subscribe(handler2.get());
|
|
EXPECT_EQ(2, (int)source->event_handlers_.size());
|
|
EXPECT_EQ(handler1.get(), source->event_handlers_[0]);
|
|
EXPECT_EQ(handler2.get(), source->event_handlers_[1]);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceSubscribeDuplicateHandler)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create mock event handler
|
|
SrsUniquePtr<MockRtcSourceEventHandler> handler(new MockRtcSourceEventHandler());
|
|
|
|
// Subscribe handler first time
|
|
source->rtc_source_subscribe(handler.get());
|
|
EXPECT_EQ(1, (int)source->event_handlers_.size());
|
|
EXPECT_EQ(handler.get(), source->event_handlers_[0]);
|
|
|
|
// Subscribe same handler again - should not add duplicate
|
|
source->rtc_source_subscribe(handler.get());
|
|
EXPECT_EQ(1, (int)source->event_handlers_.size());
|
|
EXPECT_EQ(handler.get(), source->event_handlers_[0]);
|
|
|
|
// Subscribe same handler multiple times - should still be only one
|
|
source->rtc_source_subscribe(handler.get());
|
|
source->rtc_source_subscribe(handler.get());
|
|
EXPECT_EQ(1, (int)source->event_handlers_.size());
|
|
EXPECT_EQ(handler.get(), source->event_handlers_[0]);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceSubscribeNullHandler)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Initially no handlers should be subscribed
|
|
EXPECT_EQ(0, (int)source->event_handlers_.size());
|
|
|
|
// Subscribe null handler - should add it (implementation allows null)
|
|
source->rtc_source_subscribe(NULL);
|
|
EXPECT_EQ(1, (int)source->event_handlers_.size());
|
|
EXPECT_EQ(NULL, source->event_handlers_[0]);
|
|
|
|
// Subscribe null handler again - should not add duplicate
|
|
source->rtc_source_subscribe(NULL);
|
|
EXPECT_EQ(1, (int)source->event_handlers_.size());
|
|
EXPECT_EQ(NULL, source->event_handlers_[0]);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceSubscribeMultipleHandlers)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create multiple mock event handlers
|
|
SrsUniquePtr<MockRtcSourceEventHandler> handler1(new MockRtcSourceEventHandler());
|
|
SrsUniquePtr<MockRtcSourceEventHandler> handler2(new MockRtcSourceEventHandler());
|
|
SrsUniquePtr<MockRtcSourceEventHandler> handler3(new MockRtcSourceEventHandler());
|
|
|
|
// Initially no handlers should be subscribed
|
|
EXPECT_EQ(0, (int)source->event_handlers_.size());
|
|
|
|
// Subscribe handlers in order
|
|
source->rtc_source_subscribe(handler1.get());
|
|
source->rtc_source_subscribe(handler2.get());
|
|
source->rtc_source_subscribe(handler3.get());
|
|
|
|
// Verify all handlers are subscribed in correct order
|
|
EXPECT_EQ(3, (int)source->event_handlers_.size());
|
|
EXPECT_EQ(handler1.get(), source->event_handlers_[0]);
|
|
EXPECT_EQ(handler2.get(), source->event_handlers_[1]);
|
|
EXPECT_EQ(handler3.get(), source->event_handlers_[2]);
|
|
|
|
// Try to subscribe duplicates - should not change the list
|
|
source->rtc_source_subscribe(handler2.get());
|
|
source->rtc_source_subscribe(handler1.get());
|
|
EXPECT_EQ(3, (int)source->event_handlers_.size());
|
|
EXPECT_EQ(handler1.get(), source->event_handlers_[0]);
|
|
EXPECT_EQ(handler2.get(), source->event_handlers_[1]);
|
|
EXPECT_EQ(handler3.get(), source->event_handlers_[2]);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceSubscribeUnsubscribeInteraction)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create mock event handlers
|
|
SrsUniquePtr<MockRtcSourceEventHandler> handler1(new MockRtcSourceEventHandler());
|
|
SrsUniquePtr<MockRtcSourceEventHandler> handler2(new MockRtcSourceEventHandler());
|
|
|
|
// Subscribe both handlers
|
|
source->rtc_source_subscribe(handler1.get());
|
|
source->rtc_source_subscribe(handler2.get());
|
|
EXPECT_EQ(2, (int)source->event_handlers_.size());
|
|
|
|
// Unsubscribe first handler
|
|
source->rtc_source_unsubscribe(handler1.get());
|
|
EXPECT_EQ(1, (int)source->event_handlers_.size());
|
|
EXPECT_EQ(handler2.get(), source->event_handlers_[0]);
|
|
|
|
// Re-subscribe first handler - should be added back
|
|
source->rtc_source_subscribe(handler1.get());
|
|
EXPECT_EQ(2, (int)source->event_handlers_.size());
|
|
EXPECT_EQ(handler2.get(), source->event_handlers_[0]);
|
|
EXPECT_EQ(handler1.get(), source->event_handlers_[1]);
|
|
|
|
// Unsubscribe all handlers
|
|
source->rtc_source_unsubscribe(handler1.get());
|
|
source->rtc_source_unsubscribe(handler2.get());
|
|
EXPECT_EQ(0, (int)source->event_handlers_.size());
|
|
|
|
// Re-subscribe in different order
|
|
source->rtc_source_subscribe(handler2.get());
|
|
source->rtc_source_subscribe(handler1.get());
|
|
EXPECT_EQ(2, (int)source->event_handlers_.size());
|
|
EXPECT_EQ(handler2.get(), source->event_handlers_[0]);
|
|
EXPECT_EQ(handler1.get(), source->event_handlers_[1]);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceSubscribeEventNotification)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create mock event handlers
|
|
SrsUniquePtr<MockRtcSourceEventHandler> handler1(new MockRtcSourceEventHandler());
|
|
SrsUniquePtr<MockRtcSourceEventHandler> handler2(new MockRtcSourceEventHandler());
|
|
|
|
// Subscribe handlers
|
|
source->rtc_source_subscribe(handler1.get());
|
|
source->rtc_source_subscribe(handler2.get());
|
|
|
|
// Verify initial state
|
|
EXPECT_EQ(0, handler1->on_unpublish_count_);
|
|
EXPECT_EQ(0, handler2->on_unpublish_count_);
|
|
|
|
// Manually set is_created_ to true to simulate published state
|
|
source->is_created_ = true;
|
|
|
|
// Trigger unpublish event - should notify all subscribed handlers
|
|
source->on_unpublish();
|
|
|
|
// Verify handlers were notified
|
|
EXPECT_EQ(1, handler1->on_unpublish_count_);
|
|
EXPECT_EQ(1, handler2->on_unpublish_count_);
|
|
|
|
// Test second scenario: unsubscribe one handler and test again
|
|
// Create a new source for the second test to avoid state issues
|
|
SrsUniquePtr<SrsRtcSource> source2(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source2->initialize(req.get()));
|
|
|
|
// Reset handler counters
|
|
handler1->on_unpublish_count_ = 0;
|
|
handler2->on_unpublish_count_ = 0;
|
|
|
|
// Subscribe both handlers to the new source
|
|
source2->rtc_source_subscribe(handler1.get());
|
|
source2->rtc_source_subscribe(handler2.get());
|
|
|
|
// Unsubscribe handler1
|
|
source2->rtc_source_unsubscribe(handler1.get());
|
|
|
|
// Verify only handler2 is subscribed now
|
|
EXPECT_EQ(1, (int)source2->event_handlers_.size());
|
|
EXPECT_EQ(handler2.get(), source2->event_handlers_[0]);
|
|
|
|
// Set is_created_ and trigger unpublish
|
|
source2->is_created_ = true;
|
|
source2->on_unpublish();
|
|
|
|
// Only handler2 should be notified this time
|
|
EXPECT_EQ(0, handler1->on_unpublish_count_); // Not called
|
|
EXPECT_EQ(1, handler2->on_unpublish_count_); // Called once
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourcePublishStreamGetterSetter)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Initially, publish_stream should be NULL
|
|
EXPECT_TRUE(source->publish_stream() == NULL);
|
|
|
|
// Create a mock publish stream
|
|
SrsUniquePtr<MockRtcPublishStream> publish_stream(new MockRtcPublishStream());
|
|
SrsContextId test_cid;
|
|
test_cid.set_value("test-context-id");
|
|
publish_stream->set_context_id(test_cid);
|
|
|
|
// Set the publish stream
|
|
source->set_publish_stream(publish_stream.get());
|
|
|
|
// Verify the publish stream is set correctly
|
|
EXPECT_TRUE(source->publish_stream() != NULL);
|
|
EXPECT_EQ(publish_stream.get(), source->publish_stream());
|
|
SrsContextId expected_cid;
|
|
expected_cid.set_value("test-context-id");
|
|
EXPECT_TRUE(source->publish_stream()->context_id().compare(expected_cid) == 0);
|
|
|
|
// Set to NULL
|
|
source->set_publish_stream(NULL);
|
|
EXPECT_TRUE(source->publish_stream() == NULL);
|
|
|
|
// Set again to verify it can be changed
|
|
source->set_publish_stream(publish_stream.get());
|
|
EXPECT_EQ(publish_stream.get(), source->publish_stream());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourcePublishStreamWithConsumerDestroy)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create a mock publish stream
|
|
SrsUniquePtr<MockRtcPublishStream> publish_stream(new MockRtcPublishStream());
|
|
source->set_publish_stream(publish_stream.get());
|
|
|
|
// Create a mock event handler
|
|
SrsUniquePtr<MockRtcSourceEventHandler> handler(new MockRtcSourceEventHandler());
|
|
source->rtc_source_subscribe(handler.get());
|
|
|
|
// Create consumers
|
|
ISrsRtcConsumer *consumer1 = NULL;
|
|
ISrsRtcConsumer *consumer2 = NULL;
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer1));
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer2));
|
|
|
|
// Verify consumers exist
|
|
EXPECT_EQ(2, (int)source->consumers_.size());
|
|
|
|
// Destroy first consumer - should not trigger event handler since consumers remain
|
|
source->on_consumer_destroy(consumer1);
|
|
EXPECT_EQ(1, (int)source->consumers_.size());
|
|
EXPECT_EQ(0, handler->on_consumers_finished_count_);
|
|
|
|
// Destroy second consumer - should trigger event handler since no consumers remain
|
|
source->on_consumer_destroy(consumer2);
|
|
EXPECT_EQ(0, (int)source->consumers_.size());
|
|
EXPECT_EQ(1, handler->on_consumers_finished_count_);
|
|
|
|
// Clean up
|
|
srs_freep(consumer1);
|
|
srs_freep(consumer2);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceConsumerDumpsBasicSuccess)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create a consumer
|
|
ISrsRtcConsumer *consumer = NULL;
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer));
|
|
|
|
// Test consumer_dumps with default parameters (all true)
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer));
|
|
|
|
// Verify the method returns success
|
|
// Note: The method only prints a trace message and returns srs_success
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceConsumerDumpsWithAllParameterCombinations)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create a consumer
|
|
ISrsRtcConsumer *consumer = NULL;
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer));
|
|
|
|
// Test all combinations of ds, dm, dg parameters
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer, true, true, true));
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer, true, true, false));
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer, true, false, true));
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer, true, false, false));
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer, false, true, true));
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer, false, true, false));
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer, false, false, true));
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer, false, false, false));
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceConsumerDumpsWithNullConsumer)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Test consumer_dumps with NULL consumer - should still succeed
|
|
// The current implementation doesn't use the consumer parameter
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(NULL));
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(NULL, true, true, true));
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(NULL, false, false, false));
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceConsumerDumpsMultipleConsumers)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create multiple consumers
|
|
ISrsRtcConsumer *consumer1 = NULL;
|
|
ISrsRtcConsumer *consumer2 = NULL;
|
|
ISrsRtcConsumer *consumer3 = NULL;
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer1));
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer2));
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer3));
|
|
|
|
// Test consumer_dumps with different consumers
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer1));
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer2, true, false, true));
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer3, false, true, false));
|
|
|
|
// Test multiple calls on same consumer
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer1));
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer1, false, false, false));
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceConsumerDumpsWithMockConsumer)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create a mock consumer (not created through source->create_consumer)
|
|
SrsUniquePtr<MockRtcConsumer> mock_consumer(new MockRtcConsumer());
|
|
|
|
// Test consumer_dumps with mock consumer - should still succeed
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(mock_consumer.get()));
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(mock_consumer.get(), true, true, true));
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(mock_consumer.get(), false, false, false));
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceConsumerDumpsSequentialCalls)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create a consumer
|
|
ISrsRtcConsumer *consumer = NULL;
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer));
|
|
|
|
// Test multiple sequential calls to consumer_dumps
|
|
for (int i = 0; i < 10; i++) {
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer));
|
|
}
|
|
|
|
// Test sequential calls with different parameter combinations
|
|
for (int i = 0; i < 5; i++) {
|
|
bool ds = (i % 2 == 0);
|
|
bool dm = (i % 3 == 0);
|
|
bool dg = (i % 4 == 0);
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer, ds, dm, dg));
|
|
}
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceConsumerDumpsAfterSourceStateChanges)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create a consumer
|
|
ISrsRtcConsumer *consumer = NULL;
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer));
|
|
|
|
// Test consumer_dumps before publish
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer));
|
|
|
|
// Publish the source
|
|
HELPER_EXPECT_SUCCESS(source->on_publish());
|
|
|
|
// Test consumer_dumps after publish
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer, true, true, true));
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer, false, false, false));
|
|
|
|
// Unpublish the source
|
|
source->on_unpublish();
|
|
|
|
// Test consumer_dumps after unpublish
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer));
|
|
HELPER_EXPECT_SUCCESS(source->consumer_dumps(consumer, true, false, true));
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnPublishWithRtcBridgeSuccess)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create and set up stream description with audio and video tracks
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
stream_desc->id_ = "test-stream-with-bridge";
|
|
|
|
// Add audio track
|
|
stream_desc->audio_track_desc_ = new SrsRtcTrackDescription();
|
|
stream_desc->audio_track_desc_->type_ = "audio";
|
|
stream_desc->audio_track_desc_->id_ = "audio-track-1";
|
|
stream_desc->audio_track_desc_->ssrc_ = 12345;
|
|
stream_desc->audio_track_desc_->media_ = new SrsAudioPayload(111, "opus", 48000, 2);
|
|
|
|
// Add video track
|
|
SrsRtcTrackDescription *video_track = new SrsRtcTrackDescription();
|
|
video_track->type_ = "video";
|
|
video_track->id_ = "video-track-1";
|
|
video_track->ssrc_ = 67890;
|
|
video_track->media_ = new SrsVideoPayload(102, "H264", 90000);
|
|
stream_desc->video_track_descs_.push_back(video_track);
|
|
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Create mock RTC bridge (source will take ownership)
|
|
MockRtcBridge *mock_bridge = new MockRtcBridge();
|
|
source->set_bridge(mock_bridge);
|
|
|
|
// Test on_publish with RTC bridge - should call all bridge methods
|
|
HELPER_EXPECT_SUCCESS(source->on_publish());
|
|
|
|
// Verify bridge methods were called in correct order
|
|
EXPECT_EQ(1, mock_bridge->initialize_count_);
|
|
EXPECT_EQ(1, mock_bridge->setup_codec_count_);
|
|
EXPECT_EQ(1, mock_bridge->on_publish_count_);
|
|
|
|
// Verify bridge was initialized with correct request (bridge gets a copy)
|
|
EXPECT_TRUE(mock_bridge->last_initialize_req_ != NULL);
|
|
|
|
// Verify bridge was set up with correct codecs
|
|
EXPECT_EQ(SrsAudioCodecIdOpus, mock_bridge->last_audio_codec_);
|
|
EXPECT_EQ(SrsVideoCodecIdAVC, mock_bridge->last_video_codec_);
|
|
|
|
// Verify source state
|
|
EXPECT_TRUE(source->is_created_);
|
|
EXPECT_TRUE(source->is_delivering_packets_);
|
|
|
|
// Clean up properly by calling on_unpublish to unsubscribe from timer
|
|
source->on_unpublish();
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnPublishWithRtcBridgeInitializeFailure)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create stream description
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Create mock RTC bridge that fails on initialize (source will take ownership)
|
|
MockRtcBridge *mock_bridge = new MockRtcBridge();
|
|
srs_error_t bridge_error = srs_error_new(ERROR_SYSTEM_ASSERT_FAILED, "bridge initialize failed");
|
|
mock_bridge->set_initialize_error(bridge_error);
|
|
srs_freep(bridge_error);
|
|
|
|
source->set_bridge(mock_bridge);
|
|
|
|
// Test on_publish with failing bridge - should return error
|
|
HELPER_EXPECT_FAILED(source->on_publish());
|
|
|
|
// Verify bridge initialize was called but setup_codec and on_publish were not
|
|
EXPECT_EQ(1, mock_bridge->initialize_count_);
|
|
EXPECT_EQ(0, mock_bridge->setup_codec_count_);
|
|
EXPECT_EQ(0, mock_bridge->on_publish_count_);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnPublishWithRtcBridgeSetupCodecFailure)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create stream description with audio and video tracks
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
stream_desc->audio_track_desc_ = new SrsRtcTrackDescription();
|
|
stream_desc->audio_track_desc_->type_ = "audio";
|
|
stream_desc->audio_track_desc_->media_ = new SrsAudioPayload(111, "opus", 48000, 2);
|
|
|
|
SrsRtcTrackDescription *video_track = new SrsRtcTrackDescription();
|
|
video_track->type_ = "video";
|
|
video_track->media_ = new SrsVideoPayload(102, "H264", 90000);
|
|
stream_desc->video_track_descs_.push_back(video_track);
|
|
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Create mock RTC bridge that fails on setup_codec (source will take ownership)
|
|
MockRtcBridge *mock_bridge = new MockRtcBridge();
|
|
srs_error_t bridge_error = srs_error_new(ERROR_SYSTEM_ASSERT_FAILED, "bridge setup codec failed");
|
|
mock_bridge->set_setup_codec_error(bridge_error);
|
|
srs_freep(bridge_error);
|
|
|
|
source->set_bridge(mock_bridge);
|
|
|
|
// Test on_publish with failing bridge - should return error
|
|
HELPER_EXPECT_FAILED(source->on_publish());
|
|
|
|
// Verify bridge initialize and setup_codec were called but on_publish was not
|
|
EXPECT_EQ(1, mock_bridge->initialize_count_);
|
|
EXPECT_EQ(1, mock_bridge->setup_codec_count_);
|
|
EXPECT_EQ(0, mock_bridge->on_publish_count_);
|
|
|
|
// Verify correct codecs were passed to setup_codec
|
|
EXPECT_EQ(SrsAudioCodecIdOpus, mock_bridge->last_audio_codec_);
|
|
EXPECT_EQ(SrsVideoCodecIdAVC, mock_bridge->last_video_codec_);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnPublishWithRtcBridgeOnPublishFailure)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create stream description
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Create mock RTC bridge that fails on on_publish (source will take ownership)
|
|
MockRtcBridge *mock_bridge = new MockRtcBridge();
|
|
srs_error_t bridge_error = srs_error_new(ERROR_SYSTEM_ASSERT_FAILED, "bridge on_publish failed");
|
|
mock_bridge->set_on_publish_error(bridge_error);
|
|
srs_freep(bridge_error);
|
|
|
|
source->set_bridge(mock_bridge);
|
|
|
|
// Test on_publish with failing bridge - should return error
|
|
HELPER_EXPECT_FAILED(source->on_publish());
|
|
|
|
// Verify all bridge methods were called
|
|
EXPECT_EQ(1, mock_bridge->initialize_count_);
|
|
EXPECT_EQ(1, mock_bridge->setup_codec_count_);
|
|
EXPECT_EQ(1, mock_bridge->on_publish_count_);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnPublishWithRtcBridgeDefaultCodecs)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create stream description without audio/video tracks (should use defaults)
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Create mock RTC bridge (source will take ownership)
|
|
MockRtcBridge *mock_bridge = new MockRtcBridge();
|
|
source->set_bridge(mock_bridge);
|
|
|
|
// Test on_publish with default codecs
|
|
HELPER_EXPECT_SUCCESS(source->on_publish());
|
|
|
|
// Verify bridge methods were called
|
|
EXPECT_EQ(1, mock_bridge->initialize_count_);
|
|
EXPECT_EQ(1, mock_bridge->setup_codec_count_);
|
|
EXPECT_EQ(1, mock_bridge->on_publish_count_);
|
|
|
|
// Verify default codecs were used (Opus for audio, AVC/H264 for video)
|
|
EXPECT_EQ(SrsAudioCodecIdOpus, mock_bridge->last_audio_codec_);
|
|
EXPECT_EQ(SrsVideoCodecIdAVC, mock_bridge->last_video_codec_);
|
|
|
|
// Clean up properly by calling on_unpublish to unsubscribe from timer
|
|
source->on_unpublish();
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnPublishWithRtcBridgeAudioOnlyStream)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create stream description with only audio track
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
stream_desc->audio_track_desc_ = new SrsRtcTrackDescription();
|
|
stream_desc->audio_track_desc_->type_ = "audio";
|
|
stream_desc->audio_track_desc_->media_ = new SrsAudioPayload(111, "opus", 48000, 2);
|
|
// No video tracks
|
|
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Create mock RTC bridge (source will take ownership)
|
|
MockRtcBridge *mock_bridge = new MockRtcBridge();
|
|
source->set_bridge(mock_bridge);
|
|
|
|
// Test on_publish with audio-only stream
|
|
HELPER_EXPECT_SUCCESS(source->on_publish());
|
|
|
|
// Verify bridge methods were called
|
|
EXPECT_EQ(1, mock_bridge->initialize_count_);
|
|
EXPECT_EQ(1, mock_bridge->setup_codec_count_);
|
|
EXPECT_EQ(1, mock_bridge->on_publish_count_);
|
|
|
|
// Verify codecs (Opus for audio, default AVC for video even in audio-only stream)
|
|
EXPECT_EQ(SrsAudioCodecIdOpus, mock_bridge->last_audio_codec_);
|
|
EXPECT_EQ(SrsVideoCodecIdAVC, mock_bridge->last_video_codec_);
|
|
|
|
// Clean up properly by calling on_unpublish to unsubscribe from timer
|
|
source->on_unpublish();
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnPublishWithRtcBridgeVideoOnlyStream)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create stream description with only video track
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
// No audio track
|
|
SrsRtcTrackDescription *video_track = new SrsRtcTrackDescription();
|
|
video_track->type_ = "video";
|
|
video_track->media_ = new SrsVideoPayload(102, "H264", 90000);
|
|
stream_desc->video_track_descs_.push_back(video_track);
|
|
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Create mock RTC bridge (source will take ownership)
|
|
MockRtcBridge *mock_bridge = new MockRtcBridge();
|
|
source->set_bridge(mock_bridge);
|
|
|
|
// Test on_publish with video-only stream
|
|
HELPER_EXPECT_SUCCESS(source->on_publish());
|
|
|
|
// Verify bridge methods were called
|
|
EXPECT_EQ(1, mock_bridge->initialize_count_);
|
|
EXPECT_EQ(1, mock_bridge->setup_codec_count_);
|
|
EXPECT_EQ(1, mock_bridge->on_publish_count_);
|
|
|
|
// Verify codecs (default Opus for audio even in video-only stream, AVC/H264 for video)
|
|
EXPECT_EQ(SrsAudioCodecIdOpus, mock_bridge->last_audio_codec_);
|
|
EXPECT_EQ(SrsVideoCodecIdAVC, mock_bridge->last_video_codec_);
|
|
|
|
// Clean up properly by calling on_unpublish to unsubscribe from timer
|
|
source->on_unpublish();
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnPublishWithoutRtcBridge)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create stream description
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// No RTC bridge set (rtc_bridge_ is NULL)
|
|
EXPECT_TRUE(source->rtc_bridge_ == NULL);
|
|
|
|
// Test on_publish without RTC bridge - should succeed
|
|
HELPER_EXPECT_SUCCESS(source->on_publish());
|
|
|
|
// Verify source state
|
|
EXPECT_TRUE(source->is_created_);
|
|
EXPECT_TRUE(source->is_delivering_packets_);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnPublishWithRtcBridgeTimerSubscription)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create stream description
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Create mock RTC bridge (source will take ownership)
|
|
MockRtcBridge *mock_bridge = new MockRtcBridge();
|
|
source->set_bridge(mock_bridge);
|
|
|
|
// Test on_publish with RTC bridge - should subscribe to timer
|
|
HELPER_EXPECT_SUCCESS(source->on_publish());
|
|
|
|
// Verify bridge methods were called
|
|
EXPECT_EQ(1, mock_bridge->initialize_count_);
|
|
EXPECT_EQ(1, mock_bridge->setup_codec_count_);
|
|
EXPECT_EQ(1, mock_bridge->on_publish_count_);
|
|
|
|
// Note: We can't easily test timer subscription without accessing private members
|
|
// or creating a more complex mock setup. The test verifies the method executes successfully.
|
|
|
|
// Verify source state
|
|
EXPECT_TRUE(source->is_created_);
|
|
EXPECT_TRUE(source->is_delivering_packets_);
|
|
|
|
// Clean up properly by calling on_unpublish to unsubscribe from timer
|
|
source->on_unpublish();
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceCanPublishInitialState)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Initially, stream is not created, so can_publish should return true
|
|
EXPECT_TRUE(source->can_publish());
|
|
EXPECT_FALSE(source->is_created_);
|
|
EXPECT_FALSE(source->is_delivering_packets_);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceCanPublishAfterStreamCreated)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Initially can publish
|
|
EXPECT_TRUE(source->can_publish());
|
|
|
|
// Set stream as created
|
|
source->set_stream_created();
|
|
|
|
// After stream is created, can_publish should return false
|
|
EXPECT_FALSE(source->can_publish());
|
|
EXPECT_TRUE(source->is_created_);
|
|
EXPECT_FALSE(source->is_delivering_packets_);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceCanPublishAfterPublish)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Initially can publish
|
|
EXPECT_TRUE(source->can_publish());
|
|
|
|
// Call on_publish which sets both is_created_ and is_delivering_packets_ to true
|
|
HELPER_EXPECT_SUCCESS(source->on_publish());
|
|
|
|
// After publish, can_publish should return false because is_created_ is true
|
|
EXPECT_FALSE(source->can_publish());
|
|
EXPECT_TRUE(source->is_created_);
|
|
EXPECT_TRUE(source->is_delivering_packets_);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceCanPublishAfterUnpublish)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Publish first
|
|
HELPER_EXPECT_SUCCESS(source->on_publish());
|
|
EXPECT_FALSE(source->can_publish());
|
|
|
|
// Unpublish - this sets both is_created_ and is_delivering_packets_ to false
|
|
source->on_unpublish();
|
|
|
|
// After unpublish, can_publish should return true because is_created_ is now false
|
|
EXPECT_TRUE(source->can_publish());
|
|
EXPECT_FALSE(source->is_created_);
|
|
EXPECT_FALSE(source->is_delivering_packets_);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceSetStreamCreatedBasic)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Verify initial state
|
|
EXPECT_FALSE(source->is_created_);
|
|
EXPECT_FALSE(source->is_delivering_packets_);
|
|
EXPECT_TRUE(source->can_publish());
|
|
|
|
// Call set_stream_created
|
|
source->set_stream_created();
|
|
|
|
// Verify state after set_stream_created
|
|
EXPECT_TRUE(source->is_created_);
|
|
EXPECT_FALSE(source->is_delivering_packets_); // Should remain false
|
|
EXPECT_FALSE(source->can_publish()); // Should now return false
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceSetStreamCreatedMultipleCalls)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// First call to set_stream_created
|
|
source->set_stream_created();
|
|
EXPECT_TRUE(source->is_created_);
|
|
EXPECT_FALSE(source->is_delivering_packets_);
|
|
|
|
// Note: Multiple calls to set_stream_created would violate the assertion
|
|
// srs_assert(!is_created_ && !is_delivering_packets_) in the implementation
|
|
// So we don't test multiple calls as it would cause assertion failure
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceSetStreamCreatedBeforePublish)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Set stream created first
|
|
source->set_stream_created();
|
|
EXPECT_TRUE(source->is_created_);
|
|
EXPECT_FALSE(source->is_delivering_packets_);
|
|
EXPECT_FALSE(source->can_publish());
|
|
|
|
// Then call on_publish - this should set is_delivering_packets_ to true
|
|
// but is_created_ is already true
|
|
HELPER_EXPECT_SUCCESS(source->on_publish());
|
|
EXPECT_TRUE(source->is_created_);
|
|
EXPECT_TRUE(source->is_delivering_packets_);
|
|
EXPECT_FALSE(source->can_publish());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceSetStreamCreatedStateTransitions)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Test state transitions
|
|
// Initial state: not created, not delivering, can publish
|
|
EXPECT_FALSE(source->is_created_);
|
|
EXPECT_FALSE(source->is_delivering_packets_);
|
|
EXPECT_TRUE(source->can_publish());
|
|
|
|
// After set_stream_created: created, not delivering, cannot publish
|
|
source->set_stream_created();
|
|
EXPECT_TRUE(source->is_created_);
|
|
EXPECT_FALSE(source->is_delivering_packets_);
|
|
EXPECT_FALSE(source->can_publish());
|
|
|
|
// After on_publish: created, delivering, cannot publish
|
|
HELPER_EXPECT_SUCCESS(source->on_publish());
|
|
EXPECT_TRUE(source->is_created_);
|
|
EXPECT_TRUE(source->is_delivering_packets_);
|
|
EXPECT_FALSE(source->can_publish());
|
|
|
|
// After on_unpublish: not created, not delivering, can publish
|
|
source->on_unpublish();
|
|
EXPECT_FALSE(source->is_created_);
|
|
EXPECT_FALSE(source->is_delivering_packets_);
|
|
EXPECT_TRUE(source->can_publish()); // Can publish again because is_created_ is now false
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceCanPublishWithConsumers)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create consumers - should not affect can_publish
|
|
ISrsRtcConsumer *consumer1 = NULL;
|
|
ISrsRtcConsumer *consumer2 = NULL;
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer1));
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer2));
|
|
|
|
// can_publish should still return true (only depends on is_created_)
|
|
EXPECT_TRUE(source->can_publish());
|
|
|
|
// Set stream created
|
|
source->set_stream_created();
|
|
|
|
// can_publish should now return false regardless of consumers
|
|
EXPECT_FALSE(source->can_publish());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceCanPublishWithStreamDescription)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create and set stream description - should not affect can_publish
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
stream_desc->id_ = "test-stream";
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// can_publish should still return true (only depends on is_created_)
|
|
EXPECT_TRUE(source->can_publish());
|
|
|
|
// Set stream created
|
|
source->set_stream_created();
|
|
|
|
// can_publish should now return false regardless of stream description
|
|
EXPECT_FALSE(source->can_publish());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceCanPublishConsistency)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create multiple sources to test consistency
|
|
for (int i = 0; i < 5; i++) {
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test" + std::to_string(i);
|
|
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// All sources should initially allow publishing
|
|
EXPECT_TRUE(source->can_publish());
|
|
|
|
// After setting stream created, none should allow publishing
|
|
source->set_stream_created();
|
|
EXPECT_FALSE(source->can_publish());
|
|
|
|
// Multiple calls to can_publish should return consistent results
|
|
for (int j = 0; j < 3; j++) {
|
|
EXPECT_FALSE(source->can_publish());
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceSetStreamCreatedWithEventHandlers)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->host_ = "localhost";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create and subscribe event handlers
|
|
SrsUniquePtr<MockRtcSourceEventHandler> handler1(new MockRtcSourceEventHandler());
|
|
SrsUniquePtr<MockRtcSourceEventHandler> handler2(new MockRtcSourceEventHandler());
|
|
source->rtc_source_subscribe(handler1.get());
|
|
source->rtc_source_subscribe(handler2.get());
|
|
|
|
// Verify initial state
|
|
EXPECT_FALSE(source->is_created_);
|
|
EXPECT_TRUE(source->can_publish());
|
|
|
|
// Call set_stream_created - should not trigger event handlers
|
|
source->set_stream_created();
|
|
|
|
// Verify state changed but handlers not called
|
|
EXPECT_TRUE(source->is_created_);
|
|
EXPECT_FALSE(source->can_publish());
|
|
EXPECT_EQ(0, handler1->on_unpublish_count_);
|
|
EXPECT_EQ(0, handler1->on_consumers_finished_count_);
|
|
EXPECT_EQ(0, handler2->on_unpublish_count_);
|
|
EXPECT_EQ(0, handler2->on_consumers_finished_count_);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourcePublishStreamWithoutConsumerDestroy)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Don't set publish stream (leave as NULL)
|
|
EXPECT_TRUE(source->publish_stream() == NULL);
|
|
|
|
// Create a mock event handler
|
|
SrsUniquePtr<MockRtcSourceEventHandler> handler(new MockRtcSourceEventHandler());
|
|
source->rtc_source_subscribe(handler.get());
|
|
|
|
// Create and destroy consumer
|
|
ISrsRtcConsumer *consumer = NULL;
|
|
HELPER_EXPECT_SUCCESS(source->create_consumer(consumer));
|
|
EXPECT_EQ(1, (int)source->consumers_.size());
|
|
|
|
// Destroy consumer - should not trigger event handler since publish_stream is NULL
|
|
source->on_consumer_destroy(consumer);
|
|
EXPECT_EQ(0, (int)source->consumers_.size());
|
|
EXPECT_EQ(0, handler->on_consumers_finished_count_);
|
|
|
|
// Clean up
|
|
srs_freep(consumer);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourcePublishStreamMultipleChanges)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create multiple mock publish streams
|
|
SrsUniquePtr<MockRtcPublishStream> publish_stream1(new MockRtcPublishStream());
|
|
SrsUniquePtr<MockRtcPublishStream> publish_stream2(new MockRtcPublishStream());
|
|
SrsUniquePtr<MockRtcPublishStream> publish_stream3(new MockRtcPublishStream());
|
|
|
|
SrsContextId cid1, cid2, cid3;
|
|
cid1.set_value("context-1");
|
|
cid2.set_value("context-2");
|
|
cid3.set_value("context-3");
|
|
|
|
publish_stream1->set_context_id(cid1);
|
|
publish_stream2->set_context_id(cid2);
|
|
publish_stream3->set_context_id(cid3);
|
|
|
|
// Test multiple changes
|
|
source->set_publish_stream(publish_stream1.get());
|
|
EXPECT_EQ(publish_stream1.get(), source->publish_stream());
|
|
EXPECT_TRUE(source->publish_stream()->context_id().compare(cid1) == 0);
|
|
|
|
source->set_publish_stream(publish_stream2.get());
|
|
EXPECT_EQ(publish_stream2.get(), source->publish_stream());
|
|
EXPECT_TRUE(source->publish_stream()->context_id().compare(cid2) == 0);
|
|
|
|
source->set_publish_stream(publish_stream3.get());
|
|
EXPECT_EQ(publish_stream3.get(), source->publish_stream());
|
|
EXPECT_TRUE(source->publish_stream()->context_id().compare(cid3) == 0);
|
|
|
|
// Set back to NULL
|
|
source->set_publish_stream(NULL);
|
|
EXPECT_TRUE(source->publish_stream() == NULL);
|
|
|
|
// Set to first stream again
|
|
source->set_publish_stream(publish_stream1.get());
|
|
EXPECT_EQ(publish_stream1.get(), source->publish_stream());
|
|
EXPECT_TRUE(source->publish_stream()->context_id().compare(cid1) == 0);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourcePublishStreamKeyframeRequest)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create a mock publish stream
|
|
SrsUniquePtr<MockRtcPublishStream> publish_stream(new MockRtcPublishStream());
|
|
SrsContextId test_cid;
|
|
test_cid.set_value("keyframe-test-context");
|
|
publish_stream->set_context_id(test_cid);
|
|
|
|
// Set the publish stream
|
|
source->set_publish_stream(publish_stream.get());
|
|
|
|
// Verify initial state
|
|
EXPECT_EQ(0, publish_stream->request_keyframe_count_);
|
|
EXPECT_EQ(0u, publish_stream->last_keyframe_ssrc_);
|
|
|
|
// Test keyframe request functionality through publish stream
|
|
uint32_t test_ssrc = 12345;
|
|
SrsContextId request_cid;
|
|
request_cid.set_value("request-context");
|
|
|
|
source->publish_stream()->request_keyframe(test_ssrc, request_cid);
|
|
|
|
// Verify the request was recorded
|
|
EXPECT_EQ(1, publish_stream->request_keyframe_count_);
|
|
EXPECT_EQ(test_ssrc, publish_stream->last_keyframe_ssrc_);
|
|
EXPECT_TRUE(publish_stream->last_keyframe_cid_.compare(request_cid) == 0);
|
|
|
|
// Test multiple requests
|
|
source->publish_stream()->request_keyframe(54321, test_cid);
|
|
EXPECT_EQ(2, publish_stream->request_keyframe_count_);
|
|
EXPECT_EQ(54321u, publish_stream->last_keyframe_ssrc_);
|
|
EXPECT_TRUE(publish_stream->last_keyframe_cid_.compare(test_cid) == 0);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourcePublishStreamContextIdAccess)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create a mock publish stream with specific context ID
|
|
SrsUniquePtr<MockRtcPublishStream> publish_stream(new MockRtcPublishStream());
|
|
SrsContextId original_cid;
|
|
original_cid.set_value("original-context-id");
|
|
publish_stream->set_context_id(original_cid);
|
|
|
|
// Set the publish stream
|
|
source->set_publish_stream(publish_stream.get());
|
|
|
|
// Test context ID access
|
|
const SrsContextId &retrieved_cid = source->publish_stream()->context_id();
|
|
EXPECT_TRUE(retrieved_cid.compare(original_cid) == 0);
|
|
EXPECT_STREQ("original-context-id", retrieved_cid.c_str());
|
|
|
|
// Change context ID and verify
|
|
SrsContextId new_cid;
|
|
new_cid.set_value("updated-context-id");
|
|
publish_stream->set_context_id(new_cid);
|
|
|
|
const SrsContextId &updated_cid = source->publish_stream()->context_id();
|
|
EXPECT_TRUE(updated_cid.compare(new_cid) == 0);
|
|
EXPECT_STREQ("updated-context-id", updated_cid.c_str());
|
|
EXPECT_TRUE(updated_cid.compare(original_cid) != 0); // Should be different from original
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourcePublishStreamNullSafety)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Initially should be NULL
|
|
EXPECT_TRUE(source->publish_stream() == NULL);
|
|
|
|
// Setting NULL should be safe
|
|
source->set_publish_stream(NULL);
|
|
EXPECT_TRUE(source->publish_stream() == NULL);
|
|
|
|
// Create and set a publish stream
|
|
SrsUniquePtr<MockRtcPublishStream> publish_stream(new MockRtcPublishStream());
|
|
source->set_publish_stream(publish_stream.get());
|
|
EXPECT_TRUE(source->publish_stream() != NULL);
|
|
|
|
// Set back to NULL
|
|
source->set_publish_stream(NULL);
|
|
EXPECT_TRUE(source->publish_stream() == NULL);
|
|
|
|
// Multiple NULL sets should be safe
|
|
source->set_publish_stream(NULL);
|
|
source->set_publish_stream(NULL);
|
|
EXPECT_TRUE(source->publish_stream() == NULL);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnRtpNoConsumers)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create a test RTP packet
|
|
SrsUniquePtr<SrsRtpPacket> pkt(new SrsRtpPacket());
|
|
pkt->header_.set_sequence(100);
|
|
pkt->header_.set_timestamp(1000);
|
|
pkt->header_.set_ssrc(12345);
|
|
|
|
// Set mock circuit breaker
|
|
MockCircuitBreaker mock_circuit_breaker;
|
|
source->circuit_breaker_ = &mock_circuit_breaker;
|
|
|
|
// Test: on_rtp with no consumers should succeed
|
|
HELPER_EXPECT_SUCCESS(source->on_rtp(pkt.get()));
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnRtpWithConsumers)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create mock consumers
|
|
MockRtcConsumer *consumer1 = new MockRtcConsumer();
|
|
MockRtcConsumer *consumer2 = new MockRtcConsumer();
|
|
MockRtcConsumer *consumer3 = new MockRtcConsumer();
|
|
|
|
// Add consumers to source manually (simulating create_consumer)
|
|
source->consumers_.push_back(consumer1);
|
|
source->consumers_.push_back(consumer2);
|
|
source->consumers_.push_back(consumer3);
|
|
|
|
// Create a test RTP packet
|
|
SrsUniquePtr<SrsRtpPacket> pkt(new SrsRtpPacket());
|
|
pkt->header_.set_sequence(100);
|
|
pkt->header_.set_timestamp(1000);
|
|
pkt->header_.set_ssrc(12345);
|
|
|
|
// Set mock circuit breaker
|
|
MockCircuitBreaker mock_circuit_breaker;
|
|
source->circuit_breaker_ = &mock_circuit_breaker;
|
|
|
|
// Test: on_rtp should enqueue packet to all consumers
|
|
HELPER_EXPECT_SUCCESS(source->on_rtp(pkt.get()));
|
|
|
|
// Verify all consumers received the packet
|
|
EXPECT_EQ(1, consumer1->enqueue_count_);
|
|
EXPECT_EQ(1, consumer2->enqueue_count_);
|
|
EXPECT_EQ(1, consumer3->enqueue_count_);
|
|
|
|
// Clean up consumers
|
|
srs_freep(consumer1);
|
|
srs_freep(consumer2);
|
|
srs_freep(consumer3);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnRtpCircuitBreakerDying)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create mock consumers
|
|
MockRtcConsumer *consumer1 = new MockRtcConsumer();
|
|
MockRtcConsumer *consumer2 = new MockRtcConsumer();
|
|
source->consumers_.push_back(consumer1);
|
|
source->consumers_.push_back(consumer2);
|
|
|
|
// Create a test RTP packet
|
|
SrsUniquePtr<SrsRtpPacket> pkt(new SrsRtpPacket());
|
|
pkt->header_.set_sequence(100);
|
|
pkt->header_.set_timestamp(1000);
|
|
pkt->header_.set_ssrc(12345);
|
|
|
|
// Create mock circuit breaker
|
|
MockCircuitBreaker mock_circuit_breaker;
|
|
mock_circuit_breaker.set_hybrid_dying_water_level(true); // Circuit breaker is dying
|
|
source->circuit_breaker_ = &mock_circuit_breaker;
|
|
|
|
// Test: on_rtp should drop packet when circuit breaker is dying
|
|
HELPER_EXPECT_SUCCESS(source->on_rtp(pkt.get()));
|
|
|
|
// Verify packet was dropped (consumers should not receive it)
|
|
EXPECT_EQ(0, consumer1->enqueue_count_);
|
|
EXPECT_EQ(0, consumer2->enqueue_count_);
|
|
|
|
// Clean up consumers
|
|
srs_freep(consumer1);
|
|
srs_freep(consumer2);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceOnRtpConsumerEnqueueError)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source and initialize
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create mock consumers - one will fail
|
|
MockRtcConsumer *consumer1 = new MockRtcConsumer();
|
|
MockRtcConsumer *consumer2 = new MockRtcConsumer();
|
|
MockRtcConsumer *consumer3 = new MockRtcConsumer();
|
|
|
|
// Set consumer2 to return error on enqueue
|
|
srs_error_t test_error = srs_error_new(ERROR_SYSTEM_ASSERT_FAILED, "test enqueue error");
|
|
consumer2->set_enqueue_error(test_error);
|
|
srs_freep(test_error);
|
|
|
|
source->consumers_.push_back(consumer1);
|
|
source->consumers_.push_back(consumer2);
|
|
source->consumers_.push_back(consumer3);
|
|
|
|
// Create a test RTP packet
|
|
SrsUniquePtr<SrsRtpPacket> pkt(new SrsRtpPacket());
|
|
pkt->header_.set_sequence(100);
|
|
pkt->header_.set_timestamp(1000);
|
|
pkt->header_.set_ssrc(12345);
|
|
|
|
// Set mock circuit breaker
|
|
MockCircuitBreaker mock_circuit_breaker;
|
|
source->circuit_breaker_ = &mock_circuit_breaker;
|
|
|
|
// Test: on_rtp should fail when consumer enqueue fails
|
|
HELPER_EXPECT_FAILED(source->on_rtp(pkt.get()));
|
|
|
|
// Verify first consumer received packet, but processing stopped at second consumer
|
|
EXPECT_EQ(1, consumer1->enqueue_count_);
|
|
EXPECT_EQ(1, consumer2->enqueue_count_); // Called but failed
|
|
EXPECT_EQ(0, consumer3->enqueue_count_); // Never reached due to error
|
|
|
|
// Clean up consumers
|
|
srs_freep(consumer1);
|
|
srs_freep(consumer2);
|
|
srs_freep(consumer3);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceGetTrackDescNoStreamDesc)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Clear the default stream description created by init_for_play_before_publishing
|
|
source->set_stream_desc(NULL);
|
|
|
|
// Test: get_track_desc with no stream description should return empty vector
|
|
std::vector<SrsRtcTrackDescription *> audio_tracks = source->get_track_desc("audio", "opus");
|
|
EXPECT_TRUE(audio_tracks.empty());
|
|
|
|
std::vector<SrsRtcTrackDescription *> video_tracks = source->get_track_desc("video", "H264");
|
|
EXPECT_TRUE(video_tracks.empty());
|
|
|
|
std::vector<SrsRtcTrackDescription *> all_video_tracks = source->get_track_desc("video", "");
|
|
EXPECT_TRUE(all_video_tracks.empty());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceGetTrackDescAudioTrackMatching)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create stream description with audio track
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
stream_desc->id_ = "test-stream";
|
|
|
|
// Create audio track description with Opus codec
|
|
SrsRtcTrackDescription *audio_track = new SrsRtcTrackDescription();
|
|
audio_track->type_ = "audio";
|
|
audio_track->id_ = "audio-track-1";
|
|
audio_track->ssrc_ = 12345;
|
|
audio_track->direction_ = "sendrecv";
|
|
audio_track->media_ = new SrsAudioPayload(111, "opus", 48000, 2);
|
|
stream_desc->audio_track_desc_ = audio_track;
|
|
|
|
// Set stream description
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Test: get_track_desc for matching audio codec should return the track
|
|
std::vector<SrsRtcTrackDescription *> opus_tracks = source->get_track_desc("audio", "opus");
|
|
EXPECT_EQ(1, (int)opus_tracks.size());
|
|
// Note: set_stream_desc creates a copy, so we check properties instead of pointer equality
|
|
EXPECT_EQ("audio", opus_tracks[0]->type_);
|
|
EXPECT_EQ("audio-track-1", opus_tracks[0]->id_);
|
|
EXPECT_EQ(12345u, opus_tracks[0]->ssrc_);
|
|
|
|
// Test: get_track_desc for non-matching audio codec should return empty
|
|
std::vector<SrsRtcTrackDescription *> aac_tracks = source->get_track_desc("audio", "AAC");
|
|
EXPECT_TRUE(aac_tracks.empty());
|
|
|
|
// Test: get_track_desc for non-matching audio codec should return empty
|
|
std::vector<SrsRtcTrackDescription *> mp3_tracks = source->get_track_desc("audio", "MP3");
|
|
EXPECT_TRUE(mp3_tracks.empty());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceGetTrackDescAudioTrackNoAudioTrack)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create stream description without audio track
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
stream_desc->id_ = "test-stream";
|
|
stream_desc->audio_track_desc_ = NULL; // No audio track
|
|
|
|
// Set stream description
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Test: get_track_desc for audio should return empty when no audio track exists
|
|
std::vector<SrsRtcTrackDescription *> opus_tracks = source->get_track_desc("audio", "opus");
|
|
EXPECT_TRUE(opus_tracks.empty());
|
|
|
|
std::vector<SrsRtcTrackDescription *> aac_tracks = source->get_track_desc("audio", "AAC");
|
|
EXPECT_TRUE(aac_tracks.empty());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceGetTrackDescVideoTrackMatching)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create stream description with video tracks
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
stream_desc->id_ = "test-stream";
|
|
|
|
// Create H.264 video track description
|
|
SrsRtcTrackDescription *h264_track = new SrsRtcTrackDescription();
|
|
h264_track->type_ = "video";
|
|
h264_track->id_ = "video-h264-track";
|
|
h264_track->ssrc_ = 54321;
|
|
h264_track->direction_ = "sendrecv";
|
|
h264_track->media_ = new SrsVideoPayload(102, "H264", 90000);
|
|
stream_desc->video_track_descs_.push_back(h264_track);
|
|
|
|
// Create H.265 video track description
|
|
SrsRtcTrackDescription *h265_track = new SrsRtcTrackDescription();
|
|
h265_track->type_ = "video";
|
|
h265_track->id_ = "video-h265-track";
|
|
h265_track->ssrc_ = 54322;
|
|
h265_track->direction_ = "sendrecv";
|
|
h265_track->media_ = new SrsVideoPayload(49, "H265", 90000);
|
|
stream_desc->video_track_descs_.push_back(h265_track);
|
|
|
|
// Set stream description
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Test: get_track_desc for H264 should return the H264 track
|
|
std::vector<SrsRtcTrackDescription *> h264_tracks = source->get_track_desc("video", "H264");
|
|
EXPECT_EQ(1, (int)h264_tracks.size());
|
|
// Note: set_stream_desc creates a copy, so we check properties instead of pointer equality
|
|
EXPECT_EQ("video", h264_tracks[0]->type_);
|
|
EXPECT_EQ("video-h264-track", h264_tracks[0]->id_);
|
|
EXPECT_EQ(54321u, h264_tracks[0]->ssrc_);
|
|
|
|
// Test: get_track_desc for H265 should return the H265 track
|
|
std::vector<SrsRtcTrackDescription *> h265_tracks = source->get_track_desc("video", "H265");
|
|
EXPECT_EQ(1, (int)h265_tracks.size());
|
|
// Note: set_stream_desc creates a copy, so we check properties instead of pointer equality
|
|
EXPECT_EQ("video", h265_tracks[0]->type_);
|
|
EXPECT_EQ("video-h265-track", h265_tracks[0]->id_);
|
|
EXPECT_EQ(54322u, h265_tracks[0]->ssrc_);
|
|
|
|
// Test: get_track_desc for non-matching video codec should return empty
|
|
std::vector<SrsRtcTrackDescription *> vp8_tracks = source->get_track_desc("video", "VP8");
|
|
EXPECT_TRUE(vp8_tracks.empty());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceGetTrackDescVideoTrackEmptyMediaName)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create stream description with multiple video tracks
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
stream_desc->id_ = "test-stream";
|
|
|
|
// Create H.264 video track description
|
|
SrsRtcTrackDescription *h264_track = new SrsRtcTrackDescription();
|
|
h264_track->type_ = "video";
|
|
h264_track->id_ = "video-h264-track";
|
|
h264_track->ssrc_ = 11111;
|
|
h264_track->media_ = new SrsVideoPayload(102, "H264", 90000);
|
|
stream_desc->video_track_descs_.push_back(h264_track);
|
|
|
|
// Create H.265 video track description
|
|
SrsRtcTrackDescription *h265_track = new SrsRtcTrackDescription();
|
|
h265_track->type_ = "video";
|
|
h265_track->id_ = "video-h265-track";
|
|
h265_track->ssrc_ = 22222;
|
|
h265_track->media_ = new SrsVideoPayload(49, "H265", 90000);
|
|
stream_desc->video_track_descs_.push_back(h265_track);
|
|
|
|
// Set stream description
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Test: get_track_desc with empty media_name should return all video tracks
|
|
std::vector<SrsRtcTrackDescription *> all_video_tracks = source->get_track_desc("video", "");
|
|
EXPECT_EQ(2, (int)all_video_tracks.size());
|
|
// Note: set_stream_desc creates copies, so we check properties instead of pointer equality
|
|
EXPECT_EQ("video-h264-track", all_video_tracks[0]->id_);
|
|
EXPECT_EQ("video-h265-track", all_video_tracks[1]->id_);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceGetTrackDescVideoTrackNoVideoTracks)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create stream description without video tracks
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
stream_desc->id_ = "test-stream";
|
|
// video_track_descs_ is empty by default
|
|
|
|
// Set stream description
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Test: get_track_desc for video should return empty when no video tracks exist
|
|
std::vector<SrsRtcTrackDescription *> h264_tracks = source->get_track_desc("video", "H264");
|
|
EXPECT_TRUE(h264_tracks.empty());
|
|
|
|
std::vector<SrsRtcTrackDescription *> all_video_tracks = source->get_track_desc("video", "");
|
|
EXPECT_TRUE(all_video_tracks.empty());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceGetTrackDescMixedAudioVideoTracks)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create stream description with both audio and video tracks
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
stream_desc->id_ = "test-stream";
|
|
|
|
// Create audio track description
|
|
SrsRtcTrackDescription *audio_track = new SrsRtcTrackDescription();
|
|
audio_track->type_ = "audio";
|
|
audio_track->id_ = "audio-opus-track";
|
|
audio_track->ssrc_ = 33333;
|
|
audio_track->media_ = new SrsAudioPayload(111, "opus", 48000, 2);
|
|
stream_desc->audio_track_desc_ = audio_track;
|
|
|
|
// Create multiple video track descriptions
|
|
SrsRtcTrackDescription *h264_track = new SrsRtcTrackDescription();
|
|
h264_track->type_ = "video";
|
|
h264_track->id_ = "video-h264-track";
|
|
h264_track->ssrc_ = 44444;
|
|
h264_track->media_ = new SrsVideoPayload(102, "H264", 90000);
|
|
stream_desc->video_track_descs_.push_back(h264_track);
|
|
|
|
SrsRtcTrackDescription *h265_track = new SrsRtcTrackDescription();
|
|
h265_track->type_ = "video";
|
|
h265_track->id_ = "video-h265-track";
|
|
h265_track->ssrc_ = 55555;
|
|
h265_track->media_ = new SrsVideoPayload(49, "H265", 90000);
|
|
stream_desc->video_track_descs_.push_back(h265_track);
|
|
|
|
// Set stream description
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Test: get_track_desc for audio should return only audio track
|
|
std::vector<SrsRtcTrackDescription *> opus_tracks = source->get_track_desc("audio", "opus");
|
|
EXPECT_EQ(1, (int)opus_tracks.size());
|
|
// Note: set_stream_desc creates copies, so we check properties instead of pointer equality
|
|
EXPECT_EQ("audio", opus_tracks[0]->type_);
|
|
EXPECT_EQ("audio-opus-track", opus_tracks[0]->id_);
|
|
|
|
// Test: get_track_desc for video should return only matching video tracks
|
|
std::vector<SrsRtcTrackDescription *> h264_tracks = source->get_track_desc("video", "H264");
|
|
EXPECT_EQ(1, (int)h264_tracks.size());
|
|
EXPECT_EQ("video", h264_tracks[0]->type_);
|
|
EXPECT_EQ("video-h264-track", h264_tracks[0]->id_);
|
|
|
|
std::vector<SrsRtcTrackDescription *> h265_tracks = source->get_track_desc("video", "H265");
|
|
EXPECT_EQ(1, (int)h265_tracks.size());
|
|
EXPECT_EQ("video", h265_tracks[0]->type_);
|
|
EXPECT_EQ("video-h265-track", h265_tracks[0]->id_);
|
|
|
|
// Test: get_track_desc for all video tracks
|
|
std::vector<SrsRtcTrackDescription *> all_video_tracks = source->get_track_desc("video", "");
|
|
EXPECT_EQ(2, (int)all_video_tracks.size());
|
|
EXPECT_EQ("video-h264-track", all_video_tracks[0]->id_);
|
|
EXPECT_EQ("video-h265-track", all_video_tracks[1]->id_);
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceGetTrackDescInvalidType)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create stream description with both audio and video tracks
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
stream_desc->id_ = "test-stream";
|
|
|
|
// Create audio track
|
|
SrsRtcTrackDescription *audio_track = new SrsRtcTrackDescription();
|
|
audio_track->type_ = "audio";
|
|
audio_track->media_ = new SrsAudioPayload(111, "opus", 48000, 2);
|
|
stream_desc->audio_track_desc_ = audio_track;
|
|
|
|
// Create video track
|
|
SrsRtcTrackDescription *video_track = new SrsRtcTrackDescription();
|
|
video_track->type_ = "video";
|
|
video_track->media_ = new SrsVideoPayload(102, "H264", 90000);
|
|
stream_desc->video_track_descs_.push_back(video_track);
|
|
|
|
// Set stream description
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Test: get_track_desc with invalid type should return empty
|
|
std::vector<SrsRtcTrackDescription *> invalid_tracks = source->get_track_desc("invalid", "opus");
|
|
EXPECT_TRUE(invalid_tracks.empty());
|
|
|
|
std::vector<SrsRtcTrackDescription *> data_tracks = source->get_track_desc("data", "H264");
|
|
EXPECT_TRUE(data_tracks.empty());
|
|
|
|
std::vector<SrsRtcTrackDescription *> empty_type_tracks = source->get_track_desc("", "opus");
|
|
EXPECT_TRUE(empty_type_tracks.empty());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceGetTrackDescCaseSensitivity)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create stream description with tracks
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
stream_desc->id_ = "test-stream";
|
|
|
|
// Create audio track
|
|
SrsRtcTrackDescription *audio_track = new SrsRtcTrackDescription();
|
|
audio_track->type_ = "audio";
|
|
audio_track->media_ = new SrsAudioPayload(111, "opus", 48000, 2);
|
|
stream_desc->audio_track_desc_ = audio_track;
|
|
|
|
// Create video track
|
|
SrsRtcTrackDescription *video_track = new SrsRtcTrackDescription();
|
|
video_track->type_ = "video";
|
|
video_track->media_ = new SrsVideoPayload(102, "H264", 90000);
|
|
stream_desc->video_track_descs_.push_back(video_track);
|
|
|
|
// Set stream description
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Test: type parameter is case sensitive
|
|
std::vector<SrsRtcTrackDescription *> audio_upper = source->get_track_desc("AUDIO", "opus");
|
|
EXPECT_TRUE(audio_upper.empty());
|
|
|
|
std::vector<SrsRtcTrackDescription *> video_upper = source->get_track_desc("VIDEO", "H264");
|
|
EXPECT_TRUE(video_upper.empty());
|
|
|
|
// Test: media_name parameter is case insensitive (codec string matching converts to uppercase)
|
|
std::vector<SrsRtcTrackDescription *> opus_upper = source->get_track_desc("audio", "OPUS");
|
|
EXPECT_EQ(1, (int)opus_upper.size()); // Should work due to case insensitive matching
|
|
|
|
std::vector<SrsRtcTrackDescription *> h264_lower = source->get_track_desc("video", "h264");
|
|
EXPECT_EQ(1, (int)h264_lower.size()); // Should work due to case insensitive matching
|
|
|
|
// Test: original case should also work
|
|
std::vector<SrsRtcTrackDescription *> opus_correct = source->get_track_desc("audio", "opus");
|
|
EXPECT_EQ(1, (int)opus_correct.size());
|
|
|
|
std::vector<SrsRtcTrackDescription *> h264_correct = source->get_track_desc("video", "H264");
|
|
EXPECT_EQ(1, (int)h264_correct.size());
|
|
}
|
|
|
|
VOID TEST(AppTest2, RtcSourceGetTrackDescMultipleMatchingVideoTracks)
|
|
{
|
|
srs_error_t err;
|
|
|
|
// Create a mock request
|
|
SrsUniquePtr<SrsRequest> req(new SrsRequest());
|
|
req->ip_ = "127.0.0.1";
|
|
req->vhost_ = "test.vhost";
|
|
req->app_ = "live";
|
|
req->stream_ = "test";
|
|
|
|
// Create RTC source
|
|
SrsUniquePtr<SrsRtcSource> source(new SrsRtcSource());
|
|
HELPER_EXPECT_SUCCESS(source->initialize(req.get()));
|
|
|
|
// Create stream description with multiple H264 video tracks
|
|
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
|
stream_desc->id_ = "test-stream";
|
|
|
|
// Create first H264 video track
|
|
SrsRtcTrackDescription *h264_track1 = new SrsRtcTrackDescription();
|
|
h264_track1->type_ = "video";
|
|
h264_track1->id_ = "video-h264-track-1";
|
|
h264_track1->ssrc_ = 88888;
|
|
h264_track1->media_ = new SrsVideoPayload(102, "H264", 90000);
|
|
stream_desc->video_track_descs_.push_back(h264_track1);
|
|
|
|
// Create second H264 video track
|
|
SrsRtcTrackDescription *h264_track2 = new SrsRtcTrackDescription();
|
|
h264_track2->type_ = "video";
|
|
h264_track2->id_ = "video-h264-track-2";
|
|
h264_track2->ssrc_ = 99999;
|
|
h264_track2->media_ = new SrsVideoPayload(103, "H264", 90000); // Different PT but same codec
|
|
stream_desc->video_track_descs_.push_back(h264_track2);
|
|
|
|
// Create H265 video track for contrast
|
|
SrsRtcTrackDescription *h265_track = new SrsRtcTrackDescription();
|
|
h265_track->type_ = "video";
|
|
h265_track->id_ = "video-h265-track";
|
|
h265_track->ssrc_ = 11111;
|
|
h265_track->media_ = new SrsVideoPayload(49, "H265", 90000);
|
|
stream_desc->video_track_descs_.push_back(h265_track);
|
|
|
|
// Set stream description
|
|
source->set_stream_desc(stream_desc.get());
|
|
|
|
// Test: get_track_desc for H264 should return both H264 tracks
|
|
std::vector<SrsRtcTrackDescription *> h264_tracks = source->get_track_desc("video", "H264");
|
|
EXPECT_EQ(2, (int)h264_tracks.size());
|
|
// Note: set_stream_desc creates copies, so we check properties instead of pointer equality
|
|
EXPECT_EQ("video-h264-track-1", h264_tracks[0]->id_);
|
|
EXPECT_EQ("video-h264-track-2", h264_tracks[1]->id_);
|
|
|
|
// Test: get_track_desc for H265 should return only the H265 track
|
|
std::vector<SrsRtcTrackDescription *> h265_tracks = source->get_track_desc("video", "H265");
|
|
EXPECT_EQ(1, (int)h265_tracks.size());
|
|
EXPECT_EQ("video-h265-track", h265_tracks[0]->id_);
|
|
|
|
// Test: get_track_desc with empty media_name should return all video tracks
|
|
std::vector<SrsRtcTrackDescription *> all_video_tracks = source->get_track_desc("video", "");
|
|
EXPECT_EQ(3, (int)all_video_tracks.size());
|
|
EXPECT_EQ("video-h264-track-1", all_video_tracks[0]->id_);
|
|
EXPECT_EQ("video-h264-track-2", all_video_tracks[1]->id_);
|
|
EXPECT_EQ("video-h265-track", all_video_tracks[2]->id_);
|
|
}
|