X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fstream_out%2Frtsp.c;h=cece5bc85e94565deb5b2a66f52a1b5b0e30e019;hb=cde81e52f2d6e17383aced57fe6312eafa79cacf;hp=73b126808a8e46575968184428e93f67daaf5599;hpb=1579921119c69639edac4b7b59830dd06753a4d7;p=vlc diff --git a/modules/stream_out/rtsp.c b/modules/stream_out/rtsp.c index 73b126808a..cece5bc85e 100644 --- a/modules/stream_out/rtsp.c +++ b/modules/stream_out/rtsp.c @@ -43,6 +43,7 @@ #include #include #include +#include #ifndef WIN32 # include @@ -64,7 +65,6 @@ struct rtsp_stream_t httpd_url_t *url; char *psz_path; unsigned track_id; - unsigned port; int sessionc; rtsp_session_t **sessionv; @@ -85,11 +85,11 @@ static void RtspClientDel( rtsp_stream_t *rtsp, rtsp_session_t *session ); static void RtspTimeOut( void *data ); rtsp_stream_t *RtspSetup( vlc_object_t *owner, vod_media_t *media, - const vlc_url_t *url ) + const char *path ) { rtsp_stream_t *rtsp = malloc( sizeof( *rtsp ) ); - if( rtsp == NULL || ( url->i_port > 99999 ) ) + if( rtsp == NULL ) { free( rtsp ); return NULL; @@ -112,21 +112,22 @@ rtsp_stream_t *RtspSetup( vlc_object_t *owner, vod_media_t *media, goto error; } - rtsp->port = (url->i_port > 0) ? url->i_port : 554; - rtsp->psz_path = strdup( ( url->psz_path != NULL ) ? url->psz_path : "/" ); + rtsp->psz_path = strdup( (path != NULL) ? path : "/" ); if( rtsp->psz_path == NULL ) goto error; - msg_Dbg( owner, "RTSP stream: host %s port %d at %s", - url->psz_host, rtsp->port, rtsp->psz_path ); + msg_Dbg( owner, "RTSP stream at %s", rtsp->psz_path ); - rtsp->host = httpd_HostNew( VLC_OBJECT(owner), url->psz_host, - rtsp->port ); + rtsp->host = vlc_rtsp_HostNew( VLC_OBJECT(owner) ); if( rtsp->host == NULL ) goto error; - rtsp->url = httpd_UrlNewUnique( rtsp->host, rtsp->psz_path, - NULL, NULL, NULL ); + char *user = var_InheritString(owner, "sout-rtsp-user"); + char *pwd = var_InheritString(owner, "sout-rtsp-pwd"); + + rtsp->url = httpd_UrlNew( rtsp->host, rtsp->psz_path, user, pwd ); + free(user); + free(pwd); if( rtsp->url == NULL ) goto error; @@ -186,7 +187,6 @@ struct rtsp_session_t rtsp_stream_t *stream; uint64_t id; mtime_t last_seen; /* for timeouts */ - bool vod_started; /* true if the VoD media instance was created */ /* output (id-access) */ int trackc; @@ -253,7 +253,13 @@ rtsp_stream_id_t *RtspAddId( rtsp_stream_t *rtsp, sout_stream_id_t *sid, } msg_Dbg( rtsp->owner, "RTSP: adding %s", urlbuf ); - url = id->url = httpd_UrlNewUnique( rtsp->host, urlbuf, NULL, NULL, NULL ); + + char *user = var_InheritString(rtsp->owner, "sout-rtsp-user"); + char *pwd = var_InheritString(rtsp->owner, "sout-rtsp-pwd"); + + url = id->url = httpd_UrlNew( rtsp->host, urlbuf, user, pwd ); + free( user ); + free( pwd ); free( urlbuf ); if( url == NULL ) @@ -353,7 +359,6 @@ rtsp_session_t *RtspClientNew( rtsp_stream_t *rtsp ) s->stream = rtsp; vlc_rand_bytes (&s->id, sizeof (s->id)); - s->vod_started = false; s->trackc = 0; s->trackv = NULL; @@ -417,7 +422,7 @@ static void RtspClientAlive( rtsp_session_t *session ) static int dup_socket(int oldfd) { int newfd; -#if !defined(WIN32) || defined(UNDER_CE) +#ifndef WIN32 newfd = vlc_dup(oldfd); #else WSAPROTOCOL_INFO info; @@ -443,31 +448,48 @@ int RtspTrackAttach( rtsp_stream_t *rtsp, const char *name, if (session == NULL) goto out; - for (int i = 0; session->trackc; i++) + rtsp_strack_t *tr = NULL; + for (int i = 0; i < session->trackc; i++) { - rtsp_strack_t *tr = session->trackv + i; - if (tr->id == id) + if (session->trackv[i].id == id) { - tr->rtp_fd = dup_socket(tr->setup_fd); - if (tr->rtp_fd == -1) - break; + tr = session->trackv + i; + break; + } + } - tr->sout_id = sout_id; + if (tr != NULL) + { + tr->sout_id = sout_id; + tr->rtp_fd = dup_socket(tr->setup_fd); + } + else + { + /* The track was not SETUP. We still create one because we'll + * need the sout_id if we set it up later. */ + rtsp_strack_t track = { .id = id, .sout_id = sout_id, + .setup_fd = -1, .rtp_fd = -1 }; + vlc_rand_bytes (&track.seq_init, sizeof (track.seq_init)); + vlc_rand_bytes (&track.ssrc, sizeof (track.ssrc)); + + INSERT_ELEM(session->trackv, session->trackc, session->trackc, track); + tr = session->trackv + session->trackc - 1; + } - uint16_t seq; - *ssrc = ntohl(tr->ssrc); - *seq_init = tr->seq_init; - rtp_add_sink(tr->sout_id, tr->rtp_fd, false, &seq); - /* To avoid race conditions, sout_id->i_seq_sent_next must - * be set here and now. Make sure the caller did its job - * properly when passing seq_init. */ - assert(tr->seq_init == seq); + *ssrc = ntohl(tr->ssrc); + *seq_init = tr->seq_init; - val = VLC_SUCCESS; - break; - } + if (tr->rtp_fd != -1) + { + uint16_t seq; + rtp_add_sink(tr->sout_id, tr->rtp_fd, false, &seq); + /* To avoid race conditions, sout_id->i_seq_sent_next must + * be set here and now. Make sure the caller did its job + * properly when passing seq_init. */ + assert(tr->seq_init == seq); } + val = VLC_SUCCESS; out: vlc_mutex_unlock(&rtsp->lock); return val; @@ -486,14 +508,26 @@ void RtspTrackDetach( rtsp_stream_t *rtsp, const char *name, if (session == NULL) goto out; - for (int i = 0; session->trackc; i++) + for (int i = 0; i < session->trackc; i++) { rtsp_strack_t *tr = session->trackv + i; if (tr->sout_id == sout_id) { + if (tr->setup_fd == -1) + { + /* No (more) SETUP information: better get rid of the + * track so that we can have new random ssrc and + * seq_init next time. */ + REMOVE_ELEM( session->trackv, session->trackc, i ); + break; + } + /* We keep the SETUP information of the track, but stop it */ + if (tr->rtp_fd != -1) + { + rtp_del_sink(tr->sout_id, tr->rtp_fd); + tr->rtp_fd = -1; + } tr->sout_id = NULL; - rtp_del_sink(sout_id, tr->rtp_fd); - tr->rtp_fd = -1; break; } } @@ -506,9 +540,16 @@ out: /** rtsp must be locked */ static void RtspTrackClose( rtsp_strack_t *tr ) { - if (tr->rtp_fd != -1) - rtp_del_sink(tr->sout_id, tr->rtp_fd); - net_Close(tr->setup_fd); + if (tr->setup_fd != -1) + { + if (tr->rtp_fd != -1) + { + rtp_del_sink(tr->sout_id, tr->rtp_fd); + tr->rtp_fd = -1; + } + net_Close(tr->setup_fd); + tr->setup_fd = -1; + } } @@ -549,14 +590,14 @@ static int64_t ParseNPT (const char *str) sec += ((hour * 60) + min) * 60; else if (sscanf (str, "%f", &sec) != 1) - sec = 0.; + sec = -1; if (loc != (locale_t)0) { uselocale (oldloc); freelocale (loc); } - return sec * CLOCK_FREQ; + return sec < 0 ? -1 : sec * CLOCK_FREQ; } @@ -585,18 +626,17 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id, { /* Build self-referential control URL */ char ip[NI_MAXNUMERICHOST], *ptr; + int port; - httpd_ServerIP( cl, ip ); + httpd_ServerIP( cl, ip, &port ); ptr = strchr( ip, '%' ); if( ptr != NULL ) *ptr = '\0'; if( strchr( ip, ':' ) != NULL ) - sprintf( control, "rtsp://[%s]:%u%s", ip, rtsp->port, - rtsp->psz_path ); + sprintf( control, "rtsp://[%s]:%d%s", ip, port, rtsp->psz_path ); else - sprintf( control, "rtsp://%s:%u%s", ip, rtsp->port, - rtsp->psz_path ); + sprintf( control, "rtsp://%s:%d%s", ip, port, rtsp->psz_path ); } /* */ @@ -777,7 +817,7 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id, int fd, sport; uint32_t ssrc; - if( httpd_ClientIP( cl, ip ) == NULL ) + if( httpd_ClientIP( cl, ip, NULL ) == NULL ) { answer->i_status = 500; continue; @@ -799,18 +839,6 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id, sizeof (int)); net_GetSockAddress( fd, src, &sport ); - rtsp_strack_t track = { .id = id, .sout_id = id->sout_id, - .setup_fd = fd, .rtp_fd = -1 }; - - if (vod) - { - vlc_rand_bytes (&track.seq_init, sizeof (track.seq_init)); - vlc_rand_bytes (&track.ssrc, sizeof (track.ssrc)); - ssrc = track.ssrc; - } - else - ssrc = id->ssrc; - vlc_mutex_lock( &rtsp->lock ); if( psz_session == NULL ) { @@ -832,55 +860,74 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id, } RtspClientAlive(ses); - /* Bail if the track is already set up: we don't - * support changing the transport parameters on the - * fly */ - bool setup = false; + rtsp_strack_t *tr = NULL; for (int i = 0; i < ses->trackc; i++) { if (ses->trackv[i].id == id) { - setup = true; + tr = ses->trackv + i; break; } } - if (setup) + + if (tr == NULL) + { + /* Set up a new track */ + rtsp_strack_t track = { .id = id, + .sout_id = id->sout_id, + .setup_fd = fd, + .rtp_fd = -1 }; + + if (vod) + { + vlc_rand_bytes (&track.seq_init, + sizeof (track.seq_init)); + vlc_rand_bytes (&track.ssrc, sizeof (track.ssrc)); + ssrc = track.ssrc; + } + else + ssrc = id->ssrc; + + INSERT_ELEM( ses->trackv, ses->trackc, ses->trackc, + track ); + } + else if (tr->setup_fd == -1) + { + /* The track was not SETUP, but it exists + * because there is a sout_id running for it */ + tr->setup_fd = fd; + ssrc = tr->ssrc; + } + else { + /* The track is already set up, and we don't + * support changing the transport parameters on + * the fly */ vlc_mutex_unlock( &rtsp->lock ); answer->i_status = 455; net_Close( fd ); break; } - - INSERT_ELEM( ses->trackv, ses->trackc, ses->trackc, - track ); vlc_mutex_unlock( &rtsp->lock ); - httpd_ServerIP( cl, ip ); + httpd_ServerIP( cl, ip, NULL ); + /* Specify source IP only if it is different from the + * RTSP control connection server address */ if( strcmp( src, ip ) ) { - /* Specify source IP if it is different from the RTSP - * control connection server address */ char *ptr = strchr( src, '%' ); if( ptr != NULL ) *ptr = '\0'; /* remove scope ID */ - - httpd_MsgAdd( answer, "Transport", - "RTP/AVP/UDP;unicast;source=%s;" - "client_port=%u-%u;server_port=%u-%u;" - "ssrc=%08X;mode=play", - src, loport, loport + 1, sport, - sport + 1, ssrc ); } else - { - httpd_MsgAdd( answer, "Transport", - "RTP/AVP/UDP;unicast;" - "client_port=%u-%u;server_port=%u-%u;" - "ssrc=%08X;mode=play", - loport, loport + 1, sport, sport + 1, - ssrc ); - } + src[0] = '\0'; + + httpd_MsgAdd( answer, "Transport", + "RTP/AVP/UDP;unicast%s%s;" + "client_port=%u-%u;server_port=%u-%u;" + "ssrc=%08X;mode=play", + src[0] ? ";source=" : "", src, + loport, loport + 1, sport, sport + 1, ssrc ); answer->i_status = 200; } @@ -894,13 +941,44 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id, answer->i_status = 200; psz_session = httpd_MsgGet( query, "Session" ); + int64_t start = -1, end = -1, npt; const char *range = httpd_MsgGet (query, "Range"); - if (range != NULL && strncmp (range, "npt=", 4)) + if (range != NULL) { - answer->i_status = 501; - break; - } + if (strncmp (range, "npt=", 4)) + { + answer->i_status = 501; + break; + } + + start = ParseNPT (range + 4); + range = strchr(range, '-'); + if (range != NULL && *(range + 1)) + end = ParseNPT (range + 1); + + if (end >= 0 && end < start) + { + answer->i_status = 457; + break; + } + if (vod) + { + if (vod_check_range(rtsp->vod_media, psz_session, + start, end) != VLC_SUCCESS) + { + answer->i_status = 457; + break; + } + } + /* We accept start times of 0 even for broadcast streams + * that already started */ + else if (start > 0 || end >= 0) + { + answer->i_status = 456; + break; + } + } vlc_mutex_lock( &rtsp->lock ); ses = RtspClientGet( rtsp, psz_session ); if( ses != NULL ) @@ -915,10 +993,7 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id, if (vod) { /* We don't keep a reference to the sout_stream_t, - * so we check if a sout_id is available instead. - * FIXME: this is broken if the stream is still - * running but with no track set up; but this case - * is already broken anyway (see below). */ + * so we check if a sout_id is available instead. */ for (int i = 0; i < ses->trackc; i++) { sout_id = ses->trackv[i].sout_id; @@ -927,24 +1002,28 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id, } } int64_t ts = rtp_get_ts(vod ? NULL : (sout_stream_t *)owner, - sout_id, rtsp->vod_media, psz_session); + sout_id, rtsp->vod_media, psz_session, + vod ? NULL : &npt); for( int i = 0; i < ses->trackc; i++ ) { rtsp_strack_t *tr = ses->trackv + i; if( ( id == NULL ) || ( tr->id == id ) ) { + if (tr->setup_fd == -1) + /* Track not SETUP */ + continue; + uint16_t seq; if( tr->rtp_fd == -1 ) { - if (vod) - /* TODO: if the RTP stream output is already - * started, it won't pick up newly set-up - * tracks, so we need to call rtp_add_sink() - * or something. */ + /* Track not PLAYing yet */ + if (tr->sout_id == NULL) + /* Instance not running yet (VoD) */ seq = tr->seq_init; else { + /* Instance running, add a sink to it */ tr->rtp_fd = dup_socket(tr->setup_fd); if (tr->rtp_fd == -1) continue; @@ -955,6 +1034,7 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id, } else { + /* Track already playing */ assert( tr->sout_id != NULL ); seq = rtp_get_seq( tr->sout_id ); } @@ -971,27 +1051,20 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id, info[infolen - 2] = '\0'; /* remove trailing ", " */ httpd_MsgAdd( answer, "RTP-Info", "%s", info ); } + } + vlc_mutex_unlock( &rtsp->lock ); + + if (ses != NULL) + { if (vod) { - /* TODO: fix that crap, this is barely RTSP */ - if (!ses->vod_started) - { - vod_start(rtsp->vod_media, psz_session); - ses->vod_started = true; - } - else - { - if (range != NULL) - { - int64_t time = ParseNPT (range + 4); - vod_seek(rtsp->vod_media, psz_session, time); - } - /* This is the thing to do to unpause... */ - vod_start(rtsp->vod_media, psz_session); - } + vod_play(rtsp->vod_media, psz_session, &start, end); + npt = start; } + + double f_npt = (double) npt / CLOCK_FREQ; + httpd_MsgAdd( answer, "Range", "npt=%f-", f_npt ); } - vlc_mutex_unlock( &rtsp->lock ); if( httpd_MsgGet( query, "Scale" ) != NULL ) httpd_MsgAdd( answer, "Scale", "1." ); @@ -1016,12 +1089,7 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id, ses = RtspClientGet( rtsp, psz_session ); if (ses != NULL) { - if (id == NULL) - { - if (vod) - vod_pause(rtsp->vod_media, psz_session); - } - else /* "Mute" the selected track */ + if (id != NULL) /* "Mute" the selected track */ { bool found = false; for (int i = 0; i < ses->trackc; i++) @@ -1029,12 +1097,15 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id, rtsp_strack_t *tr = ses->trackv + i;; if (tr->id == id) { + if (tr->setup_fd == -1) + break; + + found = true; if (tr->rtp_fd != -1) { rtp_del_sink(tr->sout_id, tr->rtp_fd); tr->rtp_fd = -1; } - found = true; break; } } @@ -1044,6 +1115,15 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id, RtspClientAlive(ses); } vlc_mutex_unlock( &rtsp->lock ); + + if (ses != NULL && id == NULL) + { + assert(vod); + int64_t npt = 0; + vod_pause(rtsp->vod_media, psz_session, &npt); + double f_npt = (double) npt / CLOCK_FREQ; + httpd_MsgAdd( answer, "Range", "npt=%f-", f_npt ); + } break; } @@ -1089,7 +1169,10 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id, if( ses->trackv[i].id == id ) { RtspTrackClose( &ses->trackv[i] ); - REMOVE_ELEM( ses->trackv, ses->trackc, i ); + /* Keep VoD tracks whose instance is still + * running */ + if (!(vod && ses->trackv[i].sout_id != NULL)) + REMOVE_ELEM( ses->trackv, ses->trackc, i ); } } RtspClientAlive(ses);