X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Finput%2Fdecoder.c;h=2bb25fc157c1ff5733bae8de6cfdf27c913263f5;hb=b1c90ec51b105550b6feabf26a43b8afc1b6dda9;hp=0544da0035ecc22e94446ac0aa2a30d332986e51;hpb=dc977392562adad0f1df7ad1c838b26bd9a8798f;p=vlc diff --git a/src/input/decoder.c b/src/input/decoder.c index 0544da0035..2bb25fc157 100644 --- a/src/input/decoder.c +++ b/src/input/decoder.c @@ -44,12 +44,17 @@ #include "audio_output/aout_internal.h" #include "stream_output/stream_output.h" #include "input_internal.h" +#include "input_clock.h" +#include "input_decoder.h" -static decoder_t * CreateDecoder( input_thread_t *, es_format_t *, int, sout_instance_t *p_sout ); -static void DeleteDecoder( decoder_t * ); +#include "../video_output/vout_internal.h" -static void* DecoderThread( vlc_object_t * ); -static int DecoderDecode( decoder_t * p_dec, block_t *p_block ); +static decoder_t *CreateDecoder( input_thread_t *, es_format_t *, int, sout_instance_t *p_sout ); +static void DeleteDecoder( decoder_t * ); + +static void *DecoderThread( vlc_object_t * ); +static int DecoderDecode( decoder_t * p_dec, block_t *p_block ); +static void DecoderOutputChangePause( decoder_t *p_dec, bool b_paused, mtime_t i_date ); /* Buffers allocation callbacks for the decoders */ static aout_buffer_t *aout_new_buffer( decoder_t *, int ); @@ -72,14 +77,11 @@ struct decoder_owner_sys_t int64_t i_preroll_end; input_thread_t *p_input; - - aout_instance_t *p_aout; - aout_input_t *p_aout_input; - - vout_thread_t *p_vout; + input_clock_t *p_clock; vout_thread_t *p_spu_vout; int i_spu_channel; + int64_t i_spu_order; sout_instance_t *p_sout; sout_packetizer_input_t *p_sout_input; @@ -95,9 +97,22 @@ struct decoder_owner_sys_t /* fifo */ block_fifo_t *p_fifo; + /* Lock for communication with decoder thread */ + vlc_mutex_t lock; + vlc_cond_t wait; + + /* -- These variables need locking on write(only) -- */ + aout_instance_t *p_aout; + aout_input_t *p_aout_input; + + vout_thread_t *p_vout; + + /* -- Theses variables need locking on read *and* write -- */ + /* Pause */ + bool b_paused; + mtime_t i_pause_date; /* CC */ bool b_cc_supported; - vlc_mutex_t lock_cc; bool pb_cc_present[4]; decoder_t *pp_cc[4]; }; @@ -136,8 +151,21 @@ int decoder_GetInputAttachments( decoder_t *p_dec, */ mtime_t decoder_GetDisplayDate( decoder_t *p_dec, mtime_t i_ts ) { - VLC_UNUSED(p_dec); - return i_ts; + decoder_owner_sys_t *p_owner = p_dec->p_owner; + + if( !p_owner->p_clock ) + return i_ts; + return input_clock_GetTS( p_owner->p_clock, p_owner->p_input->i_pts_delay, i_ts ); +} +/* decoder_GetDisplayRate: + */ +int decoder_GetDisplayRate( decoder_t *p_dec ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; + + if( !p_owner->p_clock ) + return INPUT_RATE_DEFAULT; + return input_clock_GetRate( p_owner->p_clock ); } /** @@ -148,7 +176,7 @@ mtime_t decoder_GetDisplayDate( decoder_t *p_dec, mtime_t i_ts ) * \return the spawned decoder object */ decoder_t *input_DecoderNew( input_thread_t *p_input, - es_format_t *fmt, sout_instance_t *p_sout ) + es_format_t *fmt, input_clock_t *p_clock, sout_instance_t *p_sout ) { decoder_t *p_dec = NULL; vlc_value_t val; @@ -192,6 +220,8 @@ decoder_t *input_DecoderNew( input_thread_t *p_input, return NULL; } + p_dec->p_owner->p_clock = p_clock; + if( p_sout && p_sout == p_input->p->p_sout && p_input->p->input.b_can_pace_control ) { msg_Dbg( p_input, "stream out mode -> no decoder thread" ); @@ -216,7 +246,7 @@ decoder_t *input_DecoderNew( input_thread_t *p_input, i_priority, false ) ) { msg_Err( p_dec, "cannot spawn decoder thread" ); - module_Unneed( p_dec, p_dec->p_module ); + module_unneed( p_dec, p_dec->p_module ); DeleteDecoder( p_dec ); vlc_object_release( p_dec ); return NULL; @@ -235,10 +265,21 @@ decoder_t *input_DecoderNew( input_thread_t *p_input, */ void input_DecoderDelete( decoder_t *p_dec ) { + decoder_owner_sys_t *p_owner = p_dec->p_owner; + vlc_object_kill( p_dec ); - if( p_dec->p_owner->b_own_thread ) + if( p_owner->b_own_thread ) { + /* Make sure we aren't paused anymore */ + vlc_mutex_lock( &p_owner->lock ); + if( p_owner->b_paused ) + { + p_owner->b_paused = false; + vlc_cond_signal( &p_owner->wait ); + } + vlc_mutex_unlock( &p_owner->lock ); + /* Make sure the thread leaves the function by * sending it an empty block. */ block_t *p_block = block_New( p_dec, 0 ); @@ -246,7 +287,7 @@ void input_DecoderDelete( decoder_t *p_dec ) vlc_thread_join( p_dec ); - /* Don't module_Unneed() here because of the dll loader that wants + /* Don't module_unneed() here because of the dll loader that wants * close() in the same thread than open()/decode() */ } else @@ -254,7 +295,7 @@ void input_DecoderDelete( decoder_t *p_dec ) /* Flush */ input_DecoderDecode( p_dec, NULL ); - module_Unneed( p_dec, p_dec->p_module ); + module_unneed( p_dec, p_dec->p_module ); } /* */ @@ -280,33 +321,36 @@ void input_DecoderDelete( decoder_t *p_dec ) */ void input_DecoderDecode( decoder_t * p_dec, block_t *p_block ) { - if( p_dec->p_owner->b_own_thread ) + decoder_owner_sys_t *p_owner = p_dec->p_owner; + + if( p_owner->b_own_thread ) { - if( p_dec->p_owner->p_input->p->b_out_pace_control ) + if( p_owner->p_input->p->b_out_pace_control ) { /* FIXME !!!!! */ while( !p_dec->b_die && !p_dec->b_error && - block_FifoCount( p_dec->p_owner->p_fifo ) > 10 ) + block_FifoCount( p_owner->p_fifo ) > 10 ) { msleep( 1000 ); } } - else if( block_FifoSize( p_dec->p_owner->p_fifo ) > 50000000 /* 50 MB */ ) + else if( block_FifoSize( p_owner->p_fifo ) > 50000000 /* 50 MB */ ) { /* FIXME: ideally we would check the time amount of data * in the fifo instead of its size. */ msg_Warn( p_dec, "decoder/packetizer fifo full (data not " "consumed quickly enough), resetting fifo!" ); - block_FifoEmpty( p_dec->p_owner->p_fifo ); + block_FifoEmpty( p_owner->p_fifo ); } - block_FifoPut( p_dec->p_owner->p_fifo, p_block ); + block_FifoPut( p_owner->p_fifo, p_block ); } else { - if( p_dec->b_error || (p_block && p_block->i_buffer <= 0) ) + if( p_dec->b_error || ( p_block && p_block->i_buffer <= 0 ) ) { - if( p_block ) block_Release( p_block ); + if( p_block ) + block_Release( p_block ); } else { @@ -317,17 +361,20 @@ void input_DecoderDecode( decoder_t * p_dec, block_t *p_block ) void input_DecoderDiscontinuity( decoder_t * p_dec, bool b_flush ) { + decoder_owner_sys_t *p_owner = p_dec->p_owner; block_t *p_null; /* Empty the fifo */ - if( p_dec->p_owner->b_own_thread && b_flush ) - block_FifoEmpty( p_dec->p_owner->p_fifo ); + if( p_owner->b_own_thread && b_flush ) + block_FifoEmpty( p_owner->p_fifo ); /* Send a special block */ p_null = block_New( p_dec, 128 ); p_null->i_flags |= BLOCK_FLAG_DISCONTINUITY; + if( b_flush && p_dec->fmt_in.i_cat == SPU_ES ) + p_null->i_flags |= BLOCK_FLAG_CORE_FLUSH; /* FIXME check for p_packetizer or b_packitized from es_format_t of input ? */ - if( p_dec->p_owner->p_packetizer && b_flush ) + if( p_owner->p_packetizer && b_flush ) p_null->i_flags |= BLOCK_FLAG_CORRUPTED; memset( p_null->p_buffer, 0, p_null->i_buffer ); @@ -336,8 +383,8 @@ void input_DecoderDiscontinuity( decoder_t * p_dec, bool b_flush ) bool input_DecoderEmpty( decoder_t * p_dec ) { - if( p_dec->p_owner->b_own_thread - && block_FifoCount( p_dec->p_owner->p_fifo ) > 0 ) + if( p_dec->p_owner->b_own_thread && + block_FifoCount( p_dec->p_owner->p_fifo ) > 0 ) { return false; } @@ -346,12 +393,13 @@ bool input_DecoderEmpty( decoder_t * p_dec ) void input_DecoderIsCcPresent( decoder_t *p_dec, bool pb_present[4] ) { + decoder_owner_sys_t *p_owner = p_dec->p_owner; int i; - vlc_mutex_lock( &p_dec->p_owner->lock_cc ); + vlc_mutex_lock( &p_owner->lock ); for( i = 0; i < 4; i++ ) - pb_present[i] = p_dec->p_owner->pb_cc_present[i]; - vlc_mutex_unlock( &p_dec->p_owner->lock_cc ); + pb_present[i] = p_owner->pb_cc_present[i]; + vlc_mutex_unlock( &p_owner->lock ); } int input_DecoderSetCcState( decoder_t *p_dec, bool b_decode, int i_channel ) { @@ -374,7 +422,7 @@ int input_DecoderSetCcState( decoder_t *p_dec, bool b_decode, int i_channel ) es_format_t fmt; es_format_Init( &fmt, SPU_ES, fcc[i_channel] ); - p_cc = CreateDecoder( p_owner->p_input, &fmt, VLC_OBJECT_DECODER, p_dec->p_owner->p_sout ); + p_cc = CreateDecoder( p_owner->p_input, &fmt, VLC_OBJECT_DECODER, p_owner->p_sout ); if( !p_cc ) { msg_Err( p_dec, "could not create decoder" ); @@ -389,24 +437,25 @@ int input_DecoderSetCcState( decoder_t *p_dec, bool b_decode, int i_channel ) vlc_object_release( p_cc ); return VLC_EGENERIC; } + p_cc->p_owner->p_clock = p_owner->p_clock; - vlc_mutex_lock( &p_owner->lock_cc ); - p_dec->p_owner->pp_cc[i_channel] = p_cc; - vlc_mutex_unlock( &p_owner->lock_cc ); + vlc_mutex_lock( &p_owner->lock ); + p_owner->pp_cc[i_channel] = p_cc; + vlc_mutex_unlock( &p_owner->lock ); } else { decoder_t *p_cc; - vlc_mutex_lock( &p_owner->lock_cc ); - p_cc = p_dec->p_owner->pp_cc[i_channel]; - p_dec->p_owner->pp_cc[i_channel] = NULL; - vlc_mutex_unlock( &p_dec->p_owner->lock_cc ); + vlc_mutex_lock( &p_owner->lock ); + p_cc = p_owner->pp_cc[i_channel]; + p_owner->pp_cc[i_channel] = NULL; + vlc_mutex_unlock( &p_owner->lock ); if( p_cc ) { vlc_object_kill( p_cc ); - module_Unneed( p_cc, p_cc->p_module ); + module_unneed( p_cc, p_cc->p_module ); DeleteDecoder( p_cc ); vlc_object_release( p_cc ); } @@ -421,12 +470,27 @@ int input_DecoderGetCcState( decoder_t *p_dec, bool *pb_decode, int i_channel ) if( i_channel < 0 || i_channel >= 4 || !p_owner->pb_cc_present[i_channel] ) return VLC_EGENERIC; - vlc_mutex_lock( &p_owner->lock_cc ); - *pb_decode = p_dec->p_owner->pp_cc[i_channel] != NULL; - vlc_mutex_unlock( &p_dec->p_owner->lock_cc ); + vlc_mutex_lock( &p_owner->lock ); + *pb_decode = p_owner->pp_cc[i_channel] != NULL; + vlc_mutex_unlock( &p_owner->lock ); return VLC_EGENERIC; } +void input_DecoderChangePause( decoder_t *p_dec, bool b_paused, mtime_t i_date ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; + + vlc_mutex_lock( &p_owner->lock ); + + assert( (!p_owner->b_paused) != (!b_paused) ); + p_owner->b_paused = b_paused; + p_owner->i_pause_date = i_date; + if( p_owner->b_own_thread ) + vlc_cond_signal( &p_owner->wait ); + + DecoderOutputChangePause( p_dec, b_paused, i_date ); + vlc_mutex_unlock( &p_owner->lock ); +} /** * Create a decoder object * @@ -474,6 +538,7 @@ static decoder_t * CreateDecoder( input_thread_t *p_input, p_dec->p_owner->p_vout = NULL; p_dec->p_owner->p_spu_vout = NULL; p_dec->p_owner->i_spu_channel = 0; + p_dec->p_owner->i_spu_order = 0; p_dec->p_owner->p_sout = p_sout; p_dec->p_owner->p_sout_input = NULL; p_dec->p_owner->p_packetizer = NULL; @@ -500,9 +565,9 @@ static decoder_t * CreateDecoder( input_thread_t *p_input, /* Find a suitable decoder/packetizer module */ if( i_object_type == VLC_OBJECT_DECODER ) - p_dec->p_module = module_Need( p_dec, "decoder", "$codec", 0 ); + p_dec->p_module = module_need( p_dec, "decoder", "$codec", 0 ); else - p_dec->p_module = module_Need( p_dec, "packetizer", "$packetizer", 0 ); + p_dec->p_module = module_need( p_dec, "packetizer", "$packetizer", 0 ); /* Check if decoder requires already packetized data */ if( i_object_type == VLC_OBJECT_DECODER && @@ -521,7 +586,7 @@ static decoder_t * CreateDecoder( input_thread_t *p_input, vlc_object_attach( p_dec->p_owner->p_packetizer, p_input ); p_dec->p_owner->p_packetizer->p_module = - module_Need( p_dec->p_owner->p_packetizer, + module_need( p_dec->p_owner->p_packetizer, "packetizer", "$packetizer", 0 ); if( !p_dec->p_owner->p_packetizer->p_module ) @@ -560,7 +625,10 @@ static decoder_t * CreateDecoder( input_thread_t *p_input, p_owner->b_cc_supported = true; } - vlc_mutex_init( &p_owner->lock_cc ); + vlc_mutex_init( &p_owner->lock ); + vlc_cond_init( &p_owner->wait ); + p_owner->b_paused = false; + p_owner->i_pause_date = 0; for( i = 0; i < 4; i++ ) { p_owner->pb_cc_present[i] = false; @@ -573,41 +641,75 @@ static decoder_t * CreateDecoder( input_thread_t *p_input, * The decoding main loop * * \param p_dec the decoder - * \return 0 */ static void* DecoderThread( vlc_object_t *p_this ) { - decoder_t * p_dec = (decoder_t *)p_this; + decoder_t *p_dec = (decoder_t *)p_this; + decoder_owner_sys_t *p_owner = p_dec->p_owner; + block_t *p_block; + int canc = vlc_savecancel(); /* The decoder's main loop */ - while( !p_dec->b_die && !p_dec->b_error ) + while( vlc_object_alive( p_dec ) && !p_dec->b_error ) { - if( ( p_block = block_FifoGet( p_dec->p_owner->p_fifo ) ) == NULL ) + if( ( p_block = block_FifoGet( p_owner->p_fifo ) ) == NULL ) { p_dec->b_error = 1; break; } + if( DecoderDecode( p_dec, p_block ) != VLC_SUCCESS ) - { break; - } } - while( !p_dec->b_die ) + while( vlc_object_alive( p_dec ) ) { /* Trash all received PES packets */ - p_block = block_FifoGet( p_dec->p_owner->p_fifo ); - if( p_block ) block_Release( p_block ); + p_block = block_FifoGet( p_owner->p_fifo ); + if( p_block ) + block_Release( p_block ); } /* We do it here because of the dll loader that wants close() in the * same thread than open()/decode() */ - module_Unneed( p_dec, p_dec->p_module ); - + module_unneed( p_dec, p_dec->p_module ); + vlc_restorecancel( canc ); return NULL; } +static void DecoderWaitUnpause( decoder_t *p_dec ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; + + vlc_mutex_lock( &p_owner->lock ); + + while( p_owner->b_paused ) + vlc_cond_wait( &p_owner->wait, &p_owner->lock ); + + vlc_mutex_unlock( &p_owner->lock ); +} + +static void DecoderOutputChangePause( decoder_t *p_dec, bool b_paused, mtime_t i_date ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; + + /* XXX only audio and video output have to be paused. + * - for sout it is useless + * - for subs, it is done by the vout + */ + if( p_dec->fmt_in.i_cat == AUDIO_ES ) + { + // TODO + //if( p_own->p_vout ) + // aout_ChangePause( p_own->p_aout, p_own->p_aout_input, b_paused, i_date ); + } + else if( p_dec->fmt_in.i_cat == VIDEO_ES ) + { + if( p_owner->p_vout ) + vout_ChangePause( p_owner->p_vout, b_paused, i_date ); + } +} static inline void DecoderUpdatePreroll( int64_t *pi_preroll, const block_t *p ) { if( p->i_flags & (BLOCK_FLAG_PREROLL|BLOCK_FLAG_DISCONTINUITY) ) @@ -617,16 +719,106 @@ static inline void DecoderUpdatePreroll( int64_t *pi_preroll, const block_t *p ) else if( p->i_dts > 0 ) *pi_preroll = __MIN( *pi_preroll, p->i_dts ); } + +static mtime_t DecoderTeletextFixTs( mtime_t i_ts, mtime_t i_ts_delay ) +{ + mtime_t current_date = mdate(); + + /* FIXME I don't really like that, es_out SHOULD do it using the video pts */ + if( !i_ts || i_ts > current_date + 10000000 || i_ts < current_date ) + { + /* ETSI EN 300 472 Annex A : do not take into account the PTS + * for teletext streams. */ + return current_date + 400000 + i_ts_delay; + } + return i_ts; +} + +static void DecoderSoutBufferFixTs( block_t *p_block, + input_clock_t *p_clock, mtime_t i_ts_delay, + bool b_teletext ) +{ + assert( p_clock ); + + p_block->i_rate = input_clock_GetRate( p_clock ); + + if( p_block->i_dts > 0 ) + p_block->i_dts = input_clock_GetTS( p_clock, i_ts_delay, p_block->i_dts ); + + if( p_block->i_pts > 0 ) + p_block->i_pts = input_clock_GetTS( p_clock, i_ts_delay, p_block->i_pts ); + + if( p_block->i_length > 0 ) + p_block->i_length = ( p_block->i_length * p_block->i_rate + + INPUT_RATE_DEFAULT-1 ) / INPUT_RATE_DEFAULT; + + if( b_teletext ) + p_block->i_pts = DecoderTeletextFixTs( p_block->i_pts, i_ts_delay ); +} +static void DecoderAoutBufferFixTs( aout_buffer_t *p_buffer, + input_clock_t *p_clock, mtime_t i_ts_delay ) +{ + /* sout display module does not set clock */ + if( !p_clock ) + return; + + if( p_buffer->start_date ) + p_buffer->start_date = input_clock_GetTS( p_clock, i_ts_delay, p_buffer->start_date ); + + if( p_buffer->end_date ) + p_buffer->end_date = input_clock_GetTS( p_clock, i_ts_delay, p_buffer->end_date ); +} +static void DecoderVoutBufferFixTs( picture_t *p_picture, + input_clock_t *p_clock, mtime_t i_ts_delay ) +{ + /* sout display module does not set clock */ + if( !p_clock ) + return; + + if( p_picture->date ) + p_picture->date = input_clock_GetTS( p_clock, i_ts_delay, p_picture->date ); +} +static void DecoderSpuBufferFixTs( subpicture_t *p_subpic, + input_clock_t *p_clock, mtime_t i_ts_delay, + bool b_teletext ) +{ + bool b_ephemere = p_subpic->i_start == p_subpic->i_stop; + + /* sout display module does not set clock */ + if( !p_clock ) + return; + + if( p_subpic->i_start ) + p_subpic->i_start = input_clock_GetTS( p_clock, i_ts_delay, p_subpic->i_start ); + + if( p_subpic->i_stop ) + p_subpic->i_stop = input_clock_GetTS( p_clock, i_ts_delay, p_subpic->i_stop ); + + /* Do not create ephemere picture because of rounding errors */ + if( !b_ephemere && p_subpic->i_start == p_subpic->i_stop ) + p_subpic->i_stop++; + + if( b_teletext ) + p_subpic->i_start = DecoderTeletextFixTs( p_subpic->i_start, i_ts_delay ); +} + static void DecoderDecodeAudio( decoder_t *p_dec, block_t *p_block ) { - input_thread_t *p_input = p_dec->p_owner->p_input; - const int i_rate = p_block->i_rate; + decoder_owner_sys_t *p_owner = p_dec->p_owner; + input_thread_t *p_input = p_owner->p_input; + input_clock_t *p_clock = p_owner->p_clock; aout_buffer_t *p_aout_buf; + int i_decoded = 0; + int i_lost = 0; + int i_played = 0; while( (p_aout_buf = p_dec->pf_decode_audio( p_dec, &p_block )) ) { - aout_instance_t *p_aout = p_dec->p_owner->p_aout; - aout_input_t *p_aout_input = p_dec->p_owner->p_aout_input; + aout_instance_t *p_aout = p_owner->p_aout; + aout_input_t *p_aout_input = p_owner->p_aout_input; + int i_rate; + int i_lost; + int i_played; if( p_dec->b_die ) { @@ -636,27 +828,83 @@ static void DecoderDecodeAudio( decoder_t *p_dec, block_t *p_block ) block_Release( p_block ); break; } - vlc_mutex_lock( &p_input->p->counters.counters_lock ); - stats_UpdateInteger( p_dec, p_input->p->counters.p_decoded_audio, 1, NULL ); - vlc_mutex_unlock( &p_input->p->counters.counters_lock ); + i_decoded++; - if( p_aout_buf->start_date < p_dec->p_owner->i_preroll_end ) + if( p_aout_buf->start_date < p_owner->i_preroll_end ) { aout_DecDeleteBuffer( p_aout, p_aout_input, p_aout_buf ); continue; } - if( p_dec->p_owner->i_preroll_end > 0 ) + if( p_owner->i_preroll_end > 0 ) { /* FIXME TODO flush audio output (don't know how to do that) */ msg_Dbg( p_dec, "End of audio preroll" ); - p_dec->p_owner->i_preroll_end = -1; + p_owner->i_preroll_end = -1; } - aout_DecPlay( p_aout, p_aout_input, p_aout_buf, i_rate ); + + DecoderWaitUnpause( p_dec ); + + DecoderAoutBufferFixTs( p_aout_buf, p_clock, p_input->i_pts_delay ); + + if( p_clock ) + i_rate = input_clock_GetRate( p_clock ); + else if( p_block && p_block->i_rate > 0 ) + i_rate = p_block->i_rate; + else + i_rate = INPUT_RATE_DEFAULT; + + /* FIXME TODO take care of audio-delay for mdate check */ + const mtime_t i_max_date = mdate() + p_input->i_pts_delay + AOUT_MAX_ADVANCE_TIME; + + if( p_aout_buf->start_date > 0 && + p_aout_buf->start_date <= i_max_date && + i_rate >= INPUT_RATE_DEFAULT/AOUT_MAX_INPUT_RATE && + i_rate <= INPUT_RATE_DEFAULT*AOUT_MAX_INPUT_RATE ) + { + /* Wait if we are too early + * FIXME that's plain ugly to do it here */ + mwait( p_aout_buf->start_date - AOUT_MAX_PREPARE_TIME ); + + if( !aout_DecPlay( p_aout, p_aout_input, p_aout_buf, i_rate ) ) + i_played++; + i_lost += aout_DecGetResetLost( p_aout, p_aout_input ); + } + else + { + if( p_aout_buf->start_date <= 0 ) + { + msg_Warn( p_dec, "non-dated audio buffer received" ); + } + else if( p_aout_buf->start_date > i_max_date ) + { + msg_Warn( p_aout, "received buffer in the future (%"PRId64")", + p_aout_buf->start_date - mdate() ); + } + i_lost++; + aout_DecDeleteBuffer( p_aout, p_aout_input, p_aout_buf ); + } + + } + + /* Update ugly stat */ + if( i_decoded > 0 || i_lost > 0 || i_played > 0 ) + { + vlc_mutex_lock( &p_input->p->counters.counters_lock); + + stats_UpdateInteger( p_dec, p_input->p->counters.p_lost_abuffers, + i_lost, NULL ); + stats_UpdateInteger( p_dec, p_input->p->counters.p_played_abuffers, + i_played, NULL ); + stats_UpdateInteger( p_dec, p_input->p->counters.p_decoded_audio, + i_decoded, NULL ); + + vlc_mutex_unlock( &p_input->p->counters.counters_lock); } } static void DecoderGetCc( decoder_t *p_dec, decoder_t *p_dec_cc ) { + decoder_owner_sys_t *p_owner = p_dec->p_owner; block_t *p_cc; bool pb_present[4]; int i; @@ -665,33 +913,33 @@ static void DecoderGetCc( decoder_t *p_dec, decoder_t *p_dec_cc ) assert( p_dec_cc->pf_get_cc != NULL ); /* Do not try retreiving CC if not wanted (sout) or cannot be retreived */ - if( !p_dec->p_owner->b_cc_supported ) + if( !p_owner->b_cc_supported ) return; p_cc = p_dec_cc->pf_get_cc( p_dec_cc, pb_present ); if( !p_cc ) return; - vlc_mutex_lock( &p_dec->p_owner->lock_cc ); + vlc_mutex_lock( &p_owner->lock ); for( i = 0, i_cc_decoder = 0; i < 4; i++ ) { - p_dec->p_owner->pb_cc_present[i] |= pb_present[i]; - if( p_dec->p_owner->pp_cc[i] ) + p_owner->pb_cc_present[i] |= pb_present[i]; + if( p_owner->pp_cc[i] ) i_cc_decoder++; } for( i = 0; i < 4; i++ ) { - if( !p_dec->p_owner->pp_cc[i] ) + if( !p_owner->pp_cc[i] ) continue; if( i_cc_decoder > 1 ) - DecoderDecode( p_dec->p_owner->pp_cc[i], block_Duplicate( p_cc ) ); + DecoderDecode( p_owner->pp_cc[i], block_Duplicate( p_cc ) ); else - DecoderDecode( p_dec->p_owner->pp_cc[i], p_cc ); + DecoderDecode( p_owner->pp_cc[i], p_cc ); i_cc_decoder--; } - vlc_mutex_unlock( &p_dec->p_owner->lock_cc ); + vlc_mutex_unlock( &p_owner->lock ); } static void VoutDisplayedPicture( vout_thread_t *p_vout, picture_t *p_pic ) { @@ -709,6 +957,7 @@ static void VoutDisplayedPicture( vout_thread_t *p_vout, picture_t *p_pic ) else { p_pic->i_status = DESTROYED_PICTURE; + picture_CleanupQuant( p_pic ); p_vout->i_heap_size--; } @@ -733,6 +982,7 @@ static void VoutFlushPicture( vout_thread_t *p_vout ) vlc_mutex_unlock( &p_vout->picture_lock ); } +#if 0 static void DecoderOptimizePtsDelay( decoder_t *p_dec ) { input_thread_t *p_input = p_dec->p_owner->p_input; @@ -764,7 +1014,7 @@ static void DecoderOptimizePtsDelay( decoder_t *p_dec ) return; /* Try to find if we can reduce the pts - * This first draft is way to simple, and we can't say if the + * This first draft is way too simple, and we can't say if the * algo will converge. It's also full of constants. * But this simple algo allows to reduce the latency * to the minimum. @@ -829,15 +1079,20 @@ static void DecoderOptimizePtsDelay( decoder_t *p_dec ) vlc_mutex_unlock( &p_vout->picture_lock ); } } +#endif static void DecoderDecodeVideo( decoder_t *p_dec, block_t *p_block ) { - input_thread_t *p_input = p_dec->p_owner->p_input; + decoder_owner_sys_t *p_owner = p_dec->p_owner; + input_thread_t *p_input = p_owner->p_input; picture_t *p_pic; + int i_lost = 0; + int i_decoded = 0; + int i_displayed = 0; while( (p_pic = p_dec->pf_decode_video( p_dec, &p_block )) ) { - vout_thread_t *p_vout = p_dec->p_owner->p_vout; + vout_thread_t *p_vout = p_owner->p_vout; if( p_dec->b_die ) { /* It prevent freezing VLC in case of broken decoder */ @@ -847,33 +1102,76 @@ static void DecoderDecodeVideo( decoder_t *p_dec, block_t *p_block ) break; } - vlc_mutex_lock( &p_input->p->counters.counters_lock ); - stats_UpdateInteger( p_dec, p_input->p->counters.p_decoded_video, 1, NULL ); - vlc_mutex_unlock( &p_input->p->counters.counters_lock ); + i_decoded++; - if( p_pic->date < p_dec->p_owner->i_preroll_end ) + if( p_pic->date < p_owner->i_preroll_end ) { VoutDisplayedPicture( p_vout, p_pic ); continue; } - if( p_dec->p_owner->i_preroll_end > 0 ) + if( p_owner->i_preroll_end > 0 ) { msg_Dbg( p_dec, "End of video preroll" ); if( p_vout ) VoutFlushPicture( p_vout ); /* */ - p_dec->p_owner->i_preroll_end = -1; + p_owner->i_preroll_end = -1; } - if( ( !p_dec->p_owner->p_packetizer || !p_dec->p_owner->p_packetizer->pf_get_cc ) && p_dec->pf_get_cc ) + if( p_dec->pf_get_cc && + ( !p_owner->p_packetizer || !p_owner->p_packetizer->pf_get_cc ) ) DecoderGetCc( p_dec, p_dec ); - vout_DatePicture( p_vout, p_pic, p_pic->date ); + DecoderWaitUnpause( p_dec ); - DecoderOptimizePtsDelay( p_dec ); + DecoderVoutBufferFixTs( p_pic, p_owner->p_clock, p_input->i_pts_delay ); + + /* Video is never delayed so simple */ + const mtime_t i_max_date = mdate() + p_input->i_pts_delay + VOUT_BOGUS_DELAY; + + if( p_pic->date > 0 && p_pic->date < i_max_date ) + { + vout_DatePicture( p_vout, p_pic, p_pic->date ); - vout_DisplayPicture( p_vout, p_pic ); + /* Re-enable it but do it right this time */ + //DecoderOptimizePtsDelay( p_dec ); + vout_DisplayPicture( p_vout, p_pic ); + } + else + { + if( p_pic->date <= 0 ) + { + msg_Warn( p_vout, "non-dated video buffer received" ); + } + else + { + msg_Warn( p_vout, "early picture skipped (%"PRId64")", + p_pic->date - mdate() ); + } + i_lost++; + VoutDisplayedPicture( p_vout, p_pic ); + } + int i_tmp_display; + int i_tmp_lost; + vout_GetResetStatistic( p_vout, &i_tmp_display, &i_tmp_lost ); + + i_displayed += i_tmp_display; + i_lost += i_tmp_lost; + } + if( i_decoded > 0 || i_lost > 0 || i_displayed > 0 ) + { + vlc_mutex_lock( &p_input->p->counters.counters_lock ); + + stats_UpdateInteger( p_dec, p_input->p->counters.p_decoded_video, + i_decoded, NULL ); + stats_UpdateInteger( p_dec, p_input->p->counters.p_lost_pictures, + i_lost , NULL); + + stats_UpdateInteger( p_dec, p_input->p->counters.p_displayed_pictures, + i_displayed, NULL); + + vlc_mutex_unlock( &p_input->p->counters.counters_lock ); } } @@ -886,8 +1184,8 @@ static void DecoderDecodeVideo( decoder_t *p_dec, block_t *p_block ) */ static int DecoderDecode( decoder_t *p_dec, block_t *p_block ) { - decoder_owner_sys_t *p_sys = (decoder_owner_sys_t *)p_dec->p_owner; - const int i_rate = p_block ? p_block->i_rate : INPUT_RATE_DEFAULT; + decoder_owner_sys_t *p_owner = (decoder_owner_sys_t *)p_dec->p_owner; + const bool b_telx = p_dec->fmt_in.i_codec == VLC_FOURCC('t','e','l','x'); if( p_block && p_block->i_buffer <= 0 ) { @@ -903,28 +1201,28 @@ static int DecoderDecode( decoder_t *p_dec, block_t *p_block ) while( ( p_sout_block = p_dec->pf_packetize( p_dec, p_block ? &p_block : NULL ) ) ) { - if( !p_dec->p_owner->p_sout_input ) + if( !p_owner->p_sout_input ) { - es_format_Copy( &p_dec->p_owner->sout, &p_dec->fmt_out ); + es_format_Copy( &p_owner->sout, &p_dec->fmt_out ); - 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; + p_owner->sout.i_group = p_dec->fmt_in.i_group; + p_owner->sout.i_id = p_dec->fmt_in.i_id; if( p_dec->fmt_in.psz_language ) { - if( p_dec->p_owner->sout.psz_language ) - free( p_dec->p_owner->sout.psz_language ); - p_dec->p_owner->sout.psz_language = + if( p_owner->sout.psz_language ) + free( p_owner->sout.psz_language ); + p_owner->sout.psz_language = strdup( p_dec->fmt_in.psz_language ); } - p_dec->p_owner->p_sout_input = - sout_InputNew( p_dec->p_owner->p_sout, - &p_dec->p_owner->sout ); + p_owner->p_sout_input = + sout_InputNew( p_owner->p_sout, + &p_owner->sout ); - if( p_dec->p_owner->p_sout_input == NULL ) + if( p_owner->p_sout_input == NULL ) { msg_Err( p_dec, "cannot create packetizer output (%4.4s)", - (char *)&p_dec->p_owner->sout.i_codec ); + (char *)&p_owner->sout.i_codec ); p_dec->b_error = true; while( p_sout_block ) @@ -942,26 +1240,30 @@ static int DecoderDecode( decoder_t *p_dec, block_t *p_block ) block_t *p_next = p_sout_block->p_next; p_sout_block->p_next = NULL; - p_sout_block->i_rate = i_rate; - sout_InputSendBuffer( p_dec->p_owner->p_sout_input, + DecoderWaitUnpause( p_dec ); + + DecoderSoutBufferFixTs( p_sout_block, + p_owner->p_clock, p_owner->p_input->i_pts_delay, b_telx ); + + sout_InputSendBuffer( p_owner->p_sout_input, p_sout_block ); p_sout_block = p_next; } /* For now it's enough, as only sout impact on this flag */ - if( p_dec->p_owner->p_sout->i_out_pace_nocontrol > 0 && - p_dec->p_owner->p_input->p->b_out_pace_control ) + if( p_owner->p_sout->i_out_pace_nocontrol > 0 && + p_owner->p_input->p->b_out_pace_control ) { msg_Dbg( p_dec, "switching to sync mode" ); - p_dec->p_owner->p_input->p->b_out_pace_control = false; + p_owner->p_input->p->b_out_pace_control = false; } - else if( p_dec->p_owner->p_sout->i_out_pace_nocontrol <= 0 && - !p_dec->p_owner->p_input->p->b_out_pace_control ) + else if( p_owner->p_sout->i_out_pace_nocontrol <= 0 && + !p_owner->p_input->p->b_out_pace_control ) { msg_Dbg( p_dec, "switching to async mode" ); - p_dec->p_owner->p_input->p->b_out_pace_control = true; + p_owner->p_input->p->b_out_pace_control = true; } } } @@ -970,12 +1272,12 @@ static int DecoderDecode( decoder_t *p_dec, block_t *p_block ) if( p_dec->fmt_in.i_cat == AUDIO_ES ) { if( p_block ) - DecoderUpdatePreroll( &p_dec->p_owner->i_preroll_end, p_block ); + DecoderUpdatePreroll( &p_owner->i_preroll_end, p_block ); - if( p_dec->p_owner->p_packetizer ) + if( p_owner->p_packetizer ) { block_t *p_packetized_block; - decoder_t *p_packetizer = p_dec->p_owner->p_packetizer; + decoder_t *p_packetizer = p_owner->p_packetizer; while( (p_packetized_block = p_packetizer->pf_packetize( p_packetizer, p_block ? &p_block : NULL )) ) @@ -990,7 +1292,6 @@ static int DecoderDecode( decoder_t *p_dec, block_t *p_block ) { block_t *p_next = p_packetized_block->p_next; p_packetized_block->p_next = NULL; - p_packetized_block->i_rate = i_rate; DecoderDecodeAudio( p_dec, p_packetized_block ); @@ -1006,12 +1307,12 @@ static int DecoderDecode( decoder_t *p_dec, block_t *p_block ) else if( p_dec->fmt_in.i_cat == VIDEO_ES ) { if( p_block ) - DecoderUpdatePreroll( &p_dec->p_owner->i_preroll_end, p_block ); + DecoderUpdatePreroll( &p_owner->i_preroll_end, p_block ); - if( p_dec->p_owner->p_packetizer ) + if( p_owner->p_packetizer ) { block_t *p_packetized_block; - decoder_t *p_packetizer = p_dec->p_owner->p_packetizer; + decoder_t *p_packetizer = p_owner->p_packetizer; while( (p_packetized_block = p_packetizer->pf_packetize( p_packetizer, p_block ? &p_block : NULL )) ) @@ -1028,7 +1329,6 @@ static int DecoderDecode( decoder_t *p_dec, block_t *p_block ) { block_t *p_next = p_packetized_block->p_next; p_packetized_block->p_next = NULL; - p_packetized_block->i_rate = i_rate; DecoderDecodeVideo( p_dec, p_packetized_block ); @@ -1043,12 +1343,29 @@ static int DecoderDecode( decoder_t *p_dec, block_t *p_block ) } else if( p_dec->fmt_in.i_cat == SPU_ES ) { - input_thread_t *p_input = p_dec->p_owner->p_input; + input_thread_t *p_input = p_owner->p_input; vout_thread_t *p_vout; subpicture_t *p_spu; + bool b_flushing = p_owner->i_preroll_end == INT64_MAX; + bool b_flush = false; if( p_block ) - DecoderUpdatePreroll( &p_dec->p_owner->i_preroll_end, p_block ); + { + DecoderUpdatePreroll( &p_owner->i_preroll_end, p_block ); + b_flush = (p_block->i_flags & BLOCK_FLAG_CORE_FLUSH) != 0; + } + + if( !b_flushing && b_flush && p_owner->p_spu_vout ) + { + p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE ); + + if( p_vout && p_owner->p_spu_vout == p_vout ) + spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR, + p_owner->i_spu_channel ); + + if( p_vout ) + vlc_object_release( p_vout ); + } while( (p_spu = p_dec->pf_decode_sub( p_dec, p_block ? &p_block : NULL ) ) ) { @@ -1057,14 +1374,21 @@ static int DecoderDecode( decoder_t *p_dec, block_t *p_block ) vlc_mutex_unlock( &p_input->p->counters.counters_lock ); p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE ); - if( p_vout && p_sys->p_spu_vout == p_vout ) + if( p_vout && p_owner->p_spu_vout == p_vout ) { - /* Prerool does not work very well with subtitle */ - if( p_spu->i_start < p_dec->p_owner->i_preroll_end && - ( p_spu->i_stop <= 0 || p_spu->i_stop < p_dec->p_owner->i_preroll_end ) ) - spu_DestroySubpicture( p_vout->p_spu, p_spu ); + /* Preroll does not work very well with subtitle */ + if( p_spu->i_start < p_owner->i_preroll_end && + ( p_spu->i_stop <= 0 || p_spu->i_stop < p_owner->i_preroll_end ) ) + { + subpicture_Delete( p_spu ); + } else + { + DecoderWaitUnpause( p_dec ); + + DecoderSpuBufferFixTs( p_spu, p_owner->p_clock, p_input->i_pts_delay, b_telx ); spu_DisplaySubpicture( p_vout->p_spu, p_spu ); + } } else { @@ -1091,47 +1415,50 @@ static int DecoderDecode( decoder_t *p_dec, block_t *p_block ) */ static void DeleteDecoder( decoder_t * p_dec ) { + decoder_owner_sys_t *p_owner = p_dec->p_owner; + msg_Dbg( p_dec, "killing decoder fourcc `%4.4s', %u PES in FIFO", (char*)&p_dec->fmt_in.i_codec, - (unsigned)block_FifoCount( p_dec->p_owner->p_fifo ) ); + (unsigned)block_FifoCount( p_owner->p_fifo ) ); /* Free all packets still in the decoder fifo. */ - block_FifoEmpty( p_dec->p_owner->p_fifo ); - block_FifoRelease( p_dec->p_owner->p_fifo ); + block_FifoEmpty( p_owner->p_fifo ); + block_FifoRelease( p_owner->p_fifo ); /* Cleanup */ - if( p_dec->p_owner->p_aout_input ) - aout_DecDelete( p_dec->p_owner->p_aout, p_dec->p_owner->p_aout_input ); - if( p_dec->p_owner->p_aout ) + if( p_owner->p_aout_input ) + aout_DecDelete( p_owner->p_aout, p_owner->p_aout_input ); + if( p_owner->p_aout ) { - vlc_object_release( p_dec->p_owner->p_aout ); - p_dec->p_owner->p_aout = NULL; + vlc_object_release( p_owner->p_aout ); + p_owner->p_aout = NULL; } - if( p_dec->p_owner->p_vout ) + if( p_owner->p_vout ) { int i_pic; -#define p_pic p_dec->p_owner->p_vout->render.pp_picture[i_pic] +#define p_pic p_owner->p_vout->render.pp_picture[i_pic] /* Hack to make sure all the the pictures are freed by the decoder */ - for( i_pic = 0; i_pic < p_dec->p_owner->p_vout->render.i_pictures; + for( i_pic = 0; i_pic < p_owner->p_vout->render.i_pictures; i_pic++ ) { if( p_pic->i_status == RESERVED_PICTURE ) - vout_DestroyPicture( p_dec->p_owner->p_vout, p_pic ); + vout_DestroyPicture( p_owner->p_vout, p_pic ); if( p_pic->i_refcount > 0 ) - vout_UnlinkPicture( p_dec->p_owner->p_vout, p_pic ); + vout_UnlinkPicture( p_owner->p_vout, p_pic ); } #undef p_pic /* We are about to die. Reattach video output to p_vlc. */ - vout_Request( p_dec, p_dec->p_owner->p_vout, NULL ); + vout_Request( p_dec, p_owner->p_vout, NULL ); + var_SetBool( p_owner->p_input, "intf-change-vout", true ); } #ifdef ENABLE_SOUT - if( p_dec->p_owner->p_sout_input ) + if( p_owner->p_sout_input ) { - sout_InputDelete( p_dec->p_owner->p_sout_input ); - es_format_Clean( &p_dec->p_owner->sout ); + sout_InputDelete( p_owner->p_sout_input ); + es_format_Clean( &p_owner->sout ); } #endif @@ -1140,10 +1467,10 @@ static void DeleteDecoder( decoder_t * p_dec ) vout_thread_t *p_vout; p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE ); - if( p_vout ) + if( p_vout && p_owner->p_spu_vout == p_vout ) { spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR, - p_dec->p_owner->i_spu_channel ); + p_owner->i_spu_channel ); vlc_object_release( p_vout ); } } @@ -1151,21 +1478,22 @@ static void DeleteDecoder( decoder_t * p_dec ) es_format_Clean( &p_dec->fmt_in ); es_format_Clean( &p_dec->fmt_out ); - if( p_dec->p_owner->p_packetizer ) + if( p_owner->p_packetizer ) { - module_Unneed( p_dec->p_owner->p_packetizer, - p_dec->p_owner->p_packetizer->p_module ); - es_format_Clean( &p_dec->p_owner->p_packetizer->fmt_in ); - es_format_Clean( &p_dec->p_owner->p_packetizer->fmt_out ); - vlc_object_detach( p_dec->p_owner->p_packetizer ); - vlc_object_release( p_dec->p_owner->p_packetizer ); + module_unneed( p_owner->p_packetizer, + p_owner->p_packetizer->p_module ); + es_format_Clean( &p_owner->p_packetizer->fmt_in ); + es_format_Clean( &p_owner->p_packetizer->fmt_out ); + vlc_object_detach( p_owner->p_packetizer ); + vlc_object_release( p_owner->p_packetizer ); } - vlc_mutex_destroy( &p_dec->p_owner->lock_cc ); + vlc_cond_destroy( &p_owner->wait ); + vlc_mutex_destroy( &p_owner->lock ); vlc_object_detach( p_dec ); - free( p_dec->p_owner ); + free( p_owner ); } /***************************************************************************** @@ -1173,30 +1501,38 @@ static void DeleteDecoder( decoder_t * p_dec ) *****************************************************************************/ static aout_buffer_t *aout_new_buffer( decoder_t *p_dec, int i_samples ) { - decoder_owner_sys_t *p_sys = (decoder_owner_sys_t *)p_dec->p_owner; + decoder_owner_sys_t *p_owner = p_dec->p_owner; aout_buffer_t *p_buffer; - if( p_sys->p_aout_input != NULL && - ( p_dec->fmt_out.audio.i_rate != p_sys->audio.i_rate || + if( p_owner->p_aout_input != NULL && + ( p_dec->fmt_out.audio.i_rate != p_owner->audio.i_rate || p_dec->fmt_out.audio.i_original_channels != - p_sys->audio.i_original_channels || + p_owner->audio.i_original_channels || p_dec->fmt_out.audio.i_bytes_per_frame != - p_sys->audio.i_bytes_per_frame ) ) + p_owner->audio.i_bytes_per_frame ) ) { + aout_input_t *p_aout_input = p_owner->p_aout_input; + /* Parameters changed, restart the aout */ - aout_DecDelete( p_sys->p_aout, p_sys->p_aout_input ); - p_sys->p_aout_input = NULL; + vlc_mutex_lock( &p_owner->lock ); + + p_owner->p_aout_input = NULL; + aout_DecDelete( p_owner->p_aout, p_aout_input ); + + vlc_mutex_unlock( &p_owner->lock ); } - if( p_sys->p_aout_input == NULL ) + if( p_owner->p_aout_input == NULL ) { + const int i_force_dolby = config_GetInt( p_dec, "force-dolby-surround" ); audio_sample_format_t format; - int i_force_dolby = config_GetInt( p_dec, "force-dolby-surround" ); + aout_input_t *p_aout_input; + aout_instance_t *p_aout; p_dec->fmt_out.audio.i_format = p_dec->fmt_out.i_codec; - p_sys->audio = p_dec->fmt_out.audio; + p_owner->audio = p_dec->fmt_out.audio; - memcpy( &format, &p_sys->audio, sizeof( audio_sample_format_t ) ); + memcpy( &format, &p_owner->audio, sizeof( audio_sample_format_t ) ); if ( i_force_dolby && (format.i_original_channels&AOUT_CHAN_PHYSMASK) == (AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT) ) { @@ -1212,27 +1548,36 @@ static aout_buffer_t *aout_new_buffer( decoder_t *p_dec, int i_samples ) } } - p_sys->p_aout_input = - aout_DecNew( p_dec, &p_sys->p_aout, &format, &p_dec->fmt_out.audio_replay_gain ); - if( p_sys->p_aout_input == NULL ) + p_aout = p_owner->p_aout; + p_aout_input = aout_DecNew( p_dec, &p_aout, + &format, &p_dec->fmt_out.audio_replay_gain ); + + vlc_mutex_lock( &p_owner->lock ); + p_owner->p_aout = p_aout; + p_owner->p_aout_input = p_aout_input; + vlc_mutex_unlock( &p_owner->lock ); + + if( p_owner->p_aout_input == NULL ) { msg_Err( p_dec, "failed to create audio output" ); p_dec->b_error = true; return NULL; } p_dec->fmt_out.audio.i_bytes_per_frame = - p_sys->audio.i_bytes_per_frame; + p_owner->audio.i_bytes_per_frame; } - p_buffer = aout_DecNewBuffer( p_sys->p_aout_input, i_samples ); + p_buffer = aout_DecNewBuffer( p_owner->p_aout_input, i_samples ); return p_buffer; } static void aout_del_buffer( decoder_t *p_dec, aout_buffer_t *p_buffer ) { - aout_DecDeleteBuffer( p_dec->p_owner->p_aout, - p_dec->p_owner->p_aout_input, p_buffer ); + decoder_owner_sys_t *p_owner = p_dec->p_owner; + + aout_DecDeleteBuffer( p_owner->p_aout, + p_owner->p_aout_input, p_buffer ); } @@ -1240,15 +1585,17 @@ int vout_CountPictureAvailable( vout_thread_t *p_vout ); static picture_t *vout_new_buffer( decoder_t *p_dec ) { - decoder_owner_sys_t *p_sys = (decoder_owner_sys_t *)p_dec->p_owner; + decoder_owner_sys_t *p_owner = p_dec->p_owner; picture_t *p_pic; - if( p_sys->p_vout == NULL || - p_dec->fmt_out.video.i_width != p_sys->video.i_width || - p_dec->fmt_out.video.i_height != p_sys->video.i_height || - p_dec->fmt_out.video.i_chroma != p_sys->video.i_chroma || - p_dec->fmt_out.video.i_aspect != p_sys->video.i_aspect ) + if( p_owner->p_vout == NULL || + p_dec->fmt_out.video.i_width != p_owner->video.i_width || + p_dec->fmt_out.video.i_height != p_owner->video.i_height || + p_dec->fmt_out.video.i_chroma != p_owner->video.i_chroma || + p_dec->fmt_out.video.i_aspect != p_owner->video.i_aspect ) { + vout_thread_t *p_vout; + if( !p_dec->fmt_out.video.i_width || !p_dec->fmt_out.video.i_height ) { @@ -1301,30 +1648,40 @@ static picture_t *vout_new_buffer( decoder_t *p_dec ) p_dec->fmt_out.video.i_sar_den, 50000 ); p_dec->fmt_out.video.i_chroma = p_dec->fmt_out.i_codec; - p_sys->video = p_dec->fmt_out.video; + p_owner->video = p_dec->fmt_out.video; + + vlc_mutex_lock( &p_owner->lock ); + p_vout = p_owner->p_vout; + p_owner->p_vout = NULL; + vlc_mutex_unlock( &p_owner->lock ); + + p_vout = vout_Request( p_dec, p_vout, &p_dec->fmt_out.video ); - p_sys->p_vout = vout_Request( p_dec, p_sys->p_vout, - &p_dec->fmt_out.video ); - if( p_sys->p_vout == NULL ) + vlc_mutex_lock( &p_owner->lock ); + p_owner->p_vout = p_vout; + vlc_mutex_unlock( &p_owner->lock ); + + var_SetBool( p_owner->p_input, "intf-change-vout", true ); + if( p_vout == NULL ) { msg_Err( p_dec, "failed to create video output" ); p_dec->b_error = true; return NULL; } - if( p_sys->video.i_rmask ) - p_sys->p_vout->render.i_rmask = p_sys->video.i_rmask; - if( p_sys->video.i_gmask ) - p_sys->p_vout->render.i_gmask = p_sys->video.i_gmask; - if( p_sys->video.i_bmask ) - p_sys->p_vout->render.i_bmask = p_sys->video.i_bmask; + if( p_owner->video.i_rmask ) + p_owner->p_vout->render.i_rmask = p_owner->video.i_rmask; + if( p_owner->video.i_gmask ) + p_owner->p_vout->render.i_gmask = p_owner->video.i_gmask; + if( p_owner->video.i_bmask ) + p_owner->p_vout->render.i_bmask = p_owner->video.i_bmask; } /* Get a new picture */ for( p_pic = NULL; ; ) { - int i_pic, i_ready_pic = 0; + int i_pic, i_ready_pic; if( p_dec->b_die || p_dec->b_error ) return NULL; @@ -1334,43 +1691,50 @@ static picture_t *vout_new_buffer( decoder_t *p_dec ) * buffer once its work is done, so this check is safe even if we don't * lock around both count() and create(). */ - if( vout_CountPictureAvailable( p_sys->p_vout ) >= 2 ) + if( vout_CountPictureAvailable( p_owner->p_vout ) >= 2 ) { - p_pic = vout_CreatePicture( p_sys->p_vout, 0, 0, 0 ); + p_pic = vout_CreatePicture( p_owner->p_vout, 0, 0, 0 ); if( p_pic ) break; } -#define p_pic p_dec->p_owner->p_vout->render.pp_picture[i_pic] +#define p_pic p_owner->p_vout->render.pp_picture[i_pic] /* Check the decoder doesn't leak pictures */ - for( i_pic = 0; i_pic < p_dec->p_owner->p_vout->render.i_pictures; - i_pic++ ) + for( i_pic = 0, i_ready_pic = 0; i_pic < p_owner->p_vout->render.i_pictures; i_pic++ ) { if( p_pic->i_status == READY_PICTURE ) { - if( i_ready_pic++ > 0 ) break; - else continue; - } + i_ready_pic++; + /* If we have at least 2 ready pictures, wait for the vout thread to + * process one */ + if( i_ready_pic >= 2 ) + break; - if( p_pic->i_status != DISPLAYED_PICTURE && - p_pic->i_status != RESERVED_PICTURE && - p_pic->i_status != READY_PICTURE ) break; + continue; + } - if( !p_pic->i_refcount && p_pic->i_status != RESERVED_PICTURE ) - break; + if( p_pic->i_status == DISPLAYED_PICTURE ) + { + /* If at least one displayed picture is not referenced + * let vout free it */ + if( p_pic->i_refcount == 0 ) + break; + } } - if( i_pic == p_dec->p_owner->p_vout->render.i_pictures ) + if( i_pic == p_owner->p_vout->render.i_pictures ) { + /* Too many pictures are still referenced, there is probably a bug + * with the decoder */ msg_Err( p_dec, "decoder is leaking pictures, resetting the heap" ); /* Just free all the pictures */ - for( i_pic = 0; i_pic < p_dec->p_owner->p_vout->render.i_pictures; + for( i_pic = 0; i_pic < p_owner->p_vout->render.i_pictures; i_pic++ ) { if( p_pic->i_status == RESERVED_PICTURE ) - vout_DestroyPicture( p_dec->p_owner->p_vout, p_pic ); + vout_DestroyPicture( p_owner->p_vout, p_pic ); if( p_pic->i_refcount > 0 ) - vout_UnlinkPicture( p_dec->p_owner->p_vout, p_pic ); + vout_UnlinkPicture( p_owner->p_vout, p_pic ); } } #undef p_pic @@ -1398,17 +1762,19 @@ static void vout_unlink_picture( decoder_t *p_dec, picture_t *p_pic ) static subpicture_t *spu_new_buffer( decoder_t *p_dec ) { - decoder_owner_sys_t *p_sys = (decoder_owner_sys_t *)p_dec->p_owner; + decoder_owner_sys_t *p_owner = p_dec->p_owner; vout_thread_t *p_vout = NULL; subpicture_t *p_subpic; int i_attempts = 30; while( i_attempts-- ) { - if( p_dec->b_die || p_dec->b_error ) break; + if( p_dec->b_die || p_dec->b_error ) + break; p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE ); - if( p_vout ) break; + if( p_vout ) + break; msleep( VOUT_DISPLAY_DELAY ); } @@ -1419,17 +1785,20 @@ static subpicture_t *spu_new_buffer( decoder_t *p_dec ) return NULL; } - if( p_sys->p_spu_vout != p_vout ) + if( p_owner->p_spu_vout != p_vout ) { spu_Control( p_vout->p_spu, SPU_CHANNEL_REGISTER, - &p_sys->i_spu_channel ); - p_sys->p_spu_vout = p_vout; + &p_owner->i_spu_channel ); + p_owner->i_spu_order = 0; + p_owner->p_spu_vout = p_vout; } - p_subpic = spu_CreateSubpicture( p_vout->p_spu ); + p_subpic = subpicture_New(); if( p_subpic ) { - p_subpic->i_channel = p_sys->i_spu_channel; + p_subpic->i_channel = p_owner->i_spu_channel; + p_subpic->i_order = p_owner->i_spu_order++; + p_subpic->b_subtitle = true; } vlc_object_release( p_vout ); @@ -1439,11 +1808,11 @@ static subpicture_t *spu_new_buffer( decoder_t *p_dec ) static void spu_del_buffer( decoder_t *p_dec, subpicture_t *p_subpic ) { - decoder_owner_sys_t *p_sys = (decoder_owner_sys_t *)p_dec->p_owner; + decoder_owner_sys_t *p_owner = p_dec->p_owner; vout_thread_t *p_vout = NULL; p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE ); - if( !p_vout || p_sys->p_spu_vout != p_vout ) + if( !p_vout || p_owner->p_spu_vout != p_vout ) { if( p_vout ) vlc_object_release( p_vout ); @@ -1451,7 +1820,7 @@ static void spu_del_buffer( decoder_t *p_dec, subpicture_t *p_subpic ) return; } - spu_DestroySubpicture( p_vout->p_spu, p_subpic ); + subpicture_Delete( p_subpic ); vlc_object_release( p_vout ); }