1 /*! \file   janus_videoroom.c
2  * \author Lorenzo Miniero <lorenzo@meetecho.com>
3  * \copyright GNU General Public License v3
4  * \brief  Janus VideoRoom plugin
5  * \details Check the \ref videoroom for more details.
6  *
7  * \ingroup plugins
8  * \ref plugins
9  *
10  * \page videoroom VideoRoom plugin documentation
11  * This is a plugin implementing a videoconferencing SFU
12  * (Selective Forwarding Unit) for Janus, that is an audio/video router.
13  * This means that the plugin implements a virtual conferencing room peers
14  * can join and leave at any time. This room is based on a Publish/Subscribe
15  * pattern. Each peer can publish his/her own live audio/video feeds: this
16  * feed becomes an available stream in the room the other participants can
17  * attach to. This means that this plugin allows the realization of several
18  * different scenarios, ranging from a simple webinar (one speaker, several
19  * watchers) to a fully meshed video conference (each peer sending and
20  * receiving to and from all the others).
21  *
22  * Considering that this plugin allows for several different WebRTC PeerConnections
23  * to be on at the same time for the same peer (specifically, each peer
24  * potentially has 1 PeerConnection on for publishing and N on for subscriptions
25  * from other peers), each peer may need to attach several times to the same
26  * plugin for every stream: this means that each peer needs to have at least one
27  * handle active for managing its relation with the plugin (joining a room,
28  * leaving a room, muting/unmuting, publishing, receiving events), and needs
29  * to open a new one each time he/she wants to subscribe to a feed from
30  * another publisher participant. The handle used for a subscription,
31  * however, would be logically a "slave" to the master one used for
32  * managing the room: this means that it cannot be used, for instance,
33  * to unmute in the room, as its only purpose would be to provide a
34  * context in which creating the recvonly PeerConnection for the
35  * subscription to an active publisher participant.
36  *
37  * \note Work is going on to implement SSRC multiplexing (Unified Plan),
38  * meaning that in the future you'll be able to use the same
39  * Janus handle/VideoRoom subscriber/PeerConnection to receive multiple
40  * publishers at the same time.
41  *
42  * Rooms to make available are listed in the plugin configuration file.
43  * A pre-filled configuration file is provided in \c conf/janus.plugin.videoroom.jcfg
44  * and includes a demo room for testing. The same plugin is also used
45  * dynamically (that is, with rooms created on the fly via API) in the
46  * Screen Sharing demo as well.
47  *
48  * To add more rooms or modify the existing one, you can use the following
49  * syntax:
50  *
51  * \verbatim
52 room-<unique room ID>: {
53 	description = This is my awesome room
54 	is_private = true|false (private rooms don't appear when you do a 'list' request, default=false)
55 	secret = <optional password needed for manipulating (e.g. destroying) the room>
56 	pin = <optional password needed for joining the room>
57 	require_pvtid = true|false (whether subscriptions are required to provide a valid private_id
58 				 to associate with a publisher, default=false)
59 	signed_tokens = true|false (whether access to the room requires signed tokens; default=false,
60 				 only works if signed tokens are used in the core as well)
61 	publishers = <max number of concurrent senders> (e.g., 6 for a video
62 				 conference or 1 for a webinar, default=3)
63 	bitrate = <max video bitrate for senders> (e.g., 128000)
64 	bitrate_cap = <true|false, whether the above cap should act as a limit to dynamic bitrate changes by publishers, default=false>,
65 	fir_freq = <send a FIR to publishers every fir_freq seconds> (0=disable)
66 	audiocodec = opus|g722|pcmu|pcma|isac32|isac16 (audio codec to force on publishers, default=opus
67 				can be a comma separated list in order of preference, e.g., opus,pcmu)
68 	videocodec = vp8|vp9|h264|av1|h265 (video codec to force on publishers, default=vp8
69 				can be a comma separated list in order of preference, e.g., vp9,vp8,h264)
70 	vp9_profile = VP9-specific profile to prefer (e.g., "2" for "profile-id=2")
71 	h264_profile = H.264-specific profile to prefer (e.g., "42e01f" for "profile-level-id=42e01f")
72 	opus_fec = true|false (whether inband FEC must be negotiated; only works for Opus, default=true)
73 	opus_dtx = true|false (whether DTX must be negotiated; only works for Opus, default=false)
74 	video_svc = true|false (whether SVC support must be enabled; only works for VP9, default=false)
75 	audiolevel_ext = true|false (whether the ssrc-audio-level RTP extension must be
76 		negotiated/used or not for new publishers, default=true)
77 	audiolevel_event = true|false (whether to emit event to other users or not, default=false)
78 	audio_active_packets = 100 (number of packets with audio level, default=100, 2 seconds)
79 	audio_level_average = 25 (average value of audio level, 127=muted, 0='too loud', default=25)
80 	videoorient_ext = true|false (whether the video-orientation RTP extension must be
81 		negotiated/used or not for new publishers, default=true)
82 	playoutdelay_ext = true|false (whether the playout-delay RTP extension must be
83 		negotiated/used or not for new publishers, default=true)
84 	transport_wide_cc_ext = true|false (whether the transport wide CC RTP extension must be
85 		negotiated/used or not for new publishers, default=true)
86 	record = true|false (whether this room should be recorded, default=false)
87 	rec_dir = <folder where recordings should be stored, when enabled>
88 	lock_record = true|false (whether recording can only be started/stopped if the secret
89 				is provided, or using the global enable_recording request, default=false)
90 	notify_joining = true|false (optional, whether to notify all participants when a new
91 				participant joins the room. The Videoroom plugin by design only notifies
92 				new feeds (publishers), and enabling this may result extra notification
93 				traffic. This flag is particularly useful when enabled with require_pvtid
94 				for admin to manage listening only participants. default=false)
95 	require_e2ee = true|false (whether all participants are required to publish and subscribe
96 				using end-to-end media encryption, e.g., via Insertable Streams; default=false)
97 }
98 \endverbatim
99  *
100  * Note that recording will work with all codecs except iSAC.
101  *
102  * \section sfuapi Video Room API
103  *
104  * The Video Room API supports several requests, some of which are
105  * synchronous and some asynchronous. There are some situations, though,
106  * (invalid JSON, invalid request) which will always result in a
107  * synchronous error response even for asynchronous requests.
108  *
109  * \c create , \c destroy , \c edit , \c exists, \c list, \c allowed,
110  * \c kick , \c moderate , \c enable_recording , \c listparticipants
111  * and \c listforwarders are synchronous requests, which means you'll
112  * get a response directly within the context of the transaction.
113  * \c create allows you to create a new video room dynamically, as an
114  * alternative to using the configuration file; \c edit allows you to
115  * dynamically edit some room properties (e.g., the PIN); \c destroy removes a
116  * video room and destroys it, kicking all the users out as part of the
117  * process; \c exists allows you to check whether a specific video room
118  * exists; finally, \c list lists all the available rooms, while \c
119  * listparticipants lists all the active (as in currently publishing
120  * something) participants of a specific room and their details.
121  *
122  * The \c join , \c joinandconfigure , \c configure , \c publish ,
123  * \c unpublish , \c start , \c pause , \c switch and \c leave
124  * requests instead are all asynchronous, which
125  * means you'll get a notification about their success or failure in
126  * an event. \c join allows you to join a specific video room, specifying
127  * whether that specific PeerConnection will be used for publishing or
128  * watching; \c configure can be used to modify some of the participation
129  * settings (e.g., bitrate cap); \c joinandconfigure combines the previous
130  * two requests in a single one (just for publishers); \c publish can be
131  * used to start sending media to broadcast to the other participants,
132  * while \c unpublish does the opposite; \c start allows you to start
133  * receiving media from a publisher you've subscribed to previously by
134  * means of a \c join , while \c pause pauses the delivery of the media;
135  * the \c switch request can be used to change the source of the media
136  * flowing over a specific PeerConnection (e.g., I was watching Alice,
137  * I want to watch Bob now) without having to create a new handle for
138  * that; finally, \c leave allows you to leave a video room for good
139  * (or, in the case of viewers, definitely closes a subscription).
140  *
141  * \c create can be used to create a new video room, and has to be
142  * formatted as follows:
143  *
144 \verbatim
145 {
146 	"request" : "create",
147 	"room" : <unique numeric ID, optional, chosen by plugin if missing>,
148 	"permanent" : <true|false, whether the room should be saved in the config file, default=false>,
149 	"description" : "<pretty name of the room, optional>",
150 	"secret" : "<password required to edit/destroy the room, optional>",
151 	"pin" : "<password required to join the room, optional>",
152 	"is_private" : <true|false, whether the room should appear in a list request>,
153 	"allowed" : [ array of string tokens users can use to join this room, optional],
154 	...
155 }
156 \endverbatim
157  *
158  * For the sake of brevity, not all of the available settings are listed
159  * here. You can refer to the name of the properties in the configuration
160  * file as a reference, as the ones used to programmatically create a new
161  * room are exactly the same.
162  *
163  * A successful creation procedure will result in a \c created response:
164  *
165 \verbatim
166 {
167 	"videoroom" : "created",
168 	"room" : <unique numeric ID>,
169 	"permanent" : <true if saved to config file, false if not>
170 }
171 \endverbatim
172  *
173  * If you requested a permanent room but a \c false value is returned
174  * instead, good chances are that there are permission problems.
175  *
176  * An error instead (and the same applies to all other requests, so this
177  * won't be repeated) would provide both an error code and a more verbose
178  * description of the cause of the issue:
179  *
180 \verbatim
181 {
182 	"videoroom" : "event",
183 	"error_code" : <numeric ID, check Macros below>,
184 	"error" : "<error description as a string>"
185 }
186 \endverbatim
187  *
188  * Notice that, in general, all users can create rooms. If you want to
189  * limit this functionality, you can configure an admin \c admin_key in
190  * the plugin settings. When configured, only "create" requests that
191  * include the correct \c admin_key value in an "admin_key" property
192  * will succeed, and will be rejected otherwise. Notice that you can
193  * optionally extend this functionality to RTP forwarding as well, in
194  * order to only allow trusted clients to use that feature.
195  *
196  * Once a room has been created, you can still edit some (but not all)
197  * of its properties using the \c edit request. This allows you to modify
198  * the room description, secret, pin and whether it's private or not: you
199  * won't be able to modify other more static properties, like the room ID,
200  * the sampling rate, the extensions-related stuff and so on. If you're
201  * interested in changing the ACL, instead, check the \c allowed message.
202  * An \c edit request has to be formatted as follows:
203  *
204 \verbatim
205 {
206 	"request" : "edit",
207 	"room" : <unique numeric ID of the room to edit>,
208 	"secret" : "<room secret, mandatory if configured>",
209 	"new_description" : "<new pretty name of the room, optional>",
210 	"new_secret" : "<new password required to edit/destroy the room, optional>",
211 	"new_pin" : "<new password required to join the room, optional>",
212 	"new_is_private" : <true|false, whether the room should appear in a list request>,
213 	"new_require_pvtid" : <true|false, whether the room should require private_id from subscribers>,
214 	"new_bitrate" : <new bitrate cap to force on all publishers (except those with custom overrides)>,
215 	"new_fir_freq" : <new period for regular PLI keyframe requests to publishers>,
216 	"new_publishers" : <new cap on the number of concurrent active WebRTC publishers>,
217 	"new_lock_record" : <true|false, whether recording state can only be changed when providing the room secret>,
218 	"permanent" : <true|false, whether the room should be also removed from the config file, default=false>
219 }
220 \endverbatim
221  *
222  * A successful edit procedure will result in an \c edited response:
223  *
224 \verbatim
225 {
226 	"videoroom" : "edited",
227 	"room" : <unique numeric ID>
228 }
229 \endverbatim
230  *
231  * On the other hand, \c destroy can be used to destroy an existing video
232  * room, whether created dynamically or statically, and has to be
233  * formatted as follows:
234  *
235 \verbatim
236 {
237 	"request" : "destroy",
238 	"room" : <unique numeric ID of the room to destroy>,
239 	"secret" : "<room secret, mandatory if configured>",
240 	"permanent" : <true|false, whether the room should be also removed from the config file, default=false>
241 }
242 \endverbatim
243  *
244  * A successful destruction procedure will result in a \c destroyed response:
245  *
246 \verbatim
247 {
248 	"videoroom" : "destroyed",
249 	"room" : <unique numeric ID>
250 }
251 \endverbatim
252  *
253  * This will also result in a \c destroyed event being sent to all the
254  * participants in the video room, which will look like this:
255  *
256 \verbatim
257 {
258 	"videoroom" : "destroyed",
259 	"room" : <unique numeric ID of the destroyed room>
260 }
261 \endverbatim
262  *
263  * You can check whether a room exists using the \c exists request,
264  * which has to be formatted as follows:
265  *
266 \verbatim
267 {
268 	"request" : "exists",
269 	"room" : <unique numeric ID of the room to check>
270 }
271 \endverbatim
272  *
273  * A successful request will result in a \c success response:
274  *
275 \verbatim
276 {
277 	"videoroom" : "success",
278 	"room" : <unique numeric ID>,
279 	"exists" : <true|false>
280 }
281 \endverbatim
282  *
283  * You can configure whether to check tokens or add/remove people who can join
284  * a room using the \c allowed request, which has to be formatted as follows:
285  *
286 \verbatim
287 {
288 	"request" : "allowed",
289 	"secret" : "<room secret, mandatory if configured>",
290 	"action" : "enable|disable|add|remove",
291 	"room" : <unique numeric ID of the room to update>,
292 	"allowed" : [
293 		// Array of strings (tokens users might pass in "join", only for add|remove)
294 	]
295 }
296 \endverbatim
297  *
298  * A successful request will result in a \c success response:
299  *
300 \verbatim
301 {
302 	"videoroom" : "success",
303 	"room" : <unique numeric ID>,
304 	"allowed" : [
305 		// Updated, complete, list of allowed tokens (only for enable|add|remove)
306 	]
307 }
308 \endverbatim
309  *
310  * If you're the administrator of a room (that is, you created it and have access
311  * to the secret) you can kick participants using the \c kick request. Notice
312  * that this only kicks the user out of the room, but does not prevent them from
313  * re-joining: to ban them, you need to first remove them from the list of
314  * authorized users (see \c allowed request) and then \c kick them. The \c kick
315  * request has to be formatted as follows:
316  *
317 \verbatim
318 {
319 	"request" : "kick",
320 	"secret" : "<room secret, mandatory if configured>",
321 	"room" : <unique numeric ID of the room>,
322 	"id" : <unique numeric ID of the participant to kick>
323 }
324 \endverbatim
325  *
326  * A successful request will result in a \c success response:
327  *
328 \verbatim
329 {
330 	"videoroom" : "success",
331 }
332 \endverbatim
333  *
334  * As an administrator, you can also forcibly mute/unmute any of the media
335  * streams sent by participants (i.e., audio, video and data streams),
336  * using the \c moderate requests. Notice that if the participant is self
337  * muted on a stream, and you unmute that stream with \c moderate, they
338  * will NOT be unmuted: you'll simply remove any moderation block
339  * that may have been enforced on the participant for that medium
340  * themselves. The \c moderate request has to be formatted as follows:
341  *
342 \verbatim
343 {
344 	"request" : "moderate",
345 	"secret" : "<room secret, mandatory if configured>",
346 	"room" : <unique numeric ID of the room>,
347 	"id" : <unique numeric ID of the participant to moderate>,
348 	"mute_audio" : <true|false, depending on whether or not audio should be muted by the moderator>,
349 	"mute_video" : <true|false, depending on whether or not video should be muted by the moderator>,
350 	"mute_data" : <true|false, depending on whether or not data should be muted by the moderator>,
351 }
352 \endverbatim
353  *
354  * A successful request will result in a \c success response:
355  *
356 \verbatim
357 {
358 	"videoroom" : "success",
359 }
360 \endverbatim
361  *
362  * To get a list of the available rooms you can make use of the \c list request.
363  * \c admin_key is optional. If included and correct, rooms configured/created
364  * as private will be included in the list as well.
365  *
366 \verbatim
367 {
368 	"request" : "list"
369 }
370 \endverbatim
371  *
372  * A successful request will produce a list of rooms in a \c success response:
373  *
374 \verbatim
375 {
376 	"videoroom" : "success",
377 	"list" : [		// Array of room objects
378 		{	// Room #1
379 			"room" : <unique numeric ID>,
380 			"description" : "<Name of the room>",
381 			"pin_required" : <true|false, whether a PIN is required to join this room>,
382 			"is_private" : <true|false, whether this room is 'private' (as in hidden) or not>,
383 			"max_publishers" : <how many publishers can actually publish via WebRTC at the same time>,
384 			"bitrate" : <bitrate cap that should be forced (via REMB) on all publishers by default>,
385 			"bitrate_cap" : <true|false, whether the above cap should act as a limit to dynamic bitrate changes by publishers (optional)>,
386 			"fir_freq" : <how often a keyframe request is sent via PLI/FIR to active publishers>,
387 			"require_pvtid": <true|false, whether subscriptions in this room require a private_id>,
388 			"require_e2ee": <true|false, whether end-to-end encrypted publishers are required>,
389 			"notify_joining": <true|false, whether an event is sent to notify all participants if a new participant joins the room>,
390 			"audiocodec" : "<comma separated list of allowed audio codecs>",
391 			"videocodec" : "<comma separated list of allowed video codecs>",
392 			"opus_fec": <true|false, whether inband FEC must be negotiated (note: only available for Opus) (optional)>,
393 			"opus_dtx": <true|false, whether DTX must be negotiated (note: only available for Opus) (optional)>,
394 			"video_svc": <true|false, whether SVC must be done for video (note: only available for VP9 right now) (optional)>,
395 			"record" : <true|false, whether the room is being recorded>,
396 			"rec_dir" : "<if recording, the path where the .mjr files are being saved>",
397 			"lock_record" : <true|false, whether the room recording state can only be changed providing the secret>,
398 			"num_participants" : <count of the participants (publishers, active or not; not subscribers)>
399 			"audiolevel_ext": <true|false, whether the ssrc-audio-level extension must be negotiated or not for new publishers>,
400 			"audiolevel_event": <true|false, whether to emit event to other users about audiolevel>,
401 			"audio_active_packets": <amount of packets with audio level for checkup (optional, only if audiolevel_event is true)>,
402 			"audio_level_average": <average audio level (optional, only if audiolevel_event is true)>,
403 			"videoorient_ext": <true|false, whether the video-orientation extension must be negotiated or not for new publishers>,
404 			"playoutdelay_ext": <true|false, whether the playout-delay extension must be negotiated or not for new publishers>,
405 			"transport_wide_cc_ext": <true|false, whether the transport wide cc extension must be negotiated or not for new publishers>
406 		},
407 		// Other rooms
408 	]
409 }
410 \endverbatim
411  *
412  * To get a list of the participants in a specific room, instead, you
413  * can make use of the \c listparticipants request, which has to be
414  * formatted as follows:
415  *
416 \verbatim
417 {
418 	"request" : "listparticipants",
419 	"room" : <unique numeric ID of the room>
420 }
421 \endverbatim
422  *
423  * A successful request will produce a list of participants in a
424  * \c participants response:
425  *
426 \verbatim
427 {
428 	"videoroom" : "participants",
429 	"room" : <unique numeric ID of the room>,
430 	"participants" : [		// Array of participant objects
431 		{	// Participant #1
432 			"id" : <unique numeric ID of the participant>,
433 			"display" : "<display name of the participant, if any; optional>",
434 			"publisher" : "<true|false, whether user is an active publisher in the room>",
435 			"talking" : <true|false, whether user is talking or not (only if audio levels are used)>
436 		},
437 		// Other participants
438 	]
439 }
440 \endverbatim
441  *
442  * This covers almost all the synchronous requests. All the asynchronous requests,
443  * plus a couple of additional synchronous requests we'll cover later, refer
444  * to participants instead, namely on how they can publish, subscribe, or
445  * more in general manage the media streams they may be sending or receiving.
446  *
447  * Considering the different nature of publishers and subscribers in the room,
448  * and more importantly how you establish PeerConnections in the respective
449  * cases, their API requests are addressed in separate subsections.
450  *
451  * \subsection vroompub VideoRoom Publishers
452  *
453  * In a VideoRoom, publishers are those participant handles that are able
454  * (although may choose not to, more on this later) publish media in the
455  * room, and as such become feeds that you can subscribe to.
456  *
457  * To specify that a handle will be associated with a publisher, you must use
458  * the \c join request with \c ptype set to \c publisher (note that, as it
459  * will be explained later, you can also use \c joinandconfigure for the
460  * purpose). The exact syntax of the request is the following:
461  *
462 \verbatim
463 {
464 	"request" : "join",
465 	"ptype" : "publisher",
466 	"room" : <unique ID of the room to join>,
467 	"id" : <unique ID to register for the publisher; optional, will be chosen by the plugin if missing>,
468 	"display" : "<display name for the publisher; optional>",
469 	"token" : "<invitation token, in case the room has an ACL; optional>"
470 }
471 \endverbatim
472  *
473  * This will add the user to the list of participants in the room, although
474  * in a non-active role for the time being. Anyway, this participation
475  * allows the user to receive notifications about several aspects of the
476  * room on the related handle (including streams as they become available
477  * and go away). As such, it can be used even just as a way to get
478  * notifications in a room, without the need of ever actually publishing
479  * any stream at all (which explains why the "publisher" role may actually
480  * be a bit confusing in this context).
481  *
482  * A successful \c join will result in a \c joined event, which will contain
483  * a list of the currently active (as in publishing via WebRTC) publishers,
484  * and optionally a list of passive attendees (but only if the room was
485  * configured with \c notify_joining set to \c TRUE ):
486  *
487 \verbatim
488 {
489 	"videoroom" : "joined",
490 	"room" : <room ID>,
491 	"description" : <description of the room, if available>,
492 	"id" : <unique ID of the participant>,
493 	"private_id" : <a different unique ID associated to the participant; meant to be private>,
494 	"publishers" : [
495 		{
496 			"id" : <unique ID of active publisher #1>,
497 			"display" : "<display name of active publisher #1, if any>",
498 			"audio_codec" : "<audio codec used by active publisher #1, if any>",
499 			"video_codec" : "<video codec used by active publisher #1, if any>",
500 			"audio_moderated" : <set to true if audio has been moderated for this participant>,
501 			"video_moderated" : <set to true if video has been moderated for this participant>,
502 			"data_moderated" : <set to true if data has been moderated for this participant>,
503 			"simulcast" : "<true if the publisher uses simulcast (VP8 and H.264 only)>",
504 			"talking" : <true|false, whether the publisher is talking or not (only if audio levels are used)>,
505 		},
506 		// Other active publishers
507 	],
508 	"attendees" : [		// Only present when notify_joining is set to TRUE for rooms
509 		{
510 			"id" : <unique ID of attendee #1>,
511 			"display" : "<display name of attendee #1, if any>"
512 		},
513 		// Other attendees
514 	]
515 }
516 \endverbatim
517  *
518  * Notice that the publishers list will of course be empty if no one is
519  * currently active in the room. For what concerns the \c private_id
520  * property, it is meant to be used by the user when they create subscriptions,
521  * so that the plugin can associate subscriber handles (which are typically
522  * anonymous) to a specific participant; they're usually optional, unless
523  * required by the room configuration.
524  *
525  * As explained, with a simple \c join you're not an active publisher (there
526  * is no WebRTC PeerConnection yet), which means that by default your presence
527  * is not notified to other participants. In fact, the publish/subscribe nature
528  * of the plugin implies that by default only active publishers are notified,
529  * to allow participants to subscribe to existing feeds: notifying all joins/leaves,
530  * even those related to who will just lurk, may be overly verbose and chatty,
531  * especially in large rooms. Anyway, rooms can be configured to notify those
532  * as well, if the \c notify_joining property is set to true: in that case,
533  * regular joins will be notified too, in an event formatted like this:
534  *
535 \verbatim
536 {
537 	"videoroom" : "event",
538 	"room" : <room ID>,
539 	"joining" : {
540 		"id" : <unique ID of the new participant>,
541 		"display" : "<display name of the new participant, if any>"
542 	}
543 }
544 \endverbatim
545  *
546  * If you're interested in publishing media within a room, you can do that
547  * with a \c publish request. This request MUST be accompanied by a JSEP
548  * SDP offer to negotiate a new PeerConnection. The plugin will match it
549  * to the room configuration (e.g., to make sure the codecs you negotiated
550  * are allowed in the room), and will reply with a JSEP SDP answer to
551  * close the circle and complete the setup of the PeerConnection. As soon
552  * as the PeerConnection has been established, the publisher will become
553  * active, and a new active feed other participants can subscribe to.
554  *
555  * The syntax of a \c publish request is the following:
556  *
557 \verbatim
558 {
559 	"request" : "publish",
560 	"audio" : <true|false, depending on whether or not audio should be relayed; true by default>,
561 	"video" : <true|false, depending on whether or not video should be relayed; true by default>,
562 	"data" : <true|false, depending on whether or not data should be relayed; true by default>,
563 	"audiocodec" : "<audio codec to prefer among the negotiated ones; optional>",
564 	"videocodec" : "<video codec to prefer among the negotiated ones; optional>",
565 	"bitrate" : <bitrate cap to return via REMB; optional, overrides the global room value if present>,
566 	"record" : <true|false, whether this publisher should be recorded or not; optional>,
567 	"filename" : "<if recording, the base path/file to use for the recording files; optional>",
568 	"display" : "<new display name to use in the room; optional>",
569 	"audio_level_average" : "<if provided, overrided the room audio_level_average for this user; optional>",
570 	"audio_active_packets" : "<if provided, overrided the room audio_active_packets for this user; optional>"
571 }
572 \endverbatim
573  *
574  * As anticipated, since this is supposed to be accompanied by a JSEP SDP
575  * offer describing the publisher's media streams, the plugin will negotiate
576  * and prepare a matching JSEP SDP answer. If successful, a \c configured
577  * event will be sent back, formatted like this:
578  *
579 \verbatim
580 {
581 	"videoroom" : "event",
582 	"configured" : "ok"
583 }
584 \endverbatim
585  *
586  * This event will be accompanied by the prepared JSEP SDP answer.
587  *
588  * Notice that you can also use \c configure as a request instead of
589  * \c publish to start publishing. The two are functionally equivalent
590  * for publishing, but from a semantic perspective \c publish is the
591  * right message to send when publishing. The \c configure request, as
592  * it will be clearer later, can also be used to update some properties
593  * of the publisher session: in this case the \c publish request can NOT
594  * be used, as it can only be invoked to publish, and will fail if you're
595  * already publishing something.
596  *
597  * As an additional note, notice that you can also join and publish in
598  * a single request, which is useful in case you're not interested in
599  * first join as a passive attendee and only later publish something,
600  * but want to publish something right away. In this case you can use
601  * the \c joinandconfigure request, which as you can imagine combines
602  * the properties of both \c join and \c publish in a single request:
603  * the response to a \c joinandconfigure will be a \c joined event, and
604  * will again be accompanied by a JSEP SDP answer as usual.
605  *
606  * However you decided to publish something, as soon as the PeerConnection
607  * setup succeeds and the publisher becomes active, an event is sent to
608  * all the participants in the room with information on the new feed.
609  * The event must contain an array with a single element, and be formatted like this:
610  *
611 \verbatim
612 {
613 	"videoroom" : "event",
614 	"room" : <room ID>,
615 	"publishers" : [
616 		{
617 			"id" : <unique ID of the new publisher>,
618 			"display" : "<display name of the new publisher, if any>",
619 			"audio_codec" : "<audio codec used the new publisher, if any>",
620 			"video_codec" : "<video codec used by the new publisher, if any>",
621 			"audio_moderated" : <set to true if audio has been moderated for this participant>,
622 			"video_moderated" : <set to true if video has been moderated for this participant>,
623 			"data_moderated" : <set to true if data has been moderated for this participant>,
624 			"simulcast" : "<true if the publisher uses simulcast (VP8 and H.264 only)>",
625 			"talking" : <true|false, whether the publisher is talking or not (only if audio levels are used)>,
626 		}
627 	]
628 }
629 \endverbatim
630  *
631  * To stop publishing and tear down the related PeerConnection, you can
632  * use the \c unpublish request, which requires no arguments as the context
633  * is implicit:
634  *
635 \verbatim
636 {
637 	"request" : "unpublish"
638 }
639 \endverbatim
640  *
641  * This will have the plugin tear down the PeerConnection, and remove the
642  * publisher from the list of active streams. If successful, the response
643  * will look like this:
644  *
645 \verbatim
646 {
647 	"videoroom" : "event",
648 	"unpublished" : "ok"
649 }
650 \endverbatim
651  *
652  * As soon as the PeerConnection is gone, all the other participants will
653  * also be notified about the fact that the stream is no longer available:
654  *
655 \verbatim
656 {
657 	"videoroom" : "event",
658 	"room" : <room ID>,
659 	"unpublished" : <unique ID of the publisher who unpublished>
660 }
661 \endverbatim
662  *
663  * Notice that the same event will also be sent whenever the publisher
664  * feed disappears for reasons other than an explicit \c unpublish , e.g.,
665  * because the handle was closed or the user lost their connection.
666  * Besides, notice that you can publish and unpublish multiple times
667  * within the context of the same publisher handle.
668  *
669  * As anticipated above, you can use a request called \c configure to
670  * tweak some of the properties of an active publisher session. This
671  * request must be formatted as follows:
672  *
673 \verbatim
674 {
675 	"request" : "configure",
676 	"audio" : <true|false, depending on whether or not audio should be relayed; true by default>,
677 	"video" : <true|false, depending on whether or not video should be relayed; true by default>,
678 	"data" : <true|false, depending on whether or not data should be relayed; true by default>,
679 	"bitrate" : <bitrate cap to return via REMB; optional, overrides the global room value if present (unless bitrate_cap is set)>,
680 	"keyframe" : <true|false, whether we should send this publisher a keyframe request>,
681 	"record" : <true|false, whether this publisher should be recorded or not; optional>,
682 	"filename" : "<if recording, the base path/file to use for the recording files; optional>",
683 	"display" : "<new display name to use in the room; optional>",
684 	"audio_active_packets" : "<new audio_active_packets to overwrite in the room one; optional>",
685 	"audio_level_average" : "<new audio_level_average to overwrite the room one; optional>",
686 }
687 \endverbatim
688  *
689  * As you can see, it's basically the same properties as those listed for
690  * \c publish . This is why both requests can be used to start publishing,
691  * as even in that case you configure some of the settings. If successful,
692  * a \c configured event will be sent back as before, formatted like this:
693  *
694 \verbatim
695 {
696 	"videoroom" : "event",
697 	"configured" : "ok"
698 }
699 \endverbatim
700  *
701  * When configuring the room to request the ssrc-audio-level RTP extension,
702  * ad-hoc events might be sent to all publishers if \c audiolevel_event is
703  * set to true. These events will have the following format:
704  *
705 \verbatim
706 {
707 	"videoroom" : <"talking"|"stopped-talking", whether the publisher started or stopped talking>,
708 	"room" : <unique numeric ID of the room the publisher is in>,
709 	"id" : <unique numeric ID of the publisher>,
710 	"audio-level-dBov-avg" : <average value of audio level, 127=muted, 0='too loud'>
711 }
712 \endverbatim
713  *
714  * An interesting feature VideoRoom publisher can take advantage of is
715  * RTP forwarding. In fact, while the main purpose of this plugin is
716  * getting media from WebRTC sources (publishers) and relaying it to
717  * WebRTC destinations (subscribers), there are actually several use
718  * cases and scenarios for making this media available to external,
719  * notnecessarily WebRTC-compliant, components. These components may
720  * benefit from having access to the RTP media sent by a publisher, e.g.,
721  * for media processing, external recording, transcoding to other
722  * technologies via other applications, scalability purposes or
723  * whatever else makes sense in this context. This is made possible by
724  * a request called \c rtp_forward which, as the name suggests, simply
725  * forwards in real-time the media sent by a publisher via RTP (plain
726  * or encrypted) to a remote backend.
727  *
728  * You can add a new RTP forwarder for an existing publisher using the
729  * \c rtp_forward request, which has to be formatted as follows:
730  *
731 \verbatim
732 {
733 	"request" : "rtp_forward",
734 	"room" : <unique numeric ID of the room the publisher is in>,
735 	"publisher_id" : <unique numeric ID of the publisher to relay externally>,
736 	"host" : "<host address to forward the RTP and data packets to>",
737 	"host_family" : "<ipv4|ipv6, if we need to resolve the host address to an IP; by default, whatever we get>",
738 	"audio_port" : <port to forward the audio RTP packets to>,
739 	"audio_ssrc" : <audio SSRC to use to use when streaming; optional>,
740 	"audio_pt" : <audio payload type to use when streaming; optional>,
741 	"audio_rtcp_port" : <port to contact to receive audio RTCP feedback from the recipient; optional, and currently unused for audio>,
742 	"video_port" : <port to forward the video RTP packets to>,
743 	"video_ssrc" : <video SSRC to use to use when streaming; optional>,
744 	"video_pt" : <video payload type to use when streaming; optional>,
745 	"video_rtcp_port" : <port to contact to receive video RTCP feedback from the recipient; optional>,
746 	"simulcast" : <true|false, set to true if the source is simulcast and you want the forwarder to act as a regular viewer (single stream being forwarded) or false otherwise (substreams forwarded separately); optional, default=false>,
747 	"video_port_2" : <if simulcasting and forwarding each substream, port to forward the video RTP packets from the second substream/layer to>,
748 	"video_ssrc_2" : <if simulcasting and forwarding each substream, video SSRC to use to use the second substream/layer; optional>,
749 	"video_pt_2" : <if simulcasting and forwarding each substream, video payload type to use the second substream/layer; optional>,
750 	"video_port_3" : <if simulcasting and forwarding each substream, port to forward the video RTP packets from the third substream/layer to>,
751 	"video_ssrc_3" : <if simulcasting and forwarding each substream, video SSRC to use to use the third substream/layer; optional>,
752 	"video_pt_3" : <if simulcasting and forwarding each substream, video payload type to use the third substream/layer; optional>,
753 	"data_port" : <port to forward the datachannel messages to>,
754 	"srtp_suite" : <length of authentication tag (32 or 80); optional>,
755 	"srtp_crypto" : "<key to use as crypto (base64 encoded key as in SDES); optional>"
756 }
757 \endverbatim
758  *
759  * Notice that, as explained above, in case you configured an \c admin_key
760  * property and extended it to RTP forwarding as well, you'll need to provide
761  * it in the request as well or it will be rejected as unauthorized. By
762  * default no limitation is posed on \c rtp_forward .
763  *
764  * It's worth spending some more words on how to forward simulcast publishers,
765  * as this can lead to some confusion. There are basically two ways to forward
766  * a simulcast publisher:
767  *
768  * -# you treat the forwarder as a regular viewer, which means you still only
769  * forward a single stream to the recipient, that is the highest quality
770  * available at any given time: you can do that by setting
771  * <code>simulcast: true</code> in the \c rtp_forward request;
772  * -# you forward each substream separately instead, to different target
773  * ports: you do that by specifying \c video_port_2 , \c video_port_3 and
774  * optionally the other related \c _2 and \c _3 properties; this is what
775  * you should use when you want to forward to a simulcast-aware Streaming
776  * mountpoint (see the \ref streaming for more details).
777  *
778  * The two approaches are mutually exclusive: you can NOT use them together
779  * in the same RTP forwarder.
780  *
781  * A successful request will result in an \c rtp_forward response, containing
782  * the relevant info associated to the new forwarder(s):
783  *
784 \verbatim
785 {
786 	"videoroom" : "rtp_forward",
787 	"room" : <unique numeric ID, same as request>,
788 	"publisher_id" : <unique numeric ID, same as request>,
789 	"rtp_stream" : {
790 		"host" : "<host this forwarder is streaming to, same as request if not resolved>",
791 		"audio" : <audio RTP port, same as request if configured>,
792 		"audio_rtcp" : <audio RTCP port, same as request if configured>,
793 		"audio_stream_id" : <unique numeric ID assigned to the audio RTP forwarder, if any>,
794 		"video" : <video RTP port, same as request if configured>,
795 		"video_rtcp" : <video RTCP port, same as request if configured>,
796 		"video_stream_id" : <unique numeric ID assigned to the main video RTP forwarder, if any>,
797 		"video_2" : <second video port, same as request if configured>,
798 		"video_stream_id_2" : <unique numeric ID assigned to the second video RTP forwarder, if any>,
799 		"video_3" : <third video port, same as request if configured>,
800 		"video_stream_id_3" : <unique numeric ID assigned to the third video RTP forwarder, if any>,
801 		"data" : <data port, same as request if configured>,
802 		"data_stream_id" : <unique numeric ID assigned to datachannel messages forwarder, if any>
803 	}
804 }
805 \endverbatim
806  *
807  * To stop a previously created RTP forwarder and stop it, you can use
808  * the \c stop_rtp_forward request, which has to be formatted as follows:
809  *
810 \verbatim
811 {
812 	"request" : "stop_rtp_forward",
813 	"room" : <unique numeric ID of the room the publisher is in>,
814 	"publisher_id" : <unique numeric ID of the publisher to update>,
815 	"stream_id" : <unique numeric ID of the RTP forwarder>
816 }
817 \endverbatim
818  *
819  * A successful request will result in a \c stop_rtp_forward response:
820  *
821 \verbatim
822 {
823 	"videoroom" : "stop_rtp_forward",
824 	"room" : <unique numeric ID, same as request>,
825 	"publisher_id" : <unique numeric ID, same as request>,
826 	"stream_id" : <unique numeric ID, same as request>
827 }
828 \endverbatim
829  *
830  * To get a list of all the forwarders in a specific room, instead, you
831  * can make use of the \c listforwarders request, which has to be
832  * formatted as follows:
833  *
834 \verbatim
835 {
836 	"request" : "listforwarders",
837 	"room" : <unique numeric ID of the room>,
838 	"secret" : "<room secret; mandatory if configured>"
839 }
840 \endverbatim
841  *
842  * A successful request will produce a list of RTP forwarders in a
843  * \c forwarders response:
844  *
845 \verbatim
846 {
847 	"videoroom" : "forwarders",
848 	"room" : <unique numeric ID of the room>,
849 	"rtp_forwarders" : [		// Array of publishers with RTP forwarders
850 		{	// Publisher #1
851 			"publisher_id" : <unique numeric ID of publisher #1>,
852 			"rtp_forwarders" : [		// Array of RTP forwarders
853 				{	// RTP forwarder #1
854 					"audio_stream_id" : <unique numeric ID assigned to this audio RTP forwarder, if any>,
855 					"video_stream_id" : <unique numeric ID assigned to this video RTP forwarder, if any>,
856 					"data_stream_id" : <unique numeric ID assigned to this datachannel messages forwarder, if any>
857 					"ip" : "<IP this forwarder is streaming to>",
858 					"port" : <port this forwarder is streaming to>,
859 					"rtcp_port" : <local port this forwarder is using to get RTCP feedback, if any>,
860 					"ssrc" : <SSRC this forwarder is using, if any>,
861 					"pt" : <payload type this forwarder is using, if any>,
862 					"substream" : <video substream this video forwarder is relaying, if any>,
863 					"srtp" : <true|false, whether the RTP stream is encrypted>
864 				},
865 				// Other forwarders for this publisher
866 			],
867 		},
868 		// Other publishers
869 	]
870 }
871 \endverbatim *
872  *
873  * To enable or disable recording on all participants while the conference
874  * is in progress, you can make use of the \c enable_recording request,
875  * which has to be formatted as follows:
876  *
877 \verbatim
878 {
879 	"request" : "enable_recording",
880 	"room" : <unique numeric ID of the room>,
881 	"secret" : "<room secret; mandatory if configured>"
882 	"record" : <true|false, whether participants in this room should be automatically recorded or not>,
883 }
884 \endverbatim *
885  *
886  * Notice that, as we'll see later, participants can normally change their
887  * own recording state via \c configure requests as well: this was done to
888  * allow the maximum flexibility, where rather than globally or automatically
889  * record something, you may want to individually record some streams and
890  * to a specific file. That said, if you'd rather ensure that participants
891  * can't stop their recording if a global recording is enabled, or start
892  * it when the room is not supposed to be recorded instead, then you should
893  * make sure the room is created with the \c lock_record property set to
894  * \c true : this way, the recording state can only be changed if the room
895  * secret is provided, thus ensuring that only an administrator will normally
896  * be able to do that (e.g., using the \c enable_recording just introduced).
897  *
898  * To conclude, you can leave a room you previously joined as publisher
899  * using the \c leave request. This will also implicitly unpublish you
900  * if you were an active publisher in the room. The \c leave request
901  * looks like follows:
902  *
903 \verbatim
904 {
905 	"request" : "leave"
906 }
907 \endverbatim
908  *
909  * If successful, the response will look like this:
910  *
911 \verbatim
912 {
913 	"videoroom" : "event",
914 	"leaving" : "ok"
915 }
916 \endverbatim
917  *
918  * Other participants will receive a "leaving" event to notify them the
919  * circumstance:
920  *
921 \verbatim
922 {
923 	"videoroom" : "event",
924 	"room" : <room ID>,
925 	"leaving : <unique ID of the participant who left>
926 }
927 \endverbatim
928  *
929  * If you were an active publisher, other users will also receive the
930  * corresponding "unpublished" event to notify them the stream is not longer
931  * available, as explained above. If you were simply lurking and not
932  * publishing, the other participants will only receive the "leaving" event.
933  *
934  * \subsection vroomsub VideoRoom Subscribers
935  *
936  * In a VideoRoom, subscribers are NOT participants, but simply handles
937  * that will be used exclusively to receive media from a specific publisher
938  * in the room. Since they're not participants per se, they're basically
939  * streams that can be (and typically are) associated to publisher handles
940  * as the ones we introduced in the previous section, whether active or not.
941  * In fact, the typical use case is publishers being notified about new
942  * participants becoming active in the room, and as a result new subscriber
943  * sessions being created to receive their media streams; as soon as the
944  * publisher goes away, the subscriber handle is removed as well. As such,
945  * these subscriber sessions are dependent on feedback obtained by
946  * publishers, and can't exist on their own, unless you feed them the
947  * right info out of band (which is impossible in rooms configured with
948  * \c require_pvtid).
949  *
950  * To specify that a handle will be associated with a subscriber, you must use
951  * the \c join request with \c ptype set to \c subscriber and specify which
952  * feed to subscribe to. The exact syntax of the request is the following:
953  *
954 \verbatim
955 {
956 	"request" : "join",
957 	"ptype" : "subscriber",
958 	"room" : <unique ID of the room to subscribe in>,
959 	"feed" : <unique ID of the publisher to subscribe to; mandatory>,
960 	"private_id" : <unique ID of the publisher that originated this request; optional, unless mandated by the room configuration>,
961 	"close_pc" : <true|false, depending on whether or not the PeerConnection should be automatically closed when the publisher leaves; true by default>,
962 	"audio" : <true|false, depending on whether or not audio should be relayed; true by default>,
963 	"video" : <true|false, depending on whether or not video should be relayed; true by default>,
964 	"data" : <true|false, depending on whether or not data should be relayed; true by default>,
965 	"offer_audio" : <true|false; whether or not audio should be negotiated; true by default if the publisher has audio>,
966 	"offer_video" : <true|false; whether or not video should be negotiated; true by default if the publisher has video>,
967 	"offer_data" : <true|false; whether or not datachannels should be negotiated; true by default if the publisher has datachannels>,
968 	"substream" : <substream to receive (0-2), in case simulcasting is enabled; optional>,
969 	"temporal" : <temporal layers to receive (0-2), in case simulcasting is enabled; optional>,
970 	"fallback" : <How much time (in us, default 250000) without receiving packets will make us drop to the substream below>,
971 	"spatial_layer" : <spatial layer to receive (0-2), in case VP9-SVC is enabled; optional>,
972 	"temporal_layer" : <temporal layers to receive (0-2), in case VP9-SVC is enabled; optional>
973 }
974 \endverbatim
975  *
976  * As you can see, it's just a matter of specifying the ID of the publisher to
977  * subscribe to and, if needed, your own \c private_id (if mandated by the room).
978  * The \c offer_audio , \c offer_video and \c offer_data are
979  * also particularly interesting, though, as they allow you to only subscribe
980  * to a subset of the mountpoint media. By default, in fact, this \c join
981  * request will result in the plugin preparing a new SDP offer trying to
982  * negotiate all the media streams made available by the publisher; in case
983  * the subscriber knows they don't support one of the mountpoint codecs, though
984  * (e.g., the video in the mountpoint is VP8, but they only support H.264),
985  * or are not interested in getting all the media (e.g., they're ok with
986  * just audio and not video, or don't have enough bandwidth for both),
987  * they can use those properties to shape the SDP offer to their needs.
988  * In case the publisher to subscribe to is simulcasting or doing VP9 SVC,
989  * you can choose in advance which substream you're interested in, e.g.,
990  * to only get the medium quality at best, instead of higher options if
991  * available. As we'll see later, this can be changed dynamically at any
992  * time using a subsequent \c configure request.
993  *
994  * As anticipated, if successful this request will generate a new JSEP SDP
995  * offer, which will accompany an \c attached event:
996  *
997 \verbatim
998 {
999 	"videoroom" : "attached",
1000 	"room" : <room ID>,
1001 	"feed" : <publisher ID>,
1002 	"display" : "<the display name of the publisher, if any>"
1003 }
1004 \endverbatim
1005  *
1006  * At this stage, to complete the setup of the PeerConnection the subscriber is
1007  * supposed to send a JSEP SDP answer back to the plugin. This is done
1008  * by means of a \c start request, which in this case MUST be associated
1009  * with a JSEP SDP answer but otherwise requires no arguments:
1010  *
1011 \verbatim
1012 {
1013 	"request" : "start"
1014 }
1015 \endverbatim
1016  *
1017  * If successful this request returns a \c started event:
1018  *
1019 \verbatim
1020 {
1021 	"videoroom" : "event",
1022 	"started" : "ok"
1023 }
1024 \endverbatim
1025  *
1026  * Once this is done, all that's needed is waiting for the WebRTC PeerConnection
1027  * establishment to succeed. As soon as that happens, the Streaming plugin
1028  * can start relaying media from the mountpoint the viewer subscribed to
1029  * to the viewer themselves.
1030  *
1031  * Notice that, in case you want to force an ICE restart for an existing
1032  * subscription, you'll need to use \c configure instead, and add a
1033  * \c restart attribute set to \c true ; this will result in a new JSEP
1034  * SDP offer originated by the plugin, which you'll have to follow with
1035  * a \c start request (again including the JSEP answer by the viewer).
1036  *
1037  * As a subscriber, you can temporarily pause and resume the whole media delivery
1038  * with a \c pause and, again, \c start request (in this case without any JSEP
1039  * SDP answer attached). Neither expect other arguments, as the context
1040  * is implicitly derived from the handle they're sent on:
1041  *
1042 \verbatim
1043 {
1044 	"request" : "pause"
1045 }
1046 \endverbatim
1047  *
1048 \verbatim
1049 {
1050 	"request" : "start"
1051 }
1052 \endverbatim
1053  *
1054  * Unsurprisingly, they just result in, respectively, \c paused and
1055  * \c started events:
1056  *
1057 \verbatim
1058 {
1059 	"videoroom" : "event",
1060 	"paused" : "ok"
1061 }
1062 \endverbatim
1063  *
1064 \verbatim
1065 {
1066 	"videoroom" : "event",
1067 	"started" : "ok"
1068 }
1069 \endverbatim
1070  *
1071  * For more drill-down manipulations of a subscription, a \c configure
1072  * request can be used instead. This request allows subscribers to dynamically
1073  * change some properties associated to their media subscription, e.g.,
1074  * in terms of what should and should not be sent at a specific time. A
1075  * \c configure request must be formatted as follows:
1076  *
1077 \verbatim
1078 {
1079 	"request" : "configure",
1080 	"audio" : <true|false, depending on whether audio should be relayed or not; optional>,
1081 	"video" : <true|false, depending on whether video should be relayed or not; optional>,
1082 	"data" : <true|false, depending on whether datachannel messages should be relayed or not; optional>,
1083 	"substream" : <substream to receive (0-2), in case simulcasting is enabled; optional>,
1084 	"temporal" : <temporal layers to receive (0-2), in case simulcasting is enabled; optional>,
1085 	"fallback" : <How much time (in us, default 250000) without receiving packets will make us drop to the substream below>,
1086 	"spatial_layer" : <spatial layer to receive (0-2), in case VP9-SVC is enabled; optional>,
1087 	"temporal_layer" : <temporal layers to receive (0-2), in case VP9-SVC is enabled; optional>,
1088 	"audio_level_average" : "<if provided, overrides the room audio_level_average for this user; optional>",
1089 	"audio_active_packets" : "<if provided, overrides the room audio_active_packets for this user; optional>"
1090 }
1091 \endverbatim
1092  *
1093  * As you can see, the \c audio , \c video and \c data properties can be
1094  * used as a media-level pause/resume functionality, whereas \c pause
1095  * and \c start simply pause and resume all streams at the same time.
1096  * The \c substream and \c temporal properties, instead, only make sense
1097  * when the mountpoint is configured with video simulcasting support, and
1098  * as such the viewer is interested in receiving a specific substream
1099  * or temporal layer, rather than any other of the available ones.
1100  * The \c spatial_layer and \c temporal_layer have exactly the same meaning,
1101  * but within the context of VP9-SVC publishers, and will have no effect
1102  * on subscriptions associated to regular publishers.
1103  *
1104  * Another interesting feature that subscribers can take advantage of is the
1105  * so-called publisher "switching". Basically, when subscribed to a specific
1106  * publisher and receiving media from them, you can at any time "switch"
1107  * to a different publisher, and as such start receiving media from that
1108  * other mountpoint instead. Think of it as changing channel on a TV: you
1109  * keep on using the same PeerConnection, the plugin simply changes the
1110  * source of the media transparently. Of course, while powerful and effective
1111  * this request has some limitations. First of all, it switches both audio
1112  * and video, meaning you can't just switch video and keep the audio from
1113  * the previous publisher, for instance; besides, the two publishers
1114  * must have the same media configuration, that is, use the same codecs,
1115  * the same payload types, etc. In fact, since the same PeerConnection is
1116  * used for this feature, switching to a publisher with a different
1117  * configuration might result in media incompatible with the PeerConnection
1118  * setup being relayed to the subscriber, and as such in no audio/video being
1119  * played. That said, a \c switch request must be formatted like this:
1120  *
1121 \verbatim
1122 {
1123 	"request" : "switch",
1124 	"feed" : <unique ID of the new publisher to switch to; mandatory>,
1125 	"audio" : <true|false, depending on whether audio should be relayed or not; optional>,
1126 	"video" : <true|false, depending on whether video should be relayed or not; optional>,
1127 	"data" : <true|false, depending on whether datachannel messages should be relayed or not; optional>
1128 }
1129 \endverbatim
1130  *
1131  * If successful, you'll be unsubscribed from the previous publisher,
1132  * and subscribed to the new publisher instead. The event to confirm
1133  * the switch was successful will look like this:
1134  *
1135 \verbatim
1136 {
1137 	"videoroom" : "event",
1138 	"switched" : "ok",
1139 	"room" : <room ID>,
1140 	"id" : <unique ID of the new publisher>
1141 }
1142 \endverbatim
1143  *
1144  * Finally, to stop the subscription to the mountpoint and tear down the
1145  * related PeerConnection, you can use the \c leave request. Since context
1146  * is implicit, no other argument is required:
1147  *
1148 \verbatim
1149 {
1150 	"request" : "leave"
1151 }
1152 \endverbatim
1153  *
1154  * If successful, the plugin will attempt to tear down the PeerConnection,
1155  * and will send back a \c left event:
1156  *
1157 \verbatim
1158 {
1159 	"videoroom" : "event",
1160 	"left" : "ok",
1161 }
1162 \endverbatim
1163  */
1164 
1165 #include "plugin.h"
1166 
1167 #include <jansson.h>
1168 #include <netdb.h>
1169 
1170 #include "../debug.h"
1171 #include "../apierror.h"
1172 #include "../config.h"
1173 #include "../mutex.h"
1174 #include "../rtp.h"
1175 #include "../rtpsrtp.h"
1176 #include "../rtcp.h"
1177 #include "../record.h"
1178 #include "../sdp-utils.h"
1179 #include "../utils.h"
1180 #include "../ip-utils.h"
1181 #include <sys/types.h>
1182 #include <sys/socket.h>
1183 
1184 
1185 /* Plugin information */
1186 #define JANUS_VIDEOROOM_VERSION			9
1187 #define JANUS_VIDEOROOM_VERSION_STRING	"0.0.9"
1188 #define JANUS_VIDEOROOM_DESCRIPTION		"This is a plugin implementing a videoconferencing SFU (Selective Forwarding Unit) for Janus, that is an audio/video router."
1189 #define JANUS_VIDEOROOM_NAME			"JANUS VideoRoom plugin"
1190 #define JANUS_VIDEOROOM_AUTHOR			"Meetecho s.r.l."
1191 #define JANUS_VIDEOROOM_PACKAGE			"janus.plugin.videoroom"
1192 
1193 /* Plugin methods */
1194 janus_plugin *create(void);
1195 int janus_videoroom_init(janus_callbacks *callback, const char *config_path);
1196 void janus_videoroom_destroy(void);
1197 int janus_videoroom_get_api_compatibility(void);
1198 int janus_videoroom_get_version(void);
1199 const char *janus_videoroom_get_version_string(void);
1200 const char *janus_videoroom_get_description(void);
1201 const char *janus_videoroom_get_name(void);
1202 const char *janus_videoroom_get_author(void);
1203 const char *janus_videoroom_get_package(void);
1204 void janus_videoroom_create_session(janus_plugin_session *handle, int *error);
1205 struct janus_plugin_result *janus_videoroom_handle_message(janus_plugin_session *handle, char *transaction, json_t *message, json_t *jsep);
1206 json_t *janus_videoroom_handle_admin_message(json_t *message);
1207 void janus_videoroom_setup_media(janus_plugin_session *handle);
1208 void janus_videoroom_incoming_rtp(janus_plugin_session *handle, janus_plugin_rtp *packet);
1209 void janus_videoroom_incoming_rtcp(janus_plugin_session *handle, janus_plugin_rtcp *packet);
1210 void janus_videoroom_incoming_data(janus_plugin_session *handle, janus_plugin_data *packet);
1211 void janus_videoroom_data_ready(janus_plugin_session *handle);
1212 void janus_videoroom_slow_link(janus_plugin_session *handle, int uplink, int video);
1213 void janus_videoroom_hangup_media(janus_plugin_session *handle);
1214 void janus_videoroom_destroy_session(janus_plugin_session *handle, int *error);
1215 json_t *janus_videoroom_query_session(janus_plugin_session *handle);
1216 
1217 /* Plugin setup */
1218 static janus_plugin janus_videoroom_plugin =
1219 	JANUS_PLUGIN_INIT (
1220 		.init = janus_videoroom_init,
1221 		.destroy = janus_videoroom_destroy,
1222 
1223 		.get_api_compatibility = janus_videoroom_get_api_compatibility,
1224 		.get_version = janus_videoroom_get_version,
1225 		.get_version_string = janus_videoroom_get_version_string,
1226 		.get_description = janus_videoroom_get_description,
1227 		.get_name = janus_videoroom_get_name,
1228 		.get_author = janus_videoroom_get_author,
1229 		.get_package = janus_videoroom_get_package,
1230 
1231 		.create_session = janus_videoroom_create_session,
1232 		.handle_message = janus_videoroom_handle_message,
1233 		.handle_admin_message = janus_videoroom_handle_admin_message,
1234 		.setup_media = janus_videoroom_setup_media,
1235 		.incoming_rtp = janus_videoroom_incoming_rtp,
1236 		.incoming_rtcp = janus_videoroom_incoming_rtcp,
1237 		.incoming_data = janus_videoroom_incoming_data,
1238 		.data_ready = janus_videoroom_data_ready,
1239 		.slow_link = janus_videoroom_slow_link,
1240 		.hangup_media = janus_videoroom_hangup_media,
1241 		.destroy_session = janus_videoroom_destroy_session,
1242 		.query_session = janus_videoroom_query_session,
1243 	);
1244 
1245 /* Plugin creator */
create(void)1246 janus_plugin *create(void) {
1247 	JANUS_LOG(LOG_VERB, "%s created!\n", JANUS_VIDEOROOM_NAME);
1248 	return &janus_videoroom_plugin;
1249 }
1250 
1251 /* Parameter validation */
1252 static struct janus_json_parameter request_parameters[] = {
1253 	{"request", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
1254 };
1255 static struct janus_json_parameter adminkey_parameters[] = {
1256 	{"admin_key", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
1257 };
1258 static struct janus_json_parameter create_parameters[] = {
1259 	{"description", JSON_STRING, 0},
1260 	{"is_private", JANUS_JSON_BOOL, 0},
1261 	{"allowed", JSON_ARRAY, 0},
1262 	{"secret", JSON_STRING, 0},
1263 	{"pin", JSON_STRING, 0},
1264 	{"require_pvtid", JANUS_JSON_BOOL, 0},
1265 	{"signed_tokens", JANUS_JSON_BOOL, 0},
1266 	{"bitrate", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1267 	{"bitrate_cap", JANUS_JSON_BOOL, 0},
1268 	{"fir_freq", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1269 	{"publishers", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1270 	{"audiocodec", JSON_STRING, 0},
1271 	{"videocodec", JSON_STRING, 0},
1272 	{"vp9_profile", JSON_STRING, 0},
1273 	{"h264_profile", JSON_STRING, 0},
1274 	{"opus_fec", JANUS_JSON_BOOL, 0},
1275 	{"opus_dtx", JANUS_JSON_BOOL, 0},
1276 	{"video_svc", JANUS_JSON_BOOL, 0},
1277 	{"audiolevel_ext", JANUS_JSON_BOOL, 0},
1278 	{"audiolevel_event", JANUS_JSON_BOOL, 0},
1279 	{"audio_active_packets", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1280 	{"audio_level_average", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1281 	{"videoorient_ext", JANUS_JSON_BOOL, 0},
1282 	{"playoutdelay_ext", JANUS_JSON_BOOL, 0},
1283 	{"transport_wide_cc_ext", JANUS_JSON_BOOL, 0},
1284 	{"record", JANUS_JSON_BOOL, 0},
1285 	{"rec_dir", JSON_STRING, 0},
1286 	{"lock_record", JANUS_JSON_BOOL, 0},
1287 	{"permanent", JANUS_JSON_BOOL, 0},
1288 	{"notify_joining", JANUS_JSON_BOOL, 0},
1289 	{"require_e2ee", JANUS_JSON_BOOL, 0}
1290 };
1291 static struct janus_json_parameter edit_parameters[] = {
1292 	{"secret", JSON_STRING, 0},
1293 	{"new_description", JSON_STRING, 0},
1294 	{"new_is_private", JANUS_JSON_BOOL, 0},
1295 	{"new_secret", JSON_STRING, 0},
1296 	{"new_pin", JSON_STRING, 0},
1297 	{"new_require_pvtid", JANUS_JSON_BOOL, 0},
1298 	{"new_bitrate", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1299 	{"new_fir_freq", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1300 	{"new_publishers", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1301 	{"permanent", JANUS_JSON_BOOL, 0}
1302 };
1303 static struct janus_json_parameter room_parameters[] = {
1304 	{"room", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
1305 };
1306 static struct janus_json_parameter roomopt_parameters[] = {
1307 	{"room", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
1308 };
1309 static struct janus_json_parameter roomstr_parameters[] = {
1310 	{"room", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
1311 };
1312 static struct janus_json_parameter roomstropt_parameters[] = {
1313 	{"room", JSON_STRING, 0}
1314 };
1315 static struct janus_json_parameter id_parameters[] = {
1316 	{"id", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
1317 };
1318 static struct janus_json_parameter idopt_parameters[] = {
1319 	{"id", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
1320 };
1321 static struct janus_json_parameter idstr_parameters[] = {
1322 	{"id", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
1323 };
1324 static struct janus_json_parameter idstropt_parameters[] = {
1325 	{"id", JSON_STRING, 0}
1326 };
1327 static struct janus_json_parameter pid_parameters[] = {
1328 	{"publisher_id", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
1329 };
1330 static struct janus_json_parameter pidstr_parameters[] = {
1331 	{"publisher_id", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
1332 };
1333 static struct janus_json_parameter feed_parameters[] = {
1334 	{"feed", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
1335 };
1336 static struct janus_json_parameter feedstr_parameters[] = {
1337 	{"feed", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
1338 };
1339 static struct janus_json_parameter destroy_parameters[] = {
1340 	{"permanent", JANUS_JSON_BOOL, 0}
1341 };
1342 static struct janus_json_parameter allowed_parameters[] = {
1343 	{"secret", JSON_STRING, 0},
1344 	{"action", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
1345 	{"allowed", JSON_ARRAY, 0}
1346 };
1347 static struct janus_json_parameter kick_parameters[] = {
1348 	{"secret", JSON_STRING, 0}
1349 };
1350 static struct janus_json_parameter moderate_parameters[] = {
1351 	{"secret", JSON_STRING, 0},
1352 	{"mute_audio", JANUS_JSON_BOOL, 0},
1353 	{"mute_video", JANUS_JSON_BOOL, 0},
1354 	{"mute_data", JANUS_JSON_BOOL, 0}
1355 };
1356 static struct janus_json_parameter join_parameters[] = {
1357 	{"ptype", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
1358 	{"audio", JANUS_JSON_BOOL, 0},
1359 	{"video", JANUS_JSON_BOOL, 0},
1360 	{"data", JANUS_JSON_BOOL, 0},
1361 	{"bitrate", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1362 	{"record", JANUS_JSON_BOOL, 0},
1363 	{"filename", JSON_STRING, 0},
1364 	{"token", JSON_STRING, 0}
1365 };
1366 static struct janus_json_parameter publish_parameters[] = {
1367 	{"audio", JANUS_JSON_BOOL, 0},
1368 	{"audiocodec", JSON_STRING, 0},
1369 	{"video", JANUS_JSON_BOOL, 0},
1370 	{"videocodec", JSON_STRING, 0},
1371 	{"data", JANUS_JSON_BOOL, 0},
1372 	{"bitrate", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1373 	{"keyframe", JANUS_JSON_BOOL, 0},
1374 	{"record", JANUS_JSON_BOOL, 0},
1375 	{"filename", JSON_STRING, 0},
1376 	{"display", JSON_STRING, 0},
1377 	{"secret", JSON_STRING, 0},
1378 	{"audio_level_averge", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1379 	{"audio_active_packets", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1380 	/* The following are just to force a renegotiation and/or an ICE restart */
1381 	{"update", JANUS_JSON_BOOL, 0},
1382 	{"restart", JANUS_JSON_BOOL, 0}
1383 };
1384 static struct janus_json_parameter record_parameters[] = {
1385 	{"record", JANUS_JSON_BOOL, JANUS_JSON_PARAM_REQUIRED}
1386 };
1387 static struct janus_json_parameter rtp_forward_parameters[] = {
1388 	{"video_port", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1389 	{"video_rtcp_port", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1390 	{"video_ssrc", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1391 	{"video_pt", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1392 	{"video_port_2", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1393 	{"video_ssrc_2", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1394 	{"video_pt_2", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1395 	{"video_port_3", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1396 	{"video_ssrc_3", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1397 	{"video_pt_3", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1398 	{"audio_port", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1399 	{"audio_rtcp_port", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1400 	{"audio_ssrc", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1401 	{"audio_pt", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1402 	{"data_port", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1403 	{"host", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
1404 	{"host_family", JSON_STRING, 0},
1405 	{"simulcast", JANUS_JSON_BOOL, 0},
1406 	{"srtp_suite", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1407 	{"srtp_crypto", JSON_STRING, 0}
1408 };
1409 static struct janus_json_parameter stop_rtp_forward_parameters[] = {
1410 	{"stream_id", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
1411 };
1412 static struct janus_json_parameter publisher_parameters[] = {
1413 	{"display", JSON_STRING, 0}
1414 };
1415 static struct janus_json_parameter configure_parameters[] = {
1416 	{"audio", JANUS_JSON_BOOL, 0},
1417 	{"video", JANUS_JSON_BOOL, 0},
1418 	{"data", JANUS_JSON_BOOL, 0},
1419 	/* For talk detection */
1420 	{"audio_level_averge", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1421 	{"audio_active_packets", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1422 	/* For VP8 (or H.264) simulcast */
1423 	{"substream", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1424 	{"temporal", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1425 	{"fallback", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1426 	/* For VP9 SVC */
1427 	{"spatial_layer", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1428 	{"temporal_layer", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1429 	/* The following is to handle a renegotiation */
1430 	{"update", JANUS_JSON_BOOL, 0}
1431 };
1432 static struct janus_json_parameter subscriber_parameters[] = {
1433 	{"private_id", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1434 	{"close_pc", JANUS_JSON_BOOL, 0},
1435 	{"audio", JANUS_JSON_BOOL, 0},
1436 	{"video", JANUS_JSON_BOOL, 0},
1437 	{"data", JANUS_JSON_BOOL, 0},
1438 	{"offer_audio", JANUS_JSON_BOOL, 0},
1439 	{"offer_video", JANUS_JSON_BOOL, 0},
1440 	{"offer_data", JANUS_JSON_BOOL, 0},
1441 	/* For VP8 (or H.264) simulcast */
1442 	{"substream", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1443 	{"temporal", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1444 	{"fallback", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1445 	/* For VP9 SVC */
1446 	{"spatial_layer", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1447 	{"temporal_layer", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
1448 };
1449 
1450 /* Static configuration instance */
1451 static janus_config *config = NULL;
1452 static const char *config_folder = NULL;
1453 static janus_mutex config_mutex = JANUS_MUTEX_INITIALIZER;
1454 
1455 /* Useful stuff */
1456 static volatile gint initialized = 0, stopping = 0;
1457 static gboolean notify_events = TRUE;
1458 static gboolean string_ids = FALSE;
1459 static janus_callbacks *gateway = NULL;
1460 static GThread *handler_thread;
1461 static void *janus_videoroom_handler(void *data);
1462 static void janus_videoroom_relay_rtp_packet(gpointer data, gpointer user_data);
1463 static void janus_videoroom_relay_data_packet(gpointer data, gpointer user_data);
1464 static void janus_videoroom_hangup_media_internal(gpointer session_data);
1465 
1466 typedef enum janus_videoroom_p_type {
1467 	janus_videoroom_p_type_none = 0,
1468 	janus_videoroom_p_type_subscriber,			/* Generic subscriber */
1469 	janus_videoroom_p_type_publisher,			/* Participant (for receiving events) and optionally publisher */
1470 } janus_videoroom_p_type;
1471 
1472 typedef struct janus_videoroom_message {
1473 	janus_plugin_session *handle;
1474 	char *transaction;
1475 	json_t *message;
1476 	json_t *jsep;
1477 } janus_videoroom_message;
1478 static GAsyncQueue *messages = NULL;
1479 static janus_videoroom_message exit_message;
1480 
1481 
1482 typedef struct janus_videoroom {
1483 	guint64 room_id;			/* Unique room ID (when using integers) */
1484 	gchar *room_id_str;			/* Unique room ID (when using strings) */
1485 	gchar *room_name;			/* Room description */
1486 	gchar *room_secret;			/* Secret needed to manipulate (e.g., destroy) this room */
1487 	gchar *room_pin;			/* Password needed to join this room, if any */
1488 	gboolean is_private;		/* Whether this room is 'private' (as in hidden) or not */
1489 	gboolean require_pvtid;		/* Whether subscriptions in this room require a private_id */
1490 	gboolean signed_tokens;		/* Whether signed tokens are required (assuming they're enabled in the core)  */
1491 	gboolean require_e2ee;		/* Whether end-to-end encrypted publishers are required */
1492 	int max_publishers;			/* Maximum number of concurrent publishers */
1493 	uint32_t bitrate;			/* Global bitrate limit */
1494 	gboolean bitrate_cap;		/* Whether the above limit is insormountable */
1495 	uint16_t fir_freq;			/* Regular FIR frequency (0=disabled) */
1496 	janus_audiocodec acodec[5];	/* Audio codec(s) to force on publishers */
1497 	janus_videocodec vcodec[5];	/* Video codec(s) to force on publishers */
1498 	char *vp9_profile;			/* VP9 codec profile to prefer, if more are negotiated */
1499 	char *h264_profile;			/* H.264 codec profile to prefer, if more are negotiated */
1500 	gboolean do_opusfec;		/* Whether inband FEC must be negotiated (note: only available for Opus) */
1501 	gboolean do_opusdtx;		/* Whether DTX must be negotiated (note: only available for Opus) */
1502 	gboolean do_svc;			/* Whether SVC must be done for video (note: only available for VP9 right now) */
1503 	gboolean audiolevel_ext;	/* Whether the ssrc-audio-level extension must be negotiated or not for new publishers */
1504 	gboolean audiolevel_event;	/* Whether to emit event to other users about audiolevel */
1505 	int audio_active_packets;	/* Amount of packets with audio level for checkup */
1506 	int audio_level_average;	/* Average audio level */
1507 	gboolean videoorient_ext;	/* Whether the video-orientation extension must be negotiated or not for new publishers */
1508 	gboolean playoutdelay_ext;	/* Whether the playout-delay extension must be negotiated or not for new publishers */
1509 	gboolean transport_wide_cc_ext;	/* Whether the transport wide cc extension must be negotiated or not for new publishers */
1510 	gboolean record;			/* Whether the feeds from publishers in this room should be recorded */
1511 	char *rec_dir;				/* Where to save the recordings of this room, if enabled */
1512 	gboolean lock_record;		/* Whether recording state can only be changed providing the room secret */
1513 	GHashTable *participants;	/* Map of potential publishers (we get subscribers from them) */
1514 	GHashTable *private_ids;	/* Map of existing private IDs */
1515 	volatile gint destroyed;	/* Whether this room has been destroyed */
1516 	gboolean check_allowed;		/* Whether to check tokens when participants join (see below) */
1517 	GHashTable *allowed;		/* Map of participants (as tokens) allowed to join */
1518 	gboolean notify_joining;	/* Whether an event is sent to notify all participants if a new participant joins the room */
1519 	janus_mutex mutex;			/* Mutex to lock this room instance */
1520 	janus_refcount ref;			/* Reference counter for this room */
1521 } janus_videoroom;
1522 static GHashTable *rooms;
1523 static janus_mutex rooms_mutex = JANUS_MUTEX_INITIALIZER;
1524 static char *admin_key = NULL;
1525 static gboolean lock_rtpfwd = FALSE;
1526 
1527 typedef struct janus_videoroom_session {
1528 	janus_plugin_session *handle;
1529 	gint64 sdp_sessid;
1530 	gint64 sdp_version;
1531 	janus_videoroom_p_type participant_type;
1532 	gpointer participant;
1533 	volatile gint started;
1534 	volatile gint dataready;
1535 	volatile gint hangingup;
1536 	volatile gint destroyed;
1537 	janus_mutex mutex;
1538 	janus_refcount ref;
1539 } janus_videoroom_session;
1540 static GHashTable *sessions;
1541 static janus_mutex sessions_mutex = JANUS_MUTEX_INITIALIZER;
1542 
1543 /* A host whose ports gets streamed RTP packets of the corresponding type */
1544 typedef struct janus_videoroom_srtp_context janus_videoroom_srtp_context;
1545 typedef struct janus_videoroom_rtp_forwarder {
1546 	void *source;
1547 	gboolean is_video;
1548 	gboolean is_data;
1549 	uint32_t ssrc;
1550 	int payload_type;
1551 	int substream;
1552 	struct sockaddr_in serv_addr;
1553 	struct sockaddr_in6 serv_addr6;
1554 	/* Only needed for RTCP */
1555 	int rtcp_fd;
1556 	uint16_t local_rtcp_port, remote_rtcp_port;
1557 	GSource *rtcp_recv;
1558 	/* Only needed when forwarding simulcasted streams to a single endpoint */
1559 	gboolean simulcast;
1560 	janus_rtp_switching_context context;
1561 	janus_rtp_simulcasting_context sim_context;
1562 	/* Only needed for SRTP forwarders */
1563 	gboolean is_srtp;
1564 	janus_videoroom_srtp_context *srtp_ctx;
1565 	/* Reference */
1566 	volatile gint destroyed;
1567 	janus_refcount ref;
1568 } janus_videoroom_rtp_forwarder;
1569 static void janus_videoroom_rtp_forwarder_destroy(janus_videoroom_rtp_forwarder *forward);
1570 static void janus_videoroom_rtp_forwarder_free(const janus_refcount *f_ref);
1571 /* SRTP encryption may be needed, and potentially shared */
1572 struct janus_videoroom_srtp_context {
1573 	GHashTable *contexts;
1574 	char *id;
1575 	srtp_t ctx;
1576 	srtp_policy_t policy;
1577 	char sbuf[1500];
1578 	int slen;
1579 	/* Keep track of how many forwarders are using this context */
1580 	uint8_t count;
1581 };
1582 static void janus_videoroom_srtp_context_free(gpointer data);
1583 /* RTCP support in RTP forwarders */
1584 typedef struct janus_videoroom_rtcp_receiver {
1585 	GSource parent;
1586 	janus_videoroom_rtp_forwarder *forward;
1587 	GDestroyNotify destroy;
1588 } janus_videoroom_rtcp_receiver;
1589 static void janus_videoroom_rtp_forwarder_rtcp_receive(janus_videoroom_rtp_forwarder *forward);
janus_videoroom_rtp_forwarder_rtcp_prepare(GSource * source,gint * timeout)1590 static gboolean janus_videoroom_rtp_forwarder_rtcp_prepare(GSource *source, gint *timeout) {
1591 	*timeout = -1;
1592 	return FALSE;
1593 }
janus_videoroom_rtp_forwarder_rtcp_dispatch(GSource * source,GSourceFunc callback,gpointer user_data)1594 static gboolean janus_videoroom_rtp_forwarder_rtcp_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
1595 	janus_videoroom_rtcp_receiver *r = (janus_videoroom_rtcp_receiver *)source;
1596 	/* Receive the packet */
1597 	if(r)
1598 		janus_videoroom_rtp_forwarder_rtcp_receive(r->forward);
1599 	return G_SOURCE_CONTINUE;
1600 }
1601 static void janus_videoroom_publisher_dereference_void(void *p);
janus_videoroom_rtp_forwarder_rtcp_finalize(GSource * source)1602 static void janus_videoroom_rtp_forwarder_rtcp_finalize(GSource *source) {
1603 	janus_videoroom_rtcp_receiver *r = (janus_videoroom_rtcp_receiver *)source;
1604 	/* Remove the reference to the forwarder */
1605 	if(r && r->forward) {
1606 		if(r->forward->source) {
1607 			janus_videoroom_publisher_dereference_void(r->forward->source);
1608 			r->forward->source = NULL;
1609 		}
1610 		janus_refcount_decrease(&r->forward->ref);
1611 	}
1612 }
1613 static GSourceFuncs janus_videoroom_rtp_forwarder_rtcp_funcs = {
1614 	janus_videoroom_rtp_forwarder_rtcp_prepare,
1615 	NULL,
1616 	janus_videoroom_rtp_forwarder_rtcp_dispatch,
1617 	janus_videoroom_rtp_forwarder_rtcp_finalize,
1618 	NULL, NULL
1619 };
1620 static GMainContext *rtcpfwd_ctx = NULL;
1621 static GMainLoop *rtcpfwd_loop = NULL;
1622 static GThread *rtcpfwd_thread = NULL;
1623 static void *janus_videoroom_rtp_forwarder_rtcp_thread(void *data);
1624 
1625 typedef struct janus_videoroom_publisher {
1626 	janus_videoroom_session *session;
1627 	janus_videoroom *room;	/* Room */
1628 	guint64 room_id;	/* Unique room ID */
1629 	gchar *room_id_str;	/* Unique room ID (when using strings) */
1630 	guint64 user_id;	/* Unique ID in the room */
1631 	gchar *user_id_str;	/* Unique ID in the room (when using strings) */
1632 	guint32 pvt_id;		/* This is sent to the publisher for mapping purposes, but shouldn't be shared with others */
1633 	gchar *display;		/* Display name (just for fun) */
1634 	gchar *sdp;			/* The SDP this publisher negotiated, if any */
1635 	gboolean audio, video, data;		/* Whether audio, video and/or data is going to be sent by this publisher */
1636 	janus_audiocodec acodec;	/* Audio codec this publisher is using */
1637 	janus_videocodec vcodec;	/* Video codec this publisher is using */
1638 	guint32 audio_pt;		/* Audio payload type (Opus) */
1639 	guint32 video_pt;		/* Video payload type (depends on room configuration) */
1640 	char *vfmtp;			/* Video fmtp that ended up being negotiated, if any */
1641 	guint32 audio_ssrc;		/* Audio SSRC of this publisher */
1642 	guint32 video_ssrc;		/* Video SSRC of this publisher */
1643 	gboolean do_opusfec;	/* Whether this publisher is sending inband Opus FEC */
1644 	gboolean do_opusdtx;	/* Whether this publisher is using Opus DTX (Discontinuous Transmission) */
1645 	uint32_t ssrc[3];		/* Only needed in case VP8 (or H.264) simulcasting is involved */
1646 	char *rid[3];			/* Only needed if simulcasting is rid-based */
1647 	int rid_extmap_id;		/* rid extmap ID */
1648 	guint8 audio_level_extmap_id;		/* Audio level extmap ID */
1649 	guint8 video_orient_extmap_id;		/* Video orientation extmap ID */
1650 	guint8 playout_delay_extmap_id;		/* Playout delay extmap ID */
1651 	gboolean audio_active, audio_muted;
1652 	gboolean video_active, video_muted;
1653 	int audio_dBov_level;		/* Value in dBov of the audio level (last value from extension) */
1654 	int audio_active_packets;	/* Participant's number of audio packets to accumulate */
1655 	int audio_dBov_sum;			/* Participant's accumulated dBov value for audio level*/
1656 	int user_audio_active_packets;	/* Participant's audio_active_packets overwriting global room setting */
1657 	int user_audio_level_average;	/* Participant's audio_level_average overwriting global room setting */
1658 	gboolean talking; /* Whether this participant is currently talking (uses audio levels extension) */
1659 	gboolean data_active, data_muted;
1660 	gboolean firefox;	/* We send Firefox users a different kind of FIR */
1661 	uint32_t bitrate;
1662 	gint64 remb_startup;/* Incremental changes on REMB to reach the target at startup */
1663 	gint64 remb_latest;	/* Time of latest sent REMB (to avoid flooding) */
1664 	gint64 fir_latest;	/* Time of latest sent FIR (to avoid flooding) */
1665 	gint fir_seq;		/* FIR sequence number */
1666 	gboolean recording_active;	/* Whether this publisher has to be recorded or not */
1667 	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 */
1668 	janus_recorder *arc;	/* The Janus recorder instance for this publisher's audio, if enabled */
1669 	janus_recorder *vrc;	/* The Janus recorder instance for this user's video, if enabled */
1670 	janus_recorder *drc;	/* The Janus recorder instance for this publisher's data, if enabled */
1671 	janus_rtp_switching_context rec_ctx;
1672 	janus_rtp_simulcasting_context rec_simctx;
1673 	janus_mutex rec_mutex;	/* Mutex to protect the recorders from race conditions */
1674 	GSList *subscribers;	/* Subscriptions to this publisher (who's watching this publisher)  */
1675 	GSList *subscriptions;	/* Subscriptions this publisher has created (who this publisher is watching) */
1676 	janus_mutex subscribers_mutex;
1677 	janus_mutex own_subscriptions_mutex;
1678 	GHashTable *rtp_forwarders;
1679 	GHashTable *srtp_contexts;
1680 	janus_mutex rtp_forwarders_mutex;
1681 	int udp_sock; /* The udp socket on which to forward rtp packets */
1682 	gboolean kicked;	/* Whether this participant has been kicked */
1683 	gboolean e2ee;		/* If media from this publisher is end-to-end encrypted */
1684 	volatile gint destroyed;
1685 	janus_refcount ref;
1686 } janus_videoroom_publisher;
1687 static guint32 janus_videoroom_rtp_forwarder_add_helper(janus_videoroom_publisher *p,
1688 	const gchar *host, int port, int rtcp_port, int pt, uint32_t ssrc,
1689 	gboolean simulcast, int srtp_suite, const char *srtp_crypto,
1690 	int substream, gboolean is_video, gboolean is_data);
1691 
1692 typedef struct janus_videoroom_subscriber {
1693 	janus_videoroom_session *session;
1694 	janus_videoroom *room;	/* Room */
1695 	guint64 room_id;		/* Unique room ID */
1696 	gchar *room_id_str;		/* Unique room ID (when using strings) */
1697 	janus_videoroom_publisher *feed;	/* Participant this subscriber is subscribed to */
1698 	gboolean close_pc;		/* Whether we should automatically close the PeerConnection when the publisher goes away */
1699 	guint32 pvt_id;			/* Private ID of the participant that is subscribing (if available/provided) */
1700 	janus_sdp *sdp;			/* Offer we sent this listener (may be updated within renegotiations) */
1701 	janus_rtp_switching_context context;	/* Needed in case there are publisher switches on this subscriber */
1702 	janus_rtp_simulcasting_context sim_context;
1703 	janus_vp8_simulcast_context vp8_context;
1704 	gboolean audio, video, data;		/* Whether audio, video and/or data must be sent to this subscriber */
1705 	/* As above, but can't change dynamically (says whether something was negotiated at all in SDP) */
1706 	gboolean audio_offered, video_offered, data_offered;
1707 	gboolean paused;
1708 	gboolean kicked;	/* Whether this subscription belongs to a participant that has been kicked */
1709 	/* The following are only relevant if we're doing VP9 SVC, and are not to be confused with plain
1710 	 * simulcast, which has similar info (substream/templayer) but in a completely different context */
1711 	int spatial_layer, target_spatial_layer;
1712 	gint64 last_spatial_layer[3];
1713 	int temporal_layer, target_temporal_layer;
1714 	gboolean e2ee;		/* If media for this subscriber is end-to-end encrypted */
1715 	volatile gint destroyed;
1716 	janus_refcount ref;
1717 } janus_videoroom_subscriber;
1718 
1719 typedef struct janus_videoroom_rtp_relay_packet {
1720 	janus_rtp_header *data;
1721 	gint length;
1722 	gboolean is_rtp;	/* This may be a data packet and not RTP */
1723 	gboolean is_video;
1724 	uint32_t ssrc[3];
1725 	uint32_t timestamp;
1726 	uint16_t seq_number;
1727 	/* Extensions to add, if any */
1728 	janus_plugin_rtp_extensions extensions;
1729 	/* The following are only relevant if we're doing VP9 SVC*/
1730 	gboolean svc;
1731 	janus_vp9_svc_info svc_info;
1732 	/* The following is only relevant for datachannels */
1733 	gboolean textdata;
1734 } janus_videoroom_rtp_relay_packet;
1735 
1736 /* Start / stop recording */
1737 static void janus_videoroom_recorder_create(janus_videoroom_publisher *participant, gboolean audio, gboolean video, gboolean data);
1738 static void janus_videoroom_recorder_close(janus_videoroom_publisher *participant);
1739 
1740 /* Freeing stuff */
janus_videoroom_subscriber_destroy(janus_videoroom_subscriber * s)1741 static void janus_videoroom_subscriber_destroy(janus_videoroom_subscriber *s) {
1742 	if(s && g_atomic_int_compare_and_exchange(&s->destroyed, 0, 1))
1743 		janus_refcount_decrease(&s->ref);
1744 }
1745 
janus_videoroom_subscriber_free(const janus_refcount * s_ref)1746 static void janus_videoroom_subscriber_free(const janus_refcount *s_ref) {
1747 	janus_videoroom_subscriber *s = janus_refcount_containerof(s_ref, janus_videoroom_subscriber, ref);
1748 	/* This subscriber can be destroyed, free all the resources */
1749 	g_free(s->room_id_str);
1750 	janus_sdp_destroy(s->sdp);
1751 	g_free(s);
1752 }
1753 
janus_videoroom_publisher_dereference(janus_videoroom_publisher * p)1754 static void janus_videoroom_publisher_dereference(janus_videoroom_publisher *p) {
1755 	/* This is used by g_pointer_clear and g_hash_table_new_full so that NULL is only possible if that was inserted into the hash table. */
1756 	janus_refcount_decrease(&p->ref);
1757 }
janus_videoroom_publisher_dereference_void(void * p)1758 static void janus_videoroom_publisher_dereference_void(void *p) {
1759 	janus_videoroom_publisher_dereference((janus_videoroom_publisher *)p);
1760 }
1761 
janus_videoroom_publisher_dereference_by_subscriber(janus_videoroom_publisher * p)1762 static void janus_videoroom_publisher_dereference_by_subscriber(janus_videoroom_publisher *p) {
1763 	/* This is used by g_pointer_clear and g_hash_table_new_full so that NULL is only possible if that was inserted into the hash table. */
1764 	janus_refcount_decrease(&p->session->ref);
1765 	janus_refcount_decrease(&p->ref);
1766 }
1767 
janus_videoroom_publisher_dereference_nodebug(janus_videoroom_publisher * p)1768 static void janus_videoroom_publisher_dereference_nodebug(janus_videoroom_publisher *p) {
1769 	janus_refcount_decrease_nodebug(&p->ref);
1770 }
1771 
janus_videoroom_publisher_destroy(janus_videoroom_publisher * p)1772 static void janus_videoroom_publisher_destroy(janus_videoroom_publisher *p) {
1773 	if(p && g_atomic_int_compare_and_exchange(&p->destroyed, 0, 1)) {
1774 		/* Forwarders with RTCP support may have an extra reference, stop their source */
1775 		janus_mutex_lock(&p->rtp_forwarders_mutex);
1776 		if(g_hash_table_size(p->rtp_forwarders) > 0) {
1777 			GHashTableIter iter_f;
1778 			gpointer key_f, value_f;
1779 			g_hash_table_iter_init(&iter_f, p->rtp_forwarders);
1780 			while(g_hash_table_iter_next(&iter_f, &key_f, &value_f)) {
1781 				janus_videoroom_rtp_forwarder *rpv = value_f;
1782 				if(rpv->rtcp_recv) {
1783 					GSource *source = rpv->rtcp_recv;
1784 					rpv->rtcp_recv = NULL;
1785 					g_source_destroy(source);
1786 					g_source_unref(source);
1787 				}
1788 			}
1789 		}
1790 		janus_mutex_unlock(&p->rtp_forwarders_mutex);
1791 		janus_refcount_decrease(&p->ref);
1792 	}
1793 }
1794 
janus_videoroom_publisher_free(const janus_refcount * p_ref)1795 static void janus_videoroom_publisher_free(const janus_refcount *p_ref) {
1796 	janus_videoroom_publisher *p = janus_refcount_containerof(p_ref, janus_videoroom_publisher, ref);
1797 	g_free(p->room_id_str);
1798 	g_free(p->user_id_str);
1799 	g_free(p->display);
1800 	g_free(p->sdp);
1801 	g_free(p->vfmtp);
1802 	g_free(p->recording_base);
1803 	janus_recorder_destroy(p->arc);
1804 	janus_recorder_destroy(p->vrc);
1805 	janus_recorder_destroy(p->drc);
1806 
1807 	if(p->udp_sock > 0)
1808 		close(p->udp_sock);
1809 	g_hash_table_destroy(p->rtp_forwarders);
1810 	p->rtp_forwarders = NULL;
1811 	g_hash_table_destroy(p->srtp_contexts);
1812 	p->srtp_contexts = NULL;
1813 	g_slist_free(p->subscribers);
1814 
1815 	janus_mutex_destroy(&p->subscribers_mutex);
1816 	janus_mutex_destroy(&p->rtp_forwarders_mutex);
1817 	g_free(p);
1818 }
1819 
janus_videoroom_session_destroy(janus_videoroom_session * session)1820 static void janus_videoroom_session_destroy(janus_videoroom_session *session) {
1821 	if(session && g_atomic_int_compare_and_exchange(&session->destroyed, 0, 1))
1822 		janus_refcount_decrease(&session->ref);
1823 }
1824 
janus_videoroom_session_free(const janus_refcount * session_ref)1825 static void janus_videoroom_session_free(const janus_refcount *session_ref) {
1826 	janus_videoroom_session *session = janus_refcount_containerof(session_ref, janus_videoroom_session, ref);
1827 	/* Remove the reference to the core plugin session */
1828 	janus_refcount_decrease(&session->handle->ref);
1829 	/* This session can be destroyed, free all the resources */
1830 	janus_mutex_destroy(&session->mutex);
1831 	g_free(session);
1832 }
1833 
janus_videoroom_room_dereference(janus_videoroom * room)1834 static void janus_videoroom_room_dereference(janus_videoroom *room) {
1835 	janus_refcount_decrease(&room->ref);
1836 }
1837 
janus_videoroom_room_destroy(janus_videoroom * room)1838 static void janus_videoroom_room_destroy(janus_videoroom *room) {
1839 	if(room && g_atomic_int_compare_and_exchange(&room->destroyed, 0, 1))
1840 		janus_refcount_decrease(&room->ref);
1841 }
1842 
janus_videoroom_room_free(const janus_refcount * room_ref)1843 static void janus_videoroom_room_free(const janus_refcount *room_ref) {
1844 	janus_videoroom *room = janus_refcount_containerof(room_ref, janus_videoroom, ref);
1845 	/* This room can be destroyed, free all the resources */
1846 	g_free(room->room_id_str);
1847 	g_free(room->room_name);
1848 	g_free(room->room_secret);
1849 	g_free(room->room_pin);
1850 	g_free(room->rec_dir);
1851 	g_free(room->vp9_profile);
1852 	g_free(room->h264_profile);
1853 	g_hash_table_destroy(room->participants);
1854 	g_hash_table_destroy(room->private_ids);
1855 	g_hash_table_destroy(room->allowed);
1856 	g_free(room);
1857 }
1858 
janus_videoroom_message_free(janus_videoroom_message * msg)1859 static void janus_videoroom_message_free(janus_videoroom_message *msg) {
1860 	if(!msg || msg == &exit_message)
1861 		return;
1862 
1863 	if(msg->handle && msg->handle->plugin_handle) {
1864 		janus_videoroom_session *session = (janus_videoroom_session *)msg->handle->plugin_handle;
1865 		janus_refcount_decrease(&session->ref);
1866 	}
1867 	msg->handle = NULL;
1868 
1869 	g_free(msg->transaction);
1870 	msg->transaction = NULL;
1871 	if(msg->message)
1872 		json_decref(msg->message);
1873 	msg->message = NULL;
1874 	if(msg->jsep)
1875 		json_decref(msg->jsep);
1876 	msg->jsep = NULL;
1877 
1878 	g_free(msg);
1879 }
1880 
janus_videoroom_codecstr(janus_videoroom * videoroom,char * audio_codecs,char * video_codecs,int str_len,const char * split)1881 static void janus_videoroom_codecstr(janus_videoroom *videoroom, char *audio_codecs, char *video_codecs, int str_len, const char *split) {
1882 	if (audio_codecs) {
1883 		audio_codecs[0] = 0;
1884 		g_snprintf(audio_codecs, str_len, "%s", janus_audiocodec_name(videoroom->acodec[0]));
1885 		if (videoroom->acodec[1] != JANUS_AUDIOCODEC_NONE) {
1886 			janus_strlcat(audio_codecs, split, str_len);
1887 			janus_strlcat(audio_codecs, janus_audiocodec_name(videoroom->acodec[1]), str_len);
1888 		}
1889 		if (videoroom->acodec[2] != JANUS_AUDIOCODEC_NONE) {
1890 			janus_strlcat(audio_codecs, split, str_len);
1891 			janus_strlcat(audio_codecs, janus_audiocodec_name(videoroom->acodec[2]), str_len);
1892 		}
1893 		if (videoroom->acodec[3] != JANUS_AUDIOCODEC_NONE) {
1894 			janus_strlcat(audio_codecs, split, str_len);
1895 			janus_strlcat(audio_codecs, janus_audiocodec_name(videoroom->acodec[3]), str_len);
1896 		}
1897 		if (videoroom->acodec[4] != JANUS_AUDIOCODEC_NONE) {
1898 			janus_strlcat(audio_codecs, split, str_len);
1899 			janus_strlcat(audio_codecs, janus_audiocodec_name(videoroom->acodec[4]), str_len);
1900 		}
1901 	}
1902 	if (video_codecs) {
1903 		video_codecs[0] = 0;
1904 		g_snprintf(video_codecs, str_len, "%s", janus_videocodec_name(videoroom->vcodec[0]));
1905 		if (videoroom->vcodec[1] != JANUS_VIDEOCODEC_NONE) {
1906 			janus_strlcat(video_codecs, split, str_len);
1907 			janus_strlcat(video_codecs, janus_videocodec_name(videoroom->vcodec[1]), str_len);
1908 		}
1909 		if (videoroom->vcodec[2] != JANUS_VIDEOCODEC_NONE) {
1910 			janus_strlcat(video_codecs, split, str_len);
1911 			janus_strlcat(video_codecs, janus_videocodec_name(videoroom->vcodec[2]), str_len);
1912 		}
1913 		if (videoroom->vcodec[3] != JANUS_VIDEOCODEC_NONE) {
1914 			janus_strlcat(video_codecs, split, str_len);
1915 			janus_strlcat(video_codecs, janus_videocodec_name(videoroom->vcodec[3]), str_len);
1916 		}
1917 		if (videoroom->vcodec[4] != JANUS_VIDEOCODEC_NONE) {
1918 			janus_strlcat(video_codecs, split, str_len);
1919 			janus_strlcat(video_codecs, janus_videocodec_name(videoroom->vcodec[4]), str_len);
1920 		}
1921 	}
1922 }
1923 
janus_videoroom_reqpli(janus_videoroom_publisher * publisher,const char * reason)1924 static void janus_videoroom_reqpli(janus_videoroom_publisher *publisher, const char *reason) {
1925 	if(publisher == NULL || g_atomic_int_get(&publisher->destroyed))
1926 		return;
1927 	/* Send a PLI */
1928 	JANUS_LOG(LOG_VERB, "%s sending PLI to %s (%s)\n", reason,
1929 		publisher->user_id_str, publisher->display ? publisher->display : "??");
1930 	gateway->send_pli(publisher->session->handle);
1931 	/* Update the time of when we last sent a keyframe request */
1932 	publisher->fir_latest = janus_get_monotonic_time();
1933 }
1934 
1935 /* Error codes */
1936 #define JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR		499
1937 #define JANUS_VIDEOROOM_ERROR_NO_MESSAGE		421
1938 #define JANUS_VIDEOROOM_ERROR_INVALID_JSON		422
1939 #define JANUS_VIDEOROOM_ERROR_INVALID_REQUEST	423
1940 #define JANUS_VIDEOROOM_ERROR_JOIN_FIRST		424
1941 #define JANUS_VIDEOROOM_ERROR_ALREADY_JOINED	425
1942 #define JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM		426
1943 #define JANUS_VIDEOROOM_ERROR_ROOM_EXISTS		427
1944 #define JANUS_VIDEOROOM_ERROR_NO_SUCH_FEED		428
1945 #define JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT	429
1946 #define JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT	430
1947 #define JANUS_VIDEOROOM_ERROR_INVALID_SDP_TYPE	431
1948 #define JANUS_VIDEOROOM_ERROR_PUBLISHERS_FULL	432
1949 #define JANUS_VIDEOROOM_ERROR_UNAUTHORIZED		433
1950 #define JANUS_VIDEOROOM_ERROR_ALREADY_PUBLISHED	434
1951 #define JANUS_VIDEOROOM_ERROR_NOT_PUBLISHED		435
1952 #define JANUS_VIDEOROOM_ERROR_ID_EXISTS			436
1953 #define JANUS_VIDEOROOM_ERROR_INVALID_SDP		437
1954 
1955 
janus_videoroom_rtp_forwarder_add_helper(janus_videoroom_publisher * p,const gchar * host,int port,int rtcp_port,int pt,uint32_t ssrc,gboolean simulcast,int srtp_suite,const char * srtp_crypto,int substream,gboolean is_video,gboolean is_data)1956 static guint32 janus_videoroom_rtp_forwarder_add_helper(janus_videoroom_publisher *p,
1957 		const gchar *host, int port, int rtcp_port, int pt, uint32_t ssrc,
1958 		gboolean simulcast, int srtp_suite, const char *srtp_crypto,
1959 		int substream, gboolean is_video, gboolean is_data) {
1960 	if(!p || !host) {
1961 		return 0;
1962 	}
1963 	janus_refcount_increase(&p->ref);
1964 	janus_mutex_lock(&p->rtp_forwarders_mutex);
1965 	/* Do we need to bind to a port for RTCP? */
1966 	int fd = -1;
1967 	uint16_t local_rtcp_port = 0;
1968 	if(!is_data && rtcp_port > 0) {
1969 		fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
1970 		if(fd < 0) {
1971 			janus_mutex_unlock(&p->rtp_forwarders_mutex);
1972 			janus_refcount_decrease(&p->ref);
1973 			JANUS_LOG(LOG_ERR, "Error creating RTCP socket for new RTP forwarder... %d (%s)\n",
1974 				errno, g_strerror(errno));
1975 			return 0;
1976 		}
1977 		int v6only = 0;
1978 		if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)) != 0) {
1979 			janus_mutex_unlock(&p->rtp_forwarders_mutex);
1980 			janus_refcount_decrease(&p->ref);
1981 			JANUS_LOG(LOG_ERR, "Error creating RTCP socket for new RTP forwarder... %d (%s)\n",
1982 				errno, g_strerror(errno));
1983 			close(fd);
1984 			return 0;
1985 		}
1986 		struct sockaddr_in6 address = { 0 };
1987 		socklen_t len = sizeof(address);
1988 		memset(&address, 0, sizeof(address));
1989 		address.sin6_family = AF_INET6;
1990 		address.sin6_port = htons(0);	/* The RTCP port we received is the remote one */
1991 		address.sin6_addr = in6addr_any;
1992 		if(bind(fd, (struct sockaddr *)&address, len) < 0 ||
1993 				getsockname(fd, (struct sockaddr *)&address, &len) < 0) {
1994 			janus_mutex_unlock(&p->rtp_forwarders_mutex);
1995 			janus_refcount_decrease(&p->ref);
1996 			JANUS_LOG(LOG_ERR, "Error binding RTCP socket for new RTP forwarder... %d (%s)\n",
1997 				errno, g_strerror(errno));
1998 			close(fd);
1999 			return 0;
2000 		}
2001 		local_rtcp_port = ntohs(address.sin6_port);
2002 		JANUS_LOG(LOG_VERB, "Bound local %s RTCP port: %"SCNu16"\n",
2003 			is_video ? "video" : "audio", local_rtcp_port);
2004 	}
2005 	janus_videoroom_rtp_forwarder *forward = g_malloc0(sizeof(janus_videoroom_rtp_forwarder));
2006 	forward->source = p;
2007 	forward->rtcp_fd = fd;
2008 	forward->local_rtcp_port = local_rtcp_port;
2009 	forward->remote_rtcp_port = rtcp_port;
2010 	/* First of all, let's check if we need to setup an SRTP forwarder */
2011 	if(!is_data && srtp_suite > 0 && srtp_crypto != NULL) {
2012 		/* First of all, let's check if there's already an RTP forwarder with
2013 		 * the same SRTP context: make sure SSRC and pt are the same too */
2014 		char media[10] = {0};
2015 		if(!is_video) {
2016 			g_sprintf(media, "audio");
2017 		} else if(is_video) {
2018 			g_sprintf(media, "video%d", substream);
2019 		}
2020 		char srtp_id[256] = {0};
2021 		g_snprintf(srtp_id, 255, "%s-%s-%"SCNu32"-%d", srtp_crypto, media, ssrc, pt);
2022 		JANUS_LOG(LOG_VERB, "SRTP context ID: %s\n", srtp_id);
2023 		janus_videoroom_srtp_context *srtp_ctx = g_hash_table_lookup(p->srtp_contexts, srtp_id);
2024 		if(srtp_ctx != NULL) {
2025 			JANUS_LOG(LOG_VERB, "  -- Reusing existing SRTP context\n");
2026 			srtp_ctx->count++;
2027 			forward->srtp_ctx = srtp_ctx;
2028 		} else {
2029 			/* Nope, base64 decode the crypto string and set it as a new SRTP context */
2030 			JANUS_LOG(LOG_VERB, "  -- Creating new SRTP context\n");
2031 			srtp_ctx = g_malloc0(sizeof(janus_videoroom_srtp_context));
2032 			gsize len = 0;
2033 			guchar *decoded = g_base64_decode(srtp_crypto, &len);
2034 			if(len < SRTP_MASTER_LENGTH) {
2035 				janus_mutex_unlock(&p->rtp_forwarders_mutex);
2036 				janus_refcount_decrease(&p->ref);
2037 				JANUS_LOG(LOG_ERR, "Invalid SRTP crypto (%s)\n", srtp_crypto);
2038 				g_free(decoded);
2039 				g_free(srtp_ctx);
2040 				if(forward->rtcp_fd > -1)
2041 					close(forward->rtcp_fd);
2042 				g_free(forward);
2043 				return 0;
2044 			}
2045 			/* Set SRTP policy */
2046 			srtp_policy_t *policy = &srtp_ctx->policy;
2047 			srtp_crypto_policy_set_rtp_default(&(policy->rtp));
2048 			if(srtp_suite == 32) {
2049 				srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&(policy->rtp));
2050 			} else if(srtp_suite == 80) {
2051 				srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(policy->rtp));
2052 			}
2053 			policy->ssrc.type = ssrc_any_outbound;
2054 			policy->key = decoded;
2055 			policy->next = NULL;
2056 			/* Create SRTP context */
2057 			srtp_err_status_t res = srtp_create(&srtp_ctx->ctx, policy);
2058 			if(res != srtp_err_status_ok) {
2059 				/* Something went wrong... */
2060 				janus_mutex_unlock(&p->rtp_forwarders_mutex);
2061 				janus_refcount_decrease(&p->ref);
2062 				JANUS_LOG(LOG_ERR, "Error creating forwarder SRTP session: %d (%s)\n", res, janus_srtp_error_str(res));
2063 				g_free(decoded);
2064 				policy->key = NULL;
2065 				g_free(srtp_ctx);
2066 				if(forward->rtcp_fd > -1)
2067 					close(forward->rtcp_fd);
2068 				g_free(forward);
2069 				return 0;
2070 			}
2071 			srtp_ctx->contexts = p->srtp_contexts;
2072 			srtp_ctx->id = g_strdup(srtp_id);
2073 			srtp_ctx->count = 1;
2074 			g_hash_table_insert(p->srtp_contexts, srtp_ctx->id, srtp_ctx);
2075 			forward->srtp_ctx = srtp_ctx;
2076 		}
2077 		forward->is_srtp = TRUE;
2078 	}
2079 	forward->is_video = is_video;
2080 	forward->payload_type = pt;
2081 	forward->ssrc = ssrc;
2082 	forward->substream = substream;
2083 	forward->is_data = is_data;
2084 	/* Check if the host address is IPv4 or IPv6 */
2085 	if(strstr(host, ":") != NULL) {
2086 		forward->serv_addr6.sin6_family = AF_INET6;
2087 		inet_pton(AF_INET6, host, &(forward->serv_addr6.sin6_addr));
2088 		forward->serv_addr6.sin6_port = htons(port);
2089 	} else {
2090 		forward->serv_addr.sin_family = AF_INET;
2091 		inet_pton(AF_INET, host, &(forward->serv_addr.sin_addr));
2092 		forward->serv_addr.sin_port = htons(port);
2093 	}
2094 	if(is_video && simulcast) {
2095 		forward->simulcast = TRUE;
2096 		janus_rtp_switching_context_reset(&forward->context);
2097 		janus_rtp_simulcasting_context_reset(&forward->sim_context);
2098 		forward->sim_context.rid_ext_id = p->rid_extmap_id;
2099 		forward->sim_context.substream_target = 2;
2100 		forward->sim_context.templayer_target = 2;
2101 	}
2102 	janus_refcount_init(&forward->ref, janus_videoroom_rtp_forwarder_free);
2103 	guint32 stream_id = janus_random_uint32();
2104 	while(g_hash_table_lookup(p->rtp_forwarders, GUINT_TO_POINTER(stream_id)) != NULL) {
2105 		stream_id = janus_random_uint32();
2106 	}
2107 	g_hash_table_insert(p->rtp_forwarders, GUINT_TO_POINTER(stream_id), forward);
2108 	if(fd > -1) {
2109 		/* We need RTCP: track this file descriptor, and ref the forwarder */
2110 		janus_refcount_increase(&p->ref);
2111 		janus_refcount_increase(&forward->ref);
2112 		forward->rtcp_recv = g_source_new(&janus_videoroom_rtp_forwarder_rtcp_funcs, sizeof(janus_videoroom_rtcp_receiver));
2113 		janus_videoroom_rtcp_receiver *rr = (janus_videoroom_rtcp_receiver *)forward->rtcp_recv;
2114 		rr->forward = forward;
2115 		g_source_set_priority(forward->rtcp_recv, G_PRIORITY_DEFAULT);
2116 		g_source_add_unix_fd(forward->rtcp_recv, fd, G_IO_IN | G_IO_ERR);
2117 		g_source_attach((GSource *)forward->rtcp_recv, rtcpfwd_ctx);
2118 		/* Send a couple of empty RTP packets to the remote port to do latching */
2119 		struct sockaddr *address = NULL;
2120 		struct sockaddr_in addr4 = { 0 };
2121 		struct sockaddr_in6 addr6 = { 0 };
2122 		socklen_t addrlen = 0;
2123 		if(forward->serv_addr.sin_family == AF_INET) {
2124 			addr4.sin_family = AF_INET;
2125 			addr4.sin_addr.s_addr = forward->serv_addr.sin_addr.s_addr;
2126 			addr4.sin_port = htons(forward->remote_rtcp_port);
2127 			address = (struct sockaddr *)&addr4;
2128 			addrlen = sizeof(addr4);
2129 		} else {
2130 			addr6.sin6_family = AF_INET6;
2131 			memcpy(&addr6.sin6_addr, &forward->serv_addr6.sin6_addr, sizeof(struct in6_addr));
2132 			addr6.sin6_port = htons(forward->remote_rtcp_port);
2133 			address = (struct sockaddr *)&addr6;
2134 			addrlen = sizeof(addr6);
2135 		}
2136 		janus_rtp_header rtp;
2137 		memset(&rtp, 0, sizeof(rtp));
2138 		rtp.version = 2;
2139 		(void)sendto(fd, &rtp, 12, 0, address, addrlen);
2140 		(void)sendto(fd, &rtp, 12, 0, address, addrlen);
2141 	}
2142 	janus_mutex_unlock(&p->rtp_forwarders_mutex);
2143 	janus_refcount_decrease(&p->ref);
2144 	JANUS_LOG(LOG_VERB, "Added %s/%d rtp_forward to participant %s host: %s:%d stream_id: %"SCNu32"\n",
2145 		is_data ? "data" : (is_video ? "video" : "audio"), substream, p->user_id_str, host, port, stream_id);
2146 	return stream_id;
2147 }
2148 
janus_videoroom_rtp_forwarder_destroy(janus_videoroom_rtp_forwarder * forward)2149 static void janus_videoroom_rtp_forwarder_destroy(janus_videoroom_rtp_forwarder *forward) {
2150 	if(forward && g_atomic_int_compare_and_exchange(&forward->destroyed, 0, 1)) {
2151 		if(forward->rtcp_fd > -1 && forward->rtcp_recv != NULL) {
2152 			g_source_destroy(forward->rtcp_recv);
2153 			g_source_unref(forward->rtcp_recv);
2154 		}
2155 		janus_refcount_decrease(&forward->ref);
2156 	}
2157 }
janus_videoroom_rtp_forwarder_free(const janus_refcount * f_ref)2158 static void janus_videoroom_rtp_forwarder_free(const janus_refcount *f_ref) {
2159 	janus_videoroom_rtp_forwarder *forward = janus_refcount_containerof(f_ref, janus_videoroom_rtp_forwarder, ref);
2160 	if(forward->rtcp_fd > -1)
2161 		close(forward->rtcp_fd);
2162 	if(forward->is_srtp && forward->srtp_ctx) {
2163 		forward->srtp_ctx->count--;
2164 		if(forward->srtp_ctx->count == 0 && forward->srtp_ctx->contexts != NULL)
2165 			g_hash_table_remove(forward->srtp_ctx->contexts, forward->srtp_ctx->id);
2166 	}
2167 	g_free(forward);
2168 	forward = NULL;
2169 }
2170 
janus_videoroom_srtp_context_free(gpointer data)2171 static void janus_videoroom_srtp_context_free(gpointer data) {
2172 	if(data) {
2173 		janus_videoroom_srtp_context *srtp_ctx = (janus_videoroom_srtp_context *)data;
2174 		if(srtp_ctx) {
2175 			g_free(srtp_ctx->id);
2176 			srtp_dealloc(srtp_ctx->ctx);
2177 			g_free(srtp_ctx->policy.key);
2178 			g_free(srtp_ctx);
2179 			srtp_ctx = NULL;
2180 		}
2181 	}
2182 }
2183 
2184 
2185 /* Plugin implementation */
janus_videoroom_init(janus_callbacks * callback,const char * config_path)2186 int janus_videoroom_init(janus_callbacks *callback, const char *config_path) {
2187 	if(g_atomic_int_get(&stopping)) {
2188 		/* Still stopping from before */
2189 		return -1;
2190 	}
2191 	if(callback == NULL || config_path == NULL) {
2192 		/* Invalid arguments */
2193 		return -1;
2194 	}
2195 
2196 	/* Read configuration */
2197 	char filename[255];
2198 	g_snprintf(filename, 255, "%s/%s.jcfg", config_path, JANUS_VIDEOROOM_PACKAGE);
2199 	JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
2200 	config = janus_config_parse(filename);
2201 	if(config == NULL) {
2202 		JANUS_LOG(LOG_WARN, "Couldn't find .jcfg configuration file (%s), trying .cfg\n", JANUS_VIDEOROOM_PACKAGE);
2203 		g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_VIDEOROOM_PACKAGE);
2204 		JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
2205 		config = janus_config_parse(filename);
2206 	}
2207 	config_folder = config_path;
2208 	if(config != NULL)
2209 		janus_config_print(config);
2210 
2211 	sessions = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)janus_videoroom_session_destroy);
2212 	messages = g_async_queue_new_full((GDestroyNotify) janus_videoroom_message_free);
2213 
2214 	/* This is the callback we'll need to invoke to contact the Janus core */
2215 	gateway = callback;
2216 
2217 	/* Parse configuration to populate the rooms list */
2218 	if(config != NULL) {
2219 		janus_config_category *config_general = janus_config_get_create(config, NULL, janus_config_type_category, "general");
2220 		/* Any admin key to limit who can "create"? */
2221 		janus_config_item *key = janus_config_get(config, config_general, janus_config_type_item, "admin_key");
2222 		if(key != NULL && key->value != NULL)
2223 			admin_key = g_strdup(key->value);
2224 		janus_config_item *lrf = janus_config_get(config, config_general, janus_config_type_item, "lock_rtp_forward");
2225 		if(admin_key && lrf != NULL && lrf->value != NULL)
2226 			lock_rtpfwd = janus_is_true(lrf->value);
2227 		janus_config_item *events = janus_config_get(config, config_general, janus_config_type_item, "events");
2228 		if(events != NULL && events->value != NULL)
2229 			notify_events = janus_is_true(events->value);
2230 		if(!notify_events && callback->events_is_enabled()) {
2231 			JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_VIDEOROOM_NAME);
2232 		}
2233 		janus_config_item *ids = janus_config_get(config, config_general, janus_config_type_item, "string_ids");
2234 		if(ids != NULL && ids->value != NULL)
2235 			string_ids = janus_is_true(ids->value);
2236 		if(string_ids) {
2237 			JANUS_LOG(LOG_INFO, "VideoRoom will use alphanumeric IDs, not numeric\n");
2238 		}
2239 	}
2240 	rooms = g_hash_table_new_full(string_ids ? g_str_hash : g_int64_hash, string_ids ? g_str_equal : g_int64_equal,
2241 		(GDestroyNotify)g_free, (GDestroyNotify)janus_videoroom_room_destroy);
2242 	/* Iterate on all rooms */
2243 	if(config != NULL) {
2244 		GList *clist = janus_config_get_categories(config, NULL), *cl = clist;
2245 		while(cl != NULL) {
2246 			janus_config_category *cat = (janus_config_category *)cl->data;
2247 			if(cat->name == NULL || !strcasecmp(cat->name, "general")) {
2248 				cl = cl->next;
2249 				continue;
2250 			}
2251 			JANUS_LOG(LOG_VERB, "Adding VideoRoom room '%s'\n", cat->name);
2252 			janus_config_item *desc = janus_config_get(config, cat, janus_config_type_item, "description");
2253 			janus_config_item *priv = janus_config_get(config, cat, janus_config_type_item, "is_private");
2254 			janus_config_item *secret = janus_config_get(config, cat, janus_config_type_item, "secret");
2255 			janus_config_item *pin = janus_config_get(config, cat, janus_config_type_item, "pin");
2256 			janus_config_item *req_pvtid = janus_config_get(config, cat, janus_config_type_item, "require_pvtid");
2257 			janus_config_item *signed_tokens = janus_config_get(config, cat, janus_config_type_item, "signed_tokens");
2258 			janus_config_item *bitrate = janus_config_get(config, cat, janus_config_type_item, "bitrate");
2259 			janus_config_item *bitrate_cap = janus_config_get(config, cat, janus_config_type_item, "bitrate_cap");
2260 			janus_config_item *maxp = janus_config_get(config, cat, janus_config_type_item, "publishers");
2261 			janus_config_item *firfreq = janus_config_get(config, cat, janus_config_type_item, "fir_freq");
2262 			janus_config_item *audiocodec = janus_config_get(config, cat, janus_config_type_item, "audiocodec");
2263 			janus_config_item *videocodec = janus_config_get(config, cat, janus_config_type_item, "videocodec");
2264 			janus_config_item *vp9profile = janus_config_get(config, cat, janus_config_type_item, "vp9_profile");
2265 			janus_config_item *h264profile = janus_config_get(config, cat, janus_config_type_item, "h264_profile");
2266 			janus_config_item *fec = janus_config_get(config, cat, janus_config_type_item, "opus_fec");
2267 			janus_config_item *dtx = janus_config_get(config, cat, janus_config_type_item, "opus_dtx");
2268 			janus_config_item *svc = janus_config_get(config, cat, janus_config_type_item, "video_svc");
2269 			janus_config_item *audiolevel_ext = janus_config_get(config, cat, janus_config_type_item, "audiolevel_ext");
2270 			janus_config_item *audiolevel_event = janus_config_get(config, cat, janus_config_type_item, "audiolevel_event");
2271 			janus_config_item *audio_active_packets = janus_config_get(config, cat, janus_config_type_item, "audio_active_packets");
2272 			janus_config_item *audio_level_average = janus_config_get(config, cat, janus_config_type_item, "audio_level_average");
2273 			janus_config_item *videoorient_ext = janus_config_get(config, cat, janus_config_type_item, "videoorient_ext");
2274 			janus_config_item *playoutdelay_ext = janus_config_get(config, cat, janus_config_type_item, "playoutdelay_ext");
2275 			janus_config_item *transport_wide_cc_ext = janus_config_get(config, cat, janus_config_type_item, "transport_wide_cc_ext");
2276 			janus_config_item *notify_joining = janus_config_get(config, cat, janus_config_type_item, "notify_joining");
2277 			janus_config_item *req_e2ee = janus_config_get(config, cat, janus_config_type_item, "require_e2ee");
2278 			janus_config_item *record = janus_config_get(config, cat, janus_config_type_item, "record");
2279 			janus_config_item *rec_dir = janus_config_get(config, cat, janus_config_type_item, "rec_dir");
2280 			janus_config_item *lock_record = janus_config_get(config, cat, janus_config_type_item, "lock_record");
2281 			/* Create the video room */
2282 			janus_videoroom *videoroom = g_malloc0(sizeof(janus_videoroom));
2283 			const char *room_num = cat->name;
2284 			if(strstr(room_num, "room-") == room_num)
2285 				room_num += 5;
2286 			if(!string_ids) {
2287 				videoroom->room_id = g_ascii_strtoull(room_num, NULL, 0);
2288 				if(videoroom->room_id == 0) {
2289 					JANUS_LOG(LOG_ERR, "Can't add the VideoRoom room, invalid ID 0...\n");
2290 					g_free(videoroom);
2291 					cl = cl->next;
2292 					continue;
2293 				}
2294 				/* Make sure the ID is completely numeric */
2295 				char room_id_str[30];
2296 				g_snprintf(room_id_str, sizeof(room_id_str), "%"SCNu64, videoroom->room_id);
2297 				if(strcmp(room_num, room_id_str)) {
2298 					JANUS_LOG(LOG_ERR, "Can't add the VideoRoom room, ID '%s' is not numeric...\n", room_num);
2299 					g_free(videoroom);
2300 					cl = cl->next;
2301 					continue;
2302 				}
2303 			}
2304 			/* Let's make sure the room doesn't exist already */
2305 			janus_mutex_lock(&rooms_mutex);
2306 			if(g_hash_table_lookup(rooms, string_ids ? (gpointer)room_num : (gpointer)&videoroom->room_id) != NULL) {
2307 				/* It does... */
2308 				janus_mutex_unlock(&rooms_mutex);
2309 				JANUS_LOG(LOG_ERR, "Can't add the VideoRoom room, room %s already exists...\n", room_num);
2310 				g_free(videoroom);
2311 				cl = cl->next;
2312 				continue;
2313 			}
2314 			janus_mutex_unlock(&rooms_mutex);
2315 			videoroom->room_id_str = g_strdup(room_num);
2316 			char *description = NULL;
2317 			if(desc != NULL && desc->value != NULL && strlen(desc->value) > 0)
2318 				description = g_strdup(desc->value);
2319 			else
2320 				description = g_strdup(cat->name);
2321 			videoroom->room_name = description;
2322 			if(secret != NULL && secret->value != NULL) {
2323 				videoroom->room_secret = g_strdup(secret->value);
2324 			}
2325 			if(pin != NULL && pin->value != NULL) {
2326 				videoroom->room_pin = g_strdup(pin->value);
2327 			}
2328 			videoroom->is_private = priv && priv->value && janus_is_true(priv->value);
2329 			videoroom->require_pvtid = req_pvtid && req_pvtid->value && janus_is_true(req_pvtid->value);
2330 			if(signed_tokens && signed_tokens->value && janus_is_true(signed_tokens->value)) {
2331 				if(!gateway->auth_is_signed()) {
2332 					JANUS_LOG(LOG_WARN, "Can't enforce signed tokens for this room, signed-mode not in use in the core\n");
2333 				} else {
2334 					videoroom->signed_tokens = TRUE;
2335 				}
2336 			}
2337 			videoroom->require_e2ee = req_e2ee && req_e2ee->value && janus_is_true(req_e2ee->value);
2338 			videoroom->max_publishers = 3;	/* FIXME How should we choose a default? */
2339 			if(maxp != NULL && maxp->value != NULL)
2340 				videoroom->max_publishers = atol(maxp->value);
2341 			if(videoroom->max_publishers < 0)
2342 				videoroom->max_publishers = 3;	/* FIXME How should we choose a default? */
2343 			videoroom->bitrate = 0;
2344 			if(bitrate != NULL && bitrate->value != NULL)
2345 				videoroom->bitrate = atol(bitrate->value);
2346 			if(videoroom->bitrate > 0 && videoroom->bitrate < 64000)
2347 				videoroom->bitrate = 64000;	/* Don't go below 64k */
2348 			videoroom->bitrate_cap = bitrate_cap && bitrate_cap->value && janus_is_true(bitrate_cap->value);
2349 			videoroom->fir_freq = 0;
2350 			if(firfreq != NULL && firfreq->value != NULL)
2351 				videoroom->fir_freq = atol(firfreq->value);
2352 			/* By default, we force Opus as the only audio codec */
2353 			videoroom->acodec[0] = JANUS_AUDIOCODEC_OPUS;
2354 			videoroom->acodec[1] = JANUS_AUDIOCODEC_NONE;
2355 			videoroom->acodec[2] = JANUS_AUDIOCODEC_NONE;
2356 			videoroom->acodec[3] = JANUS_AUDIOCODEC_NONE;
2357 			videoroom->acodec[4] = JANUS_AUDIOCODEC_NONE;
2358 			/* Check if we're forcing a different single codec, or allowing more than one */
2359 			if(audiocodec && audiocodec->value) {
2360 				gchar **list = g_strsplit(audiocodec->value, ",", 6);
2361 				gchar *codec = list[0];
2362 				if(codec != NULL) {
2363 					int i=0;
2364 					while(codec != NULL) {
2365 						if(i == 5) {
2366 							JANUS_LOG(LOG_WARN, "Ignoring extra audio codecs: %s\n", codec);
2367 							break;
2368 						}
2369 						if(strlen(codec) > 0)
2370 							videoroom->acodec[i] = janus_audiocodec_from_name(codec);
2371 						i++;
2372 						codec = list[i];
2373 					}
2374 				}
2375 				g_clear_pointer(&list, g_strfreev);
2376 			}
2377 			/* By default, we force VP8 as the only video codec */
2378 			videoroom->vcodec[0] = JANUS_VIDEOCODEC_VP8;
2379 			videoroom->vcodec[1] = JANUS_VIDEOCODEC_NONE;
2380 			videoroom->vcodec[2] = JANUS_VIDEOCODEC_NONE;
2381 			videoroom->vcodec[3] = JANUS_VIDEOCODEC_NONE;
2382 			videoroom->vcodec[4] = JANUS_VIDEOCODEC_NONE;
2383 			/* Check if we're forcing a different single codec, or allowing more than one */
2384 			if(videocodec && videocodec->value) {
2385 				gchar **list = g_strsplit(videocodec->value, ",", 6);
2386 				gchar *codec = list[0];
2387 				if(codec != NULL) {
2388 					int i=0;
2389 					while(codec != NULL) {
2390 						if(i == 5) {
2391 							JANUS_LOG(LOG_WARN, "Ignoring extra video codecs: %s\n", codec);
2392 							break;
2393 						}
2394 						if(strlen(codec) > 0)
2395 							videoroom->vcodec[i] = janus_videocodec_from_name(codec);
2396 						i++;
2397 						codec = list[i];
2398 					}
2399 				}
2400 				g_clear_pointer(&list, g_strfreev);
2401 			}
2402 			if(vp9profile && vp9profile->value && (videoroom->vcodec[0] == JANUS_VIDEOCODEC_VP9 ||
2403 					videoroom->vcodec[1] == JANUS_VIDEOCODEC_VP9 ||
2404 					videoroom->vcodec[2] == JANUS_VIDEOCODEC_VP9 ||
2405 					videoroom->vcodec[3] == JANUS_VIDEOCODEC_VP9 ||
2406 					videoroom->vcodec[4] == JANUS_VIDEOCODEC_VP9)) {
2407 				videoroom->vp9_profile = g_strdup(vp9profile->value);
2408 			}
2409 			if(h264profile && h264profile->value && (videoroom->vcodec[0] == JANUS_VIDEOCODEC_H264 ||
2410 					videoroom->vcodec[1] == JANUS_VIDEOCODEC_H264 ||
2411 					videoroom->vcodec[2] == JANUS_VIDEOCODEC_H264 ||
2412 					videoroom->vcodec[3] == JANUS_VIDEOCODEC_H264 ||
2413 					videoroom->vcodec[4] == JANUS_VIDEOCODEC_H264)) {
2414 				videoroom->h264_profile = g_strdup(h264profile->value);
2415 			}
2416 			videoroom->do_opusfec = TRUE;
2417 			if(fec && fec->value) {
2418 				videoroom->do_opusfec = janus_is_true(fec->value);
2419 				if(videoroom->acodec[0] != JANUS_AUDIOCODEC_OPUS &&
2420 						videoroom->acodec[1] != JANUS_AUDIOCODEC_OPUS &&
2421 						videoroom->acodec[2] != JANUS_AUDIOCODEC_OPUS &&
2422 						videoroom->acodec[3] != JANUS_AUDIOCODEC_OPUS &&
2423 						videoroom->acodec[4] != JANUS_AUDIOCODEC_OPUS) {
2424 					videoroom->do_opusfec = FALSE;
2425 					JANUS_LOG(LOG_WARN, "Inband FEC is only supported for rooms that allow Opus: disabling it...\n");
2426 				}
2427 			}
2428 			if(dtx && dtx->value) {
2429 				videoroom->do_opusdtx = janus_is_true(dtx->value);
2430 				if(videoroom->acodec[0] != JANUS_AUDIOCODEC_OPUS &&
2431 						videoroom->acodec[1] != JANUS_AUDIOCODEC_OPUS &&
2432 						videoroom->acodec[2] != JANUS_AUDIOCODEC_OPUS &&
2433 						videoroom->acodec[3] != JANUS_AUDIOCODEC_OPUS &&
2434 						videoroom->acodec[4] != JANUS_AUDIOCODEC_OPUS) {
2435 					videoroom->do_opusdtx = FALSE;
2436 					JANUS_LOG(LOG_WARN, "DTX is only supported for rooms that allow Opus: disabling it...\n");
2437 				}
2438 			}
2439 			if(svc && svc->value && janus_is_true(svc->value)) {
2440 				if(videoroom->vcodec[0] == JANUS_VIDEOCODEC_VP9 &&
2441 						videoroom->vcodec[1] == JANUS_VIDEOCODEC_NONE &&
2442 						videoroom->vcodec[2] == JANUS_VIDEOCODEC_NONE &&
2443 						videoroom->vcodec[3] == JANUS_VIDEOCODEC_NONE &&
2444 						videoroom->vcodec[4] == JANUS_VIDEOCODEC_NONE) {
2445 					videoroom->do_svc = TRUE;
2446 				} else {
2447 					JANUS_LOG(LOG_WARN, "SVC is only supported, in an experimental way, for VP9 only rooms: disabling it...\n");
2448 				}
2449 			}
2450 			videoroom->audiolevel_ext = TRUE;
2451 			if(audiolevel_ext != NULL && audiolevel_ext->value != NULL)
2452 				videoroom->audiolevel_ext = janus_is_true(audiolevel_ext->value);
2453 			videoroom->audiolevel_event = FALSE;
2454 			if(audiolevel_event != NULL && audiolevel_event->value != NULL)
2455 				videoroom->audiolevel_event = janus_is_true(audiolevel_event->value);
2456 			if(videoroom->audiolevel_event) {
2457 				videoroom->audio_active_packets = 100;
2458 				if(audio_active_packets != NULL && audio_active_packets->value != NULL){
2459 					if(atoi(audio_active_packets->value) > 0) {
2460 						videoroom->audio_active_packets = atoi(audio_active_packets->value);
2461 					} else {
2462 						JANUS_LOG(LOG_WARN, "Invalid audio_active_packets value, using default: %d\n", videoroom->audio_active_packets);
2463 					}
2464 				}
2465 				videoroom->audio_level_average = 25;
2466 				if(audio_level_average != NULL && audio_level_average->value != NULL) {
2467 					if(atoi(audio_level_average->value) > 0) {
2468 						videoroom->audio_level_average = atoi(audio_level_average->value);
2469 					} else {
2470 						JANUS_LOG(LOG_WARN, "Invalid audio_level_average value provided, using default: %d\n", videoroom->audio_level_average);
2471 					}
2472 				}
2473 			}
2474 			videoroom->videoorient_ext = TRUE;
2475 			if(videoorient_ext != NULL && videoorient_ext->value != NULL)
2476 				videoroom->videoorient_ext = janus_is_true(videoorient_ext->value);
2477 			videoroom->playoutdelay_ext = TRUE;
2478 			if(playoutdelay_ext != NULL && playoutdelay_ext->value != NULL)
2479 				videoroom->playoutdelay_ext = janus_is_true(playoutdelay_ext->value);
2480 			videoroom->transport_wide_cc_ext = TRUE;
2481 			if(transport_wide_cc_ext != NULL && transport_wide_cc_ext->value != NULL)
2482 				videoroom->transport_wide_cc_ext = janus_is_true(transport_wide_cc_ext->value);
2483 			if(record && record->value) {
2484 				videoroom->record = janus_is_true(record->value);
2485 			}
2486 			if(rec_dir && rec_dir->value) {
2487 				videoroom->rec_dir = g_strdup(rec_dir->value);
2488 			}
2489 			if(lock_record && lock_record->value) {
2490 				videoroom->lock_record = janus_is_true(lock_record->value);
2491 			}
2492 			/* By default, the VideoRoom plugin does not notify about participants simply joining the room.
2493 			   It only notifies when the participant actually starts publishing media. */
2494 			videoroom->notify_joining = FALSE;
2495 			if(notify_joining != NULL && notify_joining->value != NULL)
2496 				videoroom->notify_joining = janus_is_true(notify_joining->value);
2497 			g_atomic_int_set(&videoroom->destroyed, 0);
2498 			janus_mutex_init(&videoroom->mutex);
2499 			janus_refcount_init(&videoroom->ref, janus_videoroom_room_free);
2500 			videoroom->participants = g_hash_table_new_full(string_ids ? g_str_hash : g_int64_hash, string_ids ? g_str_equal : g_int64_equal,
2501 				(GDestroyNotify)g_free, (GDestroyNotify)janus_videoroom_publisher_dereference);
2502 			videoroom->private_ids = g_hash_table_new(NULL, NULL);
2503 			videoroom->check_allowed = FALSE;	/* Static rooms can't have an "allowed" list yet, no hooks to the configuration file */
2504 			videoroom->allowed = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL);
2505 			janus_mutex_lock(&rooms_mutex);
2506 			g_hash_table_insert(rooms,
2507 				string_ids ? (gpointer)g_strdup(videoroom->room_id_str) : (gpointer)janus_uint64_dup(videoroom->room_id),
2508 				videoroom);
2509 			janus_mutex_unlock(&rooms_mutex);
2510 			/* Compute a list of the supported codecs for the summary */
2511 			char audio_codecs[100], video_codecs[100];
2512 			janus_videoroom_codecstr(videoroom, audio_codecs, video_codecs, sizeof(audio_codecs), "|");
2513 			JANUS_LOG(LOG_VERB, "Created VideoRoom: %s (%s, %s, %s/%s codecs, secret: %s, pin: %s, pvtid: %s)\n",
2514 				videoroom->room_id_str, videoroom->room_name,
2515 				videoroom->is_private ? "private" : "public",
2516 				audio_codecs, video_codecs,
2517 				videoroom->room_secret ? videoroom->room_secret : "no secret",
2518 				videoroom->room_pin ? videoroom->room_pin : "no pin",
2519 				videoroom->require_pvtid ? "required" : "optional");
2520 			if(videoroom->record) {
2521 				JANUS_LOG(LOG_VERB, "  -- Room is going to be recorded in %s\n",
2522 					videoroom->rec_dir ? videoroom->rec_dir : "the current folder");
2523 			}
2524 			if(videoroom->require_e2ee) {
2525 				JANUS_LOG(LOG_VERB, "  -- All publishers MUST use end-to-end encryption\n");
2526 			}
2527 			cl = cl->next;
2528 		}
2529 		/* Done: we keep the configuration file open in case we get a "create" or "destroy" with permanent=true */
2530 	}
2531 
2532 	/* Show available rooms */
2533 	janus_mutex_lock(&rooms_mutex);
2534 	GHashTableIter iter;
2535 	gpointer value;
2536 	g_hash_table_iter_init(&iter, rooms);
2537 	while (g_hash_table_iter_next(&iter, NULL, &value)) {
2538 		janus_videoroom *vr = value;
2539 		/* Compute a list of the supported codecs for the summary */
2540 		char audio_codecs[100], video_codecs[100];
2541 		janus_videoroom_codecstr(vr, audio_codecs, video_codecs, sizeof(audio_codecs), "|");
2542 		JANUS_LOG(LOG_VERB, "  ::: [%s][%s] %"SCNu32", max %d publishers, FIR frequency of %d seconds, %s audio codec(s), %s video codec(s)\n",
2543 			vr->room_id_str, vr->room_name, vr->bitrate, vr->max_publishers, vr->fir_freq,
2544 			audio_codecs, video_codecs);
2545 	}
2546 	janus_mutex_unlock(&rooms_mutex);
2547 
2548 	/* Thread for handling incoming RTCP packets from RTP forwarders, if any */
2549 	rtcpfwd_ctx = g_main_context_new();
2550 	rtcpfwd_loop = g_main_loop_new(rtcpfwd_ctx, FALSE);
2551 	GError *error = NULL;
2552 	rtcpfwd_thread = g_thread_try_new("videoroom rtcpfwd", janus_videoroom_rtp_forwarder_rtcp_thread, NULL, &error);
2553 	if(error != NULL) {
2554 		/* We show the error but it's not fatal */
2555 		JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the VideoRoom RTCP thread for RTP forwarders...\n",
2556 			error->code, error->message ? error->message : "??");
2557 		g_error_free(error);
2558 	}
2559 
2560 	g_atomic_int_set(&initialized, 1);
2561 
2562 	/* Launch the thread that will handle incoming messages */
2563 	error = NULL;
2564 	handler_thread = g_thread_try_new("videoroom handler", janus_videoroom_handler, NULL, &error);
2565 	if(error != NULL) {
2566 		g_atomic_int_set(&initialized, 0);
2567 		JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the VideoRoom handler thread...\n",
2568 			error->code, error->message ? error->message : "??");
2569 		g_error_free(error);
2570 		janus_config_destroy(config);
2571 		return -1;
2572 	}
2573 	JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_VIDEOROOM_NAME);
2574 	return 0;
2575 }
2576 
janus_videoroom_destroy(void)2577 void janus_videoroom_destroy(void) {
2578 	if(!g_atomic_int_get(&initialized))
2579 		return;
2580 	g_atomic_int_set(&stopping, 1);
2581 
2582 	g_async_queue_push(messages, &exit_message);
2583 	if(handler_thread != NULL) {
2584 		g_thread_join(handler_thread);
2585 		handler_thread = NULL;
2586 	}
2587 	if(rtcpfwd_thread != NULL) {
2588 		if(g_main_loop_is_running(rtcpfwd_loop)) {
2589 			g_main_loop_quit(rtcpfwd_loop);
2590 			g_main_context_wakeup(rtcpfwd_ctx);
2591 		}
2592 		g_thread_join(rtcpfwd_thread);
2593 		rtcpfwd_thread = NULL;
2594 	}
2595 
2596 	/* FIXME We should destroy the sessions cleanly */
2597 	janus_mutex_lock(&sessions_mutex);
2598 	g_hash_table_destroy(sessions);
2599 	sessions = NULL;
2600 	janus_mutex_unlock(&sessions_mutex);
2601 
2602 	janus_mutex_lock(&rooms_mutex);
2603 	g_hash_table_destroy(rooms);
2604 	rooms = NULL;
2605 	janus_mutex_unlock(&rooms_mutex);
2606 
2607 	g_async_queue_unref(messages);
2608 	messages = NULL;
2609 
2610 	janus_config_destroy(config);
2611 	g_free(admin_key);
2612 
2613 	g_atomic_int_set(&initialized, 0);
2614 	g_atomic_int_set(&stopping, 0);
2615 	JANUS_LOG(LOG_INFO, "%s destroyed!\n", JANUS_VIDEOROOM_NAME);
2616 }
2617 
janus_videoroom_get_api_compatibility(void)2618 int janus_videoroom_get_api_compatibility(void) {
2619 	/* Important! This is what your plugin MUST always return: don't lie here or bad things will happen */
2620 	return JANUS_PLUGIN_API_VERSION;
2621 }
2622 
janus_videoroom_get_version(void)2623 int janus_videoroom_get_version(void) {
2624 	return JANUS_VIDEOROOM_VERSION;
2625 }
2626 
janus_videoroom_get_version_string(void)2627 const char *janus_videoroom_get_version_string(void) {
2628 	return JANUS_VIDEOROOM_VERSION_STRING;
2629 }
2630 
janus_videoroom_get_description(void)2631 const char *janus_videoroom_get_description(void) {
2632 	return JANUS_VIDEOROOM_DESCRIPTION;
2633 }
2634 
janus_videoroom_get_name(void)2635 const char *janus_videoroom_get_name(void) {
2636 	return JANUS_VIDEOROOM_NAME;
2637 }
2638 
janus_videoroom_get_author(void)2639 const char *janus_videoroom_get_author(void) {
2640 	return JANUS_VIDEOROOM_AUTHOR;
2641 }
2642 
janus_videoroom_get_package(void)2643 const char *janus_videoroom_get_package(void) {
2644 	return JANUS_VIDEOROOM_PACKAGE;
2645 }
2646 
janus_videoroom_lookup_session(janus_plugin_session * handle)2647 static janus_videoroom_session *janus_videoroom_lookup_session(janus_plugin_session *handle) {
2648 	janus_videoroom_session *session = NULL;
2649 	if (g_hash_table_contains(sessions, handle)) {
2650 		session = (janus_videoroom_session *)handle->plugin_handle;
2651 	}
2652 	return session;
2653 }
2654 
janus_videoroom_create_session(janus_plugin_session * handle,int * error)2655 void janus_videoroom_create_session(janus_plugin_session *handle, int *error) {
2656 	if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
2657 		*error = -1;
2658 		return;
2659 	}
2660 	janus_videoroom_session *session = g_malloc0(sizeof(janus_videoroom_session));
2661 	session->handle = handle;
2662 	session->participant_type = janus_videoroom_p_type_none;
2663 	session->participant = NULL;
2664 	g_atomic_int_set(&session->hangingup, 0);
2665 	g_atomic_int_set(&session->destroyed, 0);
2666 	handle->plugin_handle = session;
2667 	janus_mutex_init(&session->mutex);
2668 	janus_refcount_init(&session->ref, janus_videoroom_session_free);
2669 
2670 	janus_mutex_lock(&sessions_mutex);
2671 	g_hash_table_insert(sessions, handle, session);
2672 	janus_mutex_unlock(&sessions_mutex);
2673 
2674 	return;
2675 }
2676 
janus_videoroom_session_get_publisher(janus_videoroom_session * session)2677 static janus_videoroom_publisher *janus_videoroom_session_get_publisher(janus_videoroom_session *session) {
2678 	janus_mutex_lock(&session->mutex);
2679 	janus_videoroom_publisher *publisher = (janus_videoroom_publisher *)session->participant;
2680 	if(publisher)
2681 		janus_refcount_increase(&publisher->ref);
2682 	janus_mutex_unlock(&session->mutex);
2683 	return publisher;
2684 }
2685 
janus_videoroom_session_get_publisher_nodebug(janus_videoroom_session * session)2686 static janus_videoroom_publisher *janus_videoroom_session_get_publisher_nodebug(janus_videoroom_session *session) {
2687 	janus_mutex_lock(&session->mutex);
2688 	janus_videoroom_publisher *publisher = (janus_videoroom_publisher *)session->participant;
2689 	if(publisher)
2690 		janus_refcount_increase_nodebug(&publisher->ref);
2691 	janus_mutex_unlock(&session->mutex);
2692 	return publisher;
2693 }
2694 
janus_videoroom_session_get_subscriber(janus_videoroom_session * session)2695 static janus_videoroom_subscriber *janus_videoroom_session_get_subscriber(janus_videoroom_session *session) {
2696 	janus_mutex_lock(&session->mutex);
2697 	janus_videoroom_subscriber *subscriber = (janus_videoroom_subscriber *)session->participant;
2698 	if(subscriber)
2699 		janus_refcount_increase(&subscriber->ref);
2700 	janus_mutex_unlock(&session->mutex);
2701 	return subscriber;
2702 }
2703 
janus_videoroom_session_get_subscriber_nodebug(janus_videoroom_session * session)2704 static janus_videoroom_subscriber *janus_videoroom_session_get_subscriber_nodebug(janus_videoroom_session *session) {
2705 	janus_mutex_lock(&session->mutex);
2706 	janus_videoroom_subscriber *subscriber = (janus_videoroom_subscriber *)session->participant;
2707 	if(subscriber)
2708 		janus_refcount_increase_nodebug(&subscriber->ref);
2709 	janus_mutex_unlock(&session->mutex);
2710 	return subscriber;
2711 }
2712 
janus_videoroom_notify_participants(janus_videoroom_publisher * participant,json_t * msg,gboolean notify_source_participant)2713 static void janus_videoroom_notify_participants(janus_videoroom_publisher *participant, json_t *msg, gboolean notify_source_participant) {
2714 	/* participant->room->mutex has to be locked. */
2715 	if(participant->room == NULL)
2716 		return;
2717 	GHashTableIter iter;
2718 	gpointer value;
2719 	g_hash_table_iter_init(&iter, participant->room->participants);
2720 	while (participant->room && !g_atomic_int_get(&participant->room->destroyed) && g_hash_table_iter_next(&iter, NULL, &value)) {
2721 		janus_videoroom_publisher *p = value;
2722 		if(p && !g_atomic_int_get(&p->destroyed) && p->session && (p != participant || notify_source_participant)) {
2723 			JANUS_LOG(LOG_VERB, "Notifying participant %s (%s)\n", p->user_id_str, p->display ? p->display : "??");
2724 			int ret = gateway->push_event(p->session->handle, &janus_videoroom_plugin, NULL, msg, NULL);
2725 			JANUS_LOG(LOG_VERB, "  >> %d (%s)\n", ret, janus_get_api_error(ret));
2726 		}
2727 	}
2728 }
2729 
janus_videoroom_participant_joining(janus_videoroom_publisher * p)2730 static void janus_videoroom_participant_joining(janus_videoroom_publisher *p) {
2731 	/* we need to check if the room still exists, may have been destroyed already */
2732 	if(p->room == NULL)
2733 		return;
2734 	if(!g_atomic_int_get(&p->room->destroyed) && p->room->notify_joining) {
2735 		json_t *event = json_object();
2736 		json_t *user = json_object();
2737 		json_object_set_new(user, "id", string_ids ? json_string(p->user_id_str) : json_integer(p->user_id));
2738 		if (p->display) {
2739 			json_object_set_new(user, "display", json_string(p->display));
2740 		}
2741 		json_object_set_new(event, "videoroom", json_string("event"));
2742 		json_object_set_new(event, "room", string_ids ? json_string(p->room_id_str) : json_integer(p->room_id));
2743 		json_object_set_new(event, "joining", user);
2744 		janus_videoroom_notify_participants(p, event, FALSE);
2745 		/* user gets deref-ed by the owner event */
2746 		json_decref(event);
2747 	}
2748 }
2749 
janus_videoroom_leave_or_unpublish(janus_videoroom_publisher * participant,gboolean is_leaving,gboolean kicked)2750 static void janus_videoroom_leave_or_unpublish(janus_videoroom_publisher *participant, gboolean is_leaving, gboolean kicked) {
2751 	/* we need to check if the room still exists, may have been destroyed already */
2752 	if(participant->room == NULL)
2753 		return;
2754 	janus_mutex_lock(&rooms_mutex);
2755 	if(!g_hash_table_lookup(rooms, string_ids ? (gpointer)participant->room_id_str : (gpointer)&participant->room_id)) {
2756 		JANUS_LOG(LOG_ERR, "No such room (%s)\n", participant->room_id_str);
2757 		janus_mutex_unlock(&rooms_mutex);
2758 		return;
2759 	}
2760 	janus_videoroom *room = participant->room;
2761 	if(!room || g_atomic_int_get(&room->destroyed)) {
2762 		janus_mutex_unlock(&rooms_mutex);
2763 		return;
2764 	}
2765 	janus_refcount_increase(&room->ref);
2766 	janus_mutex_unlock(&rooms_mutex);
2767 	janus_mutex_lock(&room->mutex);
2768 	if (!participant->room) {
2769 		janus_mutex_unlock(&room->mutex);
2770 		janus_refcount_decrease(&room->ref);
2771 		return;
2772 	}
2773 	json_t *event = json_object();
2774 	json_object_set_new(event, "videoroom", json_string("event"));
2775 	json_object_set_new(event, "room", string_ids ? json_string(participant->room_id_str) : json_integer(participant->room_id));
2776 	json_object_set_new(event, is_leaving ? (kicked ? "kicked" : "leaving") : "unpublished",
2777 		string_ids ? json_string(participant->user_id_str) : json_integer(participant->user_id));
2778 	janus_videoroom_notify_participants(participant, event, FALSE);
2779 	/* Also notify event handlers */
2780 	if(notify_events && gateway->events_is_enabled()) {
2781 		json_t *info = json_object();
2782 		json_object_set_new(info, "event", json_string(is_leaving ? (kicked ? "kicked" : "leaving") : "unpublished"));
2783 		json_object_set_new(info, "room", string_ids ? json_string(participant->room_id_str) : json_integer(participant->room_id));
2784 		json_object_set_new(info, "id", string_ids ? json_string(participant->user_id_str) : json_integer(participant->user_id));
2785 		gateway->notify_event(&janus_videoroom_plugin, NULL, info);
2786 	}
2787 	if(is_leaving) {
2788 		g_hash_table_remove(participant->room->participants,
2789 			string_ids ? (gpointer)participant->user_id_str : (gpointer)&participant->user_id);
2790 		g_hash_table_remove(participant->room->private_ids, GUINT_TO_POINTER(participant->pvt_id));
2791 		g_clear_pointer(&participant->room, janus_videoroom_room_dereference);
2792 	}
2793 	janus_mutex_unlock(&room->mutex);
2794 	janus_refcount_decrease(&room->ref);
2795 	json_decref(event);
2796 }
2797 
janus_videoroom_destroy_session(janus_plugin_session * handle,int * error)2798 void janus_videoroom_destroy_session(janus_plugin_session *handle, int *error) {
2799 	if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
2800 		*error = -1;
2801 		return;
2802 	}
2803 	janus_mutex_lock(&sessions_mutex);
2804 	janus_videoroom_session *session = janus_videoroom_lookup_session(handle);
2805 	if(!session) {
2806 		janus_mutex_unlock(&sessions_mutex);
2807 		JANUS_LOG(LOG_ERR, "No VideoRoom session associated with this handle...\n");
2808 		*error = -2;
2809 		return;
2810 	}
2811 	if(g_atomic_int_get(&session->destroyed)) {
2812 		janus_mutex_unlock(&sessions_mutex);
2813 		JANUS_LOG(LOG_WARN, "VideoRoom session already marked as destroyed...\n");
2814 		return;
2815 	}
2816 	janus_refcount_increase(&session->ref);
2817 	g_hash_table_remove(sessions, handle);
2818 	janus_mutex_unlock(&sessions_mutex);
2819 	/* Any related WebRTC PeerConnection is not available anymore either */
2820 	janus_videoroom_hangup_media_internal(session);
2821 	if(session->participant_type == janus_videoroom_p_type_publisher) {
2822 		/* Get rid of publisher */
2823 		janus_mutex_lock(&session->mutex);
2824 		janus_videoroom_publisher *p = (janus_videoroom_publisher *)session->participant;
2825 		if(p)
2826 			janus_refcount_increase(&p->ref);
2827 		session->participant = NULL;
2828 		janus_mutex_unlock(&session->mutex);
2829 		if(p && p->room) {
2830 			janus_videoroom_leave_or_unpublish(p, TRUE, FALSE);
2831 		}
2832 		janus_videoroom_publisher_destroy(p);
2833 		if(p)
2834 			janus_refcount_decrease(&p->ref);
2835 	} else if(session->participant_type == janus_videoroom_p_type_subscriber) {
2836 		janus_mutex_lock(&session->mutex);
2837 		janus_videoroom_subscriber *s = (janus_videoroom_subscriber *)session->participant;
2838 		if(s)
2839 			janus_refcount_increase(&s->ref);
2840 		session->participant = NULL;
2841 		janus_mutex_unlock(&session->mutex);
2842 		if(s && s->room) {
2843 			janus_refcount_decrease(&s->room->ref);
2844 			janus_refcount_decrease(&s->ref);
2845 		}
2846 		janus_videoroom_subscriber_destroy(s);
2847 		if(s)
2848 			janus_refcount_decrease(&s->ref);
2849 	}
2850 	janus_refcount_decrease(&session->ref);
2851 	return;
2852 }
2853 
janus_videoroom_query_session(janus_plugin_session * handle)2854 json_t *janus_videoroom_query_session(janus_plugin_session *handle) {
2855 	if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
2856 		return NULL;
2857 	}
2858 	janus_mutex_lock(&sessions_mutex);
2859 	janus_videoroom_session *session = janus_videoroom_lookup_session(handle);
2860 	if(!session) {
2861 		janus_mutex_unlock(&sessions_mutex);
2862 		JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
2863 		return NULL;
2864 	}
2865 	janus_refcount_increase(&session->ref);
2866 	janus_mutex_unlock(&sessions_mutex);
2867 	/* Show the participant/room info, if any */
2868 	json_t *info = json_object();
2869 	if(session->participant) {
2870 		if(session->participant_type == janus_videoroom_p_type_none) {
2871 			json_object_set_new(info, "type", json_string("none"));
2872 		} else if(session->participant_type == janus_videoroom_p_type_publisher) {
2873 			json_object_set_new(info, "type", json_string("publisher"));
2874 			janus_videoroom_publisher *participant = janus_videoroom_session_get_publisher(session);
2875 			if(participant && participant->room) {
2876 				janus_videoroom *room = participant->room;
2877 				json_object_set_new(info, "room", room ?
2878 					(string_ids ? json_string(room->room_id_str) : json_integer(room->room_id)) : NULL);
2879 				json_object_set_new(info, "id", string_ids ? json_string(participant->user_id_str) : json_integer(participant->user_id));
2880 				json_object_set_new(info, "private_id", json_integer(participant->pvt_id));
2881 				if(participant->display)
2882 					json_object_set_new(info, "display", json_string(participant->display));
2883 				if(participant->subscribers)
2884 					json_object_set_new(info, "viewers", json_integer(g_slist_length(participant->subscribers)));
2885 				json_t *media = json_object();
2886 				json_object_set_new(media, "audio", participant->audio ? json_true() : json_false());
2887 				if(participant->audio)
2888 					json_object_set_new(media, "audio_codec", json_string(janus_audiocodec_name(participant->acodec)));
2889 				json_object_set_new(media, "video", participant->video ? json_true() : json_false());
2890 				if(participant->video)
2891 					json_object_set_new(media, "video_codec", json_string(janus_videocodec_name(participant->vcodec)));
2892 				json_object_set_new(media, "data", participant->data ? json_true() : json_false());
2893 				json_object_set_new(info, "media", media);
2894 				json_object_set_new(info, "bitrate", json_integer(participant->bitrate));
2895 				if(participant->ssrc[0] != 0 || participant->rid[0] != NULL)
2896 					json_object_set_new(info, "simulcast", json_true());
2897 				if(participant->arc || participant->vrc || participant->drc) {
2898 					json_t *recording = json_object();
2899 					if(participant->arc && participant->arc->filename)
2900 						json_object_set_new(recording, "audio", json_string(participant->arc->filename));
2901 					if(participant->vrc && participant->vrc->filename)
2902 						json_object_set_new(recording, "video", json_string(participant->vrc->filename));
2903 					if(participant->drc && participant->drc->filename)
2904 						json_object_set_new(recording, "data", json_string(participant->drc->filename));
2905 					json_object_set_new(info, "recording", recording);
2906 				}
2907 				if(participant->audio_level_extmap_id > 0) {
2908 					json_object_set_new(info, "audio-level-dBov", json_integer(participant->audio_dBov_level));
2909 					json_object_set_new(info, "talking", participant->talking ? json_true() : json_false());
2910 				}
2911 				if(participant->e2ee)
2912 					json_object_set_new(info, "e2ee", json_true());
2913 			}
2914 			if(participant != NULL)
2915 				janus_refcount_decrease(&participant->ref);
2916 		} else if(session->participant_type == janus_videoroom_p_type_subscriber) {
2917 			json_object_set_new(info, "type", json_string("subscriber"));
2918 			janus_videoroom_subscriber *participant = janus_videoroom_session_get_subscriber(session);
2919 			if(participant && participant->room) {
2920 				janus_videoroom_publisher *feed = (janus_videoroom_publisher *)participant->feed;
2921 				if(feed && feed->room) {
2922 					janus_videoroom *room = feed->room;
2923 					json_object_set_new(info, "room", room ?
2924 						(string_ids ? json_string(room->room_id_str) : json_integer(room->room_id)) : NULL);
2925 					json_object_set_new(info, "private_id", json_integer(participant->pvt_id));
2926 					json_object_set_new(info, "feed_id", string_ids ? json_string(feed->user_id_str) : json_integer(feed->user_id));
2927 					if(feed->display)
2928 						json_object_set_new(info, "feed_display", json_string(feed->display));
2929 				}
2930 				json_t *media = json_object();
2931 				json_object_set_new(media, "audio", participant->audio ? json_true() : json_false());
2932 				json_object_set_new(media, "audio-offered", participant->audio_offered ? json_true() : json_false());
2933 				json_object_set_new(media, "video", participant->video ? json_true() : json_false());
2934 				json_object_set_new(media, "video-offered", participant->video_offered ? json_true() : json_false());
2935 				json_object_set_new(media, "data", participant->data ? json_true() : json_false());
2936 				json_object_set_new(media, "data-offered", participant->data_offered ? json_true() : json_false());
2937 				json_object_set_new(info, "media", media);
2938 				if(feed && (feed->ssrc[0] != 0 || feed->rid[0] != NULL)) {
2939 					json_t *simulcast = json_object();
2940 					json_object_set_new(simulcast, "substream", json_integer(participant->sim_context.substream));
2941 					json_object_set_new(simulcast, "substream-target", json_integer(participant->sim_context.substream_target));
2942 					json_object_set_new(simulcast, "temporal-layer", json_integer(participant->sim_context.templayer));
2943 					json_object_set_new(simulcast, "temporal-layer-target", json_integer(participant->sim_context.templayer_target));
2944 					if(participant->sim_context.drop_trigger > 0)
2945 						json_object_set_new(simulcast, "fallback", json_integer(participant->sim_context.drop_trigger));
2946 					json_object_set_new(info, "simulcast", simulcast);
2947 				}
2948 				if(participant->room && participant->room->do_svc) {
2949 					json_t *svc = json_object();
2950 					json_object_set_new(svc, "spatial-layer", json_integer(participant->spatial_layer));
2951 					json_object_set_new(svc, "target-spatial-layer", json_integer(participant->target_spatial_layer));
2952 					json_object_set_new(svc, "temporal-layer", json_integer(participant->temporal_layer));
2953 					json_object_set_new(svc, "target-temporal-layer", json_integer(participant->target_temporal_layer));
2954 					json_object_set_new(info, "svc", svc);
2955 				}
2956 				if(participant->e2ee)
2957 					json_object_set_new(info, "e2ee", json_true());
2958 			}
2959 			if(participant)
2960 				janus_refcount_decrease(&participant->ref);
2961 		}
2962 	}
2963 	json_object_set_new(info, "hangingup", json_integer(g_atomic_int_get(&session->hangingup)));
2964 	json_object_set_new(info, "destroyed", json_integer(g_atomic_int_get(&session->destroyed)));
2965 	janus_refcount_decrease(&session->ref);
2966 	return info;
2967 }
2968 
janus_videoroom_access_room(json_t * root,gboolean check_modify,gboolean check_join,janus_videoroom ** videoroom,char * error_cause,int error_cause_size)2969 static int janus_videoroom_access_room(json_t *root, gboolean check_modify, gboolean check_join, janus_videoroom **videoroom, char *error_cause, int error_cause_size) {
2970 	/* rooms_mutex has to be locked */
2971 	int error_code = 0;
2972 	json_t *room = json_object_get(root, "room");
2973 	guint64 room_id = 0;
2974 	char room_id_num[30], *room_id_str = NULL;
2975 	if(!string_ids) {
2976 		room_id = json_integer_value(room);
2977 		g_snprintf(room_id_num, sizeof(room_id_num), "%"SCNu64, room_id);
2978 		room_id_str = room_id_num;
2979 	} else {
2980 		room_id_str = (char *)json_string_value(room);
2981 	}
2982 	*videoroom = g_hash_table_lookup(rooms,
2983 		string_ids ? (gpointer)room_id_str : (gpointer)&room_id);
2984 	if(*videoroom == NULL) {
2985 		JANUS_LOG(LOG_ERR, "No such room (%s)\n", room_id_str);
2986 		error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM;
2987 		if(error_cause)
2988 			g_snprintf(error_cause, error_cause_size, "No such room (%s)", room_id_str);
2989 		return error_code;
2990 	}
2991 	if(g_atomic_int_get(&((*videoroom)->destroyed))) {
2992 		JANUS_LOG(LOG_ERR, "No such room (%s)\n", room_id_str);
2993 		error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM;
2994 		if(error_cause)
2995 			g_snprintf(error_cause, error_cause_size, "No such room (%s)", room_id_str);
2996 		return error_code;
2997 	}
2998 	if(check_modify) {
2999 		char error_cause2[100];
3000 		JANUS_CHECK_SECRET((*videoroom)->room_secret, root, "secret", error_code, error_cause2,
3001 			JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT, JANUS_VIDEOROOM_ERROR_UNAUTHORIZED);
3002 		if(error_code != 0) {
3003 			g_strlcpy(error_cause, error_cause2, error_cause_size);
3004 			return error_code;
3005 		}
3006 	}
3007 	if(check_join) {
3008 		char error_cause2[100];
3009 		/* Signed tokens are enforced, so they precede any pin validation */
3010 		if(gateway->auth_is_signed() && (*videoroom)->signed_tokens) {
3011 			json_t *token = json_object_get(root, "token");
3012 			char room_descriptor[100];
3013 			g_snprintf(room_descriptor, sizeof(room_descriptor), "room=%s", room_id_str);
3014 			if(!gateway->auth_signature_contains(&janus_videoroom_plugin, json_string_value(token), room_descriptor)) {
3015 				error_code = JANUS_VIDEOROOM_ERROR_UNAUTHORIZED;
3016 				if(error_cause)
3017 					g_snprintf(error_cause, error_cause_size, "Unauthorized (wrong token)");
3018 				return error_code;
3019 			}
3020 		}
3021 		JANUS_CHECK_SECRET((*videoroom)->room_pin, root, "pin", error_code, error_cause2,
3022 			JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT, JANUS_VIDEOROOM_ERROR_UNAUTHORIZED);
3023 		if(error_code != 0) {
3024 			g_strlcpy(error_cause, error_cause2, error_cause_size);
3025 			return error_code;
3026 		}
3027 	}
3028 	return 0;
3029 }
3030 
3031 /* Helper method to process synchronous requests */
janus_videoroom_process_synchronous_request(janus_videoroom_session * session,json_t * message)3032 static json_t *janus_videoroom_process_synchronous_request(janus_videoroom_session *session, json_t *message) {
3033 	json_t *request = json_object_get(message, "request");
3034 	const char *request_text = json_string_value(request);
3035 
3036 	/* Parse the message */
3037 	int error_code = 0;
3038 	char error_cause[512];
3039 	json_t *root = message;
3040 	json_t *response = NULL;
3041 
3042 	if(!strcasecmp(request_text, "create")) {
3043 		/* Create a new VideoRoom */
3044 		JANUS_LOG(LOG_VERB, "Creating a new VideoRoom room\n");
3045 		JANUS_VALIDATE_JSON_OBJECT(root, create_parameters,
3046 			error_code, error_cause, TRUE,
3047 			JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
3048 		if(error_code != 0)
3049 			goto prepare_response;
3050 		if(!string_ids) {
3051 			JANUS_VALIDATE_JSON_OBJECT(root, roomopt_parameters,
3052 				error_code, error_cause, TRUE,
3053 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
3054 		} else {
3055 			JANUS_VALIDATE_JSON_OBJECT(root, roomstropt_parameters,
3056 				error_code, error_cause, TRUE,
3057 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
3058 		}
3059 		if(error_code != 0)
3060 			goto prepare_response;
3061 		if(admin_key != NULL) {
3062 			/* An admin key was specified: make sure it was provided, and that it's valid */
3063 			JANUS_VALIDATE_JSON_OBJECT(root, adminkey_parameters,
3064 				error_code, error_cause, TRUE,
3065 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
3066 			if(error_code != 0)
3067 				goto prepare_response;
3068 			JANUS_CHECK_SECRET(admin_key, root, "admin_key", error_code, error_cause,
3069 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT, JANUS_VIDEOROOM_ERROR_UNAUTHORIZED);
3070 			if(error_code != 0)
3071 				goto prepare_response;
3072 		}
3073 		json_t *desc = json_object_get(root, "description");
3074 		json_t *is_private = json_object_get(root, "is_private");
3075 		json_t *req_pvtid = json_object_get(root, "require_pvtid");
3076 		json_t *signed_tokens = json_object_get(root, "signed_tokens");
3077 		json_t *req_e2ee = json_object_get(root, "require_e2ee");
3078 		json_t *secret = json_object_get(root, "secret");
3079 		json_t *pin = json_object_get(root, "pin");
3080 		json_t *bitrate = json_object_get(root, "bitrate");
3081 		json_t *bitrate_cap = json_object_get(root, "bitrate_cap");
3082 		json_t *fir_freq = json_object_get(root, "fir_freq");
3083 		json_t *publishers = json_object_get(root, "publishers");
3084 		json_t *allowed = json_object_get(root, "allowed");
3085 		json_t *audiocodec = json_object_get(root, "audiocodec");
3086 		if(audiocodec) {
3087 			const char *audiocodec_value = json_string_value(audiocodec);
3088 			gchar **list = g_strsplit(audiocodec_value, ",", 6);
3089 			gchar *codec = list[0];
3090 			if(codec != NULL) {
3091 				int i=0;
3092 				while(codec != NULL) {
3093 					if(i == 5) {
3094 						break;
3095 					}
3096 					if(strlen(codec) == 0 || JANUS_AUDIOCODEC_NONE == janus_audiocodec_from_name(codec)) {
3097 						JANUS_LOG(LOG_ERR, "Invalid element (audiocodec can only be or contain opus, isac32, isac16, pcmu, pcma or g722)\n");
3098 						error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
3099 						g_snprintf(error_cause, 512, "Invalid element (audiocodec can only be or contain opus, isac32, isac16, pcmu, pcma or g722)");
3100 						goto prepare_response;
3101 					}
3102 					i++;
3103 					codec = list[i];
3104 				}
3105 			}
3106 			g_clear_pointer(&list, g_strfreev);
3107 		}
3108 		json_t *videocodec = json_object_get(root, "videocodec");
3109 		if(videocodec) {
3110 			const char *videocodec_value = json_string_value(videocodec);
3111 			gchar **list = g_strsplit(videocodec_value, ",", 6);
3112 			gchar *codec = list[0];
3113 			if(codec != NULL) {
3114 				int i=0;
3115 				while(codec != NULL) {
3116 					if(i == 5) {
3117 						break;
3118 					}
3119 					if(strlen(codec) == 0 || JANUS_VIDEOCODEC_NONE == janus_videocodec_from_name(codec)) {
3120 						JANUS_LOG(LOG_ERR, "Invalid element (videocodec can only be or contain vp8, vp9, h264, av1 or h265)\n");
3121 						error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
3122 						g_snprintf(error_cause, 512, "Invalid element (videocodec can only be or contain vp8, vp9, av1, h264 or h265)");
3123 						goto prepare_response;
3124 					}
3125 					i++;
3126 					codec = list[i];
3127 				}
3128 			}
3129 			g_clear_pointer(&list, g_strfreev);
3130 		}
3131 		json_t *vp9profile = json_object_get(root, "vp9_profile");
3132 		json_t *h264profile = json_object_get(root, "h264_profile");
3133 		json_t *fec = json_object_get(root, "opus_fec");
3134 		json_t *dtx = json_object_get(root, "opus_dtx");
3135 		json_t *svc = json_object_get(root, "video_svc");
3136 		json_t *audiolevel_ext = json_object_get(root, "audiolevel_ext");
3137 		json_t *audiolevel_event = json_object_get(root, "audiolevel_event");
3138 		json_t *audio_active_packets = json_object_get(root, "audio_active_packets");
3139 		json_t *audio_level_average = json_object_get(root, "audio_level_average");
3140 		json_t *videoorient_ext = json_object_get(root, "videoorient_ext");
3141 		json_t *playoutdelay_ext = json_object_get(root, "playoutdelay_ext");
3142 		json_t *transport_wide_cc_ext = json_object_get(root, "transport_wide_cc_ext");
3143 		json_t *notify_joining = json_object_get(root, "notify_joining");
3144 		json_t *record = json_object_get(root, "record");
3145 		json_t *rec_dir = json_object_get(root, "rec_dir");
3146 		json_t *lock_record = json_object_get(root, "lock_record");
3147 		json_t *permanent = json_object_get(root, "permanent");
3148 		if(allowed) {
3149 			/* Make sure the "allowed" array only contains strings */
3150 			gboolean ok = TRUE;
3151 			if(json_array_size(allowed) > 0) {
3152 				size_t i = 0;
3153 				for(i=0; i<json_array_size(allowed); i++) {
3154 					json_t *a = json_array_get(allowed, i);
3155 					if(!a || !json_is_string(a)) {
3156 						ok = FALSE;
3157 						break;
3158 					}
3159 				}
3160 			}
3161 			if(!ok) {
3162 				JANUS_LOG(LOG_ERR, "Invalid element in the allowed array (not a string)\n");
3163 				error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
3164 				g_snprintf(error_cause, 512, "Invalid element in the allowed array (not a string)");
3165 				goto prepare_response;
3166 			}
3167 		}
3168 		gboolean save = permanent ? json_is_true(permanent) : FALSE;
3169 		if(save && config == NULL) {
3170 			JANUS_LOG(LOG_ERR, "No configuration file, can't create permanent room\n");
3171 			error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR;
3172 			g_snprintf(error_cause, 512, "No configuration file, can't create permanent room");
3173 			goto prepare_response;
3174 		}
3175 		guint64 room_id = 0;
3176 		char room_id_num[30], *room_id_str = NULL;
3177 		json_t *room = json_object_get(root, "room");
3178 		if(!string_ids) {
3179 			room_id = json_integer_value(room);
3180 			g_snprintf(room_id_num, sizeof(room_id_num), "%"SCNu64, room_id);
3181 			room_id_str = room_id_num;
3182 		} else {
3183 			room_id_str = (char *)json_string_value(room);
3184 		}
3185 		if(room_id == 0 && room_id_str == NULL) {
3186 			JANUS_LOG(LOG_WARN, "Desired room ID is empty, which is not allowed... picking random ID instead\n");
3187 		}
3188 		janus_mutex_lock(&rooms_mutex);
3189 		if(room_id > 0 || room_id_str != NULL) {
3190 			/* Let's make sure the room doesn't exist already */
3191 			if(g_hash_table_lookup(rooms, string_ids ? (gpointer)room_id_str : (gpointer)&room_id) != NULL) {
3192 				/* It does... */
3193 				janus_mutex_unlock(&rooms_mutex);
3194 				error_code = JANUS_VIDEOROOM_ERROR_ROOM_EXISTS;
3195 				JANUS_LOG(LOG_ERR, "Room %s already exists!\n", room_id_str);
3196 				g_snprintf(error_cause, 512, "Room %s already exists", room_id_str);
3197 				goto prepare_response;
3198 			}
3199 		}
3200 		/* Create the room */
3201 		janus_videoroom *videoroom = g_malloc0(sizeof(janus_videoroom));
3202 		/* Generate a random ID */
3203 		gboolean room_id_allocated = FALSE;
3204 		if(!string_ids && room_id == 0) {
3205 			while(room_id == 0) {
3206 				room_id = janus_random_uint64();
3207 				if(g_hash_table_lookup(rooms, &room_id) != NULL) {
3208 					/* Room ID already taken, try another one */
3209 					room_id = 0;
3210 				}
3211 			}
3212 			g_snprintf(room_id_num, sizeof(room_id_num), "%"SCNu64, room_id);
3213 			room_id_str = room_id_num;
3214 		} else if(string_ids && room_id_str == NULL) {
3215 			while(room_id_str == NULL) {
3216 				room_id_str = janus_random_uuid();
3217 				if(g_hash_table_lookup(rooms, room_id_str) != NULL) {
3218 					/* Room ID already taken, try another one */
3219 					g_clear_pointer(&room_id_str, g_free);
3220 				}
3221 			}
3222 			room_id_allocated = TRUE;
3223 		}
3224 		videoroom->room_id = room_id;
3225 		videoroom->room_id_str = room_id_str ? g_strdup(room_id_str) : NULL;
3226 		if(room_id_allocated)
3227 			g_free(room_id_str);
3228 		char *description = NULL;
3229 		if(desc != NULL && strlen(json_string_value(desc)) > 0) {
3230 			description = g_strdup(json_string_value(desc));
3231 		} else {
3232 			char roomname[255];
3233 			g_snprintf(roomname, 255, "Room %s", videoroom->room_id_str);
3234 			description = g_strdup(roomname);
3235 		}
3236 		videoroom->room_name = description;
3237 		videoroom->is_private = is_private ? json_is_true(is_private) : FALSE;
3238 		videoroom->require_pvtid = req_pvtid ? json_is_true(req_pvtid) : FALSE;
3239 		if(signed_tokens && json_is_true(signed_tokens)) {
3240 			if(!gateway->auth_is_signed()) {
3241 				JANUS_LOG(LOG_WARN, "Can't enforce signed tokens for this room, signed-mode not in use in the core\n");
3242 			} else {
3243 				videoroom->signed_tokens = TRUE;
3244 			}
3245 		}
3246 		videoroom->require_e2ee = req_e2ee ? json_is_true(req_e2ee) : FALSE;
3247 		if(secret)
3248 			videoroom->room_secret = g_strdup(json_string_value(secret));
3249 		if(pin)
3250 			videoroom->room_pin = g_strdup(json_string_value(pin));
3251 		videoroom->max_publishers = 3;	/* FIXME How should we choose a default? */
3252 		if(publishers)
3253 			videoroom->max_publishers = json_integer_value(publishers);
3254 		if(videoroom->max_publishers < 0)
3255 			videoroom->max_publishers = 3;	/* FIXME How should we choose a default? */
3256 		videoroom->bitrate = 0;
3257 		if(bitrate)
3258 			videoroom->bitrate = json_integer_value(bitrate);
3259 		if(videoroom->bitrate > 0 && videoroom->bitrate < 64000)
3260 			videoroom->bitrate = 64000;	/* Don't go below 64k */
3261 		videoroom->bitrate_cap = bitrate_cap ? json_is_true(bitrate_cap) : FALSE;
3262 		videoroom->fir_freq = 0;
3263 		if(fir_freq)
3264 			videoroom->fir_freq = json_integer_value(fir_freq);
3265 		/* By default, we force Opus as the only audio codec */
3266 		videoroom->acodec[0] = JANUS_AUDIOCODEC_OPUS;
3267 		videoroom->acodec[1] = JANUS_AUDIOCODEC_NONE;
3268 		videoroom->acodec[2] = JANUS_AUDIOCODEC_NONE;
3269 		videoroom->acodec[3] = JANUS_AUDIOCODEC_NONE;
3270 		videoroom->acodec[4] = JANUS_AUDIOCODEC_NONE;
3271 		/* Check if we're forcing a different single codec, or allowing more than one */
3272 		if(audiocodec) {
3273 			const char *audiocodec_value = json_string_value(audiocodec);
3274 			gchar **list = g_strsplit(audiocodec_value, ",", 6);
3275 			gchar *codec = list[0];
3276 			if(codec != NULL) {
3277 				int i=0;
3278 				while(codec != NULL) {
3279 					if(i == 5) {
3280 						JANUS_LOG(LOG_WARN, "Ignoring extra audio codecs: %s\n", codec);
3281 						break;
3282 					}
3283 					if(strlen(codec) > 0)
3284 						videoroom->acodec[i] = janus_audiocodec_from_name(codec);
3285 					i++;
3286 					codec = list[i];
3287 				}
3288 			}
3289 			g_clear_pointer(&list, g_strfreev);
3290 		}
3291 		/* By default, we force VP8 as the only video codec */
3292 		videoroom->vcodec[0] = JANUS_VIDEOCODEC_VP8;
3293 		videoroom->vcodec[1] = JANUS_VIDEOCODEC_NONE;
3294 		videoroom->vcodec[2] = JANUS_VIDEOCODEC_NONE;
3295 		videoroom->vcodec[3] = JANUS_VIDEOCODEC_NONE;
3296 		videoroom->vcodec[4] = JANUS_VIDEOCODEC_NONE;
3297 		/* Check if we're forcing a different single codec, or allowing more than one */
3298 		if(videocodec) {
3299 			const char *videocodec_value = json_string_value(videocodec);
3300 			gchar **list = g_strsplit(videocodec_value, ",", 6);
3301 			gchar *codec = list[0];
3302 			if(codec != NULL) {
3303 				int i=0;
3304 				while(codec != NULL) {
3305 					if(i == 5) {
3306 						JANUS_LOG(LOG_WARN, "Ignoring extra video codecs: %s\n", codec);
3307 						break;
3308 					}
3309 					if(strlen(codec) > 0)
3310 						videoroom->vcodec[i] = janus_videocodec_from_name(codec);
3311 					i++;
3312 					codec = list[i];
3313 				}
3314 			}
3315 			g_clear_pointer(&list, g_strfreev);
3316 		}
3317 		const char *vp9_profile = json_string_value(vp9profile);
3318 		if(vp9_profile && (videoroom->vcodec[0] == JANUS_VIDEOCODEC_VP9 ||
3319 				videoroom->vcodec[1] == JANUS_VIDEOCODEC_VP9 ||
3320 				videoroom->vcodec[2] == JANUS_VIDEOCODEC_VP9 ||
3321 				videoroom->vcodec[3] == JANUS_VIDEOCODEC_VP9 ||
3322 				videoroom->vcodec[4] == JANUS_VIDEOCODEC_VP9)) {
3323 			videoroom->vp9_profile = g_strdup(vp9_profile);
3324 		}
3325 		const char *h264_profile = json_string_value(h264profile);
3326 		if(h264_profile && (videoroom->vcodec[0] == JANUS_VIDEOCODEC_H264 ||
3327 				videoroom->vcodec[1] == JANUS_VIDEOCODEC_H264 ||
3328 				videoroom->vcodec[2] == JANUS_VIDEOCODEC_H264 ||
3329 				videoroom->vcodec[3] == JANUS_VIDEOCODEC_H264 ||
3330 				videoroom->vcodec[4] == JANUS_VIDEOCODEC_H264)) {
3331 			videoroom->h264_profile = g_strdup(h264_profile);
3332 		}
3333 		videoroom->do_opusfec = TRUE;
3334 		if(fec) {
3335 			videoroom->do_opusfec = json_is_true(fec);
3336 			if(videoroom->acodec[0] != JANUS_AUDIOCODEC_OPUS &&
3337 					videoroom->acodec[1] != JANUS_AUDIOCODEC_OPUS &&
3338 					videoroom->acodec[2] != JANUS_AUDIOCODEC_OPUS &&
3339 					videoroom->acodec[3] != JANUS_AUDIOCODEC_OPUS &&
3340 					videoroom->acodec[4] != JANUS_AUDIOCODEC_OPUS) {
3341 				videoroom->do_opusfec = FALSE;
3342 				JANUS_LOG(LOG_WARN, "Inband FEC is only supported for rooms that allow Opus: disabling it...\n");
3343 			}
3344 		}
3345 		if(dtx) {
3346 			videoroom->do_opusdtx = json_is_true(dtx);
3347 			if(videoroom->acodec[0] != JANUS_AUDIOCODEC_OPUS &&
3348 					videoroom->acodec[1] != JANUS_AUDIOCODEC_OPUS &&
3349 					videoroom->acodec[2] != JANUS_AUDIOCODEC_OPUS &&
3350 					videoroom->acodec[3] != JANUS_AUDIOCODEC_OPUS &&
3351 					videoroom->acodec[4] != JANUS_AUDIOCODEC_OPUS) {
3352 				videoroom->do_opusdtx = FALSE;
3353 				JANUS_LOG(LOG_WARN, "DTX is only supported for rooms that allow Opus: disabling it...\n");
3354 			}
3355 		}
3356 		if(svc && json_is_true(svc)) {
3357 			if(videoroom->vcodec[0] == JANUS_VIDEOCODEC_VP9 &&
3358 					videoroom->vcodec[1] == JANUS_VIDEOCODEC_NONE &&
3359 					videoroom->vcodec[2] == JANUS_VIDEOCODEC_NONE &&
3360 					videoroom->vcodec[3] == JANUS_VIDEOCODEC_NONE &&
3361 					videoroom->vcodec[4] == JANUS_VIDEOCODEC_NONE) {
3362 				videoroom->do_svc = TRUE;
3363 			} else {
3364 				JANUS_LOG(LOG_WARN, "SVC is only supported, in an experimental way, for VP9 only rooms: disabling it...\n");
3365 			}
3366 		}
3367 		videoroom->audiolevel_ext = audiolevel_ext ? json_is_true(audiolevel_ext) : TRUE;
3368 		videoroom->audiolevel_event = audiolevel_event ? json_is_true(audiolevel_event) : FALSE;
3369 		if(videoroom->audiolevel_event) {
3370 			videoroom->audio_active_packets = 100;
3371 			if(json_integer_value(audio_active_packets) > 0) {
3372 				videoroom->audio_active_packets = json_integer_value(audio_active_packets);
3373 			} else {
3374 				JANUS_LOG(LOG_WARN, "Invalid audio_active_packets value provided, using default: %d\n", videoroom->audio_active_packets);
3375 			}
3376 			videoroom->audio_level_average = 25;
3377 			if(json_integer_value(audio_level_average) > 0) {
3378 				videoroom->audio_level_average = json_integer_value(audio_level_average);
3379 			} else {
3380 				JANUS_LOG(LOG_WARN, "Invalid audio_level_average value provided, using default: %d\n", videoroom->audio_level_average);
3381 			}
3382 		}
3383 		videoroom->videoorient_ext = videoorient_ext ? json_is_true(videoorient_ext) : TRUE;
3384 		videoroom->playoutdelay_ext = playoutdelay_ext ? json_is_true(playoutdelay_ext) : TRUE;
3385 		videoroom->transport_wide_cc_ext = transport_wide_cc_ext ? json_is_true(transport_wide_cc_ext) : TRUE;
3386 		/* By default, the VideoRoom plugin does not notify about participants simply joining the room.
3387 		   It only notifies when the participant actually starts publishing media. */
3388 		videoroom->notify_joining = notify_joining ? json_is_true(notify_joining) : FALSE;
3389 		if(record) {
3390 			videoroom->record = json_is_true(record);
3391 		}
3392 		if(rec_dir) {
3393 			videoroom->rec_dir = g_strdup(json_string_value(rec_dir));
3394 		}
3395 		if(lock_record) {
3396 			videoroom->lock_record = json_is_true(lock_record);
3397 		}
3398 		g_atomic_int_set(&videoroom->destroyed, 0);
3399 		janus_mutex_init(&videoroom->mutex);
3400 		janus_refcount_init(&videoroom->ref, janus_videoroom_room_free);
3401 		videoroom->participants = g_hash_table_new_full(string_ids ? g_str_hash : g_int64_hash, string_ids ? g_str_equal : g_int64_equal,
3402 			(GDestroyNotify)g_free, (GDestroyNotify)janus_videoroom_publisher_dereference);
3403 		videoroom->private_ids = g_hash_table_new(NULL, NULL);
3404 		videoroom->allowed = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL);
3405 		if(allowed != NULL) {
3406 			/* Populate the "allowed" list as an ACL for people trying to join */
3407 			if(json_array_size(allowed) > 0) {
3408 				size_t i = 0;
3409 				for(i=0; i<json_array_size(allowed); i++) {
3410 					const char *token = json_string_value(json_array_get(allowed, i));
3411 					if(!g_hash_table_lookup(videoroom->allowed, token))
3412 						g_hash_table_insert(videoroom->allowed, g_strdup(token), GINT_TO_POINTER(TRUE));
3413 				}
3414 			}
3415 			videoroom->check_allowed = TRUE;
3416 		}
3417 		/* Compute a list of the supported codecs for the summary */
3418 		char audio_codecs[100], video_codecs[100];
3419 		janus_videoroom_codecstr(videoroom, audio_codecs, video_codecs, sizeof(audio_codecs), "|");
3420 		JANUS_LOG(LOG_VERB, "Created VideoRoom: %s (%s, %s, %s/%s codecs, secret: %s, pin: %s, pvtid: %s)\n",
3421 			videoroom->room_id_str, videoroom->room_name,
3422 			videoroom->is_private ? "private" : "public",
3423 			audio_codecs, video_codecs,
3424 			videoroom->room_secret ? videoroom->room_secret : "no secret",
3425 			videoroom->room_pin ? videoroom->room_pin : "no pin",
3426 			videoroom->require_pvtid ? "required" : "optional");
3427 		if(videoroom->record) {
3428 			JANUS_LOG(LOG_VERB, "  -- Room is going to be recorded in %s\n", videoroom->rec_dir ? videoroom->rec_dir : "the current folder");
3429 		}
3430 		if(videoroom->require_e2ee) {
3431 			JANUS_LOG(LOG_VERB, "  -- All publishers MUST use end-to-end encryption\n");
3432 		}
3433 		if(save) {
3434 			/* This room is permanent: save to the configuration file too
3435 			 * FIXME: We should check if anything fails... */
3436 			JANUS_LOG(LOG_VERB, "Saving room %s permanently in config file\n", videoroom->room_id_str);
3437 			janus_mutex_lock(&config_mutex);
3438 			char cat[BUFSIZ], value[BUFSIZ];
3439 			/* The room ID is the category (prefixed by "room-") */
3440 			g_snprintf(cat, BUFSIZ, "room-%s", videoroom->room_id_str);
3441 			janus_config_category *c = janus_config_get_create(config, NULL, janus_config_type_category, cat);
3442 			/* Now for the values */
3443 			janus_config_add(config, c, janus_config_item_create("description", videoroom->room_name));
3444 			if(videoroom->is_private)
3445 				janus_config_add(config, c, janus_config_item_create("is_private", "yes"));
3446 			if(videoroom->require_pvtid)
3447 				janus_config_add(config, c, janus_config_item_create("require_pvtid", "yes"));
3448 			if(videoroom->require_e2ee)
3449 				janus_config_add(config, c, janus_config_item_create("require_e2ee", "yes"));
3450 			g_snprintf(value, BUFSIZ, "%"SCNu32, videoroom->bitrate);
3451 			janus_config_add(config, c, janus_config_item_create("bitrate", value));
3452 			if(videoroom->bitrate_cap)
3453 				janus_config_add(config, c, janus_config_item_create("bitrate_cap", "yes"));
3454 			g_snprintf(value, BUFSIZ, "%d", videoroom->max_publishers);
3455 			janus_config_add(config, c, janus_config_item_create("publishers", value));
3456 			if(videoroom->fir_freq) {
3457 				g_snprintf(value, BUFSIZ, "%"SCNu16, videoroom->fir_freq);
3458 				janus_config_add(config, c, janus_config_item_create("fir_freq", value));
3459 			}
3460 			char video_codecs[100];
3461 			char audio_codecs[100];
3462 			janus_videoroom_codecstr(videoroom, audio_codecs, video_codecs, sizeof(audio_codecs), ",");
3463 			janus_config_add(config, c, janus_config_item_create("audiocodec", audio_codecs));
3464 			janus_config_add(config, c, janus_config_item_create("videocodec", video_codecs));
3465 			if(videoroom->vp9_profile)
3466 				janus_config_add(config, c, janus_config_item_create("vp9_profile", videoroom->vp9_profile));
3467 			if(videoroom->h264_profile)
3468 				janus_config_add(config, c, janus_config_item_create("h264_profile", videoroom->h264_profile));
3469 			if(videoroom->do_opusfec)
3470 				janus_config_add(config, c, janus_config_item_create("opus_fec", "yes"));
3471 			if(videoroom->do_opusdtx)
3472 				janus_config_add(config, c, janus_config_item_create("opus_dtx", "yes"));
3473 			if(videoroom->do_svc)
3474 				janus_config_add(config, c, janus_config_item_create("video_svc", "yes"));
3475 			if(videoroom->room_secret)
3476 				janus_config_add(config, c, janus_config_item_create("secret", videoroom->room_secret));
3477 			if(videoroom->room_pin)
3478 				janus_config_add(config, c, janus_config_item_create("pin", videoroom->room_pin));
3479 			if(videoroom->audiolevel_ext) {
3480 				janus_config_add(config, c, janus_config_item_create("audiolevel_ext", "yes"));
3481 				if(videoroom->audiolevel_event)
3482 					janus_config_add(config, c, janus_config_item_create("audiolevel_event", "yes"));
3483 				if(videoroom->audio_active_packets > 0) {
3484 					g_snprintf(value, BUFSIZ, "%d", videoroom->audio_active_packets);
3485 					janus_config_add(config, c, janus_config_item_create("audio_active_packets", value));
3486 				}
3487 				if(videoroom->audio_level_average > 0) {
3488 					g_snprintf(value, BUFSIZ, "%d", videoroom->audio_level_average);
3489 					janus_config_add(config, c, janus_config_item_create("audio_level_average", value));
3490 				}
3491 			} else {
3492 				janus_config_add(config, c, janus_config_item_create("audiolevel_ext", "no"));
3493 			}
3494 			janus_config_add(config, c, janus_config_item_create("videoorient_ext", videoroom->videoorient_ext ? "yes" : "no"));
3495 			janus_config_add(config, c, janus_config_item_create("playoutdelay_ext", videoroom->playoutdelay_ext ? "yes" : "no"));
3496 			janus_config_add(config, c, janus_config_item_create("transport_wide_cc_ext", videoroom->transport_wide_cc_ext ? "yes" : "no"));
3497 			if(videoroom->notify_joining)
3498 				janus_config_add(config, c, janus_config_item_create("notify_joining", "yes"));
3499 			if(videoroom->record)
3500 				janus_config_add(config, c, janus_config_item_create("record", "yes"));
3501 			if(videoroom->rec_dir)
3502 				janus_config_add(config, c, janus_config_item_create("rec_dir", videoroom->rec_dir));
3503 			if(videoroom->lock_record)
3504 				janus_config_add(config, c, janus_config_item_create("lock_record", "yes"));
3505 			/* Save modified configuration */
3506 			if(janus_config_save(config, config_folder, JANUS_VIDEOROOM_PACKAGE) < 0)
3507 				save = FALSE;	/* This will notify the user the room is not permanent */
3508 			janus_mutex_unlock(&config_mutex);
3509 		}
3510 
3511 		g_hash_table_insert(rooms,
3512 			string_ids ? (gpointer)g_strdup(videoroom->room_id_str) : (gpointer)janus_uint64_dup(videoroom->room_id),
3513 			videoroom);
3514 		/* Show updated rooms list */
3515 		GHashTableIter iter;
3516 		gpointer value;
3517 		g_hash_table_iter_init(&iter, rooms);
3518 		while (g_hash_table_iter_next(&iter, NULL, &value)) {
3519 			janus_videoroom *vr = value;
3520 			JANUS_LOG(LOG_VERB, "  ::: [%s][%s] %"SCNu32", max %d publishers, FIR frequency of %d seconds\n",
3521 				vr->room_id_str, vr->room_name, vr->bitrate, vr->max_publishers, vr->fir_freq);
3522 		}
3523 		janus_mutex_unlock(&rooms_mutex);
3524 		/* Send info back */
3525 		response = json_object();
3526 		json_object_set_new(response, "videoroom", json_string("created"));
3527 		json_object_set_new(response, "room", string_ids ? json_string(videoroom->room_id_str) : json_integer(videoroom->room_id));
3528 		json_object_set_new(response, "permanent", save ? json_true() : json_false());
3529 		/* Also notify event handlers */
3530 		if(notify_events && gateway->events_is_enabled()) {
3531 			json_t *info = json_object();
3532 			json_object_set_new(info, "event", json_string("created"));
3533 			json_object_set_new(info, "room", string_ids ? json_string(videoroom->room_id_str) : json_integer(videoroom->room_id));
3534 			gateway->notify_event(&janus_videoroom_plugin, session ? session->handle : NULL, info);
3535 		}
3536 		goto prepare_response;
3537 	} else if(!strcasecmp(request_text, "edit")) {
3538 		/* Edit the properties for an existing VideoRoom */
3539 		JANUS_LOG(LOG_VERB, "Attempt to edit the properties of an existing VideoRoom room\n");
3540 		if(!string_ids) {
3541 			JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
3542 				error_code, error_cause, TRUE,
3543 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
3544 		} else {
3545 			JANUS_VALIDATE_JSON_OBJECT(root, roomstr_parameters,
3546 				error_code, error_cause, TRUE,
3547 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
3548 		}
3549 		if(error_code != 0)
3550 			goto prepare_response;
3551 		JANUS_VALIDATE_JSON_OBJECT(root, edit_parameters,
3552 			error_code, error_cause, TRUE,
3553 			JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
3554 		if(error_code != 0)
3555 			goto prepare_response;
3556 		/* We only allow for a limited set of properties to be edited */
3557 		json_t *desc = json_object_get(root, "new_description");
3558 		json_t *is_private = json_object_get(root, "new_is_private");
3559 		json_t *req_pvtid = json_object_get(root, "new_require_pvtid");
3560 		json_t *secret = json_object_get(root, "new_secret");
3561 		json_t *pin = json_object_get(root, "new_pin");
3562 		json_t *bitrate = json_object_get(root, "new_bitrate");
3563 		json_t *fir_freq = json_object_get(root, "new_fir_freq");
3564 		json_t *publishers = json_object_get(root, "new_publishers");
3565 		json_t *lock_record = json_object_get(root, "new_lock_record");
3566 		json_t *permanent = json_object_get(root, "permanent");
3567 		gboolean save = permanent ? json_is_true(permanent) : FALSE;
3568 		if(save && config == NULL) {
3569 			JANUS_LOG(LOG_ERR, "No configuration file, can't edit room permanently\n");
3570 			error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR;
3571 			g_snprintf(error_cause, 512, "No configuration file, can't edit room permanently");
3572 			goto prepare_response;
3573 		}
3574 		janus_mutex_lock(&rooms_mutex);
3575 		janus_videoroom *videoroom = NULL;
3576 		error_code = janus_videoroom_access_room(root, TRUE, FALSE, &videoroom, error_cause, sizeof(error_cause));
3577 		if(error_code != 0) {
3578 			janus_mutex_unlock(&rooms_mutex);
3579 			goto prepare_response;
3580 		}
3581 		/* Edit the room properties that were provided */
3582 		if(desc != NULL && strlen(json_string_value(desc)) > 0) {
3583 			char *old_description = videoroom->room_name;
3584 			char *new_description = g_strdup(json_string_value(desc));
3585 			videoroom->room_name = new_description;
3586 			g_free(old_description);
3587 		}
3588 		if(is_private)
3589 			videoroom->is_private = json_is_true(is_private);
3590 		if(req_pvtid)
3591 			videoroom->require_pvtid = json_is_true(req_pvtid);
3592 		if(publishers)
3593 			videoroom->max_publishers = json_integer_value(publishers);
3594 		if(bitrate) {
3595 			videoroom->bitrate = json_integer_value(bitrate);
3596 			if(videoroom->bitrate > 0 && videoroom->bitrate < 64000)
3597 				videoroom->bitrate = 64000;	/* Don't go below 64k */
3598 		}
3599 		if(fir_freq)
3600 			videoroom->fir_freq = json_integer_value(fir_freq);
3601 		if(secret && strlen(json_string_value(secret)) > 0) {
3602 			char *old_secret = videoroom->room_secret;
3603 			char *new_secret = g_strdup(json_string_value(secret));
3604 			videoroom->room_secret = new_secret;
3605 			g_free(old_secret);
3606 		}
3607 		if(pin && strlen(json_string_value(pin)) > 0) {
3608 			char *old_pin = videoroom->room_pin;
3609 			char *new_pin = g_strdup(json_string_value(pin));
3610 			videoroom->room_pin = new_pin;
3611 			g_free(old_pin);
3612 		}
3613 		if(lock_record)
3614 			videoroom->lock_record = json_is_true(lock_record);
3615 		if(save) {
3616 			/* This room is permanent: save to the configuration file too
3617 			 * FIXME: We should check if anything fails... */
3618 			JANUS_LOG(LOG_VERB, "Modifying room %s permanently in config file\n", videoroom->room_id_str);
3619 			janus_mutex_lock(&config_mutex);
3620 			char cat[BUFSIZ], value[BUFSIZ];
3621 			/* The room ID is the category (prefixed by "room-") */
3622 			g_snprintf(cat, BUFSIZ, "room-%s", videoroom->room_id_str);
3623 			/* Remove the old category first */
3624 			janus_config_remove(config, NULL, cat);
3625 			/* Now write the room details again */
3626 			janus_config_category *c = janus_config_get_create(config, NULL, janus_config_type_category, cat);
3627 			janus_config_add(config, c, janus_config_item_create("description", videoroom->room_name));
3628 			if(videoroom->is_private)
3629 				janus_config_add(config, c, janus_config_item_create("is_private", "yes"));
3630 			if(videoroom->require_pvtid)
3631 				janus_config_add(config, c, janus_config_item_create("require_pvtid", "yes"));
3632 			if(videoroom->require_e2ee)
3633 				janus_config_add(config, c, janus_config_item_create("require_e2ee", "yes"));
3634 			g_snprintf(value, BUFSIZ, "%"SCNu32, videoroom->bitrate);
3635 			janus_config_add(config, c, janus_config_item_create("bitrate", value));
3636 			if(videoroom->bitrate_cap)
3637 				janus_config_add(config, c, janus_config_item_create("bitrate_cap", "yes"));
3638 			g_snprintf(value, BUFSIZ, "%d", videoroom->max_publishers);
3639 			janus_config_add(config, c, janus_config_item_create("publishers", value));
3640 			if(videoroom->fir_freq) {
3641 				g_snprintf(value, BUFSIZ, "%"SCNu16, videoroom->fir_freq);
3642 				janus_config_add(config, c, janus_config_item_create("fir_freq", value));
3643 			}
3644 			char audio_codecs[100];
3645 			char video_codecs[100];
3646 			janus_videoroom_codecstr(videoroom, audio_codecs, video_codecs, sizeof(audio_codecs), ",");
3647 			janus_config_add(config, c, janus_config_item_create("audiocodec", audio_codecs));
3648 			janus_config_add(config, c, janus_config_item_create("videocodec", video_codecs));
3649 			if(videoroom->vp9_profile)
3650 				janus_config_add(config, c, janus_config_item_create("vp9_profile", videoroom->vp9_profile));
3651 			if(videoroom->h264_profile)
3652 				janus_config_add(config, c, janus_config_item_create("h264_profile", videoroom->h264_profile));
3653 			if(videoroom->do_opusfec)
3654 				janus_config_add(config, c, janus_config_item_create("opus_fec", "yes"));
3655 			if(videoroom->do_opusdtx)
3656 				janus_config_add(config, c, janus_config_item_create("opus_dtx", "yes"));
3657 			if(videoroom->do_svc)
3658 				janus_config_add(config, c, janus_config_item_create("video_svc", "yes"));
3659 			if(videoroom->room_secret)
3660 				janus_config_add(config, c, janus_config_item_create("secret", videoroom->room_secret));
3661 			if(videoroom->room_pin)
3662 				janus_config_add(config, c, janus_config_item_create("pin", videoroom->room_pin));
3663 			if(videoroom->audiolevel_ext) {
3664 				janus_config_add(config, c, janus_config_item_create("audiolevel_ext", "yes"));
3665 				if(videoroom->audiolevel_event)
3666 					janus_config_add(config, c, janus_config_item_create("audiolevel_event", "yes"));
3667 				if(videoroom->audio_active_packets > 0) {
3668 					g_snprintf(value, BUFSIZ, "%d", videoroom->audio_active_packets);
3669 					janus_config_add(config, c, janus_config_item_create("audio_active_packets", value));
3670 				}
3671 				if(videoroom->audio_level_average > 0) {
3672 					g_snprintf(value, BUFSIZ, "%d", videoroom->audio_level_average);
3673 					janus_config_add(config, c, janus_config_item_create("audio_level_average", value));
3674 				}
3675 			} else {
3676 				janus_config_add(config, c, janus_config_item_create("audiolevel_ext", "no"));
3677 			}
3678 			janus_config_add(config, c, janus_config_item_create("videoorient_ext", videoroom->videoorient_ext ? "yes" : "no"));
3679 			janus_config_add(config, c, janus_config_item_create("playoutdelay_ext", videoroom->playoutdelay_ext ? "yes" : "no"));
3680 			janus_config_add(config, c, janus_config_item_create("transport_wide_cc_ext", videoroom->transport_wide_cc_ext ? "yes" : "no"));
3681 			if(videoroom->notify_joining)
3682 				janus_config_add(config, c, janus_config_item_create("notify_joining", "yes"));
3683 			if(videoroom->record)
3684 				janus_config_add(config, c, janus_config_item_create("record", "yes"));
3685 			if(videoroom->rec_dir)
3686 				janus_config_add(config, c, janus_config_item_create("rec_dir", videoroom->rec_dir));
3687 			if(videoroom->lock_record)
3688 				janus_config_add(config, c, janus_config_item_create("lock_record", "yes"));
3689 			/* Save modified configuration */
3690 			if(janus_config_save(config, config_folder, JANUS_VIDEOROOM_PACKAGE) < 0)
3691 				save = FALSE;	/* This will notify the user the room changes are not permanent */
3692 			janus_mutex_unlock(&config_mutex);
3693 		}
3694 		janus_mutex_unlock(&rooms_mutex);
3695 		/* Send info back */
3696 		response = json_object();
3697 		json_object_set_new(response, "videoroom", json_string("edited"));
3698 		json_object_set_new(response, "room", string_ids ? json_string(videoroom->room_id_str) : json_integer(videoroom->room_id));
3699 		json_object_set_new(response, "permanent", save ? json_true() : json_false());
3700 		/* Also notify event handlers */
3701 		if(notify_events && gateway->events_is_enabled()) {
3702 			json_t *info = json_object();
3703 			json_object_set_new(info, "event", json_string("edited"));
3704 			json_object_set_new(info, "room", string_ids ? json_string(videoroom->room_id_str) : json_integer(videoroom->room_id));
3705 			gateway->notify_event(&janus_videoroom_plugin, session ? session->handle : NULL, info);
3706 		}
3707 		goto prepare_response;
3708 	} else if(!strcasecmp(request_text, "destroy")) {
3709 		JANUS_LOG(LOG_VERB, "Attempt to destroy an existing VideoRoom room\n");
3710 		if(!string_ids) {
3711 			JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
3712 				error_code, error_cause, TRUE,
3713 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
3714 		} else {
3715 			JANUS_VALIDATE_JSON_OBJECT(root, roomstr_parameters,
3716 				error_code, error_cause, TRUE,
3717 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
3718 		}
3719 		if(error_code != 0)
3720 			goto prepare_response;
3721 		JANUS_VALIDATE_JSON_OBJECT(root, destroy_parameters,
3722 			error_code, error_cause, TRUE,
3723 			JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
3724 		if(error_code != 0)
3725 			goto prepare_response;
3726 		json_t *room = json_object_get(root, "room");
3727 		json_t *permanent = json_object_get(root, "permanent");
3728 		gboolean save = permanent ? json_is_true(permanent) : FALSE;
3729 		if(save && config == NULL) {
3730 			JANUS_LOG(LOG_ERR, "No configuration file, can't destroy room permanently\n");
3731 			error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR;
3732 			g_snprintf(error_cause, 512, "No configuration file, can't destroy room permanently");
3733 			goto prepare_response;
3734 		}
3735 		guint64 room_id = 0;
3736 		char room_id_num[30], *room_id_str = NULL;
3737 		if(!string_ids) {
3738 			room_id = json_integer_value(room);
3739 			g_snprintf(room_id_num, sizeof(room_id_num), "%"SCNu64, room_id);
3740 			room_id_str = room_id_num;
3741 		} else {
3742 			room_id_str = (char *)json_string_value(room);
3743 		}
3744 		janus_mutex_lock(&rooms_mutex);
3745 		janus_videoroom *videoroom = NULL;
3746 		error_code = janus_videoroom_access_room(root, TRUE, FALSE, &videoroom, error_cause, sizeof(error_cause));
3747 		if(error_code != 0) {
3748 			janus_mutex_unlock(&rooms_mutex);
3749 			goto prepare_response;
3750 		}
3751 		/* Remove room, but add a reference until we're done */
3752 		janus_refcount_increase(&videoroom->ref);
3753 		g_hash_table_remove(rooms, string_ids ? (gpointer)room_id_str : (gpointer)&room_id);
3754 		/* Notify all participants that the fun is over, and that they'll be kicked */
3755 		JANUS_LOG(LOG_VERB, "Notifying all participants\n");
3756 		json_t *destroyed = json_object();
3757 		json_object_set_new(destroyed, "videoroom", json_string("destroyed"));
3758 		json_object_set_new(destroyed, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
3759 		GHashTableIter iter;
3760 		gpointer value;
3761 		janus_mutex_lock(&videoroom->mutex);
3762 		g_hash_table_iter_init(&iter, videoroom->participants);
3763 		while (g_hash_table_iter_next(&iter, NULL, &value)) {
3764 			janus_videoroom_publisher *p = value;
3765 			if(p && !g_atomic_int_get(&p->destroyed) && p->session && p->room) {
3766 				g_clear_pointer(&p->room, janus_videoroom_room_dereference);
3767 				/* Notify the user we're going to destroy the room... */
3768 				int ret = gateway->push_event(p->session->handle, &janus_videoroom_plugin, NULL, destroyed, NULL);
3769 				JANUS_LOG(LOG_VERB, "  >> %d (%s)\n", ret, janus_get_api_error(ret));
3770 				/* ... and then ask the core to close the PeerConnection */
3771 				gateway->close_pc(p->session->handle);
3772 			}
3773 		}
3774 		json_decref(destroyed);
3775 		janus_mutex_unlock(&videoroom->mutex);
3776 		/* Also notify event handlers */
3777 		if(notify_events && gateway->events_is_enabled()) {
3778 			json_t *info = json_object();
3779 			json_object_set_new(info, "event", json_string("destroyed"));
3780 			json_object_set_new(info, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
3781 			gateway->notify_event(&janus_videoroom_plugin, session ? session->handle : NULL, info);
3782 		}
3783 		janus_mutex_unlock(&rooms_mutex);
3784 		if(save) {
3785 			/* This change is permanent: save to the configuration file too
3786 			 * FIXME: We should check if anything fails... */
3787 			JANUS_LOG(LOG_VERB, "Destroying room %s permanently in config file\n", room_id_str);
3788 			janus_mutex_lock(&config_mutex);
3789 			char cat[BUFSIZ];
3790 			/* The room ID is the category (prefixed by "room-") */
3791 			g_snprintf(cat, BUFSIZ, "room-%s", room_id_str);
3792 			janus_config_remove(config, NULL, cat);
3793 			/* Save modified configuration */
3794 			if(janus_config_save(config, config_folder, JANUS_VIDEOROOM_PACKAGE) < 0)
3795 				save = FALSE;	/* This will notify the user the room destruction is not permanent */
3796 			janus_mutex_unlock(&config_mutex);
3797 		}
3798 		janus_refcount_decrease(&videoroom->ref);
3799 		/* Done */
3800 		response = json_object();
3801 		json_object_set_new(response, "videoroom", json_string("destroyed"));
3802 		json_object_set_new(response, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
3803 		json_object_set_new(response, "permanent", save ? json_true() : json_false());
3804 		goto prepare_response;
3805 	} else if(!strcasecmp(request_text, "list")) {
3806 		/* List all rooms (but private ones) and their details (except for the secret, of course...) */
3807 		JANUS_LOG(LOG_VERB, "Getting the list of VideoRoom rooms\n");
3808 		gboolean lock_room_list = TRUE;
3809 		if(admin_key != NULL) {
3810 			json_t *admin_key_json = json_object_get(root, "admin_key");
3811 			/* Verify admin_key if it was provided */
3812 			if(admin_key_json != NULL && json_is_string(admin_key_json) && strlen(json_string_value(admin_key_json)) > 0) {
3813 				JANUS_CHECK_SECRET(admin_key, root, "admin_key", error_code, error_cause,
3814 					JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT, JANUS_VIDEOROOM_ERROR_UNAUTHORIZED);
3815 				if(error_code != 0) {
3816 					goto prepare_response;
3817 				} else {
3818 					lock_room_list = FALSE;
3819 				}
3820 			}
3821 		}
3822 		json_t *list = json_array();
3823 		janus_mutex_lock(&rooms_mutex);
3824 		GHashTableIter iter;
3825 		gpointer value;
3826 		g_hash_table_iter_init(&iter, rooms);
3827 		while(g_hash_table_iter_next(&iter, NULL, &value)) {
3828 			janus_videoroom *room = value;
3829 			if(!room)
3830 				continue;
3831 			janus_refcount_increase(&room->ref);
3832 			if(room->is_private && lock_room_list) {
3833 				/* Skip private room if no valid admin_key was provided */
3834 				JANUS_LOG(LOG_VERB, "Skipping private room '%s'\n", room->room_name);
3835 				janus_refcount_decrease(&room->ref);
3836 				continue;
3837 			}
3838 			if(!g_atomic_int_get(&room->destroyed)) {
3839 				json_t *rl = json_object();
3840 				json_object_set_new(rl, "room", string_ids ? json_string(room->room_id_str) : json_integer(room->room_id));
3841 				json_object_set_new(rl, "description", json_string(room->room_name));
3842 				json_object_set_new(rl, "pin_required", room->room_pin ? json_true() : json_false());
3843 				json_object_set_new(rl, "is_private", room->is_private ? json_true() : json_false());
3844 				json_object_set_new(rl, "max_publishers", json_integer(room->max_publishers));
3845 				json_object_set_new(rl, "bitrate", json_integer(room->bitrate));
3846 				if(room->bitrate_cap)
3847 					json_object_set_new(rl, "bitrate_cap", json_true());
3848 				json_object_set_new(rl, "fir_freq", json_integer(room->fir_freq));
3849 				json_object_set_new(rl, "require_pvtid", room->require_pvtid ? json_true() : json_false());
3850 				json_object_set_new(rl, "require_e2ee", room->require_e2ee ? json_true() : json_false());
3851 				json_object_set_new(rl, "notify_joining", room->notify_joining ? json_true() : json_false());
3852 				char audio_codecs[100];
3853 				char video_codecs[100];
3854 				janus_videoroom_codecstr(room, audio_codecs, video_codecs, sizeof(audio_codecs), ",");
3855 				json_object_set_new(rl, "audiocodec", json_string(audio_codecs));
3856 				json_object_set_new(rl, "videocodec", json_string(video_codecs));
3857 				if(room->do_opusfec)
3858 					json_object_set_new(rl, "opus_fec", json_true());
3859 				if(room->do_opusdtx)
3860 					json_object_set_new(rl, "opus_dtx", json_true());
3861 				if(room->do_svc)
3862 					json_object_set_new(rl, "video_svc", json_true());
3863 				json_object_set_new(rl, "record", room->record ? json_true() : json_false());
3864 				json_object_set_new(rl, "rec_dir", json_string(room->rec_dir));
3865 				json_object_set_new(rl, "lock_record", room->lock_record ? json_true() : json_false());
3866 				json_object_set_new(rl, "num_participants", json_integer(g_hash_table_size(room->participants)));
3867 				json_object_set_new(rl, "audiolevel_ext", room->audiolevel_ext ? json_true() : json_false());
3868 				json_object_set_new(rl, "audiolevel_event", room->audiolevel_event ? json_true() : json_false());
3869 				if(room->audiolevel_event) {
3870 					json_object_set_new(rl, "audio_active_packets", json_integer(room->audio_active_packets));
3871 					json_object_set_new(rl, "audio_level_average", json_integer(room->audio_level_average));
3872 				}
3873 				json_object_set_new(rl, "videoorient_ext", room->videoorient_ext ? json_true() : json_false());
3874 				json_object_set_new(rl, "playoutdelay_ext", room->playoutdelay_ext ? json_true() : json_false());
3875 				json_object_set_new(rl, "transport_wide_cc_ext", room->transport_wide_cc_ext ? json_true() : json_false());
3876 				json_array_append_new(list, rl);
3877 			}
3878 			janus_refcount_decrease(&room->ref);
3879 		}
3880 		janus_mutex_unlock(&rooms_mutex);
3881 		response = json_object();
3882 		json_object_set_new(response, "videoroom", json_string("success"));
3883 		json_object_set_new(response, "list", list);
3884 		goto prepare_response;
3885 	} else if(!strcasecmp(request_text, "rtp_forward")) {
3886 		JANUS_VALIDATE_JSON_OBJECT(root, rtp_forward_parameters,
3887 			error_code, error_cause, TRUE,
3888 			JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
3889 		if(error_code != 0)
3890 			goto prepare_response;
3891 		if(!string_ids) {
3892 			JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
3893 				error_code, error_cause, TRUE,
3894 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
3895 		} else {
3896 			JANUS_VALIDATE_JSON_OBJECT(root, roomstr_parameters,
3897 				error_code, error_cause, TRUE,
3898 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
3899 		}
3900 		if(error_code != 0)
3901 			goto prepare_response;
3902 		if(!string_ids) {
3903 			JANUS_VALIDATE_JSON_OBJECT(root, pid_parameters,
3904 				error_code, error_cause, TRUE,
3905 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
3906 		} else {
3907 			JANUS_VALIDATE_JSON_OBJECT(root, pidstr_parameters,
3908 				error_code, error_cause, TRUE,
3909 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
3910 		}
3911 		if(error_code != 0)
3912 			goto prepare_response;
3913 		if(lock_rtpfwd && admin_key != NULL) {
3914 			/* An admin key was specified: make sure it was provided, and that it's valid */
3915 			JANUS_VALIDATE_JSON_OBJECT(root, adminkey_parameters,
3916 				error_code, error_cause, TRUE,
3917 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
3918 			if(error_code != 0)
3919 				goto prepare_response;
3920 			JANUS_CHECK_SECRET(admin_key, root, "admin_key", error_code, error_cause,
3921 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT, JANUS_VIDEOROOM_ERROR_UNAUTHORIZED);
3922 			if(error_code != 0)
3923 				goto prepare_response;
3924 		}
3925 		json_t *room = json_object_get(root, "room");
3926 		json_t *pub_id = json_object_get(root, "publisher_id");
3927 		int video_port[3] = {-1, -1, -1}, video_rtcp_port = -1, video_pt[3] = {0, 0, 0};
3928 		uint32_t video_ssrc[3] = {0, 0, 0};
3929 		int audio_port = -1, audio_rtcp_port = -1, audio_pt = 0;
3930 		uint32_t audio_ssrc = 0;
3931 		int data_port = -1;
3932 		int srtp_suite = 0;
3933 		const char *srtp_crypto = NULL;
3934 		/* There may be multiple target video ports (e.g., publisher simulcasting) */
3935 		json_t *vid_port = json_object_get(root, "video_port");
3936 		if(vid_port) {
3937 			video_port[0] = json_integer_value(vid_port);
3938 			json_t *pt = json_object_get(root, "video_pt");
3939 			if(pt)
3940 				video_pt[0] = json_integer_value(pt);
3941 			json_t *ssrc = json_object_get(root, "video_ssrc");
3942 			if(ssrc)
3943 				video_ssrc[0] = json_integer_value(ssrc);
3944 		}
3945 		vid_port = json_object_get(root, "video_port_2");
3946 		if(vid_port) {
3947 			video_port[1] = json_integer_value(vid_port);
3948 			json_t *pt = json_object_get(root, "video_pt_2");
3949 			if(pt)
3950 				video_pt[1] = json_integer_value(pt);
3951 			json_t *ssrc = json_object_get(root, "video_ssrc_2");
3952 			if(ssrc)
3953 				video_ssrc[1] = json_integer_value(ssrc);
3954 		}
3955 		vid_port = json_object_get(root, "video_port_3");
3956 		if(vid_port) {
3957 			video_port[2] = json_integer_value(vid_port);
3958 			json_t *pt = json_object_get(root, "video_pt_3");
3959 			if(pt)
3960 				video_pt[2] = json_integer_value(pt);
3961 			json_t *ssrc = json_object_get(root, "video_ssrc_3");
3962 			if(ssrc)
3963 				video_ssrc[2] = json_integer_value(ssrc);
3964 		}
3965 		json_t *vid_rtcp_port = json_object_get(root, "video_rtcp_port");
3966 		if(vid_rtcp_port)
3967 			video_rtcp_port = json_integer_value(vid_rtcp_port);
3968 		/* Audio target */
3969 		json_t *au_port = json_object_get(root, "audio_port");
3970 		if(au_port) {
3971 			audio_port = json_integer_value(au_port);
3972 			json_t *pt = json_object_get(root, "audio_pt");
3973 			if(pt)
3974 				audio_pt = json_integer_value(pt);
3975 			json_t *ssrc = json_object_get(root, "audio_ssrc");
3976 			if(ssrc)
3977 				audio_ssrc = json_integer_value(ssrc);
3978 		}
3979 		json_t *au_rtcp_port = json_object_get(root, "audio_rtcp_port");
3980 		if(au_rtcp_port)
3981 			audio_rtcp_port = json_integer_value(au_rtcp_port);
3982 		/* Data target */
3983 		json_t *d_port = json_object_get(root, "data_port");
3984 		if(d_port) {
3985 			data_port = json_integer_value(d_port);
3986 		}
3987 		json_t *json_host = json_object_get(root, "host");
3988 		json_t *json_host_family = json_object_get(root, "host_family");
3989 		const char *host_family = json_string_value(json_host_family);
3990 		int family = 0;
3991 		if(host_family) {
3992 			if(!strcasecmp(host_family, "ipv4")) {
3993 				family = AF_INET;
3994 			} else if(!strcasecmp(host_family, "ipv6")) {
3995 				family = AF_INET6;
3996 			} else {
3997 				JANUS_LOG(LOG_ERR, "Unsupported protocol family (%s)\n", host_family);
3998 				error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
3999 				g_snprintf(error_cause, 512, "Unsupported protocol family (%s)", host_family);
4000 				goto prepare_response;
4001 			}
4002 		}
4003 		/* Do we need to forward multiple simulcast streams to a single endpoint? */
4004 		gboolean simulcast = FALSE;
4005 		if(json_object_get(root, "simulcast") != NULL)
4006 			simulcast = json_is_true(json_object_get(root, "simulcast"));
4007 		if(simulcast) {
4008 			/* We do, disable the other video ports if they were requested */
4009 			video_port[1] = -1;
4010 			video_port[2] = -1;
4011 		}
4012 		/* Besides, we may need to SRTP-encrypt this stream */
4013 		json_t *s_suite = json_object_get(root, "srtp_suite");
4014 		json_t *s_crypto = json_object_get(root, "srtp_crypto");
4015 		if(s_suite && s_crypto) {
4016 			srtp_suite = json_integer_value(s_suite);
4017 			if(srtp_suite != 32 && srtp_suite != 80) {
4018 				JANUS_LOG(LOG_ERR, "Invalid SRTP suite (%d)\n", srtp_suite);
4019 				error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
4020 				g_snprintf(error_cause, 512, "Invalid SRTP suite (%d)", srtp_suite);
4021 				goto prepare_response;
4022 			}
4023 			srtp_crypto = json_string_value(s_crypto);
4024 		}
4025 		guint64 room_id = 0;
4026 		char room_id_num[30], *room_id_str = NULL;
4027 		if(!string_ids) {
4028 			room_id = json_integer_value(room);
4029 			g_snprintf(room_id_num, sizeof(room_id_num), "%"SCNu64, room_id);
4030 			room_id_str = room_id_num;
4031 		} else {
4032 			room_id_str = (char *)json_string_value(room);
4033 		}
4034 		guint64 publisher_id = 0;
4035 		char publisher_id_num[30], *publisher_id_str = NULL;
4036 		if(!string_ids) {
4037 			publisher_id = json_integer_value(pub_id);
4038 			g_snprintf(publisher_id_num, sizeof(publisher_id_num), "%"SCNu64, publisher_id);
4039 			publisher_id_str = publisher_id_num;
4040 		} else {
4041 			publisher_id_str = (char *)json_string_value(pub_id);
4042 		}
4043 		const char *host = json_string_value(json_host), *resolved_host = NULL;
4044 		/* Check if we need to resolve this host address */
4045 		struct addrinfo *res = NULL, *start = NULL;
4046 		janus_network_address addr;
4047 		janus_network_address_string_buffer addr_buf;
4048 		struct addrinfo hints;
4049 		memset(&hints, 0, sizeof(hints));
4050 		if(family != 0)
4051 			hints.ai_family = family;
4052 		if(getaddrinfo(host, NULL, family != 0 ? &hints : NULL, &res) == 0) {
4053 			start = res;
4054 			while(res != NULL) {
4055 				if(janus_network_address_from_sockaddr(res->ai_addr, &addr) == 0 &&
4056 						janus_network_address_to_string_buffer(&addr, &addr_buf) == 0) {
4057 					/* Resolved */
4058 					resolved_host = janus_network_address_string_from_buffer(&addr_buf);
4059 					freeaddrinfo(start);
4060 					start = NULL;
4061 					break;
4062 				}
4063 				res = res->ai_next;
4064 			}
4065 		}
4066 		if(resolved_host == NULL) {
4067 			if(start)
4068 				freeaddrinfo(start);
4069 			JANUS_LOG(LOG_ERR, "Could not resolve address (%s)...\n", host);
4070 			error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
4071 			g_snprintf(error_cause, 512, "Could not resolve address (%s)...", host);
4072 			goto prepare_response;
4073 		}
4074 		host = resolved_host;
4075 		janus_mutex_lock(&rooms_mutex);
4076 		janus_videoroom *videoroom = NULL;
4077 		error_code = janus_videoroom_access_room(root, TRUE, FALSE, &videoroom, error_cause, sizeof(error_cause));
4078 		if(error_code != 0) {
4079 			janus_mutex_unlock(&rooms_mutex);
4080 			goto prepare_response;
4081 		}
4082 		janus_refcount_increase(&videoroom->ref);
4083 		janus_mutex_unlock(&rooms_mutex);
4084 		janus_mutex_lock(&videoroom->mutex);
4085 		janus_videoroom_publisher *publisher = g_hash_table_lookup(videoroom->participants,
4086 			string_ids ? (gpointer)publisher_id_str : (gpointer)&publisher_id);
4087 		if(publisher == NULL) {
4088 			janus_mutex_unlock(&videoroom->mutex);
4089 			janus_refcount_decrease(&videoroom->ref);
4090 			JANUS_LOG(LOG_ERR, "No such publisher (%s)\n", publisher_id_str);
4091 			error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_FEED;
4092 			g_snprintf(error_cause, 512, "No such feed (%s)", publisher_id_str);
4093 			goto prepare_response;
4094 		}
4095 		janus_refcount_increase(&publisher->ref);	/* This is just to handle the request for now */
4096 		if(publisher->udp_sock <= 0) {
4097 			publisher->udp_sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
4098 			int v6only = 0;
4099 			if(publisher->udp_sock <= 0 ||
4100 					setsockopt(publisher->udp_sock, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)) != 0) {
4101 				janus_refcount_decrease(&publisher->ref);
4102 				janus_mutex_unlock(&videoroom->mutex);
4103 				janus_refcount_decrease(&videoroom->ref);
4104 				JANUS_LOG(LOG_ERR, "Could not open UDP socket for RTP stream for publisher (%s)\n", publisher_id_str);
4105 				error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR;
4106 				g_snprintf(error_cause, 512, "Could not open UDP socket for RTP stream");
4107 				goto prepare_response;
4108 			}
4109 		}
4110 		guint32 audio_handle = 0;
4111 		guint32 video_handle[3] = {0, 0, 0};
4112 		guint32 data_handle = 0;
4113 		if(audio_port > 0) {
4114 			audio_handle = janus_videoroom_rtp_forwarder_add_helper(publisher, host, audio_port, audio_rtcp_port, audio_pt, audio_ssrc,
4115 				FALSE, srtp_suite, srtp_crypto, 0, FALSE, FALSE);
4116 		}
4117 		if(video_port[0] > 0) {
4118 			video_handle[0] = janus_videoroom_rtp_forwarder_add_helper(publisher, host, video_port[0], video_rtcp_port, video_pt[0], video_ssrc[0],
4119 				simulcast, srtp_suite, srtp_crypto, 0, TRUE, FALSE);
4120 		}
4121 		if(video_port[1] > 0) {
4122 			video_handle[1] = janus_videoroom_rtp_forwarder_add_helper(publisher, host, video_port[1], 0, video_pt[1], video_ssrc[1],
4123 				FALSE, srtp_suite, srtp_crypto, 1, TRUE, FALSE);
4124 		}
4125 		if(video_port[2] > 0) {
4126 			video_handle[2] = janus_videoroom_rtp_forwarder_add_helper(publisher, host, video_port[2], 0, video_pt[2], video_ssrc[2],
4127 				FALSE, srtp_suite, srtp_crypto, 2, TRUE, FALSE);
4128 		}
4129 		if(data_port > 0) {
4130 			data_handle = janus_videoroom_rtp_forwarder_add_helper(publisher, host, data_port, 0, 0, 0, FALSE, 0, NULL, 0, FALSE, TRUE);
4131 		}
4132 		janus_mutex_unlock(&videoroom->mutex);
4133 		response = json_object();
4134 		json_t *rtp_stream = json_object();
4135 		if(audio_handle > 0) {
4136 			json_object_set_new(rtp_stream, "audio_stream_id", json_integer(audio_handle));
4137 			json_object_set_new(rtp_stream, "audio", json_integer(audio_port));
4138 			if(audio_rtcp_port > 0)
4139 				json_object_set_new(rtp_stream, "audio_rtcp", json_integer(audio_rtcp_port));
4140 			/* Also notify event handlers */
4141 			if(notify_events && gateway->events_is_enabled()) {
4142 				json_t *info = json_object();
4143 				json_object_set_new(info, "event", json_string("rtp_forward"));
4144 				json_object_set_new(info, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
4145 				json_object_set_new(info, "publisher_id", string_ids ? json_string(publisher_id_str) : json_integer(publisher_id));
4146 				json_object_set_new(info, "media", json_string("audio"));
4147 				json_object_set_new(info, "codec", json_string(janus_audiocodec_name(publisher->acodec)));
4148 				json_object_set_new(info, "stream_id", json_integer(audio_handle));
4149 				json_object_set_new(info, "host", json_string(host));
4150 				json_object_set_new(info, "port", json_integer(audio_port));
4151 				gateway->notify_event(&janus_videoroom_plugin, NULL, info);
4152 			}
4153 		}
4154 		if(video_handle[0] > 0 || video_handle[1] > 0 || video_handle[2] > 0) {
4155 			janus_videoroom_reqpli(publisher, "New RTP forward publisher");
4156 			/* Done */
4157 			if(video_handle[0] > 0) {
4158 				json_object_set_new(rtp_stream, "video_stream_id", json_integer(video_handle[0]));
4159 				json_object_set_new(rtp_stream, "video", json_integer(video_port[0]));
4160 				if(video_rtcp_port > 0)
4161 					json_object_set_new(rtp_stream, "video_rtcp", json_integer(video_rtcp_port));
4162 				/* Also notify event handlers */
4163 				if(notify_events && gateway->events_is_enabled()) {
4164 					json_t *info = json_object();
4165 					json_object_set_new(info, "event", json_string("rtp_forward"));
4166 					json_object_set_new(info, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
4167 					json_object_set_new(info, "publisher_id", string_ids ? json_string(publisher_id_str) : json_integer(publisher_id));
4168 					json_object_set_new(info, "media", json_string("video"));
4169 					json_object_set_new(info, "codec", json_string(janus_videocodec_name(publisher->vcodec)));
4170 					if(video_handle[1] > 0 || video_handle[2] > 0)
4171 						json_object_set_new(info, "video_substream", json_integer(0));
4172 					json_object_set_new(info, "stream_id", json_integer(video_handle[0]));
4173 					json_object_set_new(info, "host", json_string(host));
4174 					json_object_set_new(info, "port", json_integer(video_port[0]));
4175 					gateway->notify_event(&janus_videoroom_plugin, NULL, info);
4176 				}
4177 			}
4178 			if(video_handle[1] > 0) {
4179 				json_object_set_new(rtp_stream, "video_stream_id_2", json_integer(video_handle[1]));
4180 				json_object_set_new(rtp_stream, "video_2", json_integer(video_port[1]));
4181 				/* Also notify event handlers */
4182 				if(notify_events && gateway->events_is_enabled()) {
4183 					json_t *info = json_object();
4184 					json_object_set_new(info, "event", json_string("rtp_forward"));
4185 					json_object_set_new(info, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
4186 					json_object_set_new(info, "publisher_id", string_ids ? json_string(publisher_id_str) : json_integer(publisher_id));
4187 					json_object_set_new(info, "media", json_string("video"));
4188 					json_object_set_new(info, "codec", json_string(janus_videocodec_name(publisher->vcodec)));
4189 					json_object_set_new(info, "video_substream", json_integer(1));
4190 					json_object_set_new(info, "stream_id", json_integer(video_handle[1]));
4191 					json_object_set_new(info, "host", json_string(host));
4192 					json_object_set_new(info, "port", json_integer(video_port[1]));
4193 					gateway->notify_event(&janus_videoroom_plugin, NULL, info);
4194 				}
4195 			}
4196 			if(video_handle[2] > 0) {
4197 				json_object_set_new(rtp_stream, "video_stream_id_3", json_integer(video_handle[2]));
4198 				json_object_set_new(rtp_stream, "video_3", json_integer(video_port[2]));
4199 				/* Also notify event handlers */
4200 				if(notify_events && gateway->events_is_enabled()) {
4201 					json_t *info = json_object();
4202 					json_object_set_new(info, "event", json_string("rtp_forward"));
4203 					json_object_set_new(info, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
4204 					json_object_set_new(info, "publisher_id", string_ids ? json_string(publisher_id_str) : json_integer(publisher_id));
4205 					json_object_set_new(info, "media", json_string("video"));
4206 					json_object_set_new(info, "codec", json_string(janus_videocodec_name(publisher->vcodec)));
4207 					json_object_set_new(info, "video_substream", json_integer(2));
4208 					json_object_set_new(info, "stream_id", json_integer(video_handle[2]));
4209 					json_object_set_new(info, "host", json_string(host));
4210 					json_object_set_new(info, "port", json_integer(video_port[2]));
4211 					gateway->notify_event(&janus_videoroom_plugin, NULL, info);
4212 				}
4213 			}
4214 		}
4215 		if(data_handle > 0) {
4216 			json_object_set_new(rtp_stream, "data_stream_id", json_integer(data_handle));
4217 			json_object_set_new(rtp_stream, "data", json_integer(data_port));
4218 			/* Also notify event handlers */
4219 			if(notify_events && gateway->events_is_enabled()) {
4220 				json_t *info = json_object();
4221 				json_object_set_new(info, "event", json_string("rtp_forward"));
4222 				json_object_set_new(info, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
4223 				json_object_set_new(info, "publisher_id", string_ids ? json_string(publisher_id_str) : json_integer(publisher_id));
4224 				json_object_set_new(info, "media", json_string("data"));
4225 				json_object_set_new(info, "stream_id", json_integer(data_handle));
4226 				json_object_set_new(info, "host", json_string(host));
4227 				json_object_set_new(info, "port", json_integer(data_port));
4228 				gateway->notify_event(&janus_videoroom_plugin, NULL, info);
4229 			}
4230 		}
4231 		/* These two unrefs are related to the message handling */
4232 		janus_refcount_decrease(&publisher->ref);
4233 		janus_refcount_decrease(&videoroom->ref);
4234 		json_object_set_new(rtp_stream, "host", json_string(host));
4235 		json_object_set_new(response, "publisher_id", string_ids ? json_string(publisher_id_str) : json_integer(publisher_id));
4236 		json_object_set_new(response, "rtp_stream", rtp_stream);
4237 		json_object_set_new(response, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
4238 		json_object_set_new(response, "videoroom", json_string("rtp_forward"));
4239 		goto prepare_response;
4240 	} else if(!strcasecmp(request_text, "stop_rtp_forward")) {
4241 		if(!string_ids) {
4242 			JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
4243 				error_code, error_cause, TRUE,
4244 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4245 		} else {
4246 			JANUS_VALIDATE_JSON_OBJECT(root, roomstr_parameters,
4247 				error_code, error_cause, TRUE,
4248 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4249 		}
4250 		if(error_code != 0)
4251 			goto prepare_response;
4252 		if(!string_ids) {
4253 			JANUS_VALIDATE_JSON_OBJECT(root, pid_parameters,
4254 				error_code, error_cause, TRUE,
4255 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4256 		} else {
4257 			JANUS_VALIDATE_JSON_OBJECT(root, pidstr_parameters,
4258 				error_code, error_cause, TRUE,
4259 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4260 		}
4261 		if(error_code != 0)
4262 			goto prepare_response;
4263 		JANUS_VALIDATE_JSON_OBJECT(root, stop_rtp_forward_parameters,
4264 			error_code, error_cause, TRUE,
4265 			JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4266 		if(error_code != 0)
4267 			goto prepare_response;
4268 		if(lock_rtpfwd && admin_key != NULL) {
4269 			/* An admin key was specified: make sure it was provided, and that it's valid */
4270 			JANUS_VALIDATE_JSON_OBJECT(root, adminkey_parameters,
4271 				error_code, error_cause, TRUE,
4272 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4273 			if(error_code != 0)
4274 				goto prepare_response;
4275 			JANUS_CHECK_SECRET(admin_key, root, "admin_key", error_code, error_cause,
4276 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT, JANUS_VIDEOROOM_ERROR_UNAUTHORIZED);
4277 			if(error_code != 0)
4278 				goto prepare_response;
4279 		}
4280 		json_t *room = json_object_get(root, "room");
4281 		json_t *pub_id = json_object_get(root, "publisher_id");
4282 		json_t *id = json_object_get(root, "stream_id");
4283 
4284 		guint64 room_id = 0;
4285 		char room_id_num[30], *room_id_str = NULL;
4286 		if(!string_ids) {
4287 			room_id = json_integer_value(room);
4288 			g_snprintf(room_id_num, sizeof(room_id_num), "%"SCNu64, room_id);
4289 			room_id_str = room_id_num;
4290 		} else {
4291 			room_id_str = (char *)json_string_value(room);
4292 		}
4293 		guint64 publisher_id = 0;
4294 		char publisher_id_num[30], *publisher_id_str = NULL;
4295 		if(!string_ids) {
4296 			publisher_id = json_integer_value(pub_id);
4297 			g_snprintf(publisher_id_num, sizeof(publisher_id_num), "%"SCNu64, publisher_id);
4298 			publisher_id_str = publisher_id_num;
4299 		} else {
4300 			publisher_id_str = (char *)json_string_value(pub_id);
4301 		}
4302 		guint32 stream_id = json_integer_value(id);
4303 		janus_mutex_lock(&rooms_mutex);
4304 		janus_videoroom *videoroom = NULL;
4305 		error_code = janus_videoroom_access_room(root, TRUE, FALSE, &videoroom, error_cause, sizeof(error_cause));
4306 		if(error_code != 0) {
4307 			janus_mutex_unlock(&rooms_mutex);
4308 			goto prepare_response;
4309 		}
4310 		janus_refcount_increase(&videoroom->ref);
4311 		janus_mutex_unlock(&rooms_mutex);
4312 		janus_mutex_lock(&videoroom->mutex);
4313 		janus_videoroom_publisher *publisher = g_hash_table_lookup(videoroom->participants,
4314 			string_ids ? (gpointer)publisher_id_str : (gpointer)&publisher_id);
4315 		if(publisher == NULL) {
4316 			janus_mutex_unlock(&videoroom->mutex);
4317 			janus_refcount_decrease(&videoroom->ref);
4318 			JANUS_LOG(LOG_ERR, "No such publisher (%s)\n", publisher_id_str);
4319 			error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_FEED;
4320 			g_snprintf(error_cause, 512, "No such feed (%s)", publisher_id_str);
4321 			goto prepare_response;
4322 		}
4323 		janus_refcount_increase(&publisher->ref);	/* Just to handle the message now */
4324 		janus_mutex_lock(&publisher->rtp_forwarders_mutex);
4325 		if(!g_hash_table_remove(publisher->rtp_forwarders, GUINT_TO_POINTER(stream_id))) {
4326 			janus_mutex_unlock(&publisher->rtp_forwarders_mutex);
4327 			janus_refcount_decrease(&publisher->ref);
4328 			janus_mutex_unlock(&videoroom->mutex);
4329 			janus_refcount_decrease(&videoroom->ref);
4330 			JANUS_LOG(LOG_ERR, "No such stream (%"SCNu32")\n", stream_id);
4331 			error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_FEED;
4332 			g_snprintf(error_cause, 512, "No such stream (%"SCNu32")", stream_id);
4333 			goto prepare_response;
4334 		}
4335 		janus_mutex_unlock(&publisher->rtp_forwarders_mutex);
4336 		janus_refcount_decrease(&publisher->ref);
4337 		janus_mutex_unlock(&videoroom->mutex);
4338 		janus_refcount_decrease(&videoroom->ref);
4339 		response = json_object();
4340 		json_object_set_new(response, "videoroom", json_string("stop_rtp_forward"));
4341 		json_object_set_new(response, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
4342 		json_object_set_new(response, "publisher_id", string_ids ? json_string(publisher_id_str) : json_integer(publisher_id));
4343 		json_object_set_new(response, "stream_id", json_integer(stream_id));
4344 		/* Also notify event handlers */
4345 		if(notify_events && gateway->events_is_enabled()) {
4346 			json_t *info = json_object();
4347 			json_object_set_new(info, "event", json_string("stop_rtp_forward"));
4348 			json_object_set_new(info, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
4349 			json_object_set_new(info, "publisher_id", string_ids ? json_string(publisher_id_str) : json_integer(publisher_id));
4350 			json_object_set_new(info, "stream_id", json_integer(stream_id));
4351 			gateway->notify_event(&janus_videoroom_plugin, NULL, info);
4352 		}
4353 		goto prepare_response;
4354 	} else if(!strcasecmp(request_text, "exists")) {
4355 		/* Check whether a given room exists or not, returns true/false */
4356 		if(!string_ids) {
4357 			JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
4358 				error_code, error_cause, TRUE,
4359 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4360 		} else {
4361 			JANUS_VALIDATE_JSON_OBJECT(root, roomstr_parameters,
4362 				error_code, error_cause, TRUE,
4363 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4364 		}
4365 		if(error_code != 0)
4366 			goto prepare_response;
4367 		json_t *room = json_object_get(root, "room");
4368 		guint64 room_id = 0;
4369 		char room_id_num[30], *room_id_str = NULL;
4370 		if(!string_ids) {
4371 			room_id = json_integer_value(room);
4372 			g_snprintf(room_id_num, sizeof(room_id_num), "%"SCNu64, room_id);
4373 			room_id_str = room_id_num;
4374 		} else {
4375 			room_id_str = (char *)json_string_value(room);
4376 		}
4377 		janus_mutex_lock(&rooms_mutex);
4378 		gboolean room_exists = g_hash_table_contains(rooms, string_ids ? (gpointer)room_id_str : (gpointer)&room_id);
4379 		janus_mutex_unlock(&rooms_mutex);
4380 		response = json_object();
4381 		json_object_set_new(response, "videoroom", json_string("success"));
4382 		json_object_set_new(response, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
4383 		json_object_set_new(response, "exists", room_exists ? json_true() : json_false());
4384 		goto prepare_response;
4385 	} else if(!strcasecmp(request_text, "allowed")) {
4386 		JANUS_LOG(LOG_VERB, "Attempt to edit the list of allowed participants in an existing VideoRoom room\n");
4387 		if(!string_ids) {
4388 			JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
4389 				error_code, error_cause, TRUE,
4390 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4391 		} else {
4392 			JANUS_VALIDATE_JSON_OBJECT(root, roomstr_parameters,
4393 				error_code, error_cause, TRUE,
4394 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4395 		}
4396 		if(error_code != 0)
4397 			goto prepare_response;
4398 		JANUS_VALIDATE_JSON_OBJECT(root, allowed_parameters,
4399 			error_code, error_cause, TRUE,
4400 			JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4401 		if(error_code != 0)
4402 			goto prepare_response;
4403 		json_t *action = json_object_get(root, "action");
4404 		json_t *room = json_object_get(root, "room");
4405 		json_t *allowed = json_object_get(root, "allowed");
4406 		const char *action_text = json_string_value(action);
4407 		if(strcasecmp(action_text, "enable") && strcasecmp(action_text, "disable") &&
4408 				strcasecmp(action_text, "add") && strcasecmp(action_text, "remove")) {
4409 			JANUS_LOG(LOG_ERR, "Unsupported action '%s' (allowed)\n", action_text);
4410 			error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
4411 			g_snprintf(error_cause, 512, "Unsupported action '%s' (allowed)", action_text);
4412 			goto prepare_response;
4413 		}
4414 		guint64 room_id = 0;
4415 		char room_id_num[30], *room_id_str = NULL;
4416 		if(!string_ids) {
4417 			room_id = json_integer_value(room);
4418 			g_snprintf(room_id_num, sizeof(room_id_num), "%"SCNu64, room_id);
4419 			room_id_str = room_id_num;
4420 		} else {
4421 			room_id_str = (char *)json_string_value(room);
4422 		}
4423 		janus_mutex_lock(&rooms_mutex);
4424 		janus_videoroom *videoroom = NULL;
4425 		error_code = janus_videoroom_access_room(root, TRUE, FALSE, &videoroom, error_cause, sizeof(error_cause));
4426 		if(error_code != 0) {
4427 			janus_mutex_unlock(&rooms_mutex);
4428 			goto prepare_response;
4429 		}
4430 		janus_refcount_increase(&videoroom->ref);
4431 		janus_mutex_unlock(&rooms_mutex);
4432 		janus_mutex_lock(&videoroom->mutex);
4433 		/* A secret may be required for this action */
4434 		JANUS_CHECK_SECRET(videoroom->room_secret, root, "secret", error_code, error_cause,
4435 			JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT, JANUS_VIDEOROOM_ERROR_UNAUTHORIZED);
4436 		if(error_code != 0) {
4437 			janus_mutex_unlock(&videoroom->mutex);
4438 			janus_refcount_decrease(&videoroom->ref);
4439 			goto prepare_response;
4440 		}
4441 		if(!strcasecmp(action_text, "enable")) {
4442 			JANUS_LOG(LOG_VERB, "Enabling the check on allowed authorization tokens for room %s\n", room_id_str);
4443 			videoroom->check_allowed = TRUE;
4444 		} else if(!strcasecmp(action_text, "disable")) {
4445 			JANUS_LOG(LOG_VERB, "Disabling the check on allowed authorization tokens for room %s (free entry)\n", room_id_str);
4446 			videoroom->check_allowed = FALSE;
4447 		} else {
4448 			gboolean add = !strcasecmp(action_text, "add");
4449 			if(allowed) {
4450 				/* Make sure the "allowed" array only contains strings */
4451 				gboolean ok = TRUE;
4452 				if(json_array_size(allowed) > 0) {
4453 					size_t i = 0;
4454 					for(i=0; i<json_array_size(allowed); i++) {
4455 						json_t *a = json_array_get(allowed, i);
4456 						if(!a || !json_is_string(a)) {
4457 							ok = FALSE;
4458 							break;
4459 						}
4460 					}
4461 				}
4462 				if(!ok) {
4463 					janus_mutex_unlock(&videoroom->mutex);
4464 					JANUS_LOG(LOG_ERR, "Invalid element in the allowed array (not a string)\n");
4465 					error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
4466 					g_snprintf(error_cause, 512, "Invalid element in the allowed array (not a string)");
4467 					janus_refcount_decrease(&videoroom->ref);
4468 					goto prepare_response;
4469 				}
4470 				size_t i = 0;
4471 				for(i=0; i<json_array_size(allowed); i++) {
4472 					const char *token = json_string_value(json_array_get(allowed, i));
4473 					if(add) {
4474 						if(!g_hash_table_lookup(videoroom->allowed, token))
4475 							g_hash_table_insert(videoroom->allowed, g_strdup(token), GINT_TO_POINTER(TRUE));
4476 					} else {
4477 						g_hash_table_remove(videoroom->allowed, token);
4478 					}
4479 				}
4480 			}
4481 		}
4482 		/* Prepare response */
4483 		response = json_object();
4484 		json_object_set_new(response, "videoroom", json_string("success"));
4485 		json_object_set_new(response, "room", string_ids ? json_string(videoroom->room_id_str) : json_integer(videoroom->room_id));
4486 		json_t *list = json_array();
4487 		if(strcasecmp(action_text, "disable")) {
4488 			if(g_hash_table_size(videoroom->allowed) > 0) {
4489 				GHashTableIter iter;
4490 				gpointer key;
4491 				g_hash_table_iter_init(&iter, videoroom->allowed);
4492 				while(g_hash_table_iter_next(&iter, &key, NULL)) {
4493 					char *token = key;
4494 					json_array_append_new(list, json_string(token));
4495 				}
4496 			}
4497 			json_object_set_new(response, "allowed", list);
4498 		}
4499 		/* Done */
4500 		janus_mutex_unlock(&videoroom->mutex);
4501 		janus_refcount_decrease(&videoroom->ref);
4502 		JANUS_LOG(LOG_VERB, "VideoRoom room allowed list updated\n");
4503 		goto prepare_response;
4504 	} else if(!strcasecmp(request_text, "kick")) {
4505 		JANUS_LOG(LOG_VERB, "Attempt to kick a participant from an existing VideoRoom room\n");
4506 		if(!string_ids) {
4507 			JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
4508 				error_code, error_cause, TRUE,
4509 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4510 		} else {
4511 			JANUS_VALIDATE_JSON_OBJECT(root, roomstr_parameters,
4512 				error_code, error_cause, TRUE,
4513 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4514 		}
4515 		if(!string_ids) {
4516 			JANUS_VALIDATE_JSON_OBJECT(root, id_parameters,
4517 				error_code, error_cause, TRUE,
4518 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4519 		} else {
4520 			JANUS_VALIDATE_JSON_OBJECT(root, idstr_parameters,
4521 				error_code, error_cause, TRUE,
4522 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4523 		}
4524 		if(error_code != 0)
4525 			goto prepare_response;
4526 		JANUS_VALIDATE_JSON_OBJECT(root, kick_parameters,
4527 			error_code, error_cause, TRUE,
4528 			JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4529 		if(error_code != 0)
4530 			goto prepare_response;
4531 		json_t *room = json_object_get(root, "room");
4532 		json_t *id = json_object_get(root, "id");
4533 		guint64 room_id = 0;
4534 		char room_id_num[30], *room_id_str = NULL;
4535 		if(!string_ids) {
4536 			room_id = json_integer_value(room);
4537 			g_snprintf(room_id_num, sizeof(room_id_num), "%"SCNu64, room_id);
4538 			room_id_str = room_id_num;
4539 		} else {
4540 			room_id_str = (char *)json_string_value(room);
4541 		}
4542 		janus_mutex_lock(&rooms_mutex);
4543 		janus_videoroom *videoroom = NULL;
4544 		error_code = janus_videoroom_access_room(root, TRUE, FALSE, &videoroom, error_cause, sizeof(error_cause));
4545 		if(error_code != 0) {
4546 			janus_mutex_unlock(&rooms_mutex);
4547 			goto prepare_response;
4548 		}
4549 		janus_refcount_increase(&videoroom->ref);
4550 		janus_mutex_unlock(&rooms_mutex);
4551 		janus_mutex_lock(&videoroom->mutex);
4552 		/* A secret may be required for this action */
4553 		JANUS_CHECK_SECRET(videoroom->room_secret, root, "secret", error_code, error_cause,
4554 			JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT, JANUS_VIDEOROOM_ERROR_UNAUTHORIZED);
4555 		if(error_code != 0) {
4556 			janus_mutex_unlock(&videoroom->mutex);
4557 			janus_refcount_decrease(&videoroom->ref);
4558 			goto prepare_response;
4559 		}
4560 		guint64 user_id = 0;
4561 		char user_id_num[30], *user_id_str = NULL;
4562 		if(!string_ids) {
4563 			user_id = json_integer_value(id);
4564 			g_snprintf(user_id_num, sizeof(user_id_num), "%"SCNu64, user_id);
4565 			user_id_str = user_id_num;
4566 		} else {
4567 			user_id_str = (char *)json_string_value(id);
4568 		}
4569 		janus_videoroom_publisher *participant = g_hash_table_lookup(videoroom->participants,
4570 			string_ids ? (gpointer)user_id_str : (gpointer)&user_id);
4571 		if(participant == NULL) {
4572 			janus_mutex_unlock(&videoroom->mutex);
4573 			janus_refcount_decrease(&videoroom->ref);
4574 			JANUS_LOG(LOG_ERR, "No such user %s in room %s\n", user_id_str, room_id_str);
4575 			error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_FEED;
4576 			g_snprintf(error_cause, 512, "No such user %s in room %s", user_id_str, room_id_str);
4577 			goto prepare_response;
4578 		}
4579 		janus_refcount_increase(&participant->ref);
4580 		if(participant->kicked) {
4581 			/* Already kicked */
4582 			janus_mutex_unlock(&videoroom->mutex);
4583 			janus_refcount_decrease(&videoroom->ref);
4584 			janus_refcount_decrease(&participant->ref);
4585 			response = json_object();
4586 			json_object_set_new(response, "videoroom", json_string("success"));
4587 			/* Done */
4588 			goto prepare_response;
4589 		}
4590 		participant->kicked = TRUE;
4591 		g_atomic_int_set(&participant->session->started, 0);
4592 		participant->audio_active = FALSE;
4593 		participant->video_active = FALSE;
4594 		participant->data_active = FALSE;
4595 		/* Prepare an event for this */
4596 		json_t *kicked = json_object();
4597 		json_object_set_new(kicked, "videoroom", json_string("event"));
4598 		json_object_set_new(kicked, "room", string_ids ? json_string(participant->room_id_str) : json_integer(participant->room_id));
4599 		json_object_set_new(kicked, "leaving", json_string("ok"));
4600 		json_object_set_new(kicked, "reason", json_string("kicked"));
4601 		int ret = gateway->push_event(participant->session->handle, &janus_videoroom_plugin, NULL, kicked, NULL);
4602 		JANUS_LOG(LOG_VERB, "  >> %d (%s)\n", ret, janus_get_api_error(ret));
4603 		json_decref(kicked);
4604 		janus_mutex_unlock(&videoroom->mutex);
4605 		/* If this room requires valid private_id values, we can kick subscriptions too */
4606 		if(videoroom->require_pvtid && participant->subscriptions != NULL) {
4607 			/* Iterate on the subscriptions we know this user has */
4608 			janus_mutex_lock(&participant->own_subscriptions_mutex);
4609 			GSList *s = participant->subscriptions;
4610 			while(s) {
4611 				janus_videoroom_subscriber *subscriber = (janus_videoroom_subscriber *)s->data;
4612 				if(subscriber) {
4613 					subscriber->kicked = TRUE;
4614 					subscriber->audio = FALSE;
4615 					subscriber->video = FALSE;
4616 					subscriber->data = FALSE;
4617 					/* FIXME We should also close the PeerConnection, but we risk race conditions if we do it here,
4618 					 * so for now we mark the subscriber as kicked and prevent it from getting any media after this */
4619 				}
4620 				s = s->next;
4621 			}
4622 			janus_mutex_unlock(&participant->own_subscriptions_mutex);
4623 		}
4624 		/* This publisher is leaving, tell everybody */
4625 		janus_videoroom_leave_or_unpublish(participant, TRUE, TRUE);
4626 		/* Tell the core to tear down the PeerConnection, hangup_media will do the rest */
4627 		if(participant && !g_atomic_int_get(&participant->destroyed) && participant->session)
4628 			gateway->close_pc(participant->session->handle);
4629 		JANUS_LOG(LOG_INFO, "Kicked user %s from room %s\n", user_id_str, room_id_str);
4630 		/* Prepare response */
4631 		response = json_object();
4632 		json_object_set_new(response, "videoroom", json_string("success"));
4633 		/* Done */
4634 		janus_refcount_decrease(&videoroom->ref);
4635 		janus_refcount_decrease(&participant->ref);
4636 		goto prepare_response;
4637 	} else if(!strcasecmp(request_text, "moderate")) {
4638 		JANUS_LOG(LOG_VERB, "Attempt to moderate a participant as a moderator in an existing VideoRoom room\n");
4639 		if(!string_ids) {
4640 			JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
4641 				error_code, error_cause, TRUE,
4642 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4643 		} else {
4644 			JANUS_VALIDATE_JSON_OBJECT(root, roomstr_parameters,
4645 				error_code, error_cause, TRUE,
4646 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4647 		}
4648 		if(!string_ids) {
4649 			JANUS_VALIDATE_JSON_OBJECT(root, id_parameters,
4650 				error_code, error_cause, TRUE,
4651 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4652 		} else {
4653 			JANUS_VALIDATE_JSON_OBJECT(root, idstr_parameters,
4654 				error_code, error_cause, TRUE,
4655 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4656 		}
4657 		if(error_code != 0)
4658 			goto prepare_response;
4659 		JANUS_VALIDATE_JSON_OBJECT(root, moderate_parameters,
4660 			error_code, error_cause, TRUE,
4661 			JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4662 		if(error_code != 0)
4663 			goto prepare_response;
4664 		json_t *room = json_object_get(root, "room");
4665 		json_t *id = json_object_get(root, "id");
4666 		guint64 room_id = 0;
4667 		char room_id_num[30], *room_id_str = NULL;
4668 		if(!string_ids) {
4669 			room_id = json_integer_value(room);
4670 			g_snprintf(room_id_num, sizeof(room_id_num), "%"SCNu64, room_id);
4671 			room_id_str = room_id_num;
4672 		} else {
4673 			room_id_str = (char *)json_string_value(room);
4674 		}
4675 		janus_mutex_lock(&rooms_mutex);
4676 		janus_videoroom *videoroom = NULL;
4677 		error_code = janus_videoroom_access_room(root, TRUE, FALSE, &videoroom, error_cause, sizeof(error_cause));
4678 		if(error_code != 0) {
4679 			janus_mutex_unlock(&rooms_mutex);
4680 			goto prepare_response;
4681 		}
4682 		janus_refcount_increase(&videoroom->ref);
4683 		janus_mutex_unlock(&rooms_mutex);
4684 		janus_mutex_lock(&videoroom->mutex);
4685 		/* A secret may be required for this action */
4686 		JANUS_CHECK_SECRET(videoroom->room_secret, root, "secret", error_code, error_cause,
4687 			JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT, JANUS_VIDEOROOM_ERROR_UNAUTHORIZED);
4688 		if(error_code != 0) {
4689 			janus_mutex_unlock(&videoroom->mutex);
4690 			janus_refcount_decrease(&videoroom->ref);
4691 			goto prepare_response;
4692 		}
4693 		guint64 user_id = 0;
4694 		char user_id_num[30], *user_id_str = NULL;
4695 		if(!string_ids) {
4696 			user_id = json_integer_value(id);
4697 			g_snprintf(user_id_num, sizeof(user_id_num), "%"SCNu64, user_id);
4698 			user_id_str = user_id_num;
4699 		} else {
4700 			user_id_str = (char *)json_string_value(id);
4701 		}
4702 		janus_videoroom_publisher *participant = g_hash_table_lookup(videoroom->participants,
4703 			string_ids ? (gpointer)user_id_str : (gpointer)&user_id);
4704 		if(participant == NULL) {
4705 			janus_mutex_unlock(&videoroom->mutex);
4706 			janus_refcount_decrease(&videoroom->ref);
4707 			JANUS_LOG(LOG_ERR, "No such user %s in room %s\n", user_id_str, room_id_str);
4708 			error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_FEED;
4709 			g_snprintf(error_cause, 512, "No such user %s in room %s", user_id_str, room_id_str);
4710 			goto prepare_response;
4711 		}
4712 		janus_refcount_increase(&participant->ref);
4713 		/* Check if there's any media delivery to change */
4714 		json_t *audio = json_object_get(root, "mute_audio");
4715 		if(audio != NULL) {
4716 			gboolean audio_muted = json_is_true(audio);
4717 			if(participant->session && g_atomic_int_get(&participant->session->started) &&
4718 					!audio_muted && participant->audio_active && participant->audio_muted) {
4719 				/* Video was just resumed, try resetting the RTP headers for viewers */
4720 				janus_mutex_lock(&participant->subscribers_mutex);
4721 				GSList *ps = participant->subscribers;
4722 				while(ps) {
4723 					janus_videoroom_subscriber *l = (janus_videoroom_subscriber *)ps->data;
4724 					if(l)
4725 						l->context.v_seq_reset = TRUE;
4726 					ps = ps->next;
4727 				}
4728 				janus_mutex_unlock(&participant->subscribers_mutex);
4729 			}
4730 			participant->audio_muted = audio_muted;
4731 		}
4732 		json_t *video = json_object_get(root, "mute_video");
4733 		if(video != NULL) {
4734 			gboolean video_muted = json_is_true(video);
4735 			if(participant->session && g_atomic_int_get(&participant->session->started) &&
4736 					!video_muted && participant->video_active && participant->video_muted) {
4737 				/* Video was just resumed, try resetting the RTP headers for viewers */
4738 				janus_mutex_lock(&participant->subscribers_mutex);
4739 				GSList *ps = participant->subscribers;
4740 				while(ps) {
4741 					janus_videoroom_subscriber *l = (janus_videoroom_subscriber *)ps->data;
4742 					if(l)
4743 						l->context.v_seq_reset = TRUE;
4744 					ps = ps->next;
4745 				}
4746 				janus_mutex_unlock(&participant->subscribers_mutex);
4747 			}
4748 			participant->video_muted = video_muted;
4749 		}
4750 		json_t *data = json_object_get(root, "mute_data");
4751 		if(data != NULL) {
4752 			participant->data_muted = json_is_true(data);
4753 		}
4754 		/* If anything changed, prepare an event for this */
4755 		if(audio || video || data) {
4756 			json_t *event = json_object();
4757 			json_object_set_new(event, "videoroom", json_string("event"));
4758 			json_object_set_new(event, "room", string_ids ? json_string(participant->room_id_str) : json_integer(participant->room_id));
4759 			json_object_set_new(event, "id", string_ids ? json_string(participant->user_id_str) : json_integer(participant->user_id));
4760 			if(audio)
4761 				json_object_set_new(event, "audio-moderation", participant->audio_muted ? json_string("muted") : json_string("unmuted"));
4762 			if(video)
4763 				json_object_set_new(event, "video-moderation", participant->video_muted ? json_string("muted") : json_string("unmuted"));
4764 			if(data)
4765 				json_object_set_new(event, "data-moderation", participant->data_muted ? json_string("muted") : json_string("unmuted"));
4766 			/* Notify the speaker this event is related to as well */
4767 			janus_videoroom_notify_participants(participant, event, TRUE);
4768 			json_decref(event);
4769 			/* Also notify event handlers */
4770 			if(notify_events && gateway->events_is_enabled()) {
4771 				json_t *info = json_object();
4772 				json_object_set_new(info, "videoroom", json_string("moderated"));
4773 				json_object_set_new(info, "room", string_ids ? json_string(videoroom->room_id_str) : json_integer(videoroom->room_id));
4774 				json_object_set_new(info, "id", string_ids ? json_string(participant->user_id_str) : json_integer(participant->user_id));
4775 				if(audio)
4776 					json_object_set_new(info, "audio", participant->audio_muted ? json_string("muted") : json_string("unmuted"));
4777 				if(video)
4778 					json_object_set_new(info, "video", participant->video_muted ? json_string("muted") : json_string("unmuted"));
4779 				if(data)
4780 					json_object_set_new(info, "data", participant->data_muted ? json_string("muted") : json_string("unmuted"));
4781 				gateway->notify_event(&janus_videoroom_plugin, NULL, info);
4782 			}
4783 		}
4784 		janus_mutex_unlock(&videoroom->mutex);
4785 		/* Prepare response */
4786 		response = json_object();
4787 		json_object_set_new(response, "videoroom", json_string("success"));
4788 		/* Done */
4789 		janus_refcount_decrease(&videoroom->ref);
4790 		janus_refcount_decrease(&participant->ref);
4791 		goto prepare_response;
4792 	} else if(!strcasecmp(request_text, "listparticipants")) {
4793 		/* List all participants in a room, specifying whether they're publishers or just attendees */
4794 		if(!string_ids) {
4795 			JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
4796 				error_code, error_cause, TRUE,
4797 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4798 		} else {
4799 			JANUS_VALIDATE_JSON_OBJECT(root, roomstr_parameters,
4800 				error_code, error_cause, TRUE,
4801 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4802 		}
4803 		if(error_code != 0)
4804 			goto prepare_response;
4805 		json_t *room = json_object_get(root, "room");
4806 		guint64 room_id = 0;
4807 		char room_id_num[30], *room_id_str = NULL;
4808 		if(!string_ids) {
4809 			room_id = json_integer_value(room);
4810 			g_snprintf(room_id_num, sizeof(room_id_num), "%"SCNu64, room_id);
4811 			room_id_str = room_id_num;
4812 		} else {
4813 			room_id_str = (char *)json_string_value(room);
4814 		}
4815 		janus_mutex_lock(&rooms_mutex);
4816 		janus_videoroom *videoroom = NULL;
4817 		error_code = janus_videoroom_access_room(root, FALSE, FALSE, &videoroom, error_cause, sizeof(error_cause));
4818 		if(error_code != 0) {
4819 			janus_mutex_unlock(&rooms_mutex);
4820 			goto prepare_response;
4821 		}
4822 		janus_refcount_increase(&videoroom->ref);
4823 		janus_mutex_unlock(&rooms_mutex);
4824 		/* Return a list of all participants (whether they're publishing or not) */
4825 		json_t *list = json_array();
4826 		GHashTableIter iter;
4827 		gpointer value;
4828 		janus_mutex_lock(&videoroom->mutex);
4829 		g_hash_table_iter_init(&iter, videoroom->participants);
4830 		while (!g_atomic_int_get(&videoroom->destroyed) && g_hash_table_iter_next(&iter, NULL, &value)) {
4831 			janus_videoroom_publisher *p = value;
4832 			json_t *pl = json_object();
4833 			json_object_set_new(pl, "id", string_ids ? json_string(p->user_id_str) : json_integer(p->user_id));
4834 			if(p->display)
4835 				json_object_set_new(pl, "display", json_string(p->display));
4836 			json_object_set_new(pl, "publisher", (p->sdp && g_atomic_int_get(&p->session->started)) ? json_true() : json_false());
4837 			if(p->sdp && g_atomic_int_get(&p->session->started)) {
4838 				if(p->audio_level_extmap_id > 0)
4839 					json_object_set_new(pl, "talking", p->talking ? json_true() : json_false());
4840 			}
4841 			json_array_append_new(list, pl);
4842 		}
4843 		janus_mutex_unlock(&videoroom->mutex);
4844 		janus_refcount_decrease(&videoroom->ref);
4845 		response = json_object();
4846 		json_object_set_new(response, "videoroom", json_string("participants"));
4847 		json_object_set_new(response, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
4848 		json_object_set_new(response, "participants", list);
4849 		goto prepare_response;
4850 	} else if(!strcasecmp(request_text, "listforwarders")) {
4851 		/* List all forwarders in a room */
4852 		if(!string_ids) {
4853 			JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
4854 				error_code, error_cause, TRUE,
4855 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4856 		} else {
4857 			JANUS_VALIDATE_JSON_OBJECT(root, roomstr_parameters,
4858 				error_code, error_cause, TRUE,
4859 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4860 		}
4861 		if(error_code != 0)
4862 			goto prepare_response;
4863 		json_t *room = json_object_get(root, "room");
4864 		guint64 room_id = 0;
4865 		char room_id_num[30], *room_id_str = NULL;
4866 		if(!string_ids) {
4867 			room_id = json_integer_value(room);
4868 			g_snprintf(room_id_num, sizeof(room_id_num), "%"SCNu64, room_id);
4869 			room_id_str = room_id_num;
4870 		} else {
4871 			room_id_str = (char *)json_string_value(room);
4872 		}
4873 		janus_mutex_lock(&rooms_mutex);
4874 		janus_videoroom *videoroom = NULL;
4875 		error_code = janus_videoroom_access_room(root, TRUE, FALSE, &videoroom, error_cause, sizeof(error_cause));
4876 		if(error_code != 0) {
4877 			janus_mutex_unlock(&rooms_mutex);
4878 			goto prepare_response;
4879 		}
4880 		janus_refcount_increase(&videoroom->ref);
4881 		janus_mutex_unlock(&rooms_mutex);
4882 		/* Return a list of all forwarders */
4883 		json_t *list = json_array();
4884 		GHashTableIter iter;
4885 		gpointer value;
4886 		janus_mutex_lock(&videoroom->mutex);
4887 		g_hash_table_iter_init(&iter, videoroom->participants);
4888 		while (!g_atomic_int_get(&videoroom->destroyed) && g_hash_table_iter_next(&iter, NULL, &value)) {
4889 			janus_videoroom_publisher *p = value;
4890 			janus_mutex_lock(&p->rtp_forwarders_mutex);
4891 			if(g_hash_table_size(p->rtp_forwarders) == 0) {
4892 				janus_mutex_unlock(&p->rtp_forwarders_mutex);
4893 				continue;
4894 			}
4895 			json_t *pl = json_object();
4896 			json_object_set_new(pl, "publisher_id", string_ids ? json_string(p->user_id_str) : json_integer(p->user_id));
4897 			if(p->display)
4898 				json_object_set_new(pl, "display", json_string(p->display));
4899 			json_t *flist = json_array();
4900 			GHashTableIter iter_f;
4901 			gpointer key_f, value_f;
4902 			g_hash_table_iter_init(&iter_f, p->rtp_forwarders);
4903 			while(g_hash_table_iter_next(&iter_f, &key_f, &value_f)) {
4904 				json_t *fl = json_object();
4905 				guint32 rpk = GPOINTER_TO_UINT(key_f);
4906 				janus_videoroom_rtp_forwarder *rpv = value_f;
4907 				char address[100];
4908 				if(rpv->serv_addr.sin_family == AF_INET) {
4909 					json_object_set_new(fl, "ip", json_string(
4910 						inet_ntop(AF_INET, &rpv->serv_addr.sin_addr, address, sizeof(address))));
4911 				} else {
4912 					json_object_set_new(fl, "ip", json_string(
4913 						inet_ntop(AF_INET6, &rpv->serv_addr6.sin6_addr, address, sizeof(address))));
4914 				}
4915 				if(rpv->is_data) {
4916 					json_object_set_new(fl, "data_stream_id", json_integer(rpk));
4917 					json_object_set_new(fl, "port", json_integer(ntohs(rpv->serv_addr.sin_port)));
4918 				} else if(rpv->is_video) {
4919 					json_object_set_new(fl, "video_stream_id", json_integer(rpk));
4920 					json_object_set_new(fl, "port", json_integer(ntohs(rpv->serv_addr.sin_port)));
4921 					if(rpv->local_rtcp_port > 0)
4922 						json_object_set_new(fl, "local_rtcp_port", json_integer(rpv->local_rtcp_port));
4923 					if(rpv->remote_rtcp_port > 0)
4924 						json_object_set_new(fl, "remote_rtcp_port", json_integer(rpv->remote_rtcp_port));
4925 					if(rpv->payload_type)
4926 						json_object_set_new(fl, "pt", json_integer(rpv->payload_type));
4927 					if(rpv->ssrc)
4928 						json_object_set_new(fl, "ssrc", json_integer(rpv->ssrc));
4929 					if(rpv->substream)
4930 						json_object_set_new(fl, "substream", json_integer(rpv->substream));
4931 				} else {
4932 					json_object_set_new(fl, "audio_stream_id", json_integer(rpk));
4933 					json_object_set_new(fl, "port", json_integer(ntohs(rpv->serv_addr.sin_port)));
4934 					if(rpv->local_rtcp_port > 0)
4935 						json_object_set_new(fl, "local_rtcp_port", json_integer(rpv->local_rtcp_port));
4936 					if(rpv->remote_rtcp_port > 0)
4937 						json_object_set_new(fl, "remote_rtcp_port", json_integer(rpv->remote_rtcp_port));
4938 					if(rpv->payload_type)
4939 						json_object_set_new(fl, "pt", json_integer(rpv->payload_type));
4940 					if(rpv->ssrc)
4941 						json_object_set_new(fl, "ssrc", json_integer(rpv->ssrc));
4942 				}
4943 				if(rpv->is_srtp)
4944 					json_object_set_new(fl, "srtp", json_true());
4945 				json_array_append_new(flist, fl);
4946 			}
4947 			janus_mutex_unlock(&p->rtp_forwarders_mutex);
4948 			json_object_set_new(pl, "rtp_forwarder", flist);
4949 			json_array_append_new(list, pl);
4950 		}
4951 		janus_mutex_unlock(&videoroom->mutex);
4952 		janus_refcount_decrease(&videoroom->ref);
4953 		response = json_object();
4954 		json_object_set_new(response, "videoroom", json_string("forwarders"));
4955 		json_object_set_new(response, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
4956 		json_object_set_new(response, "rtp_forwarders", list);
4957 		goto prepare_response;
4958 	} else if(!strcasecmp(request_text, "enable_recording")) {
4959 		JANUS_VALIDATE_JSON_OBJECT(root, record_parameters,
4960 			error_code, error_cause, TRUE,
4961 			JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
4962 		if(error_code != 0)
4963 			goto prepare_response;
4964 		json_t *record = json_object_get(root, "record");
4965 		gboolean recording_active = json_is_true(record);
4966 		JANUS_LOG(LOG_VERB, "Enable Recording : %d \n", (recording_active ? 1 : 0));
4967 		/* Lookup room */
4968 		janus_mutex_lock(&rooms_mutex);
4969 		janus_videoroom *videoroom = NULL;
4970 		error_code = janus_videoroom_access_room(root, TRUE, FALSE, &videoroom, error_cause, sizeof(error_cause));
4971 		if(error_code != 0) {
4972 			JANUS_LOG(LOG_ERR, "Failed to access videoroom\n");
4973 			janus_mutex_unlock(&rooms_mutex);
4974 			goto prepare_response;
4975 		}
4976 		janus_refcount_increase(&videoroom->ref);
4977 		janus_mutex_unlock(&rooms_mutex);
4978 		janus_mutex_lock(&videoroom->mutex);
4979 		/* Set recording status */
4980 		gboolean room_prev_recording_active = recording_active;
4981 		if (room_prev_recording_active != videoroom->record) {
4982 			/* Room recording state has changed */
4983 			videoroom->record = room_prev_recording_active;
4984 			/* Iterate over all participants */
4985 			gpointer value;
4986 			GHashTableIter iter;
4987 			g_hash_table_iter_init(&iter, videoroom->participants);
4988 			while (g_hash_table_iter_next(&iter, NULL, &value)) {
4989 				janus_videoroom_publisher *participant = value;
4990 				if(participant && participant->session) {
4991 					janus_mutex_lock(&participant->rec_mutex);
4992 					gboolean prev_recording_active = participant->recording_active;
4993 					participant->recording_active = recording_active;
4994 					JANUS_LOG(LOG_VERB, "Setting record property: %s (room %"SCNu64", user %"SCNu64")\n", participant->recording_active ? "true" : "false", participant->room_id, participant->user_id);
4995 					/* Do we need to do something with the recordings right now? */
4996 					if(participant->recording_active != prev_recording_active) {
4997 						/* Something changed */
4998 						if(!participant->recording_active) {
4999 							/* Not recording (anymore?) */
5000 							janus_videoroom_recorder_close(participant);
5001 						} else if(participant->recording_active && participant->sdp) {
5002 							/* We've started recording, send a PLI/FIR and go on */
5003 							janus_videoroom_recorder_create(
5004 								participant, strstr(participant->sdp, "m=audio") != NULL,
5005 								strstr(participant->sdp, "m=video") != NULL,
5006 								strstr(participant->sdp, "m=application") != NULL);
5007 							if(strstr(participant->sdp, "m=video")) {
5008 								/* Send a FIR */
5009 								janus_videoroom_reqpli(participant, "Recording video");
5010 							}
5011 						}
5012 					}
5013 					janus_mutex_unlock(&participant->rec_mutex);
5014 				}
5015 			}
5016         }
5017 		janus_mutex_unlock(&videoroom->mutex);
5018 		janus_refcount_decrease(&videoroom->ref);
5019 		response = json_object();
5020 		json_object_set_new(response, "videoroom", json_string("success"));
5021 		json_object_set_new(response, "record", json_boolean(recording_active));
5022 		goto prepare_response;
5023 	} else {
5024 		/* Not a request we recognize, don't do anything */
5025 		return NULL;
5026 	}
5027 
5028 prepare_response:
5029 		{
5030 			if(error_code == 0 && !response) {
5031 				error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR;
5032 				g_snprintf(error_cause, 512, "Invalid response");
5033 			}
5034 			if(error_code != 0) {
5035 				/* Prepare JSON error event */
5036 				response = json_object();
5037 				json_object_set_new(response, "videoroom", json_string("event"));
5038 				json_object_set_new(response, "error_code", json_integer(error_code));
5039 				json_object_set_new(response, "error", json_string(error_cause));
5040 			}
5041 			return response;
5042 		}
5043 
5044 }
5045 
janus_videoroom_handle_message(janus_plugin_session * handle,char * transaction,json_t * message,json_t * jsep)5046 struct janus_plugin_result *janus_videoroom_handle_message(janus_plugin_session *handle, char *transaction, json_t *message, json_t *jsep) {
5047 	if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
5048 		return janus_plugin_result_new(JANUS_PLUGIN_ERROR, g_atomic_int_get(&stopping) ? "Shutting down" : "Plugin not initialized", NULL);
5049 
5050 	/* Pre-parse the message */
5051 	int error_code = 0;
5052 	char error_cause[512];
5053 	json_t *root = message;
5054 	json_t *response = NULL;
5055 
5056 	janus_mutex_lock(&sessions_mutex);
5057 	janus_videoroom_session *session = janus_videoroom_lookup_session(handle);
5058 	if(!session) {
5059 		janus_mutex_unlock(&sessions_mutex);
5060 		JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
5061 		error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR;
5062 		g_snprintf(error_cause, 512, "%s", "No session associated with this handle...");
5063 		goto plugin_response;
5064 	}
5065 	/* Increase the reference counter for this session: we'll decrease it after we handle the message */
5066 	janus_refcount_increase(&session->ref);
5067 	janus_mutex_unlock(&sessions_mutex);
5068 	if(g_atomic_int_get(&session->destroyed)) {
5069 		JANUS_LOG(LOG_ERR, "Session has already been marked as destroyed...\n");
5070 		error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR;
5071 		g_snprintf(error_cause, 512, "%s", "Session has already been marked as destroyed...");
5072 		goto plugin_response;
5073 	}
5074 
5075 	if(message == NULL) {
5076 		JANUS_LOG(LOG_ERR, "No message??\n");
5077 		error_code = JANUS_VIDEOROOM_ERROR_NO_MESSAGE;
5078 		g_snprintf(error_cause, 512, "%s", "No message??");
5079 		goto plugin_response;
5080 	}
5081 	if(!json_is_object(root)) {
5082 		JANUS_LOG(LOG_ERR, "JSON error: not an object\n");
5083 		error_code = JANUS_VIDEOROOM_ERROR_INVALID_JSON;
5084 		g_snprintf(error_cause, 512, "JSON error: not an object");
5085 		goto plugin_response;
5086 	}
5087 	/* Get the request first */
5088 	JANUS_VALIDATE_JSON_OBJECT(root, request_parameters,
5089 		error_code, error_cause, TRUE,
5090 		JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
5091 	if(error_code != 0)
5092 		goto plugin_response;
5093 	json_t *request = json_object_get(root, "request");
5094 	/* Some requests ('create', 'destroy', 'exists', 'list') can be handled synchronously */
5095 	const char *request_text = json_string_value(request);
5096 	/* We have a separate method to process synchronous requests, as those may
5097 	 * arrive from the Admin API as well, and so we handle them the same way */
5098 	response = janus_videoroom_process_synchronous_request(session, root);
5099 	if(response != NULL) {
5100 		/* We got a response, send it back */
5101 		goto plugin_response;
5102 	} else if(!strcasecmp(request_text, "join") || !strcasecmp(request_text, "joinandconfigure")
5103 			|| !strcasecmp(request_text, "configure") || !strcasecmp(request_text, "publish") || !strcasecmp(request_text, "unpublish")
5104 			|| !strcasecmp(request_text, "start") || !strcasecmp(request_text, "pause") || !strcasecmp(request_text, "switch")
5105 			|| !strcasecmp(request_text, "leave")) {
5106 		/* These messages are handled asynchronously */
5107 
5108 		janus_videoroom_message *msg = g_malloc(sizeof(janus_videoroom_message));
5109 		msg->handle = handle;
5110 		msg->transaction = transaction;
5111 		msg->message = root;
5112 		msg->jsep = jsep;
5113 		g_async_queue_push(messages, msg);
5114 
5115 		return janus_plugin_result_new(JANUS_PLUGIN_OK_WAIT, NULL, NULL);
5116 	} else {
5117 		JANUS_LOG(LOG_VERB, "Unknown request '%s'\n", request_text);
5118 		error_code = JANUS_VIDEOROOM_ERROR_INVALID_REQUEST;
5119 		g_snprintf(error_cause, 512, "Unknown request '%s'", request_text);
5120 	}
5121 
5122 plugin_response:
5123 		{
5124 			if(error_code == 0 && !response) {
5125 				error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR;
5126 				g_snprintf(error_cause, 512, "Invalid response");
5127 			}
5128 			if(error_code != 0) {
5129 				/* Prepare JSON error event */
5130 				json_t *event = json_object();
5131 				json_object_set_new(event, "videoroom", json_string("event"));
5132 				json_object_set_new(event, "error_code", json_integer(error_code));
5133 				json_object_set_new(event, "error", json_string(error_cause));
5134 				response = event;
5135 			}
5136 			if(root != NULL)
5137 				json_decref(root);
5138 			if(jsep != NULL)
5139 				json_decref(jsep);
5140 			g_free(transaction);
5141 
5142 			if(session != NULL)
5143 				janus_refcount_decrease(&session->ref);
5144 			return janus_plugin_result_new(JANUS_PLUGIN_OK, NULL, response);
5145 		}
5146 
5147 }
5148 
janus_videoroom_handle_admin_message(json_t * message)5149 json_t *janus_videoroom_handle_admin_message(json_t *message) {
5150 	/* Some requests (e.g., 'create' and 'destroy') can be handled via Admin API */
5151 	int error_code = 0;
5152 	char error_cause[512];
5153 	json_t *response = NULL;
5154 
5155 	JANUS_VALIDATE_JSON_OBJECT(message, request_parameters,
5156 		error_code, error_cause, TRUE,
5157 		JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
5158 	if(error_code != 0)
5159 		goto admin_response;
5160 	json_t *request = json_object_get(message, "request");
5161 	const char *request_text = json_string_value(request);
5162 	if((response = janus_videoroom_process_synchronous_request(NULL, message)) != NULL) {
5163 		/* We got a response, send it back */
5164 		goto admin_response;
5165 	} else {
5166 		JANUS_LOG(LOG_VERB, "Unknown request '%s'\n", request_text);
5167 		error_code = JANUS_VIDEOROOM_ERROR_INVALID_REQUEST;
5168 		g_snprintf(error_cause, 512, "Unknown request '%s'", request_text);
5169 	}
5170 
5171 admin_response:
5172 		{
5173 			if(!response) {
5174 				/* Prepare JSON error event */
5175 				response = json_object();
5176 				json_object_set_new(response, "videoroom", json_string("event"));
5177 				json_object_set_new(response, "error_code", json_integer(error_code));
5178 				json_object_set_new(response, "error", json_string(error_cause));
5179 			}
5180 			return response;
5181 		}
5182 
5183 }
5184 
janus_videoroom_setup_media(janus_plugin_session * handle)5185 void janus_videoroom_setup_media(janus_plugin_session *handle) {
5186 	JANUS_LOG(LOG_INFO, "[%s-%p] WebRTC media is now available\n", JANUS_VIDEOROOM_PACKAGE, handle);
5187 	if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
5188 		return;
5189 	janus_mutex_lock(&sessions_mutex);
5190 	janus_videoroom_session *session = janus_videoroom_lookup_session(handle);
5191 	if(!session) {
5192 		janus_mutex_unlock(&sessions_mutex);
5193 		JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
5194 		return;
5195 	}
5196 	if(g_atomic_int_get(&session->destroyed)) {
5197 		janus_mutex_unlock(&sessions_mutex);
5198 		return;
5199 	}
5200 	janus_refcount_increase(&session->ref);
5201 	g_atomic_int_set(&session->hangingup, 0);
5202 	janus_mutex_unlock(&sessions_mutex);
5203 
5204 	/* Media relaying can start now */
5205 	g_atomic_int_set(&session->started, 1);
5206 	if(session->participant) {
5207 		/* If this is a publisher, notify all subscribers about the fact they can
5208 		 * now subscribe; if this is a subscriber, instead, ask the publisher a FIR */
5209 		if(session->participant_type == janus_videoroom_p_type_publisher) {
5210 			janus_videoroom_publisher *participant = janus_videoroom_session_get_publisher(session);
5211 			/* Notify all other participants that there's a new boy in town */
5212 			json_t *list = json_array();
5213 			json_t *pl = json_object();
5214 			json_object_set_new(pl, "id", string_ids ? json_string(participant->user_id_str) : json_integer(participant->user_id));
5215 			if(participant->display)
5216 				json_object_set_new(pl, "display", json_string(participant->display));
5217 			if(participant->audio)
5218 				json_object_set_new(pl, "audio_codec", json_string(janus_audiocodec_name(participant->acodec)));
5219 			if(participant->video)
5220 				json_object_set_new(pl, "video_codec", json_string(janus_videocodec_name(participant->vcodec)));
5221 			if(participant->audio_muted)
5222 				json_object_set_new(pl, "audio_moderated", json_true());
5223 			if(participant->video_muted)
5224 				json_object_set_new(pl, "video_moderated", json_true());
5225 			if(participant->data_muted)
5226 				json_object_set_new(pl, "data_moderated", json_true());
5227 			if(participant->ssrc[0] || participant->rid[0])
5228 				json_object_set_new(pl, "simulcast", json_true());
5229 			if(participant->audio_level_extmap_id > 0)
5230 				json_object_set_new(pl, "talking", participant->talking ? json_true() : json_false());
5231 			json_array_append_new(list, pl);
5232 			json_t *pub = json_object();
5233 			json_object_set_new(pub, "videoroom", json_string("event"));
5234 			json_object_set_new(pub, "room", string_ids ? json_string(participant->room_id_str) : json_integer(participant->room_id));
5235 			json_object_set_new(pub, "publishers", list);
5236 			janus_videoroom *room = participant->room;
5237 			if(room && !g_atomic_int_get(&room->destroyed)) {
5238 				janus_refcount_increase(&room->ref);
5239 				janus_mutex_lock(&room->mutex);
5240 				janus_videoroom_notify_participants(participant, pub, FALSE);
5241 				janus_mutex_unlock(&room->mutex);
5242 				janus_refcount_decrease(&room->ref);
5243 			}
5244 			json_decref(pub);
5245 			/* Also notify event handlers */
5246 			if(notify_events && gateway->events_is_enabled()) {
5247 				json_t *info = json_object();
5248 				json_object_set_new(info, "event", json_string("published"));
5249 				json_object_set_new(info, "room", string_ids ? json_string(participant->room_id_str) : json_integer(participant->room_id));
5250 				json_object_set_new(info, "id", string_ids ? json_string(participant->user_id_str) : json_integer(participant->user_id));
5251 				json_object_set_new(info, "audio_codec", json_string(janus_audiocodec_name(participant->acodec)));
5252 				json_object_set_new(info, "video_codec", json_string(janus_videocodec_name(participant->vcodec)));
5253 				gateway->notify_event(&janus_videoroom_plugin, session->handle, info);
5254 			}
5255 			janus_refcount_decrease(&participant->ref);
5256 		} else if(session->participant_type == janus_videoroom_p_type_subscriber) {
5257 			janus_videoroom_subscriber *s = janus_videoroom_session_get_subscriber(session);
5258 			if(s && s->feed) {
5259 				janus_videoroom_publisher *p = s->feed;
5260 				if(p && p->session) {
5261 					janus_videoroom_reqpli(p, "New subscriber available");
5262 					/* Also notify event handlers */
5263 					if(notify_events && gateway->events_is_enabled()) {
5264 						json_t *info = json_object();
5265 						json_object_set_new(info, "event", json_string("subscribed"));
5266 						json_object_set_new(info, "room", string_ids ? json_string(p->room_id_str) : json_integer(p->room_id));
5267 						json_object_set_new(info, "feed", string_ids ? json_string(p->user_id_str) : json_integer(p->user_id));
5268 						gateway->notify_event(&janus_videoroom_plugin, session->handle, info);
5269 					}
5270 				}
5271 			}
5272 			if(s)
5273 				janus_refcount_decrease(&s->ref);
5274 		}
5275 	}
5276 	janus_refcount_decrease(&session->ref);
5277 }
5278 
janus_videoroom_incoming_rtp(janus_plugin_session * handle,janus_plugin_rtp * pkt)5279 void janus_videoroom_incoming_rtp(janus_plugin_session *handle, janus_plugin_rtp *pkt) {
5280 	if(handle == NULL || g_atomic_int_get(&handle->stopped) || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
5281 		return;
5282 	janus_videoroom_session *session = (janus_videoroom_session *)handle->plugin_handle;
5283 	if(!session || g_atomic_int_get(&session->destroyed) || session->participant_type != janus_videoroom_p_type_publisher)
5284 		return;
5285 	janus_videoroom_publisher *participant = janus_videoroom_session_get_publisher_nodebug(session);
5286 	if(participant == NULL)
5287 		return;
5288 	if(g_atomic_int_get(&participant->destroyed) || participant->kicked || participant->room == NULL) {
5289 		janus_videoroom_publisher_dereference_nodebug(participant);
5290 		return;
5291 	}
5292 	janus_videoroom *videoroom = participant->room;
5293 
5294 	gboolean video = pkt->video;
5295 	char *buf = pkt->buffer;
5296 	uint16_t len = pkt->length;
5297 	/* In case this is an audio packet and we're doing talk detection, check the audio level extension */
5298 	if(!video && videoroom->audiolevel_event && participant->audio_active && !participant->audio_muted) {
5299 		int level = pkt->extensions.audio_level;
5300 		if(level != -1) {
5301 			participant->audio_dBov_sum += level;
5302 			participant->audio_active_packets++;
5303 			participant->audio_dBov_level = level;
5304 			int audio_active_packets = participant->user_audio_active_packets ? participant->user_audio_active_packets : videoroom->audio_active_packets;
5305 			int audio_level_average = participant->user_audio_level_average ? participant->user_audio_level_average : videoroom->audio_level_average;
5306 			if(participant->audio_active_packets > 0 && participant->audio_active_packets == audio_active_packets) {
5307 				gboolean notify_talk_event = FALSE;
5308 				float audio_dBov_avg = (float)participant->audio_dBov_sum/(float)participant->audio_active_packets;
5309 				if(audio_dBov_avg < audio_level_average) {
5310 					/* Participant talking, should we notify all participants? */
5311 					if(!participant->talking)
5312 						notify_talk_event = TRUE;
5313 					participant->talking = TRUE;
5314 				} else {
5315 					/* Participant not talking anymore, should we notify all participants? */
5316 					if(participant->talking)
5317 						notify_talk_event = TRUE;
5318 					participant->talking = FALSE;
5319 				}
5320 				participant->audio_active_packets = 0;
5321 				participant->audio_dBov_sum = 0;
5322 				/* Only notify in case of state changes */
5323 				if(notify_talk_event) {
5324 					janus_mutex_lock(&videoroom->mutex);
5325 					json_t *event = json_object();
5326 					json_object_set_new(event, "videoroom", json_string(participant->talking ? "talking" : "stopped-talking"));
5327 					json_object_set_new(event, "room", string_ids ? json_string(videoroom->room_id_str) : json_integer(videoroom->room_id));
5328 					json_object_set_new(event, "id", string_ids ? json_string(participant->user_id_str) : json_integer(participant->user_id));
5329 					json_object_set_new(event, "audio-level-dBov-avg", json_real(audio_dBov_avg));
5330 					/* Notify the speaker this event is related to as well */
5331 					janus_videoroom_notify_participants(participant, event, TRUE);
5332 					json_decref(event);
5333 					janus_mutex_unlock(&videoroom->mutex);
5334 					/* Also notify event handlers */
5335 					if(notify_events && gateway->events_is_enabled()) {
5336 						json_t *info = json_object();
5337 						json_object_set_new(info, "videoroom", json_string(participant->talking ? "talking" : "stopped-talking"));
5338 						json_object_set_new(info, "room", string_ids ? json_string(videoroom->room_id_str) : json_integer(videoroom->room_id));
5339 						json_object_set_new(info, "id", string_ids ? json_string(participant->user_id_str) : json_integer(participant->user_id));
5340 						json_object_set_new(info, "audio-level-dBov-avg", json_real(audio_dBov_avg));
5341 						gateway->notify_event(&janus_videoroom_plugin, session->handle, info);
5342 					}
5343 				}
5344 			}
5345 		}
5346 	}
5347 
5348 	if((!video && participant->audio_active && !participant->audio_muted) || (video && participant->video_active && !participant->video_muted)) {
5349 		janus_rtp_header *rtp = (janus_rtp_header *)buf;
5350 		int sc = video ? 0 : -1;
5351 		/* Check if we're simulcasting, and if so, keep track of the "layer" */
5352 		if(video && (participant->ssrc[0] != 0 || participant->rid[0] != NULL)) {
5353 			uint32_t ssrc = ntohl(rtp->ssrc);
5354 			if(ssrc == participant->ssrc[0])
5355 				sc = 0;
5356 			else if(ssrc == participant->ssrc[1])
5357 				sc = 1;
5358 			else if(ssrc == participant->ssrc[2])
5359 				sc = 2;
5360 			else if(participant->rid_extmap_id > 0) {
5361 				/* We may not know the SSRC yet, try the rid RTP extension */
5362 				char sdes_item[16];
5363 				if(janus_rtp_header_extension_parse_rid(buf, len, participant->rid_extmap_id, sdes_item, sizeof(sdes_item)) == 0) {
5364 					if(participant->rid[0] != NULL && !strcmp(participant->rid[0], sdes_item)) {
5365 						participant->ssrc[0] = ssrc;
5366 						sc = 0;
5367 					} else if(participant->rid[1] != NULL && !strcmp(participant->rid[1], sdes_item)) {
5368 						participant->ssrc[1] = ssrc;
5369 						sc = 1;
5370 					} else if(participant->rid[2] != NULL && !strcmp(participant->rid[2], sdes_item)) {
5371 						participant->ssrc[2] = ssrc;
5372 						sc = 2;
5373 					}
5374 				}
5375 			}
5376 		}
5377 		/* Forward RTP to the appropriate port for the rtp_forwarders associated with this publisher, if there are any */
5378 		janus_mutex_lock(&participant->rtp_forwarders_mutex);
5379 		if(participant->srtp_contexts && g_hash_table_size(participant->srtp_contexts) > 0) {
5380 			GHashTableIter iter;
5381 			gpointer value;
5382 			g_hash_table_iter_init(&iter, participant->srtp_contexts);
5383 			while(g_hash_table_iter_next(&iter, NULL, &value)) {
5384 				janus_videoroom_srtp_context *srtp_ctx = (janus_videoroom_srtp_context *)value;
5385 				srtp_ctx->slen = 0;
5386 			}
5387 		}
5388 		GHashTableIter iter;
5389 		gpointer value;
5390 		g_hash_table_iter_init(&iter, participant->rtp_forwarders);
5391 		while(participant->udp_sock > 0 && g_hash_table_iter_next(&iter, NULL, &value)) {
5392 			janus_videoroom_rtp_forwarder *rtp_forward = (janus_videoroom_rtp_forwarder *)value;
5393 			if(rtp_forward->is_data || (video && !rtp_forward->is_video) || (!video && rtp_forward->is_video))
5394 				continue;
5395 			/* Backup the RTP header info, as we may rewrite part of it */
5396 			uint32_t seq_number = ntohs(rtp->seq_number);
5397 			uint32_t timestamp = ntohl(rtp->timestamp);
5398 			int pt = rtp->type;
5399 			uint32_t ssrc = ntohl(rtp->ssrc);
5400 			/* First of all, check if we're simulcasting and if we need to forward or ignore this frame */
5401 			if(video && !rtp_forward->simulcast && rtp_forward->substream != sc) {
5402 				continue;
5403 			} else if(video && rtp_forward->simulcast) {
5404 				/* This is video and we're simulcasting, check if we need to forward this frame */
5405 				if(!janus_rtp_simulcasting_context_process_rtp(&rtp_forward->sim_context,
5406 						buf, len, participant->ssrc, participant->rid, participant->vcodec, &rtp_forward->context))
5407 					continue;
5408 				janus_rtp_header_update(rtp, &rtp_forward->context, TRUE, 0);
5409 				/* By default we use a fixed SSRC (it may be overwritten later) */
5410 				rtp->ssrc = htonl(participant->user_id & 0xffffffff);
5411 			}
5412 			/* Check if payload type and/or SSRC need to be overwritten for this forwarder */
5413 			if(rtp_forward->payload_type > 0)
5414 				rtp->type = rtp_forward->payload_type;
5415 			if(rtp_forward->ssrc > 0)
5416 				rtp->ssrc = htonl(rtp_forward->ssrc);
5417 			/* Check if this is an RTP or SRTP forwarder */
5418 			if(!rtp_forward->is_srtp) {
5419 				/* Plain RTP */
5420 				struct sockaddr *address = (rtp_forward->serv_addr.sin_family == AF_INET ?
5421 					(struct sockaddr *)&rtp_forward->serv_addr : (struct sockaddr *)&rtp_forward->serv_addr6);
5422 				size_t addrlen = (rtp_forward->serv_addr.sin_family == AF_INET ? sizeof(rtp_forward->serv_addr) : sizeof(rtp_forward->serv_addr6));
5423 				if(sendto(participant->udp_sock, buf, len, 0, address, addrlen) < 0) {
5424 					JANUS_LOG(LOG_HUGE, "Error forwarding RTP %s packet for %s... %s (len=%d)...\n",
5425 						(video ? "video" : "audio"), participant->display, g_strerror(errno), len);
5426 				}
5427 			} else {
5428 				/* SRTP: check if we already encrypted the packet before */
5429 				if(rtp_forward->srtp_ctx->slen == 0) {
5430 					memcpy(&rtp_forward->srtp_ctx->sbuf, buf, len);
5431 					int protected = len;
5432 					int res = srtp_protect(rtp_forward->srtp_ctx->ctx, &rtp_forward->srtp_ctx->sbuf, &protected);
5433 					if(res != srtp_err_status_ok) {
5434 						janus_rtp_header *header = (janus_rtp_header *)&rtp_forward->srtp_ctx->sbuf;
5435 						guint32 timestamp = ntohl(header->timestamp);
5436 						guint16 seq = ntohs(header->seq_number);
5437 						JANUS_LOG(LOG_ERR, "Error encrypting %s packet for %s... %s (len=%d-->%d, ts=%"SCNu32", seq=%"SCNu16")...\n",
5438 							(video ? "Video" : "Audio"), participant->display, janus_srtp_error_str(res), len, protected, timestamp, seq);
5439 					} else {
5440 						rtp_forward->srtp_ctx->slen = protected;
5441 					}
5442 				}
5443 				if(rtp_forward->srtp_ctx->slen > 0) {
5444 					struct sockaddr *address = (rtp_forward->serv_addr.sin_family == AF_INET ?
5445 						(struct sockaddr *)&rtp_forward->serv_addr : (struct sockaddr *)&rtp_forward->serv_addr6);
5446 					size_t addrlen = (rtp_forward->serv_addr.sin_family == AF_INET ? sizeof(rtp_forward->serv_addr) : sizeof(rtp_forward->serv_addr6));
5447 					if(sendto(participant->udp_sock, rtp_forward->srtp_ctx->sbuf, rtp_forward->srtp_ctx->slen, 0, address, addrlen) < 0) {
5448 						JANUS_LOG(LOG_HUGE, "Error forwarding SRTP %s packet for %s... %s (len=%d)...\n",
5449 							(video ? "video" : "audio"), participant->display, g_strerror(errno), rtp_forward->srtp_ctx->slen);
5450 					}
5451 				}
5452 			}
5453 			/* Restore original values of payload type and SSRC before going on */
5454 			rtp->type = pt;
5455 			rtp->ssrc = htonl(ssrc);
5456 			rtp->timestamp = htonl(timestamp);
5457 			rtp->seq_number = htons(seq_number);
5458 		}
5459 		janus_mutex_unlock(&participant->rtp_forwarders_mutex);
5460 		/* Set the payload type of the publisher */
5461 		rtp->type = video ? participant->video_pt : participant->audio_pt;
5462 		/* Save the frame if we're recording */
5463 		if(!video || (participant->ssrc[0] == 0 && participant->rid[0] == NULL)) {
5464 			janus_recorder_save_frame(video ? participant->vrc : participant->arc, buf, len);
5465 		} else {
5466 			/* We're simulcasting, save the best video quality */
5467 			gboolean save = janus_rtp_simulcasting_context_process_rtp(&participant->rec_simctx,
5468 				buf, len, participant->ssrc, participant->rid, participant->vcodec, &participant->rec_ctx);
5469 			if(save) {
5470 				uint32_t seq_number = ntohs(rtp->seq_number);
5471 				uint32_t timestamp = ntohl(rtp->timestamp);
5472 				uint32_t ssrc = ntohl(rtp->ssrc);
5473 				janus_rtp_header_update(rtp, &participant->rec_ctx, TRUE, 0);
5474 				/* We use a fixed SSRC for the whole recording */
5475 				rtp->ssrc = participant->ssrc[0];
5476 				janus_recorder_save_frame(participant->vrc, buf, len);
5477 				/* Restore the header, as it will be needed by subscribers */
5478 				rtp->ssrc = htonl(ssrc);
5479 				rtp->timestamp = htonl(timestamp);
5480 				rtp->seq_number = htons(seq_number);
5481 			}
5482 		}
5483 		/* Done, relay it */
5484 		janus_videoroom_rtp_relay_packet packet;
5485 		packet.data = rtp;
5486 		packet.length = len;
5487 		packet.extensions = pkt->extensions;
5488 		packet.is_rtp = TRUE;
5489 		packet.is_video = video;
5490 		packet.svc = FALSE;
5491 		if(video && videoroom->do_svc) {
5492 			/* We're doing SVC: let's parse this packet to see which layers are there */
5493 			int plen = 0;
5494 			char *payload = janus_rtp_payload(buf, len, &plen);
5495 			if(payload == NULL) {
5496 				janus_videoroom_publisher_dereference_nodebug(participant);
5497 				return;
5498 			}
5499 			gboolean found = FALSE;
5500 			memset(&packet.svc_info, 0, sizeof(packet.svc_info));
5501 			if(janus_vp9_parse_svc(payload, plen, &found, &packet.svc_info) == 0) {
5502 				packet.svc = found;
5503 			}
5504 		}
5505 		packet.ssrc[0] = (sc != -1 ? participant->ssrc[0] : 0);
5506 		packet.ssrc[1] = (sc != -1 ? participant->ssrc[1] : 0);
5507 		packet.ssrc[2] = (sc != -1 ? participant->ssrc[2] : 0);
5508 		/* Backup the actual timestamp and sequence number set by the publisher, in case switching is involved */
5509 		packet.timestamp = ntohl(packet.data->timestamp);
5510 		packet.seq_number = ntohs(packet.data->seq_number);
5511 		/* Go: some viewers may decide to drop the packet, but that's up to them */
5512 		janus_mutex_lock_nodebug(&participant->subscribers_mutex);
5513 		g_slist_foreach(participant->subscribers, janus_videoroom_relay_rtp_packet, &packet);
5514 		janus_mutex_unlock_nodebug(&participant->subscribers_mutex);
5515 
5516 		/* Check if we need to send any REMB, FIR or PLI back to this publisher */
5517 		if(video && participant->video_active && !participant->video_muted) {
5518 			/* Did we send a REMB already, or is it time to send one? */
5519 			gboolean send_remb = FALSE;
5520 			if(participant->remb_latest == 0 && participant->remb_startup > 0) {
5521 				/* Still in the starting phase, send the ramp-up REMB feedback */
5522 				send_remb = TRUE;
5523 			} else if(participant->remb_latest > 0 && janus_get_monotonic_time()-participant->remb_latest >= 5*G_USEC_PER_SEC) {
5524 				/* 5 seconds have passed since the last REMB, send a new one */
5525 				send_remb = TRUE;
5526 			}
5527 
5528 			if(send_remb && participant->bitrate) {
5529 				/* We send a few incremental REMB messages at startup */
5530 				uint32_t bitrate = participant->bitrate;
5531 				if(participant->remb_startup > 0) {
5532 					bitrate = bitrate/participant->remb_startup;
5533 					participant->remb_startup--;
5534 				}
5535 				JANUS_LOG(LOG_VERB, "Sending REMB (%s, %"SCNu32")\n", participant->display, bitrate);
5536 				gateway->send_remb(handle, bitrate);
5537 				if(participant->remb_startup == 0)
5538 					participant->remb_latest = janus_get_monotonic_time();
5539 			}
5540 			/* Generate FIR/PLI too, if needed */
5541 			if(video && participant->video_active && !participant->video_muted && (videoroom->fir_freq > 0)) {
5542 				/* We generate RTCP every tot seconds/frames */
5543 				gint64 now = janus_get_monotonic_time();
5544 				/* First check if this is a keyframe, though: if so, we reset the timer */
5545 				int plen = 0;
5546 				char *payload = janus_rtp_payload(buf, len, &plen);
5547 				if(payload == NULL) {
5548 					janus_videoroom_publisher_dereference_nodebug(participant);
5549 					return;
5550 				}
5551 				if(participant->vcodec == JANUS_VIDEOCODEC_VP8) {
5552 					if(janus_vp8_is_keyframe(payload, plen))
5553 						participant->fir_latest = now;
5554 				} else if(participant->vcodec == JANUS_VIDEOCODEC_VP9) {
5555 					if(janus_vp9_is_keyframe(payload, plen))
5556 						participant->fir_latest = now;
5557 				} else if(participant->vcodec == JANUS_VIDEOCODEC_H264) {
5558 					if(janus_h264_is_keyframe(payload, plen))
5559 						participant->fir_latest = now;
5560 				} else if(participant->vcodec == JANUS_VIDEOCODEC_AV1) {
5561 					if(janus_av1_is_keyframe(payload, plen))
5562 						participant->fir_latest = now;
5563 				} else if(participant->vcodec == JANUS_VIDEOCODEC_H265) {
5564 					if(janus_h265_is_keyframe(payload, plen))
5565 						participant->fir_latest = now;
5566 				}
5567 				if((now-participant->fir_latest) >= ((gint64)videoroom->fir_freq*G_USEC_PER_SEC)) {
5568 					/* FIXME We send a FIR every tot seconds */
5569 					janus_videoroom_reqpli(participant, "Regular keyframe request");
5570 				}
5571 			}
5572 		}
5573 	}
5574 	janus_videoroom_publisher_dereference_nodebug(participant);
5575 }
5576 
janus_videoroom_incoming_rtcp(janus_plugin_session * handle,janus_plugin_rtcp * packet)5577 void janus_videoroom_incoming_rtcp(janus_plugin_session *handle, janus_plugin_rtcp *packet) {
5578 	if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
5579 		return;
5580 	janus_videoroom_session *session = (janus_videoroom_session *)handle->plugin_handle;
5581 	if(!session) {
5582 		JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
5583 		return;
5584 	}
5585 	if(g_atomic_int_get(&session->destroyed))
5586 		return;
5587 	char *buf = packet->buffer;
5588 	uint16_t len = packet->length;
5589 	if(session->participant_type == janus_videoroom_p_type_subscriber) {
5590 		/* A subscriber sent some RTCP, check what it is and if we need to forward it to the publisher */
5591 		janus_videoroom_subscriber *s = janus_videoroom_session_get_subscriber_nodebug(session);
5592 		if(s == NULL)
5593 			return;
5594 		if(g_atomic_int_get(&s->destroyed) || !s->video) {
5595 			janus_refcount_decrease_nodebug(&s->ref);
5596 			return;	/* The only feedback we handle is video related anyway... */
5597 		}
5598 		if(janus_rtcp_has_fir(buf, len) || janus_rtcp_has_pli(buf, len)) {
5599 			/* We got a FIR or PLI, forward a PLI it to the publisher */
5600 			if(s->feed) {
5601 				janus_videoroom_publisher *p = s->feed;
5602 				if(p && p->session) {
5603 					janus_videoroom_reqpli(p, "PLI from subscriber");
5604 				}
5605 			}
5606 		}
5607 		uint32_t bitrate = janus_rtcp_get_remb(buf, len);
5608 		if(bitrate > 0) {
5609 			/* FIXME We got a REMB from this subscriber, should we do something about it? */
5610 		}
5611 		janus_refcount_decrease_nodebug(&s->ref);
5612 	}
5613 }
5614 
janus_videoroom_incoming_data(janus_plugin_session * handle,janus_plugin_data * packet)5615 void janus_videoroom_incoming_data(janus_plugin_session *handle, janus_plugin_data *packet) {
5616 	if(handle == NULL || g_atomic_int_get(&handle->stopped) || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
5617 		return;
5618 	if(packet->buffer == NULL || packet->length == 0)
5619 		return;
5620 	janus_videoroom_session *session = (janus_videoroom_session *)handle->plugin_handle;
5621 	if(!session || g_atomic_int_get(&session->destroyed) || session->participant_type != janus_videoroom_p_type_publisher)
5622 		return;
5623 	janus_videoroom_publisher *participant = janus_videoroom_session_get_publisher_nodebug(session);
5624 	if(participant == NULL)
5625 		return;
5626 	if(g_atomic_int_get(&participant->destroyed) || !participant->data_active || participant->data_muted || participant->kicked) {
5627 		janus_videoroom_publisher_dereference_nodebug(participant);
5628 		return;
5629 	}
5630 	char *buf = packet->buffer;
5631 	uint16_t len = packet->length;
5632 	/* Any forwarder involved? */
5633 	janus_mutex_lock(&participant->rtp_forwarders_mutex);
5634 	/* Forward RTP to the appropriate port for the rtp_forwarders associated with this publisher, if there are any */
5635 	GHashTableIter iter;
5636 	gpointer value;
5637 	g_hash_table_iter_init(&iter, participant->rtp_forwarders);
5638 	while(participant->udp_sock > 0 && g_hash_table_iter_next(&iter, NULL, &value)) {
5639 		janus_videoroom_rtp_forwarder* rtp_forward = (janus_videoroom_rtp_forwarder*)value;
5640 		if(rtp_forward->is_data) {
5641 			struct sockaddr *address = (rtp_forward->serv_addr.sin_family == AF_INET ?
5642 				(struct sockaddr *)&rtp_forward->serv_addr : (struct sockaddr *)&rtp_forward->serv_addr6);
5643 			size_t addrlen = (rtp_forward->serv_addr.sin_family == AF_INET ? sizeof(rtp_forward->serv_addr) : sizeof(rtp_forward->serv_addr6));
5644 			if(sendto(participant->udp_sock, buf, len, 0, address, addrlen) < 0) {
5645 				JANUS_LOG(LOG_HUGE, "Error forwarding data packet for %s... %s (len=%d)...\n",
5646 					participant->display, g_strerror(errno), len);
5647 			}
5648 		}
5649 	}
5650 	janus_mutex_unlock(&participant->rtp_forwarders_mutex);
5651 	JANUS_LOG(LOG_VERB, "Got a %s DataChannel message (%d bytes) to forward\n",
5652 		packet->binary ? "binary" : "text", len);
5653 	/* Save the message if we're recording */
5654 	janus_recorder_save_frame(participant->drc, buf, len);
5655 	/* Relay to all subscribers */
5656 	janus_videoroom_rtp_relay_packet pkt;
5657 	pkt.data = (struct rtp_header *)buf;
5658 	pkt.length = len;
5659 	pkt.is_rtp = FALSE;
5660 	pkt.textdata = !packet->binary;
5661 	janus_mutex_lock_nodebug(&participant->subscribers_mutex);
5662 	g_slist_foreach(participant->subscribers, janus_videoroom_relay_data_packet, &pkt);
5663 	janus_mutex_unlock_nodebug(&participant->subscribers_mutex);
5664 	janus_videoroom_publisher_dereference_nodebug(participant);
5665 }
5666 
janus_videoroom_data_ready(janus_plugin_session * handle)5667 void janus_videoroom_data_ready(janus_plugin_session *handle) {
5668 	if(handle == NULL || g_atomic_int_get(&handle->stopped) ||
5669 			g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized) || !gateway)
5670 		return;
5671 	/* Data channels are writable */
5672 	janus_videoroom_session *session = (janus_videoroom_session *)handle->plugin_handle;
5673 	if(!session || g_atomic_int_get(&session->destroyed) || g_atomic_int_get(&session->hangingup))
5674 		return;
5675 	if(g_atomic_int_compare_and_exchange(&session->dataready, 0, 1)) {
5676 		JANUS_LOG(LOG_INFO, "[%s-%p] Data channel available\n", JANUS_VIDEOROOM_PACKAGE, handle);
5677 	}
5678 }
5679 
janus_videoroom_slow_link(janus_plugin_session * handle,int uplink,int video)5680 void janus_videoroom_slow_link(janus_plugin_session *handle, int uplink, int video) {
5681 	/* The core is informing us that our peer got too many NACKs, are we pushing media too hard? */
5682 	if(handle == NULL || g_atomic_int_get(&handle->stopped) || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
5683 		return;
5684 	janus_mutex_lock(&sessions_mutex);
5685 	janus_videoroom_session *session = janus_videoroom_lookup_session(handle);
5686 	if(!session || g_atomic_int_get(&session->destroyed) || !session->participant) {
5687 		janus_mutex_unlock(&sessions_mutex);
5688 		return;
5689 	}
5690 	janus_refcount_increase(&session->ref);
5691 	janus_mutex_unlock(&sessions_mutex);
5692 	/* Check if it's an uplink (publisher) or downlink (viewer) issue */
5693 	if(session->participant_type == janus_videoroom_p_type_publisher) {
5694 		if(!uplink) {
5695 			janus_videoroom_publisher *publisher = janus_videoroom_session_get_publisher(session);
5696 			if(publisher == NULL) {
5697 				janus_refcount_decrease(&session->ref);
5698 				return;
5699 			}
5700 			if(g_atomic_int_get(&publisher->destroyed)) {
5701 				janus_refcount_decrease(&publisher->ref);
5702 				janus_refcount_decrease(&session->ref);
5703 				return;
5704 			}
5705 			/* Send an event on the handle to notify the application: it's
5706 			 * up to the application to then choose a policy and enforce it */
5707 			json_t *event = json_object();
5708 			json_object_set_new(event, "videoroom", json_string("slow_link"));
5709 			/* Also add info on what the current bitrate cap is */
5710 			uint32_t bitrate = publisher->bitrate;
5711 			json_object_set_new(event, "current-bitrate", json_integer(bitrate));
5712 			gateway->push_event(session->handle, &janus_videoroom_plugin, NULL, event, NULL);
5713 			json_decref(event);
5714 			janus_refcount_decrease(&publisher->ref);
5715 		} else {
5716 			JANUS_LOG(LOG_WARN, "Got a slow uplink on a VideoRoom publisher? Weird, because it doesn't receive media...\n");
5717 		}
5718 	} else if(session->participant_type == janus_videoroom_p_type_subscriber) {
5719 		if(uplink) {
5720 			janus_videoroom_subscriber *subscriber = janus_videoroom_session_get_subscriber(session);
5721 			if(subscriber == NULL) {
5722 				janus_refcount_decrease(&session->ref);
5723 				return;
5724 			}
5725 			if(g_atomic_int_get(&subscriber->destroyed)) {
5726 				janus_refcount_decrease(&subscriber->ref);
5727 				janus_refcount_decrease(&session->ref);
5728 				return;
5729 			}
5730 			/* Send an event on the handle to notify the application: it's
5731 			 * up to the application to then choose a policy and enforce it */
5732 			json_t *event = json_object();
5733 			json_object_set_new(event, "videoroom", json_string("slow_link"));
5734 			gateway->push_event(session->handle, &janus_videoroom_plugin, NULL, event, NULL);
5735 			json_decref(event);
5736 		} else {
5737 			JANUS_LOG(LOG_WARN, "Got a slow downlink on a VideoRoom viewer? Weird, because it doesn't send media...\n");
5738 		}
5739 	}
5740 	janus_refcount_decrease(&session->ref);
5741 }
5742 
janus_videoroom_recorder_create(janus_videoroom_publisher * participant,gboolean audio,gboolean video,gboolean data)5743 static void janus_videoroom_recorder_create(janus_videoroom_publisher *participant, gboolean audio, gboolean video, gboolean data) {
5744 	char filename[255];
5745 	janus_recorder *rc = NULL;
5746 	gint64 now = janus_get_real_time();
5747 	if(audio && participant->arc == NULL) {
5748 		memset(filename, 0, 255);
5749 		if(participant->recording_base) {
5750 			/* Use the filename and path we have been provided */
5751 			g_snprintf(filename, 255, "%s-audio", participant->recording_base);
5752 			rc = janus_recorder_create(participant->room->rec_dir,
5753 				janus_audiocodec_name(participant->acodec), filename);
5754 			if(rc == NULL) {
5755 				JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this publisher!\n");
5756 			}
5757 		} else {
5758 			/* Build a filename */
5759 			g_snprintf(filename, 255, "videoroom-%s-user-%s-%"SCNi64"-audio",
5760 				participant->room_id_str, participant->user_id_str, now);
5761 			rc = janus_recorder_create(participant->room->rec_dir,
5762 				janus_audiocodec_name(participant->acodec), filename);
5763 			if(rc == NULL) {
5764 				JANUS_LOG(LOG_ERR, "Couldn't open an audio recording file for this publisher!\n");
5765 			}
5766 		}
5767 		/* If media is encrypted, mark it in the recording */
5768 		if(participant->e2ee)
5769 			janus_recorder_encrypted(rc);
5770 		participant->arc = rc;
5771 	}
5772 	if(video && participant->vrc == NULL) {
5773 		janus_rtp_switching_context_reset(&participant->rec_ctx);
5774 		janus_rtp_simulcasting_context_reset(&participant->rec_simctx);
5775 		participant->rec_simctx.substream_target = 2;
5776 		participant->rec_simctx.templayer_target = 2;
5777 		memset(filename, 0, 255);
5778 		if(participant->recording_base) {
5779 			/* Use the filename and path we have been provided */
5780 			g_snprintf(filename, 255, "%s-video", participant->recording_base);
5781 			rc = janus_recorder_create_full(participant->room->rec_dir,
5782 				janus_videocodec_name(participant->vcodec), participant->vfmtp, filename);
5783 			if(rc == NULL) {
5784 				JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this publisher!\n");
5785 			}
5786 		} else {
5787 			/* Build a filename */
5788 			g_snprintf(filename, 255, "videoroom-%s-user-%s-%"SCNi64"-video",
5789 				participant->room_id_str, participant->user_id_str, now);
5790 			rc = janus_recorder_create_full(participant->room->rec_dir,
5791 				janus_videocodec_name(participant->vcodec), participant->vfmtp, filename);
5792 			if(rc == NULL) {
5793 				JANUS_LOG(LOG_ERR, "Couldn't open an video recording file for this publisher!\n");
5794 			}
5795 		}
5796 		/* If media is encrypted, mark it in the recording */
5797 		if(participant->e2ee)
5798 			janus_recorder_encrypted(rc);
5799 		participant->vrc = rc;
5800 	}
5801 	if(data && participant->drc == NULL) {
5802 		memset(filename, 0, 255);
5803 		if(participant->recording_base) {
5804 			/* Use the filename and path we have been provided */
5805 			g_snprintf(filename, 255, "%s-data", participant->recording_base);
5806 			rc = janus_recorder_create(participant->room->rec_dir,
5807 				"text", filename);
5808 			if(rc == NULL) {
5809 				JANUS_LOG(LOG_ERR, "Couldn't open an data recording file for this publisher!\n");
5810 			}
5811 		} else {
5812 			/* Build a filename */
5813 			g_snprintf(filename, 255, "videoroom-%s-user-%s-%"SCNi64"-data",
5814 				participant->room_id_str, participant->user_id_str, now);
5815 			rc = janus_recorder_create(participant->room->rec_dir,
5816 				"text", filename);
5817 			if(rc == NULL) {
5818 				JANUS_LOG(LOG_ERR, "Couldn't open an data recording file for this publisher!\n");
5819 			}
5820 		}
5821 		/* Media encryption doesn't apply to data channels */
5822 		participant->drc = rc;
5823 	}
5824 }
5825 
janus_videoroom_recorder_close(janus_videoroom_publisher * participant)5826 static void janus_videoroom_recorder_close(janus_videoroom_publisher *participant) {
5827 	if(participant->arc) {
5828 		janus_recorder *rc = participant->arc;
5829 		participant->arc = NULL;
5830 		janus_recorder_close(rc);
5831 		JANUS_LOG(LOG_INFO, "Closed audio recording %s\n", rc->filename ? rc->filename : "??");
5832 		janus_recorder_destroy(rc);
5833 	}
5834 	if(participant->vrc) {
5835 		janus_recorder *rc = participant->vrc;
5836 		participant->vrc = NULL;
5837 		janus_recorder_close(rc);
5838 		JANUS_LOG(LOG_INFO, "Closed video recording %s\n", rc->filename ? rc->filename : "??");
5839 		janus_recorder_destroy(rc);
5840 	}
5841 	if(participant->drc) {
5842 		janus_recorder *rc = participant->drc;
5843 		participant->drc = NULL;
5844 		janus_recorder_close(rc);
5845 		JANUS_LOG(LOG_INFO, "Closed data recording %s\n", rc->filename ? rc->filename : "??");
5846 		janus_recorder_destroy(rc);
5847 	}
5848 }
5849 
janus_videoroom_hangup_media(janus_plugin_session * handle)5850 void janus_videoroom_hangup_media(janus_plugin_session *handle) {
5851 	JANUS_LOG(LOG_INFO, "[%s-%p] No WebRTC media anymore; %p %p\n", JANUS_VIDEOROOM_PACKAGE, handle, handle->gateway_handle, handle->plugin_handle);
5852 	if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
5853 		return;
5854 	janus_mutex_lock(&sessions_mutex);
5855 	janus_videoroom_session *session = janus_videoroom_lookup_session(handle);
5856 	if(!session) {
5857 		JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
5858 		janus_mutex_unlock(&sessions_mutex);
5859 		return;
5860 	}
5861 	if(g_atomic_int_get(&session->destroyed)) {
5862 		janus_mutex_unlock(&sessions_mutex);
5863 		return;
5864 	}
5865 	janus_refcount_increase(&session->ref);
5866 	janus_mutex_unlock(&sessions_mutex);
5867 	janus_videoroom_hangup_media_internal(session);
5868 	janus_refcount_decrease(&session->ref);
5869 }
5870 
janus_videoroom_hangup_subscriber(janus_videoroom_subscriber * s)5871 static void janus_videoroom_hangup_subscriber(janus_videoroom_subscriber *s) {
5872 	/* Already hung up */
5873 	if (!s->feed) {
5874 		return;
5875 	}
5876 	/* Check if the owner needs to be cleaned up */
5877 	janus_videoroom *room = s->room;
5878 	if(room != NULL)
5879 		janus_refcount_increase(&room->ref);
5880 	if(s->pvt_id > 0 && room != NULL) {
5881 		janus_mutex_lock(&room->mutex);
5882 		janus_videoroom_publisher *owner = g_hash_table_lookup(room->private_ids, GUINT_TO_POINTER(s->pvt_id));
5883 		if(owner != NULL) {
5884 			janus_mutex_lock(&owner->own_subscriptions_mutex);
5885 			/* Note: we should refcount these subscription-publisher mappings as well */
5886 			owner->subscriptions = g_slist_remove(owner->subscriptions, s);
5887 			janus_mutex_unlock(&owner->own_subscriptions_mutex);
5888 		}
5889 		janus_mutex_unlock(&room->mutex);
5890 	}
5891 	janus_mutex_lock(&s->session->mutex);
5892 	if(s->feed)
5893 		g_clear_pointer(&s->feed, janus_videoroom_publisher_dereference_by_subscriber);
5894 	janus_mutex_unlock(&s->session->mutex);
5895 	/* Only "leave" the room if we're closing the PeerConnection at this point */
5896 	if(s->close_pc) {
5897 		if(s->room)
5898 			g_clear_pointer(&s->room, janus_videoroom_room_dereference);
5899 		if(s->session)
5900 			gateway->close_pc(s->session->handle);
5901 		/* Remove the reference we added when "joining" the room */
5902 		janus_refcount_decrease(&s->ref);
5903 	}
5904 	if(room != NULL)
5905 		janus_refcount_decrease(&room->ref);
5906 }
5907 
janus_videoroom_hangup_media_internal(gpointer session_data)5908 static void janus_videoroom_hangup_media_internal(gpointer session_data) {
5909 	janus_videoroom_session *session = (janus_videoroom_session *)session_data;
5910 	g_atomic_int_set(&session->started, 0);
5911 	if(!g_atomic_int_compare_and_exchange(&session->hangingup, 0, 1)) {
5912 		return;
5913 	}
5914 	g_atomic_int_set(&session->dataready, 0);
5915 	/* Send an event to the browser and tell the PeerConnection is over */
5916 	if(session->participant_type == janus_videoroom_p_type_publisher) {
5917 		/* This publisher just 'unpublished' */
5918 		janus_videoroom_publisher *participant = janus_videoroom_session_get_publisher(session);
5919 		/* Get rid of the recorders, if available */
5920 		janus_mutex_lock(&participant->rec_mutex);
5921 		g_free(participant->recording_base);
5922 		participant->recording_base = NULL;
5923 		janus_videoroom_recorder_close(participant);
5924 		janus_mutex_unlock(&participant->rec_mutex);
5925 		/* Use subscribers_mutex to protect fields used in janus_videoroom_incoming_rtp */
5926 		janus_mutex_lock(&participant->subscribers_mutex);
5927 		g_free(participant->sdp);
5928 		participant->sdp = NULL;
5929 		participant->firefox = FALSE;
5930 		participant->audio_active = FALSE;
5931 		participant->video_active = FALSE;
5932 		participant->data_active = FALSE;
5933 		participant->audio_active_packets = 0;
5934 		participant->user_audio_active_packets = 0;
5935 		participant->user_audio_level_average = 0;
5936 		participant->audio_dBov_sum = 0;
5937 		participant->audio_dBov_level = 0;
5938 		participant->talking = FALSE;
5939 		participant->remb_startup = 4;
5940 		participant->remb_latest = 0;
5941 		participant->fir_latest = 0;
5942 		participant->fir_seq = 0;
5943 		g_free(participant->vfmtp);
5944 		participant->vfmtp = NULL;
5945 		participant->acodec = JANUS_AUDIOCODEC_NONE;
5946 		participant->vcodec = JANUS_VIDEOCODEC_NONE;
5947 		int i=0;
5948 		for(i=0; i<3; i++) {
5949 			participant->ssrc[i] = 0;
5950 			g_free(participant->rid[i]);
5951 			participant->rid[i] = NULL;
5952 		}
5953 		GSList *subscribers = participant->subscribers;
5954 		participant->subscribers = NULL;
5955 		/* Hangup all subscribers */
5956 		while(subscribers) {
5957 			janus_videoroom_subscriber *s = (janus_videoroom_subscriber *)subscribers->data;
5958 			subscribers = g_slist_remove(subscribers, s);
5959 			if(s) {
5960 				janus_videoroom_hangup_subscriber(s);
5961 			}
5962 		}
5963 		participant->e2ee = FALSE;
5964 		janus_mutex_unlock(&participant->subscribers_mutex);
5965 		janus_videoroom_leave_or_unpublish(participant, FALSE, FALSE);
5966 		janus_refcount_decrease(&participant->ref);
5967 	} else if(session->participant_type == janus_videoroom_p_type_subscriber) {
5968 		/* Get rid of subscriber */
5969 		janus_videoroom_subscriber *subscriber = janus_videoroom_session_get_subscriber(session);
5970 		if(subscriber) {
5971 			subscriber->paused = TRUE;
5972 			janus_mutex_lock(&session->mutex);
5973 			janus_videoroom_publisher *publisher = subscriber->feed;
5974 			/* It is safe to use feed as the only other place sets feed to NULL
5975 			   is in this function and accessing to this function is synchronized
5976 			   by sessions_mutex */
5977 			if(publisher == NULL || g_atomic_int_get(&publisher->destroyed)) {
5978 				janus_mutex_unlock(&session->mutex);
5979 			} else {
5980 				janus_refcount_increase(&publisher->ref);
5981 				janus_mutex_unlock(&session->mutex);
5982 				/* Also notify event handlers */
5983 				if(notify_events && gateway->events_is_enabled()) {
5984 					json_t *info = json_object();
5985 					json_object_set_new(info, "event", json_string("unsubscribed"));
5986 					json_object_set_new(info, "room", string_ids ? json_string(publisher->room_id_str) : json_integer(publisher->room_id));
5987 					json_object_set_new(info, "feed", string_ids ? json_string(publisher->user_id_str) : json_integer(publisher->user_id));
5988 					gateway->notify_event(&janus_videoroom_plugin, session->handle, info);
5989 				}
5990 				janus_mutex_lock(&publisher->subscribers_mutex);
5991 				publisher->subscribers = g_slist_remove(publisher->subscribers, subscriber);
5992 				janus_videoroom_hangup_subscriber(subscriber);
5993 				janus_mutex_unlock(&publisher->subscribers_mutex);
5994 				janus_refcount_decrease(&publisher->ref);
5995 			}
5996 			subscriber->e2ee = FALSE;
5997 			janus_refcount_decrease(&subscriber->ref);
5998 		}
5999 		/* TODO Should we close the handle as well? */
6000 	}
6001 	g_atomic_int_set(&session->hangingup, 0);
6002 }
6003 
6004 /* Thread to handle incoming messages */
janus_videoroom_handler(void * data)6005 static void *janus_videoroom_handler(void *data) {
6006 	JANUS_LOG(LOG_VERB, "Joining VideoRoom handler thread\n");
6007 	janus_videoroom_message *msg = NULL;
6008 	int error_code = 0;
6009 	char error_cause[512];
6010 	json_t *root = NULL;
6011 	while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) {
6012 		msg = g_async_queue_pop(messages);
6013 		if(msg == &exit_message)
6014 			break;
6015 		if(msg->handle == NULL) {
6016 			janus_videoroom_message_free(msg);
6017 			continue;
6018 		}
6019 		janus_videoroom *videoroom = NULL;
6020 		janus_videoroom_publisher *participant = NULL;
6021 		janus_videoroom_subscriber *subscriber = NULL;
6022 		janus_mutex_lock(&sessions_mutex);
6023 		janus_videoroom_session *session = janus_videoroom_lookup_session(msg->handle);
6024 		if(!session) {
6025 			janus_mutex_unlock(&sessions_mutex);
6026 			JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
6027 			janus_videoroom_message_free(msg);
6028 			continue;
6029 		}
6030 		if(g_atomic_int_get(&session->destroyed)) {
6031 			janus_mutex_unlock(&sessions_mutex);
6032 			janus_videoroom_message_free(msg);
6033 			continue;
6034 		}
6035 		if(session->participant_type == janus_videoroom_p_type_subscriber) {
6036 			subscriber = janus_videoroom_session_get_subscriber(session);
6037 			if(subscriber == NULL || g_atomic_int_get(&subscriber->destroyed)) {
6038 				if(subscriber != NULL)
6039 					janus_refcount_decrease(&subscriber->ref);
6040 				janus_mutex_unlock(&sessions_mutex);
6041 				JANUS_LOG(LOG_ERR, "Invalid subscriber instance\n");
6042 				error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR;
6043 				g_snprintf(error_cause, 512, "Invalid subscriber instance");
6044 				goto error;
6045 			}
6046 			if(subscriber->room == NULL) {
6047 				janus_refcount_decrease(&subscriber->ref);
6048 				janus_mutex_unlock(&sessions_mutex);
6049 				JANUS_LOG(LOG_ERR, "No such room\n");
6050 				error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM;
6051 				g_snprintf(error_cause, 512, "No such room");
6052 				goto error;
6053 			}
6054 		}
6055 		janus_mutex_unlock(&sessions_mutex);
6056 		/* Handle request */
6057 		error_code = 0;
6058 		root = NULL;
6059 		if(msg->message == NULL) {
6060 			if(session->participant_type == janus_videoroom_p_type_subscriber) {
6061 				janus_refcount_decrease(&subscriber->ref);
6062 			}
6063 			JANUS_LOG(LOG_ERR, "No message??\n");
6064 			error_code = JANUS_VIDEOROOM_ERROR_NO_MESSAGE;
6065 			g_snprintf(error_cause, 512, "%s", "No message??");
6066 			goto error;
6067 		}
6068 		root = msg->message;
6069 		/* Get the request first */
6070 		JANUS_VALIDATE_JSON_OBJECT(root, request_parameters,
6071 			error_code, error_cause, TRUE,
6072 			JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
6073 		if(error_code != 0) {
6074 			if(session->participant_type == janus_videoroom_p_type_subscriber) {
6075 				janus_refcount_decrease(&subscriber->ref);
6076 			}
6077 			goto error;
6078 		}
6079 		json_t *request = json_object_get(root, "request");
6080 		const char *request_text = json_string_value(request);
6081 		json_t *event = NULL;
6082 		gboolean sdp_update = FALSE;
6083 		if(json_object_get(msg->jsep, "update") != NULL)
6084 			sdp_update = json_is_true(json_object_get(msg->jsep, "update"));
6085 		/* 'create' and 'destroy' are handled synchronously: what kind of participant is this session referring to? */
6086 		if(session->participant_type == janus_videoroom_p_type_none) {
6087 			JANUS_LOG(LOG_VERB, "Configuring new participant\n");
6088 			/* Not configured yet, we need to do this now */
6089 			if(strcasecmp(request_text, "join") && strcasecmp(request_text, "joinandconfigure")) {
6090 				JANUS_LOG(LOG_ERR, "Invalid request \"%s\" on unconfigured participant\n", request_text);
6091 				error_code = JANUS_VIDEOROOM_ERROR_JOIN_FIRST;
6092 				g_snprintf(error_cause, 512, "Invalid request on unconfigured participant");
6093 				goto error;
6094 			}
6095 			if(!string_ids) {
6096 				JANUS_VALIDATE_JSON_OBJECT(root, room_parameters,
6097 					error_code, error_cause, TRUE,
6098 					JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
6099 			} else {
6100 				JANUS_VALIDATE_JSON_OBJECT(root, roomstr_parameters,
6101 					error_code, error_cause, TRUE,
6102 					JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
6103 			}
6104 			if(error_code != 0)
6105 				goto error;
6106 			JANUS_VALIDATE_JSON_OBJECT(root, join_parameters,
6107 				error_code, error_cause, TRUE,
6108 				JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
6109 			if(error_code != 0)
6110 				goto error;
6111 			janus_mutex_lock(&rooms_mutex);
6112 			error_code = janus_videoroom_access_room(root, FALSE, TRUE, &videoroom, error_cause, sizeof(error_cause));
6113 			if(error_code != 0) {
6114 				janus_mutex_unlock(&rooms_mutex);
6115 				goto error;
6116 			}
6117 			janus_refcount_increase(&videoroom->ref);
6118 			janus_mutex_unlock(&rooms_mutex);
6119 			janus_mutex_lock(&videoroom->mutex);
6120 			json_t *ptype = json_object_get(root, "ptype");
6121 			const char *ptype_text = json_string_value(ptype);
6122 			if(!strcasecmp(ptype_text, "publisher")) {
6123 				JANUS_LOG(LOG_VERB, "Configuring new publisher\n");
6124 				JANUS_VALIDATE_JSON_OBJECT(root, publisher_parameters,
6125 					error_code, error_cause, TRUE,
6126 					JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
6127 				if(error_code != 0) {
6128 					janus_mutex_unlock(&videoroom->mutex);
6129 					janus_refcount_decrease(&videoroom->ref);
6130 					goto error;
6131 				}
6132 				if(!string_ids) {
6133 					JANUS_VALIDATE_JSON_OBJECT(root, idopt_parameters,
6134 						error_code, error_cause, TRUE,
6135 						JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
6136 				} else {
6137 					JANUS_VALIDATE_JSON_OBJECT(root, idstropt_parameters,
6138 						error_code, error_cause, TRUE,
6139 						JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
6140 				}
6141 				if(error_code != 0) {
6142 					janus_mutex_unlock(&videoroom->mutex);
6143 					janus_refcount_decrease(&videoroom->ref);
6144 					goto error;
6145 				}
6146 				/* A token might be required to join */
6147 				if(videoroom->check_allowed) {
6148 					json_t *token = json_object_get(root, "token");
6149 					const char *token_text = token ? json_string_value(token) : NULL;
6150 					if(token_text == NULL || g_hash_table_lookup(videoroom->allowed, token_text) == NULL) {
6151 						janus_mutex_unlock(&videoroom->mutex);
6152 						janus_refcount_decrease(&videoroom->ref);
6153 						JANUS_LOG(LOG_ERR, "Unauthorized (not in the allowed list)\n");
6154 						error_code = JANUS_VIDEOROOM_ERROR_UNAUTHORIZED;
6155 						g_snprintf(error_cause, 512, "Unauthorized (not in the allowed list)");
6156 						goto error;
6157 					}
6158 				}
6159 				json_t *display = json_object_get(root, "display");
6160 				const char *display_text = display ? json_string_value(display) : NULL;
6161 				guint64 user_id = 0;
6162 				char user_id_num[30], *user_id_str = NULL;
6163 				gboolean user_id_allocated = FALSE;
6164 				json_t *id = json_object_get(root, "id");
6165 				if(id) {
6166 					if(!string_ids) {
6167 						user_id = json_integer_value(id);
6168 						g_snprintf(user_id_num, sizeof(user_id_num), "%"SCNu64, user_id);
6169 						user_id_str = user_id_num;
6170 					} else {
6171 						user_id_str = (char *)json_string_value(id);
6172 					}
6173 					if(g_hash_table_lookup(videoroom->participants,
6174 							string_ids ? (gpointer)user_id_str : (gpointer)&user_id) != NULL) {
6175 						/* User ID already taken */
6176 						janus_mutex_unlock(&videoroom->mutex);
6177 						janus_refcount_decrease(&videoroom->ref);
6178 						error_code = JANUS_VIDEOROOM_ERROR_ID_EXISTS;
6179 						JANUS_LOG(LOG_ERR, "User ID %s already exists\n", user_id_str);
6180 						g_snprintf(error_cause, 512, "User ID %s already exists", user_id_str);
6181 						goto error;
6182 					}
6183 				}
6184 				if(!string_ids) {
6185 					if(user_id == 0) {
6186 						/* Generate a random ID */
6187 						while(user_id == 0) {
6188 							user_id = janus_random_uint64();
6189 							if(g_hash_table_lookup(videoroom->participants, &user_id) != NULL) {
6190 								/* User ID already taken, try another one */
6191 								user_id = 0;
6192 							}
6193 						}
6194 						g_snprintf(user_id_num, sizeof(user_id_num), "%"SCNu64, user_id);
6195 						user_id_str = user_id_num;
6196 					}
6197 					JANUS_LOG(LOG_VERB, "  -- Participant ID: %"SCNu64"\n", user_id);
6198 				} else {
6199 					if(user_id_str == NULL) {
6200 						/* Generate a random ID */
6201 						while(user_id_str == NULL) {
6202 							user_id_str = janus_random_uuid();
6203 							if(g_hash_table_lookup(videoroom->participants, user_id_str) != NULL) {
6204 								/* User ID already taken, try another one */
6205 								g_clear_pointer(&user_id_str, g_free);
6206 							}
6207 						}
6208 						user_id_allocated = TRUE;
6209 					}
6210 					JANUS_LOG(LOG_VERB, "  -- Participant ID: %s\n", user_id_str);
6211 				}
6212 				/* Process the request */
6213 				json_t *audio = NULL, *video = NULL, *data = NULL,
6214 					*audiocodec = NULL, *videocodec = NULL,
6215 					*bitrate = NULL, *record = NULL, *recfile = NULL,
6216 					*user_audio_active_packets = NULL, *user_audio_level_average = NULL;
6217 				if(!strcasecmp(request_text, "joinandconfigure")) {
6218 					/* Also configure (or publish a new feed) audio/video/bitrate for this new publisher */
6219 					/* join_parameters were validated earlier. */
6220 					audio = json_object_get(root, "audio");
6221 					audiocodec = json_object_get(root, "audiocodec");
6222 					video = json_object_get(root, "video");
6223 					videocodec = json_object_get(root, "videocodec");
6224 					data = json_object_get(root, "data");
6225 					bitrate = json_object_get(root, "bitrate");
6226 					record = json_object_get(root, "record");
6227 					recfile = json_object_get(root, "filename");
6228 				}
6229 				user_audio_active_packets = json_object_get(root, "audio_active_packets");
6230 				user_audio_level_average = json_object_get(root, "audio_level_average");
6231 				janus_videoroom_publisher *publisher = g_malloc0(sizeof(janus_videoroom_publisher));
6232 				publisher->session = session;
6233 				publisher->room_id = videoroom->room_id;
6234 				publisher->room_id_str = videoroom->room_id_str ? g_strdup(videoroom->room_id_str) : NULL;
6235 				publisher->room = videoroom;
6236 				videoroom = NULL;
6237 				publisher->user_id = user_id;
6238 				publisher->user_id_str = user_id_str ? g_strdup(user_id_str) : NULL;
6239 				publisher->display = display_text ? g_strdup(display_text) : NULL;
6240 				publisher->sdp = NULL;		/* We'll deal with this later */
6241 				publisher->audio = FALSE;	/* We'll deal with this later */
6242 				publisher->video = FALSE;	/* We'll deal with this later */
6243 				publisher->data = FALSE;	/* We'll deal with this later */
6244 				publisher->acodec = JANUS_AUDIOCODEC_NONE;	/* We'll deal with this later */
6245 				publisher->vcodec = JANUS_VIDEOCODEC_NONE;	/* We'll deal with this later */
6246 				publisher->audio_active = TRUE;
6247 				publisher->video_active = TRUE;
6248 				publisher->data_active = TRUE;
6249 				publisher->recording_active = FALSE;
6250 				publisher->recording_base = NULL;
6251 				publisher->arc = NULL;
6252 				publisher->vrc = NULL;
6253 				publisher->drc = NULL;
6254 				janus_mutex_init(&publisher->rec_mutex);
6255 				publisher->firefox = FALSE;
6256 				publisher->bitrate = publisher->room->bitrate;
6257 				publisher->subscribers = NULL;
6258 				publisher->subscriptions = NULL;
6259 				janus_mutex_init(&publisher->subscribers_mutex);
6260 				janus_mutex_init(&publisher->own_subscriptions_mutex);
6261 				publisher->audio_pt = -1;	/* We'll deal with this later */
6262 				publisher->video_pt = -1;	/* We'll deal with this later */
6263 				publisher->audio_level_extmap_id = 0;
6264 				publisher->video_orient_extmap_id = 0;
6265 				publisher->playout_delay_extmap_id = 0;
6266 				publisher->remb_startup = 4;
6267 				publisher->remb_latest = 0;
6268 				publisher->fir_latest = 0;
6269 				publisher->fir_seq = 0;
6270 				janus_mutex_init(&publisher->rtp_forwarders_mutex);
6271 				publisher->rtp_forwarders = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)janus_videoroom_rtp_forwarder_destroy);
6272 				publisher->srtp_contexts = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)janus_videoroom_srtp_context_free);
6273 				publisher->udp_sock = -1;
6274 				/* Finally, generate a private ID: this is only needed in case the participant
6275 				 * wants to allow the plugin to know which subscriptions belong to them */
6276 				publisher->pvt_id = 0;
6277 				while(publisher->pvt_id == 0) {
6278 					publisher->pvt_id = janus_random_uint32();
6279 					if(g_hash_table_lookup(publisher->room->private_ids, GUINT_TO_POINTER(publisher->pvt_id)) != NULL) {
6280 						/* Private ID already taken, try another one */
6281 						publisher->pvt_id = 0;
6282 					}
6283 				}
6284 				g_atomic_int_set(&publisher->destroyed, 0);
6285 				janus_refcount_init(&publisher->ref, janus_videoroom_publisher_free);
6286 				/* In case we also wanted to configure */
6287 				if(audio) {
6288 					publisher->audio_active = json_is_true(audio);
6289 					JANUS_LOG(LOG_VERB, "Setting audio property: %s (room %s, user %s)\n",
6290 						publisher->audio_active ? "true" : "false", publisher->room_id_str, publisher->user_id_str);
6291 				}
6292 				if(audiocodec && json_string_value(json_object_get(msg->jsep, "sdp")) != NULL) {
6293 					/* The publisher would like to use an audio codec in particular */
6294 					janus_audiocodec acodec = janus_audiocodec_from_name(json_string_value(audiocodec));
6295 					if(acodec == JANUS_AUDIOCODEC_NONE ||
6296 							(acodec != publisher->room->acodec[0] &&
6297 							acodec != publisher->room->acodec[1] &&
6298 							acodec != publisher->room->acodec[2] &&
6299 							acodec != publisher->room->acodec[3] &&
6300 							acodec != publisher->room->acodec[4])) {
6301 						JANUS_LOG(LOG_ERR, "Participant asked for audio codec '%s', but it's not allowed (room %s, user %s)\n",
6302 							json_string_value(audiocodec), publisher->room_id_str, publisher->user_id_str);
6303 						janus_mutex_unlock(&publisher->room->mutex);
6304 						janus_refcount_decrease(&publisher->room->ref);
6305 						janus_refcount_decrease(&publisher->ref);
6306 						error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
6307 						g_snprintf(error_cause, 512, "Audio codec unavailable in this room");
6308 						goto error;
6309 					}
6310 					publisher->acodec = acodec;
6311 					JANUS_LOG(LOG_VERB, "Participant asked for audio codec '%s' (room %s, user %s)\n",
6312 						json_string_value(audiocodec), publisher->room_id_str, publisher->user_id_str);
6313 				}
6314 				if(video) {
6315 					publisher->video_active = json_is_true(video);
6316 					JANUS_LOG(LOG_VERB, "Setting video property: %s (room %s, user %s)\n",
6317 						publisher->video_active ? "true" : "false", publisher->room_id_str, publisher->user_id_str);
6318 				}
6319 				if(videocodec && json_string_value(json_object_get(msg->jsep, "sdp")) != NULL) {
6320 					/* The publisher would like to use a video codec in particular */
6321 					janus_videocodec vcodec = janus_videocodec_from_name(json_string_value(videocodec));
6322 					if(vcodec == JANUS_VIDEOCODEC_NONE ||
6323 							(vcodec != publisher->room->vcodec[0] &&
6324 							vcodec != publisher->room->vcodec[1] &&
6325 							vcodec != publisher->room->vcodec[2] &&
6326 							vcodec != publisher->room->vcodec[3] &&
6327 							vcodec != publisher->room->vcodec[4])) {
6328 						JANUS_LOG(LOG_ERR, "Participant asked for video codec '%s', but it's not allowed (room %s, user %s)\n",
6329 							json_string_value(videocodec), publisher->room_id_str, publisher->user_id_str);
6330 						janus_mutex_unlock(&publisher->room->mutex);
6331 						janus_refcount_decrease(&publisher->room->ref);
6332 						janus_refcount_decrease(&publisher->ref);
6333 						error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
6334 						g_snprintf(error_cause, 512, "Video codec unavailable in this room");
6335 						goto error;
6336 					}
6337 					publisher->vcodec = vcodec;
6338 					JANUS_LOG(LOG_VERB, "Participant asked for video codec '%s' (room %s, user %s)\n",
6339 						json_string_value(videocodec), publisher->room_id_str, publisher->user_id_str);
6340 				}
6341 				if(data) {
6342 					publisher->data_active = json_is_true(data);
6343 					JANUS_LOG(LOG_VERB, "Setting data property: %s (room %s, user %s)\n",
6344 						publisher->data_active ? "true" : "false", publisher->room_id_str, publisher->user_id_str);
6345 				}
6346 				if(bitrate) {
6347 					publisher->bitrate = json_integer_value(bitrate);
6348 					JANUS_LOG(LOG_VERB, "Setting video bitrate: %"SCNu32" (room %s, user %s)\n",
6349 						publisher->bitrate, publisher->room_id_str, publisher->user_id_str);
6350 				}
6351 				if(record) {
6352 					publisher->recording_active = json_is_true(record);
6353 					JANUS_LOG(LOG_VERB, "Setting record property: %s (room %s, user %s)\n",
6354 						publisher->recording_active ? "true" : "false", publisher->room_id_str, publisher->user_id_str);
6355 				}
6356 				if(recfile) {
6357 					publisher->recording_base = g_strdup(json_string_value(recfile));
6358 					JANUS_LOG(LOG_VERB, "Setting recording basename: %s (room %s, user %s)\n",
6359 						publisher->recording_base, publisher->room_id_str, publisher->user_id_str);
6360 				}
6361 				if(user_audio_active_packets) {
6362 					publisher->user_audio_active_packets = json_integer_value(user_audio_active_packets);
6363 					JANUS_LOG(LOG_VERB, "Setting user audio_active_packets: %d (room %s, user %s)\n",
6364 						publisher->user_audio_active_packets, publisher->room_id_str, publisher->user_id_str);
6365 				}
6366 				if(user_audio_level_average) {
6367 					publisher->user_audio_level_average = json_integer_value(user_audio_level_average);
6368 					JANUS_LOG(LOG_VERB, "Setting user audio_level_average: %d (room %s, user %s)\n",
6369 						publisher->user_audio_level_average, publisher->room_id_str, publisher->user_id_str);
6370 				}
6371 				/* Done */
6372 				janus_mutex_lock(&session->mutex);
6373 				/* Make sure the session has not been destroyed in the meanwhile */
6374 				if(g_atomic_int_get(&session->destroyed)) {
6375 					janus_mutex_unlock(&session->mutex);
6376 					janus_mutex_unlock(&publisher->room->mutex);
6377 					janus_refcount_decrease(&publisher->room->ref);
6378 					janus_videoroom_publisher_destroy(publisher);
6379 					JANUS_LOG(LOG_ERR, "Session destroyed, invalidating new publisher\n");
6380 					error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR;
6381 					g_snprintf(error_cause, 512, "Session destroyed, invalidating new publisher");
6382 					goto error;
6383 				}
6384 				session->participant_type = janus_videoroom_p_type_publisher;
6385 				session->participant = publisher;
6386 				/* Return a list of all available publishers (those with an SDP available, that is) */
6387 				json_t *list = json_array(), *attendees = NULL;
6388 				if(publisher->room->notify_joining)
6389 					attendees = json_array();
6390 				GHashTableIter iter;
6391 				gpointer value;
6392 				janus_refcount_increase(&publisher->ref);
6393 				g_hash_table_insert(publisher->room->participants,
6394 					string_ids ? (gpointer)g_strdup(publisher->user_id_str) : (gpointer)janus_uint64_dup(publisher->user_id),
6395 					publisher);
6396 				g_hash_table_insert(publisher->room->private_ids, GUINT_TO_POINTER(publisher->pvt_id), publisher);
6397 				janus_mutex_unlock(&session->mutex);
6398 				g_hash_table_iter_init(&iter, publisher->room->participants);
6399 				while (!g_atomic_int_get(&publisher->room->destroyed) && g_hash_table_iter_next(&iter, NULL, &value)) {
6400 					janus_videoroom_publisher *p = value;
6401 					if(p == publisher || !p->sdp || !g_atomic_int_get(&p->session->started)) {
6402 						/* Check if we're also notifying normal joins and not just publishers */
6403 						if(p != publisher && publisher->room->notify_joining) {
6404 							json_t *al = json_object();
6405 							json_object_set_new(al, "id", string_ids ? json_string(p->user_id_str) : json_integer(p->user_id));
6406 							if(p->display)
6407 								json_object_set_new(al, "display", json_string(p->display));
6408 							json_array_append_new(attendees, al);
6409 						}
6410 						continue;
6411 					}
6412 					json_t *pl = json_object();
6413 					json_object_set_new(pl, "id", string_ids ? json_string(p->user_id_str) : json_integer(p->user_id));
6414 					if(p->display)
6415 						json_object_set_new(pl, "display", json_string(p->display));
6416 					if(p->audio)
6417 						json_object_set_new(pl, "audio_codec", json_string(janus_audiocodec_name(p->acodec)));
6418 					if(p->video)
6419 						json_object_set_new(pl, "video_codec", json_string(janus_videocodec_name(p->vcodec)));
6420 					if(p->audio_muted)
6421 						json_object_set_new(pl, "audio_moderated", json_true());
6422 					if(p->video_muted)
6423 						json_object_set_new(pl, "video_moderated", json_true());
6424 					if(p->data_muted)
6425 						json_object_set_new(pl, "data_moderated", json_true());
6426 					if(p->ssrc[0] || p->rid[0])
6427 						json_object_set_new(pl, "simulcast", json_true());
6428 					if(p->audio_level_extmap_id > 0)
6429 						json_object_set_new(pl, "talking", p->talking ? json_true() : json_false());
6430 					json_array_append_new(list, pl);
6431 				}
6432 				event = json_object();
6433 				json_object_set_new(event, "videoroom", json_string("joined"));
6434 				json_object_set_new(event, "room", string_ids ? json_string(publisher->room->room_id_str) :
6435 					json_integer(publisher->room->room_id));
6436 				json_object_set_new(event, "description", json_string(publisher->room->room_name));
6437 				json_object_set_new(event, "id", string_ids ? json_string(user_id_str) : json_integer(user_id));
6438 				json_object_set_new(event, "private_id", json_integer(publisher->pvt_id));
6439 				json_object_set_new(event, "publishers", list);
6440 				if(publisher->user_audio_active_packets)
6441 					json_object_set_new(event, "audio_active_packets", json_integer(publisher->user_audio_active_packets));
6442 				if(publisher->user_audio_level_average)
6443 					json_object_set_new(event, "audio_level_average", json_integer(publisher->user_audio_level_average));
6444 				if(attendees != NULL)
6445 					json_object_set_new(event, "attendees", attendees);
6446 				/* See if we need to notify about a new participant joined the room (by default, we don't). */
6447 				janus_videoroom_participant_joining(publisher);
6448 
6449 				/* Also notify event handlers */
6450 				if(notify_events && gateway->events_is_enabled()) {
6451 					json_t *info = json_object();
6452 					json_object_set_new(info, "event", json_string("joined"));
6453 					json_object_set_new(info, "room", string_ids ? json_string(publisher->room->room_id_str) :
6454 					json_integer(publisher->room->room_id));
6455 					json_object_set_new(info, "id", string_ids ? json_string(user_id_str) : json_integer(user_id));
6456 					json_object_set_new(info, "private_id", json_integer(publisher->pvt_id));
6457                     if(publisher->room->check_allowed) {
6458                         const char *token = json_string_value(json_object_get(root, "token"));
6459                         json_object_set_new(info, "token", json_string(token));
6460                     }
6461                     if(display_text != NULL)
6462 						json_object_set_new(info, "display", json_string(display_text));
6463 					if(publisher->user_audio_active_packets)
6464 						json_object_set_new(info, "audio_active_packets", json_integer(publisher->user_audio_active_packets));
6465 					if(publisher->user_audio_level_average)
6466 						json_object_set_new(info, "audio_level_average", json_integer(publisher->user_audio_level_average));
6467 					gateway->notify_event(&janus_videoroom_plugin, session->handle, info);
6468 				}
6469 				janus_mutex_unlock(&publisher->room->mutex);
6470 				if(user_id_allocated)
6471 					g_free(user_id_str);
6472 			} else if(!strcasecmp(ptype_text, "subscriber") || !strcasecmp(ptype_text, "listener")) {
6473 				JANUS_LOG(LOG_VERB, "Configuring new subscriber\n");
6474 				gboolean legacy = !strcasecmp(ptype_text, "listener");
6475 				if(legacy) {
6476 					JANUS_LOG(LOG_WARN, "Subscriber is using the legacy 'listener' ptype\n");
6477 				}
6478 				/* This is a new subscriber */
6479 				JANUS_VALIDATE_JSON_OBJECT(root, subscriber_parameters,
6480 					error_code, error_cause, TRUE,
6481 					JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
6482 				if(error_code != 0) {
6483 					janus_mutex_unlock(&videoroom->mutex);
6484 					janus_refcount_decrease(&videoroom->ref);
6485 					goto error;
6486 				}
6487 				if(!string_ids) {
6488 					JANUS_VALIDATE_JSON_OBJECT(root, feed_parameters,
6489 						error_code, error_cause, TRUE,
6490 						JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
6491 				} else {
6492 					JANUS_VALIDATE_JSON_OBJECT(root, feedstr_parameters,
6493 						error_code, error_cause, TRUE,
6494 						JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
6495 				}
6496 				if(error_code != 0) {
6497 					janus_mutex_unlock(&videoroom->mutex);
6498 					janus_refcount_decrease(&videoroom->ref);
6499 					goto error;
6500 				}
6501 				janus_mutex_lock(&sessions_mutex);
6502 				session = janus_videoroom_lookup_session(msg->handle);
6503 				if(!session) {
6504 					janus_mutex_unlock(&sessions_mutex);
6505 					janus_mutex_unlock(&videoroom->mutex);
6506 					janus_refcount_decrease(&videoroom->ref);
6507 					JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
6508 					janus_videoroom_message_free(msg);
6509 					continue;
6510 				}
6511 				if(g_atomic_int_get(&session->destroyed)) {
6512 					janus_mutex_unlock(&sessions_mutex);
6513 					janus_mutex_unlock(&videoroom->mutex);
6514 					janus_refcount_decrease(&videoroom->ref);
6515 					janus_videoroom_message_free(msg);
6516 					continue;
6517 				}
6518 				json_t *feed = json_object_get(root, "feed");
6519 				guint64 feed_id = 0;
6520 				char feed_id_num[30], *feed_id_str = NULL;
6521 				if(!string_ids) {
6522 					feed_id = json_integer_value(feed);
6523 					g_snprintf(feed_id_num, sizeof(feed_id_num), "%"SCNu64, feed_id);
6524 					feed_id_str = feed_id_num;
6525 				} else {
6526 					feed_id_str = (char *)json_string_value(feed);
6527 				}
6528 				json_t *pvt = json_object_get(root, "private_id");
6529 				guint64 pvt_id = json_integer_value(pvt);
6530 				json_t *cpc = json_object_get(root, "close_pc");
6531 				gboolean close_pc  = cpc ? json_is_true(cpc) : TRUE;
6532 				json_t *audio = json_object_get(root, "audio");
6533 				json_t *video = json_object_get(root, "video");
6534 				json_t *data = json_object_get(root, "data");
6535 				json_t *offer_audio = json_object_get(root, "offer_audio");
6536 				json_t *offer_video = json_object_get(root, "offer_video");
6537 				json_t *offer_data = json_object_get(root, "offer_data");
6538 				json_t *spatial = json_object_get(root, "spatial_layer");
6539 				json_t *sc_substream = json_object_get(root, "substream");
6540 				if(json_integer_value(spatial) < 0 || json_integer_value(spatial) > 2 ||
6541 						json_integer_value(sc_substream) < 0 || json_integer_value(sc_substream) > 2) {
6542 					JANUS_LOG(LOG_ERR, "Invalid element (substream/spatial_layer should be 0, 1 or 2)\n");
6543 					error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
6544 					g_snprintf(error_cause, 512, "Invalid value (substream/spatial_layer should be 0, 1 or 2)");
6545 					janus_mutex_unlock(&sessions_mutex);
6546 					janus_mutex_unlock(&videoroom->mutex);
6547 					janus_refcount_decrease(&videoroom->ref);
6548 					goto error;
6549 				}
6550 				json_t *temporal = json_object_get(root, "temporal_layer");
6551 				json_t *sc_temporal = json_object_get(root, "temporal");
6552 				if(json_integer_value(temporal) < 0 || json_integer_value(temporal) > 2 ||
6553 						json_integer_value(sc_temporal) < 0 || json_integer_value(sc_temporal) > 2) {
6554 					JANUS_LOG(LOG_ERR, "Invalid element (temporal/temporal_layer should be 0, 1 or 2)\n");
6555 					error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
6556 					g_snprintf(error_cause, 512, "Invalid value (temporal/temporal_layer should be 0, 1 or 2)");
6557 					janus_mutex_unlock(&sessions_mutex);
6558 					janus_mutex_unlock(&videoroom->mutex);
6559 					janus_refcount_decrease(&videoroom->ref);
6560 					goto error;
6561 				}
6562 				json_t *sc_fallback = json_object_get(root, "fallback");
6563 				janus_videoroom_publisher *owner = NULL;
6564 				janus_videoroom_publisher *publisher = g_hash_table_lookup(videoroom->participants,
6565 					string_ids ? (gpointer)feed_id_str : (gpointer)&feed_id);
6566 				if(publisher == NULL || g_atomic_int_get(&publisher->destroyed) || publisher->sdp == NULL) {
6567 					JANUS_LOG(LOG_ERR, "No such feed (%s)\n", feed_id_str);
6568 					error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_FEED;
6569 					g_snprintf(error_cause, 512, "No such feed (%s)", feed_id_str);
6570 					janus_mutex_unlock(&sessions_mutex);
6571 					janus_mutex_unlock(&videoroom->mutex);
6572 					janus_refcount_decrease(&videoroom->ref);
6573 					goto error;
6574 				} else {
6575 					/* Increase the refcount before unlocking so that nobody can remove and free the publisher in the meantime. */
6576 					janus_refcount_increase(&publisher->ref);
6577 					janus_refcount_increase(&publisher->session->ref);
6578 					/* First of all, let's check if this room requires valid private_id values */
6579 					if(videoroom->require_pvtid) {
6580 						/* It does, let's make sure this subscription complies */
6581 						owner = g_hash_table_lookup(videoroom->private_ids, GUINT_TO_POINTER(pvt_id));
6582 						if(pvt_id == 0 || owner == NULL) {
6583 							JANUS_LOG(LOG_ERR, "Unauthorized (this room requires a valid private_id)\n");
6584 							error_code = JANUS_VIDEOROOM_ERROR_UNAUTHORIZED;
6585 							g_snprintf(error_cause, 512, "Unauthorized (this room requires a valid private_id)");
6586 							janus_refcount_decrease(&publisher->session->ref);
6587 							janus_refcount_decrease(&publisher->ref);
6588 							janus_mutex_unlock(&sessions_mutex);
6589 							janus_mutex_unlock(&videoroom->mutex);
6590 							janus_refcount_decrease(&videoroom->ref);
6591 							goto error;
6592 						}
6593 						janus_refcount_increase(&owner->ref);
6594 						janus_refcount_increase(&owner->session->ref);
6595 					}
6596 					janus_mutex_unlock(&videoroom->mutex);
6597 					janus_videoroom_subscriber *subscriber = g_malloc0(sizeof(janus_videoroom_subscriber));
6598 					subscriber->session = session;
6599 					subscriber->room_id = videoroom->room_id;
6600 					subscriber->room_id_str = videoroom->room_id_str ? g_strdup(videoroom->room_id_str) : NULL;
6601 					subscriber->room = videoroom;
6602 					videoroom = NULL;
6603 					subscriber->feed = publisher;
6604 					subscriber->e2ee = publisher->e2ee;
6605 					subscriber->pvt_id = pvt_id;
6606 					subscriber->close_pc = close_pc;
6607 					/* Initialize the subscriber context */
6608 					janus_rtp_switching_context_reset(&subscriber->context);
6609 					subscriber->audio_offered = offer_audio ? json_is_true(offer_audio) : TRUE;	/* True by default */
6610 					subscriber->video_offered = offer_video ? json_is_true(offer_video) : TRUE;	/* True by default */
6611 					subscriber->data_offered = offer_data ? json_is_true(offer_data) : TRUE;	/* True by default */
6612 					if((!publisher->audio || !subscriber->audio_offered) &&
6613 							(!publisher->video || !subscriber->video_offered) &&
6614 							(!publisher->data || !subscriber->data_offered)) {
6615 						if(owner) {
6616 							janus_refcount_decrease(&owner->session->ref);
6617 							janus_refcount_decrease(&owner->ref);
6618 						}
6619 						janus_refcount_decrease(&publisher->session->ref);
6620 						janus_refcount_decrease(&publisher->ref);
6621 						JANUS_LOG(LOG_ERR, "Can't offer an SDP with no audio, video or data\n");
6622 						error_code = JANUS_VIDEOROOM_ERROR_INVALID_SDP;
6623 						g_snprintf(error_cause, 512, "Can't offer an SDP with no audio, video or data");
6624 						janus_mutex_unlock(&sessions_mutex);
6625 						janus_refcount_decrease(&subscriber->room->ref);
6626 						g_free(subscriber);
6627 						goto error;
6628 					}
6629 					subscriber->audio = audio ? json_is_true(audio) : TRUE;	/* True by default */
6630 					if(!publisher->audio || !subscriber->audio_offered)
6631 						subscriber->audio = FALSE;	/* ... unless the publisher isn't sending any audio or we're skipping it */
6632 					subscriber->video = video ? json_is_true(video) : TRUE;	/* True by default */
6633 					if(!publisher->video || !subscriber->video_offered)
6634 						subscriber->video = FALSE;	/* ... unless the publisher isn't sending any video or we're skipping it */
6635 					subscriber->data = data ? json_is_true(data) : TRUE;	/* True by default */
6636 					if(!publisher->data || !subscriber->data_offered)
6637 						subscriber->data = FALSE;	/* ... unless the publisher isn't sending any data or we're skipping it */
6638 					subscriber->paused = TRUE;	/* We need an explicit start from the subscriber */
6639 					g_atomic_int_set(&subscriber->destroyed, 0);
6640 					janus_refcount_init(&subscriber->ref, janus_videoroom_subscriber_free);
6641 					janus_refcount_increase(&subscriber->ref);	/* This reference is for handling the setup */
6642 					janus_refcount_increase(&subscriber->ref);	/* The publisher references the new subscriber too */
6643 					/* Check if a simulcasting-related request is involved */
6644 					janus_rtp_simulcasting_context_reset(&subscriber->sim_context);
6645 					subscriber->sim_context.rid_ext_id = publisher->rid_extmap_id;
6646 					subscriber->sim_context.substream_target = sc_substream ? json_integer_value(sc_substream) : 2;
6647 					subscriber->sim_context.templayer_target = sc_temporal ? json_integer_value(sc_temporal) : 2;
6648 					subscriber->sim_context.drop_trigger = sc_fallback ? json_integer_value(sc_fallback) : 0;
6649 					janus_vp8_simulcast_context_reset(&subscriber->vp8_context);
6650 					/* Check if a VP9 SVC-related request is involved */
6651 					if(subscriber->room->do_svc) {
6652 						subscriber->spatial_layer = -1;
6653 						subscriber->target_spatial_layer = spatial ? json_integer_value(spatial) : 2;
6654 						subscriber->temporal_layer = -1;
6655 						subscriber->target_temporal_layer = temporal ? json_integer_value(temporal) : 2;
6656 					}
6657 					session->participant = subscriber;
6658 					janus_mutex_lock(&publisher->subscribers_mutex);
6659 					publisher->subscribers = g_slist_append(publisher->subscribers, subscriber);
6660 					janus_mutex_unlock(&publisher->subscribers_mutex);
6661 					if(owner != NULL) {
6662 						/* Note: we should refcount these subscription-publisher mappings as well */
6663 						janus_mutex_lock(&owner->own_subscriptions_mutex);
6664 						owner->subscriptions = g_slist_append(owner->subscriptions, subscriber);
6665 						janus_mutex_unlock(&owner->own_subscriptions_mutex);
6666 						/* Done adding the subscription, owner is safe to be released */
6667 						janus_refcount_decrease(&owner->session->ref);
6668 						janus_refcount_decrease(&owner->ref);
6669 					}
6670 					session->participant_type = janus_videoroom_p_type_subscriber;
6671 					janus_mutex_unlock(&sessions_mutex);
6672 					event = json_object();
6673 					json_object_set_new(event, "videoroom", json_string("attached"));
6674 					json_object_set_new(event, "room", string_ids ? json_string(subscriber->room_id_str) : json_integer(subscriber->room_id));
6675 					json_object_set_new(event, "id", string_ids ? json_string(feed_id_str) : json_integer(feed_id));
6676 					if(publisher->display)
6677 						json_object_set_new(event, "display", json_string(publisher->display));
6678 					if(legacy)
6679 						json_object_set_new(event, "warning", json_string("Deprecated use of 'listener' ptype, update to the new 'subscriber' ASAP"));
6680 					JANUS_LOG(LOG_VERB, "Preparing JSON event as a reply\n");
6681 					/* Negotiate by sending the selected publisher SDP back */
6682 					janus_mutex_lock(&publisher->subscribers_mutex);
6683 					if(publisher->sdp != NULL) {
6684 						/* Check if there's something the original SDP has that we should remove */
6685 						janus_sdp *offer = janus_sdp_parse(publisher->sdp, NULL, 0);
6686 						subscriber->sdp = offer;
6687 						session->sdp_version = 1;
6688 						subscriber->sdp->o_version = session->sdp_version;
6689 						if((publisher->audio && !subscriber->audio_offered) ||
6690 								(publisher->video && !subscriber->video_offered) ||
6691 								(publisher->data && !subscriber->data_offered)) {
6692 							JANUS_LOG(LOG_VERB, "Munging SDP offer to adapt it to the subscriber's requirements\n");
6693 							if(publisher->audio && !subscriber->audio_offered)
6694 								janus_sdp_mline_remove(offer, JANUS_SDP_AUDIO);
6695 							if(publisher->video && !subscriber->video_offered)
6696 								janus_sdp_mline_remove(offer, JANUS_SDP_VIDEO);
6697 							if(publisher->data && !subscriber->data_offered)
6698 								janus_sdp_mline_remove(offer, JANUS_SDP_APPLICATION);
6699 						}
6700 						char* sdp = janus_sdp_write(offer);
6701 						json_t *jsep = json_pack("{ssss}", "type", "offer", "sdp", sdp);
6702 						g_free(sdp);
6703 						if(subscriber->e2ee)
6704 							json_object_set_new(jsep, "e2ee", json_true());
6705 						janus_mutex_unlock(&publisher->subscribers_mutex);
6706 						/* How long will the Janus core take to push the event? */
6707 						g_atomic_int_set(&session->hangingup, 0);
6708 						gint64 start = janus_get_monotonic_time();
6709 						int res = gateway->push_event(msg->handle, &janus_videoroom_plugin, msg->transaction, event, jsep);
6710 						JANUS_LOG(LOG_VERB, "  >> Pushing event: %d (took %"SCNu64" us)\n", res, janus_get_monotonic_time()-start);
6711 						/* Also notify event handlers */
6712 						if(notify_events && gateway->events_is_enabled()) {
6713 							json_t *info = json_object();
6714 							json_object_set_new(info, "event", json_string("subscribing"));
6715 							json_object_set_new(info, "room", string_ids ? json_string(subscriber->room_id_str) : json_integer(subscriber->room_id));
6716 							json_object_set_new(info, "feed", string_ids ? json_string(feed_id_str) : json_integer(feed_id));
6717 							json_object_set_new(info, "private_id", json_integer(pvt_id));
6718 							gateway->notify_event(&janus_videoroom_plugin, session->handle, info);
6719 						}
6720 						json_decref(event);
6721 						json_decref(jsep);
6722 						janus_videoroom_message_free(msg);
6723 						janus_refcount_decrease(&subscriber->ref);
6724 						continue;
6725 					}
6726 					janus_refcount_decrease(&subscriber->ref);
6727 					janus_mutex_unlock(&publisher->subscribers_mutex);
6728 				}
6729 			} else {
6730 				janus_mutex_unlock(&videoroom->mutex);
6731 				janus_refcount_decrease(&videoroom->ref);
6732 				JANUS_LOG(LOG_ERR, "Invalid element (ptype)\n");
6733 				error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
6734 				g_snprintf(error_cause, 512, "Invalid element (ptype)");
6735 				goto error;
6736 			}
6737 		} else if(session->participant_type == janus_videoroom_p_type_publisher) {
6738 			/* Handle this publisher */
6739 			participant = janus_videoroom_session_get_publisher(session);
6740 			if(participant == NULL) {
6741 				JANUS_LOG(LOG_ERR, "Invalid participant instance\n");
6742 				error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR;
6743 				g_snprintf(error_cause, 512, "Invalid participant instance");
6744 				goto error;
6745 			}
6746 			if(participant->room == NULL) {
6747 				janus_refcount_decrease(&participant->ref);
6748 				JANUS_LOG(LOG_ERR, "No such room\n");
6749 				error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM;
6750 				g_snprintf(error_cause, 512, "No such room");
6751 				goto error;
6752 			}
6753 			if(!strcasecmp(request_text, "join") || !strcasecmp(request_text, "joinandconfigure")) {
6754 				janus_refcount_decrease(&participant->ref);
6755 				JANUS_LOG(LOG_ERR, "Already in as a publisher on this handle\n");
6756 				error_code = JANUS_VIDEOROOM_ERROR_ALREADY_JOINED;
6757 				g_snprintf(error_cause, 512, "Already in as a publisher on this handle");
6758 				goto error;
6759 			} else if(!strcasecmp(request_text, "configure") || !strcasecmp(request_text, "publish")) {
6760 				if(!strcasecmp(request_text, "publish") && participant->sdp) {
6761 					janus_refcount_decrease(&participant->ref);
6762 					JANUS_LOG(LOG_ERR, "Can't publish, already published\n");
6763 					error_code = JANUS_VIDEOROOM_ERROR_ALREADY_PUBLISHED;
6764 					g_snprintf(error_cause, 512, "Can't publish, already published");
6765 					goto error;
6766 				}
6767 				if(participant->kicked) {
6768 					janus_refcount_decrease(&participant->ref);
6769 					JANUS_LOG(LOG_ERR, "Unauthorized, you have been kicked\n");
6770 					error_code = JANUS_VIDEOROOM_ERROR_UNAUTHORIZED;
6771 					g_snprintf(error_cause, 512, "Unauthorized, you have been kicked");
6772 					goto error;
6773 				}
6774 				/* Configure (or publish a new feed) audio/video/bitrate for this publisher */
6775 				JANUS_VALIDATE_JSON_OBJECT(root, publish_parameters,
6776 					error_code, error_cause, TRUE,
6777 					JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
6778 				if(error_code != 0) {
6779 					janus_refcount_decrease(&participant->ref);
6780 					goto error;
6781 				}
6782 				json_t *audio = json_object_get(root, "audio");
6783 				json_t *audiocodec = json_object_get(root, "audiocodec");
6784 				json_t *video = json_object_get(root, "video");
6785 				json_t *videocodec = json_object_get(root, "videocodec");
6786 				json_t *data = json_object_get(root, "data");
6787 				json_t *bitrate = json_object_get(root, "bitrate");
6788 				json_t *keyframe = json_object_get(root, "keyframe");
6789 				json_t *record = json_object_get(root, "record");
6790 				json_t *recfile = json_object_get(root, "filename");
6791 				json_t *display = json_object_get(root, "display");
6792 				json_t *update = json_object_get(root, "update");
6793 				json_t *user_audio_active_packets = json_object_get(root, "audio_active_packets");
6794 				json_t *user_audio_level_average = json_object_get(root, "audio_level_average");
6795 				if(audio) {
6796 					gboolean audio_active = json_is_true(audio);
6797 					if(g_atomic_int_get(&session->started) && audio_active && !participant->audio_active && !participant->audio_muted) {
6798 						/* Audio was just resumed, try resetting the RTP headers for viewers */
6799 						janus_mutex_lock(&participant->subscribers_mutex);
6800 						GSList *ps = participant->subscribers;
6801 						while(ps) {
6802 							janus_videoroom_subscriber *l = (janus_videoroom_subscriber *)ps->data;
6803 							if(l)
6804 								l->context.a_seq_reset = TRUE;
6805 							ps = ps->next;
6806 						}
6807 						janus_mutex_unlock(&participant->subscribers_mutex);
6808 					}
6809 					participant->audio_active = audio_active;
6810 					JANUS_LOG(LOG_VERB, "Setting audio property: %s (room %s, user %s)\n",
6811 						participant->audio_active ? "true" : "false", participant->room_id_str, participant->user_id_str);
6812 				}
6813 				if(audiocodec && json_string_value(json_object_get(msg->jsep, "sdp")) != NULL) {
6814 					/* The participant would like to use an audio codec in particular */
6815 					janus_audiocodec acodec = janus_audiocodec_from_name(json_string_value(audiocodec));
6816 					if(acodec == JANUS_AUDIOCODEC_NONE ||
6817 							(acodec != participant->room->acodec[0] &&
6818 							acodec != participant->room->acodec[1] &&
6819 							acodec != participant->room->acodec[2] &&
6820 							acodec != participant->room->acodec[3] &&
6821 							acodec != participant->room->acodec[4])) {
6822 						JANUS_LOG(LOG_ERR, "Participant asked for audio codec '%s', but it's not allowed (room %s, user %s)\n",
6823 							json_string_value(audiocodec), participant->room_id_str, participant->user_id_str);
6824 						janus_refcount_decrease(&participant->ref);
6825 						error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
6826 						g_snprintf(error_cause, 512, "Audio codec unavailable in this room");
6827 						goto error;
6828 					}
6829 					participant->acodec = acodec;
6830 					JANUS_LOG(LOG_VERB, "Participant asked for audio codec '%s' (room %s, user %s)\n",
6831 						json_string_value(audiocodec), participant->room_id_str, participant->user_id_str);
6832 				}
6833 				if(video) {
6834 					gboolean video_active = json_is_true(video);
6835 					if(g_atomic_int_get(&session->started) && video_active && !participant->video_active && !participant->video_muted) {
6836 						/* Video was just resumed, try resetting the RTP headers for viewers */
6837 						janus_mutex_lock(&participant->subscribers_mutex);
6838 						GSList *ps = participant->subscribers;
6839 						while(ps) {
6840 							janus_videoroom_subscriber *l = (janus_videoroom_subscriber *)ps->data;
6841 							if(l)
6842 								l->context.v_seq_reset = TRUE;
6843 							ps = ps->next;
6844 						}
6845 						janus_mutex_unlock(&participant->subscribers_mutex);
6846 					}
6847 					participant->video_active = video_active;
6848 					JANUS_LOG(LOG_VERB, "Setting video property: %s (room %s, user %s)\n",
6849 						participant->video_active ? "true" : "false", participant->room_id_str, participant->user_id_str);
6850 				}
6851 				if(videocodec && json_string_value(json_object_get(msg->jsep, "sdp")) != NULL) {
6852 					/* The participant would like to use a video codec in particular */
6853 					janus_videocodec vcodec = janus_videocodec_from_name(json_string_value(videocodec));
6854 					if(vcodec == JANUS_VIDEOCODEC_NONE ||
6855 							(vcodec != participant->room->vcodec[0] &&
6856 							vcodec != participant->room->vcodec[1] &&
6857 							vcodec != participant->room->vcodec[2] &&
6858 							vcodec != participant->room->vcodec[3] &&
6859 							vcodec != participant->room->vcodec[4])) {
6860 						JANUS_LOG(LOG_ERR, "Participant asked for video codec '%s', but it's not allowed (room %s, user %s)\n",
6861 							json_string_value(videocodec), participant->room_id_str, participant->user_id_str);
6862 						janus_refcount_decrease(&participant->ref);
6863 						error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
6864 						g_snprintf(error_cause, 512, "Video codec unavailable in this room");
6865 						goto error;
6866 					}
6867 					participant->vcodec = vcodec;
6868 					JANUS_LOG(LOG_VERB, "Participant asked for video codec '%s' (room %s, user %s)\n",
6869 						json_string_value(videocodec), participant->room_id_str, participant->user_id_str);
6870 				}
6871 				if(data) {
6872 					gboolean data_active = json_is_true(data);
6873 					participant->data_active = data_active;
6874 					JANUS_LOG(LOG_VERB, "Setting data property: %s (room %s, user %s)\n",
6875 						participant->data_active ? "true" : "false", participant->room_id_str, participant->user_id_str);
6876 				}
6877 				if(bitrate) {
6878 					participant->bitrate = json_integer_value(bitrate);
6879 					JANUS_LOG(LOG_VERB, "Setting video bitrate: %"SCNu32" (room %s, user %s)\n",
6880 						participant->bitrate, participant->room_id_str, participant->user_id_str);
6881 					/* Send a new REMB */
6882 					if(g_atomic_int_get(&session->started))
6883 						participant->remb_latest = janus_get_monotonic_time();
6884 					gateway->send_remb(msg->handle, participant->bitrate);
6885 				}
6886 				if(keyframe && json_is_true(keyframe)) {
6887 					/* Send a FIR */
6888 					janus_videoroom_reqpli(participant, "Keyframe request");
6889 				}
6890 				if(user_audio_active_packets) {
6891 					participant->user_audio_active_packets = json_integer_value(user_audio_active_packets);
6892 					JANUS_LOG(LOG_VERB, "Setting user audio_active_packets: %d (room %s, user %s)\n",
6893 						participant->user_audio_active_packets, participant->room_id_str, participant->user_id_str);
6894 				}
6895 				if(user_audio_level_average) {
6896 					participant->user_audio_level_average = json_integer_value(user_audio_level_average);
6897 					JANUS_LOG(LOG_VERB, "Setting user audio_level_average: %d (room %s, user %s)\n",
6898 						participant->user_audio_level_average, participant->room_id_str, participant->user_id_str);
6899 				}
6900 				gboolean record_locked = FALSE;
6901 				if((record || recfile) && participant->room->lock_record && participant->room->room_secret) {
6902 					JANUS_CHECK_SECRET(participant->room->room_secret, root, "secret", error_code, error_cause,
6903 						JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT, JANUS_VIDEOROOM_ERROR_UNAUTHORIZED);
6904 					if(error_code != 0) {
6905 						/* Wrong secret provided, we'll prevent the recording state from being changed */
6906 						record_locked = TRUE;
6907 					}
6908 				}
6909 				janus_mutex_lock(&participant->rec_mutex);
6910 				gboolean prev_recording_active = participant->recording_active;
6911 				if(record && !record_locked) {
6912 					participant->recording_active = json_is_true(record);
6913 					JANUS_LOG(LOG_VERB, "Setting record property: %s (room %s, user %s)\n",
6914 						participant->recording_active ? "true" : "false", participant->room_id_str, participant->user_id_str);
6915 				}
6916 				if(recfile && !record_locked) {
6917 					participant->recording_base = g_strdup(json_string_value(recfile));
6918 					JANUS_LOG(LOG_VERB, "Setting recording basename: %s (room %s, user %s)\n",
6919 						participant->recording_base, participant->room_id_str, participant->user_id_str);
6920 				}
6921 				/* Do we need to do something with the recordings right now? */
6922 				if(participant->recording_active != prev_recording_active) {
6923 					/* Something changed */
6924 					if(!participant->recording_active) {
6925 						/* Not recording (anymore?) */
6926 						janus_videoroom_recorder_close(participant);
6927 					} else if(participant->recording_active && participant->sdp) {
6928 						/* We've started recording, send a PLI/FIR and go on */
6929 						janus_videoroom_recorder_create(
6930 							participant, strstr(participant->sdp, "m=audio") != NULL,
6931 							strstr(participant->sdp, "m=video") != NULL,
6932 							strstr(participant->sdp, "m=application") != NULL);
6933 						if(strstr(participant->sdp, "m=video")) {
6934 							/* Send a FIR */
6935 							janus_videoroom_reqpli(participant, "Recording video");
6936 						}
6937 					}
6938 				}
6939 				janus_mutex_unlock(&participant->rec_mutex);
6940 				if(display) {
6941 					janus_mutex_lock(&participant->room->mutex);
6942 					char *old_display = participant->display;
6943 					char *new_display = g_strdup(json_string_value(display));
6944 					participant->display = new_display;
6945 					g_free(old_display);
6946 					json_t *display_event = json_object();
6947 					json_object_set_new(display_event, "videoroom", json_string("event"));
6948 					json_object_set_new(display_event, "id", string_ids ? json_string(participant->user_id_str) : json_integer(participant->user_id));
6949 					json_object_set_new(display_event, "display", json_string(participant->display));
6950 					if(participant->room && !g_atomic_int_get(&participant->room->destroyed)) {
6951 						janus_videoroom_notify_participants(participant, display_event, FALSE);
6952 					}
6953 					janus_mutex_unlock(&participant->room->mutex);
6954 					json_decref(display_event);
6955 				}
6956 				/* A renegotiation may be taking place */
6957 				gboolean do_update = update ? json_is_true(update) : FALSE;
6958 				if(do_update && !sdp_update) {
6959 					JANUS_LOG(LOG_WARN, "Got an 'update' request, but no SDP update? Ignoring...\n");
6960 				}
6961 				/* Done */
6962 				event = json_object();
6963 				json_object_set_new(event, "videoroom", json_string("event"));
6964 				json_object_set_new(event, "room", string_ids ? json_string(participant->room_id_str) : json_integer(participant->room_id));
6965 				json_object_set_new(event, "configured", json_string("ok"));
6966 				/* Also notify event handlers */
6967 				if(notify_events && gateway->events_is_enabled()) {
6968 					json_t *info = json_object();
6969 					json_object_set_new(info, "event", json_string("configured"));
6970 					json_object_set_new(info, "room", string_ids ? json_string(participant->room_id_str) : json_integer(participant->room_id));
6971 					json_object_set_new(info, "id", string_ids ? json_string(participant->user_id_str) : json_integer(participant->user_id));
6972 					json_object_set_new(info, "audio_active", participant->audio_active ? json_true() : json_false());
6973 					json_object_set_new(info, "video_active", participant->video_active ? json_true() : json_false());
6974 					json_object_set_new(info, "data_active", participant->data_active ? json_true() : json_false());
6975 					json_object_set_new(info, "bitrate", json_integer(participant->bitrate));
6976 					if(participant->arc || participant->vrc || participant->drc) {
6977 						json_t *recording = json_object();
6978 						if(participant->arc && participant->arc->filename)
6979 							json_object_set_new(recording, "audio", json_string(participant->arc->filename));
6980 						if(participant->vrc && participant->vrc->filename)
6981 							json_object_set_new(recording, "video", json_string(participant->vrc->filename));
6982 						if(participant->drc && participant->drc->filename)
6983 							json_object_set_new(recording, "data", json_string(participant->drc->filename));
6984 						json_object_set_new(info, "recording", recording);
6985 					}
6986 					gateway->notify_event(&janus_videoroom_plugin, session->handle, info);
6987 				}
6988 			} else if(!strcasecmp(request_text, "unpublish")) {
6989 				/* This participant wants to unpublish */
6990 				if(!participant->sdp) {
6991 					janus_refcount_decrease(&participant->ref);
6992 					JANUS_LOG(LOG_ERR, "Can't unpublish, not published\n");
6993 					error_code = JANUS_VIDEOROOM_ERROR_NOT_PUBLISHED;
6994 					g_snprintf(error_cause, 512, "Can't unpublish, not published");
6995 					goto error;
6996 				}
6997 				/* Tell the core to tear down the PeerConnection, hangup_media will do the rest */
6998 				janus_videoroom_hangup_media(session->handle);
6999 				gateway->close_pc(session->handle);
7000 				/* Done */
7001 				event = json_object();
7002 				json_object_set_new(event, "videoroom", json_string("event"));
7003 				json_object_set_new(event, "room", string_ids ? json_string(participant->room_id_str) : json_integer(participant->room_id));
7004 				json_object_set_new(event, "unpublished", json_string("ok"));
7005 			} else if(!strcasecmp(request_text, "leave")) {
7006 				/* Prepare an event to confirm the request */
7007 				event = json_object();
7008 				json_object_set_new(event, "videoroom", json_string("event"));
7009 				json_object_set_new(event, "room", string_ids ? json_string(participant->room_id_str) : json_integer(participant->room_id));
7010 				json_object_set_new(event, "leaving", json_string("ok"));
7011 				/* This publisher is leaving, tell everybody */
7012 				janus_videoroom_leave_or_unpublish(participant, TRUE, FALSE);
7013 				/* Done */
7014 				participant->audio_active = FALSE;
7015 				participant->video_active = FALSE;
7016 				participant->data_active = FALSE;
7017 				g_atomic_int_set(&session->started, 0);
7018 				//~ session->destroy = TRUE;
7019 			} else {
7020 				janus_refcount_decrease(&participant->ref);
7021 				JANUS_LOG(LOG_ERR, "Unknown request '%s'\n", request_text);
7022 				error_code = JANUS_VIDEOROOM_ERROR_INVALID_REQUEST;
7023 				g_snprintf(error_cause, 512, "Unknown request '%s'", request_text);
7024 				goto error;
7025 			}
7026 			janus_refcount_decrease(&participant->ref);
7027 		} else if(session->participant_type == janus_videoroom_p_type_subscriber) {
7028 			/* Handle this subscriber */
7029 			if(!strcasecmp(request_text, "join")) {
7030 				JANUS_LOG(LOG_ERR, "Already in as a subscriber on this handle\n");
7031 				error_code = JANUS_VIDEOROOM_ERROR_ALREADY_JOINED;
7032 				g_snprintf(error_cause, 512, "Already in as a subscriber on this handle");
7033 				janus_refcount_decrease(&subscriber->ref);
7034 				goto error;
7035 			} else if(!strcasecmp(request_text, "start")) {
7036 				/* Start/restart receiving the publisher streams */
7037 				if(subscriber->paused && msg->jsep == NULL) {
7038 					janus_videoroom_publisher *feed = subscriber->feed;
7039 
7040 					/* This is just resuming a paused stream, reset the RTP sequence numbers */
7041 					subscriber->context.a_seq_reset = TRUE;
7042 					subscriber->context.v_seq_reset = TRUE;
7043 
7044 					if(feed && feed->session && g_atomic_int_get(&feed->session->started)) {
7045 						/* Send a FIR */
7046 						janus_videoroom_reqpli(feed, "Subscriber start");
7047 					}
7048 				}
7049 				subscriber->paused = FALSE;
7050 				event = json_object();
7051 				json_object_set_new(event, "videoroom", json_string("event"));
7052 				json_object_set_new(event, "room", string_ids ? json_string(subscriber->room_id_str) : json_integer(subscriber->room_id));
7053 				json_object_set_new(event, "started", json_string("ok"));
7054 			} else if(!strcasecmp(request_text, "configure")) {
7055 				JANUS_VALIDATE_JSON_OBJECT(root, configure_parameters,
7056 					error_code, error_cause, TRUE,
7057 					JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
7058 				if(error_code != 0) {
7059 					janus_refcount_decrease(&subscriber->ref);
7060 					goto error;
7061 				}
7062 				if(subscriber->kicked) {
7063 					JANUS_LOG(LOG_ERR, "Unauthorized, you have been kicked\n");
7064 					error_code = JANUS_VIDEOROOM_ERROR_UNAUTHORIZED;
7065 					g_snprintf(error_cause, 512, "Unauthorized, you have been kicked");
7066 					janus_refcount_decrease(&subscriber->ref);
7067 					goto error;
7068 				}
7069 				json_t *audio = json_object_get(root, "audio");
7070 				json_t *video = json_object_get(root, "video");
7071 				json_t *data = json_object_get(root, "data");
7072 				json_t *restart = json_object_get(root, "restart");
7073 				json_t *update = json_object_get(root, "update");
7074 				json_t *spatial = json_object_get(root, "spatial_layer");
7075 				json_t *sc_substream = json_object_get(root, "substream");
7076 				if(json_integer_value(spatial) < 0 || json_integer_value(spatial) > 2 ||
7077 						json_integer_value(sc_substream) < 0 || json_integer_value(sc_substream) > 2) {
7078 					JANUS_LOG(LOG_ERR, "Invalid element (substream/spatial_layer should be 0, 1 or 2)\n");
7079 					error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
7080 					g_snprintf(error_cause, 512, "Invalid value (substream/spatial_layer should be 0, 1 or 2)");
7081 					janus_refcount_decrease(&subscriber->ref);
7082 					goto error;
7083 				}
7084 				json_t *temporal = json_object_get(root, "temporal_layer");
7085 				json_t *sc_temporal = json_object_get(root, "temporal");
7086 				if(json_integer_value(temporal) < 0 || json_integer_value(temporal) > 2 ||
7087 						json_integer_value(sc_temporal) < 0 || json_integer_value(sc_temporal) > 2) {
7088 					JANUS_LOG(LOG_ERR, "Invalid element (temporal/temporal_layer should be 0, 1 or 2)\n");
7089 					error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
7090 					g_snprintf(error_cause, 512, "Invalid value (temporal/temporal_layer should be 0, 1 or 2)");
7091 					janus_refcount_decrease(&subscriber->ref);
7092 					goto error;
7093 				}
7094 				json_t *sc_fallback = json_object_get(root, "fallback");
7095 				/* Update the audio/video/data flags, if set */
7096 				janus_videoroom_publisher *publisher = subscriber->feed;
7097 				if(publisher) {
7098 					if(audio && publisher->audio && subscriber->audio_offered) {
7099 						gboolean oldaudio = subscriber->audio;
7100 						gboolean newaudio = json_is_true(audio);
7101 						if(!oldaudio && newaudio) {
7102 							/* Audio just resumed, reset the RTP sequence numbers */
7103 							subscriber->context.a_seq_reset = TRUE;
7104 						}
7105 						subscriber->audio = newaudio;
7106 					}
7107 					if(video && publisher->video && subscriber->video_offered) {
7108 						gboolean oldvideo = subscriber->video;
7109 						gboolean newvideo = json_is_true(video);
7110 						if(!oldvideo && newvideo) {
7111 							/* Video just resumed, reset the RTP sequence numbers */
7112 							subscriber->context.v_seq_reset = TRUE;
7113 						}
7114 						subscriber->video = newvideo;
7115 						if(subscriber->video) {
7116 							/* Send a FIR */
7117 							janus_videoroom_reqpli(publisher, "Restoring video for subscriber");
7118 						}
7119 					}
7120 					if(data && publisher->data && subscriber->data_offered)
7121 						subscriber->data = json_is_true(data);
7122 					/* Check if a simulcasting-related request is involved */
7123 					if(sc_substream && (publisher->ssrc[0] != 0 || publisher->rid[0] != NULL)) {
7124 						subscriber->sim_context.substream_target = json_integer_value(sc_substream);
7125 						if(subscriber->sim_context.substream_target >= 0 && subscriber->sim_context.substream_target <= 2) {
7126 							JANUS_LOG(LOG_VERB, "Setting video SSRC to let through (simulcast): %"SCNu32" (index %d, was %d)\n",
7127 								publisher->ssrc[subscriber->sim_context.substream_target],
7128 								subscriber->sim_context.substream_target,
7129 								subscriber->sim_context.substream);
7130 						}
7131 						if(subscriber->sim_context.substream_target == subscriber->sim_context.substream) {
7132 							/* No need to do anything, we're already getting the right substream, so notify the user */
7133 							json_t *event = json_object();
7134 							json_object_set_new(event, "videoroom", json_string("event"));
7135 							json_object_set_new(event, "room", string_ids ? json_string(subscriber->room_id_str) : json_integer(subscriber->room_id));
7136 							json_object_set_new(event, "substream", json_integer(subscriber->sim_context.substream));
7137 							gateway->push_event(msg->handle, &janus_videoroom_plugin, NULL, event, NULL);
7138 							json_decref(event);
7139 						} else {
7140 							/* Send a FIR */
7141 							janus_videoroom_reqpli(publisher, "Simulcasting substream change");
7142 						}
7143 					}
7144 					if(subscriber->feed && subscriber->feed->vcodec == JANUS_VIDEOCODEC_VP8 &&
7145 							sc_temporal && (publisher->ssrc[0] != 0 || publisher->rid[0] != NULL)) {
7146 						subscriber->sim_context.templayer_target = json_integer_value(sc_temporal);
7147 						JANUS_LOG(LOG_VERB, "Setting video temporal layer to let through (simulcast): %d (was %d)\n",
7148 							subscriber->sim_context.templayer_target, subscriber->sim_context.templayer);
7149 						if(subscriber->sim_context.templayer_target == subscriber->sim_context.templayer) {
7150 							/* No need to do anything, we're already getting the right temporal, so notify the user */
7151 							json_t *event = json_object();
7152 							json_object_set_new(event, "videoroom", json_string("event"));
7153 							json_object_set_new(event, "room", string_ids ? json_string(subscriber->room_id_str) : json_integer(subscriber->room_id));
7154 							json_object_set_new(event, "temporal", json_integer(subscriber->sim_context.templayer));
7155 							gateway->push_event(msg->handle, &janus_videoroom_plugin, NULL, event, NULL);
7156 							json_decref(event);
7157 						} else {
7158 							/* Send a FIR */
7159 							janus_videoroom_reqpli(publisher, "Simulcasting temporal layer change");
7160 						}
7161 					}
7162 					if(sc_fallback && (publisher->ssrc[0] != 0 || publisher->rid[0] != NULL)) {
7163 						subscriber->sim_context.drop_trigger = json_integer_value(sc_fallback);
7164 					}
7165 				}
7166 				if(subscriber->room && subscriber->room->do_svc) {
7167 					/* Also check if the viewer is trying to configure a layer change */
7168 					if(spatial) {
7169 						int spatial_layer = json_integer_value(spatial);
7170 						if(spatial_layer > 1) {
7171 							JANUS_LOG(LOG_WARN, "Spatial layer higher than 1, it will be ignored if using EnabledByFlag_2SL3TL\n");
7172 						}
7173 						if(spatial_layer == subscriber->spatial_layer) {
7174 							/* No need to do anything, we're already getting the right spatial layer, so notify the user */
7175 							json_t *event = json_object();
7176 							json_object_set_new(event, "videoroom", json_string("event"));
7177 							json_object_set_new(event, "room", string_ids ? json_string(subscriber->room_id_str) : json_integer(subscriber->room_id));
7178 							json_object_set_new(event, "spatial_layer", json_integer(subscriber->spatial_layer));
7179 							gateway->push_event(msg->handle, &janus_videoroom_plugin, NULL, event, NULL);
7180 							json_decref(event);
7181 						} else if(spatial_layer != subscriber->target_spatial_layer) {
7182 							/* Send a FIR to the new RTP forward publisher */
7183 							janus_videoroom_reqpli(publisher, "Need to downscale spatially");
7184 						}
7185 						subscriber->target_spatial_layer = spatial_layer;
7186 					}
7187 					if(temporal) {
7188 						int temporal_layer = json_integer_value(temporal);
7189 						if(temporal_layer > 2) {
7190 							JANUS_LOG(LOG_WARN, "Temporal layer higher than 2, will probably be ignored\n");
7191 						}
7192 						if(temporal_layer == subscriber->temporal_layer) {
7193 							/* No need to do anything, we're already getting the right temporal layer, so notify the user */
7194 							json_t *event = json_object();
7195 							json_object_set_new(event, "videoroom", json_string("event"));
7196 							json_object_set_new(event, "room", string_ids ? json_string(subscriber->room_id_str) : json_integer(subscriber->room_id));
7197 							json_object_set_new(event, "temporal_layer", json_integer(subscriber->temporal_layer));
7198 							gateway->push_event(msg->handle, &janus_videoroom_plugin, NULL, event, NULL);
7199 							json_decref(event);
7200 						}
7201 						subscriber->target_temporal_layer = temporal_layer;
7202 					}
7203 				}
7204 				event = json_object();
7205 				json_object_set_new(event, "videoroom", json_string("event"));
7206 				json_object_set_new(event, "room", string_ids ? json_string(subscriber->room_id_str) : json_integer(subscriber->room_id));
7207 				json_object_set_new(event, "configured", json_string("ok"));
7208 				/* The user may be interested in an ICE restart */
7209 				gboolean do_restart = restart ? json_is_true(restart) : FALSE;
7210 				gboolean do_update = update ? json_is_true(update) : FALSE;
7211 				if(publisher && (sdp_update || do_restart || do_update)) {
7212 					/* Negotiate by sending the selected publisher SDP back, and/or force an ICE restart */
7213 					if(publisher->sdp != NULL) {
7214 						char temp_error[512];
7215 						JANUS_LOG(LOG_VERB, "Munging SDP offer (update) to adapt it to the subscriber's requirements\n");
7216 						janus_sdp *offer = janus_sdp_parse(publisher->sdp, temp_error, sizeof(temp_error));
7217 						if(publisher->audio && !subscriber->audio_offered)
7218 							janus_sdp_mline_remove(offer, JANUS_SDP_AUDIO);
7219 						if(publisher->video && !subscriber->video_offered)
7220 							janus_sdp_mline_remove(offer, JANUS_SDP_VIDEO);
7221 						if(publisher->data && !subscriber->data_offered)
7222 							janus_sdp_mline_remove(offer, JANUS_SDP_APPLICATION);
7223 						/* This is an update, check if we need to update */
7224 						janus_sdp_mtype mtype[3] = { JANUS_SDP_AUDIO, JANUS_SDP_VIDEO, JANUS_SDP_APPLICATION };
7225 						int i=0;
7226 						for(i=0; i<3; i++) {
7227 							janus_sdp_mline *m = janus_sdp_mline_find(subscriber->sdp, mtype[i]);
7228 							janus_sdp_mline *m_new = janus_sdp_mline_find(offer, mtype[i]);
7229 							if(m != NULL && m->port > 0 && m->direction != JANUS_SDP_INACTIVE) {
7230 								/* We have such an m-line and it's active, should it be changed? */
7231 								if(m_new == NULL || m_new->port == 0 || m_new->direction == JANUS_SDP_INACTIVE) {
7232 									/* Turn the m-line to inactive */
7233 									m->direction = JANUS_SDP_INACTIVE;
7234 								}
7235 							} else {
7236 								/* We don't have such an m-line or it's disabled, should it be added/enabled? */
7237 								if(m_new != NULL && m_new->port > 0 && m_new->direction != JANUS_SDP_INACTIVE) {
7238 									if(m != NULL) {
7239 										m->port = m_new->port;
7240 										m->direction = m_new->direction;
7241 									} else {
7242 										/* Add the new m-line */
7243 										m = janus_sdp_mline_create(m_new->type, m_new->port, m_new->proto, m_new->direction);
7244 										subscriber->sdp->m_lines = g_list_append(subscriber->sdp->m_lines, m);
7245 									}
7246 									/* Copy/replace the other properties */
7247 									m->c_ipv4 = m_new->c_ipv4;
7248 									if(m_new->c_addr && (m->c_addr == NULL || strcmp(m->c_addr, m_new->c_addr))) {
7249 										g_free(m->c_addr);
7250 										m->c_addr = g_strdup(m_new->c_addr);
7251 									}
7252 									if(m_new->b_name && (m->b_name == NULL || strcmp(m->b_name, m_new->b_name))) {
7253 										g_free(m->b_name);
7254 										m->b_name = g_strdup(m_new->b_name);
7255 									}
7256 									m->b_value = m_new->b_value;
7257 									g_list_free_full(m->fmts, (GDestroyNotify)g_free);
7258 									m->fmts = NULL;
7259 									GList *fmts = m_new->fmts;
7260 									while(fmts) {
7261 										char *fmt = (char *)fmts->data;
7262 										if(fmt)
7263 											m->fmts = g_list_append(m->fmts,g_strdup(fmt));
7264 										fmts = fmts->next;
7265 									}
7266 									g_list_free(m->ptypes);
7267 									m->ptypes = g_list_copy(m_new->ptypes);
7268 									g_list_free_full(m->attributes, (GDestroyNotify)janus_sdp_attribute_destroy);
7269 									m->attributes = NULL;
7270 									GList *attr = m_new->attributes;
7271 									while(attr) {
7272 										janus_sdp_attribute *a = (janus_sdp_attribute *)attr->data;
7273 										janus_sdp_attribute_add_to_mline(m,
7274 											janus_sdp_attribute_create(a->name, "%s", a->value));
7275 										attr = attr->next;
7276 									}
7277 								}
7278 							}
7279 						}
7280 						janus_sdp_destroy(offer);
7281 						session->sdp_version++;
7282 						subscriber->sdp->o_version = session->sdp_version;
7283 						char *newsdp = janus_sdp_write(subscriber->sdp);
7284 						JANUS_LOG(LOG_VERB, "Updating subscriber:\n%s\n", newsdp);
7285 						json_t *jsep = json_pack("{ssss}", "type", "offer", "sdp", newsdp);
7286 						if(do_restart)
7287 							json_object_set_new(jsep, "restart", json_true());
7288 						if(subscriber->e2ee)
7289 							json_object_set_new(jsep, "e2ee", json_true());
7290 						/* How long will the Janus core take to push the event? */
7291 						gint64 start = janus_get_monotonic_time();
7292 						int res = gateway->push_event(msg->handle, &janus_videoroom_plugin, msg->transaction, event, jsep);
7293 						JANUS_LOG(LOG_VERB, "  >> Pushing event: %d (took %"SCNu64" us)\n", res, janus_get_monotonic_time()-start);
7294 						json_decref(event);
7295 						json_decref(jsep);
7296 						g_free(newsdp);
7297 						/* Any update in the media directions? */
7298 						subscriber->audio = publisher->audio && subscriber->audio_offered;
7299 						subscriber->video = publisher->video && subscriber->video_offered;
7300 						subscriber->data = publisher->data && subscriber->data_offered;
7301 						/* Done */
7302 						janus_videoroom_message_free(msg);
7303 						janus_refcount_decrease(&subscriber->ref);
7304 						continue;
7305 					}
7306 				}
7307 			} else if(!strcasecmp(request_text, "pause")) {
7308 				/* Stop receiving the publisher streams for a while */
7309 				subscriber->paused = TRUE;
7310 				event = json_object();
7311 				json_object_set_new(event, "videoroom", json_string("event"));
7312 				json_object_set_new(event, "room", string_ids ? json_string(subscriber->room_id_str) : json_integer(subscriber->room_id));
7313 				json_object_set_new(event, "paused", json_string("ok"));
7314 			} else if(!strcasecmp(request_text, "switch")) {
7315 				/* This subscriber wants to switch to a different publisher */
7316 				JANUS_VALIDATE_JSON_OBJECT(root, subscriber_parameters,
7317 					error_code, error_cause, TRUE,
7318 					JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
7319 				if(error_code != 0) {
7320 					janus_refcount_decrease(&subscriber->ref);
7321 					goto error;
7322 				}
7323 				if(!string_ids) {
7324 					JANUS_VALIDATE_JSON_OBJECT(root, feed_parameters,
7325 						error_code, error_cause, TRUE,
7326 						JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
7327 				} else {
7328 					JANUS_VALIDATE_JSON_OBJECT(root, feedstr_parameters,
7329 						error_code, error_cause, TRUE,
7330 						JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
7331 				}
7332 				if(error_code != 0) {
7333 					janus_refcount_decrease(&subscriber->ref);
7334 					goto error;
7335 				}
7336 				json_t *feed = json_object_get(root, "feed");
7337 				guint64 feed_id = 0;
7338 				char feed_id_num[30], *feed_id_str = NULL;
7339 				if(!string_ids) {
7340 					feed_id = json_integer_value(feed);
7341 					g_snprintf(feed_id_num, sizeof(feed_id_num), "%"SCNu64, feed_id);
7342 					feed_id_str = feed_id_num;
7343 				} else {
7344 					feed_id_str = (char *)json_string_value(feed);
7345 				}
7346 				json_t *audio = json_object_get(root, "audio");
7347 				json_t *video = json_object_get(root, "video");
7348 				json_t *data = json_object_get(root, "data");
7349 				json_t *spatial = json_object_get(root, "spatial_layer");
7350 				json_t *sc_substream = json_object_get(root, "substream");
7351 				if(json_integer_value(spatial) < 0 || json_integer_value(spatial) > 2 ||
7352 						json_integer_value(sc_substream) < 0 || json_integer_value(sc_substream) > 2) {
7353 					JANUS_LOG(LOG_ERR, "Invalid element (substream/spatial_layer should be 0, 1 or 2)\n");
7354 					error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
7355 					g_snprintf(error_cause, 512, "Invalid value (substream/spatial_layer should be 0, 1 or 2)");
7356 					janus_refcount_decrease(&subscriber->ref);
7357 					goto error;
7358 				}
7359 				json_t *temporal = json_object_get(root, "temporal_layer");
7360 				json_t *sc_temporal = json_object_get(root, "temporal");
7361 				if(json_integer_value(temporal) < 0 || json_integer_value(temporal) > 2 ||
7362 						json_integer_value(sc_temporal) < 0 || json_integer_value(sc_temporal) > 2) {
7363 					JANUS_LOG(LOG_ERR, "Invalid element (temporal/temporal_layer should be 0, 1 or 2)\n");
7364 					error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
7365 					g_snprintf(error_cause, 512, "Invalid value (temporal/temporal_layer should be 0, 1 or 2)");
7366 					janus_refcount_decrease(&subscriber->ref);
7367 					goto error;
7368 				}
7369 				json_t *sc_fallback = json_object_get(root, "fallback");
7370 				if(!subscriber->room) {
7371 					JANUS_LOG(LOG_ERR, "Room Destroyed\n");
7372 					error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM;
7373 					g_snprintf(error_cause, 512, "No such room");
7374 					janus_refcount_decrease(&subscriber->ref);
7375 					goto error;
7376 				}
7377 				if(g_atomic_int_get(&subscriber->destroyed)) {
7378 					JANUS_LOG(LOG_ERR, "Room Destroyed (%s)\n", subscriber->room_id_str);
7379 					error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM;
7380 					g_snprintf(error_cause, 512, "No such room (%s)", subscriber->room_id_str);
7381 					janus_refcount_decrease(&subscriber->ref);
7382 					goto error;
7383 				}
7384 				janus_mutex_lock(&subscriber->room->mutex);
7385 				janus_videoroom_publisher *publisher = g_hash_table_lookup(subscriber->room->participants,
7386 					string_ids ? (gpointer)feed_id_str : (gpointer)&feed_id);
7387 				if(publisher == NULL || g_atomic_int_get(&publisher->destroyed) || publisher->sdp == NULL) {
7388 					JANUS_LOG(LOG_ERR, "No such feed (%s)\n", feed_id_str);
7389 					error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_FEED;
7390 					g_snprintf(error_cause, 512, "No such feed (%s)", feed_id_str);
7391 					janus_mutex_unlock(&subscriber->room->mutex);
7392 					janus_refcount_decrease(&subscriber->ref);
7393 					goto error;
7394 				}
7395 				janus_refcount_increase(&publisher->ref);
7396 				janus_refcount_increase(&publisher->session->ref);
7397 				janus_mutex_unlock(&subscriber->room->mutex);
7398 				gboolean paused = subscriber->paused;
7399 				subscriber->paused = TRUE;
7400 				/* Unsubscribe from the previous publisher */
7401 				janus_videoroom_publisher *prev_feed = subscriber->feed;
7402 				if(prev_feed) {
7403 					/* ... but make sure the codecs are compliant first */
7404 					if(publisher->acodec != prev_feed->acodec || publisher->vcodec != prev_feed->vcodec) {
7405 						janus_refcount_decrease(&publisher->session->ref);
7406 						janus_refcount_decrease(&publisher->ref);
7407 						subscriber->paused = paused;
7408 						JANUS_LOG(LOG_ERR, "The two publishers are not using the same codecs, can't switch\n");
7409 						error_code = JANUS_VIDEOROOM_ERROR_INVALID_SDP;
7410 						g_snprintf(error_cause, 512, "The two publishers are not using the same codecs, can't switch");
7411 						janus_refcount_decrease(&subscriber->ref);
7412 						goto error;
7413 					}
7414 					/* Go on */
7415 					janus_mutex_lock(&prev_feed->subscribers_mutex);
7416 					prev_feed->subscribers = g_slist_remove(prev_feed->subscribers, subscriber);
7417 					janus_mutex_unlock(&prev_feed->subscribers_mutex);
7418 					janus_refcount_decrease(&prev_feed->session->ref);
7419 					janus_mutex_lock(&session->mutex);
7420 					g_clear_pointer(&subscriber->feed, janus_videoroom_publisher_dereference);
7421 					janus_mutex_unlock(&session->mutex);
7422 				}
7423 				/* Subscribe to the new one */
7424 				subscriber->audio = audio ? json_is_true(audio) : TRUE;	/* True by default */
7425 				if(!publisher->audio)
7426 					subscriber->audio = FALSE;	/* ... unless the publisher isn't sending any audio */
7427 				subscriber->video = video ? json_is_true(video) : TRUE;	/* True by default */
7428 				if(!publisher->video)
7429 					subscriber->video = FALSE;	/* ... unless the publisher isn't sending any video */
7430 				subscriber->data = data ? json_is_true(data) : TRUE;	/* True by default */
7431 				if(!publisher->data)
7432 					subscriber->data = FALSE;	/* ... unless the publisher isn't sending any data */
7433 				/* Check if a simulcasting-related request is involved */
7434 				janus_rtp_simulcasting_context_reset(&subscriber->sim_context);
7435 				subscriber->sim_context.rid_ext_id = publisher->rid_extmap_id;
7436 				subscriber->sim_context.substream_target = sc_substream ? json_integer_value(sc_substream) : 2;
7437 				subscriber->sim_context.templayer_target = sc_temporal ? json_integer_value(sc_temporal) : 2;
7438 				subscriber->sim_context.drop_trigger = sc_fallback ? json_integer_value(sc_fallback) : 0;
7439 				janus_vp8_simulcast_context_reset(&subscriber->vp8_context);
7440 				/* Check if a VP9 SVC-related request is involved */
7441 				if(subscriber->room && subscriber->room->do_svc) {
7442 					/* This subscriber belongs to a room where VP9 SVC has been enabled,
7443 					 * let's assume we're interested in all layers for the time being */
7444 					subscriber->spatial_layer = -1;
7445 					subscriber->target_spatial_layer = spatial ? json_integer_value(spatial) : 2;
7446 					subscriber->last_spatial_layer[0] = 0;
7447 					subscriber->last_spatial_layer[1] = 0;
7448 					subscriber->last_spatial_layer[2] = 0;
7449 					subscriber->temporal_layer = -1;
7450 					subscriber->target_temporal_layer = temporal ? json_integer_value(temporal) : 2;
7451 				}
7452 				janus_mutex_lock(&publisher->subscribers_mutex);
7453 				publisher->subscribers = g_slist_append(publisher->subscribers, subscriber);
7454 				janus_mutex_unlock(&publisher->subscribers_mutex);
7455 				janus_mutex_lock(&session->mutex);
7456 				subscriber->feed = publisher;
7457 				janus_mutex_unlock(&session->mutex);
7458 				/* Send a FIR to the new publisher */
7459 				janus_videoroom_reqpli(publisher, "Switching existing subscriber to new publisher");
7460 				/* Done */
7461 				subscriber->paused = paused;
7462 				event = json_object();
7463 				json_object_set_new(event, "videoroom", json_string("event"));
7464 				json_object_set_new(event, "switched", json_string("ok"));
7465 				json_object_set_new(event, "room", string_ids ? json_string(subscriber->room_id_str) : json_integer(subscriber->room_id));
7466 				json_object_set_new(event, "id", string_ids ? json_string(feed_id_str) : json_integer(feed_id));
7467 				if(publisher->display)
7468 					json_object_set_new(event, "display", json_string(publisher->display));
7469 				/* Also notify event handlers */
7470 				if(notify_events && gateway->events_is_enabled()) {
7471 					json_t *info = json_object();
7472 					json_object_set_new(info, "event", json_string("switched"));
7473 					json_object_set_new(info, "room", string_ids ? json_string(publisher->room_id_str) : json_integer(publisher->room_id));
7474 					json_object_set_new(info, "feed", string_ids ? json_string(publisher->user_id_str) : json_integer(publisher->user_id));
7475 					gateway->notify_event(&janus_videoroom_plugin, session->handle, info);
7476 				}
7477 			} else if(!strcasecmp(request_text, "leave")) {
7478 				guint64 room_id = subscriber ? subscriber->room_id : 0;
7479 				char *room_id_str = subscriber ? subscriber->room_id_str : NULL;
7480 				/* Tell the core to tear down the PeerConnection, hangup_media will do the rest */
7481 				janus_videoroom_hangup_media(session->handle);
7482 				gateway->close_pc(session->handle);
7483 				/* Send an event back */
7484 				event = json_object();
7485 				json_object_set_new(event, "videoroom", json_string("event"));
7486 				json_object_set_new(event, "room", string_ids ? json_string(room_id_str) : json_integer(room_id));
7487 				json_object_set_new(event, "left", json_string("ok"));
7488 				g_atomic_int_set(&session->started, 0);
7489 			} else {
7490 				JANUS_LOG(LOG_ERR, "Unknown request '%s'\n", request_text);
7491 				error_code = JANUS_VIDEOROOM_ERROR_INVALID_REQUEST;
7492 				g_snprintf(error_cause, 512, "Unknown request '%s'", request_text);
7493 				janus_refcount_decrease(&subscriber->ref);
7494 				goto error;
7495 			}
7496 			janus_refcount_decrease(&subscriber->ref);
7497 		}
7498 
7499 		/* Prepare JSON event */
7500 		JANUS_LOG(LOG_VERB, "Preparing JSON event as a reply\n");
7501 		/* Any SDP or update to handle? */
7502 		const char *msg_sdp_type = json_string_value(json_object_get(msg->jsep, "type"));
7503 		const char *msg_sdp = json_string_value(json_object_get(msg->jsep, "sdp"));
7504 		json_t *msg_simulcast = json_object_get(msg->jsep, "simulcast");
7505 		gboolean e2ee = json_is_true(json_object_get(msg->jsep, "e2ee"));
7506 		if(!msg_sdp) {
7507 			/* No SDP to send */
7508 			int ret = gateway->push_event(msg->handle, &janus_videoroom_plugin, msg->transaction, event, NULL);
7509 			JANUS_LOG(LOG_VERB, "  >> %d (%s)\n", ret, janus_get_api_error(ret));
7510 			json_decref(event);
7511 		} else {
7512 			/* Generate offer or answer */
7513 			JANUS_LOG(LOG_VERB, "This is involving a negotiation (%s) as well:\n%s\n", msg_sdp_type, msg_sdp);
7514 			if(sdp_update) {
7515 				/* Renegotiation: make sure the user provided an offer, and send answer */
7516 				JANUS_LOG(LOG_VERB, "  -- Updating existing publisher\n");
7517 				session->sdp_version++;		/* This needs to be increased when it changes */
7518 			} else {
7519 				/* New PeerConnection */
7520 				session->sdp_version = 1;	/* This needs to be increased when it changes */
7521 				session->sdp_sessid = janus_get_real_time();
7522 			}
7523 			const char *type = NULL;
7524 			if(!strcasecmp(msg_sdp_type, "offer")) {
7525 				/* We need to answer */
7526 				type = "answer";
7527 			} else if(!strcasecmp(msg_sdp_type, "answer")) {
7528 				/* We got an answer (from a subscriber?), no need to negotiate */
7529 				g_atomic_int_set(&session->hangingup, 0);
7530 				int ret = gateway->push_event(msg->handle, &janus_videoroom_plugin, msg->transaction, event, NULL);
7531 				JANUS_LOG(LOG_VERB, "  >> %d (%s)\n", ret, janus_get_api_error(ret));
7532 				json_decref(event);
7533 				janus_videoroom_message_free(msg);
7534 				continue;
7535 			} else {
7536 				/* TODO We don't support anything else right now... */
7537 				JANUS_LOG(LOG_ERR, "Unknown SDP type '%s'\n", msg_sdp_type);
7538 				error_code = JANUS_VIDEOROOM_ERROR_INVALID_SDP_TYPE;
7539 				g_snprintf(error_cause, 512, "Unknown SDP type '%s'", msg_sdp_type);
7540 				goto error;
7541 			}
7542 			if(session->participant_type != janus_videoroom_p_type_publisher) {
7543 				/* We shouldn't be here, we always offer ourselves */
7544 				JANUS_LOG(LOG_ERR, "Only publishers send offers\n");
7545 				error_code = JANUS_VIDEOROOM_ERROR_INVALID_SDP_TYPE;
7546 				g_snprintf(error_cause, 512, "Only publishers send offers");
7547 				goto error;
7548 			} else {
7549 				/* This is a new publisher: is there room? */
7550 				participant = janus_videoroom_session_get_publisher(session);
7551 				janus_videoroom *videoroom = participant->room;
7552 				int count = 0;
7553 				GHashTableIter iter;
7554 				gpointer value;
7555 				if(!videoroom) {
7556 					error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM;
7557 					goto error;
7558 				}
7559 				if(g_atomic_int_get(&videoroom->destroyed)) {
7560 					error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM;
7561 					goto error;
7562 				}
7563 				janus_mutex_lock(&videoroom->mutex);
7564 				g_hash_table_iter_init(&iter, videoroom->participants);
7565 				while (!g_atomic_int_get(&videoroom->destroyed) && g_hash_table_iter_next(&iter, NULL, &value)) {
7566 					janus_videoroom_publisher *p = value;
7567 					if(p != participant && p->sdp)
7568 						count++;
7569 				}
7570 				janus_refcount_increase(&videoroom->ref);
7571 				janus_mutex_unlock(&videoroom->mutex);
7572 				if(count == videoroom->max_publishers) {
7573 					janus_refcount_decrease(&videoroom->ref);
7574 					participant->audio_active = FALSE;
7575 					participant->video_active = FALSE;
7576 					participant->data_active = FALSE;
7577 					JANUS_LOG(LOG_ERR, "Maximum number of publishers (%d) already reached\n", videoroom->max_publishers);
7578 					error_code = JANUS_VIDEOROOM_ERROR_PUBLISHERS_FULL;
7579 					g_snprintf(error_cause, 512, "Maximum number of publishers (%d) already reached", videoroom->max_publishers);
7580 					goto error;
7581 				}
7582 				if(videoroom->require_e2ee && !e2ee && !participant->e2ee) {
7583 					janus_refcount_decrease(&videoroom->ref);
7584 					participant->audio_active = FALSE;
7585 					participant->video_active = FALSE;
7586 					participant->data_active = FALSE;
7587 					JANUS_LOG(LOG_ERR, "Room requires end-to-end encrypted media\n");
7588 					error_code = JANUS_VIDEOROOM_ERROR_UNAUTHORIZED;
7589 					g_snprintf(error_cause, 512, "Room requires end-to-end encrypted media");
7590 					goto error;
7591 				}
7592 				/* Now prepare the SDP to give back */
7593 				if(strstr(msg_sdp, "mozilla") || strstr(msg_sdp, "Mozilla")) {
7594 					participant->firefox = TRUE;
7595 				}
7596 				/* Start by parsing the offer */
7597 				char error_str[512];
7598 				janus_sdp *offer = janus_sdp_parse(msg_sdp, error_str, sizeof(error_str));
7599 				if(offer == NULL) {
7600 					janus_refcount_decrease(&videoroom->ref);
7601 					json_decref(event);
7602 					JANUS_LOG(LOG_ERR, "Error parsing offer: %s\n", error_str);
7603 					error_code = JANUS_VIDEOROOM_ERROR_INVALID_SDP;
7604 					g_snprintf(error_cause, 512, "Error parsing offer: %s", error_str);
7605 					goto error;
7606 				}
7607 				char *audio_fmtp = NULL;
7608 				char custom_fmtp[256];
7609 				custom_fmtp[0] = '\0';
7610 				GList *temp = offer->m_lines;
7611 				while(temp) {
7612 					/* Which media are available? */
7613 					janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
7614 					if(m->type == JANUS_SDP_AUDIO && m->port > 0 &&
7615 							m->direction != JANUS_SDP_RECVONLY && m->direction != JANUS_SDP_INACTIVE) {
7616 						participant->audio = TRUE;
7617 					} else if(m->type == JANUS_SDP_VIDEO && m->port > 0 &&
7618 							m->direction != JANUS_SDP_RECVONLY && m->direction != JANUS_SDP_INACTIVE) {
7619 						participant->video = TRUE;
7620 					} else if(m->type == JANUS_SDP_APPLICATION && m->port > 0) {
7621 						participant->data = TRUE;
7622 					}
7623 					if(m->type == JANUS_SDP_AUDIO || m->type == JANUS_SDP_VIDEO) {
7624 						/* Are the extmaps we care about there? */
7625 						GList *ma = m->attributes;
7626 						while(ma) {
7627 							janus_sdp_attribute *a = (janus_sdp_attribute *)ma->data;
7628 							if(a->name && a->value) {
7629 								if(videoroom->audiolevel_ext && m->type == JANUS_SDP_AUDIO && strstr(a->value, JANUS_RTP_EXTMAP_AUDIO_LEVEL)) {
7630 									if(janus_string_to_uint8(a->value, &participant->audio_level_extmap_id) < 0)
7631 										JANUS_LOG(LOG_WARN, "Invalid audio-level extension ID: %s\n", a->value);
7632 								} else if(videoroom->videoorient_ext && m->type == JANUS_SDP_VIDEO && strstr(a->value, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION)) {
7633 									if(janus_string_to_uint8(a->value, &participant->video_orient_extmap_id) < 0)
7634 										JANUS_LOG(LOG_WARN, "Invalid video-orientation extension ID: %s\n", a->value);
7635 								} else if(videoroom->playoutdelay_ext && m->type == JANUS_SDP_VIDEO && strstr(a->value, JANUS_RTP_EXTMAP_PLAYOUT_DELAY)) {
7636 									if(janus_string_to_uint8(a->value, &participant->playout_delay_extmap_id) < 0)
7637 										JANUS_LOG(LOG_WARN, "Invalid playout-delay extension ID: %s\n", a->value);
7638 								} else if(m->type == JANUS_SDP_AUDIO && !strcasecmp(a->name, "fmtp")) {
7639 									if(strstr(a->value, "useinbandfec=1") && videoroom->do_opusfec) {
7640 										participant->do_opusfec = TRUE;
7641 										if(strlen(custom_fmtp) == 0) {
7642 											g_snprintf(custom_fmtp, sizeof(custom_fmtp), "useinbandfec=1");
7643 										} else {
7644 											g_strlcat(custom_fmtp, ";useinbandfec=1", sizeof(custom_fmtp));
7645 										}
7646 									}
7647 									if(strstr(a->value, "usedtx=1") && videoroom->do_opusdtx) {
7648 										participant->do_opusdtx = TRUE;
7649 										if(strlen(custom_fmtp) == 0) {
7650 											g_snprintf(custom_fmtp, sizeof(custom_fmtp), "usedtx=1");
7651 										} else {
7652 											g_strlcat(custom_fmtp, ";usedtx=1", sizeof(custom_fmtp));
7653 										}
7654 									}
7655 									if(strstr(a->value, "stereo=1")) {
7656 										if(strlen(custom_fmtp) == 0) {
7657 											g_snprintf(custom_fmtp, sizeof(custom_fmtp), "stereo=1");
7658 										} else {
7659 											g_strlcat(custom_fmtp, ";stereo=1", sizeof(custom_fmtp));
7660 										}
7661 									}
7662 									char *tmp = strchr(a->value, ' ');
7663 									if(tmp && strlen(tmp) > 1) {
7664 										tmp++;
7665 										g_free(audio_fmtp);
7666 										audio_fmtp = g_strdup(tmp);
7667 									}
7668 								}
7669 							}
7670 							ma = ma->next;
7671 						}
7672 					}
7673 					temp = temp->next;
7674 				}
7675 				/* Prepare an answer now: force the room codecs and recvonly on the Janus side */
7676 				JANUS_LOG(LOG_VERB, "The publisher %s going to send an audio stream\n", participant->audio ? "is" : "is NOT");
7677 				JANUS_LOG(LOG_VERB, "The publisher %s going to send a video stream\n", participant->video ? "is" : "is NOT");
7678 				JANUS_LOG(LOG_VERB, "The publisher %s going to open a data channel\n", participant->data ? "is" : "is NOT");
7679 				/* Check the codecs we can use, or the ones we should */
7680 				if(participant->acodec == JANUS_AUDIOCODEC_NONE) {
7681 					int i=0;
7682 					for(i=0; i<5; i++) {
7683 						if(videoroom->acodec[i] == JANUS_AUDIOCODEC_NONE)
7684 							continue;
7685 						if(janus_sdp_get_codec_pt(offer, janus_audiocodec_name(videoroom->acodec[i])) != -1) {
7686 							participant->acodec = videoroom->acodec[i];
7687 							break;
7688 						}
7689 					}
7690 				}
7691 				JANUS_LOG(LOG_VERB, "The publisher is going to use the %s audio codec\n", janus_audiocodec_name(participant->acodec));
7692 				participant->audio_pt = janus_audiocodec_pt(participant->acodec);
7693 				if(participant->acodec != JANUS_AUDIOCODEC_MULTIOPUS) {
7694 					g_free(audio_fmtp);
7695 					audio_fmtp = NULL;
7696 				}
7697 				char *vp9_profile = videoroom->vp9_profile;
7698 				char *h264_profile = videoroom->h264_profile;
7699 				if(participant->vcodec == JANUS_VIDEOCODEC_NONE) {
7700 					int i=0;
7701 					for(i=0; i<5; i++) {
7702 						if(videoroom->vcodec[i] == JANUS_VIDEOCODEC_NONE)
7703 							continue;
7704 						if(videoroom->vcodec[i] == JANUS_VIDEOCODEC_VP9 && vp9_profile) {
7705 							/* Check if this VP9 profile is available */
7706 							if(janus_sdp_get_codec_pt_full(offer, janus_videocodec_name(videoroom->vcodec[i]), vp9_profile) != -1) {
7707 								/* It is */
7708 								h264_profile = NULL;
7709 								participant->vcodec = videoroom->vcodec[i];
7710 								break;
7711 							}
7712 							/* It isn't, fallback to checking whether VP9 is available without the profile */
7713 							vp9_profile = NULL;
7714 						} else if(videoroom->vcodec[i] == JANUS_VIDEOCODEC_H264 && h264_profile) {
7715 							/* Check if this H.264 profile is available */
7716 							if(janus_sdp_get_codec_pt_full(offer, janus_videocodec_name(videoroom->vcodec[i]), h264_profile) != -1) {
7717 								/* It is */
7718 								vp9_profile = NULL;
7719 								participant->vcodec = videoroom->vcodec[i];
7720 								break;
7721 							}
7722 							/* It isn't, fallback to checking whether H.264 is available without the profile */
7723 							h264_profile = NULL;
7724 						}
7725 						/* Check if the codec is available */
7726 						if(janus_sdp_get_codec_pt(offer, janus_videocodec_name(videoroom->vcodec[i])) != -1) {
7727 							participant->vcodec = videoroom->vcodec[i];
7728 							break;
7729 						}
7730 					}
7731 				}
7732 				JANUS_LOG(LOG_VERB, "The publisher is going to use the %s video codec\n", janus_videocodec_name(participant->vcodec));
7733 				participant->video_pt = janus_videocodec_pt(participant->vcodec);
7734 				janus_sdp *answer = janus_sdp_generate_answer(offer,
7735 					JANUS_SDP_OA_AUDIO_CODEC, janus_audiocodec_name(participant->acodec),
7736 					JANUS_SDP_OA_AUDIO_DIRECTION, JANUS_SDP_RECVONLY,
7737 					JANUS_SDP_OA_AUDIO_FMTP, audio_fmtp ? audio_fmtp : (strlen(custom_fmtp) > 0 ? custom_fmtp : NULL),
7738 					JANUS_SDP_OA_VIDEO_CODEC, janus_videocodec_name(participant->vcodec),
7739 					JANUS_SDP_OA_VP9_PROFILE, vp9_profile,
7740 					JANUS_SDP_OA_H264_PROFILE, h264_profile,
7741 					JANUS_SDP_OA_VIDEO_DIRECTION, JANUS_SDP_RECVONLY,
7742 					JANUS_SDP_OA_ACCEPT_EXTMAP, JANUS_RTP_EXTMAP_MID,
7743 					JANUS_SDP_OA_ACCEPT_EXTMAP, JANUS_RTP_EXTMAP_RID,
7744 					JANUS_SDP_OA_ACCEPT_EXTMAP, JANUS_RTP_EXTMAP_REPAIRED_RID,
7745 					JANUS_SDP_OA_ACCEPT_EXTMAP, videoroom->audiolevel_ext ? JANUS_RTP_EXTMAP_AUDIO_LEVEL : NULL,
7746 					JANUS_SDP_OA_ACCEPT_EXTMAP, videoroom->videoorient_ext ? JANUS_RTP_EXTMAP_VIDEO_ORIENTATION : NULL,
7747 					JANUS_SDP_OA_ACCEPT_EXTMAP, videoroom->playoutdelay_ext ? JANUS_RTP_EXTMAP_PLAYOUT_DELAY : NULL,
7748 					JANUS_SDP_OA_ACCEPT_EXTMAP, videoroom->transport_wide_cc_ext ? JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC : NULL,
7749 					JANUS_SDP_OA_DONE);
7750 				janus_sdp_destroy(offer);
7751 				/* Replace the session name */
7752 				g_free(answer->s_name);
7753 				char s_name[100];
7754 				g_snprintf(s_name, sizeof(s_name), "VideoRoom %s", videoroom->room_id_str);
7755 				answer->s_name = g_strdup(s_name);
7756 				/* Which media are REALLY available? (some may have been rejected) */
7757 				participant->audio = FALSE;
7758 				participant->video = FALSE;
7759 				participant->data = FALSE;
7760 				temp = answer->m_lines;
7761 				while(temp) {
7762 					janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
7763 					if(m->type == JANUS_SDP_AUDIO && m->port > 0 && m->direction != JANUS_SDP_INACTIVE) {
7764 						participant->audio = TRUE;
7765 					} else if(m->type == JANUS_SDP_VIDEO && m->port > 0 && m->direction != JANUS_SDP_INACTIVE) {
7766 						participant->video = TRUE;
7767 					} else if(m->type == JANUS_SDP_APPLICATION && m->port > 0) {
7768 						participant->data = TRUE;
7769 					}
7770 					temp = temp->next;
7771 				}
7772 				JANUS_LOG(LOG_VERB, "Per the answer, the publisher %s going to send an audio stream\n",
7773 					participant->audio ? "is" : "is NOT");
7774 				JANUS_LOG(LOG_VERB, "Per the answer, the publisher %s going to send a video stream\n",
7775 					participant->video ? "is" : "is NOT");
7776 				JANUS_LOG(LOG_VERB, "Per the answer, the publisher %s going to open a data channel\n",
7777 					participant->data ? "is" : "is NOT");
7778 				/* Update the event with info on the codecs that we'll be handling */
7779 				if(event) {
7780 					if(participant->audio)
7781 						json_object_set_new(event, "audio_codec", json_string(janus_audiocodec_name(participant->acodec)));
7782 					if(participant->video)
7783 						json_object_set_new(event, "video_codec", json_string(janus_videocodec_name(participant->vcodec)));
7784 				}
7785 				/* Also add a bandwidth SDP attribute if we're capping the bitrate in the room */
7786 				janus_sdp_mline *m = janus_sdp_mline_find(answer, JANUS_SDP_VIDEO);
7787 				if(m != NULL && videoroom->bitrate > 0 && videoroom->bitrate_cap) {
7788 					if(participant->firefox) {
7789 						/* Use TIAS (bps) instead of AS (kbps) for the b= attribute, as explained here:
7790 						 * https://github.com/meetecho/janus-gateway/issues/1277#issuecomment-397677746 */
7791 						m->b_name = g_strdup("TIAS");
7792 						m->b_value = videoroom->bitrate;
7793 					} else {
7794 						m->b_name = g_strdup("AS");
7795 						m->b_value = videoroom->bitrate/1000;
7796 					}
7797 				}
7798 				/* Find out which fmtp was used for video */
7799 				g_free(participant->vfmtp);
7800 				participant->vfmtp = NULL;
7801 				const char *video_profile = NULL;
7802 				if(m != NULL) {
7803 					int video_pt = -1;
7804 					if(m->ptypes && m->ptypes->data)
7805 						video_pt = GPOINTER_TO_INT(m->ptypes->data);
7806 					video_profile = janus_sdp_get_fmtp(answer, video_pt);
7807 					if(video_profile != NULL)
7808 						participant->vfmtp = g_strdup(video_profile);
7809 				}
7810 				/* Generate an SDP string we can send back to the publisher */
7811 				char *answer_sdp = janus_sdp_write(answer);
7812 				/* Now turn the SDP into what we'll send subscribers, using the static payload types for making switching easier */
7813 				int mid_ext_id = 1;
7814 				while(mid_ext_id < 15) {
7815 					if(mid_ext_id != participant->audio_level_extmap_id &&
7816 							mid_ext_id != participant->video_orient_extmap_id &&
7817 							mid_ext_id != participant->playout_delay_extmap_id)
7818 						break;
7819 					mid_ext_id++;
7820 				}
7821 				int abs_send_time_ext_id = 1;
7822 				while(abs_send_time_ext_id < 15) {
7823 					if(abs_send_time_ext_id != mid_ext_id &&
7824 							abs_send_time_ext_id != participant->audio_level_extmap_id &&
7825 							abs_send_time_ext_id != participant->video_orient_extmap_id &&
7826 							abs_send_time_ext_id != participant->playout_delay_extmap_id)
7827 						break;
7828 					abs_send_time_ext_id++;
7829 				}
7830 				offer = janus_sdp_generate_offer(s_name, answer->c_addr,
7831 					JANUS_SDP_OA_AUDIO, participant->audio,
7832 					JANUS_SDP_OA_AUDIO_CODEC, janus_audiocodec_name(participant->acodec),
7833 					JANUS_SDP_OA_AUDIO_PT, janus_audiocodec_pt(participant->acodec),
7834 					JANUS_SDP_OA_AUDIO_DIRECTION, JANUS_SDP_SENDONLY,
7835 					JANUS_SDP_OA_AUDIO_FMTP, audio_fmtp ? audio_fmtp : (strlen(custom_fmtp) > 0 ? custom_fmtp : NULL),
7836 					JANUS_SDP_OA_AUDIO_EXTENSION, JANUS_RTP_EXTMAP_AUDIO_LEVEL,
7837 						participant->audio_level_extmap_id > 0 ? participant->audio_level_extmap_id : 0,
7838 					JANUS_SDP_OA_AUDIO_EXTENSION, JANUS_RTP_EXTMAP_MID, mid_ext_id,
7839 					JANUS_SDP_OA_VIDEO, participant->video,
7840 					JANUS_SDP_OA_VIDEO_CODEC, janus_videocodec_name(participant->vcodec),
7841 					JANUS_SDP_OA_VIDEO_PT, janus_videocodec_pt(participant->vcodec),
7842 					JANUS_SDP_OA_VIDEO_FMTP, video_profile,
7843 					JANUS_SDP_OA_VIDEO_DIRECTION, JANUS_SDP_SENDONLY,
7844 					JANUS_SDP_OA_VIDEO_EXTENSION, JANUS_RTP_EXTMAP_MID, mid_ext_id,
7845 					JANUS_SDP_OA_VIDEO_EXTENSION, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION,
7846 						participant->video_orient_extmap_id > 0 ? participant->video_orient_extmap_id : 0,
7847 					JANUS_SDP_OA_VIDEO_EXTENSION, JANUS_RTP_EXTMAP_PLAYOUT_DELAY,
7848 						participant->playout_delay_extmap_id > 0 ? participant->playout_delay_extmap_id : 0,
7849 					JANUS_SDP_OA_VIDEO_EXTENSION, JANUS_RTP_EXTMAP_ABS_SEND_TIME, abs_send_time_ext_id,
7850 					JANUS_SDP_OA_DATA, participant->data,
7851 					JANUS_SDP_OA_DONE);
7852 				/* Is this room recorded, or are we recording this publisher already? */
7853 				janus_mutex_lock(&participant->rec_mutex);
7854 				if(videoroom->record || participant->recording_active) {
7855 					participant->recording_active = TRUE;
7856 					janus_videoroom_recorder_create(participant, participant->audio, participant->video, participant->data);
7857 				}
7858 				janus_mutex_unlock(&participant->rec_mutex);
7859 				/* Generate an SDP string we can offer subscribers later on */
7860 				char *offer_sdp = janus_sdp_write(offer);
7861 				if(!sdp_update || (participant->ssrc[0] == 0 && participant->rid[0] == NULL)) {
7862 					/* Is simulcasting involved */
7863 					if(msg_simulcast && (participant->vcodec == JANUS_VIDEOCODEC_VP8 ||
7864 							participant->vcodec == JANUS_VIDEOCODEC_H264)) {
7865 						JANUS_LOG(LOG_VERB, "Publisher is going to do simulcasting\n");
7866 						janus_rtp_simulcasting_prepare(msg_simulcast,
7867 							&participant->rid_extmap_id,
7868 							participant->ssrc, participant->rid);
7869 					} else {
7870 						/* No simulcasting involved */
7871 						int i=0;
7872 						for(i=0; i<3; i++) {
7873 							participant->ssrc[i] = 0;
7874 							g_free(participant->rid[i]);
7875 							participant->rid[i] = NULL;
7876 						}
7877 					}
7878 				}
7879 				g_free(audio_fmtp);
7880 				janus_sdp_destroy(offer);
7881 				janus_sdp_destroy(answer);
7882 				/* Send the answer back to the publisher */
7883 				JANUS_LOG(LOG_VERB, "Handling publisher: turned this into an '%s':\n%s\n", type, answer_sdp);
7884 				json_t *jsep = json_pack("{ssss}", "type", type, "sdp", answer_sdp);
7885 				g_free(answer_sdp);
7886 				if(e2ee)
7887 					participant->e2ee = TRUE;
7888 				if(participant->e2ee) {
7889 					JANUS_LOG(LOG_VERB, "Publisher is going to do end-to-end media encryption\n");
7890 					json_object_set_new(jsep, "e2ee", json_true());
7891 				}
7892 				/* How long will the Janus core take to push the event? */
7893 				g_atomic_int_set(&session->hangingup, 0);
7894 				gint64 start = janus_get_monotonic_time();
7895 				int res = gateway->push_event(msg->handle, &janus_videoroom_plugin, msg->transaction, event, jsep);
7896 				JANUS_LOG(LOG_VERB, "  >> Pushing event: %d (took %"SCNu64" us)\n", res, janus_get_monotonic_time()-start);
7897 				/* Done */
7898 				if(res != JANUS_OK) {
7899 					/* TODO Failed to negotiate? We should remove this publisher */
7900 					g_free(offer_sdp);
7901 				} else {
7902 					/* Store the participant's SDP for interested subscribers */
7903 					g_free(participant->sdp);
7904 					participant->sdp = offer_sdp;
7905 					/* We'll wait for the setup_media event before actually telling subscribers */
7906 				}
7907 				/* Unless this is an update, in which case schedule a new offer for all viewers */
7908 				if(sdp_update) {
7909 					json_t *update = json_object();
7910 					json_object_set_new(update, "request", json_string("configure"));
7911 					json_object_set_new(update, "update", json_true());
7912 					janus_mutex_lock(&participant->subscribers_mutex);
7913 					GSList *s = participant->subscribers;
7914 					while(s) {
7915 						janus_videoroom_subscriber *subscriber = (janus_videoroom_subscriber *)s->data;
7916 						if(subscriber && subscriber->session && subscriber->session->handle) {
7917 							/* Enqueue the fake request: this will trigger a renegotiation */
7918 							janus_videoroom_message *msg = g_malloc(sizeof(janus_videoroom_message));
7919 							janus_refcount_increase(&subscriber->session->ref);
7920 							msg->handle = subscriber->session->handle;
7921 							msg->message = update;
7922 							msg->transaction = NULL;
7923 							msg->jsep = NULL;
7924 							json_incref(update);
7925 							g_async_queue_push(messages, msg);
7926 						}
7927 						s = s->next;
7928 					}
7929 					janus_mutex_unlock(&participant->subscribers_mutex);
7930 					json_decref(update);
7931 				}
7932 				janus_refcount_decrease(&videoroom->ref);
7933 				json_decref(event);
7934 				json_decref(jsep);
7935 			}
7936 			if(participant != NULL)
7937 				janus_refcount_decrease(&participant->ref);
7938 		}
7939 		janus_videoroom_message_free(msg);
7940 
7941 		continue;
7942 
7943 error:
7944 		{
7945 			/* Prepare JSON error event */
7946 			json_t *event = json_object();
7947 			json_object_set_new(event, "videoroom", json_string("event"));
7948 			json_object_set_new(event, "error_code", json_integer(error_code));
7949 			json_object_set_new(event, "error", json_string(error_cause));
7950 			int ret = gateway->push_event(msg->handle, &janus_videoroom_plugin, msg->transaction, event, NULL);
7951 			JANUS_LOG(LOG_VERB, "  >> Pushing event: %d (%s)\n", ret, janus_get_api_error(ret));
7952 			json_decref(event);
7953 			janus_videoroom_message_free(msg);
7954 		}
7955 	}
7956 	JANUS_LOG(LOG_VERB, "Leaving VideoRoom handler thread\n");
7957 	return NULL;
7958 }
7959 
7960 /* Helper to quickly relay RTP packets from publishers to subscribers */
janus_videoroom_relay_rtp_packet(gpointer data,gpointer user_data)7961 static void janus_videoroom_relay_rtp_packet(gpointer data, gpointer user_data) {
7962 	janus_videoroom_rtp_relay_packet *packet = (janus_videoroom_rtp_relay_packet *)user_data;
7963 	if(!packet || !packet->data || packet->length < 1) {
7964 		JANUS_LOG(LOG_ERR, "Invalid packet...\n");
7965 		return;
7966 	}
7967 	janus_videoroom_subscriber *subscriber = (janus_videoroom_subscriber *)data;
7968 	if(!subscriber || !subscriber->session) {
7969 		// JANUS_LOG(LOG_ERR, "Invalid session...\n");
7970 		return;
7971 	}
7972 	if(subscriber->paused || subscriber->kicked) {
7973 		// JANUS_LOG(LOG_ERR, "This subscriber paused the stream...\n");
7974 		return;
7975 	}
7976 	janus_videoroom_session *session = subscriber->session;
7977 	if(!session || !session->handle) {
7978 		// JANUS_LOG(LOG_ERR, "Invalid session...\n");
7979 		return;
7980 	}
7981 	if(!g_atomic_int_get(&session->started)) {
7982 		// JANUS_LOG(LOG_ERR, "Streaming not started yet for this session...\n");
7983 		return;
7984 	}
7985 
7986 	/* Make sure there hasn't been a publisher switch by checking the SSRC */
7987 	if(packet->is_video) {
7988 		/* Check if this subscriber is subscribed to this medium */
7989 		if(!subscriber->video) {
7990 			/* Nope, don't relay */
7991 			return;
7992 		}
7993 		/* Check if there's any SVC info to take into account */
7994 		if(packet->svc) {
7995 			/* There is: check if this is a layer that can be dropped for this viewer
7996 			 * Note: Following core inspired by the excellent job done by Sergio Garcia Murillo here:
7997 			 * https://github.com/medooze/media-server/blob/master/src/vp9/VP9LayerSelector.cpp */
7998 			int plen = 0;
7999 			char *payload = janus_rtp_payload((char *)packet->data, packet->length, &plen);
8000 			gboolean keyframe = janus_vp9_is_keyframe((const char *)payload, plen);
8001 			gboolean override_mark_bit = FALSE, has_marker_bit = packet->data->markerbit;
8002 			int spatial_layer = subscriber->spatial_layer;
8003 			gint64 now = janus_get_monotonic_time();
8004 			if(packet->svc_info.spatial_layer >= 0 && packet->svc_info.spatial_layer <= 2)
8005 				subscriber->last_spatial_layer[packet->svc_info.spatial_layer] = now;
8006 			if(subscriber->target_spatial_layer > subscriber->spatial_layer) {
8007 				JANUS_LOG(LOG_HUGE, "We need to upscale spatially: (%d < %d)\n",
8008 					subscriber->spatial_layer, subscriber->target_spatial_layer);
8009 				/* We need to upscale: wait for a keyframe */
8010 				if(keyframe) {
8011 					int new_spatial_layer = subscriber->target_spatial_layer;
8012 					while(new_spatial_layer > subscriber->spatial_layer && new_spatial_layer > 0) {
8013 						if(now - subscriber->last_spatial_layer[new_spatial_layer] >= 250000) {
8014 							/* We haven't received packets from this layer for a while, try a lower layer */
8015 							JANUS_LOG(LOG_HUGE, "Haven't received packets from layer %d for a while, trying %d instead...\n",
8016 								new_spatial_layer, new_spatial_layer-1);
8017 							new_spatial_layer--;
8018 						} else {
8019 							break;
8020 						}
8021 					}
8022 					if(new_spatial_layer > subscriber->spatial_layer) {
8023 						JANUS_LOG(LOG_HUGE, "  -- Upscaling spatial layer: %d --> %d (need %d)\n",
8024 							subscriber->spatial_layer, new_spatial_layer, subscriber->target_spatial_layer);
8025 						subscriber->spatial_layer = new_spatial_layer;
8026 						spatial_layer = subscriber->spatial_layer;
8027 						/* Notify the viewer */
8028 						json_t *event = json_object();
8029 						json_object_set_new(event, "videoroom", json_string("event"));
8030 						json_object_set_new(event, "room", string_ids ? json_string(subscriber->room_id_str) : json_integer(subscriber->room_id));
8031 						json_object_set_new(event, "spatial_layer", json_integer(subscriber->spatial_layer));
8032 						if(subscriber->temporal_layer == -1) {
8033 							/* We just started: initialize the temporal layer and notify that too */
8034 							subscriber->temporal_layer = 0;
8035 							json_object_set_new(event, "temporal_layer", json_integer(subscriber->temporal_layer));
8036 						}
8037 						gateway->push_event(subscriber->session->handle, &janus_videoroom_plugin, NULL, event, NULL);
8038 						json_decref(event);
8039 					}
8040 				}
8041 			} else if(subscriber->target_spatial_layer < subscriber->spatial_layer) {
8042 				/* We need to downscale */
8043 				JANUS_LOG(LOG_HUGE, "We need to downscale spatially: (%d > %d)\n",
8044 					subscriber->spatial_layer, subscriber->target_spatial_layer);
8045 				gboolean downscaled = FALSE;
8046 				if(!packet->svc_info.fbit && keyframe) {
8047 					/* Non-flexible mode: wait for a keyframe */
8048 					downscaled = TRUE;
8049 				} else if(packet->svc_info.fbit && packet->svc_info.ebit) {
8050 					/* Flexible mode: check the E bit */
8051 					downscaled = TRUE;
8052 				}
8053 				if(downscaled) {
8054 					JANUS_LOG(LOG_HUGE, "  -- Downscaling spatial layer: %d --> %d\n",
8055 						subscriber->spatial_layer, subscriber->target_spatial_layer);
8056 					subscriber->spatial_layer = subscriber->target_spatial_layer;
8057 					/* Notify the viewer */
8058 					json_t *event = json_object();
8059 					json_object_set_new(event, "videoroom", json_string("event"));
8060 					json_object_set_new(event, "room", string_ids ? json_string(subscriber->room_id_str) : json_integer(subscriber->room_id));
8061 					json_object_set_new(event, "spatial_layer", json_integer(subscriber->spatial_layer));
8062 					gateway->push_event(subscriber->session->handle, &janus_videoroom_plugin, NULL, event, NULL);
8063 					json_decref(event);
8064 				}
8065 			}
8066 			if(spatial_layer < packet->svc_info.spatial_layer) {
8067 				/* Drop the packet: update the context to make sure sequence number is increased normally later */
8068 				JANUS_LOG(LOG_HUGE, "Dropping packet (spatial layer %d < %d)\n", spatial_layer, packet->svc_info.spatial_layer);
8069 				subscriber->context.v_base_seq++;
8070 				return;
8071 			} else if(packet->svc_info.ebit && spatial_layer == packet->svc_info.spatial_layer) {
8072 				/* If we stop at layer 0, we need a marker bit now, as the one from layer 1 will not be received */
8073 				override_mark_bit = TRUE;
8074 			}
8075 			int temporal_layer = subscriber->temporal_layer;
8076 			if(subscriber->target_temporal_layer > subscriber->temporal_layer) {
8077 				/* We need to upscale */
8078 				JANUS_LOG(LOG_HUGE, "We need to upscale temporally: (%d < %d)\n",
8079 					subscriber->temporal_layer, subscriber->target_temporal_layer);
8080 				if(packet->svc_info.ubit && packet->svc_info.bbit &&
8081 						packet->svc_info.temporal_layer > subscriber->temporal_layer &&
8082 						packet->svc_info.temporal_layer <= subscriber->target_temporal_layer) {
8083 					JANUS_LOG(LOG_HUGE, "  -- Upscaling temporal layer: %d --> %d (want %d)\n",
8084 						subscriber->temporal_layer, packet->svc_info.temporal_layer, subscriber->target_temporal_layer);
8085 					subscriber->temporal_layer = packet->svc_info.temporal_layer;
8086 					temporal_layer = subscriber->temporal_layer;
8087 					/* Notify the viewer */
8088 					json_t *event = json_object();
8089 					json_object_set_new(event, "videoroom", json_string("event"));
8090 					json_object_set_new(event, "room", string_ids ? json_string(subscriber->room_id_str) : json_integer(subscriber->room_id));
8091 					json_object_set_new(event, "temporal_layer", json_integer(subscriber->temporal_layer));
8092 					gateway->push_event(subscriber->session->handle, &janus_videoroom_plugin, NULL, event, NULL);
8093 					json_decref(event);
8094 				}
8095 			} else if(subscriber->target_temporal_layer < subscriber->temporal_layer) {
8096 				/* We need to downscale */
8097 				JANUS_LOG(LOG_HUGE, "We need to downscale temporally: (%d > %d)\n",
8098 					subscriber->temporal_layer, subscriber->target_temporal_layer);
8099 				if(packet->svc_info.ebit && packet->svc_info.temporal_layer == subscriber->target_temporal_layer) {
8100 					JANUS_LOG(LOG_HUGE, "  -- Downscaling temporal layer: %d --> %d\n",
8101 						subscriber->temporal_layer, subscriber->target_temporal_layer);
8102 					subscriber->temporal_layer = subscriber->target_temporal_layer;
8103 					/* Notify the viewer */
8104 					json_t *event = json_object();
8105 					json_object_set_new(event, "videoroom", json_string("event"));
8106 					json_object_set_new(event, "room", string_ids ? json_string(subscriber->room_id_str) : json_integer(subscriber->room_id));
8107 					json_object_set_new(event, "temporal_layer", json_integer(subscriber->temporal_layer));
8108 					gateway->push_event(subscriber->session->handle, &janus_videoroom_plugin, NULL, event, NULL);
8109 					json_decref(event);
8110 				}
8111 			}
8112 			if(temporal_layer < packet->svc_info.temporal_layer) {
8113 				/* Drop the packet: update the context to make sure sequence number is increased normally later */
8114 				JANUS_LOG(LOG_HUGE, "Dropping packet (temporal layer %d < %d)\n", temporal_layer, packet->svc_info.temporal_layer);
8115 				subscriber->context.v_base_seq++;
8116 				return;
8117 			}
8118 			/* If we got here, we can send the frame: this doesn't necessarily mean it's
8119 			 * one of the layers the user wants, as there may be dependencies involved */
8120 			JANUS_LOG(LOG_HUGE, "Sending packet (spatial=%d, temporal=%d)\n",
8121 				packet->svc_info.spatial_layer, packet->svc_info.temporal_layer);
8122 			/* Fix sequence number and timestamp (publisher switching may be involved) */
8123 			janus_rtp_header_update(packet->data, &subscriber->context, TRUE, 0);
8124 			if(override_mark_bit && !has_marker_bit) {
8125 				packet->data->markerbit = 1;
8126 			}
8127 			if(gateway != NULL) {
8128 				janus_plugin_rtp rtp = { .video = packet->is_video, .buffer = (char *)packet->data, .length = packet->length,
8129 					.extensions = packet->extensions };
8130 				gateway->relay_rtp(session->handle, &rtp);
8131 			}
8132 			if(override_mark_bit && !has_marker_bit) {
8133 				packet->data->markerbit = 0;
8134 			}
8135 			/* Restore the timestamp and sequence number to what the publisher set them to */
8136 			packet->data->timestamp = htonl(packet->timestamp);
8137 			packet->data->seq_number = htons(packet->seq_number);
8138 		} else if(packet->ssrc[0] != 0) {
8139 			/* Handle simulcast: make sure we have a payload to work with */
8140 			int plen = 0;
8141 			char *payload = janus_rtp_payload((char *)packet->data, packet->length, &plen);
8142 			if(payload == NULL)
8143 				return;
8144 			/* Process this packet: don't relay if it's not the SSRC/layer we wanted to handle */
8145 			gboolean relay = janus_rtp_simulcasting_context_process_rtp(&subscriber->sim_context,
8146 				(char *)packet->data, packet->length, packet->ssrc, NULL, subscriber->feed->vcodec, &subscriber->context);
8147 			if(!relay) {
8148 				/* Did a lot of time pass before we could relay a packet? */
8149 				gint64 now = janus_get_monotonic_time();
8150 				if((now - subscriber->sim_context.last_relayed) >= G_USEC_PER_SEC) {
8151 					g_atomic_int_set(&subscriber->sim_context.need_pli, 1);
8152 				}
8153 			}
8154 			if(subscriber->sim_context.need_pli && subscriber->feed && subscriber->feed->session &&
8155 					subscriber->feed->session->handle) {
8156 				/* Send a PLI */
8157 				JANUS_LOG(LOG_VERB, "We need a PLI for the simulcast context\n");
8158 				gateway->send_pli(subscriber->feed->session->handle);
8159 			}
8160 			/* Do we need to drop this? */
8161 			if(!relay)
8162 				return;
8163 			/* Any event we should notify? */
8164 			if(subscriber->sim_context.changed_substream) {
8165 				/* Notify the user about the substream change */
8166 				json_t *event = json_object();
8167 				json_object_set_new(event, "videoroom", json_string("event"));
8168 				json_object_set_new(event, "room", string_ids ? json_string(subscriber->room_id_str) : json_integer(subscriber->room_id));
8169 				json_object_set_new(event, "substream", json_integer(subscriber->sim_context.substream));
8170 				gateway->push_event(subscriber->session->handle, &janus_videoroom_plugin, NULL, event, NULL);
8171 				json_decref(event);
8172 			}
8173 			if(subscriber->sim_context.changed_temporal) {
8174 				/* Notify the user about the temporal layer change */
8175 				json_t *event = json_object();
8176 				json_object_set_new(event, "videoroom", json_string("event"));
8177 				json_object_set_new(event, "room", string_ids ? json_string(subscriber->room_id_str) : json_integer(subscriber->room_id));
8178 				json_object_set_new(event, "temporal", json_integer(subscriber->sim_context.templayer));
8179 				gateway->push_event(subscriber->session->handle, &janus_videoroom_plugin, NULL, event, NULL);
8180 				json_decref(event);
8181 			}
8182 			/* If we got here, update the RTP header and send the packet */
8183 			janus_rtp_header_update(packet->data, &subscriber->context, TRUE, 0);
8184 			char vp8pd[6];
8185 			if(subscriber->feed && subscriber->feed->vcodec == JANUS_VIDEOCODEC_VP8) {
8186 				/* For VP8, we save the original payload descriptor, to restore it after */
8187 				memcpy(vp8pd, payload, sizeof(vp8pd));
8188 				janus_vp8_simulcast_descriptor_update(payload, plen, &subscriber->vp8_context,
8189 					subscriber->sim_context.changed_substream);
8190 			}
8191 			/* Send the packet */
8192 			if(gateway != NULL) {
8193 				janus_plugin_rtp rtp = { .video = packet->is_video, .buffer = (char *)packet->data, .length = packet->length,
8194 					.extensions = packet->extensions };
8195 				gateway->relay_rtp(session->handle, &rtp);
8196 			}
8197 			/* Restore the timestamp and sequence number to what the publisher set them to */
8198 			packet->data->timestamp = htonl(packet->timestamp);
8199 			packet->data->seq_number = htons(packet->seq_number);
8200 			if(subscriber->feed && subscriber->feed->vcodec == JANUS_VIDEOCODEC_VP8) {
8201 				/* Restore the original payload descriptor as well, as it will be needed by the next viewer */
8202 				memcpy(payload, vp8pd, sizeof(vp8pd));
8203 			}
8204 		} else {
8205 			/* Fix sequence number and timestamp (publisher switching may be involved) */
8206 			janus_rtp_header_update(packet->data, &subscriber->context, TRUE, 0);
8207 			/* Send the packet */
8208 			if(gateway != NULL) {
8209 				janus_plugin_rtp rtp = { .video = packet->is_video, .buffer = (char *)packet->data, .length = packet->length,
8210 					.extensions = packet->extensions };
8211 				gateway->relay_rtp(session->handle, &rtp);
8212 			}
8213 			/* Restore the timestamp and sequence number to what the publisher set them to */
8214 			packet->data->timestamp = htonl(packet->timestamp);
8215 			packet->data->seq_number = htons(packet->seq_number);
8216 		}
8217 	} else {
8218 		/* Check if this subscriber is subscribed to this medium */
8219 		if(!subscriber->audio) {
8220 			/* Nope, don't relay */
8221 			return;
8222 		}
8223 		/* Fix sequence number and timestamp (publisher switching may be involved) */
8224 		janus_rtp_header_update(packet->data, &subscriber->context, FALSE, 0);
8225 		/* Send the packet */
8226 		if(gateway != NULL) {
8227 			janus_plugin_rtp rtp = { .video = packet->is_video, .buffer = (char *)packet->data, .length = packet->length,
8228 				.extensions = packet->extensions };
8229 			gateway->relay_rtp(session->handle, &rtp);
8230 		}
8231 		/* Restore the timestamp and sequence number to what the publisher set them to */
8232 		packet->data->timestamp = htonl(packet->timestamp);
8233 		packet->data->seq_number = htons(packet->seq_number);
8234 	}
8235 
8236 	return;
8237 }
8238 
janus_videoroom_relay_data_packet(gpointer data,gpointer user_data)8239 static void janus_videoroom_relay_data_packet(gpointer data, gpointer user_data) {
8240 	janus_videoroom_rtp_relay_packet *packet = (janus_videoroom_rtp_relay_packet *)user_data;
8241 	if(!packet || packet->is_rtp || !packet->data || packet->length < 1) {
8242 		JANUS_LOG(LOG_ERR, "Invalid packet...\n");
8243 		return;
8244 	}
8245 	janus_videoroom_subscriber *subscriber = (janus_videoroom_subscriber *)data;
8246 	if(!subscriber || !subscriber->session || !subscriber->data || subscriber->paused) {
8247 		return;
8248 	}
8249 	janus_videoroom_session *session = subscriber->session;
8250 	if(!session || !session->handle) {
8251 		return;
8252 	}
8253 	if(!g_atomic_int_get(&session->started) || !g_atomic_int_get(&session->dataready)) {
8254 		return;
8255 	}
8256 	if(gateway != NULL && packet->data != NULL) {
8257 		JANUS_LOG(LOG_VERB, "Forwarding %s DataChannel message (%d bytes) to viewer\n",
8258 			packet->textdata ? "text" : "binary", packet->length);
8259 		janus_plugin_data data = {
8260 			.label = NULL,
8261 			.protocol = NULL,
8262 			.binary = !packet->textdata,
8263 			.buffer = (char *)packet->data,
8264 			.length = packet->length
8265 		};
8266 		gateway->relay_data(session->handle, &data);
8267 	}
8268 	return;
8269 }
8270 
8271 /* The following methods are only relevant if RTCP is used for RTP forwarders */
janus_videoroom_rtp_forwarder_rtcp_receive(janus_videoroom_rtp_forwarder * forward)8272 static void janus_videoroom_rtp_forwarder_rtcp_receive(janus_videoroom_rtp_forwarder *forward) {
8273 	char buffer[1500];
8274 	struct sockaddr_storage remote_addr;
8275 	socklen_t addrlen = sizeof(remote_addr);
8276 	int len = recvfrom(forward->rtcp_fd, buffer, sizeof(buffer), 0, (struct sockaddr *)&remote_addr, &addrlen);
8277 	if(len > 0 && janus_is_rtcp(buffer, len)) {
8278 		JANUS_LOG(LOG_HUGE, "Got %s RTCP packet: %d bytes\n", forward->is_video ? "video" : "audio", len);
8279 		/* We only handle incoming video PLIs or FIR at the moment */
8280 		if(!janus_rtcp_has_fir(buffer, len) && !janus_rtcp_has_pli(buffer, len))
8281 			return;
8282 		janus_videoroom_reqpli((janus_videoroom_publisher *)forward->source, "RTCP from forwarder");
8283 	}
8284 }
8285 
janus_videoroom_rtp_forwarder_rtcp_thread(void * data)8286 static void *janus_videoroom_rtp_forwarder_rtcp_thread(void *data) {
8287 	JANUS_LOG(LOG_VERB, "Joining RTCP thread for RTP forwarders...\n");
8288 	/* Run the main loop */
8289 	g_main_loop_run(rtcpfwd_loop);
8290 	/* When the loop ends, we're done */
8291 	JANUS_LOG(LOG_VERB, "Leaving RTCP thread for RTP forwarders...\n");
8292 	return NULL;
8293 }
8294