X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Finput%2Fdecoder.c;h=0201e4fb316c6710385837c65a6e1b1a08e8c175;hb=d4489a37385c1c7d2e01c7118c81a56b5ad71ac2;hp=972522a41e1c6af718dd68006d8e873fde5dcb1b;hpb=7df464879acb66d7a0a19905e3d48b7e6b5fcf33;p=vlc diff --git a/src/input/decoder.c b/src/input/decoder.c index 972522a41e..0201e4fb31 100644 --- a/src/input/decoder.c +++ b/src/input/decoder.c @@ -54,25 +54,23 @@ #include "../video_output/vout_control.h" static decoder_t *CreateDecoder( vlc_object_t *, input_thread_t *, - es_format_t *, bool, input_resource_t *, + const 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 DecoderFlush( decoder_t * ); -static void DecoderSignalWait( decoder_t *, bool ); +static void DecoderSignalWait( decoder_t * ); +static void DecoderUpdateFormatLocked( decoder_t * ); static void DecoderUnsupportedCodec( decoder_t *, vlc_fourcc_t ); /* Buffers allocation callbacks for the decoders */ +static int vout_update_format( decoder_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 int aout_update_format( decoder_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 { @@ -117,8 +115,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 @@ -128,6 +124,7 @@ struct decoder_owner_sys_t } pause; /* Waiting */ + bool b_woken; bool b_waiting; bool b_first; bool b_has_data; @@ -160,23 +157,14 @@ struct decoder_owner_sys_t *****************************************************************************/ picture_t *decoder_NewPicture( decoder_t *p_decoder ) { + 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; } -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 ) -{ - p_decoder->pf_picture_unlink( p_decoder, p_picture ); -} block_t *decoder_NewAudioBuffer( decoder_t *dec, int samples ) { @@ -203,11 +191,6 @@ subpicture_t *decoder_NewSubpicture( decoder_t *p_decoder, return p_subpicture; } -void decoder_DeleteSubpicture( decoder_t *p_decoder, subpicture_t *p_subpicture ) -{ - p_decoder->pf_spu_buffer_del( p_decoder, p_subpicture ); -} - /* decoder_GetInputAttachments: */ int decoder_GetInputAttachments( decoder_t *p_dec, @@ -240,7 +223,7 @@ int decoder_GetDisplayRate( decoder_t *p_dec ) /* 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, + const es_format_t *fmt, input_clock_t *p_clock, input_resource_t *p_resource, sout_instance_t *p_sout ) { @@ -307,7 +290,7 @@ decoder_t *input_DecoderNew( input_thread_t *p_input, /** * Spawn a decoder thread outside of the input thread. */ -decoder_t *input_DecoderCreate( vlc_object_t *p_parent, es_format_t *fmt, +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 ); @@ -332,7 +315,6 @@ void input_DecoderDelete( decoder_t *p_dec ) p_owner->b_paused = false; p_owner->b_waiting = false; p_owner->b_flushing = true; - p_owner->b_exit = true; vlc_cond_signal( &p_owner->wait_request ); vlc_mutex_unlock( &p_owner->lock ); @@ -363,29 +345,30 @@ 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; - if( b_do_pace ) - { - /* The fifo is not consumed when waiting and so will - * deadlock vlc. - * There is no need to lock as b_waiting is never modified - * inside decoder thread. */ - if( !p_owner->b_waiting ) - 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 + 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. */ - msg_Warn( p_dec, "decoder/packetizer fifo full (data not " - "consumed quickly enough), resetting fifo!" ); - block_FifoEmpty( p_owner->p_fifo ); + /* 400 MiB, i.e. ~ 50mb/s for 60s */ + if( vlc_fifo_GetBytes( p_owner->p_fifo ) > 400*1024*1024 ) + { + msg_Warn( p_dec, "decoder/packetizer fifo full (data not " + "consumed quickly enough), resetting fifo!" ); + block_ChainRelease( vlc_fifo_DequeueAllUnlocked( p_owner->p_fifo ) ); + } + } + 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 ); } - block_FifoPut( p_owner->p_fifo, p_block ); + vlc_fifo_QueueUnlocked( p_owner->p_fifo, p_block ); + vlc_fifo_Unlock( p_owner->p_fifo ); } bool input_DecoderIsEmpty( decoder_t * p_dec ) @@ -399,9 +382,9 @@ bool input_DecoderIsEmpty( decoder_t * p_dec ) { vlc_mutex_lock( &p_owner->lock ); /* TODO subtitles support */ - if( p_dec->fmt_out.i_cat == VIDEO_ES && p_owner->p_vout ) + if( p_owner->fmt.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 ) + 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 ); } @@ -506,12 +489,12 @@ void input_DecoderChangePause( decoder_t *p_dec, bool b_paused, mtime_t i_date ) * - for sout it is useless * - for subs, it is done by the vout */ - if( p_dec->fmt_out.i_cat == AUDIO_ES ) + 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_dec->fmt_out.i_cat == VIDEO_ES ) + else if( p_owner->fmt.i_cat == VIDEO_ES ) { if( p_owner->p_vout ) vout_ChangePause( p_owner->p_vout, b_paused, i_date ); @@ -529,21 +512,30 @@ void input_DecoderChangeDelay( decoder_t *p_dec, mtime_t i_delay ) vlc_mutex_unlock( &p_owner->lock ); } -void input_DecoderStartWait( decoder_t *p_dec ) +/** + * 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; vlc_mutex_lock( &p_owner->lock ); - DecoderFlush( p_dec ); + vlc_mutex_unlock( &p_owner->lock ); +} +void input_DecoderStartWait( decoder_t *p_dec ) +{ + 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 ); } @@ -551,12 +543,11 @@ void input_DecoderStopWait( decoder_t *p_dec ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; - vlc_mutex_lock( &p_owner->lock ); + assert( p_owner->b_waiting ); + vlc_mutex_lock( &p_owner->lock ); p_owner->b_waiting = false; - vlc_cond_signal( &p_owner->wait_request ); - vlc_mutex_unlock( &p_owner->lock ); } @@ -564,14 +555,17 @@ void input_DecoderWait( decoder_t *p_dec ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; - vlc_mutex_lock( &p_owner->lock ); + assert( p_owner->b_waiting ); - while( p_owner->b_waiting && !p_owner->b_has_data ) + vlc_mutex_lock( &p_owner->lock ); + while( !p_owner->b_has_data ) { - block_FifoWake( p_owner->p_fifo ); + 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 ); } @@ -582,7 +576,7 @@ void input_DecoderFrameNext( decoder_t *p_dec, mtime_t *pi_duration ) *pi_duration = 0; vlc_mutex_lock( &p_owner->lock ); - if( p_dec->fmt_out.i_cat == VIDEO_ES ) + if( p_owner->fmt.i_cat == VIDEO_ES ) { if( p_owner->b_paused && p_owner->p_vout ) { @@ -594,6 +588,7 @@ void input_DecoderFrameNext( decoder_t *p_dec, mtime_t *pi_duration ) else { /* TODO subtitle should not be flushed */ + p_owner->b_waiting = false; DecoderFlush( p_dec ); } vlc_mutex_unlock( &p_owner->lock ); @@ -673,7 +668,7 @@ static mtime_t DecoderGetDisplayDate( decoder_t *p_dec, mtime_t i_ts ) 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; } @@ -718,7 +713,7 @@ static void DecoderUnsupportedCodec( decoder_t *p_dec, vlc_fourcc_t codec ) */ static decoder_t * CreateDecoder( vlc_object_t *p_parent, input_thread_t *p_input, - es_format_t *fmt, bool b_packetizer, + const es_format_t *fmt, bool b_packetizer, input_resource_t *p_resource, sout_instance_t *p_sout ) { @@ -768,6 +763,7 @@ static decoder_t * CreateDecoder( vlc_object_t *p_parent, 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) ) { @@ -778,12 +774,9 @@ static decoder_t * CreateDecoder( vlc_object_t *p_parent, /* 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_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; @@ -848,8 +841,6 @@ static decoder_t * CreateDecoder( vlc_object_t *p_parent, p_owner->b_fmt_description = false; p_owner->p_description = NULL; - p_owner->b_exit = false; - p_owner->b_paused = false; p_owner->pause.i_date = VLC_TS_INVALID; p_owner->pause.i_ignore = 0; @@ -892,14 +883,29 @@ static void *DecoderThread( void *p_data ) /* The decoder's main loop */ for( ;; ) { - block_t *p_block = block_FifoGet( p_owner->p_fifo ); + block_t *p_block; - /* Make sure there is no cancellation point other than this one^^. - * If you need one, be sure to push cleanup of p_block. */ - bool end_wait = !p_block || p_block->i_flags & BLOCK_FLAG_CORE_EOS; - DecoderSignalWait( p_dec, end_wait ); - if (end_wait) - input_DecoderStopWait( p_dec ); + 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 ) { @@ -944,7 +950,6 @@ static void DecoderFlush( decoder_t *p_dec ) /* Empty the fifo */ block_FifoEmpty( p_owner->p_fifo ); - p_owner->b_waiting = false; /* Monitor for flush end */ p_owner->b_flushing = true; vlc_cond_signal( &p_owner->wait_request ); @@ -960,7 +965,7 @@ static void DecoderFlush( decoder_t *p_dec ) vlc_cond_wait( &p_owner->wait_acknowledge, &p_owner->lock ); } -static void DecoderSignalWait( decoder_t *p_dec, bool b_has_data ) +static void DecoderSignalWait( decoder_t *p_dec ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; @@ -968,8 +973,7 @@ static void DecoderSignalWait( decoder_t *p_dec, bool b_has_data ) if( p_owner->b_waiting ) { - if( b_has_data ) - p_owner->b_has_data = true; + p_owner->b_has_data = true; vlc_cond_signal( &p_owner->wait_acknowledge ); } @@ -1052,7 +1056,7 @@ static void DecoderFixTs( decoder_t *p_dec, mtime_t *pi_ts0, mtime_t *pi_ts1, *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 ) ) { + 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); @@ -1078,17 +1082,6 @@ static void DecoderFixTs( decoder_t *p_dec, mtime_t *pi_ts0, mtime_t *pi_ts1, *pi_rate = i_rate; } -static bool DecoderIsExitRequested( decoder_t *p_dec ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; - - vlc_mutex_lock( &p_owner->lock ); - bool b_exit = p_owner->b_exit; - vlc_mutex_unlock( &p_owner->lock ); - - return b_exit; -} - /** * 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. @@ -1105,7 +1098,7 @@ static void DecoderWaitDate( decoder_t *p_dec, do { - if( p_owner->b_flushing || p_owner->b_exit ) + if( p_owner->b_flushing ) { *pb_reject = true; break; @@ -1202,7 +1195,7 @@ static void DecoderDecodeAudio( decoder_t *p_dec, block_t *p_block ) } else while( (p_aout_buf = p_dec->pf_decode_audio( p_dec, &p_block )) ) { - if( DecoderIsExitRequested( p_dec ) ) + if( DecoderIsFlushing( p_dec ) ) { /* It prevent freezing VLC in case of broken decoder */ block_Release( p_aout_buf ); @@ -1297,7 +1290,7 @@ static void DecoderPlayVideo( decoder_t *p_dec, picture_t *p_picture, { msg_Warn( p_dec, "non-dated video buffer received" ); *pi_lost_sum += 1; - vout_ReleasePicture( p_vout, p_picture ); + picture_Release( p_picture ); return; } @@ -1350,7 +1343,7 @@ static void DecoderPlayVideo( decoder_t *p_dec, picture_t *p_picture, msg_Warn( p_dec, "non-dated video buffer received" ); *pi_lost_sum += 1; - vout_ReleasePicture( p_vout, p_picture ); + picture_Release( p_picture ); } int i_tmp_display; int i_tmp_lost; @@ -1371,10 +1364,9 @@ static void DecoderDecodeVideo( decoder_t *p_dec, block_t *p_block ) while( (p_pic = p_dec->pf_decode_video( p_dec, &p_block )) ) { vout_thread_t *p_vout = p_owner->p_vout; - if( DecoderIsExitRequested( p_dec ) ) - { - /* It prevent freezing VLC in case of broken decoder */ - vout_ReleasePicture( p_vout, p_pic ); + 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; @@ -1384,7 +1376,7 @@ static void DecoderDecodeVideo( decoder_t *p_dec, block_t *p_block ) if( p_owner->i_preroll_end > VLC_TS_INVALID && p_pic->date < p_owner->i_preroll_end ) { - vout_ReleasePicture( p_vout, p_pic ); + picture_Release( p_pic ); continue; } @@ -1507,9 +1499,11 @@ static void DecoderProcessSout( decoder_t *p_dec, block_t *p_block ) { if( p_owner->p_sout_input == NULL ) { - assert( !p_owner->b_fmt_description ); // no need for owner lock + 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 ); p_owner->fmt.i_group = p_dec->fmt_in.i_group; p_owner->fmt.i_id = p_dec->fmt_in.i_id; @@ -1828,7 +1822,6 @@ 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 ); /* Cleanup */ @@ -2027,7 +2020,7 @@ static int aout_update_format( decoder_t *p_dec ) return 0; } -static picture_t *vout_new_buffer( decoder_t *p_dec ) +static int vout_update_format( decoder_t *p_dec ) { decoder_owner_sys_t *p_owner = p_dec->p_owner; @@ -2049,7 +2042,7 @@ static picture_t *vout_new_buffer( decoder_t *p_dec ) !p_dec->fmt_out.video.i_height ) { /* Can't create a new vout without display size */ - return NULL; + return -1; } video_format_t fmt = p_dec->fmt_out.video; @@ -2153,26 +2146,27 @@ static picture_t *vout_new_buffer( decoder_t *p_dec ) { msg_Err( p_dec, "failed to create video output" ); p_dec->b_error = true; - return NULL; + return -1; } } + return 0; +} + +static picture_t *vout_new_buffer( decoder_t *p_dec ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; - /* Get a new picture - */ for( ;; ) { - if( DecoderIsExitRequested( p_dec ) || p_dec->b_error ) + if( DecoderIsFlushing( p_dec ) || p_dec->b_error ) return NULL; picture_t *p_picture = vout_GetPicture( p_owner->p_vout ); if( p_picture ) return p_picture; - if( DecoderIsFlushing( p_dec ) ) - return NULL; - /* */ - DecoderSignalWait( p_dec, true ); + DecoderSignalWait( p_dec ); /* Check the decoder doesn't leak pictures */ vout_FixLeaks( p_owner->p_vout ); @@ -2182,21 +2176,6 @@ static picture_t *vout_new_buffer( decoder_t *p_dec ) } } -static void vout_del_buffer( decoder_t *p_dec, picture_t *p_pic ) -{ - vout_ReleasePicture( p_dec->p_owner->p_vout, p_pic ); -} - -static void vout_link_picture( decoder_t *p_dec, picture_t *p_pic ) -{ - vout_HoldPicture( p_dec->p_owner->p_vout, p_pic ); -} - -static void vout_unlink_picture( decoder_t *p_dec, picture_t *p_pic ) -{ - vout_ReleasePicture( p_dec->p_owner->p_vout, p_pic ); -} - static subpicture_t *spu_new_buffer( decoder_t *p_dec, const subpicture_updater_t *p_updater ) { @@ -2207,7 +2186,7 @@ static subpicture_t *spu_new_buffer( decoder_t *p_dec, while( i_attempts-- ) { - if( DecoderIsExitRequested( p_dec ) || p_dec->b_error ) + if( DecoderIsFlushing( p_dec ) || p_dec->b_error ) break; p_vout = input_resource_HoldVout( p_owner->p_resource ); @@ -2242,22 +2221,3 @@ static subpicture_t *spu_new_buffer( decoder_t *p_dec, return p_subpic; } - -static void spu_del_buffer( decoder_t *p_dec, subpicture_t *p_subpic ) -{ - decoder_owner_sys_t *p_owner = p_dec->p_owner; - vout_thread_t *p_vout = NULL; - - p_vout = input_resource_HoldVout( p_owner->p_resource ); - if( !p_vout || p_owner->p_spu_vout != p_vout ) - { - if( p_vout ) - vlc_object_release( p_vout ); - msg_Warn( p_dec, "no vout found, leaking subpicture" ); - return; - } - - subpicture_Delete( p_subpic ); - - vlc_object_release( p_vout ); -}