/*****************************************************************************
* live.cpp : live.com support.
*****************************************************************************
- * Copyright (C) 2003 VideoLAN
- * $Id: livedotcom.cpp,v 1.16 2004/02/23 20:35:42 fenrir Exp $
+ * Copyright (C) 2003-2004 VideoLAN
+ * $Id$
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
*
#include <vlc/vlc.h>
#include <vlc/input.h>
-#include <codecs.h> /* BITMAPINFOHEADER, WAVEFORMATEX */
#include <iostream>
#if defined( WIN32 )
#define CACHING_TEXT N_("Caching value (ms)")
#define CACHING_LONGTEXT N_( \
- "Allows you to modify the default caching value for rtsp streams. This " \
+ "Allows you to modify the default caching value for RTSP streams. This " \
"value should be set in miliseconds units." )
vlc_module_begin();
set_description( _("live.com (RTSP/RTP/SDP) demuxer" ) );
- set_capability( "demux", 50 );
+ set_capability( "demux2", 50 );
set_callbacks( DemuxOpen, DemuxClose );
add_shortcut( "live" );
set_capability( "access", 0 );
set_callbacks( AccessOpen, AccessClose );
add_bool( "rtsp-tcp", 0, NULL,
- N_("Use rtp over rtsp (tcp)"),
- N_("Use rtp over rtsp (tcp)"), VLC_TRUE );
+ N_("Use RTP over RTSP (TCP)"),
+ N_("Use RTP over RTSP (TCP)"), VLC_TRUE );
add_integer( "rtsp-caching", 4 * DEFAULT_PTS_DELAY / 1000, NULL,
CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
vlc_module_end();
/* TODO:
- * - Support PS/TS (need to rework the TS/PS demuxer a lot).
+ * - Improve support of PS/TS
* - Support X-QT/X-QUICKTIME generic codec for audio.
*
* - Check memory leak, delete/free -> still one when using rtsp-tcp but I'm
*/
/*****************************************************************************
- * Local prototypes
+ * Local prototypes for access
*****************************************************************************/
struct access_sys_t
{
int i_pos;
};
-typedef struct
-{
- input_thread_t *p_input;
-
- vlc_bool_t b_quicktime;
-
- es_format_t fmt;
- es_out_id_t *p_es;
-
- RTPSource *rtpSource;
- FramedSource *readSource;
- vlc_bool_t b_rtcp_sync;
-
- uint8_t buffer[65536];
-
- char waiting;
-
- mtime_t i_pts;
-} live_track_t;
-
-struct demux_sys_t
-{
- char *p_sdp; /* XXX mallocated */
-
- MediaSession *ms;
- TaskScheduler *scheduler;
- UsageEnvironment *env ;
- RTSPClient *rtsp;
-
- int i_track;
- live_track_t **track; /* XXX mallocated */
- mtime_t i_pcr;
- mtime_t i_pcr_start;
-
- mtime_t i_length;
- mtime_t i_start;
-
- char event;
-};
-
static ssize_t Read ( input_thread_t *, byte_t *, size_t );
static ssize_t MRLRead( input_thread_t *, byte_t *, size_t );
-static int Demux ( input_thread_t * );
-static int Control( input_thread_t *, int, va_list );
-
-
-
/*****************************************************************************
* AccessOpen:
*****************************************************************************/
p_input->stream.b_pace_control = val.b_bool;
p_input->stream.p_selected_area->i_tell = 0;
p_input->stream.b_seekable = 1; /* Hack to display time */
- p_input->stream.p_selected_area->i_size = 0;
+ p_input->stream.p_selected_area->i_size = p_sys->i_sdp;
p_input->stream.i_method = INPUT_METHOD_NETWORK;
vlc_mutex_unlock( &p_input->stream.stream_lock );
p_input->stream.b_pace_control = VLC_TRUE;
p_input->stream.p_selected_area->i_tell = 0;
p_input->stream.b_seekable = VLC_FALSE;
- p_input->stream.p_selected_area->i_size = 0;
+ p_input->stream.p_selected_area->i_size = strlen(p_input->psz_name);
p_input->stream.i_method = INPUT_METHOD_NETWORK;
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
+/*****************************************************************************
+ * Local prototypes for demux2
+ *****************************************************************************/
+typedef struct
+{
+ demux_t *p_demux;
+
+ vlc_bool_t b_quicktime;
+ vlc_bool_t b_muxed;
+
+ es_format_t fmt;
+ es_out_id_t *p_es;
+
+ stream_t *p_out_muxed; /* for muxed stream */
+
+ RTPSource *rtpSource;
+ FramedSource *readSource;
+ vlc_bool_t b_rtcp_sync;
+
+ uint8_t buffer[65536];
+
+ char waiting;
+
+ mtime_t i_pts;
+} live_track_t;
+
+struct demux_sys_t
+{
+ char *p_sdp; /* XXX mallocated */
+
+ MediaSession *ms;
+ TaskScheduler *scheduler;
+ UsageEnvironment *env ;
+ RTSPClient *rtsp;
+
+ int i_track;
+ live_track_t **track; /* XXX mallocated */
+ mtime_t i_pcr;
+ mtime_t i_pcr_start;
+
+ mtime_t i_length;
+ mtime_t i_start;
+
+ char event;
+};
+
+static int Demux ( demux_t * );
+static int Control( demux_t *, int, va_list );
+
/*****************************************************************************
* DemuxOpen:
*****************************************************************************/
static int DemuxOpen ( vlc_object_t *p_this )
{
- input_thread_t *p_input = (input_thread_t *)p_this;
- demux_sys_t *p_sys;
+ demux_t *p_demux = (demux_t*)p_this;
+ demux_sys_t *p_sys;
MediaSubsessionIterator *iter;
MediaSubsession *sub;
/* See if it looks like a SDP
v, o, s fields are mandatory and in this order */
- if( stream_Peek( p_input->s, &p_peek, 7 ) < 7 )
+ if( stream_Peek( p_demux->s, &p_peek, 7 ) < 7 )
{
- msg_Err( p_input, "cannot peek" );
+ msg_Err( p_demux, "cannot peek" );
return VLC_EGENERIC;
}
if( strncmp( (char*)p_peek, "v=0\r\n", 5 ) && strncmp( (char*)p_peek, "v=0\n", 4 ) &&
- ( p_input->psz_access == NULL || strcasecmp( p_input->psz_access, "rtsp" ) ||
- p_peek[0] < 'a' || p_peek[0] > 'z' || p_peek[1] != '=' ) )
+ ( strcasecmp( p_demux->psz_access, "rtsp" ) || p_peek[0] < 'a' || p_peek[0] > 'z' || p_peek[1] != '=' ) )
{
- msg_Warn( p_input, "SDP module discarded" );
+ msg_Warn( p_demux, "SDP module discarded" );
return VLC_EGENERIC;
}
- p_input->pf_demux = Demux;
- p_input->pf_demux_control = Control;
- p_input->p_demux_data = p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) );
+ p_demux->pf_demux = Demux;
+ p_demux->pf_control= Control;
+ p_demux->p_sys = p_sys = (demux_sys_t*)malloc( sizeof( demux_sys_t ) );
p_sys->p_sdp = NULL;
p_sys->scheduler = NULL;
p_sys->env = NULL;
p_sys->i_start = 0;
/* Gather the complete sdp file */
- vlc_mutex_lock( &p_input->stream.stream_lock );
- if( input_InitStream( p_input, 0 ) == -1)
- {
- vlc_mutex_unlock( &p_input->stream.stream_lock );
- msg_Err( p_input, "cannot init stream" );
- goto error;
- }
- vlc_mutex_unlock( &p_input->stream.stream_lock );
-
i_sdp = 0;
i_sdp_max = 1000;
p_sdp = (uint8_t*)malloc( i_sdp_max );
for( ;; )
{
- int i_read = stream_Read( p_input->s, &p_sdp[i_sdp], i_sdp_max - i_sdp - 1 );
+ int i_read = stream_Read( p_demux->s, &p_sdp[i_sdp], i_sdp_max - i_sdp - 1 );
if( i_read < 0 )
{
- msg_Err( p_input, "failed to read SDP" );
+ msg_Err( p_demux, "failed to read SDP" );
free( p_sys );
return VLC_EGENERIC;
}
if( ( p_sys->scheduler = BasicTaskScheduler::createNew() ) == NULL )
{
- msg_Err( p_input, "BasicTaskScheduler::createNew failed" );
+ msg_Err( p_demux, "BasicTaskScheduler::createNew failed" );
goto error;
}
if( ( p_sys->env = BasicUsageEnvironment::createNew(*p_sys->scheduler) ) == NULL )
{
- msg_Err( p_input, "BasicUsageEnvironment::createNew failed" );
+ msg_Err( p_demux, "BasicUsageEnvironment::createNew failed" );
goto error;
}
- if( p_input->psz_access != NULL && !strcasecmp( p_input->psz_access, "rtsp" ) )
+ if( !strcasecmp( p_demux->psz_access, "rtsp" ) )
{
char *psz_url;
char *psz_options;
if( ( p_sys->rtsp = RTSPClient::createNew(*p_sys->env, 1/*verbose*/, "VLC Media Player" ) ) == NULL )
{
- msg_Err( p_input, "RTSPClient::createNew failed (%s)", p_sys->env->getResultMsg() );
+ msg_Err( p_demux, "RTSPClient::createNew failed (%s)", p_sys->env->getResultMsg() );
goto error;
}
- psz_url = (char*)malloc( strlen( p_input->psz_name ) + 8 );
- sprintf( psz_url, "rtsp://%s", p_input->psz_name );
+ psz_url = (char*)malloc( strlen( p_demux->psz_path ) + 8 );
+ sprintf( psz_url, "rtsp://%s", p_demux->psz_path );
psz_options = p_sys->rtsp->sendOptionsCmd( psz_url );
/* FIXME psz_options -> delete or free */
}
if( ( p_sys->ms = MediaSession::createNew(*p_sys->env, p_sys->p_sdp ) ) == NULL )
{
- msg_Err( p_input, "MediaSession::createNew failed" );
+ msg_Err( p_demux, "MediaSession::createNew failed" );
goto error;
}
- var_Create( p_input, "rtsp-tcp", VLC_VAR_BOOL|VLC_VAR_DOINHERIT );
- var_Get( p_input, "rtsp-tcp", &val );
+ var_Create( p_demux, "rtsp-tcp", VLC_VAR_BOOL|VLC_VAR_DOINHERIT );
+ var_Get( p_demux, "rtsp-tcp", &val );
/* Initialise each media subsession */
iter = new MediaSubsessionIterator( *p_sys->ms );
if( !sub->initiate() )
{
- msg_Warn( p_input, "RTP subsession '%s/%s' failed(%s)", sub->mediumName(), sub->codecName(), p_sys->env->getResultMsg() );
+ msg_Warn( p_demux, "RTP subsession '%s/%s' failed(%s)", sub->mediumName(), sub->codecName(), p_sys->env->getResultMsg() );
}
else
{
int fd = sub->rtpSource()->RTPgs()->socketNum();
- msg_Dbg( p_input, "RTP subsession '%s/%s'", sub->mediumName(), sub->codecName() );
+ msg_Dbg( p_demux, "RTP subsession '%s/%s'", sub->mediumName(), sub->codecName() );
/* Increase the buffer size */
increaseReceiveBufferTo( *p_sys->env, fd, i_buffer );
/* The PLAY */
if( !p_sys->rtsp->playMediaSession( *p_sys->ms ) )
{
- msg_Err( p_input, "PLAY failed %s", p_sys->env->getResultMsg() );
+ msg_Err( p_demux, "PLAY failed %s", p_sys->env->getResultMsg() );
goto error;
}
}
}
tk = (live_track_t*)malloc( sizeof( live_track_t ) );
- tk->p_input = p_input;
+ tk->p_demux = p_demux;
tk->waiting = 0;
tk->i_pts = 0;
tk->b_quicktime = VLC_FALSE;
+ tk->b_muxed = VLC_FALSE;
tk->b_rtcp_sync = VLC_FALSE;
+ tk->p_out_muxed = NULL;
+ tk->p_es = NULL;
/* Value taken from mplayer */
if( !strcmp( sub->mediumName(), "audio" ) )
{
tk->b_quicktime = VLC_TRUE;
}
+ else if( !strcmp( sub->codecName(), "MP2T" ) )
+ {
+ tk->b_muxed = VLC_TRUE;
+ tk->p_out_muxed = stream_DemuxNew( p_demux, "ts2", p_demux->out );
+ }
+ else if( !strcmp( sub->codecName(), "MP2P" ) || !strcmp( sub->codecName(), "MP1S" ) ) /* FIXME check MP1S */
+ {
+ tk->b_muxed = VLC_TRUE;
+ tk->p_out_muxed = stream_DemuxNew( p_demux, "ps2", p_demux->out );
+ }
}
if( tk->fmt.i_codec != VLC_FOURCC( 'u', 'n', 'd', 'f' ) )
{
- tk->p_es = es_out_Add( p_input->p_es_out, &tk->fmt );
- }
- else
- {
- tk->p_es = NULL;
+ tk->p_es = es_out_Add( p_demux->out, &tk->fmt );
}
- if( tk->p_es || tk->b_quicktime )
+ if( tk->p_es || tk->b_quicktime || tk->b_muxed )
{
- TAB_APPEND( p_sys->i_track, p_sys->track, tk );
tk->readSource = sub->readSource();
tk->rtpSource = sub->rtpSource();
+
+ /* Append */
+ p_sys->track = (live_track_t**)realloc( p_sys->track, sizeof( live_track_t ) * ( p_sys->i_track + 1 ) );
+ p_sys->track[p_sys->i_track++] = tk;
}
else
{
}
else if( p_sys->i_length > 0 )
{
- p_input->stream.p_selected_area->i_size = 1000; /* needed for now */
+ /* FIXME */
+ /* p_input->stream.p_selected_area->i_size = 1000;*/ /* needed for now */
}
if( p_sys->i_track <= 0 )
{
- msg_Err( p_input, "no codec supported, aborting" );
+ msg_Err( p_demux, "no codec supported, aborting" );
goto error;
}
*****************************************************************************/
static void DemuxClose( vlc_object_t *p_this )
{
- input_thread_t *p_input = (input_thread_t *)p_this;
- demux_sys_t *p_sys = p_input->p_demux_data;
+ demux_t *p_demux = (demux_t*)p_this;
+ demux_sys_t *p_sys = p_demux->p_sys;
int i;
for( i = 0; i < p_sys->i_track; i++ )
{
live_track_t *tk = p_sys->track[i];
+ if( tk->b_muxed )
+ {
+ stream_DemuxDelete( tk->p_out_muxed );
+ }
+
free( tk );
}
if( p_sys->i_track )
/*****************************************************************************
* Demux:
*****************************************************************************/
-static int Demux ( input_thread_t *p_input )
+static int Demux( demux_t *p_demux )
{
- demux_sys_t *p_sys = p_input->p_demux_data;
+ demux_sys_t *p_sys = p_demux->p_sys;
TaskToken task;
mtime_t i_pcr = 0;
}
if( i_pcr != p_sys->i_pcr && i_pcr > 0 )
{
- input_ClockManageRef( p_input,
- p_input->stream.p_selected_program,
- i_pcr * 9 / 100 );
p_sys->i_pcr = i_pcr;
+
+ es_out_Control( p_demux->out, ES_OUT_SET_PCR, i_pcr );
if( p_sys->i_pcr_start <= 0 || p_sys->i_pcr_start > i_pcr )
{
p_sys->i_pcr_start = i_pcr;
}
}
/* Create a task that will be called if we wait more than 300ms */
- task = p_sys->scheduler->scheduleDelayedTask( 300000, TaskInterrupt, p_input );
+ task = p_sys->scheduler->scheduleDelayedTask( 300000, TaskInterrupt, p_demux );
/* Do the read */
p_sys->scheduler->doEventLoop( &p_sys->event );
{
live_track_t *tk = p_sys->track[i];
- if( !tk->b_rtcp_sync && tk->rtpSource->hasBeenSynchronizedUsingRTCP() )
+ if( !tk->b_muxed && !tk->b_rtcp_sync && tk->rtpSource->hasBeenSynchronizedUsingRTCP() )
{
- msg_Dbg( p_input, "tk->rtpSource->hasBeenSynchronizedUsingRTCP()" );
- p_input->stream.p_selected_program->i_synchro_state = SYNCHRO_REINIT;
+ msg_Dbg( p_demux, "tk->rtpSource->hasBeenSynchronizedUsingRTCP()" );
+
+ es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
tk->b_rtcp_sync = VLC_TRUE;
/* reset PCR and PCR start, mmh won't work well for multi-stream I fear */
}
}
- return p_input->b_error ? 0 : 1;
+ return p_demux->b_error ? 0 : 1;
}
-static int Control( input_thread_t *p_input, int i_query, va_list args )
+/*****************************************************************************
+ * Control:
+ *****************************************************************************/
+static int Control( demux_t *p_demux, int i_query, va_list args )
{
- demux_sys_t *p_sys = p_input->p_demux_data;
+ demux_sys_t *p_sys = p_demux->p_sys;
int64_t *pi64;
double *pf, f;
}
default:
- return demux_vaControlDefault( p_input, i_query, args );
+ return VLC_EGENERIC;
}
}
static void StreamRead( void *p_private, unsigned int i_size, struct timeval pts )
{
live_track_t *tk = (live_track_t*)p_private;
- input_thread_t *p_input = tk->p_input;
- demux_sys_t *p_sys = p_input->p_demux_data;
+ demux_t *p_demux = tk->p_demux;
+ demux_sys_t *p_sys = p_demux->p_sys;
block_t *p_block;
- mtime_t i_pts = (uint64_t)pts.tv_sec * 1000000ULL + (uint64_t)pts.tv_usec;
+ mtime_t i_pts = (uint64_t)pts.tv_sec * UI64C(1000000) + (uint64_t)pts.tv_usec;
/* XXX Beurk beurk beurk Avoid having negative value XXX */
- i_pts &= 0x00ffffffffffffffULL;
+ i_pts &= UI64C(0x00ffffffffffffff);
if( tk->b_quicktime && tk->p_es == NULL )
{
tk->fmt.p_extra = malloc( tk->fmt.i_extra );
memcpy( tk->fmt.p_extra, &sdAtom[12], tk->fmt.i_extra );
- tk->p_es = es_out_Add( p_input->p_es_out, &tk->fmt );
+ tk->p_es = es_out_Add( p_demux->out, &tk->fmt );
}
#if 0
i_size,
pts.tv_sec * 1000000LL + pts.tv_usec );
#endif
+ if( tk->fmt.i_codec == VLC_FOURCC('h','2','6','1') )
+ {
+ i_size += 4;
+ }
+
if( i_size > 65536 )
{
- msg_Warn( p_input, "buffer overflow" );
+ msg_Warn( p_demux, "buffer overflow" );
}
/* FIXME could i_size be > buffer size ? */
- p_block = block_New( p_input, i_size );
-
- memcpy( p_block->p_buffer, tk->buffer, i_size );
- //p_block->i_rate = p_input->stream.control.i_rate;
-
- if( i_pts != tk->i_pts )
+ p_block = block_New( p_demux, i_size );
+ if( tk->fmt.i_codec == VLC_FOURCC('h','2','6','1') )
{
- p_block->i_dts =
- p_block->i_pts = input_ClockGetTS( p_input,
- p_input->stream.p_selected_program,
- i_pts * 9 / 100 );
+ H261VideoRTPSource *h261Source = (H261VideoRTPSource*)tk->rtpSource;
+ uint32_t header = h261Source->lastSpecialHeader();
+ memcpy( p_block->p_buffer, &header, 4 );
+ memcpy( p_block->p_buffer + 4, tk->buffer, i_size );
}
else
{
- p_block->i_dts = 0;
- p_block->i_pts = 0;
+ memcpy( p_block->p_buffer, tk->buffer, i_size );
+ }
+ if( tk->fmt.i_codec == VLC_FOURCC('h','2','6','1') &&
+ tk->rtpSource->curPacketMarkerBit() )
+ {
+ p_block->i_flags |= BLOCK_FLAG_END_OF_FRAME;
+ }
+ //p_block->i_rate = p_input->stream.control.i_rate;
+
+ if( i_pts != tk->i_pts && !tk->b_muxed )
+ {
+ p_block->i_dts = i_pts;
+ p_block->i_pts = i_pts;
}
//fprintf( stderr, "tk -> dpts=%lld\n", i_pts - tk->i_pts );
- es_out_Send( p_input->p_es_out, tk->p_es, p_block );
+ if( tk->b_muxed )
+ {
+ stream_DemuxSend( tk->p_out_muxed, p_block );
+ }
+ else
+ {
+ es_out_Send( p_demux->out, tk->p_es, p_block );
+ }
/* warm that's ok */
p_sys->event = 0xff;
/* we have read data */
tk->waiting = 0;
- if( i_pts > 0 )
+ if( i_pts > 0 && !tk->b_muxed )
{
tk->i_pts = i_pts;
}
static void StreamClose( void *p_private )
{
live_track_t *tk = (live_track_t*)p_private;
- input_thread_t *p_input = tk->p_input;
- demux_sys_t *p_sys = p_input->p_demux_data;
+ demux_t *p_demux = tk->p_demux;
+ demux_sys_t *p_sys = p_demux->p_sys;
fprintf( stderr, "StreamClose\n" );
p_sys->event = 0xff;
- p_input->b_error = VLC_TRUE;
+ p_demux->b_error = VLC_TRUE;
}
*****************************************************************************/
static void TaskInterrupt( void *p_private )
{
- input_thread_t *p_input = (input_thread_t*)p_private;
+ demux_t *p_demux = (demux_t*)p_private;
fprintf( stderr, "TaskInterrupt\n" );
/* Avoid lock */
- p_input->p_demux_data->event = 0xff;
+ p_demux->p_sys->event = 0xff;
}