X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Finput%2Fdecoder.c;h=59438434a14a462e384bef572fbbdf04ec9a0276;hb=24e0092972be91d0bf09593edec90e102eeac5c0;hp=c285becafe706028d008e9297f23f90e47742a72;hpb=0df8d78a9bf243e6250ccc804ef1fc46bed01ba6;p=vlc diff --git a/src/input/decoder.c b/src/input/decoder.c index c285becafe..59438434a1 100644 --- a/src/input/decoder.c +++ b/src/input/decoder.c @@ -1,26 +1,26 @@ /***************************************************************************** * decoder.c: Functions for the management of decoders ***************************************************************************** - * Copyright (C) 1999-2004 the VideoLAN team + * Copyright (C) 1999-2004 VLC authors and 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 - * the Free Software Foundation; either version 2 of the License, or + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 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. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** @@ -53,32 +53,6 @@ #include "../video_output/vout_control.h" -static decoder_t *CreateDecoder( vlc_object_t *, input_thread_t *, - es_format_t *, bool, input_resource_t *, - sout_instance_t *p_sout ); -static void DeleteDecoder( decoder_t * ); - -static void *DecoderThread( void * ); -static void DecoderProcess( decoder_t *, block_t * ); -static void DecoderError( decoder_t *p_dec, block_t *p_block ); -static void DecoderOutputChangePause( decoder_t *, bool b_paused, mtime_t i_date ); -static void DecoderFlush( decoder_t * ); -static void DecoderSignalBuffering( decoder_t *, bool ); -static void DecoderFlushBuffering( decoder_t * ); - -static void DecoderUnsupportedCodec( decoder_t *, vlc_fourcc_t ); - -/* Buffers allocation callbacks for the decoders */ -static aout_buffer_t *aout_new_buffer( decoder_t *, int ); - -static picture_t *vout_new_buffer( decoder_t * ); -static void vout_del_buffer( decoder_t *, picture_t * ); -static void vout_link_picture( decoder_t *, picture_t * ); -static void vout_unlink_picture( decoder_t *, picture_t * ); - -static subpicture_t *spu_new_buffer( decoder_t *, const subpicture_updater_t * ); -static void spu_del_buffer( decoder_t *, subpicture_t * ); - struct decoder_owner_sys_t { int64_t i_preroll_end; @@ -102,13 +76,10 @@ struct decoder_owner_sys_t bool b_packetizer; /* Current format in use by the output */ - video_format_t video; - audio_format_t audio; - es_format_t sout; + es_format_t fmt; /* */ bool b_fmt_description; - es_format_t fmt_description; vlc_meta_t *p_description; /* fifo */ @@ -125,8 +96,6 @@ struct decoder_owner_sys_t vout_thread_t *p_vout; /* -- Theses variables need locking on read *and* write -- */ - bool b_exit; - /* Pause */ bool b_paused; struct @@ -135,26 +104,11 @@ struct decoder_owner_sys_t int i_ignore; } pause; - /* Buffering */ - bool b_buffering; - struct - { - bool b_first; - bool b_full; - int i_count; - - picture_t *p_picture; - picture_t **pp_picture_next; - - subpicture_t *p_subpic; - subpicture_t **pp_subpic_next; - - aout_buffer_t *p_audio; - aout_buffer_t **pp_audio_next; - - block_t *p_block; - block_t **pp_block_next; - } buffer; + /* Waiting */ + bool b_woken; + bool b_waiting; + bool b_first; + bool b_has_data; /* Flushing */ bool b_flushing; @@ -171,10 +125,6 @@ struct decoder_owner_sys_t mtime_t i_ts_delay; }; -#define DECODER_MAX_BUFFERING_COUNT (4) -#define DECODER_MAX_BUFFERING_AUDIO_DURATION (AOUT_MAX_PREPARE_TIME) -#define DECODER_MAX_BUFFERING_VIDEO_DURATION (1*CLOCK_FREQ) - /* Pictures which are DECODER_BOGUS_VIDEO_DELAY or more in advance probably have * a bogus PTS and won't be displayed */ #define DECODER_BOGUS_VIDEO_DELAY ((mtime_t)(DEFAULT_PTS_DELAY * 30)) @@ -182,500 +132,379 @@ struct decoder_owner_sys_t /* */ #define DECODER_SPU_VOUT_WAIT_DURATION ((int)(0.200*CLOCK_FREQ)) - -/***************************************************************************** - * Public functions - *****************************************************************************/ -picture_t *decoder_NewPicture( decoder_t *p_decoder ) -{ - picture_t *p_picture = p_decoder->pf_vout_buffer_new( p_decoder ); - if( !p_picture ) - msg_Warn( p_decoder, "can't get output picture" ); - return p_picture; -} -void decoder_DeletePicture( decoder_t *p_decoder, picture_t *p_picture ) -{ - p_decoder->pf_vout_buffer_del( p_decoder, p_picture ); -} -void decoder_LinkPicture( decoder_t *p_decoder, picture_t *p_picture ) -{ - p_decoder->pf_picture_link( p_decoder, p_picture ); -} -void decoder_UnlinkPicture( decoder_t *p_decoder, picture_t *p_picture ) +static void DecoderUpdateFormatLocked( decoder_t *p_dec ) { - p_decoder->pf_picture_unlink( p_decoder, p_picture ); -} + decoder_owner_sys_t *p_owner = p_dec->p_owner; -aout_buffer_t *decoder_NewAudioBuffer( decoder_t *p_decoder, int i_size ) -{ - if( !p_decoder->pf_aout_buffer_new ) - return NULL; - return p_decoder->pf_aout_buffer_new( p_decoder, i_size ); -} + vlc_assert_locked( &p_owner->lock ); -subpicture_t *decoder_NewSubpicture( decoder_t *p_decoder, - const subpicture_updater_t *p_dyn ) -{ - subpicture_t *p_subpicture = p_decoder->pf_spu_buffer_new( p_decoder, p_dyn ); - if( !p_subpicture ) - msg_Warn( p_decoder, "can't get output subpicture" ); - return p_subpicture; -} + p_owner->b_fmt_description = true; -void decoder_DeleteSubpicture( decoder_t *p_decoder, subpicture_t *p_subpicture ) -{ - p_decoder->pf_spu_buffer_del( p_decoder, p_subpicture ); + /* Move p_description */ + if( p_owner->p_description && p_dec->p_description ) + vlc_meta_Delete( p_owner->p_description ); + p_owner->p_description = p_dec->p_description; + p_dec->p_description = NULL; } -/* decoder_GetInputAttachments: - */ -int decoder_GetInputAttachments( decoder_t *p_dec, - input_attachment_t ***ppp_attachment, - int *pi_attachment ) +static bool DecoderIsFlushing( decoder_t *p_dec ) { - if( !p_dec->pf_get_attachments ) - return VLC_EGENERIC; + decoder_owner_sys_t *p_owner = p_dec->p_owner; + bool b_flushing; - return p_dec->pf_get_attachments( p_dec, ppp_attachment, pi_attachment ); -} -/* decoder_GetDisplayDate: - */ -mtime_t decoder_GetDisplayDate( decoder_t *p_dec, mtime_t i_ts ) -{ - if( !p_dec->pf_get_display_date ) - return VLC_TS_INVALID; + vlc_mutex_lock( &p_owner->lock ); - return p_dec->pf_get_display_date( p_dec, i_ts ); -} -/* decoder_GetDisplayRate: - */ -int decoder_GetDisplayRate( decoder_t *p_dec ) -{ - if( !p_dec->pf_get_display_rate ) - return INPUT_RATE_DEFAULT; + b_flushing = p_owner->b_flushing; - return p_dec->pf_get_display_rate( p_dec ); + vlc_mutex_unlock( &p_owner->lock ); + + return b_flushing; } -/* TODO: pass p_sout through p_resource? -- Courmisch */ -static decoder_t *decoder_New( vlc_object_t *p_parent, input_thread_t *p_input, - es_format_t *fmt, input_clock_t *p_clock, - input_resource_t *p_resource, - sout_instance_t *p_sout ) +static void DecoderSignalWait( decoder_t *p_dec ) { - decoder_t *p_dec = NULL; - const char *psz_type = p_sout ? N_("packetizer") : N_("decoder"); - int i_priority; - - /* Create the decoder configuration structure */ - p_dec = CreateDecoder( p_parent, p_input, fmt, - p_sout != NULL, p_resource, p_sout ); - if( p_dec == NULL ) - { - msg_Err( p_parent, "could not create %s", psz_type ); - dialog_Fatal( p_parent, _("Streaming / Transcoding failed"), - _("VLC could not open the %s module."), - vlc_gettext( psz_type ) ); - return NULL; - } - - if( !p_dec->p_module ) - { - DecoderUnsupportedCodec( p_dec, fmt->i_codec ); - - DeleteDecoder( p_dec ); - return NULL; - } - - p_dec->p_owner->p_clock = p_clock; - assert( p_dec->fmt_out.i_cat != UNKNOWN_ES ); + decoder_owner_sys_t *p_owner = p_dec->p_owner; - if( p_dec->fmt_out.i_cat == AUDIO_ES ) - i_priority = VLC_THREAD_PRIORITY_AUDIO; - else - i_priority = VLC_THREAD_PRIORITY_VIDEO; + vlc_mutex_lock( &p_owner->lock ); - /* Spawn the decoder thread */ - if( vlc_clone( &p_dec->p_owner->thread, DecoderThread, p_dec, i_priority ) ) + if( p_owner->b_waiting ) { - msg_Err( p_dec, "cannot spawn decoder thread" ); - module_unneed( p_dec, p_dec->p_module ); - DeleteDecoder( p_dec ); - return NULL; + p_owner->b_has_data = true; + vlc_cond_signal( &p_owner->wait_acknowledge ); } - return p_dec; + vlc_mutex_unlock( &p_owner->lock ); } - -/** - * Spawns a new decoder thread from the input thread - * - * \param p_input the input thread - * \param p_es the es descriptor - * \return the spawned decoder object - */ -decoder_t *input_DecoderNew( input_thread_t *p_input, - es_format_t *fmt, input_clock_t *p_clock, - sout_instance_t *p_sout ) +static block_t *DecoderBlockFlushNew() { - return decoder_New( VLC_OBJECT(p_input), p_input, fmt, p_clock, - p_input->p->p_resource, p_sout ); -} + block_t *p_null = block_Alloc( 128 ); + if( !p_null ) + return NULL; -/** - * Spawn a decoder thread outside of the input thread. - */ -decoder_t *input_DecoderCreate( vlc_object_t *p_parent, es_format_t *fmt, - input_resource_t *p_resource ) -{ - return decoder_New( p_parent, NULL, fmt, NULL, p_resource, NULL ); -} + p_null->i_flags |= BLOCK_FLAG_DISCONTINUITY | + BLOCK_FLAG_CORRUPTED | + BLOCK_FLAG_CORE_FLUSH; + memset( p_null->p_buffer, 0, p_null->i_buffer ); + return p_null; +} -/** - * Kills a decoder thread and waits until it's finished - * - * \param p_input the input thread - * \param p_es the es descriptor - * \return nothing - */ -void input_DecoderDelete( decoder_t *p_dec ) +/***************************************************************************** + * Buffers allocation callbacks for the decoders + *****************************************************************************/ +static vout_thread_t *aout_request_vout( void *p_private, + vout_thread_t *p_vout, video_format_t *p_fmt, bool b_recyle ) { + decoder_t *p_dec = p_private; decoder_owner_sys_t *p_owner = p_dec->p_owner; + input_thread_t *p_input = p_owner->p_input; - vlc_cancel( p_owner->thread ); - - /* Make sure we aren't paused/buffering/waiting/decoding anymore */ - vlc_mutex_lock( &p_owner->lock ); - const bool b_was_paused = p_owner->b_paused; - p_owner->b_paused = false; - p_owner->b_buffering = false; - p_owner->b_flushing = true; - p_owner->b_exit = true; - vlc_cond_signal( &p_owner->wait_request ); - vlc_mutex_unlock( &p_owner->lock ); - - vlc_join( p_owner->thread, NULL ); - p_owner->b_paused = b_was_paused; - - module_unneed( p_dec, p_dec->p_module ); - - /* */ - if( p_dec->p_owner->cc.b_supported ) - { - int i; - for( i = 0; i < 4; i++ ) - input_DecoderSetCcState( p_dec, false, i ); - } + p_vout = input_resource_RequestVout( p_owner->p_resource, p_vout, p_fmt, 1, + b_recyle ); + if( p_input != NULL ) + input_SendEventVout( p_input ); - /* Delete decoder */ - DeleteDecoder( p_dec ); + return p_vout; } -/** - * Put a block_t in the decoder's fifo. - * Thread-safe w.r.t. the decoder. May be a cancellation point. - * - * \param p_dec the decoder object - * \param p_block the data block - */ -void input_DecoderDecode( decoder_t *p_dec, block_t *p_block, bool b_do_pace ) +static int aout_update_format( decoder_t *p_dec ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; - if( b_do_pace ) + if( p_owner->p_aout + && !AOUT_FMTS_IDENTICAL(&p_dec->fmt_out.audio, &p_owner->fmt.audio) ) { - /* The fifo is not consummed when buffering and so will - * deadlock vlc. - * There is no need to lock as b_buffering is never modify - * inside decoder thread. */ - if( !p_owner->b_buffering ) - block_FifoPace( p_owner->p_fifo, 10, SIZE_MAX ); - } -#ifdef __arm__ - else if( block_FifoSize( p_owner->p_fifo ) > 50*1024*1024 /* 50 MiB */ ) -#else - else if( block_FifoSize( p_owner->p_fifo ) > 400*1024*1024 /* 400 MiB, ie ~ 50mb/s for 60s */ ) -#endif - { - /* 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_owner->p_fifo ); - } + audio_output_t *p_aout = p_owner->p_aout; - block_FifoPut( p_owner->p_fifo, p_block ); -} + /* Parameters changed, restart the aout */ + vlc_mutex_lock( &p_owner->lock ); -bool input_DecoderIsEmpty( decoder_t * p_dec ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; - assert( !p_owner->b_buffering ); + aout_DecDelete( p_owner->p_aout ); + p_owner->p_aout = NULL; - bool b_empty = block_FifoCount( p_dec->p_owner->p_fifo ) <= 0; - if( b_empty ) - { - vlc_mutex_lock( &p_owner->lock ); - /* TODO subtitles support */ - if( p_dec->fmt_out.i_cat == VIDEO_ES && p_owner->p_vout ) - b_empty = vout_IsEmpty( p_owner->p_vout ); - else if( p_dec->fmt_out.i_cat == AUDIO_ES && p_owner->p_aout ) - b_empty = aout_DecIsEmpty( p_owner->p_aout ); vlc_mutex_unlock( &p_owner->lock ); + input_resource_PutAout( p_owner->p_resource, p_aout ); } - return b_empty; -} - -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_owner->lock ); - for( i = 0; i < 4; i++ ) - pb_present[i] = p_owner->cc.pb_present[i]; - vlc_mutex_unlock( &p_owner->lock ); -} -int input_DecoderSetCcState( decoder_t *p_dec, bool b_decode, int i_channel ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; - //msg_Warn( p_dec, "input_DecoderSetCcState: %d @%d", b_decode, i_channel ); + if( p_owner->p_aout == NULL ) + { + p_dec->fmt_out.audio.i_format = p_dec->fmt_out.i_codec; - if( i_channel < 0 || i_channel >= 4 || !p_owner->cc.pb_present[i_channel] ) - return VLC_EGENERIC; + audio_sample_format_t format = p_dec->fmt_out.audio; + aout_FormatPrepare( &format ); - if( b_decode ) - { - static const vlc_fourcc_t fcc[4] = { - VLC_FOURCC('c', 'c', '1', ' '), - VLC_FOURCC('c', 'c', '2', ' '), - VLC_FOURCC('c', 'c', '3', ' '), - VLC_FOURCC('c', 'c', '4', ' '), - }; - decoder_t *p_cc; - es_format_t fmt; - - es_format_Init( &fmt, SPU_ES, fcc[i_channel] ); - p_cc = CreateDecoder( VLC_OBJECT(p_dec), p_owner->p_input, &fmt, - false, p_owner->p_resource, p_owner->p_sout ); - if( !p_cc ) - { - msg_Err( p_dec, "could not create decoder" ); - dialog_Fatal( p_dec, _("Streaming / Transcoding failed"), "%s", - _("VLC could not open the decoder module.") ); - return VLC_EGENERIC; - } - else if( !p_cc->p_module ) + const int i_force_dolby = var_InheritInteger( p_dec, "force-dolby-surround" ); + if( i_force_dolby && + (format.i_original_channels&AOUT_CHAN_PHYSMASK) == + (AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT) ) { - DecoderUnsupportedCodec( p_dec, fcc[i_channel] ); - DeleteDecoder( p_cc ); - return VLC_EGENERIC; + if( i_force_dolby == 1 ) + { + format.i_original_channels = format.i_original_channels | + AOUT_CHAN_DOLBYSTEREO; + } + else /* i_force_dolby == 2 */ + { + format.i_original_channels = format.i_original_channels & + ~AOUT_CHAN_DOLBYSTEREO; + } } - p_cc->p_owner->p_clock = p_owner->p_clock; - - vlc_mutex_lock( &p_owner->lock ); - p_owner->cc.pp_decoder[i_channel] = p_cc; - vlc_mutex_unlock( &p_owner->lock ); - } - else - { - decoder_t *p_cc; - vlc_mutex_lock( &p_owner->lock ); - p_cc = p_owner->cc.pp_decoder[i_channel]; - p_owner->cc.pp_decoder[i_channel] = NULL; - vlc_mutex_unlock( &p_owner->lock ); + aout_request_vout_t request_vout = { + .pf_request_vout = aout_request_vout, + .p_private = p_dec, + }; + audio_output_t *p_aout; - if( p_cc ) + p_aout = input_resource_GetAout( p_owner->p_resource ); + if( p_aout ) { - module_unneed( p_cc, p_cc->p_module ); - DeleteDecoder( p_cc ); + if( aout_DecNew( p_aout, &format, + &p_dec->fmt_out.audio_replay_gain, + &request_vout ) ) + { + input_resource_PutAout( p_owner->p_resource, p_aout ); + p_aout = NULL; + } } - } - return VLC_SUCCESS; -} -int input_DecoderGetCcState( decoder_t *p_dec, bool *pb_decode, int i_channel ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; - *pb_decode = false; - if( i_channel < 0 || i_channel >= 4 || !p_owner->cc.pb_present[i_channel] ) - return VLC_EGENERIC; + vlc_mutex_lock( &p_owner->lock ); - vlc_mutex_lock( &p_owner->lock ); - *pb_decode = p_owner->cc.pp_decoder[i_channel] != NULL; - vlc_mutex_unlock( &p_owner->lock ); - return VLC_EGENERIC; -} + p_owner->p_aout = p_aout; -void input_DecoderChangePause( decoder_t *p_dec, bool b_paused, mtime_t i_date ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; + es_format_Clean( &p_owner->fmt ); + es_format_Copy( &p_owner->fmt, &p_dec->fmt_out ); + aout_FormatPrepare( &p_owner->fmt.audio ); - vlc_mutex_lock( &p_owner->lock ); + DecoderUpdateFormatLocked( p_dec ); - assert( !p_owner->b_paused || !b_paused ); + if( unlikely(p_owner->b_paused) && p_aout != NULL ) + /* fake pause if needed */ + aout_DecChangePause( p_aout, true, mdate() ); - p_owner->b_paused = b_paused; - p_owner->pause.i_date = i_date; - p_owner->pause.i_ignore = 0; - vlc_cond_signal( &p_owner->wait_request ); + vlc_mutex_unlock( &p_owner->lock ); + + if( p_owner->p_input != NULL ) + input_SendEventAout( p_owner->p_input ); - DecoderOutputChangePause( p_dec, b_paused, i_date ); + if( p_aout == NULL ) + { + msg_Err( p_dec, "failed to create audio output" ); + p_dec->b_error = true; + return -1; + } - vlc_mutex_unlock( &p_owner->lock ); + p_dec->fmt_out.audio.i_bytes_per_frame = + p_owner->fmt.audio.i_bytes_per_frame; + p_dec->fmt_out.audio.i_frame_length = + p_owner->fmt.audio.i_frame_length; + } + return 0; } -void input_DecoderChangeDelay( decoder_t *p_dec, mtime_t i_delay ) +static int vout_update_format( decoder_t *p_dec ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; - vlc_mutex_lock( &p_owner->lock ); - p_owner->i_ts_delay = i_delay; - vlc_mutex_unlock( &p_owner->lock ); -} + if( p_owner->p_vout == NULL + || p_dec->fmt_out.video.i_width != p_owner->fmt.video.i_width + || p_dec->fmt_out.video.i_height != p_owner->fmt.video.i_height + || p_dec->fmt_out.video.i_visible_width != p_owner->fmt.video.i_visible_width + || p_dec->fmt_out.video.i_visible_height != p_owner->fmt.video.i_visible_height + || p_dec->fmt_out.video.i_x_offset != p_owner->fmt.video.i_x_offset + || p_dec->fmt_out.video.i_y_offset != p_owner->fmt.video.i_y_offset + || p_dec->fmt_out.i_codec != p_owner->fmt.video.i_chroma + || (int64_t)p_dec->fmt_out.video.i_sar_num * p_owner->fmt.video.i_sar_den != + (int64_t)p_dec->fmt_out.video.i_sar_den * p_owner->fmt.video.i_sar_num || + p_dec->fmt_out.video.orientation != p_owner->fmt.video.orientation ) + { + vout_thread_t *p_vout; -void input_DecoderStartBuffering( decoder_t *p_dec ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; + if( !p_dec->fmt_out.video.i_width || + !p_dec->fmt_out.video.i_height ) + { + /* Can't create a new vout without display size */ + return -1; + } - vlc_mutex_lock( &p_owner->lock ); + video_format_t fmt = p_dec->fmt_out.video; + fmt.i_chroma = p_dec->fmt_out.i_codec; - DecoderFlush( p_dec ); + if( vlc_fourcc_IsYUV( fmt.i_chroma ) ) + { + const vlc_chroma_description_t *dsc = vlc_fourcc_GetChromaDescription( fmt.i_chroma ); + for( unsigned int i = 0; dsc && i < dsc->plane_count; i++ ) + { + while( fmt.i_width % dsc->p[i].w.den ) + fmt.i_width++; + while( fmt.i_height % dsc->p[i].h.den ) + fmt.i_height++; + } + } + + if( !fmt.i_visible_width || !fmt.i_visible_height ) + { + if( p_dec->fmt_in.video.i_visible_width && + p_dec->fmt_in.video.i_visible_height ) + { + fmt.i_visible_width = p_dec->fmt_in.video.i_visible_width; + fmt.i_visible_height = p_dec->fmt_in.video.i_visible_height; + fmt.i_x_offset = p_dec->fmt_in.video.i_x_offset; + fmt.i_y_offset = p_dec->fmt_in.video.i_y_offset; + } + else + { + fmt.i_visible_width = fmt.i_width; + fmt.i_visible_height = fmt.i_height; + fmt.i_x_offset = 0; + fmt.i_y_offset = 0; + } + } - p_owner->buffer.b_first = true; - p_owner->buffer.b_full = false; - p_owner->buffer.i_count = 0; + if( fmt.i_visible_height == 1088 && + var_CreateGetBool( p_dec, "hdtv-fix" ) ) + { + fmt.i_visible_height = 1080; + if( !(fmt.i_sar_num % 136)) + { + fmt.i_sar_num *= 135; + fmt.i_sar_den *= 136; + } + msg_Warn( p_dec, "Fixing broken HDTV stream (display_height=1088)"); + } - assert( !p_owner->buffer.p_picture && !p_owner->buffer.p_subpic && - !p_owner->buffer.p_audio && !p_owner->buffer.p_block ); + if( !fmt.i_sar_num || !fmt.i_sar_den ) + { + fmt.i_sar_num = 1; + fmt.i_sar_den = 1; + } - p_owner->buffer.p_picture = NULL; - p_owner->buffer.pp_picture_next = &p_owner->buffer.p_picture; + vlc_ureduce( &fmt.i_sar_num, &fmt.i_sar_den, + fmt.i_sar_num, fmt.i_sar_den, 50000 ); - p_owner->buffer.p_subpic = NULL; - p_owner->buffer.pp_subpic_next = &p_owner->buffer.p_subpic; + vlc_mutex_lock( &p_owner->lock ); - p_owner->buffer.p_audio = NULL; - p_owner->buffer.pp_audio_next = &p_owner->buffer.p_audio; + p_vout = p_owner->p_vout; + p_owner->p_vout = NULL; + vlc_mutex_unlock( &p_owner->lock ); - p_owner->buffer.p_block = NULL; - p_owner->buffer.pp_block_next = &p_owner->buffer.p_block; + unsigned dpb_size; + switch( p_dec->fmt_in.i_codec ) + { + case VLC_CODEC_HEVC: + case VLC_CODEC_H264: + case VLC_CODEC_DIRAC: /* FIXME valid ? */ + dpb_size = 18; + break; + case VLC_CODEC_VP5: + case VLC_CODEC_VP6: + case VLC_CODEC_VP6F: + case VLC_CODEC_VP8: + dpb_size = 3; + break; + default: + dpb_size = 2; + break; + } + p_vout = input_resource_RequestVout( p_owner->p_resource, + p_vout, &fmt, + dpb_size + + p_dec->i_extra_picture_buffers + 1, + true ); + vlc_mutex_lock( &p_owner->lock ); + p_owner->p_vout = p_vout; + es_format_Clean( &p_owner->fmt ); + es_format_Copy( &p_owner->fmt, &p_dec->fmt_out ); + p_owner->fmt.video.i_chroma = p_dec->fmt_out.i_codec; - p_owner->b_buffering = true; + DecoderUpdateFormatLocked( p_dec ); - vlc_cond_signal( &p_owner->wait_request ); + vlc_mutex_unlock( &p_owner->lock ); - vlc_mutex_unlock( &p_owner->lock ); + if( p_owner->p_input != NULL ) + input_SendEventVout( p_owner->p_input ); + if( p_vout == NULL ) + { + msg_Err( p_dec, "failed to create video output" ); + p_dec->b_error = true; + return -1; + } + } + return 0; } -void input_DecoderStopBuffering( decoder_t *p_dec ) +static picture_t *vout_new_buffer( decoder_t *p_dec ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; - vlc_mutex_lock( &p_owner->lock ); + for( ;; ) + { + if( DecoderIsFlushing( p_dec ) || p_dec->b_error ) + return NULL; - p_owner->b_buffering = false; + picture_t *p_picture = vout_GetPicture( p_owner->p_vout ); + if( p_picture ) + return p_picture; - vlc_cond_signal( &p_owner->wait_request ); + /* */ + DecoderSignalWait( p_dec ); - vlc_mutex_unlock( &p_owner->lock ); + /* Check the decoder doesn't leak pictures */ + vout_FixLeaks( p_owner->p_vout ); + + /* FIXME add a vout_WaitPictureAvailable (timedwait) */ + msleep( VOUT_OUTMEM_SLEEP ); + } } -void input_DecoderWaitBuffering( decoder_t *p_dec ) +static subpicture_t *spu_new_buffer( decoder_t *p_dec, + const subpicture_updater_t *p_updater ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; + vout_thread_t *p_vout = NULL; + subpicture_t *p_subpic; + int i_attempts = 30; - vlc_mutex_lock( &p_owner->lock ); - - while( p_owner->b_buffering && !p_owner->buffer.b_full ) + while( i_attempts-- ) { - block_FifoWake( p_owner->p_fifo ); - vlc_cond_wait( &p_owner->wait_acknowledge, &p_owner->lock ); - } + if( DecoderIsFlushing( p_dec ) || p_dec->b_error ) + break; - vlc_mutex_unlock( &p_owner->lock ); -} + p_vout = input_resource_HoldVout( p_owner->p_resource ); + if( p_vout ) + break; -void input_DecoderFrameNext( decoder_t *p_dec, mtime_t *pi_duration ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; + msleep( DECODER_SPU_VOUT_WAIT_DURATION ); + } - *pi_duration = 0; + if( !p_vout ) + { + msg_Warn( p_dec, "no vout found, dropping subpicture" ); + return NULL; + } - vlc_mutex_lock( &p_owner->lock ); - if( p_dec->fmt_out.i_cat == VIDEO_ES ) + if( p_owner->p_spu_vout != p_vout ) { - if( p_owner->b_paused && p_owner->p_vout ) - { - vout_NextPicture( p_owner->p_vout, pi_duration ); - p_owner->pause.i_ignore++; - vlc_cond_signal( &p_owner->wait_request ); - } + p_owner->i_spu_channel = vout_RegisterSubpictureChannel( p_vout ); + p_owner->i_spu_order = 0; + p_owner->p_spu_vout = p_vout; } - else + + p_subpic = subpicture_New( p_updater ); + if( p_subpic ) { - /* TODO subtitle should not be flushed */ - DecoderFlush( p_dec ); + p_subpic->i_channel = p_owner->i_spu_channel; + p_subpic->i_order = p_owner->i_spu_order++; + p_subpic->b_subtitle = true; } - vlc_mutex_unlock( &p_owner->lock ); + + vlc_object_release( p_vout ); + + return p_subpic; } -bool input_DecoderHasFormatChanged( decoder_t *p_dec, es_format_t *p_fmt, vlc_meta_t **pp_meta ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; - bool b_changed; - - vlc_mutex_lock( &p_owner->lock ); - b_changed = p_owner->b_fmt_description; - if( b_changed ) - { - if( p_fmt ) - es_format_Copy( p_fmt, &p_owner->fmt_description ); - - if( pp_meta ) - { - *pp_meta = NULL; - if( p_owner->p_description ) - { - *pp_meta = vlc_meta_New(); - if( *pp_meta ) - vlc_meta_Merge( *pp_meta, p_owner->p_description ); - } - } - p_owner->b_fmt_description = false; - } - vlc_mutex_unlock( &p_owner->lock ); - return b_changed; -} - -size_t input_DecoderGetFifoSize( decoder_t *p_dec ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; - - return block_FifoSize( p_owner->p_fifo ); -} - -void input_DecoderGetObjects( decoder_t *p_dec, - vout_thread_t **pp_vout, audio_output_t **pp_aout ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; - - vlc_mutex_lock( &p_owner->lock ); - if( pp_vout ) - *pp_vout = p_owner->p_vout ? vlc_object_hold( p_owner->p_vout ) : NULL; - if( pp_aout ) - *pp_aout = p_owner->p_aout ? vlc_object_hold( p_owner->p_aout ) : NULL; - vlc_mutex_unlock( &p_owner->lock ); -} - -/***************************************************************************** - * Internal functions - *****************************************************************************/ static int DecoderGetInputAttachments( decoder_t *p_dec, input_attachment_t ***ppp_attachment, int *pi_attachment ) @@ -687,23 +516,27 @@ static int DecoderGetInputAttachments( decoder_t *p_dec, return input_Control( p_input, INPUT_GET_ATTACHMENTS, ppp_attachment, pi_attachment ); } + static mtime_t DecoderGetDisplayDate( decoder_t *p_dec, mtime_t i_ts ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; vlc_mutex_lock( &p_owner->lock ); - if( p_owner->b_buffering || p_owner->b_paused ) + if( p_owner->b_waiting || p_owner->b_paused ) i_ts = VLC_TS_INVALID; vlc_mutex_unlock( &p_owner->lock ); if( !p_owner->p_clock || i_ts <= VLC_TS_INVALID ) return i_ts; - if( input_clock_ConvertTS( p_owner->p_clock, NULL, &i_ts, NULL, INT64_MAX ) ) + if( input_clock_ConvertTS( VLC_OBJECT(p_dec), p_owner->p_clock, NULL, &i_ts, NULL, INT64_MAX ) ) { + msg_Err(p_dec, "Could not get display date for timestamp %"PRId64"", i_ts); return VLC_TS_INVALID; + } return i_ts; } + static int DecoderGetDisplayRate( decoder_t *p_dec ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; @@ -713,1145 +546,668 @@ static int DecoderGetDisplayRate( decoder_t *p_dec ) return input_clock_GetRate( p_owner->p_clock ); } -/* */ -static void DecoderUnsupportedCodec( decoder_t *p_dec, vlc_fourcc_t codec ) +/***************************************************************************** + * Public functions + *****************************************************************************/ +picture_t *decoder_NewPicture( decoder_t *p_decoder ) { - msg_Err( p_dec, "no suitable decoder module for fourcc `%4.4s'. " - "VLC probably does not support this sound or video format.", - (char*)&codec ); - dialog_Fatal( p_dec, _("No suitable decoder module"), - _("VLC does not support the audio or video format \"%4.4s\". " - "Unfortunately there is no way for you to fix this."), - (char*)&codec ); -} + if( decoder_UpdateVideoFormat( p_decoder ) ) + return NULL; + picture_t *p_picture = p_decoder->pf_vout_buffer_new( p_decoder ); + if( !p_picture ) + msg_Warn( p_decoder, "can't get output picture" ); + return p_picture; +} -/** - * Create a decoder object - * - * \param p_input the input thread - * \param p_es the es descriptor - * \param b_packetizer instead of a decoder - * \return the decoder object - */ -static decoder_t * CreateDecoder( vlc_object_t *p_parent, - input_thread_t *p_input, - es_format_t *fmt, bool b_packetizer, - input_resource_t *p_resource, - sout_instance_t *p_sout ) +block_t *decoder_NewAudioBuffer( decoder_t *dec, int samples ) { - decoder_t *p_dec; - decoder_owner_sys_t *p_owner; - es_format_t null_es_format; - - p_dec = vlc_custom_create( p_parent, sizeof( *p_dec ), "decoder" ); - if( p_dec == NULL ) + if( decoder_UpdateAudioFormat( dec ) ) return NULL; - p_dec->pf_decode_audio = NULL; - p_dec->pf_decode_video = NULL; - p_dec->pf_decode_sub = NULL; - p_dec->pf_get_cc = NULL; - p_dec->pf_packetize = NULL; - - /* Initialize the decoder */ - p_dec->p_module = NULL; - - memset( &null_es_format, 0, sizeof(es_format_t) ); - es_format_Copy( &p_dec->fmt_in, fmt ); - es_format_Copy( &p_dec->fmt_out, &null_es_format ); - - p_dec->p_description = NULL; - - /* Allocate our private structure for the decoder */ - p_dec->p_owner = p_owner = malloc( sizeof( decoder_owner_sys_t ) ); - if( unlikely(p_owner == NULL) ) + size_t length = samples * dec->fmt_out.audio.i_bytes_per_frame + / dec->fmt_out.audio.i_frame_length; + block_t *block = block_Alloc( length ); + if( likely(block != NULL) ) { - vlc_object_release( p_dec ); - return NULL; + block->i_nb_samples = samples; + block->i_pts = block->i_length = 0; } - p_owner->i_preroll_end = VLC_TS_INVALID; - p_owner->i_last_rate = INPUT_RATE_DEFAULT; - p_owner->p_input = p_input; - p_owner->p_resource = p_resource; - p_owner->p_aout = NULL; - p_owner->p_vout = NULL; - p_owner->p_spu_vout = NULL; - p_owner->i_spu_channel = 0; - p_owner->i_spu_order = 0; - p_owner->p_sout = p_sout; - p_owner->p_sout_input = NULL; - p_owner->p_packetizer = NULL; - p_owner->b_packetizer = b_packetizer; + return block; +} - /* decoder fifo */ - p_owner->p_fifo = block_FifoNew(); - if( unlikely(p_owner->p_fifo == NULL) ) - { - free( p_owner ); - vlc_object_release( p_dec ); - return NULL; - } +subpicture_t *decoder_NewSubpicture( decoder_t *p_decoder, + const subpicture_updater_t *p_dyn ) +{ + subpicture_t *p_subpicture = p_decoder->pf_spu_buffer_new( p_decoder, p_dyn ); + if( !p_subpicture ) + msg_Warn( p_decoder, "can't get output subpicture" ); + return p_subpicture; +} - /* Set buffers allocation callbacks for the decoders */ - p_dec->pf_aout_buffer_new = aout_new_buffer; - p_dec->pf_vout_buffer_new = vout_new_buffer; - p_dec->pf_vout_buffer_del = vout_del_buffer; - p_dec->pf_picture_link = vout_link_picture; - p_dec->pf_picture_unlink = vout_unlink_picture; - p_dec->pf_spu_buffer_new = spu_new_buffer; - p_dec->pf_spu_buffer_del = spu_del_buffer; - /* */ - p_dec->pf_get_attachments = DecoderGetInputAttachments; - p_dec->pf_get_display_date = DecoderGetDisplayDate; - p_dec->pf_get_display_rate = DecoderGetDisplayRate; +/* decoder_GetInputAttachments: + */ +int decoder_GetInputAttachments( decoder_t *p_dec, + input_attachment_t ***ppp_attachment, + int *pi_attachment ) +{ + if( !p_dec->pf_get_attachments ) + return VLC_EGENERIC; - /* Find a suitable decoder/packetizer module */ - if( !b_packetizer ) - p_dec->p_module = module_need( p_dec, "decoder", "$codec", false ); - else - p_dec->p_module = module_need( p_dec, "packetizer", "$packetizer", false ); + return p_dec->pf_get_attachments( p_dec, ppp_attachment, pi_attachment ); +} +/* decoder_GetDisplayDate: + */ +mtime_t decoder_GetDisplayDate( decoder_t *p_dec, mtime_t i_ts ) +{ + if( !p_dec->pf_get_display_date ) + return VLC_TS_INVALID; - /* Check if decoder requires already packetized data */ - if( !b_packetizer && - p_dec->b_need_packetized && !p_dec->fmt_in.b_packetized ) - { - p_owner->p_packetizer = - vlc_custom_create( p_parent, sizeof( decoder_t ), "packetizer" ); - if( p_owner->p_packetizer ) - { - es_format_Copy( &p_owner->p_packetizer->fmt_in, - &p_dec->fmt_in ); + return p_dec->pf_get_display_date( p_dec, i_ts ); +} +/* decoder_GetDisplayRate: + */ +int decoder_GetDisplayRate( decoder_t *p_dec ) +{ + if( !p_dec->pf_get_display_rate ) + return INPUT_RATE_DEFAULT; - es_format_Copy( &p_owner->p_packetizer->fmt_out, - &null_es_format ); + return p_dec->pf_get_display_rate( p_dec ); +} - p_owner->p_packetizer->p_module = - module_need( p_owner->p_packetizer, - "packetizer", "$packetizer", false ); +static bool DecoderWaitUnblock( decoder_t *p_dec ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; - if( !p_owner->p_packetizer->p_module ) - { - es_format_Clean( &p_owner->p_packetizer->fmt_in ); - vlc_object_release( p_owner->p_packetizer ); - } - } - } + vlc_assert_locked( &p_owner->lock ); - /* Copy ourself the input replay gain */ - if( fmt->i_cat == AUDIO_ES ) + for( ;; ) { - for( unsigned i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ ) + if( p_owner->b_flushing ) + break; + if( p_owner->b_paused ) { - if( !p_dec->fmt_out.audio_replay_gain.pb_peak[i] ) - { - p_dec->fmt_out.audio_replay_gain.pb_peak[i] = fmt->audio_replay_gain.pb_peak[i]; - p_dec->fmt_out.audio_replay_gain.pf_peak[i] = fmt->audio_replay_gain.pf_peak[i]; - } - if( !p_dec->fmt_out.audio_replay_gain.pb_gain[i] ) + if( p_owner->b_waiting && !p_owner->b_has_data ) + break; + if( p_owner->pause.i_ignore > 0 ) { - p_dec->fmt_out.audio_replay_gain.pb_gain[i] = fmt->audio_replay_gain.pb_gain[i]; - p_dec->fmt_out.audio_replay_gain.pf_gain[i] = fmt->audio_replay_gain.pf_gain[i]; + p_owner->pause.i_ignore--; + break; } } + else + { + if( !p_owner->b_waiting || !p_owner->b_has_data ) + break; + } + vlc_cond_wait( &p_owner->wait_request, &p_owner->lock ); } - /* */ - vlc_mutex_init( &p_owner->lock ); - vlc_cond_init( &p_owner->wait_request ); - vlc_cond_init( &p_owner->wait_acknowledge ); + return p_owner->b_flushing; +} - p_owner->b_fmt_description = false; - es_format_Init( &p_owner->fmt_description, UNKNOWN_ES, 0 ); - p_owner->p_description = NULL; +static inline void DecoderUpdatePreroll( int64_t *pi_preroll, const block_t *p ) +{ + if( p->i_flags & (BLOCK_FLAG_PREROLL|BLOCK_FLAG_DISCONTINUITY) ) + *pi_preroll = INT64_MAX; + else if( p->i_dts > VLC_TS_INVALID ) + *pi_preroll = __MIN( *pi_preroll, p->i_dts ); + else if( p->i_pts > VLC_TS_INVALID ) + *pi_preroll = __MIN( *pi_preroll, p->i_pts ); +} - p_owner->b_exit = false; +static void DecoderFixTs( decoder_t *p_dec, mtime_t *pi_ts0, mtime_t *pi_ts1, + mtime_t *pi_duration, int *pi_rate, mtime_t i_ts_bound ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; + input_clock_t *p_clock = p_owner->p_clock; - p_owner->b_paused = false; - p_owner->pause.i_date = VLC_TS_INVALID; - p_owner->pause.i_ignore = 0; + vlc_assert_locked( &p_owner->lock ); - p_owner->b_buffering = false; - p_owner->buffer.b_first = true; - p_owner->buffer.b_full = false; - p_owner->buffer.i_count = 0; - p_owner->buffer.p_picture = NULL; - p_owner->buffer.p_subpic = NULL; - p_owner->buffer.p_audio = NULL; - p_owner->buffer.p_block = NULL; + const mtime_t i_es_delay = p_owner->i_ts_delay; - p_owner->b_flushing = false; + if( !p_clock ) + return; - /* */ - p_owner->cc.b_supported = false; - if( !b_packetizer ) - { - if( p_owner->p_packetizer && p_owner->p_packetizer->pf_get_cc ) - p_owner->cc.b_supported = true; - if( p_dec->pf_get_cc ) - p_owner->cc.b_supported = true; - } + const bool b_ephemere = pi_ts1 && *pi_ts0 == *pi_ts1; + int i_rate; - for( unsigned i = 0; i < 4; i++ ) + if( *pi_ts0 > VLC_TS_INVALID ) { - p_owner->cc.pb_present[i] = false; - p_owner->cc.pp_decoder[i] = NULL; - } - p_owner->i_ts_delay = 0; - return p_dec; -} - -/** - * The decoding main loop - * - * \param p_dec the decoder - */ -static void *DecoderThread( void *p_data ) -{ - decoder_t *p_dec = (decoder_t *)p_data; - decoder_owner_sys_t *p_owner = p_dec->p_owner; - - /* The decoder's main loop */ - for( ;; ) - { - block_t *p_block = block_FifoGet( p_owner->p_fifo ); - - /* Make sure there is no cancellation point other than this one^^. - * If you need one, be sure to push cleanup of p_block. */ - DecoderSignalBuffering( p_dec, p_block == NULL ); - - if( p_block ) - { - int canc = vlc_savecancel(); - - if( p_block->i_flags & BLOCK_FLAG_CORE_EOS ) - { - /* calling DecoderProcess() with NULL block will make - * decoders/packetizers flush their buffers */ - block_Release( p_block ); - p_block = NULL; - } - - if( p_dec->b_error ) - DecoderError( p_dec, p_block ); + *pi_ts0 += i_es_delay; + if( pi_ts1 && *pi_ts1 > VLC_TS_INVALID ) + *pi_ts1 += i_es_delay; + if( input_clock_ConvertTS( VLC_OBJECT(p_dec), p_clock, &i_rate, pi_ts0, pi_ts1, i_ts_bound ) ) { + if( pi_ts1 != NULL ) + msg_Err(p_dec, "Could not convert timestamps %"PRId64 + ", %"PRId64"", *pi_ts0, *pi_ts1); else - DecoderProcess( p_dec, p_block ); - - vlc_restorecancel( canc ); + msg_Err(p_dec, "Could not convert timestamp %"PRId64, *pi_ts0); + *pi_ts0 = VLC_TS_INVALID; } } - return NULL; -} + else + { + i_rate = input_clock_GetRate( p_clock ); + } -static block_t *DecoderBlockFlushNew() -{ - block_t *p_null = block_Alloc( 128 ); - if( !p_null ) - return NULL; + /* Do not create ephemere data because of rounding errors */ + if( !b_ephemere && pi_ts1 && *pi_ts0 == *pi_ts1 ) + *pi_ts1 += 1; - p_null->i_flags |= BLOCK_FLAG_DISCONTINUITY | - BLOCK_FLAG_CORRUPTED | - BLOCK_FLAG_CORE_FLUSH; - memset( p_null->p_buffer, 0, p_null->i_buffer ); + if( pi_duration ) + *pi_duration = ( *pi_duration * i_rate + INPUT_RATE_DEFAULT-1 ) + / INPUT_RATE_DEFAULT; - return p_null; + if( pi_rate ) + *pi_rate = i_rate; } -static void DecoderFlush( decoder_t *p_dec ) +/** + * If *pb_reject, it does nothing, otherwise it waits for the given + * deadline or a flush request (in which case it set *pi_reject to true. + */ +static void DecoderWaitDate( decoder_t *p_dec, + bool *pb_reject, mtime_t i_deadline ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; vlc_assert_locked( &p_owner->lock ); - /* Empty the fifo */ - block_FifoEmpty( p_owner->p_fifo ); - - /* Monitor for flush end */ - p_owner->b_flushing = true; - vlc_cond_signal( &p_owner->wait_request ); - - /* Send a special block */ - block_t *p_null = DecoderBlockFlushNew(); - if( !p_null ) + if( *pb_reject || i_deadline < 0 ) return; - input_DecoderDecode( p_dec, p_null, false ); - - /* */ - while( p_owner->b_flushing ) - vlc_cond_wait( &p_owner->wait_acknowledge, &p_owner->lock ); -} - -static void DecoderSignalBuffering( decoder_t *p_dec, bool b_full ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; - - vlc_mutex_lock( &p_owner->lock ); - if( p_owner->b_buffering ) + do { - if( b_full ) - p_owner->buffer.b_full = true; - vlc_cond_signal( &p_owner->wait_acknowledge ); + if( p_owner->b_flushing ) + { + *pb_reject = true; + break; + } } - - vlc_mutex_unlock( &p_owner->lock ); + while( vlc_cond_timedwait( &p_owner->wait_request, &p_owner->lock, + i_deadline ) == 0 ); } -static bool DecoderIsFlushing( decoder_t *p_dec ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; - bool b_flushing; - - vlc_mutex_lock( &p_owner->lock ); - - b_flushing = p_owner->b_flushing; - - vlc_mutex_unlock( &p_owner->lock ); - return b_flushing; -} -static void DecoderWaitUnblock( decoder_t *p_dec, bool *pb_reject ) +#ifdef ENABLE_SOUT +static int DecoderPlaySout( decoder_t *p_dec, block_t *p_sout_block ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; - vlc_assert_locked( &p_owner->lock ); + assert( p_owner->p_clock ); + assert( !p_sout_block->p_next ); + + vlc_mutex_lock( &p_owner->lock ); - while( !p_owner->b_flushing ) + if( p_owner->b_waiting ) { - if( p_owner->b_paused ) - { - if( p_owner->b_buffering && !p_owner->buffer.b_full ) - break; - if( p_owner->pause.i_ignore > 0 ) - { - p_owner->pause.i_ignore--; - break; - } - } - else - { - if( !p_owner->b_buffering || !p_owner->buffer.b_full ) - break; - } - vlc_cond_wait( &p_owner->wait_request, &p_owner->lock ); + p_owner->b_has_data = true; + vlc_cond_signal( &p_owner->wait_acknowledge ); } - if( pb_reject ) - *pb_reject = p_owner->b_flushing; -} + bool b_reject = DecoderWaitUnblock( p_dec ); -static void DecoderOutputChangePause( decoder_t *p_dec, bool b_paused, mtime_t i_date ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; + DecoderFixTs( p_dec, &p_sout_block->i_dts, &p_sout_block->i_pts, + &p_sout_block->i_length, NULL, INT64_MAX ); - vlc_assert_locked( &p_owner->lock ); + vlc_mutex_unlock( &p_owner->lock ); - /* 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_out.i_cat == AUDIO_ES ) + if( !b_reject ) { - if( p_owner->p_aout ) - aout_DecChangePause( p_owner->p_aout, b_paused, i_date ); + /* FIXME --VLC_TS_INVALID inspect stream_output*/ + return sout_InputSendBuffer( p_owner->p_sout_input, p_sout_block ); } - else if( p_dec->fmt_out.i_cat == VIDEO_ES ) + else { - if( p_owner->p_vout ) - vout_ChangePause( p_owner->p_vout, b_paused, i_date ); + block_Release( p_sout_block ); + return VLC_EGENERIC; } } -static inline void DecoderUpdatePreroll( int64_t *pi_preroll, const block_t *p ) -{ - if( p->i_flags & (BLOCK_FLAG_PREROLL|BLOCK_FLAG_DISCONTINUITY) ) - *pi_preroll = INT64_MAX; - else if( p->i_dts > VLC_TS_INVALID ) - *pi_preroll = __MIN( *pi_preroll, p->i_dts ); - else if( p->i_pts > VLC_TS_INVALID ) - *pi_preroll = __MIN( *pi_preroll, p->i_pts ); -} -static mtime_t DecoderTeletextFixTs( mtime_t i_ts ) +/* This function process a block for sout + */ +static void DecoderProcessSout( decoder_t *p_dec, block_t *p_block ) { - mtime_t current_date = mdate(); + decoder_owner_sys_t *p_owner = (decoder_owner_sys_t *)p_dec->p_owner; + block_t *p_sout_block; - /* FIXME I don't really like that, es_out SHOULD do it using the video pts */ - if( i_ts <= VLC_TS_INVALID || i_ts > current_date + 10000000 || i_ts < current_date ) + while( ( p_sout_block = + p_dec->pf_packetize( p_dec, p_block ? &p_block : NULL ) ) ) { - /* ETSI EN 300 472 Annex A : do not take into account the PTS - * for teletext streams. */ - return current_date + 400000; - } - return i_ts; -} - -static void DecoderFixTs( decoder_t *p_dec, mtime_t *pi_ts0, mtime_t *pi_ts1, - mtime_t *pi_duration, int *pi_rate, mtime_t i_ts_bound, bool b_telx ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; - input_clock_t *p_clock = p_owner->p_clock; + if( p_owner->p_sout_input == NULL ) + { + vlc_mutex_lock( &p_owner->lock ); + es_format_Clean( &p_owner->fmt ); + es_format_Copy( &p_owner->fmt, &p_dec->fmt_out ); + DecoderUpdateFormatLocked( p_dec ); + vlc_mutex_unlock( &p_owner->lock ); - vlc_assert_locked( &p_owner->lock ); + p_owner->fmt.i_group = p_dec->fmt_in.i_group; + p_owner->fmt.i_id = p_dec->fmt_in.i_id; + if( p_dec->fmt_in.psz_language ) + { + free( p_owner->fmt.psz_language ); + p_owner->fmt.psz_language = + strdup( p_dec->fmt_in.psz_language ); + } - const mtime_t i_es_delay = p_owner->i_ts_delay; + p_owner->p_sout_input = + sout_InputNew( p_owner->p_sout, &p_owner->fmt ); - if( p_clock ) - { - const bool b_ephemere = pi_ts1 && *pi_ts0 == *pi_ts1; - int i_rate; + if( p_owner->p_sout_input == NULL ) + { + msg_Err( p_dec, "cannot create packetizer output (%4.4s)", + (char *)&p_owner->fmt.i_codec ); + p_dec->b_error = true; - if( *pi_ts0 > VLC_TS_INVALID ) - { - *pi_ts0 += i_es_delay; - if( pi_ts1 && *pi_ts1 > VLC_TS_INVALID ) - *pi_ts1 += i_es_delay; - if( input_clock_ConvertTS( p_clock, &i_rate, pi_ts0, pi_ts1, i_ts_bound ) ) - *pi_ts0 = VLC_TS_INVALID; + block_ChainRelease(p_sout_block); + break; + } } - else + + while( p_sout_block ) { - i_rate = input_clock_GetRate( p_clock ); - } + block_t *p_next = p_sout_block->p_next; + + p_sout_block->p_next = NULL; - /* Do not create ephemere data because of rounding errors */ - if( !b_ephemere && pi_ts1 && *pi_ts0 == *pi_ts1 ) - *pi_ts1 += 1; + if( DecoderPlaySout( p_dec, p_sout_block ) == VLC_EGENERIC ) + { + msg_Err( p_dec, "cannot continue streaming due to errors" ); - if( pi_duration ) - *pi_duration = ( *pi_duration * i_rate + - INPUT_RATE_DEFAULT-1 ) / INPUT_RATE_DEFAULT; + p_dec->b_error = true; - if( pi_rate ) - *pi_rate = i_rate; + /* Cleanup */ + block_ChainRelease( p_next ); + return; + } - if( b_telx ) - { - *pi_ts0 = DecoderTeletextFixTs( *pi_ts0 ); - if( pi_ts1 && *pi_ts1 <= VLC_TS_INVALID ) - *pi_ts1 = *pi_ts0; + p_sout_block = p_next; } } } +#endif -static bool DecoderIsExitRequested( decoder_t *p_dec ) +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]; + bool b_processed = false; + int i; + int i_cc_decoder; - vlc_mutex_lock( &p_owner->lock ); - bool b_exit = p_owner->b_exit; - vlc_mutex_unlock( &p_owner->lock ); - - return b_exit; -} + assert( p_dec_cc->pf_get_cc != NULL ); -/** - * If *pb_reject, it does nothing, otherwise it waits for the given - * deadline or a flush request (in which case it set *pi_reject to true. - */ -static void DecoderWaitDate( decoder_t *p_dec, - bool *pb_reject, mtime_t i_deadline ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; + /* Do not try retreiving CC if not wanted (sout) or cannot be retreived */ + if( !p_owner->cc.b_supported ) + return; - if( *pb_reject || i_deadline < 0 ) + p_cc = p_dec_cc->pf_get_cc( p_dec_cc, pb_present ); + if( !p_cc ) return; - for( ;; ) + vlc_mutex_lock( &p_owner->lock ); + for( i = 0, i_cc_decoder = 0; i < 4; i++ ) { - vlc_mutex_lock( &p_owner->lock ); - if( p_owner->b_flushing || p_owner->b_exit ) - { - *pb_reject = true; - vlc_mutex_unlock( &p_owner->lock ); - break; - } - int i_ret = vlc_cond_timedwait( &p_owner->wait_request, &p_owner->lock, - i_deadline ); - vlc_mutex_unlock( &p_owner->lock ); - if( i_ret ) - break; + p_owner->cc.pb_present[i] |= pb_present[i]; + if( p_owner->cc.pp_decoder[i] ) + i_cc_decoder++; + } + + for( i = 0; i < 4; i++ ) + { + if( !p_owner->cc.pp_decoder[i] ) + continue; + + block_FifoPut( p_owner->cc.pp_decoder[i]->p_owner->p_fifo, + (i_cc_decoder > 1) ? block_Duplicate(p_cc) : p_cc); + + i_cc_decoder--; + b_processed = true; } + vlc_mutex_unlock( &p_owner->lock ); + + if( !b_processed ) + block_Release( p_cc ); } -static void DecoderPlayAudio( decoder_t *p_dec, aout_buffer_t *p_audio, +static void DecoderPlayVideo( decoder_t *p_dec, picture_t *p_picture, int *pi_played_sum, int *pi_lost_sum ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; - audio_output_t *p_aout = p_owner->p_aout; + vout_thread_t *p_vout = p_owner->p_vout; - /* */ - if( p_audio->i_pts <= VLC_TS_INVALID ) // FIXME --VLC_TS_INVALID verify audio_output/* + if( p_picture->date <= VLC_TS_INVALID ) { - msg_Warn( p_dec, "non-dated audio buffer received" ); + msg_Warn( p_dec, "non-dated video buffer received" ); *pi_lost_sum += 1; - aout_BufferFree( p_audio ); + picture_Release( p_picture ); return; } /* */ vlc_mutex_lock( &p_owner->lock ); - if( p_owner->b_buffering || p_owner->buffer.p_audio ) + if( p_owner->b_waiting && !p_owner->b_first ) { - p_audio->p_next = NULL; + p_owner->b_has_data = true; + vlc_cond_signal( &p_owner->wait_acknowledge ); + } + bool b_first_after_wait = p_owner->b_waiting && p_owner->b_has_data; - *p_owner->buffer.pp_audio_next = p_audio; - p_owner->buffer.pp_audio_next = &p_audio->p_next; + bool b_reject = DecoderWaitUnblock( p_dec ); - p_owner->buffer.i_count++; - if( p_owner->buffer.i_count > DECODER_MAX_BUFFERING_COUNT || - p_audio->i_pts - p_owner->buffer.p_audio->i_pts > DECODER_MAX_BUFFERING_AUDIO_DURATION ) - { - p_owner->buffer.b_full = true; - vlc_cond_signal( &p_owner->wait_acknowledge ); - } + if( p_owner->b_waiting ) + { + assert( p_owner->b_first ); + msg_Dbg( p_dec, "Received first picture" ); + p_owner->b_first = false; + p_picture->b_force = true; } - for( ;; ) - { - bool b_has_more = false; - bool b_reject; - DecoderWaitUnblock( p_dec, &b_reject ); + const bool b_dated = p_picture->date > VLC_TS_INVALID; + int i_rate = INPUT_RATE_DEFAULT; + DecoderFixTs( p_dec, &p_picture->date, NULL, NULL, + &i_rate, DECODER_BOGUS_VIDEO_DELAY ); - if( p_owner->b_buffering ) - { - vlc_mutex_unlock( &p_owner->lock ); - return; - } + vlc_mutex_unlock( &p_owner->lock ); - /* */ - if( p_owner->buffer.p_audio ) - { - p_audio = p_owner->buffer.p_audio; + /* */ + if( !p_picture->b_force && p_picture->date <= VLC_TS_INVALID ) // FIXME --VLC_TS_INVALID verify video_output/* + b_reject = true; - p_owner->buffer.p_audio = p_audio->p_next; - p_owner->buffer.i_count--; - - b_has_more = p_owner->buffer.p_audio != NULL; - if( !b_has_more ) - p_owner->buffer.pp_audio_next = &p_owner->buffer.p_audio; - } - - /* */ - const bool b_dated = p_audio->i_pts > VLC_TS_INVALID; - int i_rate = INPUT_RATE_DEFAULT; - - DecoderFixTs( p_dec, &p_audio->i_pts, NULL, &p_audio->i_length, - &i_rate, AOUT_MAX_ADVANCE_TIME, false ); - - vlc_mutex_unlock( &p_owner->lock ); - - if( !p_aout || - p_audio->i_pts <= VLC_TS_INVALID || - i_rate < INPUT_RATE_DEFAULT/AOUT_MAX_INPUT_RATE || - i_rate > INPUT_RATE_DEFAULT*AOUT_MAX_INPUT_RATE ) - b_reject = true; - - DecoderWaitDate( p_dec, &b_reject, - p_audio->i_pts - AOUT_MAX_PREPARE_TIME ); - - if( !b_reject ) + if( !b_reject ) + { + if( i_rate != p_owner->i_last_rate || b_first_after_wait ) { - if( !aout_DecPlay( p_aout, p_audio, i_rate ) ) - *pi_played_sum += 1; - *pi_lost_sum += aout_DecGetResetLost( p_aout ); + /* Be sure to not display old picture after our own */ + vout_Flush( p_vout, p_picture->date ); + p_owner->i_last_rate = i_rate; } + vout_PutPicture( p_vout, p_picture ); + } + else + { + if( b_dated ) + msg_Warn( p_dec, "early picture skipped" ); else - { - if( b_dated ) - msg_Warn( p_dec, "received buffer in the future" ); - else - msg_Warn( p_dec, "non-dated audio buffer received" ); - - *pi_lost_sum += 1; - aout_BufferFree( p_audio ); - } + msg_Warn( p_dec, "non-dated video buffer received" ); - if( !b_has_more ) - break; - - vlc_mutex_lock( &p_owner->lock ); - if( !p_owner->buffer.p_audio ) - { - vlc_mutex_unlock( &p_owner->lock ); - break; - } + *pi_lost_sum += 1; + picture_Release( p_picture ); } + int i_tmp_display; + int i_tmp_lost; + vout_GetResetStatistic( p_vout, &i_tmp_display, &i_tmp_lost ); + + *pi_played_sum += i_tmp_display; + *pi_lost_sum += i_tmp_lost; } -static void DecoderDecodeAudio( decoder_t *p_dec, block_t *p_block ) +static void DecoderDecodeVideo( decoder_t *p_dec, block_t *p_block ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; - aout_buffer_t *p_aout_buf; - int i_decoded = 0; + picture_t *p_pic; int i_lost = 0; - int i_played = 0; + int i_decoded = 0; + int i_displayed = 0; - while( (p_aout_buf = p_dec->pf_decode_audio( p_dec, &p_block )) ) + while( (p_pic = p_dec->pf_decode_video( p_dec, &p_block )) ) { - audio_output_t *p_aout = p_owner->p_aout; - - if( DecoderIsExitRequested( p_dec ) ) - { - /* It prevent freezing VLC in case of broken decoder */ - aout_DecDeleteBuffer( p_aout, p_aout_buf ); + vout_thread_t *p_vout = p_owner->p_vout; + if( DecoderIsFlushing( p_dec ) ) + { /* It prevent freezing VLC in case of broken decoder */ + picture_Release( p_pic ); if( p_block ) block_Release( p_block ); break; } + i_decoded++; - if( p_owner->i_preroll_end > VLC_TS_INVALID && - p_aout_buf->i_pts < p_owner->i_preroll_end ) + if( p_owner->i_preroll_end > VLC_TS_INVALID && p_pic->date < p_owner->i_preroll_end ) { - aout_DecDeleteBuffer( p_aout, p_aout_buf ); + picture_Release( p_pic ); continue; } if( p_owner->i_preroll_end > VLC_TS_INVALID ) { - msg_Dbg( p_dec, "End of audio preroll" ); - if( p_owner->p_aout ) - aout_DecFlush( p_owner->p_aout ); + msg_Dbg( p_dec, "End of video preroll" ); + if( p_vout ) + vout_Flush( p_vout, VLC_TS_INVALID+1 ); /* */ p_owner->i_preroll_end = VLC_TS_INVALID; } - DecoderPlayAudio( p_dec, p_aout_buf, &i_played, &i_lost ); + if( p_dec->pf_get_cc && + ( !p_owner->p_packetizer || !p_owner->p_packetizer->pf_get_cc ) ) + DecoderGetCc( p_dec, p_dec ); + + DecoderPlayVideo( p_dec, p_pic, &i_displayed, &i_lost ); } /* Update ugly stat */ - input_thread_t *p_input = p_owner->p_input; + input_thread_t *p_input = p_owner->p_input; - if( p_input != NULL && (i_decoded > 0 || i_lost > 0 || i_played > 0) ) + if( p_input != NULL && (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_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); + vlc_mutex_lock( &p_input->p->counters.counters_lock ); + stats_Update( p_input->p->counters.p_decoded_video, i_decoded, NULL ); + stats_Update( p_input->p->counters.p_lost_pictures, i_lost , NULL); + stats_Update( p_input->p->counters.p_displayed_pictures, + i_displayed, NULL); + vlc_mutex_unlock( &p_input->p->counters.counters_lock ); } } -static void DecoderGetCc( decoder_t *p_dec, decoder_t *p_dec_cc ) + +/* This function process a video block + */ +static void DecoderProcessVideo( decoder_t *p_dec, block_t *p_block, bool b_flush ) { - decoder_owner_sys_t *p_owner = p_dec->p_owner; - block_t *p_cc; - bool pb_present[4]; - bool b_processed = false; - int i; - int i_cc_decoder; + decoder_owner_sys_t *p_owner = (decoder_owner_sys_t *)p_dec->p_owner; - assert( p_dec_cc->pf_get_cc != NULL ); + if( p_owner->p_packetizer ) + { + block_t *p_packetized_block; + decoder_t *p_packetizer = p_owner->p_packetizer; - /* Do not try retreiving CC if not wanted (sout) or cannot be retreived */ - if( !p_owner->cc.b_supported ) - return; + while( (p_packetized_block = + p_packetizer->pf_packetize( p_packetizer, p_block ? &p_block : NULL )) ) + { + if( p_packetizer->fmt_out.i_extra && !p_dec->fmt_in.i_extra ) + { + es_format_Clean( &p_dec->fmt_in ); + es_format_Copy( &p_dec->fmt_in, &p_packetizer->fmt_out ); + } - p_cc = p_dec_cc->pf_get_cc( p_dec_cc, pb_present ); - if( !p_cc ) - return; + /* If the packetizer provides aspect ratio information, pass it + * to the decoder as a hint if the decoder itself can't provide + * it. Copy it regardless of the current value of the decoder input + * format aspect ratio, to properly propagate changes in aspect + * ratio. */ + if( p_packetizer->fmt_out.video.i_sar_num > 0 && + p_packetizer->fmt_out.video.i_sar_den > 0) + { + p_dec->fmt_in.video.i_sar_num = + p_packetizer->fmt_out.video.i_sar_num; + p_dec->fmt_in.video.i_sar_den= + p_packetizer->fmt_out.video.i_sar_den; + } - vlc_mutex_lock( &p_owner->lock ); - for( i = 0, i_cc_decoder = 0; i < 4; i++ ) - { - p_owner->cc.pb_present[i] |= pb_present[i]; - if( p_owner->cc.pp_decoder[i] ) - i_cc_decoder++; - } + if( p_packetizer->pf_get_cc ) + DecoderGetCc( p_dec, p_packetizer ); - for( i = 0; i < 4; i++ ) - { - if( !p_owner->cc.pp_decoder[i] ) - continue; + while( p_packetized_block ) + { + block_t *p_next = p_packetized_block->p_next; + p_packetized_block->p_next = NULL; - if( i_cc_decoder > 1 ) - DecoderProcess( p_owner->cc.pp_decoder[i], block_Duplicate( p_cc ) ); - else - DecoderProcess( p_owner->cc.pp_decoder[i], p_cc ); - i_cc_decoder--; - b_processed = true; + DecoderDecodeVideo( p_dec, p_packetized_block ); + + p_packetized_block = p_next; + } + } + /* The packetizer does not output a block that tell the decoder to flush + * do it ourself */ + if( b_flush ) + { + block_t *p_null = DecoderBlockFlushNew(); + if( p_null ) + DecoderDecodeVideo( p_dec, p_null ); + } + } + else + { + DecoderDecodeVideo( p_dec, p_block ); } - vlc_mutex_unlock( &p_owner->lock ); - if( !b_processed ) - block_Release( p_cc ); + if( b_flush && p_owner->p_vout ) + vout_Flush( p_owner->p_vout, VLC_TS_INVALID+1 ); } -static void DecoderPlayVideo( decoder_t *p_dec, picture_t *p_picture, +static void DecoderPlayAudio( decoder_t *p_dec, block_t *p_audio, int *pi_played_sum, int *pi_lost_sum ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; - vout_thread_t *p_vout = p_owner->p_vout; - bool b_first_buffered; + audio_output_t *p_aout = p_owner->p_aout; - if( p_picture->date <= VLC_TS_INVALID ) + /* */ + if( p_audio && p_audio->i_pts <= VLC_TS_INVALID ) // FIXME --VLC_TS_INVALID verify audio_output/* { - msg_Warn( p_dec, "non-dated video buffer received" ); + msg_Warn( p_dec, "non-dated audio buffer received" ); *pi_lost_sum += 1; - vout_ReleasePicture( p_vout, p_picture ); + block_Release( p_audio ); return; } /* */ vlc_mutex_lock( &p_owner->lock ); - if( ( p_owner->b_buffering && !p_owner->buffer.b_first ) || p_owner->buffer.p_picture ) + if( p_audio && p_owner->b_waiting ) { - p_picture->p_next = NULL; - - *p_owner->buffer.pp_picture_next = p_picture; - p_owner->buffer.pp_picture_next = &p_picture->p_next; - - p_owner->buffer.i_count++; - if( p_owner->buffer.i_count > DECODER_MAX_BUFFERING_COUNT || - p_picture->date - p_owner->buffer.p_picture->date > DECODER_MAX_BUFFERING_VIDEO_DURATION ) - { - p_owner->buffer.b_full = true; - vlc_cond_signal( &p_owner->wait_acknowledge ); - } + p_owner->b_has_data = true; + vlc_cond_signal( &p_owner->wait_acknowledge ); } - b_first_buffered = p_owner->buffer.p_picture != NULL; - for( ;; b_first_buffered = false ) + for( ;; ) { - bool b_has_more = false; + bool b_paused; - bool b_reject; + bool b_reject = DecoderWaitUnblock( p_dec ); - DecoderWaitUnblock( p_dec, &b_reject ); + b_paused = p_owner->b_paused; - if( p_owner->b_buffering && !p_owner->buffer.b_first ) - { - vlc_mutex_unlock( &p_owner->lock ); - return; - } - bool b_buffering_first = p_owner->b_buffering; + if (!p_audio) + break; /* */ - if( p_owner->buffer.p_picture ) - { - p_picture = p_owner->buffer.p_picture; - - p_owner->buffer.p_picture = p_picture->p_next; - p_owner->buffer.i_count--; - - b_has_more = p_owner->buffer.p_picture != NULL; - if( !b_has_more ) - p_owner->buffer.pp_picture_next = &p_owner->buffer.p_picture; - } + int i_rate = INPUT_RATE_DEFAULT; - /* */ - if( b_buffering_first ) - { - assert( p_owner->buffer.b_first ); - assert( !p_owner->buffer.i_count ); - msg_Dbg( p_dec, "Received first picture" ); - p_owner->buffer.b_first = false; - p_picture->b_force = true; - } + DecoderFixTs( p_dec, &p_audio->i_pts, NULL, &p_audio->i_length, + &i_rate, AOUT_MAX_ADVANCE_TIME ); - const bool b_dated = p_picture->date > VLC_TS_INVALID; - int i_rate = INPUT_RATE_DEFAULT; - DecoderFixTs( p_dec, &p_picture->date, NULL, NULL, - &i_rate, DECODER_BOGUS_VIDEO_DELAY, false ); + if( p_audio->i_pts <= VLC_TS_INVALID + || i_rate < INPUT_RATE_DEFAULT/AOUT_MAX_INPUT_RATE + || i_rate > INPUT_RATE_DEFAULT*AOUT_MAX_INPUT_RATE ) + b_reject = true; - vlc_mutex_unlock( &p_owner->lock ); + DecoderWaitDate( p_dec, &b_reject, + p_audio->i_pts - AOUT_MAX_PREPARE_TIME ); - /* */ - if( !p_picture->b_force && p_picture->date <= VLC_TS_INVALID ) // FIXME --VLC_TS_INVALID verify video_output/* + if( unlikely(p_owner->b_paused != b_paused) ) + continue; /* race with input thread? retry... */ + if( p_aout == NULL ) b_reject = true; if( !b_reject ) { - if( i_rate != p_owner->i_last_rate || b_first_buffered ) - { - /* Be sure to not display old picture after our own */ - vout_Flush( p_vout, p_picture->date ); - p_owner->i_last_rate = i_rate; - } - vout_PutPicture( p_vout, p_picture ); + assert( !p_owner->b_paused ); + if( !aout_DecPlay( p_aout, p_audio, i_rate ) ) + *pi_played_sum += 1; + *pi_lost_sum += aout_DecGetResetLost( p_aout ); } else { - if( b_dated ) - msg_Warn( p_dec, "early picture skipped" ); - else - msg_Warn( p_dec, "non-dated video buffer received" ); - + msg_Dbg( p_dec, "discarded audio buffer" ); *pi_lost_sum += 1; - vout_ReleasePicture( p_vout, p_picture ); + block_Release( p_audio ); } - int i_tmp_display; - int i_tmp_lost; - vout_GetResetStatistic( p_vout, &i_tmp_display, &i_tmp_lost ); - - *pi_played_sum += i_tmp_display; - *pi_lost_sum += i_tmp_lost; - - if( !b_has_more || b_buffering_first ) - break; - vlc_mutex_lock( &p_owner->lock ); - if( !p_owner->buffer.p_picture ) - { - vlc_mutex_unlock( &p_owner->lock ); - break; - } + break; } + vlc_mutex_unlock( &p_owner->lock ); } -static void DecoderDecodeVideo( decoder_t *p_dec, block_t *p_block ) +static void DecoderDecodeAudio( decoder_t *p_dec, block_t *p_block ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; - picture_t *p_pic; - int i_lost = 0; + block_t *p_aout_buf; int i_decoded = 0; - int i_displayed = 0; + int i_lost = 0; + int i_played = 0; - while( (p_pic = p_dec->pf_decode_video( p_dec, &p_block )) ) + if (!p_block) { + /* Play a NULL block to output buffered frames */ + DecoderPlayAudio( p_dec, NULL, &i_played, &i_lost ); + } + else while( (p_aout_buf = p_dec->pf_decode_audio( p_dec, &p_block )) ) { - vout_thread_t *p_vout = p_owner->p_vout; - if( DecoderIsExitRequested( p_dec ) ) + if( DecoderIsFlushing( p_dec ) ) { /* It prevent freezing VLC in case of broken decoder */ - vout_ReleasePicture( p_vout, p_pic ); + block_Release( p_aout_buf ); if( p_block ) block_Release( p_block ); break; } - i_decoded++; - if( p_owner->i_preroll_end > VLC_TS_INVALID && p_pic->date < p_owner->i_preroll_end ) + if( p_owner->i_preroll_end > VLC_TS_INVALID && + p_aout_buf->i_pts < p_owner->i_preroll_end ) { - vout_ReleasePicture( p_vout, p_pic ); + block_Release( p_aout_buf ); continue; } if( p_owner->i_preroll_end > VLC_TS_INVALID ) { - msg_Dbg( p_dec, "End of video preroll" ); - if( p_vout ) - vout_Flush( p_vout, VLC_TS_INVALID+1 ); + msg_Dbg( p_dec, "End of audio preroll" ); + if( p_owner->p_aout ) + aout_DecFlush( p_owner->p_aout ); /* */ p_owner->i_preroll_end = VLC_TS_INVALID; } - if( p_dec->pf_get_cc && - ( !p_owner->p_packetizer || !p_owner->p_packetizer->pf_get_cc ) ) - DecoderGetCc( p_dec, p_dec ); - - DecoderPlayVideo( p_dec, p_pic, &i_displayed, &i_lost ); + DecoderPlayAudio( p_dec, p_aout_buf, &i_played, &i_lost ); } /* Update ugly stat */ - input_thread_t *p_input = p_owner->p_input; + input_thread_t *p_input = p_owner->p_input; - if( p_input != NULL && (i_decoded > 0 || i_lost > 0 || i_displayed > 0) ) + if( p_input != NULL && (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_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 ); + vlc_mutex_lock( &p_input->p->counters.counters_lock); + stats_Update( p_input->p->counters.p_lost_abuffers, i_lost, NULL ); + stats_Update( p_input->p->counters.p_played_abuffers, i_played, NULL ); + stats_Update( p_input->p->counters.p_decoded_audio, i_decoded, NULL ); + vlc_mutex_unlock( &p_input->p->counters.counters_lock); } } -static void DecoderPlaySpu( decoder_t *p_dec, subpicture_t *p_subpic, - bool b_telx ) +/* This function process a audio block + */ +static void DecoderProcessAudio( decoder_t *p_dec, block_t *p_block, bool b_flush ) { - decoder_owner_sys_t *p_owner = p_dec->p_owner; - vout_thread_t *p_vout = p_owner->p_spu_vout; + decoder_owner_sys_t *p_owner = (decoder_owner_sys_t *)p_dec->p_owner; - /* */ - if( p_subpic->i_start <= VLC_TS_INVALID && !b_telx ) + if( p_owner->p_packetizer ) { - msg_Warn( p_dec, "non-dated spu buffer received" ); - subpicture_Delete( p_subpic ); - return; - } - - /* */ - vlc_mutex_lock( &p_owner->lock ); - - if( p_owner->b_buffering || p_owner->buffer.p_subpic ) - { - p_subpic->p_next = NULL; - - *p_owner->buffer.pp_subpic_next = p_subpic; - p_owner->buffer.pp_subpic_next = &p_subpic->p_next; - - p_owner->buffer.i_count++; - /* XXX it is important to be full after the first one */ - if( p_owner->buffer.i_count > 0 ) - { - p_owner->buffer.b_full = true; - vlc_cond_signal( &p_owner->wait_acknowledge ); - } - } - - for( ;; ) - { - bool b_has_more = false; - bool b_reject; - DecoderWaitUnblock( p_dec, &b_reject ); - - if( p_owner->b_buffering ) - { - vlc_mutex_unlock( &p_owner->lock ); - return; - } - - /* */ - if( p_owner->buffer.p_subpic ) - { - p_subpic = p_owner->buffer.p_subpic; - - p_owner->buffer.p_subpic = p_subpic->p_next; - p_owner->buffer.i_count--; - - b_has_more = p_owner->buffer.p_subpic != NULL; - if( !b_has_more ) - p_owner->buffer.pp_subpic_next = &p_owner->buffer.p_subpic; - } - - /* */ - DecoderFixTs( p_dec, &p_subpic->i_start, &p_subpic->i_stop, NULL, - NULL, INT64_MAX, b_telx ); - - vlc_mutex_unlock( &p_owner->lock ); - - if( p_subpic->i_start <= VLC_TS_INVALID ) - b_reject = true; - - DecoderWaitDate( p_dec, &b_reject, - p_subpic->i_start - SPU_MAX_PREPARE_TIME ); - - if( !b_reject ) - vout_PutSubpicture( p_vout, p_subpic ); - else - subpicture_Delete( p_subpic ); - - if( !b_has_more ) - break; - vlc_mutex_lock( &p_owner->lock ); - if( !p_owner->buffer.p_subpic ) - { - vlc_mutex_unlock( &p_owner->lock ); - break; - } - } -} - -#ifdef ENABLE_SOUT -static void DecoderPlaySout( decoder_t *p_dec, block_t *p_sout_block, - bool b_telx ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; - - assert( p_owner->p_clock ); - assert( !p_sout_block->p_next ); - - vlc_mutex_lock( &p_owner->lock ); - - if( p_owner->b_buffering || p_owner->buffer.p_block ) - { - block_ChainLastAppend( &p_owner->buffer.pp_block_next, p_sout_block ); - - p_owner->buffer.i_count++; - /* XXX it is important to be full after the first one */ - if( p_owner->buffer.i_count > 0 ) - { - p_owner->buffer.b_full = true; - vlc_cond_signal( &p_owner->wait_acknowledge ); - } - } - - for( ;; ) - { - bool b_has_more = false; - bool b_reject; - DecoderWaitUnblock( p_dec, &b_reject ); - - if( p_owner->b_buffering ) - { - vlc_mutex_unlock( &p_owner->lock ); - return; - } - - /* */ - if( p_owner->buffer.p_block ) - { - p_sout_block = p_owner->buffer.p_block; - - p_owner->buffer.p_block = p_sout_block->p_next; - p_owner->buffer.i_count--; - - b_has_more = p_owner->buffer.p_block != NULL; - if( !b_has_more ) - p_owner->buffer.pp_block_next = &p_owner->buffer.p_block; - } - p_sout_block->p_next = NULL; - - DecoderFixTs( p_dec, &p_sout_block->i_dts, &p_sout_block->i_pts, - &p_sout_block->i_length, NULL, INT64_MAX, b_telx ); - - vlc_mutex_unlock( &p_owner->lock ); - - if( !b_reject ) - sout_InputSendBuffer( p_owner->p_sout_input, p_sout_block ); // FIXME --VLC_TS_INVALID inspect stream_output/* - else - block_Release( p_sout_block ); - - if( !b_has_more ) - break; - vlc_mutex_lock( &p_owner->lock ); - if( !p_owner->buffer.p_block ) - { - vlc_mutex_unlock( &p_owner->lock ); - break; - } - } -} -#endif - -/* */ -static void DecoderFlushBuffering( decoder_t *p_dec ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; - - vlc_assert_locked( &p_owner->lock ); - - while( p_owner->buffer.p_picture ) - { - picture_t *p_picture = p_owner->buffer.p_picture; - - p_owner->buffer.p_picture = p_picture->p_next; - p_owner->buffer.i_count--; - - if( p_owner->p_vout ) - { - vout_ReleasePicture( p_owner->p_vout, p_picture ); - } - - if( !p_owner->buffer.p_picture ) - p_owner->buffer.pp_picture_next = &p_owner->buffer.p_picture; - } - while( p_owner->buffer.p_audio ) - { - aout_buffer_t *p_audio = p_owner->buffer.p_audio; - - p_owner->buffer.p_audio = p_audio->p_next; - p_owner->buffer.i_count--; - - aout_BufferFree( p_audio ); - - if( !p_owner->buffer.p_audio ) - p_owner->buffer.pp_audio_next = &p_owner->buffer.p_audio; - } - while( p_owner->buffer.p_subpic ) - { - subpicture_t *p_subpic = p_owner->buffer.p_subpic; - - p_owner->buffer.p_subpic = p_subpic->p_next; - p_owner->buffer.i_count--; - - subpicture_Delete( p_subpic ); - - if( !p_owner->buffer.p_subpic ) - p_owner->buffer.pp_subpic_next = &p_owner->buffer.p_subpic; - } - if( p_owner->buffer.p_block ) - { - block_ChainRelease( p_owner->buffer.p_block ); - - p_owner->buffer.i_count = 0; - p_owner->buffer.p_block = NULL; - p_owner->buffer.pp_block_next = &p_owner->buffer.p_block; - } -} - -#ifdef ENABLE_SOUT -/* This function process a block for sout - */ -static void DecoderProcessSout( decoder_t *p_dec, block_t *p_block ) -{ - 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_CODEC_TELETEXT; - block_t *p_sout_block; - - while( ( p_sout_block = - p_dec->pf_packetize( p_dec, p_block ? &p_block : NULL ) ) ) - { - if( !p_owner->p_sout_input ) - { - es_format_Copy( &p_owner->sout, &p_dec->fmt_out ); - - 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 ) - { - free( p_owner->sout.psz_language ); - p_owner->sout.psz_language = - strdup( p_dec->fmt_in.psz_language ); - } - - p_owner->p_sout_input = - sout_InputNew( p_owner->p_sout, - &p_owner->sout ); - - if( p_owner->p_sout_input == NULL ) - { - msg_Err( p_dec, "cannot create packetizer output (%4.4s)", - (char *)&p_owner->sout.i_codec ); - p_dec->b_error = true; - - while( p_sout_block ) - { - block_t *p_next = p_sout_block->p_next; - block_Release( p_sout_block ); - p_sout_block = p_next; - } - break; - } - } - - while( p_sout_block ) - { - block_t *p_next = p_sout_block->p_next; - - p_sout_block->p_next = NULL; - - DecoderPlaySout( p_dec, p_sout_block, b_telx ); - - p_sout_block = p_next; - } - } -} -#endif - -/* This function process a video block - */ -static void DecoderProcessVideo( decoder_t *p_dec, block_t *p_block, bool b_flush ) -{ - decoder_owner_sys_t *p_owner = (decoder_owner_sys_t *)p_dec->p_owner; - - if( p_owner->p_packetizer ) - { - block_t *p_packetized_block; - decoder_t *p_packetizer = p_owner->p_packetizer; + block_t *p_packetized_block; + decoder_t *p_packetizer = p_owner->p_packetizer; while( (p_packetized_block = p_packetizer->pf_packetize( p_packetizer, p_block ? &p_block : NULL )) ) @@ -1861,15 +1217,13 @@ static void DecoderProcessVideo( decoder_t *p_dec, block_t *p_block, bool b_flus es_format_Clean( &p_dec->fmt_in ); es_format_Copy( &p_dec->fmt_in, &p_packetizer->fmt_out ); } - if( p_packetizer->pf_get_cc ) - DecoderGetCc( p_dec, p_packetizer ); while( p_packetized_block ) { block_t *p_next = p_packetized_block->p_next; p_packetized_block->p_next = NULL; - DecoderDecodeVideo( p_dec, p_packetized_block ); + DecoderDecodeAudio( p_dec, p_packetized_block ); p_packetized_block = p_next; } @@ -1880,64 +1234,56 @@ static void DecoderProcessVideo( decoder_t *p_dec, block_t *p_block, bool b_flus { block_t *p_null = DecoderBlockFlushNew(); if( p_null ) - DecoderDecodeVideo( p_dec, p_null ); + DecoderDecodeAudio( p_dec, p_null ); } } - else if( p_block ) + else { - DecoderDecodeVideo( p_dec, p_block ); + DecoderDecodeAudio( p_dec, p_block ); } - if( b_flush && p_owner->p_vout ) - vout_Flush( p_owner->p_vout, VLC_TS_INVALID+1 ); + if( b_flush && p_owner->p_aout ) + aout_DecFlush( p_owner->p_aout ); } -/* This function process a audio block - */ -static void DecoderProcessAudio( decoder_t *p_dec, block_t *p_block, bool b_flush ) +static void DecoderPlaySpu( decoder_t *p_dec, subpicture_t *p_subpic ) { - decoder_owner_sys_t *p_owner = (decoder_owner_sys_t *)p_dec->p_owner; + decoder_owner_sys_t *p_owner = p_dec->p_owner; + vout_thread_t *p_vout = p_owner->p_spu_vout; - if( p_owner->p_packetizer ) + /* */ + if( p_subpic->i_start <= VLC_TS_INVALID ) { - block_t *p_packetized_block; - decoder_t *p_packetizer = p_owner->p_packetizer; - - while( (p_packetized_block = - p_packetizer->pf_packetize( p_packetizer, p_block ? &p_block : NULL )) ) - { - if( p_packetizer->fmt_out.i_extra && !p_dec->fmt_in.i_extra ) - { - es_format_Clean( &p_dec->fmt_in ); - es_format_Copy( &p_dec->fmt_in, &p_packetizer->fmt_out ); - } - - while( p_packetized_block ) - { - block_t *p_next = p_packetized_block->p_next; - p_packetized_block->p_next = NULL; + msg_Warn( p_dec, "non-dated spu buffer received" ); + subpicture_Delete( p_subpic ); + return; + } - DecoderDecodeAudio( p_dec, p_packetized_block ); + /* */ + vlc_mutex_lock( &p_owner->lock ); - p_packetized_block = p_next; - } - } - /* The packetizer does not output a block that tell the decoder to flush - * do it ourself */ - if( b_flush ) - { - block_t *p_null = DecoderBlockFlushNew(); - if( p_null ) - DecoderDecodeAudio( p_dec, p_null ); - } - } - else if( p_block ) + if( p_owner->b_waiting ) { - DecoderDecodeAudio( p_dec, p_block ); + p_owner->b_has_data = true; + vlc_cond_signal( &p_owner->wait_acknowledge ); } - if( b_flush && p_owner->p_aout ) - aout_DecFlush( p_owner->p_aout ); + bool b_reject = DecoderWaitUnblock( p_dec ); + + DecoderFixTs( p_dec, &p_subpic->i_start, &p_subpic->i_stop, NULL, + NULL, INT64_MAX ); + + if( p_subpic->i_start <= VLC_TS_INVALID ) + b_reject = true; + + DecoderWaitDate( p_dec, &b_reject, + p_subpic->i_start - SPU_MAX_PREPARE_TIME ); + vlc_mutex_unlock( &p_owner->lock ); + + if( !b_reject ) + vout_PutSubpicture( p_vout, p_subpic ); + else + subpicture_Delete( p_subpic ); } /* This function process a subtitle block @@ -1945,7 +1291,6 @@ static void DecoderProcessAudio( decoder_t *p_dec, block_t *p_block, bool b_flus static void DecoderProcessSpu( decoder_t *p_dec, block_t *p_block, bool b_flush ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; - const bool b_telx = p_dec->fmt_in.i_codec == VLC_CODEC_TELETEXT; input_thread_t *p_input = p_owner->p_input; vout_thread_t *p_vout; @@ -1956,8 +1301,7 @@ static void DecoderProcessSpu( decoder_t *p_dec, block_t *p_block, bool b_flush if( p_input != NULL ) { vlc_mutex_lock( &p_input->p->counters.counters_lock ); - stats_UpdateInteger( p_dec, p_input->p->counters.p_decoded_sub, 1, - NULL ); + stats_Update( p_input->p->counters.p_decoded_sub, 1, NULL ); vlc_mutex_unlock( &p_input->p->counters.counters_lock ); } @@ -1973,7 +1317,7 @@ static void DecoderProcessSpu( decoder_t *p_dec, block_t *p_block, bool b_flush } else { - DecoderPlaySpu( p_dec, p_spu, b_telx ); + DecoderPlaySpu( p_dec, p_spu ); } } else @@ -2002,7 +1346,6 @@ static void DecoderProcessOnFlush( decoder_t *p_dec ) decoder_owner_sys_t *p_owner = p_dec->p_owner; vlc_mutex_lock( &p_owner->lock ); - DecoderFlushBuffering( p_dec ); if( p_owner->b_flushing ) { @@ -2024,6 +1367,13 @@ static void DecoderProcess( decoder_t *p_dec, block_t *p_block ) decoder_owner_sys_t *p_owner = (decoder_owner_sys_t *)p_dec->p_owner; const bool b_flush_request = p_block && (p_block->i_flags & BLOCK_FLAG_CORE_FLUSH); + if( p_dec->b_error ) + { + if( p_block ) + block_Release( p_block ); + goto flush; + } + if( p_block && p_block->i_buffer <= 0 ) { assert( !b_flush_request ); @@ -2074,30 +1424,243 @@ static void DecoderProcess( decoder_t *p_dec, block_t *p_block ) } /* */ - if( b_flush_request ) - DecoderProcessOnFlush( p_dec ); -} - -static void DecoderError( decoder_t *p_dec, block_t *p_block ) -{ - const bool b_flush_request = p_block && (p_block->i_flags & BLOCK_FLAG_CORE_FLUSH); - - /* */ - if( p_block ) - block_Release( p_block ); - +flush: if( b_flush_request ) DecoderProcessOnFlush( p_dec ); } /** - * Destroys a decoder object + * The decoding main loop * - * \param p_dec the decoder object - * \return nothing + * \param p_dec the decoder */ -static void DeleteDecoder( decoder_t * p_dec ) +static void *DecoderThread( void *p_data ) +{ + decoder_t *p_dec = (decoder_t *)p_data; + decoder_owner_sys_t *p_owner = p_dec->p_owner; + + /* The decoder's main loop */ + for( ;; ) + { + block_t *p_block; + + vlc_fifo_Lock( p_owner->p_fifo ); + vlc_fifo_CleanupPush( p_owner->p_fifo ); + + while( vlc_fifo_IsEmpty( p_owner->p_fifo ) ) + { + if( p_owner->b_woken ) + break; + vlc_fifo_Wait( p_owner->p_fifo ); + /* Make sure there is no cancellation point other than this one^^. + * If you need one, be sure to push cleanup of p_block. */ + } + + p_block = vlc_fifo_DequeueUnlocked( p_owner->p_fifo ); + if( p_block != NULL ) + vlc_cond_signal( &p_owner->wait_acknowledge ); + + p_owner->b_woken = false; + vlc_cleanup_run(); + + if( p_block == NULL || p_block->i_flags & BLOCK_FLAG_CORE_EOS ) + DecoderSignalWait( p_dec ); + + if( p_block ) + { + int canc = vlc_savecancel(); + + if( p_block->i_flags & BLOCK_FLAG_CORE_EOS ) + { + /* calling DecoderProcess() with NULL block will make + * decoders/packetizers flush their buffers */ + block_Release( p_block ); + p_block = NULL; + } + + DecoderProcess( p_dec, p_block ); + + vlc_restorecancel( canc ); + } + } + return NULL; +} + +/** + * Create a decoder object + * + * \param p_input the input thread + * \param p_es the es descriptor + * \param b_packetizer instead of a decoder + * \return the decoder object + */ +static decoder_t * CreateDecoder( vlc_object_t *p_parent, + input_thread_t *p_input, + const es_format_t *fmt, bool b_packetizer, + input_resource_t *p_resource, + sout_instance_t *p_sout ) +{ + decoder_t *p_dec; + decoder_owner_sys_t *p_owner; + es_format_t null_es_format; + + p_dec = vlc_custom_create( p_parent, sizeof( *p_dec ), "decoder" ); + if( p_dec == NULL ) + return NULL; + + p_dec->pf_decode_audio = NULL; + p_dec->pf_decode_video = NULL; + p_dec->pf_decode_sub = NULL; + p_dec->pf_get_cc = NULL; + p_dec->pf_packetize = NULL; + + /* Initialize the decoder */ + p_dec->p_module = NULL; + + memset( &null_es_format, 0, sizeof(es_format_t) ); + es_format_Copy( &p_dec->fmt_in, fmt ); + es_format_Copy( &p_dec->fmt_out, &null_es_format ); + + p_dec->p_description = NULL; + + /* Allocate our private structure for the decoder */ + p_dec->p_owner = p_owner = malloc( sizeof( decoder_owner_sys_t ) ); + if( unlikely(p_owner == NULL) ) + { + vlc_object_release( p_dec ); + return NULL; + } + p_owner->i_preroll_end = VLC_TS_INVALID; + p_owner->i_last_rate = INPUT_RATE_DEFAULT; + p_owner->p_input = p_input; + p_owner->p_resource = p_resource; + p_owner->p_aout = NULL; + p_owner->p_vout = NULL; + p_owner->p_spu_vout = NULL; + p_owner->i_spu_channel = 0; + p_owner->i_spu_order = 0; + p_owner->p_sout = p_sout; + p_owner->p_sout_input = NULL; + p_owner->p_packetizer = NULL; + p_owner->b_packetizer = b_packetizer; + es_format_Init( &p_owner->fmt, UNKNOWN_ES, 0 ); + + /* decoder fifo */ + p_owner->b_woken = false; + p_owner->p_fifo = block_FifoNew(); + if( unlikely(p_owner->p_fifo == NULL) ) + { + free( p_owner ); + vlc_object_release( p_dec ); + return NULL; + } + + /* Set buffers allocation callbacks for the decoders */ + p_dec->pf_aout_format_update = aout_update_format; + p_dec->pf_vout_format_update = vout_update_format; + p_dec->pf_vout_buffer_new = vout_new_buffer; + p_dec->pf_spu_buffer_new = spu_new_buffer; + /* */ + p_dec->pf_get_attachments = DecoderGetInputAttachments; + p_dec->pf_get_display_date = DecoderGetDisplayDate; + p_dec->pf_get_display_rate = DecoderGetDisplayRate; + + /* Find a suitable decoder/packetizer module */ + if( !b_packetizer ) + p_dec->p_module = module_need( p_dec, "decoder", "$codec", false ); + else + p_dec->p_module = module_need( p_dec, "packetizer", "$packetizer", false ); + + /* Check if decoder requires already packetized data */ + if( !b_packetizer && + p_dec->b_need_packetized && !p_dec->fmt_in.b_packetized ) + { + p_owner->p_packetizer = + vlc_custom_create( p_parent, sizeof( decoder_t ), "packetizer" ); + if( p_owner->p_packetizer ) + { + es_format_Copy( &p_owner->p_packetizer->fmt_in, + &p_dec->fmt_in ); + + es_format_Copy( &p_owner->p_packetizer->fmt_out, + &null_es_format ); + + p_owner->p_packetizer->p_module = + module_need( p_owner->p_packetizer, + "packetizer", "$packetizer", false ); + + if( !p_owner->p_packetizer->p_module ) + { + es_format_Clean( &p_owner->p_packetizer->fmt_in ); + vlc_object_release( p_owner->p_packetizer ); + p_owner->p_packetizer = NULL; + } + } + } + + /* Copy ourself the input replay gain */ + if( fmt->i_cat == AUDIO_ES ) + { + for( unsigned i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ ) + { + if( !p_dec->fmt_out.audio_replay_gain.pb_peak[i] ) + { + p_dec->fmt_out.audio_replay_gain.pb_peak[i] = fmt->audio_replay_gain.pb_peak[i]; + p_dec->fmt_out.audio_replay_gain.pf_peak[i] = fmt->audio_replay_gain.pf_peak[i]; + } + if( !p_dec->fmt_out.audio_replay_gain.pb_gain[i] ) + { + p_dec->fmt_out.audio_replay_gain.pb_gain[i] = fmt->audio_replay_gain.pb_gain[i]; + p_dec->fmt_out.audio_replay_gain.pf_gain[i] = fmt->audio_replay_gain.pf_gain[i]; + } + } + } + + /* */ + vlc_mutex_init( &p_owner->lock ); + vlc_cond_init( &p_owner->wait_request ); + vlc_cond_init( &p_owner->wait_acknowledge ); + + p_owner->b_fmt_description = false; + p_owner->p_description = NULL; + + p_owner->b_paused = false; + p_owner->pause.i_date = VLC_TS_INVALID; + p_owner->pause.i_ignore = 0; + + p_owner->b_waiting = false; + p_owner->b_first = true; + p_owner->b_has_data = false; + + p_owner->b_flushing = false; + + /* */ + p_owner->cc.b_supported = false; + if( !b_packetizer ) + { + if( p_owner->p_packetizer && p_owner->p_packetizer->pf_get_cc ) + p_owner->cc.b_supported = true; + if( p_dec->pf_get_cc ) + p_owner->cc.b_supported = true; + } + + for( unsigned i = 0; i < 4; i++ ) + { + p_owner->cc.pb_present[i] = false; + p_owner->cc.pp_decoder[i] = NULL; + } + p_owner->i_ts_delay = 0; + return p_dec; +} + +/** + * Destroys a decoder object + * + * \param p_dec the decoder object + * \return nothing + */ +static void DeleteDecoder( decoder_t * p_dec ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; @@ -2106,21 +1669,15 @@ static void DeleteDecoder( decoder_t * p_dec ) (unsigned)block_FifoCount( p_owner->p_fifo ) ); /* Free all packets still in the decoder fifo. */ - block_FifoEmpty( p_owner->p_fifo ); block_FifoRelease( p_owner->p_fifo ); - /* */ - vlc_mutex_lock( &p_owner->lock ); - DecoderFlushBuffering( p_dec ); - vlc_mutex_unlock( &p_owner->lock ); - /* Cleanup */ if( p_owner->p_aout ) { /* TODO: REVISIT gap-less audio */ aout_DecFlush( p_owner->p_aout ); aout_DecDelete( p_owner->p_aout ); - input_resource_RequestAout( p_owner->p_resource, p_owner->p_aout ); + input_resource_PutAout( p_owner->p_resource, p_owner->p_aout ); if( p_owner->p_input != NULL ) input_SendEventAout( p_owner->p_input ); } @@ -2141,15 +1698,13 @@ static void DeleteDecoder( decoder_t * p_dec ) if( p_owner->p_sout_input ) { sout_InputDelete( p_owner->p_sout_input ); - es_format_Clean( &p_owner->sout ); } #endif + es_format_Clean( &p_owner->fmt ); if( p_dec->fmt_out.i_cat == SPU_ES ) { - vout_thread_t *p_vout; - - p_vout = input_resource_HoldVout( p_owner->p_resource ); + vout_thread_t *p_vout = input_resource_HoldVout( p_owner->p_resource ); if( p_vout ) { if( p_owner->p_spu_vout == p_vout ) @@ -2162,7 +1717,6 @@ static void DeleteDecoder( decoder_t * p_dec ) es_format_Clean( &p_dec->fmt_out ); if( p_dec->p_description ) vlc_meta_Delete( p_dec->p_description ); - es_format_Clean( &p_owner->fmt_description ); if( p_owner->p_description ) vlc_meta_Delete( p_owner->p_description ); @@ -2186,359 +1740,467 @@ static void DeleteDecoder( decoder_t * p_dec ) free( p_owner ); } -/***************************************************************************** - * Buffers allocation callbacks for the decoders - *****************************************************************************/ -static void DecoderUpdateFormatLocked( decoder_t *p_dec ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; - - vlc_assert_locked( &p_owner->lock ); - - p_owner->b_fmt_description = true; - - /* Copy es_format */ - es_format_Clean( &p_owner->fmt_description ); - es_format_Copy( &p_owner->fmt_description, &p_dec->fmt_out ); - - /* Move p_description */ - if( p_owner->p_description && p_dec->p_description ) - vlc_meta_Delete( p_owner->p_description ); - p_owner->p_description = p_dec->p_description; - p_dec->p_description = NULL; -} -static vout_thread_t *aout_request_vout( void *p_private, - vout_thread_t *p_vout, video_format_t *p_fmt, bool b_recyle ) +/* */ +static void DecoderUnsupportedCodec( decoder_t *p_dec, vlc_fourcc_t codec ) { - decoder_t *p_dec = p_private; - decoder_owner_sys_t *p_owner = p_dec->p_owner; - input_thread_t *p_input = p_owner->p_input; - - p_vout = input_resource_RequestVout( p_owner->p_resource, p_vout, p_fmt, 1, - b_recyle ); - if( p_input != NULL ) - input_SendEventVout( p_input ); - - return p_vout; + if (codec != VLC_FOURCC('u','n','d','f')) { + const char *desc = vlc_fourcc_GetDescription(p_dec->fmt_in.i_cat, codec); + if (!desc || !*desc) + desc = N_("No description for this codec"); + msg_Err( p_dec, "Codec `%4.4s' (%s) is not supported.", (char*)&codec, desc ); + dialog_Fatal( p_dec, _("Codec not supported"), + _("VLC could not decode the format \"%4.4s\" (%s)"), + (char*)&codec, desc ); + } else { + msg_Err( p_dec, "could not identify codec" ); + dialog_Fatal( p_dec, _("Unidentified codec"), + _("VLC could not identify the audio or video codec" ) ); + } } -static aout_buffer_t *aout_new_buffer( decoder_t *p_dec, int i_samples ) +/* TODO: pass p_sout through p_resource? -- Courmisch */ +static decoder_t *decoder_New( vlc_object_t *p_parent, input_thread_t *p_input, + const es_format_t *fmt, input_clock_t *p_clock, + input_resource_t *p_resource, + sout_instance_t *p_sout ) { - decoder_owner_sys_t *p_owner = p_dec->p_owner; - aout_buffer_t *p_buffer; + decoder_t *p_dec = NULL; + const char *psz_type = p_sout ? N_("packetizer") : N_("decoder"); + int i_priority; - if( p_owner->p_aout && - ( p_dec->fmt_out.audio.i_rate != p_owner->audio.i_rate || - p_dec->fmt_out.audio.i_original_channels != - p_owner->audio.i_original_channels || - p_dec->fmt_out.audio.i_bytes_per_frame != - p_owner->audio.i_bytes_per_frame ) ) + /* Create the decoder configuration structure */ + p_dec = CreateDecoder( p_parent, p_input, fmt, + p_sout != NULL, p_resource, p_sout ); + if( p_dec == NULL ) { - audio_output_t *p_aout = p_owner->p_aout; + msg_Err( p_parent, "could not create %s", psz_type ); + dialog_Fatal( p_parent, _("Streaming / Transcoding failed"), + _("VLC could not open the %s module."), + vlc_gettext( psz_type ) ); + return NULL; + } - /* Parameters changed, restart the aout */ - vlc_mutex_lock( &p_owner->lock ); + if( !p_dec->p_module ) + { + DecoderUnsupportedCodec( p_dec, fmt->i_codec ); - DecoderFlushBuffering( p_dec ); + DeleteDecoder( p_dec ); + return NULL; + } - aout_DecDelete( p_owner->p_aout ); - p_owner->p_aout = NULL; + p_dec->p_owner->p_clock = p_clock; + assert( p_dec->fmt_out.i_cat != UNKNOWN_ES ); - vlc_mutex_unlock( &p_owner->lock ); - input_resource_RequestAout( p_owner->p_resource, p_aout ); - } + if( p_dec->fmt_out.i_cat == AUDIO_ES ) + i_priority = VLC_THREAD_PRIORITY_AUDIO; + else + i_priority = VLC_THREAD_PRIORITY_VIDEO; - if( p_owner->p_aout == NULL ) + /* Spawn the decoder thread */ + if( vlc_clone( &p_dec->p_owner->thread, DecoderThread, p_dec, i_priority ) ) { - const int i_force_dolby = var_InheritInteger( p_dec, "force-dolby-surround" ); - audio_sample_format_t format; - audio_output_t *p_aout; - aout_request_vout_t request_vout; + msg_Err( p_dec, "cannot spawn decoder thread" ); + module_unneed( p_dec, p_dec->p_module ); + DeleteDecoder( p_dec ); + return NULL; + } - p_dec->fmt_out.audio.i_format = p_dec->fmt_out.i_codec; - p_owner->audio = p_dec->fmt_out.audio; + return p_dec; +} - 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) ) - { - if( i_force_dolby == 1 ) - { - format.i_original_channels = format.i_original_channels | - AOUT_CHAN_DOLBYSTEREO; - } - else /* i_force_dolby == 2 */ - { - format.i_original_channels = format.i_original_channels & - ~AOUT_CHAN_DOLBYSTEREO; - } - } - request_vout.pf_request_vout = aout_request_vout; - request_vout.p_private = p_dec; +/** + * Spawns a new decoder thread from the input thread + * + * \param p_input the input thread + * \param p_es the es descriptor + * \return the spawned decoder object + */ +decoder_t *input_DecoderNew( input_thread_t *p_input, + es_format_t *fmt, input_clock_t *p_clock, + sout_instance_t *p_sout ) +{ + return decoder_New( VLC_OBJECT(p_input), p_input, fmt, p_clock, + p_input->p->p_resource, p_sout ); +} + +/** + * Spawn a decoder thread outside of the input thread. + */ +decoder_t *input_DecoderCreate( vlc_object_t *p_parent, const es_format_t *fmt, + input_resource_t *p_resource ) +{ + return decoder_New( p_parent, NULL, fmt, NULL, p_resource, NULL ); +} - assert( p_owner->p_aout == NULL ); - p_aout = input_resource_RequestAout( p_owner->p_resource, NULL ); - if( p_aout ) - { - aout_FormatPrepare( &format ); - if( aout_DecNew( p_aout, &format, - &p_dec->fmt_out.audio_replay_gain, - &request_vout ) ) - { - input_resource_RequestAout( p_owner->p_resource, p_aout ); - p_aout = NULL; - } - } - vlc_mutex_lock( &p_owner->lock ); +/** + * Kills a decoder thread and waits until it's finished + * + * \param p_input the input thread + * \param p_es the es descriptor + * \return nothing + */ +void input_DecoderDelete( decoder_t *p_dec ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; - p_owner->p_aout = p_aout; - DecoderUpdateFormatLocked( p_dec ); + vlc_cancel( p_owner->thread ); - vlc_mutex_unlock( &p_owner->lock ); + /* Make sure we aren't paused/waiting/decoding anymore */ + vlc_mutex_lock( &p_owner->lock ); + p_owner->b_paused = false; + p_owner->b_waiting = false; + p_owner->b_flushing = true; + vlc_cond_signal( &p_owner->wait_request ); + vlc_mutex_unlock( &p_owner->lock ); - if( p_owner->p_input != NULL ) - input_SendEventAout( p_owner->p_input ); + vlc_join( p_owner->thread, NULL ); - if( p_aout == NULL ) + module_unneed( p_dec, p_dec->p_module ); + + /* */ + if( p_dec->p_owner->cc.b_supported ) + { + int i; + for( i = 0; i < 4; i++ ) + input_DecoderSetCcState( p_dec, false, i ); + } + + /* Delete decoder */ + DeleteDecoder( p_dec ); +} + +/** + * Put a block_t in the decoder's fifo. + * Thread-safe w.r.t. the decoder. May be a cancellation point. + * + * \param p_dec the decoder object + * \param p_block the data block + */ +void input_DecoderDecode( decoder_t *p_dec, block_t *p_block, bool b_do_pace ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; + + vlc_fifo_Lock( p_owner->p_fifo ); + if( !b_do_pace ) + { + /* FIXME: ideally we would check the time amount of data + * in the FIFO instead of its size. */ + /* 400 MiB, i.e. ~ 50mb/s for 60s */ + if( vlc_fifo_GetBytes( p_owner->p_fifo ) > 400*1024*1024 ) { - msg_Err( p_dec, "failed to create audio output" ); - p_dec->b_error = true; - return NULL; + msg_Warn( p_dec, "decoder/packetizer fifo full (data not " + "consumed quickly enough), resetting fifo!" ); + block_ChainRelease( vlc_fifo_DequeueAllUnlocked( p_owner->p_fifo ) ); } - p_dec->fmt_out.audio.i_bytes_per_frame = - p_owner->audio.i_bytes_per_frame; + } + else + if( !p_owner->b_waiting ) + { /* The FIFO is not consumed when waiting, so pacing would deadlock VLC. + * Locking is not necessary as b_waiting is only read, not written by + * the decoder thread. */ + while( vlc_fifo_GetCount( p_owner->p_fifo ) >= 10 ) + vlc_fifo_WaitCond( p_owner->p_fifo, &p_owner->wait_acknowledge ); } - p_buffer = aout_DecNewBuffer( p_owner->p_aout, i_samples ); - - return p_buffer; + vlc_fifo_QueueUnlocked( p_owner->p_fifo, p_block ); + vlc_fifo_Unlock( p_owner->p_fifo ); } -static picture_t *vout_new_buffer( decoder_t *p_dec ) +bool input_DecoderIsEmpty( decoder_t * p_dec ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; + assert( !p_owner->b_waiting ); + + bool b_empty = block_FifoCount( p_dec->p_owner->p_fifo ) <= 0; - 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_visible_width != p_owner->video.i_visible_width || - p_dec->fmt_out.video.i_visible_height != p_owner->video.i_visible_height || - p_dec->fmt_out.video.i_x_offset != p_owner->video.i_x_offset || - p_dec->fmt_out.video.i_y_offset != p_owner->video.i_y_offset || - p_dec->fmt_out.i_codec != p_owner->video.i_chroma || - (int64_t)p_dec->fmt_out.video.i_sar_num * p_owner->video.i_sar_den != - (int64_t)p_dec->fmt_out.video.i_sar_den * p_owner->video.i_sar_num ) + if( b_empty ) { - vout_thread_t *p_vout; + vlc_mutex_lock( &p_owner->lock ); + /* TODO subtitles support */ + if( p_owner->fmt.i_cat == VIDEO_ES && p_owner->p_vout ) + b_empty = vout_IsEmpty( p_owner->p_vout ); + else if( p_owner->fmt.i_cat == AUDIO_ES && p_owner->p_aout ) + b_empty = aout_DecIsEmpty( p_owner->p_aout ); + vlc_mutex_unlock( &p_owner->lock ); + } + return b_empty; +} - if( !p_dec->fmt_out.video.i_width || - !p_dec->fmt_out.video.i_height ) - { - /* Can't create a new vout without display size */ - return NULL; - } +static void DecoderFlush( decoder_t *p_dec ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; - video_format_t fmt = p_dec->fmt_out.video; - fmt.i_chroma = p_dec->fmt_out.i_codec; - p_owner->video = fmt; + vlc_assert_locked( &p_owner->lock ); - if( !fmt.i_visible_width || !fmt.i_visible_height ) - { - if( p_dec->fmt_in.video.i_visible_width && - p_dec->fmt_in.video.i_visible_height ) - { - fmt.i_visible_width = p_dec->fmt_in.video.i_visible_width; - fmt.i_visible_height = p_dec->fmt_in.video.i_visible_height; - fmt.i_x_offset = p_dec->fmt_in.video.i_x_offset; - fmt.i_y_offset = p_dec->fmt_in.video.i_y_offset; - } - else - { - fmt.i_visible_width = fmt.i_width; - fmt.i_visible_height = fmt.i_height; - fmt.i_x_offset = 0; - fmt.i_y_offset = 0; - } - } + /* Empty the fifo */ + block_FifoEmpty( p_owner->p_fifo ); - if( fmt.i_visible_height == 1088 && - var_CreateGetBool( p_dec, "hdtv-fix" ) ) - { - fmt.i_visible_height = 1080; - if( !(fmt.i_sar_num % 136)) - { - fmt.i_sar_num *= 135; - fmt.i_sar_den *= 136; - } - msg_Warn( p_dec, "Fixing broken HDTV stream (display_height=1088)"); - } + /* Monitor for flush end */ + p_owner->b_flushing = true; + vlc_cond_signal( &p_owner->wait_request ); - if( !fmt.i_sar_num || !fmt.i_sar_den ) - { - fmt.i_sar_num = 1; - fmt.i_sar_den = 1; - } + /* Send a special block */ + block_t *p_null = DecoderBlockFlushNew(); + if( !p_null ) + return; + input_DecoderDecode( p_dec, p_null, false ); - vlc_ureduce( &fmt.i_sar_num, &fmt.i_sar_den, - fmt.i_sar_num, fmt.i_sar_den, 50000 ); + /* */ + while( p_owner->b_flushing ) + vlc_cond_wait( &p_owner->wait_acknowledge, &p_owner->lock ); +} - vlc_mutex_lock( &p_owner->lock ); +/** + * Requests that the decoder immediately discard all pending buffers. + * This is useful at end of stream, when seeking or when deselecting a stream. + */ +void input_DecoderFlush( decoder_t *p_dec ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; - DecoderFlushBuffering( p_dec ); + vlc_mutex_lock( &p_owner->lock ); + DecoderFlush( p_dec ); + vlc_mutex_unlock( &p_owner->lock ); +} - p_vout = p_owner->p_vout; - p_owner->p_vout = NULL; - vlc_mutex_unlock( &p_owner->lock ); +void input_DecoderIsCcPresent( decoder_t *p_dec, bool pb_present[4] ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; + int i; - unsigned dpb_size; - switch( p_dec->fmt_in.i_codec ) - { - case VLC_CODEC_H264: - case VLC_CODEC_DIRAC: /* FIXME valid ? */ - dpb_size = 18; - break; - case VLC_CODEC_VP5: - case VLC_CODEC_VP6: - case VLC_CODEC_VP6F: - case VLC_CODEC_VP8: - dpb_size = 3; - break; - default: - dpb_size = 2; - break; - } - p_vout = input_resource_RequestVout( p_owner->p_resource, - p_vout, &fmt, - dpb_size + 1 + DECODER_MAX_BUFFERING_COUNT, - true ); - vlc_mutex_lock( &p_owner->lock ); - p_owner->p_vout = p_vout; + vlc_mutex_lock( &p_owner->lock ); + for( i = 0; i < 4; i++ ) + pb_present[i] = p_owner->cc.pb_present[i]; + vlc_mutex_unlock( &p_owner->lock ); +} - DecoderUpdateFormatLocked( p_dec ); +int input_DecoderSetCcState( decoder_t *p_dec, bool b_decode, int i_channel ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; - vlc_mutex_unlock( &p_owner->lock ); + //msg_Warn( p_dec, "input_DecoderSetCcState: %d @%d", b_decode, i_channel ); - if( p_owner->p_input != NULL ) - input_SendEventVout( p_owner->p_input ); - if( p_vout == NULL ) + if( i_channel < 0 || i_channel >= 4 || !p_owner->cc.pb_present[i_channel] ) + return VLC_EGENERIC; + + if( b_decode ) + { + static const vlc_fourcc_t fcc[4] = { + VLC_FOURCC('c', 'c', '1', ' '), + VLC_FOURCC('c', 'c', '2', ' '), + VLC_FOURCC('c', 'c', '3', ' '), + VLC_FOURCC('c', 'c', '4', ' '), + }; + decoder_t *p_cc; + es_format_t fmt; + + es_format_Init( &fmt, SPU_ES, fcc[i_channel] ); + p_cc = input_DecoderNew( p_owner->p_input, &fmt, + p_dec->p_owner->p_clock, p_owner->p_sout ); + if( !p_cc ) { - msg_Err( p_dec, "failed to create video output" ); - p_dec->b_error = true; - return NULL; + msg_Err( p_dec, "could not create decoder" ); + dialog_Fatal( p_dec, _("Streaming / Transcoding failed"), "%s", + _("VLC could not open the decoder module.") ); + return VLC_EGENERIC; } - } + else if( !p_cc->p_module ) + { + DecoderUnsupportedCodec( p_dec, fcc[i_channel] ); + input_DecoderDelete(p_cc); + return VLC_EGENERIC; + } + p_cc->p_owner->p_clock = p_owner->p_clock; - /* Get a new picture - */ - for( ;; ) + vlc_mutex_lock( &p_owner->lock ); + p_owner->cc.pp_decoder[i_channel] = p_cc; + vlc_mutex_unlock( &p_owner->lock ); + } + else { - if( DecoderIsExitRequested( p_dec ) || p_dec->b_error ) - return NULL; + decoder_t *p_cc; - picture_t *p_picture = vout_GetPicture( p_owner->p_vout ); - if( p_picture ) - return p_picture; + vlc_mutex_lock( &p_owner->lock ); + p_cc = p_owner->cc.pp_decoder[i_channel]; + p_owner->cc.pp_decoder[i_channel] = NULL; + vlc_mutex_unlock( &p_owner->lock ); - if( DecoderIsFlushing( p_dec ) ) - return NULL; + if( p_cc ) + input_DecoderDelete(p_cc); + } + return VLC_SUCCESS; +} - /* */ - DecoderSignalBuffering( p_dec, true ); +int input_DecoderGetCcState( decoder_t *p_dec, bool *pb_decode, int i_channel ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; - /* Check the decoder doesn't leak pictures */ - vout_FixLeaks( p_owner->p_vout ); + *pb_decode = false; + if( i_channel < 0 || i_channel >= 4 || !p_owner->cc.pb_present[i_channel] ) + return VLC_EGENERIC; - /* FIXME add a vout_WaitPictureAvailable (timedwait) */ - msleep( VOUT_OUTMEM_SLEEP ); - } + vlc_mutex_lock( &p_owner->lock ); + *pb_decode = p_owner->cc.pp_decoder[i_channel] != NULL; + vlc_mutex_unlock( &p_owner->lock ); + return VLC_EGENERIC; } -static void vout_del_buffer( decoder_t *p_dec, picture_t *p_pic ) +void input_DecoderChangePause( decoder_t *p_dec, bool b_paused, mtime_t i_date ) { - vout_ReleasePicture( p_dec->p_owner->p_vout, p_pic ); + decoder_owner_sys_t *p_owner = p_dec->p_owner; + + vlc_mutex_lock( &p_owner->lock ); + /* Normally, p_owner->b_paused != b_paused here. But if a track is added + * while the input is paused (e.g. add sub file), then b_paused is + * (incorrectly) false. */ + if( likely(p_owner->b_paused != b_paused) ) { + p_owner->b_paused = b_paused; + p_owner->pause.i_date = i_date; + p_owner->pause.i_ignore = 0; + vlc_cond_signal( &p_owner->wait_request ); + + /* 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_owner->fmt.i_cat == AUDIO_ES ) + { + if( p_owner->p_aout ) + aout_DecChangePause( p_owner->p_aout, b_paused, i_date ); + } + else if( p_owner->fmt.i_cat == VIDEO_ES ) + { + if( p_owner->p_vout ) + vout_ChangePause( p_owner->p_vout, b_paused, i_date ); + } + } + vlc_mutex_unlock( &p_owner->lock ); } -static void vout_link_picture( decoder_t *p_dec, picture_t *p_pic ) +void input_DecoderChangeDelay( decoder_t *p_dec, mtime_t i_delay ) { - vout_HoldPicture( p_dec->p_owner->p_vout, p_pic ); + decoder_owner_sys_t *p_owner = p_dec->p_owner; + + vlc_mutex_lock( &p_owner->lock ); + p_owner->i_ts_delay = i_delay; + vlc_mutex_unlock( &p_owner->lock ); } -static void vout_unlink_picture( decoder_t *p_dec, picture_t *p_pic ) +void input_DecoderStartWait( decoder_t *p_dec ) { - vout_ReleasePicture( p_dec->p_owner->p_vout, p_pic ); + decoder_owner_sys_t *p_owner = p_dec->p_owner; + + assert( !p_owner->b_waiting ); + + vlc_mutex_lock( &p_owner->lock ); + p_owner->b_first = true; + p_owner->b_has_data = false; + p_owner->b_waiting = true; + vlc_cond_signal( &p_owner->wait_request ); + vlc_mutex_unlock( &p_owner->lock ); } -static subpicture_t *spu_new_buffer( decoder_t *p_dec, - const subpicture_updater_t *p_updater ) +void input_DecoderStopWait( decoder_t *p_dec ) { 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( DecoderIsExitRequested( p_dec ) || p_dec->b_error ) - break; + assert( p_owner->b_waiting ); - p_vout = input_resource_HoldVout( p_owner->p_resource ); - if( p_vout ) - break; + vlc_mutex_lock( &p_owner->lock ); + p_owner->b_waiting = false; + vlc_cond_signal( &p_owner->wait_request ); + vlc_mutex_unlock( &p_owner->lock ); +} - msleep( DECODER_SPU_VOUT_WAIT_DURATION ); - } +void input_DecoderWait( decoder_t *p_dec ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; - if( !p_vout ) - { - msg_Warn( p_dec, "no vout found, dropping subpicture" ); - return NULL; - } + assert( p_owner->b_waiting ); - if( p_owner->p_spu_vout != p_vout ) + vlc_mutex_lock( &p_owner->lock ); + while( !p_owner->b_has_data ) { - vlc_mutex_lock( &p_owner->lock ); + vlc_fifo_Lock( p_owner->p_fifo ); + p_owner->b_woken = true; + vlc_fifo_Signal( p_owner->p_fifo ); + vlc_fifo_Unlock( p_owner->p_fifo ); + vlc_cond_wait( &p_owner->wait_acknowledge, &p_owner->lock ); + } + vlc_mutex_unlock( &p_owner->lock ); +} - DecoderFlushBuffering( p_dec ); +void input_DecoderFrameNext( decoder_t *p_dec, mtime_t *pi_duration ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; - vlc_mutex_unlock( &p_owner->lock ); + *pi_duration = 0; - p_owner->i_spu_channel = vout_RegisterSubpictureChannel( p_vout ); - p_owner->i_spu_order = 0; - p_owner->p_spu_vout = p_vout; + vlc_mutex_lock( &p_owner->lock ); + if( p_owner->fmt.i_cat == VIDEO_ES ) + { + if( p_owner->b_paused && p_owner->p_vout ) + { + vout_NextPicture( p_owner->p_vout, pi_duration ); + p_owner->pause.i_ignore++; + vlc_cond_signal( &p_owner->wait_request ); + } } - - p_subpic = subpicture_New( p_updater ); - if( p_subpic ) + else { - p_subpic->i_channel = p_owner->i_spu_channel; - p_subpic->i_order = p_owner->i_spu_order++; - p_subpic->b_subtitle = true; + /* TODO subtitle should not be flushed */ + p_owner->b_waiting = false; + DecoderFlush( p_dec ); } - - vlc_object_release( p_vout ); - - return p_subpic; + vlc_mutex_unlock( &p_owner->lock ); } -static void spu_del_buffer( decoder_t *p_dec, subpicture_t *p_subpic ) +bool input_DecoderHasFormatChanged( decoder_t *p_dec, es_format_t *p_fmt, vlc_meta_t **pp_meta ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; - vout_thread_t *p_vout = NULL; + bool b_changed; - p_vout = input_resource_HoldVout( p_owner->p_resource ); - if( !p_vout || p_owner->p_spu_vout != p_vout ) + vlc_mutex_lock( &p_owner->lock ); + b_changed = p_owner->b_fmt_description; + if( b_changed ) { - if( p_vout ) - vlc_object_release( p_vout ); - msg_Warn( p_dec, "no vout found, leaking subpicture" ); - return; + if( p_fmt != NULL ) + es_format_Copy( p_fmt, &p_owner->fmt ); + + if( pp_meta ) + { + *pp_meta = NULL; + if( p_owner->p_description ) + { + *pp_meta = vlc_meta_New(); + if( *pp_meta ) + vlc_meta_Merge( *pp_meta, p_owner->p_description ); + } + } + p_owner->b_fmt_description = false; } + vlc_mutex_unlock( &p_owner->lock ); + return b_changed; +} - subpicture_Delete( p_subpic ); +size_t input_DecoderGetFifoSize( decoder_t *p_dec ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; - vlc_object_release( p_vout ); + return block_FifoSize( p_owner->p_fifo ); } +void input_DecoderGetObjects( decoder_t *p_dec, + vout_thread_t **pp_vout, audio_output_t **pp_aout ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; + + vlc_mutex_lock( &p_owner->lock ); + if( pp_vout ) + *pp_vout = p_owner->p_vout ? vlc_object_hold( p_owner->p_vout ) : NULL; + if( pp_aout ) + *pp_aout = p_owner->p_aout ? vlc_object_hold( p_owner->p_aout ) : NULL; + vlc_mutex_unlock( &p_owner->lock ); +}