1 /*! \file janus_streaming.c
2 * \author Lorenzo Miniero <lorenzo@meetecho.com>
3 * \copyright GNU General Public License v3
4 * \brief Janus Streaming plugin
5 * \details Check the \ref streaming for more details.
6 *
7 * \ingroup plugins
8 * \ref plugins
9 *
10 * \page streaming Streaming plugin documentation
11 * This is a streaming plugin for Janus, allowing WebRTC peers
12 * to watch/listen to pre-recorded files or media generated by another tool.
13 * Specifically, the plugin currently supports three different type of streams:
14 *
15 * -# on-demand streaming of pre-recorded media files (different
16 * streaming context for each peer);
17 * -# live streaming of pre-recorded media files (shared streaming
18 * context for all peers attached to the stream);
19 * -# live streaming of media generated by another tool (shared
20 * streaming context for all peers attached to the stream).
21 *
22 * For what concerns types 1. and 2., considering the proof of concept
23 * nature of the implementation the only pre-recorded media files
24 * that the plugins supports right now are Opus, raw mu-Law and a-Law files:
25 * support is of course planned for other additional widespread formats
26 * as well.
27 *
28 * For what concerns type 3., instead, the plugin is configured
29 * to listen on a couple of ports for RTP: this means that the plugin
30 * is implemented to receive RTP on those ports and relay them to all
31 * peers attached to that stream. Any tool that can generate audio/video
32 * RTP streams and specify a destination is good for the purpose: the
33 * examples section contains samples that make use of GStreamer (http://gstreamer.freedesktop.org/)
34 * but other tools like FFmpeg (http://www.ffmpeg.org/), LibAV (http://libav.org/)
35 * or others are fine as well. This makes it really easy to capture and
36 * encode whatever you want using your favourite tool, and then have it
37 * transparently broadcasted via WebRTC using Janus. Notice that we recently
38 * added the possibility to also add a datachannel track to an RTP streaming
39 * mountpoint: this allows you to send, via UDP, a text-based message to
40 * relay via datachannels (e.g., the title of the current song, if this
41 * is a radio streaming channel). When using this feature, though, beware
42 * that you'll have to stay within the boundaries of the MTU, as each
43 * message will have to stay within the size of an UDP packet.
44 *
45 * Streams to make available are listed in the plugin configuration file.
46 * A pre-filled configuration file is provided in \c conf/janus.plugin.streaming.jcfg
47 * and includes a stream of every type.
48 *
49 * To add more streams or modify the existing ones, you can use the following
50 * syntax:
51 *
52 * \verbatim
53 stream-name: {
54 [settings]
55 }
56 \endverbatim
57 *
58 * with the allowed settings listed below:
59 *
60 * \verbatim
61 type = rtp|live|ondemand|rtsp
62 rtp = stream originated by an external tool (e.g., gstreamer or
63 ffmpeg) and sent to the plugin via RTP
64 live = local file streamed live to multiple viewers
65 (multiple viewers = same streaming context)
66 ondemand = local file streamed on-demand to a single listener
67 (multiple viewers = different streaming contexts)
68 rtsp = stream originated by an external RTSP feed (only
69 available if libcurl support was compiled)
70 id = <unique numeric ID>
71 description = This is my awesome stream
72 metadata = An optional string that can contain any metadata (e.g., JSON)
73 associated with the stream you want users to receive
74 is_private = true|false (private streams don't appear when you do a 'list' request)
75 filename = path to the local file to stream (only for live/ondemand)
76 secret = <optional password needed for manipulating (e.g., destroying
77 or enabling/disabling) the stream>
78 pin = <optional password needed for watching the stream>
79 audio = true|false (do/don't stream audio)
80 video = true|false (do/don't stream video)
81 The following options are only valid for the 'rtp' type:
82 data = true|false (do/don't stream text via datachannels)
83 audioport = local port for receiving audio frames
84 audiortcpport = local port for receiving and sending audio RTCP feedback
85 audiomcast = multicast group for receiving audio frames, if any
86 audioiface = network interface or IP address to bind to, if any (binds to all otherwise)
87 audiopt = <audio RTP payload type> (e.g., 111)
88 audiortpmap = RTP map of the audio codec (e.g., opus/48000/2)
89 audiofmtp = Codec specific parameters, if any
90 audioskew = true|false (whether the plugin should perform skew
91 analisys and compensation on incoming audio RTP stream, EXPERIMENTAL)
92 videoport = local port for receiving video frames (only for rtp)
93 videortcpport = local port for receiving and sending video RTCP feedback
94 videomcast = multicast group for receiving video frames, if any
95 videoiface = network interface or IP address to bind to, if any (binds to all otherwise)
96 videopt = <video RTP payload type> (e.g., 100)
97 videortpmap = RTP map of the video codec (e.g., VP8/90000)
98 videofmtp = Codec specific parameters, if any
99 videobufferkf = true|false (whether the plugin should store the latest
100 keyframe and send it immediately for new viewers, EXPERIMENTAL)
101 videosimulcast = true|false (do|don't enable video simulcasting)
102 videoport2 = second local port for receiving video frames (only for rtp, and simulcasting)
103 videoport3 = third local port for receiving video frames (only for rtp, and simulcasting)
104 videoskew = true|false (whether the plugin should perform skew
105 analisys and compensation on incoming video RTP stream, EXPERIMENTAL)
106 videosvc = true|false (whether the video will have SVC support; works only for VP9-SVC, default=false)
107 collision = in case of collision (more than one SSRC hitting the same port), the plugin
108 will discard incoming RTP packets with a new SSRC unless this many milliseconds
109 passed, which would then change the current SSRC (0=disabled)
110 dataport = local port for receiving data messages to relay
111 dataiface = network interface or IP address to bind to, if any (binds to all otherwise)
112 datatype = text|binary (type of data this mountpoint will relay, default=text)
113 databuffermsg = true|false (whether the plugin should store the latest
114 message and send it immediately for new viewers)
115 threads = number of threads to assist with the relaying part, which can help
116 if you expect a lot of viewers that may cause the RTP receiving part
117 in the Streaming plugin to slow down and fail to catch up (default=0)
118
119 In case you want to use SRTP for your RTP-based mountpoint, you'll need
120 to configure the SRTP-related properties as well, namely the suite to
121 use for hashing (32 or 80) and the crypto information for decrypting
122 the stream (as a base64 encoded string the way SDES does it). Notice
123 that with SRTP involved you'll have to pay extra attention to what you
124 feed the mountpoint, as you may risk getting SRTP decrypt errors:
125 srtpsuite = 32
126 srtpcrypto = WbTBosdVUZqEb6Htqhn+m3z7wUh4RJVR8nE15GbN
127
128 The Streaming plugin can also be used to (re)stream media that has been
129 encrypted using something that can be consumed via Insertable Streams.
130 In that case, we only need to be aware of it, so that we can send the
131 info along with the SDP. How to decrypt the media is out of scope, and
132 up to the application since, again, this is end-to-end encryption and
133 so neither Janus nor the Streaming plugin have access to anything.
134 DO NOT SET THIS PROPERTY IF YOU DON'T KNOW WHAT YOU'RE DOING!
135 e2ee = true
136
137 The following options are only valid for the 'rtsp' type:
138 url = RTSP stream URL
139 rtsp_user = RTSP authorization username, if needed
140 rtsp_pwd = RTSP authorization password, if needed
141 rtsp_failcheck = whether an error should be returned if connecting to the RTSP server fails (default=true)
142 rtspiface = network interface IP address or device name to listen on when receiving RTSP streams
143 rtsp_reconnect_delay = after n seconds passed and no media assumed, the RTSP server has gone and schedule a reconnect (default=5s)
144 rtsp_session_timeout = by default the streaming plugin will check the RTSP connection with an OPTIONS query,
145 the value of the timeout comes from the RTSP session initializer and by default
146 this session timeout is the half of this value In some cases this value can be too high (for example more than one minute)
147 because of the media server. In that case this plugin will calculate the timeout with this
148 formula: timeout = min(session_timeout, rtsp_session_timeout / 2). (default=0s)
149 rtsp_timeout = communication timeout (CURLOPT_TIMEOUT) for cURL call gathering the RTSP information (default=10s)
150 rtsp_conn_timeout = connection timeout for cURL (CURLOPT_CONNECTTIMEOUT) call gathering the RTSP information (default=5s)
151 \endverbatim
152 *
153 * \section streamapi Streaming API
154 *
155 * The Streaming API supports several requests, some of which are
156 * synchronous and some asynchronous. There are some situations, though,
157 * (invalid JSON, invalid request) which will always result in a
158 * synchronous error response even for asynchronous requests.
159 *
160 * \c list , \c info , \c create , \c destroy , \c recording , \c edit ,
161 * \c enable and \c disable are synchronous requests, which means you'll
162 * get a response directly within the context of the transaction. \c list
163 * lists all the available streams; \c create allows you to create a new
164 * mountpoint dynamically, as an alternative to using the configuration
165 * file; \c destroy removes a mountpoint and destroys it; \c recording
166 * instructs the plugin on whether or not a live RTP stream should be
167 * recorded while it's broadcasted; \c enable and \c disable respectively
168 * enable and disable a mountpoint, that is decide whether or not a
169 * mountpoint should be available to users without destroying it.
170 * \c edit allows you to dynamically edit some mountpoint properties (e.g., the PIN);
171 *
172 * The \c watch , \c start , \c configure , \c pause , \c switch and \c stop requests
173 * instead are all asynchronous, which means you'll get a notification
174 * about their success or failure in an event. \c watch asks the plugin
175 * to prepare the playout of one of the available streams; \c start
176 * starts the actual playout; \c pause allows you to pause a playout
177 * without tearing down the PeerConnection; \c switch allows you to
178 * switch to a different mountpoint of the same kind (note: only live
179 * RTP mountpoints supported as of now) without having to stop and watch
180 * the new one; \c stop stops the playout and tears the PeerConnection
181 * down.
182 *
183 * Notice that, in general, all users can create mountpoints, no matter
184 * what type they are. If you want to limit this functionality, you can
185 * configure an admin \c admin_key in the plugin settings. When
186 * configured, only "create" requests that include the correct
187 * \c admin_key value in an "admin_key" property will succeed, and will
188 * be rejected otherwise.
189 *
190 * \subsection streamingsync Synchronous requests
191 *
192 * To list the available Streaming mountpoints (both those created via
193 * configuration file and those created via API), you can use the \c list
194 * request:
195 *
196 \verbatim
197 {
198 "request" : "list"
199 }
200 \endverbatim
201 *
202 * If successful, it will return an array with a list of all the mountpoints.
203 * Notice that only the public mountpoints will be returned: those with
204 * an \c is_private set to yes/true will be skipped. The response will
205 * be formatted like this:
206 *
207 \verbatim
208 {
209 "streaming" : "list",
210 "list" : [
211 {
212 "id" : <unique ID of mountpoint #1>,
213 "type" : "<type of mountpoint #1, in line with the types introduced above>",
214 "description" : "<description of mountpoint #1>",
215 "metadata" : "<metadata of mountpoint #1, if any>",
216 "enabled" : <true|false, depending on whether the mountpoint is currently enabled or not>,
217 "audio_age_ms" : <how much time passed since we last received audio; optional, available for RTP mountpoints only>,
218 "video_age_ms" : <how much time passed since we last received video; optional, available for RTP mountpoints only>
219 },
220 {
221 "id" : <unique ID of mountpoint #2>,
222 "type" : "<type of mountpoint #2, in line with the types introduced above>",
223 "description" : "<description of mountpoint #2>",
224 "metadata" : "<metadata of mountpoint #2, if any>",
225 "audio_age_ms" : <how much time passed since we last received audio; optional, available for RTP mountpoints only>,
226 "video_age_ms" : <how much time passed since we last received video; optional, available for RTP mountpoints only>
227 },
228 ...
229 ]
230 }
231 \endverbatim
232 *
233 * As you can see, the \c list request only returns very generic info on
234 * each mounpoint. In case you're interested in learning more details about
235 * a specific mountpoint, you can use the \c info request instead, which
236 * returns more information, or all of it if the mountpoint secret is
237 * provided in the request. An \c info request must be formatted like this:
238 *
239 \verbatim
240 {
241 "request" : "info"
242 "id" : <unique ID of mountpoint to query>,
243 "secret" : <mountpoint secret; optional, can be used to return more info>"
244 }
245 \endverbatim
246 *
247 * If successful, this will have the plugin return an object containing
248 * more info on the mountpoint:
249 *
250 \verbatim
251 {
252 "streaming" : "info",
253 "info" : {
254 "id" : <unique ID of mountpoint>,
255 "name" : "<unique name of mountpoint>",
256 "description" : "<description of mountpoint>",
257 "metadata" : "<metadata of mountpoint, if any>",
258 "secret" : "<secret of mountpoint; only available if a valid secret was provided>",
259 "pin" : "<PIN to access mountpoint; only available if a valid secret was provided>",
260 "is_private" : <true|false, depending on whether the mountpoint is listable; only available if a valid secret was provided>,
261 "viewers" : <count of current subscribers, if any>,
262 "enabled" : <true|false, depending on whether the mountpoint is currently enabled or not>,
263 "audio" : <true, only present if the mountpoint contains audio>,
264 "audiopt" : <audio payload type, only present if configured and the mountpoint contains audio>,
265 "audiortpmap" : "<audio SDP rtpmap value, only present if configured and the mountpoint contains audio>",
266 "audiofmtp" : "<audio SDP fmtp value, only present if configured and the mountpoint contains audio>",
267 "video" : <true, only present if the mountpoint contains video>,
268 "videopt" : <video payload type, only present if configured and the mountpoint contains video>,
269 "videortpmap" : "<video SDP rtpmap value, only present if configured and the mountpoint contains video>",
270 "videofmtp" : "<video SDP fmtp value, only present if configured and the mountpoint contains video>",
271 ...
272 }
273 }
274 \endverbatim
275 *
276 * Considering the different mountpoint types that you can create in this
277 * plugin, the nature of the rest of the returned info obviously depends
278 * on which mountpoint you're querying. This is especially true for RTP
279 * and RTSP mountpoints. Notice that info like the ports an RTP mountpoint
280 * is listening on will only be returned if you provide the correct secret,
281 * as otherwise they're treated like sensitive information and are not
282 * returned to generic \c info calls.
283 *
284 * We've seen how you can create a new mountpoint via configuration file,
285 * but you can create one via API as well, using the \c create request.
286 * Most importantly, you can also choose whether or not a \c create
287 * request should result in the mountpoint being saved to configuration
288 * file so that it's still available after a server restart. The common
289 * syntax for all \c create requests is the following:
290 *
291 \verbatim
292 {
293 "request" : "create",
294 "admin_key" : "<plugin administrator key; mandatory if configured>",
295 "type" : "<type of the mountpoint to create; mandatory>",
296 "id" : <unique ID to assign the mountpoint; optional, will be chosen by the server if missing>,
297 "name" : "<unique name for the mountpoint; optional, will be chosen by the server if missing>",
298 "description" : "<description of mountpoint; optional>",
299 "metadata" : "<metadata of mountpoint; optional>",
300 "secret" : "<secret to query/edit the mountpoint later; optional>",
301 "pin" : "<PIN required for viewers to access mountpoint; optional>",
302 "is_private" : <true|false, whether the mountpoint should be listable; true by default>,
303 "audio" : <true|false, whether the mountpoint will have audio; false by default>,
304 "video" : <true|false, whether the mountpoint will have video; false by default>,
305 "data" : <true|false, whether the mountpoint will have datachannels; false by default>,
306 "permanent" : <true|false, whether the mountpoint should be saved to configuration file or not; false by default>,
307 ...
308 }
309 \endverbatim
310 *
311 * Of course, different mountpoint types will have different properties
312 * you can specify in a \c create. Please refer to the documentation on
313 * configuration files to see the fields you can pass. The only important
314 * difference to highlight is that, unlike in configuration files, you will
315 * NOT have to escape semicolons with a trailing slash, in those properties
316 * where a semicolon might be needed (e.g., \c audiofmtp or \c videofmtp ).
317 *
318 * A successful \c create will result in a \c created response:
319 *
320 \verbatim
321 {
322 "streaming" : "created",
323 "create" : "<unique name of the just created mountpoint>",
324 "permanent" : <true|false, depending on whether the mountpoint was saved to configuration file or not>,
325 "stream": {
326 "id" : <unique ID of the just created mountpoint>,
327 "type" : "<type of the just created mountpoint>",
328 "description" : "<description of the just created mountpoint>",
329 "is_private" : <true|false, depending on whether the new mountpoint is listable>,
330 ...
331 }
332 }
333 \endverbatim
334 *
335 * Notice that additional information, namely the ports the mountpoint
336 * bound to, will only be added for new RTP mountpoints, otherwise this
337 * is all that a \c created request will contain. If you want to double
338 * check everything in your \c create request went as expected, you may
339 * want to issue a followup \c info request to compare the results.
340 *
341 * Once you created a mountpoint, you can modify some (not all) of its
342 * properties via an \c edit request. Namely, you can only modify generic
343 * properties like the mountpoint description, the secret, the PIN and
344 * whether or not the mountpoint should be listable. All other properties
345 * are considered to be immutable. Again, you can choose whether the changes
346 * should be permanent, e.g., saved to configuration file, or not. Notice
347 * that an \c edit request requires the right secret to be provided, if
348 * the mountpoint has one, or will return an error instead. The \c edit
349 * request must be formatted like this:
350 *
351 \verbatim
352 {
353 "request" : "edit",
354 "id" : <unique ID of the mountpoint to edit; mandatory>,
355 "secret" : "<secret to edit the mountpoint; mandatory if configured>",
356 "new_description" : "<new description for the mountpoint; optional>",
357 "new_metadata" : "<new metadata for the mountpoint; optional>",
358 "new_secret" : "<new secret for the mountpoint; optional>",
359 "new_pin" : "<new PIN for the mountpoint; optional>",
360 "new_is_private" : <true|false, depending on whether the mountpoint should be now listable; optional>,
361 "permanent" : <true|false, whether the mountpoint should be saved to configuration file or not; false by default>
362 }
363 \endverbatim
364 *
365 * A successful \c edit will result in an \c edited response:
366 *
367 \verbatim
368 {
369 "streaming" : "edited",
370 "id" : <unique ID of the just edited mountpoint>,
371 "permanent" : <true|false, depending on whether the changes were saved to configuration file or not>
372 }
373 \endverbatim
374 *
375 * Just as you can create and edit mountpoints, you can of course also destroy
376 * them. Again, this applies to all mountpoints, whether created statically
377 * via configuration file or dynamically via API, and the mountpoint destruction
378 * can be made permanent in the configuration file as well. A \c destroy
379 * request must be formatted as follows:
380 *
381 \verbatim
382 {
383 "request" : "destroy",
384 "id" : <unique ID of the mountpoint to destroy; mandatory>,
385 "secret" : "<secret to destroy the mountpoint; mandatory if configured>",
386 "permanent" : <true|false, whether the mountpoint should be removed from the configuration file or not; false by default>
387 }
388 \endverbatim
389 *
390 * If successful, the result will be confirmed in a \c destroyed event:
391 *
392 \verbatim
393 {
394 "streaming" : "destroyed",
395 "id" : <unique ID of the just destroyed mountpoint>
396 }
397 \endverbatim
398 *
399 * Notice that destroying a mountpoint while viewers are still subscribed
400 * to it will result in all viewers being removed, and their PeerConnection
401 * closed as a consequence.
402 *
403 * You can also dynamically enable and disable mountpoints via API. A
404 * disabled mountpoint is a mountpoint that exists, and still works as
405 * expected, but is not accessible to viewers until it's enabled again.
406 * This is a useful property, especially in case of mountpoints that
407 * need to be prepared in advance but must not be accessible until a
408 * specific moment, and a much better alternative to just create the
409 * mountpoint at the very last minute and destroy it otherwise. The
410 * syntax for both the \c enable and \c disable requests is the same,
411 * and looks like the following:
412 *
413 \verbatim
414 {
415 "request" : "enable",
416 "id" : <unique ID of the mountpoint to enable; mandatory>,
417 "secret" : "<secret to enable the mountpoint; mandatory if configured>"
418 }
419 \endverbatim
420 *
421 * If successful, a generic \c ok is returned:
422 *
423 \verbatim
424 {
425 "streaming" : "ok"
426 }
427 \endverbatim
428 \verbatim
429 {
430 "request" : "disable",
431 "id" : <unique ID of the mountpoint to disable; mandatory>,
432 "stop_recording" : <true|false, whether the recording should also be stopped or not; true by default>
433 "secret" : "<secret to disable the mountpoint; mandatory if configured>"
434 }
435 \endverbatim
436 *
437 * If successful, a generic \c ok is returned:
438 *
439 \verbatim
440 {
441 "streaming" : "ok"
442 }
443 \endverbatim
444 *
445 * Finally, you can record a mountpoint to the internal Janus .mjr format
446 * using the \c recording request. The same request can also be used to
447 * stop recording. Although the same request is used in both cases, though,
448 * the syntax for the two use cases differs a bit, namely in terms of the
449 * type of some properties.
450 *
451 * To start recording a new mountpoint, the request should be formatted
452 * like this:
453 *
454 \verbatim
455 {
456 "request" : "recording",
457 "action" : "start",
458 "id" : <unique ID of the mountpoint to manipulate; mandatory>,
459 "audio" : "<enable audio recording, and use this base path/filename; optional>",
460 "video" : "<enable video recording, and use this base path/filename; optional>",
461 "data" : "<enable data recording, and use this base path/filename; optional>"
462 }
463 \endverbatim
464 *
465 * To stop a recording, instead, this is the request syntax:
466 *
467 \verbatim
468 {
469 "request" : "recording",
470 "action" : "stop",
471 "id" : <unique ID of the mountpoint to manipulate; mandatory>,
472 "audio" : <true|false; whether or not audio recording should be stopped>,
473 "video" : <true|false; whether or not video recording should be stopped>,
474 "data" : <true|false; whether or not datachannel recording should be stopped>
475 }
476 \endverbatim
477 *
478 * As you can notice, when you want to start a recording the \c audio ,
479 * \c video and \c data properties are strings, and specify the base path
480 * to use for the recording filename; when stopping a recording, instead,
481 * they're interpreted as boolean properties. Notice that, as with all
482 * APIs that wrap .mjr recordings, the filename you specify here is not
483 * the actual filename: an \c .mjr extension is always going to be added
484 * by the Janus core, so you should take this into account when tracking
485 * the related recording files.
486 *
487 * Whether you started or stopped a recording, a successful request will
488 * always result in a simple \c ok response:
489 *
490 \verbatim
491 {
492 "streaming" : "ok"
493 }
494 \endverbatim
495 *
496 * \subsection streamingasync Asynchronous requests
497 *
498 * All the requests we've gone through so far are synchronous. This means
499 * that they return a response right away. That said, many of the requests
500 * this plugin supports are asynchronous instead, which means Janus will
501 * send an ack when they're received, and a response will only follow
502 * later on. This is especially true for requests dealing with the
503 * management and setup of mountpoint viewers, e.g., for the purpose of
504 * negotiating a WebRTC PeerConnection to receive media from a mountpoint.
505 *
506 * To subscribe to a specific mountpoint, an interested viewer can make
507 * use of the \c watch request. As suggested by the request name, this
508 * instructs the plugin to setup a new PeerConnection to allow the new
509 * viewer to watch the specified mountpoint. The \c watch request must
510 * be formatted like this:
511 *
512 \verbatim
513 {
514 "request" : "watch",
515 "id" : <unique ID of the mountpoint to subscribe to; mandatory>,
516 "pin" : "<PIN required to access the mountpoint; mandatory if configured>",
517 "offer_audio" : <true|false; whether or not audio should be negotiated; true by default if the mountpoint has audio>,
518 "offer_video" : <true|false; whether or not video should be negotiated; true by default if the mountpoint has video>,
519 "offer_data" : <true|false; whether or not datachannels should be negotiated; true by default if the mountpoint has datachannels>
520 }
521 \endverbatim
522 *
523 * As you can see, it's just a matter of specifying the ID of the mountpoint to
524 * subscribe to and, if needed, the PIN to access the mountpoint in case
525 * it's protected. The \c offer_audio , \c offer_video and \c offer_data are
526 * also particularly interesting, though, as they allow you to only subscribe
527 * to a subset of the mountpoint media. By default, in fact, a \c watch
528 * request will result in the plugin preparing a new SDP offer trying to
529 * negotiate all the media streams available in the mountpoint; in case
530 * the viewer knows they don't support one of the mountpoint codecs, though
531 * (e.g., the video in the mountpoint is VP8, but they only support H.264),
532 * or are not interested in getting all the media (e.g., they're ok with
533 * just audio and not video, or don't have enough bandwidth for both),
534 * they can use those properties to shape the SDP offer to their needs.
535 *
536 * As anticipated, if successful this request will generate a new JSEP SDP
537 * offer, which will be attached to a \c preparing status event:
538 *
539 \verbatim
540 {
541 "status" : "preparing"
542 }
543 \endverbatim
544 *
545 * At this stage, to complete the setup of a subscription the viewer is
546 * supposed to send a JSEP SDP answer back to the plugin. This is done
547 * by means of a \c start request, which in this case MUST be associated
548 * with a JSEP SDP answer but otherwise requires no arguments:
549 *
550 \verbatim
551 {
552 "request" : "start"
553 }
554 \endverbatim
555 *
556 * If successful this request returns a \c starting status event:
557 *
558 \verbatim
559 {
560 "status" : "starting"
561 }
562 \endverbatim
563 *
564 * Once this is done, all that's needed is waiting for the WebRTC PeerConnection
565 * establishment to succeed. As soon as that happens, the Streaming plugin
566 * can start relaying media from the mountpoint the viewer subscribed to
567 * to the viewer themselves.
568 *
569 * Notice that the same exact steps we just went through (\c watch request,
570 * followed by JSEP offer by the plugin, followed by \c start request with
571 * JSEP answer by the viewer) is what you also use when renegotiations are
572 * needed, e.g., for the purpose of ICE restarts.
573 *
574 * As a viewer, you can temporarily pause and resume the whole media delivery
575 * with a \c pause and, again, \c start request (in this case without any JSEP
576 * SDP answer attached). Neither expect other arguments, as the context
577 * is implicitly derived from the handle they're sent on:
578 *
579 \verbatim
580 {
581 "request" : "pause"
582 }
583 \endverbatim
584 *
585 \verbatim
586 {
587 "request" : "start"
588 }
589 \endverbatim
590 *
591 * Unsurprisingly, they just result in, respectively, \c pausing and
592 * \c starting events:
593 *
594 \verbatim
595 {
596 "status" : "pausing"
597 }
598 \endverbatim
599 *
600 \verbatim
601 {
602 "status" : "starting"
603 }
604 \endverbatim
605 *
606 * For more drill-down manipulations of a subscription, a \c configure
607 * request can be used instead. This request allows viewers to dynamically
608 * change some properties associated to their media subscription, e.g.,
609 * in terms of what should and should not be sent at a specific time. A
610 * \c configure request must be formatted as follows:
611 *
612 \verbatim
613 {
614 "request" : "configure",
615 "audio" : <true|false, depending on whether audio should be relayed or not; optional>,
616 "video" : <true|false, depending on whether video should be relayed or not; optional>,
617 "data" : <true|false, depending on whether datachannel messages should be relayed or not; optional>,
618 "substream" : <substream to receive (0-2), in case simulcasting is enabled; optional>,
619 "temporal" : <temporal layers to receive (0-2), in case simulcasting is enabled; optional>,
620 "fallback" : <How much time (in us, default 250000) without receiving packets will make us drop to the substream below>,
621 "spatial_layer" : <spatial layer to receive (0-1), in case VP9-SVC is enabled; optional>,
622 "temporal_layer" : <temporal layers to receive (0-2), in case VP9-SVC is enabled; optional>
623 }
624 \endverbatim
625 *
626 * As you can see, the \c audio , \c video and \c data properties can be
627 * used as a media-level pause/resume functionality, whereas \c pause
628 * and \c start simply pause and resume all streams at the same time.
629 * The \c substream and \c temporal properties, instead, only make sense
630 * when the mountpoint is configured with video simulcasting support, and
631 * as such the viewer is interested in receiving a specific substream
632 * or temporal layer, rather than any other of the available ones.
633 * The \c spatial_layer and \c temporal_layer have exactly the same meaning,
634 * but within the context of VP9-SVC mountpoints, and will have no effect
635 * on mountpoints involving a different video codec.
636 *
637 * Another interesting feature in the Streaming plugin is the so-called
638 * mountpoint "switching". Basically, when subscribed to a specific
639 * mountpoint and receiving media from there, you can at any time "switch"
640 * to a different mountpoint, and as such start receiving media from that
641 * other mountpoint instead. Think of it as changing channel on a TV: you
642 * keep on using the same PeerConnection, the plugin simply changes the
643 * source of the media transparently. Of course, while powerful and effective
644 * this request has some limitations. First of all, it only works with RTP
645 * mountpoints, and not other mountpoint types; besides, the two mountpoints
646 * must have the same media configuration, that is, use the same codecs,
647 * the same payload types, etc. In fact, since the same PeerConnection is
648 * used for this feature, switching to a mountpoint with a different
649 * configuration might result in media incompatible with the PeerConnection
650 * setup being relayed to the viewer, and as such in no audio/video being
651 * played. That said, a \c switch request must be formatted like this:
652 *
653 \verbatim
654 {
655 "request" : "switch",
656 "id" : <unique ID of the new mountpoint to switch to; mandatory>
657 }
658 \endverbatim
659 *
660 * If successful, you'll be unsubscribed from the previous mountpoint,
661 * and subscribed to the new mountpoint instead. The event to confirm
662 * the switch was successful will look like this:
663 *
664 \verbatim
665 {
666 "switched" : "ok",
667 "id" : <unique ID of the new mountpoint>
668 }
669 \endverbatim
670 *
671 * Finally, to stop the subscription to the mountpoint and tear down the
672 * related PeerConnection, you can use the \c stop request. Since context
673 * is implicit, no other argument is required:
674 *
675 \verbatim
676 {
677 "request" : "stop"
678 }
679 \endverbatim
680 *
681 * If successful, the plugin will attempt to tear down the PeerConnection,
682 * and will send back a \c stopping status event:
683 *
684 \verbatim
685 {
686 "status" : "stopping"
687 }
688 \endverbatim
689 *
690 * Once a PeerConnection has been torn down and the subscription closed,
691 * as a viewer you're free to subscribe to a different mountpoint instead.
692 * In fact, while you can't watch more than one mountpoint at the same
693 * time on the same handle, there's no limit on how many mountpoints
694 * you can watch in sequence, again on the same handle. If you're interested
695 * in subscribing to multiple mountpoints at the same time, instead, you'll
696 * have to create multiple handles for the purpose.
697 */
698
699
700 #include "plugin.h"
701
702 #include <errno.h>
703 #include <netdb.h>
704 #include <sys/poll.h>
705 #include <sys/socket.h>
706 #include <sys/time.h>
707
708 #include <jansson.h>
709
710 #ifdef HAVE_LIBCURL
711 #include <curl/curl.h>
712 #ifndef CURL_AT_LEAST_VERSION
713 #define CURL_AT_LEAST_VERSION(x,y,z) 0
714 #endif
715 #endif
716
717 #ifdef HAVE_LIBOGG
718 #include <ogg/ogg.h>
719 #endif
720
721 #include "../debug.h"
722 #include "../apierror.h"
723 #include "../config.h"
724 #include "../mutex.h"
725 #include "../rtp.h"
726 #include "../rtpsrtp.h"
727 #include "../rtcp.h"
728 #include "../record.h"
729 #include "../utils.h"
730 #include "../ip-utils.h"
731
732 /* Default settings */
733 #define JANUS_STREAMING_DEFAULT_SESSION_TIMEOUT 0 /* Overwrite the RTSP session timeout. If set to zero, the RTSP timeout is derived from a session. */
734 #define JANUS_STREAMING_DEFAULT_RECONNECT_DELAY 5 /* Reconnecting delay in seconds. */
735 #define JANUS_STREAMING_DEFAULT_CURL_TIMEOUT 10L /* Communication timeout for cURL. */
736 #define JANUS_STREAMING_DEFAULT_CURL_CONNECT_TIMEOUT 5L /* Connection timeout for cURL. */
737
738 /* Plugin information */
739 #define JANUS_STREAMING_VERSION 8
740 #define JANUS_STREAMING_VERSION_STRING "0.0.8"
741 #define JANUS_STREAMING_DESCRIPTION "This is a streaming plugin for Janus, allowing WebRTC peers to watch/listen to pre-recorded files or media generated by an external source."
742 #define JANUS_STREAMING_NAME "JANUS Streaming plugin"
743 #define JANUS_STREAMING_AUTHOR "Meetecho s.r.l."
744 #define JANUS_STREAMING_PACKAGE "janus.plugin.streaming"
745
746 /* Plugin methods */
747 janus_plugin *create(void);
748 int janus_streaming_init(janus_callbacks *callback, const char *config_path);
749 void janus_streaming_destroy(void);
750 int janus_streaming_get_api_compatibility(void);
751 int janus_streaming_get_version(void);
752 const char *janus_streaming_get_version_string(void);
753 const char *janus_streaming_get_description(void);
754 const char *janus_streaming_get_name(void);
755 const char *janus_streaming_get_author(void);
756 const char *janus_streaming_get_package(void);
757 void janus_streaming_create_session(janus_plugin_session *handle, int *error);
758 struct janus_plugin_result *janus_streaming_handle_message(janus_plugin_session *handle, char *transaction, json_t *message, json_t *jsep);
759 json_t *janus_streaming_handle_admin_message(json_t *message);
760 void janus_streaming_setup_media(janus_plugin_session *handle);
761 void janus_streaming_incoming_rtp(janus_plugin_session *handle, janus_plugin_rtp *packet);
762 void janus_streaming_incoming_rtcp(janus_plugin_session *handle, janus_plugin_rtcp *packet);
763 void janus_streaming_data_ready(janus_plugin_session *handle);
764 void janus_streaming_hangup_media(janus_plugin_session *handle);
765 void janus_streaming_destroy_session(janus_plugin_session *handle, int *error);
766 json_t *janus_streaming_query_session(janus_plugin_session *handle);
767 static int janus_streaming_get_fd_port(int fd);
768
769 /* Plugin setup */
770 static janus_plugin janus_streaming_plugin =
771 JANUS_PLUGIN_INIT (
772 .init = janus_streaming_init,
773 .destroy = janus_streaming_destroy,
774
775 .get_api_compatibility = janus_streaming_get_api_compatibility,
776 .get_version = janus_streaming_get_version,
777 .get_version_string = janus_streaming_get_version_string,
778 .get_description = janus_streaming_get_description,
779 .get_name = janus_streaming_get_name,
780 .get_author = janus_streaming_get_author,
781 .get_package = janus_streaming_get_package,
782
783 .create_session = janus_streaming_create_session,
784 .handle_message = janus_streaming_handle_message,
785 .handle_admin_message = janus_streaming_handle_admin_message,
786 .setup_media = janus_streaming_setup_media,
787 .incoming_rtp = janus_streaming_incoming_rtp,
788 .incoming_rtcp = janus_streaming_incoming_rtcp,
789 .data_ready = janus_streaming_data_ready,
790 .hangup_media = janus_streaming_hangup_media,
791 .destroy_session = janus_streaming_destroy_session,
792 .query_session = janus_streaming_query_session,
793 );
794
795 /* Plugin creator */
create(void)796 janus_plugin *create(void) {
797 JANUS_LOG(LOG_VERB, "%s created!\n", JANUS_STREAMING_NAME);
798 return &janus_streaming_plugin;
799 }
800
801 /* Parameter validation */
802 static struct janus_json_parameter request_parameters[] = {
803 {"request", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
804 };
805 static struct janus_json_parameter id_parameters[] = {
806 {"id", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
807 };
808 static struct janus_json_parameter idopt_parameters[] = {
809 {"id", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
810 };
811 static struct janus_json_parameter idstr_parameters[] = {
812 {"id", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
813 };
814 static struct janus_json_parameter idstropt_parameters[] = {
815 {"id", JSON_STRING, 0}
816 };
817 static struct janus_json_parameter watch_parameters[] = {
818 {"pin", JSON_STRING, 0},
819 {"offer_audio", JANUS_JSON_BOOL, 0},
820 {"offer_video", JANUS_JSON_BOOL, 0},
821 {"offer_data", JANUS_JSON_BOOL, 0},
822 {"restart", JANUS_JSON_BOOL, 0}
823 };
824 static struct janus_json_parameter adminkey_parameters[] = {
825 {"admin_key", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
826 };
827 static struct janus_json_parameter edit_parameters[] = {
828 {"new_description", JSON_STRING, 0},
829 {"new_secret", JSON_STRING, 0},
830 {"new_pin", JSON_STRING, 0},
831 {"new_is_private", JANUS_JSON_BOOL, 0},
832 {"permanent", JANUS_JSON_BOOL, 0}
833 };
834 static struct janus_json_parameter create_parameters[] = {
835 {"name", JSON_STRING, 0},
836 {"description", JSON_STRING, 0},
837 {"metadata", JSON_STRING, 0},
838 {"is_private", JANUS_JSON_BOOL, 0},
839 {"type", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
840 {"secret", JSON_STRING, 0},
841 {"pin", JSON_STRING, 0},
842 {"audio", JANUS_JSON_BOOL, 0},
843 {"video", JANUS_JSON_BOOL, 0},
844 {"data", JANUS_JSON_BOOL, 0},
845 {"permanent", JANUS_JSON_BOOL, 0}
846 };
847 static struct janus_json_parameter rtp_parameters[] = {
848 {"collision", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
849 {"threads", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
850 {"srtpsuite", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
851 {"srtpcrypto", JSON_STRING, 0},
852 {"e2ee", JANUS_JSON_BOOL, 0}
853 };
854 static struct janus_json_parameter live_parameters[] = {
855 {"filename", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
856 {"audiortpmap", JSON_STRING, 0},
857 {"audiofmtp", JSON_STRING, 0},
858 {"audiopt", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
859 };
860 static struct janus_json_parameter ondemand_parameters[] = {
861 {"filename", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
862 {"audiortpmap", JSON_STRING, 0},
863 {"audiofmtp", JSON_STRING, 0},
864 {"audiopt", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
865 };
866 #ifdef HAVE_LIBCURL
867 static struct janus_json_parameter rtsp_parameters[] = {
868 {"url", JSON_STRING, 0},
869 {"rtsp_user", JSON_STRING, 0},
870 {"rtsp_pwd", JSON_STRING, 0},
871 {"rtsp_reconnect_delay", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
872 {"rtsp_session_timeout", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
873 {"rtsp_timeout", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
874 {"rtsp_conn_timeout", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
875 {"audiortpmap", JSON_STRING, 0},
876 {"audiofmtp", JSON_STRING, 0},
877 {"audiopt", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
878 {"videortpmap", JSON_STRING, 0},
879 {"videofmtp", JSON_STRING, 0},
880 {"videopt", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
881 {"videobufferkf", JANUS_JSON_BOOL, 0},
882 {"threads", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
883 {"rtspiface", JSON_STRING, 0},
884 {"rtsp_failcheck", JANUS_JSON_BOOL, 0}
885 };
886 #endif
887 static struct janus_json_parameter rtp_audio_parameters[] = {
888 {"audiomcast", JSON_STRING, 0},
889 {"audioport", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
890 {"audiortcpport", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
891 {"audiopt", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
892 {"audiortpmap", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
893 {"audiofmtp", JSON_STRING, 0},
894 {"audioiface", JSON_STRING, 0},
895 {"audioskew", JANUS_JSON_BOOL, 0}
896 };
897 static struct janus_json_parameter rtp_video_parameters[] = {
898 {"videomcast", JSON_STRING, 0},
899 {"videoport", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
900 {"videortcpport", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
901 {"videopt", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
902 {"videortpmap", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
903 {"videofmtp", JSON_STRING, 0},
904 {"videobufferkf", JANUS_JSON_BOOL, 0},
905 {"videoiface", JSON_STRING, 0},
906 {"videosimulcast", JANUS_JSON_BOOL, 0},
907 {"videoport2", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
908 {"videoport3", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
909 {"videoskew", JANUS_JSON_BOOL, 0},
910 {"videosvc", JANUS_JSON_BOOL, 0}
911 };
912 static struct janus_json_parameter rtp_data_parameters[] = {
913 {"dataport", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE},
914 {"databuffermsg", JANUS_JSON_BOOL, 0},
915 {"datatype", JSON_STRING, 0},
916 {"dataiface", JSON_STRING, 0}
917 };
918 static struct janus_json_parameter destroy_parameters[] = {
919 {"permanent", JANUS_JSON_BOOL, 0}
920 };
921 static struct janus_json_parameter recording_parameters[] = {
922 {"action", JSON_STRING, JANUS_JSON_PARAM_REQUIRED}
923 };
924 static struct janus_json_parameter recording_start_parameters[] = {
925 {"audio", JSON_STRING, 0},
926 {"video", JSON_STRING, 0},
927 {"data", JSON_STRING, 0}
928 };
929 static struct janus_json_parameter recording_stop_parameters[] = {
930 {"audio", JANUS_JSON_BOOL, 0},
931 {"video", JANUS_JSON_BOOL, 0},
932 {"data", JANUS_JSON_BOOL, 0}
933 };
934 static struct janus_json_parameter simulcast_parameters[] = {
935 {"substream", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
936 {"temporal", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
937 {"fallback", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
938 };
939 static struct janus_json_parameter svc_parameters[] = {
940 {"spatial_layer", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
941 {"temporal_layer", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
942 };
943 static struct janus_json_parameter configure_parameters[] = {
944 {"audio", JANUS_JSON_BOOL, 0},
945 {"video", JANUS_JSON_BOOL, 0},
946 {"data", JANUS_JSON_BOOL, 0},
947 /* For VP8 (or H.264) simulcast */
948 {"substream", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
949 {"temporal", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
950 {"fallback", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
951 /* For VP9 SVC */
952 {"spatial_layer", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE},
953 {"temporal_layer", JSON_INTEGER, JANUS_JSON_PARAM_POSITIVE}
954 };
955 static struct janus_json_parameter disable_parameters[] = {
956 {"stop_recording", JANUS_JSON_BOOL, 0}
957 };
958
959 /* Static configuration instance */
960 static janus_config *config = NULL;
961 static const char *config_folder = NULL;
962 static janus_mutex config_mutex = JANUS_MUTEX_INITIALIZER;
963
964 /* Useful stuff */
965 static volatile gint initialized = 0, stopping = 0;
966 static gboolean notify_events = TRUE;
967 static gboolean string_ids = FALSE;
968 static janus_callbacks *gateway = NULL;
969 static GThread *handler_thread;
970 static void *janus_streaming_handler(void *data);
971
972 /* RTP range to use for random ports */
973 #define DEFAULT_RTP_RANGE_MIN 10000
974 #define DEFAULT_RTP_RANGE_MAX 60000
975 static uint16_t rtp_range_min = DEFAULT_RTP_RANGE_MIN;
976 static uint16_t rtp_range_max = DEFAULT_RTP_RANGE_MAX;
977 static uint16_t rtp_range_slider = DEFAULT_RTP_RANGE_MIN;
978 static janus_mutex fd_mutex = JANUS_MUTEX_INITIALIZER;
979
980 static void *janus_streaming_ondemand_thread(void *data);
981 static void *janus_streaming_filesource_thread(void *data);
982 static void janus_streaming_relay_rtp_packet(gpointer data, gpointer user_data);
983 static void janus_streaming_relay_rtcp_packet(gpointer data, gpointer user_data);
984 static void *janus_streaming_relay_thread(void *data);
985 static void janus_streaming_hangup_media_internal(janus_plugin_session *handle);
986
987 typedef enum janus_streaming_type {
988 janus_streaming_type_none = 0,
989 janus_streaming_type_live,
990 janus_streaming_type_on_demand,
991 } janus_streaming_type;
992
993 typedef enum janus_streaming_source {
994 janus_streaming_source_none = 0,
995 janus_streaming_source_file,
996 janus_streaming_source_rtp,
997 } janus_streaming_source;
998
999 typedef struct janus_streaming_rtp_keyframe {
1000 gboolean enabled;
1001 /* If enabled, we store the packets of the last keyframe, to immediately send them for new viewers */
1002 GList *latest_keyframe;
1003 /* This is where we store packets while we're still collecting the whole keyframe */
1004 GList *temp_keyframe;
1005 guint32 temp_ts;
1006 janus_mutex mutex;
1007 } janus_streaming_rtp_keyframe;
1008
1009 typedef struct janus_streaming_rtp_relay_packet {
1010 janus_rtp_header *data;
1011 gint length;
1012 gboolean is_rtp; /* This may be a data packet and not RTP */
1013 gboolean is_data;
1014 gboolean is_video;
1015 gboolean is_keyframe;
1016 gboolean simulcast;
1017 uint32_t ssrc[3];
1018 janus_videocodec codec;
1019 int substream;
1020 int ptype;
1021 uint32_t timestamp;
1022 uint16_t seq_number;
1023 /* The following are only relevant for VP9 SVC*/
1024 gboolean svc;
1025 janus_vp9_svc_info svc_info;
1026 /* The following is only relevant for datachannels */
1027 gboolean textdata;
1028 } janus_streaming_rtp_relay_packet;
1029 static janus_streaming_rtp_relay_packet exit_packet;
janus_streaming_rtp_relay_packet_free(janus_streaming_rtp_relay_packet * pkt)1030 static void janus_streaming_rtp_relay_packet_free(janus_streaming_rtp_relay_packet *pkt) {
1031 if(pkt == NULL || pkt == &exit_packet)
1032 return;
1033 g_free(pkt->data);
1034 g_free(pkt);
1035
1036 }
1037
1038 #ifdef HAVE_LIBCURL
1039 typedef struct janus_streaming_buffer {
1040 char *buffer;
1041 size_t size;
1042 } janus_streaming_buffer;
1043 #endif
1044
1045 typedef struct janus_streaming_rtp_source {
1046 char *audio_host;
1047 gint audio_port, remote_audio_port;
1048 gint audio_rtcp_port, remote_audio_rtcp_port;
1049 in_addr_t audio_mcast;
1050 char *video_host;
1051 gint video_port[3], remote_video_port;
1052 gint video_rtcp_port, remote_video_rtcp_port;
1053 in_addr_t video_mcast;
1054 char *data_host;
1055 gint data_port;
1056 janus_recorder *arc; /* The Janus recorder instance for this streams's audio, if enabled */
1057 janus_recorder *vrc; /* The Janus recorder instance for this streams's video, if enabled */
1058 janus_recorder *drc; /* The Janus recorder instance for this streams's data, if enabled */
1059 janus_mutex rec_mutex; /* Mutex to protect the recorders from race conditions */
1060 janus_rtp_switching_context context[3];
1061 int audio_fd;
1062 int video_fd[3];
1063 int data_fd;
1064 int pipefd[2]; /* Just needed to quickly interrupt the poll when it's time to wrap up */
1065 int audio_rtcp_fd;
1066 int video_rtcp_fd;
1067 gboolean simulcast;
1068 gboolean svc;
1069 gboolean askew, vskew;
1070 gint64 last_received_audio;
1071 gint64 last_received_video;
1072 gint64 last_received_data;
1073 uint32_t audio_ssrc; /* Only needed for fixing outgoing RTCP packets */
1074 uint32_t video_ssrc; /* Only needed for fixing outgoing RTCP packets */
1075 volatile gint need_pli; /* Whether we need to send a PLI later */
1076 volatile gint sending_pli; /* Whether we're currently sending a PLI */
1077 gint64 pli_latest; /* Time of latest sent PLI (to avoid flooding) */
1078 uint32_t lowest_bitrate; /* Lowest bitrate received by viewers via REMB since last update */
1079 gint64 remb_latest; /* Time of latest sent REMB (to avoid flooding) */
1080 struct sockaddr_storage audio_rtcp_addr, video_rtcp_addr;
1081 #ifdef HAVE_LIBCURL
1082 gboolean rtsp;
1083 CURL *curl;
1084 janus_streaming_buffer *curldata;
1085 char *rtsp_url;
1086 char *rtsp_username, *rtsp_password;
1087 gint64 ka_timeout;
1088 char *rtsp_ahost, *rtsp_vhost;
1089 gboolean reconnecting;
1090 gint64 reconnect_timer;
1091 gint64 reconnect_delay;
1092 gint64 session_timeout;
1093 int rtsp_timeout;
1094 int rtsp_conn_timeout;
1095 janus_mutex rtsp_mutex;
1096 #endif
1097 janus_streaming_rtp_keyframe keyframe;
1098 gboolean textdata;
1099 gboolean buffermsg;
1100 int rtp_collision;
1101 void *last_msg;
1102 janus_mutex buffermsg_mutex;
1103 janus_network_address audio_iface;
1104 janus_network_address video_iface;
1105 janus_network_address data_iface;
1106 /* Only needed for SRTP support */
1107 gboolean is_srtp;
1108 int srtpsuite;
1109 char *srtpcrypto;
1110 srtp_t srtp_ctx;
1111 srtp_policy_t srtp_policy;
1112 /* If the media is end-to-end encrypted, we may need to know */
1113 gboolean e2ee;
1114 } janus_streaming_rtp_source;
1115
1116 typedef struct janus_streaming_file_source {
1117 char *filename;
1118 gboolean opus;
1119 } janus_streaming_file_source;
1120
1121 /* used for audio/video fd and RTCP fd */
1122 typedef struct multiple_fds {
1123 int fd;
1124 int rtcp_fd;
1125 } multiple_fds;
1126
1127 typedef struct janus_streaming_codecs {
1128 gint audio_pt;
1129 char *audio_rtpmap;
1130 char *audio_fmtp;
1131 janus_videocodec video_codec;
1132 gint video_pt;
1133 char *video_rtpmap;
1134 char *video_fmtp;
1135 } janus_streaming_codecs;
1136
1137 typedef struct janus_streaming_mountpoint {
1138 guint64 id; /* Unique mountpoint ID (when using integers) */
1139 gchar *id_str; /* Unique mountpoint ID (when using strings) */
1140 char *name;
1141 char *description;
1142 char *metadata;
1143 gboolean is_private;
1144 char *secret;
1145 char *pin;
1146 gboolean enabled;
1147 gboolean active;
1148 GThread *thread; /* A mountpoint may or may not have a thread */
1149 janus_streaming_type streaming_type;
1150 janus_streaming_source streaming_source;
1151 void *source; /* Can differ according to the source type */
1152 GDestroyNotify source_destroy;
1153 janus_streaming_codecs codecs;
1154 gboolean audio, video, data;
1155 GList *viewers;
1156 int helper_threads; /* Only relevant for RTP/RTSP mountpoints */
1157 GList *threads; /* Only relevant for RTP/RTSP mountpoints */
1158 volatile gint destroyed;
1159 janus_mutex mutex;
1160 janus_refcount ref;
1161 } janus_streaming_mountpoint;
1162 GHashTable *mountpoints = NULL, *mountpoints_temp = NULL;
1163 janus_mutex mountpoints_mutex = JANUS_MUTEX_INITIALIZER;
1164 static char *admin_key = NULL;
1165
1166 typedef struct janus_streaming_helper {
1167 janus_streaming_mountpoint *mp;
1168 guint id;
1169 GThread *thread;
1170 int num_viewers;
1171 GList *viewers;
1172 GAsyncQueue *queued_packets;
1173 volatile gint destroyed;
1174 janus_mutex mutex;
1175 janus_refcount ref;
1176 } janus_streaming_helper;
janus_streaming_helper_destroy(janus_streaming_helper * helper)1177 static void janus_streaming_helper_destroy(janus_streaming_helper *helper) {
1178 if(helper && g_atomic_int_compare_and_exchange(&helper->destroyed, 0, 1))
1179 janus_refcount_decrease(&helper->ref);
1180 }
janus_streaming_helper_free(const janus_refcount * helper_ref)1181 static void janus_streaming_helper_free(const janus_refcount *helper_ref) {
1182 janus_streaming_helper *helper = janus_refcount_containerof(helper_ref, janus_streaming_helper, ref);
1183 /* This helper can be destroyed, free all the resources */
1184 g_async_queue_unref(helper->queued_packets);
1185 if(helper->viewers != NULL)
1186 g_list_free(helper->viewers);
1187 g_free(helper);
1188 }
1189 static void *janus_streaming_helper_thread(void *data);
1190 static void janus_streaming_helper_rtprtcp_packet(gpointer data, gpointer user_data);
1191
1192 /* Helper to create an RTP live source (e.g., from gstreamer/ffmpeg/vlc/etc.) */
1193 janus_streaming_mountpoint *janus_streaming_create_rtp_source(
1194 uint64_t id, char *id_str, char *name, char *desc, char *metadata,
1195 int srtpsuite, char *srtpcrypto, int threads, gboolean e2ee,
1196 gboolean doaudio, gboolean doaudiortcp, char *amcast, const janus_network_address *aiface,
1197 uint16_t aport, uint16_t artcpport, uint8_t acodec, char *artpmap, char *afmtp, gboolean doaskew,
1198 gboolean dovideo, gboolean dovideortcp, char *vmcast, const janus_network_address *viface,
1199 uint16_t vport, uint16_t vrtcpport, uint8_t vcodec, char *vrtpmap, char *vfmtp, gboolean bufferkf,
1200 gboolean simulcast, uint16_t vport2, uint16_t vport3, gboolean svc, gboolean dovskew, int rtp_collision,
1201 gboolean dodata, const janus_network_address *diface, uint16_t dport, gboolean textdata, gboolean buffermsg);
1202 /* Helper to create a file/ondemand live source */
1203 janus_streaming_mountpoint *janus_streaming_create_file_source(
1204 uint64_t id, char *id_str, char *name, char *desc, char *metadata, char *filename, gboolean live,
1205 gboolean doaudio, uint8_t acodec, char *artpmap, char *afmtp, gboolean dovideo);
1206 /* Helper to create a rtsp live source */
1207 janus_streaming_mountpoint *janus_streaming_create_rtsp_source(
1208 uint64_t id, char *id_str, char *name, char *desc, char *metadata,
1209 char *url, char *username, char *password,
1210 gboolean doaudio, int audiopt, char *artpmap, char *afmtp,
1211 gboolean dovideo, int videopt, char *vrtpmap, char *vfmtp, gboolean bufferkf,
1212 const janus_network_address *iface, int threads,
1213 gint64 reconnect_delay, gint64 session_timeout, int rtsp_timeout, int rtsp_conn_timeout,
1214 gboolean error_on_failure);
1215
1216 typedef struct janus_streaming_message {
1217 janus_plugin_session *handle;
1218 char *transaction;
1219 json_t *message;
1220 json_t *jsep;
1221 } janus_streaming_message;
1222 static GAsyncQueue *messages = NULL;
1223 static janus_streaming_message exit_message;
1224
1225 typedef struct janus_streaming_session {
1226 janus_plugin_session *handle;
1227 janus_streaming_mountpoint *mountpoint;
1228 gint64 sdp_sessid;
1229 gint64 sdp_version;
1230 volatile gint started;
1231 volatile gint paused;
1232 gboolean audio, video, data; /* Whether audio, video and/or data must be sent to this listener */
1233 int audio_pt, video_pt;
1234 janus_rtp_switching_context context;
1235 janus_rtp_simulcasting_context sim_context;
1236 janus_vp8_simulcast_context vp8_context;
1237 /* The following are only relevant the mountpoint is VP9-SVC, and are not to be confused with VP8
1238 * simulcast, which has similar info (substream/templayer) but in a completely different context */
1239 int spatial_layer, target_spatial_layer;
1240 gint64 last_spatial_layer[3];
1241 int temporal_layer, target_temporal_layer;
1242 /* If the media is end-to-end encrypted, we may need to know */
1243 gboolean e2ee;
1244 janus_mutex mutex;
1245 volatile gint dataready;
1246 volatile gint stopping;
1247 volatile gint renegotiating;
1248 volatile gint hangingup;
1249 volatile gint destroyed;
1250 janus_refcount ref;
1251 } janus_streaming_session;
1252 static GHashTable *sessions;
1253 static janus_mutex sessions_mutex = JANUS_MUTEX_INITIALIZER;
1254
janus_streaming_session_destroy(janus_streaming_session * session)1255 static void janus_streaming_session_destroy(janus_streaming_session *session) {
1256 if(session && g_atomic_int_compare_and_exchange(&session->destroyed, 0, 1))
1257 janus_refcount_decrease(&session->ref);
1258 }
1259
janus_streaming_session_free(const janus_refcount * session_ref)1260 static void janus_streaming_session_free(const janus_refcount *session_ref) {
1261 janus_streaming_session *session = janus_refcount_containerof(session_ref, janus_streaming_session, ref);
1262 /* Remove the reference to the core plugin session */
1263 janus_refcount_decrease(&session->handle->ref);
1264 /* This session can be destroyed, free all the resources */
1265 g_free(session);
1266 }
1267
janus_streaming_mountpoint_destroy(janus_streaming_mountpoint * mountpoint)1268 static void janus_streaming_mountpoint_destroy(janus_streaming_mountpoint *mountpoint) {
1269 if(!mountpoint)
1270 return;
1271 if(!g_atomic_int_compare_and_exchange(&mountpoint->destroyed, 0, 1))
1272 return;
1273 /* If this is an RTP source, interrupt the poll */
1274 if(mountpoint->streaming_source == janus_streaming_source_rtp) {
1275 janus_streaming_rtp_source *source = mountpoint->source;
1276 if(source != NULL && source->pipefd[1] > 0) {
1277 int code = 1;
1278 ssize_t res = 0;
1279 do {
1280 res = write(source->pipefd[1], &code, sizeof(int));
1281 } while(res == -1 && errno == EINTR);
1282 }
1283 }
1284 /* Wait for the thread to finish */
1285 if(mountpoint->thread != NULL)
1286 g_thread_join(mountpoint->thread);
1287 /* Get rid of the helper threads, if any */
1288 if(mountpoint->helper_threads > 0) {
1289 GList *l = mountpoint->threads;
1290 while(l) {
1291 janus_streaming_helper *ht = (janus_streaming_helper *)l->data;
1292 g_async_queue_push(ht->queued_packets, &exit_packet);
1293 janus_streaming_helper_destroy(ht);
1294 l = l->next;
1295 }
1296 }
1297 /* Decrease the counter */
1298 janus_refcount_decrease(&mountpoint->ref);
1299 }
1300
janus_streaming_mountpoint_free(const janus_refcount * mp_ref)1301 static void janus_streaming_mountpoint_free(const janus_refcount *mp_ref) {
1302 janus_streaming_mountpoint *mp = janus_refcount_containerof(mp_ref, janus_streaming_mountpoint, ref);
1303 /* This mountpoint can be destroyed, free all the resources */
1304
1305 g_free(mp->id_str);
1306 g_free(mp->name);
1307 g_free(mp->description);
1308 g_free(mp->metadata);
1309 g_free(mp->secret);
1310 g_free(mp->pin);
1311 janus_mutex_lock(&mp->mutex);
1312 if(mp->viewers != NULL)
1313 g_list_free(mp->viewers);
1314 if(mp->threads != NULL) {
1315 /* Remove the last reference to the helper threads, if any */
1316 GList *l = mp->threads;
1317 while(l) {
1318 janus_streaming_helper *ht = (janus_streaming_helper *)l->data;
1319 janus_refcount_decrease(&ht->ref);
1320 l = l->next;
1321 }
1322 /* Destroy the list */
1323 g_list_free(mp->threads);
1324 }
1325 janus_mutex_unlock(&mp->mutex);
1326
1327 if(mp->source != NULL && mp->source_destroy != NULL) {
1328 mp->source_destroy(mp->source);
1329 }
1330
1331 g_free(mp->codecs.audio_rtpmap);
1332 g_free(mp->codecs.audio_fmtp);
1333 g_free(mp->codecs.video_rtpmap);
1334 g_free(mp->codecs.video_fmtp);
1335
1336 g_free(mp);
1337 }
1338
janus_streaming_message_free(janus_streaming_message * msg)1339 static void janus_streaming_message_free(janus_streaming_message *msg) {
1340 if(!msg || msg == &exit_message)
1341 return;
1342
1343 if(msg->handle && msg->handle->plugin_handle) {
1344 janus_streaming_session *session = (janus_streaming_session *)msg->handle->plugin_handle;
1345 janus_refcount_decrease(&session->ref);
1346 }
1347 msg->handle = NULL;
1348
1349 g_free(msg->transaction);
1350 msg->transaction = NULL;
1351 if(msg->message)
1352 json_decref(msg->message);
1353 msg->message = NULL;
1354 if(msg->jsep)
1355 json_decref(msg->jsep);
1356 msg->jsep = NULL;
1357
1358 g_free(msg);
1359 }
1360
1361 #ifdef HAVE_LIBOGG
1362 /* Helper struct to handle the playout of Opus files */
1363 typedef struct janus_streaming_opus_context {
1364 char *name, *filename;
1365 FILE *file;
1366 ogg_sync_state sync;
1367 ogg_stream_state stream;
1368 ogg_page page;
1369 ogg_packet pkt;
1370 char *oggbuf;
1371 gint state, headers;
1372 } janus_streaming_opus_context;
1373 /* Helper method to open an Opus file, and make sure it's valid */
janus_streaming_opus_context_init(janus_streaming_opus_context * ctx)1374 static int janus_streaming_opus_context_init(janus_streaming_opus_context *ctx) {
1375 if(ctx == NULL || ctx->file == NULL)
1376 return -1;
1377 fseek(ctx->file, 0, SEEK_SET);
1378 ogg_stream_clear(&ctx->stream);
1379 ogg_sync_clear(&ctx->sync);
1380 if(ogg_sync_init(&ctx->sync) < 0) {
1381 JANUS_LOG(LOG_ERR, "[%s] Error re-initializing Ogg sync state...\n", ctx->name);
1382 return -1;
1383 }
1384 ctx->headers = 0;
1385 return 0;
1386 }
1387 /* Helper method to check if an Ogg page begins with an Ogg stream */
janus_streaming_ogg_is_opus(ogg_page * page)1388 static gboolean janus_streaming_ogg_is_opus(ogg_page *page) {
1389 ogg_stream_state state;
1390 ogg_packet pkt;
1391 ogg_stream_init(&state, ogg_page_serialno(page));
1392 ogg_stream_pagein(&state, page);
1393 if(ogg_stream_packetout(&state, &pkt) == 1) {
1394 if(pkt.bytes >= 19 && !memcmp(pkt.packet, "OpusHead", 8)) {
1395 ogg_stream_clear(&state);
1396 return 1;
1397 }
1398 }
1399 ogg_stream_clear(&state);
1400 return FALSE;
1401 }
1402 /* Helper method to traverse the Opus file until we get a packet we can send */
janus_streaming_opus_context_read(janus_streaming_opus_context * ctx,char * buffer,int length)1403 static int janus_streaming_opus_context_read(janus_streaming_opus_context *ctx, char *buffer, int length) {
1404 if(ctx == NULL || ctx->file == NULL || buffer == NULL)
1405 return -1;
1406 /* Check our current state in processing the Ogg file */
1407 int read = 0;
1408 if(ctx->state == 0) {
1409 /* Prepare a buffer, and read from the Ogg file... */
1410 ctx->oggbuf = ogg_sync_buffer(&ctx->sync, 8192);
1411 if(ctx->oggbuf == NULL) {
1412 JANUS_LOG(LOG_ERR, "[%s] ogg_sync_buffer failed...\n", ctx->name);
1413 return -2;
1414 }
1415 read = fread(ctx->oggbuf, 1, 8192, ctx->file);
1416 if(read == 0 && feof(ctx->file)) {
1417 /* FIXME We're doing this forever... should this be configurable? */
1418 JANUS_LOG(LOG_VERB, "[%s] Rewind! (%s)\n", ctx->name, ctx->filename);
1419 if(janus_streaming_opus_context_init(ctx) < 0)
1420 return -3;
1421 return janus_streaming_opus_context_read(ctx, buffer, length);
1422 }
1423 if(ogg_sync_wrote(&ctx->sync, read) < 0) {
1424 JANUS_LOG(LOG_ERR, "[%s] ogg_sync_wrote failed...\n", ctx->name);
1425 return -4;
1426 }
1427 /* Next state: sync pageout */
1428 ctx->state = 1;
1429 }
1430 if(ctx->state == 1) {
1431 /* Prepare an ogg_page out of the buffer */
1432 while((read = ogg_sync_pageout(&ctx->sync, &ctx->page)) == 1) {
1433 /* Let's look for an Opus stream, first of all */
1434 if(ctx->headers == 0) {
1435 if(janus_streaming_ogg_is_opus(&ctx->page)) {
1436 /* This is the start of an Opus stream */
1437 if(ogg_stream_init(&ctx->stream, ogg_page_serialno(&ctx->page)) < 0) {
1438 JANUS_LOG(LOG_ERR, "[%s] ogg_stream_init failed...\n", ctx->name);
1439 return -5;
1440 }
1441 ctx->headers++;
1442 } else if(!ogg_page_bos(&ctx->page)) {
1443 /* No Opus stream? */
1444 JANUS_LOG(LOG_ERR, "[%s] No Opus stream...\n", ctx->name);
1445 return -6;
1446 } else {
1447 /* Still waiting for an Opus stream */
1448 return janus_streaming_opus_context_read(ctx, buffer, length);
1449 }
1450 }
1451 /* Submit the page for packetization */
1452 if(ogg_stream_pagein(&ctx->stream, &ctx->page) < 0) {
1453 JANUS_LOG(LOG_ERR, "[%s] ogg_stream_pagein failed...\n", ctx->name);
1454 return -7;
1455 }
1456 /* Time to start reading packets */
1457 ctx->state = 2;
1458 break;
1459 }
1460 if(read != 1) {
1461 /* Go back to reading from the file */
1462 ctx->state = 0;
1463 return janus_streaming_opus_context_read(ctx, buffer, length);
1464 }
1465 }
1466 if(ctx->state == 2) {
1467 /* Read and process available packets */
1468 if(ogg_stream_packetout(&ctx->stream, &ctx->pkt) != 1) {
1469 /* Go back to reading pages */
1470 ctx->state = 1;
1471 return janus_streaming_opus_context_read(ctx, buffer, length);
1472 } else {
1473 /* Skip header packets */
1474 if(ctx->headers == 1 && ctx->pkt.bytes >= 19 && !memcmp(ctx->pkt.packet, "OpusHead", 8)) {
1475 ctx->headers++;
1476 return janus_streaming_opus_context_read(ctx, buffer, length);
1477 }
1478 if(ctx->headers == 2 && ctx->pkt.bytes >= 16 && !memcmp(ctx->pkt.packet, "OpusTags", 8)) {
1479 ctx->headers++;
1480 return janus_streaming_opus_context_read(ctx, buffer, length);
1481 }
1482 /* Get the packet duration */
1483 if(length < ctx->pkt.bytes) {
1484 JANUS_LOG(LOG_WARN, "[%s] Buffer too short for Opus packet (%d < %ld)\n",
1485 ctx->name, length, ctx->pkt.bytes);
1486 return -8;
1487 }
1488 memcpy(buffer, ctx->pkt.packet, ctx->pkt.bytes);
1489 length = ctx->pkt.bytes;
1490 return length;
1491 }
1492 }
1493 /* If we got here, continue with the iteration */
1494 return -9;
1495 }
1496 /* Helper method to cleanup an Opus context */
janus_streaming_opus_context_cleanup(janus_streaming_opus_context * ctx)1497 static void janus_streaming_opus_context_cleanup(janus_streaming_opus_context *ctx) {
1498 if(ctx == NULL)
1499 return;
1500 if(ctx->headers > 0)
1501 ogg_stream_clear(&ctx->stream);
1502 ogg_sync_clear(&ctx->sync);
1503 }
1504 #endif
1505
1506
1507 /* Helper method to send an RTCP PLI */
janus_streaming_rtcp_pli_send(janus_streaming_rtp_source * source)1508 static void janus_streaming_rtcp_pli_send(janus_streaming_rtp_source *source) {
1509 if(source == NULL || source->video_rtcp_fd < 0 || source->video_rtcp_addr.ss_family == 0)
1510 return;
1511 if(!g_atomic_int_compare_and_exchange(&source->sending_pli, 0, 1))
1512 return;
1513 gint64 now = janus_get_monotonic_time();
1514 if(now - source->pli_latest < G_USEC_PER_SEC) {
1515 /* We just sent a PLI less than a second ago, schedule a new delivery later */
1516 g_atomic_int_set(&source->need_pli, 1);
1517 g_atomic_int_set(&source->sending_pli, 0);
1518 return;
1519 }
1520 /* Update the time of when we last sent a keyframe request */
1521 g_atomic_int_set(&source->need_pli, 0);
1522 source->pli_latest = janus_get_monotonic_time();
1523 JANUS_LOG(LOG_HUGE, "Sending PLI\n");
1524 /* Generate a PLI */
1525 char rtcp_buf[12];
1526 int rtcp_len = 12;
1527 janus_rtcp_pli((char *)&rtcp_buf, rtcp_len);
1528 janus_rtcp_fix_ssrc(NULL, rtcp_buf, rtcp_len, 1, 1, source->video_ssrc);
1529 /* Send the packet */
1530 int sent = 0;
1531 if((sent = sendto(source->video_rtcp_fd, rtcp_buf, rtcp_len, 0,
1532 (struct sockaddr *)&source->video_rtcp_addr, sizeof(source->video_rtcp_addr))) < 0) {
1533 JANUS_LOG(LOG_ERR, "Error in sendto... %d (%s)\n", errno, g_strerror(errno));
1534 } else {
1535 JANUS_LOG(LOG_HUGE, "Sent %d/%d bytes\n", sent, rtcp_len);
1536 }
1537 g_atomic_int_set(&source->sending_pli, 0);
1538 }
1539
1540 /* Helper method to send an RTCP REMB */
janus_streaming_rtcp_remb_send(janus_streaming_rtp_source * source)1541 static void janus_streaming_rtcp_remb_send(janus_streaming_rtp_source *source) {
1542 if(source == NULL || source->video_rtcp_fd < 0 || source->video_rtcp_addr.ss_family == 0)
1543 return;
1544 /* Update the time of when we last sent REMB feedback */
1545 source->remb_latest = janus_get_monotonic_time();
1546 /* Generate a REMB */
1547 char rtcp_buf[24];
1548 int rtcp_len = 24;
1549 janus_rtcp_remb((char *)(&rtcp_buf), rtcp_len, source->lowest_bitrate);
1550 janus_rtcp_fix_ssrc(NULL, rtcp_buf, rtcp_len, 1, 1, source->video_ssrc);
1551 JANUS_LOG(LOG_HUGE, "Sending REMB: %"SCNu32"\n", source->lowest_bitrate);
1552 /* Reset the lowest bitrate */
1553 source->lowest_bitrate = 0;
1554 /* Send the packet */
1555 int sent = 0;
1556 if((sent = sendto(source->video_rtcp_fd, rtcp_buf, rtcp_len, 0,
1557 (struct sockaddr *)&source->video_rtcp_addr, sizeof(source->video_rtcp_addr))) < 0) {
1558 JANUS_LOG(LOG_ERR, "Error in sendto... %d (%s)\n", errno, g_strerror(errno));
1559 } else {
1560 JANUS_LOG(LOG_HUGE, "Sent %d/%d bytes\n", sent, rtcp_len);
1561 }
1562 }
1563
1564
1565 /* Error codes */
1566 #define JANUS_STREAMING_ERROR_NO_MESSAGE 450
1567 #define JANUS_STREAMING_ERROR_INVALID_JSON 451
1568 #define JANUS_STREAMING_ERROR_INVALID_REQUEST 452
1569 #define JANUS_STREAMING_ERROR_MISSING_ELEMENT 453
1570 #define JANUS_STREAMING_ERROR_INVALID_ELEMENT 454
1571 #define JANUS_STREAMING_ERROR_NO_SUCH_MOUNTPOINT 455
1572 #define JANUS_STREAMING_ERROR_CANT_CREATE 456
1573 #define JANUS_STREAMING_ERROR_UNAUTHORIZED 457
1574 #define JANUS_STREAMING_ERROR_CANT_SWITCH 458
1575 #define JANUS_STREAMING_ERROR_CANT_RECORD 459
1576 #define JANUS_STREAMING_ERROR_INVALID_STATE 460
1577 #define JANUS_STREAMING_ERROR_UNKNOWN_ERROR 470
1578
1579
1580 /* Plugin implementation */
janus_streaming_init(janus_callbacks * callback,const char * config_path)1581 int janus_streaming_init(janus_callbacks *callback, const char *config_path) {
1582 #ifdef HAVE_LIBCURL
1583 curl_global_init(CURL_GLOBAL_ALL);
1584 #else
1585 JANUS_LOG(LOG_WARN, "libcurl not available, Streaming plugin will not have RTSP support\n");
1586 #endif
1587 #ifndef HAVE_LIBOGG
1588 JANUS_LOG(LOG_WARN, "libogg not available, Streaming plugin will not have file-based Opus streaming\n");
1589 #endif
1590 if(g_atomic_int_get(&stopping)) {
1591 /* Still stopping from before */
1592 return -1;
1593 }
1594 if(callback == NULL || config_path == NULL) {
1595 /* Invalid arguments */
1596 return -1;
1597 }
1598
1599 struct ifaddrs *ifas = NULL;
1600 if(getifaddrs(&ifas) == -1) {
1601 JANUS_LOG(LOG_ERR, "Unable to acquire list of network devices/interfaces; some configurations may not work as expected... %d (%s)\n",
1602 errno, g_strerror(errno));
1603 }
1604
1605 /* Read configuration */
1606 char filename[255];
1607 g_snprintf(filename, 255, "%s/%s.jcfg", config_path, JANUS_STREAMING_PACKAGE);
1608 JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
1609 config = janus_config_parse(filename);
1610 if(config == NULL) {
1611 JANUS_LOG(LOG_WARN, "Couldn't find .jcfg configuration file (%s), trying .cfg\n", JANUS_STREAMING_PACKAGE);
1612 g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_STREAMING_PACKAGE);
1613 JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename);
1614 config = janus_config_parse(filename);
1615 }
1616 config_folder = config_path;
1617 if(config != NULL)
1618 janus_config_print(config);
1619
1620 /* Threads will expect this to be set */
1621 g_atomic_int_set(&initialized, 1);
1622
1623 /* Parse configuration to populate the mountpoints */
1624 if(config != NULL) {
1625 janus_config_category *config_general = janus_config_get_create(config, NULL, janus_config_type_category, "general");
1626 /* Any admin key to limit who can "create"? */
1627 janus_config_item *key = janus_config_get(config, config_general, janus_config_type_item, "admin_key");
1628 if(key != NULL && key->value != NULL)
1629 admin_key = g_strdup(key->value);
1630 janus_config_item *range = janus_config_get(config, config_general, janus_config_type_item, "rtp_port_range");
1631 if(range && range->value) {
1632 /* Split in min and max port */
1633 char *maxport = strrchr(range->value, '-');
1634 if(maxport != NULL) {
1635 *maxport = '\0';
1636 maxport++;
1637 if(janus_string_to_uint16(range->value, &rtp_range_min) < 0)
1638 JANUS_LOG(LOG_WARN, "Invalid RTP min port value: %s (assuming 0)\n", range->value);
1639 if(janus_string_to_uint16(maxport, &rtp_range_max) < 0)
1640 JANUS_LOG(LOG_WARN, "Invalid RTP max port value: %s (assuming 0)\n", maxport);
1641 maxport--;
1642 *maxport = '-';
1643 }
1644 if(rtp_range_min > rtp_range_max) {
1645 uint16_t temp_port = rtp_range_min;
1646 rtp_range_min = rtp_range_max;
1647 rtp_range_max = temp_port;
1648 }
1649 if(rtp_range_min % 2)
1650 rtp_range_min++; /* Pick an even port for RTP */
1651 if(rtp_range_min > rtp_range_max) {
1652 JANUS_LOG(LOG_WARN, "Incorrect port range (%u -- %u), switching min and max\n", rtp_range_min, rtp_range_max);
1653 uint16_t range_temp = rtp_range_max;
1654 rtp_range_max = rtp_range_min;
1655 rtp_range_min = range_temp;
1656 }
1657 if(rtp_range_max == 0)
1658 rtp_range_max = 65535;
1659 rtp_range_slider = rtp_range_min;
1660 JANUS_LOG(LOG_VERB, "Streaming RTP/RTCP port range: %u -- %u\n", rtp_range_min, rtp_range_max);
1661 }
1662 janus_config_item *events = janus_config_get(config, config_general, janus_config_type_item, "events");
1663 if(events != NULL && events->value != NULL)
1664 notify_events = janus_is_true(events->value);
1665 if(!notify_events && callback->events_is_enabled()) {
1666 JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_STREAMING_NAME);
1667 }
1668 janus_config_item *ids = janus_config_get(config, config_general, janus_config_type_item, "string_ids");
1669 if(ids != NULL && ids->value != NULL)
1670 string_ids = janus_is_true(ids->value);
1671 if(string_ids) {
1672 JANUS_LOG(LOG_INFO, "Streaming will use alphanumeric IDs, not numeric\n");
1673 }
1674 }
1675 /* Iterate on all mountpoints */
1676 mountpoints = g_hash_table_new_full(string_ids ? g_str_hash : g_int64_hash, string_ids ? g_str_equal : g_int64_equal,
1677 (GDestroyNotify)g_free, (GDestroyNotify)janus_streaming_mountpoint_destroy);
1678 mountpoints_temp = g_hash_table_new_full(string_ids ? g_str_hash : g_int64_hash, string_ids ? g_str_equal : g_int64_equal,
1679 (GDestroyNotify)g_free, NULL);
1680 if(config != NULL) {
1681 GList *clist = janus_config_get_categories(config, NULL), *cl = clist;
1682 while(cl != NULL) {
1683 janus_config_category *cat = (janus_config_category *)cl->data;
1684 if(cat->name == NULL || !strcasecmp(cat->name, "general")) {
1685 cl = cl->next;
1686 continue;
1687 }
1688 JANUS_LOG(LOG_VERB, "Adding Streaming mountpoint '%s'\n", cat->name);
1689 janus_config_item *type = janus_config_get(config, cat, janus_config_type_item, "type");
1690 if(type == NULL || type->value == NULL) {
1691 JANUS_LOG(LOG_WARN, " -- Invalid type, skipping mountpoint '%s'...\n", cat->name);
1692 cl = cl->next;
1693 continue;
1694 }
1695 janus_config_item *id = janus_config_get(config, cat, janus_config_type_item, "id");
1696 guint64 mpid = 0;
1697 if(id == NULL || id->value == NULL) {
1698 JANUS_LOG(LOG_VERB, "Missing id for mountpoint '%s', will generate a random one...\n", cat->name);
1699 } else {
1700 janus_mutex_lock(&mountpoints_mutex);
1701 if(!string_ids) {
1702 mpid = g_ascii_strtoull(id->value, 0, 10);
1703 /* Make sure the ID is completely numeric */
1704 char mpid_str[30];
1705 g_snprintf(mpid_str, sizeof(mpid_str), "%"SCNu64, mpid);
1706 if(strcmp(id->value, mpid_str)) {
1707 janus_mutex_unlock(&mountpoints_mutex);
1708 JANUS_LOG(LOG_ERR, "Can't add the Streaming mountpoint '%s', ID '%s' is not numeric...\n",
1709 cat->name, id->value);
1710 cl = cl->next;
1711 continue;
1712 }
1713 if(mpid == 0) {
1714 janus_mutex_unlock(&mountpoints_mutex);
1715 JANUS_LOG(LOG_ERR, "Can't add the Streaming mountpoint '%s', invalid ID '%s'...\n",
1716 cat->name, id->value);
1717 cl = cl->next;
1718 continue;
1719 }
1720 }
1721 /* Let's make sure the mountpoint doesn't exist already */
1722 if(g_hash_table_lookup(mountpoints, string_ids ? (gpointer)id->value : (gpointer)&mpid) != NULL) {
1723 /* It does... */
1724 janus_mutex_unlock(&mountpoints_mutex);
1725 JANUS_LOG(LOG_ERR, "Can't add the Streaming mountpoint '%s', ID '%s' already exists...\n",
1726 cat->name, id->value);
1727 cl = cl->next;
1728 continue;
1729 }
1730 janus_mutex_unlock(&mountpoints_mutex);
1731 }
1732 if(!strcasecmp(type->value, "rtp")) {
1733 janus_network_address video_iface, audio_iface, data_iface;
1734 /* RTP live source (e.g., from gstreamer/ffmpeg/vlc/etc.) */
1735 janus_config_item *desc = janus_config_get(config, cat, janus_config_type_item, "description");
1736 janus_config_item *md = janus_config_get(config, cat, janus_config_type_item, "metadata");
1737 janus_config_item *priv = janus_config_get(config, cat, janus_config_type_item, "is_private");
1738 janus_config_item *secret = janus_config_get(config, cat, janus_config_type_item, "secret");
1739 janus_config_item *pin = janus_config_get(config, cat, janus_config_type_item, "pin");
1740 janus_config_item *audio = janus_config_get(config, cat, janus_config_type_item, "audio");
1741 janus_config_item *askew = janus_config_get(config, cat, janus_config_type_item, "audioskew");
1742 janus_config_item *video = janus_config_get(config, cat, janus_config_type_item, "video");
1743 janus_config_item *vskew = janus_config_get(config, cat, janus_config_type_item, "videoskew");
1744 janus_config_item *vsvc = janus_config_get(config, cat, janus_config_type_item, "videosvc");
1745 janus_config_item *data = janus_config_get(config, cat, janus_config_type_item, "data");
1746 janus_config_item *diface = janus_config_get(config, cat, janus_config_type_item, "dataiface");
1747 janus_config_item *amcast = janus_config_get(config, cat, janus_config_type_item, "audiomcast");
1748 janus_config_item *aiface = janus_config_get(config, cat, janus_config_type_item, "audioiface");
1749 janus_config_item *aport = janus_config_get(config, cat, janus_config_type_item, "audioport");
1750 janus_config_item *artcpport = janus_config_get(config, cat, janus_config_type_item, "audiortcpport");
1751 janus_config_item *acodec = janus_config_get(config, cat, janus_config_type_item, "audiopt");
1752 janus_config_item *artpmap = janus_config_get(config, cat, janus_config_type_item, "audiortpmap");
1753 janus_config_item *afmtp = janus_config_get(config, cat, janus_config_type_item, "audiofmtp");
1754 janus_config_item *vmcast = janus_config_get(config, cat, janus_config_type_item, "videomcast");
1755 janus_config_item *viface = janus_config_get(config, cat, janus_config_type_item, "videoiface");
1756 janus_config_item *vport = janus_config_get(config, cat, janus_config_type_item, "videoport");
1757 janus_config_item *vrtcpport = janus_config_get(config, cat, janus_config_type_item, "videortcpport");
1758 janus_config_item *vcodec = janus_config_get(config, cat, janus_config_type_item, "videopt");
1759 janus_config_item *vrtpmap = janus_config_get(config, cat, janus_config_type_item, "videortpmap");
1760 janus_config_item *vfmtp = janus_config_get(config, cat, janus_config_type_item, "videofmtp");
1761 janus_config_item *vkf = janus_config_get(config, cat, janus_config_type_item, "videobufferkf");
1762 janus_config_item *vsc = janus_config_get(config, cat, janus_config_type_item, "videosimulcast");
1763 janus_config_item *vport2 = janus_config_get(config, cat, janus_config_type_item, "videoport2");
1764 janus_config_item *vport3 = janus_config_get(config, cat, janus_config_type_item, "videoport3");
1765 janus_config_item *dport = janus_config_get(config, cat, janus_config_type_item, "dataport");
1766 janus_config_item *dbm = janus_config_get(config, cat, janus_config_type_item, "databuffermsg");
1767 janus_config_item *dt = janus_config_get(config, cat, janus_config_type_item, "datatype");
1768 janus_config_item *rtpcollision = janus_config_get(config, cat, janus_config_type_item, "collision");
1769 janus_config_item *threads = janus_config_get(config, cat, janus_config_type_item, "threads");
1770 janus_config_item *ssuite = janus_config_get(config, cat, janus_config_type_item, "srtpsuite");
1771 janus_config_item *scrypto = janus_config_get(config, cat, janus_config_type_item, "srtpcrypto");
1772 janus_config_item *e2ee = janus_config_get(config, cat, janus_config_type_item, "e2ee");
1773 gboolean is_private = priv && priv->value && janus_is_true(priv->value);
1774 gboolean doaudio = audio && audio->value && janus_is_true(audio->value);
1775 gboolean doaskew = audio && askew && askew->value && janus_is_true(askew->value);
1776 gboolean dovideo = video && video->value && janus_is_true(video->value);
1777 gboolean dovskew = video && vskew && vskew->value && janus_is_true(vskew->value);
1778 gboolean dosvc = video && vsvc && vsvc->value && janus_is_true(vsvc->value);
1779 gboolean dodata = data && data->value && janus_is_true(data->value);
1780 gboolean bufferkf = video && vkf && vkf->value && janus_is_true(vkf->value);
1781 gboolean simulcast = video && vsc && vsc->value && janus_is_true(vsc->value);
1782 if(simulcast && bufferkf) {
1783 /* FIXME We'll need to take care of this */
1784 JANUS_LOG(LOG_WARN, "Simulcasting enabled, so disabling buffering of keyframes\n");
1785 bufferkf = FALSE;
1786 }
1787 gboolean buffermsg = data && dbm && dbm->value && janus_is_true(dbm->value);
1788 gboolean textdata = TRUE;
1789 if(data && dt && dt->value) {
1790 if(!strcasecmp(dt->value, "text"))
1791 textdata = TRUE;
1792 else if(!strcasecmp(dt->value, "binary"))
1793 textdata = FALSE;
1794 else {
1795 JANUS_LOG(LOG_ERR, "Can't add 'rtp' mountpoint '%s', invalid data type '%s'...\n", cat->name, dt->value);
1796 cl = cl->next;
1797 continue;
1798 }
1799 }
1800 if(!doaudio && !dovideo && !dodata) {
1801 JANUS_LOG(LOG_ERR, "Can't add 'rtp' mountpoint '%s', no audio, video or data have to be streamed...\n", cat->name);
1802 cl = cl->next;
1803 continue;
1804 }
1805 uint16_t audio_port = 0, audio_rtcp_port = 0;
1806 if(doaudio &&
1807 (aport == NULL || aport->value == NULL ||
1808 janus_string_to_uint16(aport->value, &audio_port) < 0 || audio_port == 0 ||
1809 acodec == NULL || acodec->value == NULL ||
1810 artpmap == NULL || artpmap->value == NULL)) {
1811 JANUS_LOG(LOG_ERR, "Can't add 'rtp' mountpoint '%s', missing mandatory information for audio...\n", cat->name);
1812 cl = cl->next;
1813 continue;
1814 }
1815 if(doaudio && artcpport != NULL && artcpport->value != NULL &&
1816 (janus_string_to_uint16(artcpport->value, &audio_rtcp_port) < 0)) {
1817 JANUS_LOG(LOG_ERR, "Can't add 'rtp' mountpoint '%s', invalid audio RTCP port...\n", cat->name);
1818 cl = cl->next;
1819 continue;
1820 }
1821 gboolean doaudiortcp = (artcpport != NULL && artcpport->value != NULL);
1822 if(doaudio && aiface) {
1823 if(!ifas) {
1824 JANUS_LOG(LOG_ERR, "Skipping 'rtp' mountpoint '%s', it relies on network configuration but network device information is unavailable...\n", cat->name);
1825 cl = cl->next;
1826 continue;
1827 }
1828 if(janus_network_lookup_interface(ifas, aiface->value, &audio_iface) != 0) {
1829 JANUS_LOG(LOG_ERR, "Can't add 'rtp' mountpoint '%s', invalid network interface configuration for audio...\n", cat->name);
1830 cl = cl->next;
1831 continue;
1832 }
1833 }
1834 uint16_t video_port = 0, video_port2 = 0, video_port3 = 0, video_rtcp_port = 0;
1835 if(dovideo &&
1836 (vport == NULL || vport->value == NULL ||
1837 janus_string_to_uint16(vport->value, &video_port) < 0 || video_port == 0 ||
1838 vcodec == NULL || vcodec->value == NULL ||
1839 vrtpmap == NULL || vrtpmap->value == NULL)) {
1840 JANUS_LOG(LOG_ERR, "Can't add 'rtp' mountpoint '%s', missing mandatory information for video...\n", cat->name);
1841 cl = cl->next;
1842 continue;
1843 }
1844 if(dovideo && vrtcpport != NULL && vrtcpport->value != NULL &&
1845 (janus_string_to_uint16(vrtcpport->value, &video_rtcp_port) < 0)) {
1846 JANUS_LOG(LOG_ERR, "Can't add 'rtp' mountpoint '%s', invalid video RTCP port...\n", cat->name);
1847 cl = cl->next;
1848 continue;
1849 }
1850 gboolean dovideortcp = (vrtcpport != NULL && vrtcpport->value != NULL);
1851 if(dovideo && vport2 != NULL && vport2->value != NULL &&
1852 (janus_string_to_uint16(vport2->value, &video_port2) < 0)) {
1853 JANUS_LOG(LOG_ERR, "Can't add 'rtp' mountpoint '%s', invalid simulcast port...\n", cat->name);
1854 cl = cl->next;
1855 continue;
1856 }
1857 if(dovideo && vport3 != NULL && vport3->value != NULL &&
1858 (janus_string_to_uint16(vport3->value, &video_port3) < 0)) {
1859 JANUS_LOG(LOG_ERR, "Can't add 'rtp' mountpoint '%s', invalid simulcast port...\n", cat->name);
1860 cl = cl->next;
1861 continue;
1862 }
1863 if(dovideo && viface) {
1864 if(!ifas) {
1865 JANUS_LOG(LOG_ERR, "Skipping 'rtp' mountpoint '%s', it relies on network configuration but network device information is unavailable...\n", cat->name);
1866 cl = cl->next;
1867 continue;
1868 }
1869 if(janus_network_lookup_interface(ifas, viface->value, &video_iface) != 0) {
1870 JANUS_LOG(LOG_ERR, "Can't add 'rtp' mountpoint '%s', invalid network interface configuration for video...\n", cat->name);
1871 cl = cl->next;
1872 continue;
1873 }
1874 }
1875 uint16_t data_port = 0;
1876 if(dodata && (dport == NULL || dport->value == NULL ||
1877 janus_string_to_uint16(dport->value, &data_port) < 0 || data_port == 0)) {
1878 JANUS_LOG(LOG_ERR, "Can't add 'rtp' mountpoint '%s', missing mandatory information for data...\n", cat->name);
1879 cl = cl->next;
1880 continue;
1881 }
1882 #ifndef HAVE_SCTP
1883 if(dodata) {
1884 JANUS_LOG(LOG_ERR, "Can't add 'rtp' mountpoint '%s': no datachannels support......\n", cat->name);
1885 cl = cl->next;
1886 continue;
1887 }
1888 #endif
1889 if(dodata && diface) {
1890 if(!ifas) {
1891 JANUS_LOG(LOG_ERR, "Skipping 'rtp' mountpoint '%s', it relies on network configuration but network device information is unavailable...\n", cat->name);
1892 cl = cl->next;
1893 continue;
1894 }
1895 if(janus_network_lookup_interface(ifas, diface->value, &data_iface) != 0) {
1896 JANUS_LOG(LOG_ERR, "Can't add 'rtp' mountpoint '%s', invalid network interface configuration for data...\n", cat->name);
1897 cl = cl->next;
1898 continue;
1899 }
1900 }
1901 if(ssuite && ssuite->value && atoi(ssuite->value) != 32 && atoi(ssuite->value) != 80) {
1902 JANUS_LOG(LOG_ERR, "Can't add 'rtp' mountpoint '%s', invalid SRTP suite...\n", cat->name);
1903 cl = cl->next;
1904 continue;
1905 }
1906 if(rtpcollision && rtpcollision->value && atoi(rtpcollision->value) < 0) {
1907 JANUS_LOG(LOG_ERR, "Can't add 'rtp' mountpoint '%s', invalid collision configuration...\n", cat->name);
1908 cl = cl->next;
1909 continue;
1910 }
1911 if(threads && threads->value && atoi(threads->value) < 0) {
1912 JANUS_LOG(LOG_ERR, "Can't add 'rtp' mountpoint '%s', invalid threads configuration...\n", cat->name);
1913 cl = cl->next;
1914 continue;
1915 }
1916 JANUS_LOG(LOG_VERB, "Audio %s, Video %s, Data %s\n",
1917 doaudio ? "enabled" : "NOT enabled",
1918 dovideo ? "enabled" : "NOT enabled",
1919 dodata ? "enabled" : "NOT enabled");
1920 janus_streaming_mountpoint *mp = NULL;
1921 if((mp = janus_streaming_create_rtp_source(
1922 mpid, (char *)(id ? id->value : NULL),
1923 (char *)cat->name,
1924 desc ? (char *)desc->value : NULL,
1925 md ? (char *)md->value : NULL,
1926 ssuite && ssuite->value ? atoi(ssuite->value) : 0,
1927 scrypto && scrypto->value ? (char *)scrypto->value : NULL,
1928 (threads && threads->value) ? atoi(threads->value) : 0,
1929 (e2ee && e2ee->value) ? janus_is_true(e2ee->value) : FALSE,
1930 doaudio, doaudiortcp,
1931 amcast ? (char *)amcast->value : NULL,
1932 doaudio && aiface && aiface->value ? &audio_iface : NULL,
1933 (aport && aport->value) ? audio_port : 0,
1934 (artcpport && artcpport->value) ? audio_rtcp_port : 0,
1935 (acodec && acodec->value) ? atoi(acodec->value) : 0,
1936 artpmap ? (char *)artpmap->value : NULL,
1937 afmtp ? (char *)afmtp->value : NULL,
1938 doaskew,
1939 dovideo, dovideortcp,
1940 vmcast ? (char *)vmcast->value : NULL,
1941 dovideo && viface && viface->value ? &video_iface : NULL,
1942 (vport && vport->value) ? video_port : 0,
1943 (vrtcpport && vrtcpport->value) ? video_rtcp_port : 0,
1944 (vcodec && vcodec->value) ? atoi(vcodec->value) : 0,
1945 vrtpmap ? (char *)vrtpmap->value : NULL,
1946 vfmtp ? (char *)vfmtp->value : NULL,
1947 bufferkf,
1948 simulcast,
1949 (vport2 && vport2->value) ? video_port2 : 0,
1950 (vport3 && vport3->value) ? video_port3 : 0,
1951 dosvc,
1952 dovskew,
1953 (rtpcollision && rtpcollision->value) ? atoi(rtpcollision->value) : 0,
1954 dodata,
1955 dodata && diface && diface->value ? &data_iface : NULL,
1956 (dport && dport->value) ? data_port : 0,
1957 textdata, buffermsg)) == NULL) {
1958 JANUS_LOG(LOG_ERR, "Error creating 'rtp' mountpoint '%s'...\n", cat->name);
1959 cl = cl->next;
1960 continue;
1961 }
1962 mp->is_private = is_private;
1963 if(secret && secret->value)
1964 mp->secret = g_strdup(secret->value);
1965 if(pin && pin->value)
1966 mp->pin = g_strdup(pin->value);
1967 } else if(!strcasecmp(type->value, "live")) {
1968 /* File-based live source */
1969 janus_config_item *desc = janus_config_get(config, cat, janus_config_type_item, "description");
1970 janus_config_item *md = janus_config_get(config, cat, janus_config_type_item, "metadata");
1971 janus_config_item *priv = janus_config_get(config, cat, janus_config_type_item, "is_private");
1972 janus_config_item *secret = janus_config_get(config, cat, janus_config_type_item, "secret");
1973 janus_config_item *pin = janus_config_get(config, cat, janus_config_type_item, "pin");
1974 janus_config_item *file = janus_config_get(config, cat, janus_config_type_item, "filename");
1975 janus_config_item *audio = janus_config_get(config, cat, janus_config_type_item, "audio");
1976 janus_config_item *acodec = janus_config_get(config, cat, janus_config_type_item, "audiopt");
1977 janus_config_item *artpmap = janus_config_get(config, cat, janus_config_type_item, "audiortpmap");
1978 janus_config_item *afmtp = janus_config_get(config, cat, janus_config_type_item, "audiofmtp");
1979 janus_config_item *video = janus_config_get(config, cat, janus_config_type_item, "video");
1980 if(file == NULL || file->value == NULL) {
1981 JANUS_LOG(LOG_ERR, "Can't add 'live' mountpoint '%s', missing mandatory information...\n", cat->name);
1982 cl = cl->next;
1983 continue;
1984 }
1985 gboolean is_private = priv && priv->value && janus_is_true(priv->value);
1986 gboolean doaudio = audio && audio->value && janus_is_true(audio->value);
1987 gboolean dovideo = video && video->value && janus_is_true(video->value);
1988 /* We only support audio for file-based streaming at the moment: for streaming
1989 * files using other codecs/formats an external tools should feed us RTP instead */
1990 if(!doaudio || dovideo) {
1991 JANUS_LOG(LOG_ERR, "Can't add 'live' mountpoint '%s', we only support audio file streaming right now...\n", cat->name);
1992 cl = cl->next;
1993 continue;
1994 }
1995 #ifdef HAVE_LIBOGG
1996 if(!strstr(file->value, ".opus") && !strstr(file->value, ".alaw") && !strstr(file->value, ".mulaw")) {
1997 JANUS_LOG(LOG_ERR, "Can't add 'live' mountpoint '%s', unsupported format (we only support Opus and raw mu-Law/a-Law files right now)\n", cat->name);
1998 #else
1999 if(!strstr(file->value, ".alaw") && !strstr(file->value, ".mulaw")) {
2000 JANUS_LOG(LOG_ERR, "Can't add 'live' mountpoint '%s', unsupported format (we only support raw mu-Law and a-Law files right now)\n", cat->name);
2001 #endif
2002 cl = cl->next;
2003 continue;
2004 }
2005 FILE *audiofile = fopen(file->value, "rb");
2006 if(!audiofile) {
2007 JANUS_LOG(LOG_ERR, "Can't add 'live' mountpoint, no such file '%s'...\n", file->value);
2008 cl = cl->next;
2009 continue;
2010 }
2011 fclose(audiofile);
2012
2013 janus_streaming_mountpoint *mp = NULL;
2014 if((mp = janus_streaming_create_file_source(
2015 mpid, (char *)(id ? id->value : NULL),
2016 (char *)cat->name,
2017 desc ? (char *)desc->value : NULL,
2018 md ? (char *)md->value : NULL,
2019 (char *)file->value, TRUE,
2020 doaudio,
2021 (acodec && acodec->value) ? atoi(acodec->value) : 0,
2022 artpmap ? (char *)artpmap->value : NULL,
2023 afmtp ? (char *)afmtp->value : NULL,
2024 dovideo)) == NULL) {
2025 JANUS_LOG(LOG_ERR, "Error creating 'live' mountpoint '%s'...\n", cat->name);
2026 cl = cl->next;
2027 continue;
2028 }
2029 mp->is_private = is_private;
2030 if(secret && secret->value)
2031 mp->secret = g_strdup(secret->value);
2032 if(pin && pin->value)
2033 mp->pin = g_strdup(pin->value);
2034 } else if(!strcasecmp(type->value, "ondemand")) {
2035 /* File-based on demand source */
2036 janus_config_item *desc = janus_config_get(config, cat, janus_config_type_item, "description");
2037 janus_config_item *md = janus_config_get(config, cat, janus_config_type_item, "metadata");
2038 janus_config_item *priv = janus_config_get(config, cat, janus_config_type_item, "is_private");
2039 janus_config_item *secret = janus_config_get(config, cat, janus_config_type_item, "secret");
2040 janus_config_item *pin = janus_config_get(config, cat, janus_config_type_item, "pin");
2041 janus_config_item *file = janus_config_get(config, cat, janus_config_type_item, "filename");
2042 janus_config_item *audio = janus_config_get(config, cat, janus_config_type_item, "audio");
2043 janus_config_item *acodec = janus_config_get(config, cat, janus_config_type_item, "audiopt");
2044 janus_config_item *artpmap = janus_config_get(config, cat, janus_config_type_item, "audiortpmap");
2045 janus_config_item *afmtp = janus_config_get(config, cat, janus_config_type_item, "audiofmtp");
2046 janus_config_item *video = janus_config_get(config, cat, janus_config_type_item, "video");
2047 if(file == NULL || file->value == NULL) {
2048 JANUS_LOG(LOG_ERR, "Can't add 'ondemand' mountpoint '%s', missing mandatory information...\n", cat->name);
2049 cl = cl->next;
2050 continue;
2051 }
2052 gboolean is_private = priv && priv->value && janus_is_true(priv->value);
2053 gboolean doaudio = audio && audio->value && janus_is_true(audio->value);
2054 gboolean dovideo = video && video->value && janus_is_true(video->value);
2055 /* We only support audio for file-based streaming at the moment: for streaming
2056 * files using other codecs/formats an external tools should feed us RTP instead */
2057 if(!doaudio || dovideo) {
2058 JANUS_LOG(LOG_ERR, "Can't add 'ondemand' mountpoint '%s', we only support audio file streaming right now...\n", cat->name);
2059 cl = cl->next;
2060 continue;
2061 }
2062 #ifdef HAVE_LIBOGG
2063 if(!strstr(file->value, ".opus") && !strstr(file->value, ".alaw") && !strstr(file->value, ".mulaw")) {
2064 JANUS_LOG(LOG_ERR, "Can't add 'live' mountpoint '%s', unsupported format (we only support Opus and raw mu-Law/a-Law files right now)\n", cat->name);
2065 #else
2066 if(!strstr(file->value, ".alaw") && !strstr(file->value, ".mulaw")) {
2067 JANUS_LOG(LOG_ERR, "Can't add 'ondemand' mountpoint '%s', unsupported format (we only support raw mu-Law and a-Law files right now)\n", cat->name);
2068 #endif
2069 cl = cl->next;
2070 continue;
2071 }
2072 FILE *audiofile = fopen(file->value, "rb");
2073 if(!audiofile) {
2074 JANUS_LOG(LOG_ERR, "Can't add 'ondemand' mountpoint, no such file '%s'...\n", file->value);
2075 cl = cl->next;
2076 continue;
2077 }
2078 fclose(audiofile);
2079
2080 janus_streaming_mountpoint *mp = NULL;
2081 if((mp = janus_streaming_create_file_source(
2082 mpid, (char *)(id ? id->value : NULL),
2083 (char *)cat->name,
2084 desc ? (char *)desc->value : NULL,
2085 md ? (char *)md->value : NULL,
2086 (char *)file->value, FALSE,
2087 doaudio,
2088 (acodec && acodec->value) ? atoi(acodec->value) : 0,
2089 artpmap ? (char *)artpmap->value : NULL,
2090 afmtp ? (char *)afmtp->value : NULL,
2091 dovideo)) == NULL) {
2092 JANUS_LOG(LOG_ERR, "Error creating 'ondemand' mountpoint '%s'...\n", cat->name);
2093 cl = cl->next;
2094 continue;
2095 }
2096 mp->is_private = is_private;
2097 if(secret && secret->value)
2098 mp->secret = g_strdup(secret->value);
2099 if(pin && pin->value)
2100 mp->pin = g_strdup(pin->value);
2101 } else if(!strcasecmp(type->value, "rtsp")) {
2102 #ifndef HAVE_LIBCURL
2103 JANUS_LOG(LOG_ERR, "Can't add 'rtsp' mountpoint '%s', libcurl support not compiled...\n", cat->name);
2104 cl = cl->next;
2105 continue;
2106 #else
2107 janus_config_item *desc = janus_config_get(config, cat, janus_config_type_item, "description");
2108 janus_config_item *md = janus_config_get(config, cat, janus_config_type_item, "metadata");
2109 janus_config_item *priv = janus_config_get(config, cat, janus_config_type_item, "is_private");
2110 janus_config_item *secret = janus_config_get(config, cat, janus_config_type_item, "secret");
2111 janus_config_item *pin = janus_config_get(config, cat, janus_config_type_item, "pin");
2112 janus_config_item *file = janus_config_get(config, cat, janus_config_type_item, "url");
2113 janus_config_item *username = janus_config_get(config, cat, janus_config_type_item, "rtsp_user");
2114 janus_config_item *password = janus_config_get(config, cat, janus_config_type_item, "rtsp_pwd");
2115 janus_config_item *audio = janus_config_get(config, cat, janus_config_type_item, "audio");
2116 janus_config_item *artpmap = janus_config_get(config, cat, janus_config_type_item, "audiortpmap");
2117 janus_config_item *acodec = janus_config_get(config, cat, janus_config_type_item, "audiopt");
2118 janus_config_item *afmtp = janus_config_get(config, cat, janus_config_type_item, "audiofmtp");
2119 janus_config_item *video = janus_config_get(config, cat, janus_config_type_item, "video");
2120 janus_config_item *vcodec = janus_config_get(config, cat, janus_config_type_item, "videopt");
2121 janus_config_item *vrtpmap = janus_config_get(config, cat, janus_config_type_item, "videortpmap");
2122 janus_config_item *vfmtp = janus_config_get(config, cat, janus_config_type_item, "videofmtp");
2123 janus_config_item *vkf = janus_config_get(config, cat, janus_config_type_item, "videobufferkf");
2124 janus_config_item *iface = janus_config_get(config, cat, janus_config_type_item, "rtspiface");
2125 janus_config_item *failerr = janus_config_get(config, cat, janus_config_type_item, "rtsp_failcheck");
2126 janus_config_item *threads = janus_config_get(config, cat, janus_config_type_item, "threads");
2127 janus_config_item *reconnect_delay = janus_config_get(config, cat, janus_config_type_item, "rtsp_reconnect_delay");
2128 janus_config_item *session_timeout = janus_config_get(config, cat, janus_config_type_item, "rtsp_session_timeout");
2129 janus_config_item *rtsp_timeout = janus_config_get(config, cat, janus_config_type_item, "rtsp_timeout");
2130 janus_config_item *rtsp_conn_timeout = janus_config_get(config, cat, janus_config_type_item, "rtsp_conn_timeout");
2131 janus_network_address iface_value;
2132 if(file == NULL || file->value == NULL) {
2133 JANUS_LOG(LOG_ERR, "Can't add 'rtsp' mountpoint '%s', missing mandatory information...\n", cat->name);
2134 cl = cl->next;
2135 continue;
2136 }
2137 gboolean is_private = priv && priv->value && janus_is_true(priv->value);
2138 gboolean doaudio = audio && audio->value && janus_is_true(audio->value);
2139 gboolean dovideo = video && video->value && janus_is_true(video->value);
2140 gboolean bufferkf = video && vkf && vkf->value && janus_is_true(vkf->value);
2141 gboolean error_on_failure = TRUE;
2142 if(failerr && failerr->value)
2143 error_on_failure = janus_is_true(failerr->value);
2144 if(threads && threads->value && atoi(threads->value) < 0) {
2145 JANUS_LOG(LOG_ERR, "Can't add 'rtsp' mountpoint '%s', invalid threads configuration...\n", cat->name);
2146 cl = cl->next;
2147 continue;
2148 }
2149
2150 if((doaudio || dovideo) && iface && iface->value) {
2151 if(!ifas) {
2152 JANUS_LOG(LOG_ERR, "Skipping 'rtsp' mountpoint '%s', it relies on network configuration but network device information is unavailable...\n", cat->name);
2153 cl = cl->next;
2154 continue;
2155 }
2156 if(janus_network_lookup_interface(ifas, iface->value, &iface_value) != 0) {
2157 JANUS_LOG(LOG_ERR, "Can't add 'rtsp' mountpoint '%s', invalid network interface configuration for stream...\n", cat->name);
2158 cl = cl->next;
2159 continue;
2160 }
2161 }
2162
2163 janus_streaming_mountpoint *mp = NULL;
2164 if((mp = janus_streaming_create_rtsp_source(
2165 mpid, (char *)(id ? id->value : NULL),
2166 (char *)cat->name,
2167 desc ? (char *)desc->value : NULL,
2168 md ? (char *)md->value : NULL,
2169 (char *)file->value,
2170 username ? (char *)username->value : NULL,
2171 password ? (char *)password->value : NULL,
2172 doaudio,
2173 (acodec && acodec->value) ? atoi(acodec->value) : -1,
2174 artpmap ? (char *)artpmap->value : NULL,
2175 afmtp ? (char *)afmtp->value : NULL,
2176 dovideo,
2177 (vcodec && vcodec->value) ? atoi(vcodec->value) : -1,
2178 vrtpmap ? (char *)vrtpmap->value : NULL,
2179 vfmtp ? (char *)vfmtp->value : NULL,
2180 bufferkf,
2181 iface && iface->value ? &iface_value : NULL,
2182 (threads && threads->value) ? atoi(threads->value) : 0,
2183 ((reconnect_delay && reconnect_delay->value) ? atoi(reconnect_delay->value) : JANUS_STREAMING_DEFAULT_RECONNECT_DELAY) * G_USEC_PER_SEC,
2184 ((session_timeout && session_timeout->value) ? atoi(session_timeout->value) : JANUS_STREAMING_DEFAULT_SESSION_TIMEOUT) * G_USEC_PER_SEC,
2185 ((rtsp_timeout && rtsp_timeout->value) ? atoi(rtsp_timeout->value) : JANUS_STREAMING_DEFAULT_CURL_TIMEOUT),
2186 ((rtsp_conn_timeout && rtsp_conn_timeout->value) ? atoi(rtsp_conn_timeout->value) : JANUS_STREAMING_DEFAULT_CURL_CONNECT_TIMEOUT),
2187 error_on_failure)) == NULL) {
2188 JANUS_LOG(LOG_ERR, "Error creating 'rtsp' mountpoint '%s'...\n", cat->name);
2189 cl = cl->next;
2190 continue;
2191 }
2192 mp->is_private = is_private;
2193 if(secret && secret->value)
2194 mp->secret = g_strdup(secret->value);
2195 if(pin && pin->value)
2196 mp->pin = g_strdup(pin->value);
2197 #endif
2198 } else {
2199 JANUS_LOG(LOG_WARN, "Ignoring unknown mountpoint type '%s' (%s)...\n", type->value, cat->name);
2200 }
2201 cl = cl->next;
2202 }
2203 g_list_free(clist);
2204 /* Done: we keep the configuration file open in case we get a "create" or "destroy" with permanent=true */
2205 }
2206 if(ifas) {
2207 freeifaddrs(ifas);
2208 }
2209
2210 /* Show available mountpoints */
2211 janus_mutex_lock(&mountpoints_mutex);
2212 GHashTableIter iter;
2213 gpointer value;
2214 g_hash_table_iter_init(&iter, mountpoints);
2215 while(g_hash_table_iter_next(&iter, NULL, &value)) {
2216 janus_streaming_mountpoint *mp = value;
2217 JANUS_LOG(LOG_VERB, " ::: [%s][%s] %s (%s, %s, %s, pin: %s)\n", mp->id_str, mp->name, mp->description,
2218 mp->streaming_type == janus_streaming_type_live ? "live" : "on demand",
2219 mp->streaming_source == janus_streaming_source_rtp ? "RTP source" : "file source",
2220 mp->is_private ? "private" : "public",
2221 mp->pin ? mp->pin : "no pin");
2222 }
2223 janus_mutex_unlock(&mountpoints_mutex);
2224
2225 sessions = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)janus_streaming_session_destroy);
2226 messages = g_async_queue_new_full((GDestroyNotify) janus_streaming_message_free);
2227 /* This is the callback we'll need to invoke to contact the Janus core */
2228 gateway = callback;
2229
2230 /* Launch the thread that will handle incoming messages */
2231 GError *error = NULL;
2232 handler_thread = g_thread_try_new("streaming handler", janus_streaming_handler, NULL, &error);
2233 if(error != NULL) {
2234 g_atomic_int_set(&initialized, 0);
2235 JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Streaming handler thread...\n",
2236 error->code, error->message ? error->message : "??");
2237 g_error_free(error);
2238 janus_config_destroy(config);
2239 return -1;
2240 }
2241 JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_STREAMING_NAME);
2242 return 0;
2243 }
2244
2245 void janus_streaming_destroy(void) {
2246 if(!g_atomic_int_get(&initialized))
2247 return;
2248 g_atomic_int_set(&stopping, 1);
2249
2250 g_async_queue_push(messages, &exit_message);
2251 if(handler_thread != NULL) {
2252 g_thread_join(handler_thread);
2253 handler_thread = NULL;
2254 }
2255
2256 /* Remove all mountpoints */
2257 janus_mutex_lock(&mountpoints_mutex);
2258 g_hash_table_destroy(mountpoints);
2259 mountpoints = NULL;
2260 g_hash_table_destroy(mountpoints_temp);
2261 mountpoints_temp = NULL;
2262 janus_mutex_unlock(&mountpoints_mutex);
2263 janus_mutex_lock(&sessions_mutex);
2264 g_hash_table_destroy(sessions);
2265 sessions = NULL;
2266 janus_mutex_unlock(&sessions_mutex);
2267 g_async_queue_unref(messages);
2268 messages = NULL;
2269
2270 janus_config_destroy(config);
2271 g_free(admin_key);
2272
2273 g_atomic_int_set(&initialized, 0);
2274 g_atomic_int_set(&stopping, 0);
2275 JANUS_LOG(LOG_INFO, "%s destroyed!\n", JANUS_STREAMING_NAME);
2276 }
2277
2278 int janus_streaming_get_api_compatibility(void) {
2279 /* Important! This is what your plugin MUST always return: don't lie here or bad things will happen */
2280 return JANUS_PLUGIN_API_VERSION;
2281 }
2282
2283 int janus_streaming_get_version(void) {
2284 return JANUS_STREAMING_VERSION;
2285 }
2286
2287 const char *janus_streaming_get_version_string(void) {
2288 return JANUS_STREAMING_VERSION_STRING;
2289 }
2290
2291 const char *janus_streaming_get_description(void) {
2292 return JANUS_STREAMING_DESCRIPTION;
2293 }
2294
2295 const char *janus_streaming_get_name(void) {
2296 return JANUS_STREAMING_NAME;
2297 }
2298
2299 const char *janus_streaming_get_author(void) {
2300 return JANUS_STREAMING_AUTHOR;
2301 }
2302
2303 const char *janus_streaming_get_package(void) {
2304 return JANUS_STREAMING_PACKAGE;
2305 }
2306
2307 static janus_streaming_session *janus_streaming_lookup_session(janus_plugin_session *handle) {
2308 janus_streaming_session *session = NULL;
2309 if(g_hash_table_contains(sessions, handle)) {
2310 session = (janus_streaming_session *)handle->plugin_handle;
2311 }
2312 return session;
2313 }
2314
2315 void janus_streaming_create_session(janus_plugin_session *handle, int *error) {
2316 if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
2317 *error = -1;
2318 return;
2319 }
2320 janus_streaming_session *session = g_malloc0(sizeof(janus_streaming_session));
2321 session->handle = handle;
2322 session->mountpoint = NULL; /* This will happen later */
2323 janus_mutex_init(&session->mutex);
2324 g_atomic_int_set(&session->started, 0);
2325 g_atomic_int_set(&session->paused, 0);
2326 g_atomic_int_set(&session->destroyed, 0);
2327 g_atomic_int_set(&session->hangingup, 0);
2328 session->audio_pt = -1;
2329 session->video_pt = -1;
2330 handle->plugin_handle = session;
2331 janus_refcount_init(&session->ref, janus_streaming_session_free);
2332 janus_mutex_lock(&sessions_mutex);
2333 g_hash_table_insert(sessions, handle, session);
2334 janus_mutex_unlock(&sessions_mutex);
2335
2336 return;
2337 }
2338
2339 void janus_streaming_destroy_session(janus_plugin_session *handle, int *error) {
2340 if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
2341 *error = -1;
2342 return;
2343 }
2344 janus_mutex_lock(&sessions_mutex);
2345 janus_streaming_session *session = janus_streaming_lookup_session(handle);
2346 if(!session) {
2347 janus_mutex_unlock(&sessions_mutex);
2348 JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
2349 *error = -2;
2350 return;
2351 }
2352 JANUS_LOG(LOG_VERB, "Removing streaming session...\n");
2353 janus_streaming_hangup_media_internal(handle);
2354 g_hash_table_remove(sessions, handle);
2355 janus_mutex_unlock(&sessions_mutex);
2356 return;
2357 }
2358
2359 json_t *janus_streaming_query_session(janus_plugin_session *handle) {
2360 if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized)) {
2361 return NULL;
2362 }
2363 janus_mutex_lock(&sessions_mutex);
2364 janus_streaming_session *session = janus_streaming_lookup_session(handle);
2365 if(!session) {
2366 janus_mutex_unlock(&sessions_mutex);
2367 JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
2368 return NULL;
2369 }
2370 janus_refcount_increase(&session->ref);
2371 janus_mutex_unlock(&sessions_mutex);
2372 /* What is this user watching, if anything? */
2373 json_t *info = json_object();
2374 janus_streaming_mountpoint *mp = session->mountpoint;
2375 json_object_set_new(info, "state", json_string(mp ? "watching" : "idle"));
2376 if(mp) {
2377 janus_refcount_increase(&mp->ref);
2378 json_object_set_new(info, "mountpoint_id", string_ids ? json_string(mp->id_str) : json_integer(mp->id));
2379 json_object_set_new(info, "mountpoint_name", mp->name ? json_string(mp->name) : NULL);
2380 janus_mutex_lock(&mp->mutex);
2381 json_object_set_new(info, "mountpoint_viewers", json_integer(mp->viewers ? g_list_length(mp->viewers) : 0));
2382 janus_mutex_unlock(&mp->mutex);
2383 json_t *media = json_object();
2384 json_object_set_new(media, "audio", session->audio ? json_true() : json_false());
2385 json_object_set_new(media, "video", session->video ? json_true() : json_false());
2386 json_object_set_new(media, "data", session->data ? json_true() : json_false());
2387 json_object_set_new(info, "media", media);
2388 if(mp->streaming_source == janus_streaming_source_rtp) {
2389 janus_streaming_rtp_source *source = mp->source;
2390 if(source->simulcast) {
2391 json_t *simulcast = json_object();
2392 json_object_set_new(simulcast, "substream", json_integer(session->sim_context.substream));
2393 json_object_set_new(simulcast, "substream-target", json_integer(session->sim_context.substream_target));
2394 json_object_set_new(simulcast, "temporal-layer", json_integer(session->sim_context.templayer));
2395 json_object_set_new(simulcast, "temporal-layer-target", json_integer(session->sim_context.templayer_target));
2396 if(session->sim_context.drop_trigger > 0)
2397 json_object_set_new(simulcast, "fallback", json_integer(session->sim_context.drop_trigger));
2398 json_object_set_new(info, "simulcast", simulcast);
2399 }
2400 if(source->svc) {
2401 json_t *svc = json_object();
2402 json_object_set_new(svc, "spatial-layer", json_integer(session->spatial_layer));
2403 json_object_set_new(svc, "target-spatial-layer", json_integer(session->target_spatial_layer));
2404 json_object_set_new(svc, "temporal-layer", json_integer(session->temporal_layer));
2405 json_object_set_new(svc, "target-temporal-layer", json_integer(session->target_temporal_layer));
2406 json_object_set_new(info, "svc", svc);
2407 }
2408 }
2409 janus_refcount_decrease(&mp->ref);
2410 }
2411 if(session->e2ee)
2412 json_object_set_new(info, "e2ee", json_true());
2413 json_object_set_new(info, "hangingup", json_integer(g_atomic_int_get(&session->hangingup)));
2414 json_object_set_new(info, "started", json_integer(g_atomic_int_get(&session->started)));
2415 json_object_set_new(info, "dataready", json_integer(g_atomic_int_get(&session->dataready)));
2416 json_object_set_new(info, "paused", json_integer(g_atomic_int_get(&session->paused)));
2417 json_object_set_new(info, "stopping", json_integer(g_atomic_int_get(&session->stopping)));
2418 json_object_set_new(info, "destroyed", json_integer(g_atomic_int_get(&session->destroyed)));
2419 janus_refcount_decrease(&session->ref);
2420 return info;
2421 }
2422
2423 /* Helper method to process synchronous requests */
2424 static json_t *janus_streaming_process_synchronous_request(janus_streaming_session *session, json_t *message) {
2425 json_t *request = json_object_get(message, "request");
2426 const char *request_text = json_string_value(request);
2427
2428 /* Parse the message */
2429 int error_code = 0;
2430 char error_cause[512];
2431 json_t *root = message;
2432 json_t *response = NULL;
2433 struct ifaddrs *ifas = NULL;
2434
2435 if(!strcasecmp(request_text, "list")) {
2436 JANUS_LOG(LOG_VERB, "Request for the list of mountpoints\n");
2437 gboolean lock_mp_list = TRUE;
2438 if(admin_key != NULL) {
2439 json_t *admin_key_json = json_object_get(root, "admin_key");
2440 /* Verify admin_key if it was provided */
2441 if(admin_key_json != NULL && json_is_string(admin_key_json) && strlen(json_string_value(admin_key_json)) > 0) {
2442 JANUS_CHECK_SECRET(admin_key, root, "admin_key", error_code, error_cause,
2443 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT, JANUS_STREAMING_ERROR_UNAUTHORIZED);
2444 if(error_code != 0) {
2445 goto prepare_response;
2446 } else {
2447 lock_mp_list = FALSE;
2448 }
2449 }
2450 }
2451 json_t *list = json_array();
2452 /* Return a list of all available mountpoints */
2453 janus_mutex_lock(&mountpoints_mutex);
2454 GHashTableIter iter;
2455 gpointer value;
2456 g_hash_table_iter_init(&iter, mountpoints);
2457 while(g_hash_table_iter_next(&iter, NULL, &value)) {
2458 janus_streaming_mountpoint *mp = value;
2459 if(mp->is_private && lock_mp_list) {
2460 /* Skip private stream if no valid admin_key was provided */
2461 JANUS_LOG(LOG_VERB, "Skipping private mountpoint '%s'\n", mp->description);
2462 continue;
2463 }
2464 janus_refcount_increase(&mp->ref);
2465 json_t *ml = json_object();
2466 json_object_set_new(ml, "id", string_ids ? json_string(mp->id_str) : json_integer(mp->id));
2467 json_object_set_new(ml, "type", json_string(mp->streaming_type == janus_streaming_type_live ? "live" : "on demand"));
2468 json_object_set_new(ml, "description", json_string(mp->description));
2469 if(mp->metadata) {
2470 json_object_set_new(ml, "metadata", json_string(mp->metadata));
2471 }
2472 json_object_set_new(ml, "enabled", mp->enabled ? json_true() : json_false());
2473 if(mp->streaming_source == janus_streaming_source_rtp) {
2474 janus_streaming_rtp_source *source = mp->source;
2475 gint64 now = janus_get_monotonic_time();
2476 if(source->audio_fd != -1)
2477 json_object_set_new(ml, "audio_age_ms", json_integer((now - source->last_received_audio) / 1000));
2478 if(source->video_fd[0] != -1 || source->video_fd[1] != -1 || source->video_fd[2] != -1)
2479 json_object_set_new(ml, "video_age_ms", json_integer((now - source->last_received_video) / 1000));
2480 }
2481 json_array_append_new(list, ml);
2482 janus_refcount_decrease(&mp->ref);
2483 }
2484 janus_mutex_unlock(&mountpoints_mutex);
2485 /* Send info back */
2486 response = json_object();
2487 json_object_set_new(response, "streaming", json_string("list"));
2488 json_object_set_new(response, "list", list);
2489 goto prepare_response;
2490 } else if(!strcasecmp(request_text, "info")) {
2491 JANUS_LOG(LOG_VERB, "Request info on a specific mountpoint\n");
2492 /* Return info on a specific mountpoint */
2493 if(!string_ids) {
2494 JANUS_VALIDATE_JSON_OBJECT(root, id_parameters,
2495 error_code, error_cause, TRUE,
2496 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
2497 } else {
2498 JANUS_VALIDATE_JSON_OBJECT(root, idstr_parameters,
2499 error_code, error_cause, TRUE,
2500 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
2501 }
2502 if(error_code != 0)
2503 goto prepare_response;
2504 json_t *id = json_object_get(root, "id");
2505 guint64 id_value = 0;
2506 char id_num[30], *id_value_str = NULL;
2507 if(!string_ids) {
2508 id_value = json_integer_value(id);
2509 g_snprintf(id_num, sizeof(id_num), "%"SCNu64, id_value);
2510 id_value_str = id_num;
2511 } else {
2512 id_value_str = (char *)json_string_value(id);
2513 }
2514 janus_mutex_lock(&mountpoints_mutex);
2515 janus_streaming_mountpoint *mp = g_hash_table_lookup(mountpoints,
2516 string_ids ? (gpointer)id_value_str : (gpointer)&id_value);
2517 if(mp == NULL) {
2518 janus_mutex_unlock(&mountpoints_mutex);
2519 JANUS_LOG(LOG_VERB, "No such mountpoint/stream %s\n", id_value_str);
2520 error_code = JANUS_STREAMING_ERROR_NO_SUCH_MOUNTPOINT;
2521 g_snprintf(error_cause, 512, "No such mountpoint/stream %s", id_value_str);
2522 goto prepare_response;
2523 }
2524 janus_refcount_increase(&mp->ref);
2525 /* Return more info if the right secret is provided */
2526 gboolean admin = FALSE;
2527 if(mp->secret) {
2528 json_t *secret = json_object_get(root, "secret");
2529 if(secret && json_string_value(secret) && janus_strcmp_const_time(mp->secret, json_string_value(secret)))
2530 admin = TRUE;
2531 } else {
2532 admin = TRUE;
2533 }
2534 json_t *ml = json_object();
2535 json_object_set_new(ml, "id", string_ids ? json_string(mp->id_str) : json_integer(mp->id));
2536 if(admin && mp->name)
2537 json_object_set_new(ml, "name", json_string(mp->name));
2538 if(mp->description)
2539 json_object_set_new(ml, "description", json_string(mp->description));
2540 if(mp->metadata)
2541 json_object_set_new(ml, "metadata", json_string(mp->metadata));
2542 if(admin && mp->secret)
2543 json_object_set_new(ml, "secret", json_string(mp->secret));
2544 if(admin && mp->pin)
2545 json_object_set_new(ml, "pin", json_string(mp->pin));
2546 if(admin && mp->is_private)
2547 json_object_set_new(ml, "is_private", json_true());
2548 json_object_set_new(ml, "enabled", mp->enabled ? json_true() : json_false());
2549 if(admin)
2550 json_object_set_new(ml, "viewers", json_integer(mp->viewers ? g_list_length(mp->viewers) : 0));
2551 if(mp->audio) {
2552 json_object_set_new(ml, "audio", json_true());
2553 if(mp->codecs.audio_pt != -1)
2554 json_object_set_new(ml, "audiopt", json_integer(mp->codecs.audio_pt));
2555 if(mp->codecs.audio_rtpmap)
2556 json_object_set_new(ml, "audiortpmap", json_string(mp->codecs.audio_rtpmap));
2557 if(mp->codecs.audio_fmtp)
2558 json_object_set_new(ml, "audiofmtp", json_string(mp->codecs.audio_fmtp));
2559 }
2560 if(mp->video) {
2561 json_object_set_new(ml, "video", json_true());
2562 if(mp->codecs.video_pt != -1)
2563 json_object_set_new(ml, "videopt", json_integer(mp->codecs.video_pt));
2564 if(mp->codecs.video_rtpmap)
2565 json_object_set_new(ml, "videortpmap", json_string(mp->codecs.video_rtpmap));
2566 if(mp->codecs.video_fmtp)
2567 json_object_set_new(ml, "videofmtp", json_string(mp->codecs.video_fmtp));
2568 }
2569 if(mp->data) {
2570 json_object_set_new(ml, "data", json_true());
2571 }
2572 json_object_set_new(ml, "type", json_string(mp->streaming_type == janus_streaming_type_live ? "live" : "on demand"));
2573 if(mp->streaming_source == janus_streaming_source_file) {
2574 janus_streaming_file_source *source = mp->source;
2575 if(admin && source->filename)
2576 json_object_set_new(ml, "filename", json_string(source->filename));
2577 } else if(mp->streaming_source == janus_streaming_source_rtp) {
2578 janus_streaming_rtp_source *source = mp->source;
2579 if(source->is_srtp) {
2580 json_object_set_new(ml, "srtp", json_true());
2581 }
2582 gint64 now = janus_get_monotonic_time();
2583 #ifdef HAVE_LIBCURL
2584 if(source->rtsp) {
2585 json_object_set_new(ml, "rtsp", json_true());
2586 if(admin) {
2587 if(source->rtsp_url)
2588 json_object_set_new(ml, "url", json_string(source->rtsp_url));
2589 if(source->rtsp_username)
2590 json_object_set_new(ml, "rtsp_user", json_string(source->rtsp_username));
2591 if(source->rtsp_password)
2592 json_object_set_new(ml, "rtsp_pwd", json_string(source->rtsp_password));
2593 }
2594 }
2595 #endif
2596 if(source->keyframe.enabled) {
2597 json_object_set_new(ml, "videobufferkf", json_true());
2598 }
2599 if(source->simulcast) {
2600 json_object_set_new(ml, "videosimulcast", json_true());
2601 }
2602 if(source->svc) {
2603 json_object_set_new(ml, "videosvc", json_true());
2604 }
2605 if(source->askew)
2606 json_object_set_new(ml, "audioskew", json_true());
2607 if(source->vskew)
2608 json_object_set_new(ml, "videoskew", json_true());
2609 if(source->rtp_collision > 0)
2610 json_object_set_new(ml, "collision", json_integer(source->rtp_collision));
2611 if(mp->helper_threads > 0)
2612 json_object_set_new(ml, "threads", json_integer(mp->helper_threads));
2613 if(admin) {
2614 if(mp->audio) {
2615 if(source->audio_host)
2616 json_object_set_new(ml, "audiohost", json_string(source->audio_host));
2617 json_object_set_new(ml, "audioport", json_integer(source->audio_port));
2618 if(source->audio_rtcp_port > -1)
2619 json_object_set_new(ml, "audiortcpport", json_integer(source->audio_rtcp_port));
2620 }
2621 if(mp->video) {
2622 if(source->video_host)
2623 json_object_set_new(ml, "videohost", json_string(source->video_host));
2624 json_object_set_new(ml, "videoport", json_integer(source->video_port[0]));
2625 if(source->video_rtcp_port > -1)
2626 json_object_set_new(ml, "videortcpport", json_integer(source->video_rtcp_port));
2627 if(source->video_port[1] > -1)
2628 json_object_set_new(ml, "videoport2", json_integer(source->video_port[1]));
2629 if(source->video_port[2] > -1)
2630 json_object_set_new(ml, "videoport3", json_integer(source->video_port[2]));
2631 }
2632 if(mp->data) {
2633 if(source->data_host)
2634 json_object_set_new(ml, "datahost", json_string(source->data_host));
2635 json_object_set_new(ml, "dataport", json_integer(source->data_port));
2636 }
2637 }
2638 if(source->audio_fd != -1)
2639 json_object_set_new(ml, "audio_age_ms", json_integer((now - source->last_received_audio) / 1000));
2640 if(source->video_fd[0] != -1 || source->video_fd[1] != -1 || source->video_fd[2] != -1)
2641 json_object_set_new(ml, "video_age_ms", json_integer((now - source->last_received_video) / 1000));
2642 if(source->data_fd != -1)
2643 json_object_set_new(ml, "data_age_ms", json_integer((now - source->last_received_data) / 1000));
2644 janus_mutex_lock(&source->rec_mutex);
2645 if(admin && (source->arc || source->vrc || source->drc)) {
2646 json_t *recording = json_object();
2647 if(source->arc && source->arc->filename)
2648 json_object_set_new(recording, "audio", json_string(source->arc->filename));
2649 if(source->vrc && source->vrc->filename)
2650 json_object_set_new(recording, "video", json_string(source->vrc->filename));
2651 if(source->drc && source->drc->filename)
2652 json_object_set_new(recording, "data", json_string(source->drc->filename));
2653 json_object_set_new(ml, "recording", recording);
2654 }
2655 janus_mutex_unlock(&source->rec_mutex);
2656 }
2657 janus_refcount_decrease(&mp->ref);
2658 janus_mutex_unlock(&mountpoints_mutex);
2659 /* Send info back */
2660 response = json_object();
2661 json_object_set_new(response, "streaming", json_string("info"));
2662 json_object_set_new(response, "info", ml);
2663 goto prepare_response;
2664 } else if(!strcasecmp(request_text, "create")) {
2665 /* Create a new stream */
2666 JANUS_VALIDATE_JSON_OBJECT(root, create_parameters,
2667 error_code, error_cause, TRUE,
2668 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
2669 if(error_code != 0)
2670 goto prepare_response;
2671 if(!string_ids) {
2672 JANUS_VALIDATE_JSON_OBJECT(root, idopt_parameters,
2673 error_code, error_cause, TRUE,
2674 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
2675 } else {
2676 JANUS_VALIDATE_JSON_OBJECT(root, idstropt_parameters,
2677 error_code, error_cause, TRUE,
2678 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
2679 }
2680 if(error_code != 0)
2681 goto prepare_response;
2682 if(admin_key != NULL) {
2683 /* An admin key was specified: make sure it was provided, and that it's valid */
2684 JANUS_VALIDATE_JSON_OBJECT(root, adminkey_parameters,
2685 error_code, error_cause, TRUE,
2686 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
2687 if(error_code != 0)
2688 goto prepare_response;
2689 JANUS_CHECK_SECRET(admin_key, root, "admin_key", error_code, error_cause,
2690 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT, JANUS_STREAMING_ERROR_UNAUTHORIZED);
2691 if(error_code != 0)
2692 goto prepare_response;
2693 }
2694
2695 if(getifaddrs(&ifas) == -1) {
2696 JANUS_LOG(LOG_ERR, "Unable to acquire list of network devices/interfaces; some configurations may not work as expected... %d (%s)\n",
2697 errno, g_strerror(errno));
2698 }
2699
2700 json_t *type = json_object_get(root, "type");
2701 const char *type_text = json_string_value(type);
2702 json_t *secret = json_object_get(root, "secret");
2703 json_t *pin = json_object_get(root, "pin");
2704 json_t *permanent = json_object_get(root, "permanent");
2705 gboolean save = permanent ? json_is_true(permanent) : FALSE;
2706 if(save && config == NULL) {
2707 JANUS_LOG(LOG_ERR, "No configuration file, can't create permanent mountpoint\n");
2708 error_code = JANUS_STREAMING_ERROR_UNKNOWN_ERROR;
2709 g_snprintf(error_cause, 512, "No configuration file, can't create permanent mountpoint");
2710 goto prepare_response;
2711 }
2712 json_t *id = json_object_get(root, "id");
2713 /* Check if an ID has been provided, or if we need to generate one ourselves */
2714 janus_mutex_lock(&mountpoints_mutex);
2715 guint64 mpid = string_ids ? 0 : json_integer_value(id);
2716 char *mpid_str = (char *)(string_ids ? json_string_value(id) : NULL);
2717 if((!string_ids && mpid > 0) || (string_ids && mpid_str != NULL)) {
2718 /* Make sure the provided ID isn't already in use */
2719 if(g_hash_table_lookup(mountpoints, string_ids ? (gpointer)mpid_str : (gpointer)&mpid) != NULL ||
2720 g_hash_table_lookup(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid) != NULL) {
2721 janus_mutex_unlock(&mountpoints_mutex);
2722 JANUS_LOG(LOG_ERR, "A stream with the provided ID already exists\n");
2723 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
2724 g_snprintf(error_cause, 512, "A stream with the provided ID already exists");
2725 goto prepare_response;
2726 }
2727 } else if(!string_ids && mpid == 0) {
2728 /* Generate a unique numeric ID */
2729 JANUS_LOG(LOG_VERB, "Missing numeric id, will generate a random one...\n");
2730 while(mpid == 0) {
2731 mpid = janus_random_uint64();
2732 if(g_hash_table_lookup(mountpoints, &mpid) != NULL ||
2733 g_hash_table_lookup(mountpoints_temp, &mpid) != NULL) {
2734 /* ID already in use, try another one */
2735 mpid = 0;
2736 }
2737 }
2738 } else if(string_ids && mpid_str == NULL) {
2739 /* Generate a unique alphanumeric ID */
2740 JANUS_LOG(LOG_VERB, "Missing alphanumeric id, will generate a random one...\n");
2741 while(mpid_str == 0) {
2742 mpid_str = janus_random_uuid();
2743 if(g_hash_table_lookup(mountpoints, mpid_str) != NULL ||
2744 g_hash_table_lookup(mountpoints_temp, mpid_str) != NULL) {
2745 /* ID already in use, try another one */
2746 g_free(mpid_str);
2747 mpid_str = NULL;
2748 }
2749 }
2750 }
2751 g_hash_table_insert(mountpoints_temp,
2752 string_ids ? (gpointer)g_strdup(mpid_str) : (gpointer)janus_uint64_dup(mpid),
2753 GUINT_TO_POINTER(TRUE));
2754 janus_mutex_unlock(&mountpoints_mutex);
2755 janus_streaming_mountpoint *mp = NULL;
2756 if(!strcasecmp(type_text, "rtp")) {
2757 janus_network_address audio_iface, video_iface, data_iface;
2758 /* RTP live source (e.g., from gstreamer/ffmpeg/vlc/etc.) */
2759 JANUS_VALIDATE_JSON_OBJECT(root, rtp_parameters,
2760 error_code, error_cause, TRUE,
2761 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
2762 if(error_code != 0) {
2763 janus_mutex_lock(&mountpoints_mutex);
2764 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
2765 janus_mutex_unlock(&mountpoints_mutex);
2766 goto prepare_response;
2767 }
2768 json_t *name = json_object_get(root, "name");
2769 json_t *desc = json_object_get(root, "description");
2770 json_t *md = json_object_get(root, "metadata");
2771 json_t *is_private = json_object_get(root, "is_private");
2772 json_t *audio = json_object_get(root, "audio");
2773 json_t *video = json_object_get(root, "video");
2774 json_t *data = json_object_get(root, "data");
2775 json_t *rtpcollision = json_object_get(root, "collision");
2776 json_t *threads = json_object_get(root, "threads");
2777 json_t *ssuite = json_object_get(root, "srtpsuite");
2778 json_t *scrypto = json_object_get(root, "srtpcrypto");
2779 json_t *e2ee = json_object_get(root, "e2ee");
2780 gboolean doaudio = audio ? json_is_true(audio) : FALSE, doaudiortcp = FALSE;
2781 gboolean dovideo = video ? json_is_true(video) : FALSE, dovideortcp = FALSE;
2782 gboolean dodata = data ? json_is_true(data) : FALSE;
2783 gboolean doaskew = FALSE, dovskew = FALSE, dosvc = FALSE;
2784 if(!doaudio && !dovideo && !dodata) {
2785 JANUS_LOG(LOG_ERR, "Can't add 'rtp' stream, no audio, video or data have to be streamed...\n");
2786 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
2787 g_snprintf(error_cause, 512, "Can't add 'rtp' stream, no audio or video have to be streamed...");
2788 janus_mutex_lock(&mountpoints_mutex);
2789 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
2790 janus_mutex_unlock(&mountpoints_mutex);
2791 goto prepare_response;
2792 }
2793 if(ssuite && json_integer_value(ssuite) != 32 && json_integer_value(ssuite) != 80) {
2794 JANUS_LOG(LOG_ERR, "Can't add 'rtp' stream, invalid SRTP suite...\n");
2795 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
2796 g_snprintf(error_cause, 512, "Can't add 'rtp' stream, invalid SRTP suite...");
2797 janus_mutex_lock(&mountpoints_mutex);
2798 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
2799 janus_mutex_unlock(&mountpoints_mutex);
2800 goto prepare_response;
2801 }
2802 uint16_t aport = 0;
2803 uint16_t artcpport = 0;
2804 uint8_t acodec = 0;
2805 char *artpmap = NULL, *afmtp = NULL, *amcast = NULL;
2806 if(doaudio) {
2807 JANUS_VALIDATE_JSON_OBJECT(root, rtp_audio_parameters,
2808 error_code, error_cause, TRUE,
2809 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
2810 if(error_code != 0) {
2811 janus_mutex_lock(&mountpoints_mutex);
2812 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
2813 janus_mutex_unlock(&mountpoints_mutex);
2814 goto prepare_response;
2815 }
2816 json_t *audiomcast = json_object_get(root, "audiomcast");
2817 amcast = (char *)json_string_value(audiomcast);
2818 json_t *audioport = json_object_get(root, "audioport");
2819 aport = json_integer_value(audioport);
2820 json_t *audiortcpport = json_object_get(root, "audiortcpport");
2821 if(audiortcpport) {
2822 doaudiortcp = TRUE;
2823 artcpport = json_integer_value(audiortcpport);
2824 }
2825 json_t *audiopt = json_object_get(root, "audiopt");
2826 acodec = json_integer_value(audiopt);
2827 json_t *audiortpmap = json_object_get(root, "audiortpmap");
2828 artpmap = (char *)json_string_value(audiortpmap);
2829 json_t *audiofmtp = json_object_get(root, "audiofmtp");
2830 afmtp = (char *)json_string_value(audiofmtp);
2831 json_t *aiface = json_object_get(root, "audioiface");
2832 if(aiface) {
2833 const char *miface = (const char *)json_string_value(aiface);
2834 if(janus_network_lookup_interface(ifas, miface, &audio_iface) != 0) {
2835 JANUS_LOG(LOG_ERR, "Can't add 'rtp' stream '%s', invalid network interface configuration for audio...\n", (const char *)json_string_value(name));
2836 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
2837 g_snprintf(error_cause, 512, ifas ? "Invalid network interface configuration for audio" : "Unable to query network device information");
2838 janus_mutex_lock(&mountpoints_mutex);
2839 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
2840 janus_mutex_unlock(&mountpoints_mutex);
2841 goto prepare_response;
2842 }
2843 } else {
2844 janus_network_address_nullify(&audio_iface);
2845 }
2846 json_t *askew = json_object_get(root, "audioskew");
2847 doaskew = askew ? json_is_true(askew) : FALSE;
2848 }
2849 uint16_t vport = 0, vport2 = 0, vport3 = 0;
2850 uint16_t vrtcpport = 0;
2851 uint8_t vcodec = 0;
2852 char *vrtpmap = NULL, *vfmtp = NULL, *vmcast = NULL;
2853 gboolean bufferkf = FALSE, simulcast = FALSE;
2854 if(dovideo) {
2855 JANUS_VALIDATE_JSON_OBJECT(root, rtp_video_parameters,
2856 error_code, error_cause, TRUE,
2857 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
2858 if(error_code != 0)
2859 goto prepare_response;
2860 json_t *videomcast = json_object_get(root, "videomcast");
2861 vmcast = (char *)json_string_value(videomcast);
2862 json_t *videoport = json_object_get(root, "videoport");
2863 vport = json_integer_value(videoport);
2864 json_t *videortcpport = json_object_get(root, "videortcpport");
2865 if(videortcpport) {
2866 dovideortcp = TRUE;
2867 vrtcpport = json_integer_value(videortcpport);
2868 }
2869 json_t *videopt = json_object_get(root, "videopt");
2870 vcodec = json_integer_value(videopt);
2871 json_t *videortpmap = json_object_get(root, "videortpmap");
2872 vrtpmap = (char *)json_string_value(videortpmap);
2873 json_t *videofmtp = json_object_get(root, "videofmtp");
2874 vfmtp = (char *)json_string_value(videofmtp);
2875 json_t *vkf = json_object_get(root, "videobufferkf");
2876 bufferkf = vkf ? json_is_true(vkf) : FALSE;
2877 json_t *vsc = json_object_get(root, "videosimulcast");
2878 simulcast = vsc ? json_is_true(vsc) : FALSE;
2879 if(simulcast && bufferkf) {
2880 /* FIXME We'll need to take care of this */
2881 JANUS_LOG(LOG_WARN, "Simulcasting enabled, so disabling buffering of keyframes\n");
2882 bufferkf = FALSE;
2883 }
2884 json_t *videoport2 = json_object_get(root, "videoport2");
2885 vport2 = json_integer_value(videoport2);
2886 json_t *videoport3 = json_object_get(root, "videoport3");
2887 vport3 = json_integer_value(videoport3);
2888 json_t *viface = json_object_get(root, "videoiface");
2889 if(viface) {
2890 const char *miface = (const char *)json_string_value(viface);
2891 if(janus_network_lookup_interface(ifas, miface, &video_iface) != 0) {
2892 JANUS_LOG(LOG_ERR, "Can't add 'rtp' stream '%s', invalid network interface configuration for video...\n", (const char *)json_string_value(name));
2893 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
2894 g_snprintf(error_cause, 512, ifas ? "Invalid network interface configuration for video" : "Unable to query network device information");
2895 janus_mutex_lock(&mountpoints_mutex);
2896 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
2897 janus_mutex_unlock(&mountpoints_mutex);
2898 goto prepare_response;
2899 }
2900 } else {
2901 janus_network_address_nullify(&video_iface);
2902 }
2903 json_t *vskew = json_object_get(root, "videoskew");
2904 dovskew = vskew ? json_is_true(vskew) : FALSE;
2905 json_t *vsvc = json_object_get(root, "videosvc");
2906 dosvc = vsvc ? json_is_true(vsvc) : FALSE;
2907 }
2908 uint16_t dport = 0;
2909 gboolean textdata = TRUE, buffermsg = FALSE;
2910 if(dodata) {
2911 JANUS_VALIDATE_JSON_OBJECT(root, rtp_data_parameters,
2912 error_code, error_cause, TRUE,
2913 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
2914 if(error_code != 0) {
2915 janus_mutex_lock(&mountpoints_mutex);
2916 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
2917 janus_mutex_unlock(&mountpoints_mutex);
2918 goto prepare_response;
2919 }
2920 #ifdef HAVE_SCTP
2921 json_t *dataport = json_object_get(root, "dataport");
2922 dport = json_integer_value(dataport);
2923 json_t *dbm = json_object_get(root, "databuffermsg");
2924 buffermsg = dbm ? json_is_true(dbm) : FALSE;
2925 json_t *dt = json_object_get(root, "datatype");
2926 if(dt) {
2927 const char *datatype = (const char *)json_string_value(dt);
2928 if(!strcasecmp(datatype, "text"))
2929 textdata = TRUE;
2930 else if(!strcasecmp(datatype, "binary"))
2931 textdata = FALSE;
2932 else {
2933 JANUS_LOG(LOG_ERR, "Invalid element (datatype can only be text or binary)\n");
2934 error_code = JANUS_STREAMING_ERROR_INVALID_ELEMENT;
2935 g_snprintf(error_cause, 512, "Invalid element (datatype can only be text or binary)");
2936 janus_mutex_lock(&mountpoints_mutex);
2937 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
2938 janus_mutex_unlock(&mountpoints_mutex);
2939 goto prepare_response;
2940 }
2941 }
2942 json_t *diface = json_object_get(root, "dataiface");
2943 if(diface) {
2944 const char *miface = (const char *)json_string_value(diface);
2945 if(janus_network_lookup_interface(ifas, miface, &data_iface) != 0) {
2946 JANUS_LOG(LOG_ERR, "Can't add 'rtp' stream '%s', invalid network interface configuration for data...\n", (const char *)json_string_value(name));
2947 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
2948 g_snprintf(error_cause, 512, ifas ? "Invalid network interface configuration for data" : "Unable to query network device information");
2949 janus_mutex_lock(&mountpoints_mutex);
2950 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
2951 janus_mutex_unlock(&mountpoints_mutex);
2952 goto prepare_response;
2953 }
2954 } else {
2955 janus_network_address_nullify(&data_iface);
2956 }
2957 #else
2958 JANUS_LOG(LOG_ERR, "Can't add 'rtp' stream: no datachannels support...\n");
2959 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
2960 g_snprintf(error_cause, 512, "Can't add 'rtp' stream: no datachannels support...");
2961 janus_mutex_lock(&mountpoints_mutex);
2962 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
2963 janus_mutex_unlock(&mountpoints_mutex);
2964 goto prepare_response;
2965 #endif
2966 }
2967 JANUS_LOG(LOG_VERB, "Audio %s, Video %s\n", doaudio ? "enabled" : "NOT enabled", dovideo ? "enabled" : "NOT enabled");
2968 mp = janus_streaming_create_rtp_source(
2969 mpid, mpid_str,
2970 name ? (char *)json_string_value(name) : NULL,
2971 desc ? (char *)json_string_value(desc) : NULL,
2972 md ? (char *)json_string_value(md) : NULL,
2973 ssuite ? json_integer_value(ssuite) : 0,
2974 scrypto ? (char *)json_string_value(scrypto) : NULL,
2975 threads ? json_integer_value(threads) : 0,
2976 e2ee ? json_is_true(e2ee) : FALSE,
2977 doaudio, doaudiortcp, amcast, &audio_iface, aport, artcpport, acodec, artpmap, afmtp, doaskew,
2978 dovideo, dovideortcp, vmcast, &video_iface, vport, vrtcpport, vcodec, vrtpmap, vfmtp, bufferkf,
2979 simulcast, vport2, vport3, dosvc, dovskew,
2980 rtpcollision ? json_integer_value(rtpcollision) : 0,
2981 dodata, &data_iface, dport, textdata, buffermsg);
2982 janus_mutex_lock(&mountpoints_mutex);
2983 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
2984 janus_mutex_unlock(&mountpoints_mutex);
2985 if(mp == NULL) {
2986 JANUS_LOG(LOG_ERR, "Error creating 'rtp' stream...\n");
2987 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
2988 g_snprintf(error_cause, 512, "Error creating 'rtp' stream");
2989 goto prepare_response;
2990 }
2991 mp->is_private = is_private ? json_is_true(is_private) : FALSE;
2992 } else if(!strcasecmp(type_text, "live")) {
2993 /* File-based live source */
2994 JANUS_VALIDATE_JSON_OBJECT(root, live_parameters,
2995 error_code, error_cause, TRUE,
2996 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
2997 if(error_code != 0) {
2998 janus_mutex_lock(&mountpoints_mutex);
2999 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
3000 janus_mutex_unlock(&mountpoints_mutex);
3001 goto prepare_response;
3002 }
3003 json_t *name = json_object_get(root, "name");
3004 json_t *desc = json_object_get(root, "description");
3005 json_t *md = json_object_get(root, "metadata");
3006 json_t *is_private = json_object_get(root, "is_private");
3007 json_t *file = json_object_get(root, "filename");
3008 json_t *audio = json_object_get(root, "audio");
3009 json_t *video = json_object_get(root, "video");
3010 gboolean doaudio = audio ? json_is_true(audio) : FALSE;
3011 uint8_t acodec = 0;
3012 char *artpmap = NULL, *afmtp = NULL;
3013 if(doaudio) {
3014 json_t *audiopt = json_object_get(root, "audiopt");
3015 acodec = json_integer_value(audiopt);
3016 json_t *audiortpmap = json_object_get(root, "audiortpmap");
3017 artpmap = (char *)json_string_value(audiortpmap);
3018 json_t *audiofmtp = json_object_get(root, "audiofmtp");
3019 afmtp = (char *)json_string_value(audiofmtp);
3020 }
3021 gboolean dovideo = video ? json_is_true(video) : FALSE;
3022 /* We only support audio for file-based streaming at the moment: for streaming
3023 * files using other codecs/formats an external tools should feed us RTP instead */
3024 if(!doaudio || dovideo) {
3025 JANUS_LOG(LOG_ERR, "Can't add 'live' stream, we only support audio file streaming right now...\n");
3026 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
3027 g_snprintf(error_cause, 512, "Can't add 'live' stream, we only support audio file streaming right now...");
3028 janus_mutex_lock(&mountpoints_mutex);
3029 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
3030 janus_mutex_unlock(&mountpoints_mutex);
3031 goto prepare_response;
3032 }
3033 char *filename = (char *)json_string_value(file);
3034 #ifdef HAVE_LIBOGG
3035 if(!strstr(filename, ".opus") && !strstr(filename, ".alaw") && !strstr(filename, ".mulaw")) {
3036 JANUS_LOG(LOG_ERR, "Can't add 'live' stream, unsupported format (we only support Opus and raw mu-Law/a-Law files right now)\n");
3037 #else
3038 if(!strstr(filename, ".alaw") && !strstr(filename, ".mulaw")) {
3039 JANUS_LOG(LOG_ERR, "Can't add 'live' stream, unsupported format (we only support raw mu-Law and a-Law files right now)\n");
3040 #endif
3041 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
3042 g_snprintf(error_cause, 512, "Can't add 'live' stream, unsupported format (we only support raw mu-Law and a-Law files right now)");
3043 janus_mutex_lock(&mountpoints_mutex);
3044 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
3045 janus_mutex_unlock(&mountpoints_mutex);
3046 goto prepare_response;
3047 }
3048 FILE *audiofile = fopen(filename, "rb");
3049 if(!audiofile) {
3050 JANUS_LOG(LOG_ERR, "Can't add 'live' stream, no such file '%s'...\n", filename);
3051 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
3052 g_snprintf(error_cause, 512, "Can't add 'live' stream, no such file '%s'\n", filename);
3053 janus_mutex_lock(&mountpoints_mutex);
3054 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
3055 janus_mutex_unlock(&mountpoints_mutex);
3056 goto prepare_response;
3057 }
3058 fclose(audiofile);
3059 mp = janus_streaming_create_file_source(
3060 mpid, mpid_str,
3061 name ? (char *)json_string_value(name) : NULL,
3062 desc ? (char *)json_string_value(desc) : NULL,
3063 md ? (char *)json_string_value(md) : NULL,
3064 filename, TRUE,
3065 doaudio, acodec, artpmap, afmtp, dovideo);
3066 janus_mutex_lock(&mountpoints_mutex);
3067 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
3068 janus_mutex_unlock(&mountpoints_mutex);
3069 if(mp == NULL) {
3070 JANUS_LOG(LOG_ERR, "Error creating 'live' stream...\n");
3071 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
3072 g_snprintf(error_cause, 512, "Error creating 'live' stream");
3073 goto prepare_response;
3074 }
3075 mp->is_private = is_private ? json_is_true(is_private) : FALSE;
3076 } else if(!strcasecmp(type_text, "ondemand")) {
3077 /* File-based on demand source */
3078 JANUS_VALIDATE_JSON_OBJECT(root, ondemand_parameters,
3079 error_code, error_cause, TRUE,
3080 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
3081 if(error_code != 0) {
3082 janus_mutex_lock(&mountpoints_mutex);
3083 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
3084 janus_mutex_unlock(&mountpoints_mutex);
3085 goto prepare_response;
3086 }
3087 json_t *name = json_object_get(root, "name");
3088 json_t *desc = json_object_get(root, "description");
3089 json_t *md = json_object_get(root, "metadata");
3090 json_t *is_private = json_object_get(root, "is_private");
3091 json_t *file = json_object_get(root, "filename");
3092 json_t *audio = json_object_get(root, "audio");
3093 json_t *video = json_object_get(root, "video");
3094 gboolean doaudio = audio ? json_is_true(audio) : FALSE;
3095 uint8_t acodec = 0;
3096 char *artpmap = NULL, *afmtp = NULL;
3097 if(doaudio) {
3098 json_t *audiopt = json_object_get(root, "audiopt");
3099 acodec = json_integer_value(audiopt);
3100 json_t *audiortpmap = json_object_get(root, "audiortpmap");
3101 artpmap = (char *)json_string_value(audiortpmap);
3102 json_t *audiofmtp = json_object_get(root, "audiofmtp");
3103 afmtp = (char *)json_string_value(audiofmtp);
3104 }
3105 gboolean dovideo = video ? json_is_true(video) : FALSE;
3106 /* We only support audio for file-based streaming at the moment: for streaming
3107 * files using other codecs/formats an external tools should feed us RTP instead */
3108 if(!doaudio || dovideo) {
3109 JANUS_LOG(LOG_ERR, "Can't add 'ondemand' stream, we only support audio file streaming right now...\n");
3110 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
3111 g_snprintf(error_cause, 512, "Can't add 'ondemand' stream, we only support audio file streaming right now...");
3112 janus_mutex_lock(&mountpoints_mutex);
3113 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
3114 janus_mutex_unlock(&mountpoints_mutex);
3115 goto prepare_response;
3116 }
3117 char *filename = (char *)json_string_value(file);
3118 #ifdef HAVE_LIBOGG
3119 if(!strstr(filename, ".opus") && !strstr(filename, ".alaw") && !strstr(filename, ".mulaw")) {
3120 JANUS_LOG(LOG_ERR, "Can't add 'live' stream, unsupported format (we only support Opus and raw mu-Law/a-Law files right now)\n");
3121 #else
3122 if(!strstr(filename, ".alaw") && !strstr(filename, ".mulaw")) {
3123 JANUS_LOG(LOG_ERR, "Can't add 'live' stream, unsupported format (we only support raw mu-Law and a-Law files right now)\n");
3124 #endif
3125 JANUS_LOG(LOG_ERR, "Can't add 'ondemand' stream, unsupported format (we only support raw mu-Law and a-Law files right now)\n");
3126 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
3127 g_snprintf(error_cause, 512, "Can't add 'ondemand' stream, unsupported format (we only support raw mu-Law and a-Law files right now)");
3128 janus_mutex_lock(&mountpoints_mutex);
3129 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
3130 janus_mutex_unlock(&mountpoints_mutex);
3131 goto prepare_response;
3132 }
3133 FILE *audiofile = fopen(filename, "rb");
3134 if(!audiofile) {
3135 JANUS_LOG(LOG_ERR, "Can't add 'ondemand' stream, no such file '%s'...\n", filename);
3136 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
3137 g_snprintf(error_cause, 512, "Can't add 'ondemand' stream, no such file '%s'\n", filename);
3138 janus_mutex_lock(&mountpoints_mutex);
3139 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
3140 janus_mutex_unlock(&mountpoints_mutex);
3141 goto prepare_response;
3142 }
3143 fclose(audiofile);
3144 mp = janus_streaming_create_file_source(
3145 mpid, mpid_str,
3146 name ? (char *)json_string_value(name) : NULL,
3147 desc ? (char *)json_string_value(desc) : NULL,
3148 md ? (char *)json_string_value(md) : NULL,
3149 filename, FALSE,
3150 doaudio, acodec, artpmap, afmtp, dovideo);
3151 janus_mutex_lock(&mountpoints_mutex);
3152 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
3153 janus_mutex_unlock(&mountpoints_mutex);
3154 if(mp == NULL) {
3155 JANUS_LOG(LOG_ERR, "Error creating 'ondemand' stream...\n");
3156 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
3157 g_snprintf(error_cause, 512, "Error creating 'ondemand' stream");
3158 goto prepare_response;
3159 }
3160 mp->is_private = is_private ? json_is_true(is_private) : FALSE;
3161 } else if(!strcasecmp(type_text, "rtsp")) {
3162 #ifndef HAVE_LIBCURL
3163 JANUS_LOG(LOG_ERR, "Can't create 'rtsp' mountpoint, libcurl support not compiled...\n");
3164 error_code = JANUS_STREAMING_ERROR_INVALID_ELEMENT;
3165 g_snprintf(error_cause, 512, "Can't create 'rtsp' mountpoint, libcurl support not compiled...\n");
3166 goto prepare_response;
3167 #else
3168 JANUS_VALIDATE_JSON_OBJECT(root, rtsp_parameters,
3169 error_code, error_cause, TRUE,
3170 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
3171 if(error_code != 0) {
3172 janus_mutex_lock(&mountpoints_mutex);
3173 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
3174 janus_mutex_unlock(&mountpoints_mutex);
3175 goto prepare_response;
3176 }
3177 /* RTSP source*/
3178 janus_network_address multicast_iface;
3179 json_t *name = json_object_get(root, "name");
3180 json_t *desc = json_object_get(root, "description");
3181 json_t *md = json_object_get(root, "metadata");
3182 json_t *is_private = json_object_get(root, "is_private");
3183 json_t *audio = json_object_get(root, "audio");
3184 json_t *audiopt = json_object_get(root, "audiopt");
3185 json_t *audiortpmap = json_object_get(root, "audiortpmap");
3186 json_t *audiofmtp = json_object_get(root, "audiofmtp");
3187 json_t *video = json_object_get(root, "video");
3188 json_t *videopt = json_object_get(root, "videopt");
3189 json_t *videortpmap = json_object_get(root, "videortpmap");
3190 json_t *videofmtp = json_object_get(root, "videofmtp");
3191 json_t *videobufferkf = json_object_get(root, "videobufferkf");
3192 json_t *url = json_object_get(root, "url");
3193 json_t *username = json_object_get(root, "rtsp_user");
3194 json_t *password = json_object_get(root, "rtsp_pwd");
3195 json_t *iface = json_object_get(root, "rtspiface");
3196 json_t *threads = json_object_get(root, "threads");
3197 json_t *failerr = json_object_get(root, "rtsp_failcheck");
3198 json_t *reconnect_delay = json_object_get(root, "rtsp_reconnect_delay");
3199 json_t *session_timeout = json_object_get(root, "rtsp_session_timeout");
3200 json_t *rtsp_timeout = json_object_get(root, "rtsp_timeout");
3201 json_t *rtsp_conn_timeout = json_object_get(root, "rtsp_conn_timeout");
3202 if(failerr == NULL) /* For an old typo, we support the legacy syntax too */
3203 failerr = json_object_get(root, "rtsp_check");
3204 gboolean doaudio = audio ? json_is_true(audio) : FALSE;
3205 gboolean dovideo = video ? json_is_true(video) : FALSE;
3206 gboolean error_on_failure = failerr ? json_is_true(failerr) : TRUE;
3207 if(!doaudio && !dovideo) {
3208 JANUS_LOG(LOG_ERR, "Can't add 'rtsp' stream, no audio or video have to be streamed...\n");
3209 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
3210 g_snprintf(error_cause, 512, "Can't add 'rtsp' stream, no audio or video have to be streamed...");
3211 janus_mutex_lock(&mountpoints_mutex);
3212 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
3213 janus_mutex_unlock(&mountpoints_mutex);
3214 goto prepare_response;
3215 } else {
3216 if(iface) {
3217 const char *miface = (const char *)json_string_value(iface);
3218 if(janus_network_lookup_interface(ifas, miface, &multicast_iface) != 0) {
3219 JANUS_LOG(LOG_ERR, "Can't add 'rtsp' stream '%s', invalid network interface configuration for stream...\n", (const char *)json_string_value(name));
3220 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
3221 g_snprintf(error_cause, 512, ifas ? "Invalid network interface configuration for stream" : "Unable to query network device information");
3222 janus_mutex_lock(&mountpoints_mutex);
3223 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
3224 janus_mutex_unlock(&mountpoints_mutex);
3225 goto prepare_response;
3226 }
3227 } else {
3228 janus_network_address_nullify(&multicast_iface);
3229 }
3230 }
3231 mp = janus_streaming_create_rtsp_source(
3232 mpid, mpid_str,
3233 name ? (char *)json_string_value(name) : NULL,
3234 desc ? (char *)json_string_value(desc) : NULL,
3235 md ? (char *)json_string_value(md) : NULL,
3236 (char *)json_string_value(url),
3237 username ? (char *)json_string_value(username) : NULL,
3238 password ? (char *)json_string_value(password) : NULL,
3239 doaudio, (audiopt ? json_integer_value(audiopt) : -1),
3240 (char *)json_string_value(audiortpmap), (char *)json_string_value(audiofmtp),
3241 dovideo, (videopt ? json_integer_value(videopt) : -1),
3242 (char *)json_string_value(videortpmap), (char *)json_string_value(videofmtp),
3243 videobufferkf ? json_is_true(videobufferkf) : FALSE,
3244 &multicast_iface, (threads ? json_integer_value(threads) : 0),
3245 ((reconnect_delay ? json_integer_value(reconnect_delay) : JANUS_STREAMING_DEFAULT_RECONNECT_DELAY) * G_USEC_PER_SEC),
3246 ((session_timeout ? json_integer_value(session_timeout) : JANUS_STREAMING_DEFAULT_SESSION_TIMEOUT) * G_USEC_PER_SEC),
3247 (rtsp_timeout ? json_integer_value(rtsp_timeout) : JANUS_STREAMING_DEFAULT_CURL_TIMEOUT),
3248 (rtsp_conn_timeout ? json_integer_value(rtsp_conn_timeout) : JANUS_STREAMING_DEFAULT_CURL_CONNECT_TIMEOUT),
3249 error_on_failure);
3250 janus_mutex_lock(&mountpoints_mutex);
3251 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)mpid_str : (gpointer)&mpid);
3252 janus_mutex_unlock(&mountpoints_mutex);
3253 if(mp == NULL) {
3254 JANUS_LOG(LOG_ERR, "Error creating 'rtsp' stream...\n");
3255 error_code = JANUS_STREAMING_ERROR_CANT_CREATE;
3256 g_snprintf(error_cause, 512, "Error creating 'RTSP' stream");
3257 goto prepare_response;
3258 }
3259 mp->is_private = is_private ? json_is_true(is_private) : FALSE;
3260 #endif
3261 } else {
3262 JANUS_LOG(LOG_ERR, "Unknown stream type '%s'...\n", type_text);
3263 error_code = JANUS_STREAMING_ERROR_INVALID_ELEMENT;
3264 g_snprintf(error_cause, 512, "Unknown stream type '%s'...\n", type_text);
3265 goto prepare_response;
3266 }
3267 /* Any secret? */
3268 if(secret)
3269 mp->secret = g_strdup(json_string_value(secret));
3270 /* Any PIN? */
3271 if(pin)
3272 mp->pin = g_strdup(json_string_value(pin));
3273 if(save) {
3274 /* This mountpoint is permanent: save to the configuration file too
3275 * FIXME: We should check if anything fails... */
3276 JANUS_LOG(LOG_VERB, "Saving mountpoint %s permanently in config file\n", mp->id_str);
3277 janus_mutex_lock(&config_mutex);
3278 char value[BUFSIZ];
3279 /* The category to add is the mountpoint name */
3280 janus_config_category *c = janus_config_get_create(config, NULL, janus_config_type_category, mp->name);
3281 /* Now for the common values */
3282 janus_config_add(config, c, janus_config_item_create("type", type_text));
3283 janus_config_add(config, c, janus_config_item_create("id", mp->id_str));
3284 janus_config_add(config, c, janus_config_item_create("description", mp->description));
3285 if(mp->metadata)
3286 janus_config_add(config, c, janus_config_item_create("metadata", mp->metadata));
3287 if(mp->is_private)
3288 janus_config_add(config, c, janus_config_item_create("is_private", "yes"));
3289 /* Per type values */
3290 if(!strcasecmp(type_text, "rtp")) {
3291 janus_config_add(config, c, janus_config_item_create("audio", mp->codecs.audio_pt >= 0 ? "yes" : "no"));
3292 janus_streaming_rtp_source *source = mp->source;
3293 if(mp->codecs.audio_pt >= 0) {
3294 g_snprintf(value, BUFSIZ, "%d", source->audio_port);
3295 janus_config_add(config, c, janus_config_item_create("audioport", value));
3296 if(source->audio_rtcp_port > 0) {
3297 g_snprintf(value, BUFSIZ, "%d", source->audio_rtcp_port);
3298 janus_config_add(config, c, janus_config_item_create("audiortcpport", value));
3299 }
3300 json_t *audiomcast = json_object_get(root, "audiomcast");
3301 if(audiomcast)
3302 janus_config_add(config, c, janus_config_item_create("audiomcast", json_string_value(audiomcast)));
3303 g_snprintf(value, BUFSIZ, "%d", mp->codecs.audio_pt);
3304 janus_config_add(config, c, janus_config_item_create("audiopt", value));
3305 janus_config_add(config, c, janus_config_item_create("audiortpmap", mp->codecs.audio_rtpmap));
3306 if(mp->codecs.audio_fmtp)
3307 janus_config_add(config, c, janus_config_item_create("audiofmtp", mp->codecs.audio_fmtp));
3308 json_t *aiface = json_object_get(root, "audioiface");
3309 if(aiface)
3310 janus_config_add(config, c, janus_config_item_create("audioiface", json_string_value(aiface)));
3311 if(source->askew)
3312 janus_config_add(config, c, janus_config_item_create("askew", "yes"));
3313 }
3314 janus_config_add(config, c, janus_config_item_create("video", mp->codecs.video_pt > 0 ? "yes" : "no"));
3315 if(mp->codecs.video_pt > 0) {
3316 g_snprintf(value, BUFSIZ, "%d", source->video_port[0]);
3317 janus_config_add(config, c, janus_config_item_create("videoport", value));
3318 if(source->video_rtcp_port > 0) {
3319 g_snprintf(value, BUFSIZ, "%d", source->video_rtcp_port);
3320 janus_config_add(config, c, janus_config_item_create("videortcpport", value));
3321 }
3322 json_t *videomcast = json_object_get(root, "videomcast");
3323 if(videomcast)
3324 janus_config_add(config, c, janus_config_item_create("videomcast", json_string_value(videomcast)));
3325 g_snprintf(value, BUFSIZ, "%d", mp->codecs.video_pt);
3326 janus_config_add(config, c, janus_config_item_create("videopt", value));
3327 janus_config_add(config, c, janus_config_item_create("videortpmap", mp->codecs.video_rtpmap));
3328 if(mp->codecs.video_fmtp)
3329 janus_config_add(config, c, janus_config_item_create("videofmtp", mp->codecs.video_fmtp));
3330 if(source->keyframe.enabled)
3331 janus_config_add(config, c, janus_config_item_create("videobufferkf", "yes"));
3332 if(source->simulcast) {
3333 janus_config_add(config, c, janus_config_item_create("videosimulcast", "yes"));
3334 if(source->video_port[1]) {
3335 g_snprintf(value, BUFSIZ, "%d", source->video_port[1]);
3336 janus_config_add(config, c, janus_config_item_create("videoport2", value));
3337 }
3338 if(source->video_port[2]) {
3339 g_snprintf(value, BUFSIZ, "%d", source->video_port[2]);
3340 janus_config_add(config, c, janus_config_item_create("videoport3", value));
3341 }
3342 }
3343 if(source->svc)
3344 janus_config_add(config, c, janus_config_item_create("videosvc", "yes"));
3345 json_t *viface = json_object_get(root, "videoiface");
3346 if(viface)
3347 janus_config_add(config, c, janus_config_item_create("videoiface", json_string_value(viface)));
3348 if(source->vskew)
3349 janus_config_add(config, c, janus_config_item_create("videoskew", "yes"));
3350 }
3351 if(source->rtp_collision > 0) {
3352 g_snprintf(value, BUFSIZ, "%d", source->rtp_collision);
3353 janus_config_add(config, c, janus_config_item_create("collision", value));
3354 }
3355 janus_config_add(config, c, janus_config_item_create("data", mp->data ? "yes" : "no"));
3356 if(source->data_port > -1) {
3357 g_snprintf(value, BUFSIZ, "%d", source->data_port);
3358 janus_config_add(config, c, janus_config_item_create("dataport", value));
3359 if(source->buffermsg)
3360 janus_config_add(config, c, janus_config_item_create("databuffermsg", "yes"));
3361 json_t *diface = json_object_get(root, "dataiface");
3362 if(diface)
3363 janus_config_add(config, c, janus_config_item_create("dataiface", json_string_value(diface)));
3364 }
3365 if(source->srtpsuite > 0 && source->srtpcrypto) {
3366 g_snprintf(value, BUFSIZ, "%d", source->srtpsuite);
3367 janus_config_add(config, c, janus_config_item_create("srtpsuite", value));
3368 janus_config_add(config, c, janus_config_item_create("srtpcrypto", source->srtpcrypto));
3369 }
3370 if(mp->helper_threads > 0) {
3371 g_snprintf(value, BUFSIZ, "%d", mp->helper_threads);
3372 janus_config_add(config, c, janus_config_item_create("threads", value));
3373 }
3374 } else if(!strcasecmp(type_text, "live") || !strcasecmp(type_text, "ondemand")) {
3375 janus_streaming_file_source *source = mp->source;
3376 janus_config_add(config, c, janus_config_item_create("filename", source->filename));
3377 janus_config_add(config, c, janus_config_item_create("audio", mp->codecs.audio_pt >= 0 ? "yes" : "no"));
3378 janus_config_add(config, c, janus_config_item_create("video", mp->codecs.video_pt > 0 ? "yes" : "no"));
3379 } else if(!strcasecmp(type_text, "rtsp")) {
3380 #ifdef HAVE_LIBCURL
3381 janus_streaming_rtp_source *source = mp->source;
3382 if(source->rtsp_url)
3383 janus_config_add(config, c, janus_config_item_create("url", source->rtsp_url));
3384 if(source->rtsp_username)
3385 janus_config_add(config, c, janus_config_item_create("rtsp_user", source->rtsp_username));
3386 if(source->rtsp_password)
3387 janus_config_add(config, c, janus_config_item_create("rtsp_pwd", source->rtsp_password));
3388 #endif
3389 janus_config_add(config, c, janus_config_item_create("audio", mp->codecs.audio_pt >= 0 ? "yes" : "no"));
3390 if(mp->codecs.audio_pt >= 0) {
3391 if(mp->codecs.audio_rtpmap)
3392 janus_config_add(config, c, janus_config_item_create("audiortpmap", mp->codecs.audio_rtpmap));
3393 if(mp->codecs.audio_fmtp)
3394 janus_config_add(config, c, janus_config_item_create("audiofmtp", mp->codecs.audio_fmtp));
3395 }
3396 janus_config_add(config, c, janus_config_item_create("video", mp->codecs.video_pt > 0 ? "yes" : "no"));
3397 if(mp->codecs.video_pt > 0) {
3398 if(mp->codecs.video_rtpmap)
3399 janus_config_add(config, c, janus_config_item_create("videortpmap", mp->codecs.video_rtpmap));
3400 if(mp->codecs.video_fmtp)
3401 janus_config_add(config, c, janus_config_item_create("videofmtp", mp->codecs.video_fmtp));
3402 }
3403 json_t *iface = json_object_get(root, "rtspiface");
3404 if(iface)
3405 janus_config_add(config, c, janus_config_item_create("rtspiface", json_string_value(iface)));
3406 if(mp->helper_threads > 0) {
3407 g_snprintf(value, BUFSIZ, "%d", mp->helper_threads);
3408 janus_config_add(config, c, janus_config_item_create("threads", value));
3409 }
3410 }
3411 /* Some more common values */
3412 if(mp->secret)
3413 janus_config_add(config, c, janus_config_item_create("secret", mp->secret));
3414 if(mp->pin)
3415 janus_config_add(config, c, janus_config_item_create("pin", mp->pin));
3416 /* Save modified configuration */
3417 if(janus_config_save(config, config_folder, JANUS_STREAMING_PACKAGE) < 0)
3418 save = FALSE; /* This will notify the user the mountpoint is not permanent */
3419 janus_mutex_unlock(&config_mutex);
3420 }
3421 /* Send info back */
3422 response = json_object();
3423 json_object_set_new(response, "streaming", json_string("created"));
3424 json_object_set_new(response, "created", json_string(mp->name));
3425 json_object_set_new(response, "permanent", save ? json_true() : json_false());
3426 json_t *ml = json_object();
3427 json_object_set_new(ml, "id", string_ids ? json_string(mp->id_str) : json_integer(mp->id));
3428 json_object_set_new(ml, "type", json_string(mp->streaming_type == janus_streaming_type_live ? "live" : "on demand"));
3429 json_object_set_new(ml, "description", json_string(mp->description));
3430 json_object_set_new(ml, "is_private", mp->is_private ? json_true() : json_false());
3431 if(!strcasecmp(type_text, "rtp")) {
3432 janus_streaming_rtp_source *source = mp->source;
3433 if(source->audio_fd != -1) {
3434 if(source->audio_host)
3435 json_object_set_new(ml, "audio_host", json_string(source->audio_host));
3436 json_object_set_new(ml, "audio_port", json_integer(source->audio_port));
3437 }
3438 if(source->audio_rtcp_fd != -1) {
3439 json_object_set_new(ml, "audio_rtcp_port", json_integer(source->audio_rtcp_port));
3440 }
3441 if(source->video_fd[0] != -1) {
3442 if(source->video_host)
3443 json_object_set_new(ml, "video_host", json_string(source->video_host));
3444 json_object_set_new(ml, "video_port", json_integer(source->video_port[0]));
3445 }
3446 if(source->video_rtcp_fd != -1) {
3447 json_object_set_new(ml, "video_rtcp_port", json_integer(source->video_rtcp_port));
3448 }
3449 if(source->video_fd[1] != -1) {
3450 json_object_set_new(ml, "video_port_2", json_integer(source->video_port[1]));
3451 }
3452 if(source->video_fd[2] != -1) {
3453 json_object_set_new(ml, "video_port_3", json_integer(source->video_port[2]));
3454 }
3455 if(source->data_fd != -1) {
3456 if(source->data_host)
3457 json_object_set_new(ml, "data_host", json_string(source->data_host));
3458 json_object_set_new(ml, "data_port", json_integer(source->data_port));
3459 }
3460 }
3461 json_object_set_new(response, "stream", ml);
3462 /* Also notify event handlers */
3463 if(notify_events && gateway->events_is_enabled()) {
3464 json_t *info = json_object();
3465 json_object_set_new(info, "event", json_string("created"));
3466 json_object_set_new(info, "id", string_ids ? json_string(mp->id_str) : json_integer(mp->id));
3467 json_object_set_new(info, "type", json_string(mp->streaming_type == janus_streaming_type_live ? "live" : "on demand"));
3468 gateway->notify_event(&janus_streaming_plugin, session ? session->handle : NULL, info);
3469 }
3470 goto prepare_response;
3471 } else if(!strcasecmp(request_text, "edit")) {
3472 JANUS_LOG(LOG_VERB, "Attempt to edit an existing streaming mountpoint\n");
3473 JANUS_VALIDATE_JSON_OBJECT(root, edit_parameters,
3474 error_code, error_cause, TRUE,
3475 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
3476 if(error_code != 0)
3477 goto prepare_response;
3478 if(!string_ids) {
3479 JANUS_VALIDATE_JSON_OBJECT(root, id_parameters,
3480 error_code, error_cause, TRUE,
3481 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
3482 } else {
3483 JANUS_VALIDATE_JSON_OBJECT(root, idstr_parameters,
3484 error_code, error_cause, TRUE,
3485 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
3486 }
3487 if(error_code != 0)
3488 goto prepare_response;
3489 /* We only allow for a limited set of properties to be edited */
3490 json_t *id = json_object_get(root, "id");
3491 json_t *desc = json_object_get(root, "new_description");
3492 json_t *md = json_object_get(root, "new_metadata");
3493 json_t *secret = json_object_get(root, "new_secret");
3494 json_t *pin = json_object_get(root, "new_pin");
3495 json_t *is_private = json_object_get(root, "new_is_private");
3496 json_t *permanent = json_object_get(root, "permanent");
3497 gboolean save = permanent ? json_is_true(permanent) : FALSE;
3498 if(save && config == NULL) {
3499 JANUS_LOG(LOG_ERR, "No configuration file, can't edit mountpoint permanently\n");
3500 error_code = JANUS_STREAMING_ERROR_UNKNOWN_ERROR;
3501 g_snprintf(error_cause, 512, "No configuration file, can't edit mountpoint permanently");
3502 goto prepare_response;
3503 }
3504 guint64 id_value = 0;
3505 char id_num[30], *id_value_str = NULL;
3506 if(!string_ids) {
3507 id_value = json_integer_value(id);
3508 g_snprintf(id_num, sizeof(id_num), "%"SCNu64, id_value);
3509 id_value_str = id_num;
3510 } else {
3511 id_value_str = (char *)json_string_value(id);
3512 }
3513 janus_mutex_lock(&mountpoints_mutex);
3514 janus_streaming_mountpoint *mp = g_hash_table_lookup(mountpoints,
3515 string_ids ? (gpointer)id_value_str : (gpointer)&id_value);
3516 if(mp == NULL) {
3517 janus_mutex_unlock(&mountpoints_mutex);
3518 JANUS_LOG(LOG_ERR, "No such mountpoint (%s)\n", id_value_str);
3519 error_code = JANUS_STREAMING_ERROR_NO_SUCH_MOUNTPOINT;
3520 g_snprintf(error_cause, 512, "No such mountpoint (%s)", id_value_str);
3521 goto prepare_response;
3522 }
3523 janus_refcount_increase(&mp->ref);
3524 janus_mutex_lock(&mp->mutex);
3525 /* A secret may be required for this action */
3526 JANUS_CHECK_SECRET(mp->secret, root, "secret", error_code, error_cause,
3527 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT, JANUS_STREAMING_ERROR_UNAUTHORIZED);
3528 if(error_code != 0) {
3529 janus_mutex_unlock(&mp->mutex);
3530 janus_mutex_unlock(&mountpoints_mutex);
3531 janus_refcount_decrease(&mp->ref);
3532 goto prepare_response;
3533 }
3534 /* Edit the mountpoint properties that were provided */
3535 if(desc != NULL && strlen(json_string_value(desc)) > 0) {
3536 char *old_description = mp->description;
3537 char *new_description = g_strdup(json_string_value(desc));
3538 mp->description = new_description;
3539 g_free(old_description);
3540 }
3541 if(md != NULL) {
3542 char *old_metadata = mp->metadata;
3543 char *new_metadata = g_strdup(json_string_value(md));
3544 mp->metadata = new_metadata;
3545 g_free(old_metadata);
3546 }
3547 if(is_private)
3548 mp->is_private = json_is_true(is_private);
3549 /* A secret may be required for this action */
3550 JANUS_CHECK_SECRET(mp->secret, root, "secret", error_code, error_cause,
3551 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT, JANUS_STREAMING_ERROR_UNAUTHORIZED);
3552 if(error_code != 0) {
3553 janus_mutex_unlock(&mp->mutex);
3554 janus_mutex_unlock(&mountpoints_mutex);
3555 janus_refcount_decrease(&mp->ref);
3556 goto prepare_response;
3557 }
3558 if(secret && strlen(json_string_value(secret)) > 0) {
3559 char *old_secret = mp->secret;
3560 char *new_secret = g_strdup(json_string_value(secret));
3561 mp->secret = new_secret;
3562 g_free(old_secret);
3563 }
3564 if(pin && strlen(json_string_value(pin)) > 0) {
3565 char *old_pin = mp->pin;
3566 char *new_pin = g_strdup(json_string_value(pin));
3567 mp->pin = new_pin;
3568 g_free(old_pin);
3569 }
3570 if(save) {
3571 JANUS_LOG(LOG_VERB, "Saving edited mountpoint %s permanently in config file\n", mp->id_str);
3572 janus_mutex_lock(&config_mutex);
3573 char value[BUFSIZ];
3574 /* Remove the old category first */
3575 janus_config_remove(config, NULL, mp->name);
3576 /* Now write the room details again */
3577 janus_config_category *c = janus_config_get_create(config, NULL, janus_config_type_category, mp->name);
3578 /* Now for the common values at top */
3579 janus_config_add(config, c, janus_config_item_create("id", mp->id_str));
3580 janus_config_add(config, c, janus_config_item_create("description", mp->description));
3581 if(mp->metadata)
3582 janus_config_add(config, c, janus_config_item_create("metadata", mp->metadata));
3583 if(mp->is_private)
3584 janus_config_add(config, c, janus_config_item_create("is_private", "yes"));
3585 /* Per type values */
3586 if(mp->streaming_source == janus_streaming_source_rtp) {
3587 gboolean rtsp = FALSE;
3588 #ifdef HAVE_LIBCURL
3589 janus_streaming_rtp_source *source = mp->source;
3590 if(source->rtsp)
3591 rtsp = TRUE;
3592 #endif
3593 if(rtsp) {
3594 #ifdef HAVE_LIBCURL
3595 janus_config_add(config, c, janus_config_item_create("type", "rtsp"));
3596 if(source->rtsp_url)
3597 janus_config_add(config, c, janus_config_item_create("url", source->rtsp_url));
3598 if(source->rtsp_username)
3599 janus_config_add(config, c, janus_config_item_create("rtsp_user", source->rtsp_username));
3600 if(source->rtsp_password)
3601 janus_config_add(config, c, janus_config_item_create("rtsp_pwd", source->rtsp_password));
3602 #endif
3603 janus_config_add(config, c, janus_config_item_create("audio", mp->codecs.audio_pt >= 0 ? "yes" : "no"));
3604 if(mp->codecs.audio_pt >= 0) {
3605 if(mp->codecs.audio_rtpmap)
3606 janus_config_add(config, c, janus_config_item_create("audiortpmap", mp->codecs.audio_rtpmap));
3607 if(mp->codecs.audio_fmtp)
3608 janus_config_add(config, c, janus_config_item_create("audiofmtp", mp->codecs.audio_fmtp));
3609 }
3610 janus_config_add(config, c, janus_config_item_create("video", mp->codecs.video_pt > 0 ? "yes" : "no"));
3611 if(mp->codecs.video_pt > 0) {
3612 if(mp->codecs.video_rtpmap)
3613 janus_config_add(config, c, janus_config_item_create("videortpmap", mp->codecs.video_rtpmap));
3614 if(mp->codecs.video_fmtp)
3615 janus_config_add(config, c, janus_config_item_create("videofmtp", mp->codecs.video_fmtp));
3616 }
3617 json_t *iface = json_object_get(root, "rtspiface");
3618 if(iface)
3619 janus_config_add(config, c, janus_config_item_create("rtspiface", json_string_value(iface)));
3620 if(mp->helper_threads > 0) {
3621 g_snprintf(value, BUFSIZ, "%d", mp->helper_threads);
3622 janus_config_add(config, c, janus_config_item_create("threads", value));
3623 }
3624 } else {
3625 janus_config_add(config, c, janus_config_item_create("type", "rtp"));
3626 janus_config_add(config, c, janus_config_item_create("audio", mp->codecs.audio_pt >= 0 ? "yes" : "no"));
3627 janus_streaming_rtp_source *source = mp->source;
3628 if(mp->codecs.audio_pt >= 0) {
3629 g_snprintf(value, BUFSIZ, "%d", source->audio_port);
3630 janus_config_add(config, c, janus_config_item_create("audioport", value));
3631 if(source->audio_rtcp_port > 0) {
3632 g_snprintf(value, BUFSIZ, "%d", source->audio_rtcp_port);
3633 janus_config_add(config, c, janus_config_item_create("audiortcpport", value));
3634 }
3635 json_t *audiomcast = json_object_get(root, "audiomcast");
3636 if(audiomcast)
3637 janus_config_add(config, c, janus_config_item_create("audiomcast", json_string_value(audiomcast)));
3638 g_snprintf(value, BUFSIZ, "%d", mp->codecs.audio_pt);
3639 janus_config_add(config, c, janus_config_item_create("audiopt", value));
3640 janus_config_add(config, c, janus_config_item_create("audiortpmap", mp->codecs.audio_rtpmap));
3641 if(mp->codecs.audio_fmtp)
3642 janus_config_add(config, c, janus_config_item_create("audiofmtp", mp->codecs.audio_fmtp));
3643 json_t *aiface = json_object_get(root, "audioiface");
3644 if(aiface)
3645 janus_config_add(config, c, janus_config_item_create("audioiface", json_string_value(aiface)));
3646 if(source->askew)
3647 janus_config_add(config, c, janus_config_item_create("askew", "yes"));
3648 }
3649 janus_config_add(config, c, janus_config_item_create("video", mp->codecs.video_pt > 0 ? "yes" : "no"));
3650 if(mp->codecs.video_pt > 0) {
3651 g_snprintf(value, BUFSIZ, "%d", source->video_port[0]);
3652 janus_config_add(config, c, janus_config_item_create("videoport", value));
3653 if(source->video_rtcp_port > 0) {
3654 g_snprintf(value, BUFSIZ, "%d", source->video_rtcp_port);
3655 janus_config_add(config, c, janus_config_item_create("videortcpport", value));
3656 }
3657 json_t *videomcast = json_object_get(root, "videomcast");
3658 if(videomcast)
3659 janus_config_add(config, c, janus_config_item_create("videomcast", json_string_value(videomcast)));
3660 g_snprintf(value, BUFSIZ, "%d", mp->codecs.video_pt);
3661 janus_config_add(config, c, janus_config_item_create("videopt", value));
3662 janus_config_add(config, c, janus_config_item_create("videortpmap", mp->codecs.video_rtpmap));
3663 if(mp->codecs.video_fmtp)
3664 janus_config_add(config, c, janus_config_item_create("videofmtp", mp->codecs.video_fmtp));
3665 if(source->keyframe.enabled)
3666 janus_config_add(config, c, janus_config_item_create("videobufferkf", "yes"));
3667 if(source->simulcast) {
3668 janus_config_add(config, c, janus_config_item_create("videosimulcast", "yes"));
3669 if(source->video_port[1]) {
3670 g_snprintf(value, BUFSIZ, "%d", source->video_port[1]);
3671 janus_config_add(config, c, janus_config_item_create("videoport2", value));
3672 }
3673 if(source->video_port[2]) {
3674 g_snprintf(value, BUFSIZ, "%d", source->video_port[2]);
3675 janus_config_add(config, c, janus_config_item_create("videoport3", value));
3676 }
3677 }
3678 if(source->svc)
3679 janus_config_add(config, c, janus_config_item_create("videosvc", "yes"));
3680 json_t *viface = json_object_get(root, "videoiface");
3681 if(viface)
3682 janus_config_add(config, c, janus_config_item_create("videoiface", json_string_value(viface)));
3683 if(source->vskew)
3684 janus_config_add(config, c, janus_config_item_create("videoskew", "yes"));
3685 }
3686 if(source->rtp_collision > 0) {
3687 g_snprintf(value, BUFSIZ, "%d", source->rtp_collision);
3688 janus_config_add(config, c, janus_config_item_create("collision", value));
3689 }
3690 janus_config_add(config, c, janus_config_item_create("data", mp->data ? "yes" : "no"));
3691 if(source->data_port > -1) {
3692 g_snprintf(value, BUFSIZ, "%d", source->data_port);
3693 janus_config_add(config, c, janus_config_item_create("dataport", value));
3694 if(source->buffermsg)
3695 janus_config_add(config, c, janus_config_item_create("databuffermsg", "yes"));
3696 json_t *diface = json_object_get(root, "dataiface");
3697 if(diface)
3698 janus_config_add(config, c, janus_config_item_create("dataiface", json_string_value(diface)));
3699 }
3700 if(source->srtpsuite > 0 && source->srtpcrypto) {
3701 g_snprintf(value, BUFSIZ, "%d", source->srtpsuite);
3702 janus_config_add(config, c, janus_config_item_create("srtpsuite", value));
3703 janus_config_add(config, c, janus_config_item_create("srtpcrypto", source->srtpcrypto));
3704 }
3705 if(mp->helper_threads > 0) {
3706 g_snprintf(value, BUFSIZ, "%d", mp->helper_threads);
3707 janus_config_add(config, c, janus_config_item_create("threads", value));
3708 }
3709 }
3710 } else {
3711 janus_config_add(config, c, janus_config_item_create("type", (mp->streaming_type == janus_streaming_type_live) ? "live" : "ondemand"));
3712 janus_streaming_file_source *source = mp->source;
3713 janus_config_add(config, c, janus_config_item_create("filename", source->filename));
3714 janus_config_add(config, c, janus_config_item_create("audio", mp->codecs.audio_pt >= 0 ? "yes" : "no"));
3715 janus_config_add(config, c, janus_config_item_create("video", mp->codecs.video_pt > 0 ? "yes" : "no"));
3716 }
3717 /* Some more common values */
3718 if(mp->secret)
3719 janus_config_add(config, c, janus_config_item_create("secret", mp->secret));
3720 if(mp->pin)
3721 janus_config_add(config, c, janus_config_item_create("pin", mp->pin));
3722 /* Save modified configuration */
3723 if(janus_config_save(config, config_folder, JANUS_STREAMING_PACKAGE) < 0)
3724 save = FALSE; /* This will notify the user the mountpoint is not permanent */
3725 janus_mutex_unlock(&config_mutex);
3726 }
3727 /* Prepare response/notification */
3728 response = json_object();
3729 json_object_set_new(response, "streaming", json_string("edited"));
3730 json_object_set_new(response, "id", string_ids ? json_string(mp->id_str) : json_integer(mp->id));
3731 json_object_set_new(response, "permanent", save ? json_true() : json_false());
3732 /* Also notify event handlers */
3733 if(notify_events && gateway->events_is_enabled()) {
3734 json_t *info = json_object();
3735 json_object_set_new(info, "event", json_string("edited"));
3736 json_object_set_new(info, "id", string_ids ? json_string(mp->id_str) : json_integer(mp->id));
3737 gateway->notify_event(&janus_streaming_plugin, session ? session->handle : NULL, info);
3738 }
3739 janus_mutex_unlock(&mp->mutex);
3740 janus_mutex_unlock(&mountpoints_mutex);
3741 janus_refcount_decrease(&mp->ref);
3742 /* Done */
3743 JANUS_LOG(LOG_VERB, "Streaming mountpoint edited\n");
3744 goto prepare_response;
3745 } else if(!strcasecmp(request_text, "destroy")) {
3746 /* Get rid of an existing stream (notice this doesn't remove it from the config file, though) */
3747 JANUS_VALIDATE_JSON_OBJECT(root, destroy_parameters,
3748 error_code, error_cause, TRUE,
3749 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
3750 if(error_code != 0)
3751 goto prepare_response;
3752 if(!string_ids) {
3753 JANUS_VALIDATE_JSON_OBJECT(root, id_parameters,
3754 error_code, error_cause, TRUE,
3755 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
3756 } else {
3757 JANUS_VALIDATE_JSON_OBJECT(root, idstr_parameters,
3758 error_code, error_cause, TRUE,
3759 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
3760 }
3761 if(error_code != 0)
3762 goto prepare_response;
3763 json_t *id = json_object_get(root, "id");
3764 guint64 id_value = 0;
3765 char id_num[30], *id_value_str = NULL;
3766 if(!string_ids) {
3767 id_value = json_integer_value(id);
3768 g_snprintf(id_num, sizeof(id_num), "%"SCNu64, id_value);
3769 id_value_str = id_num;
3770 } else {
3771 id_value_str = (char *)json_string_value(id);
3772 }
3773 json_t *permanent = json_object_get(root, "permanent");
3774 gboolean save = permanent ? json_is_true(permanent) : FALSE;
3775 if(save && config == NULL) {
3776 JANUS_LOG(LOG_ERR, "No configuration file, can't destroy mountpoint permanently\n");
3777 error_code = JANUS_STREAMING_ERROR_UNKNOWN_ERROR;
3778 g_snprintf(error_cause, 512, "No configuration file, can't destroy mountpoint permanently");
3779 goto prepare_response;
3780 }
3781 janus_mutex_lock(&mountpoints_mutex);
3782 janus_streaming_mountpoint *mp = g_hash_table_lookup(mountpoints,
3783 string_ids ? (gpointer)id_value_str : (gpointer)&id_value);
3784 if(mp == NULL) {
3785 janus_mutex_unlock(&mountpoints_mutex);
3786 JANUS_LOG(LOG_VERB, "No such mountpoint/stream %s\n", id_value_str);
3787 error_code = JANUS_STREAMING_ERROR_NO_SUCH_MOUNTPOINT;
3788 g_snprintf(error_cause, 512, "No such mountpoint/stream %s", id_value_str);
3789 goto prepare_response;
3790 }
3791 janus_refcount_increase(&mp->ref);
3792 /* A secret may be required for this action */
3793 JANUS_CHECK_SECRET(mp->secret, root, "secret", error_code, error_cause,
3794 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT, JANUS_STREAMING_ERROR_UNAUTHORIZED);
3795 if(error_code != 0) {
3796 janus_refcount_decrease(&mp->ref);
3797 janus_mutex_unlock(&mountpoints_mutex);
3798 goto prepare_response;
3799 }
3800 JANUS_LOG(LOG_VERB, "Request to unmount mountpoint/stream %s\n", id_value_str);
3801 /* Remove mountpoint from the hashtable: this will get it destroyed eventually */
3802 g_hash_table_remove(mountpoints,
3803 string_ids ? (gpointer)id_value_str : (gpointer)&id_value);
3804 /* FIXME Should we kick the current viewers as well? */
3805 janus_mutex_lock(&mp->mutex);
3806 GList *viewer = g_list_first(mp->viewers);
3807 /* Prepare JSON event */
3808 json_t *event = json_object();
3809 json_object_set_new(event, "streaming", json_string("event"));
3810 json_t *result = json_object();
3811 json_object_set_new(result, "status", json_string("stopped"));
3812 json_object_set_new(event, "result", result);
3813 while(viewer) {
3814 janus_streaming_session *s = (janus_streaming_session *)viewer->data;
3815 if(s == NULL) {
3816 mp->viewers = g_list_remove_all(mp->viewers, s);
3817 viewer = g_list_first(mp->viewers);
3818 continue;
3819 }
3820 janus_mutex_lock(&session->mutex);
3821 if(s->mountpoint != mp) {
3822 mp->viewers = g_list_remove_all(mp->viewers, s);
3823 viewer = g_list_first(mp->viewers);
3824 janus_mutex_unlock(&session->mutex);
3825 continue;
3826 }
3827 g_atomic_int_set(&s->stopping, 1);
3828 g_atomic_int_set(&s->started, 0);
3829 g_atomic_int_set(&s->paused, 0);
3830 s->mountpoint = NULL;
3831 /* Tell the core to tear down the PeerConnection, hangup_media will do the rest */
3832 gateway->push_event(s->handle, &janus_streaming_plugin, NULL, event, NULL);
3833 gateway->close_pc(s->handle);
3834 janus_refcount_decrease(&s->ref);
3835 janus_refcount_decrease(&mp->ref);
3836 if(mp->streaming_source == janus_streaming_source_rtp) {
3837 /* Remove the viewer from the helper threads too, if any */
3838 if(mp->helper_threads > 0) {
3839 GList *l = mp->threads;
3840 while(l) {
3841 janus_streaming_helper *ht = (janus_streaming_helper *)l->data;
3842 janus_mutex_lock(&ht->mutex);
3843 if(g_list_find(ht->viewers, s) != NULL) {
3844 ht->num_viewers--;
3845 ht->viewers = g_list_remove_all(ht->viewers, s);
3846 janus_mutex_unlock(&ht->mutex);
3847 JANUS_LOG(LOG_VERB, "Removing viewer from helper thread #%d (destroy)\n", ht->id);
3848 break;
3849 }
3850 janus_mutex_unlock(&ht->mutex);
3851 l = l->next;
3852 }
3853 }
3854 }
3855 mp->viewers = g_list_remove_all(mp->viewers, s);
3856 viewer = g_list_first(mp->viewers);
3857 janus_mutex_unlock(&session->mutex);
3858 }
3859 json_decref(event);
3860 janus_mutex_unlock(&mp->mutex);
3861 if(save) {
3862 /* This change is permanent: save to the configuration file too
3863 * FIXME: We should check if anything fails... */
3864 JANUS_LOG(LOG_VERB, "Destroying mountpoint %s (%s) permanently in config file\n", mp->id_str, mp->name);
3865 janus_mutex_lock(&config_mutex);
3866 /* The category to remove is the mountpoint name */
3867 janus_config_remove(config, NULL, mp->name);
3868 /* Save modified configuration */
3869 if(janus_config_save(config, config_folder, JANUS_STREAMING_PACKAGE) < 0)
3870 save = FALSE; /* This will notify the user the mountpoint is not permanent */
3871 janus_mutex_unlock(&config_mutex);
3872 }
3873 janus_refcount_decrease(&mp->ref);
3874 /* Also notify event handlers */
3875 if(notify_events && gateway->events_is_enabled()) {
3876 json_t *info = json_object();
3877 json_object_set_new(info, "event", json_string("destroyed"));
3878 json_object_set_new(info, "id", string_ids ? json_string(id_value_str) : json_integer(id_value));
3879 gateway->notify_event(&janus_streaming_plugin, session ? session->handle : NULL, info);
3880 }
3881 janus_mutex_unlock(&mountpoints_mutex);
3882 /* Send info back */
3883 response = json_object();
3884 json_object_set_new(response, "streaming", json_string("destroyed"));
3885 json_object_set_new(response, "destroyed", string_ids ? json_string(id_value_str) : json_integer(id_value));
3886 goto prepare_response;
3887 } else if(!strcasecmp(request_text, "recording")) {
3888 /* We can start/stop recording a live, RTP-based stream */
3889 JANUS_VALIDATE_JSON_OBJECT(root, recording_parameters,
3890 error_code, error_cause, TRUE,
3891 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
3892 if(error_code != 0)
3893 goto prepare_response;
3894 json_t *action = json_object_get(root, "action");
3895 const char *action_text = json_string_value(action);
3896 if(strcasecmp(action_text, "start") && strcasecmp(action_text, "stop")) {
3897 JANUS_LOG(LOG_ERR, "Invalid action (should be start|stop)\n");
3898 error_code = JANUS_STREAMING_ERROR_INVALID_ELEMENT;
3899 g_snprintf(error_cause, 512, "Invalid action (should be start|stop)");
3900 goto prepare_response;
3901 }
3902 if(!string_ids) {
3903 JANUS_VALIDATE_JSON_OBJECT(root, id_parameters,
3904 error_code, error_cause, TRUE,
3905 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
3906 } else {
3907 JANUS_VALIDATE_JSON_OBJECT(root, idstr_parameters,
3908 error_code, error_cause, TRUE,
3909 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
3910 }
3911 if(error_code != 0)
3912 goto prepare_response;
3913 json_t *id = json_object_get(root, "id");
3914 guint64 id_value = 0;
3915 char id_num[30], *id_value_str = NULL;
3916 if(!string_ids) {
3917 id_value = json_integer_value(id);
3918 g_snprintf(id_num, sizeof(id_num), "%"SCNu64, id_value);
3919 id_value_str = id_num;
3920 } else {
3921 id_value_str = (char *)json_string_value(id);
3922 }
3923 janus_mutex_lock(&mountpoints_mutex);
3924 janus_streaming_mountpoint *mp = g_hash_table_lookup(mountpoints,
3925 string_ids ? (gpointer)id_value_str : (gpointer)&id_value);
3926 if(mp == NULL) {
3927 janus_mutex_unlock(&mountpoints_mutex);
3928 JANUS_LOG(LOG_VERB, "No such mountpoint/stream %s\n", id_value_str);
3929 error_code = JANUS_STREAMING_ERROR_NO_SUCH_MOUNTPOINT;
3930 g_snprintf(error_cause, 512, "No such mountpoint/stream %s", id_value_str);
3931 goto prepare_response;
3932 }
3933 janus_refcount_increase(&mp->ref);
3934 if(mp->streaming_type != janus_streaming_type_live || mp->streaming_source != janus_streaming_source_rtp) {
3935 janus_refcount_decrease(&mp->ref);
3936 janus_mutex_unlock(&mountpoints_mutex);
3937 JANUS_LOG(LOG_ERR, "Recording is only available on RTP-based live streams\n");
3938 error_code = JANUS_STREAMING_ERROR_INVALID_REQUEST;
3939 g_snprintf(error_cause, 512, "Recording is only available on RTP-based live streams");
3940 goto prepare_response;
3941 }
3942 /* A secret may be required for this action */
3943 JANUS_CHECK_SECRET(mp->secret, root, "secret", error_code, error_cause,
3944 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT, JANUS_STREAMING_ERROR_UNAUTHORIZED);
3945 if(error_code != 0) {
3946 janus_refcount_decrease(&mp->ref);
3947 janus_mutex_unlock(&mountpoints_mutex);
3948 goto prepare_response;
3949 }
3950 janus_streaming_rtp_source *source = mp->source;
3951 if(!strcasecmp(action_text, "start")) {
3952 /* Start a recording for audio and/or video */
3953 JANUS_VALIDATE_JSON_OBJECT(root, recording_start_parameters,
3954 error_code, error_cause, TRUE,
3955 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
3956 if(error_code != 0) {
3957 janus_refcount_decrease(&mp->ref);
3958 janus_mutex_unlock(&mountpoints_mutex);
3959 goto prepare_response;
3960 }
3961 json_t *audio = json_object_get(root, "audio");
3962 json_t *video = json_object_get(root, "video");
3963 json_t *data = json_object_get(root, "data");
3964 janus_recorder *arc = NULL, *vrc = NULL, *drc = NULL;
3965 if((audio && source->arc) || (video && source->vrc) || (data && source->drc)) {
3966 janus_refcount_decrease(&mp->ref);
3967 janus_mutex_unlock(&mountpoints_mutex);
3968 JANUS_LOG(LOG_ERR, "Recording for audio, video and/or data already started for this stream\n");
3969 error_code = JANUS_STREAMING_ERROR_INVALID_REQUEST;
3970 g_snprintf(error_cause, 512, "Recording for audio, video and/or data already started for this stream");
3971 goto prepare_response;
3972 }
3973 if(!audio && !video && !data) {
3974 janus_refcount_decrease(&mp->ref);
3975 janus_mutex_unlock(&mountpoints_mutex);
3976 JANUS_LOG(LOG_ERR, "Missing audio, video and/or data\n");
3977 error_code = JANUS_STREAMING_ERROR_INVALID_REQUEST;
3978 g_snprintf(error_cause, 512, "Missing audio, video and/or data");
3979 goto prepare_response;
3980 }
3981 if(audio) {
3982 const char *codec = NULL;
3983 if (!mp->codecs.audio_rtpmap)
3984 JANUS_LOG(LOG_ERR, "[%s] Audio RTP map is uninitialized\n", mp->name);
3985 else if(strstr(mp->codecs.audio_rtpmap, "opus") || strstr(mp->codecs.audio_rtpmap, "OPUS"))
3986 codec = "opus";
3987 else if(strstr(mp->codecs.audio_rtpmap, "pcma") || strstr(mp->codecs.audio_rtpmap, "PCMA"))
3988 codec = "pcma";
3989 else if(strstr(mp->codecs.audio_rtpmap, "pcmu") || strstr(mp->codecs.audio_rtpmap, "PCMU"))
3990 codec = "pcmu";
3991 else if(strstr(mp->codecs.audio_rtpmap, "g722") || strstr(mp->codecs.audio_rtpmap, "G722"))
3992 codec = "g722";
3993 const char *audiofile = json_string_value(audio);
3994 arc = janus_recorder_create(NULL, codec, (char *)audiofile);
3995 if(arc == NULL) {
3996 JANUS_LOG(LOG_ERR, "[%s] Error starting recorder for audio\n", mp->name);
3997 janus_refcount_decrease(&mp->ref);
3998 janus_mutex_unlock(&mountpoints_mutex);
3999 error_code = JANUS_STREAMING_ERROR_CANT_RECORD;
4000 g_snprintf(error_cause, 512, "Error starting recorder for audio");
4001 goto prepare_response;
4002 }
4003 /* If media is encrypted, mark it in the recording */
4004 if(source->e2ee)
4005 janus_recorder_encrypted(arc);
4006 JANUS_LOG(LOG_INFO, "[%s] Audio recording started\n", mp->name);
4007 }
4008 if(video) {
4009 const char *codec = NULL;
4010 if (!mp->codecs.video_rtpmap)
4011 JANUS_LOG(LOG_ERR, "[%s] Video RTP map is uninitialized\n", mp->name);
4012 else if(strstr(mp->codecs.video_rtpmap, "vp8") || strstr(mp->codecs.video_rtpmap, "VP8"))
4013 codec = "vp8";
4014 else if(strstr(mp->codecs.video_rtpmap, "vp9") || strstr(mp->codecs.video_rtpmap, "VP9"))
4015 codec = "vp9";
4016 else if(strstr(mp->codecs.video_rtpmap, "h264") || strstr(mp->codecs.video_rtpmap, "H264"))
4017 codec = "h264";
4018 else if(strstr(mp->codecs.video_rtpmap, "av1") || strstr(mp->codecs.video_rtpmap, "AV1"))
4019 codec = "av1";
4020 else if(strstr(mp->codecs.video_rtpmap, "h265") || strstr(mp->codecs.video_rtpmap, "H265"))
4021 codec = "h265";
4022 const char *videofile = json_string_value(video);
4023 vrc = janus_recorder_create(NULL, codec, (char *)videofile);
4024 if(vrc == NULL) {
4025 if(arc != NULL) {
4026 janus_recorder_close(arc);
4027 janus_recorder_destroy(arc);
4028 arc = NULL;
4029 }
4030 JANUS_LOG(LOG_ERR, "[%s] Error starting recorder for video\n", mp->name);
4031 janus_refcount_decrease(&mp->ref);
4032 janus_mutex_unlock(&mountpoints_mutex);
4033 error_code = JANUS_STREAMING_ERROR_CANT_RECORD;
4034 g_snprintf(error_cause, 512, "Error starting recorder for video");
4035 goto prepare_response;
4036 }
4037 /* If media is encrypted, mark it in the recording */
4038 if(source->e2ee)
4039 janus_recorder_encrypted(vrc);
4040 JANUS_LOG(LOG_INFO, "[%s] Video recording started\n", mp->name);
4041 }
4042 if(data) {
4043 const char *datafile = json_string_value(data);
4044 drc = janus_recorder_create(NULL, "text", (char *)datafile);
4045 if(drc == NULL) {
4046 if(arc != NULL) {
4047 janus_recorder_close(arc);
4048 janus_recorder_destroy(arc);
4049 arc = NULL;
4050 }
4051 if(vrc != NULL) {
4052 janus_recorder_close(vrc);
4053 janus_recorder_destroy(vrc);
4054 vrc = NULL;
4055 }
4056 JANUS_LOG(LOG_ERR, "[%s] Error starting recorder for data\n", mp->name);
4057 janus_refcount_decrease(&mp->ref);
4058 janus_mutex_unlock(&mountpoints_mutex);
4059 error_code = JANUS_STREAMING_ERROR_CANT_RECORD;
4060 g_snprintf(error_cause, 512, "Error starting recorder for data");
4061 goto prepare_response;
4062 }
4063 JANUS_LOG(LOG_INFO, "[%s] Data recording started\n", mp->name);
4064 }
4065 if(arc != NULL)
4066 source->arc = arc;
4067 if(vrc != NULL)
4068 source->vrc = vrc;
4069 if(drc != NULL)
4070 source->drc = drc;
4071 janus_refcount_decrease(&mp->ref);
4072 janus_mutex_unlock(&mountpoints_mutex);
4073 /* Send a success response back */
4074 response = json_object();
4075 json_object_set_new(response, "streaming", json_string("ok"));
4076 goto prepare_response;
4077 } else if(!strcasecmp(action_text, "stop")) {
4078 /* Stop the recording */
4079 JANUS_VALIDATE_JSON_OBJECT(root, recording_stop_parameters,
4080 error_code, error_cause, TRUE,
4081 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
4082 if(error_code != 0) {
4083 janus_mutex_unlock(&mountpoints_mutex);
4084 goto prepare_response;
4085 }
4086 json_t *audio = json_object_get(root, "audio");
4087 json_t *video = json_object_get(root, "video");
4088 json_t *data = json_object_get(root, "data");
4089 if(!audio && !video) {
4090 janus_mutex_unlock(&mountpoints_mutex);
4091 JANUS_LOG(LOG_ERR, "Missing audio and/or video\n");
4092 error_code = JANUS_STREAMING_ERROR_INVALID_REQUEST;
4093 g_snprintf(error_cause, 512, "Missing audio and/or video");
4094 goto prepare_response;
4095 }
4096 janus_mutex_lock(&source->rec_mutex);
4097 if(audio && json_is_true(audio) && source->arc) {
4098 /* Close the audio recording */
4099 janus_recorder_close(source->arc);
4100 JANUS_LOG(LOG_INFO, "[%s] Closed audio recording %s\n", mp->name, source->arc->filename ? source->arc->filename : "??");
4101 janus_recorder *tmp = source->arc;
4102 source->arc = NULL;
4103 janus_recorder_destroy(tmp);
4104 }
4105 if(video && json_is_true(video) && source->vrc) {
4106 /* Close the video recording */
4107 janus_recorder_close(source->vrc);
4108 JANUS_LOG(LOG_INFO, "[%s] Closed video recording %s\n", mp->name, source->vrc->filename ? source->vrc->filename : "??");
4109 janus_recorder *tmp = source->vrc;
4110 source->vrc = NULL;
4111 janus_recorder_destroy(tmp);
4112 }
4113 if(data && json_is_true(data) && source->drc) {
4114 /* Close the data recording */
4115 janus_recorder_close(source->drc);
4116 JANUS_LOG(LOG_INFO, "[%s] Closed data recording %s\n", mp->name, source->drc->filename ? source->drc->filename : "??");
4117 janus_recorder *tmp = source->drc;
4118 source->drc = NULL;
4119 janus_recorder_destroy(tmp);
4120 }
4121 janus_mutex_unlock(&source->rec_mutex);
4122 janus_refcount_decrease(&mp->ref);
4123 janus_mutex_unlock(&mountpoints_mutex);
4124 /* Send a success response back */
4125 response = json_object();
4126 json_object_set_new(response, "streaming", json_string("ok"));
4127 goto prepare_response;
4128 }
4129 } else if(!strcasecmp(request_text, "enable") || !strcasecmp(request_text, "disable")) {
4130 /* A request to enable/disable a mountpoint */
4131 if(!string_ids) {
4132 JANUS_VALIDATE_JSON_OBJECT(root, id_parameters,
4133 error_code, error_cause, TRUE,
4134 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
4135 } else {
4136 JANUS_VALIDATE_JSON_OBJECT(root, idstr_parameters,
4137 error_code, error_cause, TRUE,
4138 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
4139 }
4140 if(error_code != 0)
4141 goto prepare_response;
4142 json_t *id = json_object_get(root, "id");
4143 guint64 id_value = 0;
4144 char id_num[30], *id_value_str = NULL;
4145 if(!string_ids) {
4146 id_value = json_integer_value(id);
4147 g_snprintf(id_num, sizeof(id_num), "%"SCNu64, id_value);
4148 id_value_str = id_num;
4149 } else {
4150 id_value_str = (char *)json_string_value(id);
4151 }
4152 janus_mutex_lock(&mountpoints_mutex);
4153 janus_streaming_mountpoint *mp = g_hash_table_lookup(mountpoints,
4154 string_ids ? (gpointer)id_value_str : (gpointer)&id_value);
4155 if(mp == NULL) {
4156 janus_mutex_unlock(&mountpoints_mutex);
4157 JANUS_LOG(LOG_VERB, "No such mountpoint/stream %s\n", id_value_str);
4158 error_code = JANUS_STREAMING_ERROR_NO_SUCH_MOUNTPOINT;
4159 g_snprintf(error_cause, 512, "No such mountpoint/stream %s", id_value_str);
4160 goto prepare_response;
4161 }
4162 janus_refcount_increase(&mp->ref);
4163 /* A secret may be required for this action */
4164 JANUS_CHECK_SECRET(mp->secret, root, "secret", error_code, error_cause,
4165 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT, JANUS_STREAMING_ERROR_UNAUTHORIZED);
4166 if(error_code != 0) {
4167 janus_refcount_decrease(&mp->ref);
4168 janus_mutex_unlock(&mountpoints_mutex);
4169 goto prepare_response;
4170 }
4171 if(!strcasecmp(request_text, "enable")) {
4172 /* Enable a previously disabled mountpoint */
4173 JANUS_LOG(LOG_INFO, "[%s] Stream enabled\n", mp->name);
4174 mp->enabled = TRUE;
4175 /* FIXME: Should we notify the viewers, or is this up to the controller application? */
4176 } else {
4177 /* Disable a previously enabled mountpoint */
4178 JANUS_VALIDATE_JSON_OBJECT(root, disable_parameters,
4179 error_code, error_cause, TRUE,
4180 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
4181 if(error_code != 0) {
4182 janus_refcount_decrease(&mp->ref);
4183 janus_mutex_unlock(&mountpoints_mutex);
4184 goto prepare_response;
4185 }
4186 mp->enabled = FALSE;
4187 gboolean stop_recording = TRUE;
4188 json_t *stop_rec = json_object_get(root, "stop_recording");
4189 if (stop_rec) {
4190 stop_recording = json_is_true(stop_rec);
4191 }
4192 JANUS_LOG(LOG_INFO, "[%s] Stream disabled (stop_recording=%s)\n", mp->name, stop_recording ? "yes" : "no");
4193 /* Any recording to close? */
4194 if(mp->streaming_source == janus_streaming_source_rtp && stop_recording) {
4195 janus_streaming_rtp_source *source = mp->source;
4196 janus_mutex_lock(&source->rec_mutex);
4197 if(source->arc) {
4198 janus_recorder_close(source->arc);
4199 JANUS_LOG(LOG_INFO, "[%s] Closed audio recording %s\n", mp->name, source->arc->filename ? source->arc->filename : "??");
4200 janus_recorder *tmp = source->arc;
4201 source->arc = NULL;
4202 janus_recorder_destroy(tmp);
4203 }
4204 if(source->vrc) {
4205 janus_recorder_close(source->vrc);
4206 JANUS_LOG(LOG_INFO, "[%s] Closed video recording %s\n", mp->name, source->vrc->filename ? source->vrc->filename : "??");
4207 janus_recorder *tmp = source->vrc;
4208 source->vrc = NULL;
4209 janus_recorder_destroy(tmp);
4210 }
4211 if(source->drc) {
4212 janus_recorder_close(source->drc);
4213 JANUS_LOG(LOG_INFO, "[%s] Closed data recording %s\n", mp->name, source->drc->filename ? source->drc->filename : "??");
4214 janus_recorder *tmp = source->drc;
4215 source->drc = NULL;
4216 janus_recorder_destroy(tmp);
4217 }
4218 janus_mutex_unlock(&source->rec_mutex);
4219 }
4220 /* FIXME: Should we notify the viewers, or is this up to the controller application? */
4221 }
4222 janus_refcount_decrease(&mp->ref);
4223 janus_mutex_unlock(&mountpoints_mutex);
4224 /* Send a success response back */
4225 response = json_object();
4226 json_object_set_new(response, "streaming", json_string("ok"));
4227 goto prepare_response;
4228 } else {
4229 /* Not a request we recognize, don't do anything */
4230 return NULL;
4231 }
4232
4233 prepare_response:
4234 {
4235 if(ifas) {
4236 freeifaddrs(ifas);
4237 }
4238
4239 if(error_code == 0 && !response) {
4240 error_code = JANUS_STREAMING_ERROR_UNKNOWN_ERROR;
4241 g_snprintf(error_cause, 512, "Invalid response");
4242 }
4243 if(error_code != 0) {
4244 /* Prepare JSON error event */
4245 response = json_object();
4246 json_object_set_new(response, "streaming", json_string("event"));
4247 json_object_set_new(response, "error_code", json_integer(error_code));
4248 json_object_set_new(response, "error", json_string(error_cause));
4249 }
4250 return response;
4251 }
4252
4253 }
4254
4255 struct janus_plugin_result *janus_streaming_handle_message(janus_plugin_session *handle, char *transaction, json_t *message, json_t *jsep) {
4256 if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
4257 return janus_plugin_result_new(JANUS_PLUGIN_ERROR, g_atomic_int_get(&stopping) ? "Shutting down" : "Plugin not initialized", NULL);
4258
4259 /* Pre-parse the message */
4260 int error_code = 0;
4261 char error_cause[512];
4262 json_t *root = message;
4263 json_t *response = NULL;
4264
4265 janus_mutex_lock(&sessions_mutex);
4266 janus_streaming_session *session = janus_streaming_lookup_session(handle);
4267 if(!session) {
4268 janus_mutex_unlock(&sessions_mutex);
4269 JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
4270 error_code = JANUS_STREAMING_ERROR_UNKNOWN_ERROR;
4271 g_snprintf(error_cause, 512, "%s", "No session associated with this handle...");
4272 goto plugin_response;
4273 }
4274 /* Increase the reference counter for this session: we'll decrease it after we handle the message */
4275 janus_refcount_increase(&session->ref);
4276 janus_mutex_unlock(&sessions_mutex);
4277 if(g_atomic_int_get(&session->destroyed)) {
4278 JANUS_LOG(LOG_ERR, "Session has already been destroyed...\n");
4279 error_code = JANUS_STREAMING_ERROR_UNKNOWN_ERROR;
4280 g_snprintf(error_cause, 512, "%s", "Session has already been destroyed...");
4281 goto plugin_response;
4282 }
4283
4284 if(message == NULL) {
4285 JANUS_LOG(LOG_ERR, "No message??\n");
4286 error_code = JANUS_STREAMING_ERROR_NO_MESSAGE;
4287 g_snprintf(error_cause, 512, "%s", "No message??");
4288 goto plugin_response;
4289 }
4290 if(!json_is_object(root)) {
4291 JANUS_LOG(LOG_ERR, "JSON error: not an object\n");
4292 error_code = JANUS_STREAMING_ERROR_INVALID_JSON;
4293 g_snprintf(error_cause, 512, "JSON error: not an object");
4294 goto plugin_response;
4295 }
4296 /* Get the request first */
4297 JANUS_VALIDATE_JSON_OBJECT(root, request_parameters,
4298 error_code, error_cause, TRUE,
4299 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
4300 if(error_code != 0)
4301 goto plugin_response;
4302 json_t *request = json_object_get(root, "request");
4303 /* Some requests ('create' and 'destroy') can be handled synchronously */
4304 const char *request_text = json_string_value(request);
4305 /* We have a separate method to process synchronous requests, as those may
4306 * arrive from the Admin API as well, and so we handle them the same way */
4307 response = janus_streaming_process_synchronous_request(session, root);
4308 if(response != NULL) {
4309 /* We got a response, send it back */
4310 goto plugin_response;
4311 } else if(!strcasecmp(request_text, "watch") || !strcasecmp(request_text, "start")
4312 || !strcasecmp(request_text, "pause") || !strcasecmp(request_text, "stop")
4313 || !strcasecmp(request_text, "configure") || !strcasecmp(request_text, "switch")) {
4314 /* These messages are handled asynchronously */
4315 janus_streaming_message *msg = g_malloc(sizeof(janus_streaming_message));
4316 msg->handle = handle;
4317 msg->transaction = transaction;
4318 msg->message = root;
4319 msg->jsep = jsep;
4320
4321 g_async_queue_push(messages, msg);
4322 return janus_plugin_result_new(JANUS_PLUGIN_OK_WAIT, NULL, NULL);
4323 } else {
4324 JANUS_LOG(LOG_VERB, "Unknown request '%s'\n", request_text);
4325 error_code = JANUS_STREAMING_ERROR_INVALID_REQUEST;
4326 g_snprintf(error_cause, 512, "Unknown request '%s'", request_text);
4327 }
4328
4329 plugin_response:
4330 {
4331 if(error_code == 0 && !response) {
4332 error_code = JANUS_STREAMING_ERROR_UNKNOWN_ERROR;
4333 g_snprintf(error_cause, 512, "Invalid response");
4334 }
4335 if(error_code != 0) {
4336 /* Prepare JSON error event */
4337 json_t *event = json_object();
4338 json_object_set_new(event, "streaming", json_string("event"));
4339 json_object_set_new(event, "error_code", json_integer(error_code));
4340 json_object_set_new(event, "error", json_string(error_cause));
4341 response = event;
4342 }
4343 if(root != NULL)
4344 json_decref(root);
4345 if(jsep != NULL)
4346 json_decref(jsep);
4347 g_free(transaction);
4348
4349 if(session != NULL)
4350 janus_refcount_decrease(&session->ref);
4351 return janus_plugin_result_new(JANUS_PLUGIN_OK, NULL, response);
4352 }
4353
4354 }
4355
4356 json_t *janus_streaming_handle_admin_message(json_t *message) {
4357 /* Some requests (e.g., 'create' and 'destroy') can be handled via Admin API */
4358 int error_code = 0;
4359 char error_cause[512];
4360 json_t *response = NULL;
4361
4362 JANUS_VALIDATE_JSON_OBJECT(message, request_parameters,
4363 error_code, error_cause, TRUE,
4364 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
4365 if(error_code != 0)
4366 goto admin_response;
4367 json_t *request = json_object_get(message, "request");
4368 const char *request_text = json_string_value(request);
4369 if((response = janus_streaming_process_synchronous_request(NULL, message)) != NULL) {
4370 /* We got a response, send it back */
4371 goto admin_response;
4372 } else {
4373 JANUS_LOG(LOG_VERB, "Unknown request '%s'\n", request_text);
4374 error_code = JANUS_STREAMING_ERROR_INVALID_REQUEST;
4375 g_snprintf(error_cause, 512, "Unknown request '%s'", request_text);
4376 }
4377
4378 admin_response:
4379 {
4380 if(!response) {
4381 /* Prepare JSON error event */
4382 response = json_object();
4383 json_object_set_new(response, "streaming", json_string("event"));
4384 json_object_set_new(response, "error_code", json_integer(error_code));
4385 json_object_set_new(response, "error", json_string(error_cause));
4386 }
4387 return response;
4388 }
4389
4390 }
4391
4392 void janus_streaming_setup_media(janus_plugin_session *handle) {
4393 JANUS_LOG(LOG_INFO, "[%s-%p] WebRTC media is now available\n", JANUS_STREAMING_PACKAGE, handle);
4394 if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
4395 return;
4396 janus_mutex_lock(&sessions_mutex);
4397 janus_streaming_session *session = janus_streaming_lookup_session(handle);
4398 if(!session) {
4399 janus_mutex_unlock(&sessions_mutex);
4400 JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
4401 return;
4402 }
4403 if(g_atomic_int_get(&session->destroyed)) {
4404 janus_mutex_unlock(&sessions_mutex);
4405 return;
4406 }
4407 janus_refcount_increase(&session->ref);
4408 janus_mutex_unlock(&sessions_mutex);
4409 g_atomic_int_set(&session->hangingup, 0);
4410 /* We only start streaming towards this user when we get this event */
4411 janus_rtp_switching_context_reset(&session->context);
4412 /* If this is related to a live RTP mountpoint, any keyframe we can shoot already? */
4413 janus_streaming_mountpoint *mountpoint = session->mountpoint;
4414 if (!mountpoint) {
4415 janus_refcount_decrease(&session->ref);
4416 JANUS_LOG(LOG_ERR, "No mountpoint associated with this session...\n");
4417 return;
4418 }
4419 if(mountpoint->streaming_source == janus_streaming_source_rtp) {
4420 janus_streaming_rtp_source *source = mountpoint->source;
4421 if(source->keyframe.enabled) {
4422 JANUS_LOG(LOG_HUGE, "Any keyframe to send?\n");
4423 janus_mutex_lock(&source->keyframe.mutex);
4424 if(source->keyframe.latest_keyframe != NULL) {
4425 JANUS_LOG(LOG_HUGE, "Yep! %d packets\n", g_list_length(source->keyframe.latest_keyframe));
4426 GList *temp = source->keyframe.latest_keyframe;
4427 while(temp) {
4428 janus_streaming_relay_rtp_packet(session, temp->data);
4429 temp = temp->next;
4430 }
4431 }
4432 janus_mutex_unlock(&source->keyframe.mutex);
4433 }
4434 if(source->buffermsg) {
4435 JANUS_LOG(LOG_HUGE, "Any recent datachannel message to send?\n");
4436 janus_mutex_lock(&source->buffermsg_mutex);
4437 if(source->last_msg != NULL) {
4438 JANUS_LOG(LOG_HUGE, "Yep!\n");
4439 janus_streaming_relay_rtp_packet(session, source->last_msg);
4440 }
4441 janus_mutex_unlock(&source->buffermsg_mutex);
4442 }
4443 /* If this mountpoint has RTCP support, send a PLI */
4444 janus_streaming_rtcp_pli_send(source);
4445 }
4446 g_atomic_int_set(&session->started, 1);
4447 /* Prepare JSON event */
4448 json_t *event = json_object();
4449 json_object_set_new(event, "streaming", json_string("event"));
4450 json_t *result = json_object();
4451 json_object_set_new(result, "status", json_string("started"));
4452 json_object_set_new(event, "result", result);
4453 int ret = gateway->push_event(handle, &janus_streaming_plugin, NULL, event, NULL);
4454 JANUS_LOG(LOG_VERB, " >> Pushing event: %d (%s)\n", ret, janus_get_api_error(ret));
4455 json_decref(event);
4456 janus_refcount_decrease(&session->ref);
4457 }
4458
4459 void janus_streaming_incoming_rtp(janus_plugin_session *handle, janus_plugin_rtp *packet) {
4460 if(handle == NULL || g_atomic_int_get(&handle->stopped) || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
4461 return;
4462 /* FIXME We don't care about what the browser sends us, we're sendonly */
4463 }
4464
4465 void janus_streaming_incoming_rtcp(janus_plugin_session *handle, janus_plugin_rtcp *packet) {
4466 if(handle == NULL || g_atomic_int_get(&handle->stopped) || g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
4467 return;
4468 janus_streaming_session *session = (janus_streaming_session *)handle->plugin_handle;
4469 if(!session || g_atomic_int_get(&session->destroyed) || g_atomic_int_get(&session->stopping) ||
4470 !g_atomic_int_get(&session->started) || g_atomic_int_get(&session->paused))
4471 return;
4472 janus_streaming_mountpoint *mp = (janus_streaming_mountpoint *)session->mountpoint;
4473 if(mp->streaming_source != janus_streaming_source_rtp)
4474 return;
4475 janus_streaming_rtp_source *source = (janus_streaming_rtp_source *)mp->source;
4476 gboolean video = packet->video;
4477 char *buf = packet->buffer;
4478 uint16_t len = packet->length;
4479 if(!video && (source->audio_rtcp_fd > -1) && (source->audio_rtcp_addr.ss_family != 0)) {
4480 JANUS_LOG(LOG_HUGE, "Got audio RTCP feedback from a viewer: SSRC %"SCNu32"\n",
4481 janus_rtcp_get_sender_ssrc(buf, len));
4482 /* FIXME We don't forward RR packets, so what should we check here? */
4483 } else if(video && (source->video_rtcp_fd > -1) && (source->video_rtcp_addr.ss_family != 0)) {
4484 JANUS_LOG(LOG_HUGE, "Got video RTCP feedback from a viewer: SSRC %"SCNu32"\n",
4485 janus_rtcp_get_sender_ssrc(buf, len));
4486 /* We only relay PLI/FIR and REMB packets, but in a selective way */
4487 if(janus_rtcp_has_fir(buf, len) || janus_rtcp_has_pli(buf, len)) {
4488 /* We got a PLI/FIR, pass it along unless we just sent one */
4489 JANUS_LOG(LOG_HUGE, " -- Keyframe request\n");
4490 janus_streaming_rtcp_pli_send(source);
4491 }
4492 uint64_t bw = janus_rtcp_get_remb(buf, len);
4493 if(bw > 0) {
4494 /* Keep track of this value, if this is the lowest right now */
4495 JANUS_LOG(LOG_HUGE, " -- REMB for this PeerConnection: %"SCNu64"\n", bw);
4496 if((0 == source->lowest_bitrate) || (source->lowest_bitrate > bw))
4497 source->lowest_bitrate = bw;
4498 }
4499 }
4500 }
4501
4502 void janus_streaming_data_ready(janus_plugin_session *handle) {
4503 if(handle == NULL || g_atomic_int_get(&handle->stopped) ||
4504 g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized) || !gateway)
4505 return;
4506 /* Data channels are writable: we shouldn't send any datachannel message before this happens */
4507 janus_streaming_session *session = (janus_streaming_session *)handle->plugin_handle;
4508 if(!session || g_atomic_int_get(&session->destroyed) || g_atomic_int_get(&session->hangingup))
4509 return;
4510 if(g_atomic_int_compare_and_exchange(&session->dataready, 0, 1)) {
4511 JANUS_LOG(LOG_INFO, "[%s-%p] Data channel available\n", JANUS_STREAMING_PACKAGE, handle);
4512 }
4513 }
4514
4515 void janus_streaming_hangup_media(janus_plugin_session *handle) {
4516 JANUS_LOG(LOG_INFO, "[%s-%p] No WebRTC media anymore\n", JANUS_STREAMING_PACKAGE, handle);
4517 janus_mutex_lock(&sessions_mutex);
4518 janus_streaming_hangup_media_internal(handle);
4519 janus_mutex_unlock(&sessions_mutex);
4520 }
4521
4522 static void janus_streaming_hangup_media_internal(janus_plugin_session *handle) {
4523 if(g_atomic_int_get(&stopping) || !g_atomic_int_get(&initialized))
4524 return;
4525 janus_streaming_session *session = janus_streaming_lookup_session(handle);
4526 if(!session) {
4527 JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
4528 return;
4529 }
4530 if(g_atomic_int_get(&session->destroyed))
4531 return;
4532 if(!g_atomic_int_compare_and_exchange(&session->hangingup, 0, 1))
4533 return;
4534 g_atomic_int_set(&session->dataready, 0);
4535 g_atomic_int_set(&session->stopping, 1);
4536 g_atomic_int_set(&session->started, 0);
4537 g_atomic_int_set(&session->paused, 0);
4538 session->audio_pt = -1;
4539 session->video_pt = -1;
4540 janus_rtp_switching_context_reset(&session->context);
4541 janus_rtp_simulcasting_context_reset(&session->sim_context);
4542 janus_vp8_simulcast_context_reset(&session->vp8_context);
4543 session->spatial_layer = -1;
4544 session->target_spatial_layer = 2; /* FIXME Chrome sends 0, 1 and 2 (if using EnabledByFlag_3SL3TL) */
4545 session->last_spatial_layer[0] = 0;
4546 session->last_spatial_layer[1] = 0;
4547 session->last_spatial_layer[2] = 0;
4548 session->temporal_layer = -1;
4549 session->target_temporal_layer = 2; /* FIXME Chrome sends 0, 1 and 2 */
4550 session->e2ee = FALSE;
4551 janus_mutex_lock(&session->mutex);
4552 janus_streaming_mountpoint *mp = session->mountpoint;
4553 session->mountpoint = NULL;
4554 janus_mutex_unlock(&session->mutex);
4555 if(mp) {
4556 janus_mutex_lock(&mp->mutex);
4557 JANUS_LOG(LOG_VERB, " -- Removing the session from the mountpoint viewers\n");
4558 if(g_list_find(mp->viewers, session) != NULL) {
4559 JANUS_LOG(LOG_VERB, " -- -- Found!\n");
4560 janus_refcount_decrease(&mp->ref);
4561 janus_refcount_decrease(&session->ref);
4562 }
4563 mp->viewers = g_list_remove_all(mp->viewers, session);
4564 if(mp->streaming_source == janus_streaming_source_rtp) {
4565 /* Remove the viewer from the helper threads too, if any */
4566 if(mp->helper_threads > 0) {
4567 GList *l = mp->threads;
4568 while(l) {
4569 janus_streaming_helper *ht = (janus_streaming_helper *)l->data;
4570 janus_mutex_lock(&ht->mutex);
4571 if(g_list_find(ht->viewers, session) != NULL) {
4572 ht->num_viewers--;
4573 ht->viewers = g_list_remove_all(ht->viewers, session);
4574 janus_mutex_unlock(&ht->mutex);
4575 JANUS_LOG(LOG_VERB, "Removing viewer from helper thread #%d\n", ht->id);
4576 break;
4577 }
4578 janus_mutex_unlock(&ht->mutex);
4579 l = l->next;
4580 }
4581 }
4582 }
4583 janus_mutex_unlock(&mp->mutex);
4584 }
4585 g_atomic_int_set(&session->hangingup, 0);
4586 }
4587
4588 /* Thread to handle incoming messages */
4589 static void *janus_streaming_handler(void *data) {
4590 JANUS_LOG(LOG_VERB, "Joining Streaming handler thread\n");
4591 janus_streaming_message *msg = NULL;
4592 int error_code = 0;
4593 char error_cause[512];
4594 json_t *root = NULL;
4595 while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) {
4596 msg = g_async_queue_pop(messages);
4597 if(msg == &exit_message)
4598 break;
4599 if(msg->handle == NULL) {
4600 janus_streaming_message_free(msg);
4601 continue;
4602 }
4603 janus_mutex_lock(&sessions_mutex);
4604 janus_streaming_session *session = janus_streaming_lookup_session(msg->handle);
4605 if(!session) {
4606 janus_mutex_unlock(&sessions_mutex);
4607 JANUS_LOG(LOG_ERR, "No session associated with this handle...\n");
4608 janus_streaming_message_free(msg);
4609 continue;
4610 }
4611 if(g_atomic_int_get(&session->destroyed)) {
4612 janus_mutex_unlock(&sessions_mutex);
4613 janus_streaming_message_free(msg);
4614 continue;
4615 }
4616 janus_mutex_unlock(&sessions_mutex);
4617 /* Handle request */
4618 error_code = 0;
4619 root = NULL;
4620 if(msg->message == NULL) {
4621 JANUS_LOG(LOG_ERR, "No message??\n");
4622 error_code = JANUS_STREAMING_ERROR_NO_MESSAGE;
4623 g_snprintf(error_cause, 512, "%s", "No message??");
4624 goto error;
4625 }
4626 root = msg->message;
4627 /* Get the request first */
4628 JANUS_VALIDATE_JSON_OBJECT(root, request_parameters,
4629 error_code, error_cause, TRUE,
4630 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
4631 if(error_code != 0)
4632 goto error;
4633 json_t *request = json_object_get(root, "request");
4634 const char *request_text = json_string_value(request);
4635 json_t *result = NULL;
4636 const char *sdp_type = NULL;
4637 char *sdp = NULL;
4638 gboolean do_restart = FALSE;
4639 /* All these requests can only be handled asynchronously */
4640 if(!strcasecmp(request_text, "watch")) {
4641 JANUS_VALIDATE_JSON_OBJECT(root, watch_parameters,
4642 error_code, error_cause, TRUE,
4643 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
4644 if(error_code != 0)
4645 goto error;
4646 if(!string_ids) {
4647 JANUS_VALIDATE_JSON_OBJECT(root, id_parameters,
4648 error_code, error_cause, TRUE,
4649 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
4650 } else {
4651 JANUS_VALIDATE_JSON_OBJECT(root, idstr_parameters,
4652 error_code, error_cause, TRUE,
4653 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
4654 }
4655 if(error_code != 0)
4656 goto error;
4657 json_t *id = json_object_get(root, "id");
4658 guint64 id_value = 0;
4659 char id_num[30], *id_value_str = NULL;
4660 if(!string_ids) {
4661 id_value = json_integer_value(id);
4662 g_snprintf(id_num, sizeof(id_num), "%"SCNu64, id_value);
4663 id_value_str = id_num;
4664 } else {
4665 id_value_str = (char *)json_string_value(id);
4666 }
4667 json_t *offer_audio = json_object_get(root, "offer_audio");
4668 json_t *offer_video = json_object_get(root, "offer_video");
4669 json_t *offer_data = json_object_get(root, "offer_data");
4670 json_t *restart = json_object_get(root, "restart");
4671 do_restart = restart ? json_is_true(restart) : FALSE;
4672 janus_mutex_lock(&mountpoints_mutex);
4673 janus_streaming_mountpoint *mp = g_hash_table_lookup(mountpoints,
4674 string_ids ? (gpointer)id_value_str : (gpointer)&id_value);
4675 if(mp == NULL) {
4676 janus_mutex_unlock(&mountpoints_mutex);
4677 JANUS_LOG(LOG_VERB, "No such mountpoint/stream %s\n", id_value_str);
4678 error_code = JANUS_STREAMING_ERROR_NO_SUCH_MOUNTPOINT;
4679 g_snprintf(error_cause, 512, "No such mountpoint/stream %s", id_value_str);
4680 goto error;
4681 }
4682 janus_refcount_increase(&mp->ref);
4683 /* A secret may be required for this action */
4684 JANUS_CHECK_SECRET(mp->pin, root, "pin", error_code, error_cause,
4685 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT, JANUS_STREAMING_ERROR_UNAUTHORIZED);
4686 if(error_code != 0) {
4687 janus_refcount_decrease(&mp->ref);
4688 janus_mutex_unlock(&mountpoints_mutex);
4689 goto error;
4690 }
4691 janus_mutex_lock(&mp->mutex);
4692 janus_mutex_lock(&session->mutex);
4693 janus_mutex_unlock(&mountpoints_mutex);
4694 /* Check if this is a new viewer, or if an update is taking place (i.e., ICE restart) */
4695 if(do_restart) {
4696 /* User asked for an ICE restart: provide a new offer */
4697 if(!g_atomic_int_compare_and_exchange(&session->renegotiating, 0, 1)) {
4698 /* Already triggered a renegotiation, and still waiting for an answer */
4699 janus_mutex_unlock(&session->mutex);
4700 janus_mutex_unlock(&mp->mutex);
4701 JANUS_LOG(LOG_ERR, "Already renegotiating mountpoint %s\n", session->mountpoint->id_str);
4702 error_code = JANUS_STREAMING_ERROR_INVALID_STATE;
4703 g_snprintf(error_cause, 512, "Already renegotiating mountpoint %s", session->mountpoint->id_str);
4704 janus_refcount_decrease(&mp->ref);
4705 goto error;
4706 }
4707 janus_refcount_decrease(&mp->ref);
4708 JANUS_LOG(LOG_VERB, "Request to perform an ICE restart on mountpoint/stream %s subscription\n", id_value_str);
4709 session->sdp_version++; /* This needs to be increased when it changes */
4710 goto done;
4711 }
4712 if(session->mountpoint != NULL) {
4713 if(session->mountpoint != mp) {
4714 /* Already watching something else */
4715 janus_refcount_decrease(&mp->ref);
4716 JANUS_LOG(LOG_ERR, "Already watching mountpoint %s\n", session->mountpoint->id_str);
4717 error_code = JANUS_STREAMING_ERROR_INVALID_STATE;
4718 g_snprintf(error_cause, 512, "Already watching mountpoint %s", session->mountpoint->id_str);
4719 janus_mutex_unlock(&session->mutex);
4720 janus_mutex_unlock(&mp->mutex);
4721 goto error;
4722 } else {
4723 /* Make sure it's not an API error */
4724 if(!g_atomic_int_get(&session->started)) {
4725 /* Can't be a renegotiation, PeerConnection isn't up yet */
4726 JANUS_LOG(LOG_ERR, "Already watching mountpoint %s\n", session->mountpoint->id_str);
4727 error_code = JANUS_STREAMING_ERROR_INVALID_STATE;
4728 g_snprintf(error_cause, 512, "Already watching mountpoint %s", session->mountpoint->id_str);
4729 janus_refcount_decrease(&mp->ref);
4730 janus_mutex_unlock(&session->mutex);
4731 janus_mutex_unlock(&mp->mutex);
4732 goto error;
4733 }
4734 if(!g_atomic_int_compare_and_exchange(&session->renegotiating, 0, 1)) {
4735 /* Already triggered a renegotiation, and still waiting for an answer */
4736 JANUS_LOG(LOG_ERR, "Already renegotiating mountpoint %s\n", session->mountpoint->id_str);
4737 error_code = JANUS_STREAMING_ERROR_INVALID_STATE;
4738 g_snprintf(error_cause, 512, "Already renegotiating mountpoint %s", session->mountpoint->id_str);
4739 janus_refcount_decrease(&mp->ref);
4740 janus_mutex_unlock(&session->mutex);
4741 janus_mutex_unlock(&mp->mutex);
4742 goto error;
4743 }
4744 /* Simple renegotiation, remove the extra uneeded reference */
4745 janus_refcount_decrease(&mp->ref);
4746 JANUS_LOG(LOG_VERB, "Request to update mountpoint/stream %s subscription (no restart)\n", id_value_str);
4747 session->sdp_version++; /* This needs to be increased when it changes */
4748 goto done;
4749 }
4750 }
4751 /* New viewer: we send an offer ourselves */
4752 JANUS_LOG(LOG_VERB, "Request to watch mountpoint/stream %s\n", id_value_str);
4753 if(session->mountpoint != NULL || g_list_find(mp->viewers, session) != NULL) {
4754 janus_mutex_unlock(&session->mutex);
4755 janus_mutex_unlock(&mp->mutex);
4756 janus_refcount_decrease(&mp->ref);
4757 JANUS_LOG(LOG_ERR, "Already watching a stream...\n");
4758 error_code = JANUS_STREAMING_ERROR_UNKNOWN_ERROR;
4759 g_snprintf(error_cause, 512, "Already watching a stream");
4760 goto error;
4761 }
4762 g_atomic_int_set(&session->stopping, 0);
4763 session->mountpoint = mp;
4764 session->sdp_version = 1; /* This needs to be increased when it changes */
4765 session->sdp_sessid = janus_get_real_time();
4766 /* Check what we should offer */
4767 session->audio = offer_audio ? json_is_true(offer_audio) : TRUE; /* True by default */
4768 if(!mp->audio)
4769 session->audio = FALSE; /* ... unless the mountpoint isn't sending any audio */
4770 session->video = offer_video ? json_is_true(offer_video) : TRUE; /* True by default */
4771 if(!mp->video)
4772 session->video = FALSE; /* ... unless the mountpoint isn't sending any video */
4773 session->data = offer_data ? json_is_true(offer_data) : TRUE; /* True by default */
4774 if(!mp->data)
4775 session->data = FALSE; /* ... unless the mountpoint isn't sending any data */
4776 if((!mp->audio || !session->audio) &&
4777 (!mp->video || !session->video) &&
4778 (!mp->data || !session->data)) {
4779 session->mountpoint = NULL;
4780 janus_mutex_unlock(&session->mutex);
4781 janus_mutex_unlock(&mp->mutex);
4782 janus_refcount_decrease(&mp->ref);
4783 JANUS_LOG(LOG_ERR, "Can't offer an SDP with no audio, video or data for this mountpoint\n");
4784 error_code = JANUS_STREAMING_ERROR_INVALID_REQUEST;
4785 g_snprintf(error_cause, 512, "Can't offer an SDP with no audio, video or data for this mountpoint");
4786 goto error;
4787 }
4788 if(mp->streaming_type == janus_streaming_type_on_demand) {
4789 GError *error = NULL;
4790 char tname[16];
4791 g_snprintf(tname, sizeof(tname), "mp %s", mp->id_str);
4792 janus_refcount_increase(&session->ref);
4793 janus_refcount_increase(&mp->ref);
4794 g_thread_try_new(tname, &janus_streaming_ondemand_thread, session, &error);
4795 if(error != NULL) {
4796 session->mountpoint = NULL;
4797 janus_mutex_unlock(&session->mutex);
4798 janus_refcount_decrease(&session->ref); /* This is for the failed thread */
4799 janus_mutex_unlock(&mp->mutex);
4800 janus_refcount_decrease(&mp->ref); /* This is for the failed thread */
4801 janus_refcount_decrease(&mp->ref);
4802 JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the on-demand thread...\n",
4803 error->code, error->message ? error->message : "??");
4804 error_code = JANUS_STREAMING_ERROR_UNKNOWN_ERROR;
4805 g_snprintf(error_cause, 512, "Got error %d (%s) trying to launch the on-demand thread",
4806 error->code, error->message ? error->message : "??");
4807 g_error_free(error);
4808 goto error;
4809 }
4810 } else if(mp->streaming_source == janus_streaming_source_rtp) {
4811 janus_streaming_rtp_source *source = (janus_streaming_rtp_source *)mp->source;
4812 if(source && source->simulcast) {
4813 JANUS_VALIDATE_JSON_OBJECT(root, simulcast_parameters,
4814 error_code, error_cause, TRUE,
4815 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
4816 if(error_code != 0) {
4817 session->mountpoint = NULL;
4818 janus_mutex_unlock(&session->mutex);
4819 janus_mutex_unlock(&mp->mutex);
4820 janus_refcount_decrease(&mp->ref);
4821 goto error;
4822 }
4823 /* In case this mountpoint is simulcasting, let's aim high by default */
4824 janus_rtp_switching_context_reset(&session->context);
4825 janus_rtp_simulcasting_context_reset(&session->sim_context);
4826 session->sim_context.substream_target = 2;
4827 session->sim_context.templayer_target = 2;
4828 janus_vp8_simulcast_context_reset(&session->vp8_context);
4829 /* Unless the request contains a target for either layer */
4830 json_t *substream = json_object_get(root, "substream");
4831 if(substream) {
4832 session->sim_context.substream_target = json_integer_value(substream);
4833 JANUS_LOG(LOG_VERB, "Setting video substream to let through (simulcast): %d (was %d)\n",
4834 session->sim_context.substream_target, session->sim_context.substream);
4835 }
4836 json_t *temporal = json_object_get(root, "temporal");
4837 if(temporal) {
4838 session->sim_context.templayer_target = json_integer_value(temporal);
4839 JANUS_LOG(LOG_VERB, "Setting video temporal layer to let through (simulcast): %d (was %d)\n",
4840 session->sim_context.templayer_target, session->sim_context.templayer);
4841 }
4842 /* Check if we need a custom fallback timer for the substream */
4843 json_t *fallback = json_object_get(root, "fallback");
4844 if(fallback) {
4845 JANUS_LOG(LOG_VERB, "Setting fallback timer (simulcast): %lld (was %"SCNu32")\n",
4846 json_integer_value(fallback) ? json_integer_value(fallback) : 250000,
4847 session->sim_context.drop_trigger ? session->sim_context.drop_trigger : 250000);
4848 session->sim_context.drop_trigger = json_integer_value(fallback);
4849 }
4850 } else if(source && source->svc) {
4851 JANUS_VALIDATE_JSON_OBJECT(root, svc_parameters,
4852 error_code, error_cause, TRUE,
4853 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
4854 if(error_code != 0) {
4855 session->mountpoint = NULL;
4856 janus_mutex_unlock(&session->mutex);
4857 janus_mutex_unlock(&mp->mutex);
4858 janus_refcount_decrease(&mp->ref);
4859 goto error;
4860 }
4861 /* In case this mountpoint is doing VP9-SVC, let's aim high by default */
4862 session->spatial_layer = -1;
4863 session->target_spatial_layer = 2; /* FIXME Chrome sends 0, 1 and 2 (if using EnabledByFlag_3SL3TL) */
4864 session->temporal_layer = -1;
4865 session->target_temporal_layer = 2; /* FIXME Chrome sends 0, 1 and 2 */
4866 /* Unless the request contains a target for either layer */
4867 json_t *spatial = json_object_get(root, "spatial_layer");
4868 if(spatial) {
4869 session->target_spatial_layer = json_integer_value(spatial);
4870 JANUS_LOG(LOG_VERB, "Setting video spatial layer to let through (SVC): %d (was %d)\n",
4871 session->target_spatial_layer, session->spatial_layer);
4872 }
4873 json_t *temporal = json_object_get(root, "temporal_layer");
4874 if(temporal) {
4875 session->target_temporal_layer = json_integer_value(temporal);
4876 JANUS_LOG(LOG_VERB, "Setting video temporal layer to let through (SVC): %d (was %d)\n",
4877 session->target_temporal_layer, session->temporal_layer);
4878 }
4879 }
4880 /* Initialize the payload types this subscriber will expect */
4881 session->audio_pt = -1;
4882 if(mp->codecs.audio_pt >= 0 && session->audio)
4883 session->audio_pt = mp->codecs.audio_pt;
4884 if(mp->codecs.video_pt >= 0 && session->video)
4885 session->video_pt = mp->codecs.video_pt;
4886 /* If this mountpoint is broadcasting end-to-end encrypted media,
4887 * add the info to the JSEP offer we'll be sending them */
4888 session->e2ee = source->e2ee;
4889 }
4890 janus_refcount_increase(&session->ref);
4891 done:
4892 /* Let's prepare an offer now, but let's also check if there's something we need to skip */
4893 sdp_type = "offer"; /* We're always going to do the offer ourselves, never answer */
4894 char sdptemp[2048];
4895 memset(sdptemp, 0, 2048);
4896 gchar buffer[512];
4897 memset(buffer, 0, 512);
4898 g_snprintf(buffer, 512,
4899 "v=0\r\no=%s %"SCNu64" %"SCNu64" IN IP4 127.0.0.1\r\n",
4900 "-", session->sdp_sessid, session->sdp_version);
4901 janus_strlcat(sdptemp, buffer, 2048);
4902 g_snprintf(buffer, 512,
4903 "s=Mountpoint %s\r\n", mp->id_str);
4904 janus_strlcat(sdptemp, buffer, 2048);
4905 janus_strlcat(sdptemp, "t=0 0\r\n", 2048);
4906 if(mp->codecs.audio_pt >= 0 && session->audio) {
4907 int pt = session->audio_pt >= 0 ? session->audio_pt : mp->codecs.audio_pt;
4908 /* Add audio line */
4909 g_snprintf(buffer, 512,
4910 "m=audio 1 RTP/SAVPF %d\r\n"
4911 "c=IN IP4 1.1.1.1\r\n", pt);
4912 janus_strlcat(sdptemp, buffer, 2048);
4913 if(mp->codecs.audio_rtpmap) {
4914 g_snprintf(buffer, 512,
4915 "a=rtpmap:%d %s\r\n",
4916 pt, mp->codecs.audio_rtpmap);
4917 janus_strlcat(sdptemp, buffer, 2048);
4918 }
4919 if(mp->codecs.audio_fmtp) {
4920 g_snprintf(buffer, 512,
4921 "a=fmtp:%d %s\r\n",
4922 pt, mp->codecs.audio_fmtp);
4923 janus_strlcat(sdptemp, buffer, 2048);
4924 }
4925 janus_strlcat(sdptemp, "a=sendonly\r\n", 2048);
4926 g_snprintf(buffer, 512, "a=extmap:%d %s\r\n", 1, JANUS_RTP_EXTMAP_MID);
4927 janus_strlcat(sdptemp, buffer, 2048);
4928 }
4929 if(mp->codecs.video_pt > 0 && session->video) {
4930 int pt = session->video_pt > 0 ? session->video_pt : mp->codecs.video_pt;
4931 /* Add video line */
4932 g_snprintf(buffer, 512,
4933 "m=video 1 RTP/SAVPF %d\r\n"
4934 "c=IN IP4 1.1.1.1\r\n", pt);
4935 janus_strlcat(sdptemp, buffer, 2048);
4936 if(mp->codecs.video_rtpmap) {
4937 g_snprintf(buffer, 512,
4938 "a=rtpmap:%d %s\r\n",
4939 pt, mp->codecs.video_rtpmap);
4940 janus_strlcat(sdptemp, buffer, 2048);
4941 }
4942 if(mp->codecs.video_fmtp) {
4943 g_snprintf(buffer, 512,
4944 "a=fmtp:%d %s\r\n",
4945 pt, mp->codecs.video_fmtp);
4946 janus_strlcat(sdptemp, buffer, 2048);
4947 }
4948 g_snprintf(buffer, 512,
4949 "a=rtcp-fb:%d nack\r\n", pt);
4950 janus_strlcat(sdptemp, buffer, 2048);
4951 g_snprintf(buffer, 512,
4952 "a=rtcp-fb:%d nack pli\r\n", pt);
4953 janus_strlcat(sdptemp, buffer, 2048);
4954 g_snprintf(buffer, 512,
4955 "a=rtcp-fb:%d goog-remb\r\n", pt);
4956 janus_strlcat(sdptemp, buffer, 2048);
4957 janus_strlcat(sdptemp, "a=sendonly\r\n", 2048);
4958 g_snprintf(buffer, 512, "a=extmap:%d %s\r\n", 1, JANUS_RTP_EXTMAP_MID);
4959 janus_strlcat(sdptemp, buffer, 2048);
4960 g_snprintf(buffer, 512, "a=extmap:%d %s\r\n", 2, JANUS_RTP_EXTMAP_ABS_SEND_TIME);
4961 janus_strlcat(sdptemp, buffer, 2048);
4962 }
4963 #ifdef HAVE_SCTP
4964 if(mp->data && session->data) {
4965 /* Add data line */
4966 g_snprintf(buffer, 512,
4967 "m=application 1 UDP/DTLS/SCTP webrtc-datachannel\r\n"
4968 "c=IN IP4 1.1.1.1\r\n"
4969 "a=sctp-port:5000\r\n");
4970 janus_strlcat(sdptemp, buffer, 2048);
4971 }
4972 #endif
4973 sdp = g_strdup(sdptemp);
4974 JANUS_LOG(LOG_VERB, "Going to %s this SDP:\n%s\n", sdp_type, sdp);
4975 result = json_object();
4976 json_object_set_new(result, "status", json_string(do_restart ? "updating" : "preparing"));
4977 /* Add the user to the list of watchers and we're done */
4978 if(g_list_find(mp->viewers, session) == NULL) {
4979 mp->viewers = g_list_append(mp->viewers, session);
4980 if(mp->streaming_source == janus_streaming_source_rtp) {
4981 /* If we're using helper threads, add the viewer to one of those */
4982 if(mp->helper_threads > 0) {
4983 int viewers = -1;
4984 janus_streaming_helper *helper = NULL;
4985 GList *l = mp->threads;
4986 while(l) {
4987 janus_streaming_helper *ht = (janus_streaming_helper *)l->data;
4988 if(viewers == -1 || (helper == NULL && ht->num_viewers == 0) || ht->num_viewers < viewers) {
4989 viewers = ht->num_viewers;
4990 helper = ht;
4991 }
4992 l = l->next;
4993 }
4994 janus_mutex_lock(&helper->mutex);
4995 helper->viewers = g_list_append(helper->viewers, session);
4996 helper->num_viewers++;
4997 janus_mutex_unlock(&helper->mutex);
4998 JANUS_LOG(LOG_VERB, "Added viewer to helper thread #%d (%d viewers)\n",
4999 helper->id, helper->num_viewers);
5000 }
5001 }
5002 }
5003 janus_mutex_unlock(&session->mutex);
5004 janus_mutex_unlock(&mp->mutex);
5005 } else if(!strcasecmp(request_text, "start")) {
5006 if(session->mountpoint == NULL) {
5007 JANUS_LOG(LOG_VERB, "Can't start: no mountpoint set\n");
5008 error_code = JANUS_STREAMING_ERROR_NO_SUCH_MOUNTPOINT;
5009 g_snprintf(error_cause, 512, "Can't start: no mountpoint set");
5010 goto error;
5011 }
5012 JANUS_LOG(LOG_VERB, "Starting the streaming\n");
5013 if(g_atomic_int_get(&session->paused) == 1) {
5014 /* We were paused: reset the sequence number in RTP packets */
5015 session->context.a_seq_reset = TRUE;
5016 session->context.v_seq_reset = TRUE;
5017 }
5018 g_atomic_int_set(&session->paused, 0);
5019 result = json_object();
5020 /* We wait for the setup_media event to start: on the other hand, it may have already arrived */
5021 json_object_set_new(result, "status", json_string(g_atomic_int_get(&session->started) ? "started" : "starting"));
5022 /* Also notify event handlers */
5023 if(notify_events && gateway->events_is_enabled()) {
5024 json_t *info = json_object();
5025 json_object_set_new(info, "status", json_string("starting"));
5026 if(session->mountpoint != NULL)
5027 json_object_set_new(info, "id", string_ids ?
5028 json_string(session->mountpoint->id_str) :json_integer(session->mountpoint->id));
5029 gateway->notify_event(&janus_streaming_plugin, session->handle, info);
5030 }
5031 } else if(!strcasecmp(request_text, "pause")) {
5032 if(session->mountpoint == NULL) {
5033 JANUS_LOG(LOG_VERB, "Can't pause: no mountpoint set\n");
5034 error_code = JANUS_STREAMING_ERROR_NO_SUCH_MOUNTPOINT;
5035 g_snprintf(error_cause, 512, "Can't start: no mountpoint set");
5036 goto error;
5037 }
5038 JANUS_LOG(LOG_VERB, "Pausing the streaming\n");
5039 g_atomic_int_set(&session->paused, 1);
5040 result = json_object();
5041 json_object_set_new(result, "status", json_string("pausing"));
5042 /* Also notify event handlers */
5043 if(notify_events && gateway->events_is_enabled()) {
5044 json_t *info = json_object();
5045 json_object_set_new(info, "status", json_string("pausing"));
5046 if(session->mountpoint != NULL)
5047 json_object_set_new(info, "id", string_ids ?
5048 json_string(session->mountpoint->id_str) : json_integer(session->mountpoint->id));
5049 gateway->notify_event(&janus_streaming_plugin, session->handle, info);
5050 }
5051 } else if(!strcasecmp(request_text, "configure")) {
5052 janus_streaming_mountpoint *mp = session->mountpoint;
5053 if(mp == NULL) {
5054 JANUS_LOG(LOG_VERB, "Can't configure: not on a mountpoint\n");
5055 error_code = JANUS_STREAMING_ERROR_NO_SUCH_MOUNTPOINT;
5056 g_snprintf(error_cause, 512, "Can't configure: not on a mountpoint");
5057 goto error;
5058 }
5059 JANUS_VALIDATE_JSON_OBJECT(root, configure_parameters,
5060 error_code, error_cause, TRUE,
5061 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
5062 json_t *audio = json_object_get(root, "audio");
5063 if(audio)
5064 session->audio = json_is_true(audio);
5065 json_t *video = json_object_get(root, "video");
5066 if(video)
5067 session->video = json_is_true(video);
5068 json_t *data = json_object_get(root, "data");
5069 if(data)
5070 session->data = json_is_true(data);
5071 if(mp->streaming_source == janus_streaming_source_rtp) {
5072 janus_streaming_rtp_source *source = (janus_streaming_rtp_source *)mp->source;
5073 if(source && source->simulcast) {
5074 /* Check if the viewer is requesting a different substream/temporal layer */
5075 json_t *substream = json_object_get(root, "substream");
5076 if(substream) {
5077 session->sim_context.substream_target = json_integer_value(substream);
5078 JANUS_LOG(LOG_VERB, "Setting video substream to let through (simulcast): %d (was %d)\n",
5079 session->sim_context.substream_target, session->sim_context.substream);
5080 if(session->sim_context.substream_target == session->sim_context.substream) {
5081 /* No need to do anything, we're already getting the right substream, so notify the viewer */
5082 json_t *event = json_object();
5083 json_object_set_new(event, "streaming", json_string("event"));
5084 json_t *result = json_object();
5085 json_object_set_new(result, "substream", json_integer(session->sim_context.substream));
5086 json_object_set_new(event, "result", result);
5087 gateway->push_event(session->handle, &janus_streaming_plugin, NULL, event, NULL);
5088 json_decref(event);
5089 } else {
5090 /* Schedule a PLI */
5091 JANUS_LOG(LOG_VERB, "We need a PLI for the simulcast context\n");
5092 g_atomic_int_set(&source->need_pli, 1);
5093 }
5094 }
5095 json_t *temporal = json_object_get(root, "temporal");
5096 if(temporal) {
5097 session->sim_context.templayer_target = json_integer_value(temporal);
5098 JANUS_LOG(LOG_VERB, "Setting video temporal layer to let through (simulcast): %d (was %d)\n",
5099 session->sim_context.templayer_target, session->sim_context.templayer);
5100 if(mp->codecs.video_codec == JANUS_VIDEOCODEC_VP8 && session->sim_context.templayer_target == session->sim_context.templayer) {
5101 /* No need to do anything, we're already getting the right temporal layer, so notify the viewer */
5102 json_t *event = json_object();
5103 json_object_set_new(event, "streaming", json_string("event"));
5104 json_t *result = json_object();
5105 json_object_set_new(result, "temporal", json_integer(session->sim_context.templayer));
5106 json_object_set_new(event, "result", result);
5107 gateway->push_event(session->handle, &janus_streaming_plugin, NULL, event, NULL);
5108 json_decref(event);
5109 }
5110 }
5111 /* Check if we need to change the fallback timer for the substream */
5112 json_t *fallback = json_object_get(root, "fallback");
5113 if(fallback) {
5114 JANUS_LOG(LOG_VERB, "Setting fallback timer (simulcast): %lld (was %"SCNu32")\n",
5115 json_integer_value(fallback) ? json_integer_value(fallback) : 250000,
5116 session->sim_context.drop_trigger ? session->sim_context.drop_trigger : 250000);
5117 session->sim_context.drop_trigger = json_integer_value(fallback);
5118 }
5119 }
5120 if(source && source->svc) {
5121 /* Check if the viewer is requesting a different SVC spatial/temporal layer */
5122 json_t *spatial = json_object_get(root, "spatial_layer");
5123 if(spatial) {
5124 int spatial_layer = json_integer_value(spatial);
5125 if(spatial_layer > 1) {
5126 JANUS_LOG(LOG_WARN, "Spatial layer higher than 1, will probably be ignored\n");
5127 }
5128 if(spatial_layer == session->spatial_layer) {
5129 /* No need to do anything, we're already getting the right spatial layer, so notify the user */
5130 json_t *event = json_object();
5131 json_object_set_new(event, "streaming", json_string("event"));
5132 json_t *result = json_object();
5133 json_object_set_new(result, "spatial_layer", json_integer(session->spatial_layer));
5134 json_object_set_new(event, "result", result);
5135 gateway->push_event(msg->handle, &janus_streaming_plugin, NULL, event, NULL);
5136 json_decref(event);
5137 } else if(spatial_layer != session->target_spatial_layer) {
5138 /* Send a FIR to the source, if RTCP is enabled */
5139 g_atomic_int_set(&source->need_pli, 1);
5140 }
5141 session->target_spatial_layer = spatial_layer;
5142 }
5143 json_t *temporal = json_object_get(root, "temporal_layer");
5144 if(temporal) {
5145 int temporal_layer = json_integer_value(temporal);
5146 if(temporal_layer > 2) {
5147 JANUS_LOG(LOG_WARN, "Temporal layer higher than 2, will probably be ignored\n");
5148 }
5149 if(temporal_layer == session->temporal_layer) {
5150 /* No need to do anything, we're already getting the right temporal layer, so notify the user */
5151 json_t *event = json_object();
5152 json_object_set_new(event, "streaming", json_string("event"));
5153 json_t *result = json_object();
5154 json_object_set_new(result, "temporal_layer", json_integer(session->temporal_layer));
5155 json_object_set_new(event, "result", result);
5156 gateway->push_event(msg->handle, &janus_streaming_plugin, NULL, event, NULL);
5157 json_decref(event);
5158 }
5159 session->target_temporal_layer = temporal_layer;
5160 }
5161 }
5162 }
5163 /* Done */
5164 result = json_object();
5165 json_object_set_new(result, "event", json_string("configured"));
5166 } else if(!strcasecmp(request_text, "switch")) {
5167 /* This listener wants to switch to a different mountpoint
5168 * NOTE: this only works for live RTP streams as of now: you
5169 * cannot, for instance, switch from a live RTP mountpoint to
5170 * an on demand one or viceversa (TBD.) */
5171 janus_mutex_lock(&session->mutex);
5172 janus_streaming_mountpoint *oldmp = session->mountpoint;
5173 if(oldmp == NULL) {
5174 janus_mutex_unlock(&session->mutex);
5175 JANUS_LOG(LOG_VERB, "Can't switch: not on a mountpoint\n");
5176 error_code = JANUS_STREAMING_ERROR_NO_SUCH_MOUNTPOINT;
5177 g_snprintf(error_cause, 512, "Can't switch: not on a mountpoint");
5178 goto error;
5179 }
5180 if(oldmp->streaming_type != janus_streaming_type_live ||
5181 oldmp->streaming_source != janus_streaming_source_rtp) {
5182 janus_mutex_unlock(&session->mutex);
5183 JANUS_LOG(LOG_VERB, "Can't switch: not on a live RTP mountpoint\n");
5184 error_code = JANUS_STREAMING_ERROR_CANT_SWITCH;
5185 g_snprintf(error_cause, 512, "Can't switch: not on a live RTP mountpoint");
5186 goto error;
5187 }
5188 janus_refcount_increase(&oldmp->ref);
5189 if(!string_ids) {
5190 JANUS_VALIDATE_JSON_OBJECT(root, id_parameters,
5191 error_code, error_cause, TRUE,
5192 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
5193 } else {
5194 JANUS_VALIDATE_JSON_OBJECT(root, idstr_parameters,
5195 error_code, error_cause, TRUE,
5196 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
5197 }
5198 if(error_code != 0) {
5199 janus_mutex_unlock(&session->mutex);
5200 janus_refcount_decrease(&oldmp->ref);
5201 goto error;
5202 }
5203 json_t *id = json_object_get(root, "id");
5204 guint64 id_value = 0;
5205 char id_num[30], *id_value_str = NULL;
5206 if(!string_ids) {
5207 id_value = json_integer_value(id);
5208 g_snprintf(id_num, sizeof(id_num), "%"SCNu64, id_value);
5209 id_value_str = id_num;
5210 } else {
5211 id_value_str = (char *)json_string_value(id);
5212 }
5213 janus_mutex_lock(&mountpoints_mutex);
5214 janus_streaming_mountpoint *mp = g_hash_table_lookup(mountpoints,
5215 string_ids ? (gpointer)id_value_str : (gpointer)&id_value);
5216 if(mp == NULL || g_atomic_int_get(&mp->destroyed)) {
5217 janus_mutex_unlock(&mountpoints_mutex);
5218 janus_mutex_unlock(&session->mutex);
5219 JANUS_LOG(LOG_VERB, "No such mountpoint/stream %s\n", id_value_str);
5220 error_code = JANUS_STREAMING_ERROR_NO_SUCH_MOUNTPOINT;
5221 g_snprintf(error_cause, 512, "No such mountpoint/stream %s", id_value_str);
5222 goto error;
5223 }
5224 janus_refcount_increase(&mp->ref);
5225 if(mp->streaming_type != janus_streaming_type_live ||
5226 mp->streaming_source != janus_streaming_source_rtp) {
5227 janus_refcount_decrease(&oldmp->ref);
5228 janus_refcount_decrease(&mp->ref);
5229 janus_mutex_unlock(&mountpoints_mutex);
5230 janus_mutex_unlock(&session->mutex);
5231 JANUS_LOG(LOG_VERB, "Can't switch: target is not a live RTP mountpoint\n");
5232 error_code = JANUS_STREAMING_ERROR_CANT_SWITCH;
5233 g_snprintf(error_cause, 512, "Can't switch: target is not a live RTP mountpoint");
5234 goto error;
5235 }
5236 janus_streaming_rtp_source *source = (janus_streaming_rtp_source *)mp->source;
5237 if(source && source->simulcast) {
5238 JANUS_VALIDATE_JSON_OBJECT(root, simulcast_parameters,
5239 error_code, error_cause, TRUE,
5240 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
5241 if(error_code != 0) {
5242 janus_refcount_decrease(&oldmp->ref);
5243 janus_refcount_decrease(&mp->ref);
5244 janus_mutex_unlock(&mountpoints_mutex);
5245 janus_mutex_unlock(&session->mutex);
5246 goto error;
5247 }
5248 /* In case this mountpoint is simulcasting, let's aim high by default */
5249 janus_rtp_simulcasting_context_reset(&session->sim_context);
5250 session->sim_context.substream_target = 2;
5251 session->sim_context.templayer_target = 2;
5252 janus_vp8_simulcast_context_reset(&session->vp8_context);
5253 /* Unless the request contains a target for either layer */
5254 json_t *substream = json_object_get(root, "substream");
5255 if(substream) {
5256 session->sim_context.substream_target = json_integer_value(substream);
5257 JANUS_LOG(LOG_VERB, "Setting video substream to let through (simulcast): %d (was %d)\n",
5258 session->sim_context.substream_target, session->sim_context.substream);
5259 }
5260 json_t *temporal = json_object_get(root, "temporal");
5261 if(temporal) {
5262 session->sim_context.templayer_target = json_integer_value(temporal);
5263 JANUS_LOG(LOG_VERB, "Setting video temporal layer to let through (simulcast): %d (was %d)\n",
5264 session->sim_context.templayer_target, session->sim_context.templayer);
5265 }
5266 /* Check if we need a custom fallback timer for the substream */
5267 json_t *fallback = json_object_get(root, "fallback");
5268 if(fallback) {
5269 JANUS_LOG(LOG_VERB, "Setting fallback timer (simulcast): %lld (was %"SCNu32")\n",
5270 json_integer_value(fallback) ? json_integer_value(fallback) : 250000,
5271 session->sim_context.drop_trigger ? session->sim_context.drop_trigger : 250000);
5272 session->sim_context.drop_trigger = json_integer_value(fallback);
5273 }
5274 } else if(source && source->svc) {
5275 JANUS_VALIDATE_JSON_OBJECT(root, svc_parameters,
5276 error_code, error_cause, TRUE,
5277 JANUS_STREAMING_ERROR_MISSING_ELEMENT, JANUS_STREAMING_ERROR_INVALID_ELEMENT);
5278 if(error_code != 0) {
5279 janus_refcount_decrease(&oldmp->ref);
5280 janus_refcount_decrease(&mp->ref);
5281 janus_mutex_unlock(&mountpoints_mutex);
5282 janus_mutex_unlock(&session->mutex);
5283 goto error;
5284 }
5285 /* In case this mountpoint is doing VP9-SVC, let's aim high by default */
5286 session->spatial_layer = -1;
5287 session->target_spatial_layer = 2; /* FIXME Chrome sends 0, 1 and 2 (if using EnabledByFlag_3SL3TL) */
5288 session->temporal_layer = -1;
5289 session->target_temporal_layer = 2; /* FIXME Chrome sends 0, 1 and 2 */
5290 /* Unless the request contains a target for either layer */
5291 json_t *spatial = json_object_get(root, "spatial_layer");
5292 if(spatial) {
5293 session->target_spatial_layer = json_integer_value(spatial);
5294 JANUS_LOG(LOG_VERB, "Setting video spatial layer to let through (SVC): %d (was %d)\n",
5295 session->target_spatial_layer, session->spatial_layer);
5296 }
5297 json_t *temporal = json_object_get(root, "temporal_layer");
5298 if(temporal) {
5299 session->target_temporal_layer = json_integer_value(temporal);
5300 JANUS_LOG(LOG_VERB, "Setting video temporal layer to let through (SVC): %d (was %d)\n",
5301 session->target_temporal_layer, session->temporal_layer);
5302 }
5303 }
5304 janus_mutex_unlock(&mountpoints_mutex);
5305 JANUS_LOG(LOG_VERB, "Request to switch to mountpoint/stream %s (old: %s)\n", mp->id_str, oldmp->id_str);
5306 g_atomic_int_set(&session->paused, 1);
5307 /* Unsubscribe from the previous mountpoint and subscribe to the new one */
5308 session->mountpoint = NULL;
5309 janus_mutex_unlock(&session->mutex);
5310 janus_mutex_lock(&oldmp->mutex);
5311 oldmp->viewers = g_list_remove_all(oldmp->viewers, session);
5312 /* Remove the viewer from the helper threads too, if any */
5313 if(oldmp->helper_threads > 0) {
5314 GList *l = oldmp->threads;
5315 while(l) {
5316 janus_streaming_helper *ht = (janus_streaming_helper *)l->data;
5317 janus_mutex_lock(&ht->mutex);
5318 if(g_list_find(ht->viewers, session) != NULL) {
5319 ht->num_viewers--;
5320 ht->viewers = g_list_remove_all(ht->viewers, session);
5321 janus_mutex_unlock(&ht->mutex);
5322 JANUS_LOG(LOG_VERB, "Removing viewer from helper thread #%d (switching)\n", ht->id);
5323 break;
5324 }
5325 janus_mutex_unlock(&ht->mutex);
5326 l = l->next;
5327 }
5328 }
5329 janus_refcount_decrease(&oldmp->ref); /* This is for the user going away */
5330 janus_mutex_unlock(&oldmp->mutex);
5331 /* Subscribe to the new one */
5332 janus_mutex_lock(&mp->mutex);
5333 janus_mutex_lock(&session->mutex);
5334 janus_refcount_increase(&mp->ref);
5335 mp->viewers = g_list_append(mp->viewers, session);
5336 /* If we're using helper threads, add the viewer to one of those */
5337 if(mp->helper_threads > 0) {
5338 int viewers = -1;
5339 janus_streaming_helper *helper = NULL;
5340 GList *l = mp->threads;
5341 while(l) {
5342 janus_streaming_helper *ht = (janus_streaming_helper *)l->data;
5343 if(viewers == -1 || (helper == NULL && ht->num_viewers == 0) || ht->num_viewers < viewers) {
5344 viewers = ht->num_viewers;
5345 helper = ht;
5346 }
5347 l = l->next;
5348 }
5349 JANUS_LOG(LOG_VERB, "Adding viewer to helper thread #%d\n", helper->id);
5350 janus_mutex_lock(&helper->mutex);
5351 helper->viewers = g_list_append(helper->viewers, session);
5352 helper->num_viewers++;
5353 janus_mutex_unlock(&helper->mutex);
5354 }
5355 session->mountpoint = mp;
5356 /* Send a PLI too, in case the mountpoint supports video and RTCP */
5357 janus_streaming_rtcp_pli_send(mp->source);
5358 g_atomic_int_set(&session->paused, 0);
5359 janus_mutex_unlock(&session->mutex);
5360 janus_mutex_unlock(&mp->mutex);
5361 /* Done with the request, remove the references we took for that */
5362 janus_refcount_decrease(&oldmp->ref);
5363 janus_refcount_decrease(&mp->ref);
5364 result = json_object();
5365 json_object_set_new(result, "switched", json_string("ok"));
5366 json_object_set_new(result, "id", string_ids ? json_string(id_value_str) : json_integer(id_value));
5367 /* Also notify event handlers */
5368 if(notify_events && gateway->events_is_enabled()) {
5369 json_t *info = json_object();
5370 json_object_set_new(info, "status", json_string("switching"));
5371 json_object_set_new(info, "id", string_ids ? json_string(id_value_str) : json_integer(id_value));
5372 gateway->notify_event(&janus_streaming_plugin, session->handle, info);
5373 }
5374 } else if(!strcasecmp(request_text, "stop")) {
5375 if(g_atomic_int_get(&session->stopping) || !g_atomic_int_get(&session->started)) {
5376 /* Been there, done that: ignore */
5377 janus_streaming_message_free(msg);
5378 continue;
5379 }
5380 JANUS_LOG(LOG_VERB, "Stopping the streaming\n");
5381 result = json_object();
5382 json_object_set_new(result, "status", json_string("stopping"));
5383 /* Also notify event handlers */
5384 if(notify_events && gateway->events_is_enabled()) {
5385 json_t *info = json_object();
5386 json_object_set_new(info, "status", json_string("stopping"));
5387 janus_streaming_mountpoint *mp = session->mountpoint;
5388 if(mp)
5389 json_object_set_new(info, "id", string_ids ? json_string(mp->id_str) : json_integer(mp->id));
5390 gateway->notify_event(&janus_streaming_plugin, session->handle, info);
5391 }
5392 /* Tell the core to tear down the PeerConnection, hangup_media will do the rest */
5393 gateway->close_pc(session->handle);
5394 } else {
5395 JANUS_LOG(LOG_VERB, "Unknown request '%s'\n", request_text);
5396 error_code = JANUS_STREAMING_ERROR_INVALID_REQUEST;
5397 g_snprintf(error_cause, 512, "Unknown request '%s'", request_text);
5398 goto error;
5399 }
5400
5401 /* Any SDP to handle? */
5402 const char *msg_sdp_type = json_string_value(json_object_get(msg->jsep, "type"));
5403 const char *msg_sdp = json_string_value(json_object_get(msg->jsep, "sdp"));
5404 if(msg_sdp) {
5405 JANUS_LOG(LOG_VERB, "This is involving a negotiation (%s) as well (%s):\n%s\n",
5406 do_restart ? "renegotiation occurring" : "but we really don't care", msg_sdp_type, msg_sdp);
5407 }
5408 g_atomic_int_set(&session->renegotiating, 0);
5409
5410 /* Prepare JSON event */
5411 json_t *jsep = json_pack("{ssss}", "type", sdp_type, "sdp", sdp);
5412 if(do_restart)
5413 json_object_set_new(jsep, "restart", json_true());
5414 if(session->e2ee)
5415 json_object_set_new(jsep, "e2ee", json_true());
5416 json_t *event = json_object();
5417 json_object_set_new(event, "streaming", json_string("event"));
5418 if(result != NULL)
5419 json_object_set_new(event, "result", result);
5420 int ret = gateway->push_event(msg->handle, &janus_streaming_plugin, msg->transaction, event, jsep);
5421 JANUS_LOG(LOG_VERB, " >> Pushing event: %d (%s)\n", ret, janus_get_api_error(ret));
5422 g_free(sdp);
5423 json_decref(event);
5424 json_decref(jsep);
5425 janus_streaming_message_free(msg);
5426 continue;
5427
5428 error:
5429 {
5430 /* Prepare JSON error event */
5431 json_t *event = json_object();
5432 json_object_set_new(event, "streaming", json_string("event"));
5433 json_object_set_new(event, "error_code", json_integer(error_code));
5434 json_object_set_new(event, "error", json_string(error_cause));
5435 int ret = gateway->push_event(msg->handle, &janus_streaming_plugin, msg->transaction, event, NULL);
5436 JANUS_LOG(LOG_VERB, " >> Pushing event: %d (%s)\n", ret, janus_get_api_error(ret));
5437 json_decref(event);
5438 janus_streaming_message_free(msg);
5439 }
5440 }
5441 JANUS_LOG(LOG_VERB, "Leaving Streaming handler thread\n");
5442 return NULL;
5443 }
5444
5445 /* Helpers to create a listener filedescriptor */
5446 static int janus_streaming_create_fd(int port, in_addr_t mcast, const janus_network_address *iface, char *host, size_t hostlen,
5447 const char *listenername, const char *medianame, const char *mountpointname, gboolean quiet) {
5448 janus_mutex_lock(&fd_mutex);
5449 struct sockaddr_in address = { 0 };
5450 struct sockaddr_in6 address6 = { 0 };
5451 janus_network_address_string_buffer address_representation;
5452
5453 uint16_t rtp_port_next = rtp_range_slider; /* Read global slider */
5454 uint16_t rtp_port_start = rtp_port_next;
5455 gboolean use_range = (port == 0), rtp_port_wrap = FALSE;
5456
5457 int fd = -1, family = 0;
5458 while(1) {
5459 family = 0; /* By default, we bind to both IPv4 and IPv6 */
5460 if(use_range && rtp_port_wrap && rtp_port_next >= rtp_port_start) {
5461 /* Full range scanned */
5462 JANUS_LOG(LOG_ERR, "No ports available for RTP/RTCP in range: %u -- %u\n",
5463 rtp_range_min, rtp_range_max);
5464 break;
5465 }
5466 if(!use_range) {
5467 /* Use the port specified in the arguments */
5468 if(IN_MULTICAST(ntohl(mcast))) {
5469 fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
5470 if(fd < 0) {
5471 JANUS_LOG(LOG_ERR, "[%s] Cannot create socket for %s... %d (%s)\n",
5472 mountpointname, medianame, errno, g_strerror(errno));
5473 break;
5474 }
5475 #ifdef IP_MULTICAST_ALL
5476 int mc_all = 0;
5477 if((setsockopt(fd, IPPROTO_IP, IP_MULTICAST_ALL, (void*) &mc_all, sizeof(mc_all))) < 0) {
5478 JANUS_LOG(LOG_ERR, "[%s] %s listener setsockopt IP_MULTICAST_ALL failed... %d (%s)\n",
5479 mountpointname, listenername, errno, g_strerror(errno));
5480 close(fd);
5481 janus_mutex_unlock(&fd_mutex);
5482 return -1;
5483 }
5484 #endif
5485 struct ip_mreq mreq;
5486 memset(&mreq, '\0', sizeof(mreq));
5487 mreq.imr_multiaddr.s_addr = mcast;
5488 if(!janus_network_address_is_null(iface)) {
5489 family = AF_INET;
5490 if(iface->family == AF_INET) {
5491 mreq.imr_interface = iface->ipv4;
5492 (void) janus_network_address_to_string_buffer(iface, &address_representation); /* This is OK: if we get here iface must be non-NULL */
5493 char *maddr = inet_ntoa(mreq.imr_multiaddr);
5494 JANUS_LOG(LOG_INFO, "[%s] %s listener using interface address: %s (%s)\n", mountpointname, listenername,
5495 janus_network_address_string_from_buffer(&address_representation), maddr);
5496 if(maddr && host && hostlen > 0)
5497 g_strlcpy(host, maddr, hostlen);
5498 } else {
5499 JANUS_LOG(LOG_ERR, "[%s] %s listener: invalid multicast address type (only IPv4 multicast is currently supported by this plugin)\n", mountpointname, listenername);
5500 close(fd);
5501 janus_mutex_unlock(&fd_mutex);
5502 return -1;
5503 }
5504 } else {
5505 JANUS_LOG(LOG_WARN, "[%s] No multicast interface for: %s. This may not work as expected if you have multiple network devices (NICs)\n", mountpointname, listenername);
5506 }
5507 if(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
5508 JANUS_LOG(LOG_ERR, "[%s] %s listener IP_ADD_MEMBERSHIP failed... %d (%s)\n",
5509 mountpointname, listenername, errno, g_strerror(errno));
5510 close(fd);
5511 janus_mutex_unlock(&fd_mutex);
5512 return -1;
5513 }
5514 JANUS_LOG(LOG_INFO, "[%s] %s listener IP_ADD_MEMBERSHIP ok\n", mountpointname, listenername);
5515 }
5516 } else {
5517 /* Pick a port in the configured range */
5518 port = rtp_port_next;
5519 if((uint32_t)(rtp_port_next) < rtp_range_max) {
5520 rtp_port_next++;
5521 } else {
5522 rtp_port_next = rtp_range_min;
5523 rtp_port_wrap = TRUE;
5524 }
5525 }
5526 address.sin_family = AF_INET;
5527 address.sin_port = htons(port);
5528 address.sin_addr.s_addr = INADDR_ANY;
5529 address6.sin6_family = AF_INET6;
5530 address6.sin6_port = htons(port);
5531 address6.sin6_addr = in6addr_any;
5532 /* If this is multicast, allow a re-use of the same ports (different groups may be used) */
5533 if(!use_range && IN_MULTICAST(ntohl(mcast))) {
5534 int reuse = 1;
5535 if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) {
5536 JANUS_LOG(LOG_ERR, "[%s] %s listener setsockopt SO_REUSEADDR failed... %d (%s)\n",
5537 mountpointname, listenername, errno, g_strerror(errno));
5538 close(fd);
5539 janus_mutex_unlock(&fd_mutex);
5540 return -1;
5541 }
5542 /* TODO IPv6 */
5543 family = AF_INET;
5544 address.sin_addr.s_addr = mcast;
5545 } else {
5546 if(!IN_MULTICAST(ntohl(mcast)) && !janus_network_address_is_null(iface)) {
5547 family = iface->family;
5548 if(iface->family == AF_INET) {
5549 address.sin_addr = iface->ipv4;
5550 (void) janus_network_address_to_string_buffer(iface, &address_representation); /* This is OK: if we get here iface must be non-NULL */
5551 JANUS_LOG(LOG_INFO, "[%s] %s listener restricted to interface address: %s\n",
5552 mountpointname, listenername, janus_network_address_string_from_buffer(&address_representation));
5553 if(host && hostlen > 0)
5554 g_strlcpy(host, janus_network_address_string_from_buffer(&address_representation), hostlen);
5555 } else if(iface->family == AF_INET6) {
5556 memcpy(&address6.sin6_addr, &iface->ipv6, sizeof(iface->ipv6));
5557 (void) janus_network_address_to_string_buffer(iface, &address_representation); /* This is OK: if we get here iface must be non-NULL */
5558 JANUS_LOG(LOG_INFO, "[%s] %s listener restricted to interface address: %s\n",
5559 mountpointname, listenername, janus_network_address_string_from_buffer(&address_representation));
5560 if(host && hostlen > 0)
5561 g_strlcpy(host, janus_network_address_string_from_buffer(&address_representation), hostlen);
5562 } else {
5563 JANUS_LOG(LOG_ERR, "[%s] %s listener: invalid address/restriction type\n", mountpointname, listenername);
5564 continue;
5565 }
5566 }
5567 }
5568 /* Bind to the specified port */
5569 if(fd == -1) {
5570 fd = socket(family == AF_INET ? AF_INET : AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
5571 int v6only = 0;
5572 if(fd < 0) {
5573 JANUS_LOG(LOG_ERR, "[%s] Cannot create socket for %s... %d (%s)\n",
5574 mountpointname, medianame, errno, g_strerror(errno));
5575 break;
5576 }
5577 if(family != AF_INET && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)) != 0) {
5578 JANUS_LOG(LOG_ERR, "[%s] setsockopt on socket failed for %s... %d (%s)\n",
5579 mountpointname, medianame, errno, g_strerror(errno));
5580 break;
5581 }
5582 }
5583 size_t addrlen = (family == AF_INET ? sizeof(address) : sizeof(address6));
5584 if(bind(fd, (family == AF_INET ? (struct sockaddr *)&address : (struct sockaddr *)&address6), addrlen) < 0) {
5585 close(fd);
5586 fd = -1;
5587 if(!quiet) {
5588 JANUS_LOG(LOG_ERR, "[%s] Bind failed for %s (port %d)... %d (%s)\n",
5589 mountpointname, medianame, port, errno, g_strerror(errno));
5590 }
5591 if(!use_range) /* Asked for a specific port but it's not available, give up */
5592 break;
5593 } else {
5594 if(use_range)
5595 rtp_range_slider = port; /* Update global slider */
5596 break;
5597 }
5598 }
5599 janus_mutex_unlock(&fd_mutex);
5600 return fd;
5601 }
5602 /* Helper to bind RTP/RTCP port pair (for RTSP) */
5603 static int janus_streaming_allocate_port_pair(const char *name, const char *media,
5604 in_addr_t mcast, const janus_network_address *iface, multiple_fds *fds, int ports[2]) {
5605 /* Start from the global slider */
5606 uint16_t rtp_port_next = rtp_range_slider;
5607 if(rtp_port_next % 2 != 0) /* We want an even port for RTP */
5608 rtp_port_next++;
5609 uint16_t rtp_port_start = rtp_port_next;
5610 gboolean rtp_port_wrap = FALSE;
5611
5612 int rtp_fd = -1, rtcp_fd = -1;
5613 while(1) {
5614 if(rtp_port_wrap && rtp_port_next >= rtp_port_start) {
5615 /* Full range scanned */
5616 JANUS_LOG(LOG_ERR, "No ports available for audio/video channel in range: %u -- %u\n",
5617 rtp_range_min, rtp_range_max);
5618 break;
5619 }
5620 int rtp_port = rtp_port_next;
5621 int rtcp_port = rtp_port+1;
5622 if((uint32_t)(rtp_port_next + 2UL) < rtp_range_max) {
5623 /* Advance to next pair */
5624 rtp_port_next += 2;
5625 } else {
5626 rtp_port_next = rtp_range_min;
5627 rtp_port_wrap = TRUE;
5628 }
5629 rtp_fd = janus_streaming_create_fd(rtp_port, mcast, iface, NULL, 0, media, media, name, TRUE);
5630 if(rtp_fd != -1) {
5631 rtcp_fd = janus_streaming_create_fd(rtcp_port, mcast, iface, NULL, 0, media, media, name, TRUE);
5632 if(rtcp_fd != -1) {
5633 /* Done */
5634 fds->fd = rtp_fd;
5635 fds->rtcp_fd = rtcp_fd;
5636 ports[0] = rtp_port;
5637 ports[1] = rtcp_port;
5638 /* Update global slider */
5639 rtp_range_slider = rtp_port_next;
5640 return 0;
5641 }
5642 }
5643 /* If we got here, something failed: try again */
5644 if(rtp_fd != -1)
5645 close(rtp_fd);
5646 }
5647 return -1;
5648 }
5649
5650 /* Helper to return fd port */
5651 static int janus_streaming_get_fd_port(int fd) {
5652 struct sockaddr_in6 server = { 0 };
5653 socklen_t len = sizeof(server);
5654 if(getsockname(fd, (struct sockaddr *)&server, &len) == -1) {
5655 return -1;
5656 }
5657
5658 return ntohs(server.sin6_port);
5659 }
5660
5661 /* Helpers to destroy a streaming mountpoint. */
5662 static void janus_streaming_rtp_source_free(janus_streaming_rtp_source *source) {
5663 if(source->audio_fd > -1) {
5664 close(source->audio_fd);
5665 }
5666 if(source->video_fd[0] > -1) {
5667 close(source->video_fd[0]);
5668 }
5669 if(source->video_fd[1] > -1) {
5670 close(source->video_fd[1]);
5671 }
5672 if(source->video_fd[2] > -1) {
5673 close(source->video_fd[2]);
5674 }
5675 if(source->data_fd > -1) {
5676 close(source->data_fd);
5677 }
5678 if(source->audio_rtcp_fd > -1) {
5679 close(source->audio_rtcp_fd);
5680 }
5681 if(source->video_rtcp_fd > -1) {
5682 close(source->video_rtcp_fd);
5683 }
5684 if(source->pipefd[0] > -1) {
5685 close(source->pipefd[0]);
5686 }
5687 if(source->pipefd[1] > -1) {
5688 close(source->pipefd[1]);
5689 }
5690 g_free(source->audio_host);
5691 g_free(source->video_host);
5692 g_free(source->data_host);
5693 janus_mutex_lock(&source->keyframe.mutex);
5694 if(source->keyframe.latest_keyframe != NULL)
5695 g_list_free_full(source->keyframe.latest_keyframe, (GDestroyNotify)janus_streaming_rtp_relay_packet_free);
5696 source->keyframe.latest_keyframe = NULL;
5697 janus_mutex_unlock(&source->keyframe.mutex);
5698 janus_mutex_lock(&source->buffermsg_mutex);
5699 if(source->last_msg != NULL)
5700 janus_streaming_rtp_relay_packet_free((janus_streaming_rtp_relay_packet *)source->last_msg);
5701 source->last_msg = NULL;
5702 janus_mutex_unlock(&source->buffermsg_mutex);
5703 if(source->is_srtp) {
5704 g_free(source->srtpcrypto);
5705 srtp_dealloc(source->srtp_ctx);
5706 g_free(source->srtp_policy.key);
5707 }
5708 #ifdef HAVE_LIBCURL
5709 janus_mutex_lock(&source->rtsp_mutex);
5710 if(source->curl) {
5711 /* Send an RTSP TEARDOWN */
5712 curl_easy_setopt(source->curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_TEARDOWN);
5713 int res = curl_easy_perform(source->curl);
5714 if(res != CURLE_OK) {
5715 JANUS_LOG(LOG_ERR, "Couldn't send TEARDOWN request: %s\n", curl_easy_strerror(res));
5716 }
5717 curl_easy_cleanup(source->curl);
5718 }
5719 janus_streaming_buffer *curldata = source->curldata;
5720 if(curldata != NULL) {
5721 g_free(curldata->buffer);
5722 g_free(curldata);
5723 }
5724 g_free(source->rtsp_url);
5725 g_free(source->rtsp_username);
5726 g_free(source->rtsp_password);
5727 g_free(source->rtsp_ahost);
5728 g_free(source->rtsp_vhost);
5729 janus_mutex_unlock(&source->rtsp_mutex);
5730 #endif
5731 g_free(source);
5732 }
5733
5734 static void janus_streaming_file_source_free(janus_streaming_file_source *source) {
5735 g_free(source->filename);
5736 g_free(source);
5737 }
5738
5739 /* Helper to create an RTP live source (e.g., from gstreamer/ffmpeg/vlc/etc.) */
5740 janus_streaming_mountpoint *janus_streaming_create_rtp_source(
5741 uint64_t id, char *id_str, char *name, char *desc, char *metadata,
5742 int srtpsuite, char *srtpcrypto, int threads, gboolean e2ee,
5743 gboolean doaudio, gboolean doaudiortcp, char *amcast, const janus_network_address *aiface, uint16_t aport, uint16_t artcpport, uint8_t acodec, char *artpmap, char *afmtp, gboolean doaskew,
5744 gboolean dovideo, gboolean dovideortcp, char *vmcast, const janus_network_address *viface, uint16_t vport, uint16_t vrtcpport, uint8_t vcodec, char *vrtpmap, char *vfmtp, gboolean bufferkf,
5745 gboolean simulcast, uint16_t vport2, uint16_t vport3, gboolean svc, gboolean dovskew, int rtp_collision,
5746 gboolean dodata, const janus_network_address *diface, uint16_t dport, gboolean textdata, gboolean buffermsg) {
5747 char id_num[30];
5748 if(!string_ids) {
5749 g_snprintf(id_num, sizeof(id_num), "%"SCNu64, id);
5750 id_str = id_num;
5751 }
5752 char tempname[255];
5753 if(name == NULL) {
5754 JANUS_LOG(LOG_VERB, "Missing name, will generate a random one...\n");
5755 memset(tempname, 0, 255);
5756 g_snprintf(tempname, 255, "mp-%s", id_str);
5757 } else if(atoi(name) != 0) {
5758 JANUS_LOG(LOG_VERB, "Names can't start with a number, prefixing it...\n");
5759 memset(tempname, 0, 255);
5760 g_snprintf(tempname, 255, "mp-%s", name);
5761 name = NULL;
5762 }
5763 if(!doaudio && !dovideo && !dodata) {
5764 JANUS_LOG(LOG_ERR, "Can't add 'rtp' stream, no audio, video or data have to be streamed...\n");
5765 janus_mutex_lock(&mountpoints_mutex);
5766 g_hash_table_remove(mountpoints_temp, &id);
5767 janus_mutex_unlock(&mountpoints_mutex);
5768 return NULL;
5769 }
5770 if(doaudio && (artpmap == NULL)) {
5771 JANUS_LOG(LOG_ERR, "Can't add 'rtp' stream, missing mandatory information for audio...\n");
5772 janus_mutex_lock(&mountpoints_mutex);
5773 g_hash_table_remove(mountpoints_temp, &id);
5774 janus_mutex_unlock(&mountpoints_mutex);
5775 return NULL;
5776 }
5777 if(dovideo && (vcodec == 0 || vrtpmap == NULL)) {
5778 JANUS_LOG(LOG_ERR, "Can't add 'rtp' stream, missing mandatory information for video...\n");
5779 janus_mutex_lock(&mountpoints_mutex);
5780 g_hash_table_remove(mountpoints_temp, &id);
5781 janus_mutex_unlock(&mountpoints_mutex);
5782 return NULL;
5783 }
5784 JANUS_LOG(LOG_VERB, "Audio %s, Video %s, Data %s\n",
5785 doaudio ? "enabled" : "NOT enabled",
5786 dovideo ? "enabled" : "NOT enabled",
5787 dodata ? "enabled" : "NOT enabled");
5788 /* First of all, let's check if the requested ports are free */
5789 int audio_fd = -1;
5790 int audio_rtcp_fd = -1;
5791 char audiohost[46];
5792 audiohost[0] = '\0';
5793 if(doaudio) {
5794 audio_fd = janus_streaming_create_fd(aport, amcast ? inet_addr(amcast) : INADDR_ANY, aiface,
5795 audiohost, sizeof(audiohost), "Audio", "audio", name ? name : tempname, aport == 0);
5796 if(audio_fd < 0) {
5797 JANUS_LOG(LOG_ERR, "Can't bind to port %d for audio...\n", aport);
5798 janus_mutex_lock(&mountpoints_mutex);
5799 g_hash_table_remove(mountpoints_temp, &id);
5800 janus_mutex_unlock(&mountpoints_mutex);
5801 return NULL;
5802 }
5803 aport = janus_streaming_get_fd_port(audio_fd);
5804 if(doaudiortcp) {
5805 audio_rtcp_fd = janus_streaming_create_fd(artcpport, amcast ? inet_addr(amcast) : INADDR_ANY, aiface,
5806 NULL, 0, "Audio", "audio", name ? name : tempname, artcpport == 0);
5807 if(audio_rtcp_fd < 0) {
5808 JANUS_LOG(LOG_ERR, "Can't bind to port %d for audio RTCP...\n", artcpport);
5809 if(audio_fd > -1)
5810 close(audio_fd);
5811 janus_mutex_lock(&mountpoints_mutex);
5812 g_hash_table_remove(mountpoints_temp, &id);
5813 janus_mutex_unlock(&mountpoints_mutex);
5814 return NULL;
5815 }
5816 artcpport = janus_streaming_get_fd_port(audio_rtcp_fd);
5817 }
5818 }
5819 int video_fd[3] = {-1, -1, -1};
5820 int video_rtcp_fd = -1;
5821 char videohost[46];
5822 videohost[0] = '\0';
5823 if(dovideo) {
5824 video_fd[0] = janus_streaming_create_fd(vport, vmcast ? inet_addr(vmcast) : INADDR_ANY, viface,
5825 videohost, sizeof(videohost), "Video", "video", name ? name : tempname, vport == 0);
5826 if(video_fd[0] < 0) {
5827 JANUS_LOG(LOG_ERR, "Can't bind to port %d for video...\n", vport);
5828 if(audio_fd > -1)
5829 close(audio_fd);
5830 if(audio_rtcp_fd > -1)
5831 close(audio_rtcp_fd);
5832 janus_mutex_lock(&mountpoints_mutex);
5833 g_hash_table_remove(mountpoints_temp, &id);
5834 janus_mutex_unlock(&mountpoints_mutex);
5835 return NULL;
5836 }
5837 vport = janus_streaming_get_fd_port(video_fd[0]);
5838 if(dovideortcp) {
5839 video_rtcp_fd = janus_streaming_create_fd(vrtcpport, vmcast ? inet_addr(vmcast) : INADDR_ANY, viface,
5840 NULL, 0, "Video", "video", name ? name : tempname, vrtcpport == 0);
5841 if(video_rtcp_fd < 0) {
5842 JANUS_LOG(LOG_ERR, "Can't bind to port %d for video RTCP...\n", vrtcpport);
5843 if(audio_fd > -1)
5844 close(audio_fd);
5845 if(audio_rtcp_fd > -1)
5846 close(audio_rtcp_fd);
5847 if(video_fd[0] > -1)
5848 close(video_fd[0]);
5849 janus_mutex_lock(&mountpoints_mutex);
5850 g_hash_table_remove(mountpoints_temp, &id);
5851 janus_mutex_unlock(&mountpoints_mutex);
5852 return NULL;
5853 }
5854 vrtcpport = janus_streaming_get_fd_port(video_rtcp_fd);
5855 }
5856 if(simulcast) {
5857 video_fd[1] = janus_streaming_create_fd(vport2, vmcast ? inet_addr(vmcast) : INADDR_ANY, viface,
5858 NULL, 0, "Video", "video", name ? name : tempname, FALSE);
5859 if(video_fd[1] < 0) {
5860 JANUS_LOG(LOG_ERR, "Can't bind to port %d for video (2nd port)...\n", vport2);
5861 if(audio_fd > -1)
5862 close(audio_fd);
5863 if(audio_rtcp_fd > -1)
5864 close(audio_rtcp_fd);
5865 if(video_fd[0] > -1)
5866 close(video_fd[0]);
5867 if(video_rtcp_fd > -1)
5868 close(video_rtcp_fd);
5869 janus_mutex_lock(&mountpoints_mutex);
5870 g_hash_table_remove(mountpoints_temp, &id);
5871 janus_mutex_unlock(&mountpoints_mutex);
5872 return NULL;
5873 }
5874 vport2 = janus_streaming_get_fd_port(video_fd[1]);
5875 video_fd[2] = janus_streaming_create_fd(vport3, vmcast ? inet_addr(vmcast) : INADDR_ANY, viface,
5876 NULL, 0, "Video", "video", name ? name : tempname, FALSE);
5877 if(video_fd[2] < 0) {
5878 JANUS_LOG(LOG_ERR, "Can't bind to port %d for video (3rd port)...\n", vport3);
5879 if(audio_fd > -1)
5880 close(audio_fd);
5881 if(audio_rtcp_fd > -1)
5882 close(audio_rtcp_fd);
5883 if(video_rtcp_fd > -1)
5884 close(video_rtcp_fd);
5885 if(video_fd[0] > -1)
5886 close(video_fd[0]);
5887 if(video_fd[1] > -1)
5888 close(video_fd[1]);
5889 janus_mutex_lock(&mountpoints_mutex);
5890 g_hash_table_remove(mountpoints_temp, &id);
5891 janus_mutex_unlock(&mountpoints_mutex);
5892 return NULL;
5893 }
5894 vport3 = janus_streaming_get_fd_port(video_fd[2]);
5895 }
5896 }
5897 int data_fd = -1;
5898 char datahost[46];
5899 datahost[0] = '\0';
5900 if(dodata) {
5901 #ifdef HAVE_SCTP
5902 data_fd = janus_streaming_create_fd(dport, INADDR_ANY, diface,
5903 datahost, sizeof(datahost), "Data", "data", name ? name : tempname, FALSE);
5904 if(data_fd < 0) {
5905 JANUS_LOG(LOG_ERR, "Can't bind to port %d for data...\n", dport);
5906 if(audio_fd > -1)
5907 close(audio_fd);
5908 if(audio_rtcp_fd > -1)
5909 close(audio_rtcp_fd);
5910 if(video_rtcp_fd > -1)
5911 close(video_rtcp_fd);
5912 if(video_fd[0] > -1)
5913 close(video_fd[0]);
5914 if(video_fd[1] > -1)
5915 close(video_fd[1]);
5916 if(video_fd[2] > -1)
5917 close(video_fd[2]);
5918 janus_mutex_lock(&mountpoints_mutex);
5919 g_hash_table_remove(mountpoints_temp, &id);
5920 janus_mutex_unlock(&mountpoints_mutex);
5921 return NULL;
5922 }
5923 dport = janus_streaming_get_fd_port(data_fd);
5924 #else
5925 JANUS_LOG(LOG_WARN, "Mountpoint wants to do datachannel relaying, but datachannels support was not compiled...\n");
5926 dodata = FALSE;
5927 #endif
5928 }
5929 /* Create the mountpoint */
5930 janus_network_address nil;
5931 janus_network_address_nullify(&nil);
5932
5933 janus_streaming_mountpoint *live_rtp = g_malloc0(sizeof(janus_streaming_mountpoint));
5934 live_rtp->id = id;
5935 live_rtp->id_str = g_strdup(id_str);
5936 live_rtp->name = g_strdup(name ? name : tempname);
5937 char *description = NULL;
5938 if(desc != NULL)
5939 description = g_strdup(desc);
5940 else
5941 description = g_strdup(name ? name : tempname);
5942 live_rtp->description = description;
5943 live_rtp->metadata = (metadata ? g_strdup(metadata) : NULL);
5944 live_rtp->enabled = TRUE;
5945 live_rtp->active = FALSE;
5946 live_rtp->audio = doaudio;
5947 live_rtp->video = dovideo;
5948 live_rtp->data = dodata;
5949 live_rtp->streaming_type = janus_streaming_type_live;
5950 live_rtp->streaming_source = janus_streaming_source_rtp;
5951 janus_streaming_rtp_source *live_rtp_source = g_malloc0(sizeof(janus_streaming_rtp_source));
5952 /* First of all, let's check if we need to setup an SRTP mountpoint */
5953 if(srtpsuite > 0 && srtpcrypto != NULL) {
5954 /* Base64 decode the crypto string and set it as the SRTP context */
5955 gsize len = 0;
5956 guchar *decoded = g_base64_decode(srtpcrypto, &len);
5957 if(len < SRTP_MASTER_LENGTH) {
5958 JANUS_LOG(LOG_ERR, "Invalid SRTP crypto (%s)\n", srtpcrypto);
5959 g_free(decoded);
5960 if(audio_fd > -1)
5961 close(audio_fd);
5962 if(audio_rtcp_fd > -1)
5963 close(audio_rtcp_fd);
5964 if(video_rtcp_fd > -1)
5965 close(video_rtcp_fd);
5966 if(video_fd[0] > -1)
5967 close(video_fd[0]);
5968 if(video_fd[1] > -1)
5969 close(video_fd[1]);
5970 if(video_fd[2] > -1)
5971 close(video_fd[2]);
5972 if(data_fd > -1)
5973 close(data_fd);
5974 janus_mutex_lock(&mountpoints_mutex);
5975 g_hash_table_remove(mountpoints_temp, &id);
5976 janus_mutex_unlock(&mountpoints_mutex);
5977 g_free(live_rtp_source);
5978 g_free(live_rtp->name);
5979 g_free(live_rtp->description);
5980 g_free(live_rtp->metadata);
5981 g_free(live_rtp);
5982 return NULL;
5983 }
5984 /* Set SRTP policy */
5985 srtp_policy_t *policy = &live_rtp_source->srtp_policy;
5986 srtp_crypto_policy_set_rtp_default(&(policy->rtp));
5987 if(srtpsuite == 32) {
5988 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&(policy->rtp));
5989 } else if(srtpsuite == 80) {
5990 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(policy->rtp));
5991 }
5992 policy->ssrc.type = ssrc_any_inbound;
5993 policy->key = decoded;
5994 policy->next = NULL;
5995 /* Create SRTP context */
5996 srtp_err_status_t res = srtp_create(&live_rtp_source->srtp_ctx, policy);
5997 if(res != srtp_err_status_ok) {
5998 /* Something went wrong... */
5999 JANUS_LOG(LOG_ERR, "Error creating forwarder SRTP session: %d (%s)\n", res, janus_srtp_error_str(res));
6000 g_free(decoded);
6001 if(audio_fd > -1)
6002 close(audio_fd);
6003 if(audio_rtcp_fd > -1)
6004 close(audio_rtcp_fd);
6005 if(video_rtcp_fd > -1)
6006 close(video_rtcp_fd);
6007 if(video_fd[0] > -1)
6008 close(video_fd[0]);
6009 if(video_fd[1] > -1)
6010 close(video_fd[1]);
6011 if(video_fd[2] > -1)
6012 close(video_fd[2]);
6013 if(data_fd > -1)
6014 close(data_fd);
6015 janus_mutex_lock(&mountpoints_mutex);
6016 g_hash_table_remove(mountpoints_temp, &id);
6017 janus_mutex_unlock(&mountpoints_mutex);
6018 g_free(live_rtp_source);
6019 g_free(live_rtp->name);
6020 g_free(live_rtp->description);
6021 g_free(live_rtp->metadata);
6022 g_free(live_rtp);
6023 return NULL;
6024 }
6025 live_rtp_source->is_srtp = TRUE;
6026 live_rtp_source->srtpsuite = srtpsuite;
6027 live_rtp_source->srtpcrypto = g_strdup(srtpcrypto);
6028 }
6029 live_rtp_source->e2ee = e2ee;
6030 live_rtp_source->audio_mcast = doaudio ? (amcast ? inet_addr(amcast) : INADDR_ANY) : INADDR_ANY;
6031 live_rtp_source->audio_iface = doaudio && !janus_network_address_is_null(aiface) ? *aiface : nil;
6032 live_rtp_source->audio_port = doaudio ? aport : -1;
6033 live_rtp_source->audio_rtcp_port = artcpport;
6034 live_rtp_source->askew = doaskew;
6035 if(doaudio && strlen(audiohost) > 0)
6036 live_rtp_source->audio_host = g_strdup(audiohost);
6037 live_rtp_source->video_mcast = dovideo ? (vmcast ? inet_addr(vmcast) : INADDR_ANY) : INADDR_ANY;
6038 live_rtp_source->video_port[0] = dovideo ? vport : -1;
6039 live_rtp_source->video_rtcp_port = vrtcpport;
6040 live_rtp_source->simulcast = dovideo && simulcast;
6041 live_rtp_source->video_port[1] = live_rtp_source->simulcast ? vport2 : -1;
6042 live_rtp_source->video_port[2] = live_rtp_source->simulcast ? vport3 : -1;
6043 live_rtp_source->video_iface = dovideo && !janus_network_address_is_null(viface) ? *viface : nil;
6044 live_rtp_source->vskew = dovskew;
6045 if(dovideo && strlen(videohost) > 0)
6046 live_rtp_source->video_host = g_strdup(videohost);
6047 live_rtp_source->data_port = dodata ? dport : -1;
6048 live_rtp_source->data_iface = dodata && !janus_network_address_is_null(diface) ? *diface : nil;
6049 if(dodata && strlen(datahost) > 0)
6050 live_rtp_source->data_host = g_strdup(datahost);
6051 live_rtp_source->arc = NULL;
6052 live_rtp_source->vrc = NULL;
6053 live_rtp_source->drc = NULL;
6054 janus_rtp_switching_context_reset(&live_rtp_source->context[0]);
6055 janus_rtp_switching_context_reset(&live_rtp_source->context[1]);
6056 janus_rtp_switching_context_reset(&live_rtp_source->context[2]);
6057 janus_mutex_init(&live_rtp_source->rec_mutex);
6058 live_rtp_source->audio_fd = audio_fd;
6059 live_rtp_source->audio_rtcp_fd = audio_rtcp_fd;
6060 live_rtp_source->video_rtcp_fd = video_rtcp_fd;
6061 live_rtp_source->video_fd[0] = video_fd[0];
6062 live_rtp_source->video_fd[1] = video_fd[1];
6063 live_rtp_source->video_fd[2] = video_fd[2];
6064 live_rtp_source->data_fd = data_fd;
6065 live_rtp_source->pipefd[0] = -1;
6066 live_rtp_source->pipefd[1] = -1;
6067 pipe(live_rtp_source->pipefd);
6068 live_rtp_source->last_received_audio = janus_get_monotonic_time();
6069 live_rtp_source->last_received_video = janus_get_monotonic_time();
6070 live_rtp_source->last_received_data = janus_get_monotonic_time();
6071 live_rtp_source->keyframe.enabled = bufferkf;
6072 live_rtp_source->keyframe.latest_keyframe = NULL;
6073 live_rtp_source->keyframe.temp_keyframe = NULL;
6074 live_rtp_source->keyframe.temp_ts = 0;
6075 janus_mutex_init(&live_rtp_source->keyframe.mutex);
6076 live_rtp_source->rtp_collision = rtp_collision;
6077 live_rtp_source->textdata = textdata;
6078 live_rtp_source->buffermsg = buffermsg;
6079 live_rtp_source->last_msg = NULL;
6080 janus_mutex_init(&live_rtp_source->buffermsg_mutex);
6081 live_rtp->source = live_rtp_source;
6082 live_rtp->source_destroy = (GDestroyNotify) janus_streaming_rtp_source_free;
6083 live_rtp->codecs.audio_pt = doaudio ? acodec : -1;
6084 live_rtp->codecs.audio_rtpmap = doaudio ? g_strdup(artpmap) : NULL;
6085 live_rtp->codecs.audio_fmtp = doaudio ? (afmtp ? g_strdup(afmtp) : NULL) : NULL;
6086 live_rtp->codecs.video_codec = JANUS_VIDEOCODEC_NONE;
6087 if(dovideo) {
6088 if(strstr(vrtpmap, "vp8") || strstr(vrtpmap, "VP8"))
6089 live_rtp->codecs.video_codec = JANUS_VIDEOCODEC_VP8;
6090 else if(strstr(vrtpmap, "vp9") || strstr(vrtpmap, "VP9"))
6091 live_rtp->codecs.video_codec = JANUS_VIDEOCODEC_VP9;
6092 else if(strstr(vrtpmap, "h264") || strstr(vrtpmap, "H264"))
6093 live_rtp->codecs.video_codec = JANUS_VIDEOCODEC_H264;
6094 else if(strstr(vrtpmap, "av1") || strstr(vrtpmap, "AV1"))
6095 live_rtp->codecs.video_codec = JANUS_VIDEOCODEC_AV1;
6096 else if(strstr(vrtpmap, "h265") || strstr(vrtpmap, "H265"))
6097 live_rtp->codecs.video_codec = JANUS_VIDEOCODEC_H265;
6098 }
6099 if(svc) {
6100 if(live_rtp->codecs.video_codec == JANUS_VIDEOCODEC_VP9) {
6101 live_rtp_source->svc = TRUE;
6102 } else {
6103 JANUS_LOG(LOG_WARN, "SVC is only supported, in an experimental way, for VP9-SVC mountpoints: disabling it...\n");
6104 }
6105 }
6106 live_rtp->codecs.video_pt = dovideo ? vcodec : -1;
6107 live_rtp->codecs.video_rtpmap = dovideo ? g_strdup(vrtpmap) : NULL;
6108 live_rtp->codecs.video_fmtp = dovideo ? (vfmtp ? g_strdup(vfmtp) : NULL) : NULL;
6109 live_rtp->viewers = NULL;
6110 g_atomic_int_set(&live_rtp->destroyed, 0);
6111 janus_refcount_init(&live_rtp->ref, janus_streaming_mountpoint_free);
6112 janus_mutex_init(&live_rtp->mutex);
6113 janus_mutex_lock(&mountpoints_mutex);
6114 g_hash_table_insert(mountpoints,
6115 string_ids ? (gpointer)g_strdup(live_rtp->id_str) : (gpointer)janus_uint64_dup(live_rtp->id),
6116 live_rtp);
6117 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)live_rtp->id_str : (gpointer)&live_rtp->id);
6118 /* If we need helper threads, spawn them now */
6119 GError *error = NULL;
6120 char tname[16];
6121 if(threads > 0) {
6122 int i=0;
6123 for(i=0; i<threads; i++) {
6124 janus_streaming_helper *helper = g_malloc0(sizeof(janus_streaming_helper));
6125 helper->id = i+1;
6126 helper->mp = live_rtp;
6127 helper->queued_packets = g_async_queue_new_full((GDestroyNotify)janus_streaming_rtp_relay_packet_free);
6128 janus_mutex_init(&helper->mutex);
6129 janus_refcount_init(&helper->ref, janus_streaming_helper_free);
6130 live_rtp->helper_threads++;
6131 /* Spawn a thread and add references */
6132 g_snprintf(tname, sizeof(tname), "help %u-%"SCNu64, helper->id, live_rtp->id);
6133 janus_refcount_increase(&live_rtp->ref);
6134 janus_refcount_increase(&helper->ref);
6135 helper->thread = g_thread_try_new(tname, &janus_streaming_helper_thread, helper, &error);
6136 if(error != NULL) {
6137 JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the helper thread...\n",
6138 error->code, error->message ? error->message : "??");
6139 g_error_free(error);
6140 janus_refcount_decrease(&live_rtp->ref); /* This is for the helper thread */
6141 g_async_queue_unref(helper->queued_packets);
6142 janus_refcount_decrease(&helper->ref);
6143 /* This extra unref is for the init */
6144 janus_refcount_decrease(&helper->ref);
6145 janus_mutex_unlock(&mountpoints_mutex);
6146 janus_streaming_mountpoint_destroy(live_rtp);
6147 return NULL;
6148 }
6149 janus_refcount_increase(&helper->ref);
6150 live_rtp->threads = g_list_append(live_rtp->threads, helper);
6151 }
6152 }
6153 janus_mutex_unlock(&mountpoints_mutex);
6154 /* Finally, create the mountpoint thread itself */
6155 g_snprintf(tname, sizeof(tname), "mp %s", live_rtp->id_str);
6156 janus_refcount_increase(&live_rtp->ref);
6157 live_rtp->thread = g_thread_try_new(tname, &janus_streaming_relay_thread, live_rtp, &error);
6158 if(error != NULL) {
6159 JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the RTP thread...\n",
6160 error->code, error->message ? error->message : "??");
6161 g_error_free(error);
6162 janus_refcount_decrease(&live_rtp->ref); /* This is for the failed thread */
6163 janus_streaming_mountpoint_destroy(live_rtp);
6164 return NULL;
6165 }
6166 return live_rtp;
6167 }
6168
6169 /* Helper to create a file/ondemand live source */
6170 janus_streaming_mountpoint *janus_streaming_create_file_source(
6171 uint64_t id, char *id_str, char *name, char *desc, char *metadata, char *filename, gboolean live,
6172 gboolean doaudio, uint8_t acodec, char *artpmap, char *afmtp, gboolean dovideo) {
6173 char id_num[30];
6174 if(!string_ids) {
6175 g_snprintf(id_num, sizeof(id_num), "%"SCNu64, id);
6176 id_str = id_num;
6177 }
6178 if(filename == NULL) {
6179 JANUS_LOG(LOG_ERR, "Can't add 'live' stream, missing filename...\n");
6180 return NULL;
6181 }
6182 if(name == NULL) {
6183 JANUS_LOG(LOG_VERB, "Missing name, will generate a random one...\n");
6184 }
6185 if(!doaudio && !dovideo) {
6186 JANUS_LOG(LOG_ERR, "Can't add 'file' stream, no audio or video have to be streamed...\n");
6187 janus_mutex_lock(&mountpoints_mutex);
6188 g_hash_table_remove(mountpoints_temp, &id);
6189 janus_mutex_unlock(&mountpoints_mutex);
6190 return NULL;
6191 }
6192 /* FIXME We don't support video streaming from file yet */
6193 if(!doaudio || dovideo) {
6194 JANUS_LOG(LOG_ERR, "Can't add 'file' stream, we only support audio file streaming right now...\n");
6195 janus_mutex_lock(&mountpoints_mutex);
6196 g_hash_table_remove(mountpoints_temp, &id);
6197 janus_mutex_unlock(&mountpoints_mutex);
6198 return NULL;
6199 }
6200 /* TODO We should support something more than raw a-Law and mu-Law streams... */
6201 #ifdef HAVE_LIBOGG
6202 if(!strstr(filename, ".opus") && !strstr(filename, ".alaw") && !strstr(filename, ".mulaw")) {
6203 JANUS_LOG(LOG_ERR, "Can't add 'file' stream, unsupported format (we only support Opus and raw mu-Law/a-Law files right now)\n");
6204 #else
6205 if(!strstr(filename, ".alaw") && !strstr(filename, ".mulaw")) {
6206 JANUS_LOG(LOG_ERR, "Can't add 'file' stream, unsupported format (we only support raw mu-Law and a-Law files right now)\n");
6207 #endif
6208 janus_mutex_lock(&mountpoints_mutex);
6209 g_hash_table_remove(mountpoints_temp, &id);
6210 janus_mutex_unlock(&mountpoints_mutex);
6211 return NULL;
6212 }
6213 #ifdef HAVE_LIBOGG
6214 if(strstr(filename, ".opus") && (artpmap == NULL || strstr(artpmap, "opus/48000") == NULL)) {
6215 JANUS_LOG(LOG_ERR, "Can't add 'file' stream, opus file is not associated with an opus rtpmap\n");
6216 janus_mutex_lock(&mountpoints_mutex);
6217 g_hash_table_remove(mountpoints_temp, &id);
6218 janus_mutex_unlock(&mountpoints_mutex);
6219 return NULL;
6220 }
6221 #endif
6222 janus_streaming_mountpoint *file_source = g_malloc0(sizeof(janus_streaming_mountpoint));
6223 file_source->id = id;
6224 file_source->id_str = g_strdup(id_str);
6225 char tempname[255];
6226 if(!name) {
6227 memset(tempname, 0, 255);
6228 g_snprintf(tempname, 255, "mp-%s", file_source->id_str);
6229 } else if(atoi(name) != 0) {
6230 memset(tempname, 0, 255);
6231 g_snprintf(tempname, 255, "mp-%s", name);
6232 name = NULL;
6233 }
6234 file_source->name = g_strdup(name ? name : tempname);
6235 char *description = NULL;
6236 if(desc != NULL)
6237 description = g_strdup(desc);
6238 else
6239 description = g_strdup(name ? name : tempname);
6240 file_source->description = description;
6241 file_source->metadata = (metadata ? g_strdup(metadata) : NULL);
6242 file_source->enabled = TRUE;
6243 file_source->active = FALSE;
6244 file_source->audio = TRUE;
6245 file_source->video = FALSE;
6246 file_source->data = FALSE;
6247 file_source->streaming_type = live ? janus_streaming_type_live : janus_streaming_type_on_demand;
6248 file_source->streaming_source = janus_streaming_source_file;
6249 janus_streaming_file_source *file_source_source = g_malloc0(sizeof(janus_streaming_file_source));
6250 file_source_source->filename = g_strdup(filename);
6251 file_source->source = file_source_source;
6252 file_source->source_destroy = (GDestroyNotify) janus_streaming_file_source_free;
6253 if(strstr(filename, ".opus")) {
6254 file_source_source->opus = TRUE;
6255 file_source->codecs.audio_pt = acodec;
6256 file_source->codecs.audio_rtpmap = g_strdup(artpmap);
6257 file_source->codecs.audio_fmtp = afmtp ? g_strdup(afmtp) : NULL;
6258 } else {
6259 file_source->codecs.audio_pt = strstr(filename, ".alaw") ? 8 : 0;
6260 file_source->codecs.audio_rtpmap = g_strdup(strstr(filename, ".alaw") ? "PCMA/8000" : "PCMU/8000");
6261 }
6262 file_source->codecs.video_pt = -1; /* FIXME We don't support video for this type yet */
6263 file_source->codecs.video_rtpmap = NULL;
6264 file_source->viewers = NULL;
6265 g_atomic_int_set(&file_source->destroyed, 0);
6266 janus_refcount_init(&file_source->ref, janus_streaming_mountpoint_free);
6267 janus_mutex_init(&file_source->mutex);
6268 janus_mutex_lock(&mountpoints_mutex);
6269 g_hash_table_insert(mountpoints,
6270 string_ids ? (gpointer)g_strdup(file_source->id_str) : (gpointer)janus_uint64_dup(file_source->id),
6271 file_source);
6272 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)file_source->id_str : (gpointer)&file_source->id);
6273 janus_mutex_unlock(&mountpoints_mutex);
6274 if(live) {
6275 GError *error = NULL;
6276 char tname[16];
6277 g_snprintf(tname, sizeof(tname), "mp %s", file_source->id_str);
6278 janus_refcount_increase(&file_source->ref);
6279 file_source->thread = g_thread_try_new(tname, &janus_streaming_filesource_thread, file_source, &error);
6280 if(error != NULL) {
6281 JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the live filesource thread...\n",
6282 error->code, error->message ? error->message : "??");
6283 g_error_free(error);
6284 janus_refcount_decrease(&file_source->ref); /* This is for the failed thread */
6285 janus_refcount_decrease(&file_source->ref);
6286 return NULL;
6287 }
6288 }
6289 return file_source;
6290 }
6291
6292 #ifdef HAVE_LIBCURL
6293 static size_t janus_streaming_rtsp_curl_callback(void *payload, size_t size, size_t nmemb, void *data) {
6294 size_t realsize = size * nmemb;
6295 janus_streaming_buffer *buf = (struct janus_streaming_buffer *)data;
6296 /* (Re)allocate if needed */
6297 buf->buffer = realloc(buf->buffer, buf->size+realsize+1);
6298 /* Update the buffer */
6299 memcpy(&(buf->buffer[buf->size]), payload, realsize);
6300 buf->size += realsize;
6301 buf->buffer[buf->size] = 0;
6302 /* Done! */
6303 return realsize;
6304 }
6305
6306 static int janus_streaming_rtsp_parse_sdp(const char *buffer, const char *name, const char *media, char *base, int *pt,
6307 char *transport, char *host, char *rtpmap, char *fmtp, char *control, const janus_network_address *iface, multiple_fds *fds) {
6308 /* Start by checking if there's any Content-Base header we should be aware of */
6309 const char *cb = strstr(buffer, "Content-Base:");
6310 if(cb == NULL)
6311 cb = strstr(buffer, "content-base:");
6312 if(cb != NULL) {
6313 cb = strstr(cb, "rtsp://");
6314 const char *crlf = (cb ? strstr(cb, "\r\n") : NULL);
6315 if(crlf != NULL && base != NULL) {
6316 gulong size = (crlf-cb)+1;
6317 if(size > 256)
6318 size = 256;
6319 g_snprintf(base, size, "%s", cb);
6320 if(base[size-2] == '/')
6321 base[size-2] = '\0';
6322 }
6323 }
6324 /* Parse the SDP now */
6325 char pattern[256];
6326 g_snprintf(pattern, sizeof(pattern), "m=%s", media);
6327 char *m = strstr(buffer, pattern);
6328 if(m == NULL) {
6329 JANUS_LOG(LOG_VERB, "[%s] no media %s...\n", name, media);
6330 return -1;
6331 }
6332 sscanf(m, "m=%*s %*d %*s %d", pt);
6333 char *s = strstr(m, "a=control:");
6334 if(s == NULL) {
6335 JANUS_LOG(LOG_ERR, "[%s] no control for %s...\n", name, media);
6336 return -1;
6337 }
6338 sscanf(s, "a=control:%2047s", control);
6339 char *r = strstr(m, "a=rtpmap:");
6340 if(r != NULL) {
6341 if (sscanf(r, "a=rtpmap:%*d%*[ ]%2047[^\r\n]s", rtpmap) != 1) {
6342 JANUS_LOG(LOG_ERR, "[%s] cannot parse %s rtpmap...\n", name, media);
6343 return -1;
6344 }
6345 }
6346 char *f = strstr(m, "a=fmtp:");
6347 if(f != NULL) {
6348 if (sscanf(f, "a=fmtp:%*d%*[ ]%2047[^\r\n]s", fmtp) != 1) {
6349 JANUS_LOG(LOG_ERR, "[%s] cannot parse %s fmtp...\n", name, media);
6350 return -1;
6351 }
6352 }
6353 char *c = strstr(m, "c=IN IP4");
6354 if(c == NULL) {
6355 /* No m-line c= attribute? try in the whole SDP */
6356 c = strstr(buffer, "c=IN IP4");
6357 }
6358 char ip[256];
6359 in_addr_t mcast = INADDR_ANY;
6360 if(c != NULL) {
6361 if(sscanf(c, "c=IN IP4 %255[^/]", ip) != 0) {
6362 memcpy(host, ip, sizeof(ip));
6363 c = strstr(host, "\r\n");
6364 if(c)
6365 *c = '\0';
6366 mcast = inet_addr(ip);
6367 }
6368 }
6369 /* Bind two adjacent ports for RTP and RTCP */
6370 int ports[2];
6371 if(janus_streaming_allocate_port_pair(name, media, mcast, iface, fds, ports)) {
6372 JANUS_LOG(LOG_ERR, "[%s] Bind failed for %s...\n", name, media);
6373 return -1;
6374 }
6375
6376 if(IN_MULTICAST(ntohl(mcast))) {
6377 g_snprintf(transport, 1024, "RTP/AVP/UDP;multicast;client_port=%d-%d", ports[0], ports[1]);
6378 } else {
6379 g_snprintf(transport, 1024, "RTP/AVP/UDP;unicast;client_port=%d-%d", ports[0], ports[1]);
6380 }
6381
6382 return 0;
6383 }
6384
6385 /* Helper function to calculating the minimum value if 'a' is bigger than zero */
6386 static inline gint64 janus_streaming_min_if(gint64 a, gint64 b) {
6387 return a > 0 ? (a > b ? b : a) : b;
6388 }
6389
6390 /* Static helper to connect to an RTSP server, considering we might do this either
6391 * when creating a new mountpoint, or when reconnecting after some failure */
6392 static int janus_streaming_rtsp_connect_to_server(janus_streaming_mountpoint *mp) {
6393 if(mp == NULL)
6394 return -1;
6395 janus_streaming_rtp_source *source = (janus_streaming_rtp_source *)mp->source;
6396 if(source == NULL)
6397 return -1;
6398
6399 char *name = mp->name;
6400 gboolean doaudio = mp->audio;
6401 gboolean dovideo = mp->video;
6402
6403 CURL *curl = curl_easy_init();
6404 if(curl == NULL) {
6405 JANUS_LOG(LOG_ERR, "Can't init CURL\n");
6406 return -1;
6407 }
6408 if(janus_log_level > LOG_INFO)
6409 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
6410 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
6411 curl_easy_setopt(curl, CURLOPT_URL, source->rtsp_url);
6412 curl_easy_setopt(curl, CURLOPT_TIMEOUT, source->rtsp_timeout);
6413 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, source->rtsp_conn_timeout);
6414 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 0L);
6415 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
6416 #if CURL_AT_LEAST_VERSION(7, 66, 0)
6417 curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_RTSP);
6418 curl_easy_setopt(curl, CURLOPT_HTTP09_ALLOWED, 1L);
6419 #endif
6420 /* Any authentication to take into account? */
6421 if(source->rtsp_username && source->rtsp_password) {
6422 /* Point out that digest authentication is only available is libcurl >= 7.45.0 */
6423 if(LIBCURL_VERSION_NUM < 0x072d00) {
6424 JANUS_LOG(LOG_WARN, "RTSP digest authentication unsupported (needs libcurl >= 7.45.0)\n");
6425 }
6426 curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
6427 curl_easy_setopt(curl, CURLOPT_USERNAME, source->rtsp_username);
6428 curl_easy_setopt(curl, CURLOPT_PASSWORD, source->rtsp_password);
6429 }
6430 /* Send an RTSP DESCRIBE */
6431 janus_streaming_buffer *curldata = g_malloc(sizeof(janus_streaming_buffer));
6432 curldata->buffer = g_malloc0(1);
6433 curldata->size = 0;
6434 curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, source->rtsp_url);
6435 curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_DESCRIBE);
6436 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, janus_streaming_rtsp_curl_callback);
6437 curl_easy_setopt(curl, CURLOPT_WRITEDATA, curldata);
6438 curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, janus_streaming_rtsp_curl_callback);
6439 curl_easy_setopt(curl, CURLOPT_HEADERDATA, curldata);
6440 int res = curl_easy_perform(curl);
6441 if(res != CURLE_OK) {
6442 JANUS_LOG(LOG_ERR, "Couldn't send DESCRIBE request: %s\n", curl_easy_strerror(res));
6443 curl_easy_cleanup(curl);
6444 g_free(curldata->buffer);
6445 g_free(curldata);
6446 return -2;
6447 }
6448 long code = 0;
6449 res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
6450 if(res != CURLE_OK) {
6451 JANUS_LOG(LOG_ERR, "Couldn't get DESCRIBE answer: %s\n", curl_easy_strerror(res));
6452 curl_easy_cleanup(curl);
6453 g_free(curldata->buffer);
6454 g_free(curldata);
6455 return -3;
6456 } else if(code != 200) {
6457 JANUS_LOG(LOG_ERR, "Couldn't get DESCRIBE code: %ld\n", code);
6458 curl_easy_cleanup(curl);
6459 g_free(curldata->buffer);
6460 g_free(curldata);
6461 return -4;
6462 }
6463 JANUS_LOG(LOG_VERB, "DESCRIBE answer:%s\n", curldata->buffer);
6464 /* Parse the SDP we just got to figure out the negotiated media */
6465 int vpt = -1;
6466 char vrtpmap[2048];
6467 vrtpmap[0] = '\0';
6468 char vfmtp[2048];
6469 vfmtp[0] = '\0';
6470 char vcontrol[2048];
6471 char uri[1024];
6472 char vtransport[1024];
6473 char vhost[256];
6474 vhost[0] = '\0';
6475 char vbase[256];
6476 vbase[0] = '\0';
6477 int vsport = 0, vsport_rtcp = 0;
6478 multiple_fds video_fds = {-1, -1};
6479
6480 int apt = -1;
6481 char artpmap[2048];
6482 artpmap[0] = '\0';
6483 char afmtp[2048];
6484 afmtp[0] = '\0';
6485 char acontrol[2048];
6486 char atransport[1024];
6487 char ahost[256];
6488 ahost[0] = '\0';
6489 char abase[256];
6490 abase[0] = '\0';
6491 int asport = 0, asport_rtcp = 0;
6492 multiple_fds audio_fds = {-1, -1};
6493
6494 while (!janus_mutex_trylock(&mountpoints_mutex)) {
6495 if(g_atomic_int_get(&mp->destroyed)) {
6496 JANUS_LOG(LOG_WARN, "[%s] Destroying mountpoint while trying to reconnect, aborting\n", mp->name);
6497 curl_easy_cleanup(curl);
6498 g_free(curldata->buffer);
6499 g_free(curldata);
6500 return -8;
6501 }
6502
6503 g_usleep(1000);
6504 }
6505
6506 /* Parse both video and audio first before proceed to setup as curldata will be reused */
6507 int vresult = -1;
6508 if(dovideo) {
6509 vresult = janus_streaming_rtsp_parse_sdp(curldata->buffer, name, "video", vbase, &vpt,
6510 vtransport, vhost, vrtpmap, vfmtp, vcontrol, &source->video_iface, &video_fds);
6511 }
6512 int aresult = -1;
6513 if(doaudio) {
6514 aresult = janus_streaming_rtsp_parse_sdp(curldata->buffer, name, "audio", abase, &apt,
6515 atransport, ahost, artpmap, afmtp, acontrol, &source->audio_iface, &audio_fds);
6516 }
6517 janus_mutex_unlock(&mountpoints_mutex);
6518
6519 if(vresult == -1 && aresult == -1) {
6520 /* Both audio and video failed? Give up... */
6521 curl_easy_cleanup(curl);
6522 g_free(curldata->buffer);
6523 g_free(curldata);
6524 return -7;
6525 }
6526
6527 /* Check if a query string is part of the URL, as that may impact the SETUP request */
6528 char *rtsp_url = source->rtsp_url, *rtsp_querystring = NULL;
6529 char **parts = g_strsplit(source->rtsp_url, "?", 2);
6530 if(parts[0] != NULL) {
6531 rtsp_url = parts[0];
6532 rtsp_querystring = parts[1];
6533 }
6534
6535 if(vresult != -1) {
6536 /* Identify video codec (useful for keyframe detection) */
6537 mp->codecs.video_codec = JANUS_VIDEOCODEC_NONE;
6538 if(strstr(vrtpmap, "vp8") || strstr(vrtpmap, "VP8"))
6539 mp->codecs.video_codec = JANUS_VIDEOCODEC_VP8;
6540 else if(strstr(vrtpmap, "vp9") || strstr(vrtpmap, "VP9"))
6541 mp->codecs.video_codec = JANUS_VIDEOCODEC_VP9;
6542 else if(strstr(vrtpmap, "h264") || strstr(vrtpmap, "H264"))
6543 mp->codecs.video_codec = JANUS_VIDEOCODEC_H264;
6544
6545 /* Send an RTSP SETUP for video */
6546 g_free(curldata->buffer);
6547 curldata->buffer = g_malloc0(1);
6548 curldata->size = 0;
6549 gboolean add_qs = (rtsp_querystring != NULL);
6550 if(add_qs && strstr(vcontrol, rtsp_querystring) != NULL)
6551 add_qs = FALSE;
6552 if(strstr(vcontrol, (strlen(vbase) > 0 ? vbase : rtsp_url)) == vcontrol) {
6553 /* The control attribute already contains the whole URL? */
6554 g_snprintf(uri, sizeof(uri), "%s%s%s", vcontrol,
6555 add_qs ? "?" : "", add_qs ? rtsp_querystring : "");
6556 } else {
6557 /* Append the control attribute to the URL */
6558 g_snprintf(uri, sizeof(uri), "%s/%s%s%s", (strlen(vbase) > 0 ? vbase : rtsp_url),
6559 vcontrol, add_qs ? "?" : "", add_qs ? rtsp_querystring : "");
6560 }
6561 curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, uri);
6562 curl_easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, vtransport);
6563 curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_SETUP);
6564 res = curl_easy_perform(curl);
6565 if(res != CURLE_OK) {
6566 JANUS_LOG(LOG_ERR, "Couldn't send SETUP request: %s\n", curl_easy_strerror(res));
6567 g_strfreev(parts);
6568 curl_easy_cleanup(curl);
6569 g_free(curldata->buffer);
6570 g_free(curldata);
6571 if(video_fds.fd != -1) close(video_fds.fd);
6572 if(video_fds.rtcp_fd != -1) close(video_fds.rtcp_fd);
6573 if(audio_fds.fd != -1) close(audio_fds.fd);
6574 if(audio_fds.rtcp_fd != -1) close(audio_fds.rtcp_fd);
6575 return -5;
6576 } else if(code != 200) {
6577 JANUS_LOG(LOG_ERR, "Couldn't get SETUP code: %ld\n", code);
6578 g_strfreev(parts);
6579 curl_easy_cleanup(curl);
6580 g_free(curldata->buffer);
6581 g_free(curldata);
6582 if(video_fds.fd != -1) close(video_fds.fd);
6583 if(video_fds.rtcp_fd != -1) close(video_fds.rtcp_fd);
6584 if(audio_fds.fd != -1) close(audio_fds.fd);
6585 if(audio_fds.rtcp_fd != -1) close(audio_fds.rtcp_fd);
6586 return -5;
6587 }
6588 JANUS_LOG(LOG_VERB, "SETUP answer:%s\n", curldata->buffer);
6589 /* Parse the RTSP message: we may need Transport and Session */
6590 gboolean success = TRUE;
6591 gchar **parts = g_strsplit(curldata->buffer, "\n", -1);
6592 if(parts) {
6593 int index = 0;
6594 char *line = NULL, *cr = NULL;
6595 while(success && (line = parts[index]) != NULL) {
6596 cr = strchr(line, '\r');
6597 if(cr != NULL)
6598 *cr = '\0';
6599 if(*line == '\0') {
6600 if(cr != NULL)
6601 *cr = '\r';
6602 index++;
6603 continue;
6604 }
6605 if(strlen(line) < 3) {
6606 JANUS_LOG(LOG_ERR, "Invalid RTSP line (%zu bytes): %s\n", strlen(line), line);
6607 success = FALSE;
6608 break;
6609 }
6610 /* Check if this is a Transport or Session header, and if so parse it */
6611 gboolean is_transport = (strstr(line, "Transport:") == line || strstr(line, "transport:") == line);
6612 gboolean is_session = (strstr(line, "Session:") == line || strstr(line, "session:") == line);
6613 if(is_transport || is_session) {
6614 /* There is, iterate on all params */
6615 char *p = line, param[100], *pi = NULL;
6616 int read = 0;
6617 gboolean first = TRUE;
6618 while(sscanf(p, "%99[^;]%n", param, &read) == 1) {
6619 if(first) {
6620 /* Skip */
6621 first = FALSE;
6622 } else {
6623 pi = param;
6624 while(*pi == ' ')
6625 pi++;
6626 char name[50], value[50];
6627 if(sscanf(pi, "%49[a-zA-Z_0-9]=%49s", name, value) == 2) {
6628 if(is_transport) {
6629 if(!strcasecmp(name, "ssrc")) {
6630 /* Take note of the video SSRC */
6631 uint32_t ssrc = strtol(value, NULL, 16);
6632 JANUS_LOG(LOG_VERB, " -- SSRC (video): %"SCNu32"\n", ssrc);
6633 source->video_ssrc = ssrc;
6634 } else if(!strcasecmp(name, "source")) {
6635 /* If we got an address via c-line, replace it */
6636 g_snprintf(vhost, sizeof(vhost), "%s", value);
6637 JANUS_LOG(LOG_VERB, " -- Source (video): %s\n", vhost);
6638 } else if(!strcasecmp(name, "server_port")) {
6639 /* Take note of the server port */
6640 char *dash = NULL;
6641 vsport = strtol(value, &dash, 10);
6642 vsport_rtcp = dash ? strtol(++dash, NULL, 10) : 0;
6643 JANUS_LOG(LOG_VERB, " -- RTP port (video): %d\n", vsport);
6644 JANUS_LOG(LOG_VERB, " -- RTCP port (video): %d\n", vsport_rtcp);
6645 }
6646 } else if(is_session) {
6647 if(!strcasecmp(name, "timeout")) {
6648 /* Take note of the timeout, for keep-alives */
6649 source->ka_timeout = janus_streaming_min_if(source->session_timeout, (gint64)atoi(value) / 2 * G_USEC_PER_SEC);
6650 JANUS_LOG(LOG_VERB, " -- RTSP session timeout (video): %"SCNi64" ms\n", source->ka_timeout / 1000);
6651 }
6652 }
6653 }
6654 }
6655 /* Move to the next param */
6656 p += read;
6657 if(*p != ';')
6658 break;
6659 while(*p == ';')
6660 p++;
6661 }
6662 }
6663 if(cr != NULL)
6664 *cr = '\r';
6665 index++;
6666 }
6667 if(cr != NULL)
6668 *cr = '\r';
6669 g_strfreev(parts);
6670 }
6671 #ifdef HAVE_LIBCURL
6672 #if CURL_AT_LEAST_VERSION(7, 62, 0)
6673 /* If we don't have a host yet (no c-line, no source in Transport), use the server address */
6674 if(strlen(vhost) == 0 || !strcmp(vhost, "0.0.0.0")) {
6675 JANUS_LOG(LOG_WARN, "No c-line or source for RTSP video address, resolving server address...\n");
6676 CURLU *url = curl_url();
6677 if(url != NULL) {
6678 CURLUcode code = curl_url_set(url, CURLUPART_URL, source->rtsp_url, 0);
6679 if(code == 0) {
6680 char *host = NULL;
6681 code = curl_url_get(url, CURLUPART_HOST, &host, 0);
6682 if(code == 0) {
6683 /* Resolve the address */
6684 struct addrinfo *info = NULL, *start = NULL;
6685 janus_network_address addr;
6686 janus_network_address_string_buffer addr_buf;
6687 if(getaddrinfo(host, NULL, NULL, &info) == 0) {
6688 start = info;
6689 while(info != NULL) {
6690 if(janus_network_address_from_sockaddr(info->ai_addr, &addr) == 0 &&
6691 janus_network_address_to_string_buffer(&addr, &addr_buf) == 0) {
6692 /* Resolved */
6693 g_snprintf(vhost, sizeof(vhost), "%s",
6694 janus_network_address_string_from_buffer(&addr_buf));
6695 JANUS_LOG(LOG_VERB, " -- %s\n", vhost);
6696 break;
6697 }
6698 info = info->ai_next;
6699 }
6700 }
6701 if(start)
6702 freeaddrinfo(start);
6703 curl_free(host);
6704 }
6705 }
6706 curl_url_cleanup(url);
6707 }
6708 }
6709 #endif
6710 #endif
6711 if(strlen(vhost) == 0 || !strcmp(vhost, "0.0.0.0")) {
6712 /* Still nothing... */
6713 JANUS_LOG(LOG_WARN, "No host address for the RTSP video stream, no latching will be performed\n");
6714 }
6715 }
6716
6717 if(aresult != -1) {
6718 /* Send an RTSP SETUP for audio */
6719 g_free(curldata->buffer);
6720 curldata->buffer = g_malloc0(1);
6721 curldata->size = 0;
6722 gboolean add_qs = (rtsp_querystring != NULL);
6723 if(add_qs && strstr(acontrol, rtsp_querystring) != NULL)
6724 add_qs = FALSE;
6725 if(strstr(acontrol, (strlen(abase) > 0 ? abase : rtsp_url)) == acontrol) {
6726 /* The control attribute already contains the whole URL? */
6727 g_snprintf(uri, sizeof(uri), "%s%s%s", acontrol,
6728 add_qs ? "?" : "", add_qs ? rtsp_querystring : "");
6729 } else {
6730 /* Append the control attribute to the URL */
6731 g_snprintf(uri, sizeof(uri), "%s/%s%s%s", (strlen(abase) > 0 ? abase : rtsp_url),
6732 acontrol, add_qs ? "?" : "", add_qs ? rtsp_querystring : "");
6733 }
6734 curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, uri);
6735 curl_easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, atransport);
6736 curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_SETUP);
6737 res = curl_easy_perform(curl);
6738 if(res != CURLE_OK) {
6739 JANUS_LOG(LOG_ERR, "Couldn't send SETUP request: %s\n", curl_easy_strerror(res));
6740 g_strfreev(parts);
6741 curl_easy_cleanup(curl);
6742 g_free(curldata->buffer);
6743 g_free(curldata);
6744 if(video_fds.fd != -1) close(video_fds.fd);
6745 if(video_fds.rtcp_fd != -1) close(video_fds.rtcp_fd);
6746 if(audio_fds.fd != -1) close(audio_fds.fd);
6747 if(audio_fds.rtcp_fd != -1) close(audio_fds.rtcp_fd);
6748 return -6;
6749 } else if(code != 200) {
6750 JANUS_LOG(LOG_ERR, "Couldn't get SETUP code: %ld\n", code);
6751 g_strfreev(parts);
6752 curl_easy_cleanup(curl);
6753 g_free(curldata->buffer);
6754 g_free(curldata);
6755 if(video_fds.fd != -1) close(video_fds.fd);
6756 if(video_fds.rtcp_fd != -1) close(video_fds.rtcp_fd);
6757 if(audio_fds.fd != -1) close(audio_fds.fd);
6758 if(audio_fds.rtcp_fd != -1) close(audio_fds.rtcp_fd);
6759 return -6;
6760 }
6761 JANUS_LOG(LOG_VERB, "SETUP answer:%s\n", curldata->buffer);
6762 /* Parse the RTSP message: we may need Transport and Session */
6763 gboolean success = TRUE;
6764 gchar **parts = g_strsplit(curldata->buffer, "\n", -1);
6765 if(parts) {
6766 int index = 0;
6767 char *line = NULL, *cr = NULL;
6768 while(success && (line = parts[index]) != NULL) {
6769 cr = strchr(line, '\r');
6770 if(cr != NULL)
6771 *cr = '\0';
6772 if(*line == '\0') {
6773 if(cr != NULL)
6774 *cr = '\r';
6775 index++;
6776 continue;
6777 }
6778 if(strlen(line) < 3) {
6779 JANUS_LOG(LOG_ERR, "Invalid RTSP line (%zu bytes): %s\n", strlen(line), line);
6780 success = FALSE;
6781 break;
6782 }
6783 /* Check if this is a Transport or Session header, and if so parse it */
6784 gboolean is_transport = (strstr(line, "Transport:") == line || strstr(line, "transport:") == line);
6785 gboolean is_session = (strstr(line, "Session:") == line || strstr(line, "session:") == line);
6786 if(is_transport || is_session) {
6787 /* There is, iterate on all params */
6788 char *p = line, param[100], *pi = NULL;
6789 int read = 0;
6790 gboolean first = TRUE;
6791 while(sscanf(p, "%99[^;]%n", param, &read) == 1) {
6792 if(first) {
6793 /* Skip */
6794 first = FALSE;
6795 } else {
6796 pi = param;
6797 while(*pi == ' ')
6798 pi++;
6799 char name[50], value[50];
6800 if(sscanf(pi, "%49[a-zA-Z_0-9]=%49s", name, value) == 2) {
6801 if(is_transport) {
6802 if(!strcasecmp(name, "ssrc")) {
6803 /* Take note of the audio SSRC */
6804 uint32_t ssrc = strtol(value, NULL, 16);
6805 JANUS_LOG(LOG_VERB, " -- SSRC (audio): %"SCNu32"\n", ssrc);
6806 source->audio_ssrc = ssrc;
6807 } else if(!strcasecmp(name, "source")) {
6808 /* If we got an address via c-line, replace it */
6809 g_snprintf(ahost, sizeof(ahost), "%s", value);
6810 JANUS_LOG(LOG_VERB, " -- Source (audio): %s\n", ahost);
6811 } else if(!strcasecmp(name, "server_port")) {
6812 /* Take note of the server port */
6813 char *dash = NULL;
6814 asport = strtol(value, &dash, 10);
6815 asport_rtcp = dash ? strtol(++dash, NULL, 10) : 0;
6816 JANUS_LOG(LOG_VERB, " -- RTP port (audio): %d\n", asport);
6817 JANUS_LOG(LOG_VERB, " -- RTCP port (audio): %d\n", asport_rtcp);
6818 }
6819 } else if(is_session) {
6820 if(!strcasecmp(name, "timeout")) {
6821 /* Take note of the timeout, for keep-alives */
6822 source->ka_timeout = janus_streaming_min_if(source->session_timeout, (gint64)atoi(value) / 2 * G_USEC_PER_SEC);
6823 JANUS_LOG(LOG_VERB, " -- RTSP session timeout (audio): %"SCNi64" ms\n", source->ka_timeout / 1000);
6824 }
6825 }
6826 }
6827 }
6828 /* Move to the next param */
6829 p += read;
6830 if(*p != ';')
6831 break;
6832 while(*p == ';')
6833 p++;
6834 }
6835 }
6836 if(cr != NULL)
6837 *cr = '\r';
6838 index++;
6839 }
6840 if(cr != NULL)
6841 *cr = '\r';
6842 g_strfreev(parts);
6843 }
6844 /* If we don't have a host yet (no c-line, no source in Transport), use the server address */
6845 if(strlen(ahost) == 0 || !strcmp(ahost, "0.0.0.0")) {
6846 if(strlen(vhost) > 0 && strcmp(vhost, "0.0.0.0")) {
6847 JANUS_LOG(LOG_WARN, "No c-line or source for RTSP audio stream, copying the video address (%s)\n", vhost);
6848 g_snprintf(ahost, sizeof(ahost), "%s", vhost);
6849 } else {
6850 #ifdef HAVE_LIBCURL
6851 #if CURL_AT_LEAST_VERSION(7, 62, 0)
6852 JANUS_LOG(LOG_WARN, "No c-line or source for RTSP audio stream, resolving server address...\n");
6853 CURLU *url = curl_url();
6854 if(url != NULL) {
6855 CURLUcode code = curl_url_set(url, CURLUPART_URL, source->rtsp_url, 0);
6856 if(code == 0) {
6857 char *host = NULL;
6858 code = curl_url_get(url, CURLUPART_HOST, &host, 0);
6859 if(code == 0) {
6860 /* Resolve the address */
6861 struct addrinfo *info = NULL, *start = NULL;
6862 janus_network_address addr;
6863 janus_network_address_string_buffer addr_buf;
6864 if(getaddrinfo(host, NULL, NULL, &info) == 0) {
6865 start = info;
6866 while(info != NULL) {
6867 if(janus_network_address_from_sockaddr(info->ai_addr, &addr) == 0 &&
6868 janus_network_address_to_string_buffer(&addr, &addr_buf) == 0) {
6869 /* Resolved */
6870 g_snprintf(ahost, sizeof(ahost), "%s",
6871 janus_network_address_string_from_buffer(&addr_buf));
6872 JANUS_LOG(LOG_VERB, " -- %s\n", ahost);
6873 break;
6874 }
6875 info = info->ai_next;
6876 }
6877 }
6878 if(start)
6879 freeaddrinfo(start);
6880 curl_free(host);
6881 }
6882 }
6883 curl_url_cleanup(url);
6884 }
6885 #endif
6886 #endif
6887 }
6888 }
6889 if(strlen(ahost) == 0 || !strcmp(ahost, "0.0.0.0")) {
6890 /* Still nothing... */
6891 JANUS_LOG(LOG_WARN, "No host address for the RTSP audio stream, no latching will be performed\n");
6892 }
6893 }
6894 g_strfreev(parts);
6895
6896 /* Update the source (but check if ptype/rtpmap/fmtp need to be overridden) */
6897 if(mp->codecs.audio_pt == -1)
6898 mp->codecs.audio_pt = doaudio ? apt : -1;
6899 if(mp->codecs.audio_rtpmap == NULL)
6900 mp->codecs.audio_rtpmap = (doaudio && strlen(artpmap)) ? g_strdup(artpmap) : NULL;
6901 if(mp->codecs.audio_fmtp == NULL)
6902 mp->codecs.audio_fmtp = (doaudio && strlen(afmtp)) ? g_strdup(afmtp) : NULL;
6903 if(mp->codecs.video_pt == -1)
6904 mp->codecs.video_pt = dovideo ? vpt : -1;
6905 if(mp->codecs.video_rtpmap == NULL)
6906 mp->codecs.video_rtpmap = (dovideo && strlen(vrtpmap)) ? g_strdup(vrtpmap) : NULL;
6907 if(mp->codecs.video_fmtp == NULL)
6908 mp->codecs.video_fmtp = (dovideo && strlen(vfmtp)) ? g_strdup(vfmtp) : NULL;
6909 source->audio_fd = audio_fds.fd;
6910 source->audio_rtcp_fd = audio_fds.rtcp_fd;
6911 source->remote_audio_port = asport;
6912 source->remote_audio_rtcp_port = asport_rtcp;
6913 g_free(source->rtsp_ahost);
6914 if(asport > 0)
6915 source->rtsp_ahost = g_strdup(ahost);
6916 source->video_fd[0] = video_fds.fd;
6917 source->video_rtcp_fd = video_fds.rtcp_fd;
6918 source->remote_video_port = vsport;
6919 source->remote_video_rtcp_port = vsport_rtcp;
6920 g_free(source->rtsp_vhost);
6921 if(vsport > 0)
6922 source->rtsp_vhost = g_strdup(vhost);
6923 source->curl = curl;
6924 source->curldata = curldata;
6925 return 0;
6926 }
6927
6928 /* Helper method to send a latching packet on an RTSP media socket */
6929 static void janus_streaming_rtsp_latch(int fd, char *host, int port, struct sockaddr *remote) {
6930 /* Resolve address to get an IP */
6931 struct addrinfo *res = NULL;
6932 janus_network_address addr;
6933 janus_network_address_string_buffer addr_buf;
6934 if(getaddrinfo(host, NULL, NULL, &res) != 0 ||
6935 janus_network_address_from_sockaddr(res->ai_addr, &addr) != 0 ||
6936 janus_network_address_to_string_buffer(&addr, &addr_buf) != 0) {
6937 JANUS_LOG(LOG_ERR, "Could not resolve %s...\n", host);
6938 if(res)
6939 freeaddrinfo(res);
6940 } else {
6941 freeaddrinfo(res);
6942 /* Prepare the recipient */
6943 struct sockaddr_in remote4 = { 0 };
6944 struct sockaddr_in6 remote6 = { 0 };
6945 socklen_t addrlen = 0;
6946 if(addr.family == AF_INET) {
6947 memset(&remote4, 0, sizeof(remote4));
6948 remote4.sin_family = AF_INET;
6949 remote4.sin_port = htons(port);
6950 memcpy(&remote4.sin_addr, &addr.ipv4, sizeof(addr.ipv4));
6951 remote = (struct sockaddr *)(&remote4);
6952 addrlen = sizeof(remote4);
6953 } else if(addr.family == AF_INET6) {
6954 memset(&remote6, 0, sizeof(remote6));
6955 remote6.sin6_family = AF_INET6;
6956 remote6.sin6_port = htons(port);
6957 memcpy(&remote6.sin6_addr, &addr.ipv6, sizeof(addr.ipv6));
6958 remote6.sin6_addr = addr.ipv6;
6959 remote = (struct sockaddr *)(&remote6);
6960 addrlen = sizeof(remote6);
6961 }
6962 /* Prepare an empty RTP packet */
6963 janus_rtp_header rtp;
6964 memset(&rtp, 0, sizeof(rtp));
6965 rtp.version = 2;
6966 /* Send a couple of latching packets */
6967 (void)sendto(fd, &rtp, 12, 0, remote, addrlen);
6968 (void)sendto(fd, &rtp, 12, 0, remote, addrlen);
6969 }
6970 }
6971
6972 /* Helper to send an RTSP PLAY (either when we create the mountpoint, or when we try reconnecting) */
6973 static int janus_streaming_rtsp_play(janus_streaming_rtp_source *source) {
6974 if(source == NULL || source->curldata == NULL)
6975 return -1;
6976 /* First of all, send a latching packet to the RTSP server port(s) */
6977 struct sockaddr_in6 remote = { 0 };
6978 if(source->remote_audio_port > 0 && source->audio_fd >= 0) {
6979 JANUS_LOG(LOG_VERB, "RTSP audio latching: %s:%d\n", source->rtsp_ahost, source->remote_audio_port);
6980 janus_streaming_rtsp_latch(source->audio_fd, source->rtsp_ahost,
6981 source->remote_audio_port, (struct sockaddr *)&remote);
6982 if(source->remote_audio_rtcp_port > 0 && source->audio_rtcp_fd >= 0) {
6983 JANUS_LOG(LOG_VERB, " -- RTCP: %s:%d\n", source->rtsp_ahost, source->remote_audio_rtcp_port);
6984 janus_streaming_rtsp_latch(source->audio_rtcp_fd, source->rtsp_ahost,
6985 source->remote_audio_rtcp_port, (struct sockaddr *)&source->audio_rtcp_addr);
6986 }
6987 }
6988 if(source->remote_video_port > 0 && source->video_fd[0] >= 0) {
6989 JANUS_LOG(LOG_VERB, "RTSP video latching: %s:%d\n", source->rtsp_vhost, source->remote_video_port);
6990 janus_streaming_rtsp_latch(source->video_fd[0], source->rtsp_vhost,
6991 source->remote_video_port, (struct sockaddr *)&remote);
6992 if(source->remote_video_rtcp_port > 0 && source->video_rtcp_fd >= 0) {
6993 JANUS_LOG(LOG_VERB, " -- RTCP: %s:%d\n", source->rtsp_vhost, source->remote_video_rtcp_port);
6994 janus_streaming_rtsp_latch(source->video_rtcp_fd, source->rtsp_vhost,
6995 source->remote_video_rtcp_port, (struct sockaddr *)&source->video_rtcp_addr);
6996 }
6997 }
6998 /* Send an RTSP PLAY */
6999 janus_mutex_lock(&source->rtsp_mutex);
7000 g_free(source->curldata->buffer);
7001 source->curldata->buffer = g_malloc0(1);
7002 source->curldata->size = 0;
7003 JANUS_LOG(LOG_VERB, "Sending PLAY request...\n");
7004 curl_easy_setopt(source->curl, CURLOPT_RTSP_STREAM_URI, source->rtsp_url);
7005 curl_easy_setopt(source->curl, CURLOPT_RANGE, "npt=0.000-");
7006 curl_easy_setopt(source->curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_PLAY);
7007 int res = curl_easy_perform(source->curl);
7008 if(res != CURLE_OK) {
7009 JANUS_LOG(LOG_ERR, "Couldn't send PLAY request: %s\n", curl_easy_strerror(res));
7010 janus_mutex_unlock(&source->rtsp_mutex);
7011 return -1;
7012 }
7013 JANUS_LOG(LOG_VERB, "PLAY answer:%s\n", source->curldata->buffer);
7014 janus_mutex_unlock(&source->rtsp_mutex);
7015 return 0;
7016 }
7017
7018 /* Helper to create an RTSP source */
7019 janus_streaming_mountpoint *janus_streaming_create_rtsp_source(
7020 uint64_t id, char *id_str, char *name, char *desc, char *metadata,
7021 char *url, char *username, char *password,
7022 gboolean doaudio, int acodec, char *artpmap, char *afmtp,
7023 gboolean dovideo, int vcodec, char *vrtpmap, char *vfmtp, gboolean bufferkf,
7024 const janus_network_address *iface, int threads,
7025 gint64 reconnect_delay, gint64 session_timeout, int rtsp_timeout, int rtsp_conn_timeout,
7026 gboolean error_on_failure) {
7027 char id_num[30];
7028 if(!string_ids) {
7029 g_snprintf(id_num, sizeof(id_num), "%"SCNu64, id);
7030 id_str = id_num;
7031 }
7032 if(url == NULL) {
7033 JANUS_LOG(LOG_ERR, "Can't add 'rtsp' stream, missing url...\n");
7034 return NULL;
7035 }
7036 if(reconnect_delay < 0) {
7037 JANUS_LOG(LOG_ERR, "rtsp_reconnect_delay can't be smaller than zero.\n");
7038 return NULL;
7039 }
7040 if(session_timeout < 0) {
7041 JANUS_LOG(LOG_ERR, "rtsp_session_timeout can't be smaller than zero.\n");
7042 return NULL;
7043 }
7044 if(rtsp_timeout < 0) {
7045 JANUS_LOG(LOG_ERR, "rtsp_timeout can't be smaller than zero.\n");
7046 return NULL;
7047 }
7048 if(rtsp_conn_timeout < 0) {
7049 JANUS_LOG(LOG_ERR, "rtsp_conn_timeout can't be smaller than zero.\n");
7050 return NULL;
7051 }
7052
7053 JANUS_LOG(LOG_VERB, "Audio %s, Video %s\n", doaudio ? "enabled" : "NOT enabled", dovideo ? "enabled" : "NOT enabled");
7054
7055 /* Create an RTP source for the media we'll get */
7056 char tempname[255];
7057 if(name == NULL) {
7058 JANUS_LOG(LOG_VERB, "Missing name, will generate a random one...\n");
7059 memset(tempname, 0, 255);
7060 g_snprintf(tempname, 255, "%s", id_str);
7061 } else if(name[0] == '0' || atoi(name) != 0) {
7062 JANUS_LOG(LOG_VERB, "Names can't start with a number, prefixing it...\n");
7063 memset(tempname, 0, 255);
7064 g_snprintf(tempname, 255, "mp-%s", name);
7065 name = NULL;
7066 }
7067 char *sourcename = g_strdup(name ? name : tempname);
7068 char *description = NULL;
7069 if(desc != NULL) {
7070 description = g_strdup(desc);
7071 } else {
7072 description = g_strdup(name ? name : tempname);
7073 }
7074
7075 janus_network_address nil;
7076 janus_network_address_nullify(&nil);
7077
7078 /* Create the mountpoint and prepare the source */
7079 janus_streaming_mountpoint *live_rtsp = g_malloc0(sizeof(janus_streaming_mountpoint));
7080 live_rtsp->id = id;
7081 live_rtsp->id_str = g_strdup(id_str);
7082 live_rtsp->name = sourcename;
7083 live_rtsp->description = description;
7084 live_rtsp->metadata = (metadata ? g_strdup(metadata) : NULL);
7085 live_rtsp->enabled = TRUE;
7086 live_rtsp->active = FALSE;
7087 live_rtsp->audio = doaudio;
7088 live_rtsp->video = dovideo;
7089 live_rtsp->data = FALSE;
7090 live_rtsp->streaming_type = janus_streaming_type_live;
7091 live_rtsp->streaming_source = janus_streaming_source_rtp;
7092 janus_streaming_rtp_source *live_rtsp_source = g_malloc0(sizeof(janus_streaming_rtp_source));
7093 live_rtsp_source->rtsp = TRUE;
7094 live_rtsp_source->rtsp_url = g_strdup(url);
7095 live_rtsp_source->rtsp_username = username ? g_strdup(username) : NULL;
7096 live_rtsp_source->rtsp_password = password ? g_strdup(password) : NULL;
7097 live_rtsp_source->arc = NULL;
7098 live_rtsp_source->vrc = NULL;
7099 live_rtsp_source->drc = NULL;
7100 live_rtsp_source->audio_fd = -1;
7101 live_rtsp_source->audio_rtcp_fd = -1;
7102 live_rtsp_source->audio_iface = iface ? *iface : nil;
7103 live_rtsp_source->video_fd[0] = -1;
7104 live_rtsp_source->video_fd[1] = -1;
7105 live_rtsp_source->video_fd[2] = -1;
7106 live_rtsp_source->video_rtcp_fd = -1;
7107 live_rtsp_source->video_iface = iface ? *iface : nil;
7108 live_rtsp_source->data_fd = -1;
7109 live_rtsp_source->pipefd[0] = -1;
7110 live_rtsp_source->pipefd[1] = -1;
7111 pipe(live_rtsp_source->pipefd);
7112 live_rtsp_source->data_iface = nil;
7113 live_rtsp_source->keyframe.enabled = bufferkf;
7114 live_rtsp_source->keyframe.latest_keyframe = NULL;
7115 live_rtsp_source->keyframe.temp_keyframe = NULL;
7116 live_rtsp_source->keyframe.temp_ts = 0;
7117 live_rtsp_source->ka_timeout = session_timeout;
7118 live_rtsp_source->reconnect_delay = reconnect_delay;
7119 live_rtsp_source->session_timeout = session_timeout;
7120 live_rtsp_source->rtsp_timeout = rtsp_timeout;
7121 live_rtsp_source->rtsp_conn_timeout = rtsp_conn_timeout;
7122 janus_mutex_init(&live_rtsp_source->keyframe.mutex);
7123 live_rtsp_source->reconnect_timer = 0;
7124 janus_mutex_init(&live_rtsp_source->rtsp_mutex);
7125 live_rtsp->source = live_rtsp_source;
7126 live_rtsp->source_destroy = (GDestroyNotify) janus_streaming_rtp_source_free;
7127 live_rtsp->viewers = NULL;
7128 g_atomic_int_set(&live_rtsp->destroyed, 0);
7129 janus_refcount_init(&live_rtsp->ref, janus_streaming_mountpoint_free);
7130 janus_mutex_init(&live_rtsp->mutex);
7131 /* We may have to override the payload type and/or rtpmap and/or fmtp for audio and/or video */
7132 live_rtsp->codecs.audio_pt = doaudio ? acodec : -1;
7133 live_rtsp->codecs.audio_rtpmap = doaudio ? (artpmap ? g_strdup(artpmap) : NULL) : NULL;
7134 live_rtsp->codecs.audio_fmtp = doaudio ? (afmtp ? g_strdup(afmtp) : NULL) : NULL;
7135 live_rtsp->codecs.video_pt = dovideo ? vcodec : -1;
7136 live_rtsp->codecs.video_rtpmap = dovideo ? (vrtpmap ? g_strdup(vrtpmap) : NULL) : NULL;
7137 live_rtsp->codecs.video_fmtp = dovideo ? (vfmtp ? g_strdup(vfmtp) : NULL) : NULL;
7138 /* If we need to return an error on failure, try connecting right now */
7139 if(error_on_failure) {
7140 /* Now connect to the RTSP server */
7141 if(janus_streaming_rtsp_connect_to_server(live_rtsp) < 0) {
7142 /* Error connecting, get rid of the mountpoint */
7143 janus_mutex_lock(&mountpoints_mutex);
7144 g_hash_table_remove(mountpoints_temp, &id);
7145 janus_mutex_unlock(&mountpoints_mutex);
7146 janus_refcount_decrease(&live_rtsp->ref);
7147 return NULL;
7148 }
7149 /* Send an RTSP PLAY, now */
7150 if(janus_streaming_rtsp_play(live_rtsp_source) < 0) {
7151 /* Error trying to play, get rid of the mountpoint */
7152 janus_mutex_lock(&mountpoints_mutex);
7153 g_hash_table_remove(mountpoints_temp, &id);
7154 janus_mutex_unlock(&mountpoints_mutex);
7155 janus_refcount_decrease(&live_rtsp->ref);
7156 return NULL;
7157 }
7158 }
7159 /* If we need helper threads, spawn them now */
7160 GError *error = NULL;
7161 char tname[16];
7162 if(threads > 0) {
7163 int i=0;
7164 for(i=0; i<threads; i++) {
7165 janus_streaming_helper *helper = g_malloc0(sizeof(janus_streaming_helper));
7166 helper->id = i+1;
7167 helper->mp = live_rtsp;
7168 helper->queued_packets = g_async_queue_new_full((GDestroyNotify)janus_streaming_rtp_relay_packet_free);
7169 janus_mutex_init(&helper->mutex);
7170 janus_refcount_init(&helper->ref, janus_streaming_helper_free);
7171 live_rtsp->helper_threads++;
7172 /* Spawn a thread and add references */
7173 g_snprintf(tname, sizeof(tname), "help %u-%"SCNu64, helper->id, live_rtsp->id);
7174 janus_refcount_increase(&live_rtsp->ref);
7175 janus_refcount_increase(&helper->ref);
7176 helper->thread = g_thread_try_new(tname, &janus_streaming_helper_thread, helper, &error);
7177 if(error != NULL) {
7178 JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the helper thread...\n",
7179 error->code, error->message ? error->message : "??");
7180 g_error_free(error);
7181 janus_refcount_decrease(&live_rtsp->ref); /* This is for the helper thread */
7182 g_async_queue_unref(helper->queued_packets);
7183 janus_refcount_decrease(&helper->ref);
7184 /* This extra unref is for the init */
7185 janus_refcount_decrease(&helper->ref);
7186 janus_mutex_lock(&mountpoints_mutex);
7187 g_hash_table_remove(mountpoints_temp, &id);
7188 janus_mutex_unlock(&mountpoints_mutex);
7189 janus_refcount_decrease(&live_rtsp->ref);
7190 return NULL;
7191 }
7192 janus_refcount_increase(&helper->ref);
7193 live_rtsp->threads = g_list_append(live_rtsp->threads, helper);
7194 }
7195 }
7196 /* Finally, start the thread that will receive the media packets */
7197 g_snprintf(tname, sizeof(tname), "mp %s", live_rtsp->id_str);
7198 janus_refcount_increase(&live_rtsp->ref);
7199 live_rtsp->thread = g_thread_try_new(tname, &janus_streaming_relay_thread, live_rtsp, &error);
7200 if(error != NULL) {
7201 JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the RTSP thread...\n",
7202 error->code, error->message ? error->message : "??");
7203 g_error_free(error);
7204 janus_mutex_lock(&mountpoints_mutex);
7205 g_hash_table_remove(mountpoints_temp, &id);
7206 janus_mutex_unlock(&mountpoints_mutex);
7207 janus_refcount_decrease(&live_rtsp->ref); /* This is for the failed thread */
7208 janus_refcount_decrease(&live_rtsp->ref);
7209 return NULL;
7210 }
7211 janus_mutex_lock(&mountpoints_mutex);
7212 g_hash_table_insert(mountpoints,
7213 string_ids ? (gpointer)g_strdup(live_rtsp->id_str) : (gpointer)janus_uint64_dup(live_rtsp->id),
7214 live_rtsp);
7215 g_hash_table_remove(mountpoints_temp, string_ids ? (gpointer)live_rtsp->id_str : (gpointer)&live_rtsp->id);
7216 janus_mutex_unlock(&mountpoints_mutex);
7217 return live_rtsp;
7218 }
7219 #else
7220 /* Helper to create an RTSP source */
7221 janus_streaming_mountpoint *janus_streaming_create_rtsp_source(
7222 uint64_t id, char *id_str, char *name, char *desc, char *metadata,
7223 char *url, char *username, char *password,
7224 gboolean doaudio, int acodec, char *audiortpmap, char *audiofmtp,
7225 gboolean dovideo, int vcodec, char *videortpmap, char *videofmtp, gboolean bufferkf,
7226 const janus_network_address *iface, int threads,
7227 gint64 reconnect_delay, gint64 session_timeout, int rtsp_timeout, int rtsp_conn_timeout,
7228 gboolean error_on_failure) {
7229 JANUS_LOG(LOG_ERR, "RTSP need libcurl\n");
7230 return NULL;
7231 }
7232 #endif
7233
7234 /* Thread to send RTP packets from a file (on demand) */
7235 static void *janus_streaming_ondemand_thread(void *data) {
7236 JANUS_LOG(LOG_VERB, "Filesource (on demand) RTP thread starting...\n");
7237 janus_streaming_session *session = (janus_streaming_session *)data;
7238 if(!session) {
7239 JANUS_LOG(LOG_ERR, "Invalid session!\n");
7240 g_thread_unref(g_thread_self());
7241 return NULL;
7242 }
7243 janus_streaming_mountpoint *mountpoint = session->mountpoint;
7244 if(!mountpoint) {
7245 JANUS_LOG(LOG_ERR, "Invalid mountpoint!\n");
7246 janus_refcount_decrease(&session->ref);
7247 g_thread_unref(g_thread_self());
7248 return NULL;
7249 }
7250 if(mountpoint->streaming_source != janus_streaming_source_file) {
7251 JANUS_LOG(LOG_ERR, "[%s] Not an file source mountpoint!\n", mountpoint->name);
7252 janus_refcount_decrease(&session->ref);
7253 janus_refcount_decrease(&mountpoint->ref);
7254 g_thread_unref(g_thread_self());
7255 return NULL;
7256 }
7257 if(mountpoint->streaming_type != janus_streaming_type_on_demand) {
7258 JANUS_LOG(LOG_ERR, "[%s] Not an on-demand file source mountpoint!\n", mountpoint->name);
7259 janus_refcount_decrease(&session->ref);
7260 janus_refcount_decrease(&mountpoint->ref);
7261 g_thread_unref(g_thread_self());
7262 return NULL;
7263 }
7264 janus_streaming_file_source *source = mountpoint->source;
7265 if(source == NULL || source->filename == NULL) {
7266 JANUS_LOG(LOG_ERR, "[%s] Invalid file source mountpoint!\n", mountpoint->name);
7267 janus_refcount_decrease(&session->ref);
7268 janus_refcount_decrease(&mountpoint->ref);
7269 g_thread_unref(g_thread_self());
7270 return NULL;
7271 }
7272 JANUS_LOG(LOG_VERB, "[%s] Opening file source %s...\n", mountpoint->name, source->filename);
7273 FILE *audio = fopen(source->filename, "rb");
7274 if(!audio) {
7275 JANUS_LOG(LOG_ERR, "[%s] Ooops, audio file missing!\n", mountpoint->name);
7276 janus_refcount_decrease(&session->ref);
7277 janus_refcount_decrease(&mountpoint->ref);
7278 g_thread_unref(g_thread_self());
7279 return NULL;
7280 }
7281 char *name = g_strdup(mountpoint->name ? mountpoint->name : "??");
7282 JANUS_LOG(LOG_VERB, "[%s] Streaming audio file: %s\n", name, source->filename);
7283
7284 #ifdef HAVE_LIBOGG
7285 /* Make sure that, if this is an .opus file, we can open it */
7286 janus_streaming_opus_context opusctx = { 0 };
7287 if(source->opus) {
7288 opusctx.name = name;
7289 opusctx.filename = source->filename;
7290 opusctx.file = audio;
7291 if(janus_streaming_opus_context_init(&opusctx) < 0) {
7292 g_free(name);
7293 fclose(audio);
7294 janus_refcount_decrease(&session->ref);
7295 janus_refcount_decrease(&mountpoint->ref);
7296 g_thread_unref(g_thread_self());
7297 return NULL;
7298 }
7299 }
7300 #endif
7301
7302 /* Buffer */
7303 char buf[1500];
7304 memset(buf, 0, sizeof(buf));
7305 /* Set up RTP */
7306 guint16 seq = 1;
7307 guint32 ts = 0;
7308 janus_rtp_header *header = (janus_rtp_header *)buf;
7309 header->version = 2;
7310 header->markerbit = 1;
7311 header->type = mountpoint->codecs.audio_pt;
7312 header->seq_number = htons(seq);
7313 header->timestamp = htonl(ts);
7314 header->ssrc = htonl(1); /* The gateway will fix this anyway */
7315 /* Timer */
7316 struct timeval now, before;
7317 gettimeofday(&before, NULL);
7318 now.tv_sec = before.tv_sec;
7319 now.tv_usec = before.tv_usec;
7320 time_t passed, d_s, d_us;
7321 /* Loop */
7322 gint read = 0;
7323 #ifdef HAVE_LIBOGG
7324 const gint plen = (sizeof(buf)-RTP_HEADER_SIZE);
7325 #endif
7326 janus_streaming_rtp_relay_packet packet;
7327 while(!g_atomic_int_get(&stopping) && !g_atomic_int_get(&mountpoint->destroyed) &&
7328 !g_atomic_int_get(&session->stopping) && !g_atomic_int_get(&session->destroyed)) {
7329 /* See if it's time to prepare a frame */
7330 gettimeofday(&now, NULL);
7331 d_s = now.tv_sec - before.tv_sec;
7332 d_us = now.tv_usec - before.tv_usec;
7333 if(d_us < 0) {
7334 d_us += 1000000;
7335 --d_s;
7336 }
7337 passed = d_s*1000000 + d_us;
7338 if(passed < 18000) { /* Let's wait about 18ms */
7339 g_usleep(5000);
7340 continue;
7341 }
7342 /* Update the reference time */
7343 before.tv_usec += 20000;
7344 if(before.tv_usec > 1000000) {
7345 before.tv_sec++;
7346 before.tv_usec -= 1000000;
7347 }
7348 /* If not started or paused, wait some more */
7349 if(!g_atomic_int_get(&session->started) || g_atomic_int_get(&session->paused) || !mountpoint->enabled)
7350 continue;
7351 if(source->opus) {
7352 #ifdef HAVE_LIBOGG
7353 /* Get the next frame from the Opus file */
7354 read = janus_streaming_opus_context_read(&opusctx, buf + RTP_HEADER_SIZE, plen);
7355 #endif
7356 } else {
7357 /* Read frame from file... */
7358 read = fread(buf + RTP_HEADER_SIZE, sizeof(char), 160, audio);
7359 if(feof(audio)) {
7360 /* FIXME We're doing this forever... should this be configurable? */
7361 JANUS_LOG(LOG_VERB, "[%s] Rewind! (%s)\n", name, source->filename);
7362 fseek(audio, 0, SEEK_SET);
7363 continue;
7364 }
7365 }
7366 if(read < 0)
7367 break;
7368 if(mountpoint->active == FALSE)
7369 mountpoint->active = TRUE;
7370 /* Relay to the listener */
7371 packet.data = header;
7372 packet.length = RTP_HEADER_SIZE + read;
7373 packet.is_rtp = TRUE;
7374 packet.is_video = FALSE;
7375 packet.is_keyframe = FALSE;
7376 /* Backup the actual payload type, timestamp and sequence number */
7377 packet.ptype = packet.data->type;
7378 packet.timestamp = ntohl(packet.data->timestamp);
7379 packet.seq_number = ntohs(packet.data->seq_number);
7380 /* Go! */
7381 janus_streaming_relay_rtp_packet(session, &packet);
7382 /* Update header */
7383 seq++;
7384 header->seq_number = htons(seq);
7385 ts += (source->opus ? 960 : 160);
7386 header->timestamp = htonl(ts);
7387 header->markerbit = 0;
7388 }
7389 JANUS_LOG(LOG_VERB, "[%s] Leaving filesource (ondemand) thread\n", name);
7390 #ifdef HAVE_LIBOGG
7391 if(source->opus)
7392 janus_streaming_opus_context_cleanup(&opusctx);
7393 #endif
7394 g_free(name);
7395 fclose(audio);
7396 janus_refcount_decrease(&session->ref);
7397 janus_refcount_decrease(&mountpoint->ref);
7398 g_thread_unref(g_thread_self());
7399 return NULL;
7400 }
7401
7402 /* Thread to send RTP packets from a file (live) */
7403 static void *janus_streaming_filesource_thread(void *data) {
7404 JANUS_LOG(LOG_VERB, "Filesource (live) thread starting...\n");
7405 janus_streaming_mountpoint *mountpoint = (janus_streaming_mountpoint *)data;
7406 if(!mountpoint) {
7407 JANUS_LOG(LOG_ERR, "Invalid mountpoint!\n");
7408 return NULL;
7409 }
7410 if(mountpoint->streaming_source != janus_streaming_source_file) {
7411 JANUS_LOG(LOG_ERR, "[%s] Not an file source mountpoint!\n", mountpoint->name);
7412 janus_refcount_decrease(&mountpoint->ref);
7413 return NULL;
7414 }
7415 if(mountpoint->streaming_type != janus_streaming_type_live) {
7416 JANUS_LOG(LOG_ERR, "[%s] Not a live file source mountpoint!\n", mountpoint->name);
7417 janus_refcount_decrease(&mountpoint->ref);
7418 return NULL;
7419 }
7420 janus_streaming_file_source *source = mountpoint->source;
7421 if(source == NULL || source->filename == NULL) {
7422 JANUS_LOG(LOG_ERR, "[%s] Invalid file source mountpoint!\n", mountpoint->name);
7423 janus_refcount_decrease(&mountpoint->ref);
7424 return NULL;
7425 }
7426 JANUS_LOG(LOG_VERB, "[%s] Opening file source %s...\n", mountpoint->name, source->filename);
7427 FILE *audio = fopen(source->filename, "rb");
7428 if(!audio) {
7429 JANUS_LOG(LOG_ERR, "[%s] Ooops, audio file missing!\n", mountpoint->name);
7430 janus_refcount_decrease(&mountpoint->ref);
7431 return NULL;
7432 }
7433 char *name = g_strdup(mountpoint->name ? mountpoint->name : "??");
7434 JANUS_LOG(LOG_VERB, "[%s] Streaming audio file: %s\n", mountpoint->name, source->filename);
7435
7436 #ifdef HAVE_LIBOGG
7437 /* Make sure that, if this is an .opus file, we can open it */
7438 janus_streaming_opus_context opusctx = { 0 };
7439 if(source->opus) {
7440 opusctx.name = name;
7441 opusctx.filename = source->filename;
7442 opusctx.file = audio;
7443 if(janus_streaming_opus_context_init(&opusctx) < 0) {
7444 g_free(name);
7445 fclose(audio);
7446 janus_refcount_decrease(&mountpoint->ref);
7447 g_thread_unref(g_thread_self());
7448 return NULL;
7449 }
7450 }
7451 #endif
7452
7453 /* Buffer */
7454 char buf[1500];
7455 memset(buf, 0, sizeof(buf));
7456 /* Set up RTP */
7457 guint16 seq = 1;
7458 guint32 ts = 0;
7459 janus_rtp_header *header = (janus_rtp_header *)buf;
7460 header->version = 2;
7461 header->markerbit = 1;
7462 header->type = mountpoint->codecs.audio_pt;
7463 header->seq_number = htons(seq);
7464 header->timestamp = htonl(ts);
7465 header->ssrc = htonl(1); /* The Janus core will fix this anyway */
7466 /* Timer */
7467 struct timeval now, before;
7468 gettimeofday(&before, NULL);
7469 now.tv_sec = before.tv_sec;
7470 now.tv_usec = before.tv_usec;
7471 time_t passed, d_s, d_us;
7472 /* Loop */
7473 gint read = 0;
7474 #ifdef HAVE_LIBOGG
7475 const gint plen = (sizeof(buf)-RTP_HEADER_SIZE);
7476 #endif
7477 janus_streaming_rtp_relay_packet packet;
7478 while(!g_atomic_int_get(&stopping) && !g_atomic_int_get(&mountpoint->destroyed)) {
7479 /* See if it's time to prepare a frame */
7480 gettimeofday(&now, NULL);
7481 d_s = now.tv_sec - before.tv_sec;
7482 d_us = now.tv_usec - before.tv_usec;
7483 if(d_us < 0) {
7484 d_us += 1000000;
7485 --d_s;
7486 }
7487 passed = d_s*1000000 + d_us;
7488 if(passed < 18000) { /* Let's wait about 18ms */
7489 g_usleep(5000);
7490 continue;
7491 }
7492 /* Update the reference time */
7493 before.tv_usec += 20000;
7494 if(before.tv_usec > 1000000) {
7495 before.tv_sec++;
7496 before.tv_usec -= 1000000;
7497 }
7498 /* If paused, wait some more */
7499 if(!mountpoint->enabled)
7500 continue;
7501 if(source->opus) {
7502 #ifdef HAVE_LIBOGG
7503 /* Get the next frame from the Opus file */
7504 read = janus_streaming_opus_context_read(&opusctx, buf + RTP_HEADER_SIZE, plen);
7505 #endif
7506 } else {
7507 /* Read frame from file... */
7508 read = fread(buf + RTP_HEADER_SIZE, sizeof(char), 160, audio);
7509 if(feof(audio)) {
7510 /* FIXME We're doing this forever... should this be configurable? */
7511 JANUS_LOG(LOG_VERB, "[%s] Rewind! (%s)\n", name, source->filename);
7512 fseek(audio, 0, SEEK_SET);
7513 continue;
7514 }
7515 }
7516 if(read < 0)
7517 break;
7518 if(mountpoint->active == FALSE)
7519 mountpoint->active = TRUE;
7520 /* Relay on all sessions */
7521 packet.data = header;
7522 packet.length = RTP_HEADER_SIZE + read;
7523 packet.is_rtp = TRUE;
7524 packet.is_video = FALSE;
7525 packet.is_keyframe = FALSE;
7526 /* Backup the actual payload type, timestamp and sequence number */
7527 packet.ptype = packet.data->type;
7528 packet.timestamp = ntohl(packet.data->timestamp);
7529 packet.seq_number = ntohs(packet.data->seq_number);
7530 /* Go! */
7531 janus_mutex_lock_nodebug(&mountpoint->mutex);
7532 g_list_foreach(mountpoint->viewers, janus_streaming_relay_rtp_packet, &packet);
7533 janus_mutex_unlock_nodebug(&mountpoint->mutex);
7534 /* Update header */
7535 seq++;
7536 header->seq_number = htons(seq);
7537 ts += (source->opus ? 960 : 160);
7538 header->timestamp = htonl(ts);
7539 header->markerbit = 0;
7540 }
7541 JANUS_LOG(LOG_VERB, "[%s] Leaving filesource (live) thread\n", name);
7542 #ifdef HAVE_LIBOGG
7543 if(source->opus)
7544 janus_streaming_opus_context_cleanup(&opusctx);
7545 #endif
7546 g_free(name);
7547 fclose(audio);
7548 janus_refcount_decrease(&mountpoint->ref);
7549 return NULL;
7550 }
7551
7552 /* Thread to relay RTP frames coming from gstreamer/ffmpeg/others */
7553 static void *janus_streaming_relay_thread(void *data) {
7554 JANUS_LOG(LOG_VERB, "Starting streaming relay thread\n");
7555 janus_streaming_mountpoint *mountpoint = (janus_streaming_mountpoint *)data;
7556 if(!mountpoint) {
7557 JANUS_LOG(LOG_ERR, "Invalid mountpoint!\n");
7558 return NULL;
7559 }
7560 if(mountpoint->streaming_source != janus_streaming_source_rtp) {
7561 janus_refcount_decrease(&mountpoint->ref);
7562 JANUS_LOG(LOG_ERR, "[%s] Not an RTP source mountpoint!\n", mountpoint->name);
7563 return NULL;
7564 }
7565 janus_streaming_rtp_source *source = mountpoint->source;
7566 if(source == NULL) {
7567 JANUS_LOG(LOG_ERR, "[%s] Invalid RTP source mountpoint!\n", mountpoint->name);
7568 janus_refcount_decrease(&mountpoint->ref);
7569 return NULL;
7570 }
7571
7572 /* Add a reference to the helper threads, if needed */
7573 if(mountpoint->helper_threads > 0) {
7574 GList *l = mountpoint->threads;
7575 while(l) {
7576 janus_streaming_helper *ht = (janus_streaming_helper *)l->data;
7577 janus_refcount_increase(&ht->ref);
7578 l = l->next;
7579 }
7580 }
7581
7582 int audio_fd = source->audio_fd;
7583 int video_fd[3] = {source->video_fd[0], source->video_fd[1], source->video_fd[2]};
7584 int data_fd = source->data_fd;
7585 int pipe_fd = source->pipefd[0];
7586 int audio_rtcp_fd = source->audio_rtcp_fd;
7587 int video_rtcp_fd = source->video_rtcp_fd;
7588 char *name = g_strdup(mountpoint->name ? mountpoint->name : "??");
7589 /* Needed to fix seq and ts */
7590 uint32_t ssrc = 0, a_last_ssrc = 0, v_last_ssrc[3] = {0, 0, 0};
7591 /* File descriptors */
7592 socklen_t addrlen;
7593 struct sockaddr_storage remote;
7594 int resfd = 0, bytes = 0;
7595 struct pollfd fds[8];
7596 char buffer[1500];
7597 memset(buffer, 0, 1500);
7598 #ifdef HAVE_LIBCURL
7599 /* In case this is an RTSP restreamer, we may have to send keep-alives from time to time */
7600 gint64 now = janus_get_monotonic_time(), before = now, ka_timeout = 0;
7601 if(source->rtsp) {
7602 source->reconnect_timer = now;
7603 ka_timeout = source->ka_timeout;
7604 }
7605 #endif
7606 /* Loop */
7607 int num = 0;
7608 janus_streaming_rtp_relay_packet packet;
7609 while(!g_atomic_int_get(&stopping) && !g_atomic_int_get(&mountpoint->destroyed)) {
7610 #ifdef HAVE_LIBCURL
7611 /* Let's check regularly if the RTSP server seems to be gone */
7612 if(source->rtsp) {
7613 if(source->reconnecting) {
7614 /* We're still reconnecting, wait some more */
7615 g_usleep(250000);
7616 continue;
7617 }
7618 now = janus_get_monotonic_time();
7619 if(!source->reconnecting && (now - source->reconnect_timer > source->reconnect_delay)) {
7620 /* Assume the RTSP server has gone and schedule a reconnect */
7621 JANUS_LOG(LOG_WARN, "[%s] %"SCNi64"s passed with no media, trying to reconnect the RTSP stream\n",
7622 name, (now - source->reconnect_timer)/G_USEC_PER_SEC);
7623 audio_fd = -1;
7624 video_fd[0] = -1;
7625 video_fd[1] = -1;
7626 video_fd[2] = -1;
7627 data_fd = -1;
7628 source->reconnect_timer = now;
7629 source->reconnecting = TRUE;
7630 /* Let's clean up the source first */
7631 curl_easy_cleanup(source->curl);
7632 source->curl = NULL;
7633 if(source->curldata)
7634 g_free(source->curldata->buffer);
7635 g_free(source->curldata);
7636 source->curldata = NULL;
7637 if(source->audio_fd > -1) {
7638 close(source->audio_fd);
7639 }
7640 source->audio_fd = -1;
7641 if(source->video_fd[0] > -1) {
7642 close(source->video_fd[0]);
7643 }
7644 source->video_fd[0] = -1;
7645 if(source->video_fd[1] > -1) {
7646 close(source->video_fd[1]);
7647 }
7648 source->video_fd[1] = -1;
7649 if(source->video_fd[2] > -1) {
7650 close(source->video_fd[2]);
7651 }
7652 source->video_fd[2] = -1;
7653 if(source->data_fd > -1) {
7654 close(source->data_fd);
7655 }
7656 source->data_fd = -1;
7657 if(source->audio_rtcp_fd > -1) {
7658 close(source->audio_rtcp_fd);
7659 }
7660 source->audio_rtcp_fd = -1;
7661 if(source->video_rtcp_fd > -1) {
7662 close(source->video_rtcp_fd);
7663 }
7664 source->video_rtcp_fd = -1;
7665 if(g_atomic_int_get(&mountpoint->destroyed))
7666 break;
7667 /* Now let's try to reconnect */
7668 if(janus_streaming_rtsp_connect_to_server(mountpoint) < 0) {
7669 /* Reconnection failed? Let's try again later */
7670 JANUS_LOG(LOG_WARN, "[%s] Reconnection of the RTSP stream failed, trying again in a few seconds...\n", name);
7671 } else {
7672 /* We're connected, let's send a PLAY */
7673 if(janus_streaming_rtsp_play(source) < 0) {
7674 /* Error trying to play? Let's try again later */
7675 JANUS_LOG(LOG_WARN, "[%s] RTSP PLAY failed, trying again in a few seconds...\n", name);
7676 } else {
7677 /* Everything should be back to normal, let's update the file descriptors */
7678 JANUS_LOG(LOG_INFO, "[%s] Reconnected to the RTSP server, streaming again\n", name);
7679 audio_fd = source->audio_fd;
7680 video_fd[0] = source->video_fd[0];
7681 data_fd = source->data_fd;
7682 audio_rtcp_fd = source->audio_rtcp_fd;
7683 video_rtcp_fd = source->video_rtcp_fd;
7684 ka_timeout = source->ka_timeout;
7685 }
7686 }
7687 source->reconnect_timer = janus_get_monotonic_time();
7688 source->reconnecting = FALSE;
7689 continue;
7690 }
7691 }
7692 if(audio_fd < 0 && video_fd[0] < 0 && video_fd[1] < 0 && video_fd[2] < 0 && data_fd < 0) {
7693 /* No socket, we may be in the process of reconnecting, or waiting to reconnect */
7694 g_usleep(source->reconnect_delay);
7695 continue;
7696 }
7697 /* We may also need to occasionally send a OPTIONS request as a keep-alive */
7698 if(ka_timeout > 0) {
7699 /* Let's be conservative and send a OPTIONS when half of the timeout has passed */
7700 now = janus_get_monotonic_time();
7701 if(now-before > ka_timeout && source->curldata) {
7702 JANUS_LOG(LOG_VERB, "[%s] %"SCNi64"s passed, sending OPTIONS\n", name, (now-before)/G_USEC_PER_SEC);
7703 before = now;
7704 /* Send an RTSP OPTIONS */
7705 janus_mutex_lock(&source->rtsp_mutex);
7706 g_free(source->curldata->buffer);
7707 source->curldata->buffer = g_malloc0(1);
7708 source->curldata->size = 0;
7709 curl_easy_setopt(source->curl, CURLOPT_RTSP_STREAM_URI, source->rtsp_url);
7710 curl_easy_setopt(source->curl, CURLOPT_RTSP_REQUEST, (long)CURL_RTSPREQ_OPTIONS);
7711 resfd = curl_easy_perform(source->curl);
7712 if(resfd != CURLE_OK) {
7713 JANUS_LOG(LOG_ERR, "[%s] Couldn't send OPTIONS request: %s\n", name, curl_easy_strerror(resfd));
7714 }
7715 janus_mutex_unlock(&source->rtsp_mutex);
7716 }
7717 }
7718 #endif
7719 /* Any PLI and/or REMB we should send back to the source? */
7720 if(g_atomic_int_get(&source->need_pli))
7721 janus_streaming_rtcp_pli_send(source);
7722 if(source->video_rtcp_fd > -1 && source->lowest_bitrate > 0) {
7723 gint64 now = janus_get_monotonic_time();
7724 if(source->remb_latest == 0)
7725 source->remb_latest = now;
7726 else if(now - source->remb_latest >= G_USEC_PER_SEC)
7727 janus_streaming_rtcp_remb_send(source);
7728 }
7729 /* Prepare poll */
7730 num = 0;
7731 if(audio_fd != -1) {
7732 fds[num].fd = audio_fd;
7733 fds[num].events = POLLIN;
7734 fds[num].revents = 0;
7735 num++;
7736 }
7737 if(video_fd[0] != -1) {
7738 fds[num].fd = video_fd[0];
7739 fds[num].events = POLLIN;
7740 fds[num].revents = 0;
7741 num++;
7742 }
7743 if(video_fd[1] != -1) {
7744 fds[num].fd = video_fd[1];
7745 fds[num].events = POLLIN;
7746 fds[num].revents = 0;
7747 num++;
7748 }
7749 if(video_fd[2] != -1) {
7750 fds[num].fd = video_fd[2];
7751 fds[num].events = POLLIN;
7752 fds[num].revents = 0;
7753 num++;
7754 }
7755 if(data_fd != -1) {
7756 fds[num].fd = data_fd;
7757 fds[num].events = POLLIN;
7758 fds[num].revents = 0;
7759 num++;
7760 }
7761 if(pipe_fd != -1) {
7762 fds[num].fd = pipe_fd;
7763 fds[num].events = POLLIN;
7764 fds[num].revents = 0;
7765 num++;
7766 }
7767 if(audio_rtcp_fd != -1) {
7768 fds[num].fd = audio_rtcp_fd;
7769 fds[num].events = POLLIN;
7770 fds[num].revents = 0;
7771 num++;
7772 }
7773 if(video_rtcp_fd != -1) {
7774 fds[num].fd = video_rtcp_fd;
7775 fds[num].events = POLLIN;
7776 fds[num].revents = 0;
7777 num++;
7778 }
7779 /* Wait for some data */
7780 resfd = poll(fds, num, 1000);
7781 if(resfd < 0) {
7782 if(errno == EINTR) {
7783 JANUS_LOG(LOG_HUGE, "[%s] Got an EINTR (%s), ignoring...\n", name, g_strerror(errno));
7784 continue;
7785 }
7786 JANUS_LOG(LOG_ERR, "[%s] Error polling... %d (%s)\n", name, errno, g_strerror(errno));
7787 mountpoint->enabled = FALSE;
7788 janus_mutex_lock(&source->rec_mutex);
7789 if(source->arc) {
7790 janus_recorder_close(source->arc);
7791 JANUS_LOG(LOG_INFO, "[%s] Closed audio recording %s\n", mountpoint->name, source->arc->filename ? source->arc->filename : "??");
7792 janus_recorder *tmp = source->arc;
7793 source->arc = NULL;
7794 janus_recorder_destroy(tmp);
7795 }
7796 if(source->vrc) {
7797 janus_recorder_close(source->vrc);
7798 JANUS_LOG(LOG_INFO, "[%s] Closed video recording %s\n", mountpoint->name, source->vrc->filename ? source->vrc->filename : "??");
7799 janus_recorder *tmp = source->vrc;
7800 source->vrc = NULL;
7801 janus_recorder_destroy(tmp);
7802 }
7803 if(source->drc) {
7804 janus_recorder_close(source->drc);
7805 JANUS_LOG(LOG_INFO, "[%s] Closed data recording %s\n", mountpoint->name, source->drc->filename ? source->drc->filename : "??");
7806 janus_recorder *tmp = source->drc;
7807 source->drc = NULL;
7808 janus_recorder_destroy(tmp);
7809 }
7810 janus_mutex_unlock(&source->rec_mutex);
7811 break;
7812 } else if(resfd == 0) {
7813 /* No data, keep going */
7814 continue;
7815 }
7816 int i = 0;
7817 for(i=0; i<num; i++) {
7818 if(fds[i].revents & (POLLERR | POLLHUP)) {
7819 /* Socket error? */
7820 JANUS_LOG(LOG_ERR, "[%s] Error polling: %s... %d (%s)\n", name,
7821 fds[i].revents & POLLERR ? "POLLERR" : "POLLHUP", errno, g_strerror(errno));
7822 mountpoint->enabled = FALSE;
7823 janus_mutex_lock(&source->rec_mutex);
7824 if(source->arc) {
7825 janus_recorder_close(source->arc);
7826 JANUS_LOG(LOG_INFO, "[%s] Closed audio recording %s\n", mountpoint->name, source->arc->filename ? source->arc->filename : "??");
7827 janus_recorder *tmp = source->arc;
7828 source->arc = NULL;
7829 janus_recorder_destroy(tmp);
7830 }
7831 if(source->vrc) {
7832 janus_recorder_close(source->vrc);
7833 JANUS_LOG(LOG_INFO, "[%s] Closed video recording %s\n", mountpoint->name, source->vrc->filename ? source->vrc->filename : "??");
7834 janus_recorder *tmp = source->vrc;
7835 source->vrc = NULL;
7836 janus_recorder_destroy(tmp);
7837 }
7838 if(source->drc) {
7839 janus_recorder_close(source->drc);
7840 JANUS_LOG(LOG_INFO, "[%s] Closed data recording %s\n", mountpoint->name, source->drc->filename ? source->drc->filename : "??");
7841 janus_recorder *tmp = source->drc;
7842 source->drc = NULL;
7843 janus_recorder_destroy(tmp);
7844 }
7845 janus_mutex_unlock(&source->rec_mutex);
7846 break;
7847 } else if(fds[i].revents & POLLIN) {
7848 /* Got an RTP or data packet */
7849 if(pipe_fd != -1 && fds[i].fd == pipe_fd) {
7850 /* We're done here */
7851 int code = 0;
7852 bytes = read(pipe_fd, &code, sizeof(int));
7853 JANUS_LOG(LOG_VERB, "[%s] Interrupting mountpoint\n", mountpoint->name);
7854 break;
7855 } else if(audio_fd != -1 && fds[i].fd == audio_fd) {
7856 /* Got something audio (RTP) */
7857 if(mountpoint->active == FALSE)
7858 mountpoint->active = TRUE;
7859 gint64 now = janus_get_monotonic_time();
7860 #ifdef HAVE_LIBCURL
7861 source->reconnect_timer = now;
7862 #endif
7863 addrlen = sizeof(remote);
7864 bytes = recvfrom(audio_fd, buffer, 1500, 0, (struct sockaddr *)&remote, &addrlen);
7865 if(bytes < 0 || !janus_is_rtp(buffer, bytes)) {
7866 /* Failed to read or not an RTP packet? */
7867 continue;
7868 }
7869 janus_rtp_header *rtp = (janus_rtp_header *)buffer;
7870 ssrc = ntohl(rtp->ssrc);
7871 if(source->rtp_collision > 0 && a_last_ssrc && ssrc != a_last_ssrc &&
7872 (now-source->last_received_audio) < (gint64)1000*source->rtp_collision) {
7873 JANUS_LOG(LOG_WARN, "[%s] RTP collision on audio mountpoint, dropping packet (ssrc=%"SCNu32")\n", name, ssrc);
7874 continue;
7875 }
7876 source->last_received_audio = now;
7877 //~ JANUS_LOG(LOG_VERB, "************************\nGot %d bytes on the audio channel...\n", bytes);
7878 /* Do we have a new stream? */
7879 if(ssrc != a_last_ssrc) {
7880 source->audio_ssrc = a_last_ssrc = ssrc;
7881 JANUS_LOG(LOG_INFO, "[%s] New audio stream! (ssrc=%"SCNu32")\n", name, a_last_ssrc);
7882 }
7883 /* If paused, ignore this packet */
7884 if(!mountpoint->enabled && !source->arc)
7885 continue;
7886 /* Is this SRTP? */
7887 if(source->is_srtp) {
7888 int buflen = bytes;
7889 srtp_err_status_t res = srtp_unprotect(source->srtp_ctx, buffer, &buflen);
7890 //~ if(res != srtp_err_status_ok && res != srtp_err_status_replay_fail && res != srtp_err_status_replay_old) {
7891 if(res != srtp_err_status_ok) {
7892 guint32 timestamp = ntohl(rtp->timestamp);
7893 guint16 seq = ntohs(rtp->seq_number);
7894 JANUS_LOG(LOG_ERR, "[%s] Audio SRTP unprotect error: %s (len=%d-->%d, ts=%"SCNu32", seq=%"SCNu16")\n",
7895 name, janus_srtp_error_str(res), bytes, buflen, timestamp, seq);
7896 continue;
7897 }
7898 bytes = buflen;
7899 }
7900 //~ JANUS_LOG(LOG_VERB, " ... parsed RTP packet (ssrc=%u, pt=%u, seq=%u, ts=%u)...\n",
7901 //~ ntohl(rtp->ssrc), rtp->type, ntohs(rtp->seq_number), ntohl(rtp->timestamp));
7902 /* Relay on all sessions */
7903 packet.data = rtp;
7904 packet.length = bytes;
7905 packet.is_rtp = TRUE;
7906 packet.is_video = FALSE;
7907 packet.is_keyframe = FALSE;
7908 packet.data->type = mountpoint->codecs.audio_pt;
7909 /* Is there a recorder? */
7910 janus_rtp_header_update(packet.data, &source->context[0], FALSE, 0);
7911 if(source->askew) {
7912 int ret = janus_rtp_skew_compensate_audio(packet.data, &source->context[0], now);
7913 if(ret < 0) {
7914 JANUS_LOG(LOG_WARN, "[%s] Dropping %d packets, audio source clock is too fast (ssrc=%"SCNu32")\n",
7915 name, -ret, a_last_ssrc);
7916 continue;
7917 } else if(ret > 0) {
7918 JANUS_LOG(LOG_WARN, "[%s] Jumping %d RTP sequence numbers, audio source clock is too slow (ssrc=%"SCNu32")\n",
7919 name, ret, a_last_ssrc);
7920 }
7921 }
7922 if(source->arc) {
7923 packet.data->ssrc = htonl((uint32_t)mountpoint->id);
7924 janus_recorder_save_frame(source->arc, buffer, bytes);
7925 }
7926 if(mountpoint->enabled) {
7927 packet.data->ssrc = htonl(ssrc);
7928 /* Backup the actual payload type, timestamp and sequence number set by the restreamer, in case switching is involved */
7929 packet.ptype = packet.data->type;
7930 packet.timestamp = ntohl(packet.data->timestamp);
7931 packet.seq_number = ntohs(packet.data->seq_number);
7932 /* Go! */
7933 janus_mutex_lock(&mountpoint->mutex);
7934 g_list_foreach(mountpoint->helper_threads == 0 ? mountpoint->viewers : mountpoint->threads,
7935 mountpoint->helper_threads == 0 ? janus_streaming_relay_rtp_packet : janus_streaming_helper_rtprtcp_packet,
7936 &packet);
7937 janus_mutex_unlock(&mountpoint->mutex);
7938 }
7939 continue;
7940 } else if((video_fd[0] != -1 && fds[i].fd == video_fd[0]) ||
7941 (video_fd[1] != -1 && fds[i].fd == video_fd[1]) ||
7942 (video_fd[2] != -1 && fds[i].fd == video_fd[2])) {
7943 /* Got something video (RTP) */
7944 int index = -1;
7945 if(fds[i].fd == video_fd[0])
7946 index = 0;
7947 else if(fds[i].fd == video_fd[1])
7948 index = 1;
7949 else if(fds[i].fd == video_fd[2])
7950 index = 2;
7951 if(mountpoint->active == FALSE)
7952 mountpoint->active = TRUE;
7953 gint64 now = janus_get_monotonic_time();
7954 #ifdef HAVE_LIBCURL
7955 source->reconnect_timer = now;
7956 #endif
7957 addrlen = sizeof(remote);
7958 bytes = recvfrom(fds[i].fd, buffer, 1500, 0, (struct sockaddr *)&remote, &addrlen);
7959 if(bytes < 0 || !janus_is_rtp(buffer, bytes)) {
7960 /* Failed to read or not an RTP packet? */
7961 continue;
7962 }
7963 janus_rtp_header *rtp = (janus_rtp_header *)buffer;
7964 ssrc = ntohl(rtp->ssrc);
7965 if(source->rtp_collision > 0 && v_last_ssrc[index] && ssrc != v_last_ssrc[index] &&
7966 (now-source->last_received_video) < (gint64)1000*source->rtp_collision) {
7967 JANUS_LOG(LOG_WARN, "[%s] RTP collision on video mountpoint, dropping packet (ssrc=%"SCNu32")\n",
7968 name, ssrc);
7969 continue;
7970 }
7971 source->last_received_video = now;
7972 //~ JANUS_LOG(LOG_VERB, "************************\nGot %d bytes on the video channel...\n", bytes);
7973 /* Do we have a new stream? */
7974 if(ssrc != v_last_ssrc[index]) {
7975 v_last_ssrc[index] = ssrc;
7976 if(index == 0)
7977 source->video_ssrc = ssrc;
7978 JANUS_LOG(LOG_INFO, "[%s] New video stream! (ssrc=%"SCNu32", index %d)\n",
7979 name, v_last_ssrc[index], index);
7980 }
7981 /* Is this SRTP? */
7982 if(source->is_srtp) {
7983 int buflen = bytes;
7984 srtp_err_status_t res = srtp_unprotect(source->srtp_ctx, buffer, &buflen);
7985 //~ if(res != srtp_err_status_ok && res != srtp_err_status_replay_fail && res != srtp_err_status_replay_old) {
7986 if(res != srtp_err_status_ok) {
7987 guint32 timestamp = ntohl(rtp->timestamp);
7988 guint16 seq = ntohs(rtp->seq_number);
7989 JANUS_LOG(LOG_ERR, "[%s] Video SRTP unprotect error: %s (len=%d-->%d, ts=%"SCNu32", seq=%"SCNu16")\n",
7990 name, janus_srtp_error_str(res), bytes, buflen, timestamp, seq);
7991 continue;
7992 }
7993 bytes = buflen;
7994 }
7995 /* First of all, let's check if this is (part of) a keyframe that we may need to save it for future reference */
7996 if(source->keyframe.enabled) {
7997 if(source->keyframe.temp_ts > 0 && ntohl(rtp->timestamp) != source->keyframe.temp_ts) {
7998 /* We received the last part of the keyframe, get rid of the old one and use this from now on */
7999 JANUS_LOG(LOG_HUGE, "[%s] ... ... last part of keyframe received! ts=%"SCNu32", %d packets\n",
8000 name, source->keyframe.temp_ts, g_list_length(source->keyframe.temp_keyframe));
8001 source->keyframe.temp_ts = 0;
8002 janus_mutex_lock(&source->keyframe.mutex);
8003 if(source->keyframe.latest_keyframe != NULL)
8004 g_list_free_full(source->keyframe.latest_keyframe, (GDestroyNotify)janus_streaming_rtp_relay_packet_free);
8005 source->keyframe.latest_keyframe = source->keyframe.temp_keyframe;
8006 source->keyframe.temp_keyframe = NULL;
8007 janus_mutex_unlock(&source->keyframe.mutex);
8008 } else if(ntohl(rtp->timestamp) == source->keyframe.temp_ts) {
8009 /* Part of the keyframe we're currently saving, store */
8010 janus_mutex_lock(&source->keyframe.mutex);
8011 JANUS_LOG(LOG_HUGE, "[%s] ... other part of keyframe received! ts=%"SCNu32"\n", name, source->keyframe.temp_ts);
8012 janus_streaming_rtp_relay_packet *pkt = g_malloc0(sizeof(janus_streaming_rtp_relay_packet));
8013 pkt->data = g_malloc(bytes);
8014 memcpy(pkt->data, buffer, bytes);
8015 pkt->data->ssrc = htons(1);
8016 pkt->data->type = mountpoint->codecs.video_pt;
8017 pkt->is_rtp = TRUE;
8018 pkt->is_video = TRUE;
8019 pkt->is_keyframe = TRUE;
8020 pkt->length = bytes;
8021 pkt->ptype = rtp->type;
8022 pkt->timestamp = source->keyframe.temp_ts;
8023 pkt->seq_number = ntohs(rtp->seq_number);
8024 source->keyframe.temp_keyframe = g_list_append(source->keyframe.temp_keyframe, pkt);
8025 janus_mutex_unlock(&source->keyframe.mutex);
8026 } else {
8027 gboolean kf = FALSE;
8028 /* Parse RTP header first */
8029 janus_rtp_header *header = (janus_rtp_header *)buffer;
8030 guint32 timestamp = ntohl(header->timestamp);
8031 guint16 seq = ntohs(header->seq_number);
8032 JANUS_LOG(LOG_HUGE, "Checking if packet (size=%d, seq=%"SCNu16", ts=%"SCNu32") is a key frame...\n",
8033 bytes, seq, timestamp);
8034 int plen = 0;
8035 char *payload = janus_rtp_payload(buffer, bytes, &plen);
8036 if(payload) {
8037 switch(mountpoint->codecs.video_codec) {
8038 case JANUS_VIDEOCODEC_VP8:
8039 kf = janus_vp8_is_keyframe(payload, plen);
8040 break;
8041 case JANUS_VIDEOCODEC_VP9:
8042 kf = janus_vp9_is_keyframe(payload, plen);
8043 break;
8044 case JANUS_VIDEOCODEC_H264:
8045 kf = janus_h264_is_keyframe(payload, plen);
8046 break;
8047 case JANUS_VIDEOCODEC_AV1:
8048 kf = janus_av1_is_keyframe(payload, plen);
8049 break;
8050 case JANUS_VIDEOCODEC_H265:
8051 kf = janus_h265_is_keyframe(payload, plen);
8052 break;
8053 default:
8054 break;
8055 }
8056 if(kf) {
8057 /* New keyframe, start saving it */
8058 source->keyframe.temp_ts = ntohl(rtp->timestamp);
8059 JANUS_LOG(LOG_HUGE, "[%s] New keyframe received! ts=%"SCNu32"\n", name, source->keyframe.temp_ts);
8060 janus_mutex_lock(&source->keyframe.mutex);
8061 janus_streaming_rtp_relay_packet *pkt = g_malloc0(sizeof(janus_streaming_rtp_relay_packet));
8062 pkt->data = g_malloc(bytes);
8063 memcpy(pkt->data, buffer, bytes);
8064 pkt->data->ssrc = htons(1);
8065 pkt->data->type = mountpoint->codecs.video_pt;
8066 pkt->is_rtp = TRUE;
8067 pkt->is_video = TRUE;
8068 pkt->is_keyframe = TRUE;
8069 pkt->length = bytes;
8070 pkt->ptype = rtp->type;
8071 pkt->timestamp = source->keyframe.temp_ts;
8072 pkt->seq_number = ntohs(rtp->seq_number);
8073 source->keyframe.temp_keyframe = g_list_append(source->keyframe.temp_keyframe, pkt);
8074 janus_mutex_unlock(&source->keyframe.mutex);
8075 }
8076 }
8077 }
8078 }
8079 /* If paused, ignore this packet */
8080 if(!mountpoint->enabled && !source->vrc)
8081 continue;
8082 //~ JANUS_LOG(LOG_VERB, " ... parsed RTP packet (ssrc=%u, pt=%u, seq=%u, ts=%u)...\n",
8083 //~ ntohl(rtp->ssrc), rtp->type, ntohs(rtp->seq_number), ntohl(rtp->timestamp));
8084 /* Relay on all sessions */
8085 packet.data = rtp;
8086 packet.length = bytes;
8087 packet.is_rtp = TRUE;
8088 packet.is_video = TRUE;
8089 packet.is_keyframe = FALSE;
8090 packet.simulcast = source->simulcast;
8091 packet.substream = index;
8092 packet.codec = mountpoint->codecs.video_codec;
8093 packet.svc = FALSE;
8094 if(source->svc) {
8095 /* We're doing SVC: let's parse this packet to see which layers are there */
8096 int plen = 0;
8097 char *payload = janus_rtp_payload(buffer, bytes, &plen);
8098 if(payload) {
8099 gboolean found = FALSE;
8100 memset(&packet.svc_info, 0, sizeof(packet.svc_info));
8101 if(janus_vp9_parse_svc(payload, plen, &found, &packet.svc_info) == 0) {
8102 packet.svc = found;
8103 }
8104 }
8105 }
8106 packet.data->type = mountpoint->codecs.video_pt;
8107 /* Is there a recorder? (FIXME notice we only record the first substream, if simulcasting) */
8108 janus_rtp_header_update(packet.data, &source->context[index], TRUE, 0);
8109 if(source->vskew) {
8110 int ret = janus_rtp_skew_compensate_video(packet.data, &source->context[index], now);
8111 if(ret < 0) {
8112 JANUS_LOG(LOG_WARN, "[%s] Dropping %d packets, video source clock is too fast (ssrc=%"SCNu32", index %d)\n",
8113 name, -ret, v_last_ssrc[index], index);
8114 continue;
8115 } else if(ret > 0) {
8116 JANUS_LOG(LOG_WARN, "[%s] Jumping %d RTP sequence numbers, video source clock is too slow (ssrc=%"SCNu32", index %d)\n",
8117 name, ret, v_last_ssrc[index], index);
8118 }
8119 }
8120 if(index == 0 && source->vrc) {
8121 packet.data->ssrc = htonl((uint32_t)mountpoint->id);
8122 janus_recorder_save_frame(source->vrc, buffer, bytes);
8123 }
8124 if (mountpoint->enabled) {
8125 packet.data->ssrc = htonl(ssrc);
8126 /* Backup the actual payload type, timestamp and sequence number set by the restreamer, in case switching is involved */
8127 packet.ptype = packet.data->type;
8128 packet.timestamp = ntohl(packet.data->timestamp);
8129 packet.seq_number = ntohs(packet.data->seq_number);
8130 /* Take note of the simulcast SSRCs */
8131 if(source->simulcast) {
8132 packet.ssrc[0] = v_last_ssrc[0];
8133 packet.ssrc[1] = v_last_ssrc[1];
8134 packet.ssrc[2] = v_last_ssrc[2];
8135 }
8136 /* Go! */
8137 janus_mutex_lock(&mountpoint->mutex);
8138 g_list_foreach(mountpoint->helper_threads == 0 ? mountpoint->viewers : mountpoint->threads,
8139 mountpoint->helper_threads == 0 ? janus_streaming_relay_rtp_packet : janus_streaming_helper_rtprtcp_packet,
8140 &packet);
8141 janus_mutex_unlock(&mountpoint->mutex);
8142 }
8143 continue;
8144 } else if(data_fd != -1 && fds[i].fd == data_fd) {
8145 /* Got something data (text) */
8146 if(mountpoint->active == FALSE)
8147 mountpoint->active = TRUE;
8148 source->last_received_data = janus_get_monotonic_time();
8149 #ifdef HAVE_LIBCURL
8150 source->reconnect_timer = janus_get_monotonic_time();
8151 #endif
8152 addrlen = sizeof(remote);
8153 bytes = recvfrom(data_fd, buffer, 1500, 0, (struct sockaddr *)&remote, &addrlen);
8154 if(bytes < 1) {
8155 /* Failed to read? */
8156 continue;
8157 }
8158 if(!mountpoint->enabled && !source->drc)
8159 continue;
8160 /* Copy the data */
8161 char *data = g_malloc(bytes);
8162 memcpy(data, buffer, bytes);
8163 /* Relay on all sessions */
8164 packet.data = (janus_rtp_header *)data;
8165 packet.length = bytes;
8166 packet.is_rtp = FALSE;
8167 packet.is_data = TRUE;
8168 packet.textdata = source->textdata;
8169 /* Is there a recorder? */
8170 janus_recorder_save_frame(source->drc, data, bytes);
8171 if(mountpoint->enabled) {
8172 /* Are we keeping track of the last message being relayed? */
8173 if(source->buffermsg) {
8174 janus_mutex_lock(&source->buffermsg_mutex);
8175 janus_streaming_rtp_relay_packet *pkt = g_malloc0(sizeof(janus_streaming_rtp_relay_packet));
8176 pkt->data = g_malloc(bytes);
8177 memcpy(pkt->data, data, bytes);
8178 packet.is_rtp = FALSE;
8179 packet.is_data = TRUE;
8180 packet.textdata = source->textdata;
8181 pkt->length = bytes;
8182 janus_mutex_unlock(&source->buffermsg_mutex);
8183 }
8184 /* Go! */
8185 janus_mutex_lock(&mountpoint->mutex);
8186 g_list_foreach(mountpoint->helper_threads == 0 ? mountpoint->viewers : mountpoint->threads,
8187 mountpoint->helper_threads == 0 ? janus_streaming_relay_rtp_packet : janus_streaming_helper_rtprtcp_packet,
8188 &packet);
8189 janus_mutex_unlock(&mountpoint->mutex);
8190 }
8191 g_free(packet.data);
8192 packet.data = NULL;
8193 continue;
8194 } else if(audio_rtcp_fd != -1 && fds[i].fd == audio_rtcp_fd) {
8195 addrlen = sizeof(remote);
8196 bytes = recvfrom(audio_rtcp_fd, buffer, 1500, 0, (struct sockaddr *)&remote, &addrlen);
8197 if(bytes < 0 || (!janus_is_rtp(buffer, bytes) && !janus_is_rtcp(buffer, bytes))) {
8198 /* For latching we need an RTP or RTCP packet */
8199 continue;
8200 }
8201 if(!mountpoint->enabled)
8202 continue;
8203 memcpy(&source->audio_rtcp_addr, &remote, addrlen);
8204 if(!janus_is_rtcp(buffer, bytes)) {
8205 /* Failed to read or not an RTCP packet? */
8206 continue;
8207 }
8208 JANUS_LOG(LOG_HUGE, "[%s] Got audio RTCP feedback: SSRC %"SCNu32"\n",
8209 name, janus_rtcp_get_sender_ssrc(buffer, bytes));
8210 /* Relay on all sessions */
8211 packet.is_rtp = FALSE;
8212 packet.is_video = FALSE;
8213 packet.data = (janus_rtp_header *)buffer;
8214 packet.length = bytes;
8215 /* Go! */
8216 janus_mutex_lock(&mountpoint->mutex);
8217 g_list_foreach(mountpoint->helper_threads == 0 ? mountpoint->viewers : mountpoint->threads,
8218 mountpoint->helper_threads == 0 ? janus_streaming_relay_rtcp_packet : janus_streaming_helper_rtprtcp_packet,
8219 &packet);
8220 janus_mutex_unlock(&mountpoint->mutex);
8221 } else if(video_rtcp_fd != -1 && fds[i].fd == video_rtcp_fd) {
8222 addrlen = sizeof(remote);
8223 bytes = recvfrom(video_rtcp_fd, buffer, 1500, 0, (struct sockaddr *)&remote, &addrlen);
8224 if(bytes < 0 || (!janus_is_rtp(buffer, bytes) && !janus_is_rtcp(buffer, bytes))) {
8225 /* For latching we need an RTP or RTCP packet */
8226 continue;
8227 }
8228 if(!mountpoint->enabled)
8229 continue;
8230 memcpy(&source->video_rtcp_addr, &remote, addrlen);
8231 if(!janus_is_rtcp(buffer, bytes)) {
8232 /* Failed to read or not an RTCP packet? */
8233 continue;
8234 }
8235 JANUS_LOG(LOG_HUGE, "[%s] Got video RTCP feedback: SSRC %"SCNu32"\n",
8236 name, janus_rtcp_get_sender_ssrc(buffer, bytes));
8237 /* Relay on all sessions */
8238 packet.is_rtp = FALSE;
8239 packet.is_video = TRUE;
8240 packet.data = (janus_rtp_header *)buffer;
8241 packet.length = bytes;
8242 /* Go! */
8243 janus_mutex_lock(&mountpoint->mutex);
8244 g_list_foreach(mountpoint->helper_threads == 0 ? mountpoint->viewers : mountpoint->threads,
8245 mountpoint->helper_threads == 0 ? janus_streaming_relay_rtcp_packet : janus_streaming_helper_rtprtcp_packet,
8246 &packet);
8247 janus_mutex_unlock(&mountpoint->mutex);
8248 }
8249 }
8250 }
8251 }
8252
8253 /* Notify users this mountpoint is done */
8254 janus_mutex_lock(&mountpoint->mutex);
8255 GList *viewer = g_list_first(mountpoint->viewers);
8256 /* Prepare JSON event */
8257 json_t *event = json_object();
8258 json_object_set_new(event, "streaming", json_string("event"));
8259 json_t *result = json_object();
8260 json_object_set_new(result, "status", json_string("stopped"));
8261 json_object_set_new(event, "result", result);
8262 while(viewer) {
8263 janus_streaming_session *session = (janus_streaming_session *)viewer->data;
8264 if(session == NULL) {
8265 mountpoint->viewers = g_list_remove_all(mountpoint->viewers, session);
8266 viewer = g_list_first(mountpoint->viewers);
8267 continue;
8268 }
8269 janus_mutex_lock(&session->mutex);
8270 if(session->mountpoint != mountpoint) {
8271 mountpoint->viewers = g_list_remove_all(mountpoint->viewers, session);
8272 viewer = g_list_first(mountpoint->viewers);
8273 janus_mutex_unlock(&session->mutex);
8274 continue;
8275 }
8276 g_atomic_int_set(&session->stopping, 1);
8277 g_atomic_int_set(&session->started, 0);
8278 g_atomic_int_set(&session->paused, 0);
8279 session->mountpoint = NULL;
8280 /* Tell the core to tear down the PeerConnection, hangup_media will do the rest */
8281 gateway->push_event(session->handle, &janus_streaming_plugin, NULL, event, NULL);
8282 gateway->close_pc(session->handle);
8283 janus_refcount_decrease(&session->ref);
8284 janus_refcount_decrease(&mountpoint->ref);
8285 mountpoint->viewers = g_list_remove_all(mountpoint->viewers, session);
8286 viewer = g_list_first(mountpoint->viewers);
8287 janus_mutex_unlock(&session->mutex);
8288 }
8289 json_decref(event);
8290 janus_mutex_unlock(&mountpoint->mutex);
8291
8292 /* Unref the helper threads */
8293 if(mountpoint->helper_threads > 0) {
8294 GList *l = mountpoint->threads;
8295 while(l) {
8296 janus_streaming_helper *ht = (janus_streaming_helper *)l->data;
8297 janus_refcount_decrease(&ht->ref);
8298 l = l->next;
8299 }
8300 }
8301
8302 JANUS_LOG(LOG_VERB, "[%s] Leaving streaming relay thread\n", name);
8303 g_free(name);
8304 janus_refcount_decrease(&mountpoint->ref);
8305 return NULL;
8306 }
8307
8308 static void janus_streaming_relay_rtp_packet(gpointer data, gpointer user_data) {
8309 janus_streaming_rtp_relay_packet *packet = (janus_streaming_rtp_relay_packet *)user_data;
8310 if(!packet || !packet->data || packet->length < 1) {
8311 JANUS_LOG(LOG_ERR, "Invalid packet...\n");
8312 return;
8313 }
8314 janus_streaming_session *session = (janus_streaming_session *)data;
8315 if(!session || !session->handle) {
8316 //~ JANUS_LOG(LOG_ERR, "Invalid session...\n");
8317 return;
8318 }
8319 if(!packet->is_keyframe && (!g_atomic_int_get(&session->started) || g_atomic_int_get(&session->paused))) {
8320 //~ JANUS_LOG(LOG_ERR, "Streaming not started yet for this session...\n");
8321 return;
8322 }
8323
8324 if(packet->is_rtp) {
8325 /* Make sure there hasn't been a video source switch by checking the SSRC */
8326 if(packet->is_video) {
8327 if(!session->video)
8328 return;
8329 /* Check if there's any SVC info to take into account */
8330 if(packet->svc) {
8331 /* There is: check if this is a layer that can be dropped for this viewer
8332 * Note: Following core inspired by the excellent job done by Sergio Garcia Murillo here:
8333 * https://github.com/medooze/media-server/blob/master/src/vp9/VP9LayerSelector.cpp */
8334 int plen = 0;
8335 char *payload = janus_rtp_payload((char *)packet->data, packet->length, &plen);
8336 gboolean keyframe = janus_vp9_is_keyframe((const char *)payload, plen);
8337 gboolean override_mark_bit = FALSE, has_marker_bit = packet->data->markerbit;
8338 int spatial_layer = session->spatial_layer;
8339 gint64 now = janus_get_monotonic_time();
8340 if(packet->svc_info.spatial_layer >= 0 && packet->svc_info.spatial_layer <= 2)
8341 session->last_spatial_layer[packet->svc_info.spatial_layer] = now;
8342 if(session->target_spatial_layer > session->spatial_layer) {
8343 JANUS_LOG(LOG_HUGE, "We need to upscale spatially: (%d < %d)\n",
8344 session->spatial_layer, session->target_spatial_layer);
8345 /* We need to upscale: wait for a keyframe */
8346 if(keyframe) {
8347 int new_spatial_layer = session->target_spatial_layer;
8348 while(new_spatial_layer > session->spatial_layer && new_spatial_layer > 0) {
8349 if(now - session->last_spatial_layer[new_spatial_layer] >= 250000) {
8350 /* We haven't received packets from this layer for a while, try a lower layer */
8351 JANUS_LOG(LOG_HUGE, "Haven't received packets from layer %d for a while, trying %d instead...\n",
8352 new_spatial_layer, new_spatial_layer-1);
8353 new_spatial_layer--;
8354 } else {
8355 break;
8356 }
8357 }
8358 if(new_spatial_layer > session->spatial_layer) {
8359 JANUS_LOG(LOG_HUGE, " -- Upscaling spatial layer: %d --> %d (need %d)\n",
8360 session->spatial_layer, new_spatial_layer, session->target_spatial_layer);
8361 session->spatial_layer = new_spatial_layer;
8362 spatial_layer = session->spatial_layer;
8363 /* Notify the viewer */
8364 json_t *event = json_object();
8365 json_object_set_new(event, "streaming", json_string("event"));
8366 json_t *result = json_object();
8367 json_object_set_new(result, "spatial_layer", json_integer(session->spatial_layer));
8368 if(session->temporal_layer == -1) {
8369 /* We just started: initialize the temporal layer and notify that too */
8370 session->temporal_layer = 0;
8371 json_object_set_new(result, "temporal_layer", json_integer(session->temporal_layer));
8372 }
8373 json_object_set_new(event, "result", result);
8374 gateway->push_event(session->handle, &janus_streaming_plugin, NULL, event, NULL);
8375 json_decref(event);
8376 }
8377 }
8378 } else if(session->target_spatial_layer < session->spatial_layer) {
8379 /* We need to downscale */
8380 JANUS_LOG(LOG_HUGE, "We need to downscale spatially: (%d > %d)\n",
8381 session->spatial_layer, session->target_spatial_layer);
8382 gboolean downscaled = FALSE;
8383 if(!packet->svc_info.fbit && keyframe) {
8384 /* Non-flexible mode: wait for a keyframe */
8385 downscaled = TRUE;
8386 } else if(packet->svc_info.fbit && packet->svc_info.ebit) {
8387 /* Flexible mode: check the E bit */
8388 downscaled = TRUE;
8389 }
8390 if(downscaled) {
8391 JANUS_LOG(LOG_HUGE, " -- Downscaling spatial layer: %d --> %d\n",
8392 session->spatial_layer, session->target_spatial_layer);
8393 session->spatial_layer = session->target_spatial_layer;
8394 /* Notify the viewer */
8395 json_t *event = json_object();
8396 json_object_set_new(event, "streaming", json_string("event"));
8397 json_t *result = json_object();
8398 json_object_set_new(result, "spatial_layer", json_integer(session->spatial_layer));
8399 json_object_set_new(event, "result", result);
8400 gateway->push_event(session->handle, &janus_streaming_plugin, NULL, event, NULL);
8401 json_decref(event);
8402 }
8403 }
8404 if(spatial_layer < packet->svc_info.spatial_layer) {
8405 /* Drop the packet: update the context to make sure sequence number is increased normally later */
8406 JANUS_LOG(LOG_HUGE, "Dropping packet (spatial layer %d < %d)\n", spatial_layer, packet->svc_info.spatial_layer);
8407 session->context.v_base_seq++;
8408 return;
8409 } else if(packet->svc_info.ebit && spatial_layer == packet->svc_info.spatial_layer) {
8410 /* If we stop at layer 0, we need a marker bit now, as the one from layer 1 will not be received */
8411 override_mark_bit = TRUE;
8412 }
8413 int temporal_layer = session->temporal_layer;
8414 if(session->target_temporal_layer > session->temporal_layer) {
8415 /* We need to upscale */
8416 JANUS_LOG(LOG_HUGE, "We need to upscale temporally: (%d < %d)\n",
8417 session->temporal_layer, session->target_temporal_layer);
8418 if(packet->svc_info.ubit && packet->svc_info.bbit &&
8419 packet->svc_info.temporal_layer > session->temporal_layer &&
8420 packet->svc_info.temporal_layer <= session->target_temporal_layer) {
8421 JANUS_LOG(LOG_HUGE, " -- Upscaling temporal layer: %d --> %d (want %d)\n",
8422 session->temporal_layer, packet->svc_info.temporal_layer, session->target_temporal_layer);
8423 session->temporal_layer = packet->svc_info.temporal_layer;
8424 temporal_layer = session->temporal_layer;
8425 /* Notify the viewer */
8426 json_t *event = json_object();
8427 json_object_set_new(event, "streaming", json_string("event"));
8428 json_t *result = json_object();
8429 json_object_set_new(result, "temporal_layer", json_integer(session->temporal_layer));
8430 json_object_set_new(event, "result", result);
8431 gateway->push_event(session->handle, &janus_streaming_plugin, NULL, event, NULL);
8432 json_decref(event);
8433 }
8434 } else if(session->target_temporal_layer < session->temporal_layer) {
8435 /* We need to downscale */
8436 JANUS_LOG(LOG_HUGE, "We need to downscale temporally: (%d > %d)\n",
8437 session->temporal_layer, session->target_temporal_layer);
8438 if(packet->svc_info.ebit && packet->svc_info.temporal_layer == session->target_temporal_layer) {
8439 JANUS_LOG(LOG_HUGE, " -- Downscaling temporal layer: %d --> %d\n",
8440 session->temporal_layer, session->target_temporal_layer);
8441 session->temporal_layer = session->target_temporal_layer;
8442 /* Notify the viewer */
8443 json_t *event = json_object();
8444 json_object_set_new(event, "streaming", json_string("event"));
8445 json_t *result = json_object();
8446 json_object_set_new(result, "temporal_layer", json_integer(session->temporal_layer));
8447 json_object_set_new(event, "result", result);
8448 gateway->push_event(session->handle, &janus_streaming_plugin, NULL, event, NULL);
8449 json_decref(event);
8450 }
8451 }
8452 if(temporal_layer < packet->svc_info.temporal_layer) {
8453 /* Drop the packet: update the context to make sure sequence number is increased normally later */
8454 JANUS_LOG(LOG_HUGE, "Dropping packet (temporal layer %d < %d)\n", temporal_layer, packet->svc_info.temporal_layer);
8455 session->context.v_base_seq++;
8456 return;
8457 }
8458 /* If we got here, we can send the frame: this doesn't necessarily mean it's
8459 * one of the layers the user wants, as there may be dependencies involved */
8460 JANUS_LOG(LOG_HUGE, "Sending packet (spatial=%d, temporal=%d)\n",
8461 packet->svc_info.spatial_layer, packet->svc_info.temporal_layer);
8462 /* Fix sequence number and timestamp (publisher switching may be involved) */
8463 janus_rtp_header_update(packet->data, &session->context, TRUE, 0);
8464 if(override_mark_bit && !has_marker_bit) {
8465 packet->data->markerbit = 1;
8466 }
8467 if(session->video_pt > 0)
8468 packet->data->type = session->video_pt;
8469 janus_plugin_rtp rtp = { .video = packet->is_video, .buffer = (char *)packet->data, .length = packet->length };
8470 janus_plugin_rtp_extensions_reset(&rtp.extensions);
8471 if(gateway != NULL)
8472 gateway->relay_rtp(session->handle, &rtp);
8473 if(override_mark_bit && !has_marker_bit) {
8474 packet->data->markerbit = 0;
8475 }
8476 /* Restore the payload type, timestamp and sequence number to what the publisher set them to */
8477 packet->data->type = packet->ptype;
8478 packet->data->timestamp = htonl(packet->timestamp);
8479 packet->data->seq_number = htons(packet->seq_number);
8480 } else if(packet->simulcast) {
8481 /* Handle simulcast: don't relay if it's not the substream we wanted to handle */
8482 int plen = 0;
8483 char *payload = janus_rtp_payload((char *)packet->data, packet->length, &plen);
8484 if(payload == NULL)
8485 return;
8486 /* Process this packet: don't relay if it's not the SSRC/layer we wanted to handle */
8487 gboolean relay = janus_rtp_simulcasting_context_process_rtp(&session->sim_context,
8488 (char *)packet->data, packet->length, packet->ssrc, NULL, packet->codec, &session->context);
8489 if(!relay) {
8490 /* Did a lot of time pass before we could relay a packet? */
8491 gint64 now = janus_get_monotonic_time();
8492 if((now - session->sim_context.last_relayed) >= G_USEC_PER_SEC) {
8493 g_atomic_int_set(&session->sim_context.need_pli, 1);
8494 }
8495 }
8496 if(session->sim_context.need_pli) {
8497 /* Schedule a PLI */
8498 JANUS_LOG(LOG_VERB, "We need a PLI for the simulcast context\n");
8499 if(session->mountpoint != NULL) {
8500 janus_streaming_rtp_source *source = session->mountpoint->source;
8501 if(source != NULL)
8502 g_atomic_int_set(&source->need_pli, 1);
8503 }
8504 }
8505 /* Do we need to drop this? */
8506 if(!relay)
8507 return;
8508 /* Any event we should notify? */
8509 if(session->sim_context.changed_substream) {
8510 /* Notify the user about the substream change */
8511 json_t *event = json_object();
8512 json_object_set_new(event, "streaming", json_string("event"));
8513 json_t *result = json_object();
8514 json_object_set_new(result, "substream", json_integer(session->sim_context.substream));
8515 json_object_set_new(event, "result", result);
8516 gateway->push_event(session->handle, &janus_streaming_plugin, NULL, event, NULL);
8517 json_decref(event);
8518 }
8519 if(session->sim_context.changed_temporal) {
8520 /* Notify the user about the temporal layer change */
8521 json_t *event = json_object();
8522 json_object_set_new(event, "streaming", json_string("event"));
8523 json_t *result = json_object();
8524 json_object_set_new(result, "temporal", json_integer(session->sim_context.templayer));
8525 json_object_set_new(event, "result", result);
8526 gateway->push_event(session->handle, &janus_streaming_plugin, NULL, event, NULL);
8527 json_decref(event);
8528 }
8529 /* If we got here, update the RTP header and send the packet */
8530 janus_rtp_header_update(packet->data, &session->context, TRUE, 0);
8531 char vp8pd[6];
8532 if(packet->codec == JANUS_VIDEOCODEC_VP8) {
8533 /* For VP8, we save the original payload descriptor, to restore it after */
8534 memcpy(vp8pd, payload, sizeof(vp8pd));
8535 janus_vp8_simulcast_descriptor_update(payload, plen, &session->vp8_context,
8536 session->sim_context.changed_substream);
8537 }
8538 if(session->video_pt > 0)
8539 packet->data->type = session->video_pt;
8540 /* Send the packet */
8541 janus_plugin_rtp rtp = { .video = packet->is_video, .buffer = (char *)packet->data, .length = packet->length };
8542 janus_plugin_rtp_extensions_reset(&rtp.extensions);
8543 if(gateway != NULL)
8544 gateway->relay_rtp(session->handle, &rtp);
8545 /* Restore the timestamp and sequence number to what the publisher set them to */
8546 packet->data->type = packet->ptype;
8547 packet->data->timestamp = htonl(packet->timestamp);
8548 packet->data->seq_number = htons(packet->seq_number);
8549 if(packet->codec == JANUS_VIDEOCODEC_VP8) {
8550 /* Restore the original payload descriptor as well, as it will be needed by the next viewer */
8551 memcpy(payload, vp8pd, sizeof(vp8pd));
8552 }
8553 } else {
8554 /* Fix sequence number and timestamp (switching may be involved) */
8555 janus_rtp_header_update(packet->data, &session->context, TRUE, 0);
8556 if(session->video_pt > 0)
8557 packet->data->type = session->video_pt;
8558 janus_plugin_rtp rtp = { .video = packet->is_video, .buffer = (char *)packet->data, .length = packet->length };
8559 janus_plugin_rtp_extensions_reset(&rtp.extensions);
8560 if(gateway != NULL)
8561 gateway->relay_rtp(session->handle, &rtp);
8562 /* Restore the timestamp and sequence number to what the video source set them to */
8563 packet->data->type = packet->ptype;
8564 packet->data->timestamp = htonl(packet->timestamp);
8565 packet->data->seq_number = htons(packet->seq_number);
8566 }
8567 } else {
8568 if(!session->audio)
8569 return;
8570 /* Fix sequence number and timestamp (switching may be involved) */
8571 janus_rtp_header_update(packet->data, &session->context, FALSE, 0);
8572 if(session->audio_pt >= 0)
8573 packet->data->type = session->audio_pt;
8574 janus_plugin_rtp rtp = { .video = packet->is_video, .buffer = (char *)packet->data, .length = packet->length };
8575 janus_plugin_rtp_extensions_reset(&rtp.extensions);
8576 if(gateway != NULL)
8577 gateway->relay_rtp(session->handle, &rtp);
8578 /* Restore the timestamp and sequence number to what the video source set them to */
8579 packet->data->type = packet->ptype;
8580 packet->data->timestamp = htonl(packet->timestamp);
8581 packet->data->seq_number = htons(packet->seq_number);
8582 }
8583 } else {
8584 /* We're broadcasting a data channel message */
8585 if(!session->data)
8586 return;
8587 if(gateway != NULL && packet->data != NULL && g_atomic_int_get(&session->dataready)) {
8588 janus_plugin_data data = {
8589 .label = NULL,
8590 .protocol = NULL,
8591 .binary = !packet->textdata,
8592 .buffer = (char *)packet->data,
8593 .length = packet->length
8594 };
8595 gateway->relay_data(session->handle, &data);
8596 }
8597 }
8598
8599 return;
8600 }
8601
8602 static void janus_streaming_relay_rtcp_packet(gpointer data, gpointer user_data) {
8603 janus_streaming_rtp_relay_packet *packet = (janus_streaming_rtp_relay_packet *)user_data;
8604 if(!packet || !packet->data || packet->length < 1) {
8605 JANUS_LOG(LOG_ERR, "Invalid packet...\n");
8606 return;
8607 }
8608 janus_streaming_session *session = (janus_streaming_session *)data;
8609 if(!session || !session->handle) {
8610 //~ JANUS_LOG(LOG_ERR, "Invalid session...\n");
8611 return;
8612 }
8613 if(!g_atomic_int_get(&session->started) || g_atomic_int_get(&session->paused)) {
8614 //~ JANUS_LOG(LOG_ERR, "Streaming not started yet for this session...\n");
8615 return;
8616 }
8617
8618 janus_plugin_rtcp rtcp = { .video = packet->is_video, .buffer = (char *)packet->data, .length = packet->length };
8619 if(gateway != NULL)
8620 gateway->relay_rtcp(session->handle, &rtcp);
8621
8622 return;
8623 }
8624
8625 static void janus_streaming_helper_rtprtcp_packet(gpointer data, gpointer user_data) {
8626 janus_streaming_rtp_relay_packet *packet = (janus_streaming_rtp_relay_packet *)user_data;
8627 if(!packet || !packet->data || packet->length < 1) {
8628 JANUS_LOG(LOG_ERR, "Invalid packet...\n");
8629 return;
8630 }
8631 janus_streaming_helper *helper = (janus_streaming_helper *)data;
8632 if(!helper) {
8633 //~ JANUS_LOG(LOG_ERR, "Invalid session...\n");
8634 return;
8635 }
8636 /* Clone the packet and queue it for delivery on the helper thread */
8637 janus_streaming_rtp_relay_packet *copy = g_malloc0(sizeof(janus_streaming_rtp_relay_packet));
8638 copy->data = g_malloc(packet->length);
8639 memcpy(copy->data, packet->data, packet->length);
8640 copy->length = packet->length;
8641 copy->is_rtp = packet->is_rtp;
8642 copy->is_data = packet->is_data;
8643 copy->textdata = packet->textdata;
8644 copy->is_video = packet->is_video;
8645 copy->is_keyframe = packet->is_keyframe;
8646 copy->simulcast = packet->simulcast;
8647 copy->ssrc[0] = packet->ssrc[0];
8648 copy->ssrc[1] = packet->ssrc[1];
8649 copy->ssrc[2] = packet->ssrc[2];
8650 copy->codec = packet->codec;
8651 copy->substream = packet->substream;
8652 copy->ptype = packet->ptype;
8653 copy->timestamp = packet->timestamp;
8654 copy->seq_number = packet->seq_number;
8655 g_async_queue_push(helper->queued_packets, copy);
8656 }
8657
8658 static void *janus_streaming_helper_thread(void *data) {
8659 janus_streaming_helper *helper = (janus_streaming_helper *)data;
8660 janus_streaming_mountpoint *mp = helper->mp;
8661 JANUS_LOG(LOG_INFO, "[%s/#%d] Joining Streaming helper thread\n", mp->name, helper->id);
8662 janus_streaming_rtp_relay_packet *pkt = NULL;
8663 while(!g_atomic_int_get(&stopping) && !g_atomic_int_get(&mp->destroyed) && !g_atomic_int_get(&helper->destroyed)) {
8664 pkt = g_async_queue_pop(helper->queued_packets);
8665 if(pkt == &exit_packet)
8666 break;
8667 janus_mutex_lock(&helper->mutex);
8668 g_list_foreach(helper->viewers,
8669 pkt->is_rtp || pkt->is_data ? janus_streaming_relay_rtp_packet : janus_streaming_relay_rtcp_packet,
8670 pkt);
8671 janus_mutex_unlock(&helper->mutex);
8672 janus_streaming_rtp_relay_packet_free(pkt);
8673 }
8674 JANUS_LOG(LOG_INFO, "[%s/#%d] Leaving Streaming helper thread\n", mp->name, helper->id);
8675 janus_refcount_decrease(&helper->ref);
8676 janus_refcount_decrease(&mp->ref);
8677 g_thread_unref(g_thread_self());
8678 return NULL;
8679 }
8680