X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Finput%2Finput_dec.c;h=e4998c9834f2a79391fc924b3d05a54af8885dd9;hb=6f2c07b9f42c241fdc75456095dc29320aba4849;hp=638ba765599dea464964ed0873f9613a292a062d;hpb=dd6bbcec746d5482f61a8fba5e63cf3427d88cc7;p=vlc diff --git a/src/input/input_dec.c b/src/input/input_dec.c index 638ba76559..e4998c9834 100644 --- a/src/input/input_dec.c +++ b/src/input/input_dec.c @@ -1,8 +1,8 @@ /***************************************************************************** * input_dec.c: Functions for the management of decoders ***************************************************************************** - * Copyright (C) 1999-2001 VideoLAN - * $Id: input_dec.c,v 1.68 2003/11/16 22:23:47 gbazin Exp $ + * Copyright (C) 1999-2004 VideoLAN + * $Id$ * * Authors: Christophe Massiot * Gildas Bazin @@ -39,8 +39,11 @@ #include "codecs.h" +static void input_NullPacket( input_thread_t *, es_descriptor_t * ); + static decoder_t * CreateDecoder( input_thread_t *, es_descriptor_t *, int ); static int DecoderThread( decoder_t * ); +static int DecoderDecode( decoder_t * p_dec, block_t *p_block ); static void DeleteDecoder( decoder_t * ); /* Buffers allocation callbacks for the decoders */ @@ -49,47 +52,63 @@ static void aout_del_buffer( decoder_t *, aout_buffer_t * ); 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 es_format_t null_es_format = {0}; -/***************************************************************************** - * input_RunDecoder: spawns a new decoder thread - *****************************************************************************/ -decoder_fifo_t * input_RunDecoder( input_thread_t * p_input, - es_descriptor_t * p_es ) +struct decoder_owner_sys_t { - vlc_value_t val; - decoder_t *p_dec = NULL; - int i_priority; + vlc_bool_t b_own_thread; - /* If we are in sout mode, search for packetizer module */ - var_Get( p_input, "sout", &val ); - if( !p_es->b_force_decoder && val.psz_string && *val.psz_string ) - { - free( val.psz_string ); - val.b_bool = VLC_TRUE; + input_thread_t *p_input; - if( p_es->i_cat == AUDIO_ES ) - { - var_Get( p_input, "sout-audio", &val ); - } - else if( p_es->i_cat == VIDEO_ES ) - { - var_Get( p_input, "sout-video", &val ); - } + aout_instance_t *p_aout; + aout_input_t *p_aout_input; - if( val.b_bool ) - { - /* Create the decoder configuration structure */ - p_dec = CreateDecoder( p_input, p_es, VLC_OBJECT_PACKETIZER ); - if( p_dec == NULL ) - { - msg_Err( p_input, "could not create packetizer" ); - return NULL; - } + vout_thread_t *p_vout; + + sout_instance_t *p_sout; + sout_packetizer_input_t *p_sout_input; - p_dec->p_module = - module_Need( p_dec, "packetizer", "$packetizer" ); + /* Some decoders require already packetized data (ie. not truncated) */ + decoder_t *p_packetizer; + + /* Current format in use by the output */ + video_format_t video; + audio_format_t audio; + es_format_t sout; + + /* fifo */ + block_fifo_t *p_fifo; + + /* */ + input_buffers_t *p_method_data; + es_descriptor_t *p_es_descriptor; +}; + + +/** + * Spawns a new decoder thread + * + * \param p_input the input thread + * \param p_es the es descriptor + * \return the spawned decoder object + */ +decoder_t * input_RunDecoder( input_thread_t * p_input, es_descriptor_t * p_es ) +{ + decoder_t *p_dec = NULL; + vlc_value_t val; + + /* If we are in sout mode, search for packetizer module */ + if( !p_es->b_force_decoder && p_input->stream.p_sout ) + { + /* Create the decoder configuration structure */ + p_dec = CreateDecoder( p_input, p_es, VLC_OBJECT_PACKETIZER ); + if( p_dec == NULL ) + { + msg_Err( p_input, "could not create packetizer" ); + return NULL; } } else @@ -101,82 +120,96 @@ decoder_fifo_t * input_RunDecoder( input_thread_t * p_input, msg_Err( p_input, "could not create decoder" ); return NULL; } - - /* default Get a suitable decoder module */ - p_dec->p_module = module_Need( p_dec, "decoder", "$codec" ); - - if( val.psz_string ) free( val.psz_string ); } - if( !p_dec || !p_dec->p_module ) + if( !p_dec->p_module ) { msg_Err( p_dec, "no suitable decoder module for fourcc `%4.4s'.\n" "VLC probably does not support this sound or video format.", - (char*)&p_dec->p_fifo->i_fourcc ); + (char*)&p_dec->fmt_in.i_codec ); DeleteDecoder( p_dec ); vlc_object_destroy( p_dec ); return NULL; } - if ( p_es->i_cat == AUDIO_ES ) + if( !p_es->b_force_decoder && p_input->stream.p_sout && p_input->stream.b_pace_control ) { - i_priority = VLC_THREAD_PRIORITY_AUDIO; + msg_Dbg( p_input, "stream out mode -> no decoder thread" ); + p_dec->p_owner->b_own_thread = VLC_FALSE; } else { - i_priority = VLC_THREAD_PRIORITY_VIDEO; + var_Get( p_input, "minimize-threads", &val ); + p_dec->p_owner->b_own_thread = !val.b_bool; } - /* Spawn the decoder thread */ - if( vlc_thread_create( p_dec, "decoder", DecoderThread, - i_priority, VLC_FALSE ) ) + if( p_dec->p_owner->b_own_thread ) { - msg_Err( p_dec, "cannot spawn decoder thread \"%s\"", - p_dec->p_module->psz_object_name ); - module_Unneed( p_dec, p_dec->p_module ); - DeleteDecoder( p_dec ); - vlc_object_destroy( p_dec ); - return NULL; + int i_priority; + if ( p_es->i_cat == AUDIO_ES ) + { + i_priority = VLC_THREAD_PRIORITY_AUDIO; + } + else + { + i_priority = VLC_THREAD_PRIORITY_VIDEO; + } + + /* Spawn the decoder thread */ + if( vlc_thread_create( p_dec, "decoder", DecoderThread, + i_priority, VLC_FALSE ) ) + { + msg_Err( p_dec, "cannot spawn decoder thread \"%s\"", + p_dec->p_module->psz_object_name ); + module_Unneed( p_dec, p_dec->p_module ); + DeleteDecoder( p_dec ); + vlc_object_destroy( p_dec ); + return NULL; + } } + /* Select a new ES */ + INSERT_ELEM( p_input->stream.pp_selected_es, + p_input->stream.i_selected_es_number, + p_input->stream.i_selected_es_number, + p_es ); + p_input->stream.b_changed = 1; - return p_dec->p_fifo; + return p_dec; } -/***************************************************************************** - * input_EndDecoder: kills a decoder thread and waits until it's finished - *****************************************************************************/ +/** + * 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_EndDecoder( input_thread_t * p_input, es_descriptor_t * p_es ) { - int i_dummy; - decoder_t *p_dec = p_es->p_decoder_fifo->p_dec; + decoder_t *p_dec = p_es->p_dec; - p_es->p_decoder_fifo->b_die = 1; + p_dec->b_die = VLC_TRUE; - /* Make sure the thread leaves the NextDataPacket() function by - * sending it a few null packets. */ - for( i_dummy = 0; i_dummy < PADDING_PACKET_NUMBER; i_dummy++ ) + if( p_dec->p_owner->b_own_thread ) { - input_NullPacket( p_input, p_es ); - } + /* Make sure the thread leaves the function by + * sending it an empty block. */ + block_t *p_block = block_New( p_dec, 0 ); + input_DecodeBlock( p_dec, p_block ); + + vlc_thread_join( p_dec ); - if( p_es->p_pes != NULL ) + /* Don't module_Unneed() here because of the dll loader that wants + * close() in the same thread than open()/decode() */ + } + else { - input_DecodePES( p_es->p_decoder_fifo, p_es->p_pes ); + module_Unneed( p_dec, p_dec->p_module ); } - /* Waiting for the thread to exit */ - /* I thought that unlocking was better since thread join can be long - * but it actually creates late pictures and freezes --stef */ - /* vlc_mutex_unlock( &p_input->stream.stream_lock ); */ - vlc_thread_join( p_dec ); - /* vlc_mutex_lock( &p_input->stream.stream_lock ); */ - - /* Unneed module */ - module_Unneed( p_dec, p_dec->p_module ); - /* Delete decoder configuration */ DeleteDecoder( p_dec ); @@ -184,78 +217,116 @@ void input_EndDecoder( input_thread_t * p_input, es_descriptor_t * p_es ) vlc_object_destroy( p_dec ); /* Tell the input there is no more decoder */ - p_es->p_decoder_fifo = NULL; + p_es->p_dec = NULL; p_input->stream.b_changed = 1; } -/***************************************************************************** - * input_DecodePES - ***************************************************************************** +/** * Put a PES in the decoder's fifo. - *****************************************************************************/ -void input_DecodePES( decoder_fifo_t * p_decoder_fifo, pes_packet_t * p_pes ) + * + * \param p_dec the decoder object + * \param p_pes the pes packet + * \return nothing + */ +void input_DecodePES( decoder_t * p_dec, pes_packet_t * p_pes ) { - vlc_mutex_lock( &p_decoder_fifo->data_lock ); + data_packet_t *p_data; + int i_size = 0; - p_pes->p_next = NULL; - *p_decoder_fifo->pp_last = p_pes; - p_decoder_fifo->pp_last = &p_pes->p_next; - p_decoder_fifo->i_depth++; + for( p_data = p_pes->p_first; p_data != NULL; p_data = p_data->p_next ) + { + i_size += p_data->p_payload_end - p_data->p_payload_start; + } + if( i_size > 0 ) + { + block_t *p_block = block_New( p_dec, i_size ); + if( p_block ) + { + uint8_t *p_buffer = p_block->p_buffer; - /* Warn the decoder that it's got work to do. */ - vlc_cond_signal( &p_decoder_fifo->data_wait ); - vlc_mutex_unlock( &p_decoder_fifo->data_lock ); -} + for( p_data = p_pes->p_first; p_data; p_data = p_data->p_next ) + { + int i_copy = p_data->p_payload_end - p_data->p_payload_start; -/***************************************************************************** - * Create a NULL packet for padding in case of a data loss - *****************************************************************************/ -void input_NullPacket( input_thread_t * p_input, - es_descriptor_t * p_es ) -{ - data_packet_t * p_pad_data; - pes_packet_t * p_pes; + memcpy( p_buffer, p_data->p_payload_start, i_copy ); - if( (p_pad_data = input_NewPacketForce( p_input->p_method_data, - PADDING_PACKET_SIZE)) == NULL ) - { - msg_Err( p_input, "no new packet" ); - p_input->b_error = 1; - return; + p_buffer += i_copy; + } + p_block->i_pts = p_pes->i_pts; + p_block->i_dts = p_pes->i_dts; + if( p_pes->b_discontinuity ) + p_block->i_flags |= BLOCK_FLAG_DISCONTINUITY; + p_block->i_rate = p_pes->i_rate; + + input_DecodeBlock( p_dec, p_block ); + } } - memset( p_pad_data->p_payload_start, 0, PADDING_PACKET_SIZE ); - p_pad_data->b_discard_payload = 1; - p_pes = p_es->p_pes; + input_DeletePES( p_dec->p_owner->p_method_data, p_pes ); +} - if( p_pes != NULL ) +/** + * Put a block_t in the decoder's fifo. + * + * \param p_dec the decoder object + * \param p_block the data block + */ +void input_DecodeBlock( decoder_t * p_dec, block_t *p_block ) +{ + if( p_dec->p_owner->b_own_thread ) { - p_pes->b_discontinuity = 1; - p_pes->p_last->p_next = p_pad_data; - p_pes->p_last = p_pad_data; - p_pes->i_nb_data++; + block_FifoPut( p_dec->p_owner->p_fifo, p_block ); + + if( p_dec->p_owner->p_input->b_out_pace_control ) + { + /* FIXME !!!!! */ + while( !p_dec->b_die && !p_dec->b_error && + p_dec->p_owner->p_fifo->i_depth > 10 ) + { + msleep( 1000 ); + } + } } else { - if( (p_pes = input_NewPES( p_input->p_method_data )) == NULL ) + if( p_dec->b_error || p_block->i_buffer <= 0 ) { - msg_Err( p_input, "no PES packet" ); - p_input->b_error = 1; - return; + block_Release( p_block ); } + else + { + DecoderDecode( p_dec, p_block ); + } + } +} + +/** + * Create a NULL packet for padding in case of a data loss + * + * \param p_input the input thread + * \param p_es es descriptor + * \return nothing + */ +static void input_NullPacket( input_thread_t * p_input, + es_descriptor_t * p_es ) +{ + block_t *p_block = block_New( p_input, PADDING_PACKET_SIZE ); + if( p_block ) + { + memset( p_block->p_buffer, 0, PADDING_PACKET_SIZE ); + p_block->i_flags |= BLOCK_FLAG_DISCONTINUITY; - p_pes->i_rate = p_input->stream.control.i_rate; - p_pes->p_first = p_pes->p_last = p_pad_data; - p_pes->i_nb_data = 1; - p_pes->b_discontinuity = 1; - input_DecodePES( p_es->p_decoder_fifo, p_pes ); + block_FifoPut( p_es->p_dec->p_owner->p_fifo, p_block ); } } -/***************************************************************************** - * input_EscapeDiscontinuity: send a NULL packet to the decoders - *****************************************************************************/ +/** + * Send a NULL packet to the decoders + * + * \param p_input the input thread + * \return nothing + */ void input_EscapeDiscontinuity( input_thread_t * p_input ) { unsigned int i_es, i; @@ -264,7 +335,7 @@ void input_EscapeDiscontinuity( input_thread_t * p_input ) { es_descriptor_t * p_es = p_input->stream.pp_selected_es[i_es]; - if( p_es->p_decoder_fifo != NULL ) + if( p_es->p_dec != NULL ) { for( i = 0; i < PADDING_PACKET_NUMBER; i++ ) { @@ -274,9 +345,12 @@ void input_EscapeDiscontinuity( input_thread_t * p_input ) } } -/***************************************************************************** - * input_EscapeAudioDiscontinuity: send a NULL packet to the audio decoders - *****************************************************************************/ +/** + * Send a NULL packet to the audio decoders + * + * \param p_input the input thread + * \return nothing + */ void input_EscapeAudioDiscontinuity( input_thread_t * p_input ) { unsigned int i_es, i; @@ -285,7 +359,7 @@ void input_EscapeAudioDiscontinuity( input_thread_t * p_input ) { es_descriptor_t * p_es = p_input->stream.pp_selected_es[i_es]; - if( p_es->p_decoder_fifo != NULL && p_es->i_cat == AUDIO_ES ) + if( p_es->p_dec != NULL && p_es->i_cat == AUDIO_ES ) { for( i = 0; i < PADDING_PACKET_NUMBER; i++ ) { @@ -295,23 +369,14 @@ void input_EscapeAudioDiscontinuity( input_thread_t * p_input ) } } -struct decoder_owner_sys_t -{ - aout_instance_t *p_aout; - aout_input_t *p_aout_input; - - vout_thread_t *p_vout; - - sout_packetizer_input_t *p_sout; - - /* Current format in use by the output */ - video_format_t video; - audio_format_t audio; -}; - -/***************************************************************************** - * CreateDecoder: create a decoder object - *****************************************************************************/ +/** + * Create a decoder object + * + * \param p_input the input thread + * \param p_es the es descriptor + * \param i_object_type Object type as define in include/vlc_objects.h + * \return the decoder object + */ static decoder_t * CreateDecoder( input_thread_t * p_input, es_descriptor_t * p_es, int i_object_type ) { @@ -324,31 +389,15 @@ static decoder_t * CreateDecoder( input_thread_t * p_input, return NULL; } - p_dec->pf_decode = 0; p_dec->pf_decode_audio = 0; p_dec->pf_decode_video = 0; p_dec->pf_decode_sub = 0; p_dec->pf_packetize = 0; - p_dec->pf_run = 0; - - /* Select a new ES */ - INSERT_ELEM( p_input->stream.pp_selected_es, - p_input->stream.i_selected_es_number, - p_input->stream.i_selected_es_number, - p_es ); - - /* Allocate the memory needed to store the decoder's fifo */ - p_dec->p_fifo = vlc_object_create( p_input, VLC_OBJECT_DECODER_FIFO ); - if( p_dec->p_fifo == NULL ) - { - msg_Err( p_input, "out of memory" ); - return NULL; - } /* Initialize the decoder fifo */ p_dec->p_module = NULL; - p_dec->fmt_in = p_es->fmt; + es_format_Copy( &p_dec->fmt_in, &p_es->fmt ); if( p_es->p_waveformatex ) { @@ -382,6 +431,26 @@ static decoder_t * CreateDecoder( input_thread_t * p_input, p_dec->fmt_in.video.i_height = p_bih->biHeight; } + /* FIXME + * - 1: beurk + * - 2: I'm not sure there isn't any endian problem here (spu)... */ + if( p_es->i_cat == SPU_ES && p_es->p_demux_data ) + { + if( ( p_es->i_fourcc == VLC_FOURCC( 's', 'p', 'u', ' ' ) || + p_es->i_fourcc == VLC_FOURCC( 's', 'p', 'u', 'b' ) ) && + *((uint32_t*)p_es->p_demux_data) == 0xBeef ) + { + memcpy( p_dec->fmt_in.subs.spu.palette, + p_es->p_demux_data, 17 * 4 ); + } + else if( p_es->i_fourcc == VLC_FOURCC( 'd', 'v', 'b', 's' ) ) + { + dvb_spuinfo_t *p_dvbs = (dvb_spuinfo_t*)p_es->p_demux_data; + + p_dec->fmt_in.subs.dvb.i_id = p_dvbs->i_id; + } + } + p_dec->fmt_in.i_cat = p_es->i_cat; p_dec->fmt_in.i_codec = p_es->i_fourcc; @@ -394,200 +463,278 @@ static decoder_t * CreateDecoder( input_thread_t * p_input, msg_Err( p_dec, "out of memory" ); return NULL; } + p_dec->p_owner->b_own_thread = VLC_TRUE; + p_dec->p_owner->p_input = p_input; p_dec->p_owner->p_aout = NULL; p_dec->p_owner->p_aout_input = NULL; p_dec->p_owner->p_vout = NULL; - p_dec->p_owner->p_sout = NULL; + p_dec->p_owner->p_sout = p_input->stream.p_sout; + p_dec->p_owner->p_sout_input = NULL; + p_dec->p_owner->p_packetizer = NULL; + p_dec->p_owner->p_es_descriptor = p_es; + + + /* decoder fifo */ + if( ( p_dec->p_owner->p_fifo = block_FifoNew( p_dec ) ) == NULL ) + { + msg_Err( p_dec, "out of memory" ); + return NULL; + } + p_dec->p_owner->p_method_data = p_input->p_method_data; /* Set buffers allocation callbacks for the decoders */ p_dec->pf_aout_buffer_new = aout_new_buffer; p_dec->pf_aout_buffer_del = aout_del_buffer; p_dec->pf_vout_buffer_new = vout_new_buffer; p_dec->pf_vout_buffer_del = vout_del_buffer; - - /* For old decoders only */ - vlc_mutex_init( p_input, &p_dec->p_fifo->data_lock ); - vlc_cond_init( p_input, &p_dec->p_fifo->data_wait ); - p_es->p_decoder_fifo = p_dec->p_fifo; - p_dec->p_fifo->i_id = p_es->i_id; - p_dec->p_fifo->i_fourcc = p_es->i_fourcc; - p_dec->p_fifo->p_demux_data = p_es->p_demux_data; - p_dec->p_fifo->p_waveformatex = p_es->p_waveformatex; - p_dec->p_fifo->p_bitmapinfoheader = p_es->p_bitmapinfoheader; - p_dec->p_fifo->p_spuinfo = p_es->p_spuinfo; - p_dec->p_fifo->p_stream_ctrl = &p_input->stream.control; - p_dec->p_fifo->p_sout = p_input->stream.p_sout; - p_dec->p_fifo->p_first = NULL; - p_dec->p_fifo->pp_last = &p_dec->p_fifo->p_first; - p_dec->p_fifo->i_depth = 0; - p_dec->p_fifo->b_die = p_dec->p_fifo->b_error = 0; - p_dec->p_fifo->p_packets_mgt = p_input->p_method_data; - p_dec->p_fifo->p_dec = p_dec; - vlc_object_attach( p_dec->p_fifo, p_input ); + p_dec->pf_picture_link = vout_link_picture; + p_dec->pf_picture_unlink = vout_unlink_picture; vlc_object_attach( p_dec, p_input ); + /* Find a suitable decoder/packetizer module */ + if( i_object_type == VLC_OBJECT_DECODER ) + p_dec->p_module = module_Need( p_dec, "decoder", "$codec", 0 ); + else + p_dec->p_module = module_Need( p_dec, "packetizer", "$packetizer", 0 ); + + /* Check if decoder requires already packetized data */ + if( i_object_type == VLC_OBJECT_DECODER && + p_dec->b_need_packetized && !p_dec->fmt_in.b_packetized ) + { + p_dec->p_owner->p_packetizer = + vlc_object_create( p_input, VLC_OBJECT_PACKETIZER ); + if( p_dec->p_owner->p_packetizer ) + { + p_dec->p_owner->p_packetizer->fmt_in = null_es_format; + p_dec->p_owner->p_packetizer->fmt_out = null_es_format; + es_format_Copy( &p_dec->p_owner->p_packetizer->fmt_in, + &p_dec->fmt_in ); + + vlc_object_attach( p_dec->p_owner->p_packetizer, p_input ); + + p_dec->p_owner->p_packetizer->p_module = + module_Need( p_dec->p_owner->p_packetizer, + "packetizer", "$packetizer", 0 ); + + if( !p_dec->p_owner->p_packetizer->p_module ) + { + es_format_Clean( &p_dec->p_owner->p_packetizer->fmt_in ); + vlc_object_detach( p_dec->p_owner->p_packetizer ); + vlc_object_destroy( p_dec->p_owner->p_packetizer ); + } + } + } + return p_dec; } -/***************************************************************************** - * DecoderThread: the decoding main loop - *****************************************************************************/ +/** + * The decoding main loop + * + * \param p_dec the decoder + * \return 0 + */ static int DecoderThread( decoder_t * p_dec ) { - pes_packet_t *p_pes; - data_packet_t *p_data; - block_t *p_block; - - /* Temporary wrapper to keep old decoder api functional */ - if( p_dec->pf_run ) - { - p_dec->pf_run( p_dec->p_fifo ); - return 0; - } + block_t *p_block; /* The decoder's main loop */ - while( !p_dec->p_fifo->b_die && !p_dec->p_fifo->b_error ) + while( !p_dec->b_die && !p_dec->b_error ) { - int i_size; - - input_ExtractPES( p_dec->p_fifo, &p_pes ); - if( !p_pes ) + if( ( p_block = block_FifoGet( p_dec->p_owner->p_fifo ) ) == NULL ) { - p_dec->p_fifo->b_error = 1; + p_dec->b_error = 1; break; } - - for( i_size = 0, p_data = p_pes->p_first; - p_data != NULL; p_data = p_data->p_next ) - { - i_size += p_data->p_payload_end - p_data->p_payload_start; - } - p_block = block_New( p_dec, i_size ); - for( i_size = 0, p_data = p_pes->p_first; - p_data != NULL; p_data = p_data->p_next ) + if( DecoderDecode( p_dec, p_block ) != VLC_SUCCESS ) { - if( p_data->p_payload_end == p_data->p_payload_start ) - continue; - memcpy( p_block->p_buffer + i_size, p_data->p_payload_start, - p_data->p_payload_end - p_data->p_payload_start ); - i_size += p_data->p_payload_end - p_data->p_payload_start; + break; } + } - p_block->i_pts = p_pes->i_pts; - p_block->i_dts = p_pes->i_dts; - p_block->b_discontinuity = p_pes->b_discontinuity; + while( !p_dec->b_die ) + { + /* Trash all received PES packets */ + p_block = block_FifoGet( p_dec->p_owner->p_fifo ); + if( p_block ) block_Release( p_block ); + } - if( p_dec->i_object_type == VLC_OBJECT_PACKETIZER ) - { - sout_buffer_t *p_sout_buffer; - block_t *p_sout_block; + /* We do it here because of the dll loader that wants close() in the + * same thread than open()/decode() */ + module_Unneed( p_dec, p_dec->p_module ); + + return 0; +} + +/** + * Decode a block + * + * \param p_dec the decoder object + * \param p_block the block to decode + * \return VLC_SUCCESS or an error code + */ +static int DecoderDecode( decoder_t *p_dec, block_t *p_block ) +{ + if( p_block->i_buffer <= 0 ) + { + block_Release( p_block ); + return VLC_SUCCESS; + } + + if( p_dec->i_object_type == VLC_OBJECT_PACKETIZER ) + { + block_t *p_sout_block; - while( (p_sout_block = p_dec->pf_packetize( p_dec, &p_block )) ) + while( (p_sout_block = p_dec->pf_packetize( p_dec, &p_block )) ) + { + if( !p_dec->p_owner->p_sout_input ) { - if( !p_dec->p_owner->p_sout ) + es_format_Copy( &p_dec->p_owner->sout, &p_dec->fmt_out ); + if( p_dec->p_owner->p_es_descriptor->p_pgrm ) { - sout_format_t sout_format; - - sout_format.i_cat = p_dec->fmt_out.i_cat; - sout_format.i_fourcc = p_dec->fmt_out.i_codec; - sout_format.i_sample_rate = - p_dec->fmt_out.audio.i_rate; - sout_format.i_channels = - p_dec->fmt_out.audio.i_channels; - sout_format.i_block_align = - p_dec->fmt_out.audio.i_blockalign; - sout_format.i_width = - p_dec->fmt_out.video.i_width; - sout_format.i_height = - p_dec->fmt_out.video.i_height; - sout_format.i_bitrate = p_dec->fmt_out.i_bitrate; - sout_format.i_extra_data = p_dec->fmt_out.i_extra; - sout_format.p_extra_data = p_dec->fmt_out.p_extra; - - p_dec->p_owner->p_sout = - sout_InputNew( p_dec, &sout_format ); - - if( p_dec->p_owner->p_sout == NULL ) - { - msg_Err( p_dec, "cannot create packetizer output" ); - break; - } + p_dec->p_owner->sout.i_group = + p_dec->p_owner->p_es_descriptor->p_pgrm->i_number; + } + p_dec->p_owner->sout.i_id = + p_dec->p_owner->p_es_descriptor->i_id - 1; + if( p_dec->fmt_in.psz_language ) + { + p_dec->p_owner->sout.psz_language = + strdup( p_dec->fmt_in.psz_language ); } - p_sout_buffer = - sout_BufferNew( p_dec->p_owner->p_sout->p_sout, - p_sout_block->i_buffer ); - if( p_sout_buffer == NULL ) + p_dec->p_owner->p_sout_input = + sout_InputNew( p_dec->p_owner->p_sout, + &p_dec->p_owner->sout ); + + if( p_dec->p_owner->p_sout_input == NULL ) { - msg_Err( p_dec, "cannot get sout buffer" ); + msg_Err( p_dec, "cannot create packetizer output" ); + p_dec->b_error = VLC_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; } + } - memcpy( p_sout_buffer->p_buffer, p_sout_block->p_buffer, - p_sout_block->i_buffer ); + while( p_sout_block ) + { + block_t *p_next = p_sout_block->p_next; + + p_sout_block->p_next = NULL; - p_sout_buffer->i_pts = p_sout_block->i_pts; - p_sout_buffer->i_dts = p_sout_block->i_dts; - p_sout_buffer->i_length = p_sout_block->i_length; + sout_InputSendBuffer( p_dec->p_owner->p_sout_input, + p_sout_block ); - block_Release( p_sout_block ); + p_sout_block = p_next; + } - sout_InputSendBuffer( p_dec->p_owner->p_sout, p_sout_buffer ); + /* For now it's enough, as only sout inpact on this flag */ + if( p_dec->p_owner->p_sout->i_out_pace_nocontrol > 0 && + p_dec->p_owner->p_input->b_out_pace_control ) + { + msg_Dbg( p_dec, "switching to synch mode" ); + p_dec->p_owner->p_input->b_out_pace_control = VLC_FALSE; + } + else if( p_dec->p_owner->p_sout->i_out_pace_nocontrol <= 0 && + !p_dec->p_owner->p_input->b_out_pace_control ) + { + msg_Dbg( p_dec, "switching to asynch mode" ); + p_dec->p_owner->p_input->b_out_pace_control = VLC_TRUE; } } - else if( p_dec->fmt_in.i_cat == AUDIO_ES ) + } + else if( p_dec->fmt_in.i_cat == AUDIO_ES ) + { + aout_buffer_t *p_aout_buf; + + if( p_dec->p_owner->p_packetizer ) { - aout_buffer_t *p_aout_buf; + block_t *p_packetized_block; + decoder_t *p_packetizer = p_dec->p_owner->p_packetizer; - while( (p_aout_buf = p_dec->pf_decode_audio( p_dec, &p_block )) ) + while( (p_packetized_block = + p_packetizer->pf_packetize( p_packetizer, &p_block )) ) { - aout_DecPlay( p_dec->p_owner->p_aout, - p_dec->p_owner->p_aout_input, p_aout_buf ); + while( (p_aout_buf = + p_dec->pf_decode_audio( p_dec, &p_packetized_block )) ) + { + aout_DecPlay( p_dec->p_owner->p_aout, + p_dec->p_owner->p_aout_input, p_aout_buf ); + } } } - else + else while( (p_aout_buf = p_dec->pf_decode_audio( p_dec, &p_block )) ) { - picture_t *p_pic; + aout_DecPlay( p_dec->p_owner->p_aout, + p_dec->p_owner->p_aout_input, p_aout_buf ); + } + } + else if( p_dec->fmt_in.i_cat == VIDEO_ES ) + { + picture_t *p_pic; + + if( p_dec->p_owner->p_packetizer ) + { + block_t *p_packetized_block; + decoder_t *p_packetizer = p_dec->p_owner->p_packetizer; - while( (p_pic = p_dec->pf_decode_video( p_dec, &p_block )) ) + while( (p_packetized_block = + p_packetizer->pf_packetize( p_packetizer, &p_block )) ) { - vout_DatePicture( p_dec->p_owner->p_vout, p_pic, p_pic->date ); - vout_DisplayPicture( p_dec->p_owner->p_vout, p_pic ); + while( (p_pic = + p_dec->pf_decode_video( p_dec, &p_packetized_block )) ) + { + vout_DatePicture( p_dec->p_owner->p_vout, p_pic, + p_pic->date ); + vout_DisplayPicture( p_dec->p_owner->p_vout, p_pic ); + } } } - - input_DeletePES( p_dec->p_fifo->p_packets_mgt, p_pes ); + else while( (p_pic = p_dec->pf_decode_video( p_dec, &p_block )) ) + { + vout_DatePicture( p_dec->p_owner->p_vout, p_pic, p_pic->date ); + vout_DisplayPicture( p_dec->p_owner->p_vout, p_pic ); + } + } + else if( p_dec->fmt_in.i_cat == SPU_ES ) + { + p_dec->pf_decode_sub( p_dec, &p_block ); + } + else + { + msg_Err( p_dec, "unknown ES format" ); + p_dec->b_error = 1; } - return 0; + return p_dec->b_error ? VLC_EGENERIC : VLC_SUCCESS; } -/***************************************************************************** - * DeleteDecoder: destroys a decoder object - *****************************************************************************/ +/** + * Destroys a decoder object + * + * \param p_dec the decoder object + * \return nothing + */ static void DeleteDecoder( decoder_t * p_dec ) { vlc_object_detach( p_dec ); - vlc_object_detach( p_dec->p_fifo ); - msg_Dbg( p_dec, - "killing decoder for 0x%x, fourcc `%4.4s', %d PES in FIFO", - p_dec->p_fifo->i_id, (char*)&p_dec->p_fifo->i_fourcc, - p_dec->p_fifo->i_depth ); + msg_Dbg( p_dec, "killing decoder fourcc `%4.4s', %d PES in FIFO", + (char*)&p_dec->fmt_in.i_codec, + p_dec->p_owner->p_fifo->i_depth ); /* Free all packets still in the decoder fifo. */ - input_DeletePES( p_dec->p_fifo->p_packets_mgt, - p_dec->p_fifo->p_first ); - - /* Destroy the lock and cond */ - vlc_cond_destroy( &p_dec->p_fifo->data_wait ); - vlc_mutex_destroy( &p_dec->p_fifo->data_lock ); + block_FifoEmpty( p_dec->p_owner->p_fifo ); + block_FifoRelease( p_dec->p_owner->p_fifo ); - /* Free fifo */ - vlc_object_destroy( p_dec->p_fifo ); - - /* Cleanup */ + /* Cleanup */ if( p_dec->p_owner->p_aout_input ) aout_DecDelete( p_dec->p_owner->p_aout, p_dec->p_owner->p_aout_input ); @@ -595,29 +742,42 @@ static void DeleteDecoder( decoder_t * p_dec ) { int i_pic; +#define p_pic p_dec->p_owner->p_vout->render.pp_picture[i_pic] /* Hack to make sure all the the pictures are freed by the decoder */ for( i_pic = 0; i_pic < p_dec->p_owner->p_vout->render.i_pictures; i_pic++ ) { - if( p_dec->p_owner->p_vout->render.pp_picture[i_pic]->i_status == - RESERVED_PICTURE ) - vout_DestroyPicture( p_dec->p_owner->p_vout, - p_dec->p_owner->p_vout->render.pp_picture[i_pic] ); - if( p_dec->p_owner->p_vout->render.pp_picture[i_pic]->i_refcount - > 0 ) - vout_UnlinkPicture( p_dec->p_owner->p_vout, - p_dec->p_owner->p_vout->render.pp_picture[i_pic] ); + if( p_pic->i_status == RESERVED_PICTURE ) + vout_DestroyPicture( p_dec->p_owner->p_vout, p_pic ); + if( p_pic->i_refcount > 0 ) + vout_UnlinkPicture( p_dec->p_owner->p_vout, p_pic ); } +#undef p_pic /* We are about to die. Reattach video output to p_vlc. */ vout_Request( p_dec, p_dec->p_owner->p_vout, 0, 0, 0, 0 ); } - if( p_dec->p_owner->p_sout ) - sout_InputDelete( p_dec->p_owner->p_sout ); + if( p_dec->p_owner->p_sout_input ) + { + sout_InputDelete( p_dec->p_owner->p_sout_input ); + es_format_Clean( &p_dec->p_owner->sout ); + } - free( p_dec->p_owner ); + es_format_Clean( &p_dec->fmt_in ); + es_format_Clean( &p_dec->fmt_out ); + if( p_dec->p_owner->p_packetizer ) + { + module_Unneed( p_dec->p_owner->p_packetizer, + p_dec->p_owner->p_packetizer->p_module ); + es_format_Clean( &p_dec->p_owner->p_packetizer->fmt_in ); + es_format_Clean( &p_dec->p_owner->p_packetizer->fmt_out ); + vlc_object_detach( p_dec->p_owner->p_packetizer ); + vlc_object_destroy( p_dec->p_owner->p_packetizer ); + } + + free( p_dec->p_owner ); } /***************************************************************************** @@ -649,6 +809,7 @@ static aout_buffer_t *aout_new_buffer( decoder_t *p_dec, int i_samples ) if( p_sys->p_aout_input == NULL ) { msg_Err( p_dec, "failed to create audio output" ); + p_dec->b_error = VLC_TRUE; return NULL; } p_dec->fmt_out.audio.i_bytes_per_frame = @@ -697,6 +858,7 @@ static picture_t *vout_new_buffer( decoder_t *p_dec ) if( p_sys->p_vout == NULL ) { msg_Err( p_dec, "failed to create video output" ); + p_dec->b_error = VLC_TRUE; return NULL; } } @@ -704,10 +866,42 @@ static picture_t *vout_new_buffer( decoder_t *p_dec ) /* Get a new picture */ while( !(p_pic = vout_CreatePicture( p_sys->p_vout, 0, 0, 0 ) ) ) { + int i_pic, i_ready_pic = 0; + if( p_dec->b_die || p_dec->b_error ) { return NULL; } + +#define p_pic p_dec->p_owner->p_vout->render.pp_picture[i_pic] + /* Check the decoder doesn't leak pictures */ + for( i_pic = 0; i_pic < p_dec->p_owner->p_vout->render.i_pictures; + i_pic++ ) + { + if( p_pic->i_status == READY_PICTURE && i_ready_pic++ > 0 ) break; + + if( p_pic->i_status != DISPLAYED_PICTURE && + p_pic->i_status != RESERVED_PICTURE && + p_pic->i_status != READY_PICTURE ) break; + + if( !p_pic->i_refcount ) break; + } + if( i_pic == p_dec->p_owner->p_vout->render.i_pictures ) + { + msg_Err( p_dec, "decoder is leaking pictures, resetting the heap" ); + + /* Just free all the pictures */ + for( i_pic = 0; i_pic < p_dec->p_owner->p_vout->render.i_pictures; + i_pic++ ) + { + if( p_pic->i_status == RESERVED_PICTURE ) + vout_DestroyPicture( p_dec->p_owner->p_vout, p_pic ); + if( p_pic->i_refcount > 0 ) + vout_UnlinkPicture( p_dec->p_owner->p_vout, p_pic ); + } + } +#undef p_pic + msleep( VOUT_OUTMEM_SLEEP ); } @@ -718,3 +912,13 @@ static void vout_del_buffer( decoder_t *p_dec, picture_t *p_pic ) { vout_DestroyPicture( p_dec->p_owner->p_vout, p_pic ); } + +static void vout_link_picture( decoder_t *p_dec, picture_t *p_pic ) +{ + vout_LinkPicture( p_dec->p_owner->p_vout, p_pic ); +} + +static void vout_unlink_picture( decoder_t *p_dec, picture_t *p_pic ) +{ + vout_UnlinkPicture( p_dec->p_owner->p_vout, p_pic ); +}