AI: Add workflow utest for rtc conn.
This commit is contained in:
parent
8b76e1f6d2
commit
054d3a3563
2
trunk/configure
vendored
2
trunk/configure
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ public:
|
|||
};
|
||||
|
||||
// Mock implementation of ISrsRtcPublishStream for testing
|
||||
class MockRtcPublishStream : public ISrsRtcPublishStream
|
||||
class MockRtcPublishStream : public SrsRtcPublishStream
|
||||
{
|
||||
public:
|
||||
int request_keyframe_count_;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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_; }
|
||||
|
|
|
|||
462
trunk/src/utest/srs_utest_rtc_conn.cpp
Normal file
462
trunk/src/utest/srs_utest_rtc_conn.cpp
Normal 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();
|
||||
}
|
||||
80
trunk/src/utest/srs_utest_rtc_conn.hpp
Normal file
80
trunk/src/utest/srs_utest_rtc_conn.hpp
Normal 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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,4 +27,3 @@
|
|||
#include <srs_utest.hpp>
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user