Statistics
| Branch: | Revision:

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
}