Commit 53fc8f8c authored by Pierre Ynard's avatar Pierre Ynard

rtsp: support Range header in responses

For VoD, the synchronization is bad, but accurate synchronization would
require at least changes in the core to add support for some kind of
"instant pausing" to the stream output. Synchronous calls to the VLM
shouldn't cause delay problems, no operation waits for the input.
parent 31b8ffbf
...@@ -69,8 +69,8 @@ static inline int vod_MediaControl( vod_t *p_vod, vod_media_t *p_media, ...@@ -69,8 +69,8 @@ static inline int vod_MediaControl( vod_t *p_vod, vod_media_t *p_media,
enum vod_query_e enum vod_query_e
{ {
VOD_MEDIA_PLAY, /* arg1= char * res= */ VOD_MEDIA_PLAY, /* arg1= char *, arg2= int64_t *, res= */
VOD_MEDIA_PAUSE, /* arg1= res= */ VOD_MEDIA_PAUSE, /* arg1= int64_t * res= */
VOD_MEDIA_STOP, /* arg1= res=can fail */ VOD_MEDIA_STOP, /* arg1= res=can fail */
VOD_MEDIA_SEEK, /* arg1= double res= */ VOD_MEDIA_SEEK, /* arg1= double res= */
VOD_MEDIA_REWIND, /* arg1= double res= */ VOD_MEDIA_REWIND, /* arg1= double res= */
......
...@@ -826,12 +826,14 @@ static void* CommandThread( vlc_object_t *p_this ) ...@@ -826,12 +826,14 @@ static void* CommandThread( vlc_object_t *p_this )
switch( cmd.i_type ) switch( cmd.i_type )
{ {
case RTSP_CMD_TYPE_PLAY: case RTSP_CMD_TYPE_PLAY:
cmd.i_arg = -1;
vod_MediaControl( p_vod, p_media, cmd.psz_session, vod_MediaControl( p_vod, p_media, cmd.psz_session,
VOD_MEDIA_PLAY, cmd.psz_arg ); VOD_MEDIA_PLAY, cmd.psz_arg, &cmd.i_arg );
break; break;
case RTSP_CMD_TYPE_PAUSE: case RTSP_CMD_TYPE_PAUSE:
cmd.i_arg = -1;
vod_MediaControl( p_vod, p_media, cmd.psz_session, vod_MediaControl( p_vod, p_media, cmd.psz_session,
VOD_MEDIA_PAUSE ); VOD_MEDIA_PAUSE, &cmd.i_arg );
break; break;
case RTSP_CMD_TYPE_STOP: case RTSP_CMD_TYPE_STOP:
......
...@@ -1552,11 +1552,16 @@ static int64_t rtp_init_ts( const vod_media_t *p_media, ...@@ -1552,11 +1552,16 @@ static int64_t rtp_init_ts( const vod_media_t *p_media,
/* Return a timestamp corresponding to packets being sent now, and that /* Return a timestamp corresponding to packets being sent now, and that
* can be passed to rtp_compute_ts() to get rtptime values for each ES. * can be passed to rtp_compute_ts() to get rtptime values for each ES.
* If the stream output is not started, the initial timestamp that will * Also return the NPT corresponding to this timestamp. If the stream
* be used with the first packets is returned instead. */ * output is not started, the initial timestamp that will be used with
* the first packets for NPT=0 is returned instead. */
int64_t rtp_get_ts( const sout_stream_t *p_stream, const sout_stream_id_t *id, int64_t rtp_get_ts( const sout_stream_t *p_stream, const sout_stream_id_t *id,
const vod_media_t *p_media, const char *psz_vod_session ) const vod_media_t *p_media, const char *psz_vod_session,
int64_t *p_npt )
{ {
if (p_npt != NULL)
*p_npt = 0;
if (id != NULL) if (id != NULL)
p_stream = id->p_stream; p_stream = id->p_stream;
...@@ -1576,7 +1581,11 @@ int64_t rtp_get_ts( const sout_stream_t *p_stream, const sout_stream_id_t *id, ...@@ -1576,7 +1581,11 @@ int64_t rtp_get_ts( const sout_stream_t *p_stream, const sout_stream_id_t *id,
if( now < i_npt_zero ) if( now < i_npt_zero )
return p_sys->i_pts_zero; return p_sys->i_pts_zero;
return p_sys->i_pts_zero + (now - i_npt_zero); int64_t npt = now - i_npt_zero;
if (p_npt != NULL)
*p_npt = npt;
return p_sys->i_pts_zero + npt;
} }
void rtp_packetize_common( sout_stream_id_t *id, block_t *out, void rtp_packetize_common( sout_stream_id_t *id, block_t *out,
......
...@@ -50,7 +50,8 @@ int rtp_add_sink( sout_stream_id_t *id, int fd, bool rtcp_mux, uint16_t *seq ); ...@@ -50,7 +50,8 @@ int rtp_add_sink( sout_stream_id_t *id, int fd, bool rtcp_mux, uint16_t *seq );
void rtp_del_sink( sout_stream_id_t *id, int fd ); void rtp_del_sink( sout_stream_id_t *id, int fd );
uint16_t rtp_get_seq( sout_stream_id_t *id ); uint16_t rtp_get_seq( sout_stream_id_t *id );
int64_t rtp_get_ts( const sout_stream_t *p_stream, const sout_stream_id_t *id, int64_t rtp_get_ts( const sout_stream_t *p_stream, const sout_stream_id_t *id,
const vod_media_t *p_media, const char *psz_vod_session ); const vod_media_t *p_media, const char *psz_vod_session,
int64_t *p_npt );
/* RTP packetization */ /* RTP packetization */
void rtp_packetize_common (sout_stream_id_t *id, block_t *out, void rtp_packetize_common (sout_stream_id_t *id, block_t *out,
...@@ -95,8 +96,8 @@ void CloseVoD( vlc_object_t * ); ...@@ -95,8 +96,8 @@ void CloseVoD( vlc_object_t * );
int vod_check_range(vod_media_t *p_media, const char *psz_session, int vod_check_range(vod_media_t *p_media, const char *psz_session,
int64_t start, int64_t end); int64_t start, int64_t end);
void vod_play(vod_media_t *p_media, const char *psz_session, void vod_play(vod_media_t *p_media, const char *psz_session,
int64_t start, int64_t end, bool running); int64_t *start, int64_t end);
void vod_pause(vod_media_t *p_media, const char *psz_session); void vod_pause(vod_media_t *p_media, const char *psz_session, int64_t *npt);
void vod_stop(vod_media_t *p_media, const char *psz_session); void vod_stop(vod_media_t *p_media, const char *psz_session);
const char *vod_get_mux(const vod_media_t *p_media); const char *vod_get_mux(const vod_media_t *p_media);
......
...@@ -934,7 +934,7 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id, ...@@ -934,7 +934,7 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
answer->i_status = 200; answer->i_status = 200;
psz_session = httpd_MsgGet( query, "Session" ); psz_session = httpd_MsgGet( query, "Session" );
int64_t start = -1, end = -1; int64_t start = -1, end = -1, npt;
const char *range = httpd_MsgGet (query, "Range"); const char *range = httpd_MsgGet (query, "Range");
if (range != NULL) if (range != NULL)
{ {
...@@ -995,7 +995,8 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id, ...@@ -995,7 +995,8 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
} }
} }
int64_t ts = rtp_get_ts(vod ? NULL : (sout_stream_t *)owner, 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++ ) for( int i = 0; i < ses->trackc; i++ )
{ {
...@@ -1045,12 +1046,18 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id, ...@@ -1045,12 +1046,18 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
} }
if (vod) if (vod)
{ {
bool running = (sout_id != NULL); vod_play(rtsp->vod_media, psz_session, &start, end);
vod_play(rtsp->vod_media, psz_session, start, end, running); npt = start;
} }
} }
vlc_mutex_unlock( &rtsp->lock ); vlc_mutex_unlock( &rtsp->lock );
if (ses != NULL)
{
double f_npt = (double) npt / CLOCK_FREQ;
httpd_MsgAdd( answer, "Range", "npt=%f-", f_npt );
}
if( httpd_MsgGet( query, "Scale" ) != NULL ) if( httpd_MsgGet( query, "Scale" ) != NULL )
httpd_MsgAdd( answer, "Scale", "1." ); httpd_MsgAdd( answer, "Scale", "1." );
break; break;
...@@ -1076,8 +1083,11 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id, ...@@ -1076,8 +1083,11 @@ static int RtspHandler( rtsp_stream_t *rtsp, rtsp_stream_id_t *id,
{ {
if (id == NULL) if (id == NULL)
{ {
if (vod) assert(vod);
vod_pause(rtsp->vod_media, psz_session); int64_t npt;
vod_pause(rtsp->vod_media, psz_session, &npt);
double f_npt = (double) npt / CLOCK_FREQ;
httpd_MsgAdd( answer, "Range", "npt=%f-", f_npt );
} }
else /* "Mute" the selected track */ else /* "Mute" the selected track */
{ {
......
...@@ -88,11 +88,13 @@ typedef enum ...@@ -88,11 +88,13 @@ typedef enum
{ {
RTSP_CMD_TYPE_NONE, /* Exit requested */ RTSP_CMD_TYPE_NONE, /* Exit requested */
#if 0
RTSP_CMD_TYPE_PLAY, RTSP_CMD_TYPE_PLAY,
RTSP_CMD_TYPE_PAUSE, RTSP_CMD_TYPE_PAUSE,
#endif
RTSP_CMD_TYPE_STOP, RTSP_CMD_TYPE_STOP,
RTSP_CMD_TYPE_SEEK,
#if 0 #if 0
RTSP_CMD_TYPE_SEEK,
RTSP_CMD_TYPE_REWIND, RTSP_CMD_TYPE_REWIND,
RTSP_CMD_TYPE_FORWARD, RTSP_CMD_TYPE_FORWARD,
#endif #endif
...@@ -391,6 +393,7 @@ static void* CommandThread( vlc_object_t *p_this ) ...@@ -391,6 +393,7 @@ static void* CommandThread( vlc_object_t *p_this )
/* */ /* */
switch( cmd.i_type ) switch( cmd.i_type )
{ {
#if 0
case RTSP_CMD_TYPE_PLAY: case RTSP_CMD_TYPE_PLAY:
vod_MediaControl( p_vod, p_media, cmd.psz_session, vod_MediaControl( p_vod, p_media, cmd.psz_session,
VOD_MEDIA_PLAY, cmd.psz_arg ); VOD_MEDIA_PLAY, cmd.psz_arg );
...@@ -399,17 +402,18 @@ static void* CommandThread( vlc_object_t *p_this ) ...@@ -399,17 +402,18 @@ static void* CommandThread( vlc_object_t *p_this )
vod_MediaControl( p_vod, p_media, cmd.psz_session, vod_MediaControl( p_vod, p_media, cmd.psz_session,
VOD_MEDIA_PAUSE ); VOD_MEDIA_PAUSE );
break; break;
#endif
case RTSP_CMD_TYPE_STOP: case RTSP_CMD_TYPE_STOP:
vod_MediaControl( p_vod, p_media, cmd.psz_session, VOD_MEDIA_STOP ); vod_MediaControl( p_vod, p_media, cmd.psz_session, VOD_MEDIA_STOP );
break; break;
#if 0
case RTSP_CMD_TYPE_SEEK: case RTSP_CMD_TYPE_SEEK:
vod_MediaControl( p_vod, p_media, cmd.psz_session, vod_MediaControl( p_vod, p_media, cmd.psz_session,
VOD_MEDIA_SEEK, cmd.i_arg ); VOD_MEDIA_SEEK, cmd.i_arg );
break; break;
#if 0
case RTSP_CMD_TYPE_REWIND: case RTSP_CMD_TYPE_REWIND:
vod_MediaControl( p_vod, p_media, cmd.psz_session, vod_MediaControl( p_vod, p_media, cmd.psz_session,
VOD_MEDIA_REWIND, cmd.f_arg ); VOD_MEDIA_REWIND, cmd.f_arg );
...@@ -522,33 +526,22 @@ int vod_check_range(vod_media_t *p_media, const char *psz_session, ...@@ -522,33 +526,22 @@ int vod_check_range(vod_media_t *p_media, const char *psz_session,
/* TODO: add support in the VLM for queueing proper PLAY requests with /* TODO: add support in the VLM for queueing proper PLAY requests with
* start and end times, fetch whether the input is seekable... and then * start and end times, fetch whether the input is seekable... and then
* clean this up and remove the running argument */ * clean this up */
void vod_play(vod_media_t *p_media, const char *psz_session, void vod_play(vod_media_t *p_media, const char *psz_session,
int64_t start, int64_t end, bool running) int64_t *start, int64_t end)
{ {
if (vod_check_range(p_media, psz_session, start, end) != VLC_SUCCESS) if (vod_check_range(p_media, psz_session, *start, end) != VLC_SUCCESS)
return; return;
/* We want to seek before unpausing, but it won't /* We're passing the #vod{} sout chain here */
* work if the instance is not running yet. */ vod_MediaControl(p_media->p_vod, p_media, psz_session,
VOD_MEDIA_PLAY, "vod", start);
if (!running)
/* We're passing the #vod{} sout chain here */
CommandPush(p_media->p_vod, RTSP_CMD_TYPE_PLAY, p_media,
psz_session, 0, "vod");
if (start >= 0)
CommandPush(p_media->p_vod, RTSP_CMD_TYPE_SEEK, p_media,
psz_session, start, NULL);
if (running)
/* This is the thing to do to unpause... */
CommandPush(p_media->p_vod, RTSP_CMD_TYPE_PLAY, p_media,
psz_session, 0, "vod");
} }
void vod_pause(vod_media_t *p_media, const char *psz_session) void vod_pause(vod_media_t *p_media, const char *psz_session, int64_t *npt)
{ {
CommandPush(p_media->p_vod, RTSP_CMD_TYPE_PAUSE, p_media, vod_MediaControl(p_media->p_vod, p_media, psz_session,
psz_session, 0, NULL); VOD_MEDIA_PAUSE, npt);
} }
void vod_stop(vod_media_t *p_media, const char *psz_session) void vod_stop(vod_media_t *p_media, const char *psz_session)
......
...@@ -317,13 +317,40 @@ static int vlm_MediaVodControl( void *p_private, vod_media_t *p_vod_media, ...@@ -317,13 +317,40 @@ static int vlm_MediaVodControl( void *p_private, vod_media_t *p_vod_media,
switch( i_query ) switch( i_query )
{ {
case VOD_MEDIA_PLAY: case VOD_MEDIA_PLAY:
{
psz = (const char *)va_arg( args, const char * ); psz = (const char *)va_arg( args, const char * );
int64_t *i_time = (int64_t *)va_arg( args, int64_t *);
bool b_retry = false;
if (*i_time < 0)
{
/* No start time requested: return the current NPT */
i_ret = vlm_ControlInternal( vlm, VLM_GET_MEDIA_INSTANCE_TIME, id, psz_id, i_time );
/* The instance is not running yet, it will start at 0 */
if (i_ret)
*i_time = 0;
}
else
{
/* We want to seek before unpausing, but it won't
* work if the instance is not running yet. */
b_retry = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_TIME, id, psz_id, *i_time );
}
i_ret = vlm_ControlInternal( vlm, VLM_START_MEDIA_VOD_INSTANCE, id, psz_id, 0, psz ); i_ret = vlm_ControlInternal( vlm, VLM_START_MEDIA_VOD_INSTANCE, id, psz_id, 0, psz );
if (!i_ret && b_retry)
i_ret = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_TIME, id, psz_id, *i_time );
break; break;
}
case VOD_MEDIA_PAUSE: case VOD_MEDIA_PAUSE:
{
int64_t *i_time = (int64_t *)va_arg( args, int64_t *);
i_ret = vlm_ControlInternal( vlm, VLM_PAUSE_MEDIA_INSTANCE, id, psz_id ); i_ret = vlm_ControlInternal( vlm, VLM_PAUSE_MEDIA_INSTANCE, id, psz_id );
if (!i_ret)
i_ret = vlm_ControlInternal( vlm, VLM_GET_MEDIA_INSTANCE_TIME, id, psz_id, i_time );
break; break;
}
case VOD_MEDIA_STOP: case VOD_MEDIA_STOP:
i_ret = vlm_ControlInternal( vlm, VLM_STOP_MEDIA_INSTANCE, id, psz_id ); i_ret = vlm_ControlInternal( vlm, VLM_STOP_MEDIA_INSTANCE, id, psz_id );
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment