Statistics
| Branch: | Revision:

janus-gateway / plugins / janus_videocall.c @ 8241c758

History | View | Annotate | Download (50.4 KB)

1 be35facb meetecho
/*! \file   janus_videocall.c
2
 * \author Lorenzo Miniero <lorenzo@meetecho.com>
3 9d11ac50 meetecho
 * \copyright GNU General Public License v3
4 be35facb meetecho
 * \brief  Janus VideoCall plugin
5
 * \details  This is a simple video call plugin for Janus, allowing two
6
 * WebRTC peers to call each other through the gateway. The idea is to
7
 * provide a similar service as the well known AppRTC demo (https://apprtc.appspot.com),
8
 * but with the media flowing through the gateway rather than being peer-to-peer.
9
 * 
10
 * The plugin provides a simple fake registration mechanism. A peer attaching
11
 * to the plugin needs to specify a username, which acts as a "phone number":
12
 * if the username is free, it is associated with the peer, which means
13
 * he/she can be "called" using that username by another peer. Peers can
14
 * either "call" another peer, by specifying their username, or wait for a call.
15
 * The approach used by this plugin is similar to the one employed by the
16
 * echo test one: all frames (RTP/RTCP) coming from one peer are relayed
17
 * to the other.
18
 * 
19 667b2005 meetecho
 * Just as in the janus_videocall.c plugin, there are knobs to control
20 be35facb meetecho
 * whether audio and/or video should be muted or not, and if the bitrate
21
 * of the peer needs to be capped by means of REMB messages.
22
 * 
23 02fac6b4 meetecho
 * \section vcallapi Video Call API
24
 * 
25
 * All requests you can send in the Video Call API are asynchronous,
26
 * which means all responses (successes and errors) will be delivered
27
 * as events with the same transaction. 
28
 * 
29
 * The supported requests are \c list , \c register , \c call ,
30
 * \c accept , \c set and \c hangup . \c list allows you to get a list
31
 * of all the registered peers; \c register can be used to register
32
 * a username to call and be called; \c call is used to start a video
33
 * call with somebody through the plugin, while \c accept is used to
34
 * accept the call in case one is invited instead of inviting; \c set
35
 * can be used to configure some call-related settings (e.g., a cap on
36
 * the send bandwidth); finally, \c hangup can be used to terminate the
37
 * communication at any time, either to hangup an ongoing call or to
38
 * cancel/decline a call that hasn't started yet.
39
 * 
40
 * The \c list request has to be formatted as follows:
41
 * 
42
\verbatim
43
{
44
        "request" : "list"
45
}
46
\endverbatim
47
 *
48
 * A successful request will result in an array of peers to be returned:
49
 * 
50
\verbatim
51
{
52
        "videocall" : "event",
53
        "result" : {
54
                "list": [        // Array of peers
55
                        "alice78",
56
                        "bob51",
57
                        // others
58
                ]
59
        }
60
}
61
\endverbatim
62
 * 
63
 * An error instead (and the same applies to all other requests, so this
64
 * won't be repeated) would provide both an error code and a more verbose
65
 * description of the cause of the issue:
66
 * 
67
\verbatim
68
{
69
        "videocall" : "event",
70
        "error_code" : <numeric ID, check Macros below>,
71
        "error" : "<error description as a string>"
72
}
73
\endverbatim
74
 * 
75
 * To register a username to call and be called, the \c register request
76
 * can be used. This works on a "first come, first served" basis: there's
77
 * no authetication involved, you just specify the username you'd like
78
 * to use and, if free, it's assigned to you. The \c request has to be
79
 * formatted as follows:
80
 * 
81
\verbatim
82
{
83
        "request" : "register",
84
        "username" : "<desired unique username>"
85
}
86
\endverbatim
87
 * 
88
 * If successul, this will result in a \c registered event:
89
 * 
90
\verbatim
91
{
92
        "videocall" : "event",
93
        "result" : {
94
                "event" : "registered",
95
                "username" : "<same username, registered>"
96
        }
97
}
98
\endverbatim
99
 * 
100
 * Once you're registered, you can either start a new call or wait to
101
 * be called by someone else who knows your username. To start a new
102
 * call, the \c call request can be used: this request must be attached
103
 * to a JSEP offer containing the WebRTC-related info to setup a new
104
 * media session. A \c call request has to be formatted as follows:
105
 * 
106
\verbatim
107
{
108
        "request" : "call",
109
        "username" : "<username to call>"
110
}
111
\endverbatim
112
 * 
113
 * If successul, this will result in a \c calling event:
114
 * 
115
\verbatim
116
{
117
        "videocall" : "event",
118
        "result" : {
119
                "event" : "calling",
120
                "username" : "<same username, registered>"
121
        }
122
}
123
\endverbatim
124
 *
125
 * At the same time, the user being called will receive an
126
 * \c incomingcall event
127
 *  
128
\verbatim
129
{
130
        "videocall" : "event",
131
        "result" : {
132
                "event" : "incomingcall",
133
                "username" : "<your username>"
134
        }
135
}
136
\endverbatim
137
 * 
138
 * To accept the call, the \c accept request can be used. This request
139
 * must be attached to a JSEP answer containing the WebRTC-related
140
 * information to complete the actual PeerConnection setup. A \c accept
141
 * request has to be formatted as follows:
142
 * 
143
\verbatim
144
{
145
        "request" : "accept"
146
}
147
\endverbatim
148
 * 
149
 * If successul, both the caller and the callee will receive an
150
 * \c accepted event to notify them about the success of the signalling:
151
 * 
152
\verbatim
153
{
154
        "videocall" : "event",
155
        "result" : {
156
                "event" : "accepted",
157
                "username" : "<caller username>"
158
        }
159
}
160
\endverbatim
161
 *
162
 * At this point, the media-related settings of the call can be modified
163
 * on either side by means of a \c set request, which acts pretty much
164
 * as the one in the \ref echoapi . The \c set request has to be
165 6089eb4e Lorenzo Miniero
 * formatted as follows. All the attributes (except \c request) are
166
 * optional, so any request can contain a subset of them:
167 02fac6b4 meetecho
 *
168
\verbatim
169
{
170
        "request" : "set",
171
        "audio" : true|false,
172
        "video" : true|false,
173 6089eb4e Lorenzo Miniero
        "bitrate" : <numeric bitrate value>,
174
        "record" : true|false,
175
        "filename" : <base path/filename to use for the recording>
176 02fac6b4 meetecho
}
177
\endverbatim
178
 *
179
 * \c audio instructs the plugin to do or do not relay audio frames;
180
 * \c video does the same for video; \c bitrate caps the bandwidth to
181 6089eb4e Lorenzo Miniero
 * force on the browser encoding side (e.g., 128000 for 128kbps);
182
 * \c record enables or disables the recording of this peer; in case
183
 * recording is enabled, \c filename allows to specify a base
184
 * path/filename to use for the files (-audio.mjr and -video.mjr are
185
 * automatically appended). Beware that enabling the recording only
186
 * records this user's contribution, and not the whole call: to record
187
 * both sides, you need to enable recording for both the peers in the
188
 * call.
189
 * 
190 02fac6b4 meetecho
 * A successful request will result in a \c set event:
191
 * 
192
\verbatim
193
{
194
        "videocall" : "event",
195
        "result" : {
196
                "event" : "set"
197
        }
198
}
199
\endverbatim
200
 * 
201
 * To decline an incoming call, cancel an attempt to call or simply
202
 * hangup an ongoing conversation, the \c hangup request can be used,
203
 * which has to be formatted as follows:
204
 * 
205
\verbatim
206
{
207
        "request" : "hangup"
208
}
209
\endverbatim
210
 *
211
 * Whatever the reason of a call being closed (e.g., a \c hangup request,
212
 * a PeerConnection being closed, or something else), both parties in
213
 * the communication will receive a \c hangup event:
214
 * 
215
\verbatim
216
{
217
        "videocall" : "event",
218
        "result" : {
219
                "event" : "hangup",
220
                "username" : "<username of who closed the communication>",
221
                "reason" : "<description of what happened>"
222
        }
223
}
224
\endverbatim
225
 * 
226 be35facb meetecho
 * \ingroup plugins
227
 * \ref plugins
228
 */
229
230
#include "plugin.h"
231
232
#include <jansson.h>
233
234 6bb3f34d Nicholas Wylie
#include "../debug.h"
235 3a26e009 meetecho
#include "../apierror.h"
236 be35facb meetecho
#include "../config.h"
237 3a26e009 meetecho
#include "../mutex.h"
238 6089eb4e Lorenzo Miniero
#include "../record.h"
239 be35facb meetecho
#include "../rtcp.h"
240 5fa9a305 meetecho
#include "../utils.h"
241 be35facb meetecho
242
243
/* Plugin information */
244 1281ca86 meetecho
#define JANUS_VIDEOCALL_VERSION                        5
245
#define JANUS_VIDEOCALL_VERSION_STRING        "0.0.5"
246 be35facb meetecho
#define JANUS_VIDEOCALL_DESCRIPTION                "This is a simple video call plugin for Janus, allowing two WebRTC peers to call each other through the gateway."
247
#define JANUS_VIDEOCALL_NAME                        "JANUS VideoCall plugin"
248 a3d13e20 meetecho
#define JANUS_VIDEOCALL_AUTHOR                        "Meetecho s.r.l."
249 be35facb meetecho
#define JANUS_VIDEOCALL_PACKAGE                        "janus.plugin.videocall"
250
251
/* Plugin methods */
252
janus_plugin *create(void);
253
int janus_videocall_init(janus_callbacks *callback, const char *config_path);
254
void janus_videocall_destroy(void);
255 667b2005 meetecho
int janus_videocall_get_api_compatibility(void);
256 be35facb meetecho
int janus_videocall_get_version(void);
257
const char *janus_videocall_get_version_string(void);
258
const char *janus_videocall_get_description(void);
259
const char *janus_videocall_get_name(void);
260 a3d13e20 meetecho
const char *janus_videocall_get_author(void);
261 be35facb meetecho
const char *janus_videocall_get_package(void);
262 cfd5c0d6 meetecho
void janus_videocall_create_session(janus_plugin_session *handle, int *error);
263 dd11fa0a Lorenzo Miniero
struct janus_plugin_result *janus_videocall_handle_message(janus_plugin_session *handle, char *transaction, json_t *message, json_t *jsep);
264 cfd5c0d6 meetecho
void janus_videocall_setup_media(janus_plugin_session *handle);
265
void janus_videocall_incoming_rtp(janus_plugin_session *handle, int video, char *buf, int len);
266
void janus_videocall_incoming_rtcp(janus_plugin_session *handle, int video, char *buf, int len);
267 a3d13e20 meetecho
void janus_videocall_incoming_data(janus_plugin_session *handle, char *buf, int len);
268 24c01741 meetecho
void janus_videocall_slow_link(janus_plugin_session *handle, int uplink, int video);
269 cfd5c0d6 meetecho
void janus_videocall_hangup_media(janus_plugin_session *handle);
270
void janus_videocall_destroy_session(janus_plugin_session *handle, int *error);
271 dd11fa0a Lorenzo Miniero
json_t *janus_videocall_query_session(janus_plugin_session *handle);
272 be35facb meetecho
273
/* Plugin setup */
274
static janus_plugin janus_videocall_plugin =
275 1281ca86 meetecho
        JANUS_PLUGIN_INIT (
276 be35facb meetecho
                .init = janus_videocall_init,
277
                .destroy = janus_videocall_destroy,
278
279 667b2005 meetecho
                .get_api_compatibility = janus_videocall_get_api_compatibility,
280 be35facb meetecho
                .get_version = janus_videocall_get_version,
281
                .get_version_string = janus_videocall_get_version_string,
282
                .get_description = janus_videocall_get_description,
283
                .get_name = janus_videocall_get_name,
284 a3d13e20 meetecho
                .get_author = janus_videocall_get_author,
285 be35facb meetecho
                .get_package = janus_videocall_get_package,
286
                
287
                .create_session = janus_videocall_create_session,
288
                .handle_message = janus_videocall_handle_message,
289
                .setup_media = janus_videocall_setup_media,
290
                .incoming_rtp = janus_videocall_incoming_rtp,
291
                .incoming_rtcp = janus_videocall_incoming_rtcp,
292 a3d13e20 meetecho
                .incoming_data = janus_videocall_incoming_data,
293 24c01741 meetecho
                .slow_link = janus_videocall_slow_link,
294 be35facb meetecho
                .hangup_media = janus_videocall_hangup_media,
295
                .destroy_session = janus_videocall_destroy_session,
296 667b2005 meetecho
                .query_session = janus_videocall_query_session,
297 1281ca86 meetecho
        );
298 be35facb meetecho
299
/* Plugin creator */
300
janus_plugin *create(void) {
301 3a26e009 meetecho
        JANUS_LOG(LOG_VERB, "%s created!\n", JANUS_VIDEOCALL_NAME);
302 be35facb meetecho
        return &janus_videocall_plugin;
303
}
304
305 94926fcd Andreas Girgensohn
/* Parameter validation */
306
static struct janus_json_parameter request_parameters[] = {
307
        {"request", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
308
};
309
static struct janus_json_parameter username_parameters[] = {
310
        {"username", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
311
};
312
static struct janus_json_parameter set_parameters[] = {
313
        {"audio", JANUS_JSON_BOOL, 0},
314
        {"video", JANUS_JSON_BOOL, 0},
315
        {"bitrate", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
316
        {"record", JANUS_JSON_BOOL, 0},
317
        {"filename", JSON_STRING, 0}
318
};
319 be35facb meetecho
320
/* Useful stuff */
321 4239218c meetecho
static volatile gint initialized = 0, stopping = 0;
322 71a04f89 Lorenzo Miniero
static gboolean notify_events = TRUE;
323 be35facb meetecho
static janus_callbacks *gateway = NULL;
324
static GThread *handler_thread;
325 dbf63b61 meetecho
static GThread *watchdog;
326 be35facb meetecho
static void *janus_videocall_handler(void *data);
327
328
typedef struct janus_videocall_message {
329 cfd5c0d6 meetecho
        janus_plugin_session *handle;
330 be35facb meetecho
        char *transaction;
331 dd11fa0a Lorenzo Miniero
        json_t *message;
332
        json_t *jsep;
333 be35facb meetecho
} janus_videocall_message;
334 4d13c20d meetecho
static GAsyncQueue *messages = NULL;
335 b9e8b01f Lorenzo Miniero
static janus_videocall_message exit_message;
336 be35facb meetecho
337 90e0cdb4 Lorenzo Miniero
static void janus_videocall_message_free(janus_videocall_message *msg) {
338
        if(!msg || msg == &exit_message)
339 3a26e009 meetecho
                return;
340 4d13c20d meetecho
341 3a26e009 meetecho
        msg->handle = NULL;
342 4d13c20d meetecho
343
        g_free(msg->transaction);
344 3a26e009 meetecho
        msg->transaction = NULL;
345 dd11fa0a Lorenzo Miniero
        if(msg->message)
346
                json_decref(msg->message);
347 3a26e009 meetecho
        msg->message = NULL;
348 dd11fa0a Lorenzo Miniero
        if(msg->jsep)
349
                json_decref(msg->jsep);
350
        msg->jsep = NULL;
351 4d13c20d meetecho
352 3a26e009 meetecho
        g_free(msg);
353
}
354
355 be35facb meetecho
typedef struct janus_videocall_session {
356 cfd5c0d6 meetecho
        janus_plugin_session *handle;
357 be35facb meetecho
        gchar *username;
358 6089eb4e Lorenzo Miniero
        gboolean has_audio;
359
        gboolean has_video;
360 be35facb meetecho
        gboolean audio_active;
361
        gboolean video_active;
362
        uint64_t bitrate;
363 24c01741 meetecho
        guint16 slowlink_count;
364 be35facb meetecho
        struct janus_videocall_session *peer;
365 6089eb4e Lorenzo Miniero
        janus_recorder *arc;        /* The Janus recorder instance for this user's audio, if enabled */
366
        janus_recorder *vrc;        /* The Janus recorder instance for this user's video, if enabled */
367 d223cef1 Lorenzo Miniero
        janus_mutex rec_mutex;        /* Mutex to protect the recorders from race conditions */
368 4239218c meetecho
        volatile gint hangingup;
369 78955474 meetecho
        gint64 destroyed;        /* Time at which this session was marked as destroyed */
370 be35facb meetecho
} janus_videocall_session;
371 506f60ae Jack Leigh
static GHashTable *sessions;
372 5fa9a305 meetecho
static GList *old_sessions;
373 506f60ae Jack Leigh
static janus_mutex sessions_mutex;
374 be35facb meetecho
375
376 3cc3cab3 meetecho
/* Error codes */
377
#define JANUS_VIDEOCALL_ERROR_UNKNOWN_ERROR                        499
378
#define JANUS_VIDEOCALL_ERROR_NO_MESSAGE                        470
379
#define JANUS_VIDEOCALL_ERROR_INVALID_JSON                        471
380
#define JANUS_VIDEOCALL_ERROR_INVALID_REQUEST                472
381
#define JANUS_VIDEOCALL_ERROR_REGISTER_FIRST                473
382
#define JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT                474
383
#define JANUS_VIDEOCALL_ERROR_MISSING_ELEMENT                475
384
#define JANUS_VIDEOCALL_ERROR_USERNAME_TAKEN                476
385
#define JANUS_VIDEOCALL_ERROR_ALREADY_REGISTERED        477
386
#define JANUS_VIDEOCALL_ERROR_NO_SUCH_USERNAME                478
387
#define JANUS_VIDEOCALL_ERROR_USE_ECHO_TEST                        479
388
#define JANUS_VIDEOCALL_ERROR_ALREADY_IN_CALL                480
389
#define JANUS_VIDEOCALL_ERROR_NO_CALL                                481
390
#define JANUS_VIDEOCALL_ERROR_MISSING_SDP                        482
391
392
393 5fa9a305 meetecho
/* VideoCall watchdog/garbage collector (sort of) */
394
void *janus_videocall_watchdog(void *data);
395
void *janus_videocall_watchdog(void *data) {
396
        JANUS_LOG(LOG_INFO, "VideoCall watchdog started\n");
397
        gint64 now = 0;
398 dbf63b61 meetecho
        while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) {
399 5fa9a305 meetecho
                janus_mutex_lock(&sessions_mutex);
400
                /* Iterate on all the sessions */
401
                now = janus_get_monotonic_time();
402
                if(old_sessions != NULL) {
403
                        GList *sl = old_sessions;
404 1281ca86 meetecho
                        JANUS_LOG(LOG_HUGE, "Checking %d old VideoCall sessions...\n", g_list_length(old_sessions));
405 5fa9a305 meetecho
                        while(sl) {
406
                                janus_videocall_session *session = (janus_videocall_session *)sl->data;
407 dbf63b61 meetecho
                                if(!session) {
408 5fa9a305 meetecho
                                        sl = sl->next;
409
                                        continue;
410
                                }
411
                                if(now-session->destroyed >= 5*G_USEC_PER_SEC) {
412
                                        /* We're lazy and actually get rid of the stuff only after a few seconds */
413 1281ca86 meetecho
                                        JANUS_LOG(LOG_VERB, "Freeing old VideoCall session\n");
414 5fa9a305 meetecho
                                        GList *rm = sl->next;
415
                                        old_sessions = g_list_delete_link(old_sessions, sl);
416
                                        sl = rm;
417
                                        session->handle = NULL;
418
                                        g_free(session);
419
                                        session = NULL;
420
                                        continue;
421
                                }
422
                                sl = sl->next;
423
                        }
424
                }
425
                janus_mutex_unlock(&sessions_mutex);
426 1e2f5d89 meetecho
                g_usleep(500000);
427 5fa9a305 meetecho
        }
428
        JANUS_LOG(LOG_INFO, "VideoCall watchdog stopped\n");
429
        return NULL;
430
}
431
432
433 be35facb meetecho
/* Plugin implementation */
434
int janus_videocall_init(janus_callbacks *callback, const char *config_path) {
435 dbf63b61 meetecho
        if(g_atomic_int_get(&stopping)) {
436 be35facb meetecho
                /* Still stopping from before */
437
                return -1;
438
        }
439
        if(callback == NULL || config_path == NULL) {
440
                /* Invalid arguments */
441
                return -1;
442
        }
443
444
        /* Read configuration */
445
        char filename[255];
446 c94052c2 meetecho
        g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_VIDEOCALL_PACKAGE);
447 3a26e009 meetecho
        JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
448 be35facb meetecho
        janus_config *config = janus_config_parse(filename);
449 71a04f89 Lorenzo Miniero
        if(config != NULL) {
450 be35facb meetecho
                janus_config_print(config);
451 71a04f89 Lorenzo Miniero
                janus_config_item *events = janus_config_get_item_drilldown(config, "general", "events");
452
                if(events != NULL && events->value != NULL)
453
                        notify_events = janus_is_true(events->value);
454
                if(!notify_events && callback->events_is_enabled()) {
455
                        JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_VIDEOCALL_NAME);
456
                }
457
        }
458 be35facb meetecho
        janus_config_destroy(config);
459
        config = NULL;
460
        
461
        sessions = g_hash_table_new(g_str_hash, g_str_equal);
462 3a26e009 meetecho
        janus_mutex_init(&sessions_mutex);
463 4d13c20d meetecho
        messages = g_async_queue_new_full((GDestroyNotify) janus_videocall_message_free);
464 be35facb meetecho
        /* This is the callback we'll need to invoke to contact the gateway */
465
        gateway = callback;
466
467 dbf63b61 meetecho
        g_atomic_int_set(&initialized, 1);
468
469
        GError *error = NULL;
470 5fa9a305 meetecho
        /* Start the sessions watchdog */
471 c7498fa9 Lorenzo Miniero
        watchdog = g_thread_try_new("videocall watchdog", &janus_videocall_watchdog, NULL, &error);
472 dbf63b61 meetecho
        if(error != NULL) {
473
                g_atomic_int_set(&initialized, 0);
474
                JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the VideoCall watchdog thread...\n", error->code, error->message ? error->message : "??");
475 5fa9a305 meetecho
                return -1;
476
        }
477 be35facb meetecho
        /* Launch the thread that will handle incoming messages */
478 c7498fa9 Lorenzo Miniero
        handler_thread = g_thread_try_new("videocall handler", janus_videocall_handler, NULL, &error);
479 be35facb meetecho
        if(error != NULL) {
480 dbf63b61 meetecho
                g_atomic_int_set(&initialized, 0);
481
                JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the VideoCall handler thread...\n", error->code, error->message ? error->message : "??");
482 be35facb meetecho
                return -1;
483
        }
484 3a26e009 meetecho
        JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_VIDEOCALL_NAME);
485 be35facb meetecho
        return 0;
486
}
487
488 77992057 Philip Withnall
void janus_videocall_destroy(void) {
489 dbf63b61 meetecho
        if(!g_atomic_int_get(&initialized))
490 be35facb meetecho
                return;
491 dbf63b61 meetecho
        g_atomic_int_set(&stopping, 1);
492 2e28ee7b Lorenzo Miniero
493 b9e8b01f Lorenzo Miniero
        g_async_queue_push(messages, &exit_message);
494 be35facb meetecho
        if(handler_thread != NULL) {
495
                g_thread_join(handler_thread);
496 dbf63b61 meetecho
                handler_thread = NULL;
497
        }
498
        if(watchdog != NULL) {
499
                g_thread_join(watchdog);
500
                watchdog = NULL;
501 be35facb meetecho
        }
502 3a26e009 meetecho
        /* FIXME We should destroy the sessions cleanly */
503 dbf63b61 meetecho
        janus_mutex_lock(&sessions_mutex);
504 be35facb meetecho
        g_hash_table_destroy(sessions);
505 dbf63b61 meetecho
        janus_mutex_unlock(&sessions_mutex);
506 4d13c20d meetecho
        g_async_queue_unref(messages);
507
        messages = NULL;
508 be35facb meetecho
        sessions = NULL;
509 dbf63b61 meetecho
        g_atomic_int_set(&initialized, 0);
510
        g_atomic_int_set(&stopping, 0);
511 3a26e009 meetecho
        JANUS_LOG(LOG_INFO, "%s destroyed!\n", JANUS_VIDEOCALL_NAME);
512 be35facb meetecho
}
513
514 667b2005 meetecho
int janus_videocall_get_api_compatibility(void) {
515
        /* Important! This is what your plugin MUST always return: don't lie here or bad things will happen */
516
        return JANUS_PLUGIN_API_VERSION;
517
}
518
519 77992057 Philip Withnall
int janus_videocall_get_version(void) {
520 be35facb meetecho
        return JANUS_VIDEOCALL_VERSION;
521
}
522
523 77992057 Philip Withnall
const char *janus_videocall_get_version_string(void) {
524 be35facb meetecho
        return JANUS_VIDEOCALL_VERSION_STRING;
525
}
526
527 77992057 Philip Withnall
const char *janus_videocall_get_description(void) {
528 be35facb meetecho
        return JANUS_VIDEOCALL_DESCRIPTION;
529
}
530
531 77992057 Philip Withnall
const char *janus_videocall_get_name(void) {
532 be35facb meetecho
        return JANUS_VIDEOCALL_NAME;
533
}
534
535 77992057 Philip Withnall
const char *janus_videocall_get_author(void) {
536 a3d13e20 meetecho
        return JANUS_VIDEOCALL_AUTHOR;
537
}
538
539 77992057 Philip Withnall
const char *janus_videocall_get_package(void) {
540 be35facb meetecho
        return JANUS_VIDEOCALL_PACKAGE;
541
}
542
543 cfd5c0d6 meetecho
void janus_videocall_create_session(janus_plugin_session *handle, int *error) {
544 8878e296 meetecho
        if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
545 be35facb meetecho
                *error = -1;
546
                return;
547
        }        
548 1f067658 Lorenzo Miniero
        janus_videocall_session *session = (janus_videocall_session *)g_malloc0(sizeof(janus_videocall_session));
549 be35facb meetecho
        session->handle = handle;
550 6089eb4e Lorenzo Miniero
        session->has_audio = FALSE;
551
        session->has_video = FALSE;
552 be35facb meetecho
        session->audio_active = TRUE;
553
        session->video_active = TRUE;
554
        session->bitrate = 0;        /* No limit */
555
        session->peer = NULL;
556
        session->username = NULL;
557 d223cef1 Lorenzo Miniero
        janus_mutex_init(&session->rec_mutex);
558 5fa9a305 meetecho
        session->destroyed = 0;
559 f65c61ff Lorenzo Miniero
        g_atomic_int_set(&session->hangingup, 0);
560 be35facb meetecho
        handle->plugin_handle = session;
561
562
        return;
563
}
564
565 cfd5c0d6 meetecho
void janus_videocall_destroy_session(janus_plugin_session *handle, int *error) {
566 8878e296 meetecho
        if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
567 be35facb meetecho
                *error = -1;
568
                return;
569
        }
570
        janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle; 
571
        if(!session) {
572 ce97b4a5 meetecho
                JANUS_LOG(LOG_ERR, "No VideoCall session associated with this handle...\n");
573 be35facb meetecho
                *error = -2;
574
                return;
575
        }
576 5fa9a305 meetecho
        janus_mutex_lock(&sessions_mutex);
577 793d18b1 meetecho
        if(!session->destroyed) {
578
                JANUS_LOG(LOG_VERB, "Removing VideoCall user %s session...\n", session->username ? session->username : "'unknown'");
579
                janus_videocall_hangup_media(handle);
580 78955474 meetecho
                session->destroyed = janus_get_monotonic_time();
581 793d18b1 meetecho
                if(session->username != NULL) {
582
                        int res = g_hash_table_remove(sessions, (gpointer)session->username);
583
                        JANUS_LOG(LOG_VERB, "  -- Removed: %d\n", res);
584
                }
585
                /* Cleaning up and removing the session is done in a lazy way */
586
                old_sessions = g_list_append(old_sessions, session);
587
        }
588 5fa9a305 meetecho
        janus_mutex_unlock(&sessions_mutex);
589 be35facb meetecho
        return;
590
}
591
592 dd11fa0a Lorenzo Miniero
json_t *janus_videocall_query_session(janus_plugin_session *handle) {
593 667b2005 meetecho
        if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
594
                return NULL;
595
        }        
596
        janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;
597
        if(!session) {
598
                JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
599
                return NULL;
600
        }
601
        /* Provide some generic info, e.g., if we're in a call and with whom */
602
        json_t *info = json_object();
603
        json_object_set_new(info, "state", json_string(session->peer ? "incall" : "idle"));
604
        json_object_set_new(info, "username", session->username ? json_string(session->username) : NULL);
605
        if(session->peer) {
606
                json_object_set_new(info, "peer", session->peer->username ? json_string(session->peer->username) : NULL);
607 51db8a86 Lorenzo Miniero
                json_object_set_new(info, "audio_active", session->audio_active ? json_true() : json_false());
608
                json_object_set_new(info, "video_active", session->video_active ? json_true() : json_false());
609 667b2005 meetecho
                json_object_set_new(info, "bitrate", json_integer(session->bitrate));
610 24c01741 meetecho
                json_object_set_new(info, "slowlink_count", json_integer(session->slowlink_count));
611 667b2005 meetecho
        }
612 6089eb4e Lorenzo Miniero
        if(session->arc || session->vrc) {
613
                json_t *recording = json_object();
614
                if(session->arc && session->arc->filename)
615
                        json_object_set_new(recording, "audio", json_string(session->arc->filename));
616
                if(session->vrc && session->vrc->filename)
617
                        json_object_set_new(recording, "video", json_string(session->vrc->filename));
618
                json_object_set_new(info, "recording", recording);
619
        }
620 667b2005 meetecho
        json_object_set_new(info, "destroyed", json_integer(session->destroyed));
621 dd11fa0a Lorenzo Miniero
        return info;
622 667b2005 meetecho
}
623
624 dd11fa0a Lorenzo Miniero
struct janus_plugin_result *janus_videocall_handle_message(janus_plugin_session *handle, char *transaction, json_t *message, json_t *jsep) {
625 8878e296 meetecho
        if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
626 dd11fa0a Lorenzo Miniero
                return janus_plugin_result_new(JANUS_PLUGIN_ERROR, g_atomic_int_get(&stopping) ? "Shutting down" : "Plugin not initialized", NULL);
627 1f067658 Lorenzo Miniero
        janus_videocall_message *msg = g_malloc0(sizeof(janus_videocall_message));
628 be35facb meetecho
        msg->handle = handle;
629 3a26e009 meetecho
        msg->transaction = transaction;
630 be35facb meetecho
        msg->message = message;
631 dd11fa0a Lorenzo Miniero
        msg->jsep = jsep;
632 4d13c20d meetecho
        g_async_queue_push(messages, msg);
633 292d035f meetecho
634
        /* All the requests to this plugin are handled asynchronously */
635 dd11fa0a Lorenzo Miniero
        return janus_plugin_result_new(JANUS_PLUGIN_OK_WAIT, NULL, NULL);
636 be35facb meetecho
}
637
638 cfd5c0d6 meetecho
void janus_videocall_setup_media(janus_plugin_session *handle) {
639 3a26e009 meetecho
        JANUS_LOG(LOG_INFO, "WebRTC media is now available\n");
640 8878e296 meetecho
        if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
641 be35facb meetecho
                return;
642
        janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;        
643
        if(!session) {
644 3a26e009 meetecho
                JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
645 be35facb meetecho
                return;
646
        }
647 5fa9a305 meetecho
        if(session->destroyed)
648 be35facb meetecho
                return;
649 3a3cc054 meetecho
        g_atomic_int_set(&session->hangingup, 0);
650 be35facb meetecho
        /* We really don't care, as we only relay RTP/RTCP we get in the first place anyway */
651
}
652
653 cfd5c0d6 meetecho
void janus_videocall_incoming_rtp(janus_plugin_session *handle, int video, char *buf, int len) {
654 8878e296 meetecho
        if(handle == NULL || handle->stopped || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
655 be35facb meetecho
                return;
656
        if(gateway) {
657
                /* Honour the audio/video active flags */
658
                janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;        
659
                if(!session) {
660 3a26e009 meetecho
                        JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
661 be35facb meetecho
                        return;
662
                }
663
                if(!session->peer) {
664 3a26e009 meetecho
                        JANUS_LOG(LOG_ERR, "Session has no peer...\n");
665 be35facb meetecho
                        return;
666
                }
667 5fa9a305 meetecho
                if(session->destroyed || session->peer->destroyed)
668 be35facb meetecho
                        return;
669
                if((!video && session->audio_active) || (video && session->video_active)) {
670 6089eb4e Lorenzo Miniero
                        /* Save the frame if we're recording */
671 d223cef1 Lorenzo Miniero
                        janus_recorder_save_frame(video ? session->vrc : session->arc, buf, len);
672 6089eb4e Lorenzo Miniero
                        /* Forward the packet to the peer */
673 be35facb meetecho
                        gateway->relay_rtp(session->peer->handle, video, buf, len);
674
                }
675
        }
676
}
677
678 cfd5c0d6 meetecho
void janus_videocall_incoming_rtcp(janus_plugin_session *handle, int video, char *buf, int len) {
679 8878e296 meetecho
        if(handle == NULL || handle->stopped || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
680 be35facb meetecho
                return;
681
        if(gateway) {
682
                janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;        
683
                if(!session) {
684 3a26e009 meetecho
                        JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
685 be35facb meetecho
                        return;
686
                }
687
                if(!session->peer) {
688 3a26e009 meetecho
                        JANUS_LOG(LOG_ERR, "Session has no peer...\n");
689 be35facb meetecho
                        return;
690
                }
691 5fa9a305 meetecho
                if(session->destroyed || session->peer->destroyed)
692 be35facb meetecho
                        return;
693
                if(session->bitrate > 0)
694
                        janus_rtcp_cap_remb(buf, len, session->bitrate);
695
                gateway->relay_rtcp(session->peer->handle, video, buf, len);
696
        }
697
}
698
699 a3d13e20 meetecho
void janus_videocall_incoming_data(janus_plugin_session *handle, char *buf, int len) {
700 8878e296 meetecho
        if(handle == NULL || handle->stopped || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
701 a3d13e20 meetecho
                return;
702
        if(gateway) {
703
                janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;        
704
                if(!session) {
705
                        JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
706
                        return;
707
                }
708
                if(!session->peer) {
709
                        JANUS_LOG(LOG_ERR, "Session has no peer...\n");
710
                        return;
711
                }
712 5fa9a305 meetecho
                if(session->destroyed || session->peer->destroyed)
713 a3d13e20 meetecho
                        return;
714
                if(buf == NULL || len <= 0)
715
                        return;
716 18943780 Lorenzo Miniero
                char *text = g_malloc0(len+1);
717 a3d13e20 meetecho
                memcpy(text, buf, len);
718 18943780 Lorenzo Miniero
                *(text+len) = '\0';
719 a3d13e20 meetecho
                JANUS_LOG(LOG_VERB, "Got a DataChannel message (%zu bytes) to forward: %s\n", strlen(text), text);
720
                gateway->relay_data(session->peer->handle, text, strlen(text));
721 18943780 Lorenzo Miniero
                g_free(text);
722 a3d13e20 meetecho
        }
723
}
724
725 24c01741 meetecho
void janus_videocall_slow_link(janus_plugin_session *handle, int uplink, int video) {
726
        /* The core is informing us that our peer got or sent too many NACKs, are we pushing media too hard? */
727
        if(handle == NULL || handle->stopped || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
728
                return;
729
        janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;        
730
        if(!session) {
731
                JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
732
                return;
733
        }
734
        if(session->destroyed)
735
                return;
736
        session->slowlink_count++;
737
        if(uplink && !video && !session->audio_active) {
738
                /* We're not relaying audio and the peer is expecting it, so NACKs are normal */
739
                JANUS_LOG(LOG_VERB, "Getting a lot of NACKs (slow uplink) for audio, but that's expected, a configure disabled the audio forwarding\n");
740
        } else if(uplink && video && !session->video_active) {
741
                /* We're not relaying video and the peer is expecting it, so NACKs are normal */
742
                JANUS_LOG(LOG_VERB, "Getting a lot of NACKs (slow uplink) for video, but that's expected, a configure disabled the video forwarding\n");
743
        } else {
744
                /* Slow uplink or downlink, maybe we set the bitrate cap too high? */
745
                if(video) {
746
                        /* Halve the bitrate, but don't go too low... */
747 83e899f3 meetecho
                        if(!uplink) {
748
                                /* Downlink issue, user has trouble sending, halve this user's bitrate cap */
749 24c01741 meetecho
                                session->bitrate = session->bitrate > 0 ? session->bitrate : 512*1024;
750
                                session->bitrate = session->bitrate/2;
751
                                if(session->bitrate < 64*1024)
752
                                        session->bitrate = 64*1024;
753
                        } else {
754 83e899f3 meetecho
                                /* Uplink issue, user has trouble receiving, halve this user's peer's bitrate cap */
755 24c01741 meetecho
                                if(session->peer == NULL || session->peer->handle == NULL)
756
                                        return;        /* Nothing to do */
757
                                session->peer->bitrate = session->peer->bitrate > 0 ? session->peer->bitrate : 512*1024;
758
                                session->peer->bitrate = session->peer->bitrate/2;
759
                                if(session->peer->bitrate < 64*1024)
760
                                        session->peer->bitrate = 64*1024;
761
                        }
762
                        JANUS_LOG(LOG_WARN, "Getting a lot of NACKs (slow %s) for %s, forcing a lower REMB: %"SCNu64"\n",
763 83e899f3 meetecho
                                uplink ? "uplink" : "downlink", video ? "video" : "audio", uplink ? session->peer->bitrate : session->bitrate);
764 24c01741 meetecho
                        /* ... and send a new REMB back */
765 d49a834e Lorenzo Miniero
                        char rtcpbuf[24];
766
                        janus_rtcp_remb((char *)(&rtcpbuf), 24, uplink ? session->peer->bitrate : session->bitrate);
767
                        gateway->relay_rtcp(uplink ? session->peer->handle : handle, 1, rtcpbuf, 24);
768 83e230ed meetecho
                        /* As a last thing, notify the affected user about this */
769
                        json_t *event = json_object();
770
                        json_object_set_new(event, "videocall", json_string("event"));
771
                        json_t *result = json_object();
772
                        json_object_set_new(result, "status", json_string("slow_link"));
773
                        json_object_set_new(result, "bitrate", json_integer(uplink ? session->peer->bitrate : session->bitrate));
774
                        json_object_set_new(event, "result", result);
775 dd11fa0a Lorenzo Miniero
                        gateway->push_event(uplink ? session->peer->handle : handle, &janus_videocall_plugin, NULL, event, NULL);
776 83e230ed meetecho
                        json_decref(event);
777 24c01741 meetecho
                }
778
        }
779
}
780
781 cfd5c0d6 meetecho
void janus_videocall_hangup_media(janus_plugin_session *handle) {
782 3a26e009 meetecho
        JANUS_LOG(LOG_INFO, "No WebRTC media anymore\n");
783 8878e296 meetecho
        if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
784 be35facb meetecho
                return;
785
        janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;        
786
        if(!session) {
787 3a26e009 meetecho
                JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
788 be35facb meetecho
                return;
789
        }
790 3a3cc054 meetecho
        if(session->destroyed)
791
                return;
792
        if(g_atomic_int_add(&session->hangingup, 1))
793 be35facb meetecho
                return;
794 6089eb4e Lorenzo Miniero
        /* Get rid of the recorders, if available */
795 d223cef1 Lorenzo Miniero
        janus_mutex_lock(&session->rec_mutex);
796 6089eb4e Lorenzo Miniero
        if(session->arc) {
797
                janus_recorder_close(session->arc);
798
                JANUS_LOG(LOG_INFO, "Closed audio recording %s\n", session->arc->filename ? session->arc->filename : "??");
799
                janus_recorder_free(session->arc);
800
        }
801
        session->arc = NULL;
802
        if(session->vrc) {
803
                janus_recorder_close(session->vrc);
804
                JANUS_LOG(LOG_INFO, "Closed video recording %s\n", session->vrc->filename ? session->vrc->filename : "??");
805
                janus_recorder_free(session->vrc);
806
        }
807
        session->vrc = NULL;
808 d223cef1 Lorenzo Miniero
        janus_mutex_unlock(&session->rec_mutex);
809 be35facb meetecho
        if(session->peer) {
810
                /* Send event to our peer too */
811
                json_t *call = json_object();
812 3a26e009 meetecho
                json_object_set_new(call, "videocall", json_string("event"));
813 be35facb meetecho
                json_t *calling = json_object();
814
                json_object_set_new(calling, "event", json_string("hangup"));
815
                json_object_set_new(calling, "username", json_string(session->username));
816 4d8c7356 Lorenzo Miniero
                json_object_set_new(calling, "reason", json_string("Remote WebRTC hangup"));
817 be35facb meetecho
                json_object_set_new(call, "result", calling);
818 ed769cb0 Lorenzo Miniero
                gateway->close_pc(session->peer->handle);
819 dd11fa0a Lorenzo Miniero
                int ret = gateway->push_event(session->peer->handle, &janus_videocall_plugin, NULL, call, NULL);
820
                JANUS_LOG(LOG_VERB, "  >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
821 be35facb meetecho
                json_decref(call);
822 4d8c7356 Lorenzo Miniero
                /* Also notify event handlers */
823 71a04f89 Lorenzo Miniero
                if(notify_events && gateway->events_is_enabled()) {
824 4d8c7356 Lorenzo Miniero
                        json_t *info = json_object();
825
                        json_object_set_new(info, "event", json_string("hangup"));
826
                        json_object_set_new(info, "reason", json_string("Remote WebRTC hangup"));
827 c734a91a Lorenzo Miniero
                        gateway->notify_event(&janus_videocall_plugin, session->peer->handle, info);
828 4d8c7356 Lorenzo Miniero
                }
829 be35facb meetecho
        }
830
        session->peer = NULL;
831
        /* Reset controls */
832 6089eb4e Lorenzo Miniero
        session->has_audio = FALSE;
833
        session->has_video = FALSE;
834 be35facb meetecho
        session->audio_active = TRUE;
835
        session->video_active = TRUE;
836
        session->bitrate = 0;
837
}
838
839
/* Thread to handle incoming messages */
840
static void *janus_videocall_handler(void *data) {
841 febef1ea meetecho
        JANUS_LOG(LOG_VERB, "Joining VideoCall handler thread\n");
842 be35facb meetecho
        janus_videocall_message *msg = NULL;
843 3cc3cab3 meetecho
        int error_code = 0;
844 94926fcd Andreas Girgensohn
        char error_cause[512];
845 ead45815 meetecho
        json_t *root = NULL;
846 dbf63b61 meetecho
        while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) {
847 2e28ee7b Lorenzo Miniero
                msg = g_async_queue_pop(messages);
848
                if(msg == NULL)
849
                        continue;
850 b9e8b01f Lorenzo Miniero
                if(msg == &exit_message)
851
                        break;
852 2e28ee7b Lorenzo Miniero
                if(msg->handle == NULL) {
853
                        janus_videocall_message_free(msg);
854 be35facb meetecho
                        continue;
855
                }
856 b62d4076 Lorenzo Miniero
                janus_videocall_session *session = (janus_videocall_session *)msg->handle->plugin_handle;
857 be35facb meetecho
                if(!session) {
858 3a26e009 meetecho
                        JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
859
                        janus_videocall_message_free(msg);
860 be35facb meetecho
                        continue;
861
                }
862 5fa9a305 meetecho
                if(session->destroyed) {
863 3a26e009 meetecho
                        janus_videocall_message_free(msg);
864 be35facb meetecho
                        continue;
865 3a26e009 meetecho
                }
866 be35facb meetecho
                /* Handle request */
867 3cc3cab3 meetecho
                error_code = 0;
868 dd11fa0a Lorenzo Miniero
                root = msg->message;
869 be35facb meetecho
                if(msg->message == NULL) {
870 3a26e009 meetecho
                        JANUS_LOG(LOG_ERR, "No message??\n");
871 3cc3cab3 meetecho
                        error_code = JANUS_VIDEOCALL_ERROR_NO_MESSAGE;
872 c94052c2 meetecho
                        g_snprintf(error_cause, 512, "%s", "No message??");
873 be35facb meetecho
                        goto error;
874
                }
875
                if(!json_is_object(root)) {
876 3a26e009 meetecho
                        JANUS_LOG(LOG_ERR, "JSON error: not an object\n");
877 3cc3cab3 meetecho
                        error_code = JANUS_VIDEOCALL_ERROR_INVALID_JSON;
878 c94052c2 meetecho
                        g_snprintf(error_cause, 512, "JSON error: not an object");
879 be35facb meetecho
                        goto error;
880
                }
881 94926fcd Andreas Girgensohn
                JANUS_VALIDATE_JSON_OBJECT(root, request_parameters,
882
                        error_code, error_cause, TRUE,
883
                        JANUS_VIDEOCALL_ERROR_MISSING_ELEMENT, JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT);
884
                if(error_code != 0)
885 be35facb meetecho
                        goto error;
886 dd11fa0a Lorenzo Miniero
                const char *msg_sdp_type = json_string_value(json_object_get(msg->jsep, "type"));
887
                const char *msg_sdp = json_string_value(json_object_get(msg->jsep, "sdp"));
888 94926fcd Andreas Girgensohn
                json_t *request = json_object_get(root, "request");
889 be35facb meetecho
                const char *request_text = json_string_value(request);
890
                json_t *result = NULL;
891
                char *sdp_type = NULL, *sdp = NULL;
892
                if(!strcasecmp(request_text, "list")) {
893
                        result = json_object();
894
                        json_t *list = json_array();
895 3a26e009 meetecho
                        JANUS_LOG(LOG_VERB, "Request for the list of peers\n");
896 be35facb meetecho
                        /* Return a list of all available mountpoints */
897 3a26e009 meetecho
                        janus_mutex_lock(&sessions_mutex);
898 4d13c20d meetecho
                        GHashTableIter iter;
899
                        gpointer value;
900
                        g_hash_table_iter_init(&iter, sessions);
901
                        while (g_hash_table_iter_next(&iter, NULL, &value)) {
902
                                janus_videocall_session *user = value;
903 be35facb meetecho
                                if(user != NULL && user->username != NULL)
904
                                        json_array_append_new(list, json_string(user->username));
905
                        }
906
                        json_object_set_new(result, "list", list);
907 3a26e009 meetecho
                        janus_mutex_unlock(&sessions_mutex);
908 be35facb meetecho
                } else if(!strcasecmp(request_text, "register")) {
909
                        /* Map this handle to a username */
910
                        if(session->username != NULL) {
911 3a26e009 meetecho
                                JANUS_LOG(LOG_ERR, "Already registered (%s)\n", session->username);
912 3cc3cab3 meetecho
                                error_code = JANUS_VIDEOCALL_ERROR_ALREADY_REGISTERED;
913 c94052c2 meetecho
                                g_snprintf(error_cause, 512, "Already registered (%s)", session->username);
914 be35facb meetecho
                                goto error;
915
                        }
916 94926fcd Andreas Girgensohn
                        JANUS_VALIDATE_JSON_OBJECT(root, username_parameters,
917
                                error_code, error_cause, TRUE,
918
                                JANUS_VIDEOCALL_ERROR_MISSING_ELEMENT, JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT);
919
                        if(error_code != 0)
920 aff9f5c0 meetecho
                                goto error;
921 94926fcd Andreas Girgensohn
                        json_t *username = json_object_get(root, "username");
922 be35facb meetecho
                        const char *username_text = json_string_value(username);
923 3a26e009 meetecho
                        janus_mutex_lock(&sessions_mutex);
924 be35facb meetecho
                        if(g_hash_table_lookup(sessions, username_text) != NULL) {
925 3a26e009 meetecho
                                janus_mutex_unlock(&sessions_mutex);
926
                                JANUS_LOG(LOG_ERR, "Username '%s' already taken\n", username_text);
927 3cc3cab3 meetecho
                                error_code = JANUS_VIDEOCALL_ERROR_USERNAME_TAKEN;
928 c94052c2 meetecho
                                g_snprintf(error_cause, 512, "Username '%s' already taken", username_text);
929 be35facb meetecho
                                goto error;
930
                        }
931 3a26e009 meetecho
                        janus_mutex_unlock(&sessions_mutex);
932 be35facb meetecho
                        session->username = g_strdup(username_text);
933 3a26e009 meetecho
                        janus_mutex_lock(&sessions_mutex);
934 be35facb meetecho
                        g_hash_table_insert(sessions, (gpointer)session->username, session);
935 3a26e009 meetecho
                        janus_mutex_unlock(&sessions_mutex);
936 be35facb meetecho
                        result = json_object();
937
                        json_object_set_new(result, "event", json_string("registered"));
938
                        json_object_set_new(result, "username", json_string(username_text));
939 4d8c7356 Lorenzo Miniero
                        /* Also notify event handlers */
940 71a04f89 Lorenzo Miniero
                        if(notify_events && gateway->events_is_enabled()) {
941 4d8c7356 Lorenzo Miniero
                                json_t *info = json_object();
942
                                json_object_set_new(info, "event", json_string("registered"));
943
                                json_object_set_new(info, "username", json_string(username_text));
944 1894f5c5 Lorenzo Miniero
                                gateway->notify_event(&janus_videocall_plugin, session->handle, info);
945 4d8c7356 Lorenzo Miniero
                        }
946 be35facb meetecho
                } else if(!strcasecmp(request_text, "call")) {
947
                        /* Call another peer */
948 a3d13e20 meetecho
                        if(session->username == NULL) {
949
                                JANUS_LOG(LOG_ERR, "Register a username first\n");
950 3cc3cab3 meetecho
                                error_code = JANUS_VIDEOCALL_ERROR_REGISTER_FIRST;
951 c94052c2 meetecho
                                g_snprintf(error_cause, 512, "Register a username first");
952 a3d13e20 meetecho
                                goto error;
953
                        }
954 be35facb meetecho
                        if(session->peer != NULL) {
955 3a26e009 meetecho
                                JANUS_LOG(LOG_ERR, "Already in a call\n");
956 3cc3cab3 meetecho
                                error_code = JANUS_VIDEOCALL_ERROR_ALREADY_IN_CALL;
957 c94052c2 meetecho
                                g_snprintf(error_cause, 512, "Already in a call");
958 be35facb meetecho
                                goto error;
959
                        }
960 94926fcd Andreas Girgensohn
                        JANUS_VALIDATE_JSON_OBJECT(root, username_parameters,
961
                                error_code, error_cause, TRUE,
962
                                JANUS_VIDEOCALL_ERROR_MISSING_ELEMENT, JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT);
963
                        if(error_code != 0)
964 aff9f5c0 meetecho
                                goto error;
965 94926fcd Andreas Girgensohn
                        json_t *username = json_object_get(root, "username");
966 be35facb meetecho
                        const char *username_text = json_string_value(username);
967 a3d13e20 meetecho
                        if(!strcmp(username_text, session->username)) {
968
                                JANUS_LOG(LOG_ERR, "You can't call yourself... use the EchoTest for that\n");
969 3cc3cab3 meetecho
                                error_code = JANUS_VIDEOCALL_ERROR_USE_ECHO_TEST;
970 c94052c2 meetecho
                                g_snprintf(error_cause, 512, "You can't call yourself... use the EchoTest for that");
971 a3d13e20 meetecho
                                goto error;
972
                        }
973 3a26e009 meetecho
                        janus_mutex_lock(&sessions_mutex);
974 be35facb meetecho
                        janus_videocall_session *peer = g_hash_table_lookup(sessions, username_text);
975 5fa9a305 meetecho
                        if(peer == NULL || peer->destroyed) {
976 3a26e009 meetecho
                                janus_mutex_unlock(&sessions_mutex);
977
                                JANUS_LOG(LOG_ERR, "Username '%s' doesn't exist\n", username_text);
978 3cc3cab3 meetecho
                                error_code = JANUS_VIDEOCALL_ERROR_NO_SUCH_USERNAME;
979 c94052c2 meetecho
                                g_snprintf(error_cause, 512, "Username '%s' doesn't exist", username_text);
980 be35facb meetecho
                                goto error;
981
                        }
982
                        if(peer->peer != NULL) {
983 3a26e009 meetecho
                                janus_mutex_unlock(&sessions_mutex);
984
                                JANUS_LOG(LOG_VERB, "%s is busy\n", username_text);
985 be35facb meetecho
                                result = json_object();
986
                                json_object_set_new(result, "event", json_string("hangup"));
987
                                json_object_set_new(result, "username", json_string(session->username));
988
                                json_object_set_new(result, "reason", json_string("User busy"));
989 4d8c7356 Lorenzo Miniero
                                /* Also notify event handlers */
990 71a04f89 Lorenzo Miniero
                                if(notify_events && gateway->events_is_enabled()) {
991 4d8c7356 Lorenzo Miniero
                                        json_t *info = json_object();
992
                                        json_object_set_new(info, "event", json_string("hangup"));
993
                                        json_object_set_new(info, "reason", json_string("User busy"));
994 c734a91a Lorenzo Miniero
                                        gateway->notify_event(&janus_videocall_plugin, session->handle, info);
995 4d8c7356 Lorenzo Miniero
                                }
996 ed769cb0 Lorenzo Miniero
                                gateway->close_pc(session->handle);
997 be35facb meetecho
                        } else {
998 3a26e009 meetecho
                                janus_mutex_unlock(&sessions_mutex);
999 be35facb meetecho
                                /* Any SDP to handle? if not, something's wrong */
1000 dd11fa0a Lorenzo Miniero
                                if(!msg_sdp) {
1001 3a26e009 meetecho
                                        JANUS_LOG(LOG_ERR, "Missing SDP\n");
1002 3cc3cab3 meetecho
                                        error_code = JANUS_VIDEOCALL_ERROR_MISSING_SDP;
1003 c94052c2 meetecho
                                        g_snprintf(error_cause, 512, "Missing SDP");
1004 be35facb meetecho
                                        goto error;
1005
                                }
1006 3a26e009 meetecho
                                janus_mutex_lock(&sessions_mutex);
1007 be35facb meetecho
                                session->peer = peer;
1008
                                peer->peer = session;
1009 dd11fa0a Lorenzo Miniero
                                session->has_audio = (strstr(msg_sdp, "m=audio") != NULL);
1010
                                session->has_video = (strstr(msg_sdp, "m=video") != NULL);
1011 3a26e009 meetecho
                                janus_mutex_unlock(&sessions_mutex);
1012
                                JANUS_LOG(LOG_VERB, "%s is calling %s\n", session->username, session->peer->username);
1013 dd11fa0a Lorenzo Miniero
                                JANUS_LOG(LOG_VERB, "This is involving a negotiation (%s) as well:\n%s\n", msg_sdp_type, msg_sdp);
1014 be35facb meetecho
                                /* Send SDP to our peer */
1015
                                json_t *call = json_object();
1016 3a26e009 meetecho
                                json_object_set_new(call, "videocall", json_string("event"));
1017 be35facb meetecho
                                json_t *calling = json_object();
1018
                                json_object_set_new(calling, "event", json_string("incomingcall"));
1019
                                json_object_set_new(calling, "username", json_string(session->username));
1020
                                json_object_set_new(call, "result", calling);
1021 a7bba2fb Lorenzo Miniero
                                /* Make also sure we get rid of ULPfec, red, etc. */
1022 dd11fa0a Lorenzo Miniero
                                char *sdp = g_strdup(msg_sdp);
1023 6089eb4e Lorenzo Miniero
                                if(strstr(sdp, "ulpfec")) {
1024 a7bba2fb Lorenzo Miniero
                                        /* FIXME This really needs some better code */
1025 6089eb4e Lorenzo Miniero
                                        sdp = janus_string_replace(sdp, "a=rtpmap:116 red/90000\r\n", "");
1026
                                        sdp = janus_string_replace(sdp, "a=rtpmap:117 ulpfec/90000\r\n", "");
1027
                                        sdp = janus_string_replace(sdp, "a=rtpmap:96 rtx/90000\r\n", "");
1028
                                        sdp = janus_string_replace(sdp, "a=fmtp:96 apt=100\r\n", "");
1029 a7bba2fb Lorenzo Miniero
                                        sdp = janus_string_replace(sdp, "a=rtpmap:97 rtx/90000\r\n", "");
1030
                                        sdp = janus_string_replace(sdp, "a=fmtp:97 apt=101\r\n", "");
1031
                                        sdp = janus_string_replace(sdp, "a=rtpmap:98 rtx/90000\r\n", "");
1032
                                        sdp = janus_string_replace(sdp, "a=fmtp:98 apt=116\r\n", "");
1033
                                        sdp = janus_string_replace(sdp, " 116", "");
1034
                                        sdp = janus_string_replace(sdp, " 117", "");
1035
                                        sdp = janus_string_replace(sdp, " 96", "");
1036
                                        sdp = janus_string_replace(sdp, " 97", "");
1037
                                        sdp = janus_string_replace(sdp, " 98", "");
1038 6089eb4e Lorenzo Miniero
                                }
1039 bcd6fab7 Lorenzo Miniero
                                json_t *jsep = json_pack("{ssss}", "type", msg_sdp_type, "sdp", sdp);
1040 c1bd6f7a Lorenzo Miniero
                                g_atomic_int_set(&session->hangingup, 0);
1041 dd11fa0a Lorenzo Miniero
                                int ret = gateway->push_event(peer->handle, &janus_videocall_plugin, NULL, call, jsep);
1042
                                JANUS_LOG(LOG_VERB, "  >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
1043 6089eb4e Lorenzo Miniero
                                g_free(sdp);
1044 dd11fa0a Lorenzo Miniero
                                json_decref(call);
1045
                                json_decref(jsep);
1046 be35facb meetecho
                                /* Send an ack back */
1047
                                result = json_object();
1048
                                json_object_set_new(result, "event", json_string("calling"));
1049 4d8c7356 Lorenzo Miniero
                                /* Also notify event handlers */
1050 71a04f89 Lorenzo Miniero
                                if(notify_events && gateway->events_is_enabled()) {
1051 4d8c7356 Lorenzo Miniero
                                        json_t *info = json_object();
1052
                                        json_object_set_new(info, "event", json_string("calling"));
1053 c734a91a Lorenzo Miniero
                                        gateway->notify_event(&janus_videocall_plugin, session->handle, info);
1054 4d8c7356 Lorenzo Miniero
                                }
1055 be35facb meetecho
                        }
1056
                } else if(!strcasecmp(request_text, "accept")) {
1057
                        /* Accept a call from another peer */
1058
                        if(session->peer == NULL) {
1059 3a26e009 meetecho
                                JANUS_LOG(LOG_ERR, "No incoming call to accept\n");
1060 3cc3cab3 meetecho
                                error_code = JANUS_VIDEOCALL_ERROR_NO_CALL;
1061 c94052c2 meetecho
                                g_snprintf(error_cause, 512, "No incoming call to accept");
1062 be35facb meetecho
                                goto error;
1063
                        }
1064
                        /* Any SDP to handle? if not, something's wrong */
1065 dd11fa0a Lorenzo Miniero
                        if(!msg_sdp) {
1066 3a26e009 meetecho
                                JANUS_LOG(LOG_ERR, "Missing SDP\n");
1067 3cc3cab3 meetecho
                                error_code = JANUS_VIDEOCALL_ERROR_MISSING_SDP;
1068 c94052c2 meetecho
                                g_snprintf(error_cause, 512, "Missing SDP");
1069 be35facb meetecho
                                goto error;
1070
                        }
1071 3a26e009 meetecho
                        JANUS_LOG(LOG_VERB, "%s is accepting a call from %s\n", session->username, session->peer->username);
1072 dd11fa0a Lorenzo Miniero
                        JANUS_LOG(LOG_VERB, "This is involving a negotiation (%s) as well:\n%s\n", msg_sdp_type, msg_sdp);
1073
                        session->has_audio = (strstr(msg_sdp, "m=audio") != NULL);
1074
                        session->has_video = (strstr(msg_sdp, "m=video") != NULL);
1075 be35facb meetecho
                        /* Send SDP to our peer */
1076 dd11fa0a Lorenzo Miniero
                        json_t *jsep = json_pack("{ssss}", "type", msg_sdp_type, "sdp", msg_sdp);
1077 be35facb meetecho
                        json_t *call = json_object();
1078 3a26e009 meetecho
                        json_object_set_new(call, "videocall", json_string("event"));
1079 be35facb meetecho
                        json_t *calling = json_object();
1080
                        json_object_set_new(calling, "event", json_string("accepted"));
1081
                        json_object_set_new(calling, "username", json_string(session->username));
1082
                        json_object_set_new(call, "result", calling);
1083 c1bd6f7a Lorenzo Miniero
                        g_atomic_int_set(&session->hangingup, 0);
1084 dd11fa0a Lorenzo Miniero
                        int ret = gateway->push_event(session->peer->handle, &janus_videocall_plugin, NULL, call, jsep);
1085
                        JANUS_LOG(LOG_VERB, "  >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
1086
                        json_decref(call);
1087
                        json_decref(jsep);
1088 be35facb meetecho
                        /* Send an ack back */
1089
                        result = json_object();
1090
                        json_object_set_new(result, "event", json_string("accepted"));
1091 4d8c7356 Lorenzo Miniero
                        /* Also notify event handlers */
1092 71a04f89 Lorenzo Miniero
                        if(notify_events && gateway->events_is_enabled()) {
1093 4d8c7356 Lorenzo Miniero
                                json_t *info = json_object();
1094
                                json_object_set_new(info, "event", json_string("accepted"));
1095 c734a91a Lorenzo Miniero
                                gateway->notify_event(&janus_videocall_plugin, session->handle, info);
1096 4d8c7356 Lorenzo Miniero
                        }
1097 be35facb meetecho
                } else if(!strcasecmp(request_text, "set")) {
1098 6089eb4e Lorenzo Miniero
                        /* Update the local configuration (audio/video mute/unmute, bitrate cap or recording) */
1099 94926fcd Andreas Girgensohn
                        JANUS_VALIDATE_JSON_OBJECT(root, set_parameters,
1100
                                error_code, error_cause, TRUE,
1101
                                JANUS_VIDEOCALL_ERROR_MISSING_ELEMENT, JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT);
1102
                        if(error_code != 0)
1103 be35facb meetecho
                                goto error;
1104 94926fcd Andreas Girgensohn
                        json_t *audio = json_object_get(root, "audio");
1105 be35facb meetecho
                        json_t *video = json_object_get(root, "video");
1106
                        json_t *bitrate = json_object_get(root, "bitrate");
1107 6089eb4e Lorenzo Miniero
                        json_t *record = json_object_get(root, "record");
1108
                        json_t *recfile = json_object_get(root, "filename");
1109 be35facb meetecho
                        if(audio) {
1110
                                session->audio_active = json_is_true(audio);
1111 3a26e009 meetecho
                                JANUS_LOG(LOG_VERB, "Setting audio property: %s\n", session->audio_active ? "true" : "false");
1112 be35facb meetecho
                        }
1113
                        if(video) {
1114 1281ca86 meetecho
                                if(!session->video_active && json_is_true(video)) {
1115
                                        /* Send a PLI */
1116
                                        JANUS_LOG(LOG_VERB, "Just (re-)enabled video, sending a PLI to recover it\n");
1117
                                        char buf[12];
1118
                                        memset(buf, 0, 12);
1119
                                        janus_rtcp_pli((char *)&buf, 12);
1120
                                        gateway->relay_rtcp(session->handle, 1, buf, 12);
1121
                                }
1122 be35facb meetecho
                                session->video_active = json_is_true(video);
1123 3a26e009 meetecho
                                JANUS_LOG(LOG_VERB, "Setting video property: %s\n", session->video_active ? "true" : "false");
1124 be35facb meetecho
                        }
1125
                        if(bitrate) {
1126
                                session->bitrate = json_integer_value(bitrate);
1127 3a26e009 meetecho
                                JANUS_LOG(LOG_VERB, "Setting video bitrate: %"SCNu64"\n", session->bitrate);
1128 be35facb meetecho
                                if(session->bitrate > 0) {
1129
                                        /* FIXME Generate a new REMB (especially useful for Firefox, which doesn't send any we can cap later) */
1130
                                        char buf[24];
1131
                                        memset(buf, 0, 24);
1132
                                        janus_rtcp_remb((char *)&buf, 24, session->bitrate);
1133 3a26e009 meetecho
                                        JANUS_LOG(LOG_VERB, "Sending REMB\n");
1134 be35facb meetecho
                                        gateway->relay_rtcp(session->handle, 1, buf, 24);
1135
                                        /* FIXME How should we handle a subsequent "no limit" bitrate? */
1136
                                }
1137
                        }
1138 6089eb4e Lorenzo Miniero
                        if(record) {
1139 dd11fa0a Lorenzo Miniero
                                if(msg_sdp) {
1140
                                        session->has_audio = (strstr(msg_sdp, "m=audio") != NULL);
1141
                                        session->has_video = (strstr(msg_sdp, "m=video") != NULL);
1142 6089eb4e Lorenzo Miniero
                                }
1143
                                gboolean recording = json_is_true(record);
1144
                                const char *recording_base = json_string_value(recfile);
1145
                                JANUS_LOG(LOG_VERB, "Recording %s (base filename: %s)\n", recording ? "enabled" : "disabled", recording_base ? recording_base : "not provided");
1146 d223cef1 Lorenzo Miniero
                                janus_mutex_lock(&session->rec_mutex);
1147 6089eb4e Lorenzo Miniero
                                if(!recording) {
1148
                                        /* Not recording (anymore?) */
1149
                                        if(session->arc) {
1150
                                                janus_recorder_close(session->arc);
1151
                                                JANUS_LOG(LOG_INFO, "Closed audio recording %s\n", session->arc->filename ? session->arc->filename : "??");
1152
                                                janus_recorder_free(session->arc);
1153
                                        }
1154
                                        session->arc = NULL;
1155
                                        if(session->vrc) {
1156
                                                janus_recorder_close(session->vrc);
1157
                                                JANUS_LOG(LOG_INFO, "Closed video recording %s\n", session->vrc->filename ? session->vrc->filename : "??");
1158
                                                janus_recorder_free(session->vrc);
1159
                                        }
1160
                                        session->vrc = NULL;
1161
                                } else {
1162
                                        /* We've started recording, send a PLI and go on */
1163
                                        char filename[255];
1164 a41aab17 Lorenzo Miniero
                                        gint64 now = janus_get_real_time();
1165 6089eb4e Lorenzo Miniero
                                        if(session->has_audio) {
1166 b29447b1 Lorenzo Miniero
                                                /* FIXME We assume we're recording Opus, here */
1167 6089eb4e Lorenzo Miniero
                                                memset(filename, 0, 255);
1168
                                                if(recording_base) {
1169
                                                        /* Use the filename and path we have been provided */
1170
                                                        g_snprintf(filename, 255, "%s-audio", recording_base);
1171 b29447b1 Lorenzo Miniero
                                                        session->arc = janus_recorder_create(NULL, "opus", filename);
1172 6089eb4e Lorenzo Miniero
                                                        if(session->arc == NULL) {
1173
                                                                /* FIXME We should notify the fact the recorder could not be created */
1174
                                                                JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this VideoCall user!\n");
1175
                                                        }
1176
                                                } else {
1177
                                                        /* Build a filename */
1178
                                                        g_snprintf(filename, 255, "videocall-%s-%s-%"SCNi64"-audio",
1179
                                                                session->username ? session->username : "unknown",
1180
                                                                (session->peer && session->peer->username) ? session->peer->username : "unknown",
1181
                                                                now);
1182 b29447b1 Lorenzo Miniero
                                                        session->arc = janus_recorder_create(NULL, "opus", filename);
1183 6089eb4e Lorenzo Miniero
                                                        if(session->arc == NULL) {
1184
                                                                /* FIXME We should notify the fact the recorder could not be created */
1185
                                                                JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this VideoCall user!\n");
1186
                                                        }
1187
                                                }
1188
                                        }
1189
                                        if(session->has_video) {
1190 b29447b1 Lorenzo Miniero
                                                /* FIXME We assume we're recording VP8, here */
1191 6089eb4e Lorenzo Miniero
                                                memset(filename, 0, 255);
1192
                                                if(recording_base) {
1193
                                                        /* Use the filename and path we have been provided */
1194
                                                        g_snprintf(filename, 255, "%s-video", recording_base);
1195 b29447b1 Lorenzo Miniero
                                                        session->vrc = janus_recorder_create(NULL, "vp8", filename);
1196 6089eb4e Lorenzo Miniero
                                                        if(session->vrc == NULL) {
1197
                                                                /* FIXME We should notify the fact the recorder could not be created */
1198
                                                                JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this VideoCall user!\n");
1199
                                                        }
1200
                                                } else {
1201
                                                        /* Build a filename */
1202
                                                        g_snprintf(filename, 255, "videocall-%s-%s-%"SCNi64"-video",
1203
                                                                session->username ? session->username : "unknown",
1204
                                                                (session->peer && session->peer->username) ? session->peer->username : "unknown",
1205
                                                                now);
1206 b29447b1 Lorenzo Miniero
                                                        session->vrc = janus_recorder_create(NULL, "vp8", filename);
1207 6089eb4e Lorenzo Miniero
                                                        if(session->vrc == NULL) {
1208
                                                                /* FIXME We should notify the fact the recorder could not be created */
1209
                                                                JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this VideoCall user!\n");
1210
                                                        }
1211
                                                }
1212
                                                /* Send a PLI */
1213
                                                JANUS_LOG(LOG_VERB, "Recording video, sending a PLI to kickstart it\n");
1214
                                                char buf[12];
1215
                                                memset(buf, 0, 12);
1216
                                                janus_rtcp_pli((char *)&buf, 12);
1217
                                                gateway->relay_rtcp(session->handle, 1, buf, 12);
1218
                                        }
1219
                                }
1220 d223cef1 Lorenzo Miniero
                                janus_mutex_unlock(&session->rec_mutex);
1221 6089eb4e Lorenzo Miniero
                        }
1222 4d8c7356 Lorenzo Miniero
                        /* Also notify event handlers */
1223 71a04f89 Lorenzo Miniero
                        if(notify_events && gateway->events_is_enabled()) {
1224 4d8c7356 Lorenzo Miniero
                                json_t *info = json_object();
1225
                                json_object_set_new(info, "event", json_string("configured"));
1226
                                json_object_set_new(info, "audio_active", session->audio_active ? json_true() : json_false());
1227
                                json_object_set_new(info, "video_active", session->video_active ? json_true() : json_false());
1228
                                json_object_set_new(info, "bitrate", json_integer(session->bitrate));
1229
                                if(session->arc || session->vrc) {
1230
                                        json_t *recording = json_object();
1231
                                        if(session->arc && session->arc->filename)
1232
                                                json_object_set_new(recording, "audio", json_string(session->arc->filename));
1233
                                        if(session->vrc && session->vrc->filename)
1234
                                                json_object_set_new(recording, "video", json_string(session->vrc->filename));
1235
                                        json_object_set_new(info, "recording", recording);
1236
                                }
1237 c734a91a Lorenzo Miniero
                                gateway->notify_event(&janus_videocall_plugin, session->handle, info);
1238 4d8c7356 Lorenzo Miniero
                        }
1239 be35facb meetecho
                        /* Send an ack back */
1240
                        result = json_object();
1241 3a26e009 meetecho
                        json_object_set_new(result, "event", json_string("set"));
1242 be35facb meetecho
                } else if(!strcasecmp(request_text, "hangup")) {
1243
                        /* Hangup an ongoing call or reject an incoming one */
1244 3a26e009 meetecho
                        janus_mutex_lock(&sessions_mutex);
1245 be35facb meetecho
                        janus_videocall_session *peer = session->peer;
1246 3a26e009 meetecho
                        if(peer == NULL) {
1247
                                JANUS_LOG(LOG_WARN, "No call to hangup\n");
1248
                        } else {
1249
                                JANUS_LOG(LOG_VERB, "%s is hanging up the call with %s\n", session->username, peer->username);
1250
                                session->peer = NULL;
1251
                                peer->peer = NULL;
1252
                        }
1253
                        janus_mutex_unlock(&sessions_mutex);
1254 be35facb meetecho
                        /* Notify the success as an hangup message */
1255
                        result = json_object();
1256
                        json_object_set_new(result, "event", json_string("hangup"));
1257
                        json_object_set_new(result, "username", json_string(session->username));
1258 4d8c7356 Lorenzo Miniero
                        json_object_set_new(result, "reason", json_string("Explicit hangup"));
1259
                        /* Also notify event handlers */
1260 71a04f89 Lorenzo Miniero
                        if(notify_events && gateway->events_is_enabled()) {
1261 4d8c7356 Lorenzo Miniero
                                json_t *info = json_object();
1262
                                json_object_set_new(info, "event", json_string("hangup"));
1263
                                json_object_set_new(info, "reason", json_string("Explicit hangup"));
1264 c734a91a Lorenzo Miniero
                                gateway->notify_event(&janus_videocall_plugin, session->handle, info);
1265 4d8c7356 Lorenzo Miniero
                        }
1266 ed769cb0 Lorenzo Miniero
                        gateway->close_pc(session->handle);
1267 3a26e009 meetecho
                        if(peer != NULL) {
1268
                                /* Send event to our peer too */
1269
                                json_t *call = json_object();
1270
                                json_object_set_new(call, "videocall", json_string("event"));
1271
                                json_t *calling = json_object();
1272
                                json_object_set_new(calling, "event", json_string("hangup"));
1273
                                json_object_set_new(calling, "username", json_string(session->username));
1274
                                json_object_set_new(calling, "reason", json_string("Remote hangup"));
1275
                                json_object_set_new(call, "result", calling);
1276 ed769cb0 Lorenzo Miniero
                                gateway->close_pc(peer->handle);
1277 dd11fa0a Lorenzo Miniero
                                int ret = gateway->push_event(peer->handle, &janus_videocall_plugin, NULL, call, NULL);
1278
                                JANUS_LOG(LOG_VERB, "  >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
1279 3a26e009 meetecho
                                json_decref(call);
1280 4d8c7356 Lorenzo Miniero
                                /* Also notify event handlers */
1281 71a04f89 Lorenzo Miniero
                                if(notify_events && gateway->events_is_enabled()) {
1282 4d8c7356 Lorenzo Miniero
                                        json_t *info = json_object();
1283
                                        json_object_set_new(info, "event", json_string("hangup"));
1284
                                        json_object_set_new(info, "reason", json_string("Remote hangup"));
1285 1894f5c5 Lorenzo Miniero
                                        gateway->notify_event(&janus_videocall_plugin, peer->handle, info);
1286 4d8c7356 Lorenzo Miniero
                                }
1287 3a26e009 meetecho
                        }
1288 be35facb meetecho
                } else {
1289 3a26e009 meetecho
                        JANUS_LOG(LOG_ERR, "Unknown request (%s)\n", request_text);
1290 3cc3cab3 meetecho
                        error_code = JANUS_VIDEOCALL_ERROR_INVALID_REQUEST;
1291 c94052c2 meetecho
                        g_snprintf(error_cause, 512, "Unknown request (%s)", request_text);
1292 be35facb meetecho
                        goto error;
1293
                }
1294
1295
                /* Prepare JSON event */
1296 dd11fa0a Lorenzo Miniero
                json_t *jsep = sdp ? json_pack("{ssss}", "type", sdp_type, "sdp", sdp) : NULL;
1297 be35facb meetecho
                json_t *event = json_object();
1298 3a26e009 meetecho
                json_object_set_new(event, "videocall", json_string("event"));
1299 be35facb meetecho
                if(result != NULL)
1300 5c9efb24 meetecho
                        json_object_set_new(event, "result", result);
1301 dd11fa0a Lorenzo Miniero
                int ret = gateway->push_event(msg->handle, &janus_videocall_plugin, msg->transaction, event, jsep);
1302
                JANUS_LOG(LOG_VERB, "  >> Pushing event: %d (%s)\n", ret, janus_get_api_error(ret));
1303
                g_free(sdp);
1304 be35facb meetecho
                json_decref(event);
1305 dd11fa0a Lorenzo Miniero
                if(jsep)
1306
                        json_decref(jsep);
1307 3a26e009 meetecho
                janus_videocall_message_free(msg);
1308 be35facb meetecho
                continue;
1309
                
1310
error:
1311
                {
1312
                        /* Prepare JSON error event */
1313
                        json_t *event = json_object();
1314 3a26e009 meetecho
                        json_object_set_new(event, "videocall", json_string("event"));
1315 3cc3cab3 meetecho
                        json_object_set_new(event, "error_code", json_integer(error_code));
1316 3a26e009 meetecho
                        json_object_set_new(event, "error", json_string(error_cause));
1317 dd11fa0a Lorenzo Miniero
                        int ret = gateway->push_event(msg->handle, &janus_videocall_plugin, msg->transaction, event, NULL);
1318
                        JANUS_LOG(LOG_VERB, "  >> Pushing event: %d (%s)\n", ret, janus_get_api_error(ret));
1319 be35facb meetecho
                        json_decref(event);
1320 3a26e009 meetecho
                        janus_videocall_message_free(msg);
1321 be35facb meetecho
                }
1322
        }
1323 febef1ea meetecho
        JANUS_LOG(LOG_VERB, "Leaving VideoCall handler thread\n");
1324 be35facb meetecho
        return NULL;
1325
}