Statistics
| Branch: | Revision:

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

History | View | Annotate | Download (153 KB)

1 be35facb meetecho
/*! \file   janus_audiobridge.c
2
 * \author Lorenzo Miniero <lorenzo@meetecho.com>
3 9d11ac50 meetecho
 * \copyright GNU General Public License v3
4 be35facb meetecho
 * \brief  Janus AudioBridge plugin
5
 * \details  This is a plugin implementing an audio conference bridge for
6
 * Janus, specifically mixing Opus streams. This means that it replies
7
 * by providing in the SDP only support for Opus, and disabling video.
8
 * Opus encoding and decoding is implemented using libopus (http://opus.codec.org).
9
 * The plugin provides an API to allow peers to join and leave conference
10
 * rooms. Peers can then mute/unmute themselves by sending specific messages
11
 * to the plugin: any way a peer mutes/unmutes, an event is triggered
12
 * to the other participants, so that it can be rendered in the UI
13
 * accordingly.
14
 * 
15
 * Rooms to make available are listed in the plugin configuration file.
16
 * A pre-filled configuration file is provided in \c conf/janus.plugin.audiobridge.cfg
17
 * and includes a demo room for testing.
18
 * 
19
 * To add more rooms or modify the existing one, you can use the following
20
 * syntax:
21
 * 
22
 * \verbatim
23
[<unique room ID>]
24
description = This is my awesome room
25 0a466e28 janus
is_private = yes|no (private rooms don't appear when you do a 'list' request)
26 5d20bcec Lorenzo Miniero
secret = <optional password needed for manipulating (e.g. destroying) the room>
27
pin = <optional password needed for joining the room>
28 be35facb meetecho
sampling_rate = <sampling rate> (e.g., 16000 for wideband mixing)
29 4fb1f6d2 Lorenzo Miniero
audiolevel_ext = yes|no (whether the ssrc-audio-level RTP extension must be
30
        negotiated/used or not for new joins, default=yes)
31 6dd1c12c meetecho
record = true|false (whether this room should be recorded, default=false)
32 bf24339c meetecho
record_file =        /path/to/recording.wav (where to save the recording)
33 be35facb meetecho
\endverbatim
34
 *
35 02fac6b4 meetecho
 * \section bridgeapi Audio Bridge API
36
 * 
37
 * The Audio Bridge API supports several requests, some of which are
38
 * synchronous and some asynchronous. There are some situations, though,
39
 * (invalid JSON, invalid request) which will always result in a
40
 * synchronous error response even for asynchronous requests. 
41
 * 
42 30df7fbf Lorenzo Miniero
 * \c create , \c destroy , \c exists, \c allowed, \c kick, \c list,
43
 * \c listparticipants and \c resetdecoder are synchronous requests,
44
 * which means you'll get a response directly within the context of the
45
 * transaction. \c create allows you to create a new audio conference bridge
46 02fac6b4 meetecho
 * dynamically, as an alternative to using the configuration file;
47
 * \c destroy removes an audio conference bridge and destroys it, kicking
48
 * all the users out as part of the process; \c exists allows you to
49 30df7fbf Lorenzo Miniero
 * check whether a specific audio conference exists; \c allowed allows
50
 * you to edit who's allowed to join a room via ad-hoc tokens; \c list
51 02fac6b4 meetecho
 * lists all the available rooms, while \c listparticipants lists all
52 bf438412 meetecho
 * the participants of a specific room and their details; finally,
53
 * \c resetdecoder marks the Opus decoder for the participant as invalid,
54
 * and forces it to be recreated (which might be needed if the audio
55
 * for generated by the participant becomes garbled). 
56 02fac6b4 meetecho
 * 
57
 * The \c join , \c configure , \c changeroom and \c leave requests
58
 * instead are all asynchronous, which means you'll get a notification
59
 * about their success or failure in an event. \c join allows you to
60
 * join a specific audio conference bridge; \c configure can be used
61
 * to modify some of the participation settings (e.g., mute/unmute);
62
 * \c changeroom can be used to leave the current room and move to a
63
 * different one without having to tear down the PeerConnection and
64
 * recreate it again (useful for sidebars and "waiting rooms"); finally,
65
 * \c leave allows you to leave an audio conference bridge for good.
66
 * 
67 3760d352 Lorenzo Miniero
 * The AudioBridge plugin also allows you to forward the mix to an
68
 * external listener, e.g., a gstreamer/ffmpeg pipeline waiting to
69
 * process the mixer audio stream. You can add new RTP forwarders with
70
 * the \c rtp_forward request; a \c stop_rtp_forward request removes an
71
 * existing RTP forwarder; \c listforwarders lists all the current RTP
72
 * forwarders on a specific AudioBridge room instance.
73
 *
74 16c5579a meetecho
 * \c create can be used to create a new audio room, and has to be
75
 * formatted as follows:
76 02fac6b4 meetecho
 * 
77 16c5579a meetecho
\verbatim
78
{
79
        "request" : "create",
80
        "room" : <unique numeric ID, optional, chosen by plugin if missing>,
81 2512456f Lorenzo Miniero
        "permanent" : <true|false, whether the room should be saved in the config file, default false>,
82 16c5579a meetecho
        "description" : "<pretty name of the room, optional>",
83
        "secret" : "<password required to edit/destroy the room, optional>",
84 5d20bcec Lorenzo Miniero
        "pin" : "<password required to join the room, optional>",
85 16c5579a meetecho
        "is_private" : <true|false, whether the room should appear in a list request>,
86 30df7fbf Lorenzo Miniero
        "allowed" : [ array of string tokens users can use to join this room, optional],
87 16c5579a meetecho
        "sampling" : <sampling rate of the room, optional, 16000 by default>,
88 4fb1f6d2 Lorenzo Miniero
        "audiolevel_ext" : <true|false, whether the ssrc-audio-level RTP extension must be negotiated for new joins, default true>,
89 16c5579a meetecho
        "record" : <true|false, whether to record the room or not, default false>,
90
        "record_file" : "</path/to/the/recording.wav, optional>",
91
}
92
\endverbatim
93
 *
94
 * A successful creation procedure will result in a \c created response:
95
 * 
96
\verbatim
97
{
98
        "audiobridge" : "created",
99 b6c6c592 Lorenzo Miniero
        "room" : <unique numeric ID>,
100
        "permanent" : <true if saved to config file, false if not>
101 16c5579a meetecho
}
102
\endverbatim
103
 *
104 b6c6c592 Lorenzo Miniero
 * If you requested a permanent room but a \c false value is returned
105
 * instead, good chances are that there are permission problems.
106
 *
107 16c5579a meetecho
 * An error instead (and the same applies to all other requests, so this
108
 * won't be repeated) would provide both an error code and a more verbose
109
 * description of the cause of the issue:
110
 * 
111
\verbatim
112
{
113
        "audiobridge" : "event",
114
        "error_code" : <numeric ID, check Macros below>,
115
        "error" : "<error description as a string>"
116
}
117
\endverbatim
118
 * 
119 930a4a4c Lorenzo Miniero
 * Notice that, in general, all users can create rooms. If you want to
120
 * limit this functionality, you can configure an admin \c admin_key in
121
 * the plugin settings. When configured, only "create" requests that
122
 * include the correct \c admin_key value in an "admin_key" property
123
 * will succeed, and will be rejected otherwise.
124
 * 
125 16c5579a meetecho
 * On the other hand, \c destroy can be used to destroy an existing audio
126
 * room, whether created dynamically or statically, and has to be
127
 * formatted as follows:
128
 * 
129
\verbatim
130
{
131
        "request" : "destroy",
132
        "room" : <unique numeric ID of the room to destroy>,
133 2512456f Lorenzo Miniero
        "secret" : "<room secret, mandatory if configured>",
134
        "permanent" : <true|false, whether the room should be also removed from the config file, default false>
135 16c5579a meetecho
}
136
\endverbatim
137
 *
138
 * A successful destruction procedure will result in a \c destroyed response:
139
 * 
140
\verbatim
141
{
142
        "audiobridge" : "created",
143
        "room" : <unique numeric ID>
144
}
145
\endverbatim
146
 *
147
 * You can check whether a room exists using the \c exists request,
148
 * which has to be formatted as follows:
149
 * 
150
\verbatim
151
{
152
        "request" : "exists",
153
        "room" : <unique numeric ID of the room to check>
154
}
155
\endverbatim
156
 *
157
 * A successful request will result in a \c success response:
158
 * 
159
\verbatim
160
{
161
        "audiobridge" : "success",
162
        "room" : <unique numeric ID>,
163
        "exists" : <true|false>
164
}
165
\endverbatim
166
 * 
167 30df7fbf Lorenzo Miniero
 * You can configure whether to check tokens or add/remove people who can join
168
 * a room using the \c allowed request, which has to be formatted as follows:
169
 *
170
\verbatim
171
{
172
        "request" : "allowed",
173
        "secret" : "<room secret, mandatory if configured>",
174
        "action" : "enable|disable|add|remove",
175
        "room" : <unique numeric ID of the room to update>,
176
        "allowed" : [
177
                // Array of strings (tokens users might pass in "join", only for add|remove)
178
        ]
179
}
180
\endverbatim
181
 *
182
 * A successful request will result in a \c success response:
183
 *
184
\verbatim
185
{
186
        "audiobridge" : "success",
187
        "room" : <unique numeric ID>,
188
        "allowed" : [
189
                // Updated, complete, list of allowed tokens (only for enable|add|remove)
190
        ]
191
}
192
\endverbatim
193
 *
194
 * If you're the administrator of a room (that is, you created it and have access
195
 * to the secret) you can kick participants using the \c kick request. Notice
196
 * that this only kicks the user out of the room, but does not prevent them from
197
 * re-joining: to ban them, you need to first remove them from the list of
198
 * authorized users (see \c allowed request) and then \c kick them. The \c kick
199
 * request has to be formatted as follows:
200
 *
201
\verbatim
202
{
203
        "request" : "kick",
204
        "secret" : "<room secret, mandatory if configured>",
205
        "room" : <unique numeric ID of the room>,
206
        "id" : <unique numeric ID of the participant to kick>
207
}
208
\endverbatim
209
 *
210
 * A successful request will result in a \c success response:
211
 *
212
\verbatim
213
{
214
        "audiobridge" : "success",
215
}
216
\endverbatim
217
 *
218 16c5579a meetecho
 * To get a list of the available rooms (excluded those configured or
219
 * created as private rooms) you can make use of the \c list request,
220
 * which has to be formatted as follows:
221
 * 
222
\verbatim
223
{
224
        "request" : "list"
225
}
226
\endverbatim
227
 *
228
 * A successful request will produce a list of rooms in a \c success response:
229
 * 
230
\verbatim
231
{
232
        "audiobridge" : "success",
233
        "rooms" : [                // Array of room objects
234
                {        // Room #1
235
                        "room" : <unique numeric ID>,
236
                        "description" : "<Name of the room>",
237
                        "sampling_rate" : <sampling rate of the mixer>,
238
                        "record" : <true|false, whether the room is being recorded>,
239
                        "num_participants" : <count of the participants>
240
                },
241
                // Other rooms
242
        ]
243
}
244
\endverbatim
245
 *
246
 * To get a list of the participants in a specific room, instead, you
247
 * can make use of the \c listparticipants request, which has to be
248
 * formatted as follows:
249
 * 
250
\verbatim
251
{
252
        "request" : "listparticipants",
253
        "room" : <unique numeric ID of the room>
254
}
255
\endverbatim
256
 *
257
 * A successful request will produce a list of participants in a
258
 * \c participants response:
259
 * 
260
\verbatim
261
{
262
        "audiobridge" : "participants",
263
        "room" : <unique numeric ID of the room>,
264
        "participants" : [                // Array of participant objects
265
                {        // Participant #1
266
                        "id" : <unique numeric ID of the participant>,
267
                        "display" : "<display name of the participant, if any; optional>",
268
                        "muted" : <true|false, whether user is muted or not>
269
                },
270
                // Other participants
271
        ]
272
}
273
\endverbatim
274
 * 
275 bf438412 meetecho
 * To mark the Opus decoder context for the current participant as
276
 * invalid and force it to be recreated, use the \c resetdecoder request:
277
 * 
278
\verbatim
279
{
280
        "request" : "resetdecoder"
281
}
282
\endverbatim
283
 *
284
 * A successful request will produce a \c success response:
285
 * 
286
\verbatim
287
{
288
        "audiobridge" : "success"
289
}
290
\endverbatim
291
 * 
292 3760d352 Lorenzo Miniero
 * You can add a new RTP forwarder for an existing room exists using the
293
 * \c rtp_forward request, which has to be formatted as follows:
294
 *
295
\verbatim
296
{
297
        "request" : "rtp_forward",
298
        "room" : <unique numeric ID of the room to add the forwarder to>,
299 e75a8bd6 Lorenzo Miniero
        "ssrc" : <SSRC to use to use when streaming (optional: stream_id used if missing)>,
300
        "ptype" : <payload type to use when streaming (optional: 100 used if missing)>,
301 3760d352 Lorenzo Miniero
        "host" : "<host address to forward the RTP packets to>",
302 79e0e1e2 Lorenzo Miniero
        "port" : <port to forward the RTP packets to>,
303
        "always_on" : <true|false, whether silence should be forwarded when the room is empty>
304 3760d352 Lorenzo Miniero
}
305
\endverbatim
306
 *
307
 * A successful request will result in a \c success response:
308
 *
309
\verbatim
310
{
311
        "audiobridge" : "success",
312
        "room" : <unique numeric ID, same as request>,
313
        "stream_id" : <unique numeric ID assigned to the new RTP forwarder>
314
}
315
\endverbatim
316
 *
317
 * To stop a previously created RTP forwarder and stop it, you can use
318
 * the \c stop_rtp_forward request, which has to be formatted as follows:
319
 *
320
\verbatim
321
{
322
        "request" : "stop_rtp_forward",
323
        "room" : <unique numeric ID of the room to remove the forwarder from>,
324
        "stream_id" : <unique numeric ID of the RTP forwarder>
325
}
326
\endverbatim
327
 *
328
 * A successful request will result in a \c success response:
329
 *
330
\verbatim
331
{
332
        "audiobridge" : "success",
333
        "room" : <unique numeric ID, same as request>,
334
        "stream_id" : <unique numeric ID, same as request>
335
}
336
\endverbatim
337
 *
338
 * To get a list of the forwarders in a specific room, instead, you
339
 * can make use of the \c listforwarders request, which has to be
340
 * formatted as follows:
341
 *
342
\verbatim
343
{
344
        "request" : "listforwarders",
345
        "room" : <unique numeric ID of the room>
346
}
347
\endverbatim
348
 *
349
 * A successful request will produce a list of RTP forwarders in a
350
 * \c forwarders response:
351
 *
352
\verbatim
353
{
354
        "audiobridge" : "forwarders",
355
        "room" : <unique numeric ID of the room>,
356
        "rtp_forwarders" : [                // Array of RTP forwarder objects
357
                {        // RTP forwarder #1
358
                        "stream_id" : <unique numeric ID of the forwarder>,
359
                        "ip" : "<IP this forwarder is streaming to>",
360
                        "port" : <port this forwarder is streaming to>,
361 e75a8bd6 Lorenzo Miniero
                        "ssrc" : <SSRC this forwarder is using, if any>,
362 0284f49f Lorenzo Miniero
                        "ptype" : <payload type this forwarder is using, if any>
363 3760d352 Lorenzo Miniero
                },
364
                // Other forwarders
365
        ]
366
}
367 d53553a9 S├ębastien Saint-Sevin
\endverbatim
368 3760d352 Lorenzo Miniero
 *
369 16c5579a meetecho
 * That completes the list of synchronous requests you can send to the
370
 * AudioBridge plugin. As anticipated, though, there are also several
371
 * asynchronous requests you can send, specifically those related to
372
 * joining and updating one's presence as a participant in an audio room.
373
 * 
374
 * The way you'd interact with the plugin is usually as follows:
375
 * 
376
 * -# you use a \c join request to join an audio room, and wait for the
377
 * \c joined event; this event will also include a list of the other
378
 * participants, if any;
379
 * -# you send a \c configure request attached to an audio-only JSEP offer
380
 * to start configuring your participation in the room (e.g., join unmuted
381
 * or muted), and wait for a \c configured event, which will be attached
382
 * to a JSEP answer by the plugin to complete the setup of the WebRTC
383
 * PeerConnection;
384
 * -# you send other \c configure requests (without any JSEP-related
385
 * attachment) to mute/unmute yourself during the audio conference;
386
 * -# you intercept events originated by the plugin (\c joined , \c leaving )
387
 * to notify you about users joining/leaving/muting/unmuting;
388
 * -# you eventually send a \c leave request to leave a room; if you leave the
389
 * PeerConnection instance intact, you can subsequently join a different
390
 * room without requiring a new negotiation (and so just use a \c join + JSEP-less \c configure to join).
391
 * 
392
 * Notice that there's also a \c changeroom request available: you can use
393
 * this request to immediately leave the room you're in and join a different
394
 * one, without requiring you to do a \c leave + \c join + \c configure
395
 * round. Of course remember not to pass any JSEP-related payload when
396
 * doing a \c changeroom as the same pre-existing PeerConnection will be
397
 * re-used for the purpose.
398
 * 
399
 * About the syntax of all the above mentioned requests, \c join has
400
 * to be formatted as follows:
401
 * 
402
\verbatim
403
{
404
        "request" : "join",
405
        "room" : <numeric ID of the room to join>,
406
        "id" : <unique ID to assign to the participant; optional, assigned by the plugin if missing>,
407 5d20bcec Lorenzo Miniero
        "pin" : "<password required to join the room, if any; optional>",
408 16c5579a meetecho
        "display" : "<display name to have in the room; optional>",
409 30df7fbf Lorenzo Miniero
        "token" : "<invitation token, in case the room has an ACL; optional>",
410 16c5579a meetecho
        "muted" : <true|false, whether to start unmuted or muted>,
411 c057fb41 Lorenzo Miniero
        "quality" : <0-10, Opus-related complexity to use, lower is higher quality; optional, default is 4>,
412
        "volume" : <percent value, <100 reduces volume, >100 increases volume; optional, default is 100 (no volume change)>
413 16c5579a meetecho
}
414
\endverbatim
415
 *
416
 * A successful request will produce a \c joined event:
417
 * 
418
\verbatim
419
{
420
        "audiobridge" : "joined",
421
        "room" : <numeric ID of the room>,
422
        "id" : <unique ID assigned to the participant>,
423
        "display" : "<display name of the new participant>",
424
        "participants" : [
425
                // Array of existing participants in the room
426
        ]
427
}
428
\endverbatim
429
 * 
430
 * The other participants in the room will be notified about the new
431
 * participant by means of a different \c joined event, which will only
432
 * include the \c room and the new participant as the only object in
433
 * a \c participants array.
434
 *
435
 * At this point, the media-related settings of the participant can be
436
 * modified by means of a \c configure request. The \c configure request
437
 * has to be formatted as follows:
438
 *
439
\verbatim
440
{
441
        "request" : "configure",
442
        "muted" : <true|false, whether to unmute or mute>,
443
        "quality" : <0-10, Opus-related complexity to use, lower is higher quality; optional, default is 4>,
444 c057fb41 Lorenzo Miniero
        "volume" : <percent value, <100 reduces volume, >100 increases volume; optional, default is 100 (no volume change)>,
445 75d49bbe Lorenzo Miniero
        "record": <true|false, whether to record this user's contribution to a .mjr file (mixer not involved),
446
        "filename": "<basename of the file to record to, -audio.mjr will be added by the plugin>"
447 16c5579a meetecho
}
448
\endverbatim
449
 *
450
 * \c muted instructs the plugin to mute or unmute the participant;
451
 * \c quality changes the complexity of the Opus encoder for the
452 75d49bbe Lorenzo Miniero
 * participant; \c record can be used to record this participant's contribution
453
 * to a Janus .mjr file, and \c filename to provide a basename for the path to
454
 * save the file to (notice that this is different from the recording of a whole
455
 * room: this feature only records the packets this user is sending, and is not
456
 * related to the mixer stuff). A successful request will result in a \c ok event:
457 16c5579a meetecho
 * 
458
\verbatim
459
{
460
        "audiobridge" : "event",
461
        "room" : <numeric ID of the room>,
462
        "result" : "ok"
463
}
464
\endverbatim
465
 *
466
 * In case the \c muted property was modified, the other participants in
467
 * the room will be notified about this by means of a \c event notification,
468
 * which will only include the \c room and the updated participant as the
469
 * only object in a \c participants array.
470
 *
471
 * As anticipated, you can leave an audio room using the \c leave request,
472
 * which has to be formatted as follows:
473
 * 
474
\verbatim
475
{
476
        "request" : "leave"
477
}
478
\endverbatim
479
 * 
480
 * All the participants will receive an \c event notification with the
481
 * ID of the participant who just left:
482
 * 
483
\verbatim
484
{
485
        "audiobridge" : "event",
486
        "room" : <numeric ID of the room>,
487
        "leaving" : <numeric ID of the participant who left>
488
}
489
\endverbatim
490
 *
491
 * For what concerns the \c changeroom request, instead, it's pretty much
492
 * the same as a \c join request and as such has to be formatted as follows:
493
 * 
494
\verbatim
495
{
496
        "request" : "changeroom",
497
        "room" : <numeric ID of the room to move to>,
498
        "id" : <unique ID to assign to the participant; optional, assigned by the plugin if missing>,
499
        "display" : "<display name to have in the room; optional>",
500 30df7fbf Lorenzo Miniero
        "token" : "<invitation token, in case the new room has an ACL; optional>",
501 16c5579a meetecho
        "muted" : <true|false, whether to start unmuted or muted>,
502
        "quality" : <0-10, Opus-related complexity to use, lower is higher quality; optional, default is 4>
503
}
504
\endverbatim
505
 * 
506
 * Such a request will trigger all the above-described leaving/joined
507
 * events to the other participants, as it is indeed wrapping a \c leave
508
 * followed by a \c join and as such the other participants in both rooms
509
 * need to be updated accordingly. The participant who switched room
510
 * instead will be sent a \c roomchanged event which is pretty similar
511
 * to what \c joined looks like:
512
 * 
513
 * A successful request will produce a \c joined event:
514
 * 
515
\verbatim
516
{
517
        "audiobridge" : "roomchanged",
518
        "room" : <numeric ID of the new room>,
519
        "id" : <unique ID assigned to the participant in the new room>,
520
        "display" : "<display name of the new participant>",
521
        "participants" : [
522
                // Array of existing participants in the new room
523
        ]
524
}
525
\endverbatim
526
 *  
527 be35facb meetecho
 * \ingroup plugins
528
 * \ref plugins
529
 */
530
531
#include "plugin.h"
532
533
#include <jansson.h>
534
#include <opus/opus.h>
535
#include <sys/time.h>
536
537 6bb3f34d Nicholas Wylie
#include "../debug.h"
538 3a26e009 meetecho
#include "../apierror.h"
539 be35facb meetecho
#include "../config.h"
540
#include "../mutex.h"
541
#include "../rtp.h"
542
#include "../rtcp.h"
543 75d49bbe Lorenzo Miniero
#include "../record.h"
544 5e9e29e0 meetecho
#include "../utils.h"
545 be35facb meetecho
546
547
/* Plugin information */
548 3cc61ddb Lorenzo Miniero
#define JANUS_AUDIOBRIDGE_VERSION                        10
549
#define JANUS_AUDIOBRIDGE_VERSION_STRING        "0.0.10"
550 be35facb meetecho
#define JANUS_AUDIOBRIDGE_DESCRIPTION                "This is a plugin implementing an audio conference bridge for Janus, mixing Opus streams."
551
#define JANUS_AUDIOBRIDGE_NAME                                "JANUS AudioBridge plugin"
552 a3d13e20 meetecho
#define JANUS_AUDIOBRIDGE_AUTHOR                        "Meetecho s.r.l."
553 be35facb meetecho
#define JANUS_AUDIOBRIDGE_PACKAGE                        "janus.plugin.audiobridge"
554
555
/* Plugin methods */
556
janus_plugin *create(void);
557
int janus_audiobridge_init(janus_callbacks *callback, const char *config_path);
558
void janus_audiobridge_destroy(void);
559 667b2005 meetecho
int janus_audiobridge_get_api_compatibility(void);
560 be35facb meetecho
int janus_audiobridge_get_version(void);
561
const char *janus_audiobridge_get_version_string(void);
562
const char *janus_audiobridge_get_description(void);
563
const char *janus_audiobridge_get_name(void);
564 a3d13e20 meetecho
const char *janus_audiobridge_get_author(void);
565 be35facb meetecho
const char *janus_audiobridge_get_package(void);
566 cfd5c0d6 meetecho
void janus_audiobridge_create_session(janus_plugin_session *handle, int *error);
567 dd11fa0a Lorenzo Miniero
struct janus_plugin_result *janus_audiobridge_handle_message(janus_plugin_session *handle, char *transaction, json_t *message, json_t *jsep);
568 cfd5c0d6 meetecho
void janus_audiobridge_setup_media(janus_plugin_session *handle);
569
void janus_audiobridge_incoming_rtp(janus_plugin_session *handle, int video, char *buf, int len);
570
void janus_audiobridge_incoming_rtcp(janus_plugin_session *handle, int video, char *buf, int len);
571
void janus_audiobridge_hangup_media(janus_plugin_session *handle);
572
void janus_audiobridge_destroy_session(janus_plugin_session *handle, int *error);
573 dd11fa0a Lorenzo Miniero
json_t *janus_audiobridge_query_session(janus_plugin_session *handle);
574 be35facb meetecho
575
/* Plugin setup */
576
static janus_plugin janus_audiobridge_plugin =
577 1281ca86 meetecho
        JANUS_PLUGIN_INIT (
578 be35facb meetecho
                .init = janus_audiobridge_init,
579
                .destroy = janus_audiobridge_destroy,
580
581 667b2005 meetecho
                .get_api_compatibility = janus_audiobridge_get_api_compatibility,
582 be35facb meetecho
                .get_version = janus_audiobridge_get_version,
583
                .get_version_string = janus_audiobridge_get_version_string,
584
                .get_description = janus_audiobridge_get_description,
585
                .get_name = janus_audiobridge_get_name,
586 a3d13e20 meetecho
                .get_author = janus_audiobridge_get_author,
587 be35facb meetecho
                .get_package = janus_audiobridge_get_package,
588
                
589
                .create_session = janus_audiobridge_create_session,
590
                .handle_message = janus_audiobridge_handle_message,
591
                .setup_media = janus_audiobridge_setup_media,
592
                .incoming_rtp = janus_audiobridge_incoming_rtp,
593
                .incoming_rtcp = janus_audiobridge_incoming_rtcp,
594
                .hangup_media = janus_audiobridge_hangup_media,
595
                .destroy_session = janus_audiobridge_destroy_session,
596 667b2005 meetecho
                .query_session = janus_audiobridge_query_session,
597 1281ca86 meetecho
        );
598 be35facb meetecho
599
/* Plugin creator */
600
janus_plugin *create(void) {
601 3a26e009 meetecho
        JANUS_LOG(LOG_VERB, "%s created!\n", JANUS_AUDIOBRIDGE_NAME);
602 be35facb meetecho
        return &janus_audiobridge_plugin;
603
}
604
605 94926fcd Andreas Girgensohn
/* Parameter validation */
606
static struct janus_json_parameter request_parameters[] = {
607
        {"request", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
608
};
609 930a4a4c Lorenzo Miniero
static struct janus_json_parameter adminkey_parameters[] = {
610
        {"admin_key", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
611
};
612 94926fcd Andreas Girgensohn
static struct janus_json_parameter create_parameters[] = {
613
        {"description", JSON_STRING, 0},
614
        {"secret", JSON_STRING, 0},
615
        {"pin", JSON_STRING, 0},
616
        {"is_private", JANUS_JSON_BOOL, 0},
617 30df7fbf Lorenzo Miniero
        {"allowed", JSON_ARRAY, 0},
618 94926fcd Andreas Girgensohn
        {"sampling", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
619
        {"record", JANUS_JSON_BOOL, 0},
620
        {"record_file", JSON_STRING, 0},
621
        {"permanent", JANUS_JSON_BOOL, 0},
622 4fb1f6d2 Lorenzo Miniero
        {"audiolevel_ext", JANUS_JSON_BOOL, 0},
623 94926fcd Andreas Girgensohn
        {"room", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
624
};
625
static struct janus_json_parameter destroy_parameters[] = {
626
        {"room", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
627
        {"permanent", JANUS_JSON_BOOL, 0}
628
};
629 30df7fbf Lorenzo Miniero
static struct janus_json_parameter allowed_parameters[] = {
630
        {"room", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
631
        {"secret", JSON_STRING, 0},
632
        {"action", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
633
        {"allowed", JSON_ARRAY, 0}
634
};
635
static struct janus_json_parameter kick_parameters[] = {
636
        {"room", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
637
        {"secret", JSON_STRING, 0},
638
        {"id", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
639
};
640 94926fcd Andreas Girgensohn
static struct janus_json_parameter room_parameters[] = {
641
        {"room", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
642
};
643
static struct janus_json_parameter join_parameters[] = {
644
        {"room", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
645
        {"display", JSON_STRING, 0},
646 30df7fbf Lorenzo Miniero
        {"token", JSON_STRING, 0},
647 94926fcd Andreas Girgensohn
        {"muted", JANUS_JSON_BOOL, 0},
648
        {"quality", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
649 c057fb41 Lorenzo Miniero
        {"volume", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
650 94926fcd Andreas Girgensohn
        {"id", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
651
};
652
static struct janus_json_parameter configure_parameters[] = {
653
        {"muted", JANUS_JSON_BOOL, 0},
654 75d49bbe Lorenzo Miniero
        {"quality", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
655 c057fb41 Lorenzo Miniero
        {"volume", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
656 75d49bbe Lorenzo Miniero
        {"record", JANUS_JSON_BOOL, 0},
657
        {"filename", JSON_STRING, 0}
658 94926fcd Andreas Girgensohn
};
659 3760d352 Lorenzo Miniero
static struct janus_json_parameter rtp_forward_parameters[] = {
660
        {"room", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
661 0284f49f Lorenzo Miniero
        {"ssrc", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
662 3760d352 Lorenzo Miniero
        {"ptype", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
663
        {"port", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
664 79e0e1e2 Lorenzo Miniero
        {"host", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
665
        {"always_on", JANUS_JSON_BOOL, 0}
666 3760d352 Lorenzo Miniero
};
667
static struct janus_json_parameter stop_rtp_forward_parameters[] = {
668
        {"room", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
669
        {"stream_id", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
670
};
671 be35facb meetecho
672 2512456f Lorenzo Miniero
/* Static configuration instance */
673
static janus_config *config = NULL;
674
static const char *config_folder = NULL;
675
static janus_mutex config_mutex;
676
677 be35facb meetecho
/* Useful stuff */
678 4239218c meetecho
static volatile gint initialized = 0, stopping = 0;
679 71a04f89 Lorenzo Miniero
static gboolean notify_events = TRUE;
680 be35facb meetecho
static janus_callbacks *gateway = NULL;
681
static GThread *handler_thread;
682 dbf63b61 meetecho
static GThread *watchdog;
683 be35facb meetecho
static void *janus_audiobridge_handler(void *data);
684
static void janus_audiobridge_relay_rtp_packet(gpointer data, gpointer user_data);
685
static void *janus_audiobridge_mixer_thread(void *data);
686 4a02688f meetecho
static void *janus_audiobridge_participant_thread(void *data);
687 be35facb meetecho
688
typedef struct janus_audiobridge_message {
689 cfd5c0d6 meetecho
        janus_plugin_session *handle;
690 be35facb meetecho
        char *transaction;
691 e5d6528c meetecho
        json_t *message;
692 dd11fa0a Lorenzo Miniero
        json_t *jsep;
693 be35facb meetecho
} janus_audiobridge_message;
694 4d13c20d meetecho
static GAsyncQueue *messages = NULL;
695 b9e8b01f Lorenzo Miniero
static janus_audiobridge_message exit_message;
696 be35facb meetecho
697 90e0cdb4 Lorenzo Miniero
static void janus_audiobridge_message_free(janus_audiobridge_message *msg) {
698
        if(!msg || msg == &exit_message)
699 3a26e009 meetecho
                return;
700 4d13c20d meetecho
701 3a26e009 meetecho
        msg->handle = NULL;
702 4d13c20d meetecho
703
        g_free(msg->transaction);
704 3a26e009 meetecho
        msg->transaction = NULL;
705 e5d6528c meetecho
        if(msg->message)
706
                json_decref(msg->message);
707 3a26e009 meetecho
        msg->message = NULL;
708 dd11fa0a Lorenzo Miniero
        if(msg->jsep)
709
                json_decref(msg->jsep);
710
        msg->jsep = NULL;
711 4d13c20d meetecho
712 3a26e009 meetecho
        g_free(msg);
713
}
714
715
716 be35facb meetecho
typedef struct janus_audiobridge_room {
717 af40a682 meetecho
        guint64 room_id;                        /* Unique room ID */
718
        gchar *room_name;                        /* Room description */
719
        gchar *room_secret;                        /* Secret needed to manipulate (e.g., destroy) this room */
720 5d20bcec Lorenzo Miniero
        gchar *room_pin;                        /* Password needed to join this room, if any */
721 3760d352 Lorenzo Miniero
        gboolean is_private;                /* Whether this room is 'private' (as in hidden) or not */
722 0eea38ab meetecho
        uint32_t sampling_rate;                /* Sampling rate of the mix (e.g., 16000 for wideband; can be 8, 12, 16, 24 or 48kHz) */
723 4fb1f6d2 Lorenzo Miniero
        gboolean audiolevel_ext;        /* Whether the ssrc-audio-level extension must be negotiated or not for new joins */
724 af40a682 meetecho
        gboolean record;                        /* Whether this room has to be recorded or not */
725 bf24339c meetecho
        gchar *record_file;                        /* Path of the recording file */
726 af40a682 meetecho
        FILE *recording;                        /* File to record the room into */
727 c7498fa9 Lorenzo Miniero
        gint64 record_lastupdate;        /* Time when we last updated the wav header */
728 af40a682 meetecho
        gboolean destroy;                        /* Value to flag the room for destruction */
729 be35facb meetecho
        GHashTable *participants;        /* Map of participants */
730 30df7fbf Lorenzo Miniero
        gboolean check_tokens;                /* Whether to check tokens when participants join (see below) */
731
        GHashTable *allowed;                /* Map of participants (as tokens) allowed to join */
732 dbf63b61 meetecho
        GThread *thread;                        /* Mixer thread for this room */
733
        gint64 destroyed;                        /* When this room has been destroyed */
734 af40a682 meetecho
        janus_mutex mutex;                        /* Mutex to lock this room instance */
735 3760d352 Lorenzo Miniero
        /* RTP forwarders for this room's mix */
736
        GHashTable *rtp_forwarders;        /* RTP forwarders list (as a hashmap) */
737
        OpusEncoder *rtp_encoder;        /* Opus encoder instance to use for all RTP forwarders */
738
        janus_mutex rtp_mutex;                /* Mutex to lock the RTP forwarders list */
739
        int rtp_udp_sock;                        /* UDP socket to use to forward RTP packets */
740 be35facb meetecho
} janus_audiobridge_room;
741 506f60ae Jack Leigh
static GHashTable *rooms;
742
static janus_mutex rooms_mutex;
743 4b02e179 Lorenzo Miniero
static GList *old_rooms;
744 930a4a4c Lorenzo Miniero
static char *admin_key = NULL;
745 be35facb meetecho
746
typedef struct janus_audiobridge_session {
747 cfd5c0d6 meetecho
        janus_plugin_session *handle;
748 be35facb meetecho
        gpointer participant;
749
        gboolean started;
750
        gboolean stopping;
751 4239218c meetecho
        volatile gint hangingup;
752 78955474 meetecho
        gint64 destroyed;        /* Time at which this session was marked as destroyed */
753 be35facb meetecho
} janus_audiobridge_session;
754 506f60ae Jack Leigh
static GHashTable *sessions;
755 5fa9a305 meetecho
static GList *old_sessions;
756 506f60ae Jack Leigh
static janus_mutex sessions_mutex;
757 be35facb meetecho
758 e5d6528c meetecho
typedef struct janus_audiobridge_rtp_context {
759
        /* Needed to fix seq and ts in case of publisher switching */
760
        uint32_t a_last_ssrc, a_last_ts, a_base_ts, a_base_ts_prev;
761
        uint16_t a_last_seq, a_base_seq, a_base_seq_prev;
762
} janus_audiobridge_rtp_context;
763
764 be35facb meetecho
typedef struct janus_audiobridge_participant {
765
        janus_audiobridge_session *session;
766
        janus_audiobridge_room *room;        /* Room */
767 e5d6528c meetecho
        guint64 user_id;                /* Unique ID in the room */
768 30df7fbf Lorenzo Miniero
        gchar *display;                        /* Display name (opaque value, only meaningful to application) */
769 3f670174 Lorenzo Miniero
        gboolean prebuffering;        /* Whether this participant needs pre-buffering of a few packets (just joined) */
770 74cb2f01 meetecho
        gboolean active;                /* Whether this participant can receive media at all */
771 ce97b4a5 meetecho
        gboolean working;                /* Whether this participant is currently encoding/decoding */
772 e5d6528c meetecho
        gboolean muted;                        /* Whether this participant is muted */
773 c057fb41 Lorenzo Miniero
        int volume_gain;                /* Gain to apply to the input audio (in percentage) */
774 e5d6528c meetecho
        int opus_complexity;        /* Complexity to use in the encoder (by default, DEFAULT_COMPLEXITY) */
775 be35facb meetecho
        /* RTP stuff */
776 3f670174 Lorenzo Miniero
        GList *inbuf;                        /* Incoming audio from this participant, as an ordered list of packets */
777 4a02688f meetecho
        GAsyncQueue *outbuf;        /* Mixed audio for this participant */
778 4366f7f8 Lorenzo Miniero
        gint64 last_drop;                /* When we last dropped a packet because the imcoming queue was full */
779 4a02688f meetecho
        janus_mutex qmutex;                /* Incoming queue mutex */
780
        int opus_pt;                        /* Opus payload type */
781 3cc61ddb Lorenzo Miniero
        int extmap_id;                        /* Audio level RTP extension id, if any */
782
        int dBov_level;                        /* Value in dBov of the audio level (last value from extension) */
783 4a02688f meetecho
        janus_audiobridge_rtp_context context;        /* Needed in case the participant changes room */
784 be35facb meetecho
        /* Opus stuff */
785 4a02688f meetecho
        OpusEncoder *encoder;                /* Opus encoder instance */
786
        OpusDecoder *decoder;                /* Opus decoder instance */
787 bf438412 meetecho
        gboolean reset;                                /* Whether or not the Opus context must be reset, without re-joining the room */
788 4a02688f meetecho
        GThread *thread;                        /* Encoding thread for this participant */
789 75d49bbe Lorenzo Miniero
        janus_recorder *arc;                /* The Janus recorder instance for this user's audio, if enabled */
790
        janus_mutex rec_mutex;                /* Mutex to protect the recorder from race conditions */
791 4a02688f meetecho
        gint64 destroyed;                        /* When this participant has been destroyed */
792 be35facb meetecho
} janus_audiobridge_participant;
793
794
/* Packets we get from gstreamer and relay */
795
typedef struct janus_audiobridge_rtp_relay_packet {
796
        rtp_header *data;
797
        gint length;
798 4a02688f meetecho
        uint32_t ssrc;
799 e5d6528c meetecho
        uint32_t timestamp;
800
        uint16_t seq_number;
801 3cc61ddb Lorenzo Miniero
        gboolean silence;
802 be35facb meetecho
} janus_audiobridge_rtp_relay_packet;
803
804 3760d352 Lorenzo Miniero
/* RTP forwarder instance: address to send to, and current RTP header info */
805
typedef struct janus_audiobridge_rtp_forwarder {
806
        struct sockaddr_in serv_addr;
807 0284f49f Lorenzo Miniero
        uint32_t ssrc;
808 3760d352 Lorenzo Miniero
        int payload_type;
809
        uint16_t seq_number;
810
        uint32_t timestamp;
811 79e0e1e2 Lorenzo Miniero
        gboolean always_on;
812 3760d352 Lorenzo Miniero
} janus_audiobridge_rtp_forwarder;
813 0284f49f Lorenzo Miniero
static guint32 janus_audiobridge_rtp_forwarder_add_helper(janus_audiobridge_room *room, const gchar* host, uint16_t port, uint32_t ssrc, int pt, gboolean always_on) {
814 3760d352 Lorenzo Miniero
        if(room == NULL || host == NULL)
815
                return 0;
816
        janus_audiobridge_rtp_forwarder *rf = g_malloc0(sizeof(janus_audiobridge_rtp_forwarder));
817
        /* Resolve address */
818
        rf->serv_addr.sin_family = AF_INET;
819
        inet_pton(AF_INET, host, &(rf->serv_addr.sin_addr));
820
        rf->serv_addr.sin_port = htons(port);
821
        /* Setup RTP info (we'll use the stream ID as SSRC) */
822 0284f49f Lorenzo Miniero
        rf->ssrc = ssrc;
823 3760d352 Lorenzo Miniero
        rf->payload_type = pt;
824
        rf->seq_number = 0;
825
        rf->timestamp = 0;
826 79e0e1e2 Lorenzo Miniero
        rf->always_on = always_on;
827 3760d352 Lorenzo Miniero
        janus_mutex_lock(&room->rtp_mutex);
828
        guint32 stream_id = janus_random_uint32();
829
        while(g_hash_table_lookup(room->rtp_forwarders, GUINT_TO_POINTER(stream_id)) != NULL) {
830
                stream_id = janus_random_uint32();
831
        }
832
        g_hash_table_insert(room->rtp_forwarders, GUINT_TO_POINTER(stream_id), rf);
833
        janus_mutex_unlock(&room->rtp_mutex);
834
        JANUS_LOG(LOG_VERB, "Added RTP forwarder to room %"SCNu64": %s:%d (ID: %"SCNu32")\n",
835
                room->room_id, host, port, stream_id);
836
        return stream_id;
837
}
838
839
840 3f670174 Lorenzo Miniero
/* Helper to sort incoming RTP packets by sequence numbers */
841
static gint janus_audiobridge_rtp_sort(gconstpointer a, gconstpointer b) {
842
        janus_audiobridge_rtp_relay_packet *pkt1 = (janus_audiobridge_rtp_relay_packet *)a;
843
        janus_audiobridge_rtp_relay_packet *pkt2 = (janus_audiobridge_rtp_relay_packet *)b;
844
        if(pkt1->seq_number < 100 && pkt2->seq_number > 65000) {
845
                /* Sequence number was probably reset, pkt2 is older */
846
                return 1;
847
        } else if(pkt2->seq_number < 100 && pkt1->seq_number > 65000) {
848
                /* Sequence number was probably reset, pkt1 is older */
849
                return -1;
850
        }
851
        /* Simply compare timestamps */
852
        if(pkt1->seq_number < pkt2->seq_number)
853
                return -1;
854
        else if(pkt1->seq_number > pkt2->seq_number)
855
                return 1;
856
        return 0;
857
}
858
859 be35facb meetecho
/* SDP offer/answer template */
860 e37cc793 Philip Withnall
#define sdp_template \
861
                "v=0\r\n" \
862
                "o=- %"SCNu64" %"SCNu64" IN IP4 127.0.0.1\r\n"        /* We need current time here */ \
863
                "s=%s\r\n"                                                        /* Audio bridge name */ \
864
                "t=0 0\r\n" \
865
                "m=audio 1 RTP/SAVPF %d\r\n"                /* Opus payload type */ \
866
                "c=IN IP4 1.1.1.1\r\n" \
867
                "a=rtpmap:%d opus/48000/2\r\n"                /* Opus payload type */ \
868 3cc61ddb Lorenzo Miniero
                "a=fmtp:%d maxplaybackrate=%"SCNu32"; stereo=0; sprop-stereo=0; useinbandfec=0\r\n" /* Opus payload type and room sampling rate */ \
869
                "%s"                                                                /* extmap(s), if any */
870 be35facb meetecho
871
/* Helper struct to generate and parse WAVE headers */
872
typedef struct wav_header {
873
        char riff[4];
874
        uint32_t len;
875
        char wave[4];
876
        char fmt[4];
877
        uint32_t formatsize;
878
        uint16_t format;
879
        uint16_t channels;
880
        uint32_t samplerate;
881
        uint32_t avgbyterate;
882
        uint16_t samplebytes;
883
        uint16_t channelbits;
884
        char data[4];
885
        uint32_t blocksize;
886
} wav_header;
887
888
889 3f670174 Lorenzo Miniero
/* Mixer settings */
890
#define DEFAULT_PREBUFFERING        6
891
892
893 be35facb meetecho
/* Opus settings */                
894
#define        BUFFER_SAMPLES        8000
895
#define        OPUS_SAMPLES        160
896
#define USE_FEC                        0
897
#define DEFAULT_COMPLEXITY        4
898
899
900 3cc3cab3 meetecho
/* Error codes */
901
#define JANUS_AUDIOBRIDGE_ERROR_UNKNOWN_ERROR        499
902 e5d6528c meetecho
#define JANUS_AUDIOBRIDGE_ERROR_NO_MESSAGE                480
903
#define JANUS_AUDIOBRIDGE_ERROR_INVALID_JSON        481
904
#define JANUS_AUDIOBRIDGE_ERROR_INVALID_REQUEST        482
905
#define JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT        483
906
#define JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT        484
907
#define JANUS_AUDIOBRIDGE_ERROR_NO_SUCH_ROOM        485
908
#define JANUS_AUDIOBRIDGE_ERROR_ROOM_EXISTS                486
909
#define JANUS_AUDIOBRIDGE_ERROR_NOT_JOINED                487
910
#define JANUS_AUDIOBRIDGE_ERROR_LIBOPUS_ERROR        488
911
#define JANUS_AUDIOBRIDGE_ERROR_UNAUTHORIZED        489
912
#define JANUS_AUDIOBRIDGE_ERROR_ID_EXISTS                490
913 8f6cdc67 meetecho
#define JANUS_AUDIOBRIDGE_ERROR_ALREADY_JOINED        491
914 30df7fbf Lorenzo Miniero
#define JANUS_AUDIOBRIDGE_ERROR_NO_SUCH_USER        492
915 3cc3cab3 meetecho
916
917 5fa9a305 meetecho
/* AudioBridge watchdog/garbage collector (sort of) */
918
void *janus_audiobridge_watchdog(void *data);
919
void *janus_audiobridge_watchdog(void *data) {
920
        JANUS_LOG(LOG_INFO, "AudioBridge watchdog started\n");
921
        gint64 now = 0;
922 dbf63b61 meetecho
        while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) {
923 5fa9a305 meetecho
                janus_mutex_lock(&sessions_mutex);
924
                /* Iterate on all the sessions */
925
                now = janus_get_monotonic_time();
926
                if(old_sessions != NULL) {
927
                        GList *sl = old_sessions;
928 1281ca86 meetecho
                        JANUS_LOG(LOG_HUGE, "Checking %d old AudioBridge sessions...\n", g_list_length(old_sessions));
929 5fa9a305 meetecho
                        while(sl) {
930
                                janus_audiobridge_session *session = (janus_audiobridge_session *)sl->data;
931 dbf63b61 meetecho
                                if(!session) {
932 5fa9a305 meetecho
                                        sl = sl->next;
933
                                        continue;
934
                                }
935
                                if(now-session->destroyed >= 5*G_USEC_PER_SEC) {
936
                                        /* We're lazy and actually get rid of the stuff only after a few seconds */
937 1281ca86 meetecho
                                        JANUS_LOG(LOG_VERB, "Freeing old AudioBridge session\n");
938 5fa9a305 meetecho
                                        GList *rm = sl->next;
939
                                        old_sessions = g_list_delete_link(old_sessions, sl);
940
                                        sl = rm;
941
                                        session->handle = NULL;
942
                                        g_free(session);
943
                                        session = NULL;
944
                                        continue;
945
                                }
946
                                sl = sl->next;
947
                        }
948
                }
949
                janus_mutex_unlock(&sessions_mutex);
950 4b02e179 Lorenzo Miniero
                janus_mutex_lock(&rooms_mutex);
951
                if(old_rooms != NULL) {
952
                        GList *rl = old_rooms;
953
                        now = janus_get_monotonic_time();
954
                        while(rl) {
955
                                janus_audiobridge_room *audiobridge = (janus_audiobridge_room*)rl->data;
956
                                if(!initialized || stopping){
957
                                        break;
958
                                }
959
                                if(!audiobridge) {
960
                                        rl = rl->next;
961
                                        continue;
962
                                }
963
                                if(now - audiobridge->destroyed >= 5*G_USEC_PER_SEC) {
964
                                        /* Free resources */
965
                                        JANUS_LOG(LOG_VERB, "Freeing old AudioBridge room %"SCNu64"\n", audiobridge->room_id);
966
                                        g_free(audiobridge->room_name);
967
                                        g_free(audiobridge->room_secret);
968
                                        g_free(audiobridge->room_pin);
969
                                        g_free(audiobridge->record_file);
970
                                        g_hash_table_destroy(audiobridge->participants);
971 30df7fbf Lorenzo Miniero
                                        g_hash_table_destroy(audiobridge->allowed);
972 3760d352 Lorenzo Miniero
                                        janus_mutex_lock(&audiobridge->rtp_mutex);
973
                                        if(audiobridge->rtp_udp_sock > 0)
974
                                                close(audiobridge->rtp_udp_sock);
975
                                        if(audiobridge->rtp_encoder)
976
                                                opus_encoder_destroy(audiobridge->rtp_encoder);
977
                                        g_hash_table_destroy(audiobridge->rtp_forwarders);
978
                                        janus_mutex_unlock(&audiobridge->rtp_mutex);
979 4b02e179 Lorenzo Miniero
                                        g_free(audiobridge);
980
                                        /* Move on */
981
                                        GList *rm = rl->next;
982
                                        old_rooms = g_list_delete_link(old_rooms, rl);
983
                                        rl = rm;
984
                                        continue;
985
                                }
986
                                rl = rl->next;
987
                        }
988
                }
989
                janus_mutex_unlock(&rooms_mutex);
990 1e2f5d89 meetecho
                g_usleep(500000);
991 5fa9a305 meetecho
        }
992
        JANUS_LOG(LOG_INFO, "AudioBridge watchdog stopped\n");
993
        return NULL;
994
}
995
996
997 be35facb meetecho
/* Plugin implementation */
998
int janus_audiobridge_init(janus_callbacks *callback, const char *config_path) {
999 dbf63b61 meetecho
        if(g_atomic_int_get(&stopping)) {
1000 be35facb meetecho
                /* Still stopping from before */
1001
                return -1;
1002
        }
1003
        if(callback == NULL || config_path == NULL) {
1004
                /* Invalid arguments */
1005
                return -1;
1006
        }
1007
1008
        /* Read configuration */
1009
        char filename[255];
1010 c94052c2 meetecho
        g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_AUDIOBRIDGE_PACKAGE);
1011 3a26e009 meetecho
        JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
1012 2512456f Lorenzo Miniero
        config = janus_config_parse(filename);
1013
        config_folder = config_path;
1014 be35facb meetecho
        if(config != NULL)
1015
                janus_config_print(config);
1016 2512456f Lorenzo Miniero
        janus_mutex_init(&config_mutex);
1017 be35facb meetecho
        
1018 d236f0e9 Lorenzo Miniero
        rooms = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL);
1019 3a26e009 meetecho
        janus_mutex_init(&rooms_mutex);
1020 be35facb meetecho
        sessions = g_hash_table_new(NULL, NULL);
1021 3a26e009 meetecho
        janus_mutex_init(&sessions_mutex);
1022 4d13c20d meetecho
        messages = g_async_queue_new_full((GDestroyNotify) janus_audiobridge_message_free);
1023 be35facb meetecho
        /* This is the callback we'll need to invoke to contact the gateway */
1024
        gateway = callback;
1025
1026
        /* Parse configuration to populate the rooms list */
1027
        if(config != NULL) {
1028 930a4a4c Lorenzo Miniero
                /* Any admin key to limit who can "create"? */
1029
                janus_config_item *key = janus_config_get_item_drilldown(config, "general", "admin_key");
1030
                if(key != NULL && key->value != NULL)
1031
                        admin_key = g_strdup(key->value);
1032 71a04f89 Lorenzo Miniero
                janus_config_item *events = janus_config_get_item_drilldown(config, "general", "events");
1033
                if(events != NULL && events->value != NULL)
1034
                        notify_events = janus_is_true(events->value);
1035
                if(!notify_events && callback->events_is_enabled()) {
1036
                        JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_AUDIOBRIDGE_NAME);
1037
                }
1038 930a4a4c Lorenzo Miniero
                /* Iterate on all rooms */
1039 2512456f Lorenzo Miniero
                GList *cl = janus_config_get_categories(config);
1040
                while(cl != NULL) {
1041
                        janus_config_category *cat = (janus_config_category *)cl->data;
1042 930a4a4c Lorenzo Miniero
                        if(cat->name == NULL || !strcasecmp(cat->name, "general")) {
1043 2512456f Lorenzo Miniero
                                cl = cl->next;
1044 be35facb meetecho
                                continue;
1045
                        }
1046 3a26e009 meetecho
                        JANUS_LOG(LOG_VERB, "Adding audio room '%s'\n", cat->name);
1047 be35facb meetecho
                        janus_config_item *desc = janus_config_get_item(cat, "description");
1048 0a466e28 janus
                        janus_config_item *priv = janus_config_get_item(cat, "is_private");
1049 be35facb meetecho
                        janus_config_item *sampling = janus_config_get_item(cat, "sampling_rate");
1050 4fb1f6d2 Lorenzo Miniero
                        janus_config_item *audiolevel_ext = janus_config_get_item(cat, "audiolevel_ext");
1051 af40a682 meetecho
                        janus_config_item *secret = janus_config_get_item(cat, "secret");
1052 5d20bcec Lorenzo Miniero
                        janus_config_item *pin = janus_config_get_item(cat, "pin");
1053 be35facb meetecho
                        janus_config_item *record = janus_config_get_item(cat, "record");
1054 bf24339c meetecho
                        janus_config_item *recfile = janus_config_get_item(cat, "record_file");
1055 be35facb meetecho
                        if(sampling == NULL || sampling->value == NULL) {
1056 3a26e009 meetecho
                                JANUS_LOG(LOG_ERR, "Can't add the audio room, missing mandatory information...\n");
1057 2512456f Lorenzo Miniero
                                cl = cl->next;
1058 be35facb meetecho
                                continue;
1059
                        }
1060
                        /* Create the audio bridge room */
1061 1f067658 Lorenzo Miniero
                        janus_audiobridge_room *audiobridge = g_malloc0(sizeof(janus_audiobridge_room));
1062 a972337c Lorenzo Miniero
                        audiobridge->room_id = g_ascii_strtoull(cat->name, NULL, 0);
1063 be35facb meetecho
                        char *description = NULL;
1064 d1302e06 meetecho
                        if(desc != NULL && desc->value != NULL && strlen(desc->value) > 0)
1065 be35facb meetecho
                                description = g_strdup(desc->value);
1066
                        else
1067
                                description = g_strdup(cat->name);
1068
                        audiobridge->room_name = description;
1069 0a466e28 janus
                        audiobridge->is_private = priv && priv->value && janus_is_true(priv->value);
1070 036ee155 Lorenzo Miniero
                        audiobridge->sampling_rate = atol(sampling->value);
1071 0eea38ab meetecho
                        switch(audiobridge->sampling_rate) {
1072
                                case 8000:
1073
                                case 12000:
1074
                                case 16000:
1075
                                case 24000:
1076
                                case 48000:
1077
                                        JANUS_LOG(LOG_VERB, "Sampling rate for mixing: %"SCNu32"\n", audiobridge->sampling_rate);
1078
                                        break;
1079
                                default:
1080
                                        JANUS_LOG(LOG_ERR, "Unsupported sampling rate %"SCNu32"...\n", audiobridge->sampling_rate);
1081 2512456f Lorenzo Miniero
                                        cl = cl->next;
1082 0eea38ab meetecho
                                        continue;
1083 be35facb meetecho
                        }
1084 4fb1f6d2 Lorenzo Miniero
                        audiobridge->audiolevel_ext = TRUE;
1085
                        if(audiolevel_ext != NULL && audiolevel_ext->value != NULL)
1086
                                audiobridge->audiolevel_ext = janus_is_true(audiolevel_ext->value);
1087 af40a682 meetecho
                        if(secret != NULL && secret->value != NULL) {
1088
                                audiobridge->room_secret = g_strdup(secret->value);
1089
                        }
1090 5d20bcec Lorenzo Miniero
                        if(pin != NULL && pin->value != NULL) {
1091
                                audiobridge->room_pin = g_strdup(pin->value);
1092
                        }
1093 be35facb meetecho
                        audiobridge->record = FALSE;
1094 6dd1c12c meetecho
                        if(record && record->value && janus_is_true(record->value))
1095 be35facb meetecho
                                audiobridge->record = TRUE;
1096 bf24339c meetecho
                        if(recfile && recfile->value)
1097
                                audiobridge->record_file = g_strdup(recfile->value);
1098 be35facb meetecho
                        audiobridge->recording = NULL;
1099
                        audiobridge->destroy = 0;
1100 d236f0e9 Lorenzo Miniero
                        audiobridge->participants = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL);
1101 30df7fbf Lorenzo Miniero
                        audiobridge->check_tokens = FALSE;        /* Static rooms can't have an "allowed" list yet, no hooks to the configuration file */
1102
                        audiobridge->allowed = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL);
1103 dbf63b61 meetecho
                        audiobridge->destroyed = 0;
1104 be35facb meetecho
                        janus_mutex_init(&audiobridge->mutex);
1105 3760d352 Lorenzo Miniero
                        audiobridge->rtp_forwarders = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)g_free);
1106
                        audiobridge->rtp_encoder = NULL;
1107
                        audiobridge->rtp_udp_sock = -1;
1108
                        janus_mutex_init(&audiobridge->rtp_mutex);
1109 5d20bcec Lorenzo Miniero
                        JANUS_LOG(LOG_VERB, "Created audiobridge: %"SCNu64" (%s, %s, secret: %s, pin: %s)\n",
1110
                                audiobridge->room_id, audiobridge->room_name,
1111
                                audiobridge->is_private ? "private" : "public",
1112
                                audiobridge->room_secret ? audiobridge->room_secret : "no secret",
1113
                                audiobridge->room_pin ? audiobridge->room_pin : "no pin");
1114 be35facb meetecho
                        /* We need a thread for the mix */
1115 dbf63b61 meetecho
                        GError *error = NULL;
1116 c7498fa9 Lorenzo Miniero
                        char tname[16];
1117
                        g_snprintf(tname, sizeof(tname), "mixer %"SCNu64, audiobridge->room_id);
1118
                        audiobridge->thread = g_thread_try_new(tname, &janus_audiobridge_mixer_thread, audiobridge, &error);
1119 dbf63b61 meetecho
                        if(error != NULL) {
1120
                                /* FIXME We should clear some resources... */
1121
                                JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the mixer thread...\n", error->code, error->message ? error->message : "??");
1122
                        } else {
1123
                                janus_mutex_lock(&rooms_mutex);
1124 d236f0e9 Lorenzo Miniero
                                g_hash_table_insert(rooms, janus_uint64_dup(audiobridge->room_id), audiobridge);
1125 dbf63b61 meetecho
                                janus_mutex_unlock(&rooms_mutex);
1126
                        }
1127 2512456f Lorenzo Miniero
                        cl = cl->next;
1128 be35facb meetecho
                }
1129 2512456f Lorenzo Miniero
                /* Done: we keep the configuration file open in case we get a "create" or "destroy" with permanent=true */
1130 be35facb meetecho
        }
1131
1132
        /* Show available rooms */
1133 3a26e009 meetecho
        janus_mutex_lock(&rooms_mutex);
1134 4d13c20d meetecho
        GHashTableIter iter;
1135
        gpointer value;
1136
        g_hash_table_iter_init(&iter, rooms);
1137
        while (g_hash_table_iter_next(&iter, NULL, &value)) {
1138
                janus_audiobridge_room *ar = value;
1139 3a26e009 meetecho
                JANUS_LOG(LOG_VERB, "  ::: [%"SCNu64"][%s] %"SCNu32" (%s be recorded)\n",
1140 be35facb meetecho
                        ar->room_id, ar->room_name, ar->sampling_rate, ar->record ? "will" : "will NOT");
1141
        }
1142 3a26e009 meetecho
        janus_mutex_unlock(&rooms_mutex);
1143 be35facb meetecho
1144 dbf63b61 meetecho
        g_atomic_int_set(&initialized, 1);
1145
1146
        GError *error = NULL;
1147 5fa9a305 meetecho
        /* Start the sessions watchdog */
1148 c7498fa9 Lorenzo Miniero
        watchdog = g_thread_try_new("audiobridge watchdog", &janus_audiobridge_watchdog, NULL, &error);
1149 dbf63b61 meetecho
        if(error != NULL) {
1150
                g_atomic_int_set(&initialized, 0);
1151
                JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the AudioBridge watchdog thread...\n", error->code, error->message ? error->message : "??");
1152 2512456f Lorenzo Miniero
                janus_config_destroy(config);
1153 5fa9a305 meetecho
                return -1;
1154
        }
1155 be35facb meetecho
        /* Launch the thread that will handle incoming messages */
1156 c7498fa9 Lorenzo Miniero
        handler_thread = g_thread_try_new("audiobridge handler", janus_audiobridge_handler, NULL, &error);
1157 be35facb meetecho
        if(error != NULL) {
1158 dbf63b61 meetecho
                g_atomic_int_set(&initialized, 0);
1159
                JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the AudioBridge handler thread...\n", error->code, error->message ? error->message : "??");
1160 2512456f Lorenzo Miniero
                janus_config_destroy(config);
1161 be35facb meetecho
                return -1;
1162
        }
1163 3a26e009 meetecho
        JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_AUDIOBRIDGE_NAME);
1164 be35facb meetecho
        return 0;
1165
}
1166
1167 77992057 Philip Withnall
void janus_audiobridge_destroy(void) {
1168 dbf63b61 meetecho
        if(!g_atomic_int_get(&initialized))
1169 be35facb meetecho
                return;
1170 dbf63b61 meetecho
        g_atomic_int_set(&stopping, 1);
1171 2e28ee7b Lorenzo Miniero
1172 b9e8b01f Lorenzo Miniero
        g_async_queue_push(messages, &exit_message);
1173 be35facb meetecho
        if(handler_thread != NULL) {
1174
                g_thread_join(handler_thread);
1175 dbf63b61 meetecho
                handler_thread = NULL;
1176
        }
1177
        if(watchdog != NULL) {
1178
                g_thread_join(watchdog);
1179
                watchdog = NULL;
1180 be35facb meetecho
        }
1181 3a26e009 meetecho
        /* FIXME We should destroy the sessions cleanly */
1182
        janus_mutex_lock(&sessions_mutex);
1183 be35facb meetecho
        g_hash_table_destroy(sessions);
1184 3a26e009 meetecho
        janus_mutex_unlock(&sessions_mutex);
1185
        janus_mutex_lock(&rooms_mutex);
1186 be35facb meetecho
        g_hash_table_destroy(rooms);
1187 3a26e009 meetecho
        janus_mutex_unlock(&rooms_mutex);
1188 4d13c20d meetecho
        g_async_queue_unref(messages);
1189
        messages = NULL;
1190 be35facb meetecho
        sessions = NULL;
1191 2512456f Lorenzo Miniero
1192
        janus_config_destroy(config);
1193 930a4a4c Lorenzo Miniero
        g_free(admin_key);
1194 2512456f Lorenzo Miniero
1195 dbf63b61 meetecho
        g_atomic_int_set(&initialized, 0);
1196
        g_atomic_int_set(&stopping, 0);
1197 3a26e009 meetecho
        JANUS_LOG(LOG_INFO, "%s destroyed!\n", JANUS_AUDIOBRIDGE_NAME);
1198 be35facb meetecho
}
1199
1200 667b2005 meetecho
int janus_audiobridge_get_api_compatibility(void) {
1201
        /* Important! This is what your plugin MUST always return: don't lie here or bad things will happen */
1202
        return JANUS_PLUGIN_API_VERSION;
1203
}
1204
1205 77992057 Philip Withnall
int janus_audiobridge_get_version(void) {
1206 be35facb meetecho
        return JANUS_AUDIOBRIDGE_VERSION;
1207
}
1208
1209 77992057 Philip Withnall
const char *janus_audiobridge_get_version_string(void) {
1210 be35facb meetecho
        return JANUS_AUDIOBRIDGE_VERSION_STRING;
1211
}
1212
1213 77992057 Philip Withnall
const char *janus_audiobridge_get_description(void) {
1214 be35facb meetecho
        return JANUS_AUDIOBRIDGE_DESCRIPTION;
1215
}
1216
1217 77992057 Philip Withnall
const char *janus_audiobridge_get_name(void) {
1218 be35facb meetecho
        return JANUS_AUDIOBRIDGE_NAME;
1219
}
1220
1221 77992057 Philip Withnall
const char *janus_audiobridge_get_author(void) {
1222 a3d13e20 meetecho
        return JANUS_AUDIOBRIDGE_AUTHOR;
1223
}
1224
1225 77992057 Philip Withnall
const char *janus_audiobridge_get_package(void) {
1226 be35facb meetecho
        return JANUS_AUDIOBRIDGE_PACKAGE;
1227
}
1228
1229 cfd5c0d6 meetecho
void janus_audiobridge_create_session(janus_plugin_session *handle, int *error) {
1230 8878e296 meetecho
        if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
1231 be35facb meetecho
                *error = -1;
1232
                return;
1233
        }        
1234 1f067658 Lorenzo Miniero
        janus_audiobridge_session *session = (janus_audiobridge_session *)g_malloc0(sizeof(janus_audiobridge_session));
1235 be35facb meetecho
        session->handle = handle;
1236
        session->started = FALSE;
1237
        session->stopping = FALSE;
1238 5fa9a305 meetecho
        session->destroyed = 0;
1239 f65c61ff Lorenzo Miniero
        g_atomic_int_set(&session->hangingup, 0);
1240 be35facb meetecho
        handle->plugin_handle = session;
1241 3a26e009 meetecho
        janus_mutex_lock(&sessions_mutex);
1242 be35facb meetecho
        g_hash_table_insert(sessions, handle, session);
1243 3a26e009 meetecho
        janus_mutex_unlock(&sessions_mutex);
1244 be35facb meetecho
1245
        return;
1246
}
1247
1248 cfd5c0d6 meetecho
void janus_audiobridge_destroy_session(janus_plugin_session *handle, int *error) {
1249 8878e296 meetecho
        if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
1250 be35facb meetecho
                *error = -1;
1251
                return;
1252
        }        
1253
        janus_audiobridge_session *session = (janus_audiobridge_session *)handle->plugin_handle; 
1254
        if(!session) {
1255 ce97b4a5 meetecho
                JANUS_LOG(LOG_ERR, "No AudioBridge session associated with this handle...\n");
1256 be35facb meetecho
                *error = -2;
1257
                return;
1258
        }
1259 ce97b4a5 meetecho
        JANUS_LOG(LOG_VERB, "Removing AudioBridge session...\n");
1260 3a26e009 meetecho
        janus_mutex_lock(&sessions_mutex);
1261 793d18b1 meetecho
        if(!session->destroyed) {
1262
                g_hash_table_remove(sessions, handle);
1263
                janus_audiobridge_hangup_media(handle);
1264 78955474 meetecho
                session->destroyed = janus_get_monotonic_time();
1265 793d18b1 meetecho
                /* Cleaning up and removing the session is done in a lazy way */
1266
                old_sessions = g_list_append(old_sessions, session);
1267
        }
1268 5fa9a305 meetecho
        janus_mutex_unlock(&sessions_mutex);
1269 be35facb meetecho
1270
        return;
1271
}
1272
1273 dd11fa0a Lorenzo Miniero
json_t *janus_audiobridge_query_session(janus_plugin_session *handle) {
1274 667b2005 meetecho
        if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
1275
                return NULL;
1276
        }        
1277
        janus_audiobridge_session *session = (janus_audiobridge_session *)handle->plugin_handle;
1278
        if(!session) {
1279
                JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
1280
                return NULL;
1281
        }
1282
        /* Show the participant/room info, if any */
1283
        json_t *info = json_object();
1284 74cb2f01 meetecho
        janus_audiobridge_participant *participant = (janus_audiobridge_participant *)session->participant;
1285
        json_object_set_new(info, "state", json_string(participant && participant->room ? "inroom" : "idle"));
1286
        if(participant) {
1287 17161000 Lorenzo Miniero
                janus_mutex_lock(&rooms_mutex);
1288
                janus_audiobridge_room *room = participant->room;
1289
                if(room != NULL)
1290
                        json_object_set_new(info, "room", json_integer(room->room_id));
1291
                janus_mutex_unlock(&rooms_mutex);
1292 667b2005 meetecho
                json_object_set_new(info, "id", json_integer(participant->user_id));
1293 74cb2f01 meetecho
                if(participant->display)
1294
                        json_object_set_new(info, "display", json_string(participant->display));
1295 51db8a86 Lorenzo Miniero
                json_object_set_new(info, "muted", participant->muted ? json_true() : json_false());
1296
                json_object_set_new(info, "active", participant->active ? json_true() : json_false());
1297
                json_object_set_new(info, "pre-buffering", participant->prebuffering ? json_true() : json_false());
1298 3f670174 Lorenzo Miniero
                if(participant->inbuf) {
1299
                        janus_mutex_lock(&participant->qmutex);
1300
                        json_object_set_new(info, "queue-in", json_integer(g_list_length(participant->inbuf)));
1301
                        janus_mutex_unlock(&participant->qmutex);
1302
                }
1303 bf438412 meetecho
                if(participant->outbuf)
1304
                        json_object_set_new(info, "queue-out", json_integer(g_async_queue_length(participant->outbuf)));
1305 4366f7f8 Lorenzo Miniero
                if(participant->last_drop > 0)
1306 2a61f5c3 Lorenzo Miniero
                        json_object_set_new(info, "last-drop", json_integer(participant->last_drop));
1307 75d49bbe Lorenzo Miniero
                if(participant->arc && participant->arc->filename)
1308
                        json_object_set_new(info, "audio-recording", json_string(participant->arc->filename));
1309 3cc61ddb Lorenzo Miniero
                if(participant->extmap_id > 0)
1310
                        json_object_set_new(info, "audio-level-dBov", json_integer(participant->dBov_level));
1311 667b2005 meetecho
        }
1312 51db8a86 Lorenzo Miniero
        json_object_set_new(info, "started", session->started ? json_true() : json_false());
1313 667b2005 meetecho
        json_object_set_new(info, "destroyed", json_integer(session->destroyed));
1314 dd11fa0a Lorenzo Miniero
        return info;
1315 667b2005 meetecho
}
1316
1317 dd11fa0a Lorenzo Miniero
struct janus_plugin_result *janus_audiobridge_handle_message(janus_plugin_session *handle, char *transaction, json_t *message, json_t *jsep) {
1318 8878e296 meetecho
        if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
1319 dd11fa0a Lorenzo Miniero
                return janus_plugin_result_new(JANUS_PLUGIN_ERROR, g_atomic_int_get(&stopping) ? "Shutting down" : "Plugin not initialized", NULL);
1320 292d035f meetecho
1321 e5d6528c meetecho
        /* Pre-parse the message */
1322
        int error_code = 0;
1323 011610cd meetecho
        char error_cause[512];
1324 dd11fa0a Lorenzo Miniero
        json_t *root = message;
1325 011610cd meetecho
        json_t *response = NULL;
1326
        
1327
        if(message == NULL) {
1328
                JANUS_LOG(LOG_ERR, "No message??\n");
1329
                error_code = JANUS_AUDIOBRIDGE_ERROR_NO_MESSAGE;
1330
                g_snprintf(error_cause, 512, "%s", "No message??");
1331 db1e9a1f Lorenzo Miniero
                goto plugin_response;
1332 011610cd meetecho
        }
1333
1334 e5d6528c meetecho
        janus_audiobridge_session *session = (janus_audiobridge_session *)handle->plugin_handle;        
1335
        if(!session) {
1336
                JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
1337
                error_code = JANUS_AUDIOBRIDGE_ERROR_UNKNOWN_ERROR;
1338
                g_snprintf(error_cause, 512, "%s", "session associated with this handle...");
1339 db1e9a1f Lorenzo Miniero
                goto plugin_response;
1340 e5d6528c meetecho
        }
1341
        if(session->destroyed) {
1342
                JANUS_LOG(LOG_ERR, "Session has already been marked as destroyed...\n");
1343
                error_code = JANUS_AUDIOBRIDGE_ERROR_UNKNOWN_ERROR;
1344
                g_snprintf(error_cause, 512, "%s", "Session has already been marked as destroyed...");
1345 db1e9a1f Lorenzo Miniero
                goto plugin_response;
1346 e5d6528c meetecho
        }
1347
        if(!json_is_object(root)) {
1348
                JANUS_LOG(LOG_ERR, "JSON error: not an object\n");
1349
                error_code = JANUS_AUDIOBRIDGE_ERROR_INVALID_JSON;
1350
                g_snprintf(error_cause, 512, "JSON error: not an object");
1351 db1e9a1f Lorenzo Miniero
                goto plugin_response;
1352 e5d6528c meetecho
        }
1353
        /* Get the request first */
1354 94926fcd Andreas Girgensohn
        JANUS_VALIDATE_JSON_OBJECT(root, request_parameters,
1355
                error_code, error_cause, TRUE,
1356
                JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
1357
        if(error_code != 0)
1358 db1e9a1f Lorenzo Miniero
                goto plugin_response;
1359 94926fcd Andreas Girgensohn
        json_t *request = json_object_get(root, "request");
1360 e5d6528c meetecho
        /* Some requests ('create', 'destroy', 'exists', 'list') can be handled synchronously */
1361
        const char *request_text = json_string_value(request);
1362
        if(!strcasecmp(request_text, "create")) {
1363
                /* Create a new audiobridge */
1364
                JANUS_LOG(LOG_VERB, "Creating a new audiobridge\n");
1365 94926fcd Andreas Girgensohn
                JANUS_VALIDATE_JSON_OBJECT(root, create_parameters,
1366
                        error_code, error_cause, TRUE,
1367
                        JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
1368
                if(error_code != 0)
1369 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1370 930a4a4c Lorenzo Miniero
                if(admin_key != NULL) {
1371
                        /* An admin key was specified: make sure it was provided, and that it's valid */
1372
                        JANUS_VALIDATE_JSON_OBJECT(root, adminkey_parameters,
1373
                                error_code, error_cause, TRUE,
1374
                                JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
1375
                        if(error_code != 0)
1376 db1e9a1f Lorenzo Miniero
                                goto plugin_response;
1377 930a4a4c Lorenzo Miniero
                        JANUS_CHECK_SECRET(admin_key, root, "admin_key", error_code, error_cause,
1378
                                JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_UNAUTHORIZED);
1379
                        if(error_code != 0)
1380 db1e9a1f Lorenzo Miniero
                                goto plugin_response;
1381 930a4a4c Lorenzo Miniero
                }
1382 94926fcd Andreas Girgensohn
                json_t *desc = json_object_get(root, "description");
1383 e5d6528c meetecho
                json_t *secret = json_object_get(root, "secret");
1384 5d20bcec Lorenzo Miniero
                json_t *pin = json_object_get(root, "pin");
1385 0a466e28 janus
                json_t *is_private = json_object_get(root, "is_private");
1386 30df7fbf Lorenzo Miniero
                json_t *allowed = json_object_get(root, "allowed");
1387 e5d6528c meetecho
                json_t *sampling = json_object_get(root, "sampling");
1388 4fb1f6d2 Lorenzo Miniero
                json_t *audiolevel_ext = json_object_get(root, "audiolevel_ext");
1389 e5d6528c meetecho
                json_t *record = json_object_get(root, "record");
1390
                json_t *recfile = json_object_get(root, "record_file");
1391 2512456f Lorenzo Miniero
                json_t *permanent = json_object_get(root, "permanent");
1392 30df7fbf Lorenzo Miniero
                if(allowed) {
1393
                        /* Make sure the "allowed" array only contains strings */
1394
                        gboolean ok = TRUE;
1395
                        if(json_array_size(allowed) > 0) {
1396
                                size_t i = 0;
1397
                                for(i=0; i<json_array_size(allowed); i++) {
1398
                                        json_t *a = json_array_get(allowed, i);
1399
                                        if(!a || !json_is_string(a)) {
1400
                                                ok = FALSE;
1401
                                                break;
1402
                                        }
1403
                                }
1404
                        }
1405
                        if(!ok) {
1406
                                JANUS_LOG(LOG_ERR, "Invalid element in the allowed array (not a string)\n");
1407
                                error_code = JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT;
1408
                                g_snprintf(error_cause, 512, "Invalid element in the allowed array (not a string)");
1409
                                goto plugin_response;
1410
                        }
1411
                }
1412 2512456f Lorenzo Miniero
                gboolean save = permanent ? json_is_true(permanent) : FALSE;
1413
                if(save && config == NULL) {
1414
                        JANUS_LOG(LOG_ERR, "No configuration file, can't create permanent room\n");
1415
                        error_code = JANUS_AUDIOBRIDGE_ERROR_UNKNOWN_ERROR;
1416
                        g_snprintf(error_cause, 512, "No configuration file, can't create permanent room");
1417 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1418 2512456f Lorenzo Miniero
                }
1419 e5d6528c meetecho
                guint64 room_id = 0;
1420
                json_t *room = json_object_get(root, "room");
1421 94926fcd Andreas Girgensohn
                room_id = json_integer_value(room);
1422
                if(room_id == 0) {
1423
                        JANUS_LOG(LOG_WARN, "Desired room ID is 0, which is not allowed... picking random ID instead\n");
1424 e5d6528c meetecho
                }
1425
                janus_mutex_lock(&rooms_mutex);
1426
                if(room_id > 0) {
1427
                        /* Let's make sure the room doesn't exist already */
1428 d236f0e9 Lorenzo Miniero
                        if(g_hash_table_lookup(rooms, &room_id) != NULL) {
1429 e5d6528c meetecho
                                /* It does... */
1430
                                janus_mutex_unlock(&rooms_mutex);
1431
                                JANUS_LOG(LOG_ERR, "Room %"SCNu64" already exists!\n", room_id);
1432
                                error_code = JANUS_AUDIOBRIDGE_ERROR_ROOM_EXISTS;
1433
                                g_snprintf(error_cause, 512, "Room %"SCNu64" already exists", room_id);
1434 db1e9a1f Lorenzo Miniero
                                goto plugin_response;
1435 e5d6528c meetecho
                        }
1436
                }
1437
                /* Create the audio bridge room */
1438 1f067658 Lorenzo Miniero
                janus_audiobridge_room *audiobridge = g_malloc0(sizeof(janus_audiobridge_room));
1439 e5d6528c meetecho
                /* Generate a random ID */
1440
                if(room_id == 0) {
1441
                        while(room_id == 0) {
1442 d236f0e9 Lorenzo Miniero
                                room_id = janus_random_uint64();
1443
                                if(g_hash_table_lookup(rooms, &room_id) != NULL) {
1444 e5d6528c meetecho
                                        /* Room ID already taken, try another one */
1445
                                        room_id = 0;
1446
                                }
1447
                        }
1448
                }
1449
                audiobridge->room_id = room_id;
1450
                char *description = NULL;
1451 d1302e06 meetecho
                if(desc != NULL && strlen(json_string_value(desc)) > 0) {
1452 e5d6528c meetecho
                        description = g_strdup(json_string_value(desc));
1453
                } else {
1454
                        char roomname[255];
1455
                        g_snprintf(roomname, 255, "Room %"SCNu64"", audiobridge->room_id);
1456
                        description = g_strdup(roomname);
1457
                }
1458
                audiobridge->room_name = description;
1459 0a466e28 janus
                audiobridge->is_private = is_private ? json_is_true(is_private) : FALSE;
1460 e5d6528c meetecho
                if(secret)
1461
                        audiobridge->room_secret = g_strdup(json_string_value(secret));
1462 5d20bcec Lorenzo Miniero
                if(pin)
1463
                        audiobridge->room_pin = g_strdup(json_string_value(pin));
1464 e5d6528c meetecho
                if(sampling)
1465
                        audiobridge->sampling_rate = json_integer_value(sampling);
1466
                else
1467
                        audiobridge->sampling_rate = 16000;
1468 4fb1f6d2 Lorenzo Miniero
                audiobridge->audiolevel_ext = audiolevel_ext ? json_is_true(audiolevel_ext) : TRUE;
1469 0eea38ab meetecho
                switch(audiobridge->sampling_rate) {
1470
                        case 8000:
1471
                        case 12000:
1472
                        case 16000:
1473
                        case 24000:
1474
                        case 48000:
1475
                                JANUS_LOG(LOG_VERB, "Sampling rate for mixing: %"SCNu32"\n", audiobridge->sampling_rate);
1476
                                break;
1477
                        default:
1478
                                janus_mutex_unlock(&rooms_mutex);
1479
                                JANUS_LOG(LOG_ERR, "Unsupported sampling rate %"SCNu32"...\n", audiobridge->sampling_rate);
1480
                                error_code = JANUS_AUDIOBRIDGE_ERROR_UNKNOWN_ERROR;
1481
                                g_snprintf(error_cause, 512, "We currently only support 16kHz (wideband) as a sampling rate for audio rooms, %"SCNu32" TBD...", audiobridge->sampling_rate);
1482 db1e9a1f Lorenzo Miniero
                                goto plugin_response;
1483 e5d6528c meetecho
                }
1484
                audiobridge->record = FALSE;
1485
                if(record && json_is_true(record))
1486
                        audiobridge->record = TRUE;
1487
                if(recfile)
1488
                        audiobridge->record_file = g_strdup(json_string_value(recfile));
1489
                audiobridge->recording = NULL;
1490
                audiobridge->destroy = 0;
1491 d236f0e9 Lorenzo Miniero
                audiobridge->participants = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL);
1492 30df7fbf Lorenzo Miniero
                audiobridge->allowed = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL);
1493
                if(allowed != NULL) {
1494
                        /* Populate the "allowed" list as an ACL for people trying to join */
1495
                        if(json_array_size(allowed) > 0) {
1496
                                size_t i = 0;
1497
                                for(i=0; i<json_array_size(allowed); i++) {
1498
                                        const char *token = json_string_value(json_array_get(allowed, i));
1499
                                        if(!g_hash_table_lookup(audiobridge->allowed, token))
1500
                                                g_hash_table_insert(audiobridge->allowed, g_strdup(token), GINT_TO_POINTER(TRUE));
1501
                                }
1502
                        }
1503
                        audiobridge->check_tokens = TRUE;
1504
                }
1505 dbf63b61 meetecho
                audiobridge->destroyed = 0;
1506 e5d6528c meetecho
                janus_mutex_init(&audiobridge->mutex);
1507 3760d352 Lorenzo Miniero
                audiobridge->rtp_forwarders = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)g_free);
1508
                audiobridge->rtp_encoder = NULL;
1509
                audiobridge->rtp_udp_sock = -1;
1510
                janus_mutex_init(&audiobridge->rtp_mutex);
1511 d236f0e9 Lorenzo Miniero
                g_hash_table_insert(rooms, janus_uint64_dup(audiobridge->room_id), audiobridge);
1512 5d20bcec Lorenzo Miniero
                JANUS_LOG(LOG_VERB, "Created audiobridge: %"SCNu64" (%s, %s, secret: %s, pin: %s)\n",
1513
                        audiobridge->room_id, audiobridge->room_name,
1514
                        audiobridge->is_private ? "private" : "public",
1515
                        audiobridge->room_secret ? audiobridge->room_secret : "no secret",
1516
                        audiobridge->room_pin ? audiobridge->room_pin : "no pin");
1517 e5d6528c meetecho
                /* We need a thread for the mix */
1518 dbf63b61 meetecho
                GError *error = NULL;
1519 c7498fa9 Lorenzo Miniero
                char tname[16];
1520
                g_snprintf(tname, sizeof(tname), "mixer %"SCNu64, audiobridge->room_id);
1521
                audiobridge->thread = g_thread_try_new(tname, &janus_audiobridge_mixer_thread, audiobridge, &error);
1522 dbf63b61 meetecho
                if(error != NULL) {
1523
                        janus_mutex_unlock(&rooms_mutex);
1524
                        JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the mixer thread...\n", error->code, error->message ? error->message : "??");
1525
                        error_code = JANUS_AUDIOBRIDGE_ERROR_UNKNOWN_ERROR;
1526
                        g_snprintf(error_cause, 512, "Got error %d (%s) trying to launch the mixer thread", error->code, error->message ? error->message : "??");
1527
                        g_free(audiobridge->room_name);
1528
                        g_free(audiobridge->room_secret);
1529
                        g_free(audiobridge->record_file);
1530
                        g_hash_table_destroy(audiobridge->participants);
1531 30df7fbf Lorenzo Miniero
                        g_hash_table_destroy(audiobridge->allowed);
1532 dbf63b61 meetecho
                        g_free(audiobridge);
1533 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1534 dbf63b61 meetecho
                } else {
1535 d236f0e9 Lorenzo Miniero
                        g_hash_table_insert(rooms, janus_uint64_dup(audiobridge->room_id), audiobridge);
1536 dbf63b61 meetecho
                }
1537 2512456f Lorenzo Miniero
                if(save) {
1538
                        /* This room is permanent: save to the configuration file too
1539
                         * FIXME: We should check if anything fails... */
1540
                        JANUS_LOG(LOG_VERB, "Saving room %"SCNu64" permanently in config file\n", audiobridge->room_id);
1541
                        janus_mutex_lock(&config_mutex);
1542
                        char cat[BUFSIZ], value[BUFSIZ];
1543
                        /* The room ID is the category */
1544
                        g_snprintf(cat, BUFSIZ, "%"SCNu64, audiobridge->room_id);
1545
                        janus_config_add_category(config, cat);
1546
                        /* Now for the values */
1547
                        janus_config_add_item(config, cat, "description", audiobridge->room_name);
1548
                        if(audiobridge->is_private)
1549
                                janus_config_add_item(config, cat, "is_private", "yes");
1550
                        g_snprintf(value, BUFSIZ, "%"SCNu32, audiobridge->sampling_rate);
1551
                        janus_config_add_item(config, cat, "sampling_rate", value);
1552
                        if(audiobridge->room_secret)
1553
                                janus_config_add_item(config, cat, "secret", audiobridge->room_secret);
1554
                        if(audiobridge->room_pin)
1555
                                janus_config_add_item(config, cat, "pin", audiobridge->room_pin);
1556
                        if(audiobridge->record_file) {
1557
                                janus_config_add_item(config, cat, "record", "yes");
1558
                                janus_config_add_item(config, cat, "record_file", audiobridge->record_file);
1559
                        }
1560
                        /* Save modified configuration */
1561 b6c6c592 Lorenzo Miniero
                        if(janus_config_save(config, config_folder, JANUS_AUDIOBRIDGE_PACKAGE) < 0)
1562
                                save = FALSE;        /* This will notify the user the room is not permanent */
1563 2512456f Lorenzo Miniero
                        janus_mutex_unlock(&config_mutex);
1564
                }
1565 e5d6528c meetecho
                /* Send info back */
1566 011610cd meetecho
                response = json_object();
1567 e5d6528c meetecho
                json_object_set_new(response, "audiobridge", json_string("created"));
1568
                json_object_set_new(response, "room", json_integer(audiobridge->room_id));
1569 4d8c7356 Lorenzo Miniero
                /* Also notify event handlers */
1570 71a04f89 Lorenzo Miniero
                if(notify_events && gateway->events_is_enabled()) {
1571 4d8c7356 Lorenzo Miniero
                        json_t *info = json_object();
1572
                        json_object_set_new(info, "event", json_string("created"));
1573
                        json_object_set_new(info, "room", json_integer(audiobridge->room_id));
1574 b6c6c592 Lorenzo Miniero
                        json_object_set_new(info, "permanent", save ? json_true() : json_false());
1575 c734a91a Lorenzo Miniero
                        gateway->notify_event(&janus_audiobridge_plugin, session->handle, info);
1576 4d8c7356 Lorenzo Miniero
                }
1577 17161000 Lorenzo Miniero
                janus_mutex_unlock(&rooms_mutex);
1578 011610cd meetecho
                goto plugin_response;
1579 e5d6528c meetecho
        } else if(!strcasecmp(request_text, "destroy")) {
1580
                JANUS_LOG(LOG_VERB, "Attempt to destroy an existing audiobridge room\n");
1581 94926fcd Andreas Girgensohn
                JANUS_VALIDATE_JSON_OBJECT(root, destroy_parameters,
1582
                        error_code, error_cause, TRUE,
1583
                        JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
1584
                if(error_code != 0)
1585 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1586 94926fcd Andreas Girgensohn
                json_t *room = json_object_get(root, "room");
1587 2512456f Lorenzo Miniero
                json_t *permanent = json_object_get(root, "permanent");
1588
                gboolean save = permanent ? json_is_true(permanent) : FALSE;
1589
                if(save && config == NULL) {
1590
                        JANUS_LOG(LOG_ERR, "No configuration file, can't destroy room permanently\n");
1591
                        error_code = JANUS_AUDIOBRIDGE_ERROR_UNKNOWN_ERROR;
1592
                        g_snprintf(error_cause, 512, "No configuration file, can't destroy room permanently");
1593 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1594 2512456f Lorenzo Miniero
                }
1595 e5d6528c meetecho
                guint64 room_id = json_integer_value(room);
1596
                janus_mutex_lock(&rooms_mutex);
1597 d236f0e9 Lorenzo Miniero
                janus_audiobridge_room *audiobridge = g_hash_table_lookup(rooms, &room_id);
1598 e5d6528c meetecho
                if(audiobridge == NULL) {
1599
                        janus_mutex_unlock(&rooms_mutex);
1600
                        JANUS_LOG(LOG_ERR, "No such room (%"SCNu64")\n", room_id);
1601
                        error_code = JANUS_AUDIOBRIDGE_ERROR_NO_SUCH_ROOM;
1602
                        g_snprintf(error_cause, 512, "No such room (%"SCNu64")", room_id);
1603 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1604 e5d6528c meetecho
                }
1605 17161000 Lorenzo Miniero
                janus_mutex_lock(&audiobridge->mutex);
1606 94926fcd Andreas Girgensohn
                /* A secret may be required for this action */
1607 806f4d41 Pierce Lopez
                JANUS_CHECK_SECRET(audiobridge->room_secret, root, "secret", error_code, error_cause,
1608 94926fcd Andreas Girgensohn
                        JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_UNAUTHORIZED);
1609
                if(error_code != 0) {
1610 17161000 Lorenzo Miniero
                        janus_mutex_unlock(&audiobridge->mutex);
1611 94926fcd Andreas Girgensohn
                        janus_mutex_unlock(&rooms_mutex);
1612 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1613 e5d6528c meetecho
                }
1614
                /* Remove room */
1615 d236f0e9 Lorenzo Miniero
                g_hash_table_remove(rooms, &room_id);
1616 2512456f Lorenzo Miniero
                if(save) {
1617
                        /* This change is permanent: save to the configuration file too
1618
                         * FIXME: We should check if anything fails... */
1619
                        JANUS_LOG(LOG_VERB, "Destroying room %"SCNu64" permanently in config file\n", room_id);
1620
                        janus_mutex_lock(&config_mutex);
1621
                        char cat[BUFSIZ];
1622
                        /* The room ID is the category */
1623
                        g_snprintf(cat, BUFSIZ, "%"SCNu64, room_id);
1624
                        janus_config_remove_category(config, cat);
1625
                        /* Save modified configuration */
1626
                        janus_config_save(config, config_folder, JANUS_AUDIOBRIDGE_PACKAGE);
1627
                        janus_mutex_unlock(&config_mutex);
1628
                }
1629 e5d6528c meetecho
                /* Prepare response/notification */
1630 011610cd meetecho
                response = json_object();
1631 e5d6528c meetecho
                json_object_set_new(response, "audiobridge", json_string("destroyed"));
1632
                json_object_set_new(response, "room", json_integer(room_id));
1633
                /* Notify all participants that the fun is over, and that they'll be kicked */
1634
                JANUS_LOG(LOG_VERB, "Notifying all participants\n");
1635
                GHashTableIter iter;
1636
                gpointer value;
1637
                g_hash_table_iter_init(&iter, audiobridge->participants);
1638
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
1639
                        janus_audiobridge_participant *p = value;
1640
                        if(p && p->session) {
1641 8f6cdc67 meetecho
                                p->room = NULL;
1642 dd11fa0a Lorenzo Miniero
                                int ret = gateway->push_event(p->session->handle, &janus_audiobridge_plugin, NULL, response, NULL);
1643 e5d6528c meetecho
                                JANUS_LOG(LOG_VERB, "  >> %d (%s)\n", ret, janus_get_api_error(ret));
1644 1b1dd5fe meetecho
                                /* Get rid of queued packets */
1645
                                janus_mutex_lock(&p->qmutex);
1646
                                p->active = FALSE;
1647 3f670174 Lorenzo Miniero
                                while(p->inbuf) {
1648
                                        GList *first = g_list_first(p->inbuf);
1649
                                        janus_audiobridge_rtp_relay_packet *pkt = (janus_audiobridge_rtp_relay_packet *)first->data;
1650
                                        p->inbuf = g_list_remove_link(p->inbuf, first);
1651
                                        first = NULL;
1652 1b1dd5fe meetecho
                                        if(pkt == NULL)
1653
                                                continue;
1654
                                        if(pkt->data)
1655
                                                g_free(pkt->data);
1656
                                        pkt->data = NULL;
1657
                                        g_free(pkt);
1658
                                        pkt = NULL;
1659
                                }
1660
                                janus_mutex_unlock(&p->qmutex);
1661 e5d6528c meetecho
                        }
1662
                }
1663 4d8c7356 Lorenzo Miniero
                /* Also notify event handlers */
1664 71a04f89 Lorenzo Miniero
                if(notify_events && gateway->events_is_enabled()) {
1665 4d8c7356 Lorenzo Miniero
                        json_t *info = json_object();
1666
                        json_object_set_new(info, "event", json_string("destroyed"));
1667
                        json_object_set_new(info, "room", json_integer(room_id));
1668 c734a91a Lorenzo Miniero
                        gateway->notify_event(&janus_audiobridge_plugin, session->handle, info);
1669 4d8c7356 Lorenzo Miniero
                }
1670 dbf63b61 meetecho
                JANUS_LOG(LOG_VERB, "Waiting for the mixer thread to complete...\n");
1671
                audiobridge->destroyed = janus_get_monotonic_time();
1672 17161000 Lorenzo Miniero
                janus_mutex_unlock(&audiobridge->mutex);
1673
                janus_mutex_unlock(&rooms_mutex);
1674 dbf63b61 meetecho
                g_thread_join(audiobridge->thread);
1675 e5d6528c meetecho
                /* Done */
1676 dbf63b61 meetecho
                JANUS_LOG(LOG_VERB, "Audiobridge room destroyed\n");
1677 011610cd meetecho
                goto plugin_response;
1678 e5d6528c meetecho
        } else if(!strcasecmp(request_text, "list")) {
1679 8f05dbe2 meetecho
                /* List all rooms (but private ones) and their details (except for the secret, of course...) */
1680 e5d6528c meetecho
                json_t *list = json_array();
1681
                JANUS_LOG(LOG_VERB, "Request for the list for all video rooms\n");
1682
                janus_mutex_lock(&rooms_mutex);
1683
                GHashTableIter iter;
1684
                gpointer value;
1685
                g_hash_table_iter_init(&iter, rooms);
1686
                while(g_hash_table_iter_next(&iter, NULL, &value)) {
1687
                        janus_audiobridge_room *room = value;
1688 8f05dbe2 meetecho
                        if(!room)
1689
                                continue;
1690 17161000 Lorenzo Miniero
                        janus_mutex_lock(&room->mutex);
1691 0a466e28 janus
                        if(room->is_private) {
1692 8f05dbe2 meetecho
                                /* Skip private room */
1693 17161000 Lorenzo Miniero
                                janus_mutex_unlock(&room->mutex);
1694 8f05dbe2 meetecho
                                JANUS_LOG(LOG_VERB, "Skipping private room '%s'\n", room->room_name);
1695
                                continue;
1696
                        }
1697 e5d6528c meetecho
                        json_t *rl = json_object();
1698
                        json_object_set_new(rl, "room", json_integer(room->room_id));
1699
                        json_object_set_new(rl, "description", json_string(room->room_name));
1700
                        json_object_set_new(rl, "sampling_rate", json_integer(room->sampling_rate));
1701 51db8a86 Lorenzo Miniero
                        json_object_set_new(rl, "record", room->record ? json_true() : json_false());
1702 e5d6528c meetecho
                        /* TODO: Possibly list participant details... or make it a separate API call for a specific room */
1703
                        json_object_set_new(rl, "num_participants", json_integer(g_hash_table_size(room->participants)));
1704
                        json_array_append_new(list, rl);
1705 17161000 Lorenzo Miniero
                        janus_mutex_unlock(&room->mutex);
1706 e5d6528c meetecho
                }
1707
                janus_mutex_unlock(&rooms_mutex);
1708 011610cd meetecho
                response = json_object();
1709 e5d6528c meetecho
                json_object_set_new(response, "audiobridge", json_string("success"));
1710
                json_object_set_new(response, "list", list);
1711 011610cd meetecho
                goto plugin_response;
1712 e5d6528c meetecho
        } else if(!strcasecmp(request_text, "exists")) {
1713 8f05dbe2 meetecho
                /* Check whether a given room exists or not, returns true/false */        
1714 94926fcd Andreas Girgensohn
                JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
1715
                        error_code, error_cause, TRUE,
1716
                        JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
1717
                if(error_code != 0)
1718 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1719 94926fcd Andreas Girgensohn
                json_t *room = json_object_get(root, "room");
1720 e5d6528c meetecho
                guint64 room_id = json_integer_value(room);
1721
                janus_mutex_lock(&rooms_mutex);
1722 d236f0e9 Lorenzo Miniero
                gboolean room_exists = g_hash_table_contains(rooms, &room_id);
1723 e5d6528c meetecho
                janus_mutex_unlock(&rooms_mutex);
1724 011610cd meetecho
                response = json_object();
1725 e5d6528c meetecho
                json_object_set_new(response, "audiobridge", json_string("success"));
1726
                json_object_set_new(response, "room", json_integer(room_id));
1727 51db8a86 Lorenzo Miniero
                json_object_set_new(response, "exists", room_exists ? json_true() : json_false());
1728 011610cd meetecho
                goto plugin_response;
1729 30df7fbf Lorenzo Miniero
        } else if(!strcasecmp(request_text, "allowed")) {
1730
                JANUS_LOG(LOG_VERB, "Attempt to edit the list of allowed participants in an existing audiobridge room\n");
1731
                JANUS_VALIDATE_JSON_OBJECT(root, allowed_parameters,
1732
                        error_code, error_cause, TRUE,
1733
                        JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
1734
                if(error_code != 0)
1735
                        goto plugin_response;
1736
                json_t *action = json_object_get(root, "action");
1737
                json_t *room = json_object_get(root, "room");
1738
                json_t *allowed = json_object_get(root, "allowed");
1739
                const char *action_text = json_string_value(action);
1740
                if(strcasecmp(action_text, "enable") && strcasecmp(action_text, "disable") &&
1741
                                strcasecmp(action_text, "add") && strcasecmp(action_text, "remove")) {
1742
                        JANUS_LOG(LOG_ERR, "Unsupported action '%s' (allowed)\n", action_text);
1743
                        error_code = JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT;
1744
                        g_snprintf(error_cause, 512, "Unsupported action '%s' (allowed)", action_text);
1745
                        goto plugin_response;
1746
                }
1747
                guint64 room_id = json_integer_value(room);
1748
                janus_mutex_lock(&rooms_mutex);
1749
                janus_audiobridge_room *audiobridge = g_hash_table_lookup(rooms, &room_id);
1750
                if(audiobridge == NULL) {
1751
                        janus_mutex_unlock(&rooms_mutex);
1752
                        JANUS_LOG(LOG_ERR, "No such room (%"SCNu64")\n", room_id);
1753
                        error_code = JANUS_AUDIOBRIDGE_ERROR_NO_SUCH_ROOM;
1754
                        g_snprintf(error_cause, 512, "No such room (%"SCNu64")", room_id);
1755
                        goto plugin_response;
1756
                }
1757
                janus_mutex_lock(&audiobridge->mutex);
1758
                /* A secret may be required for this action */
1759
                JANUS_CHECK_SECRET(audiobridge->room_secret, root, "secret", error_code, error_cause,
1760
                        JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_UNAUTHORIZED);
1761
                if(error_code != 0) {
1762
                        janus_mutex_unlock(&audiobridge->mutex);
1763
                        janus_mutex_unlock(&rooms_mutex);
1764
                        goto plugin_response;
1765
                }
1766
                if(!strcasecmp(action_text, "enable")) {
1767
                        JANUS_LOG(LOG_VERB, "Enabling the check on allowed authorization tokens for room %"SCNu64"\n", room_id);
1768
                        audiobridge->check_tokens = TRUE;
1769
                } else if(!strcasecmp(action_text, "disable")) {
1770
                        JANUS_LOG(LOG_VERB, "Disabling the check on allowed authorization tokens for room %"SCNu64" (free entry)\n", room_id);
1771
                        audiobridge->check_tokens = FALSE;
1772
                } else {
1773
                        gboolean add = !strcasecmp(action_text, "add");
1774
                        if(allowed) {
1775
                                /* Make sure the "allowed" array only contains strings */
1776
                                gboolean ok = TRUE;
1777
                                if(json_array_size(allowed) > 0) {
1778
                                        size_t i = 0;
1779
                                        for(i=0; i<json_array_size(allowed); i++) {
1780
                                                json_t *a = json_array_get(allowed, i);
1781
                                                if(!a || !json_is_string(a)) {
1782
                                                        ok = FALSE;
1783
                                                        break;
1784
                                                }
1785
                                        }
1786
                                }
1787
                                if(!ok) {
1788
                                        JANUS_LOG(LOG_ERR, "Invalid element in the allowed array (not a string)\n");
1789
                                        error_code = JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT;
1790
                                        g_snprintf(error_cause, 512, "Invalid element in the allowed array (not a string)");
1791
                                        janus_mutex_unlock(&audiobridge->mutex);
1792
                                        janus_mutex_unlock(&rooms_mutex);
1793
                                        goto plugin_response;
1794
                                }
1795
                                size_t i = 0;
1796
                                for(i=0; i<json_array_size(allowed); i++) {
1797
                                        const char *token = json_string_value(json_array_get(allowed, i));
1798
                                        if(add) {
1799
                                                if(!g_hash_table_lookup(audiobridge->allowed, token))
1800
                                                        g_hash_table_insert(audiobridge->allowed, g_strdup(token), GINT_TO_POINTER(TRUE));
1801
                                        } else {
1802
                                                g_hash_table_remove(audiobridge->allowed, token);
1803
                                        }
1804
                                }
1805
                        }
1806
                }
1807
                /* Prepare response */
1808
                response = json_object();
1809
                json_object_set_new(response, "audiobridge", json_string("success"));
1810
                json_object_set_new(response, "room", json_integer(audiobridge->room_id));
1811
                json_t *list = json_array();
1812
                if(strcasecmp(action_text, "disable")) {
1813
                        if(g_hash_table_size(audiobridge->allowed) > 0) {
1814
                                GHashTableIter iter;
1815
                                gpointer key;
1816
                                g_hash_table_iter_init(&iter, audiobridge->allowed);
1817
                                while(g_hash_table_iter_next(&iter, &key, NULL)) {
1818
                                        char *token = key;
1819
                                        json_array_append_new(list, json_string(token));
1820
                                }
1821
                        }
1822
                        json_object_set_new(response, "allowed", list);
1823
                }
1824
                /* Done */
1825
                janus_mutex_unlock(&audiobridge->mutex);
1826
                janus_mutex_unlock(&rooms_mutex);
1827
                JANUS_LOG(LOG_VERB, "Audiobridge room allowed list updated\n");
1828
                goto plugin_response;
1829
        } else if(!strcasecmp(request_text, "kick")) {
1830
                JANUS_LOG(LOG_VERB, "Attempt to kick a participant from an existing audiobridge room\n");
1831
                JANUS_VALIDATE_JSON_OBJECT(root, kick_parameters,
1832
                        error_code, error_cause, TRUE,
1833
                        JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
1834
                if(error_code != 0)
1835
                        goto plugin_response;
1836
                json_t *room = json_object_get(root, "room");
1837
                json_t *id = json_object_get(root, "id");
1838
                guint64 room_id = json_integer_value(room);
1839
                janus_mutex_lock(&rooms_mutex);
1840
                janus_audiobridge_room *audiobridge = g_hash_table_lookup(rooms, &room_id);
1841
                if(audiobridge == NULL) {
1842
                        janus_mutex_unlock(&rooms_mutex);
1843
                        JANUS_LOG(LOG_ERR, "No such room (%"SCNu64")\n", room_id);
1844
                        error_code = JANUS_AUDIOBRIDGE_ERROR_NO_SUCH_ROOM;
1845
                        g_snprintf(error_cause, 512, "No such room (%"SCNu64")", room_id);
1846
                        goto plugin_response;
1847
                }
1848
                janus_mutex_lock(&audiobridge->mutex);
1849
                /* A secret may be required for this action */
1850
                JANUS_CHECK_SECRET(audiobridge->room_secret, root, "secret", error_code, error_cause,
1851
                        JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_UNAUTHORIZED);
1852
                if(error_code != 0) {
1853
                        janus_mutex_unlock(&audiobridge->mutex);
1854
                        janus_mutex_unlock(&rooms_mutex);
1855
                        goto plugin_response;
1856
                }
1857
                guint64 user_id = json_integer_value(id);
1858
                janus_audiobridge_participant *participant = g_hash_table_lookup(audiobridge->participants, &user_id);
1859
                if(participant == NULL) {
1860
                        janus_mutex_unlock(&audiobridge->mutex);
1861
                        janus_mutex_unlock(&rooms_mutex);
1862
                        JANUS_LOG(LOG_ERR, "No such user %"SCNu64" in room %"SCNu64"\n", user_id, room_id);
1863
                        error_code = JANUS_AUDIOBRIDGE_ERROR_NO_SUCH_USER;
1864
                        g_snprintf(error_cause, 512, "No such user %"SCNu64" in room %"SCNu64, user_id, room_id);
1865
                        goto plugin_response;
1866
                }
1867
                /* Tell the core to tear down the PeerConnection, hangup_media will do the rest */
1868
                if(participant && participant->session)
1869
                        gateway->close_pc(participant->session->handle);
1870
                JANUS_LOG(LOG_VERB, "Kicked user %"SCNu64" from room %"SCNu64"\n", user_id, room_id);
1871
                /* Prepare response */
1872
                response = json_object();
1873
                json_object_set_new(response, "audiobridge", json_string("success"));
1874
                /* Done */
1875
                janus_mutex_unlock(&audiobridge->mutex);
1876
                janus_mutex_unlock(&rooms_mutex);
1877
                goto plugin_response;
1878 0eea38ab meetecho
        } else if(!strcasecmp(request_text, "listparticipants")) {
1879
                /* List all participants in a room */        
1880 94926fcd Andreas Girgensohn
                JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
1881
                        error_code, error_cause, TRUE,
1882
                        JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
1883
                if(error_code != 0)
1884 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1885 94926fcd Andreas Girgensohn
                json_t *room = json_object_get(root, "room");
1886 0eea38ab meetecho
                guint64 room_id = json_integer_value(room);
1887
                janus_mutex_lock(&rooms_mutex);
1888 d236f0e9 Lorenzo Miniero
                janus_audiobridge_room *audiobridge = g_hash_table_lookup(rooms, &room_id);
1889 0eea38ab meetecho
                if(audiobridge == NULL) {
1890 17161000 Lorenzo Miniero
                        janus_mutex_unlock(&rooms_mutex);
1891 0eea38ab meetecho
                        JANUS_LOG(LOG_ERR, "No such room (%"SCNu64")\n", room_id);
1892
                        error_code = JANUS_AUDIOBRIDGE_ERROR_NO_SUCH_ROOM;
1893
                        g_snprintf(error_cause, 512, "No such room (%"SCNu64")", room_id);
1894 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1895 0eea38ab meetecho
                }
1896 17161000 Lorenzo Miniero
                janus_mutex_lock(&audiobridge->mutex);
1897 0eea38ab meetecho
                if(audiobridge->destroyed) {
1898 17161000 Lorenzo Miniero
                        janus_mutex_unlock(&audiobridge->mutex);
1899
                        janus_mutex_unlock(&rooms_mutex);
1900 0eea38ab meetecho
                        JANUS_LOG(LOG_ERR, "No such room (%"SCNu64")\n", room_id);
1901
                        error_code = JANUS_AUDIOBRIDGE_ERROR_NO_SUCH_ROOM;
1902
                        g_snprintf(error_cause, 512, "No such room (%"SCNu64")", room_id);
1903 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1904 0eea38ab meetecho
                }
1905
                /* Return a list of all participants */
1906
                json_t *list = json_array();
1907
                GHashTableIter iter;
1908
                gpointer value;
1909
                g_hash_table_iter_init(&iter, audiobridge->participants);
1910
                while (!audiobridge->destroyed && g_hash_table_iter_next(&iter, NULL, &value)) {
1911
                        janus_audiobridge_participant *p = value;
1912
                        json_t *pl = json_object();
1913
                        json_object_set_new(pl, "id", json_integer(p->user_id));
1914
                        if(p->display)
1915
                                json_object_set_new(pl, "display", json_string(p->display));
1916 51db8a86 Lorenzo Miniero
                        json_object_set_new(pl, "muted", p->muted ? json_true() : json_false());
1917 0eea38ab meetecho
                        json_array_append_new(list, pl);
1918
                }
1919
                janus_mutex_unlock(&audiobridge->mutex);
1920 17161000 Lorenzo Miniero
                janus_mutex_unlock(&rooms_mutex);
1921 011610cd meetecho
                response = json_object();
1922 0eea38ab meetecho
                json_object_set_new(response, "audiobridge", json_string("participants"));
1923
                json_object_set_new(response, "room", json_integer(room_id));
1924
                json_object_set_new(response, "participants", list);
1925 011610cd meetecho
                goto plugin_response;
1926 bf438412 meetecho
        } else if(!strcasecmp(request_text, "resetdecoder")) {
1927
                /* Mark the Opus decoder for the participant invalid and recreate it */        
1928
                janus_audiobridge_participant *participant = (janus_audiobridge_participant *)session->participant;
1929
                if(participant == NULL || participant->room == NULL) {
1930
                        JANUS_LOG(LOG_ERR, "Can't reset (not in a room)\n");
1931
                        error_code = JANUS_AUDIOBRIDGE_ERROR_NOT_JOINED;
1932
                        g_snprintf(error_cause, 512, "Can't reset (not in a room)");
1933 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1934 bf438412 meetecho
                }
1935
                participant->reset = TRUE;
1936
                response = json_object();
1937
                json_object_set_new(response, "audiobridge", json_string("success"));
1938
                goto plugin_response;
1939 3760d352 Lorenzo Miniero
        } else if(!strcasecmp(request_text, "rtp_forward")) {
1940
                JANUS_VALIDATE_JSON_OBJECT(root, rtp_forward_parameters,
1941
                        error_code, error_cause, TRUE,
1942
                        JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
1943
                if(error_code != 0)
1944
                        goto plugin_response;
1945
                /* Parse arguments */
1946
                guint64 room_id = json_integer_value(json_object_get(root, "room"));
1947 0284f49f Lorenzo Miniero
                guint32 ssrc_value = 0;
1948
                json_t *ssrc = json_object_get(root, "ssrc");
1949
                if(ssrc)
1950
                        ssrc_value = json_integer_value(ssrc);
1951 3760d352 Lorenzo Miniero
                int ptype = 100;
1952
                json_t *pt = json_object_get(root, "ptype");
1953
                if(pt)
1954
                        ptype = json_integer_value(pt);
1955
                uint16_t port = json_integer_value(json_object_get(root, "port"));
1956
                json_t *json_host = json_object_get(root, "host");
1957
                const gchar* host = json_string_value(json_host);
1958 79e0e1e2 Lorenzo Miniero
                json_t *always = json_object_get(root, "always_on");
1959
                gboolean always_on = always ? json_is_true(always) : FALSE;
1960 3760d352 Lorenzo Miniero
                /* Update room */
1961
                janus_mutex_lock(&rooms_mutex);
1962
                janus_audiobridge_room *audiobridge = g_hash_table_lookup(rooms, &room_id);
1963
                if(audiobridge == NULL) {
1964
                        janus_mutex_unlock(&rooms_mutex);
1965
                        JANUS_LOG(LOG_ERR, "No such room (%"SCNu64")\n", room_id);
1966
                        error_code = JANUS_AUDIOBRIDGE_ERROR_NO_SUCH_ROOM;
1967
                        g_snprintf(error_cause, 512, "No such room (%"SCNu64")", room_id);
1968
                        goto plugin_response;
1969
                }
1970
                /* A secret may be required for this action */
1971
                JANUS_CHECK_SECRET(audiobridge->room_secret, root, "secret", error_code, error_cause,
1972
                        JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_UNAUTHORIZED);
1973
                if(error_code != 0) {
1974
                        janus_mutex_unlock(&rooms_mutex);
1975
                        goto plugin_response;
1976
                }
1977
                janus_mutex_lock(&audiobridge->mutex);
1978
                if(audiobridge->destroyed) {
1979
                        janus_mutex_unlock(&audiobridge->mutex);
1980
                        janus_mutex_unlock(&rooms_mutex);
1981
                        JANUS_LOG(LOG_ERR, "No such room (%"SCNu64")\n", room_id);
1982
                        error_code = JANUS_AUDIOBRIDGE_ERROR_NO_SUCH_ROOM;
1983
                        g_snprintf(error_cause, 512, "No such room (%"SCNu64")", room_id);
1984
                        goto plugin_response;
1985
                }
1986
                /* Create UDP socket, if needed */
1987
                if(audiobridge->rtp_udp_sock <= 0) {
1988
                        audiobridge->rtp_udp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1989
                        if(audiobridge->rtp_udp_sock <= 0) {
1990
                                janus_mutex_unlock(&audiobridge->mutex);
1991
                                janus_mutex_unlock(&rooms_mutex);
1992
                                JANUS_LOG(LOG_ERR, "Could not open UDP socket for RTP forwarder (room %"SCNu64")\n", room_id);
1993
                                error_code = JANUS_AUDIOBRIDGE_ERROR_UNKNOWN_ERROR;
1994
                                g_snprintf(error_cause, 512, "Could not open UDP socket for RTP forwarder");
1995
                                goto plugin_response;
1996
                        }
1997
                }
1998
                /* Create Opus encoder, if needed */
1999
                if(audiobridge->rtp_encoder == NULL) {
2000
                        int error = 0;
2001
                        audiobridge->rtp_encoder = opus_encoder_create(audiobridge->sampling_rate, 1, OPUS_APPLICATION_VOIP, &error);
2002
                        if(error != OPUS_OK) {
2003
                                janus_mutex_unlock(&audiobridge->mutex);
2004
                                janus_mutex_unlock(&rooms_mutex);
2005
                                JANUS_LOG(LOG_ERR, "Error creating Opus encoder for RTP forwarder (room %"SCNu64")\n", room_id);
2006
                                error_code = JANUS_AUDIOBRIDGE_ERROR_LIBOPUS_ERROR;
2007
                                g_snprintf(error_cause, 512, "Error creating Opus decoder for RTP forwarder");
2008
                                goto plugin_response;
2009
                        }
2010
                        if(audiobridge->sampling_rate == 8000) {
2011
                                opus_encoder_ctl(audiobridge->rtp_encoder, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND));
2012
                        } else if(audiobridge->sampling_rate == 12000) {
2013
                                opus_encoder_ctl(audiobridge->rtp_encoder, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_MEDIUMBAND));
2014
                        } else if(audiobridge->sampling_rate == 16000) {
2015
                                opus_encoder_ctl(audiobridge->rtp_encoder, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND));
2016
                        } else if(audiobridge->sampling_rate == 24000) {
2017
                                opus_encoder_ctl(audiobridge->rtp_encoder, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_SUPERWIDEBAND));
2018
                        } else if(audiobridge->sampling_rate == 48000) {
2019
                                opus_encoder_ctl(audiobridge->rtp_encoder, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND));
2020
                        } else {
2021
                                JANUS_LOG(LOG_WARN, "Unsupported sampling rate %d, setting 16kHz\n", audiobridge->sampling_rate);
2022
                                opus_encoder_ctl(audiobridge->rtp_encoder, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND));
2023
                        }
2024
                }
2025 0284f49f Lorenzo Miniero
                guint32 stream_id = janus_audiobridge_rtp_forwarder_add_helper(audiobridge, host, port, ssrc_value, ptype, always_on);
2026 3760d352 Lorenzo Miniero
                janus_mutex_unlock(&audiobridge->mutex);
2027
                janus_mutex_unlock(&rooms_mutex);
2028
                /* Done, prepare response */
2029
                response = json_object();
2030
                json_object_set_new(response, "audiobridge", json_string("success"));
2031
                json_object_set_new(response, "room", json_integer(room_id));
2032
                json_object_set_new(response, "stream_id", json_integer(stream_id));
2033
                goto plugin_response;
2034
        } else if(!strcasecmp(request_text, "stop_rtp_forward")) {
2035
                JANUS_VALIDATE_JSON_OBJECT(root, stop_rtp_forward_parameters,
2036
                        error_code, error_cause, TRUE,
2037
                        JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
2038
                if(error_code != 0)
2039
                        goto plugin_response;
2040
                /* Parse parameters */
2041
                guint64 room_id = json_integer_value(json_object_get(root, "room"));
2042
                guint32 stream_id = json_integer_value(json_object_get(root, "stream_id"));
2043
                /* Update room */
2044
                /* Update room */
2045
                janus_mutex_lock(&rooms_mutex);
2046
                janus_audiobridge_room *audiobridge = g_hash_table_lookup(rooms, &room_id);
2047
                if(audiobridge == NULL) {
2048
                        janus_mutex_unlock(&rooms_mutex);
2049
                        JANUS_LOG(LOG_ERR, "No such room (%"SCNu64")\n", room_id);
2050
                        error_code = JANUS_AUDIOBRIDGE_ERROR_NO_SUCH_ROOM;
2051
                        g_snprintf(error_cause, 512, "No such room (%"SCNu64")", room_id);
2052
                        goto plugin_response;
2053
                }
2054
                /* A secret may be required for this action */
2055
                JANUS_CHECK_SECRET(audiobridge->room_secret, root, "secret", error_code, error_cause,
2056
                        JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_UNAUTHORIZED);
2057
                if(error_code != 0) {
2058
                        janus_mutex_unlock(&rooms_mutex);
2059
                        goto plugin_response;
2060
                }
2061
                janus_mutex_lock(&audiobridge->mutex);
2062
                if(audiobridge->destroyed) {
2063
                        janus_mutex_unlock(&audiobridge->mutex);
2064
                        janus_mutex_unlock(&rooms_mutex);
2065
                        JANUS_LOG(LOG_ERR, "No such room (%"SCNu64")\n", room_id);
2066
                        error_code = JANUS_AUDIOBRIDGE_ERROR_NO_SUCH_ROOM;
2067
                        g_snprintf(error_cause, 512, "No such room (%"SCNu64")", room_id);
2068
                        goto plugin_response;
2069
                }
2070
                janus_mutex_lock(&audiobridge->rtp_mutex);
2071
                g_hash_table_remove(audiobridge->rtp_forwarders, GUINT_TO_POINTER(stream_id));
2072
                janus_mutex_unlock(&audiobridge->rtp_mutex);
2073
                janus_mutex_unlock(&audiobridge->mutex);
2074
                janus_mutex_unlock(&rooms_mutex);
2075
                response = json_object();
2076
                json_object_set_new(response, "audiobridge", json_string("success"));
2077
                json_object_set_new(response, "room", json_integer(room_id));
2078
                json_object_set_new(response, "stream_id", json_integer(stream_id));
2079
                goto plugin_response;
2080
        } else if(!strcasecmp(request_text, "listforwarders")) {
2081
                /* List all forwarders in a room */
2082
                JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
2083
                        error_code, error_cause, TRUE,
2084
                        JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
2085
                if(error_code != 0)
2086
                        goto plugin_response;
2087
                json_t *room = json_object_get(root, "room");
2088
                guint64 room_id = json_integer_value(room);
2089
                janus_mutex_lock(&rooms_mutex);
2090
                janus_audiobridge_room *audiobridge = g_hash_table_lookup(rooms, &room_id);
2091
                if(audiobridge == NULL) {
2092
                        JANUS_LOG(LOG_ERR, "No such room (%"SCNu64")\n", room_id);
2093
                        error_code = JANUS_AUDIOBRIDGE_ERROR_NO_SUCH_ROOM;
2094
                        g_snprintf(error_cause, 512, "No such room (%"SCNu64")", room_id);
2095
                        janus_mutex_unlock(&rooms_mutex);
2096
                        goto plugin_response;
2097
                }
2098
                if(audiobridge->destroyed) {
2099
                        JANUS_LOG(LOG_ERR, "No such room (%"SCNu64")\n", room_id);
2100
                        error_code = JANUS_AUDIOBRIDGE_ERROR_NO_SUCH_ROOM;
2101
                        g_snprintf(error_cause, 512, "No such room (%"SCNu64")", room_id);
2102
                        janus_mutex_unlock(&rooms_mutex);
2103
                        goto plugin_response;
2104
                }
2105
                /* A secret may be required for this action */
2106
                JANUS_CHECK_SECRET(audiobridge->room_secret, root, "secret", error_code, error_cause,
2107
                        JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_UNAUTHORIZED);
2108
                if(error_code != 0) {
2109
                        janus_mutex_unlock(&rooms_mutex);
2110
                        goto plugin_response;
2111
                }
2112
                /* Return a list of all forwarders */
2113
                json_t *list = json_array();
2114
                GHashTableIter iter;
2115
                gpointer key, value;
2116
                janus_mutex_lock(&audiobridge->rtp_mutex);
2117
                g_hash_table_iter_init(&iter, audiobridge->rtp_forwarders);
2118
                while(g_hash_table_iter_next(&iter, &key, &value)) {
2119
                        guint32 stream_id = GPOINTER_TO_UINT(key);
2120
                        janus_audiobridge_rtp_forwarder *rf = (janus_audiobridge_rtp_forwarder *)value;
2121
                        json_t *fl = json_object();
2122
                        json_object_set_new(fl, "stream_id", json_integer(stream_id));
2123
                        json_object_set_new(fl, "ip", json_string(inet_ntoa(rf->serv_addr.sin_addr)));
2124
                        json_object_set_new(fl, "port", json_integer(ntohs(rf->serv_addr.sin_port)));
2125 0284f49f Lorenzo Miniero
                        json_object_set_new(fl, "ssrc", json_integer(rf->ssrc ? rf->ssrc : stream_id));
2126 3760d352 Lorenzo Miniero
                        json_object_set_new(fl, "ptype", json_integer(rf->payload_type));
2127 79e0e1e2 Lorenzo Miniero
                        json_object_set_new(fl, "always_on", rf->always_on ? json_true() : json_false());
2128 3760d352 Lorenzo Miniero
                        json_array_append_new(list, fl);
2129
                }
2130
                janus_mutex_unlock(&audiobridge->rtp_mutex);
2131
                janus_mutex_unlock(&rooms_mutex);
2132
                response = json_object();
2133
                json_object_set_new(response, "audiobridge", json_string("forwarders"));
2134
                json_object_set_new(response, "room", json_integer(room_id));
2135
                json_object_set_new(response, "rtp_forwarders", list);
2136
                goto plugin_response;
2137 e5d6528c meetecho
        } else if(!strcasecmp(request_text, "join") || !strcasecmp(request_text, "configure")
2138
                        || !strcasecmp(request_text, "changeroom") || !strcasecmp(request_text, "leave")) {
2139
                /* These messages are handled asynchronously */
2140 1f067658 Lorenzo Miniero
                janus_audiobridge_message *msg = g_malloc0(sizeof(janus_audiobridge_message));
2141 011610cd meetecho
                msg->handle = handle;
2142
                msg->transaction = transaction;
2143
                msg->message = root;
2144 dd11fa0a Lorenzo Miniero
                msg->jsep = jsep;
2145 011610cd meetecho
2146
                g_async_queue_push(messages, msg);
2147
2148 dd11fa0a Lorenzo Miniero
                return janus_plugin_result_new(JANUS_PLUGIN_OK_WAIT, NULL, NULL);
2149 e5d6528c meetecho
        } else {
2150
                JANUS_LOG(LOG_VERB, "Unknown request '%s'\n", request_text);
2151
                error_code = JANUS_AUDIOBRIDGE_ERROR_INVALID_REQUEST;
2152
                g_snprintf(error_cause, 512, "Unknown request '%s'", request_text);
2153
        }
2154
2155 011610cd meetecho
plugin_response:
2156
                {
2157 db1e9a1f Lorenzo Miniero
                        if(error_code == 0 && !response) {
2158 011610cd meetecho
                                error_code = JANUS_AUDIOBRIDGE_ERROR_UNKNOWN_ERROR;
2159
                                g_snprintf(error_cause, 512, "Invalid response");
2160 db1e9a1f Lorenzo Miniero
                        }
2161
                        if(error_code != 0) {
2162
                                /* Prepare JSON error event */
2163
                                json_t *event = json_object();
2164
                                json_object_set_new(event, "audiobridge", json_string("event"));
2165
                                json_object_set_new(event, "error_code", json_integer(error_code));
2166
                                json_object_set_new(event, "error", json_string(error_cause));
2167
                                response = event;
2168 011610cd meetecho
                        }
2169
                        if(root != NULL)
2170
                                json_decref(root);
2171 dd11fa0a Lorenzo Miniero
                        if(jsep != NULL)
2172
                                json_decref(jsep);
2173 011610cd meetecho
                        g_free(transaction);
2174
2175 db1e9a1f Lorenzo Miniero
                        return janus_plugin_result_new(JANUS_PLUGIN_OK, NULL, response);
2176 e5d6528c meetecho
                }
2177
2178 be35facb meetecho
}
2179
2180 cfd5c0d6 meetecho
void janus_audiobridge_setup_media(janus_plugin_session *handle) {
2181 3a26e009 meetecho
        JANUS_LOG(LOG_INFO, "WebRTC media is now available\n");
2182 8878e296 meetecho
        if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
2183 be35facb meetecho
                return;
2184
        janus_audiobridge_session *session = (janus_audiobridge_session *)handle->plugin_handle;        
2185
        if(!session) {
2186 3a26e009 meetecho
                JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
2187 be35facb meetecho
                return;
2188
        }
2189 5fa9a305 meetecho
        if(session->destroyed)
2190 be35facb meetecho
                return;
2191 e5d6528c meetecho
        janus_audiobridge_participant *participant = (janus_audiobridge_participant *)session->participant;
2192
        if(!participant)
2193
                return;
2194 3a3cc054 meetecho
        g_atomic_int_set(&session->hangingup, 0);
2195 e5d6528c meetecho
        /* FIXME Only send this peer the audio mix when we get this event */
2196 74cb2f01 meetecho
        session->started = TRUE;
2197 be35facb meetecho
}
2198
2199 cfd5c0d6 meetecho
void janus_audiobridge_incoming_rtp(janus_plugin_session *handle, int video, char *buf, int len) {
2200 8878e296 meetecho
        if(handle == NULL || handle->stopped || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
2201 be35facb meetecho
                return;
2202
        janus_audiobridge_session *session = (janus_audiobridge_session *)handle->plugin_handle;        
2203 5fa9a305 meetecho
        if(!session || session->destroyed || session->stopping || !session->participant)
2204 be35facb meetecho
                return;
2205
        janus_audiobridge_participant *participant = (janus_audiobridge_participant *)session->participant;
2206 bf438412 meetecho
        if(!participant->active || participant->muted || !participant->decoder || !participant->room)
2207 be35facb meetecho
                return;
2208 75d49bbe Lorenzo Miniero
        /* Save the frame if we're recording this leg */
2209
        janus_recorder_save_frame(participant->arc, buf, len);
2210 ce97b4a5 meetecho
        if(participant->active && participant->decoder) {
2211 bf438412 meetecho
                /* First of all, check if a reset on the decoder is due */
2212
                if(participant->reset) {
2213
                        /* Create a new decoder and get rid of the old one */
2214
                        int error = 0;
2215
                        OpusDecoder *decoder = opus_decoder_create(participant->room->sampling_rate, 1, &error);
2216
                        if(error != OPUS_OK) {
2217
                                JANUS_LOG(LOG_ERR, "Error resetting Opus decoder...\n");
2218
                        } else {
2219
                                if(participant->decoder)
2220
                                        opus_decoder_destroy(participant->decoder);
2221
                                participant->decoder = decoder;
2222
                                JANUS_LOG(LOG_VERB, "Opus decoder reset\n");
2223
                        }
2224
                        participant->reset = FALSE;
2225
                }
2226 4a02688f meetecho
                /* Decode frame (Opus -> slinear) */
2227 3f670174 Lorenzo Miniero
                rtp_header *rtp = (rtp_header *)buf;
2228 1f067658 Lorenzo Miniero
                janus_audiobridge_rtp_relay_packet *pkt = g_malloc0(sizeof(janus_audiobridge_rtp_relay_packet));
2229
                pkt->data = g_malloc0(BUFFER_SAMPLES*sizeof(opus_int16));
2230 4a02688f meetecho
                pkt->ssrc = 0;
2231 3f670174 Lorenzo Miniero
                pkt->timestamp = ntohl(rtp->timestamp);
2232
                pkt->seq_number = ntohs(rtp->seq_number);
2233 3cc61ddb Lorenzo Miniero
                /* Any audio level extension to quickly check if this is silence? */
2234
                pkt->silence = FALSE;
2235
                if(participant->extmap_id > 0) {
2236
                        int level = 0;
2237
                        if(janus_rtp_header_extension_parse_audio_level(buf, len, participant->extmap_id, &level) == 0) {
2238
                                pkt->silence = (level == 127);
2239
                                participant->dBov_level = level;
2240
                        }
2241
                }
2242 ce97b4a5 meetecho
                participant->working = TRUE;
2243 3cc61ddb Lorenzo Miniero
                int plen = 0;
2244
                const unsigned char *payload = (const unsigned char *)janus_rtp_payload(buf, len, &plen);
2245
                if(!payload) {
2246
                        JANUS_LOG(LOG_ERR, "[Opus] Ops! got an error accessing the RTP payload\n");
2247
                        g_free(pkt->data);
2248
                        g_free(pkt);
2249
                        return;
2250
                }
2251
                pkt->length = opus_decode(participant->decoder, payload, plen, (opus_int16 *)pkt->data, BUFFER_SAMPLES, USE_FEC);
2252 ce97b4a5 meetecho
                participant->working = FALSE;
2253 0eea38ab meetecho
                if(pkt->length < 0) {
2254
                        JANUS_LOG(LOG_ERR, "[Opus] Ops! got an error decoding the Opus frame: %d (%s)\n", pkt->length, opus_strerror(pkt->length));
2255
                        g_free(pkt->data);
2256
                        g_free(pkt);
2257
                        return;
2258
                }
2259
                /* Enqueue the decoded frame */
2260
                janus_mutex_lock(&participant->qmutex);
2261 3f670174 Lorenzo Miniero
                /* Insert packets sorting by sequence number */
2262
                participant->inbuf = g_list_insert_sorted(participant->inbuf, pkt, &janus_audiobridge_rtp_sort);
2263
                if(participant->prebuffering) {
2264
                        /* Still pre-buffering: do we have enough packets now? */
2265
                        if(g_list_length(participant->inbuf) == DEFAULT_PREBUFFERING) {
2266
                                participant->prebuffering = FALSE;
2267
                                JANUS_LOG(LOG_VERB, "Prebuffering done! Finally adding the user to the mix\n");
2268
                        } else {
2269
                                JANUS_LOG(LOG_VERB, "Still prebuffering (got %d packets), not adding the user to the mix yet\n", g_list_length(participant->inbuf));
2270
                        }
2271
                } else {
2272
                        /* Make sure we're not queueing too many packets: if so, get rid of the older ones */
2273
                        if(g_list_length(participant->inbuf) >= DEFAULT_PREBUFFERING*2) {
2274 4366f7f8 Lorenzo Miniero
                                gint64 now = janus_get_monotonic_time();
2275
                                if(now - participant->last_drop > 5*G_USEC_PER_SEC) {
2276
                                        JANUS_LOG(LOG_WARN, "Too many packets in queue (%d > %d), removing older ones\n",
2277
                                                g_list_length(participant->inbuf), DEFAULT_PREBUFFERING*2);
2278 2a61f5c3 Lorenzo Miniero
                                        participant->last_drop = now;
2279 4366f7f8 Lorenzo Miniero
                                }
2280 3f670174 Lorenzo Miniero
                                while(g_list_length(participant->inbuf) > DEFAULT_PREBUFFERING*2) {
2281
                                        /* Remove this packet: it's too old */
2282
                                        GList *first = g_list_first(participant->inbuf);
2283
                                        janus_audiobridge_rtp_relay_packet *pkt = (janus_audiobridge_rtp_relay_packet *)first->data;
2284
                                        participant->inbuf = g_list_remove_link(participant->inbuf, first);
2285
                                        first = NULL;
2286
                                        if(pkt == NULL)
2287
                                                continue;
2288
                                        if(pkt->data)
2289
                                                g_free(pkt->data);
2290
                                        pkt->data = NULL;
2291
                                        g_free(pkt);
2292
                                        pkt = NULL;
2293
                                }
2294 cc6f0e73 meetecho
                        }
2295
                }
2296 0eea38ab meetecho
                janus_mutex_unlock(&participant->qmutex);
2297 be35facb meetecho
        }
2298
}
2299
2300 cfd5c0d6 meetecho
void janus_audiobridge_incoming_rtcp(janus_plugin_session *handle, int video, char *buf, int len) {
2301 8878e296 meetecho
        if(handle == NULL || handle->stopped || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
2302 be35facb meetecho
                return;
2303
        /* FIXME Should we care? */
2304
}
2305
2306 cfd5c0d6 meetecho
void janus_audiobridge_hangup_media(janus_plugin_session *handle) {
2307 3a26e009 meetecho
        JANUS_LOG(LOG_INFO, "No WebRTC media anymore\n");
2308 8878e296 meetecho
        if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
2309 be35facb meetecho
                return;
2310
        janus_audiobridge_session *session = (janus_audiobridge_session *)handle->plugin_handle;
2311
        if(!session) {
2312 3a26e009 meetecho
                JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
2313 be35facb meetecho
                return;
2314
        }
2315 78955474 meetecho
        session->started = FALSE;
2316 3a3cc054 meetecho
        if(session->destroyed || !session->participant)
2317
                return;
2318
        if(g_atomic_int_add(&session->hangingup, 1))
2319 be35facb meetecho
                return;
2320
        /* Get rid of participant */
2321
        janus_audiobridge_participant *participant = (janus_audiobridge_participant *)session->participant;
2322 17161000 Lorenzo Miniero
        janus_mutex_lock(&rooms_mutex);
2323 be35facb meetecho
        janus_audiobridge_room *audiobridge = participant->room;
2324 8f6cdc67 meetecho
        if(audiobridge != NULL) {
2325
                janus_mutex_lock(&audiobridge->mutex);
2326
                json_t *event = json_object();
2327
                json_object_set_new(event, "audiobridge", json_string("event"));
2328
                json_object_set_new(event, "room", json_integer(audiobridge->room_id));
2329
                json_object_set_new(event, "leaving", json_integer(participant->user_id));
2330 d236f0e9 Lorenzo Miniero
                g_hash_table_remove(audiobridge->participants, &participant->user_id);
2331 8f6cdc67 meetecho
                GHashTableIter iter;
2332
                gpointer value;
2333
                g_hash_table_iter_init(&iter, audiobridge->participants);
2334
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
2335
                        janus_audiobridge_participant *p = value;
2336
                        if(p == participant) {
2337
                                continue;        /* Skip the leaving participant itself */
2338
                        }
2339
                        JANUS_LOG(LOG_VERB, "Notifying participant %"SCNu64" (%s)\n", p->user_id, p->display ? p->display : "??");
2340 dd11fa0a Lorenzo Miniero
                        int ret = gateway->push_event(p->session->handle, &janus_audiobridge_plugin, NULL, event, NULL);
2341 8f6cdc67 meetecho
                        JANUS_LOG(LOG_VERB, "  >> %d (%s)\n", ret, janus_get_api_error(ret));
2342 be35facb meetecho
                }
2343 dd11fa0a Lorenzo Miniero
                json_decref(event);
2344 4d8c7356 Lorenzo Miniero
                /* Also notify event handlers */
2345 71a04f89 Lorenzo Miniero
                if(notify_events && gateway->events_is_enabled()) {
2346 4d8c7356 Lorenzo Miniero
                        json_t *info = json_object();
2347
                        json_object_set_new(info, "event", json_string("left"));
2348
                        json_object_set_new(info, "room", json_integer(audiobridge->room_id));
2349
                        json_object_set_new(info, "id", json_integer(participant->user_id));
2350 d7e1c12a Lorenzo Miniero
                        json_object_set_new(info, "display", json_string(participant->display));
2351 c734a91a Lorenzo Miniero
                        gateway->notify_event(&janus_audiobridge_plugin, session->handle, info);
2352 4d8c7356 Lorenzo Miniero
                }
2353 be35facb meetecho
        }
2354 75d49bbe Lorenzo Miniero
        /* Get rid of the recorders, if available */
2355
        janus_mutex_lock(&participant->rec_mutex);
2356
        if(participant->arc) {
2357
                janus_recorder_close(participant->arc);
2358
                JANUS_LOG(LOG_INFO, "Closed user's audio recording %s\n", participant->arc->filename ? participant->arc->filename : "??");
2359
                janus_recorder_free(participant->arc);
2360
        }
2361
        participant->arc = NULL;
2362
        janus_mutex_unlock(&participant->rec_mutex);
2363 0eea38ab meetecho
        /* Free the participant resources */
2364
        janus_mutex_lock(&participant->qmutex);
2365 e5d6528c meetecho
        participant->active = FALSE;
2366 74cb2f01 meetecho
        participant->muted = TRUE;
2367
        if(participant->display)
2368
                g_free(participant->display);
2369
        participant->display = NULL;
2370 3f670174 Lorenzo Miniero
        participant->prebuffering = TRUE;
2371 ce97b4a5 meetecho
        /* Make sure we're not using the encoder/decoder right now, we're going to destroy them */
2372
        while(participant->working)
2373
                g_usleep(5000);
2374 74cb2f01 meetecho
        if(participant->encoder)
2375
                opus_encoder_destroy(participant->encoder);
2376
        participant->encoder = NULL;
2377
        if(participant->decoder)
2378
                opus_decoder_destroy(participant->decoder);
2379
        participant->decoder = NULL;
2380 bf438412 meetecho
        participant->reset = FALSE;
2381 3a26e009 meetecho
        /* Get rid of queued packets */
2382 3f670174 Lorenzo Miniero
        while(participant->inbuf) {
2383
                GList *first = g_list_first(participant->inbuf);
2384
                janus_audiobridge_rtp_relay_packet *pkt = (janus_audiobridge_rtp_relay_packet *)first->data;
2385
                participant->inbuf = g_list_remove_link(participant->inbuf, first);
2386
                first = NULL;
2387 3a26e009 meetecho
                if(pkt == NULL)
2388
                        continue;
2389
                if(pkt->data)
2390
                        g_free(pkt->data);
2391
                pkt->data = NULL;
2392
                g_free(pkt);
2393
                pkt = NULL;
2394
        }
2395 4366f7f8 Lorenzo Miniero
        participant->last_drop = 0;
2396 3a26e009 meetecho
        janus_mutex_unlock(&participant->qmutex);
2397 8f6cdc67 meetecho
        if(audiobridge != NULL) {
2398
                janus_mutex_unlock(&audiobridge->mutex);
2399
        }
2400 17161000 Lorenzo Miniero
        janus_mutex_unlock(&rooms_mutex);
2401 be35facb meetecho
}
2402
2403
/* Thread to handle incoming messages */
2404
static void *janus_audiobridge_handler(void *data) {
2405 febef1ea meetecho
        JANUS_LOG(LOG_VERB, "Joining AudioBridge handler thread\n");
2406 be35facb meetecho
        janus_audiobridge_message *msg = NULL;
2407 3cc3cab3 meetecho
        int error_code = 0;
2408 94926fcd Andreas Girgensohn
        char error_cause[512];
2409 ead45815 meetecho
        json_t *root = NULL;
2410 dbf63b61 meetecho
        while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) {
2411 2e28ee7b Lorenzo Miniero
                msg = g_async_queue_pop(messages);
2412
                if(msg == NULL)
2413
                        continue;
2414 b9e8b01f Lorenzo Miniero
                if(msg == &exit_message)
2415
                        break;
2416 2e28ee7b Lorenzo Miniero
                if(msg->handle == NULL) {
2417
                        janus_audiobridge_message_free(msg);
2418 be35facb meetecho
                        continue;
2419
                }
2420 638695a1 Lorenzo Miniero
                janus_audiobridge_session *session = NULL;
2421
                janus_mutex_lock(&sessions_mutex);
2422 17161000 Lorenzo Miniero
                if(g_hash_table_lookup(sessions, msg->handle) != NULL) {
2423 638695a1 Lorenzo Miniero
                        session = (janus_audiobridge_session *)msg->handle->plugin_handle;
2424
                }
2425
                janus_mutex_unlock(&sessions_mutex);
2426 be35facb meetecho
                if(!session) {
2427 3a26e009 meetecho
                        JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
2428
                        janus_audiobridge_message_free(msg);
2429 be35facb meetecho
                        continue;
2430
                }
2431 5fa9a305 meetecho
                if(session->destroyed) {
2432 3a26e009 meetecho
                        janus_audiobridge_message_free(msg);
2433 be35facb meetecho
                        continue;
2434 3a26e009 meetecho
                }
2435 be35facb meetecho
                /* Handle request */
2436 3cc3cab3 meetecho
                error_code = 0;
2437 ead45815 meetecho
                root = NULL;
2438 be35facb meetecho
                if(msg->message == NULL) {
2439 3a26e009 meetecho
                        JANUS_LOG(LOG_ERR, "No message??\n");
2440 3cc3cab3 meetecho
                        error_code = JANUS_AUDIOBRIDGE_ERROR_NO_MESSAGE;
2441 c94052c2 meetecho
                        g_snprintf(error_cause, 512, "%s", "No message??");
2442 be35facb meetecho
                        goto error;
2443
                }
2444 e5d6528c meetecho
                root = msg->message;
2445 be35facb meetecho
                /* Get the request first */
2446 94926fcd Andreas Girgensohn
                JANUS_VALIDATE_JSON_OBJECT(root, request_parameters,
2447
                        error_code, error_cause, TRUE,
2448
                        JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
2449
                if(error_code != 0)
2450 aff9f5c0 meetecho
                        goto error;
2451 94926fcd Andreas Girgensohn
                json_t *request = json_object_get(root, "request");
2452 be35facb meetecho
                const char *request_text = json_string_value(request);
2453
                json_t *event = NULL;
2454 e5d6528c meetecho
                if(!strcasecmp(request_text, "join")) {
2455
                        JANUS_LOG(LOG_VERB, "Configuring new participant\n");
2456 8f6cdc67 meetecho
                        janus_audiobridge_participant *participant = session->participant;
2457
                        if(participant != NULL && participant->room != NULL) {
2458
                                JANUS_LOG(LOG_ERR, "Already in a room (use changeroom to join another one)\n");
2459
                                error_code = JANUS_AUDIOBRIDGE_ERROR_ALREADY_JOINED;
2460
                                g_snprintf(error_cause, 512, "Already in a room (use changeroom to join another one)");
2461
                                goto error;
2462
                        }
2463 94926fcd Andreas Girgensohn
                        JANUS_VALIDATE_JSON_OBJECT(root, join_parameters,
2464
                                error_code, error_cause, TRUE,
2465
                                JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT);
2466
                        if(error_code != 0)
2467 be35facb meetecho
                                goto error;
2468 94926fcd Andreas Girgensohn
                        json_t *room = json_object_get(root, "room");
2469 be35facb meetecho
                        guint64 room_id = json_integer_value(room);
2470 3a26e009 meetecho
                        janus_mutex_lock(&rooms_mutex);
2471 d236f0e9 Lorenzo Miniero
                        janus_audiobridge_room *audiobridge = g_hash_table_lookup(rooms, &room_id);
2472 be35facb meetecho
                        if(audiobridge == NULL) {
2473 3a26e009 meetecho
                                janus_mutex_unlock(&rooms_mutex);
2474
                                JANUS_LOG(LOG_ERR, "No such room (%"SCNu64")\n", room_id);
2475 3cc3cab3 meetecho
                                error_code = JANUS_AUDIOBRIDGE_ERROR_NO_SUCH_ROOM;
2476 c94052c2 meetecho
                                g_snprintf(error_cause, 512, "No such room (%"SCNu64")", room_id);
2477 be35facb meetecho
                                goto error;
2478
                        }
2479 17161000 Lorenzo Miniero
                        janus_mutex_lock(&audiobridge->mutex);
2480 94926fcd Andreas Girgensohn
                        /* A pin may be required for this action */
2481 806f4d41 Pierce Lopez
                        JANUS_CHECK_SECRET(audiobridge->room_pin, root, "pin", error_code, error_cause,
2482 94926fcd Andreas Girgensohn
                                JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_UNAUTHORIZED);
2483
                        if(error_code != 0) {
2484 17161000 Lorenzo Miniero
                                janus_mutex_unlock(&audiobridge->mutex);
2485 94926fcd Andreas Girgensohn
                                janus_mutex_unlock(&rooms_mutex);
2486
                                goto error;
2487 5d20bcec Lorenzo Miniero
                        }
2488 30df7fbf Lorenzo Miniero
                        /* A token might be required too */
2489
                        if(audiobridge->check_tokens) {
2490
                                json_t *token = json_object_get(root, "token");
2491
                                const char *token_text = token ? json_string_value(token) : NULL;
2492
                                if(token_text == NULL || g_hash_table_lookup(audiobridge->allowed, token_text) == NULL) {
2493
                                        JANUS_LOG(LOG_ERR, "Unauthorized (not in the allowed list)\n");
2494
                                        error_code = JANUS_AUDIOBRIDGE_ERROR_UNAUTHORIZED;
2495
                                        g_snprintf(error_cause, 512, "Unauthorized (not in the allowed list)");
2496
                                        janus_mutex_unlock(&audiobridge->mutex);
2497
                                        janus_mutex_unlock(&rooms_mutex);
2498
                                        goto error;
2499
                                }
2500
                        }
2501 e5d6528c meetecho
                        json_t *display = json_object_get(root, "display");
2502
                        const char *display_text = display ? json_string_value(display) : NULL;
2503
                        json_t *muted = json_object_get(root, "muted");
2504 c057fb41 Lorenzo Miniero
                        json_t *gain = json_object_get(root, "volume");
2505 e5d6528c meetecho
                        json_t *quality = json_object_get(root, "quality");
2506 c057fb41 Lorenzo Miniero
                        int volume = gain ? json_integer_value(gain) : 100;
2507 e5d6528c meetecho
                        int complexity = quality ? json_integer_value(quality) : DEFAULT_COMPLEXITY;
2508
                        if(complexity < 1 || complexity > 10) {
2509 17161000 Lorenzo Miniero
                                janus_mutex_unlock(&audiobridge->mutex);
2510
                                janus_mutex_unlock(&rooms_mutex);
2511 9fad3f14 meetecho
                                JANUS_LOG(LOG_ERR, "Invalid element (quality should be a positive integer between 1 and 10)\n");
2512 3cc3cab3 meetecho
                                error_code = JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT;
2513 9fad3f14 meetecho
                                g_snprintf(error_cause, 512, "Invalid element (quality should be a positive integer between 1 and 10)");
2514 be35facb meetecho
                                goto error;
2515
                        }
2516
                        guint64 user_id = 0;
2517 e5d6528c meetecho
                        json_t *id = json_object_get(root, "id");
2518
                        if(id) {
2519
                                user_id = json_integer_value(id);
2520 d236f0e9 Lorenzo Miniero
                                if(g_hash_table_lookup(audiobridge->participants, &user_id) != NULL) {
2521 e5d6528c meetecho
                                        /* User ID already taken */
2522 17161000 Lorenzo Miniero
                                        janus_mutex_unlock(&audiobridge->mutex);
2523
                                        janus_mutex_unlock(&rooms_mutex);
2524 e5d6528c meetecho
                                        JANUS_LOG(LOG_ERR, "User ID %"SCNu64" already exists\n", user_id);
2525
                                        error_code = JANUS_AUDIOBRIDGE_ERROR_ID_EXISTS;
2526
                                        g_snprintf(error_cause, 512, "User ID %"SCNu64" already exists", user_id);
2527
                                        goto error;
2528
                                }
2529
                        }
2530
                        if(user_id == 0) {
2531
                                /* Generate a random ID */
2532
                                while(user_id == 0) {
2533 d236f0e9 Lorenzo Miniero
                                        user_id = janus_random_uint64();
2534
                                        if(g_hash_table_lookup(audiobridge->participants, &user_id) != NULL) {
2535 e5d6528c meetecho
                                                /* User ID already taken, try another one */
2536
                                                user_id = 0;
2537
                                        }
2538 be35facb meetecho
                                }
2539
                        }
2540 3a26e009 meetecho
                        JANUS_LOG(LOG_VERB, "  -- Participant ID: %"SCNu64"\n", user_id);
2541 be35facb meetecho
                        if(participant == NULL) {
2542 1f067658 Lorenzo Miniero
                                participant = g_malloc0(sizeof(janus_audiobridge_participant));
2543 74cb2f01 meetecho
                                participant->active = FALSE;
2544 3f670174 Lorenzo Miniero
                                participant->prebuffering = TRUE;
2545 8f6cdc67 meetecho
                                participant->display = NULL;
2546
                                participant->inbuf = NULL;
2547 4a02688f meetecho
                                participant->outbuf = NULL;
2548 4366f7f8 Lorenzo Miniero
                                participant->last_drop = 0;
2549 8f6cdc67 meetecho
                                participant->encoder = NULL;
2550
                                participant->decoder = NULL;
2551 bf438412 meetecho
                                participant->reset = FALSE;
2552 8f6cdc67 meetecho
                                janus_mutex_init(&participant->qmutex);
2553 75d49bbe Lorenzo Miniero
                                participant->arc = NULL;
2554
                                janus_mutex_init(&participant->rec_mutex);
2555 be35facb meetecho
                        }
2556
                        participant->session = session;
2557
                        participant->room = audiobridge;
2558
                        participant->user_id = user_id;
2559 8f6cdc67 meetecho
                        if(participant->display != NULL)
2560
                                g_free(participant->display);
2561 af40a682 meetecho
                        participant->display = display_text ? g_strdup(display_text) : NULL;
2562 e5d6528c meetecho
                        participant->muted = muted ? json_is_true(muted) : FALSE;        /* By default, everyone's unmuted when joining */
2563 c057fb41 Lorenzo Miniero
                        participant->volume_gain = volume;
2564 e5d6528c meetecho
                        participant->opus_complexity = complexity;
2565 4a02688f meetecho
                        if(participant->outbuf == NULL)
2566
                                participant->outbuf = g_async_queue_new();
2567 74cb2f01 meetecho
                        participant->active = session->started;
2568
                        if(!session->started) {
2569
                                /* Initialize the RTP context only if we're renegotiating */
2570
                                participant->context.a_last_ssrc = 0;
2571
                                participant->context.a_last_ts = 0;
2572
                                participant->context.a_base_ts = 0;
2573
                                participant->context.a_base_ts_prev = 0;
2574
                                participant->context.a_last_seq = 0;
2575
                                participant->context.a_base_seq = 0;
2576
                                participant->context.a_base_seq_prev = 0;
2577
                                participant->opus_pt = 0;
2578 3cc61ddb Lorenzo Miniero
                                participant->extmap_id = 0;
2579
                                participant->dBov_level = 0;
2580 74cb2f01 meetecho
                        }
2581 3a26e009 meetecho
                        JANUS_LOG(LOG_VERB, "Creating Opus encoder/decoder (sampling rate %d)\n", audiobridge->sampling_rate);
2582 be35facb