mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-26 20:25:34 +08:00
Add libnice as ice agent
This commit is contained in:
@@ -69,6 +69,8 @@ int IceAgent::CreateIceAgent(juice_cb_state_changed_t on_state_changed,
|
|||||||
|
|
||||||
agent_ = juice_create(&config_);
|
agent_ = juice_create(&config_);
|
||||||
|
|
||||||
|
LOG_INFO("Juice agent init finish");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "juice/juice.h"
|
#include "juice/juice.h"
|
||||||
|
#include "nice/agent.h"
|
||||||
|
|
||||||
class IceAgent {
|
class IceAgent {
|
||||||
public:
|
public:
|
||||||
114
src/ice/libnice/ice_agent.cpp
Normal file
114
src/ice/libnice/ice_agent.cpp
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
#include "ice_agent.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
IceAgent::IceAgent(std::string &stun_ip, uint16_t stun_port,
|
||||||
|
std::string &turn_ip, uint16_t turn_port,
|
||||||
|
std::string &turn_username, std::string &turn_password)
|
||||||
|
: stun_ip_(stun_ip),
|
||||||
|
stun_port_(stun_port),
|
||||||
|
turn_ip_(turn_ip),
|
||||||
|
turn_port_(turn_port),
|
||||||
|
turn_username_(turn_username),
|
||||||
|
turn_password_(turn_password) {}
|
||||||
|
|
||||||
|
IceAgent::~IceAgent() {}
|
||||||
|
|
||||||
|
int IceAgent::CreateIceAgent(nice_cb_state_changed_t on_state_changed,
|
||||||
|
nice_cb_candidate_t on_candidate,
|
||||||
|
nice_cb_gathering_done_t on_gathering_done,
|
||||||
|
nice_cb_recv_t on_recv, void *user_ptr) {
|
||||||
|
g_networking_init();
|
||||||
|
|
||||||
|
gloop_ = g_main_loop_new(NULL, FALSE);
|
||||||
|
// Create the nice agent_
|
||||||
|
agent_ = nice_agent_new(g_main_loop_get_context(gloop_),
|
||||||
|
NICE_COMPATIBILITY_RFC5245);
|
||||||
|
if (agent_ == NULL) {
|
||||||
|
LOG_ERROR("Failed to create agent_");
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_set(agent_, "stun-server", stun_ip_.c_str(), NULL);
|
||||||
|
g_object_set(agent_, "stun-server-port", stun_port_, NULL);
|
||||||
|
|
||||||
|
g_object_set(agent_, "controlling-mode", controlling_, NULL);
|
||||||
|
|
||||||
|
// Connect to the signals
|
||||||
|
g_signal_connect(agent_, "candidate-gathering-done",
|
||||||
|
G_CALLBACK(on_gathering_done), NULL);
|
||||||
|
g_signal_connect(agent_, "new-selected-pair", G_CALLBACK(on_candidate), NULL);
|
||||||
|
g_signal_connect(agent_, "component-state-changed",
|
||||||
|
G_CALLBACK(on_state_changed), NULL);
|
||||||
|
|
||||||
|
// Create a new stream with one component
|
||||||
|
stream_id_ = nice_agent_add_stream(agent_, 1);
|
||||||
|
if (stream_id_ == 0) {
|
||||||
|
LOG_ERROR("Failed to add stream");
|
||||||
|
}
|
||||||
|
nice_agent_set_stream_name(agent_, stream_id_, "video");
|
||||||
|
|
||||||
|
// Attach to the component to receive the data
|
||||||
|
// Without this call, candidates cannot be gathered
|
||||||
|
nice_agent_attach_recv(agent_, stream_id_, 1, g_main_loop_get_context(gloop_),
|
||||||
|
on_recv, NULL);
|
||||||
|
|
||||||
|
LOG_INFO("Nice agent init finish");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IceAgent::DestoryIceAgent() {
|
||||||
|
g_object_unref(agent_);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *IceAgent::GenerateLocalSdp() {
|
||||||
|
if (nullptr == agent_) {
|
||||||
|
LOG_INFO("agent_ is nullptr");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
local_sdp_ = nice_agent_generate_local_sdp(agent_);
|
||||||
|
// LOG_INFO("Generate local sdp:[\n{}]", local_sdp_);
|
||||||
|
|
||||||
|
return local_sdp_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IceAgent::SetRemoteSdp(const char *remote_sdp) {
|
||||||
|
int ret = nice_agent_parse_remote_sdp(agent_, remote_sdp);
|
||||||
|
if (ret > 0) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("Failed to parse remote data");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int IceAgent::GatherCandidates() {
|
||||||
|
if (!nice_agent_gather_candidates(agent_, stream_id_)) {
|
||||||
|
LOG_ERROR("Failed to start candidate gathering");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
NiceComponentState IceAgent::GetIceState() {
|
||||||
|
state_ = nice_agent_get_component_state(agent_, stream_id_, 1);
|
||||||
|
|
||||||
|
return state_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IceAgent::Send(const char *data, size_t size) {
|
||||||
|
if (NiceComponentState::NICE_COMPONENT_STATE_READY !=
|
||||||
|
nice_agent_get_component_state(agent_, stream_id_, 1)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nice_agent_send(agent_, stream_id_, 1, size, data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
66
src/ice/libnice/ice_agent.h
Normal file
66
src/ice/libnice/ice_agent.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#ifndef _ICE_AGENT_H_
|
||||||
|
#define _ICE_AGENT_H_
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "gio/gnetworking.h"
|
||||||
|
#include "nice/agent.h"
|
||||||
|
|
||||||
|
#define NICE_MAX_SDP_STRING_LEN 4096
|
||||||
|
|
||||||
|
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_gathering_done_t)(NiceAgent* agent, guint stream_id,
|
||||||
|
gpointer data);
|
||||||
|
typedef void (*nice_cb_recv_t)(NiceAgent* agent, guint stream_id,
|
||||||
|
guint component_id, guint size, gchar* buffer,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
|
class IceAgent {
|
||||||
|
public:
|
||||||
|
IceAgent(std::string& stun_ip, uint16_t stun_port, std::string& turn_ip,
|
||||||
|
uint16_t turn_port, std::string& turn_username,
|
||||||
|
std::string& turn_password);
|
||||||
|
~IceAgent();
|
||||||
|
|
||||||
|
int CreateIceAgent(nice_cb_state_changed_t on_state_changed,
|
||||||
|
nice_cb_candidate_t on_candidate,
|
||||||
|
nice_cb_gathering_done_t on_gathering_done,
|
||||||
|
nice_cb_recv_t on_recv, void* user_ptr);
|
||||||
|
|
||||||
|
int DestoryIceAgent();
|
||||||
|
|
||||||
|
char* GenerateLocalSdp();
|
||||||
|
|
||||||
|
int SetRemoteSdp(const char* remote_sdp);
|
||||||
|
|
||||||
|
int GatherCandidates();
|
||||||
|
|
||||||
|
NiceComponentState GetIceState();
|
||||||
|
|
||||||
|
int SetRemoteGatheringDone();
|
||||||
|
|
||||||
|
int Send(const char* data, size_t size);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string stun_ip_ = "";
|
||||||
|
uint16_t stun_port_ = 0;
|
||||||
|
std::string turn_ip_ = "";
|
||||||
|
uint16_t turn_port_ = 0;
|
||||||
|
std::string turn_username_ = "";
|
||||||
|
std::string turn_password_ = "";
|
||||||
|
NiceAgent* agent_ = nullptr;
|
||||||
|
GMainLoop* gloop_;
|
||||||
|
bool controlling_ = false;
|
||||||
|
uint32_t stream_id_ = 0;
|
||||||
|
// char local_sdp_[NICE_MAX_SDP_STRING_LEN];
|
||||||
|
char* local_sdp_ = nullptr;
|
||||||
|
NiceComponentState state_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -114,6 +114,63 @@ int IceTransmission::InitIceTransmission(std::string &stun_ip, int stun_port,
|
|||||||
ice_agent_ = std::make_unique<IceAgent>(
|
ice_agent_ = std::make_unique<IceAgent>(
|
||||||
stun_ip, stun_port, turn_ip, turn_port, turn_username, turn_password);
|
stun_ip, stun_port, turn_ip, turn_port, turn_username, turn_password);
|
||||||
|
|
||||||
|
#ifdef USE_NICE
|
||||||
|
ice_agent_->CreateIceAgent(
|
||||||
|
[](NiceAgent *agent, guint stream_id, guint component_id,
|
||||||
|
NiceComponentState state, gpointer user_ptr) {
|
||||||
|
if (user_ptr) {
|
||||||
|
IceTransmission *ice_transmission_obj =
|
||||||
|
static_cast<IceTransmission *>(user_ptr);
|
||||||
|
LOG_INFO("[{}->{}] state_change: {}", ice_transmission_obj->user_id_,
|
||||||
|
ice_transmission_obj->remote_user_id_,
|
||||||
|
nice_component_state_to_string(state));
|
||||||
|
ice_transmission_obj->state_ = state;
|
||||||
|
ice_transmission_obj->on_ice_status_change_(
|
||||||
|
nice_component_state_to_string(state));
|
||||||
|
} else {
|
||||||
|
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
|
||||||
|
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, guint size,
|
||||||
|
gchar *buffer, gpointer user_ptr) {
|
||||||
|
if (user_ptr) {
|
||||||
|
IceTransmission *ice_transmission_obj =
|
||||||
|
static_cast<IceTransmission *>(user_ptr);
|
||||||
|
if (ice_transmission_obj) {
|
||||||
|
if (ice_transmission_obj->CheckIsVideoPacket(buffer, size)) {
|
||||||
|
RtpPacket packet((uint8_t *)buffer, size);
|
||||||
|
ice_transmission_obj->rtp_video_receiver_->InsertRtpPacket(
|
||||||
|
packet);
|
||||||
|
} else if (ice_transmission_obj->CheckIsDataPacket(buffer, size)) {
|
||||||
|
RtpPacket packet((uint8_t *)buffer, size);
|
||||||
|
ice_transmission_obj->rtp_data_receiver_->InsertRtpPacket(packet);
|
||||||
|
} else if (ice_transmission_obj->CheckIsRtcpPacket(buffer, size)) {
|
||||||
|
// LOG_ERROR("Rtcp packet [{}]", (uint8_t)(buffer[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
this);
|
||||||
|
#else
|
||||||
ice_agent_->CreateIceAgent(
|
ice_agent_->CreateIceAgent(
|
||||||
[](juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
[](juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
||||||
if (user_ptr) {
|
if (user_ptr) {
|
||||||
@@ -172,6 +229,7 @@ int IceTransmission::InitIceTransmission(std::string &stun_ip, int stun_port,
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
this);
|
this);
|
||||||
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,11 +274,6 @@ int IceTransmission::SetRemoteSdp(const std::string &remote_sdp) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int IceTransmission::AddRemoteCandidate(const std::string &remote_candidate) {
|
|
||||||
ice_agent_->AddRemoteCandidates(remote_candidate.c_str());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int IceTransmission::CreateOffer() {
|
int IceTransmission::CreateOffer() {
|
||||||
LOG_INFO("[{}] create offer", user_id_);
|
LOG_INFO("[{}] create offer", user_id_);
|
||||||
GatherCandidates();
|
GatherCandidates();
|
||||||
@@ -262,7 +315,11 @@ int IceTransmission::SendAnswer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int IceTransmission::SendData(DATA_TYPE type, const char *data, size_t size) {
|
int IceTransmission::SendData(DATA_TYPE type, const char *data, size_t size) {
|
||||||
if (JUICE_STATE_COMPLETED == state_) {
|
#ifdef USE_NICE
|
||||||
|
if (NiceComponentState::NICE_COMPONENT_STATE_READY == state_) {
|
||||||
|
#else
|
||||||
|
if (juice_state_t::JUICE_STATE_COMPLETED == state_) {
|
||||||
|
#endif
|
||||||
std::vector<RtpPacket> packets;
|
std::vector<RtpPacket> packets;
|
||||||
|
|
||||||
if (DATA_TYPE::VIDEO == type) {
|
if (DATA_TYPE::VIDEO == type) {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "congestion_control.h"
|
#include "congestion_control.h"
|
||||||
#include "ice_agent.h"
|
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
#include "rtp_codec.h"
|
#include "rtp_codec.h"
|
||||||
#include "rtp_data_receiver.h"
|
#include "rtp_data_receiver.h"
|
||||||
@@ -14,6 +13,14 @@
|
|||||||
#include "rtp_video_sender.h"
|
#include "rtp_video_sender.h"
|
||||||
#include "ws_transmission.h"
|
#include "ws_transmission.h"
|
||||||
|
|
||||||
|
#define USE_NICE 1
|
||||||
|
|
||||||
|
#ifdef USE_NICE
|
||||||
|
#include "libnice/ice_agent.h"
|
||||||
|
#else
|
||||||
|
#include "libjuice/ice_agent.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
class IceTransmission {
|
class IceTransmission {
|
||||||
public:
|
public:
|
||||||
typedef enum { VIDEO = 96, AUDIO = 97, DATA = 127 } DATA_TYPE;
|
typedef enum { VIDEO = 96, AUDIO = 97, DATA = 127 } DATA_TYPE;
|
||||||
@@ -64,8 +71,6 @@ class IceTransmission {
|
|||||||
|
|
||||||
int SetRemoteSdp(const std::string &remote_sdp);
|
int SetRemoteSdp(const std::string &remote_sdp);
|
||||||
|
|
||||||
int AddRemoteCandidate(const std::string &remote_candidate);
|
|
||||||
|
|
||||||
int CreateOffer();
|
int CreateOffer();
|
||||||
|
|
||||||
int SendOffer();
|
int SendOffer();
|
||||||
@@ -101,7 +106,11 @@ class IceTransmission {
|
|||||||
std::string remote_user_id_ = "";
|
std::string remote_user_id_ = "";
|
||||||
bool offer_peer_ = true;
|
bool offer_peer_ = true;
|
||||||
std::string remote_ice_username_ = "";
|
std::string remote_ice_username_ = "";
|
||||||
|
#ifdef USE_NICE
|
||||||
|
NiceComponentState state_ = NICE_COMPONENT_STATE_DISCONNECTED;
|
||||||
|
#else
|
||||||
juice_state_t state_ = JUICE_STATE_DISCONNECTED;
|
juice_state_t state_ = JUICE_STATE_DISCONNECTED;
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<RtpCodec> video_rtp_codec_ = nullptr;
|
std::unique_ptr<RtpCodec> video_rtp_codec_ = nullptr;
|
||||||
|
|||||||
389
tests/peerconnection/nice.cpp
Normal file
389
tests/peerconnection/nice.cpp
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013 University of Chicago
|
||||||
|
* Contact: Bryce Allen
|
||||||
|
* Copyright 2013 Collabora Ltd.
|
||||||
|
* Contact: Youness Alaoui
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of the
|
||||||
|
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
|
||||||
|
* case the provisions of LGPL are applicable instead of those above. If you
|
||||||
|
* wish to allow use of your version of this file only under the terms of the
|
||||||
|
* LGPL and not to allow others to use your version of this file under the
|
||||||
|
* MPL, indicate your decision by deleting the provisions above and replace
|
||||||
|
* them with the notice and other provisions required by the LGPL. If you do
|
||||||
|
* not delete the provisions above, a recipient may use your version of this
|
||||||
|
* file under either the MPL or the LGPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Example using libnice to negotiate a UDP connection between two clients,
|
||||||
|
* possibly on the same network or behind different NATs and/or stateful
|
||||||
|
* firewalls.
|
||||||
|
*
|
||||||
|
* Build:
|
||||||
|
* gcc -o simple-example simple-example.c `pkg-config --cflags --libs nice`
|
||||||
|
*
|
||||||
|
* Run two clients, one controlling and one controlled:
|
||||||
|
* simple-example 0 $(host -4 -t A stun.stunprotocol.org | awk '{ print $4 }')
|
||||||
|
* simple-example 1 $(host -4 -t A stun.stunprotocol.org | awk '{ print $4 }')
|
||||||
|
*/
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "gio/gnetworking.h"
|
||||||
|
#include "nice/agent.h"
|
||||||
|
|
||||||
|
static GMainLoop *gloop;
|
||||||
|
static GIOChannel *io_stdin;
|
||||||
|
static guint stream_id;
|
||||||
|
|
||||||
|
static const gchar *candidate_type_name[] = {"host", "srflx", "prflx", "relay"};
|
||||||
|
|
||||||
|
static const gchar *state_name[] = {"disconnected", "gathering", "connecting",
|
||||||
|
"connected", "ready", "failed"};
|
||||||
|
|
||||||
|
static int print_local_data(NiceAgent *agent, guint stream_id,
|
||||||
|
guint component_id);
|
||||||
|
static int parse_remote_data(NiceAgent *agent, guint stream_id,
|
||||||
|
guint component_id, char *line);
|
||||||
|
static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id,
|
||||||
|
gpointer data);
|
||||||
|
static void cb_new_selected_pair(NiceAgent *agent, guint stream_id,
|
||||||
|
guint component_id, gchar *lfoundation,
|
||||||
|
gchar *rfoundation, gpointer data);
|
||||||
|
static void cb_component_state_changed(NiceAgent *agent, guint stream_id,
|
||||||
|
guint component_id, guint state,
|
||||||
|
gpointer data);
|
||||||
|
static void cb_nice_recv(NiceAgent *agent, guint stream_id, guint component_id,
|
||||||
|
guint len, gchar *buf, gpointer data);
|
||||||
|
static gboolean stdin_remote_info_cb(GIOChannel *source, GIOCondition cond,
|
||||||
|
gpointer data);
|
||||||
|
static gboolean stdin_send_data_cb(GIOChannel *source, GIOCondition cond,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
NiceAgent *agent;
|
||||||
|
gchar stun_addr[20] = "120.77.216.215";
|
||||||
|
guint stun_port = 3478;
|
||||||
|
gboolean controlling;
|
||||||
|
|
||||||
|
controlling = argv[1][0] - '0';
|
||||||
|
if (controlling != 0 && controlling != 1) {
|
||||||
|
fprintf(stderr, "Usage: %s 0|1 stun_addr [stun_port]\n", argv[0]);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_networking_init();
|
||||||
|
|
||||||
|
gloop = g_main_loop_new(NULL, FALSE);
|
||||||
|
#ifdef G_OS_WIN32
|
||||||
|
io_stdin = g_io_channel_win32_new_fd(_fileno(stdin));
|
||||||
|
#else
|
||||||
|
io_stdin = g_io_channel_unix_new(fileno(stdin));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Create the nice agent
|
||||||
|
agent = nice_agent_new(g_main_loop_get_context(gloop),
|
||||||
|
NICE_COMPATIBILITY_RFC5245);
|
||||||
|
if (agent == NULL) g_error("Failed to create agent");
|
||||||
|
|
||||||
|
// Set the STUN settings and controlling mode
|
||||||
|
if (stun_addr) {
|
||||||
|
g_object_set(agent, "stun-server", stun_addr, NULL);
|
||||||
|
g_object_set(agent, "stun-server-port", stun_port, NULL);
|
||||||
|
}
|
||||||
|
g_object_set(agent, "controlling-mode", controlling, NULL);
|
||||||
|
|
||||||
|
// Connect to the signals
|
||||||
|
g_signal_connect(agent, "candidate-gathering-done",
|
||||||
|
G_CALLBACK(cb_candidate_gathering_done), NULL);
|
||||||
|
g_signal_connect(agent, "new-selected-pair", G_CALLBACK(cb_new_selected_pair),
|
||||||
|
NULL);
|
||||||
|
g_signal_connect(agent, "component-state-changed",
|
||||||
|
G_CALLBACK(cb_component_state_changed), NULL);
|
||||||
|
|
||||||
|
// Create a new stream with one component
|
||||||
|
stream_id = nice_agent_add_stream(agent, 1);
|
||||||
|
if (stream_id == 0) g_error("Failed to add stream");
|
||||||
|
|
||||||
|
// Attach to the component to receive the data
|
||||||
|
// Without this call, candidates cannot be gathered
|
||||||
|
nice_agent_attach_recv(agent, stream_id, 1, g_main_loop_get_context(gloop),
|
||||||
|
cb_nice_recv, NULL);
|
||||||
|
|
||||||
|
// Start gathering local candidates
|
||||||
|
if (!nice_agent_gather_candidates(agent, stream_id))
|
||||||
|
g_error("Failed to start candidate gathering");
|
||||||
|
|
||||||
|
g_debug("waiting for candidate-gathering-done signal...");
|
||||||
|
|
||||||
|
// Run the mainloop. Everything else will happen asynchronously
|
||||||
|
// when the candidates are done gathering.
|
||||||
|
g_main_loop_run(gloop);
|
||||||
|
|
||||||
|
g_main_loop_unref(gloop);
|
||||||
|
g_object_unref(agent);
|
||||||
|
g_io_channel_unref(io_stdin);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cb_candidate_gathering_done(NiceAgent *agent, guint _stream_id,
|
||||||
|
gpointer data) {
|
||||||
|
g_debug("SIGNAL candidate gathering done\n");
|
||||||
|
|
||||||
|
// Candidate gathering is done. Send our local candidates on stdout
|
||||||
|
printf("Copy this line to remote client:\n");
|
||||||
|
printf("\n ");
|
||||||
|
print_local_data(agent, _stream_id, 1);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
// Listen on stdin for the remote candidate list
|
||||||
|
printf("Enter remote data (single line, no wrapping):\n");
|
||||||
|
g_io_add_watch(io_stdin, G_IO_IN, stdin_remote_info_cb, agent);
|
||||||
|
printf("> ");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean stdin_remote_info_cb(GIOChannel *source, GIOCondition cond,
|
||||||
|
gpointer data) {
|
||||||
|
NiceAgent *agent = (NiceAgent *)data;
|
||||||
|
gchar *line = NULL;
|
||||||
|
int rval;
|
||||||
|
gboolean ret = TRUE;
|
||||||
|
char cands[5120];
|
||||||
|
|
||||||
|
std::cin >> cands;
|
||||||
|
|
||||||
|
if (1) {
|
||||||
|
// Parse remote candidate list and set it on the agent
|
||||||
|
rval = parse_remote_data(agent, stream_id, 1, cands);
|
||||||
|
if (rval == EXIT_SUCCESS) {
|
||||||
|
// Return FALSE so we stop listening to stdin since we parsed the
|
||||||
|
// candidates correctly
|
||||||
|
ret = FALSE;
|
||||||
|
g_debug("waiting for state READY or FAILED signal...");
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "ERROR: failed to parse remote data\n");
|
||||||
|
printf("Enter remote data (single line, no wrapping):\n");
|
||||||
|
printf("> ");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
// g_free(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cb_component_state_changed(NiceAgent *agent, guint _stream_id,
|
||||||
|
guint component_id, guint state,
|
||||||
|
gpointer data) {
|
||||||
|
g_debug("SIGNAL: state changed %d %d %s[%d]\n", _stream_id, component_id,
|
||||||
|
state_name[state], state);
|
||||||
|
|
||||||
|
if (state == NICE_COMPONENT_STATE_CONNECTED) {
|
||||||
|
NiceCandidate *local, *remote;
|
||||||
|
|
||||||
|
// Get current selected candidate pair and print IP address used
|
||||||
|
if (nice_agent_get_selected_pair(agent, _stream_id, component_id, &local,
|
||||||
|
&remote)) {
|
||||||
|
gchar ipaddr[INET6_ADDRSTRLEN];
|
||||||
|
|
||||||
|
nice_address_to_string(&local->addr, ipaddr);
|
||||||
|
printf("\nNegotiation complete: ([%s]:%d,", ipaddr,
|
||||||
|
nice_address_get_port(&local->addr));
|
||||||
|
nice_address_to_string(&remote->addr, ipaddr);
|
||||||
|
printf(" [%s]:%d)\n", ipaddr, nice_address_get_port(&remote->addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen to stdin and send data written to it
|
||||||
|
printf("\nSend lines to remote (Ctrl-D to quit):\n");
|
||||||
|
g_io_add_watch(io_stdin, G_IO_IN, stdin_send_data_cb, agent);
|
||||||
|
printf("> ");
|
||||||
|
fflush(stdout);
|
||||||
|
} else if (state == NICE_COMPONENT_STATE_FAILED) {
|
||||||
|
g_main_loop_quit(gloop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean stdin_send_data_cb(GIOChannel *source, GIOCondition cond,
|
||||||
|
gpointer data) {
|
||||||
|
NiceAgent *agent = (NiceAgent *)data;
|
||||||
|
gchar *line = NULL;
|
||||||
|
|
||||||
|
if (g_io_channel_read_line(source, &line, NULL, NULL, NULL) ==
|
||||||
|
G_IO_STATUS_NORMAL) {
|
||||||
|
nice_agent_send(agent, stream_id, 1, strlen(line), line);
|
||||||
|
g_free(line);
|
||||||
|
printf("> ");
|
||||||
|
fflush(stdout);
|
||||||
|
} else {
|
||||||
|
nice_agent_send(agent, stream_id, 1, 1, "\0");
|
||||||
|
// Ctrl-D was pressed.
|
||||||
|
g_main_loop_quit(gloop);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cb_new_selected_pair(NiceAgent *agent, guint _stream_id,
|
||||||
|
guint component_id, gchar *lfoundation,
|
||||||
|
gchar *rfoundation, gpointer data) {
|
||||||
|
g_debug("SIGNAL: selected pair %s %s", lfoundation, rfoundation);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cb_nice_recv(NiceAgent *agent, guint _stream_id, guint component_id,
|
||||||
|
guint len, gchar *buf, gpointer data) {
|
||||||
|
if (len == 1 && buf[0] == '\0') g_main_loop_quit(gloop);
|
||||||
|
printf("%.*s", len, buf);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
static NiceCandidate *parse_candidate(char *scand, guint _stream_id) {
|
||||||
|
NiceCandidate *cand = NULL;
|
||||||
|
NiceCandidateType ntype = NICE_CANDIDATE_TYPE_HOST;
|
||||||
|
gchar **tokens = NULL;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
tokens = g_strsplit(scand, ",", 5);
|
||||||
|
for (i = 0; tokens[i]; i++)
|
||||||
|
;
|
||||||
|
if (i != 5) goto end;
|
||||||
|
|
||||||
|
for (i = 0; i < G_N_ELEMENTS(candidate_type_name); i++) {
|
||||||
|
if (strcmp(tokens[4], candidate_type_name[i]) == 0) {
|
||||||
|
ntype = (NiceCandidateType)i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == G_N_ELEMENTS(candidate_type_name)) goto end;
|
||||||
|
|
||||||
|
cand = nice_candidate_new(ntype);
|
||||||
|
cand->component_id = 1;
|
||||||
|
cand->stream_id = _stream_id;
|
||||||
|
cand->transport = NICE_CANDIDATE_TRANSPORT_UDP;
|
||||||
|
strncpy(cand->foundation, tokens[0], NICE_CANDIDATE_MAX_FOUNDATION - 1);
|
||||||
|
cand->foundation[NICE_CANDIDATE_MAX_FOUNDATION - 1] = 0;
|
||||||
|
cand->priority = atoi(tokens[1]);
|
||||||
|
|
||||||
|
if (!nice_address_set_from_string(&cand->addr, tokens[2])) {
|
||||||
|
g_message("failed to parse addr: %s", tokens[2]);
|
||||||
|
nice_candidate_free(cand);
|
||||||
|
cand = NULL;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
nice_address_set_port(&cand->addr, atoi(tokens[3]));
|
||||||
|
|
||||||
|
end:
|
||||||
|
g_strfreev(tokens);
|
||||||
|
|
||||||
|
return cand;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int print_local_data(NiceAgent *agent, guint _stream_id,
|
||||||
|
guint component_id) {
|
||||||
|
int result = EXIT_FAILURE;
|
||||||
|
gchar *local_ufrag = NULL;
|
||||||
|
gchar *local_password = NULL;
|
||||||
|
gchar ipaddr[INET6_ADDRSTRLEN];
|
||||||
|
GSList *cands = NULL, *item;
|
||||||
|
|
||||||
|
if (!nice_agent_get_local_credentials(agent, _stream_id, &local_ufrag,
|
||||||
|
&local_password))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
cands = nice_agent_get_local_candidates(agent, _stream_id, component_id);
|
||||||
|
if (cands == NULL) goto end;
|
||||||
|
|
||||||
|
printf("%s %s", local_ufrag, local_password);
|
||||||
|
|
||||||
|
for (item = cands; item; item = item->next) {
|
||||||
|
NiceCandidate *c = (NiceCandidate *)item->data;
|
||||||
|
|
||||||
|
nice_address_to_string(&c->addr, ipaddr);
|
||||||
|
|
||||||
|
// (foundation),(prio),(addr),(port),(type)
|
||||||
|
printf(" %s,%u,%s,%u,%s", c->foundation, c->priority, ipaddr,
|
||||||
|
nice_address_get_port(&c->addr), candidate_type_name[c->type]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
result = EXIT_SUCCESS;
|
||||||
|
|
||||||
|
end:
|
||||||
|
if (local_ufrag) g_free(local_ufrag);
|
||||||
|
if (local_password) g_free(local_password);
|
||||||
|
if (cands) g_slist_free_full(cands, (GDestroyNotify)&nice_candidate_free);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_remote_data(NiceAgent *agent, guint _stream_id,
|
||||||
|
guint component_id, char *line) {
|
||||||
|
GSList *remote_candidates = NULL;
|
||||||
|
gchar **line_argv = NULL;
|
||||||
|
const gchar *ufrag = NULL;
|
||||||
|
const gchar *passwd = NULL;
|
||||||
|
int result = EXIT_FAILURE;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
line_argv = g_strsplit_set(line, " \t\n", 0);
|
||||||
|
for (i = 0; line_argv && line_argv[i]; i++) {
|
||||||
|
if (strlen(line_argv[i]) == 0) continue;
|
||||||
|
|
||||||
|
// first two args are remote ufrag and password
|
||||||
|
if (!ufrag) {
|
||||||
|
ufrag = line_argv[i];
|
||||||
|
} else if (!passwd) {
|
||||||
|
passwd = line_argv[i];
|
||||||
|
} else {
|
||||||
|
// Remaining args are serialized canidates (at least one is required)
|
||||||
|
NiceCandidate *c = parse_candidate(line_argv[i], _stream_id);
|
||||||
|
|
||||||
|
if (c == NULL) {
|
||||||
|
g_message("failed to parse candidate: %s", line_argv[i]);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
remote_candidates = g_slist_prepend(remote_candidates, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ufrag == NULL || passwd == NULL || remote_candidates == NULL) {
|
||||||
|
g_message("line must have at least ufrag, password, and one candidate");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nice_agent_set_remote_credentials(agent, _stream_id, ufrag, passwd)) {
|
||||||
|
g_message("failed to set remote credentials");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: this will trigger the start of negotiation.
|
||||||
|
if (nice_agent_set_remote_candidates(agent, _stream_id, component_id,
|
||||||
|
remote_candidates) < 1) {
|
||||||
|
g_message("failed to set remote candidates");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = EXIT_SUCCESS;
|
||||||
|
|
||||||
|
end:
|
||||||
|
if (line_argv != NULL) g_strfreev(line_argv);
|
||||||
|
if (remote_candidates != NULL)
|
||||||
|
g_slist_free_full(remote_candidates, (GDestroyNotify)&nice_candidate_free);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
276
tests/peerconnection/nicesdp.cpp
Normal file
276
tests/peerconnection/nicesdp.cpp
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013 University of Chicago
|
||||||
|
* Contact: Bryce Allen
|
||||||
|
* Copyright 2013 Collabora Ltd.
|
||||||
|
* Contact: Youness Alaoui
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of the
|
||||||
|
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
|
||||||
|
* case the provisions of LGPL are applicable instead of those above. If you
|
||||||
|
* wish to allow use of your version of this file only under the terms of the
|
||||||
|
* LGPL and not to allow others to use your version of this file under the
|
||||||
|
* MPL, indicate your decision by deleting the provisions above and replace
|
||||||
|
* them with the notice and other provisions required by the LGPL. If you do
|
||||||
|
* not delete the provisions above, a recipient may use your version of this
|
||||||
|
* file under either the MPL or the LGPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Example using libnice to negotiate a UDP connection between two clients,
|
||||||
|
* possibly on the same network or behind different NATs and/or stateful
|
||||||
|
* firewalls.
|
||||||
|
*
|
||||||
|
* Build:
|
||||||
|
* gcc -o sdp-example sdp-example.c `pkg-config --cflags --libs nice`
|
||||||
|
*
|
||||||
|
* Run two clients, one controlling and one controlled:
|
||||||
|
* sdp-example 0 $(host -4 -t A stun.stunprotocol.org | awk '{ print $4 }')
|
||||||
|
* sdp-example 1 $(host -4 -t A stun.stunprotocol.org | awk '{ print $4 }')
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "gio/gnetworking.h"
|
||||||
|
#include "nice/agent.h"
|
||||||
|
|
||||||
|
static GMainLoop *gloop;
|
||||||
|
static gchar *stun_addr = NULL;
|
||||||
|
static guint stun_port;
|
||||||
|
static gboolean controlling;
|
||||||
|
static gboolean exit_thread, candidate_gathering_done, negotiation_done;
|
||||||
|
static GMutex gather_mutex, negotiate_mutex;
|
||||||
|
static GCond gather_cond, negotiate_cond;
|
||||||
|
|
||||||
|
static const gchar *state_name[] = {"disconnected", "gathering", "connecting",
|
||||||
|
"connected", "ready", "failed"};
|
||||||
|
|
||||||
|
static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id,
|
||||||
|
gpointer data);
|
||||||
|
static void cb_component_state_changed(NiceAgent *agent, guint stream_id,
|
||||||
|
guint component_id, guint state,
|
||||||
|
gpointer data);
|
||||||
|
static void cb_nice_recv(NiceAgent *agent, guint stream_id, guint component_id,
|
||||||
|
guint len, gchar *buf, gpointer data);
|
||||||
|
|
||||||
|
static void *example_thread(void *data);
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
GThread *gexamplethread;
|
||||||
|
|
||||||
|
// Parse arguments
|
||||||
|
// if (argc > 4 || argc < 2 || argv[1][1] != '\0') {
|
||||||
|
// fprintf(stderr, "Usage: %s 0|1 stun_addr [stun_port]\n", argv[0]);
|
||||||
|
// return EXIT_FAILURE;
|
||||||
|
// }
|
||||||
|
// controlling = argv[1][0] - '0';
|
||||||
|
// if (controlling != 0 && controlling != 1) {
|
||||||
|
// fprintf(stderr, "Usage: %s 0|1 stun_addr [stun_port]\n", argv[0]);
|
||||||
|
// return EXIT_FAILURE;
|
||||||
|
// }
|
||||||
|
|
||||||
|
stun_addr = "120.77.216.215";
|
||||||
|
|
||||||
|
// if (argc > 2) {
|
||||||
|
// stun_addr = argv[2];
|
||||||
|
// if (argc > 3)
|
||||||
|
// stun_port = atoi(argv[3]);
|
||||||
|
// else
|
||||||
|
stun_port = 3478;
|
||||||
|
|
||||||
|
// g_debug("Using stun server '[%s]:%u'\n", stun_addr, stun_port);
|
||||||
|
// }
|
||||||
|
|
||||||
|
g_networking_init();
|
||||||
|
|
||||||
|
gloop = g_main_loop_new(NULL, FALSE);
|
||||||
|
|
||||||
|
// Run the mainloop and the example thread
|
||||||
|
exit_thread = FALSE;
|
||||||
|
gexamplethread = g_thread_new("example thread", &example_thread, NULL);
|
||||||
|
g_main_loop_run(gloop);
|
||||||
|
exit_thread = TRUE;
|
||||||
|
|
||||||
|
g_thread_join(gexamplethread);
|
||||||
|
g_main_loop_unref(gloop);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *example_thread(void *data) {
|
||||||
|
NiceAgent *agent;
|
||||||
|
GIOChannel *io_stdin;
|
||||||
|
guint stream_id;
|
||||||
|
gchar *line = NULL;
|
||||||
|
gchar *sdp, *sdp64;
|
||||||
|
|
||||||
|
#ifdef G_OS_WIN32
|
||||||
|
io_stdin = g_io_channel_win32_new_fd(_fileno(stdin));
|
||||||
|
#else
|
||||||
|
io_stdin = g_io_channel_unix_new(fileno(stdin));
|
||||||
|
#endif
|
||||||
|
g_io_channel_set_flags(io_stdin, G_IO_FLAG_NONBLOCK, NULL);
|
||||||
|
|
||||||
|
// Create the nice agent
|
||||||
|
agent = nice_agent_new(g_main_loop_get_context(gloop),
|
||||||
|
NICE_COMPATIBILITY_RFC5245);
|
||||||
|
if (agent == NULL) g_error("Failed to create agent");
|
||||||
|
|
||||||
|
// Set the STUN settings and controlling mode
|
||||||
|
if (stun_addr) {
|
||||||
|
g_object_set(agent, "stun-server", stun_addr, NULL);
|
||||||
|
g_object_set(agent, "stun-server-port", stun_port, NULL);
|
||||||
|
}
|
||||||
|
g_object_set(agent, "controlling-mode", controlling, NULL);
|
||||||
|
|
||||||
|
// Connect to the signals
|
||||||
|
g_signal_connect(agent, "candidate-gathering-done",
|
||||||
|
G_CALLBACK(cb_candidate_gathering_done), NULL);
|
||||||
|
g_signal_connect(agent, "component-state-changed",
|
||||||
|
G_CALLBACK(cb_component_state_changed), NULL);
|
||||||
|
|
||||||
|
// Create a new stream with one component
|
||||||
|
stream_id = nice_agent_add_stream(agent, 1);
|
||||||
|
if (stream_id == 0) g_error("Failed to add stream");
|
||||||
|
nice_agent_set_stream_name(agent, stream_id, "text");
|
||||||
|
|
||||||
|
// Attach to the component to receive the data
|
||||||
|
// Without this call, candidates cannot be gathered
|
||||||
|
nice_agent_attach_recv(agent, stream_id, 1, g_main_loop_get_context(gloop),
|
||||||
|
cb_nice_recv, NULL);
|
||||||
|
|
||||||
|
// Start gathering local candidates
|
||||||
|
if (!nice_agent_gather_candidates(agent, stream_id))
|
||||||
|
g_error("Failed to start candidate gathering");
|
||||||
|
|
||||||
|
g_debug("waiting for candidate-gathering-done signal...");
|
||||||
|
|
||||||
|
g_mutex_lock(&gather_mutex);
|
||||||
|
while (!exit_thread && !candidate_gathering_done)
|
||||||
|
g_cond_wait(&gather_cond, &gather_mutex);
|
||||||
|
g_mutex_unlock(&gather_mutex);
|
||||||
|
if (exit_thread) goto end;
|
||||||
|
|
||||||
|
// Candidate gathering is done. Send our local candidates on stdout
|
||||||
|
sdp = nice_agent_generate_local_sdp(agent);
|
||||||
|
printf("Generated SDP from agent :\n%s\n\n", sdp);
|
||||||
|
printf("Copy the following line to remote client:\n");
|
||||||
|
sdp64 = g_base64_encode((const guchar *)sdp, strlen(sdp));
|
||||||
|
printf("\n %s\n", sdp64);
|
||||||
|
g_free(sdp);
|
||||||
|
g_free(sdp64);
|
||||||
|
|
||||||
|
// Listen on stdin for the remote candidate list
|
||||||
|
printf("Enter remote data (single line, no wrapping):\n");
|
||||||
|
printf("> ");
|
||||||
|
fflush(stdout);
|
||||||
|
while (!exit_thread) {
|
||||||
|
// GIOStatus s = g_io_channel_read_line(io_stdin, &line, NULL, NULL, NULL);
|
||||||
|
GIOStatus s = G_IO_STATUS_NORMAL;
|
||||||
|
if (s == G_IO_STATUS_NORMAL) {
|
||||||
|
char sdp_str[5120];
|
||||||
|
std::cin >> sdp_str;
|
||||||
|
gsize sdp_len;
|
||||||
|
|
||||||
|
sdp = (gchar *)g_base64_decode(sdp_str, &sdp_len);
|
||||||
|
// Parse remote candidate list and set it on the agent
|
||||||
|
if (sdp && nice_agent_parse_remote_sdp(agent, sdp) > 0) {
|
||||||
|
g_free(sdp);
|
||||||
|
// g_free(line);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "ERROR: failed to parse remote data\n");
|
||||||
|
printf("Enter remote data (single line, no wrapping):\n");
|
||||||
|
printf("> ");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
g_free(sdp);
|
||||||
|
// g_free(line);
|
||||||
|
} else if (s == G_IO_STATUS_AGAIN) {
|
||||||
|
g_usleep(100000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_debug("waiting for state READY or FAILED signal...");
|
||||||
|
g_mutex_lock(&negotiate_mutex);
|
||||||
|
while (!exit_thread && !negotiation_done)
|
||||||
|
g_cond_wait(&negotiate_cond, &negotiate_mutex);
|
||||||
|
g_mutex_unlock(&negotiate_mutex);
|
||||||
|
if (exit_thread) goto end;
|
||||||
|
|
||||||
|
// Listen to stdin and send data written to it
|
||||||
|
printf("\nSend lines to remote (Ctrl-D to quit):\n");
|
||||||
|
printf("> ");
|
||||||
|
fflush(stdout);
|
||||||
|
while (!exit_thread) {
|
||||||
|
GIOStatus s = g_io_channel_read_line(io_stdin, &line, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
if (s == G_IO_STATUS_NORMAL) {
|
||||||
|
nice_agent_send(agent, stream_id, 1, strlen(line), line);
|
||||||
|
g_free(line);
|
||||||
|
printf("> ");
|
||||||
|
fflush(stdout);
|
||||||
|
} else if (s == G_IO_STATUS_AGAIN) {
|
||||||
|
g_usleep(100000);
|
||||||
|
} else {
|
||||||
|
// Ctrl-D was pressed.
|
||||||
|
nice_agent_send(agent, stream_id, 1, 1, "\0");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
g_object_unref(agent);
|
||||||
|
g_io_channel_unref(io_stdin);
|
||||||
|
g_main_loop_quit(gloop);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id,
|
||||||
|
gpointer data) {
|
||||||
|
g_debug("SIGNAL candidate gathering done\n");
|
||||||
|
|
||||||
|
g_mutex_lock(&gather_mutex);
|
||||||
|
candidate_gathering_done = TRUE;
|
||||||
|
g_cond_signal(&gather_cond);
|
||||||
|
g_mutex_unlock(&gather_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cb_component_state_changed(NiceAgent *agent, guint stream_id,
|
||||||
|
guint component_id, guint state,
|
||||||
|
gpointer data) {
|
||||||
|
g_debug("SIGNAL: state changed %d %d %s[%d]\n", stream_id, component_id,
|
||||||
|
state_name[state], state);
|
||||||
|
|
||||||
|
if (state == NICE_COMPONENT_STATE_READY) {
|
||||||
|
g_mutex_lock(&negotiate_mutex);
|
||||||
|
negotiation_done = TRUE;
|
||||||
|
g_cond_signal(&negotiate_cond);
|
||||||
|
g_mutex_unlock(&negotiate_mutex);
|
||||||
|
} else if (state == NICE_COMPONENT_STATE_FAILED) {
|
||||||
|
g_main_loop_quit(gloop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cb_nice_recv(NiceAgent *agent, guint stream_id, guint component_id,
|
||||||
|
guint len, gchar *buf, gpointer data) {
|
||||||
|
if (len == 1 && buf[0] == '\0') g_main_loop_quit(gloop);
|
||||||
|
|
||||||
|
printf("%.*s", len, buf);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
30
xmake.lua
30
xmake.lua
@@ -7,7 +7,7 @@ set_languages("c++17")
|
|||||||
|
|
||||||
add_rules("mode.release", "mode.debug")
|
add_rules("mode.release", "mode.debug")
|
||||||
|
|
||||||
add_requires("asio 1.24.0", "nlohmann_json", "spdlog 1.11.0")
|
add_requires("asio 1.24.0", "nlohmann_json", "spdlog 1.11.0", "vcpkg::libnice 0.1.21")
|
||||||
add_requires("libjuice", {system = false})
|
add_requires("libjuice", {system = false})
|
||||||
|
|
||||||
if is_os("windows") then
|
if is_os("windows") then
|
||||||
@@ -87,10 +87,18 @@ target("rtp")
|
|||||||
target("ice")
|
target("ice")
|
||||||
set_kind("static")
|
set_kind("static")
|
||||||
add_deps("log", "common", "ws")
|
add_deps("log", "common", "ws")
|
||||||
add_packages("asio", "nlohmann_json", "libjuice")
|
add_packages("asio", "nlohmann_json", "libjuice", "vcpkg::libnice")
|
||||||
add_files("src/ice/*.cpp")
|
add_files("src/ice/libjuice/*.cpp")
|
||||||
add_includedirs("src/ws", {public = true})
|
add_files("src/ice/libnice/*.cpp")
|
||||||
|
add_includedirs("src/ws", "src/ice/libjuice", "src/ice/libnice", {public = true})
|
||||||
add_includedirs("thirdparty/libjuice/include", {public = true})
|
add_includedirs("thirdparty/libjuice/include", {public = true})
|
||||||
|
add_includedirs("E:/SourceCode/vcpkg/installed/x64-windows-static/include/glib-2.0", {public = true})
|
||||||
|
add_includedirs("E:/SourceCode/vcpkg/installed/x64-windows-static/lib/glib-2.0/include", {public = true})
|
||||||
|
add_linkdirs("E:/SourceCode/vcpkg/installed/x64-windows-static/lib")
|
||||||
|
add_links("nice", "glib-2.0", "gio-2.0", "gmodule-2.0", "gobject-2.0", "gthread-2.0",
|
||||||
|
"pcre2-8", "pcre2-16", "pcre2-32", "pcre2-posix",
|
||||||
|
"zlib", "ffi", "libcrypto", "libssl", "intl", "iconv", "charset", "bz2",
|
||||||
|
"Shell32", "Advapi32", "Dnsapi", "Shlwapi", "Iphlpapi")
|
||||||
|
|
||||||
target("ws")
|
target("ws")
|
||||||
set_kind("static")
|
set_kind("static")
|
||||||
@@ -192,4 +200,16 @@ target("guest")
|
|||||||
set_kind("binary")
|
set_kind("binary")
|
||||||
add_deps("projectx")
|
add_deps("projectx")
|
||||||
add_files("tests/peerconnection/guest.cpp")
|
add_files("tests/peerconnection/guest.cpp")
|
||||||
add_includedirs("src/interface")
|
add_includedirs("src/interface")
|
||||||
|
|
||||||
|
target("nicetest")
|
||||||
|
set_kind("binary")
|
||||||
|
add_packages("vcpkg::libnice")
|
||||||
|
add_files("tests/peerconnection/nicesdp.cpp")
|
||||||
|
add_includedirs("E:/SourceCode/vcpkg/installed/x64-windows-static/include/glib-2.0")
|
||||||
|
add_includedirs("E:/SourceCode/vcpkg/installed/x64-windows-static/lib/glib-2.0/include")
|
||||||
|
add_linkdirs("E:/SourceCode/vcpkg/installed/x64-windows-static/lib")
|
||||||
|
add_links("nice", "glib-2.0", "gio-2.0", "gmodule-2.0", "gobject-2.0", "gthread-2.0",
|
||||||
|
"pcre2-8", "pcre2-16", "pcre2-32", "pcre2-posix",
|
||||||
|
"zlib", "ffi", "libcrypto", "libssl", "intl", "iconv", "charset", "bz2",
|
||||||
|
"Shell32", "Advapi32", "Dnsapi", "Shlwapi", "Iphlpapi")
|
||||||
Reference in New Issue
Block a user