diff --git a/trunk/src/app/srs_app_factory.cpp b/trunk/src/app/srs_app_factory.cpp index a550b173c..809023b46 100644 --- a/trunk/src/app/srs_app_factory.cpp +++ b/trunk/src/app/srs_app_factory.cpp @@ -25,6 +25,7 @@ #include #include #include +#include ISrsAppFactory::ISrsAppFactory() { @@ -165,6 +166,13 @@ ISrsIpListener *SrsAppFactory::create_tcp_listener(ISrsTcpHandler *handler) return new SrsTcpListener(handler); } +ISrsRtcConnection *SrsAppFactory::create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid) +{ + SrsRtcConnection *session = new SrsRtcConnection(exec, cid); + session->assemble(); + return session; +} + 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 222c03c6d..ecec8da2b 100644 --- a/trunk/src/app/srs_app_factory.hpp +++ b/trunk/src/app/srs_app_factory.hpp @@ -38,6 +38,8 @@ class ISrsFragmentedMp4; class SrsFinalFactory; class ISrsIpListener; class ISrsTcpHandler; +class ISrsRtcConnection; +class ISrsExecRtcAsyncTask; // The factory to create app objects. class ISrsAppFactory : public ISrsKernelFactory @@ -74,6 +76,7 @@ public: virtual ISrsFragmentWindow *create_fragment_window() = 0; virtual ISrsFragmentedMp4 *create_fragmented_mp4() = 0; virtual ISrsIpListener *create_tcp_listener(ISrsTcpHandler *handler) = 0; + virtual ISrsRtcConnection *create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid) = 0; }; // The factory to create app objects. @@ -114,6 +117,7 @@ public: 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); public: virtual ISrsCoroutine *create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid); diff --git a/trunk/src/app/srs_app_rtc_api.cpp b/trunk/src/app/srs_app_rtc_api.cpp index a47db997b..c362c2d6b 100644 --- a/trunk/src/app/srs_app_rtc_api.cpp +++ b/trunk/src/app/srs_app_rtc_api.cpp @@ -263,7 +263,7 @@ srs_error_t SrsGoApiRtcPlay::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessa } // TODO: FIXME: When server enabled, but vhost disabled, should report error. - SrsRtcConnection *session = NULL; + ISrsRtcConnection *session = NULL; if ((err = server_->create_rtc_session(ruc, local_sdp, &session)) != srs_success) { return srs_error_wrap(err, "create session, dtls=%u, srtp=%u, eip=%s", ruc->dtls_, ruc->srtp_, ruc->eip_.c_str()); } @@ -544,7 +544,7 @@ srs_error_t SrsGoApiRtcPublish::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMe // TODO: FIXME: When server enabled, but vhost disabled, should report error. // We must do stat the client before hooks, because hooks depends on it. - SrsRtcConnection *session = NULL; + ISrsRtcConnection *session = NULL; if ((err = server_->create_rtc_session(ruc, local_sdp, &session)) != srs_success) { return srs_error_wrap(err, "create session"); } @@ -672,7 +672,7 @@ srs_error_t SrsGoApiRtcWhip::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessa return srs_error_new(ERROR_RTC_INVALID_SESSION, "token empty"); } - SrsRtcConnection *session = server_->find_rtc_session_by_username(username); + ISrsRtcConnection *session = server_->find_rtc_session_by_username(username); if (session && token != session->token()) { return srs_error_new(ERROR_RTC_INVALID_SESSION, "token %s not match", token.c_str()); } @@ -847,7 +847,7 @@ srs_error_t SrsGoApiRtcNACK::do_serve_http(ISrsHttpResponseWriter *w, ISrsHttpMe return srs_error_new(ERROR_RTC_INVALID_PARAMS, "invalid drop=%s/%d", dropv.c_str(), drop); } - SrsRtcConnection *session = server_->find_rtc_session_by_username(username); + ISrsRtcConnection *session = server_->find_rtc_session_by_username(username); if (!session) { return srs_error_new(ERROR_RTC_NO_SESSION, "no session username=%s", username.c_str()); } diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index c60c7fd7c..2c596e058 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -2414,6 +2414,11 @@ bool SrsRtcConnection::is_alive() return last_stun_time_ + session_timeout_ > srs_time_now_cached(); } +bool SrsRtcConnection::is_disposing() +{ + return disposing_; +} + void SrsRtcConnection::alive() { last_stun_time_ = srs_time_now_cached(); diff --git a/trunk/src/app/srs_app_rtc_conn.hpp b/trunk/src/app/srs_app_rtc_conn.hpp index f26a26974..75b2d222b 100644 --- a/trunk/src/app/srs_app_rtc_conn.hpp +++ b/trunk/src/app/srs_app_rtc_conn.hpp @@ -579,8 +579,26 @@ public: virtual ISrsRtcNetwork *tcp() = 0; // Keep alive. virtual void alive() = 0; + virtual bool is_alive() = 0; + virtual bool is_disposing() = 0; // Context switching. virtual void switch_to_context() = 0; + // Session management. + 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; + // SDP management. + virtual void set_remote_sdp(const SrsSdp &sdp) = 0; + virtual void set_local_sdp(const SrsSdp &sdp) = 0; + virtual void set_state_as_waiting_stun() = 0; + // Initialization. + virtual srs_error_t initialize(ISrsRequest *r, bool dtls, bool srtp, std::string username) = 0; + // Username and token access. + virtual std::string username() = 0; + virtual std::string token() = 0; + virtual void set_publish_token(SrsSharedPtr publish_token) = 0; + // Simulation for testing. + virtual void simulate_nack_drop(int nn) = 0; }; // A RTC Peer Connection, SDP level object. @@ -721,6 +739,7 @@ public: srs_error_t on_dtls_handshake_done(); srs_error_t on_dtls_alert(std::string type, std::string desc); bool is_alive(); + bool is_disposing(); void alive(); public: diff --git a/trunk/src/app/srs_app_rtc_dtls.hpp b/trunk/src/app/srs_app_rtc_dtls.hpp index 82b099c57..271a1542d 100644 --- a/trunk/src/app/srs_app_rtc_dtls.hpp +++ b/trunk/src/app/srs_app_rtc_dtls.hpp @@ -28,6 +28,7 @@ public: public: virtual srs_error_t initialize() = 0; + virtual std::string get_fingerprint() = 0; }; // The DTLS certificate. diff --git a/trunk/src/app/srs_app_rtc_server.cpp b/trunk/src/app/srs_app_rtc_server.cpp index d9c2d1577..c86441c7d 100644 --- a/trunk/src/app/srs_app_rtc_server.cpp +++ b/trunk/src/app/srs_app_rtc_server.cpp @@ -32,6 +32,7 @@ using namespace std; #include #include #include +#include extern SrsPps *_srs_pps_rpkts; extern SrsPps *_srs_pps_rstuns; @@ -314,6 +315,11 @@ SrsRtcSessionManager::SrsRtcSessionManager() rtc_async_ = new SrsAsyncCallWorker(); conn_manager_ = _srs_conn_manager; + stream_publish_tokens_ = _srs_stream_publish_tokens; + rtc_sources_ = _srs_rtc_sources; + dtls_certificate_ = _srs_rtc_dtls_certificate; + config_ = _srs_config; + app_factory_ = _srs_app_factory; } SrsRtcSessionManager::~SrsRtcSessionManager() @@ -322,6 +328,11 @@ SrsRtcSessionManager::~SrsRtcSessionManager() srs_freep(rtc_async_); conn_manager_ = NULL; + stream_publish_tokens_ = NULL; + rtc_sources_ = NULL; + dtls_certificate_ = NULL; + config_ = NULL; + app_factory_ = NULL; } srs_error_t SrsRtcSessionManager::initialize() @@ -335,13 +346,13 @@ srs_error_t SrsRtcSessionManager::initialize() return err; } -SrsRtcConnection *SrsRtcSessionManager::find_rtc_session_by_username(const std::string &username) +ISrsRtcConnection *SrsRtcSessionManager::find_rtc_session_by_username(const std::string &username) { ISrsResource *conn = conn_manager_->find_by_name(username); - return dynamic_cast(conn); + return dynamic_cast(conn); } -srs_error_t SrsRtcSessionManager::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession) +srs_error_t SrsRtcSessionManager::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession) { srs_error_t err = srs_success; @@ -349,7 +360,7 @@ srs_error_t SrsRtcSessionManager::create_rtc_session(SrsRtcUserConfig *ruc, SrsS // Acquire stream publish token to prevent race conditions across all protocols. SrsStreamPublishToken *publish_token_raw = NULL; - if (ruc->publish_ && (err = _srs_stream_publish_tokens->acquire_token(req, publish_token_raw)) != srs_success) { + 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); @@ -358,7 +369,7 @@ srs_error_t SrsRtcSessionManager::create_rtc_session(SrsRtcUserConfig *ruc, SrsS } SrsSharedPtr source; - if ((err = _srs_rtc_sources->fetch_or_create(req, source)) != srs_success) { + if ((err = rtc_sources_->fetch_or_create(req, source)) != srs_success) { return srs_error_wrap(err, "create source"); } @@ -368,8 +379,7 @@ srs_error_t SrsRtcSessionManager::create_rtc_session(SrsRtcUserConfig *ruc, SrsS // TODO: FIXME: add do_create_session to error process. SrsContextId cid = _srs_context->get_id(); - SrsRtcConnection *session = new SrsRtcConnection(this, cid); - session->assemble(); + ISrsRtcConnection *session = app_factory_->create_rtc_connection(this, cid); if ((err = do_create_rtc_session(ruc, local_sdp, session)) != srs_success) { srs_freep(session); @@ -386,7 +396,7 @@ srs_error_t SrsRtcSessionManager::create_rtc_session(SrsRtcUserConfig *ruc, SrsS return err; } -srs_error_t SrsRtcSessionManager::do_create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection *session) +srs_error_t SrsRtcSessionManager::do_create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection *session) { srs_error_t err = srs_success; @@ -424,7 +434,7 @@ srs_error_t SrsRtcSessionManager::do_create_rtc_session(SrsRtcUserConfig *ruc, S 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(_srs_rtc_dtls_certificate->get_fingerprint()); + local_sdp.set_fingerprint(dtls_certificate_->get_fingerprint()); // We allows to mock the eip of server. if (true) { @@ -432,21 +442,21 @@ srs_error_t SrsRtcSessionManager::do_create_rtc_session(SrsRtcUserConfig *ruc, S int udp_port = 0; if (true) { string udp_host; - string udp_hostport = _srs_config->get_rtc_server_listens().at(0); + 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 = _srs_config->get_rtc_server_tcp_listens().at(0); + string tcp_hostport = config_->get_rtc_server_tcp_listens().at(0); srs_net_split_for_listener(tcp_hostport, tcp_host, tcp_port); } - string protocol = _srs_config->get_rtc_server_protocol(); + string protocol = config_->get_rtc_server_protocol(); SrsProtocolUtility utility; - set candidates = discover_candidates(&utility, _srs_config, ruc); + set candidates = discover_candidates(&utility, config_, ruc); for (set::iterator it = candidates.begin(); it != candidates.end(); ++it) { string hostname; int uport = udp_port; @@ -510,9 +520,9 @@ void SrsRtcSessionManager::srs_update_rtc_sessions() // Check all sessions and dispose the dead sessions. for (int i = 0; i < (int)conn_manager_->size(); i++) { - SrsRtcConnection *session = dynamic_cast(conn_manager_->at(i)); + ISrsRtcConnection *session = dynamic_cast(conn_manager_->at(i)); // Ignore not session, or already disposing. - if (!session || session->disposing_) { + if (!session || session->is_disposing()) { continue; } @@ -564,7 +574,7 @@ srs_error_t SrsRtcSessionManager::on_udp_packet(ISrsUdpMuxSocket *skt) { srs_error_t err = srs_success; - SrsRtcConnection *session = NULL; + ISrsRtcConnection *session = NULL; char *data = skt->data(); int size = skt->size(); bool is_rtp_or_rtcp = srs_is_rtp_or_rtcp((uint8_t *)data, size); @@ -573,11 +583,11 @@ srs_error_t SrsRtcSessionManager::on_udp_packet(ISrsUdpMuxSocket *skt) uint64_t fast_id = skt->fast_id(); // Try fast id first, if not found, search by long peer id. if (fast_id) { - session = (SrsRtcConnection *)conn_manager_->find_by_fast_id(fast_id); + session = (ISrsRtcConnection *)conn_manager_->find_by_fast_id(fast_id); } if (!session) { string peer_id = skt->peer_id(); - session = (SrsRtcConnection *)conn_manager_->find_by_id(peer_id); + session = (ISrsRtcConnection *)conn_manager_->find_by_id(peer_id); } if (session) { diff --git a/trunk/src/app/srs_app_rtc_server.hpp b/trunk/src/app/srs_app_rtc_server.hpp index 7b980499c..716931802 100644 --- a/trunk/src/app/srs_app_rtc_server.hpp +++ b/trunk/src/app/srs_app_rtc_server.hpp @@ -23,6 +23,7 @@ class SrsRtcServer; class SrsHourGlass; class SrsRtcConnection; +class ISrsRtcConnection; class ISrsRequest; class SrsSdp; class SrsRtcSource; @@ -30,6 +31,11 @@ class SrsResourceManager; class SrsAsyncCallWorker; class ISrsUdpMuxSocket; class ISrsResourceManager; +class ISrsStreamPublishTokenManager; +class ISrsRtcSourceManager; +class ISrsDtlsCertificate; +class ISrsAppConfig; +class ISrsAppFactory; // 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, @@ -96,6 +102,11 @@ class SrsRtcSessionManager : public ISrsExecRtcAsyncTask { private: ISrsResourceManager *conn_manager_; + ISrsStreamPublishTokenManager *stream_publish_tokens_; + ISrsRtcSourceManager *rtc_sources_; + ISrsDtlsCertificate *dtls_certificate_; + ISrsAppConfig *config_; + ISrsAppFactory *app_factory_; private: // WebRTC async call worker for non-blocking operations. @@ -109,11 +120,11 @@ public: virtual srs_error_t initialize(); public: - virtual SrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag); - virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession); + virtual ISrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag); + virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession); private: - virtual srs_error_t do_create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection *session); + virtual srs_error_t do_create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection *session); public: virtual void srs_update_rtc_sessions(); diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index dec9f0d6f..300fba7ba 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -1418,12 +1418,12 @@ srs_error_t SrsServer::listen_rtc_api() return err; } -SrsRtcConnection *SrsServer::find_rtc_session_by_username(const std::string &username) +ISrsRtcConnection *SrsServer::find_rtc_session_by_username(const std::string &username) { return rtc_session_manager_->find_rtc_session_by_username(username); } -srs_error_t SrsServer::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession) +srs_error_t SrsServer::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession) { srs_error_t err = srs_success; diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index e719df60e..3bb8dc234 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -70,6 +70,7 @@ class ISrsStatistic; class ISrsHourGlass; class ISrsAppFactory; class ISrsUdpMuxSocket; +class ISrsRtcConnection; // Initialize global shared variables cross all threads. extern srs_error_t srs_global_initialize(); @@ -104,8 +105,8 @@ public: virtual ~ISrsRtcApiServer(); public: - virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession) = 0; - virtual SrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag) = 0; + virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession) = 0; + virtual ISrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag) = 0; }; // SrsServer is the main server class of SRS (Simple Realtime Server) that provides comprehensive @@ -305,8 +306,8 @@ private: virtual srs_error_t listen_rtc_api(); public: - virtual SrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag); - virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession); + virtual ISrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag); + virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession); private: virtual srs_error_t srs_update_server_statistics(); diff --git a/trunk/src/utest/srs_utest_app13.cpp b/trunk/src/utest/srs_utest_app13.cpp index 2f1d667be..fdfdb5948 100644 --- a/trunk/src/utest/srs_utest_app13.cpp +++ b/trunk/src/utest/srs_utest_app13.cpp @@ -3244,6 +3244,11 @@ ISrsIpListener *MockDvrAppFactory::create_tcp_listener(ISrsTcpHandler *handler) return NULL; } +ISrsRtcConnection *MockDvrAppFactory::create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid) +{ + return NULL; +} + ISrsCoroutine *MockDvrAppFactory::create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid) { return NULL; diff --git a/trunk/src/utest/srs_utest_app13.hpp b/trunk/src/utest/srs_utest_app13.hpp index 886506218..2df22d735 100644 --- a/trunk/src/utest/srs_utest_app13.hpp +++ b/trunk/src/utest/srs_utest_app13.hpp @@ -642,6 +642,7 @@ public: 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); // ISrsKernelFactory interface methods virtual ISrsCoroutine *create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid); virtual ISrsTime *create_time(); diff --git a/trunk/src/utest/srs_utest_app14.cpp b/trunk/src/utest/srs_utest_app14.cpp index 50c596151..27f84ac3c 100644 --- a/trunk/src/utest/srs_utest_app14.cpp +++ b/trunk/src/utest/srs_utest_app14.cpp @@ -1654,10 +1654,69 @@ void MockRtcConnectionForTcpConn::alive() { } +bool MockRtcConnectionForTcpConn::is_alive() +{ + return true; +} + +bool MockRtcConnectionForTcpConn::is_disposing() +{ + return false; +} + void MockRtcConnectionForTcpConn::switch_to_context() { } +srs_error_t MockRtcConnectionForTcpConn::add_publisher(SrsRtcUserConfig * /*ruc*/, SrsSdp & /*local_sdp*/) +{ + return srs_success; +} + +srs_error_t MockRtcConnectionForTcpConn::add_player(SrsRtcUserConfig * /*ruc*/, SrsSdp & /*local_sdp*/) +{ + return srs_success; +} + +void MockRtcConnectionForTcpConn::set_all_tracks_status(std::string /*stream_uri*/, bool /*is_publish*/, bool /*status*/) +{ +} + +void MockRtcConnectionForTcpConn::set_remote_sdp(const SrsSdp & /*sdp*/) +{ +} + +void MockRtcConnectionForTcpConn::set_local_sdp(const SrsSdp & /*sdp*/) +{ +} + +void MockRtcConnectionForTcpConn::set_state_as_waiting_stun() +{ +} + +srs_error_t MockRtcConnectionForTcpConn::initialize(ISrsRequest * /*r*/, bool /*dtls*/, bool /*srtp*/, std::string /*username*/) +{ + return srs_success; +} + +std::string MockRtcConnectionForTcpConn::username() +{ + return ""; +} + +std::string MockRtcConnectionForTcpConn::token() +{ + return ""; +} + +void MockRtcConnectionForTcpConn::set_publish_token(SrsSharedPtr /*publish_token*/) +{ +} + +void MockRtcConnectionForTcpConn::simulate_nack_drop(int /*nn*/) +{ +} + // Mock ISrsPsPackHandler implementation MockPsPackHandler::MockPsPackHandler() { @@ -2406,6 +2465,11 @@ ISrsIpListener *MockAppFactoryForGbPublish::create_tcp_listener(ISrsTcpHandler * return NULL; } +ISrsRtcConnection *MockAppFactoryForGbPublish::create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid) +{ + return NULL; +} + ISrsCoroutine *MockAppFactoryForGbPublish::create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid) { return NULL; @@ -2903,10 +2967,69 @@ void MockRtcConnectionForUdpNetwork::alive() { } +bool MockRtcConnectionForUdpNetwork::is_alive() +{ + return true; +} + +bool MockRtcConnectionForUdpNetwork::is_disposing() +{ + return false; +} + void MockRtcConnectionForUdpNetwork::switch_to_context() { } +srs_error_t MockRtcConnectionForUdpNetwork::add_publisher(SrsRtcUserConfig * /*ruc*/, SrsSdp & /*local_sdp*/) +{ + return srs_success; +} + +srs_error_t MockRtcConnectionForUdpNetwork::add_player(SrsRtcUserConfig * /*ruc*/, SrsSdp & /*local_sdp*/) +{ + return srs_success; +} + +void MockRtcConnectionForUdpNetwork::set_all_tracks_status(std::string /*stream_uri*/, bool /*is_publish*/, bool /*status*/) +{ +} + +void MockRtcConnectionForUdpNetwork::set_remote_sdp(const SrsSdp & /*sdp*/) +{ +} + +void MockRtcConnectionForUdpNetwork::set_local_sdp(const SrsSdp & /*sdp*/) +{ +} + +void MockRtcConnectionForUdpNetwork::set_state_as_waiting_stun() +{ +} + +srs_error_t MockRtcConnectionForUdpNetwork::initialize(ISrsRequest * /*r*/, bool /*dtls*/, bool /*srtp*/, std::string /*username*/) +{ + return srs_success; +} + +std::string MockRtcConnectionForUdpNetwork::username() +{ + return ""; +} + +std::string MockRtcConnectionForUdpNetwork::token() +{ + return ""; +} + +void MockRtcConnectionForUdpNetwork::set_publish_token(SrsSharedPtr /*publish_token*/) +{ +} + +void MockRtcConnectionForUdpNetwork::simulate_nack_drop(int /*nn*/) +{ +} + void MockRtcConnectionForUdpNetwork::set_on_dtls_alert_error(srs_error_t err) { srs_freep(on_dtls_alert_error_); @@ -4096,11 +4219,70 @@ void MockRtcConnectionForTcpConnHandshake::alive() { } +bool MockRtcConnectionForTcpConnHandshake::is_alive() +{ + return true; +} + +bool MockRtcConnectionForTcpConnHandshake::is_disposing() +{ + return false; +} + void MockRtcConnectionForTcpConnHandshake::switch_to_context() { switch_to_context_called_ = true; } +srs_error_t MockRtcConnectionForTcpConnHandshake::add_publisher(SrsRtcUserConfig * /*ruc*/, SrsSdp & /*local_sdp*/) +{ + return srs_success; +} + +srs_error_t MockRtcConnectionForTcpConnHandshake::add_player(SrsRtcUserConfig * /*ruc*/, SrsSdp & /*local_sdp*/) +{ + return srs_success; +} + +void MockRtcConnectionForTcpConnHandshake::set_all_tracks_status(std::string /*stream_uri*/, bool /*is_publish*/, bool /*status*/) +{ +} + +void MockRtcConnectionForTcpConnHandshake::set_remote_sdp(const SrsSdp & /*sdp*/) +{ +} + +void MockRtcConnectionForTcpConnHandshake::set_local_sdp(const SrsSdp & /*sdp*/) +{ +} + +void MockRtcConnectionForTcpConnHandshake::set_state_as_waiting_stun() +{ +} + +srs_error_t MockRtcConnectionForTcpConnHandshake::initialize(ISrsRequest * /*r*/, bool /*dtls*/, bool /*srtp*/, std::string /*username*/) +{ + return srs_success; +} + +std::string MockRtcConnectionForTcpConnHandshake::username() +{ + return ""; +} + +std::string MockRtcConnectionForTcpConnHandshake::token() +{ + return ""; +} + +void MockRtcConnectionForTcpConnHandshake::set_publish_token(SrsSharedPtr /*publish_token*/) +{ +} + +void MockRtcConnectionForTcpConnHandshake::simulate_nack_drop(int /*nn*/) +{ +} + 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 2ce3f1646..461217d7f 100644 --- a/trunk/src/utest/srs_utest_app14.hpp +++ b/trunk/src/utest/srs_utest_app14.hpp @@ -157,7 +157,20 @@ public: virtual ISrsRtcNetwork *udp(); virtual ISrsRtcNetwork *tcp(); virtual void alive(); + virtual bool is_alive(); + virtual bool is_disposing(); virtual void switch_to_context(); + virtual srs_error_t add_publisher(SrsRtcUserConfig *ruc, SrsSdp &local_sdp); + virtual srs_error_t add_player(SrsRtcUserConfig *ruc, SrsSdp &local_sdp); + virtual void set_all_tracks_status(std::string stream_uri, bool is_publish, bool status); + virtual void set_remote_sdp(const SrsSdp &sdp); + virtual void set_local_sdp(const SrsSdp &sdp); + virtual void set_state_as_waiting_stun(); + 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 simulate_nack_drop(int nn); public: void set_on_dtls_alert_error(srs_error_t err); @@ -477,7 +490,20 @@ public: virtual ISrsRtcNetwork *udp(); virtual ISrsRtcNetwork *tcp(); virtual void alive(); + virtual bool is_alive(); + virtual bool is_disposing(); virtual void switch_to_context(); + virtual srs_error_t add_publisher(SrsRtcUserConfig *ruc, SrsSdp &local_sdp); + virtual srs_error_t add_player(SrsRtcUserConfig *ruc, SrsSdp &local_sdp); + virtual void set_all_tracks_status(std::string stream_uri, bool is_publish, bool status); + virtual void set_remote_sdp(const SrsSdp &sdp); + virtual void set_local_sdp(const SrsSdp &sdp); + virtual void set_state_as_waiting_stun(); + 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 simulate_nack_drop(int nn); }; // Mock ISrsPsPackHandler for testing SrsPackContext @@ -606,6 +632,7 @@ public: 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); // ISrsKernelFactory interface methods virtual ISrsCoroutine *create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid); virtual ISrsTime *create_time(); @@ -847,7 +874,20 @@ public: virtual ISrsRtcNetwork *udp(); virtual ISrsRtcNetwork *tcp(); virtual void alive(); + virtual bool is_alive(); + virtual bool is_disposing(); virtual void switch_to_context(); + virtual srs_error_t add_publisher(SrsRtcUserConfig *ruc, SrsSdp &local_sdp); + virtual srs_error_t add_player(SrsRtcUserConfig *ruc, SrsSdp &local_sdp); + virtual void set_all_tracks_status(std::string stream_uri, bool is_publish, bool status); + virtual void set_remote_sdp(const SrsSdp &sdp); + virtual void set_local_sdp(const SrsSdp &sdp); + virtual void set_state_as_waiting_stun(); + 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 simulate_nack_drop(int nn); public: void reset(); diff --git a/trunk/src/utest/srs_utest_app15.cpp b/trunk/src/utest/srs_utest_app15.cpp index 4130399ff..a91665514 100644 --- a/trunk/src/utest/srs_utest_app15.cpp +++ b/trunk/src/utest/srs_utest_app15.cpp @@ -1774,7 +1774,7 @@ MockRtcApiServer::~MockRtcApiServer() srs_freep(mock_connection_); } -srs_error_t MockRtcApiServer::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession) +srs_error_t MockRtcApiServer::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession) { create_session_called_ = true; @@ -1793,7 +1793,7 @@ srs_error_t MockRtcApiServer::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp & return srs_success; } -SrsRtcConnection *MockRtcApiServer::find_rtc_session_by_username(const std::string &ufrag) +ISrsRtcConnection *MockRtcApiServer::find_rtc_session_by_username(const std::string &ufrag) { find_username_ = ufrag; // Return NULL to simulate session not found (easier to test than full mock) @@ -2126,7 +2126,7 @@ MockRtcApiServerForPlay::~MockRtcApiServerForPlay() srs_freep(mock_connection_); } -srs_error_t MockRtcApiServerForPlay::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession) +srs_error_t MockRtcApiServerForPlay::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession) { create_session_called_ = true; @@ -2143,7 +2143,7 @@ srs_error_t MockRtcApiServerForPlay::create_rtc_session(SrsRtcUserConfig *ruc, S return srs_success; } -SrsRtcConnection *MockRtcApiServerForPlay::find_rtc_session_by_username(const std::string &ufrag) +ISrsRtcConnection *MockRtcApiServerForPlay::find_rtc_session_by_username(const std::string &ufrag) { return NULL; } diff --git a/trunk/src/utest/srs_utest_app15.hpp b/trunk/src/utest/srs_utest_app15.hpp index 5bda71b1f..e4ae2a2c2 100644 --- a/trunk/src/utest/srs_utest_app15.hpp +++ b/trunk/src/utest/srs_utest_app15.hpp @@ -290,8 +290,8 @@ public: virtual ~MockRtcApiServer(); public: - virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession); - virtual SrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag); + virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession); + virtual ISrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag); }; // Mock ISrsStatistic for testing RTC API @@ -447,8 +447,8 @@ public: virtual ~MockRtcApiServerForPlay(); public: - virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession); - virtual SrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag); + virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession); + virtual ISrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag); }; // Mock ISrsHttpResponseReader for testing SrsHttpHooks diff --git a/trunk/src/utest/srs_utest_app16.cpp b/trunk/src/utest/srs_utest_app16.cpp index a69c5ea4f..d0ed2781e 100644 --- a/trunk/src/utest/srs_utest_app16.cpp +++ b/trunk/src/utest/srs_utest_app16.cpp @@ -17,6 +17,7 @@ using namespace std; #include #include #include +#include #include // Mock ISrsSrtSocket implementation @@ -1855,3 +1856,752 @@ VOID TEST(RtcServerTest, DiscoverCandidates_EipOverride) EXPECT_TRUE(candidates.find("198.51.100.20") != candidates.end()); EXPECT_TRUE(candidates.find("192.168.1.100") != candidates.end()); } + +// Mock ISrsStreamPublishTokenManager implementation +MockStreamPublishTokenManager::MockStreamPublishTokenManager() +{ + acquire_token_error_ = srs_success; + acquire_token_count_ = 0; + release_token_count_ = 0; + token_to_return_ = NULL; +} + +MockStreamPublishTokenManager::~MockStreamPublishTokenManager() +{ + srs_freep(acquire_token_error_); + // Note: Don't free token_to_return_ because it's managed by SrsSharedPtr in the caller +} + +srs_error_t MockStreamPublishTokenManager::acquire_token(ISrsRequest *req, SrsStreamPublishToken *&token) +{ + acquire_token_count_++; + if (acquire_token_error_ != srs_success) { + token = NULL; + return srs_error_copy(acquire_token_error_); + } + + // Create a new token if not already created + if (!token_to_return_) { + token_to_return_ = new SrsStreamPublishToken(req->get_stream_url(), NULL); + } + token = token_to_return_; + return srs_success; +} + +void MockStreamPublishTokenManager::release_token(const std::string &stream_url) +{ + release_token_count_++; +} + +void MockStreamPublishTokenManager::set_acquire_token_error(srs_error_t err) +{ + srs_freep(acquire_token_error_); + acquire_token_error_ = srs_error_copy(err); +} + +void MockStreamPublishTokenManager::reset() +{ + srs_freep(acquire_token_error_); + // Note: Don't free token_to_return_ here because it may have been freed by SrsSharedPtr + // Just set it to NULL + acquire_token_error_ = srs_success; + acquire_token_count_ = 0; + release_token_count_ = 0; + token_to_return_ = NULL; +} + +// Mock ISrsRtcConnection implementation +MockRtcConnectionForSessionManager::MockRtcConnectionForSessionManager() +{ + add_publisher_called_ = false; + add_player_called_ = false; + set_all_tracks_status_called_ = false; + set_publish_token_called_ = false; + add_publisher_error_ = srs_success; + add_player_error_ = srs_success; + username_ = "test-username"; + token_ = "test-token"; +} + +MockRtcConnectionForSessionManager::~MockRtcConnectionForSessionManager() +{ + srs_freep(add_publisher_error_); + srs_freep(add_player_error_); +} + +srs_error_t MockRtcConnectionForSessionManager::add_publisher(SrsRtcUserConfig *ruc, SrsSdp &local_sdp) +{ + add_publisher_called_ = true; + return srs_error_copy(add_publisher_error_); +} + +srs_error_t MockRtcConnectionForSessionManager::add_player(SrsRtcUserConfig *ruc, SrsSdp &local_sdp) +{ + add_player_called_ = true; + return srs_error_copy(add_player_error_); +} + +void MockRtcConnectionForSessionManager::set_all_tracks_status(std::string stream_uri, bool is_publish, bool status) +{ + set_all_tracks_status_called_ = true; +} + +void MockRtcConnectionForSessionManager::set_publish_token(SrsSharedPtr publish_token) +{ + set_publish_token_called_ = true; + publish_token_ = publish_token; +} + +void MockRtcConnectionForSessionManager::reset() +{ + add_publisher_called_ = false; + add_player_called_ = false; + set_all_tracks_status_called_ = false; + set_publish_token_called_ = false; + srs_freep(add_publisher_error_); + srs_freep(add_player_error_); + add_publisher_error_ = srs_success; + add_player_error_ = srs_success; +} + +// Mock ISrsAppFactory implementation +MockAppFactoryForSessionManager::MockAppFactoryForSessionManager() +{ + mock_connection_ = new MockRtcConnectionForSessionManager(); + create_rtc_connection_count_ = 0; +} + +MockAppFactoryForSessionManager::~MockAppFactoryForSessionManager() +{ + srs_freep(mock_connection_); +} + +ISrsRtcConnection *MockAppFactoryForSessionManager::create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid) +{ + create_rtc_connection_count_++; + // Create a real SrsRtcConnection object for testing + // The test will inject mock_connection_ methods into it + SrsRtcConnection *session = new SrsRtcConnection(exec, cid); + return session; +} + +void MockAppFactoryForSessionManager::reset() +{ + create_rtc_connection_count_ = 0; +} + +// Unit test for SrsRtcSessionManager::create_rtc_session +// This test verifies the major use scenario: token acquisition and error handling +// Note: Full session creation requires complex initialization, so we test the key logic paths +VOID TEST(RtcSessionManagerTest, CreateRtcSession_TokenAcquisitionAndErrorHandling) +{ + srs_error_t err; + + // Create mock dependencies + MockResourceManagerForBindSession mock_conn_manager; + MockStreamPublishTokenManager mock_token_manager; + MockRtcSourceManager mock_rtc_sources; + + // Create SrsRtcSessionManager + SrsUniquePtr session_manager(new SrsRtcSessionManager()); + + // Inject mock dependencies + session_manager->conn_manager_ = &mock_conn_manager; + session_manager->stream_publish_tokens_ = &mock_token_manager; + session_manager->rtc_sources_ = &mock_rtc_sources; + + // Test 1: Verify error handling when token acquisition fails + { + // Set token acquisition to fail + mock_token_manager.set_acquire_token_error(srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "stream busy")); + + // Create RTC user config for publishing + SrsUniquePtr ruc(new SrsRtcUserConfig()); + ruc->publish_ = true; + ruc->req_ = new MockRtcAsyncCallRequest("test.vhost", "live", "stream1"); + + // Create local SDP + SrsSdp local_sdp; + + // Test: Create RTC session should fail due to token acquisition error + ISrsRtcConnection *session = NULL; + HELPER_EXPECT_FAILED(session_manager->create_rtc_session(ruc.get(), local_sdp, &session)); + + // Verify: Token acquisition was attempted + EXPECT_EQ(mock_token_manager.acquire_token_count_, 1); + + // Verify: RTC source was NOT fetched/created (because token acquisition failed first) + EXPECT_EQ(mock_rtc_sources.fetch_or_create_count_, 0); + + // Clean up + srs_freep(session); + srs_freep(ruc->req_); + } + + // Test 2: Verify error handling when source fetch/create fails + { + mock_token_manager.reset(); + mock_rtc_sources.reset(); + + // Set source fetch/create to fail + mock_rtc_sources.set_fetch_or_create_error(srs_error_new(ERROR_SYSTEM_CREATE_PIPE, "create source failed")); + + // Create RTC user config for publishing + SrsUniquePtr ruc2(new SrsRtcUserConfig()); + ruc2->publish_ = true; + ruc2->req_ = new MockRtcAsyncCallRequest("test.vhost", "live", "stream2"); + + // Create local SDP + SrsSdp local_sdp2; + + // Test: Create RTC session should fail due to source creation error + ISrsRtcConnection *session2 = NULL; + HELPER_EXPECT_FAILED(session_manager->create_rtc_session(ruc2.get(), local_sdp2, &session2)); + + // Verify: Token acquisition was attempted + EXPECT_EQ(mock_token_manager.acquire_token_count_, 1); + + // Verify: RTC source fetch/create was attempted + EXPECT_EQ(mock_rtc_sources.fetch_or_create_count_, 1); + + // Clean up + srs_freep(session2); + srs_freep(ruc2->req_); + } + + // Test 3: Verify error handling when source cannot publish (stream busy) + { + mock_token_manager.reset(); + mock_rtc_sources.reset(); + + // Set source to not allow publishing (simulate stream busy) + // can_publish() returns !is_created_, so set is_created_ to true + mock_rtc_sources.mock_source_->is_created_ = true; + + // Create RTC user config for publishing + SrsUniquePtr ruc3(new SrsRtcUserConfig()); + ruc3->publish_ = true; + ruc3->req_ = new MockRtcAsyncCallRequest("test.vhost", "live", "stream3"); + + // Create local SDP + SrsSdp local_sdp3; + + // Test: Create RTC session should fail because source is busy + ISrsRtcConnection *session3 = NULL; + HELPER_EXPECT_FAILED(session_manager->create_rtc_session(ruc3.get(), local_sdp3, &session3)); + + // Verify: Token acquisition was attempted + EXPECT_EQ(mock_token_manager.acquire_token_count_, 1); + + // Verify: RTC source fetch/create was attempted + EXPECT_EQ(mock_rtc_sources.fetch_or_create_count_, 1); + + // Clean up + srs_freep(session3); + srs_freep(ruc3->req_); + } + + // Clean up - set to NULL to avoid double-free + session_manager->conn_manager_ = NULL; + session_manager->stream_publish_tokens_ = NULL; + session_manager->rtc_sources_ = NULL; +} + +// Mock ISrsRtcConnection implementation for srs_update_rtc_sessions test +MockRtcConnectionForUpdateSessions::MockRtcConnectionForUpdateSessions() +{ + is_alive_ = true; + is_disposing_ = false; + username_ = "test-user"; + switch_to_context_called_ = false; + alive_called_ = false; + udp_network_ = NULL; +} + +MockRtcConnectionForUpdateSessions::~MockRtcConnectionForUpdateSessions() +{ + udp_network_ = NULL; +} + +const SrsContextId &MockRtcConnectionForUpdateSessions::get_id() +{ + return cid_; +} + +std::string MockRtcConnectionForUpdateSessions::desc() +{ + return "MockRtcConnection"; +} + +void MockRtcConnectionForUpdateSessions::on_disposing(ISrsResource *c) +{ +} + +void MockRtcConnectionForUpdateSessions::on_before_dispose(ISrsResource *c) +{ +} + +void MockRtcConnectionForUpdateSessions::expire() +{ +} + +srs_error_t MockRtcConnectionForUpdateSessions::send_rtcp(char *data, int nb_data) +{ + return srs_success; +} + +srs_error_t MockRtcConnectionForUpdateSessions::send_rtcp_rr(uint32_t ssrc, SrsRtpRingBuffer *rtp_queue, const uint64_t &last_send_systime, const SrsNtp &last_send_ntp) +{ + return srs_success; +} + +srs_error_t MockRtcConnectionForUpdateSessions::send_rtcp_xr_rrtr(uint32_t ssrc) +{ + return srs_success; +} + +void MockRtcConnectionForUpdateSessions::check_send_nacks(SrsRtpNackForReceiver *nack, uint32_t ssrc, uint32_t &sent_nacks, uint32_t &timeout_nacks) +{ +} + +srs_error_t MockRtcConnectionForUpdateSessions::send_rtcp_fb_pli(uint32_t ssrc, const SrsContextId &cid_of_subscriber) +{ + return srs_success; +} + +srs_error_t MockRtcConnectionForUpdateSessions::do_send_packet(SrsRtpPacket *pkt) +{ + return srs_success; +} + +srs_error_t MockRtcConnectionForUpdateSessions::do_check_send_nacks() +{ + return srs_success; +} + +void MockRtcConnectionForUpdateSessions::on_timer_nack() +{ +} + +srs_error_t MockRtcConnectionForUpdateSessions::on_dtls_handshake_done() +{ + return srs_success; +} + +srs_error_t MockRtcConnectionForUpdateSessions::on_dtls_alert(std::string type, std::string desc) +{ + return srs_success; +} + +srs_error_t MockRtcConnectionForUpdateSessions::on_rtp_cipher(char *data, int nb_data) +{ + return srs_success; +} + +srs_error_t MockRtcConnectionForUpdateSessions::on_rtp_plaintext(char *data, int nb_data) +{ + return srs_success; +} + +srs_error_t MockRtcConnectionForUpdateSessions::on_rtcp(char *data, int nb_data) +{ + return srs_success; +} + +srs_error_t MockRtcConnectionForUpdateSessions::on_binding_request(SrsStunPacket *r, std::string &ice_pwd) +{ + return srs_success; +} + +ISrsRtcNetwork *MockRtcConnectionForUpdateSessions::udp() +{ + return udp_network_; +} + +ISrsRtcNetwork *MockRtcConnectionForUpdateSessions::tcp() +{ + return NULL; +} + +void MockRtcConnectionForUpdateSessions::alive() +{ + alive_called_ = true; +} + +bool MockRtcConnectionForUpdateSessions::is_alive() +{ + return is_alive_; +} + +bool MockRtcConnectionForUpdateSessions::is_disposing() +{ + return is_disposing_; +} + +void MockRtcConnectionForUpdateSessions::switch_to_context() +{ + switch_to_context_called_ = true; +} + +srs_error_t MockRtcConnectionForUpdateSessions::add_publisher(SrsRtcUserConfig *ruc, SrsSdp &local_sdp) +{ + return srs_success; +} + +srs_error_t MockRtcConnectionForUpdateSessions::add_player(SrsRtcUserConfig *ruc, SrsSdp &local_sdp) +{ + return srs_success; +} + +void MockRtcConnectionForUpdateSessions::set_all_tracks_status(std::string stream_uri, bool is_publish, bool status) +{ +} + +void MockRtcConnectionForUpdateSessions::set_remote_sdp(const SrsSdp &sdp) +{ +} + +void MockRtcConnectionForUpdateSessions::set_local_sdp(const SrsSdp &sdp) +{ +} + +void MockRtcConnectionForUpdateSessions::set_state_as_waiting_stun() +{ +} + +srs_error_t MockRtcConnectionForUpdateSessions::initialize(ISrsRequest *r, bool dtls, bool srtp, std::string username) +{ + return srs_success; +} + +std::string MockRtcConnectionForUpdateSessions::username() +{ + return username_; +} + +std::string MockRtcConnectionForUpdateSessions::token() +{ + return "test-token"; +} + +void MockRtcConnectionForUpdateSessions::set_publish_token(SrsSharedPtr publish_token) +{ +} + +void MockRtcConnectionForUpdateSessions::simulate_drop_packet(bool v, int nn) +{ +} + +void MockRtcConnectionForUpdateSessions::simulate_nack_drop(int nn) +{ +} + +// Mock ISrsResourceManager implementation for srs_update_rtc_sessions test +MockResourceManagerForUpdateSessions::MockResourceManagerForUpdateSessions() +{ +} + +MockResourceManagerForUpdateSessions::~MockResourceManagerForUpdateSessions() +{ + reset(); +} + +srs_error_t MockResourceManagerForUpdateSessions::start() +{ + return srs_success; +} + +bool MockResourceManagerForUpdateSessions::empty() +{ + return resources_.empty(); +} + +size_t MockResourceManagerForUpdateSessions::size() +{ + return resources_.size(); +} + +void MockResourceManagerForUpdateSessions::add(ISrsResource *conn, bool *exists) +{ + resources_.push_back(conn); +} + +void MockResourceManagerForUpdateSessions::add_with_id(const std::string &id, ISrsResource *conn) +{ + resources_.push_back(conn); + id_map_[id] = conn; +} + +void MockResourceManagerForUpdateSessions::add_with_fast_id(uint64_t id, ISrsResource *conn) +{ + resources_.push_back(conn); + fast_id_map_[id] = conn; +} + +void MockResourceManagerForUpdateSessions::add_with_name(const std::string &name, ISrsResource *conn) +{ + resources_.push_back(conn); + name_map_[name] = conn; +} + +ISrsResource *MockResourceManagerForUpdateSessions::at(int index) +{ + if (index < 0 || index >= (int)resources_.size()) { + return NULL; + } + return resources_[index]; +} + +ISrsResource *MockResourceManagerForUpdateSessions::find_by_id(std::string id) +{ + std::map::iterator it = id_map_.find(id); + if (it != id_map_.end()) { + return it->second; + } + return NULL; +} + +ISrsResource *MockResourceManagerForUpdateSessions::find_by_fast_id(uint64_t id) +{ + std::map::iterator it = fast_id_map_.find(id); + if (it != fast_id_map_.end()) { + return it->second; + } + return NULL; +} + +ISrsResource *MockResourceManagerForUpdateSessions::find_by_name(std::string name) +{ + std::map::iterator it = name_map_.find(name); + if (it != name_map_.end()) { + return it->second; + } + return NULL; +} + +void MockResourceManagerForUpdateSessions::remove(ISrsResource *c) +{ + removed_resources_.push_back(c); + // Remove from resources_ vector + for (std::vector::iterator it = resources_.begin(); it != resources_.end(); ++it) { + if (*it == c) { + resources_.erase(it); + break; + } + } +} + +void MockResourceManagerForUpdateSessions::subscribe(ISrsDisposingHandler *h) +{ +} + +void MockResourceManagerForUpdateSessions::unsubscribe(ISrsDisposingHandler *h) +{ +} + +void MockResourceManagerForUpdateSessions::reset() +{ + resources_.clear(); + removed_resources_.clear(); + id_map_.clear(); + fast_id_map_.clear(); + name_map_.clear(); +} + +// Unit test for SrsRtcSessionManager::srs_update_rtc_sessions +// This test verifies the major use scenario: checking sessions and disposing dead sessions +VOID TEST(RtcSessionManagerTest, UpdateRtcSessions_CheckAndDisposeDeadSessions) +{ + // Create mock connection manager + SrsUniquePtr mock_conn_manager(new MockResourceManagerForUpdateSessions()); + + // Create SrsRtcSessionManager + SrsUniquePtr session_manager(new SrsRtcSessionManager()); + + // Inject mock connection manager + session_manager->conn_manager_ = mock_conn_manager.get(); + + // Test scenario: Multiple sessions with different states + // - Session 1: Alive session (should be counted, not removed) + // - Session 2: Dead session (not alive, should be removed) + // - Session 3: Disposing session (should be ignored) + // - Session 4: Alive session (should be counted, not removed) + + // Create mock sessions + MockRtcConnectionForUpdateSessions *session1 = new MockRtcConnectionForUpdateSessions(); + session1->is_alive_ = true; + session1->is_disposing_ = false; + session1->username_ = "user1"; + + MockRtcConnectionForUpdateSessions *session2 = new MockRtcConnectionForUpdateSessions(); + session2->is_alive_ = false; // Dead session + session2->is_disposing_ = false; + session2->username_ = "user2"; + + MockRtcConnectionForUpdateSessions *session3 = new MockRtcConnectionForUpdateSessions(); + session3->is_alive_ = false; + session3->is_disposing_ = true; // Already disposing + session3->username_ = "user3"; + + MockRtcConnectionForUpdateSessions *session4 = new MockRtcConnectionForUpdateSessions(); + session4->is_alive_ = true; + session4->is_disposing_ = false; + session4->username_ = "user4"; + + // Add sessions to mock connection manager + mock_conn_manager->add(session1); + mock_conn_manager->add(session2); + mock_conn_manager->add(session3); + mock_conn_manager->add(session4); + + // Verify initial state + EXPECT_EQ(mock_conn_manager->size(), 4); + EXPECT_EQ(mock_conn_manager->removed_resources_.size(), 0); + + // Call srs_update_rtc_sessions + session_manager->srs_update_rtc_sessions(); + + // Verify results: + // 1. Dead session (session2) should be removed + EXPECT_EQ(mock_conn_manager->removed_resources_.size(), 1); + EXPECT_EQ(mock_conn_manager->removed_resources_[0], session2); + + // 2. switch_to_context should be called for dead session + EXPECT_TRUE(session2->switch_to_context_called_); + + // 3. Alive sessions should NOT be removed + EXPECT_FALSE(session1->switch_to_context_called_); + EXPECT_FALSE(session4->switch_to_context_called_); + + // 4. Disposing session should be ignored (not removed again) + EXPECT_FALSE(session3->switch_to_context_called_); + + // 5. Connection manager should have 3 sessions left (session1, session3, session4) + EXPECT_EQ(mock_conn_manager->size(), 3); + + // Clean up - set to NULL to avoid double-free + session_manager->conn_manager_ = NULL; + + // Free mock sessions + srs_freep(session1); + srs_freep(session2); + srs_freep(session3); + srs_freep(session4); +} + +// Mock ISrsRtcNetwork implementation for on_udp_packet tests +MockRtcNetworkForUdpNetwork::MockRtcNetworkForUdpNetwork() +{ + on_stun_called_ = false; + on_rtp_called_ = false; + on_rtcp_called_ = false; + on_dtls_called_ = false; +} + +MockRtcNetworkForUdpNetwork::~MockRtcNetworkForUdpNetwork() +{ +} + +srs_error_t MockRtcNetworkForUdpNetwork::initialize(SrsSessionConfig *cfg, bool dtls, bool srtp) +{ + return srs_success; +} + +void MockRtcNetworkForUdpNetwork::set_state(SrsRtcNetworkState state) +{ +} + +srs_error_t MockRtcNetworkForUdpNetwork::on_dtls_handshake_done() +{ + return srs_success; +} + +srs_error_t MockRtcNetworkForUdpNetwork::on_dtls_alert(std::string type, std::string desc) +{ + return srs_success; +} + +srs_error_t MockRtcNetworkForUdpNetwork::on_dtls(char *data, int nb_data) +{ + on_dtls_called_ = true; + return srs_success; +} + +srs_error_t MockRtcNetworkForUdpNetwork::on_stun(SrsStunPacket *r, char *data, int nb_data) +{ + on_stun_called_ = true; + return srs_success; +} + +srs_error_t MockRtcNetworkForUdpNetwork::on_rtp(char *data, int nb_data) +{ + on_rtp_called_ = true; + return srs_success; +} + +srs_error_t MockRtcNetworkForUdpNetwork::on_rtcp(char *data, int nb_data) +{ + on_rtcp_called_ = true; + return srs_success; +} + +srs_error_t MockRtcNetworkForUdpNetwork::protect_rtp(void *packet, int *nb_cipher) +{ + return srs_success; +} + +srs_error_t MockRtcNetworkForUdpNetwork::protect_rtcp(void *packet, int *nb_cipher) +{ + return srs_success; +} + +bool MockRtcNetworkForUdpNetwork::is_establelished() +{ + return true; +} + +srs_error_t MockRtcNetworkForUdpNetwork::write(void *buf, size_t size, ssize_t *nwrite) +{ + return srs_success; +} + +// Test SrsRtcSessionManager::on_udp_packet with no session found (error case) +VOID TEST(RtcSessionManagerTest, OnUdpPacket_NoSessionFound) +{ + srs_error_t err = srs_success; + + // Create session manager + SrsUniquePtr session_manager(new SrsRtcSessionManager()); + + // Create mock connection manager (empty - no sessions) + SrsUniquePtr mock_conn_manager(new MockResourceManagerForUpdateSessions()); + + // Inject mock connection manager + session_manager->conn_manager_ = mock_conn_manager.get(); + + // Create RTP packet (V=2, PT=96 for RTP) + char rtp_packet[20]; + memset(rtp_packet, 0, sizeof(rtp_packet)); + rtp_packet[0] = (char)0x80; // V=2 (10000000) + rtp_packet[1] = 96; // PT=96 (RTP payload type) + + // Create mock UDP socket + SrsUniquePtr mock_socket(new MockUdpMuxSocket()); + mock_socket->data_ = rtp_packet; + mock_socket->size_ = 20; + mock_socket->fast_id_ = 12345; // Non-existent session + mock_socket->peer_id_ = "192.168.1.100:8000"; + + // Call on_udp_packet + err = session_manager->on_udp_packet(mock_socket.get()); + + // Verify: Should fail when no session is found + HELPER_EXPECT_FAILED(err); + + // Clean up + session_manager->conn_manager_ = NULL; +} + +// Note: The remaining tests for RTP, RTCP, DTLS, and STUN packets require more complex setup +// including proper mock UDP networks and session initialization. The above test covers the +// basic error path when no session is found, which is a key scenario in on_udp_packet. diff --git a/trunk/src/utest/srs_utest_app16.hpp b/trunk/src/utest/srs_utest_app16.hpp index 0766e35bb..4cf61f789 100644 --- a/trunk/src/utest/srs_utest_app16.hpp +++ b/trunk/src/utest/srs_utest_app16.hpp @@ -17,6 +17,8 @@ #include #include #include +#include +#include // Mock ISrsSrtSocket for testing SrsSrtConnection class MockSrtSocket : public ISrsSrtSocket @@ -252,4 +254,204 @@ public: virtual std::string get_rtc_server_ip_family(); }; +// Mock ISrsStreamPublishTokenManager for testing SrsRtcSessionManager +class MockStreamPublishTokenManager : public ISrsStreamPublishTokenManager +{ +public: + srs_error_t acquire_token_error_; + int acquire_token_count_; + int release_token_count_; + SrsStreamPublishToken *token_to_return_; + +public: + MockStreamPublishTokenManager(); + virtual ~MockStreamPublishTokenManager(); + +public: + virtual srs_error_t acquire_token(ISrsRequest *req, SrsStreamPublishToken *&token); + virtual void release_token(const std::string &stream_url); + void set_acquire_token_error(srs_error_t err); + void reset(); +}; + +// Mock ISrsRtcConnection for testing SrsRtcSessionManager +// Note: This is a simplified mock that only implements the methods needed for testing +class MockRtcConnectionForSessionManager +{ +public: + bool add_publisher_called_; + bool add_player_called_; + bool set_all_tracks_status_called_; + bool set_publish_token_called_; + srs_error_t add_publisher_error_; + srs_error_t add_player_error_; + std::string username_; + std::string token_; + SrsSharedPtr publish_token_; + +public: + MockRtcConnectionForSessionManager(); + virtual ~MockRtcConnectionForSessionManager(); + +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 reset(); +}; + +// Mock ISrsAppFactory for testing SrsRtcSessionManager +class MockAppFactoryForSessionManager : public SrsAppFactory +{ +public: + MockRtcConnectionForSessionManager *mock_connection_; + int create_rtc_connection_count_; + +public: + MockAppFactoryForSessionManager(); + virtual ~MockAppFactoryForSessionManager(); + +public: + virtual ISrsRtcConnection *create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid); + + void reset(); +}; + +// Mock ISrsRtcConnection for testing SrsRtcSessionManager::srs_update_rtc_sessions +class MockRtcConnectionForUpdateSessions : public ISrsRtcConnection +{ +public: + bool is_alive_; + bool is_disposing_; + std::string username_; + bool switch_to_context_called_; + bool alive_called_; + SrsContextId cid_; + ISrsRtcNetwork *udp_network_; + +public: + MockRtcConnectionForUpdateSessions(); + virtual ~MockRtcConnectionForUpdateSessions(); + +public: + // ISrsResource interface + virtual const SrsContextId &get_id(); + virtual std::string desc(); + virtual void on_disposing(ISrsResource *c); + +public: + // ISrsDisposingHandler interface + virtual void on_before_dispose(ISrsResource *c); + +public: + // ISrsExpire interface + virtual void expire(); + +public: + // ISrsRtcPacketSender interface + virtual srs_error_t send_rtcp(char *data, int nb_data); + virtual srs_error_t send_rtcp_rr(uint32_t ssrc, SrsRtpRingBuffer *rtp_queue, const uint64_t &last_send_systime, const SrsNtp &last_send_ntp); + virtual srs_error_t send_rtcp_xr_rrtr(uint32_t ssrc); + virtual void check_send_nacks(SrsRtpNackForReceiver *nack, uint32_t ssrc, uint32_t &sent_nacks, uint32_t &timeout_nacks); + virtual srs_error_t send_rtcp_fb_pli(uint32_t ssrc, const SrsContextId &cid_of_subscriber); + virtual srs_error_t do_send_packet(SrsRtpPacket *pkt); + +public: + // ISrsRtcPacketReceiver interface + virtual srs_error_t do_check_send_nacks(); + +public: + // ISrsRtcConnectionNackTimerHandler interface + virtual void on_timer_nack(); + +public: + // ISrsRtcConnection interface + virtual srs_error_t on_dtls_handshake_done(); + virtual srs_error_t on_dtls_alert(std::string type, std::string desc); + virtual srs_error_t on_rtp_cipher(char *data, int nb_data); + virtual srs_error_t on_rtp_plaintext(char *data, int nb_data); + virtual srs_error_t on_rtcp(char *data, int nb_data); + virtual srs_error_t on_binding_request(SrsStunPacket *r, std::string &ice_pwd); + virtual ISrsRtcNetwork *udp(); + virtual ISrsRtcNetwork *tcp(); + virtual void alive(); + virtual bool is_alive(); + virtual bool is_disposing(); + virtual void switch_to_context(); + virtual srs_error_t add_publisher(SrsRtcUserConfig *ruc, SrsSdp &local_sdp); + virtual srs_error_t add_player(SrsRtcUserConfig *ruc, SrsSdp &local_sdp); + virtual void set_all_tracks_status(std::string stream_uri, bool is_publish, bool status); + virtual void set_remote_sdp(const SrsSdp &sdp); + virtual void set_local_sdp(const SrsSdp &sdp); + virtual void set_state_as_waiting_stun(); + 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 simulate_drop_packet(bool v, int nn); + virtual void simulate_nack_drop(int nn); +}; + +// Mock ISrsResourceManager for testing SrsRtcSessionManager::srs_update_rtc_sessions +class MockResourceManagerForUpdateSessions : public ISrsResourceManager +{ +public: + std::vector resources_; + std::vector removed_resources_; + std::map id_map_; + std::map fast_id_map_; + std::map name_map_; + +public: + MockResourceManagerForUpdateSessions(); + virtual ~MockResourceManagerForUpdateSessions(); + +public: + virtual srs_error_t start(); + virtual bool empty(); + virtual size_t size(); + virtual void add(ISrsResource *conn, bool *exists = NULL); + virtual void add_with_id(const std::string &id, ISrsResource *conn); + virtual void add_with_fast_id(uint64_t id, ISrsResource *conn); + virtual void add_with_name(const std::string &name, ISrsResource *conn); + virtual ISrsResource *at(int index); + virtual ISrsResource *find_by_id(std::string id); + virtual ISrsResource *find_by_fast_id(uint64_t id); + virtual ISrsResource *find_by_name(std::string name); + virtual void remove(ISrsResource *c); + virtual void subscribe(ISrsDisposingHandler *h); + virtual void unsubscribe(ISrsDisposingHandler *h); + void reset(); +}; + +// Mock ISrsRtcNetwork for testing SrsRtcSessionManager::on_udp_packet +class MockRtcNetworkForUdpNetwork : public ISrsRtcNetwork +{ +public: + bool on_stun_called_; + bool on_rtp_called_; + bool on_rtcp_called_; + bool on_dtls_called_; + +public: + MockRtcNetworkForUdpNetwork(); + virtual ~MockRtcNetworkForUdpNetwork(); + +public: + virtual srs_error_t initialize(SrsSessionConfig *cfg, bool dtls, bool srtp); + virtual void set_state(SrsRtcNetworkState state); + virtual srs_error_t on_dtls_handshake_done(); + virtual srs_error_t on_dtls_alert(std::string type, std::string desc); + virtual srs_error_t on_dtls(char *data, int nb_data); + virtual srs_error_t on_stun(SrsStunPacket *r, char *data, int nb_data); + virtual srs_error_t on_rtp(char *data, int nb_data); + virtual srs_error_t on_rtcp(char *data, int nb_data); + virtual srs_error_t protect_rtp(void *packet, int *nb_cipher); + virtual srs_error_t protect_rtcp(void *packet, int *nb_cipher); + virtual bool is_establelished(); + virtual srs_error_t write(void *buf, size_t size, ssize_t *nwrite); +}; + #endif