/*****************************************************************************
* input.c: input thread
- * Read an MPEG2 stream, demultiplex and parse it before sending it to
+ * Read a stream, demultiplex and parse it before sending it to
* decoders.
*****************************************************************************
- * Copyright (C) 1998-2002 VideoLAN
- * $Id: input.c,v 1.236 2003/08/02 15:22:07 fenrir Exp $
+ * Copyright (C) 1998-2004 VideoLAN
+ * $Id$
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
*
#include <stdlib.h>
#include <vlc/vlc.h>
-
-#include <string.h>
+#include <vlc/input.h>
+#include <vlc/decoder.h>
+#include <vlc/vout.h>
#ifdef HAVE_SYS_TIMES_H
# include <sys/times.h>
#include "vlc_playlist.h"
-#include "stream_control.h"
-#include "input_ext-intf.h"
-#include "input_ext-dec.h"
-#include "input_ext-plugins.h"
-
#include "stream_output.h"
-#include <vlc/vout.h>
#include "vlc_interface.h"
+#include "codecs.h"
+#include "vlc_meta.h"
+#include "../../modules/demux/util/sub.h"
/*****************************************************************************
* Local prototypes
*****************************************************************************/
+struct input_thread_sys_t
+{
+ /* subtitles */
+ int i_sub;
+ subtitle_demux_t **sub;
+
+ int64_t i_stop_time;
+};
+
static int RunThread ( input_thread_t *p_input );
static int InitThread ( input_thread_t *p_input );
static void ErrorThread ( input_thread_t *p_input );
static void ParseOption ( input_thread_t *p_input,
const char *psz_option );
+/*****************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static int PositionCallback( vlc_object_t *p_this, char const *psz_cmd,
+ vlc_value_t oldval, vlc_value_t newval, void *p_data );
+static int TimeCallback ( vlc_object_t *p_this, char const *psz_cmd,
+ vlc_value_t oldval, vlc_value_t newval, void *p_data );
+static int StateCallback ( vlc_object_t *p_this, char const *psz_cmd,
+ vlc_value_t oldval, vlc_value_t newval, void *p_data );
+static int RateCallback ( vlc_object_t *p_this, char const *psz_cmd,
+ vlc_value_t oldval, vlc_value_t newval, void *p_data );
+
/*****************************************************************************
* input_CreateThread: creates a new input thread
*****************************************************************************
* This function creates a new input, and returns a pointer
* to its description. On error, it returns NULL.
*****************************************************************************/
-input_thread_t *__input_CreateThread( vlc_object_t *p_parent,
- playlist_item_t *p_item )
+input_thread_t *__input_CreateThread( vlc_object_t *p_parent, char *psz_uri,
+ char **ppsz_options, int i_options )
+
{
- input_thread_t * p_input; /* thread descriptor */
- input_info_category_t * p_info;
- int i;
+ input_thread_t *p_input; /* thread descriptor */
+ vlc_value_t val;
+ int i;
/* Allocate descriptor */
p_input = vlc_object_create( p_parent, VLC_OBJECT_INPUT );
}
/* Parse input options */
- for( i = 0; i < p_item->i_options; i++ )
+ for( i = 0; i < i_options; i++ )
{
- ParseOption( p_input, p_item->ppsz_options[i] );
+ msg_Dbg( p_input, "option: %s", ppsz_options[i] );
+ ParseOption( p_input, ppsz_options[i] );
}
/* Create a few object variables we'll need later on */
var_Create( p_input, "audio", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
var_Create( p_input, "audio-channel", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
var_Create( p_input, "spu-channel", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
+ var_Create( p_input, "sub-file", VLC_VAR_FILE | VLC_VAR_DOINHERIT );
+ var_Create( p_input, "sub-autodetect-file", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
+ var_Create( p_input, "sub-autodetect-fuzzy", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_input, "sout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
+ var_Create( p_input, "sout-all", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
var_Create( p_input, "sout-audio", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
var_Create( p_input, "sout-video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
+ var_Create( p_input, "sout-keep", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
+
+ /* repeat variable */
+ var_Create( p_input, "input-repeat", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
+
+ /* start/stop time */
+ var_Create( p_input, "start-time", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
+ var_Create( p_input, "stop-time", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
+
+ /* decoders */
+ var_Create( p_input, "minimize-threads", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
+
+ /* play status */
+
+ /* position variable */
+ var_Create( p_input, "position", VLC_VAR_FLOAT ); /* position 0.0->1.0 */
+ var_Create( p_input, "position-offset", VLC_VAR_FLOAT ); /* relative */
+ val.f_float = 0.0;
+ var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL );
+ var_AddCallback( p_input, "position", PositionCallback, NULL );
+ var_AddCallback( p_input, "position-offset", PositionCallback, NULL );
+
+ /* time variable */
+ var_Create( p_input, "time", VLC_VAR_TIME );
+ var_Create( p_input, "time-offset", VLC_VAR_TIME ); /* relative */
+ val.i_time = 0;
+ var_Change( p_input, "time", VLC_VAR_SETVALUE, &val, NULL );
+ var_AddCallback( p_input, "time", TimeCallback, NULL );
+ var_AddCallback( p_input, "time-offset", TimeCallback, NULL );
+
+ /* length variable */
+ var_Create( p_input, "length", VLC_VAR_TIME );
+ val.i_time = 0;
+ var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL );
+
+ /* rate variable */
+ var_Create( p_input, "rate", VLC_VAR_INTEGER );
+ var_Create( p_input, "rate-slower", VLC_VAR_VOID );
+ var_Create( p_input, "rate-faster", VLC_VAR_VOID );
+ val.i_int = DEFAULT_RATE;
+ var_Change( p_input, "rate", VLC_VAR_SETVALUE, &val, NULL );
+ var_AddCallback( p_input, "rate", RateCallback, NULL );
+ var_AddCallback( p_input, "rate-slower", RateCallback, NULL );
+ var_AddCallback( p_input, "rate-faster", RateCallback, NULL );
+
+ /* state variable */
+ var_Create( p_input, "state", VLC_VAR_INTEGER );
+ val.i_int = INIT_S;
+ var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
+ var_AddCallback( p_input, "state", StateCallback, NULL );
+
+ /* state variable */
+ var_Create( p_input, "demuxed-id3", VLC_VAR_BOOL );
+ val.b_bool = VLC_FALSE;
+ var_Change( p_input, "demuxed-id3", VLC_VAR_SETVALUE, &val, NULL );
/* Initialize thread properties */
p_input->b_eof = 0;
+ p_input->b_out_pace_control = VLC_FALSE;
+ p_input->p_sys = NULL;
/* Set target */
- p_input->psz_source = strdup( p_item->psz_uri );
+ p_input->psz_source = strdup( psz_uri );
+
+ /* Stream */
+ p_input->s = NULL;
+
+ /* es out */
+ p_input->p_es_out = NULL;
/* Demux */
p_input->p_demux = NULL;
p_input->pf_demux = NULL;
p_input->pf_rewind = NULL;
p_input->pf_demux_control = NULL;
+ p_input->i_cr_average = config_GetInt( p_input, "cr-average" );
/* Access */
p_input->p_access = NULL;
p_input->stream.p_selected_area = p_input->stream.pp_areas[0];
/* Initialize stream control properties. */
- p_input->stream.control.i_status = PLAYING_S;
+ p_input->stream.control.i_status = INIT_S;
p_input->stream.control.i_rate = DEFAULT_RATE;
p_input->stream.control.b_mute = 0;
- p_input->stream.control.b_grayscale = config_GetInt( p_input, "grayscale" );
-
- /* Initialize input info */
- p_input->stream.p_info = malloc( sizeof( input_info_category_t ) );
- if( !p_input->stream.p_info )
- {
- msg_Err( p_input, "No memory!" );
- return NULL;
- }
- p_input->stream.p_info->psz_name = strdup("General") ;
- p_input->stream.p_info->p_info = NULL;
- p_input->stream.p_info->p_next = NULL;
+ p_input->stream.control.b_grayscale = config_GetInt( p_input, "grayscale");
msg_Info( p_input, "playlist item `%s'", p_input->psz_source );
- p_info = input_InfoCategory( p_input, _("General") );
- input_AddInfo( p_info, _("Playlist Item"), p_input->psz_source );
+ /* Initialize input info */
+ p_input->stream.p_info = NULL;
+ p_input->stream.p_info = input_InfoCategory( p_input, _("General") );
+ input_AddInfo( p_input->stream.p_info, _("Playlist Item"),
+ p_input->psz_source );
vlc_object_attach( p_input, p_parent );
/* Create thread and wait for its readiness. */
VLC_THREAD_PRIORITY_INPUT, VLC_TRUE ) )
{
msg_Err( p_input, "cannot create input thread" );
+ input_DelInfo( p_input );
free( p_input );
return NULL;
}
*****************************************************************************/
static int RunThread( input_thread_t *p_input )
{
+ vlc_value_t val;
+ mtime_t i_update_next = -1;
+
/* Signal right now, otherwise we'll get stuck in a peek */
vlc_thread_ready( p_input );
{
/* If we failed, wait before we are killed, and exit */
p_input->b_error = 1;
+
ErrorThread( p_input );
+
+ /* Tell we're dead */
p_input->b_dead = 1;
+
+ input_DelInfo( p_input );
+
return 0;
}
/* initialization is complete */
vlc_mutex_lock( &p_input->stream.stream_lock );
- p_input->stream.b_changed = 1;
+ p_input->stream.b_changed = 1;
+ p_input->stream.control.i_status = PLAYING_S;
vlc_mutex_unlock( &p_input->stream.stream_lock );
+ val.i_int = PLAYING_S;
+ var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
+
while( !p_input->b_die && !p_input->b_error && !p_input->b_eof )
{
unsigned int i, i_count;
if( p_input->stream.p_selected_area->i_seek != NO_SEEK )
{
- if( p_input->stream.b_seekable
- && p_input->pf_seek != NULL )
+ if( p_input->stream.p_selected_area->i_size > 0 )
{
- off_t i_new_pos;
+ unsigned int i;
+ mtime_t i_time;
+ double f = (double)p_input->stream.p_selected_area->i_seek /
+ (double)p_input->stream.p_selected_area->i_size;
- /* Reinitialize buffer manager. */
- input_AccessReinit( p_input );
-
- i_new_pos = p_input->stream.p_selected_area->i_seek;
vlc_mutex_unlock( &p_input->stream.stream_lock );
- p_input->pf_seek( p_input, i_new_pos );
+ demux_Control( p_input, DEMUX_SET_POSITION, f );
vlc_mutex_lock( &p_input->stream.stream_lock );
/* Escape all decoders for the stream discontinuity they
for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
{
- pgrm_descriptor_t * p_pgrm
- = p_input->stream.pp_programs[i];
+ pgrm_descriptor_t * p_pgrm=p_input->stream.pp_programs[i];
/* Reinitialize synchro. */
p_pgrm->i_synchro_state = SYNCHRO_REINIT;
}
+
+ vlc_mutex_unlock( &p_input->stream.stream_lock );
+ if( !demux_Control( p_input, DEMUX_GET_TIME, &i_time ) )
+ {
+ int i;
+ vlc_value_t val;
+
+ /* Help in bar display */
+ val.i_time = i_time;
+ var_Change( p_input, "time", VLC_VAR_SETVALUE, &val, NULL );
+
+ /* Seek subs */
+ for( i = 0; i < p_input->p_sys->i_sub; i++ )
+ {
+ subtitle_Seek( p_input->p_sys->sub[i], i_time );
+ }
+ }
+ vlc_mutex_lock( &p_input->stream.stream_lock );
}
p_input->stream.p_selected_area->i_seek = NO_SEEK;
}
if( i_count == 0 )
{
- /* End of file - we do not set b_die because only the
- * playlist is allowed to do so. */
- msg_Info( p_input, "EOF reached" );
- p_input->b_eof = 1;
+ vlc_value_t repeat;
+
+ var_Get( p_input, "input-repeat", &repeat );
+ if( repeat.i_int == 0 || p_input->stream.i_area_nb <= 0 )
+ {
+ /* End of file - we do not set b_die because only the
+ * playlist is allowed to do so. */
+ msg_Info( p_input, "EOF reached" );
+ p_input->b_eof = 1;
+ }
+ else
+ {
+ msg_Dbg( p_input, "repeating the same input (%d)", repeat.i_int );
+ if( repeat.i_int > 0 )
+ {
+ repeat.i_int--;
+ var_Set( p_input, "input-repeat", repeat );
+ }
+
+ p_input->stream.p_new_area = p_input->stream.pp_areas[0];
+ p_input->stream.p_new_area->i_seek = 0;
+ }
}
else if( i_count < 0 )
{
p_input->b_error = 1;
}
+
+ if( !p_input->b_error && !p_input->b_eof && i_update_next < mdate() )
+ {
+ int i;
+ mtime_t i_time;
+ mtime_t i_length;
+ double d_pos;
+
+ /* update input status variables */
+ if( !demux_Control( p_input, DEMUX_GET_POSITION, &d_pos ) )
+ {
+ val.f_float = (float)d_pos;
+ var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL );
+ }
+ if( !demux_Control( p_input, DEMUX_GET_TIME, &i_time ) )
+ {
+ val.i_time = i_time;
+ var_Change( p_input, "time", VLC_VAR_SETVALUE, &val, NULL );
+ }
+ if( !demux_Control( p_input, DEMUX_GET_LENGTH, &i_length ) )
+ {
+ val.i_time = i_length;
+ var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL );
+ }
+
+ /* Check stop-time */
+ if( p_input->p_sys->i_stop_time > 0 && p_input->p_sys->i_stop_time < i_time )
+ {
+ msg_Warn( p_input, "EOF reached because of stop-time" );
+ p_input->b_eof = 1;
+ }
+
+ /* update subs */
+ for( i = 0; i < p_input->p_sys->i_sub; i++ )
+ {
+ subtitle_Demux( p_input->p_sys->sub[i], i_time );
+ }
+
+ i_update_next = mdate() + I64C(150000);
+ }
}
if( p_input->b_error || p_input->b_eof )
*****************************************************************************/
static int InitThread( input_thread_t * p_input )
{
+ vlc_meta_t *p_meta = NULL, *p_meta_user = NULL;
+ float f_fps;
+ playlist_t *p_playlist;
+ mtime_t i_length;
+
/* Parse source string. Syntax : [[<access>][/<demux>]:][<source>] */
char * psz_parser = p_input->psz_dupsource = strdup(p_input->psz_source);
vlc_value_t val;
+ int64_t i_microsecondperframe;
+
+ subtitle_demux_t *p_sub_toselect = NULL;
+ char *psz_sub_file = NULL;
/* Skip the plug-in names */
while( *psz_parser && *psz_parser != ':' )
if( input_AccessInit( p_input ) == -1 )
{
- return -1;
+ free( p_input->psz_source );
+ if( p_input->psz_dupsource != NULL )
+ {
+ free( p_input->psz_dupsource );
+ }
+
+ return VLC_EGENERIC;
}
+ /* Initialize optional stream output. (before demuxer)*/
+ var_Get( p_input, "sout", &val );
+ if( val.psz_string != NULL )
+ {
+ if ( *val.psz_string && (p_input->stream.p_sout =
+ sout_NewInstance( p_input, val.psz_string )) == NULL )
+ {
+ msg_Err( p_input, "cannot start stream output instance, aborting" );
+ free( val.psz_string );
+
+ input_AccessEnd( p_input );
+ free( p_input->psz_source );
+ if( p_input->psz_dupsource != NULL )
+ {
+ free( p_input->psz_dupsource );
+ }
+ return VLC_EGENERIC;
+ }
+ free( val.psz_string );
+ }
+
+ p_input->p_es_out = input_EsOutNew( p_input );
+ es_out_Control( p_input->p_es_out, ES_OUT_SET_ACTIVE, VLC_FALSE );
+ es_out_Control( p_input->p_es_out, ES_OUT_SET_MODE, ES_OUT_MODE_NONE );
+
/* Find and open appropriate access module */
p_input->p_access = module_Need( p_input, "access",
- p_input->psz_access );
+ p_input->psz_access, VLC_TRUE );
+#ifndef WIN32 /* Remove this gross hack from the win32 build as colons
+ * are forbidden in filenames on Win32. */
+
+ /* Maybe we got something like: /Volumes/toto:titi/gabu.mpg */
if ( p_input->p_access == NULL
&& (*p_input->psz_demux || *p_input->psz_access) )
{
- /* Maybe we got something like :
- * /Volumes/toto:titi/gabu.mpg */
p_input->psz_access = p_input->psz_demux = "";
p_input->psz_name = p_input->psz_source;
free( p_input->psz_dupsource);
p_input->psz_dupsource = NULL;
p_input->p_access = module_Need( p_input, "access",
- p_input->psz_access );
+ p_input->psz_access, VLC_TRUE );
}
+#endif
if( p_input->p_access == NULL )
{
msg_Err( p_input, "no suitable access module for `%s/%s://%s'",
p_input->psz_access, p_input->psz_demux, p_input->psz_name );
- return -1;
+ if ( p_input->stream.p_sout != NULL )
+ {
+ sout_DeleteInstance( p_input->stream.p_sout );
+ }
+
+ input_AccessEnd( p_input );
+ free( p_input->psz_source );
+ if( p_input->psz_dupsource != NULL )
+ {
+ free( p_input->psz_dupsource );
+ }
+ input_EsOutDelete( p_input->p_es_out );
+ return VLC_EGENERIC;
}
/* Waiting for stream. */
/* If the desynchronisation requested by the user is < 0, we need to
* cache more data. */
- if( p_input->p_vlc->i_desync < 0 )
- p_input->i_pts_delay -= p_input->p_vlc->i_desync;
+ var_Create( p_input, "audio-desync", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
+ var_Get( p_input, "audio-desync", &val );
+ if( val.i_int < 0 )
+ p_input->i_pts_delay -= (val.i_int * 1000);
if( p_input->p_current_data == NULL && p_input->pf_read != NULL )
{
if( p_input->b_die || p_input->b_error || p_input->b_eof )
{
module_Unneed( p_input, p_input->p_access );
- return -1;
+ if ( p_input->stream.p_sout != NULL )
+ {
+ sout_DeleteInstance( p_input->stream.p_sout );
+ }
+ input_AccessEnd( p_input );
+ free( p_input->psz_source );
+ if( p_input->psz_dupsource != NULL )
+ {
+ free( p_input->psz_dupsource );
+ }
+ input_EsOutDelete( p_input->p_es_out );
+ return VLC_EGENERIC;
}
}
}
+ /* Create the stream_t facilities */
+ p_input->s = input_StreamNew( p_input );
+ if( p_input->s == NULL )
+ {
+ /* should never occur yet */
+
+ msg_Err( p_input, "cannot create stream_t" );
+
+ module_Unneed( p_input, p_input->p_access );
+ if ( p_input->stream.p_sout != NULL )
+ {
+ sout_DeleteInstance( p_input->stream.p_sout );
+ }
+ input_AccessEnd( p_input );
+ free( p_input->psz_source );
+ if( p_input->psz_dupsource != NULL )
+ {
+ free( p_input->psz_dupsource );
+ }
+ input_EsOutDelete( p_input->p_es_out );
+ return VLC_EGENERIC;
+ }
+
/* Find and open appropriate demux module */
- p_input->p_demux = module_Need( p_input, "demux",
- p_input->psz_demux );
+ p_input->p_demux =
+ module_Need( p_input, "demux",
+ (p_input->psz_demux && *p_input->psz_demux) ?
+ p_input->psz_demux : "$demux",
+ (p_input->psz_demux && *p_input->psz_demux) ?
+ VLC_TRUE : VLC_FALSE );
if( p_input->p_demux == NULL )
{
msg_Err( p_input, "no suitable demux module for `%s/%s://%s'",
p_input->psz_access, p_input->psz_demux, p_input->psz_name );
+
+ input_StreamDelete( p_input->s );
module_Unneed( p_input, p_input->p_access );
- return -1;
+ if ( p_input->stream.p_sout != NULL )
+ {
+ sout_DeleteInstance( p_input->stream.p_sout );
+ }
+ input_AccessEnd( p_input );
+ free( p_input->psz_source );
+ if( p_input->psz_dupsource != NULL )
+ {
+ free( p_input->psz_dupsource );
+ }
+ input_EsOutDelete( p_input->p_es_out );
+ return VLC_EGENERIC;
}
- /* Initialize optional stream output. */
- var_Get( p_input, "sout", &val );
- if( val.psz_string != NULL )
+ /* Init input_thread_sys_t */
+ p_input->p_sys = malloc( sizeof( input_thread_sys_t ) );
+ p_input->p_sys->i_sub = 0;
+ p_input->p_sys->sub = NULL;
+
+ p_input->p_sys->i_stop_time = 0;
+
+ /* Get meta information from user */
+ var_Create( p_input, "meta-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
+ var_Create( p_input, "meta-author", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
+ var_Create( p_input, "meta-artist", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
+ var_Create( p_input, "meta-genre", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
+ var_Create( p_input, "meta-copyright", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
+ var_Create( p_input, "meta-description", VLC_VAR_STRING|VLC_VAR_DOINHERIT);
+ var_Create( p_input, "meta-date", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
+ var_Create( p_input, "meta-url", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
+ if( (p_meta_user = vlc_meta_New()) )
{
- if ( *val.psz_string && (p_input->stream.p_sout =
- sout_NewInstance( p_input, val.psz_string )) == NULL )
+ vlc_value_t val;
+
+ var_Get( p_input, "meta-title", &val );
+ if( val.psz_string && *val.psz_string )
+ vlc_meta_Add( p_meta_user, VLC_META_TITLE, val.psz_string );
+ if( val.psz_string ) free( val.psz_string );
+ var_Get( p_input, "meta-author", &val );
+ if( val.psz_string && *val.psz_string )
+ vlc_meta_Add( p_meta_user, VLC_META_AUTHOR, val.psz_string );
+ if( val.psz_string ) free( val.psz_string );
+ var_Get( p_input, "meta-artist", &val );
+ if( val.psz_string && *val.psz_string )
+ vlc_meta_Add( p_meta_user, VLC_META_ARTIST, val.psz_string );
+ if( val.psz_string ) free( val.psz_string );
+ var_Get( p_input, "meta-genre", &val );
+ if( val.psz_string && *val.psz_string )
+ vlc_meta_Add( p_meta_user, VLC_META_GENRE, val.psz_string );
+ if( val.psz_string ) free( val.psz_string );
+ var_Get( p_input, "meta-copyright", &val );
+ if( val.psz_string && *val.psz_string )
+ vlc_meta_Add( p_meta_user, VLC_META_COPYRIGHT, val.psz_string );
+ if( val.psz_string ) free( val.psz_string );
+ var_Get( p_input, "meta-description", &val );
+ if( val.psz_string && *val.psz_string )
+ vlc_meta_Add( p_meta_user, VLC_META_DESCRIPTION, val.psz_string );
+ if( val.psz_string ) free( val.psz_string );
+ var_Get( p_input, "meta-date", &val );
+ if( val.psz_string && *val.psz_string )
+ vlc_meta_Add( p_meta_user, VLC_META_DATE, val.psz_string );
+ if( val.psz_string ) free( val.psz_string );
+ var_Get( p_input, "meta-url", &val );
+ if( val.psz_string && *val.psz_string )
+ vlc_meta_Add( p_meta_user, VLC_META_URL, val.psz_string );
+ if( val.psz_string ) free( val.psz_string );
+ }
+
+ /* Get meta informations from demuxer */
+ if( !demux_Control( p_input, DEMUX_GET_META, &p_meta ) ||
+ ( p_meta_user && p_meta_user->i_meta ) )
+ {
+ playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_input,
+ VLC_OBJECT_PLAYLIST, FIND_PARENT);
+ playlist_item_t *p_item = NULL;
+ input_info_category_t *p_cat;
+ int i;
+
+ /* Merge demux and user metadata */
+ if( !p_meta ){ p_meta = p_meta_user; p_meta_user = NULL; }
+ else if( p_meta && p_meta_user ) vlc_meta_Merge( p_meta, p_meta_user );
+
+ if( p_playlist )
{
- msg_Err( p_input, "cannot start stream output instance, aborting" );
- free( val.psz_string );
- module_Unneed( p_input, p_input->p_access );
- module_Unneed( p_input, p_input->p_demux );
- return -1;
+ vlc_mutex_lock( &p_playlist->object_lock );
+ p_item = playlist_ItemGetByPos( p_playlist, -1 );
+ if( p_item )
+ {
+ vlc_mutex_lock( &p_item->lock );
+ }
+ vlc_mutex_unlock( &p_playlist->object_lock );
}
- free( val.psz_string );
+ msg_Dbg( p_input, "meta informations:" );
+ if( p_meta->i_meta > 0 )
+ {
+ p_cat = input_InfoCategory( p_input, _("File") );
+ for( i = 0; i < p_meta->i_meta; i++ )
+ {
+ msg_Dbg( p_input, " - '%s' = '%s'", _(p_meta->name[i]),
+ p_meta->value[i] );
+ if( !strcmp( p_meta->name[i], VLC_META_TITLE ) )
+ {
+ playlist_ItemSetName( p_item, p_meta->value[i] );
+ }
+ if( !strcmp( p_meta->name[i], VLC_META_AUTHOR ) )
+ {
+ playlist_ItemAddInfo( p_item, _("General"), _("Author"),
+ p_meta->value[i] );
+ }
+ input_AddInfo( p_cat, _(p_meta->name[i]), "%s",
+ p_meta->value[i] );
+ if( p_item )
+ {
+ playlist_ItemAddInfo( p_item, _("File"),
+ _(p_meta->name[i]), "%s",
+ p_meta->value[i] );
+ }
+ }
+ }
+ for( i = 0; i < p_meta->i_track; i++ )
+ {
+ vlc_meta_t *tk = p_meta->track[i];
+ int j;
+
+ msg_Dbg( p_input, " - track[%d]:", i );
+ if( tk->i_meta > 0 )
+ {
+ char *psz_cat = malloc( strlen(_("Stream")) + 10 );
+ sprintf( psz_cat, "%s %d", _("Stream"), i );
+ p_cat = input_InfoCategory( p_input, psz_cat );
+
+ for( j = 0; j < tk->i_meta; j++ )
+ {
+ msg_Dbg( p_input, " - '%s' = '%s'", _(tk->name[j]),
+ tk->value[j] );
+ input_AddInfo( p_cat, _(tk->name[j]), "%s", tk->value[j] );
+ if( p_item )
+ {
+ playlist_ItemAddInfo( p_item, psz_cat, _(tk->name[j]),
+ "%s", tk->value[j] );
+ }
+ }
+ }
+ }
+
+ if( p_item )
+ {
+ vlc_mutex_unlock( &p_item->lock );
+ }
+ if( p_playlist ) vlc_object_release( p_playlist );
+
+ if( p_input->stream.p_sout && p_input->stream.p_sout->p_meta == NULL )
+ {
+ p_input->stream.p_sout->p_meta = p_meta;
+ }
+ else
+ {
+ vlc_meta_Delete( p_meta );
+ }
}
+ if( p_meta_user ) vlc_meta_Delete( p_meta_user );
- return 0;
+ /* Get length */
+ if( !demux_Control( p_input, DEMUX_GET_LENGTH, &i_length ) &&
+ i_length > 0 )
+ {
+ input_info_category_t *p_cat =
+ input_InfoCategory( p_input, _("File") );
+ p_playlist =
+ (playlist_t*)vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
+ FIND_PARENT );
+ if( p_playlist )
+ {
+ playlist_SetDuration( p_playlist, -1 , i_length );
+ val.b_bool = p_playlist->i_index;
+ var_Set( p_playlist, "item-change", val );
+ vlc_object_release( p_playlist );
+ }
+ if( p_cat )
+ {
+ char psz_buffer[MSTRTIME_MAX_SIZE];
+ input_AddInfo( p_cat, _("Duration"),
+ msecstotimestr( psz_buffer, i_length / 1000 ) );
+ }
+
+ /* Set start time */
+ var_Get( p_input, "start-time", &val );
+ if( val.i_int > 0 )
+ {
+ double f_pos = val.i_int * I64C(1000000) / (double)i_length;
+
+ if( f_pos >= 1.0 )
+ {
+ msg_Warn( p_input, "invalid start-time, ignored (start-time "
+ ">= media length)" );
+ }
+ else
+ {
+ p_input->stream.p_selected_area->i_seek =
+ (int64_t)( f_pos * (double)p_input->stream.p_selected_area->i_size );
+
+ msg_Dbg( p_input, "start-time %ds (%2.2f)", val.i_int, f_pos );
+ }
+ }
+ }
+
+ /* Set stop-time and check validity */
+ var_Get( p_input, "stop-time", &val );
+ if( val.i_int > 0 )
+ {
+ vlc_value_t start;
+
+ var_Get( p_input, "start-time", &start );
+ if( start.i_int >= val.i_int )
+ {
+ msg_Warn( p_input, "invalid stop-time, ignored (stop-time < "
+ "start-time)" );
+ }
+ else
+ {
+ p_input->p_sys->i_stop_time = (int64_t)val.i_int * I64C(1000000);
+ msg_Dbg( p_input, "stop-time %ds", val.i_int );
+ }
+ }
+
+ /* Get fps */
+ if( demux_Control( p_input, DEMUX_GET_FPS, &f_fps ) || f_fps < 0.1 )
+ {
+ i_microsecondperframe = 0;
+ }
+ else
+ {
+ i_microsecondperframe = (int64_t)( (double)1000000.0 / (double)f_fps );
+ }
+
+ /* Look for and add subtitle files */
+ var_Get( p_input, "sub-file", &val );
+ if( val.psz_string && *val.psz_string )
+ {
+ subtitle_demux_t *p_sub;
+
+ msg_Dbg( p_input, "force subtitle: %s", val.psz_string );
+ if( ( p_sub = subtitle_New( p_input, strdup(val.psz_string),
+ i_microsecondperframe ) ) )
+ {
+ p_sub_toselect = p_sub;
+ TAB_APPEND( p_input->p_sys->i_sub, p_input->p_sys->sub, p_sub );
+ }
+ }
+ psz_sub_file = val.psz_string;
+
+ var_Get( p_input, "sub-autodetect-file", &val );
+ if( val.b_bool )
+ {
+ subtitle_demux_t *p_sub;
+ int i;
+ char **tmp = subtitles_Detect( p_input, "", p_input->psz_name );
+ char **tmp2 = tmp;
+ for( i = 0; *tmp2 != NULL; i++ )
+ {
+ if( psz_sub_file == NULL || strcmp( psz_sub_file, *tmp2 ) )
+ {
+ if( ( p_sub = subtitle_New( p_input, *tmp2,
+ i_microsecondperframe ) ) )
+ {
+ TAB_APPEND( p_input->p_sys->i_sub, p_input->p_sys->sub,
+ p_sub );
+ }
+ }
+ free( *tmp2++ );
+ }
+ free( tmp );
+ }
+ if( psz_sub_file ) free( psz_sub_file );
+
+ es_out_Control( p_input->p_es_out, ES_OUT_SET_ACTIVE, VLC_TRUE );
+ val.b_bool = VLC_FALSE;
+ if( p_input->stream.p_sout )
+ {
+ var_Get( p_input, "sout-all", &val );
+ }
+ es_out_Control( p_input->p_es_out, ES_OUT_SET_MODE,
+ val.b_bool ? ES_OUT_MODE_ALL : ES_OUT_MODE_AUTO );
+ if( p_sub_toselect )
+ {
+ es_out_Control( p_input->p_es_out, ES_OUT_SET_ES,
+ p_sub_toselect->p_es, VLC_TRUE );
+ }
+
+ if( p_input->stream.p_sout )
+ {
+ if( p_input->stream.p_sout->i_out_pace_nocontrol > 0 )
+ {
+ p_input->b_out_pace_control = VLC_FALSE;
+ }
+ else
+ {
+ p_input->b_out_pace_control = VLC_TRUE;
+ }
+ msg_Dbg( p_input, "starting in %s mode",
+ p_input->b_out_pace_control ? "asynch" : "synch" );
+ }
+
+ return VLC_SUCCESS;
}
/*****************************************************************************
*****************************************************************************/
static void EndThread( input_thread_t * p_input )
{
- vlc_object_t *p_object;
-
+ int i;
#ifdef HAVE_SYS_TIMES_H
/* Display statistics */
struct tms cpu_usage;
input_DumpStream( p_input );
+ /* Free demultiplexer's data */
+ if( p_input->p_demux ) module_Unneed( p_input, p_input->p_demux );
+
/* Free all ES and destroy all decoder threads */
input_EndStream( p_input );
/* Close optional stream output instance */
- if ( p_input->stream.p_sout != NULL )
+ if( p_input->stream.p_sout )
{
- sout_DeleteInstance( p_input->stream.p_sout );
+ vlc_object_t *p_pl =
+ vlc_object_find( p_input, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
+ vlc_value_t keep;
+
+ if( var_Get( p_input, "sout-keep", &keep ) >= 0 && keep.b_bool && p_pl )
+ {
+ /* attach sout to the playlist */
+ msg_Warn( p_input, "keeping sout" );
+ vlc_object_detach( p_input->stream.p_sout );
+ vlc_object_attach( p_input->stream.p_sout, p_pl );
+ }
+ else
+ {
+ msg_Warn( p_input, "destroying sout" );
+ sout_DeleteInstance( p_input->stream.p_sout );
+ }
+ if( p_pl )
+ {
+ vlc_object_release( p_pl );
+ }
}
- /* Free demultiplexer's data */
- module_Unneed( p_input, p_input->p_demux );
+ /* Destroy subtitles demuxers */
+ if( p_input->p_sys )
+ {
+ for( i = 0; i < p_input->p_sys->i_sub; i++ )
+ {
+ subtitle_Close( p_input->p_sys->sub[i] );
+ }
+ if( p_input->p_sys->i_sub > 0 )
+ {
+ free( p_input->p_sys->sub );
+ }
+
+ /* Free input_thread_sys_t */
+ free( p_input->p_sys );
+ }
+
+ /* Destroy the stream_t facilities */
+ if( p_input->s ) input_StreamDelete( p_input->s );
+
+ /* Destroy es out */
+ if( p_input->p_es_out ) input_EsOutDelete( p_input->p_es_out );
/* Close the access plug-in */
- module_Unneed( p_input, p_input->p_access );
+ if( p_input->p_access ) module_Unneed( p_input, p_input->p_access );
input_AccessEnd( p_input );
msg_Dbg( p_input, "freeing info structures...");
input_DelInfo( p_input );
- /* Close the video output that should have been re-attached
- * to our object */
- while( ( p_object = vlc_object_find( p_input, VLC_OBJECT_VOUT, FIND_CHILD ) ) != NULL )
- {
- vlc_object_detach( p_object );
- vlc_object_release( p_object );
- vout_Destroy( (vout_thread_t *)p_object );
- }
-
free( p_input->psz_source );
- if ( p_input->psz_dupsource != NULL ) free( p_input->psz_dupsource );
+ if( p_input->psz_dupsource != NULL ) free( p_input->psz_dupsource );
/* Tell we're dead */
p_input->b_dead = 1;
break;
case VLC_VAR_STRING:
+ case VLC_VAR_MODULE:
case VLC_VAR_FILE:
case VLC_VAR_DIRECTORY:
val.psz_string = psz_value;
if( psz_name ) free( psz_name );
return;
}
+
+/*****************************************************************************
+ * Callbacks (position, time, state, rate )
+ *****************************************************************************/
+static int PositionCallback( vlc_object_t *p_this, char const *psz_cmd,
+ vlc_value_t oldval, vlc_value_t newval,
+ void *p_data )
+{
+ input_thread_t *p_input = (input_thread_t *)p_this;
+
+ msg_Dbg( p_input, "cmd=%s old=%f new=%f", psz_cmd,
+ oldval.f_float, newval.f_float );
+
+ if( !strcmp( psz_cmd, "position-offset" ) )
+ {
+ vlc_value_t val;
+ var_Get( p_input, "position", &val );
+
+ newval.f_float += val.f_float;
+ }
+
+ vlc_mutex_lock( &p_input->stream.stream_lock );
+ p_input->stream.p_selected_area->i_seek =
+ (int64_t)( newval.f_float *
+ (double)p_input->stream.p_selected_area->i_size );
+
+ if( p_input->stream.p_selected_area->i_seek < 0 )
+ {
+ p_input->stream.p_selected_area->i_seek = 0;
+ }
+ vlc_mutex_unlock( &p_input->stream.stream_lock );
+
+ return VLC_SUCCESS;
+}
+
+static int TimeCallback( vlc_object_t *p_this, char const *psz_cmd,
+ vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+ input_thread_t *p_input = (input_thread_t *)p_this;
+ vlc_value_t val;
+
+ /* FIXME TODO FIXME */
+ msg_Dbg( p_input, "cmd=%s old=%lld new=%lld", psz_cmd,
+ oldval.i_time, newval.i_time );
+
+ var_Get( p_input, "length", &val );
+ if( val.i_time > 0 )
+ {
+ val.f_float = (double)newval.i_time / (double)val.i_time;
+ if( !strcmp( psz_cmd, "time-offset" ) )
+ {
+ var_Set( p_input, "position-offset", val );
+ }
+ else
+ {
+ var_Set( p_input, "position", val );
+ }
+ }
+ else
+ {
+ msg_Warn( p_input, "TimeCallback: length <= 0 -> can't seek" );
+ }
+ return VLC_SUCCESS;
+}
+
+static int StateCallback( vlc_object_t *p_this, char const *psz_cmd,
+ vlc_value_t oldval, vlc_value_t newval,
+ void *p_data )
+{
+ input_thread_t *p_input = (input_thread_t *)p_this;
+
+ msg_Dbg( p_input, "cmd=%s old=%d new=%d",
+ psz_cmd, oldval.i_int, newval.i_int );
+
+ switch( newval.i_int )
+ {
+ case PLAYING_S:
+ input_SetStatus( p_input, INPUT_STATUS_PLAY );
+ return VLC_SUCCESS;
+ case PAUSE_S:
+ input_SetStatus( p_input, INPUT_STATUS_PAUSE );
+ return VLC_SUCCESS;
+ case END_S:
+ input_SetStatus( p_input, INPUT_STATUS_END );
+ return VLC_SUCCESS;
+ default:
+ msg_Err( p_input, "cannot set new state (invalid)" );
+ return VLC_EGENERIC;
+ }
+}
+
+static int RateCallback( vlc_object_t *p_this, char const *psz_cmd,
+ vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+ input_thread_t *p_input = (input_thread_t *)p_this;
+
+ if( !strcmp( psz_cmd, "rate-slower" ) )
+ {
+ input_SetStatus( p_input, INPUT_STATUS_SLOWER );
+ }
+ else if( !strcmp( psz_cmd, "rate-faster" ) )
+ {
+ input_SetStatus( p_input, INPUT_STATUS_FASTER );
+ }
+ else
+ {
+ msg_Dbg( p_input, "cmd=%s old=%d new=%d",
+ psz_cmd, oldval.i_int, newval.i_int );
+ input_SetRate( p_input, newval.i_int );
+ }
+ return VLC_SUCCESS;
+}