contrib: cargo: use the 0.6.13 cargo-c version
[vlc.git] / modules / stream_out / rtsp.c
blob8d79803c8e036ee85b01ea4c9e1602461212e24d
1 /*****************************************************************************
2 * rtsp.c: RTSP support for RTP stream output module
3 *****************************************************************************
4 * Copyright (C) 2003-2004, 2010 VLC authors and VideoLAN
5 * Copyright © 2007 Rémi Denis-Courmont
8 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
9 * Pierre Ynard
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2.1 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
27 * Preamble
28 *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #include <vlc_common.h>
34 #include <vlc_sout.h>
36 #include <vlc_httpd.h>
37 #include <vlc_url.h>
38 #include <vlc_charset.h>
39 #include <vlc_fs.h>
40 #include <vlc_network.h>
41 #include <vlc_rand.h>
42 #include <assert.h>
43 #include <errno.h>
44 #include <stdlib.h>
45 #include <time.h>
47 #ifndef _WIN32
48 # include <locale.h>
49 #endif
50 #ifdef HAVE_XLOCALE_H
51 # include <xlocale.h>
52 #endif
54 #include "rtp.h"
56 typedef struct rtsp_session_t rtsp_session_t;
58 struct rtsp_stream_t
60 vlc_mutex_t lock;
61 vlc_object_t *owner;
62 httpd_host_t *host;
63 httpd_url_t *url;
64 char *psz_path;
65 unsigned track_id;
67 int sessionc;
68 rtsp_session_t **sessionv;
70 vlc_tick_t timeout;
71 vlc_timer_t timer;
75 static int RtspCallback( httpd_callback_sys_t *p_args,
76 httpd_client_t *cl, httpd_message_t *answer,
77 const httpd_message_t *query );
78 static int RtspCallbackId( httpd_callback_sys_t *p_args,
79 httpd_client_t *cl, httpd_message_t *answer,
80 const httpd_message_t *query );
81 static void RtspClientDel( rtsp_stream_t *rtsp, rtsp_session_t *session );
83 static void RtspTimeOut( void *data );
85 rtsp_stream_t *RtspSetup( vlc_object_t *owner, const char *path )
87 rtsp_stream_t *rtsp = calloc( 1, sizeof( *rtsp ) );
89 if( unlikely(rtsp == NULL) )
90 return NULL;
92 rtsp->owner = owner;
93 vlc_mutex_init( &rtsp->lock );
95 rtsp->timeout = vlc_tick_from_sec(__MAX(0,var_InheritInteger(owner, "rtsp-timeout")));
96 if (rtsp->timeout != 0)
98 if (vlc_timer_create(&rtsp->timer, RtspTimeOut, rtsp))
99 goto error;
102 rtsp->psz_path = strdup( (path != NULL) ? path : "/" );
103 if( rtsp->psz_path == NULL )
104 goto error;
106 msg_Dbg( owner, "RTSP stream at %s", rtsp->psz_path );
108 rtsp->host = vlc_rtsp_HostNew( VLC_OBJECT(owner) );
109 if( rtsp->host == NULL )
110 goto error;
112 char *user = var_InheritString(owner, "sout-rtsp-user");
113 char *pwd = var_InheritString(owner, "sout-rtsp-pwd");
115 rtsp->url = httpd_UrlNew( rtsp->host, rtsp->psz_path, user, pwd );
116 free(user);
117 free(pwd);
118 if( rtsp->url == NULL )
119 goto error;
121 httpd_UrlCatch( rtsp->url, HTTPD_MSG_DESCRIBE, RtspCallback, (void*)rtsp );
122 httpd_UrlCatch( rtsp->url, HTTPD_MSG_SETUP, RtspCallback, (void*)rtsp );
123 httpd_UrlCatch( rtsp->url, HTTPD_MSG_PLAY, RtspCallback, (void*)rtsp );
124 httpd_UrlCatch( rtsp->url, HTTPD_MSG_PAUSE, RtspCallback, (void*)rtsp );
125 httpd_UrlCatch( rtsp->url, HTTPD_MSG_GETPARAMETER, RtspCallback,
126 (void*)rtsp );
127 httpd_UrlCatch( rtsp->url, HTTPD_MSG_TEARDOWN, RtspCallback, (void*)rtsp );
128 return rtsp;
130 error:
131 RtspUnsetup( rtsp );
132 return NULL;
136 void RtspUnsetup( rtsp_stream_t *rtsp )
138 if( rtsp->url )
139 httpd_UrlDelete( rtsp->url );
141 if( rtsp->host )
142 httpd_HostDelete( rtsp->host );
144 while( rtsp->sessionc > 0 )
145 RtspClientDel( rtsp, rtsp->sessionv[0] );
147 if (rtsp->timeout != 0)
148 vlc_timer_destroy(rtsp->timer);
150 free( rtsp->psz_path );
151 free( rtsp );
155 struct rtsp_stream_id_t
157 rtsp_stream_t *stream;
158 sout_stream_id_sys_t *sout_id;
159 httpd_url_t *url;
160 unsigned track_id;
161 uint32_t ssrc;
162 unsigned clock_rate; /* needed to compute rtptime in RTP-Info */
163 int mcast_fd;
167 typedef struct rtsp_strack_t rtsp_strack_t;
169 /* For unicast streaming */
170 struct rtsp_session_t
172 rtsp_stream_t *stream;
173 uint64_t id;
174 vlc_tick_t last_seen; /* for timeouts */
176 /* output (id-access) */
177 int trackc;
178 rtsp_strack_t *trackv;
182 /* Unicast session track */
183 struct rtsp_strack_t
185 rtsp_stream_id_t *id;
186 sout_stream_id_sys_t *sout_id;
187 int setup_fd; /* socket created by the SETUP request */
188 int rtp_fd; /* socket used by the RTP output, when playing */
189 uint32_t ssrc;
190 uint16_t seq_init;
193 static void RtspTrackClose( rtsp_strack_t *tr );
195 #define TRACK_PATH_SIZE (sizeof("/trackID=999") - 1)
197 char *RtspAppendTrackPath( rtsp_stream_id_t *id, const char *base )
199 const char *sep = strlen( base ) > 0 && base[strlen( base ) - 1] == '/' ?
200 "" : "/";
201 char *url;
203 if( asprintf( &url, "%s%strackID=%u", base, sep, id->track_id ) == -1 )
204 url = NULL;
205 return url;
209 rtsp_stream_id_t *RtspAddId( rtsp_stream_t *rtsp, sout_stream_id_sys_t *sid,
210 uint32_t ssrc, unsigned clock_rate,
211 int mcast_fd)
213 if (rtsp->track_id > 999)
215 msg_Err(rtsp->owner, "RTSP: too many IDs!");
216 return NULL;
219 char *urlbuf;
220 rtsp_stream_id_t *id = malloc( sizeof( *id ) );
221 httpd_url_t *url;
223 if( id == NULL )
224 return NULL;
226 id->stream = rtsp;
227 id->sout_id = sid;
228 id->track_id = rtsp->track_id;
229 id->ssrc = ssrc;
230 id->clock_rate = clock_rate;
231 id->mcast_fd = mcast_fd;
233 urlbuf = RtspAppendTrackPath( id, rtsp->psz_path );
234 if( urlbuf == NULL )
236 free( id );
237 return NULL;
240 msg_Dbg( rtsp->owner, "RTSP: adding %s", urlbuf );
242 char *user = var_InheritString(rtsp->owner, "sout-rtsp-user");
243 char *pwd = var_InheritString(rtsp->owner, "sout-rtsp-pwd");
245 url = id->url = httpd_UrlNew( rtsp->host, urlbuf, user, pwd );
246 free( user );
247 free( pwd );
248 free( urlbuf );
250 if( url == NULL )
252 free( id );
253 return NULL;
256 httpd_UrlCatch( url, HTTPD_MSG_DESCRIBE, RtspCallbackId, (void *)id );
257 httpd_UrlCatch( url, HTTPD_MSG_SETUP, RtspCallbackId, (void *)id );
258 httpd_UrlCatch( url, HTTPD_MSG_PLAY, RtspCallbackId, (void *)id );
259 httpd_UrlCatch( url, HTTPD_MSG_PAUSE, RtspCallbackId, (void *)id );
260 httpd_UrlCatch( url, HTTPD_MSG_GETPARAMETER, RtspCallbackId, (void *)id );
261 httpd_UrlCatch( url, HTTPD_MSG_TEARDOWN, RtspCallbackId, (void *)id );
263 rtsp->track_id++;
265 return id;
269 void RtspDelId( rtsp_stream_t *rtsp, rtsp_stream_id_t *id )
271 httpd_UrlDelete( id->url );
273 vlc_mutex_lock( &rtsp->lock );
274 for( int i = 0; i < rtsp->sessionc; i++ )
276 rtsp_session_t *ses = rtsp->sessionv[i];
278 for( int j = 0; j < ses->trackc; j++ )
280 if( ses->trackv[j].id == id )
282 rtsp_strack_t *tr = ses->trackv + j;
283 RtspTrackClose( tr );
284 TAB_ERASE(ses->trackc, ses->trackv, j);
289 vlc_mutex_unlock( &rtsp->lock );
290 free( id );
294 /** rtsp must be locked */
295 static void RtspUpdateTimer( rtsp_stream_t *rtsp )
297 if (rtsp->timeout == 0)
298 return;
300 vlc_tick_t timeout = 0;
301 for (int i = 0; i < rtsp->sessionc; i++)
303 if (timeout == 0 || rtsp->sessionv[i]->last_seen < timeout)
304 timeout = rtsp->sessionv[i]->last_seen;
306 if (timeout != 0)
308 timeout += rtsp->timeout;
309 vlc_timer_schedule(rtsp->timer, true, timeout, VLC_TIMER_FIRE_ONCE);
311 else
313 vlc_timer_disarm(rtsp->timer);
318 static void RtspTimeOut( void *data )
320 rtsp_stream_t *rtsp = data;
322 vlc_mutex_lock(&rtsp->lock);
323 vlc_tick_t now = vlc_tick_now();
324 for (int i = rtsp->sessionc - 1; i >= 0; i--)
325 if (rtsp->sessionv[i]->last_seen + rtsp->timeout < now)
326 RtspClientDel(rtsp, rtsp->sessionv[i]);
327 RtspUpdateTimer(rtsp);
328 vlc_mutex_unlock(&rtsp->lock);
332 /** rtsp must be locked */
333 static
334 rtsp_session_t *RtspClientNew( rtsp_stream_t *rtsp )
336 rtsp_session_t *s = malloc( sizeof( *s ) );
337 if( s == NULL )
338 return NULL;
340 s->stream = rtsp;
341 vlc_rand_bytes (&s->id, sizeof (s->id));
342 s->trackc = 0;
343 s->trackv = NULL;
345 TAB_APPEND( rtsp->sessionc, rtsp->sessionv, s );
347 return s;
351 /** rtsp must be locked */
352 static
353 rtsp_session_t *RtspClientGet( rtsp_stream_t *rtsp, const char *name )
355 char *end;
356 uint64_t id;
357 int i;
359 if( name == NULL )
360 return NULL;
362 errno = 0;
363 id = strtoull( name, &end, 0x10 );
364 if( errno || *end )
365 return NULL;
367 /* FIXME: use a hash/dictionary */
368 for( i = 0; i < rtsp->sessionc; i++ )
370 if( rtsp->sessionv[i]->id == id )
371 return rtsp->sessionv[i];
373 return NULL;
377 /** rtsp must be locked */
378 static
379 void RtspClientDel( rtsp_stream_t *rtsp, rtsp_session_t *session )
381 int i;
382 TAB_REMOVE( rtsp->sessionc, rtsp->sessionv, session );
384 for( i = 0; i < session->trackc; i++ )
385 RtspTrackClose( &session->trackv[i] );
387 free( session->trackv );
388 free( session );
392 /** rtsp must be locked */
393 static void RtspClientAlive( rtsp_session_t *session )
395 if (session->stream->timeout == 0)
396 return;
398 session->last_seen = vlc_tick_now();
399 RtspUpdateTimer(session->stream);
402 static int dup_socket(int oldfd)
404 int newfd;
405 #ifndef _WIN32
406 newfd = vlc_dup(oldfd);
407 #else
408 WSAPROTOCOL_INFO info;
409 WSADuplicateSocket (oldfd, GetCurrentProcessId (), &info);
410 newfd = WSASocket (info.iAddressFamily, info.iSocketType,
411 info.iProtocol, &info, 0, 0);
412 #endif
413 return newfd;
416 /** rtsp must be locked */
417 static void RtspTrackClose( rtsp_strack_t *tr )
419 if (tr->setup_fd != -1)
421 if (tr->rtp_fd != -1)
423 rtp_del_sink(tr->sout_id, tr->rtp_fd);
424 tr->rtp_fd = -1;
426 net_Close(tr->setup_fd);
427 tr->setup_fd = -1;
432 /** Finds the next transport choice */
433 static inline const char *transport_next( const char *str )
435 /* Looks for comma */
436 str = strchr( str, ',' );
437 if( str == NULL )
438 return NULL; /* No more transport options */
440 str++; /* skips comma */
441 while( strchr( "\r\n\t ", *str ) )
442 str++;
444 return (*str) ? str : NULL;
448 /** Finds the next transport parameter */
449 static inline const char *parameter_next( const char *str )
451 while( strchr( ",;", *str ) == NULL )
452 str++;
454 return (*str == ';') ? (str + 1) : NULL;
458 static vlc_tick_t ParseNPT (const char *str)
460 locale_t loc = newlocale (LC_NUMERIC_MASK, "C", NULL);
461 locale_t oldloc = uselocale (loc);
462 unsigned hour, min;
463 float sec;
465 if (sscanf (str, "%u:%u:%f", &hour, &min, &sec) == 3)
466 sec += ((hour * 60) + min) * 60;
467 else
468 if (sscanf (str, "%f", &sec) != 1)
469 sec = -1;
471 if (loc != (locale_t)0)
473 uselocale (oldloc);
474 freelocale (loc);
476 return sec < 0 ? -1 : vlc_tick_from_sec( sec );
480 /** RTSP requests handler
481 * @param id selected track for non-aggregate URLs,
482 * NULL for aggregate URLs
484 static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
485 httpd_client_t *cl,
486 httpd_message_t *answer,
487 const httpd_message_t *query )
489 vlc_object_t *owner = rtsp->owner;
490 char psz_sesbuf[17];
491 const char *psz_session = NULL, *psz;
492 char control[sizeof("rtsp://[]:12345") + NI_MAXNUMERICHOST
493 + strlen( rtsp->psz_path )];
494 time_t now;
496 time (&now);
498 if( answer == NULL || query == NULL || cl == NULL )
499 return VLC_SUCCESS;
500 else
502 /* Build self-referential control URL */
503 char ip[NI_MAXNUMERICHOST], *ptr;
504 int port;
506 httpd_ServerIP( cl, ip, &port );
507 ptr = strchr( ip, '%' );
508 if( ptr != NULL )
509 *ptr = '\0';
511 if( strchr( ip, ':' ) != NULL )
512 sprintf( control, "rtsp://[%s]:%d%s", ip, port, rtsp->psz_path );
513 else
514 sprintf( control, "rtsp://%s:%d%s", ip, port, rtsp->psz_path );
517 /* */
518 answer->i_proto = HTTPD_PROTO_RTSP;
519 answer->i_version= 0;
520 answer->i_type = HTTPD_MSG_ANSWER;
521 answer->i_body = 0;
522 answer->p_body = NULL;
524 httpd_MsgAdd( answer, "Server", "VLC/%s", VERSION );
526 /* Date: is always allowed, and sometimes mandatory with RTSP/2.0. */
527 struct tm ut;
528 if (gmtime_r (&now, &ut) != NULL)
529 { /* RFC1123 format, GMT is mandatory */
530 static const char wdays[7][4] = {
531 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
532 static const char mons[12][4] = {
533 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
534 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
535 httpd_MsgAdd (answer, "Date", "%s, %02u %s %04u %02u:%02u:%02u GMT",
536 wdays[ut.tm_wday], ut.tm_mday, mons[ut.tm_mon],
537 1900 + ut.tm_year, ut.tm_hour, ut.tm_min, ut.tm_sec);
540 if( query->i_proto != HTTPD_PROTO_RTSP )
542 answer->i_status = 505;
544 else
545 if( httpd_MsgGet( query, "Require" ) != NULL )
547 answer->i_status = 551;
548 httpd_MsgAdd( answer, "Unsupported", "%s",
549 httpd_MsgGet( query, "Require" ) );
551 else
552 switch( query->i_type )
554 case HTTPD_MSG_DESCRIBE:
555 { /* Aggregate-only */
556 if( id != NULL )
558 answer->i_status = 460;
559 break;
562 answer->i_status = 200;
563 httpd_MsgAdd( answer, "Content-Type", "%s", "application/sdp" );
564 httpd_MsgAdd( answer, "Content-Base", "%s", control );
566 answer->p_body = (uint8_t *)
567 SDPGenerate( (sout_stream_t *)owner, control );
568 if( answer->p_body != NULL )
569 answer->i_body = strlen( (char *)answer->p_body );
570 else
571 answer->i_status = 500;
572 break;
575 case HTTPD_MSG_SETUP:
576 /* Non-aggregate-only */
577 if( id == NULL )
579 answer->i_status = 459;
580 break;
583 psz_session = httpd_MsgGet( query, "Session" );
584 answer->i_status = 461;
586 for( const char *tpt = httpd_MsgGet( query, "Transport" );
587 tpt != NULL;
588 tpt = transport_next( tpt ) )
590 bool b_multicast = true, b_unsupp = false;
591 bool b_multicast_port_set = false;
592 unsigned loport = 5004, hiport; /* from RFC3551 */
593 unsigned mloport = 5004, mhiport = mloport + 1;
595 /* Check transport protocol. */
596 /* Currently, we only support RTP/AVP over UDP */
597 if( strncmp( tpt, "RTP/AVP", 7 ) )
598 continue;
599 tpt += 7;
600 if( strncmp( tpt, "/UDP", 4 ) == 0 )
601 tpt += 4;
602 if( strchr( ";,", *tpt ) == NULL )
603 continue;
605 /* Parse transport options */
606 for( const char *opt = parameter_next( tpt );
607 opt != NULL;
608 opt = parameter_next( opt ) )
610 if( strncmp( opt, "multicast", 9 ) == 0)
611 b_multicast = true;
612 else
613 if( strncmp( opt, "unicast", 7 ) == 0 )
614 b_multicast = false;
615 else
616 if( sscanf( opt, "client_port=%u-%u", &loport, &hiport )
617 == 2 )
619 else
620 if( sscanf( opt, "port=%u-%u", &mloport, &mhiport )
621 == 2 )
622 b_multicast_port_set = true;
623 else
624 if( strncmp( opt, "mode=", 5 ) == 0 )
626 if( strncasecmp( opt + 5, "play", 4 )
627 && strncasecmp( opt + 5, "\"PLAY\"", 6 ) )
629 /* Not playing?! */
630 b_unsupp = true;
631 break;
634 else
635 if( strncmp( opt,"destination=", 12 ) == 0 )
637 answer->i_status = 403;
638 b_unsupp = true;
640 else
643 * Every other option is unsupported:
645 * "source" and "append" are invalid (server-only);
646 * "ssrc" also (as clarified per RFC2326bis).
648 * For multicast, "layers", "ttl" are set by the
649 * stream output configuration.
651 * For unicast, we want to decide "server_port" values.
653 * "interleaved" is not implemented.
655 b_unsupp = true;
656 break;
660 if( b_unsupp )
661 continue;
663 if( b_multicast )
665 char dst[NI_MAXNUMERICHOST];
666 int dport, ttl;
667 if( id->mcast_fd == -1 )
668 continue;
670 net_GetPeerAddress(id->mcast_fd, dst, &dport);
672 /* Checking for multicast port override */
673 if( b_multicast_port_set
674 && ((unsigned)dport != mloport
675 || (unsigned)dport + 1 != mhiport))
677 answer->i_status = 551;
678 continue;
681 ttl = var_InheritInteger(owner, "ttl");
682 if (ttl <= 0)
683 /* FIXME: the TTL is left to the OS default, we can
684 * only guess that it's 1. */
685 ttl = 1;
687 if( psz_session == NULL )
689 /* Create a dummy session ID */
690 snprintf( psz_sesbuf, sizeof( psz_sesbuf ), "%lu",
691 vlc_mrand48() );
692 psz_session = psz_sesbuf;
694 answer->i_status = 200;
696 httpd_MsgAdd( answer, "Transport",
697 "RTP/AVP/UDP;destination=%s;port=%u-%u;"
698 "ttl=%d;mode=play",
699 dst, dport, dport + 1, ttl );
700 /* FIXME: this doesn't work with RTP + RTCP mux */
702 else
704 char ip[NI_MAXNUMERICHOST], src[NI_MAXNUMERICHOST];
705 rtsp_session_t *ses = NULL;
706 int fd, sport;
707 uint32_t ssrc;
709 if( httpd_ClientIP( cl, ip, NULL ) == NULL )
711 answer->i_status = 500;
712 continue;
715 fd = net_ConnectDgram( owner, ip, loport, -1,
716 IPPROTO_UDP );
717 if( fd == -1 )
719 msg_Err( owner,
720 "cannot create RTP socket for %s port %u",
721 ip, loport );
722 answer->i_status = 500;
723 continue;
726 /* Ignore any unexpected incoming packet */
727 setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &(int){ 0 },
728 sizeof (int));
729 net_GetSockAddress( fd, src, &sport );
731 vlc_mutex_lock( &rtsp->lock );
732 if( psz_session == NULL )
734 ses = RtspClientNew( rtsp );
735 snprintf( psz_sesbuf, sizeof( psz_sesbuf ), "%"PRIx64,
736 ses->id );
737 psz_session = psz_sesbuf;
739 else
741 ses = RtspClientGet( rtsp, psz_session );
742 if( ses == NULL )
744 answer->i_status = 454;
745 vlc_mutex_unlock( &rtsp->lock );
746 net_Close( fd );
747 continue;
750 RtspClientAlive(ses);
752 rtsp_strack_t *tr = NULL;
753 for (int i = 0; i < ses->trackc; i++)
755 if (ses->trackv[i].id == id)
757 tr = ses->trackv + i;
758 break;
762 if (tr == NULL)
764 /* Set up a new track */
765 rtsp_strack_t track = { .id = id,
766 .sout_id = id->sout_id,
767 .setup_fd = fd,
768 .rtp_fd = -1 };
770 ssrc = id->ssrc;
771 TAB_APPEND(ses->trackc, ses->trackv, track);
773 else if (tr->setup_fd == -1)
775 /* The track was not SETUP, but it exists
776 * because there is a sout_id running for it */
777 tr->setup_fd = fd;
778 ssrc = tr->ssrc;
780 else
782 /* The track is already set up, and we don't
783 * support changing the transport parameters on
784 * the fly */
785 vlc_mutex_unlock( &rtsp->lock );
786 answer->i_status = 455;
787 net_Close( fd );
788 break;
790 vlc_mutex_unlock( &rtsp->lock );
792 httpd_ServerIP( cl, ip, NULL );
794 /* Specify source IP only if it is different from the
795 * RTSP control connection server address */
796 if( strcmp( src, ip ) )
798 char *ptr = strchr( src, '%' );
799 if( ptr != NULL ) *ptr = '\0'; /* remove scope ID */
801 else
802 src[0] = '\0';
804 httpd_MsgAdd( answer, "Transport",
805 "RTP/AVP/UDP;unicast%s%s;"
806 "client_port=%u-%u;server_port=%u-%u;"
807 "ssrc=%08X;mode=play",
808 src[0] ? ";source=" : "", src,
809 loport, loport + 1, sport, sport + 1, ssrc );
811 answer->i_status = 200;
813 break;
815 break;
817 case HTTPD_MSG_PLAY:
819 rtsp_session_t *ses;
820 answer->i_status = 200;
822 psz_session = httpd_MsgGet( query, "Session" );
823 vlc_tick_t start = -1, end = -1, npt;
824 const char *range = httpd_MsgGet (query, "Range");
825 if (range != NULL)
827 if (strncmp (range, "npt=", 4))
829 answer->i_status = 501;
830 break;
833 start = ParseNPT (range + 4);
834 range = strchr(range, '-');
835 if (range != NULL && *(range + 1))
836 end = ParseNPT (range + 1);
838 if (end >= 0 && end < start)
840 answer->i_status = 457;
841 break;
844 /* We accept start times of 0 even for broadcast streams
845 * that already started */
846 else if (start > 0 || end >= 0)
848 answer->i_status = 456;
849 break;
852 vlc_mutex_lock( &rtsp->lock );
853 ses = RtspClientGet( rtsp, psz_session );
854 if( ses != NULL )
856 char info[ses->trackc * ( strlen( control ) + TRACK_PATH_SIZE
857 + sizeof("url=;seq=65535;rtptime=4294967295, ")
858 - 1 ) + 1];
859 size_t infolen = 0;
860 RtspClientAlive(ses);
862 sout_stream_id_sys_t *sout_id = NULL;
863 vlc_tick_t ts = rtp_get_ts((sout_stream_t *)owner,
864 sout_id, &npt);
866 for( int i = 0; i < ses->trackc; i++ )
868 rtsp_strack_t *tr = ses->trackv + i;
869 if( ( id == NULL ) || ( tr->id == id ) )
871 if (tr->setup_fd == -1)
872 /* Track not SETUP */
873 continue;
875 uint16_t seq;
876 if( tr->rtp_fd == -1 )
878 /* Track not PLAYing yet */
879 if (tr->sout_id == NULL)
880 /* Instance not running yet (VoD) */
881 seq = tr->seq_init;
882 else
884 /* Instance running, add a sink to it */
885 tr->rtp_fd = dup_socket(tr->setup_fd);
886 if (tr->rtp_fd == -1)
887 continue;
889 rtp_add_sink( tr->sout_id, tr->rtp_fd,
890 false, &seq );
893 else
895 /* Track already playing */
896 assert( tr->sout_id != NULL );
897 seq = rtp_get_seq( tr->sout_id );
899 char *url = RtspAppendTrackPath( tr->id, control );
900 infolen += sprintf( info + infolen,
901 "url=%s;seq=%u;rtptime=%u, ",
902 url != NULL ? url : "", seq,
903 rtp_compute_ts( tr->id->clock_rate, ts ) );
904 free( url );
907 if( infolen > 0 )
909 info[infolen - 2] = '\0'; /* remove trailing ", " */
910 httpd_MsgAdd( answer, "RTP-Info", "%s", info );
913 vlc_mutex_unlock( &rtsp->lock );
915 if (ses != NULL)
917 double f_npt = secf_from_vlc_tick(npt);
918 httpd_MsgAdd( answer, "Range", "npt=%f-", f_npt );
921 if( httpd_MsgGet( query, "Scale" ) != NULL )
922 httpd_MsgAdd( answer, "Scale", "1." );
923 break;
926 case HTTPD_MSG_PAUSE:
928 if (id == NULL)
930 answer->i_status = 405;
931 httpd_MsgAdd( answer, "Allow",
932 "DESCRIBE, TEARDOWN, PLAY, GET_PARAMETER" );
933 break;
936 rtsp_session_t *ses;
937 answer->i_status = 200;
938 psz_session = httpd_MsgGet( query, "Session" );
939 vlc_mutex_lock( &rtsp->lock );
940 ses = RtspClientGet( rtsp, psz_session );
941 if (ses != NULL)
943 if (id != NULL) /* "Mute" the selected track */
945 bool found = false;
946 for (int i = 0; i < ses->trackc; i++)
948 rtsp_strack_t *tr = ses->trackv + i;;
949 if (tr->id == id)
951 if (tr->setup_fd == -1)
952 break;
954 found = true;
955 if (tr->rtp_fd != -1)
957 rtp_del_sink(tr->sout_id, tr->rtp_fd);
958 tr->rtp_fd = -1;
960 break;
963 if (!found)
964 answer->i_status = 455;
966 RtspClientAlive(ses);
968 vlc_mutex_unlock( &rtsp->lock );
969 break;
972 case HTTPD_MSG_GETPARAMETER:
974 if( query->i_body > 0 )
976 answer->i_status = 451;
977 break;
980 psz_session = httpd_MsgGet( query, "Session" );
981 answer->i_status = 200;
982 vlc_mutex_lock( &rtsp->lock );
983 rtsp_session_t *ses = RtspClientGet( rtsp, psz_session );
984 if (ses != NULL)
985 RtspClientAlive(ses);
986 vlc_mutex_unlock( &rtsp->lock );
987 break;
990 case HTTPD_MSG_TEARDOWN:
992 rtsp_session_t *ses;
994 answer->i_status = 200;
996 psz_session = httpd_MsgGet( query, "Session" );
998 vlc_mutex_lock( &rtsp->lock );
999 ses = RtspClientGet( rtsp, psz_session );
1000 if( ses != NULL )
1002 if( id == NULL ) /* Delete the entire session */
1004 RtspClientDel( rtsp, ses );
1005 RtspUpdateTimer(rtsp);
1007 else /* Delete one track from the session */
1009 for( int i = 0; i < ses->trackc; i++ )
1011 if( ses->trackv[i].id == id )
1013 RtspTrackClose( &ses->trackv[i] );
1014 TAB_ERASE(ses->trackc, ses->trackv, i);
1017 RtspClientAlive(ses);
1020 vlc_mutex_unlock( &rtsp->lock );
1021 break;
1024 default:
1025 return VLC_EGENERIC;
1028 if( psz_session )
1030 if (rtsp->timeout != 0)
1031 httpd_MsgAdd( answer, "Session", "%s;timeout=%" PRIu64, psz_session,
1032 SEC_FROM_VLC_TICK(rtsp->timeout) );
1033 else
1034 httpd_MsgAdd( answer, "Session", "%s", psz_session );
1037 httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
1038 httpd_MsgAdd( answer, "Cache-Control", "no-cache" );
1040 psz = httpd_MsgGet( query, "Cseq" );
1041 if( psz != NULL )
1042 httpd_MsgAdd( answer, "Cseq", "%s", psz );
1043 psz = httpd_MsgGet( query, "Timestamp" );
1044 if( psz != NULL )
1045 httpd_MsgAdd( answer, "Timestamp", "%s", psz );
1047 return VLC_SUCCESS;
1051 /** Aggregate RTSP callback */
1052 static int RtspCallback( httpd_callback_sys_t *p_args,
1053 httpd_client_t *cl,
1054 httpd_message_t *answer,
1055 const httpd_message_t *query )
1057 return RtspHandler( (rtsp_stream_t *)p_args, NULL, cl, answer, query );
1061 /** Non-aggregate RTSP callback */
1062 static int RtspCallbackId( httpd_callback_sys_t *p_args,
1063 httpd_client_t *cl,
1064 httpd_message_t *answer,
1065 const httpd_message_t *query )
1067 rtsp_stream_id_t *id = (rtsp_stream_id_t *)p_args;
1068 return RtspHandler( id->stream, id, cl, answer, query );