mirror of
				https://github.com/kunkundi/crossdesk.git
				synced 2025-10-26 20:25:34 +08:00 
			
		
		
		
	[feat] Implementation for trickle ice
This commit is contained in:
		| @@ -24,12 +24,15 @@ IceAgent::~IceAgent() { | |||||||
| } | } | ||||||
|  |  | ||||||
| int IceAgent::CreateIceAgent(nice_cb_state_changed_t on_state_changed, | int IceAgent::CreateIceAgent(nice_cb_state_changed_t on_state_changed, | ||||||
|                              nice_cb_candidate_t on_candidate, |                              nice_cb_new_candidate_t on_new_candidate, | ||||||
|                              nice_cb_gathering_done_t on_gathering_done, |                              nice_cb_gathering_done_t on_gathering_done, | ||||||
|  |                              nice_cb_new_selected_pair_t on_new_selected_pair, | ||||||
|                              nice_cb_recv_t on_recv, void *user_ptr) { |                              nice_cb_recv_t on_recv, void *user_ptr) { | ||||||
|   destroyed_ = false; |   destroyed_ = false; | ||||||
|   on_state_changed_ = on_state_changed; |   on_state_changed_ = on_state_changed; | ||||||
|   on_candidate_ = on_candidate; |   on_new_selected_pair_ = on_new_selected_pair; | ||||||
|  |   on_new_candidate_ = on_new_candidate; | ||||||
|  |  | ||||||
|   on_gathering_done_ = on_gathering_done; |   on_gathering_done_ = on_gathering_done; | ||||||
|   on_recv_ = on_recv; |   on_recv_ = on_recv; | ||||||
|   user_ptr_ = user_ptr; |   user_ptr_ = user_ptr; | ||||||
| @@ -41,9 +44,9 @@ int IceAgent::CreateIceAgent(nice_cb_state_changed_t on_state_changed, | |||||||
|   nice_thread_.reset(new std::thread([this]() { |   nice_thread_.reset(new std::thread([this]() { | ||||||
|     gloop_ = g_main_loop_new(nullptr, false); |     gloop_ = g_main_loop_new(nullptr, false); | ||||||
|  |  | ||||||
|     agent_ = nice_agent_new_full(g_main_loop_get_context(gloop_), |     agent_ = nice_agent_new_full( | ||||||
|                                  NICE_COMPATIBILITY_RFC5245, |         g_main_loop_get_context(gloop_), NICE_COMPATIBILITY_RFC5245, | ||||||
|                                  (NiceAgentOption)(NICE_AGENT_OPTION_RELIABLE)); |         (NiceAgentOption)(NICE_AGENT_OPTION_ICE_TRICKLE)); | ||||||
|  |  | ||||||
|     if (agent_ == nullptr) { |     if (agent_ == nullptr) { | ||||||
|       LOG_ERROR("Failed to create agent_"); |       LOG_ERROR("Failed to create agent_"); | ||||||
| @@ -51,30 +54,37 @@ int IceAgent::CreateIceAgent(nice_cb_state_changed_t on_state_changed, | |||||||
|  |  | ||||||
|     g_object_set(agent_, "stun-server", stun_ip_.c_str(), nullptr); |     g_object_set(agent_, "stun-server", stun_ip_.c_str(), nullptr); | ||||||
|     g_object_set(agent_, "stun-server-port", stun_port_, nullptr); |     g_object_set(agent_, "stun-server-port", stun_port_, nullptr); | ||||||
|  |  | ||||||
|     g_object_set(agent_, "controlling-mode", controlling_, nullptr); |     g_object_set(agent_, "controlling-mode", controlling_, nullptr); | ||||||
|  |     // g_object_set(agent_, "ice-trickle", true, nullptr); | ||||||
|  |  | ||||||
|     g_signal_connect(agent_, "candidate-gathering-done", |     g_signal_connect(agent_, "candidate-gathering-done", | ||||||
|                      G_CALLBACK(on_gathering_done_), user_ptr_); |                      G_CALLBACK(on_gathering_done_), user_ptr_); | ||||||
|     g_signal_connect(agent_, "new-selected-pair", G_CALLBACK(on_candidate_), |     g_signal_connect(agent_, "new-selected-pair", | ||||||
|  |                      G_CALLBACK(on_new_selected_pair_), user_ptr_); | ||||||
|  |     g_signal_connect(agent_, "new-candidate", G_CALLBACK(on_new_candidate_), | ||||||
|                      user_ptr_); |                      user_ptr_); | ||||||
|     g_signal_connect(agent_, "component-state-changed", |     g_signal_connect(agent_, "component-state-changed", | ||||||
|                      G_CALLBACK(on_state_changed_), user_ptr_); |                      G_CALLBACK(on_state_changed_), user_ptr_); | ||||||
|  |  | ||||||
|     stream_id_ = nice_agent_add_stream(agent_, 1); |     stream_id_ = nice_agent_add_stream(agent_, n_components_); | ||||||
|     if (stream_id_ == 0) { |     if (stream_id_ == 0) { | ||||||
|       LOG_ERROR("Failed to add stream"); |       LOG_ERROR("Failed to add stream"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     nice_agent_set_stream_name(agent_, stream_id_, "video"); |     nice_agent_set_stream_name(agent_, stream_id_, "video"); | ||||||
|  |  | ||||||
|     nice_agent_set_relay_info(agent_, stream_id_, 1, turn_ip_.c_str(), |     nice_agent_set_relay_info(agent_, stream_id_, n_components_, | ||||||
|                               turn_port_, turn_username_.c_str(), |                               turn_ip_.c_str(), turn_port_, | ||||||
|                               turn_password_.c_str(), NICE_RELAY_TYPE_TURN_UDP); |                               turn_username_.c_str(), turn_password_.c_str(), | ||||||
|  |                               NICE_RELAY_TYPE_TURN_UDP); | ||||||
|  |  | ||||||
|     // g_object_set(agent_, "force-relay", true, NULL); |     // g_object_set(agent_, "ice-tcp", false, "ice-udp", true, "force-relay", | ||||||
|  |     // true, | ||||||
|  |     //              NULL); | ||||||
|  |  | ||||||
|     nice_agent_attach_recv(agent_, stream_id_, 1, |     // nice_agent_set_remote_credentials(agent_, stream_id_, ufrag, password); | ||||||
|  |  | ||||||
|  |     nice_agent_attach_recv(agent_, stream_id_, NICE_COMPONENT_TYPE_RTP, | ||||||
|                            g_main_loop_get_context(gloop_), on_recv_, |                            g_main_loop_get_context(gloop_), on_recv_, | ||||||
|                            user_ptr_); |                            user_ptr_); | ||||||
|  |  | ||||||
| @@ -120,6 +130,32 @@ int IceAgent::DestroyIceAgent() { | |||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int IceAgent::GetLocalCredentials() { | ||||||
|  |   if (!nice_inited_) { | ||||||
|  |     LOG_ERROR("Nice agent has not been initialized"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (nullptr == agent_) { | ||||||
|  |     LOG_ERROR("Nice agent is nullptr"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (destroyed_) { | ||||||
|  |     LOG_ERROR("Nice agent is destroyed"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   nice_agent_get_local_credentials(agent_, stream_id_, &ice_ufrag_, | ||||||
|  |                                    &ice_password_); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | char *IceAgent::GetLocalUfrag() { return ufrag_; } | ||||||
|  |  | ||||||
|  | char *IceAgent::GetLocalPassword() { return password_; } | ||||||
|  |  | ||||||
| char *IceAgent::GenerateLocalSdp() { | char *IceAgent::GenerateLocalSdp() { | ||||||
|   if (!nice_inited_) { |   if (!nice_inited_) { | ||||||
|     LOG_ERROR("Nice agent has not been initialized"); |     LOG_ERROR("Nice agent has not been initialized"); | ||||||
| @@ -162,11 +198,38 @@ int IceAgent::SetRemoteSdp(const char *remote_sdp) { | |||||||
|   if (ret > 0) { |   if (ret > 0) { | ||||||
|     return 0; |     return 0; | ||||||
|   } else { |   } else { | ||||||
|     LOG_ERROR("Failed to parse remote data"); |     LOG_ERROR("Failed to parse remote data: [{}]", remote_sdp); | ||||||
|     return -1; |     return -1; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int IceAgent::AddCandidate(const char *candidate) { | ||||||
|  |   if (!nice_inited_) { | ||||||
|  |     LOG_ERROR("Nice agent has not been initialized"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (nullptr == agent_) { | ||||||
|  |     LOG_ERROR("Nice agent is nullptr"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (destroyed_) { | ||||||
|  |     LOG_ERROR("Nice agent is destroyed"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   int ret = nice_agent_parse_remote_sdp(agent_, candidate); | ||||||
|  |   if (ret > 0) { | ||||||
|  |     return 0; | ||||||
|  |   } else { | ||||||
|  |     LOG_ERROR("Failed to parse remote candidate: [{}]", candidate); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| int IceAgent::GatherCandidates() { | int IceAgent::GatherCandidates() { | ||||||
|   if (!nice_inited_) { |   if (!nice_inited_) { | ||||||
|     LOG_ERROR("Nice agent has not been initialized"); |     LOG_ERROR("Nice agent has not been initialized"); | ||||||
|   | |||||||
| @@ -13,9 +13,14 @@ typedef void (*nice_cb_state_changed_t)(NiceAgent* agent, guint stream_id, | |||||||
|                                         guint component_id, |                                         guint component_id, | ||||||
|                                         NiceComponentState state, |                                         NiceComponentState state, | ||||||
|                                         gpointer data); |                                         gpointer data); | ||||||
| typedef void (*nice_cb_candidate_t)(NiceAgent* agent, guint stream_id, | typedef void (*nice_cb_new_candidate_t)(NiceAgent* agent, guint stream_id, | ||||||
|                                     guint component_id, const char* sdp, |                                         guint component_id, gchar* foundation, | ||||||
|                                     gpointer data); |                                         gpointer data); | ||||||
|  | typedef void (*nice_cb_new_selected_pair_t)(NiceAgent* agent, guint stream_id, | ||||||
|  |                                             guint component_id, | ||||||
|  |                                             const char* lfoundation, | ||||||
|  |                                             const char* rfoundation, | ||||||
|  |                                             gpointer data); | ||||||
| typedef void (*nice_cb_gathering_done_t)(NiceAgent* agent, guint stream_id, | typedef void (*nice_cb_gathering_done_t)(NiceAgent* agent, guint stream_id, | ||||||
|                                          gpointer data); |                                          gpointer data); | ||||||
| typedef void (*nice_cb_recv_t)(NiceAgent* agent, guint stream_id, | typedef void (*nice_cb_recv_t)(NiceAgent* agent, guint stream_id, | ||||||
| @@ -30,16 +35,25 @@ class IceAgent { | |||||||
|   ~IceAgent(); |   ~IceAgent(); | ||||||
|  |  | ||||||
|   int CreateIceAgent(nice_cb_state_changed_t on_state_changed, |   int CreateIceAgent(nice_cb_state_changed_t on_state_changed, | ||||||
|                      nice_cb_candidate_t on_candidate, |                      nice_cb_new_candidate_t on_new_candidate, | ||||||
|                      nice_cb_gathering_done_t on_gathering_done, |                      nice_cb_gathering_done_t on_gathering_done, | ||||||
|  |                      nice_cb_new_selected_pair_t on_new_selected_pair, | ||||||
|                      nice_cb_recv_t on_recv, void* user_ptr); |                      nice_cb_recv_t on_recv, void* user_ptr); | ||||||
|  |  | ||||||
|   int DestroyIceAgent(); |   int DestroyIceAgent(); | ||||||
|  |  | ||||||
|  |   int GetLocalCredentials(); | ||||||
|  |  | ||||||
|  |   char* GetLocalIceUfrag(); | ||||||
|  |  | ||||||
|  |   char* GetLocalIcePassword(); | ||||||
|  |  | ||||||
|   char* GenerateLocalSdp(); |   char* GenerateLocalSdp(); | ||||||
|  |  | ||||||
|   int SetRemoteSdp(const char* remote_sdp); |   int SetRemoteSdp(const char* remote_sdp); | ||||||
|  |  | ||||||
|  |   int AddCandidate(const char* candidate); | ||||||
|  |  | ||||||
|   int GatherCandidates(); |   int GatherCandidates(); | ||||||
|  |  | ||||||
|   NiceComponentState GetIceState(); |   NiceComponentState GetIceState(); | ||||||
| @@ -63,14 +77,18 @@ class IceAgent { | |||||||
|  |  | ||||||
|   gboolean exit_nice_thread_ = false; |   gboolean exit_nice_thread_ = false; | ||||||
|   bool controlling_ = false; |   bool controlling_ = false; | ||||||
|  |   gchar* ice_ufrag_ = nullptr; | ||||||
|  |   gchar* ice_password_ = nullptr; | ||||||
|   uint32_t stream_id_ = 0; |   uint32_t stream_id_ = 0; | ||||||
|  |   uint32_t n_components_ = 1; | ||||||
|   char* local_sdp_ = nullptr; |   char* local_sdp_ = nullptr; | ||||||
|   NiceComponentState state_ = NiceComponentState::NICE_COMPONENT_STATE_LAST; |   NiceComponentState state_ = NiceComponentState::NICE_COMPONENT_STATE_LAST; | ||||||
|   bool destroyed_ = false; |   bool destroyed_ = false; | ||||||
|   gboolean agent_closed_ = false; |   gboolean agent_closed_ = false; | ||||||
|  |  | ||||||
|   nice_cb_state_changed_t on_state_changed_; |   nice_cb_state_changed_t on_state_changed_; | ||||||
|   nice_cb_candidate_t on_candidate_; |   nice_cb_new_selected_pair_t on_new_selected_pair_; | ||||||
|  |   nice_cb_new_candidate_t on_new_candidate_; | ||||||
|   nice_cb_gathering_done_t on_gathering_done_; |   nice_cb_gathering_done_t on_gathering_done_; | ||||||
|   nice_cb_recv_t on_recv_; |   nice_cb_recv_t on_recv_; | ||||||
|   void* user_ptr_; |   void* user_ptr_; | ||||||
|   | |||||||
| @@ -525,6 +525,25 @@ void PeerConnection::ProcessSignal(const std::string &signal) { | |||||||
|       } |       } | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|  |     case "offer_candidate"_H: { | ||||||
|  |       std::string transmission_id = j["transmission_id"].get<std::string>(); | ||||||
|  |       std::string new_candidate = j["sdp"].get<std::string>(); | ||||||
|  |       std::string remote_user_id = j["remote_user_id"].get<std::string>(); | ||||||
|  |  | ||||||
|  |       LOG_INFO("[{}] receive new candidate from [{}]", user_id_, | ||||||
|  |                remote_user_id); | ||||||
|  |  | ||||||
|  |       if (ice_transmission_list_.find(remote_user_id) != | ||||||
|  |           ice_transmission_list_.end()) { | ||||||
|  |         ice_transmission_list_[remote_user_id]->AddCandidate(new_candidate); | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case "answer_candidate"_H: { | ||||||
|  |       std::string transmission_id = j["transmission_id"].get<std::string>(); | ||||||
|  |       std::string new_candidate = j["sdp"].get<std::string>(); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|     default: { |     default: { | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -167,25 +167,60 @@ int IceTransmission::InitIceTransmission( | |||||||
|           LOG_INFO("state_change: {}", nice_component_state_to_string(state)); |           LOG_INFO("state_change: {}", nice_component_state_to_string(state)); | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       [](NiceAgent *agent, guint stream_id, guint component_id, const char *sdp, |       [](NiceAgent *agent, guint stream_id, guint component_id, | ||||||
|          gpointer user_ptr) { LOG_INFO("candadite: {}", sdp); }, |          gchar *foundation, gpointer user_ptr) { | ||||||
|       [](NiceAgent *agent, guint stream_id, gpointer user_ptr) { |  | ||||||
|         // non-trickle |  | ||||||
|         if (user_ptr) { |         if (user_ptr) { | ||||||
|           IceTransmission *ice_transmission_obj = |           IceTransmission *ice_transmission_obj = | ||||||
|               static_cast<IceTransmission *>(user_ptr); |               static_cast<IceTransmission *>(user_ptr); | ||||||
|           LOG_INFO("[{}] gather_done", ice_transmission_obj->user_id_); |  | ||||||
|  |  | ||||||
|           if (ice_transmission_obj->offer_peer_) { |           GSList *cands = | ||||||
|             ice_transmission_obj->GetLocalSdp(); |               nice_agent_get_local_candidates(agent, stream_id, component_id); | ||||||
|             ice_transmission_obj->SendOffer(); |           NiceCandidate *cand; | ||||||
|  |           for (GSList *i = cands; i; i = i->next) { | ||||||
|  |             cand = (NiceCandidate *)i->data; | ||||||
|  |             if (g_strcmp0(cand->foundation, foundation) == 0) { | ||||||
|  |               ice_transmission_obj->new_local_candidate_ = | ||||||
|  |                   nice_agent_generate_local_sdp(agent); | ||||||
|  |  | ||||||
|           } else { |               json message = { | ||||||
|             ice_transmission_obj->CreateAnswer(); |                   {"type", "offer_candidate"}, | ||||||
|             ice_transmission_obj->SendAnswer(); |                   {"transmission_id", ice_transmission_obj->transmission_id_}, | ||||||
|  |                   {"user_id", ice_transmission_obj->user_id_}, | ||||||
|  |                   {"remote_user_id", ice_transmission_obj->remote_user_id_}, | ||||||
|  |                   {"sdp", ice_transmission_obj->new_local_candidate_}}; | ||||||
|  |               LOG_INFO("Send new local candidate sdp:[{}]", | ||||||
|  |                        ice_transmission_obj->new_local_candidate_); | ||||||
|  |  | ||||||
|  |               if (ice_transmission_obj->ice_ws_transport_) { | ||||||
|  |                 ice_transmission_obj->ice_ws_transport_->Send(message.dump()); | ||||||
|  |               } | ||||||
|  |             } | ||||||
|           } |           } | ||||||
|  |  | ||||||
|  |           g_slist_free_full(cands, (GDestroyNotify)nice_candidate_free); | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|  |       [](NiceAgent *agent, guint stream_id, gpointer user_ptr) { | ||||||
|  |         // non-trickle | ||||||
|  |         // if (user_ptr) { | ||||||
|  |         //   IceTransmission *ice_transmission_obj = | ||||||
|  |         //       static_cast<IceTransmission *>(user_ptr); | ||||||
|  |         //   LOG_INFO("[{}] gather_done", ice_transmission_obj->user_id_); | ||||||
|  |  | ||||||
|  |         //   if (ice_transmission_obj->offer_peer_) { | ||||||
|  |         //     ice_transmission_obj->GetLocalSdp(); | ||||||
|  |         //     ice_transmission_obj->SendOffer(); | ||||||
|  |  | ||||||
|  |         //   } else { | ||||||
|  |         //     ice_transmission_obj->CreateAnswer(); | ||||||
|  |         //     ice_transmission_obj->SendAnswer(); | ||||||
|  |         //   } | ||||||
|  |         // } | ||||||
|  |       }, | ||||||
|  |       [](NiceAgent *agent, guint stream_id, guint component_id, | ||||||
|  |          const char *lfoundation, const char *rfoundation, gpointer user_ptr) { | ||||||
|  |         LOG_INFO("new selected pair: [{}] [{}]", lfoundation, rfoundation); | ||||||
|  |       }, | ||||||
|       [](NiceAgent *agent, guint stream_id, guint component_id, guint size, |       [](NiceAgent *agent, guint stream_id, guint component_id, guint size, | ||||||
|          gchar *buffer, gpointer user_ptr) { |          gchar *buffer, gpointer user_ptr) { | ||||||
|         if (user_ptr) { |         if (user_ptr) { | ||||||
| @@ -257,9 +292,37 @@ int IceTransmission::SetRemoteSdp(const std::string &remote_sdp) { | |||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int IceTransmission::AddCandidate(const std::string &candidate) { | ||||||
|  |   ice_agent_->AddCandidate(candidate.c_str()); | ||||||
|  |   LOG_INFO("[{}] add candidate", user_id_); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| int IceTransmission::CreateOffer() { | int IceTransmission::CreateOffer() { | ||||||
|   LOG_INFO("[{}] create offer", user_id_); |   LOG_INFO("[{}] create offer", user_id_); | ||||||
|   GatherCandidates(); |   GatherCandidates(); | ||||||
|  |  | ||||||
|  |   if (trickle_ice_) { | ||||||
|  |     SendLocalCredentials(); | ||||||
|  |   } | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int IceTransmission::SendLocalCredentials() { | ||||||
|  |   ice_agent_->GetLocalIceUfrag(); | ||||||
|  |   ice_agent_->GetLocalIcePassword(); | ||||||
|  |  | ||||||
|  |   json message = {{"type", "credentials"}, | ||||||
|  |                   {"transmission_id", transmission_id_}, | ||||||
|  |                   {"user_id", user_id_}, | ||||||
|  |                   {"remote_user_id", remote_user_id_}, | ||||||
|  |                   {"ufrag", ice_agent_->GetLocalIceUfrag()}, | ||||||
|  |                   {"password", ice_agent_->GetLocalIcePassword()}}; | ||||||
|  |   LOG_INFO("Send credentials:\n{}", message.dump()); | ||||||
|  |  | ||||||
|  |   if (ice_ws_transport_) { | ||||||
|  |     ice_ws_transport_->Send(message.dump()); | ||||||
|  |   } | ||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -80,10 +80,14 @@ class IceTransmission { | |||||||
|  public: |  public: | ||||||
|   int GatherCandidates(); |   int GatherCandidates(); | ||||||
|  |  | ||||||
|  |   int SendLocalCredentials(); | ||||||
|  |  | ||||||
|   int GetLocalSdp(); |   int GetLocalSdp(); | ||||||
|  |  | ||||||
|   int SetRemoteSdp(const std::string &remote_sdp); |   int SetRemoteSdp(const std::string &remote_sdp); | ||||||
|  |  | ||||||
|  |   int AddCandidate(const std::string &candidate); | ||||||
|  |  | ||||||
|   int CreateOffer(); |   int CreateOffer(); | ||||||
|  |  | ||||||
|   int SendOffer(); |   int SendOffer(); | ||||||
| @@ -99,8 +103,10 @@ class IceTransmission { | |||||||
|   uint8_t CheckIsDataPacket(const char *buffer, size_t size); |   uint8_t CheckIsDataPacket(const char *buffer, size_t size); | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
|  |   bool trickle_ice_ = true; | ||||||
|   std::string local_sdp_; |   std::string local_sdp_; | ||||||
|   std::string remote_sdp_; |   std::string remote_sdp_; | ||||||
|  |   std::string new_local_candidate_; | ||||||
|   std::string local_candidates_; |   std::string local_candidates_; | ||||||
|   std::string remote_candidates_; |   std::string remote_candidates_; | ||||||
|   unsigned int connection_id_ = 0; |   unsigned int connection_id_ = 0; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user