1 /*! \file   janus_videocall.c
2  * \author Lorenzo Miniero <lorenzo@meetecho.com>
3  * \copyright GNU General Public License v3
4  * \brief  Janus VideoCall plugin
5  * \details Check the \ref videocall for more details.
6  *
7  * \ingroup plugins
8  * \ref plugins
9  *
10  * \page videocall VideoCall plugin documentation
11  * This is a simple video call plugin for Janus, allowing two
12  * WebRTC peers to call each other through the Janus core. The idea is to
13  * provide a similar service as the well known AppRTC demo (https://apprtc.appspot.com),
14  * but with the media flowing through a server rather than being peer-to-peer.
15  *
16  * The plugin provides a simple fake registration mechanism. A peer attaching
17  * to the plugin needs to specify a username, which acts as a "phone number":
18  * if the username is free, it is associated with the peer, which means
19  * he/she can be "called" using that username by another peer. Peers can
20  * either "call" another peer, by specifying their username, or wait for a call.
21  * The approach used by this plugin is similar to the one employed by the
22  * echo test one: all frames (RTP/RTCP) coming from one peer are relayed
23  * to the other.
24  *
25  * Just as in the janus_videocall.c plugin, there are knobs to control
26  * whether audio and/or video should be muted or not, and if the bitrate
27  * of the peer needs to be capped by means of REMB messages.
28  *
29  * \section vcallapi Video Call API
30  *
31  * All requests you can send in the Video Call API are asynchronous,
32  * which means all responses (successes and errors) will be delivered
33  * as events with the same transaction.
34  *
35  * The supported requests are \c list , \c register , \c call ,
36  * \c accept , \c set and \c hangup . \c list allows you to get a list
37  * of all the registered peers; \c register can be used to register
38  * a username to call and be called; \c call is used to start a video
39  * call with somebody through the plugin, while \c accept is used to
40  * accept the call in case one is invited instead of inviting; \c set
41  * can be used to configure some call-related settings (e.g., a cap on
42  * the send bandwidth); finally, \c hangup can be used to terminate the
43  * communication at any time, either to hangup an ongoing call or to
44  * cancel/decline a call that hasn't started yet.
45  *
46  * The \c list request has to be formatted as follows:
47  *
48 \verbatim
49 {
50 	"request" : "list"
51 }
52 \endverbatim
53  *
54  * A successful request will result in an array of peers to be returned:
55  *
56 \verbatim
57 {
58 	"videocall" : "event",
59 	"result" : {
60 		"list": [	// Array of peers
61 			"alice78",
62 			"bob51",
63 			// others
64 		]
65 	}
66 }
67 \endverbatim
68  *
69  * An error instead (and the same applies to all other requests, so this
70  * won't be repeated) would provide both an error code and a more verbose
71  * description of the cause of the issue:
72  *
73 \verbatim
74 {
75 	"videocall" : "event",
76 	"error_code" : <numeric ID, check Macros below>,
77 	"error" : "<error description as a string>"
78 }
79 \endverbatim
80  *
81  * To register a username to call and be called, the \c register request
82  * can be used. This works on a "first come, first served" basis: there's
83  * no authentication involved, you just specify the username you'd like
84  * to use and, if free, it's assigned to you. Notice that there's no
85  * way to unregister: you have to close the handle to free the username.
86  * The \c register request has to be formatted as follows:
87  *
88 \verbatim
89 {
90 	"request" : "register",
91 	"username" : "<desired unique username>"
92 }
93 \endverbatim
94  *
95  * If successul, this will result in a \c registered event:
96  *
97 \verbatim
98 {
99 	"videocall" : "event",
100 	"result" : {
101 		"event" : "registered",
102 		"username" : "<same username, registered>"
103 	}
104 }
105 \endverbatim
106  *
107  * Once you're registered, you can either start a new call or wait to
108  * be called by someone else who knows your username. To start a new
109  * call, the \c call request can be used: this request must be attached
110  * to a JSEP offer containing the WebRTC-related info to setup a new
111  * media session. A \c call request has to be formatted as follows:
112  *
113 \verbatim
114 {
115 	"request" : "call",
116 	"username" : "<username to call>"
117 }
118 \endverbatim
119  *
120  * If successul, this will result in a \c calling event:
121  *
122 \verbatim
123 {
124 	"videocall" : "event",
125 	"result" : {
126 		"event" : "calling",
127 		"username" : "<same username, registered>"
128 	}
129 }
130 \endverbatim
131  *
132  * At the same time, the user being called will receive an
133  * \c incomingcall event
134  *
135 \verbatim
136 {
137 	"videocall" : "event",
138 	"result" : {
139 		"event" : "incomingcall",
140 		"username" : "<your username>"
141 	}
142 }
143 \endverbatim
144  *
145  * To accept the call, the \c accept request can be used. This request
146  * must be attached to a JSEP answer containing the WebRTC-related
147  * information to complete the actual PeerConnection setup. A \c accept
148  * request has to be formatted as follows:
149  *
150 \verbatim
151 {
152 	"request" : "accept"
153 }
154 \endverbatim
155  *
156  * If successul, both the caller and the callee will receive an
157  * \c accepted event to notify them about the success of the signalling:
158  *
159 \verbatim
160 {
161 	"videocall" : "event",
162 	"result" : {
163 		"event" : "accepted",
164 		"username" : "<caller username>"
165 	}
166 }
167 \endverbatim
168  *
169  * At this point, the media-related settings of the call can be modified
170  * on either side by means of a \c set request, which acts pretty much
171  * as the one in the \ref echoapi . The \c set request has to be
172  * formatted as follows. All the attributes (except \c request) are
173  * optional, so any request can contain a subset of them:
174  *
175 \verbatim
176 {
177 	"request" : "set",
178 	"audio" : true|false,
179 	"video" : true|false,
180 	"bitrate" : <numeric bitrate value>,
181 	"record" : true|false,
182 	"filename" : <base path/filename to use for the recording>,
183 	"substream" : <substream to receive (0-2), in case simulcasting is enabled>,
184 	"temporal" : <temporal layers to receive (0-2), in case simulcasting is enabled>,
185 	"fallback" : <How much time (in us, default 250000) without receiving packets will make us drop to the substream below>
186 }
187 \endverbatim
188  *
189  * \c audio instructs the plugin to do or do not relay audio frames;
190  * \c video does the same for video; \c bitrate caps the bandwidth to
191  * force on the browser encoding side (e.g., 128000 for 128kbps);
192  * \c record enables or disables the recording of this peer; in case
193  * recording is enabled, \c filename allows to specify a base
194  * path/filename to use for the files (-audio.mjr, -video.mjr and -data.mjr
195  * are automatically appended). Beware that enabling the recording only
196  * records this user's contribution, and not the whole call: to record
197  * both sides, you need to enable recording for both the peers in the
198  * call. Finally, in case the call uses simulcasting, \c substream and
199  * \c temporal can be used to manually pick which substream and/or temporal
200  * layer should be received from the peer.
201  *
202  * A successful request will result in a \c set event:
203  *
204 \verbatim
205 {
206 	"videocall" : "event",
207 	"result" : {
208 		"event" : "set"
209 	}
210 }
211 \endverbatim
212  *
213  * Notice that the \c set request is also what you use when you want
214  * to renegotiate a session, e.g., for the purpose of adding/removing
215  * media streams or forcing an ICE restart. In that case, even an empty
216  * \c set request is fine, as long as it accompanies a new JSEP offer
217  * or answer (depending on who originated the session update). The user
218  * receiving the updated JSEP offer/answer will get an \c update event:
219  *
220 \verbatim
221 {
222 	"videocall" : "event",
223 	"result" : {
224 		"event" : "update",
225 	}
226 }
227 \endverbatim
228  *
229  * To decline an incoming call, cancel an attempt to call or simply
230  * hangup an ongoing conversation, the \c hangup request can be used,
231  * which has to be formatted as follows:
232  *
233 \verbatim
234 {
235 	"request" : "hangup"
236 }
237 \endverbatim
238  *
239  * Whatever the reason of a call being closed (e.g., a \c hangup request,
240  * a PeerConnection being closed, or something else), both parties in
241  * the communication will receive a \c hangup event:
242  *
243 \verbatim
244 {
245 	"videocall" : "event",
246 	"result" : {
247 		"event" : "hangup",
248 		"username" : "<username of who closed the communication>",
249 		"reason" : "<description of what happened>"
250 	}
251 }
252 \endverbatim
253  */
254 
255 #include "plugin.h"
256 
257 #include <jansson.h>
258 
259 #include "../debug.h"
260 #include "../apierror.h"
261 #include "../config.h"
262 #include "../mutex.h"
263 #include "../record.h"
264 #include "../rtp.h"
265 #include "../rtcp.h"
266 #include "../sdp-utils.h"
267 #include "../utils.h"
268 
269 
270 /* Plugin information */
271 #define JANUS_VIDEOCALL_VERSION			6
272 #define JANUS_VIDEOCALL_VERSION_STRING	"0.0.6"
273 #define JANUS_VIDEOCALL_DESCRIPTION		"This is a simple video call plugin for Janus, allowing two WebRTC peers to call each other through a server."
274 #define JANUS_VIDEOCALL_NAME			"JANUS VideoCall plugin"
275 #define JANUS_VIDEOCALL_AUTHOR			"Meetecho s.r.l."
276 #define JANUS_VIDEOCALL_PACKAGE			"janus.plugin.videocall"
277 
278 /* Plugin methods */
279 janus_plugin *create(void);
280 int janus_videocall_init(janus_callbacks *callback, const char *config_path);
281 void janus_videocall_destroy(void);
282 int janus_videocall_get_api_compatibility(void);
283 int janus_videocall_get_version(void);
284 const char *janus_videocall_get_version_string(void);
285 const char *janus_videocall_get_description(void);
286 const char *janus_videocall_get_name(void);
287 const char *janus_videocall_get_author(void);
288 const char *janus_videocall_get_package(void);
289 void janus_videocall_create_session(janus_plugin_session *handle, int *error);
290 struct janus_plugin_result *janus_videocall_handle_message(janus_plugin_session *handle, char *transaction, json_t *message, json_t *jsep);
291 void janus_videocall_setup_media(janus_plugin_session *handle);
292 void janus_videocall_incoming_rtp(janus_plugin_session *handle, janus_plugin_rtp *packet);
293 void janus_videocall_incoming_rtcp(janus_plugin_session *handle, janus_plugin_rtcp *packet);
294 void janus_videocall_incoming_data(janus_plugin_session *handle, janus_plugin_data *packet);
295 void janus_videocall_data_ready(janus_plugin_session *handle);
296 void janus_videocall_slow_link(janus_plugin_session *handle, int uplink, int video);
297 void janus_videocall_hangup_media(janus_plugin_session *handle);
298 void janus_videocall_destroy_session(janus_plugin_session *handle, int *error);
299 json_t *janus_videocall_query_session(janus_plugin_session *handle);
300 
301 /* Plugin setup */
302 static janus_plugin janus_videocall_plugin =
303 	JANUS_PLUGIN_INIT (
304 		.init = janus_videocall_init,
305 		.destroy = janus_videocall_destroy,
306 
307 		.get_api_compatibility = janus_videocall_get_api_compatibility,
308 		.get_version = janus_videocall_get_version,
309 		.get_version_string = janus_videocall_get_version_string,
310 		.get_description = janus_videocall_get_description,
311 		.get_name = janus_videocall_get_name,
312 		.get_author = janus_videocall_get_author,
313 		.get_package = janus_videocall_get_package,
314 
315 		.create_session = janus_videocall_create_session,
316 		.handle_message = janus_videocall_handle_message,
317 		.setup_media = janus_videocall_setup_media,
318 		.incoming_rtp = janus_videocall_incoming_rtp,
319 		.incoming_rtcp = janus_videocall_incoming_rtcp,
320 		.incoming_data = janus_videocall_incoming_data,
321 		.data_ready = janus_videocall_data_ready,
322 		.slow_link = janus_videocall_slow_link,
323 		.hangup_media = janus_videocall_hangup_media,
324 		.destroy_session = janus_videocall_destroy_session,
325 		.query_session = janus_videocall_query_session,
326 	);
327 
328 /* Plugin creator */
create(void)329 janus_plugin *create(void) {
330 	JANUS_LOG(LOG_VERB, "%s created!\n", JANUS_VIDEOCALL_NAME);
331 	return &janus_videocall_plugin;
332 }
333 
334 /* Parameter validation */
335 static struct janus_json_parameter request_parameters[] = {
336 	{"request", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
337 };
338 static struct janus_json_parameter username_parameters[] = {
339 	{"username", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
340 };
341 static struct janus_json_parameter set_parameters[] = {
342 	{"audio", JANUS_JSON_BOOL, 0},
343 	{"video", JANUS_JSON_BOOL, 0},
344 	{"bitrate", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
345 	{"record", JANUS_JSON_BOOL, 0},
346 	{"filename", JSON_STRING, 0},
347 	{"restart", JANUS_JSON_BOOL, 0}
348 };
349 
350 /* Useful stuff */
351 static volatile gint initialized = 0, stopping = 0;
352 static gboolean notify_events = TRUE;
353 static janus_callbacks *gateway = NULL;
354 static GThread *handler_thread;
355 static void *janus_videocall_handler(void *data);
356 static void janus_videocall_hangup_media_internal(janus_plugin_session *handle);
357 
358 typedef struct janus_videocall_message {
359 	janus_plugin_session *handle;
360 	char *transaction;
361 	json_t *message;
362 	json_t *jsep;
363 } janus_videocall_message;
364 static GAsyncQueue *messages = NULL;
365 static janus_videocall_message exit_message;
366 
367 typedef struct janus_videocall_session {
368 	janus_plugin_session *handle;
369 	gchar *username;
370 	gboolean has_audio;
371 	gboolean has_video;
372 	gboolean has_data;
373 	gboolean audio_active;
374 	gboolean video_active;
375 	janus_audiocodec acodec;/* Codec used for audio, if available */
376 	janus_videocodec vcodec;/* Codec used for video, if available */
377 	uint32_t bitrate, peer_bitrate;
378 	guint16 slowlink_count;
379 	struct janus_videocall_session *peer;
380 	janus_rtp_switching_context context;
381 	uint32_t ssrc[3];		/* Only needed in case VP8 (or H.264) simulcasting is involved */
382 	char *rid[3];			/* Only needed if simulcasting is rid-based */
383 	janus_rtp_simulcasting_context sim_context;
384 	janus_vp8_simulcast_context vp8_context;
385 	janus_recorder *arc;	/* The Janus recorder instance for this user's audio, if enabled */
386 	janus_recorder *vrc;	/* The Janus recorder instance for this user's video, if enabled */
387 	janus_recorder *drc;	/* The Janus recorder instance for this user's data, if enabled */
388 	gboolean e2ee;			/* Whether media is encrypted, e.g., using Insertable Streams */
389 	janus_mutex rec_mutex;	/* Mutex to protect the recorders from race conditions */
390 	volatile gint incall;
391 	volatile gint dataready;
392 	volatile gint hangingup;
393 	volatile gint destroyed;
394 	janus_mutex mutex;
395 	janus_refcount ref;
396 } janus_videocall_session;
397 static GHashTable *sessions = NULL, *usernames = NULL;
398 static janus_mutex sessions_mutex = JANUS_MUTEX_INITIALIZER;
399 
janus_videocall_session_destroy(janus_videocall_session * session)400 static void janus_videocall_session_destroy(janus_videocall_session *session) {
401 	if(session && g_atomic_int_compare_and_exchange(&session->destroyed, 0, 1))
402 		janus_refcount_decrease(&session->ref);
403 }
janus_videocall_session_unref(janus_videocall_session * session)404 static void janus_videocall_session_unref(janus_videocall_session *session) {
405 	if(session)
406 		janus_refcount_decrease(&session->ref);
407 }
408 
janus_videocall_session_free(const janus_refcount * session_ref)409 static void janus_videocall_session_free(const janus_refcount *session_ref) {
410 	janus_videocall_session *session = janus_refcount_containerof(session_ref, janus_videocall_session, ref);
411 	/* Remove the reference to the core plugin session */
412 	janus_refcount_decrease(&session->handle->ref);
413 	/* This session can be destroyed, free all the resources */
414 	g_free(session->username);
415 	g_free(session);
416 }
417 
janus_videocall_message_free(janus_videocall_message * msg)418 static void janus_videocall_message_free(janus_videocall_message *msg) {
419 	if(!msg || msg == &exit_message)
420 		return;
421 
422 	if(msg->handle && msg->handle->plugin_handle) {
423 		janus_videocall_session *session = (janus_videocall_session *)msg->handle->plugin_handle;
424 		janus_refcount_decrease(&session->ref);
425 	}
426 	msg->handle = NULL;
427 
428 	g_free(msg->transaction);
429 	msg->transaction = NULL;
430 	if(msg->message)
431 		json_decref(msg->message);
432 	msg->message = NULL;
433 	if(msg->jsep)
434 		json_decref(msg->jsep);
435 	msg->jsep = NULL;
436 
437 	g_free(msg);
438 }
439 
440 
441 /* Error codes */
442 #define JANUS_VIDEOCALL_ERROR_UNKNOWN_ERROR			499
443 #define JANUS_VIDEOCALL_ERROR_NO_MESSAGE			470
444 #define JANUS_VIDEOCALL_ERROR_INVALID_JSON			471
445 #define JANUS_VIDEOCALL_ERROR_INVALID_REQUEST		472
446 #define JANUS_VIDEOCALL_ERROR_REGISTER_FIRST		473
447 #define JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT		474
448 #define JANUS_VIDEOCALL_ERROR_MISSING_ELEMENT		475
449 #define JANUS_VIDEOCALL_ERROR_USERNAME_TAKEN		476
450 #define JANUS_VIDEOCALL_ERROR_ALREADY_REGISTERED	477
451 #define JANUS_VIDEOCALL_ERROR_NO_SUCH_USERNAME		478
452 #define JANUS_VIDEOCALL_ERROR_USE_ECHO_TEST			479
453 #define JANUS_VIDEOCALL_ERROR_ALREADY_IN_CALL		480
454 #define JANUS_VIDEOCALL_ERROR_NO_CALL				481
455 #define JANUS_VIDEOCALL_ERROR_MISSING_SDP			482
456 #define JANUS_VIDEOCALL_ERROR_INVALID_SDP			483
457 
458 
459 /* Plugin implementation */
janus_videocall_init(janus_callbacks * callback,const char * config_path)460 int janus_videocall_init(janus_callbacks *callback, const char *config_path) {
461 	if(g_atomic_int_get(&stopping)) {
462 		/* Still stopping from before */
463 		return -1;
464 	}
465 	if(callback == NULL || config_path == NULL) {
466 		/* Invalid arguments */
467 		return -1;
468 	}
469 
470 	/* Read configuration */
471 	char filename[255];
472 	g_snprintf(filename, 255, "%s/%s.jcfg", config_path, JANUS_VIDEOCALL_PACKAGE);
473 	JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
474 	janus_config *config = janus_config_parse(filename);
475 	if(config == NULL) {
476 		JANUS_LOG(LOG_WARN, "Couldn't find .jcfg configuration file (%s), trying .cfg\n", JANUS_VIDEOCALL_PACKAGE);
477 		g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_VIDEOCALL_PACKAGE);
478 		JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
479 		config = janus_config_parse(filename);
480 	}
481 	if(config != NULL) {
482 		janus_config_print(config);
483 		janus_config_category *config_general = janus_config_get_create(config, NULL, janus_config_type_category, "general");
484 		janus_config_item *events = janus_config_get(config, config_general, janus_config_type_item, "events");
485 		if(events != NULL && events->value != NULL)
486 			notify_events = janus_is_true(events->value);
487 		if(!notify_events && callback->events_is_enabled()) {
488 			JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_VIDEOCALL_NAME);
489 		}
490 	}
491 	janus_config_destroy(config);
492 	config = NULL;
493 
494 	sessions = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)janus_videocall_session_destroy);
495 	usernames = g_hash_table_new_full(g_str_hash, g_str_equal,
496 		(GDestroyNotify)g_free, (GDestroyNotify)janus_videocall_session_unref);
497 	messages = g_async_queue_new_full((GDestroyNotify) janus_videocall_message_free);
498 	/* This is the callback we'll need to invoke to contact the Janus core */
499 	gateway = callback;
500 
501 	g_atomic_int_set(&initialized, 1);
502 
503 	/* Launch the thread that will handle incoming messages */
504 	GError *error = NULL;
505 	handler_thread = g_thread_try_new("videocall handler", janus_videocall_handler, NULL, &error);
506 	if(error != NULL) {
507 		g_atomic_int_set(&initialized, 0);
508 		JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the VideoCall handler thread...\n",
509 			error->code, error->message ? error->message : "??");
510 		g_error_free(error);
511 		return -1;
512 	}
513 	JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_VIDEOCALL_NAME);
514 	return 0;
515 }
516 
janus_videocall_destroy(void)517 void janus_videocall_destroy(void) {
518 	if(!g_atomic_int_get(&initialized))
519 		return;
520 	g_atomic_int_set(&stopping, 1);
521 
522 	g_async_queue_push(messages, &exit_message);
523 	if(handler_thread != NULL) {
524 		g_thread_join(handler_thread);
525 		handler_thread = NULL;
526 	}
527 	/* FIXME We should destroy the sessions cleanly */
528 	janus_mutex_lock(&sessions_mutex);
529 	g_hash_table_destroy(sessions);
530 	sessions = NULL;
531 	g_hash_table_destroy(usernames);
532 	usernames = NULL;
533 	janus_mutex_unlock(&sessions_mutex);
534 	g_async_queue_unref(messages);
535 	messages = NULL;
536 	g_atomic_int_set(&initialized, 0);
537 	g_atomic_int_set(&stopping, 0);
538 	JANUS_LOG(LOG_INFO, "%s destroyed!\n", JANUS_VIDEOCALL_NAME);
539 }
540 
janus_videocall_get_api_compatibility(void)541 int janus_videocall_get_api_compatibility(void) {
542 	/* Important! This is what your plugin MUST always return: don't lie here or bad things will happen */
543 	return JANUS_PLUGIN_API_VERSION;
544 }
545 
janus_videocall_get_version(void)546 int janus_videocall_get_version(void) {
547 	return JANUS_VIDEOCALL_VERSION;
548 }
549 
janus_videocall_get_version_string(void)550 const char *janus_videocall_get_version_string(void) {
551 	return JANUS_VIDEOCALL_VERSION_STRING;
552 }
553 
janus_videocall_get_description(void)554 const char *janus_videocall_get_description(void) {
555 	return JANUS_VIDEOCALL_DESCRIPTION;
556 }
557 
janus_videocall_get_name(void)558 const char *janus_videocall_get_name(void) {
559 	return JANUS_VIDEOCALL_NAME;
560 }
561 
janus_videocall_get_author(void)562 const char *janus_videocall_get_author(void) {
563 	return JANUS_VIDEOCALL_AUTHOR;
564 }
565 
janus_videocall_get_package(void)566 const char *janus_videocall_get_package(void) {
567 	return JANUS_VIDEOCALL_PACKAGE;
568 }
569 
janus_videocall_lookup_session(janus_plugin_session * handle)570 static janus_videocall_session *janus_videocall_lookup_session(janus_plugin_session *handle) {
571 	janus_videocall_session *session = NULL;
572 	if(g_hash_table_contains(sessions, handle)) {
573 		session = (janus_videocall_session *)handle->plugin_handle;
574 	}
575 	return session;
576 }
577 
janus_videocall_create_session(janus_plugin_session * handle,int * error)578 void janus_videocall_create_session(janus_plugin_session *handle, int *error) {
579 	if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
580 		*error = -1;
581 		return;
582 	}
583 	janus_videocall_session *session = g_malloc0(sizeof(janus_videocall_session));
584 	session->handle = handle;
585 	session->has_audio = FALSE;
586 	session->has_video = FALSE;
587 	session->has_data = FALSE;
588 	session->audio_active = TRUE;
589 	session->video_active = TRUE;
590 	session->bitrate = 0;	/* No limit */
591 	session->peer_bitrate = 0;
592 	session->peer = NULL;
593 	session->username = NULL;
594 	janus_rtp_switching_context_reset(&session->context);
595 	janus_rtp_simulcasting_context_reset(&session->sim_context);
596 	janus_vp8_simulcast_context_reset(&session->vp8_context);
597 	janus_mutex_init(&session->mutex);
598 	janus_mutex_init(&session->rec_mutex);
599 	g_atomic_int_set(&session->incall, 0);
600 	g_atomic_int_set(&session->hangingup, 0);
601 	g_atomic_int_set(&session->destroyed, 0);
602 	handle->plugin_handle = session;
603 	janus_refcount_init(&session->ref, janus_videocall_session_free);
604 
605 	janus_mutex_lock(&sessions_mutex);
606 	g_hash_table_insert(sessions, handle, session);
607 	janus_mutex_unlock(&sessions_mutex);
608 
609 	return;
610 }
611 
janus_videocall_destroy_session(janus_plugin_session * handle,int * error)612 void janus_videocall_destroy_session(janus_plugin_session *handle, int *error) {
613 	if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
614 		*error = -1;
615 		return;
616 	}
617 	janus_mutex_lock(&sessions_mutex);
618 	janus_videocall_session *session = janus_videocall_lookup_session(handle);
619 	if(!session) {
620 		janus_mutex_unlock(&sessions_mutex);
621 		JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
622 		*error = -2;
623 		return;
624 	}
625 	JANUS_LOG(LOG_VERB, "Removing VideoCall user %s session...\n", session->username ? session->username : "'unknown'");
626 	janus_videocall_hangup_media_internal(handle);
627 	if(session->username != NULL) {
628 		int res = g_hash_table_remove(usernames, (gpointer)session->username);
629 		JANUS_LOG(LOG_VERB, "  -- Removed: %d\n", res);
630 	}
631 	g_hash_table_remove(sessions, handle);
632 	janus_mutex_unlock(&sessions_mutex);
633 	return;
634 }
635 
janus_videocall_query_session(janus_plugin_session * handle)636 json_t *janus_videocall_query_session(janus_plugin_session *handle) {
637 	if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
638 		return NULL;
639 	}
640 	janus_mutex_lock(&sessions_mutex);
641 	janus_videocall_session *session = janus_videocall_lookup_session(handle);
642 	if(!session) {
643 		janus_mutex_unlock(&sessions_mutex);
644 		JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
645 		return NULL;
646 	}
647 	janus_refcount_increase(&session->ref);
648 	janus_mutex_unlock(&sessions_mutex);
649 	/* Provide some generic info, e.g., if we're in a call and with whom */
650 	janus_videocall_session *peer = session->peer;
651 	json_t *info = json_object();
652 	json_object_set_new(info, "state", json_string(session->peer ? "incall" : "idle"));
653 	json_object_set_new(info, "username", session->username ? json_string(session->username) : NULL);
654 	if(peer) {
655 		json_object_set_new(info, "peer", peer->username ? json_string(peer->username) : NULL);
656 		json_object_set_new(info, "audio_active", session->audio_active ? json_true() : json_false());
657 		json_object_set_new(info, "video_active", session->video_active ? json_true() : json_false());
658 		if(session->acodec != JANUS_AUDIOCODEC_NONE)
659 			json_object_set_new(info, "audio_codec", json_string(janus_audiocodec_name(session->acodec)));
660 		if(session->vcodec != JANUS_VIDEOCODEC_NONE)
661 			json_object_set_new(info, "video_codec", json_string(janus_videocodec_name(session->vcodec)));
662 		json_object_set_new(info, "video_active", session->video_active ? json_true() : json_false());
663 		json_object_set_new(info, "bitrate", json_integer(session->bitrate));
664 		json_object_set_new(info, "peer-bitrate", json_integer(session->peer_bitrate));
665 		json_object_set_new(info, "slowlink_count", json_integer(session->slowlink_count));
666 	}
667 	if(session->ssrc[0] != 0 || session->rid[0] != NULL) {
668 		json_object_set_new(info, "simulcast", json_true());
669 	}
670 	if(peer && (peer->ssrc[0] != 0 || peer->rid[0] != NULL)) {
671 		json_object_set_new(info, "simulcast-peer", json_true());
672 		json_object_set_new(info, "substream", json_integer(session->sim_context.substream));
673 		json_object_set_new(info, "substream-target", json_integer(session->sim_context.substream_target));
674 		json_object_set_new(info, "temporal-layer", json_integer(session->sim_context.templayer));
675 		json_object_set_new(info, "temporal-layer-target", json_integer(session->sim_context.templayer_target));
676 		if(session->sim_context.drop_trigger > 0)
677 			json_object_set_new(info, "fallback", json_integer(session->sim_context.drop_trigger));
678 	}
679 	if(session->arc || session->vrc || session->drc) {
680 		json_t *recording = json_object();
681 		if(session->arc && session->arc->filename)
682 			json_object_set_new(recording, "audio", json_string(session->arc->filename));
683 		if(session->vrc && session->vrc->filename)
684 			json_object_set_new(recording, "video", json_string(session->vrc->filename));
685 		if(session->drc && session->drc->filename)
686 			json_object_set_new(recording, "data", json_string(session->drc->filename));
687 		json_object_set_new(info, "recording", recording);
688 	}
689 	json_object_set_new(info, "incall", json_integer(g_atomic_int_get(&session->incall)));
690 	if(session->e2ee)
691 		json_object_set_new(info, "e2ee", json_true());
692 	json_object_set_new(info, "hangingup", json_integer(g_atomic_int_get(&session->hangingup)));
693 	json_object_set_new(info, "destroyed", json_integer(g_atomic_int_get(&session->destroyed)));
694 	janus_refcount_decrease(&session->ref);
695 	return info;
696 }
697 
janus_videocall_handle_message(janus_plugin_session * handle,char * transaction,json_t * message,json_t * jsep)698 struct janus_plugin_result *janus_videocall_handle_message(janus_plugin_session *handle, char *transaction, json_t *message, json_t *jsep) {
699 	if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
700 		return janus_plugin_result_new(JANUS_PLUGIN_ERROR, g_atomic_int_get(&stopping) ? "Shutting down" : "Plugin not initialized", NULL);
701 
702 	janus_mutex_lock(&sessions_mutex);
703 	janus_videocall_session *session = janus_videocall_lookup_session(handle);
704 	if(!session) {
705 		janus_mutex_unlock(&sessions_mutex);
706 		return janus_plugin_result_new(JANUS_PLUGIN_ERROR, "No session associated with this handle", NULL);
707 	}
708 	/* Increase the reference counter for this session: we'll decrease it after we handle the message */
709 	janus_refcount_increase(&session->ref);
710 	janus_mutex_unlock(&sessions_mutex);
711 
712 	janus_videocall_message *msg = g_malloc(sizeof(janus_videocall_message));
713 	msg->handle = handle;
714 	msg->transaction = transaction;
715 	msg->message = message;
716 	msg->jsep = jsep;
717 	g_async_queue_push(messages, msg);
718 
719 	/* All the requests to this plugin are handled asynchronously */
720 	return janus_plugin_result_new(JANUS_PLUGIN_OK_WAIT, NULL, NULL);
721 }
722 
janus_videocall_setup_media(janus_plugin_session * handle)723 void janus_videocall_setup_media(janus_plugin_session *handle) {
724 	JANUS_LOG(LOG_INFO, "[%s-%p] WebRTC media is now available\n", JANUS_VIDEOCALL_PACKAGE, handle);
725 	if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
726 		return;
727 	janus_mutex_lock(&sessions_mutex);
728 	janus_videocall_session *session = janus_videocall_lookup_session(handle);
729 	if(!session) {
730 		janus_mutex_unlock(&sessions_mutex);
731 		JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
732 		return;
733 	}
734 	if(g_atomic_int_get(&session->destroyed)) {
735 		janus_mutex_unlock(&sessions_mutex);
736 		return;
737 	}
738 	g_atomic_int_set(&session->hangingup, 0);
739 	janus_mutex_unlock(&sessions_mutex);
740 	/* We really don't care, as we only relay RTP/RTCP we get in the first place anyway */
741 }
742 
janus_videocall_incoming_rtp(janus_plugin_session * handle,janus_plugin_rtp * packet)743 void janus_videocall_incoming_rtp(janus_plugin_session *handle, janus_plugin_rtp *packet) {
744 	if(handle == NULL || g_atomic_int_get(&handle->stopped) || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
745 		return;
746 	if(gateway) {
747 		/* Honour the audio/video active flags */
748 		janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;
749 		if(!session) {
750 			JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
751 			return;
752 		}
753 		janus_videocall_session *peer = session->peer;
754 		if(!peer) {
755 			JANUS_LOG(LOG_ERR, "Session has no peer...\n");
756 			return;
757 		}
758 		if(g_atomic_int_get(&session->destroyed) || g_atomic_int_get(&peer->destroyed))
759 			return;
760 		gboolean video = packet->video;
761 		char *buf = packet->buffer;
762 		uint16_t len = packet->length;
763 		if(video && session->video_active && (session->ssrc[0] != 0 || session->rid[0] != NULL)) {
764 			/* Handle simulcast: backup the header information first */
765 			janus_rtp_header *header = (janus_rtp_header *)buf;
766 			uint32_t seq_number = ntohs(header->seq_number);
767 			uint32_t timestamp = ntohl(header->timestamp);
768 			uint32_t ssrc = ntohl(header->ssrc);
769 			/* Process this packet: don't relay if it's not the SSRC/layer we wanted to handle
770 			 * The caveat is that the targets in OUR simulcast context are the PEER's targets */
771 			gboolean relay = janus_rtp_simulcasting_context_process_rtp(&peer->sim_context,
772 				buf, len, session->ssrc, session->rid, session->vcodec, &peer->context);
773 			/* Do we need to drop this? */
774 			if(!relay)
775 				return;
776 			if(peer->sim_context.need_pli) {
777 				/* Send a PLI */
778 				JANUS_LOG(LOG_VERB, "We need a PLI for the simulcast context\n");
779 				gateway->send_pli(session->handle);
780 			}
781 			/* Any event we should notify? */
782 			if(peer->sim_context.changed_substream) {
783 				/* Notify the user about the substream change */
784 				json_t *event = json_object();
785 				json_object_set_new(event, "videocall", json_string("event"));
786 				json_t *result = json_object();
787 				json_object_set_new(result, "event", json_string("simulcast"));
788 				json_object_set_new(result, "videocodec", json_string(janus_videocodec_name(session->vcodec)));
789 				json_object_set_new(result, "substream", json_integer(peer->sim_context.substream));
790 				json_object_set_new(event, "result", result);
791 				gateway->push_event(peer->handle, &janus_videocall_plugin, NULL, event, NULL);
792 				json_decref(event);
793 			}
794 			if(peer->sim_context.changed_temporal) {
795 				/* Notify the user about the temporal layer change */
796 				json_t *event = json_object();
797 				json_object_set_new(event, "videocall", json_string("event"));
798 				json_t *result = json_object();
799 				json_object_set_new(result, "event", json_string("simulcast"));
800 				json_object_set_new(result, "videocodec", json_string(janus_videocodec_name(session->vcodec)));
801 				json_object_set_new(result, "temporal", json_integer(peer->sim_context.templayer));
802 				json_object_set_new(event, "result", result);
803 				gateway->push_event(peer->handle, &janus_videocall_plugin, NULL, event, NULL);
804 				json_decref(event);
805 			}
806 			/* If we got here, update the RTP header and send the packet */
807 			janus_rtp_header_update(header, &peer->context, TRUE, 0);
808 			if(session->vcodec == JANUS_VIDEOCODEC_VP8) {
809 				int plen = 0;
810 				char *payload = janus_rtp_payload(buf, len, &plen);
811 				janus_vp8_simulcast_descriptor_update(payload, plen, &peer->vp8_context, peer->sim_context.changed_substream);
812 			}
813 			/* Save the frame if we're recording (and make sure the SSRC never changes even if the substream does) */
814 			header->ssrc = htonl(1);
815 			janus_recorder_save_frame(session->vrc, buf, len);
816 			/* Send the frame back */
817 			gateway->relay_rtp(peer->handle, packet);
818 			/* Restore header or core statistics will be messed up */
819 			header->ssrc = htonl(ssrc);
820 			header->timestamp = htonl(timestamp);
821 			header->seq_number = htons(seq_number);
822 		} else {
823 			if((!video && session->audio_active) || (video && session->video_active)) {
824 				/* Save the frame if we're recording */
825 				janus_recorder_save_frame(video ? session->vrc : session->arc, buf, len);
826 				/* Forward the packet to the peer */
827 				gateway->relay_rtp(peer->handle, packet);
828 			}
829 		}
830 	}
831 }
832 
janus_videocall_incoming_rtcp(janus_plugin_session * handle,janus_plugin_rtcp * packet)833 void janus_videocall_incoming_rtcp(janus_plugin_session *handle, janus_plugin_rtcp *packet) {
834 	if(handle == NULL || g_atomic_int_get(&handle->stopped) || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
835 		return;
836 	if(gateway) {
837 		janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;
838 		if(!session) {
839 			JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
840 			return;
841 		}
842 		janus_videocall_session *peer = session->peer;
843 		if(!peer) {
844 			JANUS_LOG(LOG_ERR, "Session has no peer...\n");
845 			return;
846 		}
847 		if(g_atomic_int_get(&session->destroyed) || g_atomic_int_get(&peer->destroyed))
848 			return;
849 		guint32 bitrate = janus_rtcp_get_remb(packet->buffer, packet->length);
850 		if(bitrate > 0) {
851 			/* If a REMB arrived, make sure we cap it to our configuration, and send it as a video RTCP */
852 			session->peer_bitrate = bitrate;
853 			/* No limit ~= 10000000 */
854 			gateway->send_remb(handle, session->bitrate ? session->bitrate : 10000000);
855 			return;
856 		}
857 		gateway->relay_rtcp(peer->handle, packet);
858 	}
859 }
860 
janus_videocall_incoming_data(janus_plugin_session * handle,janus_plugin_data * packet)861 void janus_videocall_incoming_data(janus_plugin_session *handle, janus_plugin_data *packet) {
862 	if(handle == NULL || g_atomic_int_get(&handle->stopped) || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
863 		return;
864 	if(gateway) {
865 		janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;
866 		if(!session) {
867 			JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
868 			return;
869 		}
870 		janus_videocall_session *peer = session->peer;
871 		if(!peer) {
872 			JANUS_LOG(LOG_ERR, "Session has no peer...\n");
873 			return;
874 		}
875 		if(g_atomic_int_get(&session->destroyed) || g_atomic_int_get(&peer->destroyed) ||
876 				!g_atomic_int_get(&peer->dataready))
877 			return;
878 		if(packet->buffer == NULL || packet->length == 0)
879 			return;
880 		char *label = packet->label;
881 		char *buf = packet->buffer;
882 		uint16_t len = packet->length;
883 		JANUS_LOG(LOG_VERB, "Got a %s DataChannel message (%d bytes) to forward\n",
884 			!packet->binary ? "text" : "binary", len);
885 		/* Save the frame if we're recording */
886 		janus_recorder_save_frame(session->drc, buf, len);
887 		/* Forward the packet to the peer */
888 		janus_plugin_data r = {
889 			.label = label,
890 			.protocol = NULL,
891 			.binary = packet->binary,
892 			.buffer = buf,
893 			.length = len
894 		};
895 		gateway->relay_data(peer->handle, &r);
896 	}
897 }
898 
janus_videocall_data_ready(janus_plugin_session * handle)899 void janus_videocall_data_ready(janus_plugin_session *handle) {
900 	if(handle == NULL || g_atomic_int_get(&handle->stopped) ||
901 			g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized) || !gateway)
902 		return;
903 	/* Data channels are writable */
904 	janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;
905 	if(!session || g_atomic_int_get(&session->destroyed) || g_atomic_int_get(&session->hangingup))
906 		return;
907 	if(g_atomic_int_compare_and_exchange(&session->dataready, 0, 1)) {
908 		JANUS_LOG(LOG_INFO, "[%s-%p] Data channel available\n", JANUS_VIDEOCALL_PACKAGE, handle);
909 	}
910 }
911 
janus_videocall_slow_link(janus_plugin_session * handle,int uplink,int video)912 void janus_videocall_slow_link(janus_plugin_session *handle, int uplink, int video) {
913 	/* The core is informing us that our peer got or sent too many NACKs, are we pushing media too hard? */
914 	if(handle == NULL || g_atomic_int_get(&handle->stopped) || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
915 		return;
916 	janus_videocall_session *session = (janus_videocall_session *)handle->plugin_handle;
917 	if(!session) {
918 		JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
919 		return;
920 	}
921 	if(g_atomic_int_get(&session->destroyed))
922 		return;
923 	session->slowlink_count++;
924 	if(uplink && !video && !session->audio_active) {
925 		/* We're not relaying audio and the peer is expecting it, so NACKs are normal */
926 		JANUS_LOG(LOG_VERB, "Getting a lot of lost packets (slow uplink) for audio, but that's expected, a configure disabled the audio forwarding\n");
927 	} else if(uplink && video && !session->video_active) {
928 		/* We're not relaying video and the peer is expecting it, so NACKs are normal */
929 		JANUS_LOG(LOG_VERB, "Getting a lot of lost packets (slow uplink) for video, but that's expected, a configure disabled the video forwarding\n");
930 	} else {
931 		JANUS_LOG(LOG_WARN, "Getting a lot of lost packets (slow %s) for %s\n",
932 			uplink ? "uplink" : "downlink", video ? "video" : "audio");
933 		if(!uplink) {
934 			/* Send an event on the handle to notify the application: it's
935 			 * up to the application to then choose a policy and enforce it */
936 			json_t *event = json_object();
937 			json_object_set_new(event, "videocall", json_string("event"));
938 			/* Also add info on what the current bitrate cap is */
939 			json_t *result = json_object();
940 			json_object_set_new(result, "event", json_string("slow_link"));
941 			json_object_set_new(result, "media", json_string(video ? "video" : "audio"));
942 			if(video)
943 				json_object_set_new(result, "current-bitrate", json_integer(session->bitrate));
944 			json_object_set_new(event, "result", result);
945 			gateway->push_event(session->handle, &janus_videocall_plugin, NULL, event, NULL);
946 			json_decref(event);
947 		}
948 	}
949 }
950 
janus_videocall_recorder_close(janus_videocall_session * session)951 static void janus_videocall_recorder_close(janus_videocall_session *session) {
952 	if(session->arc) {
953 		janus_recorder *rc = session->arc;
954 		session->arc = NULL;
955 		janus_recorder_close(rc);
956 		JANUS_LOG(LOG_INFO, "Closed audio recording %s\n", rc->filename ? rc->filename : "??");
957 		janus_recorder_destroy(rc);
958 	}
959 	if(session->vrc) {
960 		janus_recorder *rc = session->vrc;
961 		session->vrc = NULL;
962 		janus_recorder_close(rc);
963 		JANUS_LOG(LOG_INFO, "Closed video recording %s\n", rc->filename ? rc->filename : "??");
964 		janus_recorder_destroy(rc);
965 	}
966 	if(session->drc) {
967 		janus_recorder *rc = session->drc;
968 		session->drc = NULL;
969 		janus_recorder_close(rc);
970 		JANUS_LOG(LOG_INFO, "Closed data recording %s\n", rc->filename ? rc->filename : "??");
971 		janus_recorder_destroy(rc);
972 	}
973 }
974 
janus_videocall_hangup_media(janus_plugin_session * handle)975 void janus_videocall_hangup_media(janus_plugin_session *handle) {
976 	JANUS_LOG(LOG_INFO, "[%s-%p] No WebRTC media anymore\n", JANUS_VIDEOCALL_PACKAGE, handle);
977 	janus_mutex_lock(&sessions_mutex);
978 	janus_videocall_hangup_media_internal(handle);
979 	janus_mutex_unlock(&sessions_mutex);
980 }
981 
janus_videocall_hangup_media_internal(janus_plugin_session * handle)982 static void janus_videocall_hangup_media_internal(janus_plugin_session *handle) {
983 	if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
984 		return;
985 	janus_videocall_session *session = janus_videocall_lookup_session(handle);
986 	if(!session) {
987 		JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
988 		return;
989 	}
990 	if(g_atomic_int_get(&session->destroyed))
991 		return;
992 	if(!g_atomic_int_compare_and_exchange(&session->hangingup, 0, 1))
993 		return;
994 	g_atomic_int_set(&session->dataready, 0);
995 	/* Get rid of the recorders, if available */
996 	janus_mutex_lock(&session->rec_mutex);
997 	janus_videocall_recorder_close(session);
998 	janus_mutex_unlock(&session->rec_mutex);
999 	janus_mutex_lock(&session->mutex);
1000 	janus_videocall_session *peer = session->peer;
1001 	session->peer = NULL;
1002 	if(peer) {
1003 		/* Send event to our peer too */
1004 		json_t *call = json_object();
1005 		json_object_set_new(call, "videocall", json_string("event"));
1006 		json_t *calling = json_object();
1007 		json_object_set_new(calling, "event", json_string("hangup"));
1008 		json_object_set_new(calling, "username", json_string(session->username));
1009 		json_object_set_new(calling, "reason", json_string("Remote WebRTC hangup"));
1010 		json_object_set_new(call, "result", calling);
1011 		gateway->close_pc(peer->handle);
1012 		int ret = gateway->push_event(peer->handle, &janus_videocall_plugin, NULL, call, NULL);
1013 		JANUS_LOG(LOG_VERB, "  >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
1014 		json_decref(call);
1015 		/* Also notify event handlers */
1016 		if(notify_events && gateway->events_is_enabled()) {
1017 			json_t *info = json_object();
1018 			json_object_set_new(info, "event", json_string("hangup"));
1019 			json_object_set_new(info, "reason", json_string("Remote WebRTC hangup"));
1020 			gateway->notify_event(&janus_videocall_plugin, peer->handle, info);
1021 		}
1022 		janus_refcount_decrease(&peer->ref);
1023 	}
1024 	janus_mutex_unlock(&session->mutex);
1025 	/* Reset controls */
1026 	session->has_audio = FALSE;
1027 	session->has_video = FALSE;
1028 	session->has_data = FALSE;
1029 	session->audio_active = TRUE;
1030 	session->video_active = TRUE;
1031 	session->acodec = JANUS_AUDIOCODEC_NONE;
1032 	session->vcodec = JANUS_VIDEOCODEC_NONE;
1033 	session->bitrate = 0;
1034 	session->peer_bitrate = 0;
1035 	session->e2ee = FALSE;
1036 	int i=0;
1037 	for(i=0; i<3; i++) {
1038 		session->ssrc[i] = 0;
1039 		g_free(session->rid[i]);
1040 		session->rid[i] = NULL;
1041 	}
1042 	janus_rtp_switching_context_reset(&session->context);
1043 	janus_rtp_simulcasting_context_reset(&session->sim_context);
1044 	janus_vp8_simulcast_context_reset(&session->vp8_context);
1045 	if(g_atomic_int_compare_and_exchange(&session->incall, 1, 0) && peer) {
1046 		janus_refcount_decrease(&peer->ref);
1047 	}
1048 	janus_rtp_switching_context_reset(&session->context);
1049 	g_atomic_int_set(&session->hangingup, 0);
1050 }
1051 
1052 /* Thread to handle incoming messages */
janus_videocall_handler(void * data)1053 static void *janus_videocall_handler(void *data) {
1054 	JANUS_LOG(LOG_VERB, "Joining VideoCall handler thread\n");
1055 	janus_videocall_message *msg = NULL;
1056 	int error_code = 0;
1057 	char error_cause[512];
1058 	json_t *root = NULL;
1059 	while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) {
1060 		msg = g_async_queue_pop(messages);
1061 		if(msg == &exit_message)
1062 			break;
1063 		if(msg->handle == NULL) {
1064 			janus_videocall_message_free(msg);
1065 			continue;
1066 		}
1067 		janus_mutex_lock(&sessions_mutex);
1068 		janus_videocall_session *session = janus_videocall_lookup_session(msg->handle);
1069 		if(!session) {
1070 			janus_mutex_unlock(&sessions_mutex);
1071 			JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
1072 			janus_videocall_message_free(msg);
1073 			continue;
1074 		}
1075 		if(g_atomic_int_get(&session->destroyed)) {
1076 			janus_mutex_unlock(&sessions_mutex);
1077 			janus_videocall_message_free(msg);
1078 			continue;
1079 		}
1080 		janus_mutex_unlock(&sessions_mutex);
1081 		/* Handle request */
1082 		error_code = 0;
1083 		root = msg->message;
1084 		if(msg->message == NULL) {
1085 			JANUS_LOG(LOG_ERR, "No message??\n");
1086 			error_code = JANUS_VIDEOCALL_ERROR_NO_MESSAGE;
1087 			g_snprintf(error_cause, 512, "%s", "No message??");
1088 			goto error;
1089 		}
1090 		if(!json_is_object(root)) {
1091 			JANUS_LOG(LOG_ERR, "JSON error: not an object\n");
1092 			error_code = JANUS_VIDEOCALL_ERROR_INVALID_JSON;
1093 			g_snprintf(error_cause, 512, "JSON error: not an object");
1094 			goto error;
1095 		}
1096 		JANUS_VALIDATE_JSON_OBJECT(root, request_parameters,
1097 			error_code, error_cause, TRUE,
1098 			JANUS_VIDEOCALL_ERROR_MISSING_ELEMENT, JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT);
1099 		if(error_code != 0)
1100 			goto error;
1101 		const char *msg_sdp_type = json_string_value(json_object_get(msg->jsep, "type"));
1102 		const char *msg_sdp = json_string_value(json_object_get(msg->jsep, "sdp"));
1103 		json_t *msg_e2ee = json_object_get(msg->jsep, "e2ee");
1104 		if(json_is_true(msg_e2ee))
1105 			session->e2ee = TRUE;
1106 		json_t *request = json_object_get(root, "request");
1107 		const char *request_text = json_string_value(request);
1108 		json_t *result = NULL;
1109 		gboolean sdp_update = FALSE;
1110 		if(json_object_get(msg->jsep, "update") != NULL)
1111 			sdp_update = json_is_true(json_object_get(msg->jsep, "update"));
1112 		if(!strcasecmp(request_text, "list")) {
1113 			result = json_object();
1114 			json_t *list = json_array();
1115 			JANUS_LOG(LOG_VERB, "Request for the list of peers\n");
1116 			/* Return a list of all available mountpoints */
1117 			janus_mutex_lock(&sessions_mutex);
1118 			GHashTableIter iter;
1119 			gpointer value;
1120 			g_hash_table_iter_init(&iter, sessions);
1121 			while (g_hash_table_iter_next(&iter, NULL, &value)) {
1122 				janus_videocall_session *user = value;
1123 				if(user != NULL) {
1124 					janus_refcount_increase(&user->ref);
1125 					if(user->username != NULL)
1126 						json_array_append_new(list, json_string(user->username));
1127 					janus_refcount_decrease(&user->ref);
1128 				}
1129 			}
1130 			json_object_set_new(result, "list", list);
1131 			janus_mutex_unlock(&sessions_mutex);
1132 		} else if(!strcasecmp(request_text, "register")) {
1133 			/* Map this handle to a username */
1134 			if(session->username != NULL) {
1135 				JANUS_LOG(LOG_ERR, "Already registered (%s)\n", session->username);
1136 				error_code = JANUS_VIDEOCALL_ERROR_ALREADY_REGISTERED;
1137 				g_snprintf(error_cause, 512, "Already registered (%s)", session->username);
1138 				goto error;
1139 			}
1140 			JANUS_VALIDATE_JSON_OBJECT(root, username_parameters,
1141 				error_code, error_cause, TRUE,
1142 				JANUS_VIDEOCALL_ERROR_MISSING_ELEMENT, JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT);
1143 			if(error_code != 0)
1144 				goto error;
1145 			json_t *username = json_object_get(root, "username");
1146 			const char *username_text = json_string_value(username);
1147 			janus_mutex_lock(&sessions_mutex);
1148 			if(g_hash_table_lookup(usernames, username_text) != NULL) {
1149 				janus_mutex_unlock(&sessions_mutex);
1150 				JANUS_LOG(LOG_ERR, "Username '%s' already taken\n", username_text);
1151 				error_code = JANUS_VIDEOCALL_ERROR_USERNAME_TAKEN;
1152 				g_snprintf(error_cause, 512, "Username '%s' already taken", username_text);
1153 				goto error;
1154 			}
1155 			session->username = g_strdup(username_text);
1156 			janus_refcount_increase(&session->ref);
1157 			g_hash_table_insert(usernames, (gpointer)g_strdup(session->username), session);
1158 			janus_mutex_unlock(&sessions_mutex);
1159 			result = json_object();
1160 			json_object_set_new(result, "event", json_string("registered"));
1161 			json_object_set_new(result, "username", json_string(username_text));
1162 			/* Also notify event handlers */
1163 			if(notify_events && gateway->events_is_enabled()) {
1164 				json_t *info = json_object();
1165 				json_object_set_new(info, "event", json_string("registered"));
1166 				json_object_set_new(info, "username", json_string(username_text));
1167 				gateway->notify_event(&janus_videocall_plugin, session->handle, info);
1168 			}
1169 		} else if(!strcasecmp(request_text, "call")) {
1170 			/* Call another peer */
1171 			if(session->username == NULL) {
1172 				JANUS_LOG(LOG_ERR, "Register a username first\n");
1173 				error_code = JANUS_VIDEOCALL_ERROR_REGISTER_FIRST;
1174 				g_snprintf(error_cause, 512, "Register a username first");
1175 				/* Hangup the call attempt of the user */
1176 				gateway->close_pc(session->handle);
1177 				goto error;
1178 			}
1179 			if(session->peer != NULL) {
1180 				JANUS_LOG(LOG_ERR, "Already in a call\n");
1181 				error_code = JANUS_VIDEOCALL_ERROR_ALREADY_IN_CALL;
1182 				g_snprintf(error_cause, 512, "Already in a call");
1183 				/* Hangup the call attempt of the user */
1184 				gateway->close_pc(session->handle);
1185 				goto error;
1186 			}
1187 			if(!g_atomic_int_compare_and_exchange(&session->incall, 0, 1)) {
1188 				JANUS_LOG(LOG_ERR, "Already in a call (but no peer?)\n");
1189 				error_code = JANUS_VIDEOCALL_ERROR_ALREADY_IN_CALL;
1190 				g_snprintf(error_cause, 512, "Already in a call (but no peer)");
1191 				/* Hangup the call attempt of the user */
1192 				gateway->close_pc(session->handle);
1193 				goto error;
1194 			}
1195 			JANUS_VALIDATE_JSON_OBJECT(root, username_parameters,
1196 				error_code, error_cause, TRUE,
1197 				JANUS_VIDEOCALL_ERROR_MISSING_ELEMENT, JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT);
1198 			if(error_code != 0) {
1199 				/* Hangup the call attempt of the user */
1200 				g_atomic_int_set(&session->incall, 0);
1201 				gateway->close_pc(session->handle);
1202 				goto error;
1203 			}
1204 			json_t *username = json_object_get(root, "username");
1205 			const char *username_text = json_string_value(username);
1206 			if(!strcmp(username_text, session->username)) {
1207 				g_atomic_int_set(&session->incall, 0);
1208 				JANUS_LOG(LOG_ERR, "You can't call yourself... use the EchoTest for that\n");
1209 				error_code = JANUS_VIDEOCALL_ERROR_USE_ECHO_TEST;
1210 				g_snprintf(error_cause, 512, "You can't call yourself... use the EchoTest for that");
1211 				/* Hangup the call attempt of the user */
1212 				gateway->close_pc(session->handle);
1213 				goto error;
1214 			}
1215 			janus_mutex_lock(&sessions_mutex);
1216 			janus_videocall_session *peer = g_hash_table_lookup(usernames, username_text);
1217 			if(peer == NULL || g_atomic_int_get(&peer->destroyed)) {
1218 				g_atomic_int_set(&session->incall, 0);
1219 				janus_mutex_unlock(&sessions_mutex);
1220 				JANUS_LOG(LOG_ERR, "Username '%s' doesn't exist\n", username_text);
1221 				error_code = JANUS_VIDEOCALL_ERROR_NO_SUCH_USERNAME;
1222 				g_snprintf(error_cause, 512, "Username '%s' doesn't exist", username_text);
1223 				/* Hangup the call attempt of the user */
1224 				gateway->close_pc(session->handle);
1225 				goto error;
1226 			}
1227 			/* If the call attempt proceeds we keep the references */
1228 			janus_refcount_increase(&session->ref);
1229 			janus_refcount_increase(&peer->ref);
1230 			if(g_atomic_int_get(&peer->incall) || peer->peer != NULL) {
1231 				if(g_atomic_int_compare_and_exchange(&session->incall, 1, 0) && peer) {
1232 					janus_refcount_decrease(&session->ref);
1233 					janus_refcount_decrease(&peer->ref);
1234 				}
1235 				janus_mutex_unlock(&sessions_mutex);
1236 				JANUS_LOG(LOG_VERB, "%s is busy\n", username_text);
1237 				result = json_object();
1238 				json_object_set_new(result, "event", json_string("hangup"));
1239 				json_object_set_new(result, "username", json_string(session->username));
1240 				json_object_set_new(result, "reason", json_string("User busy"));
1241 				/* Also notify event handlers */
1242 				if(notify_events && gateway->events_is_enabled()) {
1243 					json_t *info = json_object();
1244 					json_object_set_new(info, "event", json_string("hangup"));
1245 					json_object_set_new(info, "reason", json_string("User busy"));
1246 					gateway->notify_event(&janus_videocall_plugin, session->handle, info);
1247 				}
1248 				/* Hangup the call attempt of the user */
1249 				gateway->close_pc(session->handle);
1250 			} else {
1251 				/* Any SDP to handle? if not, something's wrong */
1252 				if(!msg_sdp) {
1253 					if(g_atomic_int_compare_and_exchange(&session->incall, 1, 0) && peer) {
1254 						janus_refcount_decrease(&session->ref);
1255 						janus_refcount_decrease(&peer->ref);
1256 					}
1257 					janus_mutex_unlock(&sessions_mutex);
1258 					JANUS_LOG(LOG_ERR, "Missing SDP\n");
1259 					error_code = JANUS_VIDEOCALL_ERROR_MISSING_SDP;
1260 					g_snprintf(error_cause, 512, "Missing SDP");
1261 					goto error;
1262 				}
1263 				char error_str[512];
1264 				janus_sdp *offer = janus_sdp_parse(msg_sdp, error_str, sizeof(error_str));
1265 				if(offer == NULL) {
1266 					if(g_atomic_int_compare_and_exchange(&session->incall, 1, 0) && peer) {
1267 						janus_refcount_decrease(&session->ref);
1268 						janus_refcount_decrease(&peer->ref);
1269 					}
1270 					janus_mutex_unlock(&sessions_mutex);
1271 					JANUS_LOG(LOG_ERR, "Error parsing offer: %s\n", error_str);
1272 					error_code = JANUS_VIDEOCALL_ERROR_INVALID_SDP;
1273 					g_snprintf(error_cause, 512, "Error parsing offer: %s", error_str);
1274 					goto error;
1275 				}
1276 				janus_sdp_destroy(offer);
1277 				g_atomic_int_set(&peer->incall, 1);
1278 				janus_refcount_increase(&session->ref);
1279 				janus_refcount_increase(&peer->ref);
1280 				janus_mutex_lock(&session->mutex);
1281 				session->peer = peer;
1282 				janus_mutex_unlock(&session->mutex);
1283 				janus_mutex_lock(&peer->mutex);
1284 				peer->peer = session;
1285 				janus_mutex_unlock(&peer->mutex);
1286 				session->has_audio = (strstr(msg_sdp, "m=audio") != NULL);
1287 				session->has_video = (strstr(msg_sdp, "m=video") != NULL);
1288 				session->has_data = (strstr(msg_sdp, "DTLS/SCTP") != NULL);
1289 				janus_mutex_unlock(&sessions_mutex);
1290 				JANUS_LOG(LOG_VERB, "%s is calling %s\n", session->username, peer->username);
1291 				JANUS_LOG(LOG_VERB, "This is involving a negotiation (%s) as well:\n%s\n", msg_sdp_type, msg_sdp);
1292 				/* Check if this user will simulcast */
1293 				json_t *msg_simulcast = json_object_get(msg->jsep, "simulcast");
1294 				if(msg_simulcast) {
1295 					JANUS_LOG(LOG_VERB, "VideoCall caller (%s) is going to do simulcasting\n", session->username);
1296 					int rid_ext_id = -1;
1297 					janus_rtp_simulcasting_prepare(msg_simulcast, &rid_ext_id, session->ssrc, session->rid);
1298 					session->sim_context.rid_ext_id = rid_ext_id;
1299 				}
1300 				/* Send SDP to our peer */
1301 				json_t *call = json_object();
1302 				json_object_set_new(call, "videocall", json_string("event"));
1303 				json_t *calling = json_object();
1304 				json_object_set_new(calling, "event", json_string("incomingcall"));
1305 				json_object_set_new(calling, "username", json_string(session->username));
1306 				json_object_set_new(call, "result", calling);
1307 				json_t *jsep = json_pack("{ssss}", "type", msg_sdp_type, "sdp", msg_sdp);
1308 				if(session->e2ee)
1309 					json_object_set_new(jsep, "e2ee", json_true());
1310 				g_atomic_int_set(&session->hangingup, 0);
1311 				int ret = gateway->push_event(peer->handle, &janus_videocall_plugin, NULL, call, jsep);
1312 				JANUS_LOG(LOG_VERB, "  >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
1313 				json_decref(call);
1314 				json_decref(jsep);
1315 				/* Send an ack back */
1316 				result = json_object();
1317 				json_object_set_new(result, "event", json_string("calling"));
1318 				/* Also notify event handlers */
1319 				if(notify_events && gateway->events_is_enabled()) {
1320 					json_t *info = json_object();
1321 					json_object_set_new(info, "event", json_string("calling"));
1322 					gateway->notify_event(&janus_videocall_plugin, session->handle, info);
1323 				}
1324 			}
1325 		} else if(!strcasecmp(request_text, "accept")) {
1326 			/* Accept a call from another peer */
1327 			janus_videocall_session *peer = session->peer;
1328 			if(peer == NULL || !g_atomic_int_get(&session->incall) || !g_atomic_int_get(&peer->incall)) {
1329 				JANUS_LOG(LOG_ERR, "No incoming call to accept\n");
1330 				error_code = JANUS_VIDEOCALL_ERROR_NO_CALL;
1331 				g_snprintf(error_cause, 512, "No incoming call to accept");
1332 				goto error;
1333 			}
1334 			janus_refcount_increase(&peer->ref);
1335 			/* Any SDP to handle? if not, something's wrong */
1336 			if(!msg_sdp) {
1337 				janus_refcount_decrease(&peer->ref);
1338 				JANUS_LOG(LOG_ERR, "Missing SDP\n");
1339 				error_code = JANUS_VIDEOCALL_ERROR_MISSING_SDP;
1340 				g_snprintf(error_cause, 512, "Missing SDP");
1341 				goto error;
1342 			}
1343 			char error_str[512];
1344 			janus_sdp *answer = janus_sdp_parse(msg_sdp, error_str, sizeof(error_str));
1345 			if(answer == NULL) {
1346 				janus_refcount_decrease(&peer->ref);
1347 				JANUS_LOG(LOG_ERR, "Error parsing answer: %s\n", error_str);
1348 				error_code = JANUS_VIDEOCALL_ERROR_INVALID_SDP;
1349 				g_snprintf(error_cause, 512, "Error parsing answer: %s", error_str);
1350 				goto error;
1351 			}
1352 			JANUS_LOG(LOG_VERB, "%s is accepting a call from %s\n", session->username, peer->username);
1353 			JANUS_LOG(LOG_VERB, "This is involving a negotiation (%s) as well:\n%s\n", msg_sdp_type, msg_sdp);
1354 			session->has_audio = (strstr(msg_sdp, "m=audio") != NULL);
1355 			session->has_video = (strstr(msg_sdp, "m=video") != NULL);
1356 			session->has_data = (strstr(msg_sdp, "DTLS/SCTP") != NULL);
1357 			/* Check if this user will simulcast */
1358 			json_t *msg_simulcast = json_object_get(msg->jsep, "simulcast");
1359 			if(msg_simulcast && janus_get_codec_pt(msg_sdp, "vp8") > 0) {
1360 				JANUS_LOG(LOG_VERB, "VideoCall callee (%s) cannot do simulcast.\n", session->username);
1361 			} else {
1362 				int i=0;
1363 				for(i=0; i<3; i++) {
1364 					session->ssrc[i] = 0;
1365 					g_free(session->rid[0]);
1366 					session->rid[0] = NULL;
1367 				}
1368 			}
1369 			/* Check which codecs we ended up using */
1370 			const char *acodec = NULL, *vcodec = NULL;
1371 			janus_sdp_find_first_codecs(answer, &acodec, &vcodec);
1372 			session->acodec = janus_audiocodec_from_name(acodec);
1373 			session->vcodec = janus_videocodec_from_name(vcodec);
1374 			if(session->acodec == JANUS_AUDIOCODEC_NONE) {
1375 				session->has_audio = FALSE;
1376 				if(peer)
1377 					peer->has_audio = FALSE;
1378 			} else if(peer) {
1379 				peer->acodec = session->acodec;
1380 			}
1381 			if(session->vcodec == JANUS_VIDEOCODEC_NONE) {
1382 				session->has_video = FALSE;
1383 				if(peer)
1384 					peer->has_video = FALSE;
1385 			} else if(peer) {
1386 				peer->vcodec = session->vcodec;
1387 			}
1388 			janus_sdp_destroy(answer);
1389 			/* Send SDP to our peer */
1390 			json_t *jsep = json_pack("{ssss}", "type", msg_sdp_type, "sdp", msg_sdp);
1391 			if(session->e2ee)
1392 				json_object_set_new(jsep, "e2ee", json_true());
1393 			json_t *call = json_object();
1394 			json_object_set_new(call, "videocall", json_string("event"));
1395 			json_t *calling = json_object();
1396 			json_object_set_new(calling, "event", json_string("accepted"));
1397 			json_object_set_new(calling, "username", json_string(session->username));
1398 			json_object_set_new(call, "result", calling);
1399 			g_atomic_int_set(&session->hangingup, 0);
1400 			int ret = gateway->push_event(peer->handle, &janus_videocall_plugin, NULL, call, jsep);
1401 			JANUS_LOG(LOG_VERB, "  >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
1402 			json_decref(call);
1403 			json_decref(jsep);
1404 			/* Send an ack back */
1405 			result = json_object();
1406 			json_object_set_new(result, "event", json_string("accepted"));
1407 			/* Also notify event handlers */
1408 			if(notify_events && gateway->events_is_enabled()) {
1409 				json_t *info = json_object();
1410 				json_object_set_new(info, "event", json_string("accepted"));
1411 				gateway->notify_event(&janus_videocall_plugin, session->handle, info);
1412 			}
1413 			/* Is simulcasting involved on either side? */
1414 			if(session->ssrc[0] || session->rid[0]) {
1415 				peer->sim_context.substream_target = 2;	/* Let's aim for the highest quality */
1416 				peer->sim_context.templayer_target = 2;	/* Let's aim for all temporal layers */
1417 			}
1418 			if(peer->ssrc[0] || peer->rid[0]) {
1419 				session->sim_context.substream_target = 2;	/* Let's aim for the highest quality */
1420 				session->sim_context.templayer_target = 2;	/* Let's aim for all temporal layers */
1421 			}
1422 			/* We don't need this reference anymore, it was already increased by the peer calling us */
1423 			janus_refcount_decrease(&peer->ref);
1424 		} else if(!strcasecmp(request_text, "set")) {
1425 			/* Update the local configuration (audio/video mute/unmute, bitrate cap or recording) */
1426 			JANUS_VALIDATE_JSON_OBJECT(root, set_parameters,
1427 				error_code, error_cause, TRUE,
1428 				JANUS_VIDEOCALL_ERROR_MISSING_ELEMENT, JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT);
1429 			if(error_code != 0)
1430 				goto error;
1431 			json_t *audio = json_object_get(root, "audio");
1432 			json_t *video = json_object_get(root, "video");
1433 			json_t *bitrate = json_object_get(root, "bitrate");
1434 			json_t *record = json_object_get(root, "record");
1435 			json_t *recfile = json_object_get(root, "filename");
1436 			json_t *restart = json_object_get(root, "restart");
1437 			json_t *substream = json_object_get(root, "substream");
1438 			if(substream && (!json_is_integer(substream) || json_integer_value(substream) < 0 || json_integer_value(substream) > 2)) {
1439 				JANUS_LOG(LOG_ERR, "Invalid element (substream should be 0, 1 or 2)\n");
1440 				error_code = JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT;
1441 				g_snprintf(error_cause, 512, "Invalid value (substream should be 0, 1 or 2)");
1442 				goto error;
1443 			}
1444 			json_t *temporal = json_object_get(root, "temporal");
1445 			if(temporal && (!json_is_integer(temporal) || json_integer_value(temporal) < 0 || json_integer_value(temporal) > 2)) {
1446 				JANUS_LOG(LOG_ERR, "Invalid element (temporal should be 0, 1 or 2)\n");
1447 				error_code = JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT;
1448 				g_snprintf(error_cause, 512, "Invalid value (temporal should be 0, 1 or 2)");
1449 				goto error;
1450 			}
1451 			json_t *fallback = json_object_get(root, "fallback");
1452 			if(fallback && (!json_is_integer(fallback) || json_integer_value(fallback) < 0)) {
1453 				JANUS_LOG(LOG_ERR, "Invalid element (fallback should be a positive integer)\n");
1454 				error_code = JANUS_VIDEOCALL_ERROR_INVALID_ELEMENT;
1455 				g_snprintf(error_cause, 512, "Invalid value (fallback should be a positive integer)");
1456 				goto error;
1457 			}
1458 			if(audio) {
1459 				session->audio_active = json_is_true(audio);
1460 				JANUS_LOG(LOG_VERB, "Setting audio property: %s\n", session->audio_active ? "true" : "false");
1461 			}
1462 			if(video) {
1463 				if(!session->video_active && json_is_true(video)) {
1464 					/* Send a PLI */
1465 					JANUS_LOG(LOG_VERB, "Just (re-)enabled video, sending a PLI to recover it\n");
1466 					gateway->send_pli(session->handle);
1467 				}
1468 				session->video_active = json_is_true(video);
1469 				JANUS_LOG(LOG_VERB, "Setting video property: %s\n", session->video_active ? "true" : "false");
1470 			}
1471 			if(bitrate) {
1472 				session->bitrate = json_integer_value(bitrate);
1473 				JANUS_LOG(LOG_VERB, "Setting video bitrate: %"SCNu32"\n", session->bitrate);
1474 				gateway->send_remb(session->handle, session->bitrate ? session->bitrate : 10000000);
1475 			}
1476 			janus_videocall_session *peer = session->peer;
1477 			if(fallback) {
1478 				JANUS_LOG(LOG_VERB, "Setting fallback timer (simulcast): %lld (was %"SCNu32")\n",
1479 					json_integer_value(fallback) ? json_integer_value(fallback) : 250000,
1480 					session->sim_context.drop_trigger ? session->sim_context.drop_trigger : 250000);
1481 				session->sim_context.drop_trigger = json_integer_value(fallback);
1482 			}
1483 			if(substream) {
1484 				session->sim_context.substream_target = json_integer_value(substream);
1485 				if(session->sim_context.substream_target >= 0 && session->sim_context.substream_target <= 2) {
1486 					JANUS_LOG(LOG_VERB, "Setting video SSRC to let through (simulcast): %"SCNu32" (index %d, was %d)\n",
1487 						session->ssrc[session->sim_context.substream_target], session->sim_context.substream_target, session->sim_context.substream);
1488 				}
1489 				if(session->sim_context.substream_target == session->sim_context.substream) {
1490 					/* No need to do anything, we're already getting the right substream, so notify the user */
1491 					json_t *event = json_object();
1492 					json_object_set_new(event, "videocall", json_string("event"));
1493 					json_t *result = json_object();
1494 					json_object_set_new(result, "event", json_string("simulcast"));
1495 					json_object_set_new(result, "videocodec", json_string(janus_videocodec_name(session->vcodec)));
1496 					json_object_set_new(result, "substream", json_integer(session->sim_context.substream));
1497 					json_object_set_new(event, "result", result);
1498 					gateway->push_event(session->handle, &janus_videocall_plugin, NULL, event, NULL);
1499 					json_decref(event);
1500 				} else {
1501 					/* We need to change substream, send the peer a PLI */
1502 					JANUS_LOG(LOG_VERB, "Simulcasting substream change, sending a PLI to kickstart it\n");
1503 					if(peer && peer->handle)
1504 						gateway->send_pli(peer->handle);
1505 				}
1506 			}
1507 			if(temporal) {
1508 				session->sim_context.templayer_target = json_integer_value(temporal);
1509 				JANUS_LOG(LOG_VERB, "Setting video temporal layer to let through (simulcast): %d (was %d)\n",
1510 					session->sim_context.templayer_target, session->sim_context.templayer);
1511 				if(session->vcodec == JANUS_VIDEOCODEC_VP8 && session->sim_context.templayer_target == session->sim_context.templayer) {
1512 					/* No need to do anything, we're already getting the right temporal, so notify the user */
1513 					json_t *event = json_object();
1514 					json_object_set_new(event, "videocall", json_string("event"));
1515 					json_t *result = json_object();
1516 					json_object_set_new(result, "event", json_string("simulcast"));
1517 					json_object_set_new(result, "videocodec", json_string(janus_videocodec_name(session->vcodec)));
1518 					json_object_set_new(result, "temporal", json_integer(session->sim_context.templayer));
1519 					json_object_set_new(event, "result", result);
1520 					gateway->push_event(session->handle, &janus_videocall_plugin, NULL, event, NULL);
1521 					json_decref(event);
1522 				} else {
1523 					/* We need to change temporal, send a PLI */
1524 					JANUS_LOG(LOG_VERB, "Simulcasting temporal layer change, sending a PLI to kickstart it\n");
1525 					if(peer && peer->handle)
1526 						gateway->send_pli(peer->handle);
1527 				}
1528 			}
1529 			if(record) {
1530 				if(msg_sdp) {
1531 					session->has_audio = (strstr(msg_sdp, "m=audio") != NULL);
1532 					session->has_video = (strstr(msg_sdp, "m=video") != NULL);
1533 					session->has_data = (strstr(msg_sdp, "DTLS/SCTP") != NULL);
1534 				}
1535 				gboolean recording = json_is_true(record);
1536 				const char *recording_base = json_string_value(recfile);
1537 				JANUS_LOG(LOG_VERB, "Recording %s (base filename: %s)\n", recording ? "enabled" : "disabled", recording_base ? recording_base : "not provided");
1538 				janus_mutex_lock(&session->rec_mutex);
1539 				if(!recording) {
1540 					/* Not recording (anymore?) */
1541 					janus_videocall_recorder_close(session);
1542 				} else {
1543 					/* We've started recording, send a PLI and go on */
1544 					char filename[255];
1545 					gint64 now = janus_get_real_time();
1546 					if(session->has_audio) {
1547 						/* Prepare an audio recording */
1548 						janus_recorder *rc = NULL;
1549 						memset(filename, 0, 255);
1550 						if(recording_base) {
1551 							/* Use the filename and path we have been provided */
1552 							g_snprintf(filename, 255, "%s-audio", recording_base);
1553 							rc = janus_recorder_create(NULL, janus_audiocodec_name(session->acodec), filename);
1554 							if(rc == NULL) {
1555 								/* FIXME We should notify the fact the recorder could not be created */
1556 								JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this VideoCall user!\n");
1557 							}
1558 						} else {
1559 							/* Build a filename */
1560 							g_snprintf(filename, 255, "videocall-%s-%s-%"SCNi64"-audio",
1561 								session->username ? session->username : "unknown",
1562 								(peer && peer->username) ? peer->username : "unknown",
1563 								now);
1564 							rc = janus_recorder_create(NULL, janus_audiocodec_name(session->acodec), filename);
1565 							if(rc == NULL) {
1566 								/* FIXME We should notify the fact the recorder could not be created */
1567 								JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this VideoCall user!\n");
1568 							}
1569 						}
1570 						/* If media is encrypted, mark it in the recording */
1571 						if(session->e2ee)
1572 							janus_recorder_encrypted(rc);
1573 						session->arc = rc;
1574 					}
1575 					if(session->has_video) {
1576 						/* Prepare a video recording */
1577 						janus_recorder *rc = NULL;
1578 						memset(filename, 0, 255);
1579 						if(recording_base) {
1580 							/* Use the filename and path we have been provided */
1581 							g_snprintf(filename, 255, "%s-video", recording_base);
1582 							rc = janus_recorder_create(NULL, janus_videocodec_name(session->vcodec), filename);
1583 							if(rc == NULL) {
1584 								/* FIXME We should notify the fact the recorder could not be created */
1585 								JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this VideoCall user!\n");
1586 							}
1587 						} else {
1588 							/* Build a filename */
1589 							g_snprintf(filename, 255, "videocall-%s-%s-%"SCNi64"-video",
1590 								session->username ? session->username : "unknown",
1591 								(peer && peer->username) ? peer->username : "unknown",
1592 								now);
1593 							rc = janus_recorder_create(NULL, janus_videocodec_name(session->vcodec), filename);
1594 							if(rc == NULL) {
1595 								/* FIXME We should notify the fact the recorder could not be created */
1596 								JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this VideoCall user!\n");
1597 							}
1598 						}
1599 						/* Send a PLI */
1600 						JANUS_LOG(LOG_VERB, "Recording video, sending a PLI to kickstart it\n");
1601 						gateway->send_pli(session->handle);
1602 						/* If media is encrypted, mark it in the recording */
1603 						if(session->e2ee)
1604 							janus_recorder_encrypted(rc);
1605 						session->vrc = rc;
1606 					}
1607 					if(session->has_data) {
1608 						/* Prepare a data recording */
1609 						janus_recorder *rc = NULL;
1610 						memset(filename, 0, 255);
1611 						if(recording_base) {
1612 							/* Use the filename and path we have been provided */
1613 							g_snprintf(filename, 255, "%s-data", recording_base);
1614 							rc = janus_recorder_create(NULL, "text", filename);
1615 							if(rc == NULL) {
1616 								/* FIXME We should notify the fact the recorder could not be created */
1617 								JANUS_LOG(LOG_ERR, "Couldn't open a data recording file for this VideoCall user!\n");
1618 							}
1619 						} else {
1620 							/* Build a filename */
1621 							g_snprintf(filename, 255, "videocall-%s-%s-%"SCNi64"-data",
1622 								session->username ? session->username : "unknown",
1623 								(peer && peer->username) ? peer->username : "unknown",
1624 								now);
1625 							rc = janus_recorder_create(NULL, "text", filename);
1626 							if(rc == NULL) {
1627 								/* FIXME We should notify the fact the recorder could not be created */
1628 								JANUS_LOG(LOG_ERR, "Couldn't open a data recording file for this VideoCall user!\n");
1629 							}
1630 						}
1631 						/* Media encryption doesn't apply to data channels */
1632 						session->drc = rc;
1633 					}
1634 				}
1635 				janus_mutex_unlock(&session->rec_mutex);
1636 			}
1637 			/* Also notify event handlers */
1638 			if(notify_events && gateway->events_is_enabled()) {
1639 				json_t *info = json_object();
1640 				json_object_set_new(info, "event", json_string("configured"));
1641 				json_object_set_new(info, "audio_active", session->audio_active ? json_true() : json_false());
1642 				json_object_set_new(info, "video_active", session->video_active ? json_true() : json_false());
1643 				json_object_set_new(info, "bitrate", json_integer(session->bitrate));
1644 				if(session->arc || session->vrc || session->drc) {
1645 					json_t *recording = json_object();
1646 					if(session->arc && session->arc->filename)
1647 						json_object_set_new(recording, "audio", json_string(session->arc->filename));
1648 					if(session->vrc && session->vrc->filename)
1649 						json_object_set_new(recording, "video", json_string(session->vrc->filename));
1650 					if(session->drc && session->drc->filename)
1651 						json_object_set_new(recording, "data", json_string(session->drc->filename));
1652 					json_object_set_new(info, "recording", recording);
1653 				}
1654 				gateway->notify_event(&janus_videocall_plugin, session->handle, info);
1655 			}
1656 			/* Send an ack back */
1657 			result = json_object();
1658 			json_object_set_new(result, "event", json_string("set"));
1659 			/* If this is for an ICE restart, prepare the SDP to send back too */
1660 			gboolean do_restart = restart ? json_is_true(restart) : FALSE;
1661 			if(do_restart && !sdp_update) {
1662 				JANUS_LOG(LOG_WARN, "Got a 'restart' request, but no SDP update? Ignoring...\n");
1663 			}
1664 			if(sdp_update && peer != NULL) {
1665 				/* Forward new SDP to the peer */
1666 				json_t *event = json_object();
1667 				json_object_set_new(event, "videocall", json_string("event"));
1668 				json_t *update = json_object();
1669 				json_object_set_new(update, "event", json_string("update"));
1670 				json_object_set_new(event, "result", update);
1671 				json_t *jsep = json_pack("{ssss}", "type", msg_sdp_type, "sdp", msg_sdp);
1672 				int ret = gateway->push_event(peer->handle, &janus_videocall_plugin, NULL, event, jsep);
1673 				JANUS_LOG(LOG_VERB, "  >> Pushing event: %d (%s)\n", ret, janus_get_api_error(ret));
1674 				json_decref(event);
1675 				json_decref(jsep);
1676 			}
1677 		} else if(!strcasecmp(request_text, "hangup")) {
1678 			json_t *hangup = json_object_get(root, "reason");
1679 			if(hangup && !json_is_string(hangup)) {
1680 				JANUS_LOG(LOG_WARN, "Invalid element (hangup should be a string), ignoring\n");
1681 				hangup = NULL;
1682 			}
1683 			const char *hangup_text = hangup ? json_string_value(hangup) : "We did the hangup";
1684 			/* Hangup an ongoing call or reject an incoming one */
1685 			janus_videocall_session *peer = session->peer;
1686 			if(peer == NULL) {
1687 				JANUS_LOG(LOG_WARN, "No call to hangup\n");
1688 			} else {
1689 				JANUS_LOG(LOG_VERB, "%s is hanging up the call with %s (%s)\n", session->username, peer->username, hangup_text);
1690 			}
1691 			/* Check if we still need to remove any reference */
1692 			if(peer && g_atomic_int_compare_and_exchange(&peer->incall, 1, 0)) {
1693 				janus_refcount_decrease(&session->ref);
1694 			}
1695 			if(g_atomic_int_compare_and_exchange(&session->incall, 1, 0) && peer) {
1696 				janus_refcount_decrease(&peer->ref);
1697 			}
1698 			/* Notify the success as an hangup message */
1699 			result = json_object();
1700 			json_object_set_new(result, "event", json_string("hangup"));
1701 			json_object_set_new(result, "username", json_string(session->username));
1702 			json_object_set_new(result, "reason", json_string(hangup_text));
1703 			json_object_set_new(result, "reason", json_string("Explicit hangup"));
1704 			/* Also notify event handlers */
1705 			if(notify_events && gateway->events_is_enabled()) {
1706 				json_t *info = json_object();
1707 				json_object_set_new(info, "event", json_string("hangup"));
1708 				json_object_set_new(info, "reason", json_string("Explicit hangup"));
1709 				gateway->notify_event(&janus_videocall_plugin, session->handle, info);
1710 			}
1711 			/* Hangup the call on the user, if it's still up */
1712 			gateway->close_pc(session->handle);
1713 			if(peer != NULL) {
1714 				/* Send event to our peer too */
1715 				json_t *call = json_object();
1716 				json_object_set_new(call, "videocall", json_string("event"));
1717 				json_t *calling = json_object();
1718 				json_object_set_new(calling, "event", json_string("hangup"));
1719 				json_object_set_new(calling, "username", json_string(session->username));
1720 				json_object_set_new(calling, "reason", json_string(hangup_text));
1721 				json_object_set_new(call, "result", calling);
1722 				gateway->close_pc(peer->handle);
1723 				int ret = gateway->push_event(peer->handle, &janus_videocall_plugin, NULL, call, NULL);
1724 				JANUS_LOG(LOG_VERB, "  >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
1725 				json_decref(call);
1726 				/* Also notify event handlers */
1727 				if(notify_events && gateway->events_is_enabled()) {
1728 					json_t *info = json_object();
1729 					json_object_set_new(info, "event", json_string("hangup"));
1730 					json_object_set_new(info, "reason", json_string("Remote hangup"));
1731 					gateway->notify_event(&janus_videocall_plugin, peer->handle, info);
1732 				}
1733 				/* Hangup the call on the peer, if it's still up */
1734 				gateway->close_pc(peer->handle);
1735 			}
1736 		} else {
1737 			JANUS_LOG(LOG_ERR, "Unknown request (%s)\n", request_text);
1738 			error_code = JANUS_VIDEOCALL_ERROR_INVALID_REQUEST;
1739 			g_snprintf(error_cause, 512, "Unknown request (%s)", request_text);
1740 			goto error;
1741 		}
1742 
1743 		/* Prepare JSON event */
1744 		json_t *event = json_object();
1745 		json_object_set_new(event, "videocall", json_string("event"));
1746 		if(result != NULL)
1747 			json_object_set_new(event, "result", result);
1748 		int ret = gateway->push_event(msg->handle, &janus_videocall_plugin, msg->transaction, event, NULL);
1749 		JANUS_LOG(LOG_VERB, "  >> Pushing event: %d (%s)\n", ret, janus_get_api_error(ret));
1750 		json_decref(event);
1751 		janus_videocall_message_free(msg);
1752 		continue;
1753 
1754 error:
1755 		{
1756 			/* Prepare JSON error event */
1757 			json_t *event = json_object();
1758 			json_object_set_new(event, "videocall", json_string("event"));
1759 			json_object_set_new(event, "error_code", json_integer(error_code));
1760 			json_object_set_new(event, "error", json_string(error_cause));
1761 			int ret = gateway->push_event(msg->handle, &janus_videocall_plugin, msg->transaction, event, NULL);
1762 			JANUS_LOG(LOG_VERB, "  >> Pushing event: %d (%s)\n", ret, janus_get_api_error(ret));
1763 			json_decref(event);
1764 			janus_videocall_message_free(msg);
1765 		}
1766 	}
1767 	JANUS_LOG(LOG_VERB, "Leaving VideoCall handler thread\n");
1768 	return NULL;
1769 }
1770