+ vlc_mutex_unlock( &p_owner->lock );
+}
+
+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;
+ vout_thread_t *p_vout = p_owner->p_vout;
+ bool b_first_buffered;
+
+ if( p_picture->date <= 0 )
+ {
+ msg_Warn( p_vout, "non-dated video buffer received" );
+ *pi_lost_sum += 1;
+ vout_DropPicture( p_vout, p_picture );
+ return;
+ }
+
+ /* */
+ vlc_mutex_lock( &p_owner->lock );
+
+ if( ( p_owner->b_buffering && !p_owner->buffer.b_first ) || p_owner->buffer.p_picture )
+ {
+ 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 );
+ }
+ }
+ b_first_buffered = p_owner->buffer.p_picture != NULL;
+
+ for( ;; b_first_buffered = false )
+ {
+ bool b_has_more = false;
+
+ bool b_reject;
+
+ DecoderWaitUnblock( p_dec, &b_reject );
+
+ 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_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;
+ }
+
+ /* */
+ 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;
+ }
+
+ int i_rate = INPUT_RATE_DEFAULT;
+ mtime_t i_delay;
+ DecoderFixTs( p_dec, &p_picture->date, NULL, NULL,
+ &i_rate, &i_delay, false );
+
+ vlc_mutex_unlock( &p_owner->lock );
+
+ /* */
+ const mtime_t i_max_date = mdate() + i_delay + DECODER_BOGUS_VIDEO_DELAY;
+
+ if( !p_picture->b_force && ( p_picture->date <= 0 || p_picture->date >= i_max_date ) )
+ 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_DisplayPicture( p_vout, p_picture );
+ }
+ else
+ {
+ if( p_picture->date <= 0 )
+ {
+ msg_Warn( p_vout, "non-dated video buffer received" );
+ }
+ else
+ {
+ msg_Warn( p_vout, "early picture skipped (%"PRId64")",
+ p_picture->date - mdate() );
+ }
+ *pi_lost_sum += 1;
+ vout_DropPicture( p_vout, 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;
+
+ 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;
+ }
+ }
+}
+
+static void DecoderDecodeVideo( decoder_t *p_dec, block_t *p_block )
+{
+ decoder_owner_sys_t *p_owner = p_dec->p_owner;
+ input_thread_t *p_input = p_owner->p_input;
+ picture_t *p_pic;
+ int i_lost = 0;
+ int i_decoded = 0;
+ int i_displayed = 0;
+
+ while( (p_pic = p_dec->pf_decode_video( p_dec, &p_block )) )
+ {
+ vout_thread_t *p_vout = p_owner->p_vout;
+ if( p_dec->b_die )
+ {
+ /* It prevent freezing VLC in case of broken decoder */
+ vout_DropPicture( p_vout, p_pic );
+ if( p_block )
+ block_Release( p_block );
+ break;
+ }
+
+ i_decoded++;
+
+ if( p_pic->date < p_owner->i_preroll_end )
+ {
+ vout_DropPicture( p_vout, p_pic );
+ continue;
+ }
+
+ if( p_owner->i_preroll_end > 0 )
+ {
+ msg_Dbg( p_dec, "End of video preroll" );
+ if( p_vout )
+ vout_Flush( p_vout, 1 );
+ /* */
+ p_owner->i_preroll_end = -1;
+ }
+
+ 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 );
+ }
+ if( i_decoded > 0 || i_lost > 0 || i_displayed > 0 )
+ {
+ vlc_mutex_lock( &p_input->p->counters.counters_lock );
+
+ stats_UpdateInteger( p_dec, p_input->p->counters.p_decoded_video,
+ i_decoded, NULL );
+ stats_UpdateInteger( p_dec, p_input->p->counters.p_lost_pictures,
+ i_lost , NULL);
+
+ stats_UpdateInteger( p_dec, p_input->p->counters.p_displayed_pictures,
+ i_displayed, NULL);
+
+ vlc_mutex_unlock( &p_input->p->counters.counters_lock );
+ }
+}
+
+static void DecoderPlaySpu( decoder_t *p_dec, subpicture_t *p_subpic,
+ bool b_telx )
+{
+ decoder_owner_sys_t *p_owner = p_dec->p_owner;
+ vout_thread_t *p_vout = p_owner->p_spu_vout;
+
+ /* */
+ if( p_subpic->i_start <= 0 )
+ {
+ 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 );
+ }
+ }
+
+ 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, NULL, b_telx );
+
+ vlc_mutex_unlock( &p_owner->lock );
+
+ if( !b_reject )
+ spu_DisplaySubpicture( p_vout->p_spu, 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;
+ }
+ }
+}
+
+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 );
+ }
+ }
+
+ 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,
+ &p_sout_block->i_rate, NULL, b_telx );
+
+ vlc_mutex_unlock( &p_owner->lock );
+
+ if( !b_reject )
+ sout_InputSendBuffer( p_owner->p_sout_input, p_sout_block );
+ 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;
+ }
+ }