diff --git a/src/ice/ice_agent.cpp b/src/ice/ice_agent.cpp index 81ab6c5..0d366a5 100644 --- a/src/ice/ice_agent.cpp +++ b/src/ice/ice_agent.cpp @@ -24,12 +24,15 @@ IceAgent::~IceAgent() { } 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_new_selected_pair_t on_new_selected_pair, nice_cb_recv_t on_recv, void *user_ptr) { destroyed_ = false; 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_recv_ = on_recv; 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]() { gloop_ = g_main_loop_new(nullptr, false); - agent_ = nice_agent_new_full(g_main_loop_get_context(gloop_), - NICE_COMPATIBILITY_RFC5245, - (NiceAgentOption)(NICE_AGENT_OPTION_RELIABLE)); + agent_ = nice_agent_new_full( + g_main_loop_get_context(gloop_), NICE_COMPATIBILITY_RFC5245, + (NiceAgentOption)(NICE_AGENT_OPTION_ICE_TRICKLE)); if (agent_ == nullptr) { 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-port", stun_port_, 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_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_); g_signal_connect(agent_, "component-state-changed", 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) { LOG_ERROR("Failed to add stream"); } nice_agent_set_stream_name(agent_, stream_id_, "video"); - nice_agent_set_relay_info(agent_, stream_id_, 1, turn_ip_.c_str(), - turn_port_, turn_username_.c_str(), - turn_password_.c_str(), NICE_RELAY_TYPE_TURN_UDP); + nice_agent_set_relay_info(agent_, stream_id_, n_components_, + turn_ip_.c_str(), turn_port_, + 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_, user_ptr_); @@ -120,6 +130,32 @@ int IceAgent::DestroyIceAgent() { 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() { if (!nice_inited_) { LOG_ERROR("Nice agent has not been initialized"); @@ -162,11 +198,38 @@ int IceAgent::SetRemoteSdp(const char *remote_sdp) { if (ret > 0) { return 0; } else { - LOG_ERROR("Failed to parse remote data"); + LOG_ERROR("Failed to parse remote data: [{}]", remote_sdp); 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() { if (!nice_inited_) { LOG_ERROR("Nice agent has not been initialized"); diff --git a/src/ice/ice_agent.h b/src/ice/ice_agent.h index 627a73c..24ecda0 100644 --- a/src/ice/ice_agent.h +++ b/src/ice/ice_agent.h @@ -13,9 +13,14 @@ typedef void (*nice_cb_state_changed_t)(NiceAgent* agent, guint stream_id, guint component_id, NiceComponentState state, gpointer data); -typedef void (*nice_cb_candidate_t)(NiceAgent* agent, guint stream_id, - guint component_id, const char* sdp, - gpointer data); +typedef void (*nice_cb_new_candidate_t)(NiceAgent* agent, guint stream_id, + guint component_id, gchar* foundation, + 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, gpointer data); typedef void (*nice_cb_recv_t)(NiceAgent* agent, guint stream_id, @@ -30,16 +35,25 @@ class IceAgent { ~IceAgent(); 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_new_selected_pair_t on_new_selected_pair, nice_cb_recv_t on_recv, void* user_ptr); int DestroyIceAgent(); + int GetLocalCredentials(); + + char* GetLocalIceUfrag(); + + char* GetLocalIcePassword(); + char* GenerateLocalSdp(); int SetRemoteSdp(const char* remote_sdp); + int AddCandidate(const char* candidate); + int GatherCandidates(); NiceComponentState GetIceState(); @@ -63,14 +77,18 @@ class IceAgent { gboolean exit_nice_thread_ = false; bool controlling_ = false; + gchar* ice_ufrag_ = nullptr; + gchar* ice_password_ = nullptr; uint32_t stream_id_ = 0; + uint32_t n_components_ = 1; char* local_sdp_ = nullptr; NiceComponentState state_ = NiceComponentState::NICE_COMPONENT_STATE_LAST; bool destroyed_ = false; gboolean agent_closed_ = false; 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_recv_t on_recv_; void* user_ptr_; diff --git a/src/pc/peer_connection.cpp b/src/pc/peer_connection.cpp index a5f2020..cfcbdf5 100644 --- a/src/pc/peer_connection.cpp +++ b/src/pc/peer_connection.cpp @@ -525,6 +525,25 @@ void PeerConnection::ProcessSignal(const std::string &signal) { } break; } + case "offer_candidate"_H: { + std::string transmission_id = j["transmission_id"].get(); + std::string new_candidate = j["sdp"].get(); + std::string remote_user_id = j["remote_user_id"].get(); + + 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 new_candidate = j["sdp"].get(); + break; + } default: { break; } diff --git a/src/transmission/ice_transmission.cpp b/src/transmission/ice_transmission.cpp index 0d1b9b4..d39dc75 100644 --- a/src/transmission/ice_transmission.cpp +++ b/src/transmission/ice_transmission.cpp @@ -167,25 +167,60 @@ int IceTransmission::InitIceTransmission( LOG_INFO("state_change: {}", nice_component_state_to_string(state)); } }, - [](NiceAgent *agent, guint stream_id, guint component_id, const char *sdp, - gpointer user_ptr) { LOG_INFO("candadite: {}", sdp); }, - [](NiceAgent *agent, guint stream_id, gpointer user_ptr) { - // non-trickle + [](NiceAgent *agent, guint stream_id, guint component_id, + gchar *foundation, gpointer user_ptr) { if (user_ptr) { IceTransmission *ice_transmission_obj = static_cast(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(); + GSList *cands = + nice_agent_get_local_candidates(agent, stream_id, component_id); + 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 { - ice_transmission_obj->CreateAnswer(); - ice_transmission_obj->SendAnswer(); + json message = { + {"type", "offer_candidate"}, + {"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(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, gchar *buffer, gpointer user_ptr) { if (user_ptr) { @@ -257,9 +292,37 @@ int IceTransmission::SetRemoteSdp(const std::string &remote_sdp) { 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() { LOG_INFO("[{}] create offer", user_id_); 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; } diff --git a/src/transmission/ice_transmission.h b/src/transmission/ice_transmission.h index 126dbed..ef777f2 100644 --- a/src/transmission/ice_transmission.h +++ b/src/transmission/ice_transmission.h @@ -80,10 +80,14 @@ class IceTransmission { public: int GatherCandidates(); + int SendLocalCredentials(); + int GetLocalSdp(); int SetRemoteSdp(const std::string &remote_sdp); + int AddCandidate(const std::string &candidate); + int CreateOffer(); int SendOffer(); @@ -99,8 +103,10 @@ class IceTransmission { uint8_t CheckIsDataPacket(const char *buffer, size_t size); private: + bool trickle_ice_ = true; std::string local_sdp_; std::string remote_sdp_; + std::string new_local_candidate_; std::string local_candidates_; std::string remote_candidates_; unsigned int connection_id_ = 0;