diff --git a/trunk/configure b/trunk/configure index 1863c0866..735a56edb 100755 --- a/trunk/configure +++ b/trunk/configure @@ -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 diff --git a/trunk/src/app/srs_app_factory.cpp b/trunk/src/app/srs_app_factory.cpp index c7638fdf9..db4e92619 100644 --- a/trunk/src/app/srs_app_factory.cpp +++ b/trunk/src/app/srs_app_factory.cpp @@ -23,6 +23,7 @@ #ifdef SRS_RTSP #include #endif +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include 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); diff --git a/trunk/src/app/srs_app_factory.hpp b/trunk/src/app/srs_app_factory.hpp index 13a2150da..214adacb3 100644 --- a/trunk/src/app/srs_app_factory.hpp +++ b/trunk/src/app/srs_app_factory.hpp @@ -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); diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index 868efa925..216b757d5 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -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::iterator it = publishers_.begin(); it != publishers_.end(); ++it) { - SrsRtcPublishStream *publisher = it->second; + for (map::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::iterator it = players_.begin(); it != players_.end(); ++it) { - SrsRtcPlayStream *player = it->second; + for (map::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 publish_token) +void SrsRtcConnection::set_publish_token(SrsSharedPtr 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 utility(app_factory_->create_protocol_utility()); + set candidates = discover_candidates(utility.get(), config_, ruc); + for (set::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 v = vector(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::iterator it = publishers_ssrc_map_.find(ssrc); + map::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::iterator it = players_ssrc_map_.find(ssrc); + map::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::iterator it = publishers_ssrc_map_.find(ssrc); + map::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::iterator it = publishers_.begin(); it != publishers_.end(); ++it) { + for (map::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::iterator it = players_.begin(); it != players_.end(); ++it) { + for (map::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::iterator it; + std::map::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::iterator it = publishers_.begin(); it != publishers_.end(); ++it) { - SrsRtcPublishStream *publisher = it->second; + for (map::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::iterator it = publishers_.find(stream_uri); + map::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::iterator it = players_.find(stream_uri); + map::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::mapget_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::iterator it = sub_relations.begin(); it != sub_relations.end(); ++it) { SrsRtcTrackDescription *track_desc = it->second; - map::iterator it_player = players_ssrc_map_.find(track_desc->ssrc_); + map::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 track_descs = source->get_track_desc("video", ""); - if (!track_descs.empty()) { - SrsRtcTrackDescription *first_track = track_descs.at(0); + std::vector 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"); diff --git a/trunk/src/app/srs_app_rtc_conn.hpp b/trunk/src/app/srs_app_rtc_conn.hpp index 7698e9c94..288481084 100644 --- a/trunk/src/app/srs_app_rtc_conn.hpp +++ b/trunk/src/app/srs_app_rtc_conn.hpp @@ -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 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 - audio_tracks_; + std::vector audio_tracks_; std::vector 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 publish_token) = 0; + virtual void set_publish_token(SrsSharedPtr 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 - players_; + std::map players_; // key: player track's ssrc - std::map players_ssrc_map_; + std::map players_ssrc_map_; // key: stream id - std::map publishers_; + std::map publishers_; // key: publisher track's ssrc - std::map publishers_ssrc_map_; + std::map 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 publish_token_; + SrsSharedPtr 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 publish_token); + void set_publish_token(SrsSharedPtr 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 &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 &sub_relations); diff --git a/trunk/src/app/srs_app_rtc_network.hpp b/trunk/src/app/srs_app_rtc_network.hpp index 8fef2ceba..620b616ad 100644 --- a/trunk/src/app/srs_app_rtc_network.hpp +++ b/trunk/src/app/srs_app_rtc_network.hpp @@ -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); diff --git a/trunk/src/app/srs_app_rtc_server.cpp b/trunk/src/app/srs_app_rtc_server.cpp index f56e14a0d..424db5de5 100644 --- a/trunk/src/app/srs_app_rtc_server.cpp +++ b/trunk/src/app/srs_app_rtc_server.cpp @@ -219,7 +219,7 @@ srs_error_t api_server_as_candidates(ISrsAppConfig *config, string api, set discover_candidates(SrsProtocolUtility *utility, ISrsAppConfig *config, SrsRtcUserConfig *ruc) +set 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 publish_token(publish_token_raw); + SrsSharedPtr 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 candidates = discover_candidates(&utility, config_, ruc); - for (set::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 v = vector(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); diff --git a/trunk/src/app/srs_app_rtc_server.hpp b/trunk/src/app/srs_app_rtc_server.hpp index 7aa81d392..58f83f4a9 100644 --- a/trunk/src/app/srs_app_rtc_server.hpp +++ b/trunk/src/app/srs_app_rtc_server.hpp @@ -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 discover_candidates(SrsProtocolUtility *utility, ISrsAppConfig *config, SrsRtcUserConfig *ruc); +extern std::set 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); diff --git a/trunk/src/app/srs_app_rtc_source.cpp b/trunk/src/app/srs_app_rtc_source.cpp index 5ead4963f..6ba964878 100644 --- a/trunk/src/app/srs_app_rtc_source.cpp +++ b/trunk/src/app/srs_app_rtc_source.cpp @@ -379,14 +379,6 @@ SrsSharedPtr 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. diff --git a/trunk/src/app/srs_app_rtc_source.hpp b/trunk/src/app/srs_app_rtc_source.hpp index aa37ac76e..1ee7899f3 100644 --- a/trunk/src/app/srs_app_rtc_source.hpp +++ b/trunk/src/app/srs_app_rtc_source.hpp @@ -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 { diff --git a/trunk/src/app/srs_app_stream_token.cpp b/trunk/src/app/srs_app_stream_token.cpp index 9ef24c9f6..7ce52c4b9 100644 --- a/trunk/src/app/srs_app_stream_token.cpp +++ b/trunk/src/app/srs_app_stream_token.cpp @@ -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; diff --git a/trunk/src/app/srs_app_stream_token.hpp b/trunk/src/app/srs_app_stream_token.hpp index 187c6e5ef..6b1923cfa 100644 --- a/trunk/src/app/srs_app_stream_token.hpp +++ b/trunk/src/app/srs_app_stream_token.hpp @@ -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: diff --git a/trunk/src/protocol/srs_protocol_utility.cpp b/trunk/src/protocol/srs_protocol_utility.cpp index 57afbb632..390eb46c1 100644 --- a/trunk/src/protocol/srs_protocol_utility.cpp +++ b/trunk/src/protocol/srs_protocol_utility.cpp @@ -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() { } diff --git a/trunk/src/protocol/srs_protocol_utility.hpp b/trunk/src/protocol/srs_protocol_utility.hpp index 889ee72db..125bd7800 100644 --- a/trunk/src/protocol/srs_protocol_utility.hpp +++ b/trunk/src/protocol/srs_protocol_utility.hpp @@ -118,8 +118,19 @@ struct SrsIPAddress { bool is_loopback_; }; +// The interface for protocol utility. +class ISrsProtocolUtility +{ +public: + ISrsProtocolUtility(); + virtual ~ISrsProtocolUtility(); + +public: + virtual std::vector &local_ips() = 0; +}; + // The utility functions for protocol. -class SrsProtocolUtility +class SrsProtocolUtility : public ISrsProtocolUtility { public: SrsProtocolUtility(); diff --git a/trunk/src/utest/srs_utest_app13.cpp b/trunk/src/utest/srs_utest_app13.cpp index 99620e545..c905621d3 100644 --- a/trunk/src/utest/srs_utest_app13.cpp +++ b/trunk/src/utest/srs_utest_app13.cpp @@ -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; diff --git a/trunk/src/utest/srs_utest_app13.hpp b/trunk/src/utest/srs_utest_app13.hpp index 8f160ff92..32b95f01c 100644 --- a/trunk/src/utest/srs_utest_app13.hpp +++ b/trunk/src/utest/srs_utest_app13.hpp @@ -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 diff --git a/trunk/src/utest/srs_utest_app14.cpp b/trunk/src/utest/srs_utest_app14.cpp index bb074b56b..75a52e2e4 100644 --- a/trunk/src/utest/srs_utest_app14.cpp +++ b/trunk/src/utest/srs_utest_app14.cpp @@ -1641,7 +1641,7 @@ std::string MockRtcConnectionForTcpConn::token() return ""; } -void MockRtcConnectionForTcpConn::set_publish_token(SrsSharedPtr /*publish_token*/) +void MockRtcConnectionForTcpConn::set_publish_token(SrsSharedPtr /*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 /*publish_token*/) +void MockRtcConnectionForUdpNetwork::set_publish_token(SrsSharedPtr /*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 /*publish_token*/) +void MockRtcConnectionForTcpConnHandshake::set_publish_token(SrsSharedPtr /*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; diff --git a/trunk/src/utest/srs_utest_app14.hpp b/trunk/src/utest/srs_utest_app14.hpp index 4185f9c70..5ac7ac54b 100644 --- a/trunk/src/utest/srs_utest_app14.hpp +++ b/trunk/src/utest/srs_utest_app14.hpp @@ -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 publish_token); + virtual void set_publish_token(SrsSharedPtr 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 publish_token); + virtual void set_publish_token(SrsSharedPtr 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 publish_token); + virtual void set_publish_token(SrsSharedPtr 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(); diff --git a/trunk/src/utest/srs_utest_app16.cpp b/trunk/src/utest/srs_utest_app16.cpp index 15680f8bd..82938660b 100644 --- a/trunk/src/utest/srs_utest_app16.cpp +++ b/trunk/src/utest/srs_utest_app16.cpp @@ -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 publish_token) +void MockRtcConnectionForSessionManager::set_publish_token(SrsSharedPtr 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 publish_token) +void MockRtcConnectionForUpdateSessions::set_publish_token(SrsSharedPtr 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() { diff --git a/trunk/src/utest/srs_utest_app16.hpp b/trunk/src/utest/srs_utest_app16.hpp index 42f023267..074a1c526 100644 --- a/trunk/src/utest/srs_utest_app16.hpp +++ b/trunk/src/utest/srs_utest_app16.hpp @@ -289,7 +289,7 @@ public: srs_error_t add_player_error_; std::string username_; std::string token_; - SrsSharedPtr publish_token_; + SrsSharedPtr 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 publish_token); + void set_publish_token(SrsSharedPtr 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 publish_token); + virtual void set_publish_token(SrsSharedPtr 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(); }; diff --git a/trunk/src/utest/srs_utest_app2.cpp b/trunk/src/utest/srs_utest_app2.cpp index 366a14a81..321d0f03b 100644 --- a/trunk/src/utest/srs_utest_app2.cpp +++ b/trunk/src/utest/srs_utest_app2.cpp @@ -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; diff --git a/trunk/src/utest/srs_utest_app2.hpp b/trunk/src/utest/srs_utest_app2.hpp index b5b23edc4..9a7ac129f 100644 --- a/trunk/src/utest/srs_utest_app2.hpp +++ b/trunk/src/utest/srs_utest_app2.hpp @@ -90,7 +90,7 @@ public: }; // Mock implementation of ISrsRtcPublishStream for testing -class MockRtcPublishStream : public ISrsRtcPublishStream +class MockRtcPublishStream : public SrsRtcPublishStream { public: int request_keyframe_count_; diff --git a/trunk/src/utest/srs_utest_app7.cpp b/trunk/src/utest/srs_utest_app7.cpp index ed9b50125..428d00cbb 100644 --- a/trunk/src/utest/srs_utest_app7.cpp +++ b/trunk/src/utest/srs_utest_app7.cpp @@ -1356,7 +1356,7 @@ VOID TEST(SrsRtcConnectionTest, TestConnectionBasicOperations) // Test set_publish_token SrsStreamPublishTokenManager token_manager; - SrsSharedPtr publish_token(new SrsStreamPublishToken("/live/test", &token_manager)); + SrsSharedPtr 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 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 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 ruc(new SrsRtcUserConfig()); ruc->req_ = mock_request->copy(); @@ -2636,9 +2633,6 @@ VOID TEST(SrsRtcPlayerNegotiatorTest, TypicalUseScenario) // Create mock request for initialization SrsUniquePtr 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 ruc(new SrsRtcUserConfig()); ruc->req_ = mock_request->copy(); diff --git a/trunk/src/utest/srs_utest_app_rtc2rtmp.hpp b/trunk/src/utest/srs_utest_app_rtc2rtmp.hpp index a7fcd8b4f..4d8eedbf3 100644 --- a/trunk/src/utest/srs_utest_app_rtc2rtmp.hpp +++ b/trunk/src/utest/srs_utest_app_rtc2rtmp.hpp @@ -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(); diff --git a/trunk/src/utest/srs_utest_mock.cpp b/trunk/src/utest/srs_utest_mock.cpp index e6c2ca2d7..64e80996b 100644 --- a/trunk/src/utest/srs_utest_mock.cpp +++ b/trunk/src/utest/srs_utest_mock.cpp @@ -6,10 +6,173 @@ #include +#include +#include +#include +#include +#include +#include #include #include #include #include +#ifdef SRS_GB28181 +#include +#endif +#include +#include +#include +#include +#include +#ifdef SRS_RTSP +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +// 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 &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 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) diff --git a/trunk/src/utest/srs_utest_mock.hpp b/trunk/src/utest/srs_utest_mock.hpp index 2c76d5e17..120c60b15 100644 --- a/trunk/src/utest/srs_utest_mock.hpp +++ b/trunk/src/utest/srs_utest_mock.hpp @@ -13,16 +13,78 @@ #include // Include necessary SRS headers for interfaces +#include #include +#include +#include #include +#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include +#ifdef SRS_GB28181 +#include +#endif +#include // 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 get_rtc_server_tcp_listens() { return std::vector(); } + virtual std::vector get_rtc_server_tcp_listens() + { + std::vector v; + v.push_back("127.0.0.1:8000"); + return v; + } virtual std::string get_rtc_server_protocol() { return "udp"; } - virtual std::vector get_rtc_server_listens() { return std::vector(); } + virtual std::vector get_rtc_server_listens() + { + std::vector 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_; } diff --git a/trunk/src/utest/srs_utest_rtc_conn.cpp b/trunk/src/utest/srs_utest_rtc_conn.cpp new file mode 100644 index 000000000..c374d3ba1 --- /dev/null +++ b/trunk/src/utest/srs_utest_rtc_conn.cpp @@ -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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MockProtocolUtilityForRtcConn::MockProtocolUtilityForRtcConn(std::string ip) +{ + mock_ip_ = ip; +} + +MockProtocolUtilityForRtcConn::~MockProtocolUtilityForRtcConn() +{ +} + +std::vector &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 mock_circuit_breaker(new MockCircuitBreaker()); + SrsUniquePtr mock_conn_manager(new MockConnectionManager()); + SrsUniquePtr mock_rtc_sources(new MockRtcSourceManager()); + SrsUniquePtr mock_config(new MockAppConfig()); + SrsUniquePtr mock_dtls_certificate(new MockDtlsCertificate()); + SrsUniquePtr mock_sdp_factory(new MockSdpFactory()); + SrsUniquePtr 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(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 conn_ptr(_srs_app_factory->create_rtc_connection(&mock_exec, cid)); + SrsRtcConnection *conn = dynamic_cast(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(conn->publisher_negotiator_); + pub_neg->config_ = mock_config.get(); + SrsRtcPlayerNegotiator *play_neg = dynamic_cast(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 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(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 mock_circuit_breaker(new MockCircuitBreaker()); + SrsUniquePtr mock_conn_manager(new MockConnectionManager()); + SrsUniquePtr mock_rtc_sources(new MockRtcSourceManager()); + SrsUniquePtr mock_config(new MockAppConfig()); + SrsUniquePtr mock_dtls_certificate(new MockDtlsCertificate()); + SrsUniquePtr mock_sdp_factory(new MockSdpFactory()); + SrsUniquePtr 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(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 conn_ptr(_srs_app_factory->create_rtc_connection(&mock_exec, cid)); + SrsRtcConnection *conn = dynamic_cast(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(conn->publisher_negotiator_); + pub_neg->config_ = mock_config.get(); + SrsRtcPlayerNegotiator *play_neg = dynamic_cast(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 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 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(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 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 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(); +} diff --git a/trunk/src/utest/srs_utest_rtc_conn.hpp b/trunk/src/utest/srs_utest_rtc_conn.hpp new file mode 100644 index 000000000..a916b75a9 --- /dev/null +++ b/trunk/src/utest/srs_utest_rtc_conn.hpp @@ -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 + +#include +#include + +#include +#include + +class MockAppFactoryForRtcConn; + +class MockProtocolUtilityForRtcConn : public ISrsProtocolUtility +{ +public: + std::vector ips_; + std::string mock_ip_; + +public: + MockProtocolUtilityForRtcConn(std::string ip); + virtual ~MockProtocolUtilityForRtcConn(); + +public: + virtual std::vector &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 diff --git a/trunk/src/utest/srs_utest_rtc_publishstream.cpp b/trunk/src/utest/srs_utest_rtc_publishstream.cpp index d377420bb..25ce9021c 100644 --- a/trunk/src/utest/srs_utest_rtc_publishstream.cpp +++ b/trunk/src/utest/srs_utest_rtc_publishstream.cpp @@ -23,11 +23,11 @@ #include -#include #include #include -#include +#include #include +#include // 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; } - diff --git a/trunk/src/utest/srs_utest_rtc_publishstream.hpp b/trunk/src/utest/srs_utest_rtc_publishstream.hpp index 0efc16f1e..d6474d37f 100644 --- a/trunk/src/utest/srs_utest_rtc_publishstream.hpp +++ b/trunk/src/utest/srs_utest_rtc_publishstream.hpp @@ -27,4 +27,3 @@ #include #endif -