AI: Add workflow utest for rtc conn.

This commit is contained in:
OSSRS-AI 2025-10-17 17:07:09 -04:00 committed by winlin
parent 8b76e1f6d2
commit 054d3a3563
30 changed files with 1267 additions and 794 deletions

2
trunk/configure vendored
View File

@ -385,7 +385,7 @@ if [[ $SRS_UTEST == YES ]]; then
"srs_utest_protocol3" "srs_utest_app" "srs_utest_app2" "srs_utest_app3" "srs_utest_app4"
"srs_utest_app5" "srs_utest_app6" "srs_utest_app7" "srs_utest_app8" "srs_utest_app9"
"srs_utest_app10" "srs_utest_app11" "srs_utest_app15" "srs_utest_app16" "srs_utest_app17"
"srs_utest_mock" "srs_utest_rtc_playstream" "srs_utest_rtc_publishstream")
"srs_utest_mock" "srs_utest_rtc_playstream" "srs_utest_rtc_publishstream" "srs_utest_rtc_conn")
# Always include SRT utest
MODULE_FILES+=("srs_utest_srt")
if [[ $SRS_GB28181 == YES ]]; then

View File

@ -23,6 +23,7 @@
#ifdef SRS_RTSP
#include <srs_app_rtsp_source.hpp>
#endif
#include <srs_app_rtc_conn.hpp>
#include <srs_app_st.hpp>
#include <srs_kernel_file.hpp>
#include <srs_kernel_flv.hpp>
@ -32,6 +33,7 @@
#include <srs_kernel_utility.hpp>
#include <srs_protocol_http_client.hpp>
#include <srs_protocol_st.hpp>
#include <srs_protocol_utility.hpp>
ISrsAppFactory::ISrsAppFactory()
{
@ -189,6 +191,21 @@ ISrsIngesterFFMPEG *SrsAppFactory::create_ingester_ffmpeg()
return new SrsIngesterFFMPEG();
}
ISrsProtocolUtility *SrsAppFactory::create_protocol_utility()
{
return new SrsProtocolUtility();
}
ISrsRtcPublishStream *SrsAppFactory::create_rtc_publish_stream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expire, ISrsRtcPacketReceiver *receiver, const SrsContextId &cid)
{
return new SrsRtcPublishStream(exec, expire, receiver, cid);
}
ISrsRtcPlayStream *SrsAppFactory::create_rtc_play_stream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expire, ISrsRtcPacketSender *sender, const SrsContextId &cid)
{
return new SrsRtcPlayStream(exec, expire, sender, cid);
}
ISrsCoroutine *SrsAppFactory::create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid)
{
return kernel_factory_->create_coroutine(name, handler, cid);

View File

@ -42,6 +42,12 @@ class ISrsRtcConnection;
class ISrsExecRtcAsyncTask;
class ISrsFFMPEG;
class ISrsIngesterFFMPEG;
class ISrsProtocolUtility;
class ISrsRtcPublishStream;
class ISrsRtcPacketReceiver;
class ISrsExpire;
class ISrsRtcPlayStream;
class ISrsRtcPacketSender;
// The factory to create app objects.
class ISrsAppFactory : public ISrsKernelFactory
@ -81,6 +87,9 @@ public:
virtual ISrsRtcConnection *create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid) = 0;
virtual ISrsFFMPEG *create_ffmpeg(std::string ffmpeg_bin) = 0;
virtual ISrsIngesterFFMPEG *create_ingester_ffmpeg() = 0;
virtual ISrsProtocolUtility *create_protocol_utility() = 0;
virtual ISrsRtcPublishStream *create_rtc_publish_stream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expire, ISrsRtcPacketReceiver *receiver, const SrsContextId &cid) = 0;
virtual ISrsRtcPlayStream *create_rtc_play_stream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expire, ISrsRtcPacketSender *sender, const SrsContextId &cid) = 0;
};
// The factory to create app objects.
@ -125,6 +134,9 @@ public:
virtual ISrsRtcConnection *create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid);
virtual ISrsFFMPEG *create_ffmpeg(std::string ffmpeg_bin);
virtual ISrsIngesterFFMPEG *create_ingester_ffmpeg();
virtual ISrsProtocolUtility *create_protocol_utility();
virtual ISrsRtcPublishStream *create_rtc_publish_stream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expire, ISrsRtcPacketReceiver *receiver, const SrsContextId &cid);
virtual ISrsRtcPlayStream *create_rtc_play_stream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expire, ISrsRtcPacketSender *sender, const SrsContextId &cid);
public:
virtual ISrsCoroutine *create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid);

View File

@ -349,7 +349,7 @@ void SrsRtcPliWorker::request_keyframe(uint32_t ssrc, SrsContextId cid)
void SrsRtcPliWorker::stop()
{
wait_->signal();
if (trd_) {
trd_->stop();
}
@ -444,6 +444,14 @@ std::string SrsRtcAsyncCallOnStop::to_string()
return std::string("");
}
ISrsRtcPlayStream::ISrsRtcPlayStream()
{
}
ISrsRtcPlayStream::~ISrsRtcPlayStream()
{
}
SrsRtcPlayStream::SrsRtcPlayStream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expire, ISrsRtcPacketSender *sender, const SrsContextId &cid) : source_(new SrsRtcSource())
{
exec_ = exec;
@ -1167,6 +1175,14 @@ std::string SrsRtcAsyncCallOnUnpublish::to_string()
return std::string("");
}
ISrsRtcPublishStream::ISrsRtcPublishStream()
{
}
ISrsRtcPublishStream::~ISrsRtcPublishStream()
{
}
SrsRtcPublishStream::SrsRtcPublishStream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expire, ISrsRtcPacketReceiver *receiver, const SrsContextId &cid) : source_(new SrsRtcSource())
{
exec_ = exec;
@ -1197,6 +1213,10 @@ SrsRtcPublishStream::SrsRtcPublishStream(ISrsExecRtcAsyncTask *exec, ISrsExpire
timer_twcc_ = new SrsRtcPublishTwccTimer(this);
rtcp_twcc_ = new SrsRtcpTWCC();
cache_ssrc0_ = cache_ssrc1_ = cache_ssrc2_ = 0;
cache_is_audio0_ = cache_is_audio1_ = cache_is_audio2_ = false;
cache_track0_ = cache_track1_ = cache_track2_ = NULL;
stat_ = _srs_stat;
config_ = _srs_config;
rtc_sources_ = _srs_rtc_sources;
@ -1585,22 +1605,61 @@ srs_error_t SrsRtcPublishStream::do_on_rtp_plaintext(SrsRtpPacket *&pkt, SrsBuff
// For source to consume packet.
uint32_t ssrc = pkt->header_.get_ssrc();
SrsRtcAudioRecvTrack *audio_track = get_audio_track(ssrc);
SrsRtcVideoRecvTrack *video_track = get_video_track(ssrc);
if (audio_track) {
pkt->frame_type_ = SrsFrameTypeAudio;
if ((err = audio_track->on_rtp(source_, pkt)) != srs_success) {
return srs_error_wrap(err, "on audio");
// Try to find track from cache.
SrsRtcRecvTrack *track = NULL;
bool is_audio = true;
if (cache_ssrc0_ == ssrc) {
track = cache_track0_;
is_audio = cache_is_audio0_;
} else if (cache_ssrc1_ == ssrc) {
track = cache_track1_;
is_audio = cache_is_audio1_;
} else if (cache_ssrc2_ == ssrc) {
track = cache_track2_;
is_audio = cache_is_audio2_;
}
// Find by original tracks and build fast cache.
if (!track) {
track = get_audio_track(ssrc);
if (track) {
is_audio = true;
} else {
is_audio = false;
track = get_video_track(ssrc);
}
} else if (video_track) {
pkt->frame_type_ = SrsFrameTypeVideo;
if ((err = video_track->on_rtp(source_, pkt)) != srs_success) {
return srs_error_wrap(err, "on video");
if (track && !cache_ssrc2_) {
if (!cache_ssrc0_) {
cache_ssrc0_ = ssrc;
cache_is_audio0_ = is_audio;
cache_track0_ = track;
} else if (!cache_ssrc1_) {
cache_ssrc1_ = ssrc;
cache_is_audio1_ = is_audio;
cache_track1_ = track;
} else if (!cache_ssrc2_) {
cache_ssrc2_ = ssrc;
cache_is_audio2_ = is_audio;
cache_track2_ = track;
}
}
} else {
}
// Set the frame type.
pkt->frame_type_ = is_audio ? SrsFrameTypeAudio : SrsFrameTypeVideo;
// Ignore if no track found.
if (!track) {
return srs_error_new(ERROR_RTC_RTP, "unknown ssrc=%u", ssrc);
}
// Consume packet by track.
if ((err = track->on_rtp(source_, pkt)) != srs_success) {
return srs_error_wrap(err, "audio track, SSRC=%u, SEQ=%u", ssrc, pkt->header_.get_sequence());
}
// If circuit-breaker is enabled, disable nack.
if (circuit_breaker_->hybrid_critical_water_level()) {
++_srs_pps_snack4->sugar_;
@ -1609,16 +1668,8 @@ srs_error_t SrsRtcPublishStream::do_on_rtp_plaintext(SrsRtpPacket *&pkt, SrsBuff
// For NACK to handle packet.
// @remark Note that the pkt might be set to NULL.
if (nack_enabled_) {
if (audio_track) {
if ((err = audio_track->on_nack(&pkt)) != srs_success) {
return srs_error_wrap(err, "on nack");
}
} else if (video_track) {
if ((err = video_track->on_nack(&pkt)) != srs_success) {
return srs_error_wrap(err, "on nack");
}
}
if (nack_enabled_ && (err = track->on_nack(&pkt)) != srs_success) {
return srs_error_wrap(err, "on nack");
}
return err;
@ -1902,6 +1953,14 @@ ISrsRtcConnectionNackTimerHandler::~ISrsRtcConnectionNackTimerHandler()
{
}
ISrsRtcConnectionNackTimer::ISrsRtcConnectionNackTimer()
{
}
ISrsRtcConnectionNackTimer::~ISrsRtcConnectionNackTimer()
{
}
SrsRtcConnectionNackTimer::SrsRtcConnectionNackTimer(ISrsRtcConnectionNackTimerHandler *handler) : handler_(handler)
{
lock_ = srs_mutex_new();
@ -1977,6 +2036,8 @@ SrsRtcConnection::SrsRtcConnection(ISrsExecRtcAsyncTask *exec, const SrsContextI
conn_manager_ = _srs_conn_manager;
rtc_sources_ = _srs_rtc_sources;
config_ = _srs_config;
dtls_certificate_ = _srs_rtc_dtls_certificate;
app_factory_ = _srs_app_factory;
}
void SrsRtcConnection::assemble()
@ -1991,16 +2052,16 @@ SrsRtcConnection::~SrsRtcConnection()
srs_freep(timer_nack_);
// Cleanup publishers.
for (map<string, SrsRtcPublishStream *>::iterator it = publishers_.begin(); it != publishers_.end(); ++it) {
SrsRtcPublishStream *publisher = it->second;
for (map<string, ISrsRtcPublishStream *>::iterator it = publishers_.begin(); it != publishers_.end(); ++it) {
ISrsRtcPublishStream *publisher = it->second;
srs_freep(publisher);
}
publishers_.clear();
publishers_ssrc_map_.clear();
// Cleanup players.
for (map<string, SrsRtcPlayStream *>::iterator it = players_.begin(); it != players_.end(); ++it) {
SrsRtcPlayStream *player = it->second;
for (map<string, ISrsRtcPlayStream *>::iterator it = players_.begin(); it != players_.end(); ++it) {
ISrsRtcPlayStream *player = it->second;
srs_freep(player);
}
players_.clear();
@ -2027,6 +2088,8 @@ SrsRtcConnection::~SrsRtcConnection()
conn_manager_ = NULL;
rtc_sources_ = NULL;
config_ = NULL;
dtls_certificate_ = NULL;
app_factory_ = NULL;
}
void SrsRtcConnection::on_before_dispose(ISrsResource *c)
@ -2089,7 +2152,7 @@ string SrsRtcConnection::token()
return token_;
}
void SrsRtcConnection::set_publish_token(SrsSharedPtr<SrsStreamPublishToken> publish_token)
void SrsRtcConnection::set_publish_token(SrsSharedPtr<ISrsStreamPublishToken> publish_token)
{
publish_token_ = publish_token;
}
@ -2208,6 +2271,95 @@ srs_error_t SrsRtcConnection::add_player(SrsRtcUserConfig *ruc, SrsSdp &local_sd
return err;
}
srs_error_t SrsRtcConnection::generate_local_sdp(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, std::string &username)
{
srs_error_t err = srs_success;
SrsRand rand;
std::string local_pwd = ruc->req_->ice_pwd_.empty() ? rand.gen_str(32) : ruc->req_->ice_pwd_;
std::string local_ufrag = ruc->req_->ice_ufrag_.empty() ? rand.gen_str(8) : ruc->req_->ice_ufrag_;
// TODO: FIXME: Rename for a better name, it's not an username.
username = "";
while (true) {
username = local_ufrag + ":" + ruc->remote_sdp_.get_ice_ufrag();
if (!conn_manager_->find_by_name(username)) {
break;
}
// Username conflict, regenerate a new one.
local_ufrag = rand.gen_str(8);
}
local_sdp.set_ice_ufrag(local_ufrag);
local_sdp.set_ice_pwd(local_pwd);
local_sdp.set_fingerprint_algo("sha-256");
local_sdp.set_fingerprint(dtls_certificate_->get_fingerprint());
// We allows to mock the eip of server.
if (true) {
// TODO: Support multiple listen ports.
int udp_port = 0;
if (true) {
string udp_host;
string udp_hostport = config_->get_rtc_server_listens().at(0);
srs_net_split_for_listener(udp_hostport, udp_host, udp_port);
}
int tcp_port = 0;
if (true) {
string tcp_host;
string tcp_hostport = config_->get_rtc_server_tcp_listens().at(0);
srs_net_split_for_listener(tcp_hostport, tcp_host, tcp_port);
}
string protocol = config_->get_rtc_server_protocol();
SrsUniquePtr<ISrsProtocolUtility> utility(app_factory_->create_protocol_utility());
set<string> candidates = discover_candidates(utility.get(), config_, ruc);
for (set<string>::iterator it = candidates.begin(); it != candidates.end(); ++it) {
string hostname;
int uport = udp_port;
srs_net_split_hostport(*it, hostname, uport);
int tport = tcp_port;
srs_net_split_hostport(*it, hostname, tport);
if (protocol == "udp") {
local_sdp.add_candidate("udp", hostname, uport, "host");
} else if (protocol == "tcp") {
local_sdp.add_candidate("tcp", hostname, tport, "host");
} else {
local_sdp.add_candidate("udp", hostname, uport, "host");
local_sdp.add_candidate("tcp", hostname, tport, "host");
}
}
vector<string> v = vector<string>(candidates.begin(), candidates.end());
srs_trace("RTC: Use candidates %s, protocol=%s, tcp_port=%d, udp_port=%d",
srs_strings_join(v, ", ").c_str(), protocol.c_str(), tcp_port, udp_port);
}
// Setup the negotiate DTLS by config.
local_sdp.session_negotiate_ = local_sdp.session_config_;
// Setup the negotiate DTLS role.
if (ruc->remote_sdp_.get_dtls_role() == "active") {
local_sdp.session_negotiate_.dtls_role_ = "passive";
} else if (ruc->remote_sdp_.get_dtls_role() == "passive") {
local_sdp.session_negotiate_.dtls_role_ = "active";
} else if (ruc->remote_sdp_.get_dtls_role() == "actpass") {
local_sdp.session_negotiate_.dtls_role_ = local_sdp.session_config_.dtls_role_;
} else {
// @see: https://tools.ietf.org/html/rfc4145#section-4.1
// The default value of the setup attribute in an offer/answer exchange
// is 'active' in the offer and 'passive' in the answer.
local_sdp.session_negotiate_.dtls_role_ = "passive";
}
local_sdp.set_dtls_role(local_sdp.session_negotiate_.dtls_role_);
return err;
}
srs_error_t SrsRtcConnection::initialize(ISrsRequest *r, bool dtls, bool srtp, string username)
{
srs_error_t err = srs_success;
@ -2232,14 +2384,6 @@ srs_error_t SrsRtcConnection::initialize(ISrsRequest *r, bool dtls, bool srtp, s
return srs_error_wrap(err, "initialize timer nack");
}
if ((err = publisher_negotiator_->initialize(r)) != srs_success) {
return srs_error_wrap(err, "initialize publisher negotiator");
}
if ((err = player_negotiator_->initialize(r)) != srs_success) {
return srs_error_wrap(err, "initialize player negotiator");
}
srs_trace("RTC init session, user=%s, url=%s, encrypt=%u/%u, DTLS(role=%s, version=%s), timeout=%dms, nack=%d",
username.c_str(), r->get_stream_url().c_str(), dtls, srtp, cfg->dtls_role_.c_str(), cfg->dtls_version_.c_str(),
srsu2msi(session_timeout_), nack_enabled_);
@ -2319,11 +2463,11 @@ srs_error_t SrsRtcConnection::dispatch_rtcp(SrsRtcpCommon *rtcp)
}
// Find the publisher or player by SSRC, always try to got one.
SrsRtcPlayStream *player = NULL;
SrsRtcPublishStream *publisher = NULL;
ISrsRtcPlayStream *player = NULL;
ISrsRtcPublishStream *publisher = NULL;
if (true) {
uint32_t ssrc = required_publisher_ssrc ? required_publisher_ssrc : rtcp->get_ssrc();
map<uint32_t, SrsRtcPublishStream *>::iterator it = publishers_ssrc_map_.find(ssrc);
map<uint32_t, ISrsRtcPublishStream *>::iterator it = publishers_ssrc_map_.find(ssrc);
if (it != publishers_ssrc_map_.end()) {
publisher = it->second;
}
@ -2331,7 +2475,7 @@ srs_error_t SrsRtcConnection::dispatch_rtcp(SrsRtcpCommon *rtcp)
if (true) {
uint32_t ssrc = required_player_ssrc ? required_player_ssrc : rtcp->get_ssrc();
map<uint32_t, SrsRtcPlayStream *>::iterator it = players_ssrc_map_.find(ssrc);
map<uint32_t, ISrsRtcPlayStream *>::iterator it = players_ssrc_map_.find(ssrc);
if (it != players_ssrc_map_.end()) {
player = it->second;
}
@ -2373,7 +2517,7 @@ srs_error_t SrsRtcConnection::on_rtp_cipher(char *data, int nb_data)
{
srs_error_t err = srs_success;
SrsRtcPublishStream *publisher = NULL;
ISrsRtcPublishStream *publisher = NULL;
if ((err = find_publisher(data, nb_data, &publisher)) != srs_success) {
return srs_error_wrap(err, "find");
}
@ -2386,7 +2530,7 @@ srs_error_t SrsRtcConnection::on_rtp_plaintext(char *data, int nb_data)
{
srs_error_t err = srs_success;
SrsRtcPublishStream *publisher = NULL;
ISrsRtcPublishStream *publisher = NULL;
if ((err = find_publisher(data, nb_data, &publisher)) != srs_success) {
return srs_error_wrap(err, "find");
}
@ -2395,7 +2539,7 @@ srs_error_t SrsRtcConnection::on_rtp_plaintext(char *data, int nb_data)
return publisher->on_rtp_plaintext(data, nb_data);
}
srs_error_t SrsRtcConnection::find_publisher(char *buf, int size, SrsRtcPublishStream **ppublisher)
srs_error_t SrsRtcConnection::find_publisher(char *buf, int size, ISrsRtcPublishStream **ppublisher)
{
srs_error_t err = srs_success;
@ -2408,7 +2552,7 @@ srs_error_t SrsRtcConnection::find_publisher(char *buf, int size, SrsRtcPublishS
return srs_error_new(ERROR_RTC_NO_PUBLISHER, "invalid ssrc");
}
map<uint32_t, SrsRtcPublishStream *>::iterator it = publishers_ssrc_map_.find(ssrc);
map<uint32_t, ISrsRtcPublishStream *>::iterator it = publishers_ssrc_map_.find(ssrc);
if (it == publishers_ssrc_map_.end()) {
return srs_error_new(ERROR_RTC_NO_PUBLISHER, "no publisher for ssrc:%u", ssrc);
}
@ -2431,9 +2575,9 @@ srs_error_t SrsRtcConnection::on_dtls_handshake_done()
srsu2msi(session_timeout_));
// start all publisher
for (map<string, SrsRtcPublishStream *>::iterator it = publishers_.begin(); it != publishers_.end(); ++it) {
for (map<string, ISrsRtcPublishStream *>::iterator it = publishers_.begin(); it != publishers_.end(); ++it) {
string url = it->first;
SrsRtcPublishStream *publisher = it->second;
ISrsRtcPublishStream *publisher = it->second;
srs_trace("RTC: Publisher url=%s established", url.c_str());
@ -2443,9 +2587,9 @@ srs_error_t SrsRtcConnection::on_dtls_handshake_done()
}
// start all player
for (map<string, SrsRtcPlayStream *>::iterator it = players_.begin(); it != players_.end(); ++it) {
for (map<string, ISrsRtcPlayStream *>::iterator it = players_.begin(); it != players_.end(); ++it) {
string url = it->first;
SrsRtcPlayStream *player = it->second;
ISrsRtcPlayStream *player = it->second;
srs_trace("RTC: Subscriber url=%s established", url.c_str());
@ -2682,9 +2826,9 @@ srs_error_t SrsRtcConnection::do_check_send_nacks()
return err;
}
std::map<std::string, SrsRtcPublishStream *>::iterator it;
std::map<std::string, ISrsRtcPublishStream *>::iterator it;
for (it = publishers_.begin(); it != publishers_.end(); it++) {
SrsRtcPublishStream *publisher = it->second;
ISrsRtcPublishStream *publisher = it->second;
if ((err = publisher->check_send_nacks()) != srs_success) {
srs_warn("ignore nack err %s", srs_error_desc(err).c_str());
@ -2697,8 +2841,8 @@ srs_error_t SrsRtcConnection::do_check_send_nacks()
void SrsRtcConnection::simulate_nack_drop(int nn)
{
for (map<string, SrsRtcPublishStream *>::iterator it = publishers_.begin(); it != publishers_.end(); ++it) {
SrsRtcPublishStream *publisher = it->second;
for (map<string, ISrsRtcPublishStream *>::iterator it = publishers_.begin(); it != publishers_.end(); ++it) {
ISrsRtcPublishStream *publisher = it->second;
publisher->simulate_nack_drop(nn);
}
@ -2766,23 +2910,23 @@ void SrsRtcConnection::set_all_tracks_status(std::string stream_uri, bool is_pub
{
// For publishers.
if (is_publish) {
map<string, SrsRtcPublishStream *>::iterator it = publishers_.find(stream_uri);
map<string, ISrsRtcPublishStream *>::iterator it = publishers_.find(stream_uri);
if (publishers_.end() == it) {
return;
}
SrsRtcPublishStream *publisher = it->second;
ISrsRtcPublishStream *publisher = it->second;
publisher->set_all_tracks_status(status);
return;
}
// For players.
map<string, SrsRtcPlayStream *>::iterator it = players_.find(stream_uri);
map<string, ISrsRtcPlayStream *>::iterator it = players_.find(stream_uri);
if (players_.end() == it) {
return;
}
SrsRtcPlayStream *player = it->second;
ISrsRtcPlayStream *player = it->second;
player->set_all_tracks_status(status);
}
@ -2814,7 +2958,7 @@ srs_error_t SrsRtcConnection::create_player(ISrsRequest *req, std::map<uint32_t,
return err;
}
SrsRtcPlayStream *player = new SrsRtcPlayStream(exec_, this, this, _srs_context->get_id());
ISrsRtcPlayStream *player = app_factory_->create_rtc_play_stream(exec_, this, this, _srs_context->get_id());
if ((err = player->initialize(req, sub_relations)) != srs_success) {
srs_freep(player);
return srs_error_wrap(err, "SrsRtcPlayStream init");
@ -2824,7 +2968,7 @@ srs_error_t SrsRtcConnection::create_player(ISrsRequest *req, std::map<uint32_t,
// make map between ssrc and player for fastly searching
for (map<uint32_t, SrsRtcTrackDescription *>::iterator it = sub_relations.begin(); it != sub_relations.end(); ++it) {
SrsRtcTrackDescription *track_desc = it->second;
map<uint32_t, SrsRtcPlayStream *>::iterator it_player = players_ssrc_map_.find(track_desc->ssrc_);
map<uint32_t, ISrsRtcPlayStream *>::iterator it_player = players_ssrc_map_.find(track_desc->ssrc_);
if ((players_ssrc_map_.end() != it_player) && (player != it_player->second)) {
return srs_error_new(ERROR_RTC_DUPLICATED_SSRC, "duplicate ssrc %d, track id: %s",
track_desc->ssrc_, track_desc->id_.c_str());
@ -2882,7 +3026,7 @@ srs_error_t SrsRtcConnection::create_publisher(ISrsRequest *req, SrsRtcSourceDes
return err;
}
SrsRtcPublishStream *publisher = new SrsRtcPublishStream(exec_, this, this, _srs_context->get_id());
ISrsRtcPublishStream *publisher = app_factory_->create_rtc_publish_stream(exec_, this, this, _srs_context->get_id());
if ((err = publisher->initialize(req, stream_desc)) != srs_success) {
srs_freep(publisher);
return srs_error_wrap(err, "rtc publisher init");
@ -2945,44 +3089,44 @@ srs_error_t SrsRtcConnection::create_publisher(ISrsRequest *req, SrsRtcSourceDes
return err;
}
ISrsRtcPublisherNegotiator::ISrsRtcPublisherNegotiator()
{
}
ISrsRtcPublisherNegotiator::~ISrsRtcPublisherNegotiator()
{
}
SrsRtcPublisherNegotiator::SrsRtcPublisherNegotiator()
{
req_ = NULL;
config_ = _srs_config;
}
SrsRtcPublisherNegotiator::~SrsRtcPublisherNegotiator()
{
srs_freep(req_);
config_ = NULL;
}
srs_error_t SrsRtcPublisherNegotiator::initialize(ISrsRequest *r)
ISrsRtcPlayerNegotiator::ISrsRtcPlayerNegotiator()
{
}
ISrsRtcPlayerNegotiator::~ISrsRtcPlayerNegotiator()
{
req_ = r->copy();
return srs_success;
}
SrsRtcPlayerNegotiator::SrsRtcPlayerNegotiator()
{
req_ = NULL;
config_ = _srs_config;
rtc_sources_ = _srs_rtc_sources;
}
SrsRtcPlayerNegotiator::~SrsRtcPlayerNegotiator()
{
srs_freep(req_);
config_ = NULL;
rtc_sources_ = NULL;
}
srs_error_t SrsRtcPlayerNegotiator::initialize(ISrsRequest *r)
{
req_ = r->copy();
return srs_success;
}
bool srs_sdp_has_h264_profile(const SrsMediaPayloadType &payload_type, const string &profile)
{
srs_error_t err = srs_success;
@ -3564,9 +3708,9 @@ srs_error_t SrsRtcPlayerNegotiator::negotiate_play_capability(SrsRtcUserConfig *
SrsVideoCodecId prefer_codec = srs_video_codec_str2id(ruc->codec_);
if (prefer_codec == SrsVideoCodecIdReserved) {
// Get the source codec if not specified.
std::vector<SrsRtcTrackDescription *> track_descs = source->get_track_desc("video", "");
if (!track_descs.empty()) {
SrsRtcTrackDescription *first_track = track_descs.at(0);
std::vector<SrsRtcTrackDescription *> source_track_descs = source->get_track_desc("video", "");
if (!source_track_descs.empty()) {
SrsRtcTrackDescription *first_track = source_track_descs.at(0);
prefer_codec = srs_video_codec_str2id(first_track->media_->name_);
} else {
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no video track in source");

View File

@ -50,12 +50,15 @@ class SrsStatistic;
class SrsRtcUserConfig;
class SrsRtcSendTrack;
class SrsRtcPublishStream;
class ISrsRtcPublishStream;
class SrsEphemeralDelta;
class SrsRtcNetworks;
class ISrsRtcNetworks;
class SrsRtcUdpNetwork;
class ISrsRtcNetwork;
class SrsRtcTcpNetwork;
class SrsStreamPublishToken;
class ISrsStreamPublishToken;
class ISrsHttpHooks;
class ISrsAppConfig;
class ISrsStatistic;
@ -63,9 +66,14 @@ class ISrsExecRtcAsyncTask;
class ISrsSrtSourceManager;
class ISrsLiveSourceManager;
class SrsRtcPublisherNegotiator;
class ISrsRtcPublisherNegotiator;
class SrsRtcPlayerNegotiator;
class ISrsRtcPlayerNegotiator;
class ISrsAppFactory;
class ISrsCoroutine;
class ISrsDtlsCertificate;
class SrsRtcRecvTrack;
class ISrsRtcPlayStream;
const uint8_t kSR = 200;
const uint8_t kRR = 201;
@ -255,8 +263,24 @@ public:
virtual std::string to_string();
};
// The interface for RTC play stream.
class ISrsRtcPlayStream : public ISrsCoroutineHandler, public ISrsRtcPliWorkerHandler, public ISrsRtcSourceChangeCallback
{
public:
ISrsRtcPlayStream();
virtual ~ISrsRtcPlayStream();
public:
virtual srs_error_t initialize(ISrsRequest *request, std::map<uint32_t, SrsRtcTrackDescription *> sub_relations) = 0;
virtual srs_error_t start() = 0;
virtual void stop() = 0;
// Directly set the status of track, generally for init to set the default value.
virtual void set_all_tracks_status(bool status) = 0;
virtual srs_error_t on_rtcp(SrsRtcpCommon *rtcp) = 0;
};
// A RTC play stream, client pull and play stream from SRS.
class SrsRtcPlayStream : public ISrsCoroutineHandler, public ISrsRtcPliWorkerHandler, public ISrsRtcSourceChangeCallback
class SrsRtcPlayStream : public ISrsRtcPlayStream
{
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
@ -372,7 +396,7 @@ public:
};
// The RTC publish RTCP timer interface.
class ISrsRtcPublishRtcpTimer: public ISrsFastTimerHandler
class ISrsRtcPublishRtcpTimer : public ISrsFastTimerHandler
{
public:
ISrsRtcPublishRtcpTimer();
@ -408,7 +432,7 @@ SRS_DECLARE_PRIVATE: // clang-format on
};
// The RTC publish TWCC timer interface.
class ISrsRtcPublishTwccTimer: public ISrsFastTimerHandler
class ISrsRtcPublishTwccTimer : public ISrsFastTimerHandler
{
public:
ISrsRtcPublishTwccTimer();
@ -466,8 +490,30 @@ public:
virtual std::string to_string();
};
// A publish stream interface, for source to callback with.
class ISrsRtcPublishStream : public ISrsRtpPacketDecodeHandler, public ISrsRtcPliWorkerHandler, public ISrsRtcRtcpSender
{
public:
ISrsRtcPublishStream();
virtual ~ISrsRtcPublishStream();
public:
// Request keyframe(PLI) from publisher, for fresh consumer.
virtual void request_keyframe(uint32_t ssrc, SrsContextId cid) = 0;
// Get context id.
virtual const SrsContextId &context_id() = 0;
virtual srs_error_t initialize(ISrsRequest *req, SrsRtcSourceDescription *stream_desc) = 0;
virtual srs_error_t on_rtcp(SrsRtcpCommon *rtcp) = 0;
virtual srs_error_t on_rtp_cipher(char *buf, int nb_buf) = 0;
virtual srs_error_t on_rtp_plaintext(char *buf, int nb_buf) = 0;
virtual srs_error_t start() = 0;
virtual srs_error_t check_send_nacks() = 0;
virtual void simulate_nack_drop(int nn) = 0;
virtual void set_all_tracks_status(bool status) = 0;
};
// A RTC publish stream, client push and publish stream to SRS.
class SrsRtcPublishStream : public ISrsRtpPacketDecodeHandler, public ISrsRtcPublishStream, public ISrsRtcPliWorkerHandler, public ISrsRtcRtcpSender
class SrsRtcPublishStream : public ISrsRtcPublishStream
{
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
@ -521,10 +567,22 @@ SRS_DECLARE_PRIVATE: // clang-format on
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
// track vector
std::vector<SrsRtcAudioRecvTrack *>
audio_tracks_;
std::vector<SrsRtcAudioRecvTrack *> audio_tracks_;
std::vector<SrsRtcVideoRecvTrack *> video_tracks_;
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
// Fast cache for tracks.
uint32_t cache_ssrc0_;
bool cache_is_audio0_;
uint32_t cache_ssrc1_;
bool cache_is_audio1_;
uint32_t cache_ssrc2_;
bool cache_is_audio2_;
SrsRtcRecvTrack *cache_track0_;
SrsRtcRecvTrack *cache_track1_;
SrsRtcRecvTrack *cache_track2_;
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
int twcc_id_;
@ -610,8 +668,19 @@ public:
virtual srs_error_t do_check_send_nacks() = 0;
};
// The RTC connection nack timer interface.
class ISrsRtcConnectionNackTimer : public ISrsFastTimerHandler
{
public:
ISrsRtcConnectionNackTimer();
virtual ~ISrsRtcConnectionNackTimer();
public:
virtual srs_error_t initialize() = 0;
};
// A fast timer for conntion, for NACK feedback.
class SrsRtcConnectionNackTimer : public ISrsFastTimerHandler
class SrsRtcConnectionNackTimer : public ISrsRtcConnectionNackTimer
{
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
@ -682,6 +751,7 @@ public:
virtual srs_error_t add_publisher(SrsRtcUserConfig *ruc, SrsSdp &local_sdp) = 0;
virtual srs_error_t add_player(SrsRtcUserConfig *ruc, SrsSdp &local_sdp) = 0;
virtual void set_all_tracks_status(std::string stream_uri, bool is_publish, bool status) = 0;
virtual srs_error_t generate_local_sdp(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, std::string &username) = 0;
// SDP management.
virtual void set_remote_sdp(const SrsSdp &sdp) = 0;
virtual void set_local_sdp(const SrsSdp &sdp) = 0;
@ -691,7 +761,7 @@ public:
// Username and token access.
virtual std::string username() = 0;
virtual std::string token() = 0;
virtual void set_publish_token(SrsSharedPtr<SrsStreamPublishToken> publish_token) = 0;
virtual void set_publish_token(SrsSharedPtr<ISrsStreamPublishToken> publish_token) = 0;
// Simulation for testing.
virtual void simulate_nack_drop(int nn) = 0;
};
@ -710,13 +780,15 @@ SRS_DECLARE_PRIVATE: // clang-format on
ISrsResourceManager *conn_manager_;
ISrsRtcSourceManager *rtc_sources_;
ISrsAppConfig *config_;
ISrsDtlsCertificate *dtls_certificate_;
ISrsAppFactory *app_factory_;
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
SrsRtcConnectionNackTimer *timer_nack_;
ISrsRtcConnectionNackTimer *timer_nack_;
ISrsExecRtcAsyncTask *exec_;
SrsRtcPublisherNegotiator *publisher_negotiator_;
SrsRtcPlayerNegotiator *player_negotiator_;
ISrsRtcPublisherNegotiator *publisher_negotiator_;
ISrsRtcPlayerNegotiator *player_negotiator_;
public:
bool disposing_;
@ -729,14 +801,13 @@ SRS_DECLARE_PRIVATE: // clang-format on
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
// key: stream id
std::map<std::string, SrsRtcPlayStream *>
players_;
std::map<std::string, ISrsRtcPlayStream *> players_;
// key: player track's ssrc
std::map<uint32_t, SrsRtcPlayStream *> players_ssrc_map_;
std::map<uint32_t, ISrsRtcPlayStream *> players_ssrc_map_;
// key: stream id
std::map<std::string, SrsRtcPublishStream *> publishers_;
std::map<std::string, ISrsRtcPublishStream *> publishers_;
// key: publisher track's ssrc
std::map<uint32_t, SrsRtcPublishStream *> publishers_ssrc_map_;
std::map<uint32_t, ISrsRtcPublishStream *> publishers_ssrc_map_;
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
@ -745,7 +816,7 @@ SRS_DECLARE_PRIVATE: // clang-format on
// The random token to verify the WHIP DELETE request etc.
std::string token_;
// A group of networks, each has its own DTLS and SRTP context.
SrsRtcNetworks *networks_;
ISrsRtcNetworks *networks_;
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
@ -762,7 +833,7 @@ SRS_DECLARE_PRIVATE: // clang-format on
ISrsRequest *req_;
SrsSdp remote_sdp_;
SrsSdp local_sdp_;
SrsSharedPtr<SrsStreamPublishToken> publish_token_;
SrsSharedPtr<ISrsStreamPublishToken> publish_token_;
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
@ -800,7 +871,7 @@ public:
// Get the token for verify this session, for example, when delete session by WHIP API.
std::string token();
// Set the publish token for this session if publisher.
void set_publish_token(SrsSharedPtr<SrsStreamPublishToken> publish_token);
void set_publish_token(SrsSharedPtr<ISrsStreamPublishToken> publish_token);
public:
virtual ISrsKbpsDelta *delta();
@ -819,6 +890,7 @@ public:
public:
srs_error_t add_publisher(SrsRtcUserConfig *ruc, SrsSdp &local_sdp);
srs_error_t add_player(SrsRtcUserConfig *ruc, SrsSdp &local_sdp);
srs_error_t generate_local_sdp(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, std::string &username);
public:
// Before initialize, user must set the local SDP, which is used to inititlize DTLS.
@ -829,8 +901,7 @@ public:
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
// Decode the RTP header from buf, find the publisher by SSRC.
srs_error_t
find_publisher(char *buf, int size, SrsRtcPublishStream **ppublisher);
srs_error_t find_publisher(char *buf, int size, ISrsRtcPublishStream **ppublisher);
public:
srs_error_t on_rtcp(char *data, int nb_data);
@ -884,12 +955,23 @@ SRS_DECLARE_PRIVATE: // clang-format on
srs_error_t create_publisher(ISrsRequest *request, SrsRtcSourceDescription *stream_desc);
};
// Publisher negotiator interface.
class ISrsRtcPublisherNegotiator
{
public:
ISrsRtcPublisherNegotiator();
virtual ~ISrsRtcPublisherNegotiator();
public:
virtual srs_error_t negotiate_publish_capability(SrsRtcUserConfig *ruc, SrsRtcSourceDescription *stream_desc) = 0;
virtual srs_error_t generate_publish_local_sdp(ISrsRequest *req, SrsSdp &local_sdp, SrsRtcSourceDescription *stream_desc, bool unified_plan, bool audio_before_video) = 0;
};
// Negotiate via SDP exchange for WebRTC publisher.
class SrsRtcPublisherNegotiator
class SrsRtcPublisherNegotiator : public ISrsRtcPublisherNegotiator
{
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
ISrsRequest *req_;
ISrsAppConfig *config_;
public:
@ -897,20 +979,33 @@ public:
virtual ~SrsRtcPublisherNegotiator();
public:
virtual srs_error_t initialize(ISrsRequest *r);
// publish media capabilitiy negotiate
srs_error_t negotiate_publish_capability(SrsRtcUserConfig *ruc, SrsRtcSourceDescription *stream_desc);
srs_error_t generate_publish_local_sdp(ISrsRequest *req, SrsSdp &local_sdp, SrsRtcSourceDescription *stream_desc, bool unified_plan, bool audio_before_video);
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
srs_error_t generate_publish_local_sdp_for_audio(SrsSdp &local_sdp, SrsRtcSourceDescription *stream_desc);
srs_error_t generate_publish_local_sdp_for_video(SrsSdp &local_sdp, SrsRtcSourceDescription *stream_desc, bool unified_plan);
};
// Player negotiator interface.
class ISrsRtcPlayerNegotiator
{
public:
ISrsRtcPlayerNegotiator();
virtual ~ISrsRtcPlayerNegotiator();
public:
virtual srs_error_t negotiate_play_capability(SrsRtcUserConfig *ruc, std::map<uint32_t, SrsRtcTrackDescription *> &sub_relations) = 0;
virtual srs_error_t generate_play_local_sdp(ISrsRequest *req, SrsSdp &local_sdp, SrsRtcSourceDescription *stream_desc, bool unified_plan, bool audio_before_video) = 0;
};
// Negotiate via SDP exchange for WebRTC player.
class SrsRtcPlayerNegotiator
class SrsRtcPlayerNegotiator : public ISrsRtcPlayerNegotiator
{
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
ISrsRequest *req_;
ISrsAppConfig *config_;
ISrsRtcSourceManager *rtc_sources_;
@ -919,7 +1014,6 @@ public:
virtual ~SrsRtcPlayerNegotiator();
public:
virtual srs_error_t initialize(ISrsRequest *r);
// play media capabilitiy negotiate
// TODO: Use StreamDescription to negotiate and remove first negotiate_play_capability function
srs_error_t negotiate_play_capability(SrsRtcUserConfig *ruc, std::map<uint32_t, SrsRtcTrackDescription *> &sub_relations);

View File

@ -57,6 +57,12 @@ public:
virtual ~ISrsRtcNetworks();
public:
virtual srs_error_t initialize(SrsSessionConfig *cfg, bool dtls, bool srtp) = 0;
virtual void set_state(SrsRtcNetworkState state) = 0;
virtual ISrsRtcNetwork *udp() = 0;
virtual ISrsRtcNetwork *tcp() = 0;
virtual ISrsRtcNetwork *available() = 0;
virtual ISrsKbpsDelta *delta() = 0;
};
// A group of networks, each has its own DTLS and SRTP context.
@ -81,6 +87,7 @@ SRS_DECLARE_PRIVATE: // clang-format on
public:
SrsRtcNetworks(ISrsRtcConnection *conn);
virtual ~SrsRtcNetworks();
// DTLS transport functions.
public:
srs_error_t initialize(SrsSessionConfig *cfg, bool dtls, bool srtp);

View File

@ -219,7 +219,7 @@ srs_error_t api_server_as_candidates(ISrsAppConfig *config, string api, set<stri
return err;
}
set<string> discover_candidates(SrsProtocolUtility *utility, ISrsAppConfig *config, SrsRtcUserConfig *ruc)
set<string> discover_candidates(ISrsProtocolUtility *utility, ISrsAppConfig *config, SrsRtcUserConfig *ruc)
{
srs_error_t err = srs_success;
@ -363,7 +363,7 @@ srs_error_t SrsRtcSessionManager::create_rtc_session(SrsRtcUserConfig *ruc, SrsS
if (ruc->publish_ && (err = stream_publish_tokens_->acquire_token(req, publish_token_raw)) != srs_success) {
return srs_error_wrap(err, "acquire stream publish token");
}
SrsSharedPtr<SrsStreamPublishToken> publish_token(publish_token_raw);
SrsSharedPtr<ISrsStreamPublishToken> publish_token(publish_token_raw);
if (publish_token.get()) {
srs_trace("stream publish token acquired, type=rtc, url=%s", req->get_stream_url().c_str());
}
@ -416,87 +416,12 @@ srs_error_t SrsRtcSessionManager::do_create_rtc_session(SrsRtcUserConfig *ruc, S
// All tracks default as inactive, so we must enable them.
session->set_all_tracks_status(req->get_stream_url(), ruc->publish_, true);
SrsRand rand;
std::string local_pwd = ruc->req_->ice_pwd_.empty() ? rand.gen_str(32) : ruc->req_->ice_pwd_;
std::string local_ufrag = ruc->req_->ice_ufrag_.empty() ? rand.gen_str(8) : ruc->req_->ice_ufrag_;
// TODO: FIXME: Rename for a better name, it's not an username.
std::string username = "";
while (true) {
username = local_ufrag + ":" + ruc->remote_sdp_.get_ice_ufrag();
if (!conn_manager_->find_by_name(username)) {
break;
}
// Username conflict, regenerate a new one.
local_ufrag = rand.gen_str(8);
// Generate local SDP other fields.
string username;
if ((err = session->generate_local_sdp(ruc, local_sdp, username)) != srs_success) {
return srs_error_wrap(err, "generate local sdp");
}
local_sdp.set_ice_ufrag(local_ufrag);
local_sdp.set_ice_pwd(local_pwd);
local_sdp.set_fingerprint_algo("sha-256");
local_sdp.set_fingerprint(dtls_certificate_->get_fingerprint());
// We allows to mock the eip of server.
if (true) {
// TODO: Support multiple listen ports.
int udp_port = 0;
if (true) {
string udp_host;
string udp_hostport = config_->get_rtc_server_listens().at(0);
srs_net_split_for_listener(udp_hostport, udp_host, udp_port);
}
int tcp_port = 0;
if (true) {
string tcp_host;
string tcp_hostport = config_->get_rtc_server_tcp_listens().at(0);
srs_net_split_for_listener(tcp_hostport, tcp_host, tcp_port);
}
string protocol = config_->get_rtc_server_protocol();
SrsProtocolUtility utility;
set<string> candidates = discover_candidates(&utility, config_, ruc);
for (set<string>::iterator it = candidates.begin(); it != candidates.end(); ++it) {
string hostname;
int uport = udp_port;
srs_net_split_hostport(*it, hostname, uport);
int tport = tcp_port;
srs_net_split_hostport(*it, hostname, tport);
if (protocol == "udp") {
local_sdp.add_candidate("udp", hostname, uport, "host");
} else if (protocol == "tcp") {
local_sdp.add_candidate("tcp", hostname, tport, "host");
} else {
local_sdp.add_candidate("udp", hostname, uport, "host");
local_sdp.add_candidate("tcp", hostname, tport, "host");
}
}
vector<string> v = vector<string>(candidates.begin(), candidates.end());
srs_trace("RTC: Use candidates %s, protocol=%s, tcp_port=%d, udp_port=%d",
srs_strings_join(v, ", ").c_str(), protocol.c_str(), tcp_port, udp_port);
}
// Setup the negotiate DTLS by config.
local_sdp.session_negotiate_ = local_sdp.session_config_;
// Setup the negotiate DTLS role.
if (ruc->remote_sdp_.get_dtls_role() == "active") {
local_sdp.session_negotiate_.dtls_role_ = "passive";
} else if (ruc->remote_sdp_.get_dtls_role() == "passive") {
local_sdp.session_negotiate_.dtls_role_ = "active";
} else if (ruc->remote_sdp_.get_dtls_role() == "actpass") {
local_sdp.session_negotiate_.dtls_role_ = local_sdp.session_config_.dtls_role_;
} else {
// @see: https://tools.ietf.org/html/rfc4145#section-4.1
// The default value of the setup attribute in an offer/answer exchange
// is 'active' in the offer and 'passive' in the answer.
local_sdp.session_negotiate_.dtls_role_ = "passive";
}
local_sdp.set_dtls_role(local_sdp.session_negotiate_.dtls_role_);
session->set_remote_sdp(ruc->remote_sdp_);
// We must setup the local SDP, then initialize the session object.
session->set_local_sdp(local_sdp);

View File

@ -36,6 +36,7 @@ class ISrsRtcSourceManager;
class ISrsDtlsCertificate;
class ISrsAppConfig;
class ISrsAppFactory;
class ISrsProtocolUtility;
// The UDP black hole, for developer to use wireshark to catch plaintext packets.
// For example, server receive UDP packets at udp://8000, and forward the plaintext packet to black hole,
@ -93,7 +94,7 @@ public:
};
// Discover the candidates for RTC server.
extern std::set<std::string> discover_candidates(SrsProtocolUtility *utility, ISrsAppConfig *config, SrsRtcUserConfig *ruc);
extern std::set<std::string> discover_candidates(ISrsProtocolUtility *utility, ISrsAppConfig *config, SrsRtcUserConfig *ruc);
// The dns resolve utility, return the resolved ip address.
extern std::string srs_dns_resolve(std::string host, int &family);

View File

@ -379,14 +379,6 @@ SrsSharedPtr<SrsRtcSource> SrsRtcSourceManager::fetch(ISrsRequest *r)
SrsRtcSourceManager *_srs_rtc_sources = NULL;
ISrsRtcPublishStream::ISrsRtcPublishStream()
{
}
ISrsRtcPublishStream::~ISrsRtcPublishStream()
{
}
ISrsRtcSourceEventHandler::ISrsRtcSourceEventHandler()
{
}
@ -443,6 +435,7 @@ srs_error_t SrsRtcSource::initialize(ISrsRequest *r)
{
srs_error_t err = srs_success;
srs_freep(req_);
req_ = r->copy();
// Create default relations to allow play before publishing.

View File

@ -43,6 +43,7 @@ class SrsLiveSource;
class SrsRtpVideoBuilder;
class ISrsRtcConsumer;
class ISrsCircuitBreaker;
class ISrsRtcPublishStream;
// Firefox defaults as 109, Chrome is 111.
const int kAudioPayloadType = 111;
@ -207,20 +208,6 @@ public:
// Global singleton instance.
extern SrsRtcSourceManager *_srs_rtc_sources;
// A publish stream interface, for source to callback with.
class ISrsRtcPublishStream
{
public:
ISrsRtcPublishStream();
virtual ~ISrsRtcPublishStream();
public:
// Request keyframe(PLI) from publisher, for fresh consumer.
virtual void request_keyframe(uint32_t ssrc, SrsContextId cid) = 0;
// Get context id.
virtual const SrsContextId &context_id() = 0;
};
// The event handler for RTC source.
class ISrsRtcSourceEventHandler
{

View File

@ -15,7 +15,15 @@
// Global instance
SrsStreamPublishTokenManager *_srs_stream_publish_tokens = NULL;
SrsStreamPublishToken::SrsStreamPublishToken(const std::string &stream_url, SrsStreamPublishTokenManager *manager)
ISrsStreamPublishToken::ISrsStreamPublishToken()
{
}
ISrsStreamPublishToken::~ISrsStreamPublishToken()
{
}
SrsStreamPublishToken::SrsStreamPublishToken(const std::string &stream_url, ISrsStreamPublishTokenManager *manager)
{
stream_url_ = stream_url;
acquired_ = false;

View File

@ -18,11 +18,24 @@
class ISrsRequest;
class SrsStreamPublishTokenManager;
class ISrsStreamPublishTokenManager;
// The interface for stream publish token
class ISrsStreamPublishToken
{
public:
ISrsStreamPublishToken();
virtual ~ISrsStreamPublishToken();
public:
virtual bool is_acquired() = 0;
virtual void set_acquired(bool acquired) = 0;
};
// The stream publish token represents exclusive access to publish a stream.
// Only one publisher can hold a token for a given stream URL at any time.
// This prevents race conditions across all protocols (RTMP, RTC, SRT, etc.).
class SrsStreamPublishToken
class SrsStreamPublishToken : public ISrsStreamPublishToken
{
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
@ -31,12 +44,12 @@ SRS_DECLARE_PRIVATE: // clang-format on
// Whether this token is currently acquired
bool acquired_;
// The token manager that created this token
SrsStreamPublishTokenManager *manager_;
ISrsStreamPublishTokenManager *manager_;
// The context ID of the publisher that acquired this token
SrsContextId publisher_cid_;
public:
SrsStreamPublishToken(const std::string &stream_url, SrsStreamPublishTokenManager *manager);
SrsStreamPublishToken(const std::string &stream_url, ISrsStreamPublishTokenManager *manager);
virtual ~SrsStreamPublishToken();
public:

View File

@ -343,6 +343,14 @@ srs_error_t srs_rtmp_create_msg(char type, uint32_t timestamp, char *data, int s
return err;
}
ISrsProtocolUtility::ISrsProtocolUtility()
{
}
ISrsProtocolUtility::~ISrsProtocolUtility()
{
}
SrsProtocolUtility::SrsProtocolUtility()
{
}

View File

@ -118,8 +118,19 @@ struct SrsIPAddress {
bool is_loopback_;
};
// The interface for protocol utility.
class ISrsProtocolUtility
{
public:
ISrsProtocolUtility();
virtual ~ISrsProtocolUtility();
public:
virtual std::vector<SrsIPAddress *> &local_ips() = 0;
};
// The utility functions for protocol.
class SrsProtocolUtility
class SrsProtocolUtility : public ISrsProtocolUtility
{
public:
SrsProtocolUtility();

View File

@ -3110,88 +3110,6 @@ MockDvrAppFactory::~MockDvrAppFactory()
// We just keep a reference to it for testing purposes
}
ISrsFileWriter *MockDvrAppFactory::create_file_writer()
{
return new SrsFileWriter();
}
ISrsFileWriter *MockDvrAppFactory::create_enc_file_writer()
{
return new SrsFileWriter();
}
ISrsFileReader *MockDvrAppFactory::create_file_reader()
{
return new SrsFileReader();
}
SrsPath *MockDvrAppFactory::create_path()
{
return new SrsPath();
}
SrsLiveSource *MockDvrAppFactory::create_live_source()
{
return NULL;
}
ISrsOriginHub *MockDvrAppFactory::create_origin_hub()
{
return NULL;
}
ISrsHourGlass *MockDvrAppFactory::create_hourglass(const std::string &name, ISrsHourGlassHandler *handler, srs_utime_t interval)
{
return NULL;
}
ISrsBasicRtmpClient *MockDvrAppFactory::create_rtmp_client(std::string url, srs_utime_t cto, srs_utime_t sto)
{
return NULL;
}
ISrsHttpClient *MockDvrAppFactory::create_http_client()
{
return NULL;
}
ISrsHttpResponseReader *MockDvrAppFactory::create_http_response_reader(ISrsHttpResponseReader *r)
{
return NULL;
}
ISrsFileReader *MockDvrAppFactory::create_http_file_reader(ISrsHttpResponseReader *r)
{
return NULL;
}
ISrsFlvDecoder *MockDvrAppFactory::create_flv_decoder()
{
return NULL;
}
ISrsBasicRtmpClient *MockDvrAppFactory::create_basic_rtmp_client(std::string url, srs_utime_t ctm, srs_utime_t stm)
{
return NULL;
}
#ifdef SRS_RTSP
ISrsRtspSendTrack *MockDvrAppFactory::create_rtsp_audio_send_track(ISrsRtspConnection *session, SrsRtcTrackDescription *track_desc)
{
return NULL;
}
ISrsRtspSendTrack *MockDvrAppFactory::create_rtsp_video_send_track(ISrsRtspConnection *session, SrsRtcTrackDescription *track_desc)
{
return NULL;
}
#endif
ISrsFlvTransmuxer *MockDvrAppFactory::create_flv_transmuxer()
{
return NULL;
}
ISrsMp4Encoder *MockDvrAppFactory::create_mp4_encoder()
{
// Create a new mock encoder and save reference for testing
@ -3214,73 +3132,6 @@ ISrsDvrSegmenter *MockDvrAppFactory::create_dvr_mp4_segmenter()
return segmenter;
}
#ifdef SRS_GB28181
ISrsGbMediaTcpConn *MockDvrAppFactory::create_gb_media_tcp_conn()
{
return NULL;
}
ISrsGbSession *MockDvrAppFactory::create_gb_session()
{
return NULL;
}
#endif
ISrsInitMp4 *MockDvrAppFactory::create_init_mp4()
{
return NULL;
}
ISrsFragmentWindow *MockDvrAppFactory::create_fragment_window()
{
return NULL;
}
ISrsFragmentedMp4 *MockDvrAppFactory::create_fragmented_mp4()
{
return NULL;
}
ISrsIpListener *MockDvrAppFactory::create_tcp_listener(ISrsTcpHandler *handler)
{
return NULL;
}
ISrsRtcConnection *MockDvrAppFactory::create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid)
{
return NULL;
}
ISrsFFMPEG *MockDvrAppFactory::create_ffmpeg(std::string ffmpeg_bin)
{
return NULL;
}
ISrsIngesterFFMPEG *MockDvrAppFactory::create_ingester_ffmpeg()
{
return NULL;
}
ISrsCoroutine *MockDvrAppFactory::create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid)
{
return NULL;
}
ISrsTime *MockDvrAppFactory::create_time()
{
return NULL;
}
ISrsConfig *MockDvrAppFactory::create_config()
{
return NULL;
}
ISrsCond *MockDvrAppFactory::create_cond()
{
return NULL;
}
VOID TEST(DvrSegmenterTest, OpenTypicalScenario)
{
srs_error_t err;

View File

@ -609,7 +609,7 @@ public:
};
// Mock ISrsAppFactory for testing SrsDvrMp4Segmenter
class MockDvrAppFactory : public ISrsAppFactory
class MockDvrAppFactory : public SrsAppFactory
{
public:
MockMp4Encoder *mock_mp4_encoder_;
@ -619,43 +619,9 @@ public:
virtual ~MockDvrAppFactory();
public:
virtual ISrsFileWriter *create_file_writer();
virtual ISrsFileWriter *create_enc_file_writer();
virtual ISrsFileReader *create_file_reader();
virtual SrsPath *create_path();
virtual SrsLiveSource *create_live_source();
virtual ISrsOriginHub *create_origin_hub();
virtual ISrsHourGlass *create_hourglass(const std::string &name, ISrsHourGlassHandler *handler, srs_utime_t interval);
virtual ISrsBasicRtmpClient *create_rtmp_client(std::string url, srs_utime_t cto, srs_utime_t sto);
virtual ISrsHttpClient *create_http_client();
virtual ISrsHttpResponseReader *create_http_response_reader(ISrsHttpResponseReader *r);
virtual ISrsFileReader *create_http_file_reader(ISrsHttpResponseReader *r);
virtual ISrsFlvDecoder *create_flv_decoder();
virtual ISrsBasicRtmpClient *create_basic_rtmp_client(std::string url, srs_utime_t ctm, srs_utime_t stm);
#ifdef SRS_RTSP
virtual ISrsRtspSendTrack *create_rtsp_audio_send_track(ISrsRtspConnection *session, SrsRtcTrackDescription *track_desc);
virtual ISrsRtspSendTrack *create_rtsp_video_send_track(ISrsRtspConnection *session, SrsRtcTrackDescription *track_desc);
#endif
virtual ISrsFlvTransmuxer *create_flv_transmuxer();
virtual ISrsMp4Encoder *create_mp4_encoder();
virtual ISrsDvrSegmenter *create_dvr_flv_segmenter();
virtual ISrsDvrSegmenter *create_dvr_mp4_segmenter();
#ifdef SRS_GB28181
virtual ISrsGbMediaTcpConn *create_gb_media_tcp_conn();
virtual ISrsGbSession *create_gb_session();
#endif
virtual ISrsInitMp4 *create_init_mp4();
virtual ISrsFragmentWindow *create_fragment_window();
virtual ISrsFragmentedMp4 *create_fragmented_mp4();
virtual ISrsIpListener *create_tcp_listener(ISrsTcpHandler *handler);
virtual ISrsRtcConnection *create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid);
virtual ISrsFFMPEG *create_ffmpeg(std::string ffmpeg_bin);
virtual ISrsIngesterFFMPEG *create_ingester_ffmpeg();
// ISrsKernelFactory interface methods
virtual ISrsCoroutine *create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid);
virtual ISrsTime *create_time();
virtual ISrsConfig *create_config();
virtual ISrsCond *create_cond();
};
// Mock ISrsDvrSegmenter for testing SrsDvrPlan

View File

@ -1641,7 +1641,7 @@ std::string MockRtcConnectionForTcpConn::token()
return "";
}
void MockRtcConnectionForTcpConn::set_publish_token(SrsSharedPtr<SrsStreamPublishToken> /*publish_token*/)
void MockRtcConnectionForTcpConn::set_publish_token(SrsSharedPtr<ISrsStreamPublishToken> /*publish_token*/)
{
}
@ -1649,6 +1649,11 @@ void MockRtcConnectionForTcpConn::simulate_nack_drop(int /*nn*/)
{
}
srs_error_t MockRtcConnectionForTcpConn::generate_local_sdp(SrsRtcUserConfig * /*ruc*/, SrsSdp & /*local_sdp*/, std::string & /*username*/)
{
return srs_success;
}
// Mock ISrsPsPackHandler implementation
MockPsPackHandler::MockPsPackHandler()
{
@ -2267,109 +2272,7 @@ MockAppFactoryForGbPublish::~MockAppFactoryForGbPublish()
srs_freep(mock_gb_session_);
}
ISrsFileWriter *MockAppFactoryForGbPublish::create_file_writer()
{
return NULL;
}
ISrsFileWriter *MockAppFactoryForGbPublish::create_enc_file_writer()
{
return NULL;
}
ISrsFileReader *MockAppFactoryForGbPublish::create_file_reader()
{
return NULL;
}
SrsPath *MockAppFactoryForGbPublish::create_path()
{
return NULL;
}
SrsLiveSource *MockAppFactoryForGbPublish::create_live_source()
{
return NULL;
}
ISrsOriginHub *MockAppFactoryForGbPublish::create_origin_hub()
{
return NULL;
}
ISrsHourGlass *MockAppFactoryForGbPublish::create_hourglass(const std::string &name, ISrsHourGlassHandler *handler, srs_utime_t interval)
{
return NULL;
}
ISrsBasicRtmpClient *MockAppFactoryForGbPublish::create_rtmp_client(std::string url, srs_utime_t cto, srs_utime_t sto)
{
return NULL;
}
SrsHttpClient *MockAppFactoryForGbPublish::create_http_client()
{
return NULL;
}
ISrsHttpResponseReader *MockAppFactoryForGbPublish::create_http_response_reader(ISrsHttpResponseReader *r)
{
return NULL;
}
ISrsFileReader *MockAppFactoryForGbPublish::create_http_file_reader(ISrsHttpResponseReader *r)
{
return NULL;
}
ISrsFlvDecoder *MockAppFactoryForGbPublish::create_flv_decoder()
{
return NULL;
}
ISrsBasicRtmpClient *MockAppFactoryForGbPublish::create_basic_rtmp_client(std::string url, srs_utime_t ctm, srs_utime_t stm)
{
return NULL;
}
#ifdef SRS_RTSP
ISrsRtspSendTrack *MockAppFactoryForGbPublish::create_rtsp_audio_send_track(ISrsRtspConnection *session, SrsRtcTrackDescription *track_desc)
{
return NULL;
}
ISrsRtspSendTrack *MockAppFactoryForGbPublish::create_rtsp_video_send_track(ISrsRtspConnection *session, SrsRtcTrackDescription *track_desc)
{
return NULL;
}
#endif
ISrsFlvTransmuxer *MockAppFactoryForGbPublish::create_flv_transmuxer()
{
return NULL;
}
ISrsMp4Encoder *MockAppFactoryForGbPublish::create_mp4_encoder()
{
return NULL;
}
SrsDvrFlvSegmenter *MockAppFactoryForGbPublish::create_dvr_flv_segmenter()
{
return NULL;
}
SrsDvrMp4Segmenter *MockAppFactoryForGbPublish::create_dvr_mp4_segmenter()
{
return NULL;
}
#ifdef SRS_GB28181
ISrsGbMediaTcpConn *MockAppFactoryForGbPublish::create_gb_media_tcp_conn()
{
return NULL;
}
ISrsGbSession *MockAppFactoryForGbPublish::create_gb_session()
{
// Return the mock session (ownership transferred to caller)
@ -2379,61 +2282,6 @@ ISrsGbSession *MockAppFactoryForGbPublish::create_gb_session()
}
#endif
ISrsInitMp4 *MockAppFactoryForGbPublish::create_init_mp4()
{
return NULL;
}
ISrsFragmentWindow *MockAppFactoryForGbPublish::create_fragment_window()
{
return NULL;
}
ISrsFragmentedMp4 *MockAppFactoryForGbPublish::create_fragmented_mp4()
{
return NULL;
}
ISrsIpListener *MockAppFactoryForGbPublish::create_tcp_listener(ISrsTcpHandler *handler)
{
return NULL;
}
ISrsRtcConnection *MockAppFactoryForGbPublish::create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid)
{
return NULL;
}
ISrsFFMPEG *MockAppFactoryForGbPublish::create_ffmpeg(std::string ffmpeg_bin)
{
return NULL;
}
ISrsIngesterFFMPEG *MockAppFactoryForGbPublish::create_ingester_ffmpeg()
{
return NULL;
}
ISrsCoroutine *MockAppFactoryForGbPublish::create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid)
{
return NULL;
}
ISrsTime *MockAppFactoryForGbPublish::create_time()
{
return NULL;
}
ISrsConfig *MockAppFactoryForGbPublish::create_config()
{
return NULL;
}
ISrsCond *MockAppFactoryForGbPublish::create_cond()
{
return NULL;
}
void MockAppFactoryForGbPublish::reset()
{
srs_freep(mock_gb_session_);
@ -2966,7 +2814,7 @@ std::string MockRtcConnectionForUdpNetwork::token()
return "";
}
void MockRtcConnectionForUdpNetwork::set_publish_token(SrsSharedPtr<SrsStreamPublishToken> /*publish_token*/)
void MockRtcConnectionForUdpNetwork::set_publish_token(SrsSharedPtr<ISrsStreamPublishToken> /*publish_token*/)
{
}
@ -2974,6 +2822,11 @@ void MockRtcConnectionForUdpNetwork::simulate_nack_drop(int /*nn*/)
{
}
srs_error_t MockRtcConnectionForUdpNetwork::generate_local_sdp(SrsRtcUserConfig * /*ruc*/, SrsSdp & /*local_sdp*/, std::string & /*username*/)
{
return srs_success;
}
void MockRtcConnectionForUdpNetwork::set_on_dtls_alert_error(srs_error_t err)
{
srs_freep(on_dtls_alert_error_);
@ -4102,7 +3955,7 @@ std::string MockRtcConnectionForTcpConnHandshake::token()
return "";
}
void MockRtcConnectionForTcpConnHandshake::set_publish_token(SrsSharedPtr<SrsStreamPublishToken> /*publish_token*/)
void MockRtcConnectionForTcpConnHandshake::set_publish_token(SrsSharedPtr<ISrsStreamPublishToken> /*publish_token*/)
{
}
@ -4110,6 +3963,11 @@ void MockRtcConnectionForTcpConnHandshake::simulate_nack_drop(int /*nn*/)
{
}
srs_error_t MockRtcConnectionForTcpConnHandshake::generate_local_sdp(SrsRtcUserConfig * /*ruc*/, SrsSdp & /*local_sdp*/, std::string & /*username*/)
{
return srs_success;
}
void MockRtcConnectionForTcpConnHandshake::reset()
{
tcp_network_ = NULL;

View File

@ -171,8 +171,9 @@ public:
virtual srs_error_t initialize(ISrsRequest *r, bool dtls, bool srtp, std::string username);
virtual std::string username();
virtual std::string token();
virtual void set_publish_token(SrsSharedPtr<SrsStreamPublishToken> publish_token);
virtual void set_publish_token(SrsSharedPtr<ISrsStreamPublishToken> publish_token);
virtual void simulate_nack_drop(int nn);
virtual srs_error_t generate_local_sdp(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, std::string &username);
public:
void set_on_dtls_alert_error(srs_error_t err);
@ -461,8 +462,9 @@ public:
virtual srs_error_t initialize(ISrsRequest *r, bool dtls, bool srtp, std::string username);
virtual std::string username();
virtual std::string token();
virtual void set_publish_token(SrsSharedPtr<SrsStreamPublishToken> publish_token);
virtual void set_publish_token(SrsSharedPtr<ISrsStreamPublishToken> publish_token);
virtual void simulate_nack_drop(int nn);
virtual srs_error_t generate_local_sdp(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, std::string &username);
};
// Mock ISrsPsPackHandler for testing SrsPackContext
@ -554,7 +556,7 @@ public:
};
// Mock ISrsAppFactory for testing SrsGoApiGbPublish
class MockAppFactoryForGbPublish : public ISrsAppFactory
class MockAppFactoryForGbPublish : public SrsAppFactory
{
public:
MockGbSessionForApiPublish *mock_gb_session_;
@ -564,43 +566,9 @@ public:
virtual ~MockAppFactoryForGbPublish();
public:
virtual ISrsFileWriter *create_file_writer();
virtual ISrsFileWriter *create_enc_file_writer();
virtual ISrsFileReader *create_file_reader();
virtual SrsPath *create_path();
virtual SrsLiveSource *create_live_source();
virtual ISrsOriginHub *create_origin_hub();
virtual ISrsHourGlass *create_hourglass(const std::string &name, ISrsHourGlassHandler *handler, srs_utime_t interval);
virtual ISrsBasicRtmpClient *create_rtmp_client(std::string url, srs_utime_t cto, srs_utime_t sto);
virtual SrsHttpClient *create_http_client();
virtual ISrsHttpResponseReader *create_http_response_reader(ISrsHttpResponseReader *r);
virtual ISrsFileReader *create_http_file_reader(ISrsHttpResponseReader *r);
virtual ISrsFlvDecoder *create_flv_decoder();
virtual ISrsBasicRtmpClient *create_basic_rtmp_client(std::string url, srs_utime_t ctm, srs_utime_t stm);
#ifdef SRS_RTSP
virtual ISrsRtspSendTrack *create_rtsp_audio_send_track(ISrsRtspConnection *session, SrsRtcTrackDescription *track_desc);
virtual ISrsRtspSendTrack *create_rtsp_video_send_track(ISrsRtspConnection *session, SrsRtcTrackDescription *track_desc);
#endif
virtual ISrsFlvTransmuxer *create_flv_transmuxer();
virtual ISrsMp4Encoder *create_mp4_encoder();
virtual SrsDvrFlvSegmenter *create_dvr_flv_segmenter();
virtual SrsDvrMp4Segmenter *create_dvr_mp4_segmenter();
#ifdef SRS_GB28181
virtual ISrsGbMediaTcpConn *create_gb_media_tcp_conn();
virtual ISrsGbSession *create_gb_session();
#endif
virtual ISrsInitMp4 *create_init_mp4();
virtual ISrsFragmentWindow *create_fragment_window();
virtual ISrsFragmentedMp4 *create_fragmented_mp4();
virtual ISrsIpListener *create_tcp_listener(ISrsTcpHandler *handler);
virtual ISrsRtcConnection *create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid);
virtual ISrsFFMPEG *create_ffmpeg(std::string ffmpeg_bin);
virtual ISrsIngesterFFMPEG *create_ingester_ffmpeg();
// ISrsKernelFactory interface methods
virtual ISrsCoroutine *create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid);
virtual ISrsTime *create_time();
virtual ISrsConfig *create_config();
virtual ISrsCond *create_cond();
void reset();
};
@ -815,8 +783,9 @@ public:
virtual srs_error_t initialize(ISrsRequest *r, bool dtls, bool srtp, std::string username);
virtual std::string username();
virtual std::string token();
virtual void set_publish_token(SrsSharedPtr<SrsStreamPublishToken> publish_token);
virtual void set_publish_token(SrsSharedPtr<ISrsStreamPublishToken> publish_token);
virtual void simulate_nack_drop(int nn);
virtual srs_error_t generate_local_sdp(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, std::string &username);
public:
void reset();

View File

@ -1481,130 +1481,6 @@ MockAppFactoryForIngester::~MockAppFactoryForIngester()
// Don't free mock_coroutine_ and mock_time_ - they are managed by the test
}
ISrsFileWriter *MockAppFactoryForIngester::create_file_writer()
{
return NULL;
}
ISrsFileWriter *MockAppFactoryForIngester::create_enc_file_writer()
{
return NULL;
}
ISrsFileReader *MockAppFactoryForIngester::create_file_reader()
{
return NULL;
}
SrsPath *MockAppFactoryForIngester::create_path()
{
return NULL;
}
SrsLiveSource *MockAppFactoryForIngester::create_live_source()
{
return NULL;
}
ISrsOriginHub *MockAppFactoryForIngester::create_origin_hub()
{
return NULL;
}
ISrsHourGlass *MockAppFactoryForIngester::create_hourglass(const std::string &name, ISrsHourGlassHandler *handler, srs_utime_t interval)
{
return NULL;
}
ISrsBasicRtmpClient *MockAppFactoryForIngester::create_rtmp_client(std::string url, srs_utime_t cto, srs_utime_t sto)
{
return NULL;
}
ISrsHttpClient *MockAppFactoryForIngester::create_http_client()
{
return NULL;
}
ISrsFileReader *MockAppFactoryForIngester::create_http_file_reader(ISrsHttpResponseReader *r)
{
return NULL;
}
ISrsFlvDecoder *MockAppFactoryForIngester::create_flv_decoder()
{
return NULL;
}
#ifdef SRS_RTSP
ISrsRtspSendTrack *MockAppFactoryForIngester::create_rtsp_audio_send_track(ISrsRtspConnection *session, SrsRtcTrackDescription *track_desc)
{
return NULL;
}
ISrsRtspSendTrack *MockAppFactoryForIngester::create_rtsp_video_send_track(ISrsRtspConnection *session, SrsRtcTrackDescription *track_desc)
{
return NULL;
}
#endif
ISrsFlvTransmuxer *MockAppFactoryForIngester::create_flv_transmuxer()
{
return NULL;
}
ISrsMp4Encoder *MockAppFactoryForIngester::create_mp4_encoder()
{
return NULL;
}
ISrsDvrSegmenter *MockAppFactoryForIngester::create_dvr_flv_segmenter()
{
return NULL;
}
ISrsDvrSegmenter *MockAppFactoryForIngester::create_dvr_mp4_segmenter()
{
return NULL;
}
#ifdef SRS_GB28181
ISrsGbMediaTcpConn *MockAppFactoryForIngester::create_gb_media_tcp_conn()
{
return NULL;
}
ISrsGbSession *MockAppFactoryForIngester::create_gb_session()
{
return NULL;
}
#endif
ISrsInitMp4 *MockAppFactoryForIngester::create_init_mp4()
{
return NULL;
}
ISrsFragmentWindow *MockAppFactoryForIngester::create_fragment_window()
{
return NULL;
}
ISrsFragmentedMp4 *MockAppFactoryForIngester::create_fragmented_mp4()
{
return NULL;
}
ISrsIpListener *MockAppFactoryForIngester::create_tcp_listener(ISrsTcpHandler *handler)
{
return NULL;
}
ISrsRtcConnection *MockAppFactoryForIngester::create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid)
{
return NULL;
}
ISrsFFMPEG *MockAppFactoryForIngester::create_ffmpeg(std::string ffmpeg_bin)
{
return new MockFFMPEG();
@ -1627,16 +1503,6 @@ ISrsTime *MockAppFactoryForIngester::create_time()
return mock_time_;
}
ISrsConfig *MockAppFactoryForIngester::create_config()
{
return NULL;
}
ISrsCond *MockAppFactoryForIngester::create_cond()
{
return NULL;
}
void MockAppFactoryForIngester::reset()
{
create_coroutine_count_ = 0;
@ -2568,7 +2434,7 @@ srs_error_t MockStreamPublishTokenManager::acquire_token(ISrsRequest *req, SrsSt
// Create a new token if not already created
if (!token_to_return_) {
token_to_return_ = new SrsStreamPublishToken(req->get_stream_url(), NULL);
token_to_return_ = new SrsStreamPublishToken(req->get_stream_url(), this);
}
token = token_to_return_;
return srs_success;
@ -2632,7 +2498,7 @@ void MockRtcConnectionForSessionManager::set_all_tracks_status(std::string strea
set_all_tracks_status_called_ = true;
}
void MockRtcConnectionForSessionManager::set_publish_token(SrsSharedPtr<SrsStreamPublishToken> publish_token)
void MockRtcConnectionForSessionManager::set_publish_token(SrsSharedPtr<ISrsStreamPublishToken> publish_token)
{
set_publish_token_called_ = true;
publish_token_ = publish_token;
@ -2970,7 +2836,7 @@ std::string MockRtcConnectionForUpdateSessions::token()
return "test-token";
}
void MockRtcConnectionForUpdateSessions::set_publish_token(SrsSharedPtr<SrsStreamPublishToken> publish_token)
void MockRtcConnectionForUpdateSessions::set_publish_token(SrsSharedPtr<ISrsStreamPublishToken> publish_token)
{
}
@ -2982,6 +2848,11 @@ void MockRtcConnectionForUpdateSessions::simulate_nack_drop(int nn)
{
}
srs_error_t MockRtcConnectionForUpdateSessions::generate_local_sdp(SrsRtcUserConfig * /*ruc*/, SrsSdp & /*local_sdp*/, std::string & /*username*/)
{
return srs_success;
}
// Mock ISrsResourceManager implementation for srs_update_rtc_sessions test
MockResourceManagerForUpdateSessions::MockResourceManagerForUpdateSessions()
{

View File

@ -289,7 +289,7 @@ public:
srs_error_t add_player_error_;
std::string username_;
std::string token_;
SrsSharedPtr<SrsStreamPublishToken> publish_token_;
SrsSharedPtr<ISrsStreamPublishToken> publish_token_;
public:
MockRtcConnectionForSessionManager();
@ -299,7 +299,7 @@ public:
srs_error_t add_publisher(SrsRtcUserConfig *ruc, SrsSdp &local_sdp);
srs_error_t add_player(SrsRtcUserConfig *ruc, SrsSdp &local_sdp);
void set_all_tracks_status(std::string stream_uri, bool is_publish, bool status);
void set_publish_token(SrsSharedPtr<SrsStreamPublishToken> publish_token);
void set_publish_token(SrsSharedPtr<ISrsStreamPublishToken> publish_token);
void reset();
};
@ -391,9 +391,10 @@ public:
virtual srs_error_t initialize(ISrsRequest *r, bool dtls, bool srtp, std::string username);
virtual std::string username();
virtual std::string token();
virtual void set_publish_token(SrsSharedPtr<SrsStreamPublishToken> publish_token);
virtual void set_publish_token(SrsSharedPtr<ISrsStreamPublishToken> publish_token);
virtual void simulate_drop_packet(bool v, int nn);
virtual void simulate_nack_drop(int nn);
virtual srs_error_t generate_local_sdp(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, std::string &username);
};
// Mock ISrsResourceManager for testing SrsRtcSessionManager::srs_update_rtc_sessions
@ -513,7 +514,7 @@ public:
};
// Mock ISrsAppFactory for testing SrsIngester
class MockAppFactoryForIngester : public ISrsAppFactory
class MockAppFactoryForIngester : public SrsAppFactory
{
public:
MockSrtCoroutine *mock_coroutine_;
@ -526,40 +527,10 @@ public:
virtual ~MockAppFactoryForIngester();
public:
virtual ISrsFileWriter *create_file_writer();
virtual ISrsFileWriter *create_enc_file_writer();
virtual ISrsFileReader *create_file_reader();
virtual SrsPath *create_path();
virtual SrsLiveSource *create_live_source();
virtual ISrsOriginHub *create_origin_hub();
virtual ISrsHourGlass *create_hourglass(const std::string &name, ISrsHourGlassHandler *handler, srs_utime_t interval);
virtual ISrsBasicRtmpClient *create_rtmp_client(std::string url, srs_utime_t cto, srs_utime_t sto);
virtual ISrsHttpClient *create_http_client();
virtual ISrsFileReader *create_http_file_reader(ISrsHttpResponseReader *r);
virtual ISrsFlvDecoder *create_flv_decoder();
#ifdef SRS_RTSP
virtual ISrsRtspSendTrack *create_rtsp_audio_send_track(ISrsRtspConnection *session, SrsRtcTrackDescription *track_desc);
virtual ISrsRtspSendTrack *create_rtsp_video_send_track(ISrsRtspConnection *session, SrsRtcTrackDescription *track_desc);
#endif
virtual ISrsFlvTransmuxer *create_flv_transmuxer();
virtual ISrsMp4Encoder *create_mp4_encoder();
virtual ISrsDvrSegmenter *create_dvr_flv_segmenter();
virtual ISrsDvrSegmenter *create_dvr_mp4_segmenter();
#ifdef SRS_GB28181
virtual ISrsGbMediaTcpConn *create_gb_media_tcp_conn();
virtual ISrsGbSession *create_gb_session();
#endif
virtual ISrsInitMp4 *create_init_mp4();
virtual ISrsFragmentWindow *create_fragment_window();
virtual ISrsFragmentedMp4 *create_fragmented_mp4();
virtual ISrsIpListener *create_tcp_listener(ISrsTcpHandler *handler);
virtual ISrsRtcConnection *create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid);
virtual ISrsFFMPEG *create_ffmpeg(std::string ffmpeg_bin);
virtual ISrsIngesterFFMPEG *create_ingester_ffmpeg();
virtual ISrsCoroutine *create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid);
virtual ISrsTime *create_time();
virtual ISrsConfig *create_config();
virtual ISrsCond *create_cond();
void reset();
};

View File

@ -153,7 +153,7 @@ void MockRtcSourceEventHandler::on_consumers_finished()
on_consumers_finished_count_++;
}
MockRtcPublishStream::MockRtcPublishStream()
MockRtcPublishStream::MockRtcPublishStream() : SrsRtcPublishStream(NULL, NULL, NULL, SrsContextId())
{
request_keyframe_count_ = 0;
last_keyframe_ssrc_ = 0;

View File

@ -90,7 +90,7 @@ public:
};
// Mock implementation of ISrsRtcPublishStream for testing
class MockRtcPublishStream : public ISrsRtcPublishStream
class MockRtcPublishStream : public SrsRtcPublishStream
{
public:
int request_keyframe_count_;

View File

@ -1356,7 +1356,7 @@ VOID TEST(SrsRtcConnectionTest, TestConnectionBasicOperations)
// Test set_publish_token
SrsStreamPublishTokenManager token_manager;
SrsSharedPtr<SrsStreamPublishToken> publish_token(new SrsStreamPublishToken("/live/test", &token_manager));
SrsSharedPtr<ISrsStreamPublishToken> publish_token(new SrsStreamPublishToken("/live/test", &token_manager));
conn->set_publish_token(publish_token);
// No direct getter for publish_token_, but setting should not crash
@ -1781,7 +1781,7 @@ VOID TEST(SrsRtcConnectionTest, FindPublisherTypicalScenario)
SrsUniquePtr<SrsRtcPublishStream> publish_stream(new SrsRtcPublishStream(&mock_exec, &mock_expire, &mock_receiver, stream_cid));
// Test scenario 1: No publishers - should return error
SrsRtcPublishStream *found_publisher = NULL;
ISrsRtcPublishStream *found_publisher = NULL;
unsigned char rtp_data[] = {
// RTP header (12 bytes)
0x80, 0x60, 0x12, 0x34, // V=2, P=0, X=0, CC=0, M=0, PT=96, seq=0x1234
@ -1950,9 +1950,6 @@ VOID TEST(SrsRtcPublisherNegotiatorTest, TypicalUseScenario)
// Create mock request for initialization
SrsUniquePtr<MockRtcConnectionRequest> mock_request(new MockRtcConnectionRequest("test.vhost", "live", "stream1"));
// Test initialize method
HELPER_EXPECT_SUCCESS(negotiator->initialize(mock_request.get()));
// Create mock RTC user config with remote SDP
SrsUniquePtr<SrsRtcUserConfig> ruc(new SrsRtcUserConfig());
ruc->req_ = mock_request->copy();
@ -2636,9 +2633,6 @@ VOID TEST(SrsRtcPlayerNegotiatorTest, TypicalUseScenario)
// Create mock request for initialization
SrsUniquePtr<MockRtcConnectionRequest> mock_request(new MockRtcConnectionRequest("test.vhost", "live", "stream1"));
// Test initialize method
HELPER_EXPECT_SUCCESS(negotiator->initialize(mock_request.get()));
// Create mock RTC user config with remote SDP for play scenario
SrsUniquePtr<SrsRtcUserConfig> ruc(new SrsRtcUserConfig());
ruc->req_ = mock_request->copy();

View File

@ -34,12 +34,6 @@ public:
// Mock request class for testing
class MockRtc2RtmpRequest : public ISrsRequest
{
public:
std::string vhost_;
std::string app_;
std::string stream_;
std::string host_;
public:
MockRtc2RtmpRequest(std::string vhost = "__defaultVhost__", std::string app = "live", std::string stream = "test");
virtual ~MockRtc2RtmpRequest();

View File

@ -6,10 +6,173 @@
#include <srs_utest_mock.hpp>
#include <srs_app_caster_flv.hpp>
#include <srs_app_config.hpp>
#include <srs_app_dash.hpp>
#include <srs_app_dvr.hpp>
#include <srs_app_ffmpeg.hpp>
#include <srs_app_fragment.hpp>
#include <srs_app_rtc_conn.hpp>
#include <srs_app_rtc_source.hpp>
#include <srs_app_rtmp_source.hpp>
#include <srs_kernel_error.hpp>
#ifdef SRS_GB28181
#include <srs_app_gb28181.hpp>
#endif
#include <srs_app_ingest.hpp>
#include <srs_app_listener.hpp>
#include <srs_app_rtc_conn.hpp>
#include <srs_app_rtmp_conn.hpp>
#include <srs_app_rtmp_source.hpp>
#ifdef SRS_RTSP
#include <srs_app_rtsp_source.hpp>
#endif
#include <srs_app_st.hpp>
#include <srs_kernel_file.hpp>
#include <srs_kernel_flv.hpp>
#include <srs_kernel_hourglass.hpp>
#include <srs_kernel_mp4.hpp>
#include <srs_kernel_ts.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_protocol_http_client.hpp>
#include <srs_protocol_st.hpp>
#include <srs_protocol_utility.hpp>
#include <sstream>
#include <srs_utest_app13.hpp>
// MockSdpFactory implementation
MockSdpFactory::MockSdpFactory()
{
// Initialize default SSRC and payload type values
audio_ssrc_ = 1001;
audio_pt_ = 111;
video_ssrc_ = 2002;
video_pt_ = 96;
}
MockSdpFactory::~MockSdpFactory()
{
}
std::string MockSdpFactory::create_chrome_player_offer()
{
// Create a real Chrome-like WebRTC SDP offer for a player (subscriber) with H.264 video and Opus audio
// Use member variables for SSRC and payload type values
// Key difference from publisher: uses recvonly instead of sendonly
std::stringstream ss;
ss << "v=0\r\n"
<< "o=- 4611731400430051337 2 IN IP4 127.0.0.1\r\n"
<< "s=-\r\n"
<< "t=0 0\r\n"
<< "a=group:BUNDLE 0 1\r\n"
<< "a=msid-semantic: WMS\r\n"
// Audio media description (Opus)
<< "m=audio 9 UDP/TLS/RTP/SAVPF " << (int)audio_pt_ << "\r\n"
<< "c=IN IP4 0.0.0.0\r\n"
<< "a=rtcp:9 IN IP4 0.0.0.0\r\n"
<< "a=ice-ufrag:test1234\r\n"
<< "a=ice-pwd:testpassword1234567890\r\n"
<< "a=ice-options:trickle\r\n"
<< "a=fingerprint:sha-256 AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99\r\n"
<< "a=setup:actpass\r\n"
<< "a=mid:0\r\n"
<< "a=recvonly\r\n"
<< "a=rtcp-mux\r\n"
<< "a=rtpmap:" << (int)audio_pt_ << " opus/48000/2\r\n"
<< "a=fmtp:" << (int)audio_pt_ << " minptime=10;useinbandfec=1\r\n"
// Video media description (H.264)
<< "m=video 9 UDP/TLS/RTP/SAVPF " << (int)video_pt_ << "\r\n"
<< "c=IN IP4 0.0.0.0\r\n"
<< "a=rtcp:9 IN IP4 0.0.0.0\r\n"
<< "a=ice-ufrag:test1234\r\n"
<< "a=ice-pwd:testpassword1234567890\r\n"
<< "a=ice-options:trickle\r\n"
<< "a=fingerprint:sha-256 AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99\r\n"
<< "a=setup:actpass\r\n"
<< "a=mid:1\r\n"
<< "a=recvonly\r\n"
<< "a=rtcp-mux\r\n"
<< "a=rtcp-rsize\r\n"
<< "a=rtpmap:" << (int)video_pt_ << " H264/90000\r\n"
<< "a=rtcp-fb:" << (int)video_pt_ << " nack\r\n"
<< "a=rtcp-fb:" << (int)video_pt_ << " nack pli\r\n"
<< "a=rtcp-fb:" << (int)video_pt_ << " transport-cc\r\n"
<< "a=fmtp:" << (int)video_pt_ << " level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\n";
return ss.str();
}
std::string MockSdpFactory::create_chrome_publisher_offer()
{
// Create a real Chrome-like WebRTC SDP offer with H.264 video and Opus audio
// Use member variables for SSRC and payload type values
std::stringstream ss;
ss << "v=0\r\n"
<< "o=- 4611731400430051336 2 IN IP4 127.0.0.1\r\n"
<< "s=-\r\n"
<< "t=0 0\r\n"
<< "a=group:BUNDLE 0 1\r\n"
<< "a=msid-semantic: WMS stream\r\n"
// Audio media description (Opus)
<< "m=audio 9 UDP/TLS/RTP/SAVPF " << (int)audio_pt_ << "\r\n"
<< "c=IN IP4 0.0.0.0\r\n"
<< "a=rtcp:9 IN IP4 0.0.0.0\r\n"
<< "a=ice-ufrag:test1234\r\n"
<< "a=ice-pwd:testpassword1234567890\r\n"
<< "a=ice-options:trickle\r\n"
<< "a=fingerprint:sha-256 AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99\r\n"
<< "a=setup:actpass\r\n"
<< "a=mid:0\r\n"
<< "a=sendonly\r\n"
<< "a=rtcp-mux\r\n"
<< "a=rtpmap:" << (int)audio_pt_ << " opus/48000/2\r\n"
<< "a=fmtp:" << (int)audio_pt_ << " minptime=10;useinbandfec=1\r\n"
<< "a=ssrc:" << audio_ssrc_ << " cname:test-audio-cname\r\n"
<< "a=ssrc:" << audio_ssrc_ << " msid:stream audio\r\n"
// Video media description (H.264)
<< "m=video 9 UDP/TLS/RTP/SAVPF " << (int)video_pt_ << "\r\n"
<< "c=IN IP4 0.0.0.0\r\n"
<< "a=rtcp:9 IN IP4 0.0.0.0\r\n"
<< "a=ice-ufrag:test1234\r\n"
<< "a=ice-pwd:testpassword1234567890\r\n"
<< "a=ice-options:trickle\r\n"
<< "a=fingerprint:sha-256 AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99\r\n"
<< "a=setup:actpass\r\n"
<< "a=mid:1\r\n"
<< "a=sendonly\r\n"
<< "a=rtcp-mux\r\n"
<< "a=rtcp-rsize\r\n"
<< "a=rtpmap:" << (int)video_pt_ << " H264/90000\r\n"
<< "a=rtcp-fb:" << (int)video_pt_ << " nack\r\n"
<< "a=rtcp-fb:" << (int)video_pt_ << " nack pli\r\n"
<< "a=rtcp-fb:" << (int)video_pt_ << " transport-cc\r\n"
<< "a=fmtp:" << (int)video_pt_ << " level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\n"
<< "a=ssrc:" << video_ssrc_ << " cname:test-video-cname\r\n"
<< "a=ssrc:" << video_ssrc_ << " msid:stream video\r\n";
return ss.str();
}
MockDtlsCertificate::MockDtlsCertificate()
{
fingerprint_ = "AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99";
}
MockDtlsCertificate::~MockDtlsCertificate()
{
}
srs_error_t MockDtlsCertificate::initialize()
{
return srs_success;
}
std::string MockDtlsCertificate::get_fingerprint()
{
return fingerprint_;
}
// MockRtcTrackDescriptionFactory implementation
MockRtcTrackDescriptionFactory::MockRtcTrackDescriptionFactory()
@ -155,13 +318,20 @@ srs_error_t MockRtcSourceManager::initialize()
srs_error_t MockRtcSourceManager::fetch_or_create(ISrsRequest *r, SrsSharedPtr<SrsRtcSource> &pps)
{
srs_error_t err = srs_success;
if (fetch_or_create_count_ == 0) {
err = mock_source_->initialize(r);
}
fetch_or_create_count_++;
if (fetch_or_create_error_ != srs_success) {
return srs_error_copy(fetch_or_create_error_);
}
pps = mock_source_;
return mock_source_->initialize(r);
return err;
}
SrsSharedPtr<SrsRtcSource> MockRtcSourceManager::fetch(ISrsRequest *r)
@ -407,6 +577,7 @@ MockAppConfig::MockAppConfig()
resolve_api_domain_ = true;
keep_api_domain_ = false;
mw_msgs_ = 8;
rtc_dtls_role_ = "passive";
}
MockAppConfig::~MockAppConfig()
@ -522,7 +693,7 @@ bool MockAppConfig::get_rtc_stun_strict_check(std::string vhost)
std::string MockAppConfig::get_rtc_dtls_role(std::string vhost)
{
return "passive";
return rtc_dtls_role_;
}
std::string MockAppConfig::get_rtc_dtls_version(std::string vhost)

View File

@ -13,16 +13,78 @@
#include <vector>
// Include necessary SRS headers for interfaces
#include <srs_app_circuit_breaker.hpp>
#include <srs_app_config.hpp>
#include <srs_app_factory.hpp>
#include <srs_app_ffmpeg.hpp>
#include <srs_app_rtc_conn.hpp>
#include <srs_app_rtc_dtls.hpp>
#include <srs_app_rtc_source.hpp>
#include <srs_app_statistic.hpp>
#include <srs_kernel_buffer.hpp>
#include <srs_kernel_file.hpp>
#include <srs_kernel_mp4.hpp>
#include <srs_kernel_ps.hpp>
#include <srs_kernel_resource.hpp>
#include <srs_kernel_stream.hpp>
#include <srs_kernel_ts.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_protocol_conn.hpp>
#include <srs_protocol_rtmp_conn.hpp>
#include <srs_protocol_stream.hpp>
#ifdef SRS_GB28181
#include <srs_app_gb28181.hpp>
#endif
#include <srs_protocol_utility.hpp>
// Forward declarations
class SrsRtcTrackDescription;
class SrsRtpPacket;
class MockMp4Encoder;
class MockSrsFileWriter;
class MockSrsFile;
class MockSrsFileReader;
class MockSrtCoroutine;
class ISrsGbSession;
class ISrsProtocolUtility;
// Mock SDP factory for creating test SDP offers/answers
class MockSdpFactory
{
public:
// Audio track properties
uint32_t audio_ssrc_;
uint8_t audio_pt_;
// Video track properties
uint32_t video_ssrc_;
uint8_t video_pt_;
public:
MockSdpFactory();
virtual ~MockSdpFactory();
public:
// Create a Chrome-like WebRTC publisher offer SDP
std::string create_chrome_publisher_offer();
// Create a Chrome-like WebRTC player offer SDP
std::string create_chrome_player_offer();
};
// Mock DTLS certificate for testing
class MockDtlsCertificate : public ISrsDtlsCertificate
{
public:
std::string fingerprint_;
public:
MockDtlsCertificate();
virtual ~MockDtlsCertificate();
public:
virtual srs_error_t initialize();
virtual std::string get_fingerprint();
};
// Helper class to create mock track descriptions for testing
class MockRtcTrackDescriptionFactory
@ -68,11 +130,6 @@ public:
// Mock request for testing
class MockRtcAsyncCallRequest : public ISrsRequest
{
public:
std::string vhost_;
std::string app_;
std::string stream_;
public:
MockRtcAsyncCallRequest(std::string vhost = "__defaultVhost__", std::string app = "live", std::string stream = "test");
virtual ~MockRtcAsyncCallRequest();
@ -198,6 +255,7 @@ public:
bool resolve_api_domain_;
bool keep_api_domain_;
int mw_msgs_;
std::string rtc_dtls_role_;
public:
MockAppConfig();
@ -248,9 +306,19 @@ public:
virtual bool get_http_stream_crossdomain() { return false; }
virtual bool get_rtc_server_enabled() { return false; }
virtual bool get_rtc_server_tcp_enabled() { return false; }
virtual std::vector<std::string> get_rtc_server_tcp_listens() { return std::vector<std::string>(); }
virtual std::vector<std::string> get_rtc_server_tcp_listens()
{
std::vector<std::string> v;
v.push_back("127.0.0.1:8000");
return v;
}
virtual std::string get_rtc_server_protocol() { return "udp"; }
virtual std::vector<std::string> get_rtc_server_listens() { return std::vector<std::string>(); }
virtual std::vector<std::string> get_rtc_server_listens()
{
std::vector<std::string> v;
v.push_back("127.0.0.1:8000");
return v;
}
virtual int get_rtc_server_reuseport() { return 1; }
virtual bool get_rtc_server_encrypt() { return false; }
virtual bool get_api_as_candidates() { return api_as_candidates_; }

View File

@ -0,0 +1,462 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2013-2025 Winlin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <srs_utest_rtc_conn.hpp>
#include <srs_app_factory.hpp>
#include <srs_app_rtc_conn.hpp>
#include <srs_app_rtc_server.hpp>
#include <srs_app_rtc_source.hpp>
#include <srs_app_stream_token.hpp>
#include <srs_kernel_error.hpp>
#include <srs_protocol_rtmp_stack.hpp>
#include <srs_utest_app6.hpp>
#include <srs_utest_mock.hpp>
#include <srs_utest_service.hpp>
MockProtocolUtilityForRtcConn::MockProtocolUtilityForRtcConn(std::string ip)
{
mock_ip_ = ip;
}
MockProtocolUtilityForRtcConn::~MockProtocolUtilityForRtcConn()
{
}
std::vector<SrsIPAddress *> &MockProtocolUtilityForRtcConn::local_ips()
{
if (!ips_.empty()) {
return ips_;
}
SrsIPAddress *addr = new SrsIPAddress();
addr->ip_ = mock_ip_;
addr->is_ipv4_ = true;
addr->is_loopback_ = false; // Not loopback
addr->is_internet_ = true; // Public IP
addr->ifname_ = "eth0"; // Interface name
ips_.push_back(addr);
return ips_;
}
MockAppFactoryForRtcConn::MockAppFactoryForRtcConn()
{
mock_protocol_utility_ = NULL;
}
MockAppFactoryForRtcConn::~MockAppFactoryForRtcConn()
{
}
ISrsProtocolUtility *MockAppFactoryForRtcConn::create_protocol_utility()
{
return mock_protocol_utility_;
}
ISrsRtcPublishStream *MockAppFactoryForRtcConn::create_rtc_publish_stream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expire, ISrsRtcPacketReceiver *receiver, const SrsContextId &cid)
{
SrsRtcPublishStream *publisher = new SrsRtcPublishStream(exec, expire, receiver, cid);
publisher->rtc_sources_ = rtc_sources_;
return publisher;
}
MockRtcSourceForRtcConn::MockRtcSourceForRtcConn()
{
rtp_audio_count_ = 0;
rtp_video_count_ = 0;
}
MockRtcSourceForRtcConn::~MockRtcSourceForRtcConn()
{
}
srs_error_t MockRtcSourceForRtcConn::on_rtp(SrsRtpPacket *pkt)
{
if (pkt->frame_type_ == SrsFrameTypeAudio) {
rtp_audio_count_++;
} else if (pkt->frame_type_ == SrsFrameTypeVideo) {
rtp_video_count_++;
}
return srs_success;
}
// This test is used to verify the basic workflow of the RTC connection.
// It's finished with the help of AI, but each step is manually designed
// and verified. So this is not dominated by AI, but by humanbeing.
VOID TEST(RtcConnTest, ManuallyVerifyBasicWorkflowForPlayer)
{
srs_error_t err;
// Create mock dependencies FIRST (they must outlive the connection)
SrsUniquePtr<MockCircuitBreaker> mock_circuit_breaker(new MockCircuitBreaker());
SrsUniquePtr<MockConnectionManager> mock_conn_manager(new MockConnectionManager());
SrsUniquePtr<MockRtcSourceManager> mock_rtc_sources(new MockRtcSourceManager());
SrsUniquePtr<MockAppConfig> mock_config(new MockAppConfig());
SrsUniquePtr<MockDtlsCertificate> mock_dtls_certificate(new MockDtlsCertificate());
SrsUniquePtr<MockSdpFactory> mock_sdp_factory(new MockSdpFactory());
SrsUniquePtr<MockAppFactoryForRtcConn> mock_app_factory(new MockAppFactoryForRtcConn());
mock_config->rtc_dtls_role_ = "passive";
mock_dtls_certificate->fingerprint_ = "test-fingerprint";
mock_app_factory->rtc_sources_ = mock_rtc_sources.get();
mock_app_factory->mock_protocol_utility_ = new MockProtocolUtilityForRtcConn("192.168.1.100");
MockRtcSourceForRtcConn *mock_rtc_source = new MockRtcSourceForRtcConn();
mock_rtc_sources->mock_source_ = SrsSharedPtr<SrsRtcSource>(mock_rtc_source);
// Create a real ISrsRtcConnection using _srs_app_factory_
MockRtcAsyncTaskExecutor mock_exec;
SrsContextId cid;
cid.set_value("test-rtc-conn-player-workflow");
SrsUniquePtr<ISrsRtcConnection> conn_ptr(_srs_app_factory->create_rtc_connection(&mock_exec, cid));
SrsRtcConnection *conn = dynamic_cast<SrsRtcConnection *>(conn_ptr.get());
EXPECT_TRUE(conn != NULL);
// Mock the RTC conn, also mock the config in publisher_negotiator_ and player_negotiator_
conn->circuit_breaker_ = mock_circuit_breaker.get();
conn->conn_manager_ = mock_conn_manager.get();
conn->rtc_sources_ = mock_rtc_sources.get();
conn->config_ = mock_config.get();
conn->dtls_certificate_ = mock_dtls_certificate.get();
conn->app_factory_ = mock_app_factory.get();
SrsRtcPublisherNegotiator *pub_neg = dynamic_cast<SrsRtcPublisherNegotiator *>(conn->publisher_negotiator_);
pub_neg->config_ = mock_config.get();
SrsRtcPlayerNegotiator *play_neg = dynamic_cast<SrsRtcPlayerNegotiator *>(conn->player_negotiator_);
play_neg->config_ = mock_config.get();
play_neg->rtc_sources_ = mock_rtc_sources.get();
// Create RTC user config for add_player
SrsUniquePtr<SrsRtcUserConfig> ruc(new SrsRtcUserConfig());
if (true) {
srs_freep(ruc->req_);
ruc->req_ = new MockRtcAsyncCallRequest("test.vhost", "live", "stream1");
ruc->publish_ = false;
ruc->dtls_ = true;
ruc->srtp_ = true;
ruc->audio_before_video_ = false;
ruc->remote_sdp_str_ = mock_sdp_factory->create_chrome_player_offer();
HELPER_EXPECT_SUCCESS(ruc->remote_sdp_.parse(ruc->remote_sdp_str_));
}
// Add player, which negotiate the SDP and generate local SDP
SrsSdp local_sdp;
local_sdp.session_config_.dtls_role_ = mock_config->get_rtc_dtls_role(ruc->req_->vhost_);
if (true) {
HELPER_EXPECT_SUCCESS(conn->add_player(ruc.get(), local_sdp));
// Verify publishers and SSRC mappings
EXPECT_TRUE(conn->players_.size() == 1);
EXPECT_TRUE(conn->players_ssrc_map_.size() == 2);
// Verify the local SDP was generated with media information
EXPECT_TRUE(local_sdp.version_ == "0");
EXPECT_TRUE(local_sdp.group_policy_ == "BUNDLE");
EXPECT_TRUE(local_sdp.msids_.size() == 1);
EXPECT_TRUE(local_sdp.msids_[0] == "live/stream1");
EXPECT_TRUE(local_sdp.media_descs_.size() == 2);
// First should be audio media desc
SrsMediaDesc *audio_desc = &local_sdp.media_descs_[0];
EXPECT_TRUE(audio_desc->type_ == "audio");
EXPECT_FALSE(audio_desc->recvonly_);
EXPECT_TRUE(audio_desc->payload_types_.size() == 1);
EXPECT_TRUE(audio_desc->payload_types_[0].payload_type_ == mock_sdp_factory->audio_pt_);
EXPECT_TRUE(audio_desc->payload_types_[0].encoding_name_ == "opus");
EXPECT_TRUE(audio_desc->payload_types_[0].clock_rate_ == 48000);
// Second should be video media desc
SrsMediaDesc *video_desc = &local_sdp.media_descs_[1];
EXPECT_TRUE(video_desc->type_ == "video");
EXPECT_FALSE(video_desc->recvonly_);
EXPECT_TRUE(video_desc->payload_types_.size() == 1);
EXPECT_TRUE(video_desc->payload_types_[0].payload_type_ == mock_sdp_factory->video_pt_);
EXPECT_TRUE(video_desc->payload_types_[0].encoding_name_ == "H264");
EXPECT_TRUE(video_desc->payload_types_[0].clock_rate_ == 90000);
}
// Generate local SDP and setup SDP.
std::string username;
if (true) {
bool status = true;
conn->set_all_tracks_status(ruc->req_->get_stream_url(), ruc->publish_, status);
HELPER_EXPECT_SUCCESS(conn->generate_local_sdp(ruc.get(), local_sdp, username));
conn->set_remote_sdp(ruc->remote_sdp_);
conn->set_local_sdp(local_sdp);
conn->set_state_as_waiting_stun();
// Verify the local SDP was generated ice pwd
SrsMediaDesc *audio_desc = &local_sdp.media_descs_[0];
EXPECT_TRUE(!audio_desc->session_info_.ice_pwd_.empty());
EXPECT_TRUE(!audio_desc->session_info_.fingerprint_.empty());
EXPECT_TRUE(audio_desc->candidates_.size() == 1);
EXPECT_TRUE(audio_desc->candidates_[0].ip_ == "192.168.1.100");
EXPECT_TRUE(audio_desc->session_info_.setup_ == "passive");
SrsMediaDesc *video_desc = &local_sdp.media_descs_[1];
EXPECT_TRUE(!video_desc->session_info_.ice_pwd_.empty());
EXPECT_TRUE(!video_desc->session_info_.fingerprint_.empty());
EXPECT_TRUE(video_desc->candidates_.size() == 1);
EXPECT_TRUE(video_desc->candidates_[0].ip_ == "192.168.1.100");
EXPECT_TRUE(video_desc->session_info_.setup_ == "passive");
EXPECT_TRUE(local_sdp.session_negotiate_.dtls_role_ == "passive");
}
// Initialize the connection
if (true) {
HELPER_EXPECT_SUCCESS(conn->initialize(ruc->req_, ruc->dtls_, ruc->srtp_, username));
EXPECT_TRUE(conn->nack_enabled_);
}
// DTLS done, start player consumer
if (true) {
HELPER_EXPECT_SUCCESS(conn->on_dtls_handshake_done());
// Wait for coroutine to start. Normally it should be ready and stopped at wait for
// RTP packets from consumer.
srs_usleep(1 * SRS_UTIME_MILLISECONDS);
// Verify the consumer is created and started
EXPECT_TRUE(conn->players_.size() == 1);
SrsRtcPlayStream *player = dynamic_cast<SrsRtcPlayStream *>(conn->players_.begin()->second);
EXPECT_TRUE(player->is_started_);
// Stop the player
player->stop();
}
}
// This test is used to verify the basic workflow of the RTC connection.
// It's finished with the help of AI, but each step is manually designed
// and verified. So this is not dominated by AI, but by humanbeing.
VOID TEST(RtcConnTest, ManuallyVerifyBasicWorkflowForPublisher)
{
srs_error_t err;
// Create mock dependencies FIRST (they must outlive the connection)
SrsUniquePtr<MockCircuitBreaker> mock_circuit_breaker(new MockCircuitBreaker());
SrsUniquePtr<MockConnectionManager> mock_conn_manager(new MockConnectionManager());
SrsUniquePtr<MockRtcSourceManager> mock_rtc_sources(new MockRtcSourceManager());
SrsUniquePtr<MockAppConfig> mock_config(new MockAppConfig());
SrsUniquePtr<MockDtlsCertificate> mock_dtls_certificate(new MockDtlsCertificate());
SrsUniquePtr<MockSdpFactory> mock_sdp_factory(new MockSdpFactory());
SrsUniquePtr<MockAppFactoryForRtcConn> mock_app_factory(new MockAppFactoryForRtcConn());
SrsStreamPublishTokenManager token_manager;
mock_config->rtc_dtls_role_ = "passive";
mock_dtls_certificate->fingerprint_ = "test-fingerprint";
mock_app_factory->rtc_sources_ = mock_rtc_sources.get();
mock_app_factory->mock_protocol_utility_ = new MockProtocolUtilityForRtcConn("192.168.1.100");
MockRtcSourceForRtcConn *mock_rtc_source = new MockRtcSourceForRtcConn();
mock_rtc_sources->mock_source_ = SrsSharedPtr<SrsRtcSource>(mock_rtc_source);
// Create a real ISrsRtcConnection using _srs_app_factory_
MockRtcAsyncTaskExecutor mock_exec;
SrsContextId cid;
cid.set_value("test-rtc-conn-publisher-workflow");
SrsUniquePtr<ISrsRtcConnection> conn_ptr(_srs_app_factory->create_rtc_connection(&mock_exec, cid));
SrsRtcConnection *conn = dynamic_cast<SrsRtcConnection *>(conn_ptr.get());
EXPECT_TRUE(conn != NULL);
// Mock the RTC conn, also mock the config in publisher_negotiator_ and player_negotiator_
conn->circuit_breaker_ = mock_circuit_breaker.get();
conn->conn_manager_ = mock_conn_manager.get();
conn->rtc_sources_ = mock_rtc_sources.get();
conn->config_ = mock_config.get();
conn->dtls_certificate_ = mock_dtls_certificate.get();
conn->app_factory_ = mock_app_factory.get();
SrsRtcPublisherNegotiator *pub_neg = dynamic_cast<SrsRtcPublisherNegotiator *>(conn->publisher_negotiator_);
pub_neg->config_ = mock_config.get();
SrsRtcPlayerNegotiator *play_neg = dynamic_cast<SrsRtcPlayerNegotiator *>(conn->player_negotiator_);
play_neg->config_ = mock_config.get();
play_neg->rtc_sources_ = mock_rtc_sources.get();
// Create RTC user config for add_publisher
SrsUniquePtr<SrsRtcUserConfig> ruc(new SrsRtcUserConfig());
if (true) {
srs_freep(ruc->req_);
ruc->req_ = new MockRtcAsyncCallRequest("test.vhost", "live", "stream1");
ruc->publish_ = true;
ruc->dtls_ = true;
ruc->srtp_ = true;
ruc->audio_before_video_ = false;
ruc->remote_sdp_str_ = mock_sdp_factory->create_chrome_publisher_offer();
HELPER_EXPECT_SUCCESS(ruc->remote_sdp_.parse(ruc->remote_sdp_str_));
}
// Add publisher, which negotiate the SDP and generate local SDP
SrsSdp local_sdp;
local_sdp.session_config_.dtls_role_ = mock_config->get_rtc_dtls_role(ruc->req_->vhost_);
if (true) {
HELPER_EXPECT_SUCCESS(conn->add_publisher(ruc.get(), local_sdp));
// Verify publishers and SSRC mappings
EXPECT_TRUE(conn->publishers_.size() == 1);
EXPECT_TRUE(conn->publishers_ssrc_map_.size() == 2);
EXPECT_TRUE(conn->publishers_ssrc_map_.find(mock_sdp_factory->audio_ssrc_) != conn->publishers_ssrc_map_.end());
EXPECT_TRUE(conn->publishers_ssrc_map_.find(mock_sdp_factory->video_ssrc_) != conn->publishers_ssrc_map_.end());
// Verify the source stream desription, should have two tracks.
SrsRtcSourceDescription *stream_desc = mock_rtc_sources->mock_source_->stream_desc_;
EXPECT_TRUE(stream_desc->audio_track_desc_ != NULL);
EXPECT_TRUE(stream_desc->video_track_descs_.size() == 1);
// Verify the audio track ssrc and payload type.
EXPECT_TRUE(stream_desc->audio_track_desc_->ssrc_ == mock_sdp_factory->audio_ssrc_);
EXPECT_TRUE(stream_desc->audio_track_desc_->media_->pt_ == mock_sdp_factory->audio_pt_);
// Verify the video track ssrc and payload type.
EXPECT_TRUE(stream_desc->video_track_descs_[0]->ssrc_ == mock_sdp_factory->video_ssrc_);
EXPECT_TRUE(stream_desc->video_track_descs_[0]->media_->pt_ == mock_sdp_factory->video_pt_);
// Verify the local SDP was generated with media information
EXPECT_TRUE(local_sdp.version_ == "0");
EXPECT_TRUE(local_sdp.group_policy_ == "BUNDLE");
EXPECT_TRUE(local_sdp.msids_.size() == 1);
EXPECT_TRUE(local_sdp.msids_[0] == "live/stream1");
EXPECT_TRUE(local_sdp.media_descs_.size() == 2);
// First should be audio media desc
SrsMediaDesc *audio_desc = &local_sdp.media_descs_[0];
EXPECT_TRUE(audio_desc->type_ == "audio");
EXPECT_TRUE(audio_desc->recvonly_);
EXPECT_TRUE(audio_desc->payload_types_.size() == 1);
EXPECT_TRUE(audio_desc->payload_types_[0].payload_type_ == mock_sdp_factory->audio_pt_);
EXPECT_TRUE(audio_desc->payload_types_[0].encoding_name_ == "opus");
EXPECT_TRUE(audio_desc->payload_types_[0].clock_rate_ == 48000);
// Second should be video media desc
SrsMediaDesc *video_desc = &local_sdp.media_descs_[1];
EXPECT_TRUE(video_desc->type_ == "video");
EXPECT_TRUE(video_desc->recvonly_);
EXPECT_TRUE(video_desc->payload_types_.size() == 1);
EXPECT_TRUE(video_desc->payload_types_[0].payload_type_ == mock_sdp_factory->video_pt_);
EXPECT_TRUE(video_desc->payload_types_[0].encoding_name_ == "H264");
EXPECT_TRUE(video_desc->payload_types_[0].clock_rate_ == 90000);
}
// Generate local SDP and setup SDP.
std::string username;
if (true) {
bool status = true;
conn->set_all_tracks_status(ruc->req_->get_stream_url(), ruc->publish_, status);
HELPER_EXPECT_SUCCESS(conn->generate_local_sdp(ruc.get(), local_sdp, username));
conn->set_remote_sdp(ruc->remote_sdp_);
conn->set_local_sdp(local_sdp);
conn->set_state_as_waiting_stun();
// Verify the local SDP was generated ice pwd
SrsMediaDesc *audio_desc = &local_sdp.media_descs_[0];
EXPECT_TRUE(!audio_desc->session_info_.ice_pwd_.empty());
EXPECT_TRUE(!audio_desc->session_info_.fingerprint_.empty());
EXPECT_TRUE(audio_desc->candidates_.size() == 1);
EXPECT_TRUE(audio_desc->candidates_[0].ip_ == "192.168.1.100");
EXPECT_TRUE(audio_desc->session_info_.setup_ == "passive");
SrsMediaDesc *video_desc = &local_sdp.media_descs_[1];
EXPECT_TRUE(!video_desc->session_info_.ice_pwd_.empty());
EXPECT_TRUE(!video_desc->session_info_.fingerprint_.empty());
EXPECT_TRUE(video_desc->candidates_.size() == 1);
EXPECT_TRUE(video_desc->candidates_[0].ip_ == "192.168.1.100");
EXPECT_TRUE(video_desc->session_info_.setup_ == "passive");
EXPECT_TRUE(local_sdp.session_negotiate_.dtls_role_ == "passive");
}
// Initialize the connection
if (true) {
HELPER_EXPECT_SUCCESS(conn->initialize(ruc->req_, ruc->dtls_, ruc->srtp_, username));
EXPECT_TRUE(conn->nack_enabled_);
// Create and set publish token
SrsStreamPublishToken *publish_token_raw = NULL;
HELPER_EXPECT_SUCCESS(token_manager.acquire_token(ruc->req_, publish_token_raw));
SrsSharedPtr<ISrsStreamPublishToken> publish_token(publish_token_raw);
conn->set_publish_token(publish_token);
EXPECT_TRUE(conn->publish_token_->is_acquired());
}
// DTLS done, start publisher
SrsRtcPublishStream *publisher = NULL;
if (true) {
HELPER_EXPECT_SUCCESS(conn->on_dtls_handshake_done());
// Wait for coroutine to start. Normally it should be ready wait for PLI requests.
srs_usleep(1 * SRS_UTIME_MILLISECONDS);
// Verify the publisher is created and started
EXPECT_TRUE(conn->publishers_.size() == 1);
publisher = dynamic_cast<SrsRtcPublishStream *>(conn->publishers_.begin()->second);
EXPECT_TRUE(publisher->is_sender_started_);
}
// Got a RTP audio packet.
for (int i = 0; i < 3; i++) {
SrsRtpPacket pkt;
pkt.header_.set_ssrc(mock_sdp_factory->audio_ssrc_);
pkt.header_.set_sequence(100);
pkt.header_.set_timestamp(1000);
pkt.header_.set_payload_type(mock_sdp_factory->audio_pt_);
SrsUniquePtr<char[]> data(new char[1500]);
SrsBuffer buf(data.get(), 1500);
HELPER_EXPECT_SUCCESS(pkt.encode(&buf));
HELPER_EXPECT_SUCCESS(conn->on_rtp_cipher(data.get(), buf.pos()));
HELPER_EXPECT_SUCCESS(conn->on_rtp_plaintext(data.get(), buf.pos()));
EXPECT_EQ(mock_rtc_source->rtp_audio_count_, i + 1);
}
// Got a RTP video packet.
for (int i = 0; i < 3; i++) {
SrsRtpPacket pkt;
pkt.header_.set_ssrc(mock_sdp_factory->video_ssrc_);
pkt.header_.set_sequence(100);
pkt.header_.set_timestamp(1000);
pkt.header_.set_payload_type(mock_sdp_factory->video_pt_);
SrsUniquePtr<char[]> data(new char[1500]);
SrsBuffer buf(data.get(), 1500);
HELPER_EXPECT_SUCCESS(pkt.encode(&buf));
HELPER_EXPECT_SUCCESS(conn->on_rtp_cipher(data.get(), buf.pos()));
HELPER_EXPECT_SUCCESS(conn->on_rtp_plaintext(data.get(), buf.pos()));
EXPECT_EQ(mock_rtc_source->rtp_video_count_, i + 1);
}
// Stop the publisher
publisher->stop();
}

View File

@ -0,0 +1,80 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2013-2025 Winlin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SRS_UTEST_RTC_CONN_HPP
#define SRS_UTEST_RTC_CONN_HPP
#include <srs_utest.hpp>
#include <srs_app_factory.hpp>
#include <srs_protocol_utility.hpp>
#include <string>
#include <vector>
class MockAppFactoryForRtcConn;
class MockProtocolUtilityForRtcConn : public ISrsProtocolUtility
{
public:
std::vector<SrsIPAddress *> ips_;
std::string mock_ip_;
public:
MockProtocolUtilityForRtcConn(std::string ip);
virtual ~MockProtocolUtilityForRtcConn();
public:
virtual std::vector<SrsIPAddress *> &local_ips();
};
class MockAppFactoryForRtcConn : public SrsAppFactory
{
public:
ISrsRtcSourceManager *rtc_sources_;
MockProtocolUtilityForRtcConn *mock_protocol_utility_;
public:
MockAppFactoryForRtcConn();
virtual ~MockAppFactoryForRtcConn();
public:
virtual ISrsProtocolUtility *create_protocol_utility();
virtual ISrsRtcPublishStream *create_rtc_publish_stream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expire, ISrsRtcPacketReceiver *receiver, const SrsContextId &cid);
};
class MockRtcSourceForRtcConn : public SrsRtcSource
{
public:
int rtp_audio_count_;
int rtp_video_count_;
public:
MockRtcSourceForRtcConn();
virtual ~MockRtcSourceForRtcConn();
public:
virtual srs_error_t on_rtp(SrsRtpPacket *pkt);
};
#endif

View File

@ -23,11 +23,11 @@
#include <srs_utest_rtc_publishstream.hpp>
#include <srs_kernel_error.hpp>
#include <srs_app_rtc_conn.hpp>
#include <srs_app_rtc_source.hpp>
#include <srs_utest_mock.hpp>
#include <srs_kernel_error.hpp>
#include <srs_utest_app6.hpp>
#include <srs_utest_mock.hpp>
// This test is used to verify the basic workflow of the RTC publish stream.
// It's finished with the help of AI, but each step is manually designed
@ -77,7 +77,7 @@ VOID TEST(RtcPublishStreamTest, ManuallyVerifyBasicWorkflow)
// Verify is_sender_started_ flag is set
EXPECT_TRUE(publish_stream->is_sender_started_);
// Wait for coroutine to start. Normally it should be ready and stopped at wait
// Wait for coroutine to start. Normally it should be ready and stopped at wait
// for PLI requests.
srs_usleep(1 * SRS_UTIME_MILLISECONDS);
}
@ -102,4 +102,3 @@ VOID TEST(RtcPublishStreamTest, ManuallyVerifyBasicWorkflow)
publish_stream->rtc_sources_ = NULL;
publish_stream->stat_ = NULL;
}

View File

@ -27,4 +27,3 @@
#include <srs_utest.hpp>
#endif