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>
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 /*****************************************************************************
28 *****************************************************************************/
33 #include <vlc_common.h>
36 #include <vlc_httpd.h>
38 #include <vlc_charset.h>
40 #include <vlc_network.h>
56 typedef struct rtsp_session_t rtsp_session_t
;
68 rtsp_session_t
**sessionv
;
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
) )
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
))
102 rtsp
->psz_path
= strdup( (path
!= NULL
) ? path
: "/" );
103 if( rtsp
->psz_path
== NULL
)
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
)
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
);
118 if( rtsp
->url
== NULL
)
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
,
127 httpd_UrlCatch( rtsp
->url
, HTTPD_MSG_TEARDOWN
, RtspCallback
, (void*)rtsp
);
136 void RtspUnsetup( rtsp_stream_t
*rtsp
)
139 httpd_UrlDelete( rtsp
->url
);
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
);
155 struct rtsp_stream_id_t
157 rtsp_stream_t
*stream
;
158 sout_stream_id_sys_t
*sout_id
;
162 unsigned clock_rate
; /* needed to compute rtptime in RTP-Info */
167 typedef struct rtsp_strack_t rtsp_strack_t
;
169 /* For unicast streaming */
170 struct rtsp_session_t
172 rtsp_stream_t
*stream
;
174 vlc_tick_t last_seen
; /* for timeouts */
176 /* output (id-access) */
178 rtsp_strack_t
*trackv
;
182 /* Unicast session track */
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 */
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] == '/' ?
203 if( asprintf( &url
, "%s%strackID=%u", base
, sep
, id
->track_id
) == -1 )
209 rtsp_stream_id_t
*RtspAddId( rtsp_stream_t
*rtsp
, sout_stream_id_sys_t
*sid
,
210 uint32_t ssrc
, unsigned clock_rate
,
213 if (rtsp
->track_id
> 999)
215 msg_Err(rtsp
->owner
, "RTSP: too many IDs!");
220 rtsp_stream_id_t
*id
= malloc( sizeof( *id
) );
228 id
->track_id
= rtsp
->track_id
;
230 id
->clock_rate
= clock_rate
;
231 id
->mcast_fd
= mcast_fd
;
233 urlbuf
= RtspAppendTrackPath( id
, rtsp
->psz_path
);
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
);
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
);
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
);
294 /** rtsp must be locked */
295 static void RtspUpdateTimer( rtsp_stream_t
*rtsp
)
297 if (rtsp
->timeout
== 0)
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
;
308 timeout
+= rtsp
->timeout
;
309 vlc_timer_schedule(rtsp
->timer
, true, timeout
, VLC_TIMER_FIRE_ONCE
);
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 */
334 rtsp_session_t
*RtspClientNew( rtsp_stream_t
*rtsp
)
336 rtsp_session_t
*s
= malloc( sizeof( *s
) );
341 vlc_rand_bytes (&s
->id
, sizeof (s
->id
));
345 TAB_APPEND( rtsp
->sessionc
, rtsp
->sessionv
, s
);
351 /** rtsp must be locked */
353 rtsp_session_t
*RtspClientGet( rtsp_stream_t
*rtsp
, const char *name
)
363 id
= strtoull( name
, &end
, 0x10 );
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
];
377 /** rtsp must be locked */
379 void RtspClientDel( rtsp_stream_t
*rtsp
, rtsp_session_t
*session
)
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
);
392 /** rtsp must be locked */
393 static void RtspClientAlive( rtsp_session_t
*session
)
395 if (session
->stream
->timeout
== 0)
398 session
->last_seen
= vlc_tick_now();
399 RtspUpdateTimer(session
->stream
);
402 static int dup_socket(int oldfd
)
406 newfd
= vlc_dup(oldfd
);
408 WSAPROTOCOL_INFO info
;
409 WSADuplicateSocket (oldfd
, GetCurrentProcessId (), &info
);
410 newfd
= WSASocket (info
.iAddressFamily
, info
.iSocketType
,
411 info
.iProtocol
, &info
, 0, 0);
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
);
426 net_Close(tr
->setup_fd
);
432 /** Finds the next transport choice */
433 static inline const char *transport_next( const char *str
)
435 /* Looks for comma */
436 str
= strchr( str
, ',' );
438 return NULL
; /* No more transport options */
440 str
++; /* skips comma */
441 while( strchr( "\r\n\t ", *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
)
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
);
465 if (sscanf (str
, "%u:%u:%f", &hour
, &min
, &sec
) == 3)
466 sec
+= ((hour
* 60) + min
) * 60;
468 if (sscanf (str
, "%f", &sec
) != 1)
471 if (loc
!= (locale_t
)0)
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
,
486 httpd_message_t
*answer
,
487 const httpd_message_t
*query
)
489 vlc_object_t
*owner
= rtsp
->owner
;
491 const char *psz_session
= NULL
, *psz
;
492 char control
[sizeof("rtsp://[]:12345") + NI_MAXNUMERICHOST
493 + strlen( rtsp
->psz_path
)];
498 if( answer
== NULL
|| query
== NULL
|| cl
== NULL
)
502 /* Build self-referential control URL */
503 char ip
[NI_MAXNUMERICHOST
], *ptr
;
506 httpd_ServerIP( cl
, ip
, &port
);
507 ptr
= strchr( ip
, '%' );
511 if( strchr( ip
, ':' ) != NULL
)
512 sprintf( control
, "rtsp://[%s]:%d%s", ip
, port
, rtsp
->psz_path
);
514 sprintf( control
, "rtsp://%s:%d%s", ip
, port
, rtsp
->psz_path
);
518 answer
->i_proto
= HTTPD_PROTO_RTSP
;
519 answer
->i_version
= 0;
520 answer
->i_type
= HTTPD_MSG_ANSWER
;
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. */
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;
545 if( httpd_MsgGet( query
, "Require" ) != NULL
)
547 answer
->i_status
= 551;
548 httpd_MsgAdd( answer
, "Unsupported", "%s",
549 httpd_MsgGet( query
, "Require" ) );
552 switch( query
->i_type
)
554 case HTTPD_MSG_DESCRIBE
:
555 { /* Aggregate-only */
558 answer
->i_status
= 460;
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
);
571 answer
->i_status
= 500;
575 case HTTPD_MSG_SETUP
:
576 /* Non-aggregate-only */
579 answer
->i_status
= 459;
583 psz_session
= httpd_MsgGet( query
, "Session" );
584 answer
->i_status
= 461;
586 for( const char *tpt
= httpd_MsgGet( query
, "Transport" );
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 ) )
600 if( strncmp( tpt
, "/UDP", 4 ) == 0 )
602 if( strchr( ";,", *tpt
) == NULL
)
605 /* Parse transport options */
606 for( const char *opt
= parameter_next( tpt
);
608 opt
= parameter_next( opt
) )
610 if( strncmp( opt
, "multicast", 9 ) == 0)
613 if( strncmp( opt
, "unicast", 7 ) == 0 )
616 if( sscanf( opt
, "client_port=%u-%u", &loport
, &hiport
)
620 if( sscanf( opt
, "port=%u-%u", &mloport
, &mhiport
)
622 b_multicast_port_set
= true;
624 if( strncmp( opt
, "mode=", 5 ) == 0 )
626 if( strncasecmp( opt
+ 5, "play", 4 )
627 && strncasecmp( opt
+ 5, "\"PLAY\"", 6 ) )
635 if( strncmp( opt
,"destination=", 12 ) == 0 )
637 answer
->i_status
= 403;
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.
665 char dst
[NI_MAXNUMERICHOST
];
667 if( id
->mcast_fd
== -1 )
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;
681 ttl
= var_InheritInteger(owner
, "ttl");
683 /* FIXME: the TTL is left to the OS default, we can
684 * only guess that it's 1. */
687 if( psz_session
== NULL
)
689 /* Create a dummy session ID */
690 snprintf( psz_sesbuf
, sizeof( psz_sesbuf
), "%lu",
692 psz_session
= psz_sesbuf
;
694 answer
->i_status
= 200;
696 httpd_MsgAdd( answer
, "Transport",
697 "RTP/AVP/UDP;destination=%s;port=%u-%u;"
699 dst
, dport
, dport
+ 1, ttl
);
700 /* FIXME: this doesn't work with RTP + RTCP mux */
704 char ip
[NI_MAXNUMERICHOST
], src
[NI_MAXNUMERICHOST
];
705 rtsp_session_t
*ses
= NULL
;
709 if( httpd_ClientIP( cl
, ip
, NULL
) == NULL
)
711 answer
->i_status
= 500;
715 fd
= net_ConnectDgram( owner
, ip
, loport
, -1,
720 "cannot create RTP socket for %s port %u",
722 answer
->i_status
= 500;
726 /* Ignore any unexpected incoming packet */
727 setsockopt (fd
, SOL_SOCKET
, SO_RCVBUF
, &(int){ 0 },
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
,
737 psz_session
= psz_sesbuf
;
741 ses
= RtspClientGet( rtsp
, psz_session
);
744 answer
->i_status
= 454;
745 vlc_mutex_unlock( &rtsp
->lock
);
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
;
764 /* Set up a new track */
765 rtsp_strack_t track
= { .id
= id
,
766 .sout_id
= id
->sout_id
,
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 */
782 /* The track is already set up, and we don't
783 * support changing the transport parameters on
785 vlc_mutex_unlock( &rtsp
->lock
);
786 answer
->i_status
= 455;
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 */
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;
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");
827 if (strncmp (range
, "npt=", 4))
829 answer
->i_status
= 501;
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;
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;
852 vlc_mutex_lock( &rtsp
->lock
);
853 ses
= RtspClientGet( rtsp
, psz_session
);
856 char info
[ses
->trackc
* ( strlen( control
) + TRACK_PATH_SIZE
857 + sizeof("url=;seq=65535;rtptime=4294967295, ")
860 RtspClientAlive(ses
);
862 sout_stream_id_sys_t
*sout_id
= NULL
;
863 vlc_tick_t ts
= rtp_get_ts((sout_stream_t
*)owner
,
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 */
876 if( tr
->rtp_fd
== -1 )
878 /* Track not PLAYing yet */
879 if (tr
->sout_id
== NULL
)
880 /* Instance not running yet (VoD) */
884 /* Instance running, add a sink to it */
885 tr
->rtp_fd
= dup_socket(tr
->setup_fd
);
886 if (tr
->rtp_fd
== -1)
889 rtp_add_sink( tr
->sout_id
, tr
->rtp_fd
,
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
) );
909 info
[infolen
- 2] = '\0'; /* remove trailing ", " */
910 httpd_MsgAdd( answer
, "RTP-Info", "%s", info
);
913 vlc_mutex_unlock( &rtsp
->lock
);
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." );
926 case HTTPD_MSG_PAUSE
:
930 answer
->i_status
= 405;
931 httpd_MsgAdd( answer
, "Allow",
932 "DESCRIBE, TEARDOWN, PLAY, GET_PARAMETER" );
937 answer
->i_status
= 200;
938 psz_session
= httpd_MsgGet( query
, "Session" );
939 vlc_mutex_lock( &rtsp
->lock
);
940 ses
= RtspClientGet( rtsp
, psz_session
);
943 if (id
!= NULL
) /* "Mute" the selected track */
946 for (int i
= 0; i
< ses
->trackc
; i
++)
948 rtsp_strack_t
*tr
= ses
->trackv
+ i
;;
951 if (tr
->setup_fd
== -1)
955 if (tr
->rtp_fd
!= -1)
957 rtp_del_sink(tr
->sout_id
, tr
->rtp_fd
);
964 answer
->i_status
= 455;
966 RtspClientAlive(ses
);
968 vlc_mutex_unlock( &rtsp
->lock
);
972 case HTTPD_MSG_GETPARAMETER
:
974 if( query
->i_body
> 0 )
976 answer
->i_status
= 451;
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
);
985 RtspClientAlive(ses
);
986 vlc_mutex_unlock( &rtsp
->lock
);
990 case HTTPD_MSG_TEARDOWN
:
994 answer
->i_status
= 200;
996 psz_session
= httpd_MsgGet( query
, "Session" );
998 vlc_mutex_lock( &rtsp
->lock
);
999 ses
= RtspClientGet( rtsp
, psz_session
);
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
);
1025 return VLC_EGENERIC
;
1030 if (rtsp
->timeout
!= 0)
1031 httpd_MsgAdd( answer
, "Session", "%s;timeout=%" PRIu64
, psz_session
,
1032 SEC_FROM_VLC_TICK(rtsp
->timeout
) );
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" );
1042 httpd_MsgAdd( answer
, "Cseq", "%s", psz
);
1043 psz
= httpd_MsgGet( query
, "Timestamp" );
1045 httpd_MsgAdd( answer
, "Timestamp", "%s", psz
);
1051 /** Aggregate RTSP callback */
1052 static int RtspCallback( httpd_callback_sys_t
*p_args
,
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
,
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
);