janus-gateway / plugins / janus_nosip.c @ 636790a9
History | View | Annotate | Download (74.4 KB)
1 |
/*! \file janus_nosip.c
|
---|---|
2 |
* \author Lorenzo Miniero <lorenzo@meetecho.com>
|
3 |
* \copyright GNU General Public License v3
|
4 |
* \brief Janus NoSIP plugin
|
5 |
* \details
|
6 |
*
|
7 |
* This is quite a basic plugin, as it only takes care of acting as an
|
8 |
* RTP bridge. It is named "NoSIP" since, as the name suggests, signalling
|
9 |
* takes no place here, and is entirely up to the application. The typical
|
10 |
* usage of this application is something like this:
|
11 |
*
|
12 |
* 1. a WebRTC application handles signalling on its own (e.g., SIP), but
|
13 |
* needs to interact with a peer that doesn't support WebRTC (DTLS/ICE);
|
14 |
* 2. it creates a handle with the NoSIP plugin, creates a JSEP SDP offer,
|
15 |
* and passes it to the plugin;
|
16 |
* 3. the plugin creates a barebone SDP that can be used to communicate
|
17 |
* with the legacy peer, binds to the ports for RTP/RTCP, and sends this
|
18 |
* plain SDP back to the application;
|
19 |
* 4. the application uses this barebone SDP in its signalling, and expects
|
20 |
* an answer from the peer;
|
21 |
* 5. the SDP answer from the peer will be barebone as well, and so unfit
|
22 |
* for WebRTC usage; as such, the application passes it to the plugin as
|
23 |
* the answer to match the offer created before;
|
24 |
* 6. the plugin matches the answer to the offer, and starts exchanging
|
25 |
* RTP/RTCP with the legacy peer: media coming from the peer is relayed
|
26 |
* via WebRTC to the application, and WebRTC stuff coming from the application
|
27 |
* is relayed via plain RTP/RTCP to the legacy peer.
|
28 |
*
|
29 |
* The same behaviour can be followed if the application is the callee
|
30 |
* instead, with the only difference being that the barebone offer will
|
31 |
* come from the peer in this case, and the application will ask the
|
32 |
* NoSIP plugin for a barebone answer instead.
|
33 |
*
|
34 |
* As you can see, the behaviour is pretty much the same as the SIP plugin,
|
35 |
* with the key difference being that in this case there's no SIP stack in
|
36 |
* the plugin itself. All signalling is left to the application, and Janus
|
37 |
* (via the NoSIP plugin) is only responsible for bridging the media. This
|
38 |
* might be more appropriate than the SIP plugin in cases where developers
|
39 |
* want to keep control on the signalling layer, while still involving a
|
40 |
* gateway of sorts. Of course, SIP is just an example here: other signalling
|
41 |
* protocols may be involved as well (e.g., IAX, XMPP, others). The NoSIP
|
42 |
* plugin, though, will generate and expect plain SDP, so you'll need to
|
43 |
* take care of any adaptation that may be needed to make this work with
|
44 |
* the signalling protocol of your choice.
|
45 |
*
|
46 |
* Actual API docs: TBD.
|
47 |
*
|
48 |
* \ingroup plugins
|
49 |
* \ref plugins
|
50 |
*/
|
51 |
|
52 |
#include "plugin.h" |
53 |
|
54 |
#include <arpa/inet.h> |
55 |
#include <net/if.h> |
56 |
#include <sys/socket.h> |
57 |
#include <netdb.h> |
58 |
#include <poll.h> |
59 |
|
60 |
#include <jansson.h> |
61 |
|
62 |
#include "../debug.h" |
63 |
#include "../apierror.h" |
64 |
#include "../config.h" |
65 |
#include "../mutex.h" |
66 |
#include "../record.h" |
67 |
#include "../rtp.h" |
68 |
#include "../rtcp.h" |
69 |
#include "../ip-utils.h" |
70 |
#include "../sdp-utils.h" |
71 |
#include "../utils.h" |
72 |
|
73 |
|
74 |
/* Plugin information */
|
75 |
#define JANUS_NOSIP_VERSION 1 |
76 |
#define JANUS_NOSIP_VERSION_STRING "0.0.1" |
77 |
#define JANUS_NOSIP_DESCRIPTION "This is a simple RTP bridging plugin that leaves signalling details (e.g., SIP) up to the application." |
78 |
#define JANUS_NOSIP_NAME "JANUS NoSIP plugin" |
79 |
#define JANUS_NOSIP_AUTHOR "Meetecho s.r.l." |
80 |
#define JANUS_NOSIP_PACKAGE "janus.plugin.nosip" |
81 |
|
82 |
/* Plugin methods */
|
83 |
janus_plugin *create(void);
|
84 |
int janus_nosip_init(janus_callbacks *callback, const char *config_path); |
85 |
void janus_nosip_destroy(void); |
86 |
int janus_nosip_get_api_compatibility(void); |
87 |
int janus_nosip_get_version(void); |
88 |
const char *janus_nosip_get_version_string(void); |
89 |
const char *janus_nosip_get_description(void); |
90 |
const char *janus_nosip_get_name(void); |
91 |
const char *janus_nosip_get_author(void); |
92 |
const char *janus_nosip_get_package(void); |
93 |
void janus_nosip_create_session(janus_plugin_session *handle, int *error); |
94 |
struct janus_plugin_result *janus_nosip_handle_message(janus_plugin_session *handle, char *transaction, json_t *message, json_t *jsep); |
95 |
void janus_nosip_setup_media(janus_plugin_session *handle);
|
96 |
void janus_nosip_incoming_rtp(janus_plugin_session *handle, int video, char *buf, int len); |
97 |
void janus_nosip_incoming_rtcp(janus_plugin_session *handle, int video, char *buf, int len); |
98 |
void janus_nosip_hangup_media(janus_plugin_session *handle);
|
99 |
void janus_nosip_destroy_session(janus_plugin_session *handle, int *error); |
100 |
json_t *janus_nosip_query_session(janus_plugin_session *handle); |
101 |
|
102 |
/* Plugin setup */
|
103 |
static janus_plugin janus_nosip_plugin =
|
104 |
JANUS_PLUGIN_INIT ( |
105 |
.init = janus_nosip_init, |
106 |
.destroy = janus_nosip_destroy, |
107 |
|
108 |
.get_api_compatibility = janus_nosip_get_api_compatibility, |
109 |
.get_version = janus_nosip_get_version, |
110 |
.get_version_string = janus_nosip_get_version_string, |
111 |
.get_description = janus_nosip_get_description, |
112 |
.get_name = janus_nosip_get_name, |
113 |
.get_author = janus_nosip_get_author, |
114 |
.get_package = janus_nosip_get_package, |
115 |
|
116 |
.create_session = janus_nosip_create_session, |
117 |
.handle_message = janus_nosip_handle_message, |
118 |
.setup_media = janus_nosip_setup_media, |
119 |
.incoming_rtp = janus_nosip_incoming_rtp, |
120 |
.incoming_rtcp = janus_nosip_incoming_rtcp, |
121 |
.hangup_media = janus_nosip_hangup_media, |
122 |
.destroy_session = janus_nosip_destroy_session, |
123 |
.query_session = janus_nosip_query_session, |
124 |
); |
125 |
|
126 |
/* Plugin creator */
|
127 |
janus_plugin *create(void) {
|
128 |
JANUS_LOG(LOG_VERB, "%s created!\n", JANUS_NOSIP_NAME);
|
129 |
return &janus_nosip_plugin;
|
130 |
} |
131 |
|
132 |
/* Parameter validation */
|
133 |
static struct janus_json_parameter request_parameters[] = { |
134 |
{"request", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
|
135 |
}; |
136 |
static struct janus_json_parameter generate_parameters[] = { |
137 |
{"info", JSON_STRING, 0}, |
138 |
{"srtp", JSON_STRING, 0} |
139 |
}; |
140 |
static struct janus_json_parameter process_parameters[] = { |
141 |
{"type", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
|
142 |
{"sdp", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
|
143 |
{"info", JSON_STRING, 0}, |
144 |
{"srtp", JSON_STRING, 0} |
145 |
}; |
146 |
static struct janus_json_parameter recording_parameters[] = { |
147 |
{"action", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
|
148 |
{"audio", JANUS_JSON_BOOL, 0}, |
149 |
{"video", JANUS_JSON_BOOL, 0}, |
150 |
{"peer_audio", JANUS_JSON_BOOL, 0}, |
151 |
{"peer_video", JANUS_JSON_BOOL, 0}, |
152 |
{"filename", JSON_STRING, 0} |
153 |
}; |
154 |
|
155 |
/* Useful stuff */
|
156 |
static volatile gint initialized = 0, stopping = 0; |
157 |
static gboolean notify_events = TRUE;
|
158 |
static janus_callbacks *gateway = NULL; |
159 |
|
160 |
static char *local_ip = NULL; |
161 |
|
162 |
static GThread *handler_thread;
|
163 |
static GThread *watchdog;
|
164 |
static void *janus_nosip_handler(void *data); |
165 |
|
166 |
typedef struct janus_nosip_message { |
167 |
janus_plugin_session *handle; |
168 |
char *transaction;
|
169 |
json_t *message; |
170 |
json_t *jsep; |
171 |
} janus_nosip_message; |
172 |
static GAsyncQueue *messages = NULL; |
173 |
static janus_nosip_message exit_message;
|
174 |
|
175 |
static void janus_nosip_message_free(janus_nosip_message *msg) { |
176 |
if(!msg || msg == &exit_message)
|
177 |
return;
|
178 |
|
179 |
msg->handle = NULL;
|
180 |
|
181 |
g_free(msg->transaction); |
182 |
msg->transaction = NULL;
|
183 |
if(msg->message)
|
184 |
json_decref(msg->message); |
185 |
msg->message = NULL;
|
186 |
if(msg->jsep)
|
187 |
json_decref(msg->jsep); |
188 |
msg->jsep = NULL;
|
189 |
|
190 |
g_free(msg); |
191 |
} |
192 |
|
193 |
|
194 |
typedef struct janus_nosip_media { |
195 |
char *remote_ip;
|
196 |
int ready:1; |
197 |
gboolean autoack; |
198 |
gboolean require_srtp, has_srtp_local, has_srtp_remote; |
199 |
int has_audio:1; |
200 |
int audio_rtp_fd, audio_rtcp_fd;
|
201 |
int local_audio_rtp_port, remote_audio_rtp_port;
|
202 |
int local_audio_rtcp_port, remote_audio_rtcp_port;
|
203 |
guint32 audio_ssrc, audio_ssrc_peer; |
204 |
int audio_pt;
|
205 |
const char *audio_pt_name; |
206 |
srtp_t audio_srtp_in, audio_srtp_out; |
207 |
srtp_policy_t audio_remote_policy, audio_local_policy; |
208 |
int audio_srtp_suite_in, audio_srtp_suite_out;
|
209 |
gboolean audio_send; |
210 |
int has_video:1; |
211 |
int video_rtp_fd, video_rtcp_fd;
|
212 |
int local_video_rtp_port, remote_video_rtp_port;
|
213 |
int local_video_rtcp_port, remote_video_rtcp_port;
|
214 |
guint32 video_ssrc, video_ssrc_peer; |
215 |
int video_pt;
|
216 |
const char *video_pt_name; |
217 |
srtp_t video_srtp_in, video_srtp_out; |
218 |
srtp_policy_t video_remote_policy, video_local_policy; |
219 |
int video_srtp_suite_in, video_srtp_suite_out;
|
220 |
gboolean video_send; |
221 |
janus_rtp_switching_context context; |
222 |
int pipefd[2]; |
223 |
gboolean updated; |
224 |
} janus_nosip_media; |
225 |
|
226 |
typedef struct janus_nosip_session { |
227 |
janus_plugin_session *handle; |
228 |
janus_nosip_media media; /* Media gatewaying stuff (same stuff as the SIP plugin) */
|
229 |
janus_sdp *sdp; /* The SDP this user sent */
|
230 |
janus_recorder *arc; /* The Janus recorder instance for this user's audio, if enabled */
|
231 |
janus_recorder *arc_peer; /* The Janus recorder instance for the peer's audio, if enabled */
|
232 |
janus_recorder *vrc; /* The Janus recorder instance for this user's video, if enabled */
|
233 |
janus_recorder *vrc_peer; /* The Janus recorder instance for the peer's video, if enabled */
|
234 |
janus_mutex rec_mutex; /* Mutex to protect the recorders from race conditions */
|
235 |
volatile gint hangingup;
|
236 |
gint64 destroyed; /* Time at which this session was marked as destroyed */
|
237 |
janus_mutex mutex; |
238 |
} janus_nosip_session; |
239 |
static GHashTable *sessions;
|
240 |
static GList *old_sessions;
|
241 |
static janus_mutex sessions_mutex;
|
242 |
|
243 |
|
244 |
/* SRTP stuff (in case we need SDES) */
|
245 |
static int janus_nosip_srtp_set_local(janus_nosip_session *session, gboolean video, char **crypto) { |
246 |
if(session == NULL) |
247 |
return -1; |
248 |
/* Generate key/salt */
|
249 |
uint8_t *key = g_malloc0(SRTP_MASTER_LENGTH); |
250 |
srtp_crypto_get_random(key, SRTP_MASTER_LENGTH); |
251 |
/* Set SRTP policies */
|
252 |
srtp_policy_t *policy = video ? &session->media.video_local_policy : &session->media.audio_local_policy; |
253 |
srtp_crypto_policy_set_rtp_default(&(policy->rtp)); |
254 |
srtp_crypto_policy_set_rtcp_default(&(policy->rtcp)); |
255 |
policy->ssrc.type = ssrc_any_inbound; |
256 |
policy->key = key; |
257 |
policy->next = NULL;
|
258 |
/* Create SRTP context */
|
259 |
srtp_err_status_t res = srtp_create(video ? &session->media.video_srtp_out : &session->media.audio_srtp_out, policy); |
260 |
if(res != srtp_err_status_ok) {
|
261 |
/* Something went wrong... */
|
262 |
JANUS_LOG(LOG_ERR, "Oops, error creating outbound SRTP session: %d (%s)\n", res, janus_srtp_error_str(res));
|
263 |
g_free(key); |
264 |
policy->key = NULL;
|
265 |
return -2; |
266 |
} |
267 |
/* Base64 encode the salt */
|
268 |
*crypto = g_base64_encode(key, SRTP_MASTER_LENGTH); |
269 |
if((video && session->media.video_srtp_out) || (!video && session->media.audio_srtp_out)) {
|
270 |
JANUS_LOG(LOG_VERB, "%s outbound SRTP session created\n", video ? "Video" : "Audio"); |
271 |
} |
272 |
return 0; |
273 |
} |
274 |
static int janus_nosip_srtp_set_remote(janus_nosip_session *session, gboolean video, const char *crypto, int suite) { |
275 |
if(session == NULL || crypto == NULL) |
276 |
return -1; |
277 |
/* Base64 decode the crypto string and set it as the remote SRTP context */
|
278 |
gsize len = 0;
|
279 |
guchar *decoded = g_base64_decode(crypto, &len); |
280 |
if(len < SRTP_MASTER_LENGTH) {
|
281 |
/* FIXME Can this happen? */
|
282 |
g_free(decoded); |
283 |
return -2; |
284 |
} |
285 |
/* Set SRTP policies */
|
286 |
srtp_policy_t *policy = video ? &session->media.video_remote_policy : &session->media.audio_remote_policy; |
287 |
srtp_crypto_policy_set_rtp_default(&(policy->rtp)); |
288 |
srtp_crypto_policy_set_rtcp_default(&(policy->rtcp)); |
289 |
if(suite == 32) { |
290 |
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&(policy->rtp)); |
291 |
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&(policy->rtcp)); |
292 |
} else if(suite == 80) { |
293 |
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(policy->rtp)); |
294 |
srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(policy->rtcp)); |
295 |
} |
296 |
policy->ssrc.type = ssrc_any_inbound; |
297 |
policy->key = decoded; |
298 |
policy->next = NULL;
|
299 |
/* Create SRTP context */
|
300 |
srtp_err_status_t res = srtp_create(video ? &session->media.video_srtp_in : &session->media.audio_srtp_in, policy); |
301 |
if(res != srtp_err_status_ok) {
|
302 |
/* Something went wrong... */
|
303 |
JANUS_LOG(LOG_ERR, "Oops, error creating inbound SRTP session: %d (%s)\n", res, janus_srtp_error_str(res));
|
304 |
g_free(decoded); |
305 |
policy->key = NULL;
|
306 |
return -2; |
307 |
} |
308 |
if((video && session->media.video_srtp_in) || (!video && session->media.audio_srtp_in)) {
|
309 |
JANUS_LOG(LOG_VERB, "%s inbound SRTP session created\n", video ? "Video" : "Audio"); |
310 |
} |
311 |
return 0; |
312 |
} |
313 |
static void janus_nosip_srtp_cleanup(janus_nosip_session *session) { |
314 |
if(session == NULL) |
315 |
return;
|
316 |
session->media.autoack = TRUE; |
317 |
session->media.require_srtp = FALSE; |
318 |
session->media.has_srtp_local = FALSE; |
319 |
session->media.has_srtp_remote = FALSE; |
320 |
/* Audio */
|
321 |
if(session->media.audio_srtp_out)
|
322 |
srtp_dealloc(session->media.audio_srtp_out); |
323 |
session->media.audio_srtp_out = NULL;
|
324 |
g_free(session->media.audio_local_policy.key); |
325 |
session->media.audio_local_policy.key = NULL;
|
326 |
session->media.audio_srtp_suite_out = 0;
|
327 |
if(session->media.audio_srtp_in)
|
328 |
srtp_dealloc(session->media.audio_srtp_in); |
329 |
session->media.audio_srtp_in = NULL;
|
330 |
g_free(session->media.audio_remote_policy.key); |
331 |
session->media.audio_remote_policy.key = NULL;
|
332 |
session->media.audio_srtp_suite_in = 0;
|
333 |
/* Video */
|
334 |
if(session->media.video_srtp_out)
|
335 |
srtp_dealloc(session->media.video_srtp_out); |
336 |
session->media.video_srtp_out = NULL;
|
337 |
g_free(session->media.video_local_policy.key); |
338 |
session->media.video_local_policy.key = NULL;
|
339 |
session->media.video_srtp_suite_out = 0;
|
340 |
if(session->media.video_srtp_in)
|
341 |
srtp_dealloc(session->media.video_srtp_in); |
342 |
session->media.video_srtp_in = NULL;
|
343 |
g_free(session->media.video_remote_policy.key); |
344 |
session->media.video_remote_policy.key = NULL;
|
345 |
session->media.video_srtp_suite_in = 0;
|
346 |
} |
347 |
|
348 |
|
349 |
/* SDP parsing and manipulation */
|
350 |
void janus_nosip_sdp_process(janus_nosip_session *session, janus_sdp *sdp, gboolean answer, gboolean update, gboolean *changed);
|
351 |
char *janus_nosip_sdp_manipulate(janus_nosip_session *session, janus_sdp *sdp, gboolean answer);
|
352 |
/* Media */
|
353 |
static int janus_nosip_allocate_local_ports(janus_nosip_session *session); |
354 |
static void *janus_nosip_relay_thread(void *data); |
355 |
|
356 |
|
357 |
/* Error codes */
|
358 |
#define JANUS_NOSIP_ERROR_UNKNOWN_ERROR 499 |
359 |
#define JANUS_NOSIP_ERROR_NO_MESSAGE 440 |
360 |
#define JANUS_NOSIP_ERROR_INVALID_JSON 441 |
361 |
#define JANUS_NOSIP_ERROR_INVALID_REQUEST 442 |
362 |
#define JANUS_NOSIP_ERROR_MISSING_ELEMENT 443 |
363 |
#define JANUS_NOSIP_ERROR_INVALID_ELEMENT 444 |
364 |
#define JANUS_NOSIP_ERROR_WRONG_STATE 445 |
365 |
#define JANUS_NOSIP_ERROR_MISSING_SDP 446 |
366 |
#define JANUS_NOSIP_ERROR_INVALID_SDP 447 |
367 |
#define JANUS_NOSIP_ERROR_IO_ERROR 448 |
368 |
#define JANUS_NOSIP_ERROR_RECORDING_ERROR 449 |
369 |
#define JANUS_NOSIP_ERROR_TOO_STRICT 450 |
370 |
|
371 |
|
372 |
/* NoSIP watchdog/garbage collector (sort of) */
|
373 |
static void *janus_nosip_watchdog(void *data) { |
374 |
JANUS_LOG(LOG_INFO, "NoSIP watchdog started\n");
|
375 |
gint64 now = 0;
|
376 |
while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) {
|
377 |
janus_mutex_lock(&sessions_mutex); |
378 |
/* Iterate on all the sessions */
|
379 |
now = janus_get_monotonic_time(); |
380 |
if(old_sessions != NULL) { |
381 |
GList *sl = old_sessions; |
382 |
JANUS_LOG(LOG_HUGE, "Checking %d old NoSIP sessions...\n", g_list_length(old_sessions));
|
383 |
while(sl) {
|
384 |
janus_nosip_session *session = (janus_nosip_session *)sl->data; |
385 |
if(!session) {
|
386 |
sl = sl->next; |
387 |
continue;
|
388 |
} |
389 |
if(now-session->destroyed >= 5*G_USEC_PER_SEC) { |
390 |
/* We're lazy and actually get rid of the stuff only after a few seconds */
|
391 |
JANUS_LOG(LOG_VERB, "Freeing old NoSIP session\n");
|
392 |
GList *rm = sl->next; |
393 |
old_sessions = g_list_delete_link(old_sessions, sl); |
394 |
sl = rm; |
395 |
janus_sdp_free(session->sdp); |
396 |
session->sdp = NULL;
|
397 |
g_free(session->media.remote_ip); |
398 |
session->media.remote_ip = NULL;
|
399 |
janus_nosip_srtp_cleanup(session); |
400 |
session->handle = NULL;
|
401 |
g_free(session); |
402 |
session = NULL;
|
403 |
continue;
|
404 |
} |
405 |
sl = sl->next; |
406 |
} |
407 |
} |
408 |
janus_mutex_unlock(&sessions_mutex); |
409 |
g_usleep(500000);
|
410 |
} |
411 |
JANUS_LOG(LOG_INFO, "NoSIP watchdog stopped\n");
|
412 |
return NULL; |
413 |
} |
414 |
|
415 |
|
416 |
/* Plugin implementation */
|
417 |
int janus_nosip_init(janus_callbacks *callback, const char *config_path) { |
418 |
if(g_atomic_int_get(&stopping)) {
|
419 |
/* Still stopping from before */
|
420 |
return -1; |
421 |
} |
422 |
if(callback == NULL || config_path == NULL) { |
423 |
/* Invalid arguments */
|
424 |
return -1; |
425 |
} |
426 |
|
427 |
/* Read configuration */
|
428 |
char filename[255]; |
429 |
g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_NOSIP_PACKAGE); |
430 |
JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
|
431 |
janus_config *config = janus_config_parse(filename); |
432 |
if(config != NULL) { |
433 |
janus_config_print(config); |
434 |
|
435 |
janus_config_item *item = janus_config_get_item_drilldown(config, "general", "local_ip"); |
436 |
if(item && item->value) {
|
437 |
/* Verify that the address is valid */
|
438 |
struct ifaddrs *ifas = NULL; |
439 |
janus_network_address iface; |
440 |
janus_network_address_string_buffer ibuf; |
441 |
if(getifaddrs(&ifas) || ifas == NULL) { |
442 |
JANUS_LOG(LOG_ERR, "Unable to acquire list of network devices/interfaces; some configurations may not work as expected...\n");
|
443 |
} else {
|
444 |
if(janus_network_lookup_interface(ifas, item->value, &iface) != 0) { |
445 |
JANUS_LOG(LOG_WARN, "Error setting local IP address to %s, falling back to detecting IP address...\n", item->value);
|
446 |
} else {
|
447 |
if(janus_network_address_to_string_buffer(&iface, &ibuf) != 0 || janus_network_address_string_buffer_is_null(&ibuf)) { |
448 |
JANUS_LOG(LOG_WARN, "Error getting local IP address from %s, falling back to detecting IP address...\n", item->value);
|
449 |
} else {
|
450 |
local_ip = g_strdup(janus_network_address_string_from_buffer(&ibuf)); |
451 |
} |
452 |
} |
453 |
} |
454 |
} |
455 |
|
456 |
item = janus_config_get_item_drilldown(config, "general", "events"); |
457 |
if(item != NULL && item->value != NULL) |
458 |
notify_events = janus_is_true(item->value); |
459 |
if(!notify_events && callback->events_is_enabled()) {
|
460 |
JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_NOSIP_NAME);
|
461 |
} |
462 |
|
463 |
janus_config_destroy(config); |
464 |
} |
465 |
config = NULL;
|
466 |
|
467 |
if(local_ip == NULL) { |
468 |
local_ip = janus_network_detect_local_ip_as_string(janus_network_query_options_any_ip); |
469 |
if(local_ip == NULL) { |
470 |
JANUS_LOG(LOG_WARN, "Couldn't find any address! using 127.0.0.1 as the local IP... (which is NOT going to work out of your machine)\n");
|
471 |
local_ip = g_strdup("127.0.0.1");
|
472 |
} |
473 |
} |
474 |
JANUS_LOG(LOG_VERB, "Local IP set to %s\n", local_ip);
|
475 |
|
476 |
#ifdef HAVE_SRTP_2
|
477 |
/* Init randomizer (for randum numbers in SRTP) */
|
478 |
RAND_poll(); |
479 |
#endif
|
480 |
|
481 |
sessions = g_hash_table_new(NULL, NULL); |
482 |
janus_mutex_init(&sessions_mutex); |
483 |
messages = g_async_queue_new_full((GDestroyNotify) janus_nosip_message_free); |
484 |
/* This is the callback we'll need to invoke to contact the gateway */
|
485 |
gateway = callback; |
486 |
|
487 |
g_atomic_int_set(&initialized, 1);
|
488 |
|
489 |
GError *error = NULL;
|
490 |
/* Start the sessions watchdog */
|
491 |
watchdog = g_thread_try_new("nosip watchdog", &janus_nosip_watchdog, NULL, &error); |
492 |
if(error != NULL) { |
493 |
g_atomic_int_set(&initialized, 0);
|
494 |
JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the NoSIP watchdog thread...\n", error->code, error->message ? error->message : "??"); |
495 |
return -1; |
496 |
} |
497 |
/* Launch the thread that will handle incoming messages */
|
498 |
handler_thread = g_thread_try_new("nosip handler", janus_nosip_handler, NULL, &error); |
499 |
if(error != NULL) { |
500 |
g_atomic_int_set(&initialized, 0);
|
501 |
JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the NoSIP handler thread...\n", error->code, error->message ? error->message : "??"); |
502 |
return -1; |
503 |
} |
504 |
JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_NOSIP_NAME);
|
505 |
return 0; |
506 |
} |
507 |
|
508 |
void janus_nosip_destroy(void) { |
509 |
if(!g_atomic_int_get(&initialized))
|
510 |
return;
|
511 |
g_atomic_int_set(&stopping, 1);
|
512 |
|
513 |
g_async_queue_push(messages, &exit_message); |
514 |
if(handler_thread != NULL) { |
515 |
g_thread_join(handler_thread); |
516 |
handler_thread = NULL;
|
517 |
} |
518 |
if(watchdog != NULL) { |
519 |
g_thread_join(watchdog); |
520 |
watchdog = NULL;
|
521 |
} |
522 |
/* FIXME We should destroy the sessions cleanly */
|
523 |
janus_mutex_lock(&sessions_mutex); |
524 |
g_hash_table_destroy(sessions); |
525 |
sessions = NULL;
|
526 |
janus_mutex_unlock(&sessions_mutex); |
527 |
g_async_queue_unref(messages); |
528 |
messages = NULL;
|
529 |
g_atomic_int_set(&initialized, 0);
|
530 |
g_atomic_int_set(&stopping, 0);
|
531 |
|
532 |
g_free(local_ip); |
533 |
|
534 |
JANUS_LOG(LOG_INFO, "%s destroyed!\n", JANUS_NOSIP_NAME);
|
535 |
} |
536 |
|
537 |
int janus_nosip_get_api_compatibility(void) { |
538 |
/* Important! This is what your plugin MUST always return: don't lie here or bad things will happen */
|
539 |
return JANUS_PLUGIN_API_VERSION;
|
540 |
} |
541 |
|
542 |
int janus_nosip_get_version(void) { |
543 |
return JANUS_NOSIP_VERSION;
|
544 |
} |
545 |
|
546 |
const char *janus_nosip_get_version_string(void) { |
547 |
return JANUS_NOSIP_VERSION_STRING;
|
548 |
} |
549 |
|
550 |
const char *janus_nosip_get_description(void) { |
551 |
return JANUS_NOSIP_DESCRIPTION;
|
552 |
} |
553 |
|
554 |
const char *janus_nosip_get_name(void) { |
555 |
return JANUS_NOSIP_NAME;
|
556 |
} |
557 |
|
558 |
const char *janus_nosip_get_author(void) { |
559 |
return JANUS_NOSIP_AUTHOR;
|
560 |
} |
561 |
|
562 |
const char *janus_nosip_get_package(void) { |
563 |
return JANUS_NOSIP_PACKAGE;
|
564 |
} |
565 |
|
566 |
void janus_nosip_create_session(janus_plugin_session *handle, int *error) { |
567 |
if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
|
568 |
*error = -1;
|
569 |
return;
|
570 |
} |
571 |
janus_nosip_session *session = g_malloc0(sizeof(janus_nosip_session));
|
572 |
session->handle = handle; |
573 |
session->sdp = NULL;
|
574 |
session->media.remote_ip = NULL;
|
575 |
session->media.ready = 0;
|
576 |
session->media.autoack = TRUE; |
577 |
session->media.require_srtp = FALSE; |
578 |
session->media.has_srtp_local = FALSE; |
579 |
session->media.has_srtp_remote = FALSE; |
580 |
session->media.has_audio = 0;
|
581 |
session->media.audio_rtp_fd = -1;
|
582 |
session->media.audio_rtcp_fd = -1;
|
583 |
session->media.local_audio_rtp_port = 0;
|
584 |
session->media.remote_audio_rtp_port = 0;
|
585 |
session->media.local_audio_rtcp_port = 0;
|
586 |
session->media.remote_audio_rtcp_port = 0;
|
587 |
session->media.audio_ssrc = 0;
|
588 |
session->media.audio_ssrc_peer = 0;
|
589 |
session->media.audio_pt = -1;
|
590 |
session->media.audio_pt_name = NULL;
|
591 |
session->media.audio_srtp_suite_in = 0;
|
592 |
session->media.audio_srtp_suite_out = 0;
|
593 |
session->media.audio_send = TRUE; |
594 |
session->media.has_video = 0;
|
595 |
session->media.video_rtp_fd = -1;
|
596 |
session->media.video_rtcp_fd = -1;
|
597 |
session->media.local_video_rtp_port = 0;
|
598 |
session->media.remote_video_rtp_port = 0;
|
599 |
session->media.local_video_rtcp_port = 0;
|
600 |
session->media.remote_video_rtcp_port = 0;
|
601 |
session->media.video_ssrc = 0;
|
602 |
session->media.video_ssrc_peer = 0;
|
603 |
session->media.video_pt = -1;
|
604 |
session->media.video_pt_name = NULL;
|
605 |
session->media.video_srtp_suite_in = 0;
|
606 |
session->media.video_srtp_suite_out = 0;
|
607 |
session->media.video_send = TRUE; |
608 |
/* Initialize the RTP context */
|
609 |
janus_rtp_switching_context_reset(&session->media.context); |
610 |
session->media.pipefd[0] = -1; |
611 |
session->media.pipefd[1] = -1; |
612 |
session->media.updated = FALSE; |
613 |
janus_mutex_init(&session->rec_mutex); |
614 |
session->destroyed = 0;
|
615 |
g_atomic_int_set(&session->hangingup, 0);
|
616 |
janus_mutex_init(&session->mutex); |
617 |
handle->plugin_handle = session; |
618 |
|
619 |
janus_mutex_lock(&sessions_mutex); |
620 |
g_hash_table_insert(sessions, handle, session); |
621 |
janus_mutex_unlock(&sessions_mutex); |
622 |
|
623 |
return;
|
624 |
} |
625 |
|
626 |
void janus_nosip_destroy_session(janus_plugin_session *handle, int *error) { |
627 |
if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
|
628 |
*error = -1;
|
629 |
return;
|
630 |
} |
631 |
janus_nosip_session *session = (janus_nosip_session *)handle->plugin_handle; |
632 |
if(!session) {
|
633 |
JANUS_LOG(LOG_ERR, "No NoSIP session associated with this handle...\n");
|
634 |
*error = -2;
|
635 |
return;
|
636 |
} |
637 |
janus_mutex_lock(&sessions_mutex); |
638 |
if(!session->destroyed) {
|
639 |
g_hash_table_remove(sessions, handle); |
640 |
janus_nosip_hangup_media(handle); |
641 |
session->destroyed = janus_get_monotonic_time(); |
642 |
JANUS_LOG(LOG_VERB, "Destroying NoSIP session (%p)...\n", session);
|
643 |
/* Cleaning up and removing the session is done in a lazy way */
|
644 |
old_sessions = g_list_append(old_sessions, session); |
645 |
} |
646 |
janus_mutex_unlock(&sessions_mutex); |
647 |
return;
|
648 |
} |
649 |
|
650 |
json_t *janus_nosip_query_session(janus_plugin_session *handle) { |
651 |
if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
|
652 |
return NULL; |
653 |
} |
654 |
janus_nosip_session *session = (janus_nosip_session *)handle->plugin_handle; |
655 |
if(!session) {
|
656 |
JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
|
657 |
return NULL; |
658 |
} |
659 |
/* Provide some generic info, e.g., if we're in a call and with whom */
|
660 |
json_t *info = json_object(); |
661 |
if(session->sdp) {
|
662 |
json_object_set_new(info, "srtp-required", json_string(session->media.require_srtp ? "yes" : "no")); |
663 |
json_object_set_new(info, "sdes-local", json_string(session->media.has_srtp_local ? "yes" : "no")); |
664 |
json_object_set_new(info, "sdes-remote", json_string(session->media.has_srtp_remote ? "yes" : "no")); |
665 |
} |
666 |
if(session->arc || session->vrc || session->arc_peer || session->vrc_peer) {
|
667 |
json_t *recording = json_object(); |
668 |
if(session->arc && session->arc->filename)
|
669 |
json_object_set_new(recording, "audio", json_string(session->arc->filename));
|
670 |
if(session->vrc && session->vrc->filename)
|
671 |
json_object_set_new(recording, "video", json_string(session->vrc->filename));
|
672 |
if(session->arc_peer && session->arc_peer->filename)
|
673 |
json_object_set_new(recording, "audio-peer", json_string(session->arc_peer->filename));
|
674 |
if(session->vrc_peer && session->vrc_peer->filename)
|
675 |
json_object_set_new(recording, "video-peer", json_string(session->vrc_peer->filename));
|
676 |
json_object_set_new(info, "recording", recording);
|
677 |
} |
678 |
json_object_set_new(info, "destroyed", json_integer(session->destroyed));
|
679 |
return info;
|
680 |
} |
681 |
|
682 |
struct janus_plugin_result *janus_nosip_handle_message(janus_plugin_session *handle, char *transaction, json_t *message, json_t *jsep) { |
683 |
if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
|
684 |
return janus_plugin_result_new(JANUS_PLUGIN_ERROR, g_atomic_int_get(&stopping) ? "Shutting down" : "Plugin not initialized", NULL); |
685 |
janus_nosip_message *msg = g_malloc0(sizeof(janus_nosip_message));
|
686 |
msg->handle = handle; |
687 |
msg->transaction = transaction; |
688 |
msg->message = message; |
689 |
msg->jsep = jsep; |
690 |
g_async_queue_push(messages, msg); |
691 |
|
692 |
/* All the requests to this plugin are handled asynchronously */
|
693 |
return janus_plugin_result_new(JANUS_PLUGIN_OK_WAIT, NULL, NULL); |
694 |
} |
695 |
|
696 |
void janus_nosip_setup_media(janus_plugin_session *handle) {
|
697 |
JANUS_LOG(LOG_INFO, "WebRTC media is now available\n");
|
698 |
if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
|
699 |
return;
|
700 |
janus_nosip_session *session = (janus_nosip_session *)handle->plugin_handle; |
701 |
if(!session) {
|
702 |
JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
|
703 |
return;
|
704 |
} |
705 |
if(session->destroyed)
|
706 |
return;
|
707 |
g_atomic_int_set(&session->hangingup, 0);
|
708 |
} |
709 |
|
710 |
void janus_nosip_incoming_rtp(janus_plugin_session *handle, int video, char *buf, int len) { |
711 |
if(handle == NULL || handle->stopped || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) |
712 |
return;
|
713 |
if(gateway) {
|
714 |
/* Honour the audio/video active flags */
|
715 |
janus_nosip_session *session = (janus_nosip_session *)handle->plugin_handle; |
716 |
if(!session || session->destroyed) {
|
717 |
JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
|
718 |
return;
|
719 |
} |
720 |
/* Forward to our NoSIP peer */
|
721 |
if((video && !session->media.video_send) || (!video && !session->media.audio_send)) {
|
722 |
/* Dropping packet, peer doesn't want to receive it */
|
723 |
return;
|
724 |
} |
725 |
if((video && session->media.video_ssrc == 0) || (!video && session->media.audio_ssrc == 0)) { |
726 |
rtp_header *header = (rtp_header *)buf; |
727 |
if(video) {
|
728 |
session->media.video_ssrc = ntohl(header->ssrc); |
729 |
} else {
|
730 |
session->media.audio_ssrc = ntohl(header->ssrc); |
731 |
} |
732 |
JANUS_LOG(LOG_VERB, "Got NoSIP %s SSRC: %"SCNu32"\n", |
733 |
video ? "video" : "audio", |
734 |
video ? session->media.video_ssrc : session->media.audio_ssrc); |
735 |
} |
736 |
if((video && session->media.has_video && session->media.video_rtp_fd != -1) || |
737 |
(!video && session->media.has_audio && session->media.audio_rtp_fd != -1)) {
|
738 |
/* Save the frame if we're recording */
|
739 |
janus_recorder_save_frame(video ? session->vrc : session->arc, buf, len); |
740 |
/* Is SRTP involved? */
|
741 |
if(session->media.has_srtp_local) {
|
742 |
char sbuf[2048]; |
743 |
memcpy(&sbuf, buf, len); |
744 |
int protected = len;
|
745 |
int res = srtp_protect(
|
746 |
(video ? session->media.video_srtp_out : session->media.audio_srtp_out), |
747 |
&sbuf, &protected); |
748 |
if(res != srtp_err_status_ok) {
|
749 |
rtp_header *header = (rtp_header *)&sbuf; |
750 |
guint32 timestamp = ntohl(header->timestamp); |
751 |
guint16 seq = ntohs(header->seq_number); |
752 |
JANUS_LOG(LOG_ERR, "[NoSIP-%p] %s SRTP protect error... %s (len=%d-->%d, ts=%"SCNu32", seq=%"SCNu16")...\n", |
753 |
session, video ? "Video" : "Audio", janus_srtp_error_str(res), len, protected, timestamp, seq); |
754 |
} else {
|
755 |
/* Forward the frame to the peer */
|
756 |
send((video ? session->media.video_rtp_fd : session->media.audio_rtp_fd), sbuf, protected, 0);
|
757 |
} |
758 |
} else {
|
759 |
/* Forward the frame to the peer */
|
760 |
send((video ? session->media.video_rtp_fd : session->media.audio_rtp_fd), buf, len, 0);
|
761 |
} |
762 |
} |
763 |
} |
764 |
} |
765 |
|
766 |
void janus_nosip_incoming_rtcp(janus_plugin_session *handle, int video, char *buf, int len) { |
767 |
if(handle == NULL || handle->stopped || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) |
768 |
return;
|
769 |
if(gateway) {
|
770 |
janus_nosip_session *session = (janus_nosip_session *)handle->plugin_handle; |
771 |
if(!session || session->destroyed) {
|
772 |
JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
|
773 |
return;
|
774 |
} |
775 |
/* Forward to our NoSIP peer */
|
776 |
if((video && session->media.has_video && session->media.video_rtcp_fd != -1) || |
777 |
(!video && session->media.has_audio && session->media.audio_rtcp_fd != -1)) {
|
778 |
/* Fix SSRCs as the gateway does */
|
779 |
JANUS_LOG(LOG_HUGE, "[NoSIP-%p] Fixing %s SSRCs (local %u, peer %u)\n",
|
780 |
session, video ? "video" : "audio", |
781 |
(video ? session->media.video_ssrc : session->media.audio_ssrc), |
782 |
(video ? session->media.video_ssrc_peer : session->media.audio_ssrc_peer)); |
783 |
janus_rtcp_fix_ssrc(NULL, (char *)buf, len, video, |
784 |
(video ? session->media.video_ssrc : session->media.audio_ssrc), |
785 |
(video ? session->media.video_ssrc_peer : session->media.audio_ssrc_peer)); |
786 |
/* Is SRTP involved? */
|
787 |
if(session->media.has_srtp_local) {
|
788 |
char sbuf[2048]; |
789 |
memcpy(&sbuf, buf, len); |
790 |
int protected = len;
|
791 |
int res = srtp_protect_rtcp(
|
792 |
(video ? session->media.video_srtp_out : session->media.audio_srtp_out), |
793 |
&sbuf, &protected); |
794 |
if(res != srtp_err_status_ok) {
|
795 |
JANUS_LOG(LOG_ERR, "[NoSIP-%p] %s SRTCP protect error... %s (len=%d-->%d)...\n",
|
796 |
session, video ? "Video" : "Audio", |
797 |
janus_srtp_error_str(res), len, protected); |
798 |
} else {
|
799 |
/* Forward the message to the peer */
|
800 |
send((video ? session->media.video_rtcp_fd : session->media.audio_rtcp_fd), sbuf, protected, 0);
|
801 |
} |
802 |
} else {
|
803 |
/* Forward the message to the peer */
|
804 |
send((video ? session->media.video_rtcp_fd : session->media.audio_rtcp_fd), buf, len, 0);
|
805 |
} |
806 |
} |
807 |
} |
808 |
} |
809 |
|
810 |
void janus_nosip_hangup_media(janus_plugin_session *handle) {
|
811 |
JANUS_LOG(LOG_INFO, "No WebRTC media anymore\n");
|
812 |
if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
|
813 |
return;
|
814 |
janus_nosip_session *session = (janus_nosip_session *)handle->plugin_handle; |
815 |
if(!session) {
|
816 |
JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
|
817 |
return;
|
818 |
} |
819 |
if(session->destroyed)
|
820 |
return;
|
821 |
if(g_atomic_int_add(&session->hangingup, 1)) |
822 |
return;
|
823 |
/* Notify the thread that it's time to go */
|
824 |
if(session->media.pipefd[1] > 0) { |
825 |
int code = 1; |
826 |
ssize_t res = 0;
|
827 |
do {
|
828 |
res = write(session->media.pipefd[1], &code, sizeof(int)); |
829 |
} while(res == -1 && errno == EINTR); |
830 |
} |
831 |
/* Get rid of the recorders, if available */
|
832 |
janus_mutex_lock(&session->rec_mutex); |
833 |
if(session->arc) {
|
834 |
janus_recorder_close(session->arc); |
835 |
JANUS_LOG(LOG_INFO, "Closed user's audio recording %s\n", session->arc->filename ? session->arc->filename : "??"); |
836 |
janus_recorder_free(session->arc); |
837 |
} |
838 |
session->arc = NULL;
|
839 |
if(session->arc_peer) {
|
840 |
janus_recorder_close(session->arc_peer); |
841 |
JANUS_LOG(LOG_INFO, "Closed peer's audio recording %s\n", session->arc_peer->filename ? session->arc_peer->filename : "??"); |
842 |
janus_recorder_free(session->arc_peer); |
843 |
} |
844 |
session->arc_peer = NULL;
|
845 |
if(session->vrc) {
|
846 |
janus_recorder_close(session->vrc); |
847 |
JANUS_LOG(LOG_INFO, "Closed user's video recording %s\n", session->vrc->filename ? session->vrc->filename : "??"); |
848 |
janus_recorder_free(session->vrc); |
849 |
} |
850 |
session->vrc = NULL;
|
851 |
if(session->vrc_peer) {
|
852 |
janus_recorder_close(session->vrc_peer); |
853 |
JANUS_LOG(LOG_INFO, "Closed peer's video recording %s\n", session->vrc_peer->filename ? session->vrc_peer->filename : "??"); |
854 |
janus_recorder_free(session->vrc_peer); |
855 |
} |
856 |
session->vrc_peer = NULL;
|
857 |
janus_mutex_unlock(&session->rec_mutex); |
858 |
} |
859 |
|
860 |
/* Thread to handle incoming messages */
|
861 |
static void *janus_nosip_handler(void *data) { |
862 |
JANUS_LOG(LOG_VERB, "Joining NoSIP handler thread\n");
|
863 |
janus_nosip_message *msg = NULL;
|
864 |
int error_code = 0; |
865 |
char error_cause[512]; |
866 |
json_t *root = NULL;
|
867 |
while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) {
|
868 |
msg = g_async_queue_pop(messages); |
869 |
if(msg == NULL) |
870 |
continue;
|
871 |
if(msg == &exit_message)
|
872 |
break;
|
873 |
if(msg->handle == NULL) { |
874 |
janus_nosip_message_free(msg); |
875 |
continue;
|
876 |
} |
877 |
janus_nosip_session *session = NULL;
|
878 |
janus_mutex_lock(&sessions_mutex); |
879 |
if(g_hash_table_lookup(sessions, msg->handle) != NULL ) { |
880 |
session = (janus_nosip_session *)msg->handle->plugin_handle; |
881 |
} |
882 |
janus_mutex_unlock(&sessions_mutex); |
883 |
if(!session) {
|
884 |
JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
|
885 |
janus_nosip_message_free(msg); |
886 |
continue;
|
887 |
} |
888 |
if(session->destroyed) {
|
889 |
janus_nosip_message_free(msg); |
890 |
continue;
|
891 |
} |
892 |
/* Handle request */
|
893 |
error_code = 0;
|
894 |
root = msg->message; |
895 |
if(msg->message == NULL) { |
896 |
JANUS_LOG(LOG_ERR, "No message??\n");
|
897 |
error_code = JANUS_NOSIP_ERROR_NO_MESSAGE; |
898 |
g_snprintf(error_cause, 512, "%s", "No message??"); |
899 |
goto error;
|
900 |
} |
901 |
if(!json_is_object(root)) {
|
902 |
JANUS_LOG(LOG_ERR, "JSON error: not an object\n");
|
903 |
error_code = JANUS_NOSIP_ERROR_INVALID_JSON; |
904 |
g_snprintf(error_cause, 512, "JSON error: not an object"); |
905 |
goto error;
|
906 |
} |
907 |
JANUS_VALIDATE_JSON_OBJECT(root, request_parameters, |
908 |
error_code, error_cause, TRUE, |
909 |
JANUS_NOSIP_ERROR_MISSING_ELEMENT, JANUS_NOSIP_ERROR_INVALID_ELEMENT); |
910 |
if(error_code != 0) |
911 |
goto error;
|
912 |
json_t *request = json_object_get(root, "request");
|
913 |
const char *request_text = json_string_value(request); |
914 |
json_t *result = NULL, *localjsep = NULL; |
915 |
|
916 |
if(!strcasecmp(request_text, "generate") || !strcasecmp(request_text, "process")) { |
917 |
/* Shared code for two different requests:
|
918 |
* generate: Take a JSEP offer or answer and generate a barebone SDP the application can use
|
919 |
* process: Process a remote barebone SDP, and match it to the one we may have generated before */
|
920 |
gboolean generate = !strcasecmp(request_text, "generate") ? TRUE : FALSE;
|
921 |
if(generate) {
|
922 |
JANUS_VALIDATE_JSON_OBJECT(root, generate_parameters, |
923 |
error_code, error_cause, TRUE, |
924 |
JANUS_NOSIP_ERROR_MISSING_ELEMENT, JANUS_NOSIP_ERROR_INVALID_ELEMENT); |
925 |
} else {
|
926 |
JANUS_VALIDATE_JSON_OBJECT(root, process_parameters, |
927 |
error_code, error_cause, TRUE, |
928 |
JANUS_NOSIP_ERROR_MISSING_ELEMENT, JANUS_NOSIP_ERROR_INVALID_ELEMENT); |
929 |
} |
930 |
if(error_code != 0) |
931 |
goto error;
|
932 |
/* Any SDP to handle? if not, something's wrong */
|
933 |
const char *msg_sdp_type = json_string_value(json_object_get(generate ? msg->jsep : root, "type")); |
934 |
const char *msg_sdp = json_string_value(json_object_get(generate ? msg->jsep : root, "sdp")); |
935 |
if(!msg_sdp) {
|
936 |
JANUS_LOG(LOG_ERR, "Missing SDP\n");
|
937 |
error_code = JANUS_NOSIP_ERROR_MISSING_SDP; |
938 |
g_snprintf(error_cause, 512, "Missing SDP"); |
939 |
goto error;
|
940 |
} |
941 |
if(!msg_sdp_type || (strcasecmp(msg_sdp_type, "offer") && strcasecmp(msg_sdp_type, "answer"))) { |
942 |
JANUS_LOG(LOG_ERR, "Missing or invalid SDP type\n");
|
943 |
error_code = JANUS_NOSIP_ERROR_MISSING_SDP; |
944 |
g_snprintf(error_cause, 512, "Missing or invalid SDP type"); |
945 |
goto error;
|
946 |
} |
947 |
gboolean offer = !strcasecmp(msg_sdp_type, "offer");
|
948 |
if(strstr(msg_sdp, "m=application")) { |
949 |
JANUS_LOG(LOG_ERR, "The NoSIP plugin does not support DataChannels\n");
|
950 |
error_code = JANUS_NOSIP_ERROR_MISSING_SDP; |
951 |
g_snprintf(error_cause, 512, "The NoSIP plugin does not support DataChannels"); |
952 |
goto error;
|
953 |
} |
954 |
/* Check if the user provided an info string to provide context */
|
955 |
const char *info = json_string_value(json_object_get(root, "info")); |
956 |
/* SDES-SRTP is disabled by default, let's see if we need to enable it */
|
957 |
gboolean do_srtp = FALSE, require_srtp = FALSE; |
958 |
if(generate) {
|
959 |
json_t *srtp = json_object_get(root, "srtp");
|
960 |
if(srtp) {
|
961 |
const char *srtp_text = json_string_value(srtp); |
962 |
if(!strcasecmp(srtp_text, "sdes_optional")) { |
963 |
/* Negotiate SDES, but make it optional */
|
964 |
do_srtp = TRUE; |
965 |
} else if(!strcasecmp(srtp_text, "sdes_mandatory")) { |
966 |
/* Negotiate SDES, and require it */
|
967 |
do_srtp = TRUE; |
968 |
require_srtp = TRUE; |
969 |
} else {
|
970 |
JANUS_LOG(LOG_ERR, "Invalid element (srtp can only be sdes_optional or sdes_mandatory)\n");
|
971 |
error_code = JANUS_NOSIP_ERROR_INVALID_ELEMENT; |
972 |
g_snprintf(error_cause, 512, "Invalid element (srtp can only be sdes_optional or sdes_mandatory)"); |
973 |
goto error;
|
974 |
} |
975 |
} |
976 |
if(offer) {
|
977 |
/* Clean up SRTP stuff from before first, in case it's still needed */
|
978 |
janus_nosip_srtp_cleanup(session); |
979 |
session->media.require_srtp = require_srtp; |
980 |
session->media.has_srtp_local = do_srtp; |
981 |
if(do_srtp) {
|
982 |
JANUS_LOG(LOG_VERB, "Going to negotiate SDES-SRTP (%s)...\n", require_srtp ? "mandatory" : "optional"); |
983 |
} |
984 |
} else {
|
985 |
/* Make sure the request is consistent with the state (original offer) */
|
986 |
if(session->media.require_srtp && !session->media.has_srtp_remote) {
|
987 |
JANUS_LOG(LOG_ERR, "Can't generate answer: SDES-SRTP required, but caller didn't offer it\n");
|
988 |
error_code = JANUS_NOSIP_ERROR_TOO_STRICT; |
989 |
g_snprintf(error_cause, 512, "Can't generate answer: SDES-SRTP required, but caller didn't offer it"); |
990 |
goto error;
|
991 |
} |
992 |
do_srtp = do_srtp || session->media.has_srtp_remote; |
993 |
} |
994 |
} |
995 |
/* Parse the SDP we got, manipulate some things, and generate a new one */
|
996 |
char sdperror[100]; |
997 |
janus_sdp *parsed_sdp = janus_sdp_parse(msg_sdp, sdperror, sizeof(sdperror));
|
998 |
if(!parsed_sdp) {
|
999 |
JANUS_LOG(LOG_ERR, "Error parsing SDP: %s\n", sdperror);
|
1000 |
error_code = JANUS_NOSIP_ERROR_MISSING_SDP; |
1001 |
g_snprintf(error_cause, 512, "Error parsing SDP: %s", sdperror); |
1002 |
goto error;
|
1003 |
} |
1004 |
if(generate) {
|
1005 |
/* Allocate RTP ports and merge them with the anonymized SDP */
|
1006 |
if(strstr(msg_sdp, "m=audio") && !strstr(msg_sdp, "m=audio 0")) { |
1007 |
JANUS_LOG(LOG_VERB, "Going to negotiate audio...\n");
|
1008 |
session->media.has_audio = 1; /* FIXME Maybe we need a better way to signal this */ |
1009 |
} |
1010 |
if(strstr(msg_sdp, "m=video") && !strstr(msg_sdp, "m=video 0")) { |
1011 |
JANUS_LOG(LOG_VERB, "Going to negotiate video...\n");
|
1012 |
session->media.has_video = 1; /* FIXME Maybe we need a better way to signal this */ |
1013 |
} |
1014 |
if(janus_nosip_allocate_local_ports(session) < 0) { |
1015 |
JANUS_LOG(LOG_ERR, "Could not allocate RTP/RTCP ports\n");
|
1016 |
janus_sdp_free(parsed_sdp); |
1017 |
error_code = JANUS_NOSIP_ERROR_IO_ERROR; |
1018 |
g_snprintf(error_cause, 512, "Could not allocate RTP/RTCP ports"); |
1019 |
goto error;
|
1020 |
} |
1021 |
char *sdp = janus_nosip_sdp_manipulate(session, parsed_sdp, FALSE);
|
1022 |
if(sdp == NULL) { |
1023 |
JANUS_LOG(LOG_ERR, "Could not allocate RTP/RTCP ports\n");
|
1024 |
janus_sdp_free(parsed_sdp); |
1025 |
error_code = JANUS_NOSIP_ERROR_IO_ERROR; |
1026 |
g_snprintf(error_cause, 512, "Could not allocate RTP/RTCP ports"); |
1027 |
goto error;
|
1028 |
} |
1029 |
/* Take note of the SDP (may be useful for UPDATEs or re-INVITEs) */
|
1030 |
janus_sdp_free(session->sdp); |
1031 |
session->sdp = parsed_sdp; |
1032 |
JANUS_LOG(LOG_VERB, "Prepared SDP %s for (%p)\n%s", msg_sdp_type, info, sdp);
|
1033 |
g_atomic_int_set(&session->hangingup, 0);
|
1034 |
/* Also notify event handlers */
|
1035 |
if(notify_events && gateway->events_is_enabled()) {
|
1036 |
json_t *info = json_object(); |
1037 |
json_object_set_new(info, "event", json_string("generated")); |
1038 |
json_object_set_new(info, "type", json_string(offer ? "offer" : "answer")); |
1039 |
json_object_set_new(info, "sdp", json_string(sdp));
|
1040 |
gateway->notify_event(&janus_nosip_plugin, session->handle, info); |
1041 |
} |
1042 |
/* Send the barebone SDP back */
|
1043 |
result = json_object(); |
1044 |
json_object_set_new(result, "event", json_string("generated")); |
1045 |
json_object_set_new(result, "type", json_string(offer ? "offer" : "answer")); |
1046 |
json_object_set_new(result, "sdp", json_string(sdp));
|
1047 |
g_free(sdp); |
1048 |
} else {
|
1049 |
/* We got a barebone offer or answer from our peer: process it accordingly */
|
1050 |
gboolean changed = FALSE; |
1051 |
if(offer) {
|
1052 |
/* Clean up SRTP stuff from before first, in case it's still needed */
|
1053 |
janus_nosip_srtp_cleanup(session); |
1054 |
} |
1055 |
janus_nosip_sdp_process(session, parsed_sdp, !offer, FALSE, &changed); |
1056 |
/* Check if offer has neither audio nor video, fail */
|
1057 |
if(!session->media.has_audio && !session->media.has_video) {
|
1058 |
JANUS_LOG(LOG_ERR, "No audio and no video being negotiated\n");
|
1059 |
janus_sdp_free(parsed_sdp); |
1060 |
error_code = JANUS_NOSIP_ERROR_INVALID_SDP; |
1061 |
g_snprintf(error_cause, 512, "No audio and no video being negotiated"); |
1062 |
goto error;
|
1063 |
} |
1064 |
/* Also fail if there's no remote IP address that can be used for RTP */
|
1065 |
if(!session->media.remote_ip) {
|
1066 |
JANUS_LOG(LOG_ERR, "No remote IP address\n");
|
1067 |
janus_sdp_free(parsed_sdp); |
1068 |
error_code = JANUS_NOSIP_ERROR_INVALID_SDP; |
1069 |
g_snprintf(error_cause, 512, "No remote IP address"); |
1070 |
goto error;
|
1071 |
} |
1072 |
/* Take note of the SDP (may be useful for UPDATEs or re-INVITEs) */
|
1073 |
janus_sdp_free(session->sdp); |
1074 |
session->sdp = parsed_sdp; |
1075 |
/* Also notify event handlers */
|
1076 |
if(notify_events && gateway->events_is_enabled()) {
|
1077 |
json_t *info = json_object(); |
1078 |
json_object_set_new(info, "event", json_string("processed")); |
1079 |
json_object_set_new(info, "type", json_string(offer ? "offer" : "answer")); |
1080 |
json_object_set_new(info, "sdp", json_string(msg_sdp));
|
1081 |
gateway->notify_event(&janus_nosip_plugin, session->handle, info); |
1082 |
} |
1083 |
/* Send SDP to the browser */
|
1084 |
result = json_object(); |
1085 |
json_object_set_new(result, "event", json_string("processed")); |
1086 |
if(session->media.has_srtp_remote) {
|
1087 |
json_object_set_new(result, "srtp",
|
1088 |
json_string(session->media.require_srtp ? "sdes_mandatory" : "sdes_optional")); |
1089 |
} |
1090 |
localjsep = json_pack("{ssss}", "type", msg_sdp_type, "sdp", msg_sdp); |
1091 |
} |
1092 |
/* If this is an answer, start the media */
|
1093 |
if(!offer) {
|
1094 |
/* Start the media */
|
1095 |
session->media.ready = 1; /* FIXME Maybe we need a better way to signal this */ |
1096 |
GError *error = NULL;
|
1097 |
char tname[16]; |
1098 |
g_snprintf(tname, sizeof(tname), "nosiprtp %p", session); |
1099 |
g_thread_try_new(tname, janus_nosip_relay_thread, session, &error); |
1100 |
if(error != NULL) { |
1101 |
JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the RTP/RTCP thread...\n", error->code, error->message ? error->message : "??"); |
1102 |
} |
1103 |
} |
1104 |
} else if(!strcasecmp(request_text, "hangup")) { |
1105 |
/* Get rid of an ongoing session */
|
1106 |
gateway->close_pc(session->handle); |
1107 |
result = json_object(); |
1108 |
json_object_set_new(result, "event", json_string("hangingup")); |
1109 |
} else if(!strcasecmp(request_text, "recording")) { |
1110 |
/* Start or stop recording */
|
1111 |
JANUS_VALIDATE_JSON_OBJECT(root, recording_parameters, |
1112 |
error_code, error_cause, TRUE, |
1113 |
JANUS_NOSIP_ERROR_MISSING_ELEMENT, JANUS_NOSIP_ERROR_INVALID_ELEMENT); |
1114 |
if(error_code != 0) |
1115 |
goto error;
|
1116 |
json_t *action = json_object_get(root, "action");
|
1117 |
const char *action_text = json_string_value(action); |
1118 |
if(strcasecmp(action_text, "start") && strcasecmp(action_text, "stop")) { |
1119 |
JANUS_LOG(LOG_ERR, "Invalid action (should be start|stop)\n");
|
1120 |
error_code = JANUS_NOSIP_ERROR_INVALID_ELEMENT; |
1121 |
g_snprintf(error_cause, 512, "Invalid action (should be start|stop)"); |
1122 |
goto error;
|
1123 |
} |
1124 |
gboolean record_audio = FALSE, record_video = FALSE, /* No media is recorded by default */
|
1125 |
record_peer_audio = FALSE, record_peer_video = FALSE; |
1126 |
json_t *audio = json_object_get(root, "audio");
|
1127 |
record_audio = audio ? json_is_true(audio) : FALSE; |
1128 |
json_t *video = json_object_get(root, "video");
|
1129 |
record_video = video ? json_is_true(video) : FALSE; |
1130 |
json_t *peer_audio = json_object_get(root, "peer_audio");
|
1131 |
record_peer_audio = peer_audio ? json_is_true(peer_audio) : FALSE; |
1132 |
json_t *peer_video = json_object_get(root, "peer_video");
|
1133 |
record_peer_video = peer_video ? json_is_true(peer_video) : FALSE; |
1134 |
if(!record_audio && !record_video && !record_peer_audio && !record_peer_video) {
|
1135 |
JANUS_LOG(LOG_ERR, "Invalid request (at least one of audio, video, peer_audio and peer_video should be true)\n");
|
1136 |
error_code = JANUS_NOSIP_ERROR_RECORDING_ERROR; |
1137 |
g_snprintf(error_cause, 512, "Invalid request (at least one of audio, video, peer_audio and peer_video should be true)"); |
1138 |
goto error;
|
1139 |
} |
1140 |
json_t *recfile = json_object_get(root, "filename");
|
1141 |
const char *recording_base = json_string_value(recfile); |
1142 |
janus_mutex_lock(&session->rec_mutex); |
1143 |
if(!strcasecmp(action_text, "start")) { |
1144 |
/* Start recording something */
|
1145 |
char filename[255]; |
1146 |
gint64 now = janus_get_real_time(); |
1147 |
if(record_peer_audio || record_peer_video) {
|
1148 |
JANUS_LOG(LOG_INFO, "Starting recording of peer's %s\n",
|
1149 |
(record_peer_audio && record_peer_video ? "audio and video" : (record_peer_audio ? "audio" : "video"))); |
1150 |
/* Start recording this peer's audio and/or video */
|
1151 |
if(record_peer_audio) {
|
1152 |
memset(filename, 0, 255); |
1153 |
if(recording_base) {
|
1154 |
/* Use the filename and path we have been provided */
|
1155 |
g_snprintf(filename, 255, "%s-peer-audio", recording_base); |
1156 |
/* FIXME This only works if offer/answer happened */
|
1157 |
session->arc_peer = janus_recorder_create(NULL, session->media.audio_pt_name, filename);
|
1158 |
if(session->arc_peer == NULL) { |
1159 |
/* FIXME We should notify the fact the recorder could not be created */
|
1160 |
JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this peer!\n");
|
1161 |
} |
1162 |
} else {
|
1163 |
/* Build a filename */
|
1164 |
g_snprintf(filename, 255, "nosip-%p-%"SCNi64"-peer-audio", session, now); |
1165 |
/* FIXME This only works if offer/answer happened */
|
1166 |
session->arc_peer = janus_recorder_create(NULL, session->media.audio_pt_name, filename);
|
1167 |
if(session->arc_peer == NULL) { |
1168 |
/* FIXME We should notify the fact the recorder could not be created */
|
1169 |
JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this peer!\n");
|
1170 |
} |
1171 |
} |
1172 |
} |
1173 |
if(record_peer_video) {
|
1174 |
memset(filename, 0, 255); |
1175 |
if(recording_base) {
|
1176 |
/* Use the filename and path we have been provided */
|
1177 |
g_snprintf(filename, 255, "%s-peer-video", recording_base); |
1178 |
/* FIXME This only works if offer/answer happened */
|
1179 |
session->vrc_peer = janus_recorder_create(NULL, session->media.video_pt_name, filename);
|
1180 |
if(session->vrc_peer == NULL) { |
1181 |
/* FIXME We should notify the fact the recorder could not be created */
|
1182 |
JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this peer!\n");
|
1183 |
} |
1184 |
} else {
|
1185 |
/* Build a filename */
|
1186 |
g_snprintf(filename, 255, "nosip-%p-%"SCNi64"-peer-video", session, now); |
1187 |
/* FIXME This only works if offer/answer happened */
|
1188 |
session->vrc_peer = janus_recorder_create(NULL, session->media.video_pt_name, filename);
|
1189 |
if(session->vrc_peer == NULL) { |
1190 |
/* FIXME We should notify the fact the recorder could not be created */
|
1191 |
JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this peer!\n");
|
1192 |
} |
1193 |
} |
1194 |
/* TODO We should send a FIR/PLI to this peer... */
|
1195 |
} |
1196 |
} |
1197 |
if(record_audio || record_video) {
|
1198 |
/* Start recording the user's audio and/or video */
|
1199 |
JANUS_LOG(LOG_INFO, "Starting recording of user's %s (%p)\n",
|
1200 |
(record_audio && record_video ? "audio and video" : (record_audio ? "audio" : "video")), session); |
1201 |
if(record_audio) {
|
1202 |
memset(filename, 0, 255); |
1203 |
if(recording_base) {
|
1204 |
/* Use the filename and path we have been provided */
|
1205 |
g_snprintf(filename, 255, "%s-user-audio", recording_base); |
1206 |
/* FIXME This only works if offer/answer happened */
|
1207 |
session->arc = janus_recorder_create(NULL, session->media.audio_pt_name, filename);
|
1208 |
if(session->arc == NULL) { |
1209 |
/* FIXME We should notify the fact the recorder could not be created */
|
1210 |
JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this peer!\n");
|
1211 |
} |
1212 |
} else {
|
1213 |
/* Build a filename */
|
1214 |
g_snprintf(filename, 255, "nosip-%p-%"SCNi64"-own-audio", session, now); |
1215 |
/* FIXME This only works if offer/answer happened */
|
1216 |
session->arc = janus_recorder_create(NULL, session->media.audio_pt_name, filename);
|
1217 |
if(session->arc == NULL) { |
1218 |
/* FIXME We should notify the fact the recorder could not be created */
|
1219 |
JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this peer!\n");
|
1220 |
} |
1221 |
} |
1222 |
} |
1223 |
if(record_video) {
|
1224 |
memset(filename, 0, 255); |
1225 |
if(recording_base) {
|
1226 |
/* Use the filename and path we have been provided */
|
1227 |
g_snprintf(filename, 255, "%s-user-video", recording_base); |
1228 |
/* FIXME This only works if offer/answer happened */
|
1229 |
session->vrc = janus_recorder_create(NULL, session->media.video_pt_name, filename);
|
1230 |
if(session->vrc == NULL) { |
1231 |
/* FIXME We should notify the fact the recorder could not be created */
|
1232 |
JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this user!\n");
|
1233 |
} |
1234 |
} else {
|
1235 |
/* Build a filename */
|
1236 |
g_snprintf(filename, 255, "nosip-%p-%"SCNi64"-own-video", session, now); |
1237 |
/* FIXME This only works if offer/answer happened */
|
1238 |
session->vrc = janus_recorder_create(NULL, session->media.video_pt_name, filename);
|
1239 |
if(session->vrc == NULL) { |
1240 |
/* FIXME We should notify the fact the recorder could not be created */
|
1241 |
JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this user!\n");
|
1242 |
} |
1243 |
} |
1244 |
/* Send a PLI */
|
1245 |
JANUS_LOG(LOG_VERB, "Recording video, sending a PLI to kickstart it\n");
|
1246 |
char buf[12]; |
1247 |
janus_rtcp_pli((char *)&buf, 12); |
1248 |
gateway->relay_rtcp(session->handle, 1, buf, 12); |
1249 |
} |
1250 |
} |
1251 |
} else {
|
1252 |
/* Stop recording something: notice that this never returns an error, even when we were not recording anything */
|
1253 |
if(record_audio) {
|
1254 |
if(session->arc) {
|
1255 |
janus_recorder_close(session->arc); |
1256 |
JANUS_LOG(LOG_INFO, "Closed user's audio recording %s\n", session->arc->filename ? session->arc->filename : "??"); |
1257 |
janus_recorder_free(session->arc); |
1258 |
} |
1259 |
session->arc = NULL;
|
1260 |
} |
1261 |
if(record_video) {
|
1262 |
if(session->vrc) {
|
1263 |
janus_recorder_close(session->vrc); |
1264 |
JANUS_LOG(LOG_INFO, "Closed user's video recording %s\n", session->vrc->filename ? session->vrc->filename : "??"); |
1265 |
janus_recorder_free(session->vrc); |
1266 |
} |
1267 |
session->vrc = NULL;
|
1268 |
} |
1269 |
if(record_peer_audio) {
|
1270 |
if(session->arc_peer) {
|
1271 |
janus_recorder_close(session->arc_peer); |
1272 |
JANUS_LOG(LOG_INFO, "Closed peer's audio recording %s\n", session->arc_peer->filename ? session->arc_peer->filename : "??"); |
1273 |
janus_recorder_free(session->arc_peer); |
1274 |
} |
1275 |
session->arc_peer = NULL;
|
1276 |
} |
1277 |
if(record_peer_video) {
|
1278 |
if(session->vrc_peer) {
|
1279 |
janus_recorder_close(session->vrc_peer); |
1280 |
JANUS_LOG(LOG_INFO, "Closed peer's video recording %s\n", session->vrc_peer->filename ? session->vrc_peer->filename : "??"); |
1281 |
janus_recorder_free(session->vrc_peer); |
1282 |
} |
1283 |
session->vrc_peer = NULL;
|
1284 |
} |
1285 |
} |
1286 |
janus_mutex_unlock(&session->rec_mutex); |
1287 |
/* Notify the result */
|
1288 |
result = json_object(); |
1289 |
json_object_set_new(result, "event", json_string("recordingupdated")); |
1290 |
} else {
|
1291 |
JANUS_LOG(LOG_ERR, "Unknown request (%s)\n", request_text);
|
1292 |
error_code = JANUS_NOSIP_ERROR_INVALID_REQUEST; |
1293 |
g_snprintf(error_cause, 512, "Unknown request (%s)", request_text); |
1294 |
goto error;
|
1295 |
} |
1296 |
|
1297 |
/* Prepare JSON event */
|
1298 |
json_t *event = json_object(); |
1299 |
json_object_set_new(event, "nosip", json_string("event")); |
1300 |
if(result != NULL) |
1301 |
json_object_set_new(event, "result", result);
|
1302 |
int ret = gateway->push_event(msg->handle, &janus_nosip_plugin, msg->transaction, event, localjsep);
|
1303 |
JANUS_LOG(LOG_VERB, " >> Pushing event: %d (%s)\n", ret, janus_get_api_error(ret));
|
1304 |
json_decref(event); |
1305 |
if(localjsep)
|
1306 |
json_decref(localjsep); |
1307 |
janus_nosip_message_free(msg); |
1308 |
continue;
|
1309 |
|
1310 |
error:
|
1311 |
{ |
1312 |
/* Prepare JSON error event */
|
1313 |
json_t *event = json_object(); |
1314 |
json_object_set_new(event, "nosip", json_string("event")); |
1315 |
json_object_set_new(event, "error_code", json_integer(error_code));
|
1316 |
json_object_set_new(event, "error", json_string(error_cause));
|
1317 |
int ret = gateway->push_event(msg->handle, &janus_nosip_plugin, msg->transaction, event, NULL); |
1318 |
JANUS_LOG(LOG_VERB, " >> Pushing event: %d (%s)\n", ret, janus_get_api_error(ret));
|
1319 |
json_decref(event); |
1320 |
janus_nosip_message_free(msg); |
1321 |
} |
1322 |
} |
1323 |
JANUS_LOG(LOG_VERB, "Leaving NoSIP handler thread\n");
|
1324 |
return NULL; |
1325 |
} |
1326 |
|
1327 |
|
1328 |
void janus_nosip_sdp_process(janus_nosip_session *session, janus_sdp *sdp, gboolean answer, gboolean update, gboolean *changed) {
|
1329 |
if(!session || !sdp)
|
1330 |
return;
|
1331 |
/* c= */
|
1332 |
if(sdp->c_addr) {
|
1333 |
if(update && strcmp(sdp->c_addr, session->media.remote_ip)) {
|
1334 |
/* This is an update and an address changed */
|
1335 |
if(changed)
|
1336 |
*changed = TRUE; |
1337 |
} |
1338 |
g_free(session->media.remote_ip); |
1339 |
session->media.remote_ip = g_strdup(sdp->c_addr); |
1340 |
} |
1341 |
GList *temp = sdp->m_lines; |
1342 |
while(temp) {
|
1343 |
janus_sdp_mline *m = (janus_sdp_mline *)temp->data; |
1344 |
session->media.require_srtp = session->media.require_srtp || (m->proto && !strcasecmp(m->proto, "RTP/SAVP"));
|
1345 |
if(m->type == JANUS_SDP_AUDIO) {
|
1346 |
if(m->port) {
|
1347 |
if(m->port != session->media.remote_audio_rtp_port) {
|
1348 |
/* This is an update and an address changed */
|
1349 |
if(changed)
|
1350 |
*changed = TRUE; |
1351 |
} |
1352 |
session->media.has_audio = 1;
|
1353 |
session->media.remote_audio_rtp_port = m->port; |
1354 |
session->media.remote_audio_rtcp_port = m->port+1; /* FIXME We're assuming RTCP is on the next port */ |
1355 |
if(m->direction == JANUS_SDP_SENDONLY || m->direction == JANUS_SDP_INACTIVE)
|
1356 |
session->media.audio_send = FALSE; |
1357 |
else
|
1358 |
session->media.audio_send = TRUE; |
1359 |
} else {
|
1360 |
session->media.audio_send = FALSE; |
1361 |
} |
1362 |
} else if(m->type == JANUS_SDP_VIDEO) { |
1363 |
if(m->port) {
|
1364 |
if(m->port != session->media.remote_video_rtp_port) {
|
1365 |
/* This is an update and an address changed */
|
1366 |
if(changed)
|
1367 |
*changed = TRUE; |
1368 |
} |
1369 |
session->media.has_video = 1;
|
1370 |
session->media.remote_video_rtp_port = m->port; |
1371 |
session->media.remote_video_rtcp_port = m->port+1; /* FIXME We're assuming RTCP is on the next port */ |
1372 |
if(m->direction == JANUS_SDP_SENDONLY || m->direction == JANUS_SDP_INACTIVE)
|
1373 |
session->media.video_send = FALSE; |
1374 |
else
|
1375 |
session->media.video_send = TRUE; |
1376 |
} else {
|
1377 |
session->media.video_send = FALSE; |
1378 |
} |
1379 |
} else {
|
1380 |
JANUS_LOG(LOG_WARN, "Unsupported media line (not audio/video)\n");
|
1381 |
temp = temp->next; |
1382 |
continue;
|
1383 |
} |
1384 |
if(m->c_addr) {
|
1385 |
if(update && strcmp(m->c_addr, session->media.remote_ip)) {
|
1386 |
/* This is an update and an address changed */
|
1387 |
if(changed)
|
1388 |
*changed = TRUE; |
1389 |
} |
1390 |
g_free(session->media.remote_ip); |
1391 |
session->media.remote_ip = g_strdup(m->c_addr); |
1392 |
} |
1393 |
if(update) {
|
1394 |
/* FIXME This is a session update, we only accept changes in IP/ports */
|
1395 |
temp = temp->next; |
1396 |
continue;
|
1397 |
} |
1398 |
GList *tempA = m->attributes; |
1399 |
while(tempA) {
|
1400 |
janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data; |
1401 |
if(a->name) {
|
1402 |
if(!strcasecmp(a->name, "crypto")) { |
1403 |
if(m->type == JANUS_SDP_AUDIO || m->type == JANUS_SDP_VIDEO) {
|
1404 |
gint32 tag = 0;
|
1405 |
int suite;
|
1406 |
char crypto[81]; |
1407 |
/* FIXME inline can be more complex than that, and we're currently only offering SHA1_80 */
|
1408 |
int res = sscanf(a->value, "%"SCNi32" AES_CM_128_HMAC_SHA1_%2d inline:%80s", |
1409 |
&tag, &suite, crypto); |
1410 |
if(res != 3) { |
1411 |
JANUS_LOG(LOG_WARN, "Failed to parse crypto line, ignoring... %s\n", a->value);
|
1412 |
} else {
|
1413 |
gboolean video = (m->type == JANUS_SDP_VIDEO); |
1414 |
int current_suite = video ? session->media.video_srtp_suite_in : session->media.audio_srtp_suite_in;
|
1415 |
if(current_suite == 0) { |
1416 |
if(video)
|
1417 |
session->media.video_srtp_suite_in = suite; |
1418 |
else
|
1419 |
session->media.audio_srtp_suite_in = suite; |
1420 |
janus_nosip_srtp_set_remote(session, video, crypto, suite); |
1421 |
session->media.has_srtp_remote = TRUE; |
1422 |
} else {
|
1423 |
JANUS_LOG(LOG_WARN, "We already configured a %s crypto context (AES_CM_128_HMAC_SHA1_%d), skipping additional crypto line\n",
|
1424 |
video ? "video" : "audio", current_suite); |
1425 |
} |
1426 |
} |
1427 |
} |
1428 |
} |
1429 |
} |
1430 |
tempA = tempA->next; |
1431 |
} |
1432 |
if(answer && (m->type == JANUS_SDP_AUDIO || m->type == JANUS_SDP_VIDEO)) {
|
1433 |
/* Check which codec was negotiated eventually */
|
1434 |
int pt = -1; |
1435 |
if(m->ptypes)
|
1436 |
pt = GPOINTER_TO_INT(m->ptypes->data); |
1437 |
if(pt > -1) { |
1438 |
if(m->type == JANUS_SDP_AUDIO) {
|
1439 |
session->media.audio_pt = pt; |
1440 |
} else {
|
1441 |
session->media.video_pt = pt; |
1442 |
} |
1443 |
} |
1444 |
} |
1445 |
temp = temp->next; |
1446 |
} |
1447 |
if(update && changed && *changed) {
|
1448 |
/* Something changed: mark this on the session, so that the thread can update the sockets */
|
1449 |
session->media.updated = TRUE; |
1450 |
if(session->media.pipefd[1] > 0) { |
1451 |
int code = 1; |
1452 |
ssize_t res = 0;
|
1453 |
do {
|
1454 |
res = write(session->media.pipefd[1], &code, sizeof(int)); |
1455 |
} while(res == -1 && errno == EINTR); |
1456 |
} |
1457 |
} |
1458 |
} |
1459 |
|
1460 |
char *janus_nosip_sdp_manipulate(janus_nosip_session *session, janus_sdp *sdp, gboolean answer) {
|
1461 |
if(!session || !sdp)
|
1462 |
return NULL; |
1463 |
/* Start replacing stuff */
|
1464 |
JANUS_LOG(LOG_VERB, "Setting protocol to %s\n", session->media.require_srtp ? "RTP/SAVP" : "RTP/AVP"); |
1465 |
GList *temp = sdp->m_lines; |
1466 |
while(temp) {
|
1467 |
janus_sdp_mline *m = (janus_sdp_mline *)temp->data; |
1468 |
g_free(m->proto); |
1469 |
m->proto = g_strdup(session->media.require_srtp ? "RTP/SAVP" : "RTP/AVP"); |
1470 |
if(m->type == JANUS_SDP_AUDIO) {
|
1471 |
m->port = session->media.local_audio_rtp_port; |
1472 |
if(session->media.has_srtp_local) {
|
1473 |
char *crypto = NULL; |
1474 |
session->media.audio_srtp_suite_out = 80;
|
1475 |
janus_nosip_srtp_set_local(session, FALSE, &crypto); |
1476 |
/* FIXME 32? 80? Both? */
|
1477 |
janus_sdp_attribute *a = janus_sdp_attribute_create("crypto", "1 AES_CM_128_HMAC_SHA1_80 inline:%s", crypto); |
1478 |
g_free(crypto); |
1479 |
m->attributes = g_list_append(m->attributes, a); |
1480 |
} |
1481 |
} else if(m->type == JANUS_SDP_VIDEO) { |
1482 |
m->port = session->media.local_video_rtp_port; |
1483 |
if(session->media.has_srtp_local) {
|
1484 |
char *crypto = NULL; |
1485 |
session->media.audio_srtp_suite_out = 80;
|
1486 |
janus_nosip_srtp_set_local(session, TRUE, &crypto); |
1487 |
/* FIXME 32? 80? Both? */
|
1488 |
janus_sdp_attribute *a = janus_sdp_attribute_create("crypto", "1 AES_CM_128_HMAC_SHA1_80 inline:%s", crypto); |
1489 |
g_free(crypto); |
1490 |
m->attributes = g_list_append(m->attributes, a); |
1491 |
} |
1492 |
} |
1493 |
g_free(m->c_addr); |
1494 |
m->c_addr = g_strdup(local_ip); |
1495 |
if(answer && (m->type == JANUS_SDP_AUDIO || m->type == JANUS_SDP_VIDEO)) {
|
1496 |
/* Check which codec was negotiated eventually */
|
1497 |
int pt = -1; |
1498 |
if(m->ptypes)
|
1499 |
pt = GPOINTER_TO_INT(m->ptypes->data); |
1500 |
if(pt > -1) { |
1501 |
if(m->type == JANUS_SDP_AUDIO) {
|
1502 |
session->media.audio_pt = pt; |
1503 |
} else {
|
1504 |
session->media.video_pt = pt; |
1505 |
} |
1506 |
} |
1507 |
} |
1508 |
temp = temp->next; |
1509 |
} |
1510 |
/* Generate a SDP string out of our changes */
|
1511 |
return janus_sdp_write(sdp);
|
1512 |
} |
1513 |
|
1514 |
/* Bind local RTP/RTCP sockets */
|
1515 |
static int janus_nosip_allocate_local_ports(janus_nosip_session *session) { |
1516 |
if(session == NULL) { |
1517 |
JANUS_LOG(LOG_ERR, "Invalid session\n");
|
1518 |
return -1; |
1519 |
} |
1520 |
/* Reset status */
|
1521 |
if(session->media.audio_rtp_fd != -1) { |
1522 |
close(session->media.audio_rtp_fd); |
1523 |
session->media.audio_rtp_fd = -1;
|
1524 |
} |
1525 |
if(session->media.audio_rtcp_fd != -1) { |
1526 |
close(session->media.audio_rtcp_fd); |
1527 |
session->media.audio_rtcp_fd = -1;
|
1528 |
} |
1529 |
session->media.local_audio_rtp_port = 0;
|
1530 |
session->media.local_audio_rtcp_port = 0;
|
1531 |
session->media.audio_ssrc = 0;
|
1532 |
if(session->media.video_rtp_fd != -1) { |
1533 |
close(session->media.video_rtp_fd); |
1534 |
session->media.video_rtp_fd = -1;
|
1535 |
} |
1536 |
if(session->media.video_rtcp_fd != -1) { |
1537 |
close(session->media.video_rtcp_fd); |
1538 |
session->media.video_rtcp_fd = -1;
|
1539 |
} |
1540 |
session->media.local_video_rtp_port = 0;
|
1541 |
session->media.local_video_rtcp_port = 0;
|
1542 |
session->media.video_ssrc = 0;
|
1543 |
if(session->media.pipefd[0] > 0) { |
1544 |
close(session->media.pipefd[0]);
|
1545 |
session->media.pipefd[0] = -1; |
1546 |
} |
1547 |
if(session->media.pipefd[1] > 0) { |
1548 |
close(session->media.pipefd[1]);
|
1549 |
session->media.pipefd[1] = -1; |
1550 |
} |
1551 |
/* Start */
|
1552 |
int attempts = 100; /* FIXME Don't retry forever */ |
1553 |
if(session->media.has_audio) {
|
1554 |
JANUS_LOG(LOG_VERB, "Allocating audio ports:\n");
|
1555 |
struct sockaddr_in audio_rtp_address, audio_rtcp_address;
|
1556 |
while(session->media.local_audio_rtp_port == 0 || session->media.local_audio_rtcp_port == 0) { |
1557 |
if(attempts == 0) /* Too many failures */ |
1558 |
return -1; |
1559 |
if(session->media.audio_rtp_fd == -1) { |
1560 |
session->media.audio_rtp_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
1561 |
} |
1562 |
if(session->media.audio_rtcp_fd == -1) { |
1563 |
session->media.audio_rtcp_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
1564 |
} |
1565 |
int rtp_port = g_random_int_range(10000, 60000); /* FIXME Should this be configurable? */ |
1566 |
if(rtp_port % 2) |
1567 |
rtp_port++; /* Pick an even port for RTP */
|
1568 |
audio_rtp_address.sin_family = AF_INET; |
1569 |
audio_rtp_address.sin_port = htons(rtp_port); |
1570 |
inet_pton(AF_INET, local_ip, &audio_rtp_address.sin_addr.s_addr); |
1571 |
if(bind(session->media.audio_rtp_fd, (struct sockaddr *)(&audio_rtp_address), sizeof(struct sockaddr)) < 0) { |
1572 |
JANUS_LOG(LOG_ERR, "Bind failed for audio RTP (port %d), trying a different one...\n", rtp_port);
|
1573 |
attempts--; |
1574 |
continue;
|
1575 |
} |
1576 |
JANUS_LOG(LOG_VERB, "Audio RTP listener bound to port %d\n", rtp_port);
|
1577 |
int rtcp_port = rtp_port+1; |
1578 |
audio_rtcp_address.sin_family = AF_INET; |
1579 |
audio_rtcp_address.sin_port = htons(rtcp_port); |
1580 |
inet_pton(AF_INET, local_ip, &audio_rtcp_address.sin_addr.s_addr); |
1581 |
if(bind(session->media.audio_rtcp_fd, (struct sockaddr *)(&audio_rtcp_address), sizeof(struct sockaddr)) < 0) { |
1582 |
JANUS_LOG(LOG_ERR, "Bind failed for audio RTCP (port %d), trying a different one...\n", rtcp_port);
|
1583 |
/* RTP socket is not valid anymore, reset it */
|
1584 |
close(session->media.audio_rtp_fd); |
1585 |
session->media.audio_rtp_fd = -1;
|
1586 |
attempts--; |
1587 |
continue;
|
1588 |
} |
1589 |
JANUS_LOG(LOG_VERB, "Audio RTCP listener bound to port %d\n", rtcp_port);
|
1590 |
session->media.local_audio_rtp_port = rtp_port; |
1591 |
session->media.local_audio_rtcp_port = rtcp_port; |
1592 |
} |
1593 |
} |
1594 |
if(session->media.has_video) {
|
1595 |
JANUS_LOG(LOG_VERB, "Allocating video ports:\n");
|
1596 |
struct sockaddr_in video_rtp_address, video_rtcp_address;
|
1597 |
while(session->media.local_video_rtp_port == 0 || session->media.local_video_rtcp_port == 0) { |
1598 |
if(attempts == 0) /* Too many failures */ |
1599 |
return -1; |
1600 |
if(session->media.video_rtp_fd == -1) { |
1601 |
session->media.video_rtp_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
1602 |
} |
1603 |
if(session->media.video_rtcp_fd == -1) { |
1604 |
session->media.video_rtcp_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
1605 |
} |
1606 |
int rtp_port = g_random_int_range(10000, 60000); /* FIXME Should this be configurable? */ |
1607 |
if(rtp_port % 2) |
1608 |
rtp_port++; /* Pick an even port for RTP */
|
1609 |
video_rtp_address.sin_family = AF_INET; |
1610 |
video_rtp_address.sin_port = htons(rtp_port); |
1611 |
inet_pton(AF_INET, local_ip, &video_rtp_address.sin_addr.s_addr); |
1612 |
if(bind(session->media.video_rtp_fd, (struct sockaddr *)(&video_rtp_address), sizeof(struct sockaddr)) < 0) { |
1613 |
JANUS_LOG(LOG_ERR, "Bind failed for video RTP (port %d), trying a different one...\n", rtp_port);
|
1614 |
attempts--; |
1615 |
continue;
|
1616 |
} |
1617 |
JANUS_LOG(LOG_VERB, "Video RTP listener bound to port %d\n", rtp_port);
|
1618 |
int rtcp_port = rtp_port+1; |
1619 |
video_rtcp_address.sin_family = AF_INET; |
1620 |
video_rtcp_address.sin_port = htons(rtcp_port); |
1621 |
inet_pton(AF_INET, local_ip, &video_rtcp_address.sin_addr.s_addr); |
1622 |
if(bind(session->media.video_rtcp_fd, (struct sockaddr *)(&video_rtcp_address), sizeof(struct sockaddr)) < 0) { |
1623 |
JANUS_LOG(LOG_ERR, "Bind failed for video RTCP (port %d), trying a different one...\n", rtcp_port);
|
1624 |
/* RTP socket is not valid anymore, reset it */
|
1625 |
close(session->media.video_rtp_fd); |
1626 |
session->media.video_rtp_fd = -1;
|
1627 |
attempts--; |
1628 |
continue;
|
1629 |
} |
1630 |
JANUS_LOG(LOG_VERB, "Video RTCP listener bound to port %d\n", rtcp_port);
|
1631 |
session->media.local_video_rtp_port = rtp_port; |
1632 |
session->media.local_video_rtcp_port = rtcp_port; |
1633 |
} |
1634 |
} |
1635 |
/* We need this to quickly interrupt the poll when it's time to update a session or wrap up */
|
1636 |
pipe(session->media.pipefd); |
1637 |
return 0; |
1638 |
} |
1639 |
|
1640 |
/* Helper method to (re)connect RTP/RTCP sockets */
|
1641 |
static void janus_nosip_connect_sockets(janus_nosip_session *session, struct sockaddr_in *server_addr) { |
1642 |
if(!session || !server_addr)
|
1643 |
return;
|
1644 |
|
1645 |
if(session->media.updated) {
|
1646 |
JANUS_LOG(LOG_VERB, "Updating session sockets\n");
|
1647 |
} |
1648 |
|
1649 |
/* Connect peers (FIXME This pretty much sucks right now) */
|
1650 |
if(session->media.remote_audio_rtp_port) {
|
1651 |
server_addr->sin_port = htons(session->media.remote_audio_rtp_port); |
1652 |
if(connect(session->media.audio_rtp_fd, (struct sockaddr *)server_addr, sizeof(struct sockaddr)) == -1) { |
1653 |
JANUS_LOG(LOG_ERR, "[NoSIP-%p] Couldn't connect audio RTP? (%s:%d)\n", session, session->media.remote_ip, session->media.remote_audio_rtp_port);
|
1654 |
JANUS_LOG(LOG_ERR, "[NoSIP-%p] -- %d (%s)\n", session, errno, strerror(errno));
|
1655 |
} |
1656 |
} |
1657 |
if(session->media.remote_audio_rtcp_port) {
|
1658 |
server_addr->sin_port = htons(session->media.remote_audio_rtcp_port); |
1659 |
if(connect(session->media.audio_rtcp_fd, (struct sockaddr *)server_addr, sizeof(struct sockaddr)) == -1) { |
1660 |
JANUS_LOG(LOG_ERR, "[NoSIP-%p] Couldn't connect audio RTCP? (%s:%d)\n", session, session->media.remote_ip, session->media.remote_audio_rtcp_port);
|
1661 |
JANUS_LOG(LOG_ERR, "[NoSIP-%p] -- %d (%s)\n", session, errno, strerror(errno));
|
1662 |
} |
1663 |
} |
1664 |
if(session->media.remote_video_rtp_port) {
|
1665 |
server_addr->sin_port = htons(session->media.remote_video_rtp_port); |
1666 |
if(connect(session->media.video_rtp_fd, (struct sockaddr *)server_addr, sizeof(struct sockaddr)) == -1) { |
1667 |
JANUS_LOG(LOG_ERR, "[NoSIP-%p] Couldn't connect video RTP? (%s:%d)\n", session, session->media.remote_ip, session->media.remote_video_rtp_port);
|
1668 |
JANUS_LOG(LOG_ERR, "[NoSIP-%p] -- %d (%s)\n", session, errno, strerror(errno));
|
1669 |
} |
1670 |
} |
1671 |
if(session->media.remote_video_rtcp_port) {
|
1672 |
server_addr->sin_port = htons(session->media.remote_video_rtcp_port); |
1673 |
if(connect(session->media.video_rtcp_fd, (struct sockaddr *)server_addr, sizeof(struct sockaddr)) == -1) { |
1674 |
JANUS_LOG(LOG_ERR, "[NoSIP-%p] Couldn't connect video RTCP? (%s:%d)\n", session, session->media.remote_ip, session->media.remote_video_rtcp_port);
|
1675 |
JANUS_LOG(LOG_ERR, "[NoSIP-%p] -- %d (%s)\n", session, errno, strerror(errno));
|
1676 |
} |
1677 |
} |
1678 |
|
1679 |
} |
1680 |
|
1681 |
/* Thread to relay RTP/RTCP frames coming from the peer */
|
1682 |
static void *janus_nosip_relay_thread(void *data) { |
1683 |
janus_nosip_session *session = (janus_nosip_session *)data; |
1684 |
if(!session) {
|
1685 |
g_thread_unref(g_thread_self()); |
1686 |
return NULL; |
1687 |
} |
1688 |
JANUS_LOG(LOG_INFO, "[NoSIP-%p] Starting relay thread\n", session);
|
1689 |
|
1690 |
gboolean have_server_ip = TRUE; |
1691 |
struct sockaddr_in server_addr;
|
1692 |
memset(&server_addr, 0, sizeof(server_addr)); |
1693 |
server_addr.sin_family = AF_INET; |
1694 |
if(session->media.remote_ip == NULL) { |
1695 |
JANUS_LOG(LOG_WARN, "[NoSIP-%p] No remote IP?\n", session);
|
1696 |
} else {
|
1697 |
if((inet_aton(session->media.remote_ip, &server_addr.sin_addr)) <= 0) { /* Not a numeric IP... */ |
1698 |
struct hostent *host = gethostbyname(session->media.remote_ip); /* ...resolve name */ |
1699 |
if(!host) {
|
1700 |
JANUS_LOG(LOG_ERR, "[NoSIP-%p] Couldn't get host (%s)\n", session, session->media.remote_ip);
|
1701 |
have_server_ip = FALSE; |
1702 |
} else {
|
1703 |
server_addr.sin_addr = *(struct in_addr *)host->h_addr_list;
|
1704 |
} |
1705 |
} |
1706 |
} |
1707 |
if(have_server_ip) {
|
1708 |
janus_nosip_connect_sockets(session, &server_addr); |
1709 |
} |
1710 |
|
1711 |
/* File descriptors */
|
1712 |
socklen_t addrlen; |
1713 |
struct sockaddr_in remote;
|
1714 |
int resfd = 0, bytes = 0; |
1715 |
struct pollfd fds[5]; |
1716 |
int pipe_fd = session->media.pipefd[0]; |
1717 |
char buffer[1500]; |
1718 |
memset(buffer, 0, 1500); |
1719 |
/* Loop */
|
1720 |
int num = 0; |
1721 |
gboolean goon = TRUE; |
1722 |
int astep = 0, vstep = 0; |
1723 |
guint32 ats = 0, vts = 0; |
1724 |
while(goon && session != NULL && |
1725 |
!session->destroyed && !g_atomic_int_get(&session->hangingup)) { |
1726 |
if(session->media.updated) {
|
1727 |
/* Apparently there was a session update */
|
1728 |
if(have_server_ip && (inet_aton(session->media.remote_ip, &server_addr.sin_addr) == 0)) { |
1729 |
janus_nosip_connect_sockets(session, &server_addr); |
1730 |
} else {
|
1731 |
JANUS_LOG(LOG_ERR, "[NoSIP-%p] Couldn't update session details: missing or invalid remote IP address? (%s)\n",
|
1732 |
session, session->media.remote_ip); |
1733 |
} |
1734 |
session->media.updated = FALSE; |
1735 |
} |
1736 |
|
1737 |
/* Prepare poll */
|
1738 |
num = 0;
|
1739 |
if(session->media.audio_rtp_fd != -1) { |
1740 |
fds[num].fd = session->media.audio_rtp_fd; |
1741 |
fds[num].events = POLLIN; |
1742 |
fds[num].revents = 0;
|
1743 |
num++; |
1744 |
} |
1745 |
if(session->media.audio_rtcp_fd != -1) { |
1746 |
fds[num].fd = session->media.audio_rtcp_fd; |
1747 |
fds[num].events = POLLIN; |
1748 |
fds[num].revents = 0;
|
1749 |
num++; |
1750 |
} |
1751 |
if(session->media.video_rtp_fd != -1) { |
1752 |
fds[num].fd = session->media.video_rtp_fd; |
1753 |
fds[num].events = POLLIN; |
1754 |
fds[num].revents = 0;
|
1755 |
num++; |
1756 |
} |
1757 |
if(session->media.video_rtcp_fd != -1) { |
1758 |
fds[num].fd = session->media.video_rtcp_fd; |
1759 |
fds[num].events = POLLIN; |
1760 |
fds[num].revents = 0;
|
1761 |
num++; |
1762 |
} |
1763 |
if(pipe_fd != -1) { |
1764 |
fds[num].fd = pipe_fd; |
1765 |
fds[num].events = POLLIN; |
1766 |
fds[num].revents = 0;
|
1767 |
num++; |
1768 |
} |
1769 |
/* Wait for some data */
|
1770 |
resfd = poll(fds, num, 1000);
|
1771 |
if(resfd < 0) { |
1772 |
JANUS_LOG(LOG_ERR, "[NoSIP-%p] Error polling...\n", session);
|
1773 |
JANUS_LOG(LOG_ERR, "[NoSIP-%p] -- %d (%s)\n", session, errno, strerror(errno));
|
1774 |
break;
|
1775 |
} else if(resfd == 0) { |
1776 |
/* No data, keep going */
|
1777 |
continue;
|
1778 |
} |
1779 |
if(session == NULL || session->destroyed) |
1780 |
break;
|
1781 |
int i = 0; |
1782 |
for(i=0; i<num; i++) { |
1783 |
if(fds[i].revents & (POLLERR | POLLHUP)) {
|
1784 |
/* If we just updated the session, let's wait until things have calmed down */
|
1785 |
if(session->media.updated)
|
1786 |
break;
|
1787 |
/* Check the socket error */
|
1788 |
int error = 0; |
1789 |
socklen_t errlen = sizeof(error);
|
1790 |
getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, (void *)&error, &errlen);
|
1791 |
if(error == 0) { |
1792 |
/* Maybe not a breaking error after all? */
|
1793 |
continue;
|
1794 |
} else if(error == 111) { |
1795 |
/* ICMP error? If it's related to RTCP, let's just close the RTCP socket and move on */
|
1796 |
if(fds[i].fd == session->media.audio_rtcp_fd) {
|
1797 |
JANUS_LOG(LOG_WARN, "[NoSIP-%p] Got a '%s' on the audio RTCP socket, closing it\n",
|
1798 |
session, strerror(error)); |
1799 |
close(session->media.audio_rtcp_fd); |
1800 |
session->media.audio_rtcp_fd = -1;
|
1801 |
} else if(fds[i].fd == session->media.video_rtcp_fd) { |
1802 |
JANUS_LOG(LOG_WARN, "[NoSIP-%p] Got a '%s' on the video RTCP socket, closing it\n",
|
1803 |
session, strerror(error)); |
1804 |
close(session->media.video_rtcp_fd); |
1805 |
session->media.video_rtcp_fd = -1;
|
1806 |
} |
1807 |
/* FIXME Should we do the same with the RTP sockets as well? We may risk overreacting, there... */
|
1808 |
continue;
|
1809 |
} |
1810 |
JANUS_LOG(LOG_ERR, "[NoSIP-%p] Error polling %d (socket #%d): %s...\n", session,
|
1811 |
fds[i].fd, i, fds[i].revents & POLLERR ? "POLLERR" : "POLLHUP"); |
1812 |
JANUS_LOG(LOG_ERR, "[NoSIP-%p] -- %d (%s)\n", session, error, strerror(error));
|
1813 |
/* Can we assume it's pretty much over, after a POLLERR? */
|
1814 |
goon = FALSE; |
1815 |
/* FIXME Close the PeerConnection */
|
1816 |
gateway->close_pc(session->handle); |
1817 |
break;
|
1818 |
} else if(fds[i].revents & POLLIN) { |
1819 |
if(pipe_fd != -1 && fds[i].fd == pipe_fd) { |
1820 |
/* Poll interrupted for a reason, go on */
|
1821 |
int code = 0; |
1822 |
bytes = read(pipe_fd, &code, sizeof(int)); |
1823 |
break;
|
1824 |
} |
1825 |
/* Got an RTP/RTCP packet */
|
1826 |
addrlen = sizeof(remote);
|
1827 |
bytes = recvfrom(fds[i].fd, buffer, 1500, 0, (struct sockaddr*)&remote, &addrlen); |
1828 |
/* Let's check what this is */
|
1829 |
gboolean video = fds[i].fd == session->media.video_rtp_fd || fds[i].fd == session->media.video_rtcp_fd; |
1830 |
gboolean rtcp = fds[i].fd == session->media.audio_rtcp_fd || fds[i].fd == session->media.video_rtcp_fd; |
1831 |
if(!rtcp) {
|
1832 |
/* Audio or Video RTP */
|
1833 |
rtp_header *header = (rtp_header *)buffer; |
1834 |
if((video && session->media.video_ssrc_peer != ntohl(header->ssrc)) ||
|
1835 |
(!video && session->media.audio_ssrc_peer != ntohl(header->ssrc))) { |
1836 |
if(video) {
|
1837 |
session->media.video_ssrc_peer = ntohl(header->ssrc); |
1838 |
} else {
|
1839 |
session->media.audio_ssrc_peer = ntohl(header->ssrc); |
1840 |
} |
1841 |
JANUS_LOG(LOG_VERB, "[NoSIP-%p] Got SIP peer %s SSRC: %"SCNu32"\n", |
1842 |
session, video ? "video" : "audio", session->media.audio_ssrc_peer); |
1843 |
} |
1844 |
/* Is this SRTP? */
|
1845 |
if(session->media.has_srtp_remote) {
|
1846 |
int buflen = bytes;
|
1847 |
srtp_err_status_t res = srtp_unprotect( |
1848 |
(video ? session->media.video_srtp_in : session->media.audio_srtp_in), |
1849 |
buffer, &buflen); |
1850 |
if(res != srtp_err_status_ok && res != srtp_err_status_replay_fail && res != srtp_err_status_replay_old) {
|
1851 |
guint32 timestamp = ntohl(header->timestamp); |
1852 |
guint16 seq = ntohs(header->seq_number); |
1853 |
JANUS_LOG(LOG_ERR, "[NoSIP-%p] %s SRTP unprotect error: %s (len=%d-->%d, ts=%"SCNu32", seq=%"SCNu16")\n", |
1854 |
session, video ? "Video" : "Audio", janus_srtp_error_str(res), bytes, buflen, timestamp, seq); |
1855 |
continue;
|
1856 |
} |
1857 |
bytes = buflen; |
1858 |
} |
1859 |
/* Check if the SSRC changed (e.g., after a re-INVITE or UPDATE) */
|
1860 |
guint32 timestamp = ntohl(header->timestamp); |
1861 |
janus_rtp_header_update(header, &session->media.context, video, |
1862 |
(video ? (vstep ? vstep : 4500) : (astep ? astep : 960))); |
1863 |
if(video) {
|
1864 |
if(vts == 0) { |
1865 |
vts = timestamp; |
1866 |
} else if(vstep == 0) { |
1867 |
vstep = timestamp-vts; |
1868 |
if(vstep < 0) { |
1869 |
vstep = 0;
|
1870 |
} |
1871 |
} |
1872 |
} else {
|
1873 |
if(ats == 0) { |
1874 |
ats = timestamp; |
1875 |
} else if(astep == 0) { |
1876 |
astep = timestamp-ats; |
1877 |
if(astep < 0) { |
1878 |
astep = 0;
|
1879 |
} |
1880 |
} |
1881 |
} |
1882 |
/* Save the frame if we're recording */
|
1883 |
janus_recorder_save_frame(video ? session->vrc_peer : session->arc_peer, buffer, bytes); |
1884 |
/* Relay to browser */
|
1885 |
gateway->relay_rtp(session->handle, video, buffer, bytes); |
1886 |
continue;
|
1887 |
} else {
|
1888 |
/* Audio or Video RTCP */
|
1889 |
if(session->media.has_srtp_remote) {
|
1890 |
int buflen = bytes;
|
1891 |
srtp_err_status_t res = srtp_unprotect_rtcp( |
1892 |
(video ? session->media.video_srtp_in : session->media.audio_srtp_in), |
1893 |
buffer, &buflen); |
1894 |
if(res != srtp_err_status_ok && res != srtp_err_status_replay_fail && res != srtp_err_status_replay_old) {
|
1895 |
JANUS_LOG(LOG_ERR, "[NoSIP-%p] %s SRTCP unprotect error: %s (len=%d-->%d)\n",
|
1896 |
session, video ? "Video" : "Audio", janus_srtp_error_str(res), bytes, buflen); |
1897 |
continue;
|
1898 |
} |
1899 |
bytes = buflen; |
1900 |
} |
1901 |
/* Relay to browser */
|
1902 |
gateway->relay_rtcp(session->handle, video, buffer, bytes); |
1903 |
continue;
|
1904 |
} |
1905 |
} |
1906 |
} |
1907 |
} |
1908 |
if(session->media.audio_rtp_fd != -1) { |
1909 |
close(session->media.audio_rtp_fd); |
1910 |
session->media.audio_rtp_fd = -1;
|
1911 |
} |
1912 |
if(session->media.audio_rtcp_fd != -1) { |
1913 |
close(session->media.audio_rtcp_fd); |
1914 |
session->media.audio_rtcp_fd = -1;
|
1915 |
} |
1916 |
session->media.local_audio_rtp_port = 0;
|
1917 |
session->media.local_audio_rtcp_port = 0;
|
1918 |
session->media.audio_ssrc = 0;
|
1919 |
if(session->media.video_rtp_fd != -1) { |
1920 |
close(session->media.video_rtp_fd); |
1921 |
session->media.video_rtp_fd = -1;
|
1922 |
} |
1923 |
if(session->media.video_rtcp_fd != -1) { |
1924 |
close(session->media.video_rtcp_fd); |
1925 |
session->media.video_rtcp_fd = -1;
|
1926 |
} |
1927 |
session->media.local_video_rtp_port = 0;
|
1928 |
session->media.local_video_rtcp_port = 0;
|
1929 |
session->media.video_ssrc = 0;
|
1930 |
if(session->media.pipefd[0] > 0) { |
1931 |
close(session->media.pipefd[0]);
|
1932 |
session->media.pipefd[0] = -1; |
1933 |
} |
1934 |
if(session->media.pipefd[1] > 0) { |
1935 |
close(session->media.pipefd[1]);
|
1936 |
session->media.pipefd[1] = -1; |
1937 |
} |
1938 |
/* Clean up SRTP stuff, if needed */
|
1939 |
janus_nosip_srtp_cleanup(session); |
1940 |
/* Done */
|
1941 |
JANUS_LOG(LOG_INFO, "Leaving NoSIP relay thread\n");
|
1942 |
g_thread_unref(g_thread_self()); |
1943 |
return NULL; |
1944 |
} |