Statistics
| Branch: | Revision:

janus-gateway / plugins / janus_videoroom.c @ 5de69b28

History | View | Annotate | Download (168 KB)

1 be35facb meetecho
/*! \file   janus_videoroom.c
2
 * \author Lorenzo Miniero <lorenzo@meetecho.com>
3 9d11ac50 meetecho
 * \copyright GNU General Public License v3
4 be35facb meetecho
 * \brief  Janus VideoRoom plugin
5 550a36f0 meetecho
 * \details  This is a plugin implementing a videoconferencing SFU
6
 * (Selective Forwarding Unit) for Janus, that is an audio/video router.
7 be35facb meetecho
 * This means that the plugin implements a virtual conferencing room peers
8
 * can join and leave at any time. This room is based on a Publish/Subscribe
9
 * pattern. Each peer can publish his/her own live audio/video feeds: this
10
 * feed becomes an available stream in the room the other participants can
11
 * attach to. This means that this plugin allows the realization of several
12
 * different scenarios, ranging from a simple webinar (one speaker, several
13
 * listeners) to a fully meshed video conference (each peer sending and
14
 * receiving to and from all the others).
15 39b52c9a mirkobrankovic
 * 
16 3f0b4067 meetecho
 * For what concerns the subscriber side, there are two different ways to
17
 * attach to a publisher's feed: a generic 'listener', which can attach to
18
 * a single feed, and a more complex 'Multiplexed listener', which instead can
19
 * attach to more feeds using the same PeerConnection. The generic 'listener'
20
 * is the default, which means that if you want to watch more feeds at the
21
 * same time, you'll need to create multiple 'listeners' to attach at any
22
 * of them. The 'Multiplexed listener', instead, is a more complex alternative
23
 * that exploits the so called RTCWEB 'Plan B', which multiplexes more
24
 * streams on a single PeerConnection and in the SDP: while more efficient in terms of
25
 * resources, though, this approach is experimental, and currently only
26
 * available on Google Chrome, so use it wisely.
27
 * \note As of now, work on Plan B is still going on, and as such its support in Janus
28
 * is flaky to say the least. Don't try to attach as a Multiplexed listener or bad
29
 * things will probably happen!
30 39b52c9a mirkobrankovic
 * 
31 be35facb meetecho
 * Considering that this plugin allows for several different WebRTC PeerConnections
32
 * to be on at the same time for the same peer (specifically, each peer
33
 * potentially has 1 PeerConnection on for publishing and N on for subscriptions
34
 * from other peers), each peer may need to attach several times to the same
35
 * plugin for every stream: this means that each peer needs to have at least one
36
 * handle active for managing its relation with the plugin (joining a room,
37
 * leaving a room, muting/unmuting, publishing, receiving events), and needs
38
 * to open a new one each time he/she wants to subscribe to a feed from
39 3f0b4067 meetecho
 * another participant (or a single one in case a 'Multiplexed listener is used).
40
 * The handle used for a subscription, however, would be logically a "slave"
41
 * to the master one used for managing the room: this means that it cannot
42
 * be used, for instance, to unmute in the room, as its only purpose would
43
 * be to provide a context in which creating the sendonly PeerConnection
44
 * for the subscription to the active participant.
45 39b52c9a mirkobrankovic
 * 
46 be35facb meetecho
 * Rooms to make available are listed in the plugin configuration file.
47
 * A pre-filled configuration file is provided in \c conf/janus.plugin.videoroom.cfg
48 cfd5c0d6 meetecho
 * and includes a demo room for testing. The same plugin is also used
49
 * dynamically (that is, with rooms created on the fly via API) in the
50
 * Screen Sharing demo as well.
51 39b52c9a mirkobrankovic
 * 
52 be35facb meetecho
 * To add more rooms or modify the existing one, you can use the following
53
 * syntax:
54 39b52c9a mirkobrankovic
 * 
55 be35facb meetecho
 * \verbatim
56
[<unique room ID>]
57
description = This is my awesome room
58 0a466e28 janus
is_private = yes|no (private rooms don't appear when you do a 'list' request)
59 5d20bcec Lorenzo Miniero
secret = <optional password needed for manipulating (e.g. destroying) the room>
60
pin = <optional password needed for joining the room>
61 be35facb meetecho
publishers = <max number of concurrent senders> (e.g., 6 for a video
62 39b52c9a mirkobrankovic
             conference or 1 for a webinar)
63 be35facb meetecho
bitrate = <max video bitrate for senders> (e.g., 128000)
64 5e9e29e0 meetecho
fir_freq = <send a FIR to publishers every fir_freq seconds> (0=disable)
65 183d715f Lorenzo Miniero
audiocodec = opus|isac32|isac16|pcmu|pcma|g722 (audio codec to force on publishers, default=opus)
66 fc23d811 Lorenzo Miniero
videocodec = vp8|vp9|h264 (video codec to force on publishers, default=vp8)
67 ef61c470 Lorenzo Miniero
audiolevel_ext = yes|no (whether the ssrc-audio-level RTP extension must be
68
        negotiated/used or not for new publishers, default=yes)
69 bd0e3260 mirkobrankovic
audiolevel_event = yes|no (whether to emit event to other users or not)
70
audio_active_packets = 100 (number of packets with audio level, default=100, 2 seconds)
71
audio_level_average = 25 (average value of audio level, 127=muted, 0='too loud', default=25)
72 ef61c470 Lorenzo Miniero
videoorientation_ext = yes|no (whether the video-orientation RTP extension must be
73
        negotiated/used or not for new publishers, default=yes)
74
playoutdelay_ext = yes|no (whether the playout-delay RTP extension must be
75
        negotiated/used or not for new publishers, default=yes)
76 9d11ac50 meetecho
record = true|false (whether this room should be recorded, default=false)
77
rec_dir = <folder where recordings should be stored, when enabled>
78 be35facb meetecho
\endverbatim
79
 *
80 02eeeba4 Lorenzo Miniero
 * Note that recording will work with all codecs except iSAC.
81 fc23d811 Lorenzo Miniero
 *
82 550a36f0 meetecho
 * \section sfuapi Video Room API
83 39b52c9a mirkobrankovic
 * 
84 02fac6b4 meetecho
 * The Video Room API supports several requests, some of which are
85
 * synchronous and some asynchronous. There are some situations, though,
86
 * (invalid JSON, invalid request) which will always result in a
87 39b52c9a mirkobrankovic
 * synchronous error response even for asynchronous requests. 
88
 * 
89 30df7fbf Lorenzo Miniero
 * \c create , \c destroy , \c exists, \c list, \c allowed, \c kick and
90
 * and \c listparticipants are synchronous requests, which means you'll
91 02fac6b4 meetecho
 * get a response directly within the context of the transaction.
92
 * \c create allows you to create a new video room dynamically, as an
93
 * alternative to using the configuration file; \c destroy removes a
94
 * video room and destroys it, kicking all the users out as part of the
95
 * process; \c exists allows you to check whether a specific video room
96
 * exists; finally, \c list lists all the available rooms, while \c
97
 * listparticipants lists all the participants of a specific room and
98 ce97b4a5 meetecho
 * their details.
99 39b52c9a mirkobrankovic
 * 
100 02fac6b4 meetecho
 * The \c join , \c joinandconfigure , \c configure , \c publish ,
101
 * \c unpublish , \c start , \c pause , \c switch , \c stop , \c add ,
102
 * \c remove and \c leave requests instead are all asynchronous, which
103
 * means you'll get a notification about their success or failure in
104
 * an event. \c join allows you to join a specific video room, specifying
105
 * whether that specific PeerConnection will be used for publishing or
106
 * watching; \c configure can be used to modify some of the participation
107
 * settings (e.g., bitrate cap); \c joinandconfigure combines the previous
108
 * two requests in a single one (just for publishers); \c publish can be
109
 * used to start sending media to broadcast to the other participants,
110
 * while \c unpublish does the opposite; \c start allows you to start
111
 * receiving media from a publisher you've subscribed to previously by
112
 * means of a \c join , while \c pause pauses the delivery of the media;
113
 * the \c switch request can be used to change the source of the media
114
 * flowing over a specific PeerConnection (e.g., I was watching Alice,
115
 * I want to watch Bob now) without having to create a new handle for
116 183d715f Lorenzo Miniero
 * that; \c stop interrupts a viewer instance; finally, \c leave allows
117
 * you to leave a video room for good.
118 927d88c5 pallab-gain
 * 
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 39b52c9a mirkobrankovic
 * 
125 02fac6b4 meetecho
 * Actual API docs: TBD.
126 39b52c9a mirkobrankovic
 * 
127 be35facb meetecho
 * \ingroup plugins
128
 * \ref plugins
129
 */
130
131
#include "plugin.h"
132
133
#include <jansson.h>
134
135 6bb3f34d Nicholas Wylie
#include "../debug.h"
136 3a26e009 meetecho
#include "../apierror.h"
137 be35facb meetecho
#include "../config.h"
138 3a26e009 meetecho
#include "../mutex.h"
139 3f0b4067 meetecho
#include "../rtp.h"
140 be35facb meetecho
#include "../rtcp.h"
141 9d11ac50 meetecho
#include "../record.h"
142 825cd223 Lorenzo Miniero
#include "../sdp-utils.h"
143 5e9e29e0 meetecho
#include "../utils.h"
144 eedcc449 Computician
#include <sys/types.h>
145
#include <sys/socket.h>
146 be35facb meetecho
147
148
/* Plugin information */
149 3cc61ddb Lorenzo Miniero
#define JANUS_VIDEOROOM_VERSION                        8
150
#define JANUS_VIDEOROOM_VERSION_STRING        "0.0.8"
151 550a36f0 meetecho
#define JANUS_VIDEOROOM_DESCRIPTION                "This is a plugin implementing a videoconferencing SFU (Selective Forwarding Unit) for Janus, that is an audio/video router."
152 be35facb meetecho
#define JANUS_VIDEOROOM_NAME                        "JANUS VideoRoom plugin"
153 a3d13e20 meetecho
#define JANUS_VIDEOROOM_AUTHOR                        "Meetecho s.r.l."
154 be35facb meetecho
#define JANUS_VIDEOROOM_PACKAGE                        "janus.plugin.videoroom"
155
156
/* Plugin methods */
157
janus_plugin *create(void);
158
int janus_videoroom_init(janus_callbacks *callback, const char *config_path);
159
void janus_videoroom_destroy(void);
160 667b2005 meetecho
int janus_videoroom_get_api_compatibility(void);
161 be35facb meetecho
int janus_videoroom_get_version(void);
162
const char *janus_videoroom_get_version_string(void);
163
const char *janus_videoroom_get_description(void);
164
const char *janus_videoroom_get_name(void);
165 a3d13e20 meetecho
const char *janus_videoroom_get_author(void);
166 be35facb meetecho
const char *janus_videoroom_get_package(void);
167 cfd5c0d6 meetecho
void janus_videoroom_create_session(janus_plugin_session *handle, int *error);
168 dd11fa0a Lorenzo Miniero
struct janus_plugin_result *janus_videoroom_handle_message(janus_plugin_session *handle, char *transaction, json_t *message, json_t *jsep);
169 cfd5c0d6 meetecho
void janus_videoroom_setup_media(janus_plugin_session *handle);
170
void janus_videoroom_incoming_rtp(janus_plugin_session *handle, int video, char *buf, int len);
171
void janus_videoroom_incoming_rtcp(janus_plugin_session *handle, int video, char *buf, int len);
172 83e88860 Ancor Gonzalez Sosa
void janus_videoroom_incoming_data(janus_plugin_session *handle, char *buf, int len);
173 24c01741 meetecho
void janus_videoroom_slow_link(janus_plugin_session *handle, int uplink, int video);
174 cfd5c0d6 meetecho
void janus_videoroom_hangup_media(janus_plugin_session *handle);
175
void janus_videoroom_destroy_session(janus_plugin_session *handle, int *error);
176 dd11fa0a Lorenzo Miniero
json_t *janus_videoroom_query_session(janus_plugin_session *handle);
177 be35facb meetecho
178
/* Plugin setup */
179
static janus_plugin janus_videoroom_plugin =
180 1281ca86 meetecho
        JANUS_PLUGIN_INIT (
181 be35facb meetecho
                .init = janus_videoroom_init,
182
                .destroy = janus_videoroom_destroy,
183
184 667b2005 meetecho
                .get_api_compatibility = janus_videoroom_get_api_compatibility,
185 be35facb meetecho
                .get_version = janus_videoroom_get_version,
186
                .get_version_string = janus_videoroom_get_version_string,
187
                .get_description = janus_videoroom_get_description,
188
                .get_name = janus_videoroom_get_name,
189 a3d13e20 meetecho
                .get_author = janus_videoroom_get_author,
190 be35facb meetecho
                .get_package = janus_videoroom_get_package,
191 39b52c9a mirkobrankovic
                
192 be35facb meetecho
                .create_session = janus_videoroom_create_session,
193
                .handle_message = janus_videoroom_handle_message,
194
                .setup_media = janus_videoroom_setup_media,
195
                .incoming_rtp = janus_videoroom_incoming_rtp,
196
                .incoming_rtcp = janus_videoroom_incoming_rtcp,
197 83e88860 Ancor Gonzalez Sosa
                .incoming_data = janus_videoroom_incoming_data,
198 24c01741 meetecho
                .slow_link = janus_videoroom_slow_link,
199 be35facb meetecho
                .hangup_media = janus_videoroom_hangup_media,
200
                .destroy_session = janus_videoroom_destroy_session,
201 667b2005 meetecho
                .query_session = janus_videoroom_query_session,
202 1281ca86 meetecho
        );
203 be35facb meetecho
204
/* Plugin creator */
205
janus_plugin *create(void) {
206 3a26e009 meetecho
        JANUS_LOG(LOG_VERB, "%s created!\n", JANUS_VIDEOROOM_NAME);
207 be35facb meetecho
        return &janus_videoroom_plugin;
208
}
209
210 887df302 Andreas Girgensohn
/* Parameter validation */
211
static struct janus_json_parameter request_parameters[] = {
212
        {"request", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
213
};
214 930a4a4c Lorenzo Miniero
static struct janus_json_parameter adminkey_parameters[] = {
215
        {"admin_key", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
216
};
217 887df302 Andreas Girgensohn
static struct janus_json_parameter create_parameters[] = {
218
        {"room", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
219
        {"description", JSON_STRING, 0},
220
        {"is_private", JANUS_JSON_BOOL, 0},
221 30df7fbf Lorenzo Miniero
        {"allowed", JSON_ARRAY, 0},
222 8ef4486f Pierce Lopez
        {"secret", JSON_STRING, 0},
223 887df302 Andreas Girgensohn
        {"pin", JSON_STRING, 0},
224
        {"bitrate", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
225
        {"fir_freq", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
226
        {"publishers", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
227
        {"audiocodec", JSON_STRING, 0},
228
        {"videocodec", JSON_STRING, 0},
229 ef61c470 Lorenzo Miniero
        {"audiolevel_ext", JANUS_JSON_BOOL, 0},
230 bd0e3260 mirkobrankovic
        {"audiolevel_event", JANUS_JSON_BOOL, 0},
231
        {"audio_active_packets", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
232
        {"audio_level_average", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
233 ef61c470 Lorenzo Miniero
        {"videoorient_ext", JANUS_JSON_BOOL, 0},
234
        {"playoutdelay_ext", JANUS_JSON_BOOL, 0},
235 887df302 Andreas Girgensohn
        {"record", JANUS_JSON_BOOL, 0},
236
        {"rec_dir", JSON_STRING, 0},
237
        {"permanent", JANUS_JSON_BOOL, 0}
238
};
239
static struct janus_json_parameter room_parameters[] = {
240
        {"room", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
241
};
242
static struct janus_json_parameter destroy_parameters[] = {
243
        {"room", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
244
        {"permanent", JANUS_JSON_BOOL, 0}
245
};
246 30df7fbf Lorenzo Miniero
static struct janus_json_parameter allowed_parameters[] = {
247
        {"room", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
248
        {"secret", JSON_STRING, 0},
249
        {"action", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
250
        {"allowed", JSON_ARRAY, 0}
251
};
252
static struct janus_json_parameter kick_parameters[] = {
253
        {"room", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
254
        {"secret", JSON_STRING, 0},
255
        {"id", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
256
};
257 887df302 Andreas Girgensohn
static struct janus_json_parameter join_parameters[] = {
258
        {"room", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
259
        {"ptype", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
260
        {"audio", JANUS_JSON_BOOL, 0},
261
        {"video", JANUS_JSON_BOOL, 0},
262 7713bf1c Lorenzo Miniero
        {"data", JANUS_JSON_BOOL, 0},
263 887df302 Andreas Girgensohn
        {"bitrate", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
264
        {"record", JANUS_JSON_BOOL, 0},
265
        {"filename", JSON_STRING, 0}
266
};
267
static struct janus_json_parameter publish_parameters[] = {
268
        {"audio", JANUS_JSON_BOOL, 0},
269
        {"video", JANUS_JSON_BOOL, 0},
270 7713bf1c Lorenzo Miniero
        {"data", JANUS_JSON_BOOL, 0},
271 887df302 Andreas Girgensohn
        {"bitrate", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
272
        {"record", JANUS_JSON_BOOL, 0},
273 76bbd360 Chad Phillips
        {"filename", JSON_STRING, 0},
274
        {"display", JSON_STRING, 0}
275 887df302 Andreas Girgensohn
};
276
static struct janus_json_parameter rtp_forward_parameters[] = {
277
        {"room", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
278
        {"publisher_id", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
279 ae62ca8f Lorenzo Miniero
        {"video_port", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
280 0284f49f Lorenzo Miniero
        {"video_ssrc", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
281
        {"video_pt", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
282 ae62ca8f Lorenzo Miniero
        {"audio_port", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
283 0284f49f Lorenzo Miniero
        {"audio_ssrc", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
284
        {"audio_pt", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
285 39b52c9a mirkobrankovic
        {"data_port", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
286 887df302 Andreas Girgensohn
        {"host", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
287
};
288
static struct janus_json_parameter stop_rtp_forward_parameters[] = {
289
        {"room", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
290
        {"publisher_id", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
291
        {"stream_id", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
292
};
293
static struct janus_json_parameter publisher_parameters[] = {
294
        {"id", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
295
        {"display", JSON_STRING, 0}
296
};
297
static struct janus_json_parameter configure_parameters[] = {
298
        {"audio", JANUS_JSON_BOOL, 0},
299
        {"video", JANUS_JSON_BOOL, 0},
300
        {"data", JANUS_JSON_BOOL, 0}
301
};
302
static struct janus_json_parameter listener_parameters[] = {
303
        {"feed", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
304 5d2deea8 Lorenzo Miniero
        {"private_id", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
305 887df302 Andreas Girgensohn
        {"audio", JANUS_JSON_BOOL, 0},
306
        {"video", JANUS_JSON_BOOL, 0},
307
        {"data", JANUS_JSON_BOOL, 0}
308
};
309 be35facb meetecho
310 2512456f Lorenzo Miniero
/* Static configuration instance */
311
static janus_config *config = NULL;
312
static const char *config_folder = NULL;
313
static janus_mutex config_mutex;
314
315 be35facb meetecho
/* Useful stuff */
316 4239218c meetecho
static volatile gint initialized = 0, stopping = 0;
317 71a04f89 Lorenzo Miniero
static gboolean notify_events = TRUE;
318 be35facb meetecho
static janus_callbacks *gateway = NULL;
319
static GThread *handler_thread;
320 dbf63b61 meetecho
static GThread *watchdog;
321 be35facb meetecho
static void *janus_videoroom_handler(void *data);
322
static void janus_videoroom_relay_rtp_packet(gpointer data, gpointer user_data);
323 83e88860 Ancor Gonzalez Sosa
static void janus_videoroom_relay_data_packet(gpointer data, gpointer user_data);
324 be35facb meetecho
325
typedef enum janus_videoroom_p_type {
326
        janus_videoroom_p_type_none = 0,
327 3f0b4067 meetecho
        janus_videoroom_p_type_subscriber,                        /* Generic listener/subscriber */
328
        janus_videoroom_p_type_publisher,                        /* Participant/publisher */
329 be35facb meetecho
} janus_videoroom_p_type;
330
331
typedef struct janus_videoroom_message {
332 cfd5c0d6 meetecho
        janus_plugin_session *handle;
333 be35facb meetecho
        char *transaction;
334 d24aff33 meetecho
        json_t *message;
335 dd11fa0a Lorenzo Miniero
        json_t *jsep;
336 be35facb meetecho
} janus_videoroom_message;
337 7000b914 Philip Withnall
static GAsyncQueue *messages = NULL;
338 b9e8b01f Lorenzo Miniero
static janus_videoroom_message exit_message;
339 be35facb meetecho
340 76ce5502 Philip Withnall
static void janus_videoroom_message_free(janus_videoroom_message *msg) {
341 90e0cdb4 Lorenzo Miniero
        if(!msg || msg == &exit_message)
342 3a26e009 meetecho
                return;
343 76ce5502 Philip Withnall
344 3a26e009 meetecho
        msg->handle = NULL;
345 76ce5502 Philip Withnall
346
        g_free(msg->transaction);
347 3a26e009 meetecho
        msg->transaction = NULL;
348 d24aff33 meetecho
        if(msg->message)
349
                json_decref(msg->message);
350 3a26e009 meetecho
        msg->message = NULL;
351 dd11fa0a Lorenzo Miniero
        if(msg->jsep)
352
                json_decref(msg->jsep);
353
        msg->jsep = NULL;
354 76ce5502 Philip Withnall
355 3a26e009 meetecho
        g_free(msg);
356
}
357
358 183d715f Lorenzo Miniero
/* Payload types we'll offer internally */
359
#define OPUS_PT                111
360
#define ISAC32_PT        104
361
#define ISAC16_PT        103
362
#define PCMU_PT                0
363
#define PCMA_PT                8
364
#define G722_PT                9
365
#define VP8_PT                96
366
#define VP9_PT                101
367
#define H264_PT                107
368
369 ba99f281 pallab-gain
typedef enum janus_videoroom_audiocodec {
370 927d88c5 pallab-gain
        JANUS_VIDEOROOM_OPUS,                /* Publishers will have to use OPUS         */
371 ba99f281 pallab-gain
        JANUS_VIDEOROOM_ISAC_32K,        /* Publishers will have to use ISAC 32K */
372
        JANUS_VIDEOROOM_ISAC_16K,        /* Publishers will have to use ISAC 16K */
373 927d88c5 pallab-gain
        JANUS_VIDEOROOM_PCMU,                /* Publishers will have to use PCMU 8K         */
374 183d715f Lorenzo Miniero
        JANUS_VIDEOROOM_PCMA,                /* Publishers will have to use PCMA 8K         */
375
        JANUS_VIDEOROOM_G722                /* Publishers will have to use G.722         */
376 ba99f281 pallab-gain
} janus_videoroom_audiocodec;
377
static const char *janus_videoroom_audiocodec_name(janus_videoroom_audiocodec acodec) {
378
        switch(acodec) {
379
                case JANUS_VIDEOROOM_OPUS:
380
                        return "opus";
381
                case JANUS_VIDEOROOM_ISAC_32K:
382
                        return "isac32";
383
                case JANUS_VIDEOROOM_ISAC_16K:
384
                        return "isac16";
385
                case JANUS_VIDEOROOM_PCMU:
386
                        return "pcmu";
387 fd8cb2d5 pallab-gain
                case JANUS_VIDEOROOM_PCMA:
388
                        return "pcma";
389 183d715f Lorenzo Miniero
                case JANUS_VIDEOROOM_G722:
390
                        return "g722";
391 ba99f281 pallab-gain
                default:
392
                        /* Shouldn't happen */
393
                        return "opus";
394
        }
395
}
396 183d715f Lorenzo Miniero
static int janus_videoroom_audiocodec_pt(janus_videoroom_audiocodec acodec) {
397
        switch(acodec) {
398
                case JANUS_VIDEOROOM_OPUS:
399
                        return OPUS_PT;
400
                case JANUS_VIDEOROOM_ISAC_32K:
401
                        return ISAC32_PT;
402
                case JANUS_VIDEOROOM_ISAC_16K:
403
                        return ISAC16_PT;
404
                case JANUS_VIDEOROOM_PCMU:
405
                        return PCMU_PT;
406
                case JANUS_VIDEOROOM_PCMA:
407
                        return PCMA_PT;
408
                case JANUS_VIDEOROOM_G722:
409
                        return G722_PT;
410
                default:
411
                        /* Shouldn't happen */
412
                        return OPUS_PT;
413
        }
414
}
415 ba99f281 pallab-gain
416 fd8cb2d5 pallab-gain
typedef enum janus_videoroom_videocodec {
417
        JANUS_VIDEOROOM_VP8,        /* Publishers will have to use VP8 */
418
        JANUS_VIDEOROOM_VP9,        /* Publishers will have to use VP9 */
419
        JANUS_VIDEOROOM_H264        /* Publishers will have to use H264 */
420
} janus_videoroom_videocodec;
421
static const char *janus_videoroom_videocodec_name(janus_videoroom_videocodec vcodec) {
422
        switch(vcodec) {
423
                case JANUS_VIDEOROOM_VP8:
424
                        return "vp8";
425
                case JANUS_VIDEOROOM_VP9:
426
                        return "vp9";
427
                case JANUS_VIDEOROOM_H264:
428
                        return "h264";
429
                default:
430
                        /* Shouldn't happen */
431
                        return "vp8";
432
        }
433
}
434 183d715f Lorenzo Miniero
static int janus_videoroom_videocodec_pt(janus_videoroom_videocodec vcodec) {
435
        switch(vcodec) {
436
                case JANUS_VIDEOROOM_VP8:
437
                        return VP8_PT;
438
                case JANUS_VIDEOROOM_VP9:
439
                        return VP9_PT;
440
                case JANUS_VIDEOROOM_H264:
441
                        return H264_PT;
442
                default:
443
                        /* Shouldn't happen */
444
                        return VP8_PT;
445
        }
446
}
447 fd8cb2d5 pallab-gain
448 be35facb meetecho
typedef struct janus_videoroom {
449 af40a682 meetecho
        guint64 room_id;                        /* Unique room ID */
450
        gchar *room_name;                        /* Room description */
451
        gchar *room_secret;                        /* Secret needed to manipulate (e.g., destroy) this room */
452 5d20bcec Lorenzo Miniero
        gchar *room_pin;                        /* Password needed to join this room, if any */
453 fc23d811 Lorenzo Miniero
        gboolean is_private;                /* Whether this room is 'private' (as in hidden) or not */
454 af40a682 meetecho
        int max_publishers;                        /* Maximum number of concurrent publishers */
455
        uint64_t bitrate;                        /* Global bitrate limit */
456
        uint16_t fir_freq;                        /* Regular FIR frequency (0=disabled) */
457 ba99f281 pallab-gain
        janus_videoroom_audiocodec acodec;        /* Audio codec to force on publishers*/
458 78229f3f pallab-gain
        janus_videoroom_videocodec vcodec;        /* Video codec to force on publishers*/
459 ef61c470 Lorenzo Miniero
        gboolean audiolevel_ext;        /* Whether the ssrc-audio-level extension must be negotiated or not for new publishers */
460 bd0e3260 mirkobrankovic
        gboolean audiolevel_event;        /* Whether to emit event to other users about audiolevel */
461
        int audio_active_packets;        /* amount of packets with audio level for checkup */
462
        int audio_level_average;        /* average audio level */
463 ef61c470 Lorenzo Miniero
        gboolean videoorient_ext;        /* Whether the video-orientation extension must be negotiated or not for new publishers */
464
        gboolean playoutdelay_ext;        /* Whether the playout-delay extension must be negotiated or not for new publishers */
465 9d11ac50 meetecho
        gboolean record;                        /* Whether the feeds from publishers in this room should be recorded */
466
        char *rec_dir;                                /* Where to save the recordings of this room, if enabled */
467 78955474 meetecho
        gint64 destroyed;                        /* Value to flag the room for destruction, done lazily */
468 be35facb meetecho
        GHashTable *participants;        /* Map of potential publishers (we get listeners from them) */
469 543cffca Lorenzo Miniero
        GHashTable *private_ids;        /* Map of existing private IDs */
470 30df7fbf Lorenzo Miniero
        gboolean check_tokens;                /* Whether to check tokens when participants join (see below) */
471
        GHashTable *allowed;                /* Map of participants (as tokens) allowed to join */
472 46d28722 Computician
        janus_mutex participants_mutex;/* Mutex to protect room properties */
473 be35facb meetecho
} janus_videoroom;
474 506f60ae Jack Leigh
static GHashTable *rooms;
475
static janus_mutex rooms_mutex;
476 598aac14 Computician
static GList *old_rooms;
477 930a4a4c Lorenzo Miniero
static char *admin_key = NULL;
478 a444c736 Philip Withnall
static void janus_videoroom_free(janus_videoroom *room);
479
480 be35facb meetecho
typedef struct janus_videoroom_session {
481 cfd5c0d6 meetecho
        janus_plugin_session *handle;
482 be35facb meetecho
        janus_videoroom_p_type participant_type;
483
        gpointer participant;
484
        gboolean started;
485
        gboolean stopping;
486 4239218c meetecho
        volatile gint hangingup;
487 78955474 meetecho
        gint64 destroyed;        /* Time at which this session was marked as destroyed */
488 be35facb meetecho
} janus_videoroom_session;
489 506f60ae Jack Leigh
static GHashTable *sessions;
490 c94052c2 meetecho
static GList *old_sessions;
491 506f60ae Jack Leigh
static janus_mutex sessions_mutex;
492 be35facb meetecho
493 e62ea677 Computician
/* a host whose ports gets streamed rtp packets of the corresponding type. */
494 0284f49f Lorenzo Miniero
typedef struct janus_videoroom_rtp_forwarder {
495 25d3a827 Lorenzo Miniero
        gboolean is_video;
496 39b52c9a mirkobrankovic
        gboolean is_data;
497 0284f49f Lorenzo Miniero
        uint32_t ssrc;
498
        int payload_type;
499 e62ea677 Computician
        struct sockaddr_in serv_addr;
500 0284f49f Lorenzo Miniero
} janus_videoroom_rtp_forwarder;
501 eedcc449 Computician
502 be35facb meetecho
typedef struct janus_videoroom_participant {
503
        janus_videoroom_session *session;
504
        janus_videoroom *room;        /* Room */
505
        guint64 user_id;        /* Unique ID in the room */
506 543cffca Lorenzo Miniero
        guint32 pvt_id;                /* This is sent to the publisher for mapping purposes, but shouldn't be shared with others */
507 5d2deea8 Lorenzo Miniero
        gchar *display;                /* Display name (just for fun) */
508 be35facb meetecho
        gchar *sdp;                        /* The SDP this publisher negotiated, if any */
509 ca2ccd4a meetecho
        gboolean audio, video, data;                /* Whether audio, video and/or data is going to be sent by this publisher */
510 fc23d811 Lorenzo Miniero
        guint32 audio_pt;                /* Audio payload type (Opus) */
511
        guint32 video_pt;                /* Video payload type (depends on room configuration) */
512 3f0b4067 meetecho
        guint32 audio_ssrc;                /* Audio SSRC of this publisher */
513
        guint32 video_ssrc;                /* Video SSRC of this publisher */
514 c70cb37b Lorenzo Miniero
        guint8 audio_level_extmap_id;        /* Audio level extmap ID */
515
        guint8 video_orient_extmap_id;        /* Video orientation extmap ID */
516
        guint8 playout_delay_extmap_id;        /* Playout delay extmap ID */
517 be35facb meetecho
        gboolean audio_active;
518
        gboolean video_active;
519 87cbc63b mirkobrankovic
        int audio_active_packets; /* participants number of audio packets to accumulate */
520
        int audio_dBov_sum;        /* participants accumulated dBov value for audio level*/
521 7713bf1c Lorenzo Miniero
        gboolean data_active;
522 5e9e29e0 meetecho
        gboolean firefox;        /* We send Firefox users a different kind of FIR */
523 be35facb meetecho
        uint64_t bitrate;
524 fbbf4fbb meetecho
        gint64 remb_startup;/* Incremental changes on REMB to reach the target at startup */
525 d60a9646 meetecho
        gint64 remb_latest;        /* Time of latest sent REMB (to avoid flooding) */
526 be35facb meetecho
        gint64 fir_latest;        /* Time of latest sent FIR (to avoid flooding) */
527
        gint fir_seq;                /* FIR sequence number */
528 bf24339c meetecho
        gboolean recording_active;        /* Whether this publisher has to be recorded or not */
529
        gchar *recording_base;        /* Base name for the recording (e.g., /path/to/filename, will generate /path/to/filename-audio.mjr and/or /path/to/filename-video.mjr */
530 9d11ac50 meetecho
        janus_recorder *arc;        /* The Janus recorder instance for this publisher's audio, if enabled */
531
        janus_recorder *vrc;        /* The Janus recorder instance for this publisher's video, if enabled */
532 39b52c9a mirkobrankovic
        janus_recorder *drc;        /* The Janus recorder instance for this publisher's data, if enabled */
533 d223cef1 Lorenzo Miniero
        janus_mutex rec_mutex;        /* Mutex to protect the recorders from race conditions */
534 be35facb meetecho
        GSList *listeners;
535 72639557 meetecho
        janus_mutex listeners_mutex;
536 e62ea677 Computician
        GHashTable *rtp_forwarders;
537
        janus_mutex rtp_forwarders_mutex;
538
        int udp_sock; /* The udp socket on which to forward rtp packets */
539 7273e5d8 Lorenzo Miniero
        gboolean kicked;        /* Whether this participant has been kicked */
540 be35facb meetecho
} janus_videoroom_participant;
541 a444c736 Philip Withnall
static void janus_videoroom_participant_free(janus_videoroom_participant *p);
542 0284f49f Lorenzo Miniero
static void janus_videoroom_rtp_forwarder_free_helper(gpointer data);
543 db1114b3 Lorenzo Miniero
static guint32 janus_videoroom_rtp_forwarder_add_helper(janus_videoroom_participant *p,
544
        const gchar* host, int port, int pt, uint32_t ssrc, gboolean is_video, gboolean is_data);
545 cd28bdc7 meetecho
546 be35facb meetecho
typedef struct janus_videoroom_listener {
547
        janus_videoroom_session *session;
548
        janus_videoroom *room;        /* Room */
549
        janus_videoroom_participant *feed;        /* Participant this listener is subscribed to */
550 543cffca Lorenzo Miniero
        guint32 pvt_id;                /* Private ID of the participant that is subscribing (if available/provided) */
551 e8323e14 Lorenzo Miniero
        janus_rtp_switching_context context;        /* Needed in case there are publisher switches on this listener */
552 ca2ccd4a meetecho
        gboolean audio, video, data;                /* Whether audio, video and/or data must be sent to this publisher */
553 be35facb meetecho
        gboolean paused;
554
} janus_videoroom_listener;
555 a444c736 Philip Withnall
static void janus_videoroom_listener_free(janus_videoroom_listener *l);
556
557 be35facb meetecho
typedef struct janus_videoroom_rtp_relay_packet {
558 cd28bdc7 meetecho
        rtp_header *data;
559 be35facb meetecho
        gint length;
560 0284f49f Lorenzo Miniero
        gboolean is_video;
561 cd28bdc7 meetecho
        uint32_t timestamp;
562
        uint16_t seq_number;
563 be35facb meetecho
} janus_videoroom_rtp_relay_packet;
564
565
566 3cc3cab3 meetecho
/* Error codes */
567
#define JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR                499
568
#define JANUS_VIDEOROOM_ERROR_NO_MESSAGE                421
569
#define JANUS_VIDEOROOM_ERROR_INVALID_JSON                422
570
#define JANUS_VIDEOROOM_ERROR_INVALID_REQUEST        423
571
#define JANUS_VIDEOROOM_ERROR_JOIN_FIRST                424
572
#define JANUS_VIDEOROOM_ERROR_ALREADY_JOINED        425
573
#define JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM                426
574 57d02a2d meetecho
#define JANUS_VIDEOROOM_ERROR_ROOM_EXISTS                427
575
#define JANUS_VIDEOROOM_ERROR_NO_SUCH_FEED                428
576
#define JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT        429
577
#define JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT        430
578
#define JANUS_VIDEOROOM_ERROR_INVALID_SDP_TYPE        431
579
#define JANUS_VIDEOROOM_ERROR_PUBLISHERS_FULL        432
580
#define JANUS_VIDEOROOM_ERROR_UNAUTHORIZED                433
581
#define JANUS_VIDEOROOM_ERROR_ALREADY_PUBLISHED        434
582
#define JANUS_VIDEOROOM_ERROR_NOT_PUBLISHED                435
583 ab5622d0 meetecho
#define JANUS_VIDEOROOM_ERROR_ID_EXISTS                        436
584 8878e296 meetecho
#define JANUS_VIDEOROOM_ERROR_INVALID_SDP                437
585 3cc3cab3 meetecho
586 39b52c9a mirkobrankovic
587 db1114b3 Lorenzo Miniero
static guint32 janus_videoroom_rtp_forwarder_add_helper(janus_videoroom_participant *p,
588
                const gchar* host, int port, int pt, uint32_t ssrc, gboolean is_video, gboolean is_data) {
589 e62ea677 Computician
        if(!p || !host) {
590
                return 0;
591
        }
592 0284f49f Lorenzo Miniero
        janus_videoroom_rtp_forwarder *forward = g_malloc0(sizeof(janus_videoroom_rtp_forwarder));
593 e62ea677 Computician
        forward->is_video = is_video;
594 0284f49f Lorenzo Miniero
        forward->payload_type = pt;
595
        forward->ssrc = ssrc;
596 39b52c9a mirkobrankovic
        forward->is_data = is_data;
597 e62ea677 Computician
        forward->serv_addr.sin_family = AF_INET;
598
        inet_pton(AF_INET, host, &(forward->serv_addr.sin_addr));
599
        forward->serv_addr.sin_port = htons(port);
600
        janus_mutex_lock(&p->rtp_forwarders_mutex);
601 d236f0e9 Lorenzo Miniero
        guint32 stream_id = janus_random_uint32();
602 e62ea677 Computician
        while(g_hash_table_lookup(p->rtp_forwarders, GUINT_TO_POINTER(stream_id)) != NULL) {
603 d236f0e9 Lorenzo Miniero
                stream_id = janus_random_uint32();
604 e62ea677 Computician
        }
605
        g_hash_table_insert(p->rtp_forwarders, GUINT_TO_POINTER(stream_id), forward);
606
        janus_mutex_unlock(&p->rtp_forwarders_mutex);
607 39b52c9a mirkobrankovic
        JANUS_LOG(LOG_VERB, "Added %s rtp_forward to participant %"SCNu64" host: %s:%d stream_id: %"SCNu32"\n",
608
                is_data ? "data" : (is_video ? "video" : "audio"), p->user_id, host, port, stream_id);
609 e62ea677 Computician
        return stream_id;
610
}
611
612
613 554cc3a5 Computician
/* Convenience function for freeing a session */
614
static void session_free(gpointer data) {
615
        if(data) {
616
                janus_videoroom_session* session = (janus_videoroom_session*)data;
617
                switch(session->participant_type) {
618 39b52c9a mirkobrankovic
                case janus_videoroom_p_type_publisher: 
619 554cc3a5 Computician
                        janus_videoroom_participant_free(session->participant);
620 39b52c9a mirkobrankovic
                        break;   
621 554cc3a5 Computician
                case janus_videoroom_p_type_subscriber:
622
                        janus_videoroom_listener_free(session->participant);
623
                        break;
624
                default:
625
                        break;
626
                }
627
                session->handle = NULL;
628
                g_free(session);
629 bb1c6d6d Benjamin Trent
                session = NULL;
630 554cc3a5 Computician
        }
631
}
632
633 0284f49f Lorenzo Miniero
static void janus_videoroom_rtp_forwarder_free_helper(gpointer data) {
634 eedcc449 Computician
        if(data) {
635 0284f49f Lorenzo Miniero
                janus_videoroom_rtp_forwarder* forward = (janus_videoroom_rtp_forwarder*)data;
636 e62ea677 Computician
                if(forward) {
637 1f067658 Lorenzo Miniero
                        g_free(forward);
638 e62ea677 Computician
                        forward = NULL;
639 eedcc449 Computician
                }
640
        }
641
}
642
643 554cc3a5 Computician
/* Convenience wrapper function for session_free that corresponds to GHRFunc() format for hash table cleanup */
644
static gboolean session_hash_table_remove(gpointer key, gpointer value, gpointer not_used) {
645
        if(value) {
646
                session_free(value);
647
        }
648
        return TRUE;
649
}
650 3f0b4067 meetecho
651 5fa9a305 meetecho
/* VideoRoom watchdog/garbage collector (sort of) */
652 e13f2687 Lorenzo Miniero
static void *janus_videoroom_watchdog(void *data) {
653 5fa9a305 meetecho
        JANUS_LOG(LOG_INFO, "VideoRoom watchdog started\n");
654 598aac14 Computician
        gint64 now = 0, room_now = 0;
655 dbf63b61 meetecho
        while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) {
656 c94052c2 meetecho
                janus_mutex_lock(&sessions_mutex);
657
                /* Iterate on all the participants/listeners and check if we need to remove any of them */
658
                now = janus_get_monotonic_time();
659
                if(old_sessions != NULL) {
660
                        GList *sl = old_sessions;
661 1281ca86 meetecho
                        JANUS_LOG(LOG_HUGE, "Checking %d old VideoRoom sessions...\n", g_list_length(old_sessions));
662 c94052c2 meetecho
                        while(sl) {
663
                                janus_videoroom_session *session = (janus_videoroom_session *)sl->data;
664 554cc3a5 Computician
                                /* If we are stopping, their is no point to continue to iterate */
665
                                if(!initialized || stopping) {
666
                                        break;
667
                                }
668
                                if(!session) {
669 c94052c2 meetecho
                                        sl = sl->next;
670
                                        continue;
671
                                }
672
                                if(now-session->destroyed >= 5*G_USEC_PER_SEC) {
673
                                        /* We're lazy and actually get rid of the stuff only after a few seconds */
674 1281ca86 meetecho
                                        JANUS_LOG(LOG_VERB, "Freeing old VideoRoom session\n");
675 c94052c2 meetecho
                                        GList *rm = sl->next;
676
                                        old_sessions = g_list_delete_link(old_sessions, sl);
677
                                        sl = rm;
678 554cc3a5 Computician
                                        g_hash_table_steal(sessions, session->handle);
679
                                        session_free(session);
680 c94052c2 meetecho
                                        continue;
681
                                }
682
                                sl = sl->next;
683
                        }
684
                }
685
                janus_mutex_unlock(&sessions_mutex);
686 598aac14 Computician
                janus_mutex_lock(&rooms_mutex);
687
                if(old_rooms != NULL) {
688
                        GList *rl = old_rooms;
689
                        room_now = janus_get_monotonic_time();
690
                        while(rl) {
691
                                janus_videoroom* room = (janus_videoroom*)rl->data;
692
                                if(!initialized || stopping){
693
                                        break;
694
                                }
695
                                if(!room) {
696
                                        rl = rl->next;
697
                                        continue;
698
                                }
699
                                if(room_now - room->destroyed >= 5*G_USEC_PER_SEC) {
700
                                        GList *rm = rl->next;
701
                                        old_rooms = g_list_delete_link(old_rooms, rl);
702
                                        rl = rm;
703 d236f0e9 Lorenzo Miniero
                                        g_hash_table_remove(rooms, &room->room_id);
704 598aac14 Computician
                                        continue;
705
                                }
706
                                rl = rl->next;
707
                        }
708
                }
709
                janus_mutex_unlock(&rooms_mutex);
710 1e2f5d89 meetecho
                g_usleep(500000);
711 c94052c2 meetecho
        }
712 5fa9a305 meetecho
        JANUS_LOG(LOG_INFO, "VideoRoom watchdog stopped\n");
713 c94052c2 meetecho
        return NULL;
714
}
715
716
717 be35facb meetecho
/* Plugin implementation */
718
int janus_videoroom_init(janus_callbacks *callback, const char *config_path) {
719 dbf63b61 meetecho
        if(g_atomic_int_get(&stopping)) {
720 be35facb meetecho
                /* Still stopping from before */
721
                return -1;
722
        }
723
        if(callback == NULL || config_path == NULL) {
724
                /* Invalid arguments */
725
                return -1;
726
        }
727
728
        /* Read configuration */
729
        char filename[255];
730 c94052c2 meetecho
        g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_VIDEOROOM_PACKAGE);
731 3a26e009 meetecho
        JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
732 2512456f Lorenzo Miniero
        config = janus_config_parse(filename);
733
        config_folder = config_path;
734 be35facb meetecho
        if(config != NULL)
735
                janus_config_print(config);
736 2512456f Lorenzo Miniero
        janus_mutex_init(&config_mutex);
737 be35facb meetecho
738 d236f0e9 Lorenzo Miniero
        rooms = g_hash_table_new_full(g_int64_hash, g_int64_equal,
739
                (GDestroyNotify)g_free, (GDestroyNotify) janus_videoroom_free);
740 3a26e009 meetecho
        janus_mutex_init(&rooms_mutex);
741 be35facb meetecho
        sessions = g_hash_table_new(NULL, NULL);
742 3a26e009 meetecho
        janus_mutex_init(&sessions_mutex);
743 7000b914 Philip Withnall
744 681a736e Philip Withnall
        messages = g_async_queue_new_full((GDestroyNotify) janus_videoroom_message_free);
745 7000b914 Philip Withnall
746 be35facb meetecho
        /* This is the callback we'll need to invoke to contact the gateway */
747
        gateway = callback;
748
749
        /* Parse configuration to populate the rooms list */
750
        if(config != NULL) {
751 930a4a4c Lorenzo Miniero
                /* Any admin key to limit who can "create"? */
752
                janus_config_item *key = janus_config_get_item_drilldown(config, "general", "admin_key");
753
                if(key != NULL && key->value != NULL)
754
                        admin_key = g_strdup(key->value);
755 71a04f89 Lorenzo Miniero
                janus_config_item *events = janus_config_get_item_drilldown(config, "general", "events");
756
                if(events != NULL && events->value != NULL)
757
                        notify_events = janus_is_true(events->value);
758
                if(!notify_events && callback->events_is_enabled()) {
759
                        JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_VIDEOROOM_NAME);
760
                }
761 930a4a4c Lorenzo Miniero
                /* Iterate on all rooms */
762 2512456f Lorenzo Miniero
                GList *cl = janus_config_get_categories(config);
763
                while(cl != NULL) {
764
                        janus_config_category *cat = (janus_config_category *)cl->data;
765 930a4a4c Lorenzo Miniero
                        if(cat->name == NULL || !strcasecmp(cat->name, "general")) {
766 2512456f Lorenzo Miniero
                                cl = cl->next;
767 be35facb meetecho
                                continue;
768
                        }
769 3a26e009 meetecho
                        JANUS_LOG(LOG_VERB, "Adding video room '%s'\n", cat->name);
770 be35facb meetecho
                        janus_config_item *desc = janus_config_get_item(cat, "description");
771 0a466e28 janus
                        janus_config_item *priv = janus_config_get_item(cat, "is_private");
772 af40a682 meetecho
                        janus_config_item *secret = janus_config_get_item(cat, "secret");
773 5d20bcec Lorenzo Miniero
                        janus_config_item *pin = janus_config_get_item(cat, "pin");
774 be35facb meetecho
                        janus_config_item *bitrate = janus_config_get_item(cat, "bitrate");
775
                        janus_config_item *maxp = janus_config_get_item(cat, "publishers");
776 5e9e29e0 meetecho
                        janus_config_item *firfreq = janus_config_get_item(cat, "fir_freq");
777 ba99f281 pallab-gain
                        janus_config_item *audiocodec = janus_config_get_item(cat, "audiocodec");
778 fd8cb2d5 pallab-gain
                        janus_config_item *videocodec = janus_config_get_item(cat, "videocodec");
779 ef61c470 Lorenzo Miniero
                        janus_config_item *audiolevel_ext = janus_config_get_item(cat, "audiolevel_ext");
780 bd0e3260 mirkobrankovic
                        janus_config_item *audiolevel_event = janus_config_get_item(cat, "audiolevel_event");
781
                        janus_config_item *audio_active_packets = janus_config_get_item(cat, "audio_active_packets");
782
                        janus_config_item *audio_level_average = janus_config_get_item(cat, "audio_level_average");
783 ef61c470 Lorenzo Miniero
                        janus_config_item *videoorient_ext = janus_config_get_item(cat, "videoorient_ext");
784
                        janus_config_item *playoutdelay_ext = janus_config_get_item(cat, "playoutdelay_ext");
785 9d11ac50 meetecho
                        janus_config_item *record = janus_config_get_item(cat, "record");
786
                        janus_config_item *rec_dir = janus_config_get_item(cat, "rec_dir");
787 550a36f0 meetecho
                        /* Create the video room */
788 1f067658 Lorenzo Miniero
                        janus_videoroom *videoroom = g_malloc0(sizeof(janus_videoroom));
789 a972337c Lorenzo Miniero
                        videoroom->room_id = g_ascii_strtoull(cat->name, NULL, 0);
790 be35facb meetecho
                        char *description = NULL;
791 d1302e06 meetecho
                        if(desc != NULL && desc->value != NULL && strlen(desc->value) > 0)
792 be35facb meetecho
                                description = g_strdup(desc->value);
793
                        else
794
                                description = g_strdup(cat->name);
795
                        videoroom->room_name = description;
796 af40a682 meetecho
                        if(secret != NULL && secret->value != NULL) {
797
                                videoroom->room_secret = g_strdup(secret->value);
798
                        }
799 5d20bcec Lorenzo Miniero
                        if(pin != NULL && pin->value != NULL) {
800
                                videoroom->room_pin = g_strdup(pin->value);
801
                        }
802 0a466e28 janus
                        videoroom->is_private = priv && priv->value && janus_is_true(priv->value);
803 be35facb meetecho
                        videoroom->max_publishers = 3;        /* FIXME How should we choose a default? */
804
                        if(maxp != NULL && maxp->value != NULL)
805
                                videoroom->max_publishers = atol(maxp->value);
806
                        if(videoroom->max_publishers < 0)
807
                                videoroom->max_publishers = 3;        /* FIXME How should we choose a default? */
808
                        videoroom->bitrate = 0;
809
                        if(bitrate != NULL && bitrate->value != NULL)
810
                                videoroom->bitrate = atol(bitrate->value);
811 ddc1f702 meetecho
                        if(videoroom->bitrate > 0 && videoroom->bitrate < 64000)
812
                                videoroom->bitrate = 64000;        /* Don't go below 64k */
813 5e9e29e0 meetecho
                        videoroom->fir_freq = 0;
814
                        if(firfreq != NULL && firfreq->value != NULL)
815
                                videoroom->fir_freq = atol(firfreq->value);
816 ba99f281 pallab-gain
                        videoroom->acodec = JANUS_VIDEOROOM_OPUS;
817
                        if(audiocodec && audiocodec->value) {
818 3de6d536 Lorenzo Miniero
                                if(!strcasecmp(audiocodec->value, "opus"))
819 ba99f281 pallab-gain
                                        videoroom->acodec = JANUS_VIDEOROOM_OPUS;
820
                                else if(!strcasecmp(audiocodec->value, "isac32"))
821
                                        videoroom->acodec = JANUS_VIDEOROOM_ISAC_32K;
822
                                else if(!strcasecmp(audiocodec->value, "isac16"))
823
                                        videoroom->acodec = JANUS_VIDEOROOM_ISAC_16K;
824
                                else if(!strcasecmp(audiocodec->value, "pcmu"))
825
                                        videoroom->acodec = JANUS_VIDEOROOM_PCMU;
826 fd8cb2d5 pallab-gain
                                else if(!strcasecmp(audiocodec->value, "pcma"))
827
                                        videoroom->acodec = JANUS_VIDEOROOM_PCMA;
828 183d715f Lorenzo Miniero
                                else if(!strcasecmp(audiocodec->value, "g722"))
829
                                        videoroom->acodec = JANUS_VIDEOROOM_G722;
830 ba99f281 pallab-gain
                                else {
831
                                        JANUS_LOG(LOG_WARN, "Unsupported audio codec '%s', falling back to OPUS\n", audiocodec->value);
832
                                        videoroom->acodec = JANUS_VIDEOROOM_OPUS;
833
                                }
834
                        }
835 fd8cb2d5 pallab-gain
                        videoroom->vcodec = JANUS_VIDEOROOM_VP8;
836
                        if(videocodec && videocodec->value) {
837
                                if(!strcasecmp(videocodec->value, "vp8"))
838
                                        videoroom->vcodec = JANUS_VIDEOROOM_VP8;
839
                                else if(!strcasecmp(videocodec->value, "vp9"))
840
                                        videoroom->vcodec = JANUS_VIDEOROOM_VP9;
841
                                else if(!strcasecmp(videocodec->value, "h264"))
842
                                        videoroom->vcodec = JANUS_VIDEOROOM_H264;
843
                                else {
844
                                        JANUS_LOG(LOG_WARN, "Unsupported video codec '%s', falling back to VP8\n", videocodec->value);
845
                                        videoroom->vcodec = JANUS_VIDEOROOM_VP8;
846
                                }
847
                        }
848 ef61c470 Lorenzo Miniero
                        videoroom->audiolevel_ext = TRUE;
849
                        if(audiolevel_ext != NULL && audiolevel_ext->value != NULL)
850
                                videoroom->audiolevel_ext = janus_is_true(audiolevel_ext->value);
851 bd0e3260 mirkobrankovic
                        videoroom->audiolevel_event = FALSE;
852
                        if(audiolevel_event != NULL && audiolevel_event->value != NULL)
853
                                videoroom->audiolevel_event = janus_is_true(audiolevel_event->value);
854 fa9b2d6a Lorenzo Miniero
                        if(videoroom->audiolevel_event) {
855
                                videoroom->audio_active_packets = 100;
856
                                if(audio_active_packets != NULL && audio_active_packets->value != NULL){
857
                                        if(atoi(audio_active_packets->value) > 0) {
858
                                                videoroom->audio_active_packets = atoi(audio_active_packets->value);
859
                                        } else {
860
                                                JANUS_LOG(LOG_WARN, "Invalid audio_active_packets value, using default: %d\n", videoroom->audio_active_packets);
861
                                        }
862 1cbed49f mirkobrankovic
                                }
863 fa9b2d6a Lorenzo Miniero
                                videoroom->audio_level_average = 25;
864
                                if(audio_level_average != NULL && audio_level_average->value != NULL) {
865
                                        if(atoi(audio_level_average->value) > 0) {
866
                                                videoroom->audio_level_average = atoi(audio_level_average->value);
867
                                        } else {
868
                                                JANUS_LOG(LOG_WARN, "Invalid audio_level_average value provided, using default: %d\n", videoroom->audio_level_average);
869
                                        }
870 1cbed49f mirkobrankovic
                                }
871
                        }
872 ef61c470 Lorenzo Miniero
                        videoroom->videoorient_ext = TRUE;
873
                        if(videoorient_ext != NULL && videoorient_ext->value != NULL)
874
                                videoroom->videoorient_ext = janus_is_true(videoorient_ext->value);
875
                        videoroom->playoutdelay_ext = TRUE;
876
                        if(playoutdelay_ext != NULL && playoutdelay_ext->value != NULL)
877
                                videoroom->playoutdelay_ext = janus_is_true(playoutdelay_ext->value);
878 9d11ac50 meetecho
                        if(record && record->value) {
879 6dd1c12c meetecho
                                videoroom->record = janus_is_true(record->value);
880 ab0d61f1 Lorenzo Miniero
                        }
881
                        if(rec_dir && rec_dir->value) {
882
                                videoroom->rec_dir = g_strdup(rec_dir->value);
883 9d11ac50 meetecho
                        }
884 598aac14 Computician
                        videoroom->destroyed = 0;
885 46d28722 Computician
                        janus_mutex_init(&videoroom->participants_mutex);
886 d236f0e9 Lorenzo Miniero
                        videoroom->participants = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL);
887 543cffca Lorenzo Miniero
                        videoroom->private_ids = g_hash_table_new(NULL, NULL);
888 30df7fbf Lorenzo Miniero
                        videoroom->check_tokens = FALSE;        /* Static rooms can't have an "allowed" list yet, no hooks to the configuration file */
889
                        videoroom->allowed = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL);
890 3a26e009 meetecho
                        janus_mutex_lock(&rooms_mutex);
891 d236f0e9 Lorenzo Miniero
                        g_hash_table_insert(rooms, janus_uint64_dup(videoroom->room_id), videoroom);
892 3a26e009 meetecho
                        janus_mutex_unlock(&rooms_mutex);
893 ba5ca81a Lorenzo Miniero
                        JANUS_LOG(LOG_VERB, "Created videoroom: %"SCNu64" (%s, %s, %s/%s codecs, secret: %s, pin: %s)\n",
894 5d20bcec Lorenzo Miniero
                                videoroom->room_id, videoroom->room_name,
895
                                videoroom->is_private ? "private" : "public",
896 ba99f281 pallab-gain
                                janus_videoroom_audiocodec_name(videoroom->acodec),
897 fd8cb2d5 pallab-gain
                                janus_videoroom_videocodec_name(videoroom->vcodec),
898 5d20bcec Lorenzo Miniero
                                videoroom->room_secret ? videoroom->room_secret : "no secret",
899
                                videoroom->room_pin ? videoroom->room_pin : "no pin");
900 9d11ac50 meetecho
                        if(videoroom->record) {
901
                                JANUS_LOG(LOG_VERB, "  -- Room is going to be recorded in %s\n", videoroom->rec_dir ? videoroom->rec_dir : "the current folder");
902
                        }
903 2512456f Lorenzo Miniero
                        cl = cl->next;
904 be35facb meetecho
                }
905 2512456f Lorenzo Miniero
                /* Done: we keep the configuration file open in case we get a "create" or "destroy" with permanent=true */
906 be35facb meetecho
        }
907
908
        /* Show available rooms */
909 3a26e009 meetecho
        janus_mutex_lock(&rooms_mutex);
910 4d13c20d meetecho
        GHashTableIter iter;
911
        gpointer value;
912
        g_hash_table_iter_init(&iter, rooms);
913
        while (g_hash_table_iter_next(&iter, NULL, &value)) {
914
                janus_videoroom *vr = value;
915 78229f3f pallab-gain
                JANUS_LOG(LOG_VERB, "  ::: [%"SCNu64"][%s] %"SCNu64", max %d publishers, FIR frequency of %d seconds, %s audio codec, %s video codec\n",
916 fc23d811 Lorenzo Miniero
                        vr->room_id, vr->room_name, vr->bitrate, vr->max_publishers, vr->fir_freq,
917 fd8cb2d5 pallab-gain
                        janus_videoroom_audiocodec_name(vr->acodec), janus_videoroom_videocodec_name(vr->vcodec));
918 be35facb meetecho
        }
919 3a26e009 meetecho
        janus_mutex_unlock(&rooms_mutex);
920 be35facb meetecho
921 dbf63b61 meetecho
        g_atomic_int_set(&initialized, 1);
922
923
        GError *error = NULL;
924 c94052c2 meetecho
        /* Start the sessions watchdog */
925 c7498fa9 Lorenzo Miniero
        watchdog = g_thread_try_new("videoroom watchdog", &janus_videoroom_watchdog, NULL, &error);
926 dbf63b61 meetecho
        if(error != NULL) {
927
                g_atomic_int_set(&initialized, 0);
928
                JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the VideoRoom watchdog thread...\n", error->code, error->message ? error->message : "??");
929 2512456f Lorenzo Miniero
                janus_config_destroy(config);
930 c94052c2 meetecho
                return -1;
931
        }
932 be35facb meetecho
        /* Launch the thread that will handle incoming messages */
933 c7498fa9 Lorenzo Miniero
        handler_thread = g_thread_try_new("videoroom handler", janus_videoroom_handler, NULL, &error);
934 be35facb meetecho
        if(error != NULL) {
935 dbf63b61 meetecho
                g_atomic_int_set(&initialized, 0);
936
                JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the VideoRoom handler thread...\n", error->code, error->message ? error->message : "??");
937 2512456f Lorenzo Miniero
                janus_config_destroy(config);
938 be35facb meetecho
                return -1;
939
        }
940 3a26e009 meetecho
        JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_VIDEOROOM_NAME);
941 be35facb meetecho
        return 0;
942
}
943
944 77992057 Philip Withnall
void janus_videoroom_destroy(void) {
945 dbf63b61 meetecho
        if(!g_atomic_int_get(&initialized))
946 be35facb meetecho
                return;
947 dbf63b61 meetecho
        g_atomic_int_set(&stopping, 1);
948 2e28ee7b Lorenzo Miniero
949 b9e8b01f Lorenzo Miniero
        g_async_queue_push(messages, &exit_message);
950 be35facb meetecho
        if(handler_thread != NULL) {
951
                g_thread_join(handler_thread);
952 dbf63b61 meetecho
                handler_thread = NULL;
953
        }
954
        if(watchdog != NULL) {
955
                g_thread_join(watchdog);
956
                watchdog = NULL;
957 be35facb meetecho
        }
958 7000b914 Philip Withnall
959 3a26e009 meetecho
        /* FIXME We should destroy the sessions cleanly */
960
        janus_mutex_lock(&sessions_mutex);
961 554cc3a5 Computician
        g_hash_table_foreach_remove(sessions, (GHRFunc)session_hash_table_remove, NULL);
962 be35facb meetecho
        g_hash_table_destroy(sessions);
963 7000b914 Philip Withnall
        sessions = NULL;
964 3a26e009 meetecho
        janus_mutex_unlock(&sessions_mutex);
965 7000b914 Philip Withnall
966 3a26e009 meetecho
        janus_mutex_lock(&rooms_mutex);
967 be35facb meetecho
        g_hash_table_destroy(rooms);
968
        rooms = NULL;
969 7000b914 Philip Withnall
        janus_mutex_unlock(&rooms_mutex);
970
        janus_mutex_destroy(&rooms_mutex);
971
972
        g_async_queue_unref(messages);
973
        messages = NULL;
974
975 2512456f Lorenzo Miniero
        janus_config_destroy(config);
976 930a4a4c Lorenzo Miniero
        g_free(admin_key);
977 2512456f Lorenzo Miniero
978 dbf63b61 meetecho
        g_atomic_int_set(&initialized, 0);
979
        g_atomic_int_set(&stopping, 0);
980 3a26e009 meetecho
        JANUS_LOG(LOG_INFO, "%s destroyed!\n", JANUS_VIDEOROOM_NAME);
981 be35facb meetecho
}
982
983 667b2005 meetecho
int janus_videoroom_get_api_compatibility(void) {
984
        /* Important! This is what your plugin MUST always return: don't lie here or bad things will happen */
985
        return JANUS_PLUGIN_API_VERSION;
986
}
987
988 77992057 Philip Withnall
int janus_videoroom_get_version(void) {
989 be35facb meetecho
        return JANUS_VIDEOROOM_VERSION;
990
}
991
992 77992057 Philip Withnall
const char *janus_videoroom_get_version_string(void) {
993 be35facb meetecho
        return JANUS_VIDEOROOM_VERSION_STRING;
994
}
995
996 77992057 Philip Withnall
const char *janus_videoroom_get_description(void) {
997 be35facb meetecho
        return JANUS_VIDEOROOM_DESCRIPTION;
998
}
999
1000 77992057 Philip Withnall
const char *janus_videoroom_get_name(void) {
1001 be35facb meetecho
        return JANUS_VIDEOROOM_NAME;
1002
}
1003
1004 77992057 Philip Withnall
const char *janus_videoroom_get_author(void) {
1005 a3d13e20 meetecho
        return JANUS_VIDEOROOM_AUTHOR;
1006
}
1007
1008 77992057 Philip Withnall
const char *janus_videoroom_get_package(void) {
1009 be35facb meetecho
        return JANUS_VIDEOROOM_PACKAGE;
1010
}
1011
1012 cfd5c0d6 meetecho
void janus_videoroom_create_session(janus_plugin_session *handle, int *error) {
1013 8878e296 meetecho
        if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
1014 be35facb meetecho
                *error = -1;
1015
                return;
1016 39b52c9a mirkobrankovic
        }        
1017 1f067658 Lorenzo Miniero
        janus_videoroom_session *session = (janus_videoroom_session *)g_malloc0(sizeof(janus_videoroom_session));
1018 be35facb meetecho
        session->handle = handle;
1019
        session->participant_type = janus_videoroom_p_type_none;
1020
        session->participant = NULL;
1021 5fa9a305 meetecho
        session->destroyed = 0;
1022 f65c61ff Lorenzo Miniero
        g_atomic_int_set(&session->hangingup, 0);
1023 be35facb meetecho
        handle->plugin_handle = session;
1024 3a26e009 meetecho
        janus_mutex_lock(&sessions_mutex);
1025 be35facb meetecho
        g_hash_table_insert(sessions, handle, session);
1026 3a26e009 meetecho
        janus_mutex_unlock(&sessions_mutex);
1027 be35facb meetecho
1028
        return;
1029
}
1030
1031 a3436ed0 Lorenzo Miniero
static void janus_videoroom_notify_participants(janus_videoroom_participant *participant, json_t *msg) {
1032 f12f3dbb Andreas Girgensohn
        /* participant->room->participants_mutex has to be locked. */
1033
        GHashTableIter iter;
1034
        gpointer value;
1035
        g_hash_table_iter_init(&iter, participant->room->participants);
1036
        while (!participant->room->destroyed && g_hash_table_iter_next(&iter, NULL, &value)) {
1037
                janus_videoroom_participant *p = value;
1038
                if(p && p->session && p != participant) {
1039
                        JANUS_LOG(LOG_VERB, "Notifying participant %"SCNu64" (%s)\n", p->user_id, p->display ? p->display : "??");
1040 a3436ed0 Lorenzo Miniero
                        int ret = gateway->push_event(p->session->handle, &janus_videoroom_plugin, NULL, msg, NULL);
1041 f12f3dbb Andreas Girgensohn
                        JANUS_LOG(LOG_VERB, "  >> %d (%s)\n", ret, janus_get_api_error(ret));
1042
                }
1043
        }
1044
}
1045
1046 7273e5d8 Lorenzo Miniero
static void janus_videoroom_leave_or_unpublish(janus_videoroom_participant *participant, gboolean is_leaving, gboolean kicked) {
1047 f12f3dbb Andreas Girgensohn
        /* we need to check if the room still exists, may have been destroyed already */
1048 c3654169 Alex Smirnov
        if(participant->room && !participant->room->destroyed) {
1049
                json_t *event = json_object();
1050
                json_object_set_new(event, "videoroom", json_string("event"));
1051
                json_object_set_new(event, "room", json_integer(participant->room->room_id));
1052 7273e5d8 Lorenzo Miniero
                json_object_set_new(event, is_leaving ? (kicked ? "kicked" : "leaving") : "unpublished",
1053
                        json_integer(participant->user_id));
1054 c3654169 Alex Smirnov
                janus_mutex_lock(&participant->room->participants_mutex);
1055
                janus_videoroom_notify_participants(participant, event);
1056
                if(is_leaving) {
1057
                        g_hash_table_remove(participant->room->participants, &participant->user_id);
1058 543cffca Lorenzo Miniero
                        g_hash_table_remove(participant->room->private_ids, GUINT_TO_POINTER(participant->pvt_id));
1059 f12f3dbb Andreas Girgensohn
                }
1060 c3654169 Alex Smirnov
                janus_mutex_unlock(&participant->room->participants_mutex);
1061
                json_decref(event);
1062 f12f3dbb Andreas Girgensohn
        }
1063
}
1064
1065 cfd5c0d6 meetecho
void janus_videoroom_destroy_session(janus_plugin_session *handle, int *error) {
1066 8878e296 meetecho
        if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
1067 be35facb meetecho
                *error = -1;
1068
                return;
1069 39b52c9a mirkobrankovic
        }        
1070
        janus_videoroom_session *session = (janus_videoroom_session *)handle->plugin_handle; 
1071 be35facb meetecho
        if(!session) {
1072 ce97b4a5 meetecho
                JANUS_LOG(LOG_ERR, "No VideoRoom session associated with this handle...\n");
1073 be35facb meetecho
                *error = -2;
1074
                return;
1075
        }
1076 c94052c2 meetecho
        if(session->destroyed) {
1077 ce97b4a5 meetecho
                JANUS_LOG(LOG_WARN, "VideoRoom session already marked as destroyed...\n");
1078 be35facb meetecho
                return;
1079
        }
1080 ce97b4a5 meetecho
        JANUS_LOG(LOG_VERB, "Removing VideoRoom session...\n");
1081 c94052c2 meetecho
        /* Cleaning up and removing the session is done in a lazy way */
1082
        janus_mutex_lock(&sessions_mutex);
1083 793d18b1 meetecho
        if(!session->destroyed) {
1084
                /* Any related WebRTC PeerConnection is not available anymore either */
1085
                janus_videoroom_hangup_media(handle);
1086 78955474 meetecho
                session->destroyed = janus_get_monotonic_time();
1087 793d18b1 meetecho
                old_sessions = g_list_append(old_sessions, session);
1088
                if(session->participant_type == janus_videoroom_p_type_publisher) {
1089
                        /* Get rid of publisher */
1090
                        janus_videoroom_participant *participant = (janus_videoroom_participant *)session->participant;
1091
                        participant->audio = FALSE;
1092
                        participant->video = FALSE;
1093
                        participant->data = FALSE;
1094
                        participant->audio_active = FALSE;
1095
                        participant->video_active = FALSE;
1096 7713bf1c Lorenzo Miniero
                        participant->data_active = FALSE;
1097 793d18b1 meetecho
                        participant->recording_active = FALSE;
1098
                        if(participant->recording_base)
1099
                                g_free(participant->recording_base);
1100
                        participant->recording_base = NULL;
1101 99d4f2bf Lorenzo Miniero
                        session->participant_type = janus_videoroom_p_type_none;
1102 7273e5d8 Lorenzo Miniero
                        janus_videoroom_leave_or_unpublish(participant, TRUE, FALSE);
1103 793d18b1 meetecho
                } else if(session->participant_type == janus_videoroom_p_type_subscriber) {
1104
                        /* Detaching this listener from its publisher is already done by hangup_media */
1105 af40a682 meetecho
                }
1106 be35facb meetecho
        }
1107 793d18b1 meetecho
        janus_mutex_unlock(&sessions_mutex);
1108 be35facb meetecho
1109
        return;
1110
}
1111
1112 dd11fa0a Lorenzo Miniero
json_t *janus_videoroom_query_session(janus_plugin_session *handle) {
1113 667b2005 meetecho
        if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
1114
                return NULL;
1115 39b52c9a mirkobrankovic
        }        
1116 667b2005 meetecho
        janus_videoroom_session *session = (janus_videoroom_session *)handle->plugin_handle;
1117
        if(!session) {
1118
                JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
1119
                return NULL;
1120
        }
1121
        /* Show the participant/room info, if any */
1122
        json_t *info = json_object();
1123
        if(session->participant) {
1124
                if(session->participant_type == janus_videoroom_p_type_none) {
1125
                        json_object_set_new(info, "type", json_string("none"));
1126
                } else if(session->participant_type == janus_videoroom_p_type_publisher) {
1127
                        json_object_set_new(info, "type", json_string("publisher"));
1128
                        janus_videoroom_participant *participant = (janus_videoroom_participant *)session->participant;
1129
                        if(participant) {
1130 39b52c9a mirkobrankovic
                                janus_videoroom *room = participant->room; 
1131 667b2005 meetecho
                                json_object_set_new(info, "room", room ? json_integer(room->room_id) : NULL);
1132
                                json_object_set_new(info, "id", json_integer(participant->user_id));
1133 543cffca Lorenzo Miniero
                                json_object_set_new(info, "private_id", json_integer(participant->pvt_id));
1134 667b2005 meetecho
                                if(participant->display)
1135 924ff9ab meetecho
                                        json_object_set_new(info, "display", json_string(participant->display));
1136 fbcd699c meetecho
                                if(participant->listeners)
1137
                                        json_object_set_new(info, "viewers", json_integer(g_slist_length(participant->listeners)));
1138 ca2ccd4a meetecho
                                json_t *media = json_object();
1139
                                json_object_set_new(media, "audio", json_integer(participant->audio));
1140
                                json_object_set_new(media, "video", json_integer(participant->video));
1141
                                json_object_set_new(media, "data", json_integer(participant->data));
1142
                                json_object_set_new(info, "media", media);
1143 2185b377 Lorenzo Miniero
                                json_object_set_new(info, "bitrate", json_integer(participant->bitrate));
1144 39b52c9a mirkobrankovic
                                if(participant->arc || participant->vrc || participant->drc) {
1145 6089eb4e Lorenzo Miniero
                                        json_t *recording = json_object();
1146
                                        if(participant->arc && participant->arc->filename)
1147
                                                json_object_set_new(recording, "audio", json_string(participant->arc->filename));
1148
                                        if(participant->vrc && participant->vrc->filename)
1149
                                                json_object_set_new(recording, "video", json_string(participant->vrc->filename));
1150 39b52c9a mirkobrankovic
                                        if(participant->drc && participant->drc->filename)
1151
                                                json_object_set_new(recording, "data", json_string(participant->drc->filename));
1152 6089eb4e Lorenzo Miniero
                                        json_object_set_new(info, "recording", recording);
1153
                                }
1154 667b2005 meetecho
                        }
1155
                } else if(session->participant_type == janus_videoroom_p_type_subscriber) {
1156
                        json_object_set_new(info, "type", json_string("listener"));
1157
                        janus_videoroom_listener *participant = (janus_videoroom_listener *)session->participant;
1158
                        if(participant) {
1159
                                janus_videoroom_participant *feed = (janus_videoroom_participant *)participant->feed;
1160
                                if(feed) {
1161 39b52c9a mirkobrankovic
                                        janus_videoroom *room = feed->room; 
1162 667b2005 meetecho
                                        json_object_set_new(info, "room", room ? json_integer(room->room_id) : NULL);
1163 543cffca Lorenzo Miniero
                                        json_object_set_new(info, "private_id", json_integer(participant->pvt_id));
1164 667b2005 meetecho
                                        json_object_set_new(info, "feed_id", json_integer(feed->user_id));
1165
                                        if(feed->display)
1166 924ff9ab meetecho
                                                json_object_set_new(info, "feed_display", json_string(feed->display));
1167 667b2005 meetecho
                                }
1168 ca2ccd4a meetecho
                                json_t *media = json_object();
1169
                                json_object_set_new(media, "audio", json_integer(participant->audio));
1170
                                json_object_set_new(media, "video", json_integer(participant->video));
1171
                                json_object_set_new(media, "data", json_integer(participant->data));
1172
                                json_object_set_new(info, "media", media);
1173 667b2005 meetecho
                        }
1174
                }
1175
        }
1176
        json_object_set_new(info, "destroyed", json_integer(session->destroyed));
1177 dd11fa0a Lorenzo Miniero
        return info;
1178 667b2005 meetecho
}
1179
1180 a046cf75 Andreas Girgensohn
static int janus_videoroom_access_room(json_t *root, gboolean check_secret, gboolean check_pin, janus_videoroom **videoroom, char *error_cause, int error_cause_size) {
1181 f12f3dbb Andreas Girgensohn
        /* rooms_mutex has to be locked */
1182
        int error_code = 0;
1183
        json_t *room = json_object_get(root, "room");
1184
        guint64 room_id = json_integer_value(room);
1185 d236f0e9 Lorenzo Miniero
        *videoroom = g_hash_table_lookup(rooms, &room_id);
1186 f12f3dbb Andreas Girgensohn
        if(*videoroom == NULL) {
1187
                JANUS_LOG(LOG_ERR, "No such room (%"SCNu64")\n", room_id);
1188
                error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM;
1189 a046cf75 Andreas Girgensohn
                if(error_cause)
1190
                        g_snprintf(error_cause, error_cause_size, "No such room (%"SCNu64")", room_id);
1191 f12f3dbb Andreas Girgensohn
                return error_code;
1192
        }
1193
        if((*videoroom)->destroyed) {
1194
                JANUS_LOG(LOG_ERR, "No such room (%"SCNu64")\n", room_id);
1195
                error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM;
1196 a046cf75 Andreas Girgensohn
                if(error_cause)
1197
                        g_snprintf(error_cause, error_cause_size, "No such room (%"SCNu64")", room_id);
1198 f12f3dbb Andreas Girgensohn
                return error_code;
1199
        }
1200
        if(check_secret) {
1201 a046cf75 Andreas Girgensohn
                char error_cause2[100];
1202
                JANUS_CHECK_SECRET((*videoroom)->room_secret, root, "secret", error_code, error_cause2,
1203 f12f3dbb Andreas Girgensohn
                        JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT, JANUS_VIDEOROOM_ERROR_UNAUTHORIZED);
1204 a046cf75 Andreas Girgensohn
                if(error_code != 0) {
1205
                        g_strlcpy(error_cause, error_cause2, error_cause_size);
1206 f12f3dbb Andreas Girgensohn
                        return error_code;
1207 a046cf75 Andreas Girgensohn
                }
1208 f12f3dbb Andreas Girgensohn
        }
1209
        if(check_pin) {
1210 a046cf75 Andreas Girgensohn
                char error_cause2[100];
1211
                JANUS_CHECK_SECRET((*videoroom)->room_pin, root, "pin", error_code, error_cause2,
1212 f12f3dbb Andreas Girgensohn
                        JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT, JANUS_VIDEOROOM_ERROR_UNAUTHORIZED);
1213 a046cf75 Andreas Girgensohn
                if(error_code != 0) {
1214
                        g_strlcpy(error_cause, error_cause2, error_cause_size);
1215 f12f3dbb Andreas Girgensohn
                        return error_code;
1216 a046cf75 Andreas Girgensohn
                }
1217 f12f3dbb Andreas Girgensohn
        }
1218
        return 0;
1219
}
1220
1221 dd11fa0a Lorenzo Miniero
struct janus_plugin_result *janus_videoroom_handle_message(janus_plugin_session *handle, char *transaction, json_t *message, json_t *jsep) {
1222 8878e296 meetecho
        if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
1223 dd11fa0a Lorenzo Miniero
                return janus_plugin_result_new(JANUS_PLUGIN_ERROR, g_atomic_int_get(&stopping) ? "Shutting down" : "Plugin not initialized", NULL);
1224 39b52c9a mirkobrankovic
        
1225 d24aff33 meetecho
        /* Pre-parse the message */
1226
        int error_code = 0;
1227 4078ed95 Pierce Lopez
        char error_cause[512];
1228 dd11fa0a Lorenzo Miniero
        json_t *root = message;
1229 4078ed95 Pierce Lopez
        json_t *response = NULL;
1230
1231
        if(message == NULL) {
1232
                JANUS_LOG(LOG_ERR, "No message??\n");
1233
                error_code = JANUS_VIDEOROOM_ERROR_NO_MESSAGE;
1234
                g_snprintf(error_cause, 512, "%s", "No message??");
1235 db1e9a1f Lorenzo Miniero
                goto plugin_response;
1236 4078ed95 Pierce Lopez
        }
1237
1238 39b52c9a mirkobrankovic
        janus_videoroom_session *session = (janus_videoroom_session *)handle->plugin_handle;        
1239 d24aff33 meetecho
        if(!session) {
1240
                JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
1241
                error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR;
1242 c94052c2 meetecho
                g_snprintf(error_cause, 512, "%s", "session associated with this handle...");
1243 db1e9a1f Lorenzo Miniero
                goto plugin_response;
1244 d24aff33 meetecho
        }
1245 c94052c2 meetecho
        if(session->destroyed) {
1246
                JANUS_LOG(LOG_ERR, "Session has already been marked as destroyed...\n");
1247 d24aff33 meetecho
                error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR;
1248 c94052c2 meetecho
                g_snprintf(error_cause, 512, "%s", "Session has already been marked as destroyed...");
1249 db1e9a1f Lorenzo Miniero
                goto plugin_response;
1250 d24aff33 meetecho
        }
1251
        if(!json_is_object(root)) {
1252
                JANUS_LOG(LOG_ERR, "JSON error: not an object\n");
1253
                error_code = JANUS_VIDEOROOM_ERROR_INVALID_JSON;
1254 c94052c2 meetecho
                g_snprintf(error_cause, 512, "JSON error: not an object");
1255 db1e9a1f Lorenzo Miniero
                goto plugin_response;
1256 d24aff33 meetecho
        }
1257
        /* Get the request first */
1258 887df302 Andreas Girgensohn
        JANUS_VALIDATE_JSON_OBJECT(root, request_parameters,
1259
                error_code, error_cause, TRUE,
1260
                JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
1261
        if(error_code != 0)
1262 db1e9a1f Lorenzo Miniero
                goto plugin_response;
1263 887df302 Andreas Girgensohn
        json_t *request = json_object_get(root, "request");
1264 08ab49d9 Computician
        /* Some requests ('create', 'destroy', 'exists', 'list') can be handled synchronously */
1265 d24aff33 meetecho
        const char *request_text = json_string_value(request);
1266
        if(!strcasecmp(request_text, "create")) {
1267
                /* Create a new videoroom */
1268
                JANUS_LOG(LOG_VERB, "Creating a new videoroom\n");
1269 887df302 Andreas Girgensohn
                JANUS_VALIDATE_JSON_OBJECT(root, create_parameters,
1270
                        error_code, error_cause, TRUE,
1271
                        JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
1272
                if(error_code != 0)
1273 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1274 930a4a4c Lorenzo Miniero
                if(admin_key != NULL) {
1275
                        /* An admin key was specified: make sure it was provided, and that it's valid */
1276
                        JANUS_VALIDATE_JSON_OBJECT(root, adminkey_parameters,
1277
                                error_code, error_cause, TRUE,
1278
                                JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
1279
                        if(error_code != 0)
1280 db1e9a1f Lorenzo Miniero
                                goto plugin_response;
1281 930a4a4c Lorenzo Miniero
                        JANUS_CHECK_SECRET(admin_key, root, "admin_key", error_code, error_cause,
1282
                                JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT, JANUS_VIDEOROOM_ERROR_UNAUTHORIZED);
1283
                        if(error_code != 0)
1284 db1e9a1f Lorenzo Miniero
                                goto plugin_response;
1285 930a4a4c Lorenzo Miniero
                }
1286 887df302 Andreas Girgensohn
                json_t *desc = json_object_get(root, "description");
1287 0a466e28 janus
                json_t *is_private = json_object_get(root, "is_private");
1288 d24aff33 meetecho
                json_t *secret = json_object_get(root, "secret");
1289 5d20bcec Lorenzo Miniero
                json_t *pin = json_object_get(root, "pin");
1290 d24aff33 meetecho
                json_t *bitrate = json_object_get(root, "bitrate");
1291
                json_t *fir_freq = json_object_get(root, "fir_freq");
1292
                json_t *publishers = json_object_get(root, "publishers");
1293 30df7fbf Lorenzo Miniero
                json_t *allowed = json_object_get(root, "allowed");
1294 fd8cb2d5 pallab-gain
                json_t *audiocodec = json_object_get(root, "audiocodec");
1295
                if(audiocodec) {
1296
                        const char *audiocodec_value = json_string_value(audiocodec);
1297 183d715f Lorenzo Miniero
                        if(!strcasecmp(audiocodec_value, "opus") && !strcasecmp(audiocodec_value, "g722") &&
1298
                                        !strcasecmp(audiocodec_value, "isac32") && !strcasecmp(audiocodec_value, "isac16") &&
1299
                                        !strcasecmp(audiocodec_value, "pcmu") && !strcasecmp(audiocodec_value, "pcma")) {
1300
                                JANUS_LOG(LOG_ERR, "Invalid element (audiocodec can only be opus, isac32, isac16, pcmu, pcma or g722)\n");
1301 fd8cb2d5 pallab-gain
                                error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
1302 183d715f Lorenzo Miniero
                                g_snprintf(error_cause, 512, "Invalid element (audiocodec can only be opus, isac32, isac16, pcmu, pcma or g722)");
1303 db1e9a1f Lorenzo Miniero
                                goto plugin_response;
1304 fd8cb2d5 pallab-gain
                        }
1305
                }
1306 fc23d811 Lorenzo Miniero
                json_t *videocodec = json_object_get(root, "videocodec");
1307
                if(videocodec) {
1308
                        const char *videocodec_value = json_string_value(videocodec);
1309
                        if(!strcasecmp(videocodec_value, "vp8") && !strcasecmp(videocodec_value, "vp9") && !strcasecmp(videocodec_value, "h264")) {
1310
                                JANUS_LOG(LOG_ERR, "Invalid element (videocodec can only be vp8, vp9 or h264)\n");
1311
                                error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
1312
                                g_snprintf(error_cause, 512, "Invalid element (videocodec can only be vp8, vp9 or h264)");
1313 db1e9a1f Lorenzo Miniero
                                goto plugin_response;
1314 fc23d811 Lorenzo Miniero
                        }
1315
                }
1316 ef61c470 Lorenzo Miniero
                json_t *audiolevel_ext = json_object_get(root, "audiolevel_ext");
1317 bd0e3260 mirkobrankovic
                json_t *audiolevel_event = json_object_get(root, "audiolevel_event");
1318
                json_t *audio_active_packets = json_object_get(root, "audio_active_packets");
1319
                json_t *audio_level_average = json_object_get(root, "audio_level_average");
1320 ef61c470 Lorenzo Miniero
                json_t *videoorient_ext = json_object_get(root, "videoorient_ext");
1321
                json_t *playoutdelay_ext = json_object_get(root, "playoutdelay_ext");
1322 d24aff33 meetecho
                json_t *record = json_object_get(root, "record");
1323
                json_t *rec_dir = json_object_get(root, "rec_dir");
1324 2512456f Lorenzo Miniero
                json_t *permanent = json_object_get(root, "permanent");
1325 30df7fbf Lorenzo Miniero
                if(allowed) {
1326
                        /* Make sure the "allowed" array only contains strings */
1327
                        gboolean ok = TRUE;
1328
                        if(json_array_size(allowed) > 0) {
1329
                                size_t i = 0;
1330
                                for(i=0; i<json_array_size(allowed); i++) {
1331
                                        json_t *a = json_array_get(allowed, i);
1332
                                        if(!a || !json_is_string(a)) {
1333
                                                ok = FALSE;
1334
                                                break;
1335
                                        }
1336
                                }
1337
                        }
1338
                        if(!ok) {
1339
                                JANUS_LOG(LOG_ERR, "Invalid element in the allowed array (not a string)\n");
1340
                                error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
1341
                                g_snprintf(error_cause, 512, "Invalid element in the allowed array (not a string)");
1342
                                goto plugin_response;
1343
                        }
1344
                }
1345 2512456f Lorenzo Miniero
                gboolean save = permanent ? json_is_true(permanent) : FALSE;
1346
                if(save && config == NULL) {
1347
                        JANUS_LOG(LOG_ERR, "No configuration file, can't create permanent room\n");
1348
                        error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR;
1349
                        g_snprintf(error_cause, 512, "No configuration file, can't create permanent room");
1350 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1351 2512456f Lorenzo Miniero
                }
1352 d24aff33 meetecho
                guint64 room_id = 0;
1353
                json_t *room = json_object_get(root, "room");
1354 887df302 Andreas Girgensohn
                if(room) {
1355 d24aff33 meetecho
                        room_id = json_integer_value(room);
1356
                        if(room_id == 0) {
1357
                                JANUS_LOG(LOG_WARN, "Desired room ID is 0, which is not allowed... picking random ID instead\n");
1358
                        }
1359
                }
1360
                janus_mutex_lock(&rooms_mutex);
1361
                if(room_id > 0) {
1362
                        /* Let's make sure the room doesn't exist already */
1363 d236f0e9 Lorenzo Miniero
                        if(g_hash_table_lookup(rooms, &room_id) != NULL) {
1364 d24aff33 meetecho
                                /* It does... */
1365
                                janus_mutex_unlock(&rooms_mutex);
1366
                                JANUS_LOG(LOG_ERR, "Room %"SCNu64" already exists!\n", room_id);
1367
                                error_code = JANUS_VIDEOROOM_ERROR_ROOM_EXISTS;
1368 c94052c2 meetecho
                                g_snprintf(error_cause, 512, "Room %"SCNu64" already exists", room_id);
1369 db1e9a1f Lorenzo Miniero
                                goto plugin_response;
1370 d24aff33 meetecho
                        }
1371
                }
1372 554cc3a5 Computician
                /* Create the room */
1373 1f067658 Lorenzo Miniero
                janus_videoroom *videoroom = g_malloc0(sizeof(janus_videoroom));
1374 d24aff33 meetecho
                /* Generate a random ID */
1375
                if(room_id == 0) {
1376
                        while(room_id == 0) {
1377 d236f0e9 Lorenzo Miniero
                                room_id = janus_random_uint64();
1378
                                if(g_hash_table_lookup(rooms, &room_id) != NULL) {
1379 d24aff33 meetecho
                                        /* Room ID already taken, try another one */
1380
                                        room_id = 0;
1381
                                }
1382
                        }
1383
                }
1384
                videoroom->room_id = room_id;
1385
                char *description = NULL;
1386 d1302e06 meetecho
                if(desc != NULL && strlen(json_string_value(desc)) > 0) {
1387 d24aff33 meetecho
                        description = g_strdup(json_string_value(desc));
1388
                } else {
1389
                        char roomname[255];
1390 c94052c2 meetecho
                        g_snprintf(roomname, 255, "Room %"SCNu64"", videoroom->room_id);
1391 d24aff33 meetecho
                        description = g_strdup(roomname);
1392
                }
1393
                videoroom->room_name = description;
1394 0a466e28 janus
                videoroom->is_private = is_private ? json_is_true(is_private) : FALSE;
1395 d24aff33 meetecho
                if(secret)
1396
                        videoroom->room_secret = g_strdup(json_string_value(secret));
1397 5d20bcec Lorenzo Miniero
                if(pin)
1398
                        videoroom->room_pin = g_strdup(json_string_value(pin));
1399 d24aff33 meetecho
                videoroom->max_publishers = 3;        /* FIXME How should we choose a default? */
1400
                if(publishers)
1401
                        videoroom->max_publishers = json_integer_value(publishers);
1402
                if(videoroom->max_publishers < 0)
1403
                        videoroom->max_publishers = 3;        /* FIXME How should we choose a default? */
1404
                videoroom->bitrate = 0;
1405
                if(bitrate)
1406
                        videoroom->bitrate = json_integer_value(bitrate);
1407
                if(videoroom->bitrate > 0 && videoroom->bitrate < 64000)
1408
                        videoroom->bitrate = 64000;        /* Don't go below 64k */
1409
                videoroom->fir_freq = 0;
1410
                if(fir_freq)
1411
                        videoroom->fir_freq = json_integer_value(fir_freq);
1412 ba99f281 pallab-gain
                videoroom->acodec = JANUS_VIDEOROOM_OPUS;
1413
                if(audiocodec) {
1414
                        const char *audiocodec_value = json_string_value(audiocodec);
1415
                        if(!strcasecmp(audiocodec_value, "opus"))
1416
                                videoroom->acodec = JANUS_VIDEOROOM_OPUS;
1417
                        else if(!strcasecmp(audiocodec_value, "isac32"))
1418
                                videoroom->acodec = JANUS_VIDEOROOM_ISAC_32K;
1419
                        else if(!strcasecmp(audiocodec_value, "isac16"))
1420
                                videoroom->acodec = JANUS_VIDEOROOM_ISAC_16K;
1421
                        else if(!strcasecmp(audiocodec_value, "pcmu"))
1422
                                videoroom->acodec = JANUS_VIDEOROOM_PCMU;
1423 fd8cb2d5 pallab-gain
                        else if(!strcasecmp(audiocodec_value, "pcma"))
1424
                                videoroom->acodec = JANUS_VIDEOROOM_PCMA;
1425 183d715f Lorenzo Miniero
                        else if(!strcasecmp(audiocodec_value, "g722"))
1426
                                videoroom->acodec = JANUS_VIDEOROOM_G722;
1427 ba99f281 pallab-gain
                        else {
1428
                                JANUS_LOG(LOG_WARN, "Unsupported audio codec '%s', falling back to OPUS\n", audiocodec_value);
1429
                                videoroom->acodec = JANUS_VIDEOROOM_OPUS;
1430
                        }
1431
                }
1432 fd8cb2d5 pallab-gain
                videoroom->vcodec = JANUS_VIDEOROOM_VP8;
1433
                if(videocodec) {
1434
                        const char *videocodec_value = json_string_value(videocodec);
1435
                        if(!strcasecmp(videocodec_value, "vp8"))
1436
                                videoroom->vcodec = JANUS_VIDEOROOM_VP8;
1437
                        else if(!strcasecmp(videocodec_value, "vp9"))
1438
                                videoroom->vcodec = JANUS_VIDEOROOM_VP9;
1439
                        else if(!strcasecmp(videocodec_value, "h264"))
1440
                                videoroom->vcodec = JANUS_VIDEOROOM_H264;
1441
                        else {
1442
                                JANUS_LOG(LOG_WARN, "Unsupported video codec '%s', falling back to VP8\n", videocodec_value);
1443
                                videoroom->vcodec = JANUS_VIDEOROOM_VP8;
1444
                        }
1445
                }
1446 ef61c470 Lorenzo Miniero
                videoroom->audiolevel_ext = audiolevel_ext ? json_is_true(audiolevel_ext) : TRUE;
1447 bd0e3260 mirkobrankovic
                videoroom->audiolevel_event = audiolevel_event ? json_is_true(audiolevel_event) : FALSE;
1448 fa9b2d6a Lorenzo Miniero
                if(videoroom->audiolevel_event) {
1449
                        videoroom->audio_active_packets = 100;
1450
                        if(json_integer_value(audio_active_packets) > 0) {
1451
                                videoroom->audio_active_packets = json_integer_value(audio_active_packets);
1452
                        } else {
1453
                                JANUS_LOG(LOG_WARN, "Invalid audio_active_packets value provided, using default: %d\n", videoroom->audio_active_packets);
1454
                        }
1455
                        videoroom->audio_level_average = 25;
1456
                        if(json_integer_value(audio_level_average) > 0) {
1457
                                videoroom->audio_level_average = json_integer_value(audio_level_average);
1458
                        } else {
1459
                                JANUS_LOG(LOG_WARN, "Invalid audio_level_average value provided, using default: %d\n", videoroom->audio_level_average);
1460
                        }
1461 1cbed49f mirkobrankovic
                }
1462 ef61c470 Lorenzo Miniero
                videoroom->videoorient_ext = videoorient_ext ? json_is_true(videoorient_ext) : TRUE;
1463
                videoroom->playoutdelay_ext = playoutdelay_ext ? json_is_true(playoutdelay_ext) : TRUE;
1464 d24aff33 meetecho
                if(record) {
1465
                        videoroom->record = json_is_true(record);
1466 ab0d61f1 Lorenzo Miniero
                }
1467
                if(rec_dir) {
1468
                        videoroom->rec_dir = g_strdup(json_string_value(rec_dir));
1469 d24aff33 meetecho
                }
1470 598aac14 Computician
                videoroom->destroyed = 0;
1471 46d28722 Computician
                janus_mutex_init(&videoroom->participants_mutex);
1472 d236f0e9 Lorenzo Miniero
                videoroom->participants = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL);
1473 543cffca Lorenzo Miniero
                videoroom->private_ids = g_hash_table_new(NULL, NULL);
1474 30df7fbf Lorenzo Miniero
                videoroom->allowed = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL);
1475
                if(allowed != NULL) {
1476
                        /* Populate the "allowed" list as an ACL for people trying to join */
1477
                        if(json_array_size(allowed) > 0) {
1478
                                size_t i = 0;
1479
                                for(i=0; i<json_array_size(allowed); i++) {
1480
                                        const char *token = json_string_value(json_array_get(allowed, i));
1481
                                        if(!g_hash_table_lookup(videoroom->allowed, token))
1482
                                                g_hash_table_insert(videoroom->allowed, g_strdup(token), GINT_TO_POINTER(TRUE));
1483
                                }
1484
                        }
1485
                        videoroom->check_tokens = TRUE;
1486
                }
1487 ba5ca81a Lorenzo Miniero
                JANUS_LOG(LOG_VERB, "Created videoroom: %"SCNu64" (%s, %s, %s/%s codecs, secret: %s, pin: %s)\n",
1488 5d20bcec Lorenzo Miniero
                        videoroom->room_id, videoroom->room_name,
1489
                        videoroom->is_private ? "private" : "public",
1490 ba99f281 pallab-gain
                        janus_videoroom_audiocodec_name(videoroom->acodec),
1491 a277099d pallab-gain
                        janus_videoroom_videocodec_name(videoroom->vcodec),
1492 5d20bcec Lorenzo Miniero
                        videoroom->room_secret ? videoroom->room_secret : "no secret",
1493
                        videoroom->room_pin ? videoroom->room_pin : "no pin");
1494 d24aff33 meetecho
                if(videoroom->record) {
1495
                        JANUS_LOG(LOG_VERB, "  -- Room is going to be recorded in %s\n", videoroom->rec_dir ? videoroom->rec_dir : "the current folder");
1496
                }
1497 2512456f Lorenzo Miniero
                if(save) {
1498
                        /* This room is permanent: save to the configuration file too
1499
                         * FIXME: We should check if anything fails... */
1500
                        JANUS_LOG(LOG_VERB, "Saving room %"SCNu64" permanently in config file\n", videoroom->room_id);
1501
                        janus_mutex_lock(&config_mutex);
1502
                        char cat[BUFSIZ], value[BUFSIZ];
1503
                        /* The room ID is the category */
1504
                        g_snprintf(cat, BUFSIZ, "%"SCNu64, videoroom->room_id);
1505
                        janus_config_add_category(config, cat);
1506
                        /* Now for the values */
1507
                        janus_config_add_item(config, cat, "description", videoroom->room_name);
1508
                        if(videoroom->is_private)
1509
                                janus_config_add_item(config, cat, "is_private", "yes");
1510
                        g_snprintf(value, BUFSIZ, "%"SCNu64, videoroom->bitrate);
1511
                        janus_config_add_item(config, cat, "bitrate", value);
1512
                        g_snprintf(value, BUFSIZ, "%d", videoroom->max_publishers);
1513
                        janus_config_add_item(config, cat, "publishers", value);
1514
                        if(videoroom->fir_freq) {
1515
                                g_snprintf(value, BUFSIZ, "%"SCNu16, videoroom->fir_freq);
1516
                                janus_config_add_item(config, cat, "fir_freq", value);
1517
                        }
1518 ba99f281 pallab-gain
                        janus_config_add_item(config, cat, "audiocodec", janus_videoroom_audiocodec_name(videoroom->acodec));
1519 78229f3f pallab-gain
                        janus_config_add_item(config, cat, "videocodec", janus_videoroom_videocodec_name(videoroom->vcodec));
1520 2512456f Lorenzo Miniero
                        if(videoroom->room_secret)
1521
                                janus_config_add_item(config, cat, "secret", videoroom->room_secret);
1522
                        if(videoroom->room_pin)
1523
                                janus_config_add_item(config, cat, "pin", videoroom->room_pin);
1524 ab0d61f1 Lorenzo Miniero
                        if(videoroom->record)
1525 2512456f Lorenzo Miniero
                                janus_config_add_item(config, cat, "record", "yes");
1526 ab0d61f1 Lorenzo Miniero
                        if(videoroom->rec_dir)
1527 2512456f Lorenzo Miniero
                                janus_config_add_item(config, cat, "rec_dir", videoroom->rec_dir);
1528
                        /* Save modified configuration */
1529 b6c6c592 Lorenzo Miniero
                        if(janus_config_save(config, config_folder, JANUS_VIDEOROOM_PACKAGE) < 0)
1530
                                save = FALSE;        /* This will notify the user the room is not permanent */
1531 2512456f Lorenzo Miniero
                        janus_mutex_unlock(&config_mutex);
1532
                }
1533 d24aff33 meetecho
                /* Show updated rooms list */
1534
                GHashTableIter iter;
1535
                gpointer value;
1536 d236f0e9 Lorenzo Miniero
                g_hash_table_insert(rooms, janus_uint64_dup(videoroom->room_id), videoroom);
1537 d24aff33 meetecho
                g_hash_table_iter_init(&iter, rooms);
1538
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
1539
                        janus_videoroom *vr = value;
1540
                        JANUS_LOG(LOG_VERB, "  ::: [%"SCNu64"][%s] %"SCNu64", max %d publishers, FIR frequency of %d seconds\n", vr->room_id, vr->room_name, vr->bitrate, vr->max_publishers, vr->fir_freq);
1541
                }
1542
                janus_mutex_unlock(&rooms_mutex);
1543
                /* Send info back */
1544 4078ed95 Pierce Lopez
                response = json_object();
1545 d24aff33 meetecho
                json_object_set_new(response, "videoroom", json_string("created"));
1546
                json_object_set_new(response, "room", json_integer(videoroom->room_id));
1547 b6c6c592 Lorenzo Miniero
                json_object_set_new(response, "permanent", save ? json_true() : json_false());
1548 4d8c7356 Lorenzo Miniero
                /* Also notify event handlers */
1549 71a04f89 Lorenzo Miniero
                if(notify_events && gateway->events_is_enabled()) {
1550 4d8c7356 Lorenzo Miniero
                        json_t *info = json_object();
1551
                        json_object_set_new(info, "event", json_string("created"));
1552
                        json_object_set_new(info, "room", json_integer(videoroom->room_id));
1553 c734a91a Lorenzo Miniero
                        gateway->notify_event(&janus_videoroom_plugin, session->handle, info);
1554 4d8c7356 Lorenzo Miniero
                }
1555 4078ed95 Pierce Lopez
                goto plugin_response;
1556 d24aff33 meetecho
        } else if(!strcasecmp(request_text, "destroy")) {
1557
                JANUS_LOG(LOG_VERB, "Attempt to destroy an existing videoroom room\n");
1558 887df302 Andreas Girgensohn
                JANUS_VALIDATE_JSON_OBJECT(root, destroy_parameters,
1559
                        error_code, error_cause, TRUE,
1560
                        JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
1561
                if(error_code != 0)
1562 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1563 887df302 Andreas Girgensohn
                json_t *room = json_object_get(root, "room");
1564 2512456f Lorenzo Miniero
                json_t *permanent = json_object_get(root, "permanent");
1565
                gboolean save = permanent ? json_is_true(permanent) : FALSE;
1566
                if(save && config == NULL) {
1567
                        JANUS_LOG(LOG_ERR, "No configuration file, can't destroy room permanently\n");
1568
                        error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR;
1569
                        g_snprintf(error_cause, 512, "No configuration file, can't destroy room permanently");
1570 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1571 2512456f Lorenzo Miniero
                }
1572 d24aff33 meetecho
                guint64 room_id = json_integer_value(room);
1573
                janus_mutex_lock(&rooms_mutex);
1574 f12f3dbb Andreas Girgensohn
                janus_videoroom *videoroom = NULL;
1575 a046cf75 Andreas Girgensohn
                error_code = janus_videoroom_access_room(root, TRUE, FALSE, &videoroom, error_cause, sizeof(error_cause));
1576 4a64b558 Andreas Girgensohn
                if(error_code != 0) {
1577
                        janus_mutex_unlock(&rooms_mutex);
1578 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1579 d24aff33 meetecho
                }
1580
                /* Notify all participants that the fun is over, and that they'll be kicked */
1581
                JANUS_LOG(LOG_VERB, "Notifying all participants\n");
1582
                json_t *destroyed = json_object();
1583
                json_object_set_new(destroyed, "videoroom", json_string("destroyed"));
1584
                json_object_set_new(destroyed, "room", json_integer(videoroom->room_id));
1585
                GHashTableIter iter;
1586
                gpointer value;
1587 46d28722 Computician
                /* Remove room lazily*/
1588
                videoroom->destroyed = janus_get_monotonic_time();
1589
                old_rooms = g_list_append(old_rooms, videoroom);
1590
                janus_mutex_lock(&videoroom->participants_mutex);
1591 d24aff33 meetecho
                g_hash_table_iter_init(&iter, videoroom->participants);
1592
                while (g_hash_table_iter_next(&iter, NULL, &value)) {
1593
                        janus_videoroom_participant *p = value;
1594
                        if(p && p->session) {
1595
                                /* Notify the user we're going to destroy the room... */
1596 dd11fa0a Lorenzo Miniero
                                int ret = gateway->push_event(p->session->handle, &janus_videoroom_plugin, NULL, destroyed, NULL);
1597 d24aff33 meetecho
                                JANUS_LOG(LOG_VERB, "  >> %d (%s)\n", ret, janus_get_api_error(ret));
1598
                                /* ... and then ask the core to remove the handle */
1599
                                gateway->end_session(p->session->handle);
1600
                        }
1601
                }
1602
                json_decref(destroyed);
1603 46d28722 Computician
                janus_mutex_unlock(&videoroom->participants_mutex);
1604 4d8c7356 Lorenzo Miniero
                /* Also notify event handlers */
1605 71a04f89 Lorenzo Miniero
                if(notify_events && gateway->events_is_enabled()) {
1606 4d8c7356 Lorenzo Miniero
                        json_t *info = json_object();
1607
                        json_object_set_new(info, "event", json_string("destroyed"));
1608
                        json_object_set_new(info, "room", json_integer(room_id));
1609 c734a91a Lorenzo Miniero
                        gateway->notify_event(&janus_videoroom_plugin, session->handle, info);
1610 4d8c7356 Lorenzo Miniero
                }
1611 d24aff33 meetecho
                janus_mutex_unlock(&rooms_mutex);
1612 2512456f Lorenzo Miniero
                if(save) {
1613
                        /* This change is permanent: save to the configuration file too
1614
                         * FIXME: We should check if anything fails... */
1615
                        JANUS_LOG(LOG_VERB, "Destroying room %"SCNu64" permanently in config file\n", room_id);
1616
                        janus_mutex_lock(&config_mutex);
1617
                        char cat[BUFSIZ];
1618
                        /* The room ID is the category */
1619
                        g_snprintf(cat, BUFSIZ, "%"SCNu64, room_id);
1620
                        janus_config_remove_category(config, cat);
1621
                        /* Save modified configuration */
1622
                        janus_config_save(config, config_folder, JANUS_VIDEOROOM_PACKAGE);
1623
                        janus_mutex_unlock(&config_mutex);
1624
                }
1625 d24aff33 meetecho
                /* Done */
1626 4078ed95 Pierce Lopez
                response = json_object();
1627 d24aff33 meetecho
                json_object_set_new(response, "videoroom", json_string("destroyed"));
1628
                json_object_set_new(response, "room", json_integer(room_id));
1629 4078ed95 Pierce Lopez
                goto plugin_response;
1630 a12faa33 Computician
        } else if(!strcasecmp(request_text, "list")) {
1631 8f05dbe2 meetecho
                /* List all rooms (but private ones) and their details (except for the secret, of course...) */
1632 a12faa33 Computician
                json_t *list = json_array();
1633 8f05dbe2 meetecho
                JANUS_LOG(LOG_VERB, "Getting the list of video rooms\n");
1634 a12faa33 Computician
                janus_mutex_lock(&rooms_mutex);
1635
                GHashTableIter iter;
1636
                gpointer value;
1637
                g_hash_table_iter_init(&iter, rooms);
1638
                while(g_hash_table_iter_next(&iter, NULL, &value)) {
1639
                        janus_videoroom *room = value;
1640 8f05dbe2 meetecho
                        if(!room)
1641
                                continue;
1642 0a466e28 janus
                        if(room->is_private) {
1643 8f05dbe2 meetecho
                                /* Skip private room */
1644
                                JANUS_LOG(LOG_VERB, "Skipping private room '%s'\n", room->room_name);
1645
                                continue;
1646
                        }
1647 598aac14 Computician
                        if(!room->destroyed) {
1648
                                json_t *rl = json_object();
1649
                                json_object_set_new(rl, "room", json_integer(room->room_id));
1650
                                json_object_set_new(rl, "description", json_string(room->room_name));
1651 cabe80ea Lorenzo Miniero
                                json_object_set_new(rl, "pin_required", room->room_pin ? json_true() : json_false());
1652 598aac14 Computician
                                json_object_set_new(rl, "max_publishers", json_integer(room->max_publishers));
1653
                                json_object_set_new(rl, "bitrate", json_integer(room->bitrate));
1654
                                json_object_set_new(rl, "fir_freq", json_integer(room->fir_freq));
1655 ba99f281 pallab-gain
                                json_object_set_new(rl, "audiocodec", json_string(janus_videoroom_audiocodec_name(room->acodec)));
1656 fd8cb2d5 pallab-gain
                                json_object_set_new(rl, "videocodec", json_string(janus_videoroom_videocodec_name(room->vcodec)));
1657 51db8a86 Lorenzo Miniero
                                json_object_set_new(rl, "record", room->record ? json_true() : json_false());
1658 598aac14 Computician
                                json_object_set_new(rl, "rec_dir", json_string(room->rec_dir));
1659 8f05dbe2 meetecho
                                /* TODO: Should we list participants as well? or should there be a separate API call on a specific room for this? */
1660 598aac14 Computician
                                json_object_set_new(rl, "num_participants", json_integer(g_hash_table_size(room->participants)));
1661
                                json_array_append_new(list, rl);
1662
                        }
1663 a12faa33 Computician
                }
1664
                janus_mutex_unlock(&rooms_mutex);
1665 4078ed95 Pierce Lopez
                response = json_object();
1666 a12faa33 Computician
                json_object_set_new(response, "videoroom", json_string("success"));
1667
                json_object_set_new(response, "list", list);
1668 4078ed95 Pierce Lopez
                goto plugin_response;
1669 e62ea677 Computician
        } else if(!strcasecmp(request_text, "rtp_forward")) {
1670 887df302 Andreas Girgensohn
                JANUS_VALIDATE_JSON_OBJECT(root, rtp_forward_parameters,
1671
                        error_code, error_cause, TRUE,
1672
                        JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
1673
                if(error_code != 0)
1674 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1675 887df302 Andreas Girgensohn
                json_t *room = json_object_get(root, "room");
1676 eedcc449 Computician
                json_t *pub_id = json_object_get(root, "publisher_id");
1677 0284f49f Lorenzo Miniero
                int video_port = -1, video_pt = 0;
1678
                uint32_t video_ssrc = 0;
1679
                int audio_port = -1, audio_pt = 0;
1680
                uint32_t audio_ssrc = 0;
1681 39b52c9a mirkobrankovic
                int data_port = -1;
1682 eedcc449 Computician
                json_t *vid_port = json_object_get(root, "video_port");
1683 e62ea677 Computician
                if(vid_port) {
1684
                        video_port = json_integer_value(vid_port);
1685 0284f49f Lorenzo Miniero
                        json_t *pt = json_object_get(root, "video_pt");
1686
                        if(pt)
1687
                                video_pt = json_integer_value(pt);
1688
                        json_t *ssrc = json_object_get(root, "video_ssrc");
1689
                        if(ssrc)
1690
                                video_ssrc = json_integer_value(ssrc);
1691 eedcc449 Computician
                }
1692 e62ea677 Computician
                json_t *au_port = json_object_get(root, "audio_port");
1693
                if(au_port) {
1694
                        audio_port = json_integer_value(au_port);
1695 0284f49f Lorenzo Miniero
                        json_t *pt = json_object_get(root, "audio_pt");
1696
                        if(pt)
1697
                                audio_pt = json_integer_value(pt);
1698
                        json_t *ssrc = json_object_get(root, "audio_ssrc");
1699
                        if(ssrc)
1700
                                audio_ssrc = json_integer_value(ssrc);
1701 e62ea677 Computician
                }
1702 39b52c9a mirkobrankovic
                json_t *d_port = json_object_get(root, "data_port");
1703
                if(d_port) {
1704
                        data_port = json_integer_value(d_port);
1705
                }
1706 eedcc449 Computician
                json_t *json_host = json_object_get(root, "host");
1707 39b52c9a mirkobrankovic
                
1708 eedcc449 Computician
                guint64 room_id = json_integer_value(room);
1709
                guint64 publisher_id = json_integer_value(pub_id);
1710
                const gchar* host = json_string_value(json_host);
1711
                janus_mutex_lock(&rooms_mutex);
1712 f12f3dbb Andreas Girgensohn
                janus_videoroom *videoroom = NULL;
1713 a046cf75 Andreas Girgensohn
                error_code = janus_videoroom_access_room(root, TRUE, FALSE, &videoroom, error_cause, sizeof(error_cause));
1714 24998246 Pierce Lopez
                janus_mutex_unlock(&rooms_mutex);
1715 a046cf75 Andreas Girgensohn
                if(error_code != 0)
1716 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1717 eedcc449 Computician
                janus_mutex_lock(&videoroom->participants_mutex);
1718 d236f0e9 Lorenzo Miniero
                janus_videoroom_participant* publisher = g_hash_table_lookup(videoroom->participants, &publisher_id);
1719 eedcc449 Computician
                if(publisher == NULL) {
1720
                        janus_mutex_unlock(&videoroom->participants_mutex);
1721
                        JANUS_LOG(LOG_ERR, "No such publisher (%"SCNu64")\n", publisher_id);
1722
                        error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_FEED;
1723
                        g_snprintf(error_cause, 512, "No such feed (%"SCNu64")", publisher_id);
1724 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1725 eedcc449 Computician
                }
1726
                if(publisher->udp_sock <= 0) {
1727
                        publisher->udp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1728
                        if(publisher->udp_sock <= 0) {
1729
                                janus_mutex_unlock(&videoroom->participants_mutex);
1730
                                JANUS_LOG(LOG_ERR, "Could not open UDP socket for rtp stream for publisher (%"SCNu64")\n", publisher_id);
1731
                                error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR;
1732
                                g_snprintf(error_cause, 512, "Could not open UDP socket for rtp stream");
1733 db1e9a1f Lorenzo Miniero
                                goto plugin_response;
1734 eedcc449 Computician
                        }
1735
                }
1736 e62ea677 Computician
                guint32 audio_handle = 0;
1737
                guint32 video_handle = 0;
1738 39b52c9a mirkobrankovic
                guint32 data_handle = 0;
1739 e62ea677 Computician
                if(audio_port > 0) {
1740 39b52c9a mirkobrankovic
                        audio_handle = janus_videoroom_rtp_forwarder_add_helper(publisher, host, audio_port, audio_pt, audio_ssrc, FALSE, FALSE);
1741 e62ea677 Computician
                }
1742
                if(video_port > 0) {
1743 39b52c9a mirkobrankovic
                        video_handle = janus_videoroom_rtp_forwarder_add_helper(publisher, host, video_port, video_pt, video_ssrc, TRUE, FALSE);
1744
                }
1745
                if(data_port > 0) {
1746
                        data_handle = janus_videoroom_rtp_forwarder_add_helper(publisher, host, data_port, 0, 0, FALSE, TRUE);
1747 e62ea677 Computician
                }
1748 eedcc449 Computician
                janus_mutex_unlock(&videoroom->participants_mutex);
1749
                response = json_object();
1750
                json_t* rtp_stream = json_object();
1751 e62ea677 Computician
                if(audio_handle > 0) {
1752
                        json_object_set_new(rtp_stream, "audio_stream_id", json_integer(audio_handle));
1753
                        json_object_set_new(rtp_stream, "audio", json_integer(audio_port));
1754
                }
1755
                if(video_handle > 0) {
1756 9c214010 amnonbb
                        /* Send a FIR to the new RTP forward publisher */
1757
                        char buf[20];
1758
                        janus_rtcp_fir((char *)&buf, 20, &publisher->fir_seq);
1759
                        JANUS_LOG(LOG_VERB, "New RTP forward publisher, sending FIR to %"SCNu64" (%s)\n", publisher->user_id, publisher->display ? publisher->display : "??");
1760
                        gateway->relay_rtcp(publisher->session->handle, 1, buf, 20);
1761
                        /* Send a PLI too, just in case... */
1762
                        janus_rtcp_pli((char *)&buf, 12);
1763
                        JANUS_LOG(LOG_VERB, "New RTP forward publisher, sending PLI to %"SCNu64" (%s)\n", publisher->user_id, publisher->display ? publisher->display : "??");
1764
                        gateway->relay_rtcp(publisher->session->handle, 1, buf, 12);
1765
                        /* Done */
1766 e62ea677 Computician
                        json_object_set_new(rtp_stream, "video_stream_id", json_integer(video_handle));
1767
                        json_object_set_new(rtp_stream, "video", json_integer(video_port));
1768
                }
1769 39b52c9a mirkobrankovic
                if(data_handle > 0) {
1770
                        json_object_set_new(rtp_stream, "data_stream_id", json_integer(data_handle));
1771
                        json_object_set_new(rtp_stream, "data", json_integer(data_port));
1772
                }
1773 e62ea677 Computician
                json_object_set_new(rtp_stream, "host", json_string(host));
1774 eedcc449 Computician
                json_object_set_new(response, "publisher_id", json_integer(publisher_id));
1775
                json_object_set_new(response, "rtp_stream", rtp_stream);
1776
                json_object_set_new(response, "room", json_integer(room_id));
1777 e62ea677 Computician
                json_object_set_new(response, "videoroom", json_string("rtp_forward"));
1778 eedcc449 Computician
                goto plugin_response;
1779 e62ea677 Computician
        } else if(!strcasecmp(request_text, "stop_rtp_forward")) {
1780 887df302 Andreas Girgensohn
                JANUS_VALIDATE_JSON_OBJECT(root, stop_rtp_forward_parameters,
1781
                        error_code, error_cause, TRUE,
1782
                        JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
1783
                if(error_code != 0)
1784 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1785 887df302 Andreas Girgensohn
                json_t *room = json_object_get(root, "room");
1786 eedcc449 Computician
                json_t *pub_id = json_object_get(root, "publisher_id");
1787
                json_t *id = json_object_get(root, "stream_id");
1788
1789
                guint64 room_id = json_integer_value(room);
1790
                guint64 publisher_id = json_integer_value(pub_id);
1791 a417f443 Lorenzo Miniero
                guint32 stream_id = json_integer_value(id);
1792 eedcc449 Computician
                janus_mutex_lock(&rooms_mutex);
1793 f12f3dbb Andreas Girgensohn
                janus_videoroom *videoroom = NULL;
1794 a046cf75 Andreas Girgensohn
                error_code = janus_videoroom_access_room(root, TRUE, FALSE, &videoroom, error_cause, sizeof(error_cause));
1795 24998246 Pierce Lopez
                janus_mutex_unlock(&rooms_mutex);
1796 a046cf75 Andreas Girgensohn
                if(error_code != 0)
1797 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1798 eedcc449 Computician
                janus_mutex_lock(&videoroom->participants_mutex);
1799 d236f0e9 Lorenzo Miniero
                janus_videoroom_participant *publisher = g_hash_table_lookup(videoroom->participants, &publisher_id);
1800 eedcc449 Computician
                if(publisher == NULL) {
1801
                        janus_mutex_unlock(&videoroom->participants_mutex);
1802
                        JANUS_LOG(LOG_ERR, "No such publisher (%"SCNu64")\n", publisher_id);
1803
                        error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_FEED;
1804
                        g_snprintf(error_cause, 512, "No such feed (%"SCNu64")", publisher_id);
1805 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1806 eedcc449 Computician
                }
1807 e62ea677 Computician
                janus_mutex_lock(&publisher->rtp_forwarders_mutex);
1808 a417f443 Lorenzo Miniero
                if(g_hash_table_lookup(publisher->rtp_forwarders, GUINT_TO_POINTER(stream_id)) == NULL) {
1809
                        janus_mutex_unlock(&publisher->rtp_forwarders_mutex);
1810
                        janus_mutex_unlock(&videoroom->participants_mutex);
1811
                        JANUS_LOG(LOG_ERR, "No such stream (%"SCNu32")\n", stream_id);
1812
                        error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_FEED;
1813
                        g_snprintf(error_cause, 512, "No such stream (%"SCNu32")", stream_id);
1814 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1815 a417f443 Lorenzo Miniero
                }
1816 e62ea677 Computician
                g_hash_table_remove(publisher->rtp_forwarders, GUINT_TO_POINTER(stream_id));
1817
                janus_mutex_unlock(&publisher->rtp_forwarders_mutex);
1818 eedcc449 Computician
                janus_mutex_unlock(&videoroom->participants_mutex);
1819
                response = json_object();
1820 e62ea677 Computician
                json_object_set_new(response, "videoroom", json_string("stop_rtp_forward"));
1821 eedcc449 Computician
                json_object_set_new(response, "room", json_integer(room_id));
1822
                json_object_set_new(response, "publisher_id", json_integer(publisher_id));
1823
                json_object_set_new(response, "stream_id", json_integer(stream_id));
1824
                goto plugin_response;
1825 a12faa33 Computician
        } else if(!strcasecmp(request_text, "exists")) {
1826 39b52c9a mirkobrankovic
                /* Check whether a given room exists or not, returns true/false */        
1827 887df302 Andreas Girgensohn
                JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
1828
                        error_code, error_cause, TRUE,
1829
                        JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
1830
                if(error_code != 0)
1831 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
1832 887df302 Andreas Girgensohn
                json_t *room = json_object_get(root, "room");
1833 a12faa33 Computician
                guint64 room_id = json_integer_value(room);
1834
                janus_mutex_lock(&rooms_mutex);
1835 d236f0e9 Lorenzo Miniero
                gboolean room_exists = g_hash_table_contains(rooms, &room_id);
1836 a12faa33 Computician
                janus_mutex_unlock(&rooms_mutex);
1837 4078ed95 Pierce Lopez
                response = json_object();
1838 a12faa33 Computician
                json_object_set_new(response, "videoroom", json_string("success"));
1839
                json_object_set_new(response, "room", json_integer(room_id));
1840 51db8a86 Lorenzo Miniero
                json_object_set_new(response, "exists", room_exists ? json_true() : json_false());
1841 4078ed95 Pierce Lopez
                goto plugin_response;
1842 30df7fbf Lorenzo Miniero
        } else if(!strcasecmp(request_text, "allowed")) {
1843
                JANUS_LOG(LOG_VERB, "Attempt to edit the list of allowed participants in an existing videoroom room\n");
1844
                JANUS_VALIDATE_JSON_OBJECT(root, allowed_parameters,
1845
                        error_code, error_cause, TRUE,
1846
                        JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
1847
                if(error_code != 0)
1848
                        goto plugin_response;
1849
                json_t *action = json_object_get(root, "action");
1850
                json_t *room = json_object_get(root, "room");
1851
                json_t *allowed = json_object_get(root, "allowed");
1852
                const char *action_text = json_string_value(action);
1853
                if(strcasecmp(action_text, "enable") && strcasecmp(action_text, "disable") &&
1854
                                strcasecmp(action_text, "add") && strcasecmp(action_text, "remove")) {
1855
                        JANUS_LOG(LOG_ERR, "Unsupported action '%s' (allowed)\n", action_text);
1856
                        error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
1857
                        g_snprintf(error_cause, 512, "Unsupported action '%s' (allowed)", action_text);
1858
                        goto plugin_response;
1859
                }
1860
                guint64 room_id = json_integer_value(room);
1861
                janus_mutex_lock(&rooms_mutex);
1862
                janus_videoroom *videoroom = g_hash_table_lookup(rooms, &room_id);
1863
                if(videoroom == NULL) {
1864
                        janus_mutex_unlock(&rooms_mutex);
1865
                        JANUS_LOG(LOG_ERR, "No such room (%"SCNu64")\n", room_id);
1866
                        error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM;
1867
                        g_snprintf(error_cause, 512, "No such room (%"SCNu64")", room_id);
1868
                        goto plugin_response;
1869
                }
1870
                /* A secret may be required for this action */
1871
                JANUS_CHECK_SECRET(videoroom->room_secret, root, "secret", error_code, error_cause,
1872
                        JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT, JANUS_VIDEOROOM_ERROR_UNAUTHORIZED);
1873
                if(error_code != 0) {
1874
                        janus_mutex_unlock(&rooms_mutex);
1875
                        goto plugin_response;
1876
                }
1877
                if(!strcasecmp(action_text, "enable")) {
1878
                        JANUS_LOG(LOG_VERB, "Enabling the check on allowed authorization tokens for room %"SCNu64"\n", room_id);
1879
                        videoroom->check_tokens = TRUE;
1880
                } else if(!strcasecmp(action_text, "disable")) {
1881
                        JANUS_LOG(LOG_VERB, "Disabling the check on allowed authorization tokens for room %"SCNu64" (free entry)\n", room_id);
1882
                        videoroom->check_tokens = FALSE;
1883
                } else {
1884
                        gboolean add = !strcasecmp(action_text, "add");
1885
                        if(allowed) {
1886
                                /* Make sure the "allowed" array only contains strings */
1887
                                gboolean ok = TRUE;
1888
                                if(json_array_size(allowed) > 0) {
1889
                                        size_t i = 0;
1890
                                        for(i=0; i<json_array_size(allowed); i++) {
1891
                                                json_t *a = json_array_get(allowed, i);
1892
                                                if(!a || !json_is_string(a)) {
1893
                                                        ok = FALSE;
1894
                                                        break;
1895
                                                }
1896
                                        }
1897
                                }
1898
                                if(!ok) {
1899
                                        JANUS_LOG(LOG_ERR, "Invalid element in the allowed array (not a string)\n");
1900
                                        error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
1901
                                        g_snprintf(error_cause, 512, "Invalid element in the allowed array (not a string)");
1902
                                        janus_mutex_unlock(&rooms_mutex);
1903
                                        goto plugin_response;
1904
                                }
1905
                                size_t i = 0;
1906
                                for(i=0; i<json_array_size(allowed); i++) {
1907
                                        const char *token = json_string_value(json_array_get(allowed, i));
1908
                                        if(add) {
1909
                                                if(!g_hash_table_lookup(videoroom->allowed, token))
1910
                                                        g_hash_table_insert(videoroom->allowed, g_strdup(token), GINT_TO_POINTER(TRUE));
1911
                                        } else {
1912
                                                g_hash_table_remove(videoroom->allowed, token);
1913
                                        }
1914
                                }
1915
                        }
1916
                }
1917
                /* Prepare response */
1918
                response = json_object();
1919
                json_object_set_new(response, "videoroom", json_string("success"));
1920
                json_object_set_new(response, "room", json_integer(videoroom->room_id));
1921
                json_t *list = json_array();
1922
                if(strcasecmp(action_text, "disable")) {
1923
                        if(g_hash_table_size(videoroom->allowed) > 0) {
1924
                                GHashTableIter iter;
1925
                                gpointer key;
1926
                                g_hash_table_iter_init(&iter, videoroom->allowed);
1927
                                while(g_hash_table_iter_next(&iter, &key, NULL)) {
1928
                                        char *token = key;
1929
                                        json_array_append_new(list, json_string(token));
1930
                                }
1931
                        }
1932
                        json_object_set_new(response, "allowed", list);
1933
                }
1934
                /* Done */
1935
                janus_mutex_unlock(&rooms_mutex);
1936
                JANUS_LOG(LOG_VERB, "VideoRoom room allowed list updated\n");
1937
                goto plugin_response;
1938
        } else if(!strcasecmp(request_text, "kick")) {
1939
                JANUS_LOG(LOG_VERB, "Attempt to kick a participant from an existing videoroom room\n");
1940
                JANUS_VALIDATE_JSON_OBJECT(root, kick_parameters,
1941
                        error_code, error_cause, TRUE,
1942
                        JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
1943
                if(error_code != 0)
1944
                        goto plugin_response;
1945
                json_t *room = json_object_get(root, "room");
1946
                json_t *id = json_object_get(root, "id");
1947
                guint64 room_id = json_integer_value(room);
1948
                janus_mutex_lock(&rooms_mutex);
1949
                janus_videoroom *videoroom = g_hash_table_lookup(rooms, &room_id);
1950
                if(videoroom == NULL) {
1951
                        janus_mutex_unlock(&rooms_mutex);
1952
                        JANUS_LOG(LOG_ERR, "No such room (%"SCNu64")\n", room_id);
1953
                        error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM;
1954
                        g_snprintf(error_cause, 512, "No such room (%"SCNu64")", room_id);
1955
                        goto plugin_response;
1956
                }
1957
                janus_mutex_lock(&videoroom->participants_mutex);
1958
                /* A secret may be required for this action */
1959
                JANUS_CHECK_SECRET(videoroom->room_secret, root, "secret", error_code, error_cause,
1960
                        JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT, JANUS_VIDEOROOM_ERROR_UNAUTHORIZED);
1961
                if(error_code != 0) {
1962
                        janus_mutex_unlock(&videoroom->participants_mutex);
1963
                        janus_mutex_unlock(&rooms_mutex);
1964
                        goto plugin_response;
1965
                }
1966
                guint64 user_id = json_integer_value(id);
1967
                janus_videoroom_participant *participant = g_hash_table_lookup(videoroom->participants, &user_id);
1968
                if(participant == NULL) {
1969
                        janus_mutex_unlock(&videoroom->participants_mutex);
1970
                        janus_mutex_unlock(&rooms_mutex);
1971
                        JANUS_LOG(LOG_ERR, "No such user %"SCNu64" in room %"SCNu64"\n", user_id, room_id);
1972
                        error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_FEED;
1973
                        g_snprintf(error_cause, 512, "No such user %"SCNu64" in room %"SCNu64, user_id, room_id);
1974
                        goto plugin_response;
1975
                }
1976 7273e5d8 Lorenzo Miniero
                participant->audio_active = FALSE;
1977
                participant->video_active = FALSE;
1978
                participant->data_active = FALSE;
1979
                /* Prepare an event for this */
1980
                json_t *kicked = json_object();
1981
                json_object_set_new(kicked, "videoroom", json_string("event"));
1982
                json_object_set_new(kicked, "room", json_integer(participant->room->room_id));
1983
                json_object_set_new(kicked, "leaving", json_string("ok"));
1984
                json_object_set_new(kicked, "reason", json_string("kicked"));
1985
                int ret = gateway->push_event(participant->session->handle, &janus_videoroom_plugin, NULL, kicked, NULL);
1986
                JANUS_LOG(LOG_VERB, "  >> %d (%s)\n", ret, janus_get_api_error(ret));
1987
                json_decref(kicked);
1988
                participant->kicked = TRUE;
1989
                participant->session->started = FALSE;
1990
                janus_mutex_unlock(&videoroom->participants_mutex);
1991
                /* This publisher is leaving, tell everybody */
1992
                janus_videoroom_leave_or_unpublish(participant, TRUE, TRUE);
1993 30df7fbf Lorenzo Miniero
                /* Tell the core to tear down the PeerConnection, hangup_media will do the rest
1994
                 *         FIXME: this only kicks the publisher, but not the subscriptions it created */
1995
                if(participant && participant->session)
1996
                        gateway->close_pc(participant->session->handle);
1997
                JANUS_LOG(LOG_VERB, "Kicked user %"SCNu64" from room %"SCNu64"\n", user_id, room_id);
1998
                /* Prepare response */
1999
                response = json_object();
2000
                json_object_set_new(response, "videoroom", json_string("success"));
2001
                /* Done */
2002
                janus_mutex_unlock(&rooms_mutex);
2003
                goto plugin_response;
2004 0eea38ab meetecho
        } else if(!strcasecmp(request_text, "listparticipants")) {
2005 39b52c9a mirkobrankovic
                /* List all participants in a room, specifying whether they're publishers or just attendees */        
2006 887df302 Andreas Girgensohn
                JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
2007
                        error_code, error_cause, TRUE,
2008
                        JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
2009
                if(error_code != 0)
2010 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
2011 887df302 Andreas Girgensohn
                json_t *room = json_object_get(root, "room");
2012 0eea38ab meetecho
                guint64 room_id = json_integer_value(room);
2013
                janus_mutex_lock(&rooms_mutex);
2014 f12f3dbb Andreas Girgensohn
                janus_videoroom *videoroom = NULL;
2015 a046cf75 Andreas Girgensohn
                error_code = janus_videoroom_access_room(root, FALSE, FALSE, &videoroom, error_cause, sizeof(error_cause));
2016 0eea38ab meetecho
                janus_mutex_unlock(&rooms_mutex);
2017 a046cf75 Andreas Girgensohn
                if(error_code != 0)
2018 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
2019 0eea38ab meetecho
                /* Return a list of all participants (whether they're publishing or not) */
2020
                json_t *list = json_array();
2021
                GHashTableIter iter;
2022
                gpointer value;
2023
                janus_mutex_lock(&videoroom->participants_mutex);
2024
                g_hash_table_iter_init(&iter, videoroom->participants);
2025
                while (!videoroom->destroyed && g_hash_table_iter_next(&iter, NULL, &value)) {
2026
                        janus_videoroom_participant *p = value;
2027
                        json_t *pl = json_object();
2028
                        json_object_set_new(pl, "id", json_integer(p->user_id));
2029
                        if(p->display)
2030
                                json_object_set_new(pl, "display", json_string(p->display));
2031 51db8a86 Lorenzo Miniero
                        json_object_set_new(pl, "publisher", (p->sdp && p->session->started) ? json_true() : json_false());
2032 0f8c9674 Sean DuBois
                        if ((p->sdp && p->session->started)) {
2033
                                json_object_set_new(pl, "internal_audio_ssrc", json_integer(p->audio_ssrc));
2034
                                json_object_set_new(pl, "internal_video_ssrc", json_integer(p->video_ssrc));
2035
                        }
2036 0eea38ab meetecho
                        json_array_append_new(list, pl);
2037
                }
2038
                janus_mutex_unlock(&videoroom->participants_mutex);
2039 4078ed95 Pierce Lopez
                response = json_object();
2040 0eea38ab meetecho
                json_object_set_new(response, "videoroom", json_string("participants"));
2041
                json_object_set_new(response, "room", json_integer(room_id));
2042
                json_object_set_new(response, "participants", list);
2043 4078ed95 Pierce Lopez
                goto plugin_response;
2044 319df1b0 amnonbb
        } else if(!strcasecmp(request_text, "listforwarders")) {
2045 39b52c9a mirkobrankovic
                /* List all forwarders in a room */        
2046 319df1b0 amnonbb
                JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
2047
                        error_code, error_cause, TRUE,
2048
                        JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
2049
                if(error_code != 0)
2050 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
2051 319df1b0 amnonbb
                json_t *room = json_object_get(root, "room");
2052
                guint64 room_id = json_integer_value(room);
2053
                janus_mutex_lock(&rooms_mutex);
2054 d236f0e9 Lorenzo Miniero
                janus_videoroom *videoroom = g_hash_table_lookup(rooms, &room_id);
2055 319df1b0 amnonbb
                if(videoroom == NULL) {
2056
                        JANUS_LOG(LOG_ERR, "No such room (%"SCNu64")\n", room_id);
2057
                        error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM;
2058
                        g_snprintf(error_cause, 512, "No such room (%"SCNu64")", room_id);
2059 761bf186 amnonbb
                        janus_mutex_unlock(&rooms_mutex);
2060 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
2061 319df1b0 amnonbb
                }
2062
                if(videoroom->destroyed) {
2063
                        JANUS_LOG(LOG_ERR, "No such room (%"SCNu64")\n", room_id);
2064
                        error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM;
2065
                        g_snprintf(error_cause, 512, "No such room (%"SCNu64")", room_id);
2066 761bf186 amnonbb
                        janus_mutex_unlock(&rooms_mutex);
2067 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
2068 319df1b0 amnonbb
                }
2069
                /* A secret may be required for this action */
2070
                JANUS_CHECK_SECRET(videoroom->room_secret, root, "secret", error_code, error_cause,
2071
                        JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT, JANUS_VIDEOROOM_ERROR_UNAUTHORIZED);
2072
                if(error_code != 0) {
2073
                        janus_mutex_unlock(&rooms_mutex);
2074 db1e9a1f Lorenzo Miniero
                        goto plugin_response;
2075 319df1b0 amnonbb
                }
2076
                /* Return a list of all forwarders */
2077
                json_t *list = json_array();
2078
                GHashTableIter iter;
2079
                gpointer value;
2080
                janus_mutex_lock(&videoroom->participants_mutex);
2081
                g_hash_table_iter_init(&iter, videoroom->participants);
2082
                while (!videoroom->destroyed && g_hash_table_iter_next(&iter, NULL, &value)) {
2083
                        janus_videoroom_participant *p = value;
2084 8413da38 Lorenzo Miniero
                        janus_mutex_lock(&p->rtp_forwarders_mutex);
2085
                        if(g_hash_table_size(p->rtp_forwarders) == 0) {
2086
                                janus_mutex_unlock(&p->rtp_forwarders_mutex);
2087 319df1b0 amnonbb
                                continue;
2088 8413da38 Lorenzo Miniero
                        }
2089 319df1b0 amnonbb
                        json_t *pl = json_object();
2090 761bf186 amnonbb
                        json_object_set_new(pl, "publisher_id", json_integer(p->user_id));
2091 319df1b0 amnonbb
                        if(p->display)
2092
                                json_object_set_new(pl, "display", json_string(p->display));
2093
                        json_t *flist = json_array();
2094
                        GHashTableIter iter_f;
2095 39b52c9a mirkobrankovic
                        gpointer key_f, value_f;                        
2096 319df1b0 amnonbb
                        g_hash_table_iter_init(&iter_f, p->rtp_forwarders);
2097 39b52c9a mirkobrankovic
                        while(g_hash_table_iter_next(&iter_f, &key_f, &value_f)) {                                
2098 319df1b0 amnonbb
                                json_t *fl = json_object();
2099 4a66c743 Lorenzo Miniero
                                guint32 rpk = GPOINTER_TO_UINT(key_f);
2100 0284f49f Lorenzo Miniero
                                janus_videoroom_rtp_forwarder *rpv = value_f;
2101 ae62ca8f Lorenzo Miniero
                                json_object_set_new(fl, "ip", json_string(inet_ntoa(rpv->serv_addr.sin_addr)));
2102 39b52c9a mirkobrankovic
                                if(rpv->is_data) {
2103
                                        json_object_set_new(fl, "data_stream_id", json_integer(rpk));
2104
                                        json_object_set_new(fl, "port", json_integer(ntohs(rpv->serv_addr.sin_port)));
2105
                                } else if(rpv->is_video) {
2106 ae62ca8f Lorenzo Miniero
                                        json_object_set_new(fl, "video_stream_id", json_integer(rpk));
2107
                                        json_object_set_new(fl, "port", json_integer(ntohs(rpv->serv_addr.sin_port)));
2108 0284f49f Lorenzo Miniero
                                        if(rpv->payload_type)
2109
                                                json_object_set_new(fl, "pt", json_integer(rpv->payload_type));
2110
                                        if(rpv->ssrc)
2111
                                                json_object_set_new(fl, "ssrc", json_integer(rpv->ssrc));
2112 ae62ca8f Lorenzo Miniero
                                } else {
2113
                                        json_object_set_new(fl, "audio_stream_id", json_integer(rpk));
2114
                                        json_object_set_new(fl, "port", json_integer(ntohs(rpv->serv_addr.sin_port)));
2115 0284f49f Lorenzo Miniero
                                        if(rpv->payload_type)
2116
                                                json_object_set_new(fl, "pt", json_integer(rpv->payload_type));
2117
                                        if(rpv->ssrc)
2118
                                                json_object_set_new(fl, "ssrc", json_integer(rpv->ssrc));
2119 319df1b0 amnonbb
                                }
2120 ae62ca8f Lorenzo Miniero
                                json_array_append_new(flist, fl);
2121 39b52c9a mirkobrankovic
                        }                
2122 319df1b0 amnonbb
                        janus_mutex_unlock(&p->rtp_forwarders_mutex);
2123 761bf186 amnonbb
                        json_object_set_new(pl, "rtp_forwarder", flist);
2124 319df1b0 amnonbb
                        json_array_append_new(list, pl);
2125
                }
2126
                janus_mutex_unlock(&videoroom->participants_mutex);
2127 761bf186 amnonbb
                janus_mutex_unlock(&rooms_mutex);
2128 319df1b0 amnonbb
                response = json_object();
2129
                json_object_set_new(response, "room", json_integer(room_id));
2130 761bf186 amnonbb
                json_object_set_new(response, "rtp_forwarders", list);
2131 319df1b0 amnonbb
                goto plugin_response;
2132 d24aff33 meetecho
        } else if(!strcasecmp(request_text, "join") || !strcasecmp(request_text, "joinandconfigure")
2133
                        || !strcasecmp(request_text, "configure") || !strcasecmp(request_text, "publish") || !strcasecmp(request_text, "unpublish")
2134 cd28bdc7 meetecho
                        || !strcasecmp(request_text, "start") || !strcasecmp(request_text, "pause") || !strcasecmp(request_text, "switch") || !strcasecmp(request_text, "stop")
2135 d24aff33 meetecho
                        || !strcasecmp(request_text, "add") || !strcasecmp(request_text, "remove") || !strcasecmp(request_text, "leave")) {
2136
                /* These messages are handled asynchronously */
2137 4078ed95 Pierce Lopez
2138 1f067658 Lorenzo Miniero
                janus_videoroom_message *msg = g_malloc0(sizeof(janus_videoroom_message));
2139 4078ed95 Pierce Lopez
                msg->handle = handle;
2140
                msg->transaction = transaction;
2141
                msg->message = root;
2142 dd11fa0a Lorenzo Miniero
                msg->jsep = jsep;
2143 4078ed95 Pierce Lopez
                g_async_queue_push(messages, msg);
2144
2145 dd11fa0a Lorenzo Miniero
                return janus_plugin_result_new(JANUS_PLUGIN_OK_WAIT, NULL, NULL);
2146 d24aff33 meetecho
        } else {
2147
                JANUS_LOG(LOG_VERB, "Unknown request '%s'\n", request_text);
2148
                error_code = JANUS_VIDEOROOM_ERROR_INVALID_REQUEST;
2149 c94052c2 meetecho
                g_snprintf(error_cause, 512, "Unknown request '%s'", request_text);
2150 d24aff33 meetecho
        }
2151 7000b914 Philip Withnall
2152 4078ed95 Pierce Lopez
plugin_response:
2153
                {
2154 db1e9a1f Lorenzo Miniero
                        if(error_code == 0 && !response) {
2155 4078ed95 Pierce Lopez
                                error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR;
2156 011610cd meetecho
                                g_snprintf(error_cause, 512, "Invalid response");
2157 db1e9a1f Lorenzo Miniero
                        }
2158
                        if(error_code != 0) {
2159
                                /* Prepare JSON error event */
2160
                                json_t *event = json_object();
2161
                                json_object_set_new(event, "videoroom", json_string("event"));
2162
                                json_object_set_new(event, "error_code", json_integer(error_code));
2163
                                json_object_set_new(event, "error", json_string(error_cause));
2164
                                response = event;
2165 4078ed95 Pierce Lopez
                        }
2166
                        if(root != NULL)
2167
                                json_decref(root);
2168 dd11fa0a Lorenzo Miniero
                        if(jsep != NULL)
2169
                                json_decref(jsep);
2170 4078ed95 Pierce Lopez
                        g_free(transaction);
2171
2172 db1e9a1f Lorenzo Miniero
                        return janus_plugin_result_new(JANUS_PLUGIN_OK, NULL, response);
2173 d24aff33 meetecho
                }
2174 292d035f meetecho
2175 be35facb meetecho
}
2176
2177 cfd5c0d6 meetecho
void janus_videoroom_setup_media(janus_plugin_session *handle) {
2178 3a26e009 meetecho
        JANUS_LOG(LOG_INFO, "WebRTC media is now available\n");
2179 8878e296 meetecho
        if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
2180 be35facb meetecho
                return;
2181 39b52c9a mirkobrankovic
        janus_videoroom_session *session = (janus_videoroom_session *)handle->plugin_handle;        
2182 be35facb meetecho
        if(!session) {
2183 3a26e009 meetecho
                JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
2184 be35facb meetecho
                return;
2185
        }
2186 c94052c2 meetecho
        if(session->destroyed)
2187 be35facb meetecho
                return;
2188 3a3cc054 meetecho
        g_atomic_int_set(&session->hangingup, 0);
2189 b41765a2 Lorenzo Miniero
2190 be35facb meetecho
        /* Media relaying can start now */
2191
        session->started = TRUE;
2192 b41765a2 Lorenzo Miniero
2193 3f0b4067 meetecho
        if(session->participant) {
2194 b41765a2 Lorenzo Miniero
                /* If this is a publisher, notify all listeners about the fact they can
2195
                 * now subscribe; if this is a listener, instead, ask the publisher a FIR */
2196
                if(session->participant_type == janus_videoroom_p_type_publisher) {
2197
                        janus_videoroom_participant *participant = (janus_videoroom_participant *)session->participant;
2198
                        /* Notify all other participants that there's a new boy in town */
2199
                        json_t *list = json_array();
2200
                        json_t *pl = json_object();
2201
                        json_object_set_new(pl, "id", json_integer(participant->user_id));
2202
                        if(participant->display)
2203
                                json_object_set_new(pl, "display", json_string(participant->display));
2204
                        json_array_append_new(list, pl);
2205
                        json_t *pub = json_object();
2206
                        json_object_set_new(pub, "videoroom", json_string("event"));
2207
                        json_object_set_new(pub, "room", json_integer(participant->room->room_id));
2208
                        json_object_set_new(pub, "publishers", list);
2209
                        GHashTableIter iter;
2210
                        gpointer value;
2211
                        janus_videoroom *videoroom = participant->room;
2212
                        janus_mutex_lock(&videoroom->participants_mutex);
2213
                        g_hash_table_iter_init(&iter, videoroom->participants);
2214
                        while (!videoroom->destroyed && g_hash_table_iter_next(&iter, NULL, &value)) {
2215
                                janus_videoroom_participant *p = value;
2216
                                if(p == participant) {
2217
                                        continue;        /* Skip the new publisher itself */
2218
                                }
2219
                                JANUS_LOG(LOG_VERB, "Notifying participant %"SCNu64" (%s)\n", p->user_id, p->display ? p->display : "??");
2220 be648eca Lorenzo Miniero
                                int ret = gateway->push_event(p->session->handle, &janus_videoroom_plugin, NULL, pub, NULL);
2221 b41765a2 Lorenzo Miniero
                                JANUS_LOG(LOG_VERB, "  >> %d (%s)\n", ret, janus_get_api_error(ret));
2222
                        }
2223 be648eca Lorenzo Miniero
                        json_decref(pub);
2224 b41765a2 Lorenzo Miniero
                        janus_mutex_unlock(&videoroom->participants_mutex);
2225 4d8c7356 Lorenzo Miniero
                        /* Also notify event handlers */
2226 71a04f89 Lorenzo Miniero
                        if(notify_events && gateway->events_is_enabled()) {
2227 4d8c7356 Lorenzo Miniero
                                json_t *info = json_object();
2228
                                json_object_set_new(info, "event", json_string("published"));
2229
                                json_object_set_new(info, "room", json_integer(participant->room->room_id));
2230
                                json_object_set_new(info, "id", json_integer(participant->user_id));
2231 c734a91a Lorenzo Miniero
                                gateway->notify_event(&janus_videoroom_plugin, session->handle, info);
2232 4d8c7356 Lorenzo Miniero
                        }
2233 b41765a2 Lorenzo Miniero
                } else if(session->participant_type == janus_videoroom_p_type_subscriber) {
2234 3f0b4067 meetecho
                        janus_videoroom_listener *l = (janus_videoroom_listener *)session->participant;
2235
                        if(l && l->feed) {
2236
                                janus_videoroom_participant *p = l->feed;
2237
                                if(p && p->session) {
2238
                                        /* Send a FIR */
2239
                                        char buf[20];
2240 d60a9646 meetecho
                                        janus_rtcp_fir((char *)&buf, 20, &p->fir_seq);
2241 3f0b4067 meetecho
                                        JANUS_LOG(LOG_VERB, "New listener available, sending FIR to %"SCNu64" (%s)\n", p->user_id, p->display ? p->display : "??");
2242
                                        gateway->relay_rtcp(p->session->handle, 1, buf, 20);
2243
                                        /* Send a PLI too, just in case... */
2244
                                        janus_rtcp_pli((char *)&buf, 12);
2245
                                        JANUS_LOG(LOG_VERB, "New listener available, sending PLI to %"SCNu64" (%s)\n", p->user_id, p->display ? p->display : "??");
2246
                                        gateway->relay_rtcp(p->session->handle, 1, buf, 12);
2247 4d8c7356 Lorenzo Miniero
                                        /* Also notify event handlers */
2248 71a04f89 Lorenzo Miniero
                                        if(notify_events && gateway->events_is_enabled()) {
2249 4d8c7356 Lorenzo Miniero
                                                json_t *info = json_object();
2250
                                                json_object_set_new(info, "event", json_string("subscribed"));
2251
                                                json_object_set_new(info, "room", json_integer(p->room->room_id));
2252
                                                json_object_set_new(info, "feed", json_integer(p->user_id));
2253 c734a91a Lorenzo Miniero
                                                gateway->notify_event(&janus_videoroom_plugin, session->handle, info);
2254 4d8c7356 Lorenzo Miniero
                                        }
2255 3f0b4067 meetecho
                                }
2256
                        }
2257 be35facb meetecho
                }
2258
        }
2259
}
2260
2261 cfd5c0d6 meetecho
void janus_videoroom_incoming_rtp(janus_plugin_session *handle, int video, char *buf, int len) {
2262 8878e296 meetecho
        if(handle == NULL || handle->stopped || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized) || !gateway)
2263 be35facb meetecho
                return;
2264
        janus_videoroom_session *session = (janus_videoroom_session *)handle->plugin_handle;
2265 c94052c2 meetecho
        if(!session || session->destroyed || !session->participant || session->participant_type != janus_videoroom_p_type_publisher)
2266 be35facb meetecho
                return;
2267
        janus_videoroom_participant *participant = (janus_videoroom_participant *)session->participant;
2268 bd0e3260 mirkobrankovic
        janus_videoroom *videoroom = participant->room;
2269
2270 7055fe99 Lorenzo Miniero
        if(participant->kicked)
2271
                return;
2272
        /* In case this is an audio packet and we're doing talk detection, check the audio level extension */
2273 6398828c mirkobrankovic
        if(!video && videoroom->audiolevel_event && participant->audio_active) {
2274 d4ec9d2b Mirko Brankovic
                int level = 0;
2275 752a621f mirkobrankovic
                if(janus_rtp_header_extension_parse_audio_level(buf, len, participant->audio_level_extmap_id, &level) == 0) {
2276 7055fe99 Lorenzo Miniero
                        participant->audio_dBov_sum += level;
2277
                        participant->audio_active_packets++;
2278 12825213 mirkobrankovic
                        if(participant->audio_active_packets > 0 && participant->audio_active_packets == videoroom->audio_active_packets) {
2279 bd0e3260 mirkobrankovic
                                if((float)participant->audio_dBov_sum/(float)participant->audio_active_packets < videoroom->audio_level_average) {
2280 7055fe99 Lorenzo Miniero
                                        /* Notify all participants */
2281 12825213 mirkobrankovic
                                        janus_mutex_lock(&participant->room->participants_mutex);
2282 752a621f mirkobrankovic
                                        json_t *event = json_object();
2283 248aede7 mirkobrankovic
                                        json_object_set_new(event, "videoroom", json_string("talking"));
2284 f6cc5a2e mirkobrankovic
                                        json_object_set_new(event, "room", json_integer(participant->room->room_id));
2285 248aede7 mirkobrankovic
                                        json_object_set_new(event, "id", json_integer(participant->user_id));
2286 752a621f mirkobrankovic
                                        janus_videoroom_notify_participants(participant, event);
2287
                                        json_decref(event);
2288 12825213 mirkobrankovic
                                        janus_mutex_unlock(&participant->room->participants_mutex);
2289 752a621f mirkobrankovic
                                        /* Also notify event handlers */
2290
                                        if(notify_events && gateway->events_is_enabled()) {
2291
                                                json_t *info = json_object();
2292 248aede7 mirkobrankovic
                                                json_object_set_new(info, "videoroom", json_string("talking"));
2293 752a621f mirkobrankovic
                                                json_object_set_new(info, "room", json_integer(participant->room->room_id));
2294 248aede7 mirkobrankovic
                                                json_object_set_new(info, "id", json_integer(participant->user_id));
2295 752a621f mirkobrankovic
                                                gateway->notify_event(&janus_videoroom_plugin, session->handle, info);
2296
                                        }
2297
                                }
2298
                                participant->audio_active_packets = 0;
2299
                                participant->audio_dBov_sum = 0;
2300
                        }
2301
                }
2302 d4ec9d2b Mirko Brankovic
        }
2303 7055fe99 Lorenzo Miniero
2304 be35facb meetecho
        if((!video && participant->audio_active) || (video && participant->video_active)) {
2305 3f0b4067 meetecho
                /* Update payload type and SSRC */
2306 8413da38 Lorenzo Miniero
                janus_mutex_lock(&participant->rtp_forwarders_mutex);
2307 3f0b4067 meetecho
                rtp_header *rtp = (rtp_header *)buf;
2308 fc23d811 Lorenzo Miniero
                rtp->type = video ? participant->video_pt : participant->audio_pt;
2309 3f0b4067 meetecho
                rtp->ssrc = htonl(video ? participant->video_ssrc : participant->audio_ssrc);
2310 ae62ca8f Lorenzo Miniero
                /* Forward RTP to the appropriate port for the rtp_forwarders associated with this publisher, if there are any */
2311 eedcc449 Computician
                GHashTableIter iter;
2312
                gpointer value;
2313 e62ea677 Computician
                g_hash_table_iter_init(&iter, participant->rtp_forwarders);
2314 eedcc449 Computician
                while(participant->udp_sock > 0 && g_hash_table_iter_next(&iter, NULL, &value)) {
2315 0284f49f Lorenzo Miniero
                        janus_videoroom_rtp_forwarder* rtp_forward = (janus_videoroom_rtp_forwarder*)value;
2316
                        /* Check if payload type and/or SSRC need to be overwritten for this forwarder */
2317
                        int pt = rtp->type;
2318
                        uint32_t ssrc = ntohl(rtp->ssrc);
2319
                        if(rtp_forward->payload_type > 0)
2320
                                rtp->type = rtp_forward->payload_type;
2321
                        if(rtp_forward->ssrc > 0)
2322
                                rtp->ssrc = htonl(rtp_forward->ssrc);
2323 e62ea677 Computician
                        if(video && rtp_forward->is_video) {
2324
                                sendto(participant->udp_sock, buf, len, 0, (struct sockaddr*)&rtp_forward->serv_addr, sizeof(rtp_forward->serv_addr));
2325 eedcc449 Computician
                        }
2326 39b52c9a mirkobrankovic
                        else if(!video && !rtp_forward->is_video && !rtp_forward->is_data) {
2327 e62ea677 Computician
                                sendto(participant->udp_sock, buf, len, 0, (struct sockaddr*)&rtp_forward->serv_addr, sizeof(rtp_forward->serv_addr));
2328 eedcc449 Computician
                        }
2329 0284f49f Lorenzo Miniero
                        /* Restore original values of payload type and SSRC before going on */
2330
                        rtp->type = pt;
2331
                        rtp->ssrc = htonl(ssrc);
2332 eedcc449 Computician
                }
2333 e62ea677 Computician
                janus_mutex_unlock(&participant->rtp_forwarders_mutex);
2334 9d11ac50 meetecho
                /* Save the frame if we're recording */
2335 d223cef1 Lorenzo Miniero
                janus_recorder_save_frame(video ? participant->vrc : participant->arc, buf, len);
2336 3f0b4067 meetecho
                /* Done, relay it */
2337 be35facb meetecho
                janus_videoroom_rtp_relay_packet packet;
2338 cd28bdc7 meetecho
                packet.data = rtp;
2339 be35facb meetecho
                packet.length = len;
2340
                packet.is_video = video;
2341 cd28bdc7 meetecho
                /* Backup the actual timestamp and sequence number set by the publisher, in case switching is involved */
2342
                packet.timestamp = ntohl(packet.data->timestamp);
2343
                packet.seq_number = ntohs(packet.data->seq_number);
2344
                /* Go */
2345 be35facb meetecho
                g_slist_foreach(participant->listeners, janus_videoroom_relay_rtp_packet, &packet);
2346 39b52c9a mirkobrankovic
                
2347 2f142b3d meetecho
                /* Check if we need to send any REMB, FIR or PLI back to this publisher */
2348
                if(video && participant->video_active) {
2349
                        /* Did we send a REMB already, or is it time to send one? */
2350
                        gboolean send_remb = FALSE;
2351
                        if(participant->remb_latest == 0 && participant->remb_startup > 0) {
2352
                                /* Still in the starting phase, send the ramp-up REMB feedback */
2353
                                send_remb = TRUE;
2354
                        } else if(participant->remb_latest > 0 && janus_get_monotonic_time()-participant->remb_latest >= 5*G_USEC_PER_SEC) {
2355
                                /* 5 seconds have passed since the last REMB, send a new one */
2356
                                send_remb = TRUE;
2357 39b52c9a mirkobrankovic
                        }                
2358 2f142b3d meetecho
                        if(send_remb) {
2359
                                /* We send a few incremental REMB messages at startup */
2360
                                uint64_t bitrate = (participant->bitrate ? participant->bitrate : 256*1024);
2361
                                if(participant->remb_startup > 0) {
2362
                                        bitrate = bitrate/participant->remb_startup;
2363
                                        participant->remb_startup--;
2364
                                }
2365 2185b377 Lorenzo Miniero
                                JANUS_LOG(LOG_VERB, "Sending REMB (%s, %"SCNu64")\n", participant->display, bitrate);
2366 d49a834e Lorenzo Miniero
                                char rtcpbuf[24];
2367
                                janus_rtcp_remb((char *)(&rtcpbuf), 24, bitrate);
2368
                                gateway->relay_rtcp(handle, video, rtcpbuf, 24);
2369 fbbf4fbb meetecho
                                if(participant->remb_startup == 0)
2370
                                        participant->remb_latest = janus_get_monotonic_time();
2371
                        }
2372 2f142b3d meetecho
                        /* Generate FIR/PLI too, if needed */
2373
                        if(video && participant->video_active && (participant->room->fir_freq > 0)) {
2374
                                /* FIXME Very ugly hack to generate RTCP every tot seconds/frames */
2375
                                gint64 now = janus_get_monotonic_time();
2376
                                if((now-participant->fir_latest) >= (participant->room->fir_freq*G_USEC_PER_SEC)) {
2377
                                        /* FIXME We send a FIR every tot seconds */
2378
                                        participant->fir_latest = now;
2379
                                        char rtcpbuf[24];
2380
                                        janus_rtcp_fir((char *)&rtcpbuf, 20, &participant->fir_seq);
2381
                                        JANUS_LOG(LOG_VERB, "Sending FIR to %"SCNu64" (%s)\n", participant->user_id, participant->display ? participant->display : "??");
2382
                                        gateway->relay_rtcp(handle, video, rtcpbuf, 20);
2383
                                        /* Send a PLI too, just in case... */
2384
                                        janus_rtcp_pli((char *)&rtcpbuf, 12);
2385
                                        JANUS_LOG(LOG_VERB, "Sending PLI to %"SCNu64" (%s)\n", participant->user_id, participant->display ? participant->display : "??");
2386
                                        gateway->relay_rtcp(handle, video, rtcpbuf, 12);
2387
                                }
2388 be35facb meetecho
                        }
2389
                }
2390
        }
2391
}
2392
2393 cfd5c0d6 meetecho
void janus_videoroom_incoming_rtcp(janus_plugin_session *handle, int video, char *buf, int len) {
2394 3f16cb8a meetecho
        if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
2395
                return;
2396 39b52c9a mirkobrankovic