From 21835c38b7f418d56eee78b1260dcc3eebdcb531 Mon Sep 17 00:00:00 2001
From: winlin <winlin@vip.126.com>
Date: Sat, 25 Jul 2020 09:33:18 +0800
Subject: [PATCH] RTC: Support multiple address for client. 4.0.36

---
 README.md                            |  1 +
 trunk/src/app/srs_app_rtc_api.cpp    |  3 +-
 trunk/src/app/srs_app_rtc_conn.cpp   | 95 ++++++++++++++++++----------
 trunk/src/app/srs_app_rtc_conn.hpp   | 16 +++--
 trunk/src/app/srs_app_rtc_server.cpp | 28 ++++----
 trunk/src/app/srs_app_rtc_server.hpp |  2 +-
 trunk/src/core/srs_core_version4.hpp |  2 +-
 7 files changed, 87 insertions(+), 60 deletions(-)

diff --git a/README.md b/README.md
index 1075f7b02..a8ceb58ca 100755
--- a/README.md
+++ b/README.md
@@ -159,6 +159,7 @@ For previous versions, please read:
 
 ## V4 changes
 
+* v4.0, 2020-07-25, RTC: Support multiple address for client. 4.0.36
 * v4.0, 2020-07-11, Refine log context with random string. 4.0.35
 * v4.0, 2020-07-04, Fix some bugs for RTC. 4.0.34
 * v4.0, 2020-07-03, Merge [#1830][bug #1830] to fix bugs in GB28181. 4.0.33
diff --git a/trunk/src/app/srs_app_rtc_api.cpp b/trunk/src/app/srs_app_rtc_api.cpp
index c2ff32a96..3a5791cb2 100644
--- a/trunk/src/app/srs_app_rtc_api.cpp
+++ b/trunk/src/app/srs_app_rtc_api.cpp
@@ -808,8 +808,7 @@ srs_error_t SrsGoApiRtcNACK::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe
 
     session->simulate_nack_drop(drop);
 
-    srs_trace("RTC NACK session peer_id=%s, username=%s, drop=%s/%d", session->peer_id().c_str(),
-        username.c_str(), dropv.c_str(), drop);
+    srs_trace("RTC: NACK session username=%s, drop=%s/%d", username.c_str(), dropv.c_str(), drop);
 
     return srs_success;
 }
diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp
index c737bf774..da29fceeb 100644
--- a/trunk/src/app/srs_app_rtc_conn.cpp
+++ b/trunk/src/app/srs_app_rtc_conn.cpp
@@ -113,7 +113,8 @@ srs_error_t SrsSecurityTransport::on_dtls_handshake_done()
         return err;
     }
 
-    srs_trace("RTC session=%s, DTLS handshake done.", session_->id().c_str());
+    // TODO: FIXME: Add cost for DTLS.
+    srs_trace("RTC: DTLS handshake done.");
 
     handshake_done = true;
     if ((err = srtp_initialize()) != srs_success) {
@@ -366,13 +367,13 @@ srs_error_t SrsRtcPlayStream::cycle()
     realtime = _srs_config->get_realtime_enabled(req->vhost, true);
     mw_msgs = _srs_config->get_mw_msgs(req->vhost, realtime, true);
 
-    srs_trace("RTC source url=%s, source_id=[%d][%s], encrypt=%d, realtime=%d, mw_msgs=%d", req->get_stream_url().c_str(),
+    // TODO: FIXME: Add cost in ms.
+    srs_trace("RTC: start play, url=%s, source_id=[%d][%s], encrypt=%d, realtime=%d, mw_msgs=%d", req->get_stream_url().c_str(),
         ::getpid(), source->source_id().c_str(), session_->encrypt, realtime, mw_msgs);
 
     SrsPithyPrint* pprint = SrsPithyPrint::create_rtc_play();
     SrsAutoFree(SrsPithyPrint, pprint);
 
-    srs_trace("RTC session=%s, start play", session_->id().c_str());
     bool stat_enabled = _srs_config->get_rtc_server_perf_stat();
     SrsStatistic* stat = SrsStatistic::instance();
 
@@ -1582,8 +1583,15 @@ SrsRtcConnection::~SrsRtcConnection()
     srs_freep(publisher_);
     srs_freep(transport_);
     srs_freep(req);
-    srs_freep(sendonly_skt);
     srs_freep(stat_);
+
+    // Note that we should never delete the sendonly_skt,
+    // it's just point to the object in peer_addresses_.
+    map<string, SrsUdpMuxSocket*>::iterator it;
+    for (it = peer_addresses_.begin(); it != peer_addresses_.end(); ++it) {
+        SrsUdpMuxSocket* addr = it->second;
+        srs_freep(addr);
+    }
 }
 
 SrsSdp* SrsRtcConnection::get_local_sdp()
@@ -1616,20 +1624,22 @@ void SrsRtcConnection::set_state(SrsRtcConnectionStateType state)
     state_ = state;
 }
 
-string SrsRtcConnection::id()
+string SrsRtcConnection::username()
 {
-    return peer_id_ + "/" + username_;
+    return username_;
 }
 
-
-string SrsRtcConnection::peer_id()
+vector<SrsUdpMuxSocket*> SrsRtcConnection::peer_addresses()
 {
-    return peer_id_;
-}
+    vector<SrsUdpMuxSocket*> addresses;
 
-string SrsRtcConnection::username()
-{
-    return username_;
+    map<string, SrsUdpMuxSocket*>::iterator it;
+    for (it = peer_addresses_.begin(); it != peer_addresses_.end(); ++it) {
+        SrsUdpMuxSocket* addr = it->second;
+        addresses.push_back(addr);
+    }
+
+    return addresses;
 }
 
 void SrsRtcConnection::set_encrypt(bool v)
@@ -1774,8 +1784,8 @@ srs_error_t SrsRtcConnection::initialize(SrsRtcStream* source, SrsRequest* r, bo
     session_timeout = _srs_config->get_rtc_stun_timeout(req->vhost);
     last_stun_time = srs_get_system_time();
 
-    srs_trace("RTC init session, DTLS(role=%s, version=%s), timeout=%dms",
-        cfg->dtls_role.c_str(), cfg->dtls_version.c_str(), srsu2msi(session_timeout));
+    srs_trace("RTC init session, user=%s, url=%s, DTLS(role=%s, version=%s), timeout=%dms", username.c_str(),
+        r->get_stream_url().c_str(), cfg->dtls_role.c_str(), cfg->dtls_version.c_str(), srsu2msi(session_timeout));
 
     return err;
 }
@@ -1792,9 +1802,7 @@ srs_error_t SrsRtcConnection::on_stun(SrsUdpMuxSocket* skt, SrsStunPacket* r)
 
     // We are running in the ice-lite(server) mode. If client have multi network interface,
     // we only choose one candidate pair which is determined by client.
-    if (!sendonly_skt || sendonly_skt->peer_id() != skt->peer_id()) {
-        update_sendonly_socket(skt);
-    }
+    update_sendonly_socket(skt);
 
     // Write STUN messages to blackhole.
     if (_srs_blackhole->blackhole) {
@@ -1865,8 +1873,8 @@ srs_error_t SrsRtcConnection::on_connection_established()
 {
     srs_error_t err = srs_success;
 
-    srs_trace("RTC %s session=%s, to=%dms connection established", (is_publisher_? "Publisher":"Subscriber"),
-        id().c_str(), srsu2msi(session_timeout));
+    srs_trace("RTC: session %s, to=%dms connection established", (is_publisher_? "Publisher":"Subscriber"),
+        srsu2msi(session_timeout));
 
     if (is_publisher_) {
         if ((err = start_publish()) != srs_success) {
@@ -1908,28 +1916,44 @@ bool SrsRtcConnection::is_stun_timeout()
     return last_stun_time + session_timeout < srs_get_system_time();
 }
 
-// TODO: FIXME: We should support multiple addresses, because client may use more than one addresses.
 void SrsRtcConnection::update_sendonly_socket(SrsUdpMuxSocket* skt)
 {
-    std::string old_peer_id;
+    // TODO: FIXME: Refine performance.
+    string prev_peer_id, peer_id = skt->peer_id();
     if (sendonly_skt) {
-        srs_trace("session %s address changed, update %s -> %s",
-            id().c_str(), sendonly_skt->peer_id().c_str(), skt->peer_id().c_str());
-        old_peer_id = sendonly_skt->peer_id();
+        prev_peer_id = sendonly_skt->peer_id();
     }
 
-    // Update the transport.
-    srs_freep(sendonly_skt);
-    sendonly_skt = skt->copy_sendonly();
+    // Ignore if same address.
+    if (prev_peer_id == peer_id) {
+        return;
+    }
 
-    // Update the sessions to handle packets from the new address.
-    peer_id_ = sendonly_skt->peer_id();
-    server_->insert_into_id_sessions(peer_id_, this);
+    // Detect address change.
+    if (prev_peer_id.empty()) {
+        srs_trace("RTC: session address init %s", peer_id.c_str());
+    } else {
+        srs_trace("RTC: session address changed, update %s -> %s, total %u", prev_peer_id.c_str(),
+            peer_id.c_str(), peer_addresses_.size());
+    }
 
-    // Remove the old address.
-    if (!old_peer_id.empty()) {
-        server_->remove_id_sessions(old_peer_id);
+    // Find object from cache.
+    SrsUdpMuxSocket* addr_cache = NULL;
+    if (true) {
+        map<string, SrsUdpMuxSocket*>::iterator it = peer_addresses_.find(peer_id);
+        if (it != peer_addresses_.end()) {
+            addr_cache = it->second;
+        }
+    }
+
+    // If no cache, build cache and setup the relations in connection.
+    if (!addr_cache) {
+        peer_addresses_[peer_id] = addr_cache = skt->copy_sendonly();
+        server_->insert_into_id_sessions(peer_id, this);
     }
+
+    // Update the transport.
+    sendonly_skt = addr_cache;
 }
 
 void SrsRtcConnection::check_send_nacks(SrsRtpNackForReceiver* nack, uint32_t ssrc)
@@ -2248,7 +2272,8 @@ srs_error_t SrsRtcConnection::on_binding_request(SrsStunPacket* r)
 
     if (state_ == WAITING_STUN) {
         state_ = DOING_DTLS_HANDSHAKE;
-        srs_trace("RTC session=%s, STUN done, waiting DTLS handshake.", id().c_str());
+        // TODO: FIXME: Add cost.
+        srs_trace("RTC: session STUN done, waiting DTLS handshake.");
 
         if((err = transport_->start_active_handshake()) != srs_success) {
             return srs_error_wrap(err, "fail to dtls handshake");
diff --git a/trunk/src/app/srs_app_rtc_conn.hpp b/trunk/src/app/srs_app_rtc_conn.hpp
index a7e4726af..d3a2c127d 100644
--- a/trunk/src/app/srs_app_rtc_conn.hpp
+++ b/trunk/src/app/srs_app_rtc_conn.hpp
@@ -323,9 +323,12 @@ private:
     SrsRtcPublishStream* publisher_;
     bool is_publisher_;
 private:
-    SrsUdpMuxSocket* sendonly_skt;
+    // The local:remote username, such as m5x0n128:jvOm where local name is m5x0n128.
     std::string username_;
-    std::string peer_id_;
+    // The peer address, client maybe use more than one address, it's the current selected one.
+    SrsUdpMuxSocket* sendonly_skt;
+    // The address list, client may use multiple addresses.
+    std::map<std::string, SrsUdpMuxSocket*> peer_addresses_;
 private:
     // The timeout of session, keep alive by STUN ping pong.
     srs_utime_t session_timeout;
@@ -343,6 +346,7 @@ private:
     SrsSdp remote_sdp;
     SrsSdp local_sdp;
 public:
+    // TODO: FIXME: Remove dead code.
     // User debugging parameters, overwrite config.
     std::string sequence_startup;
     std::string sequence_delta;
@@ -364,12 +368,10 @@ public:
     // Connection level state machine, for ARQ of UDP packets.
     SrsRtcConnectionStateType state();
     void set_state(SrsRtcConnectionStateType state);
-    // TODO: FIXME: Rename it.
-    std::string id();
-    // TODO: FIXME: Rename it.
-    std::string peer_id();
-    // TODO: FIXME: Rename it.
+    // Get username pair for this connection, used as ID of session.
     std::string username();
+    // Get all addresses client used.
+    std::vector<SrsUdpMuxSocket*> peer_addresses();
 public:
     void set_encrypt(bool v);
     void switch_to_context();
diff --git a/trunk/src/app/srs_app_rtc_server.cpp b/trunk/src/app/srs_app_rtc_server.cpp
index ce8412367..e2b101636 100644
--- a/trunk/src/app/srs_app_rtc_server.cpp
+++ b/trunk/src/app/srs_app_rtc_server.cpp
@@ -542,17 +542,20 @@ void SrsRtcServer::destroy(SrsRtcConnection* session)
 
     std::map<std::string, SrsRtcConnection*>::iterator it;
 
-    if ((it = map_username_session.find(session->username())) != map_username_session.end()) {
+    string username = session->username();
+    if ((it = map_username_session.find(username)) != map_username_session.end()) {
         map_username_session.erase(it);
     }
 
-    if ((it = map_id_session.find(session->peer_id())) != map_id_session.end()) {
-        map_id_session.erase(it);
+    vector<SrsUdpMuxSocket*> addresses = session->peer_addresses();
+    for (int i = 0; i < (int)addresses.size(); i++) {
+        SrsUdpMuxSocket* addr = addresses.at(i);
+        map_id_session.erase(addr->peer_id());
     }
 
     SrsContextRestore(_srs_context->get_id());
     session->switch_to_context();
-    srs_trace("RTC session=%s, destroy, summary: %s", session->id().c_str(), session->stat_->summary().c_str());
+    srs_trace("RTC: session destroy, username=%s, summary: %s", username.c_str(), session->stat_->summary().c_str());
 
     zombies_.push_back(session);
 }
@@ -562,14 +565,6 @@ bool SrsRtcServer::insert_into_id_sessions(const string& peer_id, SrsRtcConnecti
     return map_id_session.insert(make_pair(peer_id, session)).second;
 }
 
-void SrsRtcServer::remove_id_sessions(const string& peer_id)
-{
-    std::map<std::string, SrsRtcConnection*>::iterator it;
-    if ((it = map_id_session.find(peer_id)) != map_id_session.end()) {
-        map_id_session.erase(it);
-    }
-}
-
 void SrsRtcServer::check_and_clean_timeout_session()
 {
     map<string, SrsRtcConnection*>::iterator iter = map_username_session.begin();
@@ -585,14 +580,19 @@ void SrsRtcServer::check_and_clean_timeout_session()
         // Now, we got the RTC session to cleanup, switch to its context
         // to make all logs write to the "correct" pid+cid.
         session->switch_to_context();
-        srs_trace("RTC session=%s, STUN timeout, summary: %s", session->id().c_str(), session->stat_->summary().c_str());
+        srs_trace("RTC: session STUN timeout, summary: %s", session->stat_->summary().c_str());
 
         session->disposing_ = true;
         zombies_.push_back(session);
 
         // Use C++98 style: https://stackoverflow.com/a/4636230
         map_username_session.erase(iter++);
-        map_id_session.erase(session->peer_id());
+
+        vector<SrsUdpMuxSocket*> addresses = session->peer_addresses();
+        for (int i = 0; i < (int)addresses.size(); i++) {
+            SrsUdpMuxSocket* addr = addresses.at(i);
+            map_id_session.erase(addr->peer_id());
+        }
 
         if (handler) {
             handler->on_timeout(session);
diff --git a/trunk/src/app/srs_app_rtc_server.hpp b/trunk/src/app/srs_app_rtc_server.hpp
index 3a878aa7e..f44428023 100644
--- a/trunk/src/app/srs_app_rtc_server.hpp
+++ b/trunk/src/app/srs_app_rtc_server.hpp
@@ -118,7 +118,7 @@ public:
     void destroy(SrsRtcConnection* session);
 public:
     bool insert_into_id_sessions(const std::string& peer_id, SrsRtcConnection* session);
-    void remove_id_sessions(const std::string& peer_id);
+    // TODO: FIXME: Change to private.
     void check_and_clean_timeout_session();
     int nn_sessions();
     SrsRtcConnection* find_session_by_username(const std::string& ufrag);
diff --git a/trunk/src/core/srs_core_version4.hpp b/trunk/src/core/srs_core_version4.hpp
index 0ca8ba1f3..035cfb349 100644
--- a/trunk/src/core/srs_core_version4.hpp
+++ b/trunk/src/core/srs_core_version4.hpp
@@ -24,6 +24,6 @@
 #ifndef SRS_CORE_VERSION4_HPP
 #define SRS_CORE_VERSION4_HPP
 
-#define SRS_VERSION4_REVISION 35
+#define SRS_VERSION4_REVISION 36
 
 #endif