diff --git a/src/input/access.c b/src/input/access.c index 49c51b2c0adcc0257ff2382b8c148733cb6f0350..dfca13c7165d23632939d0bdad69f06efcfd1cfb 100644 --- a/src/input/access.c +++ b/src/input/access.c @@ -25,33 +25,7 @@ #include #include -#include "ninput.h" - -int access_vaControl( input_thread_t *p_input, int i_query, va_list args ) -{ - if( p_input->pf_access_control ) - { - return p_input->pf_access_control( p_input, i_query, args ); - } - return VLC_EGENERIC; -} - -int access_Control( input_thread_t *p_input, int i_query, ... ) -{ - va_list args; - int i_result; - - va_start( args, i_query ); - i_result = access_vaControl( p_input, i_query, args ); - va_end( args ); - - return i_result; -} - -int access_vaControlDefault( input_thread_t *p_input, int i_query, va_list args ) -{ - return VLC_EGENERIC; -} +#include "input_internal.h" /***************************************************************************** * access2_New: diff --git a/src/input/input_clock.c b/src/input/clock.c similarity index 72% rename from src/input/input_clock.c rename to src/input/clock.c index a5c873f2413e1a30c3970f69264356fc90fea8da..e39e65330b4bd24b7b0003292537204e605546c4 100644 --- a/src/input/input_clock.c +++ b/src/input/clock.c @@ -24,14 +24,11 @@ /***************************************************************************** * Preamble *****************************************************************************/ -#include /* memcpy(), memset() */ - +#include #include -#include "stream_control.h" -#include "input_ext-intf.h" -#include "input_ext-dec.h" -#include "input_ext-plugins.h" +#include +#include "input_internal.h" /* * DISCUSSION : SYNCHRONIZATION METHOD @@ -67,7 +64,8 @@ * new_average = (old_average * c_average + new_sample_value) / (c_average +1) */ -static void ClockNewRef( pgrm_descriptor_t * p_pgrm, + +static void ClockNewRef( input_clock_t * p_pgrm, mtime_t i_clock, mtime_t i_sysdate ); /***************************************************************************** @@ -84,19 +82,19 @@ static void ClockNewRef( pgrm_descriptor_t * p_pgrm, /***************************************************************************** * ClockToSysdate: converts a movie clock to system date *****************************************************************************/ -static mtime_t ClockToSysdate( input_thread_t * p_input, - pgrm_descriptor_t * p_pgrm, mtime_t i_clock ) +static mtime_t ClockToSysdate( input_thread_t *p_input, + input_clock_t *cl, mtime_t i_clock ) { mtime_t i_sysdate = 0; - if( p_pgrm->i_synchro_state == SYNCHRO_OK ) + if( cl->i_synchro_state == SYNCHRO_OK ) { - i_sysdate = (mtime_t)(i_clock - p_pgrm->cr_ref) - * (mtime_t)p_input->stream.control.i_rate + i_sysdate = (mtime_t)(i_clock - cl->cr_ref) + * (mtime_t)p_input->i_rate * (mtime_t)300; i_sysdate /= 27; i_sysdate /= 1000; - i_sysdate += (mtime_t)p_pgrm->sysdate_ref; + i_sysdate += (mtime_t)cl->sysdate_ref; } return( i_sysdate ); @@ -107,46 +105,54 @@ static mtime_t ClockToSysdate( input_thread_t * p_input, ***************************************************************************** * Caution : the synchro state must be SYNCHRO_OK for this to operate. *****************************************************************************/ -static mtime_t ClockCurrent( input_thread_t * p_input, - pgrm_descriptor_t * p_pgrm ) +static mtime_t ClockCurrent( input_thread_t *p_input, + input_clock_t *cl ) { - return( (mdate() - p_pgrm->sysdate_ref) * 27 * DEFAULT_RATE - / p_input->stream.control.i_rate / 300 - + p_pgrm->cr_ref ); + return( (mdate() - cl->sysdate_ref) * 27 * INPUT_RATE_DEFAULT + / p_input->i_rate / 300 + + cl->cr_ref ); } /***************************************************************************** * ClockNewRef: writes a new clock reference *****************************************************************************/ -static void ClockNewRef( pgrm_descriptor_t * p_pgrm, +static void ClockNewRef( input_clock_t *cl, mtime_t i_clock, mtime_t i_sysdate ) { - p_pgrm->cr_ref = i_clock; - p_pgrm->sysdate_ref = i_sysdate ; + cl->cr_ref = i_clock; + cl->sysdate_ref = i_sysdate ; } /***************************************************************************** * input_ClockInit: reinitializes the clock reference after a stream * discontinuity *****************************************************************************/ -void input_ClockInit( pgrm_descriptor_t * p_pgrm ) +void input_ClockInit( input_clock_t *cl, vlc_bool_t b_master, int i_cr_average ) { - p_pgrm->last_cr = 0; - p_pgrm->last_pts = 0; - p_pgrm->cr_ref = 0; - p_pgrm->sysdate_ref = 0; - p_pgrm->delta_cr = 0; - p_pgrm->c_average_count = 0; + cl->i_synchro_state = SYNCHRO_START; + + cl->last_cr = 0; + cl->last_pts = 0; + cl->cr_ref = 0; + cl->sysdate_ref = 0; + cl->delta_cr = 0; + cl->c_average_count = 0; + + cl->i_cr_average = i_cr_average; + + cl->b_master = b_master; } +#if 0 /***************************************************************************** * input_ClockManageControl: handles the messages from the interface ***************************************************************************** * Returns UNDEF_S if nothing happened, PAUSE_S if the stream was paused *****************************************************************************/ int input_ClockManageControl( input_thread_t * p_input, - pgrm_descriptor_t * p_pgrm, mtime_t i_clock ) + input_clock_t *cl, mtime_t i_clock ) { +#if 0 vlc_value_t val; int i_return_value = UNDEF_S; @@ -221,36 +227,32 @@ int input_ClockManageControl( input_thread_t * p_input, vlc_mutex_unlock( &p_input->stream.stream_lock ); return( i_return_value ); +#endif + return UNDEF_S; } +#endif /***************************************************************************** - * input_ClockManageRef: manages a clock reference + * input_ClockSetPCR: manages a clock reference *****************************************************************************/ -void input_ClockManageRef( input_thread_t * p_input, - pgrm_descriptor_t * p_pgrm, mtime_t i_clock ) +void input_ClockSetPCR( input_thread_t *p_input, + input_clock_t *cl, mtime_t i_clock ) { - /* take selected program if none specified */ - if( !p_pgrm ) - { - p_pgrm = p_input->stream.p_selected_program; - } - - if( ( p_pgrm->i_synchro_state != SYNCHRO_OK ) || - ( i_clock == 0 && p_pgrm->last_cr != 0 ) ) + if( ( cl->i_synchro_state != SYNCHRO_OK ) || + ( i_clock == 0 && cl->last_cr != 0 ) ) { /* Feed synchro with a new reference point. */ - ClockNewRef( p_pgrm, i_clock, - p_pgrm->last_pts + CR_MEAN_PTS_GAP > mdate() ? - p_pgrm->last_pts + CR_MEAN_PTS_GAP : mdate() ); - p_pgrm->i_synchro_state = SYNCHRO_OK; + ClockNewRef( cl, i_clock, + cl->last_pts + CR_MEAN_PTS_GAP > mdate() ? + cl->last_pts + CR_MEAN_PTS_GAP : mdate() ); + cl->i_synchro_state = SYNCHRO_OK; - if( p_input->stream.b_pace_control - && p_input->stream.p_selected_program == p_pgrm ) + if( p_input->b_can_pace_control && cl->b_master ) { - p_pgrm->last_cr = i_clock; + cl->last_cr = i_clock; if( !p_input->b_out_pace_control ) { - mtime_t i_wakeup = ClockToSysdate( p_input, p_pgrm, i_clock ); + mtime_t i_wakeup = ClockToSysdate( p_input, cl, i_clock ); while( (i_wakeup - mdate()) / CLOCK_FREQ > 1 ) { msleep( CLOCK_FREQ ); @@ -261,37 +263,38 @@ void input_ClockManageRef( input_thread_t * p_input, } else { - p_pgrm->last_cr = 0; - p_pgrm->delta_cr = 0; - p_pgrm->c_average_count = 0; + cl->last_cr = 0; + cl->delta_cr = 0; + cl->c_average_count = 0; } } else { - if ( p_pgrm->last_cr != 0 && - ( (p_pgrm->last_cr - i_clock) > CR_MAX_GAP - || (p_pgrm->last_cr - i_clock) < - CR_MAX_GAP ) ) + if ( cl->last_cr != 0 && + ( (cl->last_cr - i_clock) > CR_MAX_GAP + || (cl->last_cr - i_clock) < - CR_MAX_GAP ) ) { /* Stream discontinuity, for which we haven't received a * warning from the stream control facilities (dd-edited * stream ?). */ msg_Warn( p_input, "clock gap, unexpected stream discontinuity" ); - input_ClockInit( p_pgrm ); - p_pgrm->i_synchro_state = SYNCHRO_START; + input_ClockInit( cl, cl->b_master, cl->i_cr_average ); + /* FIXME needed ? */ +#if 0 input_EscapeDiscontinuity( p_input ); +#endif } - p_pgrm->last_cr = i_clock; + cl->last_cr = i_clock; - if( p_input->stream.b_pace_control - && p_input->stream.p_selected_program == p_pgrm ) + if( p_input->b_can_pace_control && cl->b_master ) { /* Wait a while before delivering the packets to the decoder. * In case of multiple programs, we arbitrarily follow the * clock of the selected program. */ if( !p_input->b_out_pace_control ) { - mtime_t i_wakeup = ClockToSysdate( p_input, p_pgrm, i_clock ); + mtime_t i_wakeup = ClockToSysdate( p_input, cl, i_clock ); while( (i_wakeup - mdate()) / CLOCK_FREQ > 1 ) { msleep( CLOCK_FREQ ); @@ -299,30 +302,32 @@ void input_ClockManageRef( input_thread_t * p_input, } mwait( i_wakeup ); } - + /* FIXME Not needed anymore ? */ +#if 0 /* Now take into account interface changes. */ - input_ClockManageControl( p_input, p_pgrm, i_clock ); + input_ClockManageControl( p_input, cl, i_clock ); +#endif } else { /* Smooth clock reference variations. */ - mtime_t i_extrapoled_clock = ClockCurrent( p_input, p_pgrm ); + mtime_t i_extrapoled_clock = ClockCurrent( p_input, cl ); /* Bresenham algorithm to smooth variations. */ - if( p_pgrm->c_average_count == p_input->i_cr_average ) + if( cl->c_average_count == cl->i_cr_average ) { - p_pgrm->delta_cr = ( p_pgrm->delta_cr - * (p_input->i_cr_average - 1) + cl->delta_cr = ( cl->delta_cr + * (cl->i_cr_average - 1) + ( i_extrapoled_clock - i_clock ) ) - / p_input->i_cr_average; + / cl->i_cr_average; } else { - p_pgrm->delta_cr = ( p_pgrm->delta_cr - * p_pgrm->c_average_count + cl->delta_cr = ( cl->delta_cr + * cl->c_average_count + ( i_extrapoled_clock - i_clock ) ) - / (p_pgrm->c_average_count + 1); - p_pgrm->c_average_count++; + / (cl->c_average_count + 1); + cl->c_average_count++; } } } @@ -332,23 +337,12 @@ void input_ClockManageRef( input_thread_t * p_input, * input_ClockGetTS: manages a PTS or DTS *****************************************************************************/ mtime_t input_ClockGetTS( input_thread_t * p_input, - pgrm_descriptor_t * p_pgrm, mtime_t i_ts ) + input_clock_t *cl, mtime_t i_ts ) { - /* take selected program if none specified */ - if( !p_pgrm ) - { - p_pgrm = p_input->stream.p_selected_program; - } - - if( p_pgrm->i_synchro_state == SYNCHRO_OK ) - { - p_pgrm->last_pts = ClockToSysdate( p_input, p_pgrm, - i_ts + p_pgrm->delta_cr ); - return( p_pgrm->last_pts + p_input->i_pts_delay ); - } - else - { + if( cl->i_synchro_state != SYNCHRO_OK ) return 0; - } + + cl->last_pts = ClockToSysdate( p_input, cl, i_ts + cl->delta_cr ); + return cl->last_pts + p_input->i_pts_delay; } diff --git a/src/input/control.c b/src/input/control.c index b43947049cc03fc86374c1d3ad614587fe310774..da237352742eb7bf55ba46eeca800c8dbbe05a9c 100644 --- a/src/input/control.c +++ b/src/input/control.c @@ -22,22 +22,12 @@ *****************************************************************************/ #include -#include #include #include +#include "input_internal.h" #include "vlc_playlist.h" -#include "ninput.h" -#include "../../modules/demux/util/sub.h" - -struct input_thread_sys_t -{ - /* subtitles */ - int i_sub; - subtitle_demux_t **sub; - int64_t i_stop_time; -}; static void UpdateBookmarksOption( input_thread_t * ); @@ -64,7 +54,6 @@ int input_Control( input_thread_t *p_input, int i_query, ... ) int input_vaControl( input_thread_t *p_input, int i_query, va_list args ) { - int i_ret; seekpoint_t *p_bkmk, ***ppp_bkmk; int i_bkmk = 0; int *pi_bkmk; @@ -75,54 +64,50 @@ int input_vaControl( input_thread_t *p_input, int i_query, va_list args ) double f, *pf; int64_t i_64, *pi_64; - vlc_mutex_lock( &p_input->stream.stream_lock ); switch( i_query ) { case INPUT_GET_POSITION: pf = (double*)va_arg( args, double * ); *pf = var_GetFloat( p_input, "position" ); - i_ret = VLC_SUCCESS; - break; + return VLC_SUCCESS; + case INPUT_SET_POSITION: f = (double)va_arg( args, double ); - i_ret = var_SetFloat( p_input, "position", f ); - break; + return var_SetFloat( p_input, "position", f ); case INPUT_GET_LENGTH: pi_64 = (int64_t*)va_arg( args, int64_t * ); *pi_64 = var_GetTime( p_input, "length" ); - i_ret = VLC_SUCCESS; - break; + return VLC_SUCCESS; + case INPUT_GET_TIME: pi_64 = (int64_t*)va_arg( args, int64_t * ); *pi_64 = var_GetTime( p_input, "time" ); - i_ret = VLC_SUCCESS; - break; + return VLC_SUCCESS; + case INPUT_SET_TIME: i_64 = (int64_t)va_arg( args, int64_t ); - i_ret = var_SetTime( p_input, "time", i_64 ); - break; + return var_SetTime( p_input, "time", i_64 ); case INPUT_GET_RATE: pi_int = (int*)va_arg( args, int * ); *pi_int = var_GetInteger( p_input, "rate" ); - i_ret = VLC_SUCCESS; - break; + return VLC_SUCCESS; + case INPUT_SET_RATE: i_int = (int)va_arg( args, int ); - i_ret = var_SetInteger( p_input, "rate", i_int ); - break; + return var_SetInteger( p_input, "rate", i_int ); case INPUT_GET_STATE: pi_int = (int*)va_arg( args, int * ); *pi_int = var_GetInteger( p_input, "state" ); - i_ret = VLC_SUCCESS; - break; + return VLC_SUCCESS; + case INPUT_SET_STATE: i_int = (int)va_arg( args, int ); - i_ret = var_SetInteger( p_input, "state", i_int ); - break; + return var_SetInteger( p_input, "state", i_int ); +#if 0 case INPUT_ADD_OPTION: { psz_option = (char *)va_arg( args, char * ); @@ -130,7 +115,7 @@ int input_vaControl( input_thread_t *p_input, int i_query, va_list args ) i_ret = VLC_EGENERIC; vlc_mutex_lock( &p_input->p_item->lock ); - /* Check if option already exists */ + /* Check if option already exists */ for( i = 0; i < p_input->p_item->i_options; i++ ) { if( !strncmp( p_input->p_item->ppsz_options[i], psz_option, @@ -500,22 +485,33 @@ int input_vaControl( input_thread_t *p_input, int i_query, va_list args ) i_ret = VLC_EGENERIC; } break; - +#endif + case INPUT_GET_BOOKMARKS: + case INPUT_CLEAR_BOOKMARKS: + case INPUT_ADD_BOOKMARK: + case INPUT_CHANGE_BOOKMARK: + case INPUT_DEL_BOOKMARK: + case INPUT_SET_BOOKMARK: + case INPUT_ADD_OPTION: + case INPUT_ADD_INFO: + case INPUT_GET_INFO: + case INPUT_SET_NAME: + case INPUT_GET_SUBDELAY: + case INPUT_SET_SUBDELAY: + /* FIXME */ + msg_Err( p_input, "unimplemented query in input_vaControl" ); default: msg_Err( p_input, "unknown query in input_vaControl" ); - i_ret = VLC_EGENERIC; - break; + return VLC_EGENERIC; } - vlc_mutex_unlock( &p_input->stream.stream_lock ); - - return i_ret; } static void UpdateBookmarksOption( input_thread_t *p_input ) { int i, i_len = 0; char *psz_value = NULL, *psz_next = NULL; - + /* FIXME */ +#if 0 vlc_mutex_unlock( &p_input->stream.stream_lock ); for( i = 0; i < p_input->i_bookmarks; i++ ) @@ -546,4 +542,5 @@ static void UpdateBookmarksOption( input_thread_t *p_input ) psz_value ? psz_value : "" ); vlc_mutex_lock( &p_input->stream.stream_lock ); +#endif } diff --git a/src/input/input_dec.c b/src/input/decoder.c similarity index 80% rename from src/input/input_dec.c rename to src/input/decoder.c index 98647b2ab5c85538219c1620c40e7ca7e7441510..671a002e3cf1d43c0a1adf0d72b9b38d06229348 100644 --- a/src/input/input_dec.c +++ b/src/input/decoder.c @@ -1,11 +1,12 @@ /***************************************************************************** - * input_dec.c: Functions for the management of decoders + * decoder.c: Functions for the management of decoders ***************************************************************************** * Copyright (C) 1999-2004 VideoLAN * $Id$ * * Authors: Christophe Massiot * Gildas Bazin + * Laurent Aimar * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,25 +27,20 @@ * Preamble *****************************************************************************/ #include -#include /* memcpy(), memset() */ - #include + #include #include +#include #include "stream_output.h" +#include "input_internal.h" -#include "input_ext-intf.h" -#include "input_ext-plugins.h" - -#include "codecs.h" - -static void input_NullPacket( input_thread_t *, es_descriptor_t * ); +static decoder_t * CreateDecoder( input_thread_t *, es_format_t *, int ); +static void DeleteDecoder( decoder_t * ); -static decoder_t * CreateDecoder( input_thread_t *, es_descriptor_t *, int ); static int DecoderThread( decoder_t * ); static int DecoderDecode( decoder_t * p_dec, block_t *p_block ); -static void DeleteDecoder( decoder_t * ); /* Buffers allocation callbacks for the decoders */ static aout_buffer_t *aout_new_buffer( decoder_t *, int ); @@ -81,10 +77,6 @@ struct decoder_owner_sys_t /* fifo */ block_fifo_t *p_fifo; - - /* */ - input_buffers_t *p_method_data; - es_descriptor_t *p_es_descriptor; }; @@ -95,16 +87,17 @@ struct decoder_owner_sys_t * \param p_es the es descriptor * \return the spawned decoder object */ -decoder_t * input_RunDecoder( input_thread_t * p_input, es_descriptor_t * p_es ) +decoder_t *input_DecoderNew( input_thread_t *p_input, + es_format_t *fmt, vlc_bool_t b_force_decoder ) { decoder_t *p_dec = NULL; vlc_value_t val; /* If we are in sout mode, search for packetizer module */ - if( !p_es->b_force_decoder && p_input->stream.p_sout ) + if( p_input->p_sout && !b_force_decoder ) { /* Create the decoder configuration structure */ - p_dec = CreateDecoder( p_input, p_es, VLC_OBJECT_PACKETIZER ); + p_dec = CreateDecoder( p_input, fmt, VLC_OBJECT_PACKETIZER ); if( p_dec == NULL ) { msg_Err( p_input, "could not create packetizer" ); @@ -114,7 +107,7 @@ decoder_t * input_RunDecoder( input_thread_t * p_input, es_descriptor_t * p_es ) else { /* Create the decoder configuration structure */ - p_dec = CreateDecoder( p_input, p_es, VLC_OBJECT_DECODER ); + p_dec = CreateDecoder( p_input, fmt, VLC_OBJECT_DECODER ); if( p_dec == NULL ) { msg_Err( p_input, "could not create decoder" ); @@ -133,7 +126,8 @@ decoder_t * input_RunDecoder( input_thread_t * p_input, es_descriptor_t * p_es ) return NULL; } - if( !p_es->b_force_decoder && p_input->stream.p_sout && p_input->stream.b_pace_control ) + if( p_input->p_sout && p_input->input.b_can_pace_control && + !b_force_decoder ) { msg_Dbg( p_input, "stream out mode -> no decoder thread" ); p_dec->p_owner->b_own_thread = VLC_FALSE; @@ -147,14 +141,10 @@ decoder_t * input_RunDecoder( input_thread_t * p_input, es_descriptor_t * p_es ) if( p_dec->p_owner->b_own_thread ) { int i_priority; - if ( p_es->i_cat == AUDIO_ES ) - { + if( fmt->i_cat == AUDIO_ES ) i_priority = VLC_THREAD_PRIORITY_AUDIO; - } else - { i_priority = VLC_THREAD_PRIORITY_VIDEO; - } /* Spawn the decoder thread */ if( vlc_thread_create( p_dec, "decoder", DecoderThread, @@ -169,14 +159,6 @@ decoder_t * input_RunDecoder( input_thread_t * p_input, es_descriptor_t * p_es ) } } - /* Select a new ES */ - INSERT_ELEM( p_input->stream.pp_selected_es, - p_input->stream.i_selected_es_number, - p_input->stream.i_selected_es_number, - p_es ); - - p_input->stream.b_changed = 1; - return p_dec; } @@ -187,10 +169,8 @@ decoder_t * input_RunDecoder( input_thread_t * p_input, es_descriptor_t * p_es ) * \param p_es the es descriptor * \return nothing */ -void input_EndDecoder( input_thread_t * p_input, es_descriptor_t * p_es ) +void input_DecoderDelete( decoder_t *p_dec ) { - decoder_t *p_dec = p_es->p_dec; - p_dec->b_die = VLC_TRUE; if( p_dec->p_owner->b_own_thread ) @@ -198,7 +178,7 @@ void input_EndDecoder( input_thread_t * p_input, es_descriptor_t * p_es ) /* Make sure the thread leaves the function by * sending it an empty block. */ block_t *p_block = block_New( p_dec, 0 ); - input_DecodeBlock( p_dec, p_block ); + input_DecoderDecode( p_dec, p_block ); vlc_thread_join( p_dec ); @@ -215,55 +195,6 @@ void input_EndDecoder( input_thread_t * p_input, es_descriptor_t * p_es ) /* Delete the decoder */ vlc_object_destroy( p_dec ); - - /* Tell the input there is no more decoder */ - p_es->p_dec = NULL; - - p_input->stream.b_changed = 1; -} - -/** - * Put a PES in the decoder's fifo. - * - * \param p_dec the decoder object - * \param p_pes the pes packet - * \return nothing - */ -void input_DecodePES( decoder_t * p_dec, pes_packet_t * p_pes ) -{ - data_packet_t *p_data; - int i_size = 0; - - for( p_data = p_pes->p_first; p_data != NULL; p_data = p_data->p_next ) - { - i_size += p_data->p_payload_end - p_data->p_payload_start; - } - if( i_size > 0 ) - { - block_t *p_block = block_New( p_dec, i_size ); - if( p_block ) - { - uint8_t *p_buffer = p_block->p_buffer; - - for( p_data = p_pes->p_first; p_data; p_data = p_data->p_next ) - { - int i_copy = p_data->p_payload_end - p_data->p_payload_start; - - memcpy( p_buffer, p_data->p_payload_start, i_copy ); - - p_buffer += i_copy; - } - p_block->i_pts = p_pes->i_pts; - p_block->i_dts = p_pes->i_dts; - if( p_pes->b_discontinuity ) - p_block->i_flags |= BLOCK_FLAG_DISCONTINUITY; - p_block->i_rate = p_pes->i_rate; - - input_DecodeBlock( p_dec, p_block ); - } - } - - input_DeletePES( p_dec->p_owner->p_method_data, p_pes ); } /** @@ -272,7 +203,7 @@ void input_DecodePES( decoder_t * p_dec, pes_packet_t * p_pes ) * \param p_dec the decoder object * \param p_block the data block */ -void input_DecodeBlock( decoder_t * p_dec, block_t *p_block ) +void input_DecoderDecode( decoder_t * p_dec, block_t *p_block ) { if( p_dec->p_owner->b_own_thread ) { @@ -301,6 +232,25 @@ void input_DecodeBlock( decoder_t * p_dec, block_t *p_block ) } } +void input_DecoderDiscontinuity( decoder_t * p_dec ) +{ + block_t *p_null; + + /* Empty the fifo */ + if( p_dec->p_owner->b_own_thread ) + { + block_FifoEmpty( p_dec->p_owner->p_fifo ); + } + + /* Send a special block */ + p_null = block_New( p_dec, 128 ); + p_null->i_flags |= BLOCK_FLAG_DISCONTINUITY; + memset( p_null->p_buffer, 0, p_null->i_buffer ); + + input_DecoderDecode( p_dec, p_null ); +} + +#if 0 /** * Create a NULL packet for padding in case of a data loss * @@ -311,6 +261,7 @@ void input_DecodeBlock( decoder_t * p_dec, block_t *p_block ) static void input_NullPacket( input_thread_t * p_input, es_descriptor_t * p_es ) { +#if 0 block_t *p_block = block_New( p_input, PADDING_PACKET_SIZE ); if( p_block ) { @@ -319,6 +270,7 @@ static void input_NullPacket( input_thread_t * p_input, block_FifoPut( p_es->p_dec->p_owner->p_fifo, p_block ); } +#endif } /** @@ -329,6 +281,7 @@ static void input_NullPacket( input_thread_t * p_input, */ void input_EscapeDiscontinuity( input_thread_t * p_input ) { +#if 0 unsigned int i_es, i; for( i_es = 0; i_es < p_input->stream.i_selected_es_number; i_es++ ) @@ -343,6 +296,7 @@ void input_EscapeDiscontinuity( input_thread_t * p_input ) } } } +#endif } /** @@ -353,6 +307,7 @@ void input_EscapeDiscontinuity( input_thread_t * p_input ) */ void input_EscapeAudioDiscontinuity( input_thread_t * p_input ) { +#if 0 unsigned int i_es, i; for( i_es = 0; i_es < p_input->stream.i_selected_es_number; i_es++ ) @@ -367,7 +322,9 @@ void input_EscapeAudioDiscontinuity( input_thread_t * p_input ) } } } +#endif } +#endif /** * Create a decoder object @@ -377,8 +334,8 @@ void input_EscapeAudioDiscontinuity( input_thread_t * p_input ) * \param i_object_type Object type as define in include/vlc_objects.h * \return the decoder object */ -static decoder_t * CreateDecoder( input_thread_t * p_input, - es_descriptor_t * p_es, int i_object_type ) +static decoder_t * CreateDecoder( input_thread_t *p_input, + es_format_t *fmt, int i_object_type ) { decoder_t *p_dec; @@ -397,67 +354,12 @@ static decoder_t * CreateDecoder( input_thread_t * p_input, /* Initialize the decoder fifo */ p_dec->p_module = NULL; - es_format_Copy( &p_dec->fmt_in, &p_es->fmt ); - if( p_es->p_waveformatex ) - { -#define p_wf ((WAVEFORMATEX *)p_es->p_waveformatex) - p_dec->fmt_in.audio.i_channels = p_wf->nChannels; - p_dec->fmt_in.audio.i_rate = p_wf->nSamplesPerSec; - p_dec->fmt_in.i_bitrate = p_wf->nAvgBytesPerSec * 8; - p_dec->fmt_in.audio.i_blockalign = p_wf->nBlockAlign; - p_dec->fmt_in.audio.i_bitspersample = p_wf->wBitsPerSample; - p_dec->fmt_in.i_extra = p_wf->cbSize; - p_dec->fmt_in.p_extra = NULL; - if( p_wf->cbSize ) - { - p_dec->fmt_in.p_extra = malloc( p_wf->cbSize ); - memcpy( p_dec->fmt_in.p_extra, &p_wf[1], p_wf->cbSize ); - } - } - - if( p_es->p_bitmapinfoheader ) - { -#define p_bih ((BITMAPINFOHEADER *) p_es->p_bitmapinfoheader) - p_dec->fmt_in.i_extra = p_bih->biSize - sizeof(BITMAPINFOHEADER); - p_dec->fmt_in.p_extra = NULL; - if( p_dec->fmt_in.i_extra ) - { - p_dec->fmt_in.p_extra = malloc( p_dec->fmt_in.i_extra ); - memcpy( p_dec->fmt_in.p_extra, &p_bih[1], p_dec->fmt_in.i_extra ); - } - - p_dec->fmt_in.video.i_width = p_bih->biWidth; - p_dec->fmt_in.video.i_height = p_bih->biHeight; - } - - /* FIXME - * - 1: beurk - * - 2: I'm not sure there isn't any endian problem here (spu)... */ - if( p_es->i_cat == SPU_ES && p_es->p_demux_data ) - { - if( ( p_es->i_fourcc == VLC_FOURCC( 's', 'p', 'u', ' ' ) || - p_es->i_fourcc == VLC_FOURCC( 's', 'p', 'u', 'b' ) ) && - *((uint32_t*)p_es->p_demux_data) == 0xBeef ) - { - memcpy( p_dec->fmt_in.subs.spu.palette, - p_es->p_demux_data, 17 * 4 ); - } - else if( p_es->i_fourcc == VLC_FOURCC( 'd', 'v', 'b', 's' ) && - p_es->p_spuinfo ) - { - dvb_spuinfo_t *p_dvbs = (dvb_spuinfo_t*)p_es->p_spuinfo; - p_dec->fmt_in.subs.dvb.i_id = p_dvbs->i_id; - } - } - - p_dec->fmt_in.i_cat = p_es->i_cat; - p_dec->fmt_in.i_codec = p_es->i_fourcc; - - p_dec->fmt_out = null_es_format; + es_format_Copy( &p_dec->fmt_in, fmt ); + es_format_Copy( &p_dec->fmt_out, &null_es_format ); /* Allocate our private structure for the decoder */ - p_dec->p_owner = (decoder_owner_sys_t*)malloc(sizeof(decoder_owner_sys_t)); + p_dec->p_owner = malloc( sizeof( decoder_owner_sys_t ) ); if( p_dec->p_owner == NULL ) { msg_Err( p_dec, "out of memory" ); @@ -468,10 +370,9 @@ static decoder_t * CreateDecoder( input_thread_t * p_input, p_dec->p_owner->p_aout = NULL; p_dec->p_owner->p_aout_input = NULL; p_dec->p_owner->p_vout = NULL; - p_dec->p_owner->p_sout = p_input->stream.p_sout; + p_dec->p_owner->p_sout = p_input->p_sout; p_dec->p_owner->p_sout_input = NULL; p_dec->p_owner->p_packetizer = NULL; - p_dec->p_owner->p_es_descriptor = p_es; /* decoder fifo */ @@ -480,8 +381,6 @@ static decoder_t * CreateDecoder( input_thread_t * p_input, msg_Err( p_dec, "out of memory" ); return NULL; } - p_dec->p_owner->p_method_data = p_input->p_method_data; - /* Set buffers allocation callbacks for the decoders */ p_dec->pf_aout_buffer_new = aout_new_buffer; p_dec->pf_aout_buffer_del = aout_del_buffer; @@ -506,11 +405,12 @@ static decoder_t * CreateDecoder( input_thread_t * p_input, vlc_object_create( p_input, VLC_OBJECT_PACKETIZER ); if( p_dec->p_owner->p_packetizer ) { - p_dec->p_owner->p_packetizer->fmt_in = null_es_format; - p_dec->p_owner->p_packetizer->fmt_out = null_es_format; es_format_Copy( &p_dec->p_owner->p_packetizer->fmt_in, &p_dec->fmt_in ); + es_format_Copy( &p_dec->p_owner->p_packetizer->fmt_out, + &null_es_format ); + vlc_object_attach( p_dec->p_owner->p_packetizer, p_input ); p_dec->p_owner->p_packetizer->p_module = @@ -591,13 +491,9 @@ static int DecoderDecode( decoder_t *p_dec, block_t *p_block ) if( !p_dec->p_owner->p_sout_input ) { es_format_Copy( &p_dec->p_owner->sout, &p_dec->fmt_out ); - if( p_dec->p_owner->p_es_descriptor->p_pgrm ) - { - p_dec->p_owner->sout.i_group = - p_dec->p_owner->p_es_descriptor->p_pgrm->i_number; - } - p_dec->p_owner->sout.i_id = - p_dec->p_owner->p_es_descriptor->i_id - 1; + + p_dec->p_owner->sout.i_group =p_dec->fmt_in.i_group; + p_dec->p_owner->sout.i_id = p_dec->fmt_in.i_id; if( p_dec->fmt_in.psz_language ) { p_dec->p_owner->sout.psz_language = diff --git a/src/input/demux.c b/src/input/demux.c index 9373953dc6a700d30308a709880fb00ee7d3ae08..f05c7d392780fc0a6633e1b5eb079825dfc8390f 100644 --- a/src/input/demux.c +++ b/src/input/demux.c @@ -25,148 +25,11 @@ #include #include -#include "ninput.h" - -int demux_vaControl( input_thread_t *p_input, int i_query, va_list args ) -{ - if( p_input->pf_demux_control ) - { - return p_input->pf_demux_control( p_input, i_query, args ); - } - return VLC_EGENERIC; -} - -int demux_Control( input_thread_t *p_input, int i_query, ... ) -{ - va_list args; - int i_result; - - va_start( args, i_query ); - i_result = demux_vaControl( p_input, i_query, args ); - va_end( args ); - - return i_result; -} - -static void SeekOffset( input_thread_t *p_input, int64_t i_pos ); - -int demux_vaControlDefault( input_thread_t *p_input, int i_query, - va_list args ) -{ - int i_ret; - double f, *pf; - int64_t i64, *pi64; - - vlc_mutex_lock( &p_input->stream.stream_lock ); - switch( i_query ) - { - case DEMUX_GET_POSITION: - pf = (double*)va_arg( args, double * ); - if( p_input->stream.p_selected_area->i_size <= 0 ) - { - *pf = 0.0; - } - else - { - *pf = (double)p_input->stream.p_selected_area->i_tell / - (double)p_input->stream.p_selected_area->i_size; - } - i_ret = VLC_SUCCESS; - break; - - case DEMUX_SET_POSITION: - f = (double)va_arg( args, double ); - if( p_input->stream.b_seekable && p_input->pf_seek != NULL && - f >= 0.0 && f <= 1.0 ) - { - SeekOffset( p_input, (int64_t)(f * - (double)p_input->stream.p_selected_area->i_size) ); - i_ret = VLC_SUCCESS; - } - else - { - i_ret = VLC_EGENERIC; - } - break; - - case DEMUX_GET_TIME: - pi64 = (int64_t*)va_arg( args, int64_t * ); - if( p_input->stream.i_mux_rate > 0 ) - { - *pi64 = (int64_t)1000000 * - ( p_input->stream.p_selected_area->i_tell / 50 ) / - p_input->stream.i_mux_rate; - i_ret = VLC_SUCCESS; - } - else - { - *pi64 = 0; - i_ret = VLC_EGENERIC; - } - break; - - case DEMUX_SET_TIME: - i64 = (int64_t)va_arg( args, int64_t ); - if( p_input->stream.i_mux_rate > 0 && - p_input->stream.b_seekable && - p_input->pf_seek != NULL && i64 >= 0 ) - { - SeekOffset( p_input, i64 * 50 * - (int64_t)p_input->stream.i_mux_rate / - (int64_t)1000000 ); - i_ret = VLC_SUCCESS; - } - else - { - i_ret = VLC_EGENERIC; - } - break; - - case DEMUX_GET_LENGTH: - pi64 = (int64_t*)va_arg( args, int64_t * ); - if( p_input->stream.i_mux_rate > 0 ) - { - *pi64 = (int64_t)1000000 * - ( p_input->stream.p_selected_area->i_size / 50 ) / - p_input->stream.i_mux_rate; - i_ret = VLC_SUCCESS; - } - else - { - *pi64 = 0; - i_ret = VLC_EGENERIC; - } - break; - case DEMUX_GET_FPS: - i_ret = VLC_EGENERIC; - break; - case DEMUX_GET_META: - i_ret = VLC_EGENERIC; - break; - - default: - msg_Err( p_input, "unknown query in demux_vaControlDefault" ); - i_ret = VLC_EGENERIC; - break; - } - vlc_mutex_unlock( &p_input->stream.stream_lock ); - - return i_ret; -} - -static void SeekOffset( input_thread_t *p_input, int64_t i_pos ) -{ - /* Reinitialize buffer manager. */ - input_AccessReinit( p_input ); - - vlc_mutex_unlock( &p_input->stream.stream_lock ); - p_input->pf_seek( p_input, i_pos ); - vlc_mutex_lock( &p_input->stream.stream_lock ); -} - +#include "input_internal.h" /***************************************************************************** * demux2_New: + * if s is NULL then load a access_demux *****************************************************************************/ demux_t *__demux2_New( vlc_object_t *p_obj, char *psz_access, char *psz_demux, char *psz_path, @@ -198,8 +61,12 @@ demux_t *__demux2_New( vlc_object_t *p_obj, p_demux->info.i_title = 0; p_demux->info.i_seekpoint = 0; - psz_module = p_demux->psz_demux; - if( *psz_module == '\0' && strrchr( p_demux->psz_path, '.' ) ) + if( s ) + psz_module = p_demux->psz_demux; + else + psz_module = p_demux->psz_access; + + if( s && *psz_module == '\0' && strrchr( p_demux->psz_path, '.' ) ) { /* XXX: add only file without any problem here and with strong detection. * - no .mp3, .a52, ... (aac is added as it works only by file ext anyway @@ -241,9 +108,18 @@ demux_t *__demux2_New( vlc_object_t *p_obj, /* Before module_Need (for var_Create...) */ vlc_object_attach( p_demux, p_obj ); - p_demux->p_module = - module_Need( p_demux, "demux2", psz_module, - !strcmp( psz_module, p_demux->psz_demux ) ? VLC_TRUE : VLC_FALSE ); + if( s ) + { + p_demux->p_module = + module_Need( p_demux, "demux2", psz_module, + !strcmp( psz_module, p_demux->psz_demux ) ? VLC_TRUE : VLC_FALSE ); + } + else + { + p_demux->p_module = + module_Need( p_demux, "access_demux", psz_module, + !strcmp( psz_module, p_demux->psz_access ) ? VLC_TRUE : VLC_FALSE ); + } if( p_demux->p_module == NULL ) { @@ -358,3 +234,263 @@ int demux2_vaControlHelper( stream_t *s, } } +/**************************************************************************** + * stream_Demux*: create a demuxer for an outpout stream (allow demuxer chain) + ****************************************************************************/ +typedef struct +{ + /* Data buffer */ + vlc_mutex_t lock; + int i_buffer; + int i_buffer_size; + uint8_t *p_buffer; + + int64_t i_pos; + + /* Demuxer */ + char *psz_name; + es_out_t *out; + demux_t *p_demux; +} d_stream_sys_t; + +static int DStreamRead ( stream_t *, void *p_read, int i_read ); +static int DStreamPeek ( stream_t *, uint8_t **pp_peek, int i_peek ); +static int DStreamControl( stream_t *, int i_query, va_list ); +static int DStreamThread ( stream_t * ); + + +stream_t *__stream_DemuxNew( vlc_object_t *p_obj, char *psz_demux, es_out_t *out ) +{ + /* We create a stream reader, and launch a thread */ + stream_t *s; + d_stream_sys_t *p_sys; + + if( psz_demux == NULL || *psz_demux == '\0' ) + { + return NULL; + } + + s = vlc_object_create( p_obj, VLC_OBJECT_STREAM ); + s->pf_block = NULL; + s->pf_read = DStreamRead; + s->pf_peek = DStreamPeek; + s->pf_control= DStreamControl; + + s->p_sys = malloc( sizeof( d_stream_sys_t) ); + p_sys = (d_stream_sys_t*)s->p_sys; + + vlc_mutex_init( s, &p_sys->lock ); + p_sys->i_buffer = 0; + p_sys->i_buffer_size = 1000000; + p_sys->p_buffer = malloc( p_sys->i_buffer_size ); + p_sys->i_pos = 0; + p_sys->psz_name = strdup( psz_demux ); + p_sys->out = out; + p_sys->p_demux = NULL; + + if( vlc_thread_create( s, "stream out", DStreamThread, VLC_THREAD_PRIORITY_INPUT, VLC_FALSE ) ) + { + vlc_mutex_destroy( &p_sys->lock ); + vlc_object_destroy( s ); + free( p_sys ); + return NULL; + } + + return s; +} + +void stream_DemuxSend( stream_t *s, block_t *p_block ) +{ + d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys; + + if( p_block->i_buffer > 0 ) + { + vlc_mutex_lock( &p_sys->lock ); + /* Realloc if needed */ + if( p_sys->i_buffer + p_block->i_buffer > p_sys->i_buffer_size ) + { + if( p_sys->i_buffer_size > 5000000 ) + { + vlc_mutex_unlock( &p_sys->lock ); + msg_Err( s, "stream_DemuxSend: buffer size > 5000000" ); + block_Release( p_block ); + return; + } + /* I know, it's more than needed but that's perfect */ + p_sys->i_buffer_size += p_block->i_buffer; + /* FIXME won't work with PEEK -> segfault */ + p_sys->p_buffer = realloc( p_sys->p_buffer, p_sys->i_buffer_size ); + msg_Dbg( s, "stream_DemuxSend: realloc to %d", p_sys->i_buffer_size ); + } + + /* copy data */ + memcpy( &p_sys->p_buffer[p_sys->i_buffer], p_block->p_buffer, p_block->i_buffer ); + p_sys->i_buffer += p_block->i_buffer; + + vlc_mutex_unlock( &p_sys->lock ); + } + + block_Release( p_block ); +} + +void stream_DemuxDelete( stream_t *s ) +{ + d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys; + + s->b_die = VLC_TRUE; + + vlc_mutex_lock( &p_sys->lock ); + if( p_sys->p_demux ) + { + p_sys->p_demux->b_die = VLC_TRUE; + } + vlc_mutex_unlock( &p_sys->lock ); + + vlc_thread_join( s ); + + if( p_sys->p_demux ) + { + demux2_Delete( p_sys->p_demux ); + } + vlc_mutex_destroy( &p_sys->lock ); + free( p_sys->psz_name ); + free( p_sys->p_buffer ); + free( p_sys ); + vlc_object_destroy( s ); +} + + +static int DStreamRead ( stream_t *s, void *p_read, int i_read ) +{ + d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys; + int i_copy; + + //msg_Dbg( s, "DStreamRead: wanted %d bytes", i_read ); + for( ;; ) + { + vlc_mutex_lock( &p_sys->lock ); + //msg_Dbg( s, "DStreamRead: buffer %d", p_sys->i_buffer ); + if( p_sys->i_buffer >= i_read || s->b_die ) + { + break; + } + vlc_mutex_unlock( &p_sys->lock ); + msleep( 10000 ); + } + + //msg_Dbg( s, "DStreamRead: read %d buffer %d", i_read, p_sys->i_buffer ); + + i_copy = __MIN( i_read, p_sys->i_buffer ); + if( i_copy > 0 ) + { + if( p_read ) + { + memcpy( p_read, p_sys->p_buffer, i_copy ); + } + p_sys->i_buffer -= i_copy; + p_sys->i_pos += i_copy; + + if( p_sys->i_buffer > 0 ) + { + memmove( p_sys->p_buffer, &p_sys->p_buffer[i_copy], p_sys->i_buffer ); + } + + } + vlc_mutex_unlock( &p_sys->lock ); + + return i_copy; +} +static int DStreamPeek ( stream_t *s, uint8_t **pp_peek, int i_peek ) +{ + d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys; + int i_copy; + + //msg_Dbg( s, "DStreamPeek: wanted %d bytes", i_peek ); + for( ;; ) + { + vlc_mutex_lock( &p_sys->lock ); + //msg_Dbg( s, "DStreamPeek: buffer %d", p_sys->i_buffer ); + if( p_sys->i_buffer >= i_peek || s->b_die ) + { + break; + } + vlc_mutex_unlock( &p_sys->lock ); + msleep( 10000 ); + } + *pp_peek = p_sys->p_buffer; + i_copy = __MIN( i_peek, p_sys->i_buffer ); + + vlc_mutex_unlock( &p_sys->lock ); + + return i_copy; +} + +static int DStreamControl( stream_t *s, int i_query, va_list args ) +{ + d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys; + int64_t *p_i64; + vlc_bool_t *p_b; + int *p_int; + switch( i_query ) + { + case STREAM_GET_SIZE: + p_i64 = (int64_t*) va_arg( args, int64_t * ); + *p_i64 = 0; + return VLC_SUCCESS; + + case STREAM_CAN_SEEK: + p_b = (vlc_bool_t*) va_arg( args, vlc_bool_t * ); + *p_b = VLC_FALSE; + return VLC_SUCCESS; + + case STREAM_CAN_FASTSEEK: + p_b = (vlc_bool_t*) va_arg( args, vlc_bool_t * ); + *p_b = VLC_FALSE; + return VLC_SUCCESS; + + case STREAM_GET_POSITION: + p_i64 = (int64_t*) va_arg( args, int64_t * ); + *p_i64 = p_sys->i_pos; + return VLC_SUCCESS; + + case STREAM_SET_POSITION: + return VLC_EGENERIC; + + case STREAM_GET_MTU: + p_int = (int*) va_arg( args, int * ); + *p_int = 0; + return VLC_SUCCESS; + + default: + msg_Err( s, "invalid DStreamControl query=0x%x", i_query ); + return VLC_EGENERIC; + } +} + +static int DStreamThread ( stream_t *s ) +{ + d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys; + demux_t *p_demux; + + /* Create the demuxer */ + + if( ( p_demux = demux2_New( s, "", p_sys->psz_name, "", s, p_sys->out ) ) == NULL ) + { + return VLC_EGENERIC; + } + + vlc_mutex_lock( &p_sys->lock ); + p_sys->p_demux = p_demux; + vlc_mutex_unlock( &p_sys->lock ); + + /* Main loop */ + while( !s->b_die && !p_demux->b_die ) + { + if( p_demux->pf_demux( p_demux ) <= 0 ) + { + break; + } + } + p_demux->b_die = VLC_TRUE; + return VLC_SUCCESS; +} diff --git a/src/input/es_out.c b/src/input/es_out.c index 23e4546297e9b3b803566f3a7b61b86d2dd2a67e..978c32f1b2f7fa33f08ddeb6ea7734c56e0cdb94 100644 --- a/src/input/es_out.c +++ b/src/input/es_out.c @@ -30,26 +30,53 @@ #include #include +#include "input_internal.h" + #include "vlc_playlist.h" -#include "codecs.h" #include "iso_lang.h" /***************************************************************************** * Local prototypes *****************************************************************************/ +typedef struct +{ + /* Program ID */ + int i_id; + + /* Number of es for this pgrm */ + int i_es; + + vlc_bool_t b_selected; + + /* Clock for this program */ + input_clock_t clock; + +} es_out_pgrm_t; + struct es_out_id_t { - int i_channel; - es_descriptor_t *p_es; + /* ES ID */ + int i_id; + es_out_pgrm_t *p_pgrm; + + /* Channel in the track type */ + int i_channel; + es_format_t fmt; + char *psz_description; + decoder_t *p_dec; }; struct es_out_sys_t { input_thread_t *p_input; + /* all programs */ + int i_pgrm; + es_out_pgrm_t **pgrm; + es_out_pgrm_t *p_pgrm; /* Master program */ + /* all es */ int i_id; - int i_es; es_out_id_t **es; @@ -75,16 +102,17 @@ struct es_out_sys_t static es_out_id_t *EsOutAdd ( es_out_t *, es_format_t * ); static int EsOutSend ( es_out_t *, es_out_id_t *, block_t * ); static void EsOutDel ( es_out_t *, es_out_id_t * ); +static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force ); static int EsOutControl( es_out_t *, int i_query, va_list ); + +static void EsSelect( es_out_t *out, es_out_id_t *es ); +static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update ); static char *LanguageGetName( const char *psz_code ); -/** - * Create a new es_out structure - * - * \param p_input The related input thread - * \return the new es_out_t - */ +/***************************************************************************** + * input_EsOutNew: + *****************************************************************************/ es_out_t *input_EsOutNew( input_thread_t *p_input ) { es_out_t *out = malloc( sizeof( es_out_t ) ); @@ -102,8 +130,12 @@ es_out_t *input_EsOutNew( input_thread_t *p_input ) p_sys->b_active = VLC_FALSE; p_sys->i_mode = ES_OUT_MODE_AUTO; - p_sys->i_id = 1; + p_sys->i_pgrm = 0; + p_sys->pgrm = NULL; + p_sys->p_pgrm = NULL; + + p_sys->i_id = 0; p_sys->i_es = 0; p_sys->es = NULL; @@ -124,12 +156,9 @@ es_out_t *input_EsOutNew( input_thread_t *p_input ) return out; } -/** - * Deletes an es_out structure - * - * \param out the es_out structure to destroy - * \return nothing - */ +/***************************************************************************** + * input_EsOutDelete: + *****************************************************************************/ void input_EsOutDelete( es_out_t *out ) { es_out_sys_t *p_sys = out->p_sys; @@ -137,191 +166,249 @@ void input_EsOutDelete( es_out_t *out ) for( i = 0; i < p_sys->i_es; i++ ) { + if( p_sys->es[i]->p_dec ) + { + input_DecoderDelete( p_sys->es[i]->p_dec ); + } + if( p_sys->es[i]->psz_description ) + free( p_sys->es[i]->psz_description ); + es_format_Clean( &p_sys->es[i]->fmt ); + free( p_sys->es[i] ); } if( p_sys->es ) - { free( p_sys->es ); + + for( i = 0; i < p_sys->i_pgrm; i++ ) + { + free( p_sys->pgrm[i] ); } + if( p_sys->pgrm ) + free( p_sys->pgrm ); + free( p_sys ); free( out ); } -/** - * Add a program - * - * \param out the es_out - * \param i_group ... - * \return a program descriptor for the new program - */ -static pgrm_descriptor_t *EsOutAddProgram( es_out_t *out, int i_group ) +es_out_id_t *input_EsOutGetFromID( es_out_t *out, int i_id ) { - input_thread_t *p_input = out->p_sys->p_input; - pgrm_descriptor_t *p_pgrm; - es_descriptor_t *p_pmt; - - /* FIXME we should use a object variable but a lot of place in src/input - * have to be changed */ - int i_select = config_GetInt( p_input, "program" ); + int i; + if( i_id < 0 ) + { + /* Special HACK, -i_id is tha cat of the stream */ + return (es_out_id_t*)((uint8_t*)NULL-i_id); + } - /* create it */ - p_pgrm = input_AddProgram( p_input, i_group, 0 ); + for( i = 0; i < out->p_sys->i_es; i++ ) + { + if( out->p_sys->es[i]->i_id == i_id ) + return out->p_sys->es[i]; + } + return NULL; +} - /* XXX welcome to kludge, add a dummy es, if you want to understand - * why have a look at input_SetProgram. Basicaly, it assume the first - * es to be the PMT, how that is stupid, nevertheless it is needed for - * the old ts demuxer */ - p_pmt = input_AddES( p_input, p_pgrm, 0, UNKNOWN_ES, NULL, 0 ); - p_pmt->i_fourcc = VLC_FOURCC( 'n', 'u', 'l', 'l' ); +void input_EsOutDiscontinuity( es_out_t *out, vlc_bool_t b_audio ) +{ + es_out_sys_t *p_sys = out->p_sys; + int i; - /* Select i_select or the first by default */ - if( p_input->stream.p_selected_program == NULL && - ( i_select <= 0 || i_select == i_group ) ) + for( i = 0; i < p_sys->i_es; i++ ) { - p_input->stream.p_selected_program = p_pgrm; - } + es_out_id_t *es = p_sys->es[i]; - return p_pgrm; + /* Send a dummy block to let decoder know that + * there is a discontinuity */ + if( es->p_dec && ( !b_audio || es->fmt.i_cat == AUDIO_ES ) ) + { + input_DecoderDiscontinuity( es->p_dec ); + } + } } -/** - * Select an ES given the current mode - * XXX: you need to take a the lock before (stream.stream_lock) +/***************************************************************************** * - * \param out The es_out structure - * \param es es_out_id structure - * \param b_force ... - * \return nothing - */ -static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force ) + *****************************************************************************/ +static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es ) { es_out_sys_t *p_sys = out->p_sys; input_thread_t *p_input = p_sys->p_input; + vlc_value_t val, text; - int i_cat = es->p_es->i_cat; + char *psz_var; - if( !p_sys->b_active || - ( !b_force && es->p_es->fmt.i_priority < 0 ) ) - { + if( es->fmt.i_cat == AUDIO_ES ) + psz_var = "audio-es"; + else if( es->fmt.i_cat == VIDEO_ES ) + psz_var = "video-es"; + else if( es->fmt.i_cat == SPU_ES ) + psz_var = "spu-es"; + else return; + + /* Get the number of ES already added */ + var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL ); + if( val.i_int == 0 ) + { + vlc_value_t val2; + + /* First one, we need to add the "Disable" choice */ + val2.i_int = -1; text.psz_string = _("Disable"); + var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text ); + val.i_int++; } - if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force ) + /* Take care of the ES description */ + if( es->psz_description && *es->psz_description ) { - if( !es->p_es->p_dec ) - { - input_SelectES( p_input, es->p_es ); - } + text.psz_string = strdup( es->psz_description ); } - else if( p_sys->i_mode == ES_OUT_MODE_AUTO ) + else { - int i_wanted = -1; + text.psz_string = malloc( strlen( _("Track %i") ) + 20 ); + sprintf( text.psz_string, _("Track %i"), val.i_int ); + } - if( es->p_es->p_pgrm != NULL && - es->p_es->p_pgrm != p_input->stream.p_selected_program ) - { - return; - } + val.i_int = es->i_id; + var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text ); - if( i_cat == AUDIO_ES ) - { - if( p_sys->p_es_audio && - p_sys->p_es_audio->p_es->fmt.i_priority >= - es->p_es->fmt.i_priority ) - { - return; - } - i_wanted = p_sys->i_audio_last >= 0 ? - p_sys->i_audio_last : es->i_channel; - } - else if( i_cat == SPU_ES ) - { - if( p_sys->p_es_sub && - p_sys->p_es_sub->p_es->fmt.i_priority >= - es->p_es->fmt.i_priority ) - { - return; - } - i_wanted = p_sys->i_sub_last; - } - else if( i_cat == VIDEO_ES ) - { - i_wanted = es->i_channel; - } + free( text.psz_string ); + + var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE ); +} + +/* EsOutProgramSelect: + * Select a program and update the object variable + */ +static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm ) +{ + es_out_sys_t *p_sys = out->p_sys; + input_thread_t *p_input = p_sys->p_input; + vlc_value_t val; + int i; + + if( p_sys->p_pgrm == p_pgrm ) + return; /* Nothing to do */ + + if( p_sys->p_pgrm ) + { + es_out_pgrm_t *old = p_sys->p_pgrm; + msg_Dbg( p_input, "Unselecting program id=%d", old->i_id ); - if( i_wanted == es->i_channel && es->p_es->p_dec == NULL ) + for( i = 0; i < p_sys->i_es; i++ ) { - input_SelectES( p_input, es->p_es ); + if( p_sys->es[i]->p_pgrm == old && p_sys->es[i]->p_dec && + p_sys->i_mode != ES_OUT_MODE_ALL ) + EsUnselect( out, p_sys->es[i], VLC_TRUE ); } } - /* FIXME TODO handle priority here */ - if( es->p_es->p_dec ) + msg_Dbg( p_input, "Selecting program id=%d", p_pgrm->i_id ); + + /* Mark it selected */ + p_pgrm->b_selected = VLC_TRUE; + + /* Switch master stream */ + if( p_sys->p_pgrm && p_sys->p_pgrm->clock.b_master ) { - if( i_cat == AUDIO_ES ) - { - if( p_sys->i_mode == ES_OUT_MODE_AUTO && - p_sys->p_es_audio && p_sys->p_es_audio->p_es->p_dec ) - { - input_UnselectES( p_input, p_sys->p_es_audio->p_es ); - } - p_sys->p_es_audio = es; - } - else if( i_cat == SPU_ES ) - { - if( p_sys->i_mode == ES_OUT_MODE_AUTO && - p_sys->p_es_sub && p_sys->p_es_sub->p_es->p_dec ) - { - input_UnselectES( p_input, p_sys->p_es_sub->p_es ); - } - p_sys->p_es_sub = es; - } - else if( i_cat == VIDEO_ES ) - { - p_sys->p_es_video = es; - } + p_sys->p_pgrm->clock.b_master = VLC_FALSE; + } + p_pgrm->clock.b_master = VLC_TRUE; + p_sys->p_pgrm = p_pgrm; + + /* Update "program" */ + val.i_int = p_pgrm->i_id; + var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL ); + + /* Update "es-*" */ + var_Change( p_input, "audio-es", VLC_VAR_CLEARCHOICES, NULL, NULL ); + var_Change( p_input, "video-es", VLC_VAR_CLEARCHOICES, NULL, NULL ); + var_Change( p_input, "spu-es", VLC_VAR_CLEARCHOICES, NULL, NULL ); + for( i = 0; i < p_sys->i_es; i++ ) + { + EsOutESVarUpdate( out, p_sys->es[i] ); + EsOutSelect( out, p_sys->es[i], VLC_FALSE ); + } + + var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE ); +} + +/* EsOutAddProgram: + * Add a program + */ +static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group ) +{ + es_out_sys_t *p_sys = out->p_sys; + input_thread_t *p_input = p_sys->p_input; + vlc_value_t val; + + es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) ); + + /* Init */ + p_pgrm->i_id = i_group; + p_pgrm->i_es = 0; + p_pgrm->b_selected = VLC_FALSE; + input_ClockInit( &p_pgrm->clock, VLC_FALSE, p_input->input.i_cr_average ); + + /* Append it */ + TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm ); + + /* Update "program" variable */ + val.i_int = i_group; + var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, NULL ); + + if( i_group == var_GetInteger( p_input, "program" ) ) + { + EsOutProgramSelect( out, p_pgrm ); + } + else + { + var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE ); } + return p_pgrm; } -/** - * Add an es_out - * - * \param out the es_out to add - * \param fmt the es_format of the es_out - * \return an es_out id +/* EsOutAdd: + * Add an es_out */ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt ) { es_out_sys_t *p_sys = out->p_sys; input_thread_t *p_input = p_sys->p_input; + es_out_id_t *es = malloc( sizeof( es_out_id_t ) ); - pgrm_descriptor_t *p_pgrm = NULL; - char psz_cat[sizeof( _("Stream ") ) + 10]; - char *psz_description; + es_out_pgrm_t *p_pgrm = NULL; + int i; - vlc_mutex_lock( &p_input->stream.stream_lock ); - if( fmt->i_group >= 0 ) + if( fmt->i_group < 0 ) { - /* search program */ - p_pgrm = input_FindProgram( p_input, fmt->i_group ); + msg_Err( p_input, "invakud group number" ); + return NULL; + } - if( p_pgrm == NULL ) + /* Search the program */ + for( i = 0; i < p_sys->i_pgrm; i++ ) + { + if( fmt->i_group == p_sys->pgrm[i]->i_id ) { - /* Create it */ - p_pgrm = EsOutAddProgram( out, fmt->i_group ); + p_pgrm = p_sys->pgrm[i]; + break; } } - if( fmt->i_id < 0 ) + if( p_pgrm == NULL ) { - fmt->i_id = out->p_sys->i_id - 1; + /* Create a new one */ + p_pgrm = EsOutProgramAdd( out, fmt->i_group ); } - psz_description = LanguageGetName( fmt->psz_language ); - es->p_es = input_AddES( p_input, p_pgrm, fmt->i_id + 1, - fmt->i_cat, psz_description, 0 ); - es->p_es->i_stream_id = fmt->i_id; - es->p_es->i_fourcc = fmt->i_codec; + /* Increase ref count for program */ + p_pgrm->i_es++; + /* Set up ES */ + if( fmt->i_id < 0 ) + fmt->i_id = out->p_sys->i_id; + es->i_id = fmt->i_id; + es->p_pgrm = p_pgrm; + es_format_Copy( &es->fmt, fmt ); switch( fmt->i_cat ) { case AUDIO_ES: @@ -340,9 +427,14 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt ) es->i_channel = 0; break; } + es->psz_description = LanguageGetName( fmt->psz_language ); + es->p_dec = NULL; + + if( es->p_pgrm == p_sys->p_pgrm ) + EsOutESVarUpdate( out, es ); +#if 0 /* Add stream info */ - vlc_mutex_unlock( &p_input->stream.stream_lock ); sprintf( psz_cat, _("Stream %d"), out->p_sys->i_id - 1 ); input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Codec"), @@ -406,19 +498,12 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt ) default: break; } - - vlc_mutex_lock( &p_input->stream.stream_lock ); free( psz_description ); +#endif - es_format_Copy( &es->p_es->fmt, fmt ); + /* Select it if needed */ + EsOutSelect( out, es, VLC_FALSE ); - /* Apply mode - * XXX change that when we do group too */ - if( 1 ) - { - EsOutSelect( out, es, VLC_FALSE ); - } - vlc_mutex_unlock( &p_input->stream.stream_lock ); TAB_APPEND( out->p_sys->i_es, out->p_sys->es, es ); p_sys->i_id++; /* always incremented */ @@ -438,6 +523,186 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt ) return es; } +static void EsSelect( es_out_t *out, es_out_id_t *es ) +{ + es_out_sys_t *p_sys = out->p_sys; + input_thread_t *p_input = p_sys->p_input; + vlc_value_t val; + char *psz_var; + + if( es->p_dec ) + { + msg_Warn( p_input, "ES 0x%x is already selected", es->i_id ); + return; + } + + if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES ) + { + if( !var_GetBool( p_input, "video" ) || ( p_input->p_sout && !var_GetBool( p_input, "sout-video" ) ) ) + { + msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x", es->i_id ); + return; + } + } + else if( es->fmt.i_cat == AUDIO_ES ) + { + var_Get( p_input, "audio", &val ); + if( !var_GetBool( p_input, "audio" ) || ( p_input->p_sout && !var_GetBool( p_input, "sout-audio" ) ) ) + { + msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x", es->i_id ); + return; + } + } + + es->p_dec = input_DecoderNew( p_input, &es->fmt, VLC_FALSE ); + if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm ) + return; + + if( es->fmt.i_cat == VIDEO_ES ) + psz_var = "video-es"; + else if( es->fmt.i_cat == AUDIO_ES ) + psz_var = "audio-es"; + else if( es->fmt.i_cat == SPU_ES ) + psz_var = "spu-es"; + else + return; + + /* Mark it as selected */ + val.i_int = es->i_id; + var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL ); + + + var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE ); +} + +static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update ) +{ + es_out_sys_t *p_sys = out->p_sys; + input_thread_t *p_input = p_sys->p_input; + vlc_value_t val; + char *psz_var; + + if( es->p_dec == NULL ) + { + msg_Warn( p_input, "ES 0x%x is already unselected", es->i_id ); + return; + } + + input_DecoderDelete( es->p_dec ); + es->p_dec = NULL; + + if( !b_update ) + return; + + /* Update var */ + if( es->p_dec == NULL ) + return; + if( es->fmt.i_cat == VIDEO_ES ) + psz_var = "video-es"; + else if( es->fmt.i_cat == AUDIO_ES ) + psz_var = "audio-es"; + else if( es->fmt.i_cat == SPU_ES ) + psz_var = "spu-es"; + else + return; + + /* Mark it as selected */ + val.i_int = -1; + var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL ); + + var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE ); +} + +/** + * Select an ES given the current mode + * XXX: you need to take a the lock before (stream.stream_lock) + * + * \param out The es_out structure + * \param es es_out_id structure + * \param b_force ... + * \return nothing + */ +static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force ) +{ + es_out_sys_t *p_sys = out->p_sys; + + int i_cat = es->fmt.i_cat; + + if( !p_sys->b_active || + ( !b_force && es->fmt.i_priority < 0 ) ) + { + return; + } + + if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force ) + { + if( !es->p_dec ) + EsSelect( out, es ); + } + else if( p_sys->i_mode == ES_OUT_MODE_AUTO ) + { + int i_wanted = -1; + + if( es->p_pgrm != p_sys->p_pgrm ) + return; + + if( i_cat == AUDIO_ES ) + { + if( p_sys->p_es_audio && + p_sys->p_es_audio->fmt.i_priority >= es->fmt.i_priority ) + { + return; + } + i_wanted = p_sys->i_audio_last >= 0 ? + p_sys->i_audio_last : es->i_channel; + } + else if( i_cat == SPU_ES ) + { + if( p_sys->p_es_sub && + p_sys->p_es_sub->fmt.i_priority >= + es->fmt.i_priority ) + { + return; + } + i_wanted = p_sys->i_sub_last; + } + else if( i_cat == VIDEO_ES ) + { + i_wanted = es->i_channel; + } + + if( i_wanted == es->i_channel && es->p_dec == NULL ) + EsSelect( out, es ); + } + + /* FIXME TODO handle priority here */ + if( es->p_dec ) + { + if( i_cat == AUDIO_ES ) + { + if( p_sys->i_mode == ES_OUT_MODE_AUTO && + p_sys->p_es_audio && p_sys->p_es_audio->p_dec ) + { + EsUnselect( out, p_sys->p_es_audio, VLC_FALSE ); + } + p_sys->p_es_audio = es; + } + else if( i_cat == SPU_ES ) + { + if( p_sys->i_mode == ES_OUT_MODE_AUTO && + p_sys->p_es_sub && p_sys->p_es_sub->p_dec ) + { + EsUnselect( out, p_sys->p_es_sub, VLC_FALSE ); + } + p_sys->p_es_sub = es; + } + else if( i_cat == VIDEO_ES ) + { + p_sys->p_es_video = es; + } + } +} + /** * Send a block for the given es_out * @@ -448,37 +713,30 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt ) static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block ) { es_out_sys_t *p_sys = out->p_sys; - pgrm_descriptor_t *p_pgrm = es->p_es->p_pgrm; input_thread_t *p_input = p_sys->p_input; - - if( p_pgrm == NULL ) - { - p_pgrm = p_sys->p_input->stream.p_selected_program; - } + es_out_pgrm_t *p_pgrm = es->p_pgrm; /* +11 -> avoid null value with non null dts/pts */ - if( p_block->i_dts > 0 && p_pgrm ) + if( p_block->i_dts > 0 ) { p_block->i_dts = - input_ClockGetTS( p_input, p_pgrm, ( p_block->i_dts + 11 ) * 9 / 100 ); + input_ClockGetTS( p_input, &p_pgrm->clock, ( p_block->i_dts + 11 ) * 9 / 100 ); } - if( p_block->i_pts > 0 && p_pgrm ) + if( p_block->i_pts > 0 ) { p_block->i_pts = - input_ClockGetTS( p_input, p_pgrm, ( p_block->i_pts + 11 )* 9 / 100 ); + input_ClockGetTS( p_input, &p_pgrm->clock, ( p_block->i_pts + 11 )* 9 / 100 ); } - vlc_mutex_lock( &out->p_sys->p_input->stream.stream_lock ); - p_block->i_rate = out->p_sys->p_input->stream.control.i_rate; - if( es->p_es->p_dec && - (es->p_es->i_cat!=AUDIO_ES || !p_sys->p_input->stream.control.b_mute) ) + p_block->i_rate = p_input->i_rate; + + /* TODO handle mute */ + if( es->p_dec && ( es->fmt.i_cat != AUDIO_ES || p_input->i_rate == INPUT_RATE_DEFAULT ) ) { - vlc_mutex_unlock( &out->p_sys->p_input->stream.stream_lock ); - input_DecodeBlock( es->p_es->p_dec, p_block ); + input_DecoderDecode( es->p_dec, p_block ); } else { - vlc_mutex_unlock( &out->p_sys->p_input->stream.stream_lock ); block_Release( p_block ); } @@ -492,9 +750,23 @@ static void EsOutDel( es_out_t *out, es_out_id_t *es ) { es_out_sys_t *p_sys = out->p_sys; + /* We don't try to reselect */ + if( es->p_dec ) + EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm ); + TAB_REMOVE( p_sys->i_es, p_sys->es, es ); - switch( es->p_es->i_cat ) + es->p_pgrm->i_es--; + if( es->p_pgrm->i_es == 0 ) + { + msg_Err( p_sys->p_input, "Program doesn't have es anymore, clenaing TODO ?" ); + } + + if( p_sys->p_es_audio == es ) p_sys->p_es_audio = NULL; + if( p_sys->p_es_video == es ) p_sys->p_es_video = NULL; + if( p_sys->p_es_sub == es ) p_sys->p_es_sub = NULL; + + switch( es->fmt.i_cat ) { case AUDIO_ES: p_sys->i_audio--; @@ -507,30 +779,10 @@ static void EsOutDel( es_out_t *out, es_out_id_t *es ) break; } - /* We don't try to reselect */ - vlc_mutex_lock( &p_sys->p_input->stream.stream_lock ); - if( es->p_es->p_dec ) - { - input_UnselectES( p_sys->p_input, es->p_es ); - } - - if( es->p_es->p_waveformatex ) - { - free( es->p_es->p_waveformatex ); - es->p_es->p_waveformatex = NULL; - } - if( es->p_es->p_bitmapinfoheader ) - { - free( es->p_es->p_bitmapinfoheader ); - es->p_es->p_bitmapinfoheader = NULL; - } - input_DelES( p_sys->p_input, es->p_es ); + if( es->psz_description ) + free( es->psz_description ); - if( p_sys->p_es_audio == es ) p_sys->p_es_audio = NULL; - if( p_sys->p_es_video == es ) p_sys->p_es_video = NULL; - if( p_sys->p_es_sub == es ) p_sys->p_es_sub = NULL; - - vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock ); + es_format_Clean( &es->fmt ); free( es ); } @@ -554,42 +806,34 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) switch( i_query ) { case ES_OUT_SET_ES_STATE: - vlc_mutex_lock( &p_sys->p_input->stream.stream_lock ); es = (es_out_id_t*) va_arg( args, es_out_id_t * ); b = (vlc_bool_t) va_arg( args, vlc_bool_t ); - if( b && es->p_es->p_dec == NULL ) + if( b && es->p_dec == NULL ) { - input_SelectES( p_sys->p_input, es->p_es ); - vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock ); - return es->p_es->p_dec ? VLC_SUCCESS : VLC_EGENERIC; + EsSelect( out, es ); + return es->p_dec ? VLC_SUCCESS : VLC_EGENERIC; } - else if( !b && es->p_es->p_dec ) + else if( !b && es->p_dec ) { - input_UnselectES( p_sys->p_input, es->p_es ); - vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock ); + EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm ); return VLC_SUCCESS; } - vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock ); return VLC_SUCCESS; case ES_OUT_GET_ES_STATE: es = (es_out_id_t*) va_arg( args, es_out_id_t * ); pb = (vlc_bool_t*) va_arg( args, vlc_bool_t * ); - *pb = es->p_es->p_dec ? VLC_TRUE : VLC_FALSE; + *pb = es->p_dec ? VLC_TRUE : VLC_FALSE; return VLC_SUCCESS; case ES_OUT_SET_ACTIVE: { b = (vlc_bool_t) va_arg( args, vlc_bool_t ); p_sys->b_active = b; - + /* Needed ? */ if( b ) - { - vlc_value_t val; - val.b_bool = VLC_TRUE; - var_Set( p_sys->p_input, "intf-change", val ); - } + var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE ); return VLC_SUCCESS; } @@ -603,28 +847,20 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) if( i == ES_OUT_MODE_NONE || i == ES_OUT_MODE_ALL || i == ES_OUT_MODE_AUTO ) { - vlc_value_t val; - p_sys->i_mode = i; /* Reapply policy mode */ - vlc_mutex_lock( &p_sys->p_input->stream.stream_lock ); for( i = 0; i < p_sys->i_es; i++ ) { - if( p_sys->es[i]->p_es->p_dec ) + if( p_sys->es[i]->p_dec ) { - input_UnselectES( p_sys->p_input, p_sys->es[i]->p_es ); + EsUnselect( out, p_sys->es[i], p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); } } for( i = 0; i < p_sys->i_es; i++ ) { EsOutSelect( out, p_sys->es[i], VLC_FALSE ); } - vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock ); - - val.b_bool = VLC_TRUE; - var_Set( p_sys->p_input, "intf-change", val ); - return VLC_SUCCESS; } return VLC_EGENERIC; @@ -636,65 +872,119 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args ) case ES_OUT_SET_ES: es = (es_out_id_t*) va_arg( args, es_out_id_t * ); + /* Special case NULL, NULL+i_cat */ if( es == NULL ) { for( i = 0; i < p_sys->i_es; i++ ) { - vlc_mutex_lock( &p_sys->p_input->stream.stream_lock ); - if( p_sys->es[i]->p_es->p_dec ) - { - input_UnselectES( p_sys->p_input, p_sys->es[i]->p_es ); - } - vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock ); + if( p_sys->es[i]->p_dec ) + EsUnselect( out, p_sys->es[i], + p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); + } + } + else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) ) + { + for( i = 0; i < p_sys->i_es; i++ ) + { + if( p_sys->es[i]->p_dec && p_sys->es[i]->fmt.i_cat == AUDIO_ES ) + EsUnselect( out, p_sys->es[i], p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); + } + } + else if( es == (es_out_id_t*)((uint8_t*)NULL+VIDEO_ES) ) + { + for( i = 0; i < p_sys->i_es; i++ ) + { + if( p_sys->es[i]->p_dec && p_sys->es[i]->fmt.i_cat == VIDEO_ES ) + EsUnselect( out, p_sys->es[i], p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); + } + } + else if( es == (es_out_id_t*)((uint8_t*)NULL+SPU_ES) ) + { + for( i = 0; i < p_sys->i_es; i++ ) + { + if( p_sys->es[i]->p_dec && p_sys->es[i]->fmt.i_cat == SPU_ES ) + EsUnselect( out, p_sys->es[i], p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); } } else { - vlc_mutex_lock( &p_sys->p_input->stream.stream_lock ); - EsOutSelect( out, es, VLC_TRUE ); - vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock ); + for( i = 0; i < p_sys->i_es; i++ ) + { + if( es == p_sys->es[i] ) + { + EsOutSelect( out, es, VLC_TRUE ); + break; + } + } } return VLC_SUCCESS; case ES_OUT_SET_PCR: case ES_OUT_SET_GROUP_PCR: { - pgrm_descriptor_t *p_pgrm = NULL; - int64_t i_pcr; + es_out_pgrm_t *p_pgrm = NULL; + int i_group = 0; + int64_t i_pcr; if( i_query == ES_OUT_SET_PCR ) { - p_pgrm = p_sys->p_input->stream.p_selected_program; + p_pgrm = p_sys->p_pgrm; } else { - int i_group = (int)va_arg( args, int ); - p_pgrm = input_FindProgram( p_sys->p_input, i_group ); - if( p_pgrm == NULL ) + int i; + i_group = (int)va_arg( args, int ); + for( i = 0; i < p_sys->i_pgrm; i++ ) { - /* we create the requested program */ - p_pgrm = EsOutAddProgram( out, i_group ); + if( p_sys->pgrm[i]->i_id == i_group ) + { + p_pgrm = p_sys->pgrm[i]; + break; + } } } + if( p_pgrm == NULL ) + p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */ + i_pcr = (int64_t)va_arg( args, int64_t ); /* search program */ - if( p_pgrm ) - { /* 11 is a vodoo trick to avoid non_pcr*9/100 to be null */ - input_ClockManageRef( p_sys->p_input, p_pgrm, (i_pcr + 11 ) * 9 / 100); - } + input_ClockSetPCR( p_sys->p_input, &p_pgrm->clock, (i_pcr + 11 ) * 9 / 100); return VLC_SUCCESS; } case ES_OUT_RESET_PCR: - for( i = 0; i < p_sys->p_input->stream.i_pgrm_number; i++ ) + for( i = 0; i < p_sys->i_pgrm; i++ ) { - p_sys->p_input->stream.pp_programs[i]->i_synchro_state = - SYNCHRO_REINIT; - p_sys->p_input->stream.pp_programs[i]->last_pts = 0; + p_sys->pgrm[i]->clock.i_synchro_state = SYNCHRO_REINIT; + p_sys->pgrm[i]->clock.last_pts = 0; } return VLC_SUCCESS; + case ES_OUT_GET_GROUP: + pi = (int*) va_arg( args, int* ); + if( p_sys->p_pgrm ) + *pi = p_sys->p_pgrm->i_id; + else + *pi = -1; /* FIXME */ + return VLC_SUCCESS; + + case ES_OUT_SET_GROUP: + { + int j; + i = (int) va_arg( args, int ); + for( j = 0; j < p_sys->i_pgrm; j++ ) + { + es_out_pgrm_t *p_pgrm = p_sys->pgrm[j]; + if( p_pgrm->i_id == i ) + { + EsOutProgramSelect( out, p_pgrm ); + return VLC_SUCCESS; + } + } + return VLC_EGENERIC; + } + default: msg_Err( p_sys->p_input, "unknown query in es_out_Control" ); return VLC_EGENERIC; diff --git a/src/input/input.c b/src/input/input.c index 5e0b231d427f442f9c215b3971f06328a8380c2a..2374b9fac7890ba4878755f670e440bc64bccf7f 100644 --- a/src/input/input.c +++ b/src/input/input.c @@ -1,12 +1,11 @@ /***************************************************************************** * input.c: input thread - * Read a stream, demultiplex and parse it before sending it to - * decoders. ***************************************************************************** * Copyright (C) 1998-2004 VideoLAN * $Id$ * * Authors: Christophe Massiot + * Laurent Aimar * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,64 +32,63 @@ #include #include -#ifdef HAVE_SYS_TIMES_H -# include -#endif +#include "input_internal.h" #include "stream_output.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; +static int Run ( input_thread_t *p_input ); + +static int Init ( input_thread_t *p_input ); +static void Error( input_thread_t *p_input ); +static void End ( input_thread_t *p_input ); + +static inline int ControlPopNoLock( input_thread_t *, int *, vlc_value_t * ); +static void ControlReduce( input_thread_t * ); +static vlc_bool_t Control( input_thread_t *, int, vlc_value_t ); - 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 EndThread ( input_thread_t *p_input ); +static void UpdateFromAccess( input_thread_t * ); +static void UpdateFromDemux( input_thread_t * ); static void ParseOption( input_thread_t *p_input, const char *psz_option ); static void DecodeUrl ( char * ); -/***************************************************************************** - * 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 ); -static int BookmarkCallback( 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. + * + * Variables for _public_ use: + * * Get and Set: + * - state + * - rate,rate-slower, rate-faster + * - position, position-offset + * - time, time-offset + * - title,title-next,title-prev + * - chapter,chapter-next, chapter-prev + * - program, audio-es, video-es, spu-es + * - bookmark + * * Get only: + * - length + * - bookmarks + * * For intf callback upon changes + * - intf-change + * TODO explain when Callback is called + * TODO complete this list (?) *****************************************************************************/ input_thread_t *__input_CreateThread( vlc_object_t *p_parent, input_item_t *p_item ) { input_thread_t *p_input; /* thread descriptor */ - vlc_value_t val; int i; /* Allocate descriptor */ @@ -101,8 +99,40 @@ input_thread_t *__input_CreateThread( vlc_object_t *p_parent, return NULL; } - /* Store pointer to input item descriptor */ - p_input->p_item = p_item; + /* Init Common fields */ + p_input->b_eof = VLC_FALSE; + p_input->b_can_pace_control = VLC_TRUE; + p_input->i_start = 0; + p_input->i_time = 0; + p_input->i_stop = 0; + p_input->i_title = 0; + p_input->title = NULL; + p_input->i_state = INIT_S; + p_input->i_rate = INPUT_RATE_DEFAULT; + p_input->i_bookmark = 0; + p_input->bookmark = NULL; + p_input->p_es_out = NULL; + p_input->p_sout = NULL; + p_input->b_out_pace_control = VLC_FALSE; + p_input->i_pts_delay = 0; + + + /* Init Input fields */ + p_input->input.p_item = p_item; + p_input->input.p_access = NULL; + p_input->input.p_stream = NULL; + p_input->input.p_demux = NULL; + p_input->input.b_title_demux = VLC_FALSE; + p_input->input.i_title = 0; + p_input->input.title = NULL; + p_input->input.b_can_pace_control = VLC_TRUE; + p_input->input.b_eof = VLC_FALSE; + p_input->input.i_cr_average = 0; + + /* Init control buffer */ + vlc_mutex_init( p_input, &p_input->lock_control ); + p_input->i_control = 0; + p_input->p_sys = NULL; /* Parse input options */ vlc_mutex_lock( &p_item->lock ); @@ -113,166 +143,15 @@ input_thread_t *__input_CreateThread( vlc_object_t *p_parent, } vlc_mutex_unlock( &p_item->lock ); - /* Create a few object variables we'll need later on */ - var_Create( p_input, "video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); - 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-path", VLC_VAR_STRING | - 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 */ - vlc_mutex_lock( &p_item->lock ); - p_input->psz_source = strdup( p_item->psz_uri ); - vlc_mutex_unlock( &p_item->lock ); - - /* 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 = demux_vaControlDefault; - p_input->i_cr_average = config_GetInt( p_input, "cr-average" ); - - /* Access */ - p_input->p_access = NULL; - p_input->pf_access_control = NULL; - - p_input->i_bufsize = 0; - p_input->i_mtu = 0; - p_input->i_pts_delay = DEFAULT_PTS_DELAY; - - /* Initialize statistics */ - p_input->c_loops = 0; - p_input->stream.c_packets_read = 0; - p_input->stream.c_packets_trashed = 0; - - /* Set locks. */ - vlc_mutex_init( p_input, &p_input->stream.stream_lock ); - vlc_cond_init( p_input, &p_input->stream.stream_wait ); - vlc_mutex_init( p_input, &p_input->stream.control.control_lock ); - - /* Initialize stream description */ - p_input->stream.b_changed = 0; - p_input->stream.i_es_number = 0; - p_input->stream.i_selected_es_number = 0; - p_input->stream.i_pgrm_number = 0; - p_input->stream.i_new_status = p_input->stream.i_new_rate = 0; - p_input->stream.b_new_mute = MUTE_NO_CHANGE; - p_input->stream.i_mux_rate = 0; - p_input->stream.b_seekable = 0; - p_input->stream.p_sout = NULL; - - /* no stream, no program, no area, no es */ - p_input->stream.p_new_program = NULL; - - p_input->stream.i_area_nb = 0; - p_input->stream.pp_areas = NULL; - p_input->stream.p_selected_area = NULL; - p_input->stream.p_new_area = NULL; - - p_input->stream.pp_selected_es = NULL; - p_input->stream.p_removed_es = NULL; - p_input->stream.p_newly_selected_es = NULL; - - /* By default there is one area in a stream */ - input_AddArea( p_input, 0, 1 ); - p_input->stream.p_selected_area = p_input->stream.pp_areas[0]; - - /* Initialize stream control properties. */ - 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"); - - msg_Info( p_input, "playlist item `%s'", p_input->psz_source ); + /* Create Object Variables for private use only */ + input_ConfigVarInit( p_input ); - /* Bookmarks */ - var_Create( p_input, "bookmarks", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); - var_Create( p_input, "bookmark", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE | - VLC_VAR_ISCOMMAND ); - val.psz_string = _("Bookmark"); - var_Change( p_input, "bookmark", VLC_VAR_SETTEXT, &val, NULL ); - var_AddCallback( p_input, "bookmark", BookmarkCallback, NULL ); - - p_input->i_bookmarks = 0; - p_input->pp_bookmarks = NULL; + /* Create Objects variables for public Get and Set */ + input_ControlVarInit( p_input ); + p_input->input.i_cr_average = var_GetInteger( p_input, "cr-average" ); +#if 0 + /* TODO */ var_Get( p_input, "bookmarks", &val ); if( val.psz_string ) { @@ -319,15 +198,18 @@ input_thread_t *__input_CreateThread( vlc_object_t *p_parent, } free( val.psz_string ); } +#endif + /* Now we can attach our new input */ vlc_object_attach( p_input, p_parent ); /* Create thread and wait for its readiness. */ - if( vlc_thread_create( p_input, "input", RunThread, + if( vlc_thread_create( p_input, "input", Run, VLC_THREAD_PRIORITY_INPUT, VLC_TRUE ) ) { msg_Err( p_input, "cannot create input thread" ); - free( p_input ); + vlc_object_detach( p_input ); + vlc_object_destroy( p_input ); return NULL; } @@ -341,34 +223,40 @@ input_thread_t *__input_CreateThread( vlc_object_t *p_parent, *****************************************************************************/ void input_StopThread( input_thread_t *p_input ) { - demux_t *p_demux; - access_t *p_access; + vlc_list_t *p_list; + int i; - /* Make the thread exit from a possible vlc_cond_wait() */ - vlc_mutex_lock( &p_input->stream.stream_lock ); + /* Set die for input */ + p_input->b_die = VLC_TRUE; - /* Request thread destruction */ + /* We cannot touch p_input fields directly (we can from another thread), + * so use the vlc_object_find way, it's perfectly safe */ - /* Temporary demux2 hack */ - p_demux = (demux_t *)vlc_object_find( p_input, VLC_OBJECT_DEMUX, FIND_CHILD ); - if( p_demux ) + /* Set die for all access */ + p_list = vlc_list_find( p_input, VLC_OBJECT_ACCESS, FIND_CHILD ); + for( i = 0; i < p_list->i_count; i++ ) { - p_demux->b_die = 1; - vlc_object_release( p_demux ); + p_list->p_values[i].p_object->b_die = VLC_TRUE; } + vlc_list_release( p_list ); - /* Temporary access2 hack */ - p_access = (access_t *)vlc_object_find( p_input, VLC_OBJECT_ACCESS, FIND_CHILD ); - if( p_access ) + /* Set die for all stream */ + p_list = vlc_list_find( p_input, VLC_OBJECT_STREAM, FIND_CHILD ); + for( i = 0; i < p_list->i_count; i++ ) { - p_access->b_die = 1; - vlc_object_release( p_access ); + p_list->p_values[i].p_object->b_die = VLC_TRUE; } + vlc_list_release( p_list ); - p_input->b_die = 1; + /* Set die for all demux */ + p_list = vlc_list_find( p_input, VLC_OBJECT_DEMUX, FIND_CHILD ); + for( i = 0; i < p_list->i_count; i++ ) + { + p_list->p_values[i].p_object->b_die = VLC_TRUE; + } + vlc_list_release( p_list ); - vlc_cond_signal( &p_input->stream.stream_wait ); - vlc_mutex_unlock( &p_input->stream.stream_lock ); + input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL ); } /***************************************************************************** @@ -381,106 +269,192 @@ void input_DestroyThread( input_thread_t *p_input ) /* Join the thread */ vlc_thread_join( p_input ); - /* Destroy Mutex locks */ - vlc_mutex_destroy( &p_input->stream.control.control_lock ); - vlc_cond_destroy( &p_input->stream.stream_wait ); - vlc_mutex_destroy( &p_input->stream.stream_lock ); + /* Delete input lock (only after thread joined) */ + vlc_mutex_destroy( &p_input->lock_control ); + + /* TODO: maybe input_DestroyThread should also delete p_input instead + * of the playlist but I'm not sure if it's possible */ } /***************************************************************************** - * RunThread: main thread loop + * Run: main thread loop ***************************************************************************** * Thread in charge of processing the network packets and demultiplexing. *****************************************************************************/ -static int RunThread( input_thread_t *p_input ) +static int Run( input_thread_t *p_input ) { - vlc_value_t val; - mtime_t i_update_next = -1; + int64_t i_intf_update = 0; - /* Signal right now, otherwise we'll get stuck in a peek */ + /* Signal that the thread is launched */ vlc_thread_ready( p_input ); - if( InitThread( p_input ) ) + if( Init( p_input ) ) { /* If we failed, wait before we are killed, and exit */ - p_input->b_error = 1; + p_input->b_error = VLC_TRUE; - ErrorThread( p_input ); + Error( p_input ); /* Tell we're dead */ - p_input->b_dead = 1; + p_input->b_dead = VLC_TRUE; return 0; } - /* initialization is complete */ - vlc_mutex_lock( &p_input->stream.stream_lock ); - 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 ); - + /* Main loop */ while( !p_input->b_die && !p_input->b_error && !p_input->b_eof ) { - unsigned int i, i_count; - - p_input->c_loops++; - - vlc_mutex_lock( &p_input->stream.stream_lock ); + vlc_bool_t b_force_update = VLC_FALSE; + int i_ret; + int i_type; + vlc_value_t val; - if( p_input->stream.p_new_program ) + /* Do the read */ + if( p_input->i_state != PAUSE_S ) { - if( p_input->pf_set_program != NULL ) - { - - /* Reinitialize buffer manager. */ - input_AccessReinit( p_input ); - - p_input->pf_set_program( p_input, - p_input->stream.p_new_program ); + if( p_input->i_stop <= 0 || p_input->i_time < p_input->i_stop ) + i_ret=p_input->input.p_demux->pf_demux(p_input->input.p_demux); + else + i_ret = 0; /* EOF */ - /* Escape all decoders for the stream discontinuity they - * will encounter. */ - input_EscapeDiscontinuity( p_input ); + if( i_ret > 0 ) + { + /* TODO */ + if( p_input->input.b_title_demux && + p_input->input.p_demux->info.i_update ) + { + UpdateFromDemux( p_input ); + b_force_update = VLC_TRUE; + } + else if( !p_input->input.b_title_demux && + p_input->input.p_access && + p_input->input.p_access->info.i_update ) + { + UpdateFromAccess( p_input ); + b_force_update = VLC_TRUE; + } + } + else if( i_ret == 0 ) /* EOF */ + { + vlc_value_t repeat; - for( i = 0; i < p_input->stream.i_pgrm_number; i++ ) + var_Get( p_input, "input-repeat", &repeat ); + if( repeat.i_int == 0 ) + { + /* End of file - we do not set b_die because only the + * playlist is allowed to do so. */ + msg_Dbg( p_input, "EOF reached" ); + p_input->b_eof = VLC_TRUE; + p_input->input.b_eof = VLC_TRUE; + } + else { - pgrm_descriptor_t * p_pgrm - = p_input->stream.pp_programs[i]; + 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 ); + } - /* Reinitialize synchro. */ - p_pgrm->i_synchro_state = SYNCHRO_REINIT; + /* Seek to title 0 position 0(start) */ + val.i_int = 0; + input_ControlPush( p_input, INPUT_CONTROL_SET_TITLE, &val ); + if( p_input->i_start > 0 ) + { + val.i_time = p_input->i_start; + input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, + &val ); + } + else + { + val.f_float = 0.0; + input_ControlPush( p_input, INPUT_CONTROL_SET_POSITION, + &val ); + } } } - p_input->stream.p_new_program = NULL; + else if( i_ret < 0 ) + { + p_input->b_error = VLC_TRUE; + } } - - if( p_input->stream.p_new_area ) + else { - if( p_input->stream.b_seekable && p_input->pf_set_area != NULL ) - { - input_AccessReinit( p_input ); + /* Small wait */ + msleep( 10*1000 ); + } - p_input->pf_set_area( p_input, p_input->stream.p_new_area ); + /* Handle control */ + vlc_mutex_lock( &p_input->lock_control ); + ControlReduce( p_input ); + while( !ControlPopNoLock( p_input, &i_type, &val ) ) + { + msg_Dbg( p_input, "control type=%d", i_type ); + if( Control( p_input, i_type, val ) ) + b_force_update = VLC_TRUE; + } + vlc_mutex_unlock( &p_input->lock_control ); - /* Escape all decoders for the stream discontinuity they - * will encounter. */ - input_EscapeDiscontinuity( p_input ); + if( b_force_update || + i_intf_update < mdate() ) + { + vlc_value_t val; + double f_pos; + int64_t i_time, i_length; + /* update input status variables */ + if( !demux2_Control( p_input->input.p_demux, DEMUX_GET_POSITION, &f_pos ) ) + { + val.f_float = (float)f_pos; + var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL ); + } + if( !demux2_Control( p_input->input.p_demux, DEMUX_GET_TIME, &i_time ) ) + { + p_input->i_time = i_time; + val.i_time = i_time; + var_Change( p_input, "time", VLC_VAR_SETVALUE, &val, NULL ); + } + if( !demux2_Control( p_input->input.p_demux, DEMUX_GET_LENGTH, &i_length ) ) + { + vlc_value_t old_val; + var_Get( p_input, "length", &old_val ); + val.i_time = i_length; + var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL ); - for( i = 0; i < p_input->stream.i_pgrm_number; i++ ) + if( old_val.i_time != val.i_time ) { - pgrm_descriptor_t * p_pgrm - = p_input->stream.pp_programs[i]; + /* TODO */ +#if 0 + char psz_buffer[MSTRTIME_MAX_SIZE]; - /* Reinitialize synchro. */ - p_pgrm->i_synchro_state = SYNCHRO_REINIT; + vlc_mutex_lock( &p_input->p_item->lock ); + p_input->p_item->i_duration = i_length; + vlc_mutex_unlock( &p_input->p_item->lock ); + + input_Control( p_input, INPUT_ADD_INFO, _("General"), _("Duration"), + msecstotimestr( psz_buffer, i_length / 1000 ) ); +#endif } } - p_input->stream.p_new_area = NULL; + + var_SetBool( p_input, "intf-change", VLC_TRUE ); + i_intf_update = mdate() + I64C(150000); } + } + + /* Wait we are asked to die */ + if( !p_input->b_die ) + { + Error( p_input ); + } + + /* Clean up */ + End( p_input ); + + return 0; +#if 0 + while( !p_input->b_die && !p_input->b_error && !p_input->b_eof ) + { if( p_input->stream.p_selected_area->i_seek != NO_SEEK ) { if( p_input->stream.p_selected_area->i_size > 0 ) @@ -532,66 +506,10 @@ static int RunThread( input_thread_t *p_input ) p_input->stream.p_selected_area->i_seek = NO_SEEK; } - if( p_input->stream.p_removed_es ) - { - input_UnselectES( p_input, p_input->stream.p_removed_es ); - p_input->stream.p_removed_es = NULL; - } - - if( p_input->stream.p_newly_selected_es ) - { - input_SelectES( p_input, p_input->stream.p_newly_selected_es ); - p_input->stream.p_newly_selected_es = NULL; - } - - if( p_input->stream.b_new_mute != MUTE_NO_CHANGE ) - { - if( p_input->stream.b_new_mute ) - { - input_EscapeAudioDiscontinuity( p_input ); - } - - vlc_mutex_lock( &p_input->stream.control.control_lock ); - p_input->stream.control.b_mute = p_input->stream.b_new_mute; - vlc_mutex_unlock( &p_input->stream.control.control_lock ); - - p_input->stream.b_new_mute = MUTE_NO_CHANGE; - } - - vlc_mutex_unlock( &p_input->stream.stream_lock ); - /* Read and demultiplex some data. */ i_count = p_input->pf_demux( p_input ); - if( i_count == 0 ) - { - 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; - } + XXXXX if( !p_input->b_error && !p_input->b_eof && i_update_next < mdate() ) { @@ -655,285 +573,363 @@ static int RunThread( input_thread_t *p_input ) EndThread( p_input ); return 0; +#endif } /***************************************************************************** - * InitThread: init the input Thread + * Init: init the input Thread *****************************************************************************/ -static int InitThread( input_thread_t * p_input ) +static int Init( input_thread_t * p_input ) { - vlc_meta_t *p_meta = NULL, *p_meta_user = NULL; -// float f_fps; - double f_fps; - mtime_t i_length; - - /* Parse source string. Syntax : [[][/]:][] */ - char * psz_parser = p_input->psz_dupsource = strdup(p_input->psz_source); - vlc_value_t val, val1; - int64_t i_microsecondperframe; - - subtitle_demux_t *p_sub_toselect = NULL; - char *psz_sub_file = NULL; + char *psz_dup = strdup( p_input->input.p_item->psz_uri ); + char *psz_access = NULL; + char *psz_demux = NULL; + char *psz_path = NULL; + char *psz; + vlc_value_t val; - /* Skip the plug-in names */ - while( *psz_parser && *psz_parser != ':' ) - { - psz_parser++; - } + /* Open access/stream/demux */ + psz = strchr( psz_dup, ':' ); #if defined( WIN32 ) || defined( UNDER_CE ) - if( psz_parser - p_input->psz_dupsource == 1 ) - { - msg_Warn( p_input, "drive letter %c: found in source string", - p_input->psz_dupsource[0] ) ; - psz_parser = ""; - } -#endif - - if( !*psz_parser ) + if( psz - psz_dup == 1 ) { - 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; + msg_Warn( p_input, "drive letter %c: found in source string", psz_dup[0] ); } else +#endif + if( psz ) { - *psz_parser++ = '\0'; - - /* let's skip '//' */ - if( psz_parser[0] == '/' && psz_parser[1] == '/' ) - { - psz_parser += 2 ; - } + *psz++ = '\0'; + if( psz[0] == '/' && psz[1] == '/' ) + psz += 2; - p_input->psz_name = psz_parser ; + psz_path = psz; - /* Come back to parse the access and demux plug-ins */ - psz_parser = p_input->psz_dupsource; - - if( !*psz_parser ) - { - /* No access */ - p_input->psz_access = ""; - } - else if( *psz_parser == '/' ) - { - /* No access */ - p_input->psz_access = ""; - psz_parser++; - } - else + psz = strchr( psz_dup, '/' ); + if( psz ) { - p_input->psz_access = psz_parser; - - while( *psz_parser && *psz_parser != '/' ) - { - psz_parser++; - } - - if( *psz_parser == '/' ) - { - *psz_parser++ = '\0'; - } + *psz++ = '\0'; + psz_demux = psz; } - if( !*psz_parser ) - { - /* No demux */ - p_input->psz_demux = ""; - } - else - { - p_input->psz_demux = psz_parser; - } + psz_access = psz_dup; } - - msg_Dbg( p_input, "access `%s', demux `%s', name `%s'", - p_input->psz_access, p_input->psz_demux, p_input->psz_name ); - - if( input_AccessInit( p_input ) == -1 ) + else { - free( p_input->psz_source ); - if( p_input->psz_dupsource != NULL ) - { - free( p_input->psz_dupsource ); - } - - return VLC_EGENERIC; + psz_path = psz_dup; } - /* Initialize optional stream output. (before demuxer)*/ - var_Get( p_input, "sout", &val ); - if( val.psz_string != NULL ) + if( psz_access == NULL ) psz_access = ""; + if( psz_demux == NULL ) psz_demux = ""; + if( psz_path == NULL ) psz_path = ""; + + msg_Dbg( p_input, "`%s' gives access `%s' demux `%s' path `%s'", + p_input->input.p_item->psz_uri, + psz_access, psz_demux, psz_path ); + + /* Initialize optional stream output. (before access/demuxer) */ + psz = var_GetString( p_input, "sout" ); + if( *psz ) { - if ( *val.psz_string && (p_input->stream.p_sout = - sout_NewInstance( p_input, val.psz_string )) == NULL ) + p_input->p_sout = sout_NewInstance( p_input, psz ); + if( p_input->p_sout == NULL ) { msg_Err( p_input, "cannot start stream output instance, aborting" ); - free( val.psz_string ); + free( psz ); + free( psz_dup ); - 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 ); } + free( psz ); + /* Create es out */ 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, VLC_TRUE ); - - /* Maybe we had an encoded url */ - if( !p_input->p_access && strchr( p_input->psz_name, '%' ) ) + /* Try access_demux if no demux given */ + if( *psz_access && *psz_demux == '\0' ) { - DecodeUrl( p_input->psz_name ); + p_input->input.p_demux = demux2_New( p_input, psz_access, psz_demux, psz_path, + NULL, p_input->p_es_out ); + } - msg_Dbg( p_input, "retying with %s", p_input->psz_name ); - p_input->p_access = module_Need( p_input, "access", - p_input->psz_access, VLC_TRUE ); + if( p_input->input.p_demux ) + { + /* Get infos from access_demux */ + demux2_Control( p_input->input.p_demux, + DEMUX_GET_PTS_DELAY, &p_input->i_pts_delay ); + p_input->input.b_title_demux = VLC_TRUE; + if( demux2_Control( p_input->input.p_demux, + DEMUX_GET_TITLE_INFO, + &p_input->input.title, &p_input->input.i_title ) ) + { + p_input->input.i_title = 0; + p_input->input.title = NULL; + } + demux2_Control( p_input->input.p_demux, DEMUX_CAN_CONTROL_PACE, + &p_input->input.b_can_pace_control ); + demux2_Control( p_input->input.p_demux, DEMUX_CAN_PAUSE, + &p_input->input.b_can_pause ); } + else + { + /* Now try a real access */ + p_input->input.p_access = access2_New( p_input, psz_access, psz_demux, psz_path ); + + /* Access failed, URL encoded ? */ + if( p_input->input.p_access == NULL && strchr( psz_path, '%' ) ) + { + DecodeUrl( psz_path ); + + msg_Dbg( p_input, "retying with access `%s' demux `%s' path `%s'", + psz_access, psz_demux, psz_path ); + + p_input->input.p_access = access2_New( p_input, psz_access, psz_demux, psz_path ); + } #ifndef WIN32 /* Remove this gross hack from the win32 build as colons - * are forbidden in filenames on Win32. */ + * 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) ) - { - 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; + /* Maybe we got something like: /Volumes/toto:titi/gabu.mpg */ + if( p_input->input.p_access == NULL && + *psz_access == '\0' && ( *psz_demux || *psz_path ) ) + { + free( psz_dup ); + psz_dup = strdup( p_input->input.p_item->psz_uri ); + psz_access = ""; + psz_demux = ""; + psz_path = psz_dup; - p_input->p_access = module_Need( p_input, "access", - p_input->psz_access, VLC_TRUE ); - } + p_input->input.p_access = access2_New( p_input, psz_access, psz_demux, psz_path ); + } #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 ); - if ( p_input->stream.p_sout != NULL ) + + if( p_input->input.p_access == NULL ) { - sout_DeleteInstance( p_input->stream.p_sout ); + msg_Err( p_input, "no suitable access module for `%s'", + p_input->input.p_item->psz_uri ); + goto error; } - input_AccessEnd( p_input ); - free( p_input->psz_source ); - if( p_input->psz_dupsource != NULL ) + /* Get infos from access */ + access2_Control( p_input->input.p_access, + ACCESS_GET_PTS_DELAY, &p_input->i_pts_delay ); + p_input->input.b_title_demux = VLC_FALSE; + if( access2_Control( p_input->input.p_access, + ACCESS_GET_TITLE_INFO, + &p_input->input.title, &p_input->input.i_title ) ) { - free( p_input->psz_dupsource ); + p_input->input.i_title = 0; + p_input->input.title = NULL; + } + access2_Control( p_input->input.p_access, ACCESS_CAN_CONTROL_PACE, + &p_input->input.b_can_pace_control ); + access2_Control( p_input->input.p_access, ACCESS_CAN_PAUSE, + &p_input->input.b_can_pace_control ); + + /* Create the stream_t */ + p_input->input.p_stream = stream_AccessNew( p_input->input.p_access ); + if( p_input->input.p_stream == NULL ) + { + msg_Warn( p_input, "cannot create a stream_t from access" ); + goto error; } - input_EsOutDelete( p_input->p_es_out ); - return VLC_EGENERIC; - } - /* Waiting for stream. */ - if( p_input->i_mtu ) - { - p_input->i_bufsize = p_input->i_mtu; + /* Open a demuxer */ + if( *psz_demux == '\0' && *p_input->input.p_access->psz_demux ) + { + psz_demux = p_input->input.p_access->psz_demux; + } + p_input->input.p_demux = demux2_New( p_input, psz_access, psz_demux, psz_path, + p_input->input.p_stream, + p_input->p_es_out ); + if( p_input->input.p_demux == NULL ) + { + msg_Err( p_input, "no suitable demux module for `%s/%s://%s'", + psz_access, psz_demux, psz_path ); + goto error; + } + + /* TODO get title from demux */ + if( p_input->input.i_title <= 0 ) + { + if( demux2_Control( p_input->input.p_demux, DEMUX_GET_TITLE_INFO, + &p_input->input.title, &p_input->input.i_title ) ) + { + p_input->input.i_title = 0; + p_input->input.title = NULL; + } + else + { + p_input->input.b_title_demux = VLC_TRUE; + } + } } - else + /* Create global title (for now, just a copy) */ + p_input->i_title = p_input->input.i_title; + if( p_input->i_title > 0 ) { - p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE; + int i; + p_input->title = malloc( sizeof( input_title_t *) * p_input->i_title ); + for( i = 0; i < p_input->i_title; i++ ) + { + p_input->title[i] = vlc_input_title_Duplicate( p_input->input.title[i] ); + } + + /* Setup variables */ + input_ControlVarNavigation( p_input ); + input_ControlVarTitle( p_input, 0 ); } + /* Global flag */ + p_input->b_can_pace_control = p_input->input.b_can_pace_control; + p_input->b_can_pause = p_input->input.b_can_pause; + + /* Fix pts delay */ + if( p_input->i_pts_delay <= 0 ) + p_input->i_pts_delay = DEFAULT_PTS_DELAY; /* If the desynchronisation requested by the user is < 0, we need to * cache more data. */ - 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 ) - { - while( !input_FillBuffer( p_input ) ) - { - if( p_input->b_die || p_input->b_error || p_input->b_eof ) - { - 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; - } - } - } + /* 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; - /* Create the stream_t facilities */ - p_input->s = input_StreamNew( p_input ); - if( p_input->s == NULL ) - { - /* should never occur yet */ + /* TODO: check meta data from users */ - msg_Err( p_input, "cannot create stream_t" ); + /* TODO: get meta data from demuxer */ - module_Unneed( p_input, p_input->p_access ); - if ( p_input->stream.p_sout != NULL ) + /* Init length */ + if( !demux2_Control( p_input->input.p_demux, DEMUX_GET_LENGTH, &val.i_time ) && val.i_time > 0 ) + { + var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL ); + /* TODO update playlist meta data */ + } + /* Start time*/ + /* Set start time */ + p_input->i_start = (int64_t)var_GetInteger( p_input, "start-time" ) * + I64C(1000000); + p_input->i_stop = (int64_t)var_GetInteger( p_input, "stop-time" ) * + I64C(1000000); + + if( p_input->i_start > 0 ) + { + if( p_input->i_start >= val.i_time ) { - sout_DeleteInstance( p_input->stream.p_sout ); + msg_Warn( p_input, "invalid start-time ignored" ); } - input_AccessEnd( p_input ); - free( p_input->psz_source ); - if( p_input->psz_dupsource != NULL ) + else { - free( p_input->psz_dupsource ); + vlc_value_t s; + + msg_Dbg( p_input, "start-time: %ds", + (int)( p_input->i_start / I64C(1000000) ) ); + + s.i_time = p_input->i_start; + input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &s ); } - input_EsOutDelete( p_input->p_es_out ); - return VLC_EGENERIC; + } + if( p_input->i_stop > 0 && p_input->i_stop <= p_input->i_start ) + { + msg_Warn( p_input, "invalid stop-time ignored" ); + p_input->i_stop = 0; } - /* Find and open appropriate demux module */ - 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 ); + /* TODO: do subtitle loading */ - input_StreamDelete( p_input->s ); - 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 ); + + /* Set up es_out */ + es_out_Control( p_input->p_es_out, ES_OUT_SET_ACTIVE, VLC_TRUE ); + val.b_bool = VLC_FALSE; + if( p_input->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 ); + + /* TODO select forced subs */ +#if 0 + if( p_sub_toselect ) + { + es_out_Control( p_input->p_es_out, ES_OUT_SET_ES, + p_sub_toselect->p_es, VLC_TRUE ); + } +#endif + + if( p_input->p_sout ) + { + if( p_input->p_sout->i_out_pace_nocontrol > 0 ) + { + p_input->b_out_pace_control = VLC_FALSE; } - input_EsOutDelete( p_input->p_es_out ); - return VLC_EGENERIC; + 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" ); } + msg_Dbg( p_input, "`%s' sucessfully opened", + p_input->input.p_item->psz_uri ); + + /* initialization is complete */ + p_input->i_state = PLAYING_S; + + val.i_int = PLAYING_S; + var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL ); + + return VLC_SUCCESS; + +error: + if( p_input->input.p_demux ) + demux2_Delete( p_input->input.p_demux ); + + if( p_input->input.p_stream ) + stream_AccessDelete( p_input->input.p_stream ); + + if( p_input->input.p_access ) + access2_Delete( p_input->input.p_access ); + + if( p_input->p_es_out ) + input_EsOutDelete( p_input->p_es_out ); + + if( p_input->p_sout ) + sout_DeleteInstance( p_input->p_sout ); + + /* Mark them deleted */ + p_input->input.p_demux = NULL; + p_input->input.p_stream = NULL; + p_input->input.p_access = NULL; + p_input->p_es_out = NULL; + p_input->p_sout = NULL; + + return VLC_EGENERIC; + +#if 0 + vlc_meta_t *p_meta = NULL, *p_meta_user = NULL; +// float f_fps; + double f_fps; + mtime_t i_length; + + FIXME + p_input->input.i_cr_average = config_GetInt( p_input, "cr-average" ); + p_input->stream.control.i_status = INIT_S; + p_input->stream.control.i_rate = DEFAULT_RATE; + + /* 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 ); @@ -948,37 +944,37 @@ static int InitThread( input_thread_t * p_input ) vlc_value_t val; var_Get( p_input, "meta-title", &val ); - if( val.psz_string && *val.psz_string ) + if( *val.psz_string ) vlc_meta_Add( p_meta_user, VLC_META_TITLE, val.psz_string ); - if( val.psz_string ) free( val.psz_string ); + free( val.psz_string ); var_Get( p_input, "meta-author", &val ); - if( val.psz_string && *val.psz_string ) + if( *val.psz_string ) vlc_meta_Add( p_meta_user, VLC_META_AUTHOR, val.psz_string ); - if( val.psz_string ) free( val.psz_string ); + free( val.psz_string ); var_Get( p_input, "meta-artist", &val ); - if( val.psz_string && *val.psz_string ) + if( *val.psz_string ) vlc_meta_Add( p_meta_user, VLC_META_ARTIST, val.psz_string ); - if( val.psz_string ) free( val.psz_string ); + free( val.psz_string ); var_Get( p_input, "meta-genre", &val ); - if( val.psz_string && *val.psz_string ) + if( *val.psz_string ) vlc_meta_Add( p_meta_user, VLC_META_GENRE, val.psz_string ); - if( val.psz_string ) free( val.psz_string ); + free( val.psz_string ); var_Get( p_input, "meta-copyright", &val ); - if( val.psz_string && *val.psz_string ) + if( *val.psz_string ) vlc_meta_Add( p_meta_user, VLC_META_COPYRIGHT, val.psz_string ); - if( val.psz_string ) free( val.psz_string ); + free( val.psz_string ); var_Get( p_input, "meta-description", &val ); - if( val.psz_string && *val.psz_string ) + if( *val.psz_string ) vlc_meta_Add( p_meta_user, VLC_META_DESCRIPTION, val.psz_string ); - if( val.psz_string ) free( val.psz_string ); + free( val.psz_string ); var_Get( p_input, "meta-date", &val ); - if( val.psz_string && *val.psz_string ) + if( *val.psz_string ) vlc_meta_Add( p_meta_user, VLC_META_DATE, val.psz_string ); - if( val.psz_string ) free( val.psz_string ); + free( val.psz_string ); var_Get( p_input, "meta-url", &val ); - if( val.psz_string && *val.psz_string ) + if( *val.psz_string ) vlc_meta_Add( p_meta_user, VLC_META_URL, val.psz_string ); - if( val.psz_string ) free( val.psz_string ); + free( val.psz_string ); } /* Get meta informations from demuxer */ @@ -1077,25 +1073,6 @@ static int InitThread( input_thread_t * p_input ) } } - /* 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 ) { @@ -1161,30 +1138,15 @@ static int InitThread( input_thread_t * p_input ) 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; +#endif } /***************************************************************************** - * ErrorThread: RunThread() error loop + * Error: RunThread() error loop ***************************************************************************** * This function is called when an error occured during thread main's loop. *****************************************************************************/ -static void ErrorThread( input_thread_t *p_input ) +static void Error( input_thread_t *p_input ) { while( !p_input->b_die ) { @@ -1194,55 +1156,39 @@ static void ErrorThread( input_thread_t *p_input ) } /***************************************************************************** - * EndThread: end the input thread + * End: end the input thread *****************************************************************************/ -static void EndThread( input_thread_t * p_input ) +static void End( input_thread_t * p_input ) { - int i, j; -#ifdef HAVE_SYS_TIMES_H - /* Display statistics */ - struct tms cpu_usage; - times( &cpu_usage ); - - msg_Dbg( p_input, "%ld loops consuming user: %ld, system: %ld", - p_input->c_loops, cpu_usage.tms_utime, cpu_usage.tms_stime ); -#else - msg_Dbg( p_input, "%ld loops", p_input->c_loops ); -#endif + vlc_value_t val; + msg_Dbg( p_input, "closing `%s'", + p_input->input.p_item->psz_uri ); - /* DumpStream: printf some info for debugging purpose */ -#define S p_input->stream - msg_Dbg( p_input, "dumping stream ID 0x%x [OK:%ld/D:%ld]", S.i_stream_id, - S.c_packets_read, S.c_packets_trashed ); -#undef S - for( i = 0; i < p_input->stream.i_pgrm_number; i++ ) - { -#define P p_input->stream.pp_programs[i] - msg_Dbg( p_input, "dumping program 0x%x, version %d (%s)", - P->i_number, P->i_version, - P->b_is_ok ? "complete" : "partial" ); -#undef P - for( j = 0; j < p_input->stream.pp_programs[i]->i_es_number; j++ ) - { -#define ES p_input->stream.pp_programs[i]->pp_es[j] - msg_Dbg( p_input, "ES 0x%x, " - "stream 0x%x, fourcc `%4.4s', %s [OK:%ld/ERR:%ld]", - ES->i_id, ES->i_stream_id, (char*)&ES->i_fourcc, - ES->p_dec != NULL ? "selected" : "not selected", - ES->c_packets, ES->c_invalid_packets ); -#undef ES - } - } + /* We are at the end */ + p_input->i_state = END_S; + + val.i_int = END_S; + var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL ); + + /* Clean control variables */ + input_ControlVarClean( p_input ); - /* Free demultiplexer's data */ - if( p_input->p_demux ) module_Unneed( p_input, p_input->p_demux ); + /* Unload all modules */ + if( p_input->input.p_demux ) + demux2_Delete( p_input->input.p_demux ); - /* Free all ES and destroy all decoder threads */ - input_EndStream( p_input ); + if( p_input->input.p_stream ) + stream_AccessDelete( p_input->input.p_stream ); + + if( p_input->input.p_access ) + access2_Delete( p_input->input.p_access ); + + if( p_input->p_es_out ) + input_EsOutDelete( p_input->p_es_out ); /* Close optional stream output instance */ - if( p_input->stream.p_sout ) + if( p_input->p_sout ) { vlc_object_t *p_pl = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE ); @@ -1252,20 +1198,20 @@ static void EndThread( input_thread_t * p_input ) { /* 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 ); + vlc_object_detach( p_input->p_sout ); + vlc_object_attach( p_input->p_sout, p_pl ); } else { msg_Warn( p_input, "destroying sout" ); - sout_DeleteInstance( p_input->stream.p_sout ); + sout_DeleteInstance( p_input->p_sout ); } if( p_pl ) - { vlc_object_release( p_pl ); - } } + /* TODO subs */ +#if 0 /* Destroy subtitles demuxers */ if( p_input->p_sys ) { @@ -1278,30 +1224,471 @@ static void EndThread( input_thread_t * p_input ) free( p_input->p_sys->sub ); } - /* Free input_thread_sys_t */ - free( p_input->p_sys ); } +#endif + + /* Free input_thread_sys_t */ + free( p_input->p_sys ); - /* Destroy the stream_t facilities */ - if( p_input->s ) input_StreamDelete( p_input->s ); + /* Tell we're dead */ + p_input->b_dead = VLC_TRUE; +} - /* Destroy es out */ - if( p_input->p_es_out ) input_EsOutDelete( p_input->p_es_out ); +/***************************************************************************** + * Control + *****************************************************************************/ +static inline int ControlPopNoLock( input_thread_t *p_input, + int *pi_type, vlc_value_t *p_val ) +{ + if( p_input->i_control <= 0 ) + { + return VLC_EGENERIC; + } - /* Close the access plug-in */ - if( p_input->p_access ) module_Unneed( p_input, p_input->p_access ); + *pi_type = p_input->control[0].i_type; + *p_val = p_input->control[0].val; - input_AccessEnd( p_input ); + p_input->i_control--; + if( p_input->i_control > 0 ) + { + int i; - /* Free info structures XXX destroy es before 'cause vorbis */ - msg_Dbg( p_input, "freeing info structures..."); + for( i = 0; i < p_input->i_control; i++ ) + { + p_input->control[i].i_type = p_input->control[i+1].i_type; + p_input->control[i].val = p_input->control[i+1].val; + } + } - free( p_input->psz_source ); - if( p_input->psz_dupsource != NULL ) free( p_input->psz_dupsource ); + return VLC_SUCCESS; +} - /* Tell we're dead */ - p_input->b_dead = 1; +static void ControlReduce( input_thread_t *p_input ) +{ + int i; + for( i = 1; i < p_input->i_control; i++ ) + { + const int i_lt = p_input->control[i-1].i_type; + const int i_ct = p_input->control[i].i_type; + + /* XXX We can't merge INPUT_CONTROL_SET_ES */ + msg_Dbg( p_input, "[%d/%d] l=%d c=%d", i, p_input->i_control, i_lt, i_ct ); + if( i_lt == i_ct && + ( i_ct == INPUT_CONTROL_SET_STATE || + i_ct == INPUT_CONTROL_SET_RATE || + i_ct == INPUT_CONTROL_SET_POSITION || + i_ct == INPUT_CONTROL_SET_TIME || + i_ct == INPUT_CONTROL_SET_PROGRAM || + i_ct == INPUT_CONTROL_SET_TITLE || + i_ct == INPUT_CONTROL_SET_SEEKPOINT || + i_ct == INPUT_CONTROL_SET_BOOKMARK ) ) + { + int j; + msg_Dbg( p_input, "merged at %d", i ); + /* Remove the i-1 */ + for( j = i; j < p_input->i_control; j++ ) + p_input->control[j-1] = p_input->control[j]; + p_input->i_control--; + } + else + { + /* TODO but that's not that important + - merge SET_X with SET_X_CMD + - remove SET_SEEKPOINT/SET_POSITION/SET_TIME before a SET_TITLE + - remove SET_SEEKPOINT/SET_POSITION/SET_TIME before another among them + - ? + */ + } + } } + +static vlc_bool_t Control( input_thread_t *p_input, int i_type, vlc_value_t val ) +{ + vlc_bool_t b_force_update = VLC_FALSE; + + switch( i_type ) + { + case INPUT_CONTROL_SET_DIE: + msg_Dbg( p_input, "control: INPUT_CONTROL_SET_DIE proceed" ); + /* Mark all submodules to die */ + if( p_input->input.p_access ) + p_input->input.p_access->b_die = VLC_TRUE; + if( p_input->input.p_stream ) + p_input->input.p_stream->b_die = VLC_TRUE; + p_input->input.p_demux->b_die = VLC_TRUE; + + p_input->b_die = VLC_TRUE; + break; + + case INPUT_CONTROL_SET_POSITION: + case INPUT_CONTROL_SET_POSITION_OFFSET: + { + double f_pos; + if( i_type == INPUT_CONTROL_SET_POSITION ) + { + f_pos = val.f_float; + } + else + { + /* Should not fail */ + demux2_Control( p_input->input.p_demux, DEMUX_GET_POSITION, &f_pos ); + f_pos += val.f_float; + } + if( f_pos < 0.0 ) f_pos = 0.0; + if( f_pos > 1.0 ) f_pos = 1.0; + if( demux2_Control( p_input->input.p_demux, DEMUX_SET_POSITION, f_pos ) ) + { + msg_Err( p_input, "INPUT_CONTROL_SET_POSITION(_OFFSET) %2.1f%% failed", + f_pos * 100 ); + } + else + { + input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE ); + es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR ); + b_force_update = VLC_TRUE; + } + break; + } + + case INPUT_CONTROL_SET_TIME: + case INPUT_CONTROL_SET_TIME_OFFSET: + { + int64_t i_time; + int i_ret; + + if( i_type == INPUT_CONTROL_SET_TIME ) + { + i_time = val.i_time; + } + else + { + /* Should not fail */ + demux2_Control( p_input->input.p_demux, + DEMUX_GET_TIME, &i_time ); + i_time += val.i_time; + } + if( i_time < 0 ) i_time = 0; + i_ret = demux2_Control( p_input->input.p_demux, + DEMUX_SET_TIME, i_time ); + if( i_ret ) + { + int64_t i_length; + /* Emulate it with a SET_POS */ + + demux2_Control( p_input->input.p_demux, + DEMUX_GET_LENGTH, &i_length ); + if( i_length > 0 ) + { + double f_pos = (double)i_time / (double)i_length; + i_ret = demux2_Control( p_input->input.p_demux, + DEMUX_SET_POSITION, f_pos ); + } + } + if( i_ret ) + { + msg_Err( p_input, "INPUT_CONTROL_SET_TIME(_OFFSET) %lld failed", + i_time ); + } + else + { + input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE ); + + es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR ); + b_force_update = VLC_TRUE; + } + break; + } + + case INPUT_CONTROL_SET_STATE: + if( ( val.i_int == PLAYING_S && p_input->i_state == PAUSE_S ) || + ( val.i_int == PAUSE_S && p_input->i_state == PAUSE_S ) ) + { + int i_ret; + if( p_input->input.p_access ) + i_ret = access2_Control( p_input->input.p_access, + ACCESS_SET_PAUSE_STATE, VLC_FALSE ); + else + i_ret = demux2_Control( p_input->input.p_demux, + DEMUX_SET_PAUSE_STATE, VLC_FALSE ); + + if( i_ret ) + { + /* FIXME What to do ? */ + msg_Warn( p_input, "cannot unset pause -> EOF" ); + input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL ); + } + + b_force_update = VLC_TRUE; + + /* Switch to play */ + p_input->i_state = PLAYING_S; + val.i_int = PLAYING_S; + var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL ); + + /* Reset clock */ + es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR ); + } + else if( val.i_int == PAUSE_S && p_input->i_state == PLAYING_S && p_input->b_can_pause ) + { + int i_ret; + if( p_input->input.p_access ) + i_ret = access2_Control( p_input->input.p_access, + ACCESS_SET_PAUSE_STATE, VLC_TRUE ); + else + i_ret = demux2_Control( p_input->input.p_demux, + DEMUX_SET_PAUSE_STATE, VLC_TRUE ); + + b_force_update = VLC_TRUE; + + if( i_ret ) + { + msg_Warn( p_input, "cannot set pause state" ); + val.i_int = p_input->i_state; + } + else + { + val.i_int = PAUSE_S; + } + + /* Switch to new state */ + p_input->i_state = val.i_int; + var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL ); + } + else if( val.i_int == PAUSE_S && !p_input->b_can_pause ) + { + b_force_update = VLC_TRUE; + + /* Correct "state" value */ + val.i_int = p_input->i_state; + var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL ); + } + else if( val.i_int != PLAYING_S && val.i_int != PAUSE_S ) + { + msg_Err( p_input, "invalid state in INPUT_CONTROL_SET_STATE" ); + } + break; + + case INPUT_CONTROL_SET_RATE: + case INPUT_CONTROL_SET_RATE_SLOWER: + case INPUT_CONTROL_SET_RATE_FASTER: + { + int i_rate; + + if( i_type == INPUT_CONTROL_SET_RATE_SLOWER ) + i_rate = p_input->i_rate * 2; + else if( i_type == INPUT_CONTROL_SET_RATE_FASTER ) + i_rate = p_input->i_rate / 2; + else + i_rate = val.i_int; + + if( i_rate < INPUT_RATE_MIN ) + { + msg_Dbg( p_input, "cannot set rate faster" ); + i_rate = INPUT_RATE_MIN; + } + else if( i_rate > INPUT_RATE_MAX ) + { + msg_Dbg( p_input, "cannot set rate slower" ); + i_rate = INPUT_RATE_MAX; + } + if( i_rate != INPUT_RATE_DEFAULT && + ( !p_input->b_can_pace_control || !p_input->b_out_pace_control ) ) + { + msg_Dbg( p_input, "cannot change rate" ); + i_rate = INPUT_RATE_DEFAULT; + } + if( i_rate != p_input->i_rate ) + { + p_input->i_rate = i_rate; + val.i_int = i_rate; + var_Change( p_input, "rate", VLC_VAR_SETVALUE, &val, NULL ); + + /* We haven't send data to decoder when rate != default */ + if( i_rate == INPUT_RATE_DEFAULT ) + input_EsOutDiscontinuity( p_input->p_es_out, VLC_TRUE ); + + /* Reset clock */ + es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR ); + + b_force_update = VLC_TRUE; + } + break; + } + + case INPUT_CONTROL_SET_PROGRAM: + /* No need to force update, es_out does it if needed */ + es_out_Control( p_input->p_es_out, + ES_OUT_SET_GROUP, val.i_int ); + break; + + case INPUT_CONTROL_SET_ES: + /* No need to force update, es_out does it if needed */ + es_out_Control( p_input->p_es_out, + ES_OUT_SET_ES, input_EsOutGetFromID( p_input->p_es_out, val.i_int ) ); + break; + + case INPUT_CONTROL_SET_TITLE: + case INPUT_CONTROL_SET_TITLE_NEXT: + case INPUT_CONTROL_SET_TITLE_PREV: + if( p_input->input.b_title_demux && + p_input->input.i_title > 0 ) + { + /* TODO */ + /* FIXME handle demux title */ + demux_t *p_demux = p_input->input.p_demux; + int i_title; + + if( i_type == INPUT_CONTROL_SET_TITLE_PREV ) + i_title = p_demux->info.i_title - 1; + else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT ) + i_title = p_demux->info.i_title + 1; + else + i_title = val.i_int; + + if( i_title >= 0 && i_title < p_input->input.i_title ) + { + demux2_Control( p_demux, DEMUX_SET_TITLE, i_title ); + + input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE ); + es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR ); + + input_ControlVarTitle( p_input, i_title ); + } + } + else if( p_input->input.i_title > 0 ) + { + access_t *p_access = p_input->input.p_access; + int i_title; + + if( i_type == INPUT_CONTROL_SET_TITLE_PREV ) + i_title = p_access->info.i_title - 1; + else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT ) + i_title = p_access->info.i_title + 1; + else + i_title = val.i_int; + + if( i_title >= 0 && i_title < p_input->input.i_title ) + { + access2_Control( p_access, ACCESS_SET_TITLE, i_title ); + stream_AccessReset( p_input->input.p_stream ); + + input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE ); + es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR ); + } + } + break; + case INPUT_CONTROL_SET_SEEKPOINT: + case INPUT_CONTROL_SET_SEEKPOINT_NEXT: + case INPUT_CONTROL_SET_SEEKPOINT_PREV: + if( p_input->input.b_title_demux && + p_input->input.i_title > 0 ) + { + demux_t *p_demux = p_input->input.p_demux; + int i_seekpoint; + + if( i_type == INPUT_CONTROL_SET_SEEKPOINT_PREV ) + i_seekpoint = p_demux->info.i_seekpoint - 1; + else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT ) + i_seekpoint = p_demux->info.i_seekpoint + 1; + else + i_seekpoint = val.i_int; + + if( i_seekpoint >= 0 && + i_seekpoint < p_input->input.title[p_demux->info.i_title]->i_seekpoint ) + { + demux2_Control( p_demux, DEMUX_SET_SEEKPOINT, i_seekpoint ); + + input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE ); + es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR ); + } + } + else if( p_input->input.i_title > 0 ) + { + access_t *p_access = p_input->input.p_access; + int i_seekpoint; + + if( i_type == INPUT_CONTROL_SET_SEEKPOINT_PREV ) + i_seekpoint = p_access->info.i_seekpoint - 1; + else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT ) + i_seekpoint = p_access->info.i_seekpoint + 1; + else + i_seekpoint = val.i_int; + + if( i_seekpoint >= 0 && + i_seekpoint < p_input->input.title[p_access->info.i_title]->i_seekpoint ) + { + access2_Control( p_access, ACCESS_SET_SEEKPOINT, i_seekpoint ); + stream_AccessReset( p_input->input.p_stream ); + + input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE ); + es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR ); + } + } + break; + + case INPUT_CONTROL_SET_BOOKMARK: + default: + msg_Err( p_input, "not yet implemented" ); + break; + } + + return b_force_update; +} + +/***************************************************************************** + * UpdateFromDemux: + *****************************************************************************/ +static void UpdateFromDemux( input_thread_t *p_input ) +{ + demux_t *p_demux = p_input->input.p_demux; + vlc_value_t v; + + if( p_demux->info.i_update & INPUT_UPDATE_TITLE ) + { + v.i_int = p_demux->info.i_title; + var_Change( p_input, "title", VLC_VAR_SETVALUE, &v, NULL ); + + input_ControlVarTitle( p_input, p_demux->info.i_title ); + + p_demux->info.i_update &= ~INPUT_UPDATE_TITLE; + } + if( p_demux->info.i_update & INPUT_UPDATE_SEEKPOINT ) + { + v.i_int = p_demux->info.i_seekpoint; + var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &v, NULL); + + p_demux->info.i_update &= ~INPUT_UPDATE_SEEKPOINT; + } + p_demux->info.i_update &= ~INPUT_UPDATE_SIZE; +} + +/***************************************************************************** + * UpdateFromAccess: + *****************************************************************************/ +static void UpdateFromAccess( input_thread_t *p_input ) +{ + access_t *p_access = p_input->input.p_access; + vlc_value_t v; + + if( p_access->info.i_update & INPUT_UPDATE_TITLE ) + { + v.i_int = p_access->info.i_title; + var_Change( p_input, "title", VLC_VAR_SETVALUE, &v, NULL ); + + input_ControlVarTitle( p_input, p_access->info.i_title ); + + p_access->info.i_update &= ~INPUT_UPDATE_TITLE; + } + if( p_access->info.i_update & INPUT_UPDATE_SEEKPOINT ) + { + v.i_int = p_access->info.i_seekpoint; + var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &v, NULL); + + p_access->info.i_update &= ~INPUT_UPDATE_SEEKPOINT; + } + p_access->info.i_update &= ~INPUT_UPDATE_SIZE; +} + /***************************************************************************** * DecodeUrl: decode a given encoded url *****************************************************************************/ @@ -1438,273 +1825,4 @@ static void ParseOption( input_thread_t *p_input, const char *psz_option ) return; } -/***************************************************************************** - * input_SetStatus: change the reading status - *****************************************************************************/ - -/* Status changing methods */ -enum -{ - INPUT_STATUS_END = 0, - INPUT_STATUS_PLAY = 1, - INPUT_STATUS_PAUSE = 2, - INPUT_STATUS_FASTER = 3, - INPUT_STATUS_SLOWER = 4 -}; - -static void input_SetStatus( input_thread_t *p_input, int i_mode ) -{ - vlc_mutex_lock( &p_input->stream.stream_lock ); - - switch( i_mode ) - { - case INPUT_STATUS_END: - p_input->stream.i_new_status = PLAYING_S; - p_input->b_eof = 1; - msg_Dbg( p_input, "end of stream" ); - break; - - case INPUT_STATUS_PLAY: - p_input->stream.i_new_status = PLAYING_S; - msg_Dbg( p_input, "playing at normal rate" ); - break; - - case INPUT_STATUS_PAUSE: - /* XXX: we don't need to check i_status, because input_clock.c - * does it for us */ - p_input->stream.i_new_status = PAUSE_S; - msg_Dbg( p_input, "toggling pause" ); - break; - - case INPUT_STATUS_FASTER: - if( p_input->stream.control.i_rate * 4 <= DEFAULT_RATE ) - { - msg_Dbg( p_input, "can not play any faster" ); - } - else - { - p_input->stream.i_new_status = FORWARD_S; - p_input->stream.i_new_rate = - p_input->stream.control.i_rate / 2; - - if ( p_input->stream.i_new_rate < DEFAULT_RATE ) - { - msg_Dbg( p_input, "playing at %i:1 fast forward", - DEFAULT_RATE / p_input->stream.i_new_rate ); - } - else if ( p_input->stream.i_new_rate > DEFAULT_RATE ) - { - msg_Dbg( p_input, "playing at 1:%i slow motion", - p_input->stream.i_new_rate / DEFAULT_RATE ); - } - else if ( p_input->stream.i_new_rate == DEFAULT_RATE ) - { - p_input->stream.i_new_status = PLAYING_S; - msg_Dbg( p_input, "playing at normal rate" ); - } - } - break; - - case INPUT_STATUS_SLOWER: - if( p_input->stream.control.i_rate >= 8 * DEFAULT_RATE ) - { - msg_Dbg( p_input, "can not play any slower" ); - } - else - { - p_input->stream.i_new_status = FORWARD_S; - p_input->stream.i_new_rate = - p_input->stream.control.i_rate * 2; - - if ( p_input->stream.i_new_rate < DEFAULT_RATE ) - { - msg_Dbg( p_input, "playing at %i:1 fast forward", - DEFAULT_RATE / p_input->stream.i_new_rate ); - } - else if ( p_input->stream.i_new_rate > DEFAULT_RATE ) - { - msg_Dbg( p_input, "playing at 1:%i slow motion", - p_input->stream.i_new_rate / DEFAULT_RATE ); - } - else if ( p_input->stream.i_new_rate == DEFAULT_RATE ) - { - p_input->stream.i_new_status = PLAYING_S; - msg_Dbg( p_input, "playing at normal rate" ); - } - } - break; - - default: - break; - } - - vlc_cond_signal( &p_input->stream.stream_wait ); - vlc_mutex_unlock( &p_input->stream.stream_lock ); -} -/***************************************************************************** - * input_SetRate: - *****************************************************************************/ -static void input_SetRate( input_thread_t *p_input, int i_rate ) -{ - vlc_mutex_lock( &p_input->stream.stream_lock ); - - if( i_rate * 8 < DEFAULT_RATE ) - { - msg_Dbg( p_input, "can not play faster than 8x" ); - vlc_mutex_unlock( &p_input->stream.stream_lock ); - return; - } - if( i_rate > DEFAULT_RATE * 8 ) - { - msg_Dbg( p_input, "can not play slower than 1/8x" ); - vlc_mutex_unlock( &p_input->stream.stream_lock ); - return; - } - p_input->stream.i_new_status = FORWARD_S; - p_input->stream.i_new_rate = i_rate; - - if ( p_input->stream.i_new_rate < DEFAULT_RATE ) - { - msg_Dbg( p_input, "playing at %i:1 fast forward", - DEFAULT_RATE / p_input->stream.i_new_rate ); - } - else if ( p_input->stream.i_new_rate > DEFAULT_RATE ) - { - msg_Dbg( p_input, "playing at 1:%i slow motion", - p_input->stream.i_new_rate / DEFAULT_RATE ); - } - else if ( p_input->stream.i_new_rate == DEFAULT_RATE ) - { - p_input->stream.i_new_status = PLAYING_S; - msg_Dbg( p_input, "playing at normal rate" ); - } - - vlc_cond_signal( &p_input->stream.stream_wait ); - vlc_mutex_unlock( &p_input->stream.stream_lock ); -} - -/***************************************************************************** - * 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; - } - var_Change( p_input, "position", VLC_VAR_SETVALUE, &newval, NULL ); - 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" ) ) - { - vlc_value_t t; - var_Set( p_input, "position-offset", val ); - - var_Get( p_input, "time", &t ); - t.i_time += newval.i_time; - var_Change( p_input, "time", VLC_VAR_SETVALUE, &t, NULL ); - } - else - { - var_Set( p_input, "position", val ); - var_Change( p_input, "time", VLC_VAR_SETVALUE, &newval, NULL ); - } - } - 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; -} - -static int BookmarkCallback( 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; - return input_Control( p_input, INPUT_SET_BOOKMARK, newval ); -} diff --git a/src/input/input_ext-plugins.c b/src/input/input_ext-plugins.c deleted file mode 100644 index c0ee28c80488509582f7218707077dea88bfe464..0000000000000000000000000000000000000000 --- a/src/input/input_ext-plugins.c +++ /dev/null @@ -1,658 +0,0 @@ -/***************************************************************************** - * input_ext-plugins.c: useful functions for access and demux plug-ins - ***************************************************************************** - * Copyright (C) 2001-2004 VideoLAN - * $Id$ - * - * Authors: Christophe Massiot - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. - *****************************************************************************/ - -/***************************************************************************** - * Preamble - *****************************************************************************/ -#include -#include - -#include - -#include "stream_control.h" -#include "input_ext-intf.h" -#include "input_ext-dec.h" -#include "input_ext-plugins.h" - - -/* - * Buffers management : internal functions - * - * All functions are static, but exported versions with mutex protection - * start with input_*. Not all of these exported functions are actually used, - * but they are included here for completeness. - */ - -#define BUFFERS_CACHE_SIZE 500 -#define DATA_CACHE_SIZE 1000 -#define PES_CACHE_SIZE 1000 - -/***************************************************************************** - * data_buffer_t: shared data type - *****************************************************************************/ -struct data_buffer_t -{ - data_buffer_t * p_next; - - /* number of data packets this buffer is referenced from - when it falls - * down to 0, the buffer is freed */ - int i_refcount; - - /* size of the current buffer (starting right after this byte) */ - size_t i_size; -}; - -/***************************************************************************** - * input_buffers_t: defines a LIFO per data type to keep - *****************************************************************************/ -#define PACKETS_LIFO( TYPE, NAME ) \ -struct \ -{ \ - TYPE * p_stack; \ - unsigned int i_depth; \ -} NAME; - -struct input_buffers_t -{ - vlc_mutex_t lock; - PACKETS_LIFO( pes_packet_t, pes ) - PACKETS_LIFO( data_packet_t, data ) - PACKETS_LIFO( data_buffer_t, buffers ) - size_t i_allocated; -}; - - -/***************************************************************************** - * input_BuffersInit: initialize the cache structures, return a pointer to it - *****************************************************************************/ -void * __input_BuffersInit( vlc_object_t *p_this ) -{ - input_buffers_t * p_buffers = malloc( sizeof( input_buffers_t ) ); - - if( p_buffers == NULL ) - { - return NULL; - } - - memset( p_buffers, 0, sizeof( input_buffers_t ) ); - vlc_mutex_init( p_this, &p_buffers->lock ); - - return p_buffers; -} - -/***************************************************************************** - * input_BuffersEnd: free all cached structures - *****************************************************************************/ -#define BUFFERS_END_PACKETS_LOOP \ - while( p_packet != NULL ) \ - { \ - p_next = p_packet->p_next; \ - free( p_packet ); \ - p_packet = p_next; \ - } - -void input_BuffersEnd( input_thread_t * p_input, input_buffers_t * p_buffers ) -{ - if( p_buffers != NULL ) - { - msg_Dbg( p_input, "pes: %d packets", p_buffers->pes.i_depth ); - msg_Dbg( p_input, "data: %d packets", p_buffers->data.i_depth ); - msg_Dbg( p_input, "buffers: %d packets", p_buffers->buffers.i_depth ); - - { - /* Free PES */ - pes_packet_t * p_next, * p_packet = p_buffers->pes.p_stack; - BUFFERS_END_PACKETS_LOOP; - } - - { - /* Free data packets */ - data_packet_t * p_next, * p_packet = p_buffers->data.p_stack; - BUFFERS_END_PACKETS_LOOP; - } - - { - /* Free buffers */ - data_buffer_t * p_next, * p_buf = p_buffers->buffers.p_stack; - while( p_buf != NULL ) - { - p_next = p_buf->p_next; - p_buffers->i_allocated -= p_buf->i_size; - free( p_buf ); - p_buf = p_next; - } - } - - if( p_buffers->i_allocated ) - { - msg_Warn( p_input, "%u bytes have not been freed, " - "expect memory leak", p_buffers->i_allocated ); - } - - vlc_mutex_destroy( &p_buffers->lock ); - free( p_buffers ); - } -} - -/***************************************************************************** - * input_NewBuffer: return a pointer to a data buffer of the appropriate size - *****************************************************************************/ -static inline data_buffer_t * NewBuffer( input_buffers_t * p_buffers, - size_t i_size ) -{ - data_buffer_t * p_buf; - - /* Safety check */ - if( p_buffers->i_allocated > INPUT_MAX_ALLOCATION ) - { - return NULL; - } - - if( p_buffers->buffers.p_stack != NULL ) - { - /* Take the buffer from the cache */ - p_buf = p_buffers->buffers.p_stack; - p_buffers->buffers.p_stack = p_buf->p_next; - p_buffers->buffers.i_depth--; - - /* Reallocate the packet if it is too small or too large */ - if( p_buf->i_size < i_size || p_buf->i_size > 3 * i_size ) - { - p_buffers->i_allocated -= p_buf->i_size; - free( p_buf ); - p_buf = malloc( sizeof(input_buffers_t) + i_size ); - if( p_buf == NULL ) - { - return NULL; - } - p_buf->i_size = i_size; - p_buffers->i_allocated += i_size; - } - } - else - { - /* Allocate a new buffer */ - p_buf = malloc( sizeof(input_buffers_t) + i_size ); - if( p_buf == NULL ) - { - return NULL; - } - p_buf->i_size = i_size; - p_buffers->i_allocated += i_size; - } - - /* Initialize data */ - p_buf->p_next = NULL; - p_buf->i_refcount = 0; - - return p_buf; -} - -data_buffer_t * input_NewBuffer( input_buffers_t * p_buffers, size_t i_size ) -{ - data_buffer_t * p_buf; - - vlc_mutex_lock( &p_buffers->lock ); - p_buf = NewBuffer( p_buffers, i_size ); - vlc_mutex_unlock( &p_buffers->lock ); - - return p_buf; -} - -/***************************************************************************** - * input_ReleaseBuffer: put a buffer back into the cache - *****************************************************************************/ -static inline void ReleaseBuffer( input_buffers_t * p_buffers, - data_buffer_t * p_buf ) -{ - /* Decrement refcount */ - if( --p_buf->i_refcount > 0 ) - { - return; - } - - if( p_buffers->buffers.i_depth < BUFFERS_CACHE_SIZE ) - { - /* Cache not full : store the buffer in it */ - p_buf->p_next = p_buffers->buffers.p_stack; - p_buffers->buffers.p_stack = p_buf; - p_buffers->buffers.i_depth++; - } - else - { - p_buffers->i_allocated -= p_buf->i_size; - free( p_buf ); - } -} - -void input_ReleaseBuffer( input_buffers_t * p_buffers, data_buffer_t * p_buf ) -{ - vlc_mutex_lock( &p_buffers->lock ); - ReleaseBuffer( p_buffers, p_buf ); - vlc_mutex_unlock( &p_buffers->lock ); -} - -/***************************************************************************** - * input_ShareBuffer: allocate a data_packet_t pointing to a given buffer - *****************************************************************************/ -static inline data_packet_t * ShareBuffer( input_buffers_t * p_buffers, - data_buffer_t * p_buf ) -{ - data_packet_t * p_data; - - if( p_buffers->data.p_stack != NULL ) - { - /* Take the packet from the cache */ - p_data = p_buffers->data.p_stack; - p_buffers->data.p_stack = p_data->p_next; - p_buffers->data.i_depth--; - } - else - { - /* Allocate a new packet */ - p_data = malloc( sizeof(data_packet_t) ); - if( p_data == NULL ) - { - return NULL; - } - } - - p_data->p_buffer = p_buf; - p_data->p_next = NULL; - p_data->b_discard_payload = 0; - p_data->p_payload_start = p_data->p_demux_start - = (byte_t *)p_buf + sizeof(input_buffers_t); - p_data->p_payload_end = p_data->p_demux_start + p_buf->i_size; - p_buf->i_refcount++; - - return p_data; -} - -data_packet_t * input_ShareBuffer( input_buffers_t * p_buffers, - data_buffer_t * p_buf ) -{ - data_packet_t * p_data; - - vlc_mutex_lock( &p_buffers->lock ); - p_data = ShareBuffer( p_buffers, p_buf ); - vlc_mutex_unlock( &p_buffers->lock ); - - return p_data; -} - -/***************************************************************************** - * input_NewPacket: allocate a packet along with a buffer - *****************************************************************************/ -static inline data_packet_t * NewPacket( input_buffers_t * p_buffers, - size_t i_size ) -{ - data_buffer_t * p_buf; - data_packet_t * p_data; - - p_buf = NewBuffer( p_buffers, i_size ); - - if( p_buf == NULL ) - { - return NULL; - } - - p_data = ShareBuffer( p_buffers, p_buf ); - if( p_data == NULL ) - { - ReleaseBuffer( p_buffers, p_buf ); - } - return p_data; -} - -data_packet_t * input_NewPacket( input_buffers_t * p_buffers, size_t i_size ) -{ - data_packet_t * p_data; - - vlc_mutex_lock( &p_buffers->lock ); - p_data = NewPacket( p_buffers, i_size ); - vlc_mutex_unlock( &p_buffers->lock ); - - return p_data; -} - -/***************************************************************************** - * input_DeletePacket: deallocate a packet and its buffers - *****************************************************************************/ -static inline void DeletePacket( input_buffers_t * p_buffers, - data_packet_t * p_data ) -{ - while( p_data != NULL ) - { - data_packet_t * p_next = p_data->p_next; - - ReleaseBuffer( p_buffers, p_data->p_buffer ); - - if( p_buffers->data.i_depth < DATA_CACHE_SIZE ) - { - /* Cache not full : store the packet in it */ - p_data->p_next = p_buffers->data.p_stack; - p_buffers->data.p_stack = p_data; - p_buffers->data.i_depth++; - } - else - { - free( p_data ); - } - - p_data = p_next; - } -} - -void input_DeletePacket( input_buffers_t * p_buffers, data_packet_t * p_data ) -{ - vlc_mutex_lock( &p_buffers->lock ); - DeletePacket( p_buffers, p_data ); - vlc_mutex_unlock( &p_buffers->lock ); -} - -/***************************************************************************** - * input_NewPES: return a pointer to a new PES packet - *****************************************************************************/ -static inline pes_packet_t * NewPES( input_buffers_t * p_buffers ) -{ - pes_packet_t * p_pes; - - if( p_buffers->pes.p_stack != NULL ) - { - /* Take the packet from the cache */ - p_pes = p_buffers->pes.p_stack; - p_buffers->pes.p_stack = p_pes->p_next; - p_buffers->pes.i_depth--; - } - else - { - /* Allocate a new packet */ - p_pes = malloc( sizeof(pes_packet_t) ); - if( p_pes == NULL ) - { - return NULL; - } - } - - p_pes->p_next = NULL; - p_pes->b_data_alignment = p_pes->b_discontinuity = VLC_FALSE; - p_pes->i_pts = p_pes->i_dts = 0; - p_pes->p_first = p_pes->p_last = NULL; - p_pes->i_pes_size = 0; - p_pes->i_nb_data = 0; - - return p_pes; -} - -pes_packet_t * input_NewPES( input_buffers_t * p_buffers ) -{ - pes_packet_t * p_pes; - - vlc_mutex_lock( &p_buffers->lock ); - p_pes = NewPES( p_buffers ); - vlc_mutex_unlock( &p_buffers->lock ); - - return p_pes; -} - -/***************************************************************************** - * input_DeletePES: put a pes and all data packets and all buffers back into - * the cache - *****************************************************************************/ -static inline void DeletePES( input_buffers_t * p_buffers, - pes_packet_t * p_pes ) -{ - while( p_pes != NULL ) - { - pes_packet_t * p_next = p_pes->p_next; - - /* Delete all data packets */ - if( p_pes->p_first != NULL ) - { - DeletePacket( p_buffers, p_pes->p_first ); - } - - if( p_buffers->pes.i_depth < PES_CACHE_SIZE ) - { - /* Cache not full : store the packet in it */ - p_pes->p_next = p_buffers->pes.p_stack; - p_buffers->pes.p_stack = p_pes; - p_buffers->pes.i_depth++; - } - else - { - free( p_pes ); - } - - p_pes = p_next; - } -} - -void input_DeletePES( input_buffers_t * p_buffers, pes_packet_t * p_pes ) -{ - vlc_mutex_lock( &p_buffers->lock ); - DeletePES( p_buffers, p_pes ); - vlc_mutex_unlock( &p_buffers->lock ); -} - - -/* - * Buffers management : external functions - * - * These functions make the glu between the access plug-in (pf_read) and - * the demux plug-in (pf_demux). We fill in a large buffer (approx. 10s kB) - * with a call to pf_read, then allow the demux plug-in to have a peep at - * it (input_Peek), and to split it in data_packet_t (input_SplitBuffer). - */ -/***************************************************************************** - * input_FillBuffer: fill in p_data_buffer with data from pf_read - *****************************************************************************/ -ssize_t input_FillBuffer( input_thread_t * p_input ) -{ - ptrdiff_t i_remains = p_input->p_last_data - p_input->p_current_data; - data_buffer_t * p_buf = NULL; - ssize_t i_ret; - - vlc_mutex_lock( &p_input->p_method_data->lock ); - - while( p_buf == NULL ) - { - p_buf = NewBuffer( p_input->p_method_data, - i_remains + p_input->i_bufsize ); - if( p_buf == NULL ) - { - vlc_mutex_unlock( &p_input->p_method_data->lock ); - msg_Err( p_input, - "failed allocating a new buffer (decoder stuck?)" ); - msleep( INPUT_IDLE_SLEEP ); - - if( p_input->b_die || p_input->b_error || p_input->b_eof ) - { - return -1; - } - vlc_mutex_lock( &p_input->p_method_data->lock ); - } - } - - p_buf->i_refcount = 1; - - if( p_input->p_data_buffer != NULL ) - { - if( i_remains ) - { - p_input->p_vlc->pf_memcpy( (byte_t *)p_buf + sizeof(data_buffer_t), - p_input->p_current_data, - (size_t)i_remains ); - } - ReleaseBuffer( p_input->p_method_data, p_input->p_data_buffer ); - } - - p_input->p_data_buffer = p_buf; - p_input->p_current_data = (byte_t *)p_buf + sizeof(data_buffer_t); - p_input->p_last_data = p_input->p_current_data + i_remains; - - /* Do not hold the lock during pf_read (blocking call). */ - vlc_mutex_unlock( &p_input->p_method_data->lock ); - - i_ret = p_input->pf_read( p_input, - (byte_t *)p_buf + sizeof(data_buffer_t) - + i_remains, - p_input->i_bufsize ); - if( i_ret < 0 && i_remains == 0 ) - { - /* Our internal buffers are empty, we can signal the error */ - return -1; - } - - if( i_ret < 0 ) i_ret = 0; - - p_input->p_last_data += i_ret; - - return (ssize_t)i_remains + i_ret; -} - -/***************************************************************************** - * input_Peek: give a pointer to the next available bytes in the buffer - * (min. i_size bytes) - * Returns the number of bytes read, or -1 in case of error - *****************************************************************************/ -ssize_t input_Peek( input_thread_t * p_input, byte_t ** pp_byte, - size_t i_size ) -{ - ssize_t i_data = p_input->p_last_data - p_input->p_current_data; - - while( i_data < (ssize_t)i_size ) - { - /* Go to the next buffer */ - ssize_t i_ret = input_FillBuffer( p_input ); - - if( i_ret < 0 ) - { - return -1; - } - - if( i_ret == i_data ) - { - /* We didn't get anymore data, must be the EOF */ - i_size = i_data; - break; - } - - i_data = i_ret; - } - - *pp_byte = p_input->p_current_data; - return i_size; -} - -/***************************************************************************** - * input_SplitBuffer: give a pointer to a data packet containing i_size bytes - * Returns the number of bytes read, or -1 in case of error - *****************************************************************************/ -ssize_t input_SplitBuffer( input_thread_t * p_input, - data_packet_t ** pp_data, size_t i_size ) -{ - ssize_t i_data = p_input->p_last_data - p_input->p_current_data; - - while( i_data < (ssize_t)i_size ) - { - /* Go to the next buffer */ - ssize_t i_ret = input_FillBuffer( p_input ); - - if( i_ret < 0 ) - { - return -1; - } - - if( i_ret == i_data ) - { - /* We didn't get anymore data, must be the EOF */ - i_size = i_data; - break; - } - - i_data = i_ret; - } - - if( i_size < 0) - { - return 0; - } - - *pp_data = input_ShareBuffer( p_input->p_method_data, - p_input->p_data_buffer ); - - (*pp_data)->p_demux_start = (*pp_data)->p_payload_start - = p_input->p_current_data; - (*pp_data)->p_payload_end = (*pp_data)->p_demux_start + i_size; - - p_input->p_current_data += i_size; - - /* Update stream position */ - vlc_mutex_lock( &p_input->stream.stream_lock ); - p_input->stream.p_selected_area->i_tell += i_size; - vlc_mutex_unlock( &p_input->stream.stream_lock ); - - return i_size; -} - -/***************************************************************************** - * input_AccessInit: initialize access plug-in wrapper structures - *****************************************************************************/ -int input_AccessInit( input_thread_t * p_input ) -{ - p_input->p_method_data = input_BuffersInit( p_input ); - if( p_input->p_method_data == NULL ) return -1; - p_input->p_data_buffer = NULL; - p_input->p_current_data = NULL; - p_input->p_last_data = NULL; - return 0; -} - -/***************************************************************************** - * input_AccessReinit: reinit structures before a random seek - *****************************************************************************/ -void input_AccessReinit( input_thread_t * p_input ) -{ - if( p_input->p_data_buffer != NULL ) - { - ReleaseBuffer( p_input->p_method_data, p_input->p_data_buffer ); - } - p_input->p_data_buffer = NULL; - p_input->p_current_data = NULL; - p_input->p_last_data = NULL; -} - -/***************************************************************************** - * input_AccessEnd: free access plug-in wrapper structures - *****************************************************************************/ -void input_AccessEnd( input_thread_t * p_input ) -{ - if( p_input->p_data_buffer != NULL ) - { - ReleaseBuffer( p_input->p_method_data, p_input->p_data_buffer ); - } - - input_BuffersEnd( p_input, p_input->p_method_data ); -} - diff --git a/src/input/input_programs.c b/src/input/input_programs.c deleted file mode 100644 index fd7ad40f3e5b671116511584785d8aab7a8fab77..0000000000000000000000000000000000000000 --- a/src/input/input_programs.c +++ /dev/null @@ -1,1268 +0,0 @@ -/***************************************************************************** - * input_programs.c: es_descriptor_t, pgrm_descriptor_t management - ***************************************************************************** - * Copyright (C) 1999-2004 VideoLAN - * $Id$ - * - * Authors: Christophe Massiot - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. - *****************************************************************************/ - -/***************************************************************************** - * Preamble - *****************************************************************************/ -#include -#include /* memcpy(), memset() */ - -#include - -#include "stream_control.h" -#include "input_ext-intf.h" -#include "input_ext-dec.h" -#include "input_ext-plugins.h" - -/* - * NOTICE : all of these functions expect you to have taken the lock on - * p_input->stream.lock - */ - -/* Navigation callbacks */ -static int ProgramCallback( vlc_object_t *, char const *, - vlc_value_t, vlc_value_t, void * ); -static int TitleCallback( vlc_object_t *, char const *, - vlc_value_t, vlc_value_t, void * ); -static int ChapterCallback( vlc_object_t *, char const *, - vlc_value_t, vlc_value_t, void * ); -static int NavigationCallback( vlc_object_t *, char const *, - vlc_value_t, vlc_value_t, void * ); -static int ESCallback( vlc_object_t *, char const *, - vlc_value_t, vlc_value_t, void * ); - -/***************************************************************************** - * input_InitStream: init the stream descriptor of the given input - *****************************************************************************/ -int input_InitStream( input_thread_t * p_input, size_t i_data_len ) -{ - vlc_value_t text,val; - - p_input->stream.i_stream_id = 0; - - /* initialized to 0 since we don't give the signal to the interface - * before the end of input initialization */ - p_input->stream.b_changed = 0; - p_input->stream.pp_es = NULL; - p_input->stream.pp_selected_es = NULL; - p_input->stream.p_removed_es = NULL; - p_input->stream.p_newly_selected_es = NULL; - p_input->stream.i_pgrm_number = 0; - p_input->stream.pp_programs = NULL; - p_input->stream.p_selected_program = NULL; - p_input->stream.p_new_program = NULL; - - if( i_data_len ) - { - if ( (p_input->stream.p_demux_data = malloc( i_data_len )) == NULL ) - { - msg_Err( p_input, "out of memory" ); - return 1; - } - memset( p_input->stream.p_demux_data, 0, i_data_len ); - } - else - { - p_input->stream.p_demux_data = NULL; - } - - var_Create( p_input, "intf-change", VLC_VAR_BOOL ); - val.b_bool = VLC_TRUE; - var_Set( p_input, "intf-change", val ); - - /* Create a few object variables used for navigation in the interfaces */ - var_Create( p_input, "program", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE | - VLC_VAR_DOINHERIT ); - var_Get( p_input, "program", &val ); - if( val.i_int <= 0 ) - var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL ); - text.psz_string = _("Program"); - var_Change( p_input, "program", VLC_VAR_SETTEXT, &text, NULL ); - - var_Create( p_input, "title", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); - text.psz_string = _("Title"); - var_Change( p_input, "title", VLC_VAR_SETTEXT, &text, NULL ); - - var_Create( p_input, "chapter", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); - text.psz_string = _("Chapter"); - var_Change( p_input, "chapter", VLC_VAR_SETTEXT, &text, NULL ); - - var_Create( p_input, "navigation", VLC_VAR_VARIABLE | VLC_VAR_HASCHOICE ); - text.psz_string = _("Navigation"); - var_Change( p_input, "navigation", VLC_VAR_SETTEXT, &text, NULL ); - - var_Create( p_input, "video-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); - text.psz_string = _("Video Track"); - var_Change( p_input, "video-es", VLC_VAR_SETTEXT, &text, NULL ); - var_Create( p_input, "audio-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); - text.psz_string = _("Audio Track"); - var_Change( p_input, "audio-es", VLC_VAR_SETTEXT, &text, NULL ); - var_Create( p_input, "spu-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); - text.psz_string = _("Subtitles Track"); - var_Change( p_input, "spu-es", VLC_VAR_SETTEXT, &text, NULL ); - - var_AddCallback( p_input, "program", ProgramCallback, NULL ); - var_AddCallback( p_input, "title", TitleCallback, NULL ); - var_AddCallback( p_input, "chapter", ChapterCallback, NULL ); - var_AddCallback( p_input, "video-es", ESCallback, NULL ); - var_AddCallback( p_input, "audio-es", ESCallback, NULL ); - var_AddCallback( p_input, "spu-es", ESCallback, NULL ); - - return VLC_SUCCESS; -} - -/***************************************************************************** - * input_EndStream: free all stream descriptors - *****************************************************************************/ -void input_EndStream( input_thread_t * p_input ) -{ - vlc_mutex_lock( &p_input->stream.stream_lock ); - - /* Free all programs and associated ES, and associated decoders. */ - while( p_input->stream.i_pgrm_number ) - { - input_DelProgram( p_input, p_input->stream.pp_programs[0] ); - } - - /* Free standalone ES */ - while( p_input->stream.i_es_number ) - { - input_DelES( p_input, p_input->stream.pp_es[0] ); - } - - /* Free all areas */ - while( p_input->stream.i_area_nb ) - { - input_DelArea( p_input, p_input->stream.pp_areas[0] ); - } - - /* Free selected ES */ - if( p_input->stream.pp_selected_es != NULL ) - { - free( p_input->stream.pp_selected_es ); - } - - if( p_input->stream.p_demux_data != NULL ) - { - free( p_input->stream.p_demux_data ); - } - - vlc_mutex_unlock( &p_input->stream.stream_lock ); - - /* Free navigation variables */ - var_Destroy( p_input, "program" ); - var_Destroy( p_input, "title" ); - var_Destroy( p_input, "chapter" ); - var_Destroy( p_input, "video-es" ); - var_Destroy( p_input, "audio-es" ); - var_Destroy( p_input, "spu-es" ); - var_Destroy( p_input, "intf-change" ); -} - -/***************************************************************************** - * input_FindProgram: returns a pointer to a program described by its ID - *****************************************************************************/ -pgrm_descriptor_t * input_FindProgram( input_thread_t * p_input, - uint16_t i_pgrm_id ) -{ - unsigned int i; - - for( i = 0; i < p_input->stream.i_pgrm_number; i++ ) - { - if( p_input->stream.pp_programs[i]->i_number == i_pgrm_id ) - { - return p_input->stream.pp_programs[i]; - } - } - - return NULL; -} - -/***************************************************************************** - * input_AddProgram: add and init a program descriptor - ***************************************************************************** - * This program descriptor will be referenced in the given stream descriptor - *****************************************************************************/ -pgrm_descriptor_t * input_AddProgram( input_thread_t * p_input, - uint16_t i_pgrm_id, size_t i_data_len ) -{ - /* Where to add the pgrm */ - pgrm_descriptor_t * p_pgrm = malloc( sizeof(pgrm_descriptor_t) ); - vlc_value_t val; - - if( p_pgrm == NULL ) - { - msg_Err( p_input, "out of memory" ); - return NULL; - } - - /* Init this entry */ - p_pgrm->i_number = i_pgrm_id; - p_pgrm->b_is_ok = 0; - p_pgrm->i_version = 0; - - p_pgrm->i_es_number = 0; - p_pgrm->pp_es = NULL; - - input_ClockInit( p_pgrm ); - - p_pgrm->i_synchro_state = SYNCHRO_START; - - if( i_data_len ) - { - p_pgrm->p_demux_data = malloc( i_data_len ); - if( p_pgrm->p_demux_data == NULL ) - { - msg_Err( p_input, "out of memory" ); - return NULL; - } - memset( p_pgrm->p_demux_data, 0, i_data_len ); - } - else - { - p_pgrm->p_demux_data = NULL; - } - - /* Add an entry to the list of program associated with the stream */ - INSERT_ELEM( p_input->stream.pp_programs, - p_input->stream.i_pgrm_number, - p_input->stream.i_pgrm_number, - p_pgrm ); - - val.i_int = i_pgrm_id; - var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, NULL ); - - return p_pgrm; -} - -/***************************************************************************** - * input_DelProgram: destroy a program descriptor - ***************************************************************************** - * All ES descriptions referenced in the descriptor will be deleted. - *****************************************************************************/ -void input_DelProgram( input_thread_t * p_input, pgrm_descriptor_t * p_pgrm ) -{ - unsigned int i_pgrm_index; - vlc_value_t val; - - /* Find the program in the programs table */ - for( i_pgrm_index = 0; i_pgrm_index < p_input->stream.i_pgrm_number; - i_pgrm_index++ ) - { - if( p_input->stream.pp_programs[i_pgrm_index] == p_pgrm ) - break; - } - - /* If the program wasn't found, do nothing */ - if( i_pgrm_index == p_input->stream.i_pgrm_number ) - { - msg_Err( p_input, "program does not belong to this input" ); - return; - } - - val.i_int = p_input->stream.pp_programs[i_pgrm_index]->i_number; - var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL ); - - /* Free the structures that describe the es that belongs to that program */ - while( p_pgrm->i_es_number ) - { - input_DelES( p_input, p_pgrm->pp_es[0] ); - } - - /* Free the demux data */ - if( p_pgrm->p_demux_data != NULL ) - { - free( p_pgrm->p_demux_data ); - } - - /* Remove this program from the stream's list of programs */ - REMOVE_ELEM( p_input->stream.pp_programs, - p_input->stream.i_pgrm_number, - i_pgrm_index ); - - if( p_pgrm == p_input->stream.p_selected_program ) - p_input->stream.p_selected_program = NULL; - - /* Free the description of this program */ - free( p_pgrm ); -} - -/**************************************************************************** - * input_ChangeProgram: interface request a program change (internal) - ****************************************************************************/ -static int input_ChangeProgram( input_thread_t * p_input, uint16_t i_program_number ) -{ - pgrm_descriptor_t * p_program; - vlc_value_t val; - - vlc_mutex_lock( &p_input->stream.stream_lock ); - - p_program = input_FindProgram( p_input, i_program_number ); - - if ( p_program == NULL ) - { - msg_Err( p_input, "could not find selected program" ); - return -1; - } - - p_input->stream.p_new_program = p_program; - - vlc_mutex_unlock( &p_input->stream.stream_lock ); - - /* Update the navigation variables without triggering a callback */ - val.i_int = i_program_number; - var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL ); - - return 0; -} - -/***************************************************************************** - * input_AddArea: add and init an area descriptor - ***************************************************************************** - * This area descriptor will be referenced in the given stream descriptor - *****************************************************************************/ -input_area_t * input_AddArea( input_thread_t * p_input, - uint16_t i_area_id, uint16_t i_part_nb ) -{ - /* Where to add the pgrm */ - input_area_t * p_area = malloc( sizeof(input_area_t) ); - vlc_value_t val; - int i; - - if( p_area == NULL ) - { - msg_Err( p_input, "out of memory" ); - return NULL; - } - - /* Init this entry */ - p_area->i_id = i_area_id; - p_area->i_part_nb = i_part_nb; - p_area->i_part= 0; - p_area->i_start = 0; - p_area->i_size = 0; - p_area->i_tell = 0; - p_area->i_seek = NO_SEEK; - - /* Add an entry to the list of program associated with the stream */ - INSERT_ELEM( p_input->stream.pp_areas, - p_input->stream.i_area_nb, - p_input->stream.i_area_nb, - p_area ); - - /* Don't add empty areas */ - if( i_part_nb == 0 ) - return NULL; - - /* Take care of the navigation variables */ - val.i_int = i_area_id; - var_Change( p_input, "title", VLC_VAR_ADDCHOICE, &val, NULL ); - - val.psz_string = malloc( sizeof("title ") + 5 ); - if( val.psz_string ) - { - vlc_value_t val2, text, text2; - - sprintf( val.psz_string, "title %2i", i_area_id ); - var_Destroy( p_input, val.psz_string ); - var_Create( p_input, val.psz_string, VLC_VAR_INTEGER | - VLC_VAR_HASCHOICE | VLC_VAR_ISCOMMAND ); - var_AddCallback( p_input, val.psz_string, NavigationCallback, - (void *)(int)i_area_id ); - - text.psz_string = malloc( strlen( _("Title %i") ) + 20 ); - if( text.psz_string ) - sprintf( text.psz_string, _("Title %i"), i_area_id ); - - var_Change( p_input, "navigation", VLC_VAR_ADDCHOICE, &val, &text ); - - if( text.psz_string ) free( text.psz_string ); - - text2.psz_string = malloc( strlen( _("Chapter %i") ) + 20 ); - - for( i = 1; i <= i_part_nb; i++ ) - { - val2.i_int = i; - - if( text2.psz_string ) - sprintf( text2.psz_string, _("Chapter %i"), i ); - - var_Change( p_input, val.psz_string, - VLC_VAR_ADDCHOICE, &val2, &text2 ); - } - - if( text2.psz_string ) free( text2.psz_string ); - free( val.psz_string ); - } - - if( p_input->stream.i_area_nb == 2 ) - { - vlc_value_t text; - - /* Add another bunch of navigation object variables */ - var_Create( p_input, "next-title", VLC_VAR_VOID ); - text.psz_string = _("Next title"); - var_Change( p_input, "next-title", VLC_VAR_SETTEXT, &text, NULL ); - var_Create( p_input, "prev-title", VLC_VAR_VOID ); - text.psz_string = _("Previous title"); - var_Change( p_input, "prev-title", VLC_VAR_SETTEXT, &text, NULL ); - var_AddCallback( p_input, "next-title", TitleCallback, NULL ); - var_AddCallback( p_input, "prev-title", TitleCallback, NULL ); - - var_Create( p_input, "next-chapter", VLC_VAR_VOID ); - text.psz_string = _("Next chapter"); - var_Change( p_input, "next-chapter", VLC_VAR_SETTEXT, &text, NULL ); - var_Create( p_input, "prev-chapter", VLC_VAR_VOID ); - text.psz_string = _("Previous chapter"); - var_Change( p_input, "prev-chapter", VLC_VAR_SETTEXT, &text, NULL ); - var_AddCallback( p_input, "next-chapter", ChapterCallback, NULL ); - var_AddCallback( p_input, "prev-chapter", ChapterCallback, NULL ); - } - - return p_area; -} - -/***************************************************************************** - * input_SetProgram: changes the current program - *****************************************************************************/ -int input_SetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_new_prg ) -{ - unsigned int i_es_index; - int i_required_audio_es; - int i_required_spu_es; - int i_audio_es = 0; - int i_spu_es = 0; - vlc_value_t val; - - if ( p_input->stream.p_selected_program ) - { - for ( i_es_index = 1 ; /* 0 should be the PMT */ - i_es_index < p_input->stream.p_selected_program-> - i_es_number ; - i_es_index ++ ) - { -#define p_es p_input->stream.p_selected_program->pp_es[i_es_index] - if ( p_es->p_dec ) /* if the ES was selected */ - { - input_UnselectES( p_input , p_es ); - } -#undef p_es - } - } - - /* Get the number of the required audio stream */ - var_Get( p_input, "audio", &val ); - if( val.b_bool ) - { - /* Default is the first one */ - var_Get( p_input, "audio-channel", &val ); - i_required_audio_es = val.i_int; - if( i_required_audio_es < 0 ) - { - i_required_audio_es = 1; - } - } - else - { - i_required_audio_es = 0; - } - - /* Same thing for subtitles */ - var_Get( p_input, "video", &val ); - if( val.b_bool ) - { - /* for spu, default is none */ - var_Get( p_input, "spu-channel", &val ); - i_required_spu_es = val.i_int; - if( i_required_spu_es < 0 ) - { - i_required_spu_es = 0; - } - } - else - { - i_required_spu_es = 0; - } - - for( i_es_index = 0 ; i_es_index < p_new_prg->i_es_number ; i_es_index ++ ) - { - switch( p_new_prg->pp_es[i_es_index]->i_cat ) - { - case VIDEO_ES: - msg_Dbg( p_input, "selecting video ES %x", - p_new_prg->pp_es[i_es_index]->i_id ); - input_SelectES( p_input, p_new_prg->pp_es[i_es_index] ); - break; - case AUDIO_ES: - i_audio_es += 1; - if( i_audio_es <= i_required_audio_es ) - { - msg_Dbg( p_input, "selecting audio ES %x", - p_new_prg->pp_es[i_es_index]->i_id ); - input_SelectES( p_input, p_new_prg->pp_es[i_es_index]); - } - break; - /* Not sure this one is fully specification-compliant */ - case SPU_ES : - i_spu_es += 1; - if( i_spu_es <= i_required_spu_es ) - { - msg_Dbg( p_input, "selecting spu ES %x", - p_new_prg->pp_es[i_es_index]->i_id ); - input_SelectES( p_input, p_new_prg->pp_es[i_es_index] ); - } - break; - default : - msg_Dbg( p_input, "ES %x has unknown type", - p_new_prg->pp_es[i_es_index]->i_id ); - break; - } - - } - - p_input->stream.p_selected_program = p_new_prg; - - /* Update the navigation variables without triggering a callback */ - val.i_int = p_new_prg->i_number; - var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL ); - - return( 0 ); -} - -/***************************************************************************** - * input_DelArea: destroy a area descriptor - ***************************************************************************** - * All ES descriptions referenced in the descriptor will be deleted. - *****************************************************************************/ -void input_DelArea( input_thread_t * p_input, input_area_t * p_area ) -{ - unsigned int i_area_index; - vlc_value_t val; - - /* Find the area in the areas table */ - for( i_area_index = 0; i_area_index < p_input->stream.i_area_nb; - i_area_index++ ) - { - if( p_input->stream.pp_areas[i_area_index] == p_area ) - break; - } - - /* If the area wasn't found, do nothing */ - if( i_area_index == p_input->stream.i_area_nb ) - { - msg_Err( p_input, "area does not belong to this input" ); - return; - } - - /* Take care of the navigation variables */ - val.psz_string = malloc( sizeof("title ") + 5 ); - if( val.psz_string ) - { - sprintf( val.psz_string, "title %i", p_area->i_id ); - var_Change( p_input, "navigation", VLC_VAR_DELCHOICE, &val, NULL ); - var_Destroy( p_input, val.psz_string ); - - free( val.psz_string ); - } - - /* Remove this area from the stream's list of areas */ - REMOVE_ELEM( p_input->stream.pp_areas, - p_input->stream.i_area_nb, - i_area_index ); - - /* Free the description of this area */ - free( p_area ); - - if( p_input->stream.i_area_nb == 1 ) - { - /* Del unneeded navigation object variables */ - var_Destroy( p_input, "next-title" ); - var_Destroy( p_input, "prev-title" ); - var_Destroy( p_input, "next-chapter" ); - var_Destroy( p_input, "prev-chapter" ); - } -} - -/**************************************************************************** - * input_ChangeArea: interface request an area change (internal) - ****************************************************************************/ -static int input_ChangeArea( input_thread_t * p_input, input_area_t * p_area ) -{ - vlc_mutex_lock( &p_input->stream.stream_lock ); - - p_input->stream.p_new_area = p_area; - - vlc_mutex_unlock( &p_input->stream.stream_lock ); - - return 0; -} - -/***************************************************************************** - * input_FindES: returns a pointer to an ES described by its ID - *****************************************************************************/ -es_descriptor_t * input_FindES( input_thread_t * p_input, uint16_t i_es_id ) -{ - unsigned int i; - - for( i = 0; i < p_input->stream.i_es_number; i++ ) - { - if( p_input->stream.pp_es[i]->i_id == i_es_id ) - { - return p_input->stream.pp_es[i]; - } - } - - return NULL; -} - -/***************************************************************************** - * input_AddES: - ***************************************************************************** - * Reserve a slot in the table of ES descriptors for the ES and add it to the - * list of ES of p_pgrm. If p_pgrm if NULL, then the ES is considered as stand - * alone (PSI ?) - *****************************************************************************/ -es_descriptor_t * input_AddES( input_thread_t * p_input, - pgrm_descriptor_t * p_pgrm, uint16_t i_es_id, - int i_category, char const *psz_desc, - size_t i_data_len ) -{ - es_descriptor_t * p_es; - vlc_value_t val, text; - char *psz_var = NULL; - - p_es = (es_descriptor_t *)malloc( sizeof(es_descriptor_t) ); - if( p_es == NULL ) - { - msg_Err( p_input, "out of memory" ); - return( NULL); - } - - INSERT_ELEM( p_input->stream.pp_es, - p_input->stream.i_es_number, - p_input->stream.i_es_number, - p_es ); - - /* Init its values */ - p_es->i_id = i_es_id; - p_es->i_stream_id = 0; - p_es->p_pes = NULL; - p_es->p_dec = NULL; - p_es->i_cat = i_category; - p_es->i_demux_fd = 0; - p_es->c_packets = 0; - p_es->c_invalid_packets = 0; - p_es->b_force_decoder = VLC_FALSE; - es_format_Init( &p_es->fmt, UNKNOWN_ES, 0 ); - p_es->fmt.b_packetized = VLC_FALSE; /* Only there for old mpeg demuxers */ - - if( i_data_len ) - { - p_es->p_demux_data = malloc( i_data_len ); - if( p_es->p_demux_data == NULL ) - { - msg_Err( p_input, "out of memory" ); - return( NULL ); - } - memset( p_es->p_demux_data, 0, i_data_len ); - } - else - { - p_es->p_demux_data = NULL; - } - p_es->p_waveformatex = NULL; - p_es->p_bitmapinfoheader = NULL; - p_es->p_spuinfo = NULL; - - /* Add this ES to the program definition if one is given */ - if( p_pgrm ) - { - INSERT_ELEM( p_pgrm->pp_es, - p_pgrm->i_es_number, - p_pgrm->i_es_number, - p_es ); - p_es->p_pgrm = p_pgrm; - } - else - { - p_es->p_pgrm = NULL; - } - - switch( i_category ) - { - case AUDIO_ES: - psz_var = "audio-es"; - break; - case SPU_ES: - psz_var = "spu-es"; - break; - case VIDEO_ES: - psz_var = "video-es"; - break; - } - - if( psz_var ) - { - /* Get the number of ES already added */ - var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL ); - if( val.i_int == 0 ) - { - vlc_value_t val2; - - /* First one, we need to add the "Disable" choice */ - val2.i_int = -1; text.psz_string = _("Disable"); - var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text ); - val.i_int++; - } - - /* Take care of the ES description */ - if( psz_desc && *psz_desc ) - { - p_es->psz_desc = strdup( psz_desc ); - } - else - { - p_es->psz_desc = malloc( strlen( _("Track %i") ) + 20 ); - if( p_es->psz_desc ) - sprintf( p_es->psz_desc, _("Track %i"), val.i_int ); - } - - val.i_int = p_es->i_id; - text.psz_string = p_es->psz_desc; - var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text ); - } - else p_es->psz_desc = NULL; - - return p_es; -} - -/***************************************************************************** - * input_DelES: - *****************************************************************************/ -void input_DelES( input_thread_t * p_input, es_descriptor_t * p_es ) -{ - unsigned int i_index, i_es_index; - pgrm_descriptor_t * p_pgrm; - char * psz_var = NULL; - vlc_value_t val; - - /* Find the ES in the ES table */ - for( i_es_index = 0; i_es_index < p_input->stream.i_es_number; - i_es_index++ ) - { - if( p_input->stream.pp_es[i_es_index] == p_es ) - break; - } - - /* If the ES wasn't found, do nothing */ - if( i_es_index == p_input->stream.i_es_number ) - { - msg_Err( p_input, "ES does not belong to this input" ); - return; - } - - /* Remove es from its associated variable */ - switch( p_es->i_cat ) - { - case AUDIO_ES: - psz_var = "audio-es"; - break; - case SPU_ES: - psz_var = "spu-es"; - break; - case VIDEO_ES: - psz_var = "video-es"; - break; - } - - if( psz_var ) - { - val.i_int = p_es->i_id; - var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL ); - - /* Remove the "Disable" entry if needed */ - var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL ); - if( val.i_int == 1 ) - { - val.i_int = -1; - var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL ); - } - } - - /* Kill associated decoder, if any. */ - if( p_es->p_dec != NULL ) - { - input_UnselectES( p_input, p_es ); - } - - /* Remove this ES from the description of the program if it is associated - * to one */ - p_pgrm = p_es->p_pgrm; - if( p_pgrm ) - { - for( i_index = 0; i_index < p_pgrm->i_es_number; i_index++ ) - { - if( p_pgrm->pp_es[i_index] == p_es ) - { - REMOVE_ELEM( p_pgrm->pp_es, - p_pgrm->i_es_number, - i_index ); - break; - } - } - } - - /* Free the demux data */ - if( p_es->p_demux_data != NULL ) - { - free( p_es->p_demux_data ); - } - if( p_es->p_waveformatex ) - { - free( p_es->p_waveformatex ); - } - if( p_es->p_bitmapinfoheader ) - { - free( p_es->p_bitmapinfoheader ); - } - if( p_es->p_spuinfo ) - { - free( p_es->p_spuinfo ); - } - - /* Free the description string */ - if( p_es->psz_desc != NULL ) - { - free( p_es->psz_desc ); - } - - /* Clean the es format */ - es_format_Clean( &p_es->fmt ); - - /* Find the ES in the ES table */ - for( i_es_index = 0; i_es_index < p_input->stream.i_es_number; - i_es_index++ ) - { - if( p_input->stream.pp_es[i_es_index] == p_es ) - break; - } - - /* Remove this ES from the stream's list of ES */ - REMOVE_ELEM( p_input->stream.pp_es, - p_input->stream.i_es_number, - i_es_index ); - - /* Free the ES */ - free( p_es ); -} - -/***************************************************************************** - * input_SelectES: selects an ES and spawns the associated decoder - ***************************************************************************** - * Remember we are still supposed to have stream_lock when entering this - * function ? - *****************************************************************************/ -int input_SelectES( input_thread_t * p_input, es_descriptor_t * p_es ) -{ - vlc_value_t val; - char *psz_var = NULL; - - if( p_es == NULL ) - { - msg_Err( p_input, "nothing to do in input_SelectES" ); - return -1; - } - - if( p_es->i_cat == VIDEO_ES || p_es->i_cat == SPU_ES ) - { - var_Get( p_input, "video", &val ); - if( val.b_bool && p_input->stream.p_sout ) - { - var_Get( p_input, "sout-video", &val ); - } - if( !val.b_bool ) - { - msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x", - p_es->i_id ); - return -1; - } - } - - if( p_es->i_cat == AUDIO_ES ) - { - var_Get( p_input, "audio", &val ); - if( val.b_bool && p_input->stream.p_sout ) - { - var_Get( p_input, "sout-audio", &val ); - } - if( !val.b_bool ) - { - msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x", - p_es->i_id ); - return -1; - } - } - - msg_Dbg( p_input, "selecting ES 0x%x", p_es->i_id ); - - if( p_es->p_dec != NULL ) - { - msg_Err( p_input, "ES 0x%x is already selected", p_es->i_id ); - return -1; - } - - /* Release the lock, not to block the input thread during - * the creation of the thread. */ - vlc_mutex_unlock( &p_input->stream.stream_lock ); - p_es->p_dec = input_RunDecoder( p_input, p_es ); - vlc_mutex_lock( &p_input->stream.stream_lock ); - - if( p_es->p_dec == NULL ) - { - return -1; - } - - /* Update the es variable without triggering a callback */ - switch( p_es->i_cat ) - { - case AUDIO_ES: - psz_var = "audio-es"; - break; - case SPU_ES: - psz_var = "spu-es"; - break; - case VIDEO_ES: - psz_var = "video-es"; - break; - } - - if( psz_var ) - { - val.i_int = p_es->i_id; - var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL ); - } - - return 0; -} - -/***************************************************************************** - * input_UnselectES: removes an ES from the list of selected ES - *****************************************************************************/ -int input_UnselectES( input_thread_t * p_input, es_descriptor_t * p_es ) -{ - unsigned int i_index = 0; - vlc_value_t val; - char *psz_var = NULL; - - if( p_es == NULL ) - { - msg_Err( p_input, "nothing to do in input_UnselectES" ); - return -1; - } - - msg_Dbg( p_input, "unselecting ES 0x%x", p_es->i_id ); - - if( p_es->p_dec == NULL ) - { - msg_Err( p_input, "ES 0x%x is not selected", p_es->i_id ); - return( -1 ); - } - - /* Update the es variable without triggering a callback */ - switch( p_es->i_cat ) - { - case AUDIO_ES: - psz_var = "audio-es"; - break; - case SPU_ES: - psz_var = "spu-es"; - break; - case VIDEO_ES: - psz_var = "video-es"; - break; - } - - if( psz_var ) - { - val.i_int = -1; - var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL ); - } - - /* FIXME: input_UnselectES() shouldn't actually be entered with the - * input lock, the locking should be done here and only where necessary. */ - vlc_mutex_unlock( &p_input->stream.stream_lock ); - /* Actually unselect the ES */ - input_EndDecoder( p_input, p_es ); - vlc_mutex_lock( &p_input->stream.stream_lock ); - - p_es->p_pes = NULL; - - if( ( p_es->p_dec == NULL ) && - ( p_input->stream.i_selected_es_number > 0 ) ) - { - while( ( i_index < p_input->stream.i_selected_es_number - 1 ) && - ( p_input->stream.pp_selected_es[i_index] != p_es ) ) - { - i_index++; - } - - /* XXX: no need to memmove, we have unsorted data */ - REMOVE_ELEM( p_input->stream.pp_selected_es, - p_input->stream.i_selected_es_number, - i_index ); - - if( p_input->stream.i_selected_es_number == 0 ) - { - msg_Dbg( p_input, "no more selected ES" ); - return 1; - } - } - - return 0; -} - -/***************************************************************************** - * Navigation callback: a bunch of navigation variables are used as an - * alternative to the navigation API. - *****************************************************************************/ -static int ProgramCallback( 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; - - if( oldval.i_int == newval.i_int ) - return VLC_SUCCESS; - - vlc_mutex_lock( &p_input->stream.stream_lock ); - if( ( newval.i_int > 0 ) ) - { - vlc_mutex_unlock( &p_input->stream.stream_lock ); - input_ChangeProgram( p_input, (uint16_t)newval.i_int ); - var_SetInteger( p_input, "state", PLAYING_S ); - vlc_mutex_lock( &p_input->stream.stream_lock ); - } - vlc_mutex_unlock( &p_input->stream.stream_lock ); - - val.b_bool = VLC_TRUE; - var_Set( p_input, "intf-change", val ); - - return VLC_SUCCESS; -} - -static int TitleCallback( 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; - input_area_t *p_area; - vlc_value_t val, val_list; - int i, i_step = 0; - - if( !strcmp( psz_cmd, "next-title" ) ) i_step++; - else if( !strcmp( psz_cmd, "prev-title" ) ) i_step--; - - if( !i_step && oldval.i_int == newval.i_int ) return VLC_SUCCESS; - - /* Sanity check should have already been done by var_Set(). */ - vlc_mutex_lock( &p_input->stream.stream_lock ); - - if( i_step ) - { - var_Get( p_this, "title", &newval ); - var_Change( p_this, "title", VLC_VAR_GETCHOICES, &val_list, NULL ); - for( i = 0; i < val_list.p_list->i_count; i++ ) - { - if( val_list.p_list->p_values[i].i_int == newval.i_int && - i + i_step >= 0 && i + i_step < val_list.p_list->i_count ) - { - newval.i_int = val_list.p_list->p_values[i + i_step].i_int; - break; - } - } - var_Change( p_this, "title", VLC_VAR_FREELIST, &val_list, NULL ); - } - - p_area = p_input->stream.pp_areas[newval.i_int]; - p_area->i_part = 1; - - vlc_mutex_unlock( &p_input->stream.stream_lock ); - - input_ChangeArea( p_input, p_area ); - var_SetInteger( p_input, "state", PLAYING_S ); - - val.b_bool = VLC_TRUE; - var_Set( p_input, "intf-change", val ); - - return VLC_SUCCESS; -} - -static int ChapterCallback( 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; - input_area_t *p_area; - vlc_value_t val, val_list; - int i, i_step = 0; - - if( !strcmp( psz_cmd, "next-chapter" ) ) i_step++; - else if( !strcmp( psz_cmd, "prev-chapter" ) ) i_step--; - - if( !i_step && oldval.i_int == newval.i_int ) return VLC_SUCCESS; - - /* Sanity check should have already been done by var_Set(). */ - vlc_mutex_lock( &p_input->stream.stream_lock ); - - if( i_step ) - { - var_Get( p_this, "chapter", &newval ); - var_Change( p_this, "chapter", VLC_VAR_GETCHOICES, &val_list, NULL ); - for( i = 0; i < val_list.p_list->i_count; i++ ) - { - if( val_list.p_list->p_values[i].i_int == newval.i_int && - i + i_step >= 0 && i + i_step < val_list.p_list->i_count ) - { - newval.i_int = val_list.p_list->p_values[i + i_step].i_int; - break; - } - } - var_Change( p_this, "chapter", VLC_VAR_FREELIST, &val_list, NULL ); - } - - p_area = p_input->stream.p_selected_area; - p_input->stream.p_selected_area->i_part = newval.i_int; - vlc_mutex_unlock( &p_input->stream.stream_lock ); - - input_ChangeArea( p_input, p_area ); - var_SetInteger( p_input, "state", PLAYING_S ); - - val.b_bool = VLC_TRUE; - var_Set( p_input, "intf-change", val ); - - return VLC_SUCCESS; -} - -static int NavigationCallback( 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; - uint16_t i_area_id = (int)p_data; - vlc_value_t val; - - vlc_mutex_lock( &p_input->stream.stream_lock ); - - if( p_input->stream.p_selected_area->i_id == i_area_id && - oldval.i_int == newval.i_int ) - { - /* Nothing to do */ - vlc_mutex_unlock( &p_input->stream.stream_lock ); - return VLC_SUCCESS; - } - - if( ( i_area_id < p_input->stream.i_area_nb ) && ( newval.i_int > 0 ) && - ( (uint16_t)newval.i_int <= - p_input->stream.pp_areas[i_area_id]->i_part_nb ) ) - { - input_area_t *p_area = p_input->stream.pp_areas[i_area_id]; - p_area->i_part = newval.i_int; - vlc_mutex_unlock( &p_input->stream.stream_lock ); - input_ChangeArea( p_input, p_area ); - var_SetInteger( p_input, "state", PLAYING_S ); - vlc_mutex_lock( &p_input->stream.stream_lock ); - } - vlc_mutex_unlock( &p_input->stream.stream_lock ); - - val.b_bool = VLC_TRUE; - var_Set( p_input, "intf-change", val ); - - return VLC_SUCCESS; -} - -static int ESCallback( 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; - unsigned int i; - vlc_value_t val; - unsigned int i_cat = UNKNOWN_ES; - es_descriptor_t *p_es = NULL; - - vlc_mutex_lock( &p_input->stream.stream_lock ); - - /* First search old es type */ - for( i = 0 ; i < p_input->stream.i_es_number ; i++ ) - { - if( p_input->stream.pp_es[i]->i_id == oldval.i_int ) - { - i_cat = p_input->stream.pp_es[i]->i_cat; - } - } - - /* Unselect all old ES */ - for( i = 0 ; i < p_input->stream.i_es_number ; i++ ) - { - if( p_input->stream.pp_es[i]->i_cat == i_cat && - p_input->stream.pp_es[i]->i_id != newval.i_int && - p_input->stream.pp_es[i]->p_dec != NULL ) - { - input_UnselectES( p_input, p_input->stream.pp_es[i] ); - } - } - - /* Select new ES */ - for( i = 0 ; i < p_input->stream.i_es_number ; i++ ) - { - if( p_input->stream.pp_es[i]->i_id == newval.i_int ) - { - p_es = p_input->stream.pp_es[i]; - if( p_es->p_dec == NULL ) - { - input_SelectES( p_input, p_es ); - } - } - } - - if( p_es ) - { - /* Fix value (mainly for multiple selected ES */ - val.i_int = p_es->i_id; - switch( p_es->i_cat ) - { - case AUDIO_ES: - var_Change( p_input, "audio-es", VLC_VAR_SETVALUE, &val, NULL ); - break; - case SPU_ES: - var_Change( p_input, "spu-es", VLC_VAR_SETVALUE, &val, NULL ); - break; - case VIDEO_ES: - var_Change( p_input, "video-es", VLC_VAR_SETVALUE, &val, NULL ); - break; - } - } - - vlc_mutex_unlock( &p_input->stream.stream_lock ); - - val.b_bool = VLC_TRUE; - var_Set( p_input, "intf-change", val ); - - return VLC_SUCCESS; -} diff --git a/src/input/stream.c b/src/input/stream.c index 72b2f20dedfb6861321a03dee00f756cf8ba0442..cfd65325284dd0fe30fcc0d87a5a3469c3ae1082 100644 --- a/src/input/stream.c +++ b/src/input/stream.c @@ -25,576 +25,1138 @@ #include #include -#include "ninput.h" +#include "input_internal.h" -/**************************************************************************** - * stream_ReadLine: - ****************************************************************************/ -/** - * Read from the stream untill first newline. - * \param s Stream handle to read from - * \return A null-terminated string. This must be freed, +/* TODO: + * - tune the 2 methods + * - compute cost for seek + * - improve stream mode seeking with closest segments + * - ... */ -/* FIXME don't use stupid MAX_LINE -> do the same than net_ReadLine */ -#define MAX_LINE 1024 -char *stream_ReadLine( stream_t *s ) + +/* Two methods: + * - using pf_block + * One linked list of data read + * - using pf_read + * More complex scheme using mutliple track to avoid seeking + */ + +/* How many track we have, currently only used for stream mode */ +#define STREAM_CACHE_TRACK 3 +/* Max size of our cache 4Mo per track */ +#define STREAM_CACHE_SIZE (4*STREAM_CACHE_TRACK*1024*1024) + /* How many data we try to prebuffer */ +#define STREAM_CACHE_PREBUFFER_SIZE (32767) + +#define STREAM_CACHE_PREBUFFER_LENGTH (100*1000) /* Maximum time we take to pre-buffer */ + + +/* Method1: Simple, for pf_block. + * We get blocks and put them in the linked list. + * We release blocks once the total size is bigger than CACHE_BLOCK_SIZE + */ +#define STREAM_DATA_WAIT 40000 /* Time between before a pf_block retry */ + +/* Method2: A bit more complex, for pf_read + * - We use ring buffers, only one if unseekable, all if seekable + * - Upon seek date current ring, then search if one ring match the pos, + * yes: switch to it, seek the access to match the end of the ring + * no: search the ring with i_end the closer to i_pos, + * if close enough, read data and use this ring + * else use the oldest ring, seek and use it. + * + * TODO: - with access non seekable: use all space available for only one ring, but + * we have to support seekable/non-seekable switch on the fly. + * - compute a good value for i_read_size + * - ? + */ +#define STREAM_READ_ATONCE 32767 +#define STREAM_CACHE_TRACK_SIZE (STREAM_CACHE_SIZE/STREAM_CACHE_TRACK) + +typedef struct { - uint8_t *p_data; - char *p_line; - int i_data; - int i = 0; - i_data = stream_Peek( s, &p_data, MAX_LINE ); + int64_t i_date; - while( i < i_data && p_data[i] != '\n' && p_data[i] != '\r' ) + int64_t i_start; + int64_t i_end; + + uint8_t *p_buffer; +} stream_track_t; + +struct stream_sys_t +{ + access_t *p_access; + + vlc_bool_t b_block; /* Block method (1) or stream */ + + int64_t i_pos; /* Current reading offset */ + + /* Method 1: pf_block */ + struct { - i++; - } - if( i_data <= 0 ) + int64_t i_start; /* Offset of block for p_first */ + int i_offset; /* Offset for data in p_current */ + block_t *p_current; /* Current block */ + + int i_size; /* Total amount of data in the list */ + block_t *p_first; + block_t **pp_last; + } block; + + /* Method 2: for pf_read */ + struct { - return NULL; - } - else + int i_offset; /* Buffer ofset in the current track */ + int i_tk; /* Current track */ + stream_track_t tk[STREAM_CACHE_TRACK]; + + /* Global buffer */ + uint8_t *p_buffer; + + /* */ + int i_used; /* Used since last read */ + int i_read_size; + } stream; + + /* Peek temporary buffer */ + int i_peek; + uint8_t *p_peek; + + /* Stat for both method */ + struct { - p_line = malloc( i + 1 ); - if( p_line == NULL ) - { - msg_Err( s, "out of memory" ); - return NULL; - } - i = stream_Read( s, p_line, i + 1 ); - p_line[ i - 1 ] = '\0'; + vlc_bool_t b_fastseek; /* From access */ - return p_line; - } -} + /* Stat about reading data */ + int64_t i_read_count; + int64_t i_bytes; + int64_t i_read_time; + /* Stat about seek */ + int i_seek_count; + int64_t i_seek_time; + } stat; +}; +/* Method 1: */ +static int AStreamReadBlock( stream_t *, void *p_read, int i_read ); +static int AStreamPeekBlock( stream_t *, uint8_t **p_peek, int i_read ); +static int AStreamSeekBlock( stream_t *s, int64_t i_pos ); +static void AStreamPrebufferBlock( stream_t * ); -/* TODO: one day we should create a special module stream - * when we would have a access wrapper, and stream filter - * (like caching, progessive, gunzip, ... ) - */ +/* Method 2 */ +static int AStreamReadStream( stream_t *, void *p_read, int i_read ); +static int AStreamPeekStream( stream_t *, uint8_t **pp_peek, int i_read ); +static int AStreamSeekStream( stream_t *s, int64_t i_pos ); +static void AStreamPrebufferStream( stream_t * ); -/* private stream_sys_t for input_Stream* */ -typedef struct -{ - input_thread_t *p_input; -} input_stream_sys_t; +/* Common */ +static int AStreamControl( stream_t *, int i_query, va_list ); -/* private pf_* functions declarations */ -static int IStreamRead ( stream_t *, void *p_read, int i_read ); -static int IStreamPeek ( stream_t *, uint8_t **pp_peek, int i_peek ); -static int IStreamControl( stream_t *, int i_query, va_list ); /**************************************************************************** - * input_StreamNew: create a wrapper for p_input access + * stream_AccessNew: create a stream from a access ****************************************************************************/ -stream_t *input_StreamNew( input_thread_t *p_input ) +stream_t *stream_AccessNew( access_t *p_access ) { - stream_t *s = vlc_object_create( p_input, sizeof( stream_t ) ); - input_stream_sys_t *p_sys; - if( s ) + stream_t *s = vlc_object_create( p_access, VLC_OBJECT_STREAM ); + stream_sys_t *p_sys; + + if( !s ) + return NULL; + + /* Attach it now, needed for b_die */ + vlc_object_attach( s, p_access ); + + s->pf_block = NULL; + s->pf_read = NULL; /* Set up later */ + s->pf_peek = NULL; + s->pf_control= AStreamControl; + + s->p_sys = p_sys = malloc( sizeof( stream_sys_t ) ); + + /* Common field */ + p_sys->p_access = p_access; + p_sys->b_block = p_access->pf_block ? VLC_TRUE : VLC_FALSE; + p_sys->i_pos = p_access->info.i_pos; + + /* Stats */ + access2_Control( p_access, ACCESS_CAN_FASTSEEK, &p_sys->stat.b_fastseek ); + p_sys->stat.i_bytes = 0; + p_sys->stat.i_read_time = 0; + p_sys->stat.i_read_count = 0; + p_sys->stat.i_seek_count = 0; + p_sys->stat.i_seek_time = 0; + + /* Peek */ + p_sys->i_peek = 0; + p_sys->p_peek = NULL; + + if( p_sys->b_block ) + { + s->pf_read = AStreamReadBlock; + s->pf_peek = AStreamPeekBlock; + + /* Init all fields of p_sys->block */ + p_sys->block.i_start = p_sys->i_pos; + p_sys->block.i_offset = 0; + p_sys->block.p_current = NULL; + p_sys->block.i_size = 0; + p_sys->block.p_first = NULL; + p_sys->block.pp_last = &p_sys->block.p_first; + + /* Do the prebuffering */ + AStreamPrebufferBlock( s ); + + if( p_sys->block.i_size <= 0 ) + { + msg_Err( s, "cannot pre fill buffer" ); + goto error; + } + } + else { - s->pf_block = NULL; - s->pf_read = IStreamRead; - s->pf_peek = IStreamPeek; - s->pf_control= IStreamControl; + int i; + + s->pf_read = AStreamReadStream; + s->pf_peek = AStreamPeekStream; + + /* Allocate/Setup our tracks */ + p_sys->stream.i_offset = 0; + p_sys->stream.i_tk = 0; + p_sys->stream.p_buffer = malloc( STREAM_CACHE_SIZE ); + p_sys->stream.i_used = 0; + access2_Control( p_access, ACCESS_GET_MTU, &p_sys->stream.i_read_size ); + if( p_sys->stream.i_read_size <= 0 ) + p_sys->stream.i_read_size = STREAM_READ_ATONCE; + else if( p_sys->stream.i_read_size <= 256 ) + p_sys->stream.i_read_size = 256; + + for( i = 0; i < STREAM_CACHE_TRACK; i++ ) + { + p_sys->stream.tk[i].i_date = 0; + p_sys->stream.tk[i].i_start = p_sys->i_pos; + p_sys->stream.tk[i].i_end = p_sys->i_pos; + p_sys->stream.tk[i].p_buffer= + &p_sys->stream.p_buffer[i * STREAM_CACHE_TRACK_SIZE]; + } + + /* Do the prebuffering */ + AStreamPrebufferStream( s ); - s->p_sys = malloc( sizeof( input_stream_sys_t ) ); - p_sys = (input_stream_sys_t*)s->p_sys; - p_sys->p_input = p_input; + if( p_sys->stream.tk[p_sys->stream.i_tk].i_end <= 0 ) + { + msg_Err( s, "cannot pre fill buffer" ); + goto error; + } } + + return s; + +error: + if( p_sys->b_block ) + { + /* Nothing yet */ + } + else + { + free( p_sys->stream.p_buffer ); + } + free( s->p_sys ); + vlc_object_detach( s ); + vlc_object_destroy( s ); + return NULL; } /**************************************************************************** - * input_StreamDelete: + * stream_AccessDelete: ****************************************************************************/ -void input_StreamDelete( stream_t *s ) +void stream_AccessDelete( stream_t *s ) { + stream_sys_t *p_sys = s->p_sys; + + vlc_object_detach( s ); + + if( p_sys->b_block ) + { + block_ChainRelease( p_sys->block.p_first ); + } + else + { + free( p_sys->stream.p_buffer ); + } + + if( p_sys->p_peek ) + free( p_sys->p_peek ); + free( s->p_sys ); vlc_object_destroy( s ); } +/**************************************************************************** + * stream_AccessReset: + ****************************************************************************/ +void stream_AccessReset( stream_t *s ) +{ + stream_sys_t *p_sys = s->p_sys; + + p_sys->i_pos = p_sys->p_access->info.i_pos; + + if( p_sys->b_block ) + { + block_ChainRelease( p_sys->block.p_first ); + + /* Init all fields of p_sys->block */ + p_sys->block.i_start = p_sys->i_pos; + p_sys->block.i_offset = 0; + p_sys->block.p_current = NULL; + p_sys->block.i_size = 0; + p_sys->block.p_first = NULL; + p_sys->block.pp_last = &p_sys->block.p_first; + + /* Do the prebuffering */ + AStreamPrebufferBlock( s ); + } + else + { + int i; + + /* Setup our tracks */ + p_sys->stream.i_offset = 0; + p_sys->stream.i_tk = 0; + p_sys->stream.i_used = 0; + + for( i = 0; i < STREAM_CACHE_TRACK; i++ ) + { + p_sys->stream.tk[i].i_date = 0; + p_sys->stream.tk[i].i_start = p_sys->i_pos; + p_sys->stream.tk[i].i_end = p_sys->i_pos; + } + + /* Do the prebuffering */ + AStreamPrebufferStream( s ); + } +} /**************************************************************************** - * IStreamControl: + * AStreamControl: ****************************************************************************/ -static int IStreamControl( stream_t *s, int i_query, va_list args ) +static int AStreamControl( stream_t *s, int i_query, va_list args ) { - input_stream_sys_t * p_sys = (input_stream_sys_t*)s->p_sys; - input_thread_t *p_input = p_sys->p_input; + stream_sys_t *p_sys = s->p_sys; + access_t *p_access = p_sys->p_access; - vlc_bool_t *p_b; - int64_t *p_i64, i64; - int *p_int; + vlc_bool_t *p_bool; + int64_t *pi_64, i_64; + int i_int; switch( i_query ) { case STREAM_GET_SIZE: - p_i64 = (int64_t*) va_arg( args, int64_t * ); - - vlc_mutex_lock( &p_input->stream.stream_lock ); - *p_i64 = p_input->stream.p_selected_area->i_size; - vlc_mutex_unlock( &p_input->stream.stream_lock ); - return VLC_SUCCESS; + pi_64 = (int64_t*)va_arg( args, int64_t * ); + *pi_64 = p_access->info.i_size; + break; case STREAM_CAN_SEEK: - p_b = (vlc_bool_t*) va_arg( args, vlc_bool_t * ); - - vlc_mutex_lock( &p_input->stream.stream_lock ); - *p_b = p_input->stream.b_seekable; - vlc_mutex_unlock( &p_input->stream.stream_lock ); - return VLC_SUCCESS; + p_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * ); + access2_Control( p_access, ACCESS_CAN_SEEK, p_bool ); + break; case STREAM_CAN_FASTSEEK: - p_b = (vlc_bool_t*) va_arg( args, vlc_bool_t * ); - - vlc_mutex_lock( &p_input->stream.stream_lock ); - *p_b = p_input->stream.b_seekable && - p_input->stream.i_method == INPUT_METHOD_FILE; - vlc_mutex_unlock( &p_input->stream.stream_lock ); - return VLC_SUCCESS; + p_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * ); + access2_Control( p_access, ACCESS_CAN_FASTSEEK, p_bool ); + break; case STREAM_GET_POSITION: - p_i64 = (int64_t*) va_arg( args, int64_t * ); - - vlc_mutex_lock( &p_input->stream.stream_lock ); - *p_i64 = p_input->stream.p_selected_area->i_tell; - vlc_mutex_unlock( &p_input->stream.stream_lock ); - return VLC_SUCCESS; + pi_64 = (int64_t*)va_arg( args, int64_t * ); + *pi_64 = p_sys->i_pos; + break; case STREAM_SET_POSITION: - { - int64_t i_skip; - i64 = (int64_t) va_arg( args, int64_t ); - - vlc_mutex_lock( &p_input->stream.stream_lock ); - if( i64 < 0 || - ( p_input->stream.p_selected_area->i_size > 0 && - p_input->stream.p_selected_area->i_size < i64 ) ) - { - vlc_mutex_unlock( &p_input->stream.stream_lock ); - msg_Warn( s, "seek out of bound" ); - return VLC_EGENERIC; - } - - i_skip = i64 - p_input->stream.p_selected_area->i_tell; - - if( i_skip == 0 ) - { - vlc_mutex_unlock( &p_input->stream.stream_lock ); - return VLC_SUCCESS; - } - - if( i_skip > 0 && i_skip < p_input->p_last_data - - p_input->p_current_data - 1 ) - { - /* We can skip without reading/seeking */ - p_input->p_current_data += i_skip; - p_input->stream.p_selected_area->i_tell = i64; - vlc_mutex_unlock( &p_input->stream.stream_lock ); - return VLC_SUCCESS; - } - vlc_mutex_unlock( &p_input->stream.stream_lock ); - - if( p_input->stream.b_seekable && - ( p_input->stream.i_method == INPUT_METHOD_FILE || - i_skip < 0 || i_skip >= ( p_input->i_mtu > 0 ? - p_input->i_mtu : 4096 ) ) ) - { - input_AccessReinit( p_input ); - p_input->pf_seek( p_input, i64 ); - return VLC_SUCCESS; - } - - if( i_skip > 0 ) - { - data_packet_t *p_data; - - if( i_skip > 1000 ) - { - msg_Warn( s, "will skip "I64Fd" bytes, slow", i_skip ); - } - - while( i_skip > 0 ) - { - int i_read; - - i_read = input_SplitBuffer( p_input, &p_data, - __MIN( (int)p_input->i_bufsize, i_skip ) ); - if( i_read < 0 ) - { - return VLC_EGENERIC; - } - i_skip -= i_read; - - input_DeletePacket( p_input->p_method_data, p_data ); - if( i_read == 0 && i_skip > 0 ) - { - return VLC_EGENERIC; - } - } - } - return VLC_SUCCESS; - } + i_64 = (int64_t)va_arg( args, int64_t ); + if( p_sys->b_block ) + return AStreamSeekBlock( s, i_64 ); + else + return AStreamSeekStream( s, i_64 ); case STREAM_GET_MTU: - p_int = (int*) va_arg( args, int * ); - *p_int = p_input->i_mtu; - return VLC_SUCCESS; + return VLC_EGENERIC; case STREAM_CONTROL_ACCESS: - { - int i_int = (int) va_arg( args, int ); + i_int = (int) va_arg( args, int ); if( i_int != ACCESS_SET_PRIVATE_ID_STATE ) { msg_Err( s, "Hey, what are you thinking ?" "DON'T USE STREAM_CONTROL_ACCESS !!!" ); return VLC_EGENERIC; } - if( p_input->pf_access_control ) - { - return p_input->pf_access_control( p_input, i_int, args ); - } - return VLC_EGENERIC; - } + return access2_Control( p_access, i_int, args ); default: msg_Err( s, "invalid stream_vaControl query=0x%x", i_query ); return VLC_EGENERIC; } + return VLC_SUCCESS; } + + /**************************************************************************** - * IStreamRead: + * Method 1: ****************************************************************************/ -static int IStreamRead( stream_t *s, void *p_data, int i_data ) +static void AStreamPrebufferBlock( stream_t *s ) { - input_stream_sys_t * p_sys = (input_stream_sys_t*)s->p_sys; - input_thread_t *p_input = p_sys->p_input; - uint8_t *p = (uint8_t*)p_data; + stream_sys_t *p_sys = s->p_sys; + access_t *p_access = p_sys->p_access; - int i_read = 0; + int64_t i_first = 0; + int64_t i_start; - if( p_data == NULL && i_data > 0 ) + msg_Dbg( s, "pre buffering" ); + i_start = mdate(); + for( ;; ) { - int64_t i_pos; + int64_t i_date = mdate(); + block_t *b; - stream_Control( s, STREAM_GET_POSITION, &i_pos ); + if( s->b_die || + p_sys->block.i_size > STREAM_CACHE_PREBUFFER_SIZE || + ( i_first > 0 && i_first + STREAM_CACHE_PREBUFFER_LENGTH < i_date ) ) + { + int64_t i_byterate; + + /* Update stat */ + p_sys->stat.i_bytes = p_sys->block.i_size; + p_sys->stat.i_read_time = i_date - i_start; + i_byterate = ( I64C(1000000) * p_sys->stat.i_bytes ) / + (p_sys->stat.i_read_time+1); + + msg_Dbg( s, "prebuffering done %lld bytes in %llds - %lld kbytes/s", + p_sys->stat.i_bytes, + p_sys->stat.i_read_time / I64C(1000000), + i_byterate / 1024 ); + break; + } - i_pos += i_data; - if( stream_Control( s, STREAM_SET_POSITION, i_pos ) ) + /* Fetch a block */ + if( ( b = p_access->pf_block( p_access ) ) == NULL ) { - return 0; + if( p_access->info.b_eof ) + break; + + msleep( STREAM_DATA_WAIT ); + continue; } - return i_data; + + if( i_first == 0 ) + i_first = mdate(); + + /* Append the block */ + p_sys->block.i_size += b->i_buffer; + *p_sys->block.pp_last = b; + p_sys->block.pp_last = &b->p_next; + + p_sys->stat.i_read_count++; } - while( i_data > 0 && !p_input->b_die ) + p_sys->block.p_current = p_sys->block.p_first; +} + +static int AStreamRefillBlock( stream_t *s ); + +static int AStreamReadBlock( stream_t *s, void *p_read, int i_read ) +{ + stream_sys_t *p_sys = s->p_sys; + + uint8_t *p_data= (uint8_t*)p_read; + int i_data = 0; + + /* It means EOF */ + if( p_sys->block.p_current == NULL ) + return 0; + + while( i_data < i_read ) { - ssize_t i_count = p_input->p_last_data - p_input->p_current_data; + int i_current = p_sys->block.p_current->i_buffer - p_sys->block.i_offset; + int i_copy = __MIN( i_current, i_read - i_data); - if( i_count <= 0 ) + /* Copy data */ + if( p_data ) { - /* Go to the next buffer */ - i_count = input_FillBuffer( p_input ); + memcpy( p_data, &p_sys->block.p_current->p_buffer[p_sys->block.i_offset], i_copy ); + p_data += i_copy; + } + i_data += i_copy; - if( i_count < 0 ) return -1; - else if( i_count == 0 ) + p_sys->block.i_offset += i_copy; + if( p_sys->block.i_offset >= p_sys->block.p_current->i_buffer ) + { + /* Current block is now empty, switch to next */ + if( p_sys->block.p_current ) + { + p_sys->block.i_offset = 0; + p_sys->block.p_current = p_sys->block.p_current->p_next; + } + /*Get a new block */ + if( AStreamRefillBlock( s ) ) { - /* We reached the EOF */ break; } } - - i_count = __MIN( i_data, i_count ); - memcpy( p, p_input->p_current_data, i_count ); - p_input->p_current_data += i_count; - p += i_count; - i_data -= i_count; - i_read += i_count; - - /* Update stream position */ - vlc_mutex_lock( &p_input->stream.stream_lock ); - p_input->stream.p_selected_area->i_tell += i_count; - vlc_mutex_unlock( &p_input->stream.stream_lock ); } - return i_read; + p_sys->i_pos += i_data; + return i_data; } -/**************************************************************************** - * IStreamPeek: - ****************************************************************************/ -static int IStreamPeek( stream_t *s, uint8_t **pp_peek, int i_peek ) +static int AStreamPeekBlock( stream_t *s, uint8_t **pp_peek, int i_read ) { - input_stream_sys_t * p_sys = (input_stream_sys_t*)s->p_sys; - return input_Peek( p_sys->p_input, pp_peek, i_peek ); + stream_sys_t *p_sys = s->p_sys; + uint8_t *p_data; + int i_data = 0; + block_t *b; + int i_offset; + + if( p_sys->block.p_current == NULL ) + return 0; /* EOF */ + + /* We can directly give a pointer over our buffer */ + if( i_read <= p_sys->block.p_current->i_buffer - p_sys->block.i_offset ) + { + *pp_peek = &p_sys->block.p_current->p_buffer[p_sys->block.i_offset]; + return i_read; + } + + /* We need to create a local copy */ + if( p_sys->i_peek < i_read ) + { + if( p_sys->p_peek ) + free( p_sys->p_peek ); + p_sys->i_peek = i_read; + p_sys->p_peek = malloc( p_sys->i_peek ); + } + + /* Fill enough data */ + while( p_sys->block.i_size - (p_sys->i_pos - p_sys->block.i_start) < i_read ) + { + block_t **pp_last = p_sys->block.pp_last; + + if( AStreamRefillBlock( s ) ) + break; + + /* Our buffer are probably filled enough, don't try anymore */ + if( pp_last == p_sys->block.pp_last ) + break; + } + + /* Copy what we have */ + b = p_sys->block.p_current; + i_offset = p_sys->block.i_offset; + p_data = p_sys->p_peek; + + while( b && i_data < i_read ) + { + int i_current = b->i_buffer - i_offset; + int i_copy = __MIN( i_current, i_read - i_data ); + + memcpy( p_data, &b->p_buffer[i_offset], i_copy ); + i_data += i_copy; + p_data += i_copy; + i_offset += i_copy; + + if( i_offset >= b->i_buffer ) + { + i_offset = 0; + b = b->p_next; + } + } + + *pp_peek = p_sys->p_peek; + return i_data; } -/**************************************************************************** - * stream_Demux*: create a demuxer for an outpout stream (allow demuxer chain) - ****************************************************************************/ -typedef struct +static int AStreamSeekBlock( stream_t *s, int64_t i_pos ) { - /* Data buffer */ - vlc_mutex_t lock; - int i_buffer; - int i_buffer_size; - uint8_t *p_buffer; + stream_sys_t *p_sys = s->p_sys; + access_t *p_access = p_sys->p_access; + int64_t i_offset = i_pos - p_sys->block.i_start; + vlc_bool_t b_seek; - int64_t i_pos; + /* We already have thoses data, just update p_current/i_offset */ + if( i_offset >= 0 && i_offset < p_sys->block.i_size ) + { + block_t *b = p_sys->block.p_first; + int i_current = 0; - /* Demuxer */ - char *psz_name; - es_out_t *out; - demux_t *p_demux; -} d_stream_sys_t; + while( i_current + b->i_buffer < i_offset ) + { + i_current += b->i_buffer; + b = b->p_next; + } -static int DStreamRead ( stream_t *, void *p_read, int i_read ); -static int DStreamPeek ( stream_t *, uint8_t **pp_peek, int i_peek ); -static int DStreamControl( stream_t *, int i_query, va_list ); -static int DStreamThread ( stream_t * ); + p_sys->block.p_current = b; + p_sys->block.i_offset = i_offset - i_current; + p_sys->i_pos = i_pos; -stream_t *__stream_DemuxNew( vlc_object_t *p_obj, char *psz_demux, es_out_t *out ) -{ - /* We create a stream reader, and launch a thread */ - stream_t *s; - d_stream_sys_t *p_sys; + return VLC_SUCCESS; + } - if( psz_demux == NULL || *psz_demux == '\0' ) + /* We may need to seek or to read data */ + if( i_offset < 0 ) { - return NULL; + vlc_bool_t b_aseek; + access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek ); + + if( !b_aseek ) + { + msg_Err( s, "backward seek impossible (access non seekable)" ); + return VLC_EGENERIC; + } + + b_seek = VLC_TRUE; } + else + { + vlc_bool_t b_aseek, b_aseekfast; - s = vlc_object_create( p_obj, sizeof( stream_t ) ); - s->pf_block = NULL; - s->pf_read = DStreamRead; - s->pf_peek = DStreamPeek; - s->pf_control= DStreamControl; - - s->p_sys = malloc( sizeof( d_stream_sys_t) ); - p_sys = (d_stream_sys_t*)s->p_sys; - - vlc_mutex_init( s, &p_sys->lock ); - p_sys->i_buffer = 0; - p_sys->i_buffer_size = 1000000; - p_sys->p_buffer = malloc( p_sys->i_buffer_size ); - p_sys->i_pos = 0; - p_sys->psz_name = strdup( psz_demux ); - p_sys->out = out; - p_sys->p_demux = NULL; - - if( vlc_thread_create( s, "stream out", DStreamThread, VLC_THREAD_PRIORITY_INPUT, VLC_FALSE ) ) - { - vlc_mutex_destroy( &p_sys->lock ); - vlc_object_destroy( s ); - free( p_sys ); - return NULL; + access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek ); + access2_Control( p_access, ACCESS_CAN_FASTSEEK, &b_aseekfast ); + + if( !b_aseek ) + { + b_seek = VLC_FALSE; + msg_Warn( s, "%lld bytes need to be skipped (access non seekable)", + i_offset - p_sys->block.i_size ); + } + else + { + int64_t i_skip = i_offset - p_sys->block.i_size; + + /* Avg bytes per packets */ + int i_avg = p_sys->stat.i_bytes / p_sys->stat.i_read_count; + /* TODO compute a seek cost instead of fixed threshold */ + int i_th = b_aseekfast ? 1 : 5; + + if( i_skip <= i_th * i_avg && + i_skip < STREAM_CACHE_SIZE ) + b_seek = VLC_FALSE; + else + b_seek = VLC_TRUE; + + msg_Dbg( s, "b_seek=%d th*avg=%d skip=%lld", + b_seek, i_th*i_avg, i_skip ); + } } - return s; -} + if( b_seek ) + { + int64_t i_start, i_end; + /* Do the access seek */ + i_start = mdate(); + if( p_access->pf_seek( p_access, i_pos ) ) + return VLC_EGENERIC; + i_end = mdate(); -void stream_DemuxSend( stream_t *s, block_t *p_block ) -{ - d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys; + /* Release data */ + block_ChainRelease( p_sys->block.p_first ); + + /* Reinit */ + p_sys->block.i_start = p_sys->i_pos = i_pos; + p_sys->block.i_offset = 0; + p_sys->block.p_current = NULL; + p_sys->block.i_size = 0; + p_sys->block.p_first = NULL; + p_sys->block.pp_last = &p_sys->block.p_first; - if( p_block->i_buffer > 0 ) + /* Refill a block */ + if( AStreamRefillBlock( s ) ) + { + msg_Err( s, "cannot re fill buffer" ); + return VLC_EGENERIC; + } + /* Update stat */ + p_sys->stat.i_seek_time += i_end - i_start; + p_sys->stat.i_seek_count++; + return VLC_SUCCESS; + } + else { - vlc_mutex_lock( &p_sys->lock ); - /* Realloc if needed */ - if( p_sys->i_buffer + p_block->i_buffer > p_sys->i_buffer_size ) + /* Read enought data */ + while( p_sys->block.i_start + p_sys->block.i_size < i_pos ) { - if( p_sys->i_buffer_size > 5000000 ) + if( AStreamRefillBlock( s ) ) + { + msg_Err( s, "can't read enough data in seek" ); + return VLC_EGENERIC; + } + while( p_sys->block.p_current && + p_sys->i_pos + p_sys->block.p_current->i_buffer < i_pos ) { - vlc_mutex_unlock( &p_sys->lock ); - msg_Err( s, "stream_DemuxSend: buffer size > 5000000" ); - block_Release( p_block ); - return; + p_sys->i_pos += p_sys->block.p_current->i_buffer; + p_sys->block.p_current = p_sys->block.p_current->p_next; } - /* I know, it's more than needed but that's perfect */ - p_sys->i_buffer_size += p_block->i_buffer; - /* FIXME won't work with PEEK -> segfault */ - p_sys->p_buffer = realloc( p_sys->p_buffer, p_sys->i_buffer_size ); - msg_Dbg( s, "stream_DemuxSend: realloc to %d", p_sys->i_buffer_size ); } - /* copy data */ - memcpy( &p_sys->p_buffer[p_sys->i_buffer], p_block->p_buffer, p_block->i_buffer ); - p_sys->i_buffer += p_block->i_buffer; + p_sys->block.i_offset = i_pos - p_sys->i_pos; + p_sys->i_pos = i_pos; - vlc_mutex_unlock( &p_sys->lock ); + /* TODO read data */ + return VLC_SUCCESS; } - block_Release( p_block ); + return VLC_EGENERIC; } -void stream_DemuxDelete( stream_t *s ) +static int AStreamRefillBlock( stream_t *s ) { - d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys; + stream_sys_t *p_sys = s->p_sys; + access_t *p_access = p_sys->p_access; + int64_t i_start, i_stop; + block_t *b; + + /* Release data */ + while( p_sys->block.i_size >= STREAM_CACHE_SIZE && + p_sys->block.p_first != p_sys->block.p_current ) + { + block_t *b = p_sys->block.p_first; - s->b_die = VLC_TRUE; + p_sys->block.i_start += b->i_buffer; + p_sys->block.i_size -= b->i_buffer; + p_sys->block.p_first = b->p_next; - vlc_mutex_lock( &p_sys->lock ); - if( p_sys->p_demux ) + block_Release( b ); + } + if( p_sys->block.i_size >= STREAM_CACHE_SIZE && + p_sys->block.p_current == p_sys->block.p_first && + p_sys->block.p_current->p_next ) /* At least 2 packets */ { - p_sys->p_demux->b_die = VLC_TRUE; + /* Enough data, don't read more */ + return VLC_SUCCESS; } - vlc_mutex_unlock( &p_sys->lock ); - - vlc_thread_join( s ); - if( p_sys->p_demux ) + /* Now read a new block */ + i_start = mdate(); + for( ;; ) { - demux2_Delete( p_sys->p_demux ); + if( s->b_die ) + return VLC_EGENERIC; + + + /* Fetch a block */ + if( ( b = p_access->pf_block( p_access ) ) ) + break; + + if( p_access->info.b_eof ) + return VLC_EGENERIC; + + msleep( STREAM_DATA_WAIT ); } - vlc_mutex_destroy( &p_sys->lock ); - free( p_sys->psz_name ); - free( p_sys->p_buffer ); - free( p_sys ); - vlc_object_destroy( s ); + i_stop = mdate(); + + /* Append the block */ + p_sys->block.i_size += b->i_buffer; + *p_sys->block.pp_last = b; + p_sys->block.pp_last = &b->p_next; + + /* Fix p_current */ + if( p_sys->block.p_current == NULL ) + p_sys->block.p_current = b; + + /* Update stat */ + p_sys->stat.i_bytes += b->i_buffer; + p_sys->stat.i_read_time += i_stop - i_start; + p_sys->stat.i_read_count++; + + return VLC_SUCCESS; } -static int DStreamRead ( stream_t *s, void *p_read, int i_read ) +/**************************************************************************** + * Method 2: + ****************************************************************************/ +static int AStreamRefillStream( stream_t *s ); + +static int AStreamReadStream( stream_t *s, void *p_read, int i_read ) { - d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys; - int i_copy; + stream_sys_t *p_sys = s->p_sys; + stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk]; - //msg_Dbg( s, "DStreamRead: wanted %d bytes", i_read ); - for( ;; ) - { - vlc_mutex_lock( &p_sys->lock ); - //msg_Dbg( s, "DStreamRead: buffer %d", p_sys->i_buffer ); - if( p_sys->i_buffer >= i_read || s->b_die ) - { - break; - } - vlc_mutex_unlock( &p_sys->lock ); - msleep( 10000 ); - } + uint8_t *p_data = (uint8_t*)p_read; + int i_data = 0; - //msg_Dbg( s, "DStreamRead: read %d buffer %d", i_read, p_sys->i_buffer ); + if( tk->i_start >= tk->i_end ) + return 0; /* EOF */ - i_copy = __MIN( i_read, p_sys->i_buffer ); - if( i_copy > 0 ) + /*msg_Dbg( s, "AStreamReadStream: %d pos=%lld tk=%d start=%lld offset=%d end=%lld", + i_read, + p_sys->i_pos, + p_sys->stream.i_tk, + tk->i_start, p_sys->stream.i_offset, tk->i_end );*/ + + while( i_data < i_read ) { - if( p_read ) + int i_off = (tk->i_start + p_sys->stream.i_offset) % STREAM_CACHE_TRACK_SIZE; + int i_current = __MIN( tk->i_end - tk->i_start - p_sys->stream.i_offset, STREAM_CACHE_TRACK_SIZE - i_off ); + int i_copy = __MIN( i_current, i_read - i_data ); + + /* Copy data */ + //msg_Dbg( s, "AStreamReadStream: copy %d", i_copy ); + if( p_data ) { - memcpy( p_read, p_sys->p_buffer, i_copy ); + memcpy( p_data, &tk->p_buffer[i_off], i_copy ); + p_data += i_copy; } - p_sys->i_buffer -= i_copy; + i_data += i_copy; + p_sys->stream.i_offset += i_copy; + + /* Update pos now */ p_sys->i_pos += i_copy; - if( p_sys->i_buffer > 0 ) + /* */ + p_sys->stream.i_used += i_copy; + if( tk->i_start + p_sys->stream.i_offset >= tk->i_end || + p_sys->stream.i_used >= p_sys->stream.i_read_size ) { - memmove( p_sys->p_buffer, &p_sys->p_buffer[i_copy], p_sys->i_buffer ); + if( AStreamRefillStream( s ) ) + { + /* Eof */ + if( tk->i_start >= tk->i_end ) + break; + } } - } - vlc_mutex_unlock( &p_sys->lock ); - return i_copy; + return i_data; } -static int DStreamPeek ( stream_t *s, uint8_t **pp_peek, int i_peek ) + +static int AStreamPeekStream( stream_t *s, uint8_t **pp_peek, int i_read ) { - d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys; - int i_copy; + stream_sys_t *p_sys = s->p_sys; + stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk]; + int64_t i_off; - //msg_Dbg( s, "DStreamPeek: wanted %d bytes", i_peek ); - for( ;; ) + if( tk->i_start >= tk->i_end ) + return 0; /* EOF */ + + /*msg_Dbg( s, "AStreamPeekStream: %d pos=%lld tk=%d start=%lld offset=%d end=%lld", + i_read, + p_sys->i_pos, + p_sys->stream.i_tk, + tk->i_start, p_sys->stream.i_offset, tk->i_end );*/ + + /* Avoid problem, but that should *never* happen */ + if( i_read > STREAM_CACHE_TRACK_SIZE / 2 ) + i_read = STREAM_CACHE_TRACK_SIZE / 2; + + while( tk->i_end - tk->i_start - p_sys->stream.i_offset < i_read ) { - vlc_mutex_lock( &p_sys->lock ); - //msg_Dbg( s, "DStreamPeek: buffer %d", p_sys->i_buffer ); - if( p_sys->i_buffer >= i_peek || s->b_die ) - { + if( AStreamRefillStream( s ) ) break; - } - vlc_mutex_unlock( &p_sys->lock ); - msleep( 10000 ); } - *pp_peek = p_sys->p_buffer; - i_copy = __MIN( i_peek, p_sys->i_buffer ); - vlc_mutex_unlock( &p_sys->lock ); + if( tk->i_end - tk->i_start - p_sys->stream.i_offset < i_read ) + i_read = tk->i_end - tk->i_start - p_sys->stream.i_offset; - return i_copy; + /* Now, direct pointer or a copy ? */ + i_off = (tk->i_start + p_sys->stream.i_offset) % STREAM_CACHE_TRACK_SIZE; + if( i_off + i_read <= STREAM_CACHE_TRACK_SIZE ) + { + *pp_peek = &tk->p_buffer[i_off]; + return i_read; + } + + if( p_sys->i_peek < i_read ) + { + if( p_sys->p_peek ) free( p_sys->p_peek ); + p_sys->i_peek = i_read; + p_sys->p_peek = malloc( i_read ); + } + + memcpy( p_sys->p_peek, &tk->p_buffer[i_off], STREAM_CACHE_TRACK_SIZE - i_off ); + memcpy( &p_sys->p_peek[STREAM_CACHE_TRACK_SIZE - i_off], &tk->p_buffer[0], i_read - (STREAM_CACHE_TRACK_SIZE - i_off) ); + + *pp_peek = p_sys->p_peek; + return i_read; } -static int DStreamControl( stream_t *s, int i_query, va_list args ) +static int AStreamSeekStream( stream_t *s, int64_t i_pos ) { - d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys; - int64_t *p_i64; - vlc_bool_t *p_b; - int *p_int; - switch( i_query ) + stream_sys_t *p_sys = s->p_sys; + access_t *p_access = p_sys->p_access; + vlc_bool_t b_aseek; + vlc_bool_t b_afastseek; + int i_maxth; + int i_new; + int i; + + /* + msg_Dbg( s, "AStreamSeekStream: to %lld pos=%lld tk=%d start=%lld offset=%d end=%lld", + i_pos, + p_sys->i_pos, + p_sys->stream.i_tk, + p_sys->stream.tk[p_sys->stream.i_tk].i_start, p_sys->stream.i_offset, p_sys->stream.tk[p_sys->stream.i_tk].i_end );*/ + + + /* Seek in our current track ? */ + if( i_pos >= p_sys->stream.tk[p_sys->stream.i_tk].i_start && + i_pos < p_sys->stream.tk[p_sys->stream.i_tk].i_end ) { - case STREAM_GET_SIZE: - p_i64 = (int64_t*) va_arg( args, int64_t * ); - *p_i64 = 0; - return VLC_SUCCESS; + //msg_Dbg( s, "AStreamSeekStream: current track" ); + p_sys->i_pos = i_pos; + p_sys->stream.i_offset = i_pos - p_sys->stream.tk[p_sys->stream.i_tk].i_start; + return VLC_SUCCESS; + } - case STREAM_CAN_SEEK: - p_b = (vlc_bool_t*) va_arg( args, vlc_bool_t * ); - *p_b = VLC_FALSE; - return VLC_SUCCESS; + access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek ); + if( !b_aseek ) + { + /* We can't do nothing */ + return VLC_EGENERIC; + } - case STREAM_CAN_FASTSEEK: - p_b = (vlc_bool_t*) va_arg( args, vlc_bool_t * ); - *p_b = VLC_FALSE; - return VLC_SUCCESS; + /* Date the current track */ + p_sys->stream.tk[p_sys->stream.i_tk].i_date = mdate(); - case STREAM_GET_POSITION: - p_i64 = (int64_t*) va_arg( args, int64_t * ); - *p_i64 = p_sys->i_pos; - return VLC_SUCCESS; + /* Try to reuse already read data */ + for( i = 0; i < STREAM_CACHE_TRACK; i++ ) + { + stream_track_t *tk = &p_sys->stream.tk[i]; - case STREAM_SET_POSITION: - return VLC_EGENERIC; + if( i_pos >= tk->i_start && i_pos <= tk->i_end ) + { + /*msg_Dbg( s, "AStreamSeekStream: reusing %d start=%lld end=%lld", + i, + tk->i_start, tk->i_end );*/ + /* Seek at the end of the buffer */ + if( p_access->pf_seek( p_access, tk->i_end ) ) + return VLC_EGENERIC; + + /* That's it */ + p_sys->i_pos = i_pos; + p_sys->stream.i_tk = i; + p_sys->stream.i_offset = i_pos - tk->i_start; + + if( p_sys->stream.i_used < 1024 ) + p_sys->stream.i_used = 1024; + + if( AStreamRefillStream( s ) ) + return VLC_EGENERIC; - case STREAM_GET_MTU: - p_int = (int*) va_arg( args, int * ); - *p_int = 0; return VLC_SUCCESS; + } + } - default: - msg_Err( s, "invalid DStreamControl query=0x%x", i_query ); - return VLC_EGENERIC; + access2_Control( p_access, ACCESS_CAN_SEEK, &b_afastseek ); + /* FIXME compute seek cost (instead of static 'stupid' value) */ + i_maxth = __MIN( p_sys->stream.i_read_size, STREAM_READ_ATONCE / 2 ); + if( !b_afastseek ) + i_maxth *= 3; + + /* FIXME TODO */ +#if 0 + /* Search closest segment TODO */ + for( i = 0; i < STREAM_CACHE_TRACK; i++ ) + { + stream_track_t *tk = &p_sys->stream.tk[i]; + + if( i_pos + i_maxth >= tk->i_start ) + { + msg_Dbg( s, "good segment before current pos, TODO" ); + } + if( i_pos - i_maxth <= tk->i_end ) + { + msg_Dbg( s, "good segment after current pos, TODO" ); + } } +#endif + + /* Nothing good, seek and choose oldest segment */ + if( p_access->pf_seek( p_access, i_pos ) ) + return VLC_EGENERIC; + p_sys->i_pos = i_pos; + + i_new = 0; + for( i = 1; i < STREAM_CACHE_TRACK; i++ ) + { + if( p_sys->stream.tk[i].i_date < p_sys->stream.tk[i_new].i_date ) + i_new = i; + } + + /* Reset the segment */ + p_sys->stream.i_tk = i_new; + p_sys->stream.i_offset = 0; + p_sys->stream.tk[i_new].i_start = i_pos; + p_sys->stream.tk[i_new].i_end = i_pos; + + /* Read data */ + if( p_sys->stream.i_used < STREAM_READ_ATONCE / 2 ) + p_sys->stream.i_used = STREAM_READ_ATONCE / 2; + + if( AStreamRefillStream( s ) ) + return VLC_EGENERIC; + + return VLC_SUCCESS; } -static int DStreamThread ( stream_t *s ) +static int AStreamRefillStream( stream_t *s ) { - d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys; - demux_t *p_demux; + stream_sys_t *p_sys = s->p_sys; + access_t *p_access = p_sys->p_access; + stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk]; + /* We read but won't increase i_start after initial start+offset */ + int i_toread = __MIN( p_sys->stream.i_used, STREAM_CACHE_TRACK_SIZE - (tk->i_end - tk->i_start - p_sys->stream.i_offset ) ); + int64_t i_start, i_stop; - /* Create the demuxer */ + //msg_Dbg( s, "AStreamRefillStream: toread=%d", i_toread ); - if( ( p_demux = demux2_New( s, "", p_sys->psz_name, "", s, p_sys->out ) ) == NULL ) + i_start = mdate(); + while( i_toread > 0 ) { - return VLC_EGENERIC; + int i_off = tk->i_end % STREAM_CACHE_TRACK_SIZE; + int i_read; + + if( s->b_die ) + return VLC_EGENERIC; + + i_read = __MIN( i_toread, STREAM_CACHE_TRACK_SIZE - i_off ); + i_read = p_access->pf_read( p_access, &tk->p_buffer[i_off], i_read ); + //msg_Dbg( s, "AStreamRefillStream: read=%d", i_read ); + if( i_read < 0 ) + { + msleep( STREAM_DATA_WAIT ); + continue; + } + else if( i_read == 0 ) + { + return VLC_EGENERIC; + } + + /* Update end */ + tk->i_end += i_read; + + /* Windows of STREAM_CACHE_TRACK_SIZE */ + if( tk->i_end - tk->i_start > STREAM_CACHE_TRACK_SIZE ) + { + int i_invalid = tk->i_end - tk->i_start - STREAM_CACHE_TRACK_SIZE; + + tk->i_start += i_invalid; + p_sys->stream.i_offset -= i_invalid; + } + + + i_toread -= i_read; + p_sys->stream.i_used -= i_read; + + p_sys->stat.i_bytes += i_read; + p_sys->stat.i_read_count++; } + i_stop = mdate(); + + p_sys->stat.i_read_time += i_stop - i_start; + + return VLC_SUCCESS; +} + +static void AStreamPrebufferStream( stream_t *s ) +{ + stream_sys_t *p_sys = s->p_sys; + access_t *p_access = p_sys->p_access; - vlc_mutex_lock( &p_sys->lock ); - p_sys->p_demux = p_demux; - vlc_mutex_unlock( &p_sys->lock ); + int64_t i_first = 0; + int64_t i_start; - /* Main loop */ - while( !s->b_die && !p_demux->b_die ) + msg_Dbg( s, "pre buffering" ); + i_start = mdate(); + for( ;; ) { - if( p_demux->pf_demux( p_demux ) <= 0 ) + stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk]; + + int64_t i_date = mdate(); + int i_read; + + if( s->b_die || + tk->i_end >= STREAM_CACHE_PREBUFFER_SIZE || + ( i_first > 0 && i_first + STREAM_CACHE_PREBUFFER_LENGTH < i_date ) ) { + int64_t i_byterate; + + /* Update stat */ + p_sys->stat.i_bytes = tk->i_end - tk->i_start; + p_sys->stat.i_read_time = i_date - i_start; + i_byterate = ( I64C(1000000) * p_sys->stat.i_bytes ) / + (p_sys->stat.i_read_time+1); + + msg_Dbg( s, "prebuffering done %lld bytes in %llds - %lld kbytes/s", + p_sys->stat.i_bytes, + p_sys->stat.i_read_time / I64C(1000000), + i_byterate / 1024 ); break; } + + /* */ + i_read = __MIN( p_sys->stream.i_read_size, STREAM_CACHE_TRACK_SIZE - tk->i_end ); + i_read = p_access->pf_read( p_access, &tk->p_buffer[tk->i_end], i_read ); + if( i_read < 0 ) + { + msleep( STREAM_DATA_WAIT ); + continue; + } + else if( i_read == 0 ) + { + /* EOF */ + break; + } + + if( i_first == 0 ) + i_first = mdate(); + + tk->i_end += i_read; + + p_sys->stat.i_read_count++; } - p_demux->b_die = VLC_TRUE; - return VLC_SUCCESS; } +/**************************************************************************** + * stream_ReadLine: + ****************************************************************************/ +/** + * Read from the stream untill first newline. + * \param s Stream handle to read from + * \return A null-terminated string. This must be freed, + */ +/* FIXME don't use stupid MAX_LINE -> do the same than net_ReadLine */ +#define MAX_LINE 1024 +char *stream_ReadLine( stream_t *s ) +{ + uint8_t *p_data; + char *p_line; + int i_data; + int i = 0; + i_data = stream_Peek( s, &p_data, MAX_LINE ); + while( i < i_data && p_data[i] != '\n' && p_data[i] != '\r' ) + { + i++; + } + if( i_data <= 0 ) + { + return NULL; + } + else + { + p_line = malloc( i + 1 ); + if( p_line == NULL ) + { + msg_Err( s, "out of memory" ); + return NULL; + } + i = stream_Read( s, p_line, i + 1 ); + p_line[ i - 1 ] = '\0'; + return p_line; + } +} diff --git a/src/input/subtitles.c b/src/input/subtitles.c index 84f6a2a00449869a7bf0a4bc1c703ada95ac28b2..7c15ae283f4245484f1b920d6812b8103e7fb005 100644 --- a/src/input/subtitles.c +++ b/src/input/subtitles.c @@ -31,8 +31,6 @@ #include #include -#include "ninput.h" - #ifdef HAVE_DIRENT_H # include #else