Browse Source

input: rework next-frame buffering workaround

ea93b2c847 is working by luck.

vlc_input_decoder_IsEmpty() should not be used to know if the input need
more buffering, it might return false when the fifo is empty and when
the vout has few pictures. In the unlikely case where the vout has fewer
pictures than the number of frame-next request (only when sending
requests in a burst via API, and not by hotkeys), it will keep returning
false, not triggering the buffering when it needed.

Furthermore, when paused, if ES_OUT_PRIV_GET_BUFFERING return false, the
input_thread_t will wait undefinitely (only wake by a new request).

This commit introduces a workaround, that is not cleaner than the
previous one, reinventing buffering when we decided to postpone it, but
it is only done for the next-frame usage and works reliably (cf.
unit-tests).

Really fixes #28145
Fixes #29487
pull/193/head
Thomas Guillem 8 months ago
committed by Jean-Baptiste Kempf
parent
commit
9dd9d811bf
  1. 10
      src/input/decoder.c
  2. 2
      src/input/decoder.h
  3. 28
      src/input/es_out.c
  4. 19
      src/input/input.c
  5. 3
      src/input/input_internal.h

10
src/input/decoder.c

@ -2036,6 +2036,13 @@ static void *DecoderThread( void *p_data )
{ /* Wait for a block to decode (or a request to drain) */
p_owner->b_idle = true;
vlc_cond_signal( &p_owner->wait_acknowledge );
if (p_owner->frames_countdown > 0)
{
/* next-frames are requested but the FIFO is empty, ask for
* more buffering */
decoder_Notify( p_owner, frame_next_need_data, true );
}
vlc_fifo_Wait( p_owner->p_fifo );
p_owner->b_idle = false;
continue;
@ -2645,6 +2652,9 @@ void vlc_input_decoder_DecodeWithStatus(vlc_input_decoder_t *p_owner, vlc_frame_
vlc_fifo_WaitCond( p_owner->p_fifo, &p_owner->wait_fifo );
}
if (vlc_fifo_IsEmpty(p_owner->p_fifo) && p_owner->frames_countdown > 0)
decoder_Notify(p_owner, frame_next_need_data, false);
vlc_fifo_QueueUnlocked( p_owner->p_fifo, frame );
if (status != NULL)
GetStatusLocked(p_owner, status);

2
src/input/decoder.h

@ -54,6 +54,8 @@ struct vlc_input_decoder_callbacks {
unsigned lost, unsigned played, void *userdata);
void (*frame_next_status)(vlc_input_decoder_t *decoder, int status,
void *userdata);
void (*frame_next_need_data)(vlc_input_decoder_t *decoder, bool need_data,
void *userdata);
void (*frame_previous_status)(vlc_input_decoder_t *decoder, int status,
void *userdata);
void (*frame_previous_seek)(vlc_input_decoder_t *decoder, vlc_tick_t pts,

28
src/input/es_out.c

@ -526,6 +526,24 @@ decoder_frame_next_status(vlc_input_decoder_t *decoder, int status,
input_SendEvent(p_sys->p_input, &event);
}
static void
decoder_frame_next_need_data(vlc_input_decoder_t *decoder, bool need_data,
void *userdata)
{
(void) decoder;
es_out_id_t *id = userdata;
struct vlc_input_es_out *out = id->out;
es_out_sys_t *p_sys = container_of(out, es_out_sys_t, out);
if (!p_sys->p_input)
return;
vlc_value_t val = { .b_bool = need_data };
input_ControlPushHelper(p_sys->p_input, INPUT_CONTROL_NEED_DATA_FRAME_NEXT,
&val);
}
static void
decoder_frame_previous_status(vlc_input_decoder_t *decoder, int status,
void *userdata)
@ -598,6 +616,7 @@ static const struct vlc_input_decoder_callbacks decoder_cbs = {
.on_new_video_stats = decoder_on_new_video_stats,
.on_new_audio_stats = decoder_on_new_audio_stats,
.frame_next_status = decoder_frame_next_status,
.frame_next_need_data = decoder_frame_next_need_data,
.frame_previous_status = decoder_frame_previous_status,
.frame_previous_seek = decoder_frame_previous_seek,
.get_attachments = decoder_get_attachments,
@ -4019,15 +4038,6 @@ static int EsOutVaPrivControlLocked(es_out_sys_t *p_sys, input_source_t *source,
bool *pb = va_arg( args, bool* );
if( p_sys->b_buffering )
*pb = true;
else if( p_sys->p_next_frame_es != NULL )
{
/* The input thread will continue to call demux() if this control
* returns true. In case of next-frame, ask the input thread to
* continue to demux() until the vout has a picture to display. */
assert( p_sys->b_paused );
*pb = p_sys->p_next_frame_es->p_dec != NULL
&& vlc_input_decoder_IsEmpty( p_sys->p_next_frame_es->p_dec );
}
else
*pb = false;
return VLC_SUCCESS;

19
src/input/input.c

@ -283,6 +283,7 @@ input_thread_t * input_Create( vlc_object_t *p_parent, input_item_t *p_item,
vlc_renderer_item_hold( cfg->renderer ) : NULL;
priv->prev_frame.enabled = priv->prev_frame.end = false;
priv->prev_frame.last_pts = VLC_TICK_INVALID;
priv->next_frame_need_data = false;
priv->viewpoint_changed = false;
/* Fetch the viewpoint from the mediaplayer or the playlist if any */
@ -660,8 +661,12 @@ static void MainLoop( input_thread_t *p_input, bool b_interactive )
* is paused -> this may cause problem with some of them
* The same problem can be seen when seeking while paused */
if( b_paused )
{
b_paused = !es_out_GetBuffering( input_priv(p_input)->p_es_out )
|| input_priv(p_input)->master->b_eof;
if( b_paused && input_priv(p_input)->next_frame_need_data )
b_paused = false;
}
if( !b_paused )
{
@ -751,8 +756,12 @@ static void MainLoop( input_thread_t *p_input, bool b_interactive )
}
/* Update the wakeup time */
if( i_wakeup != 0 )
if( input_priv(p_input)->next_frame_need_data )
i_wakeup = 0;
else if( i_wakeup != 0 )
{
i_wakeup = es_out_GetWakeup( input_priv(p_input)->p_es_out );
}
}
}
}
@ -2061,6 +2070,7 @@ static bool Control( input_thread_t *p_input,
/* Reset the decoders states and clock sync (before calling the demuxer */
es_out_Control(&priv->p_es_out->out, ES_OUT_RESET_PCR);
ResetFramePrevious( p_input );
priv->next_frame_need_data = false;
int i_ret = ControlSetPosition( p_input, param.pos.f_val,
param.pos.b_fast_seek );
@ -2084,6 +2094,7 @@ static bool Control( input_thread_t *p_input,
/* Reset the decoders states and clock sync (before calling the demuxer */
es_out_Control(&priv->p_es_out->out, ES_OUT_RESET_PCR);
ResetFramePrevious( p_input );
priv->next_frame_need_data = false;
int i_ret = ControlSetTime( p_input, param.time.i_val,
param.time.b_fast_seek );
@ -2103,6 +2114,7 @@ static bool Control( input_thread_t *p_input,
if( priv->i_state == PAUSE_S )
{
ResetFramePrevious( p_input );
priv->next_frame_need_data = false;
ControlUnpause( p_input, i_control_date );
b_force_update = true;
}
@ -2306,6 +2318,7 @@ static bool Control( input_thread_t *p_input,
es_out_Control(&priv->p_es_out->out, ES_OUT_RESET_PCR);
ResetFramePrevious( p_input );
priv->next_frame_need_data = false;
demux_Control(priv->master->p_demux,
DEMUX_SET_TITLE, i_title);
break;
@ -2349,6 +2362,7 @@ static bool Control( input_thread_t *p_input,
es_out_Control(&priv->p_es_out->out, ES_OUT_RESET_PCR);
ResetFramePrevious( p_input );
priv->next_frame_need_data = false;
demux_Control( priv->master->p_demux,
DEMUX_SET_SEEKPOINT, i_seekpoint );
input_SendEventSeekpoint( p_input, i_title, i_seekpoint );
@ -2484,6 +2498,9 @@ static bool Control( input_thread_t *p_input,
}
b_force_update = true;
break;
case INPUT_CONTROL_NEED_DATA_FRAME_NEXT:
priv->next_frame_need_data = param.val.b_bool;
break;
case INPUT_CONTROL_SEEK_FRAME_PREVIOUS:
SeekFramePrevious(p_input, param.frame_previous_seek.pts,
param.frame_previous_seek.frame_rate,

3
src/input/input_internal.h

@ -565,6 +565,8 @@ typedef struct input_thread_private_t
bool enabled;
bool end;
} prev_frame;
bool next_frame_need_data;
} input_thread_private_t;
static inline input_thread_private_t *input_priv(input_thread_t *input)
@ -627,6 +629,7 @@ enum input_control_e
INPUT_CONTROL_SET_FRAME_NEXT,
INPUT_CONTROL_SET_FRAME_PREVIOUS,
INPUT_CONTROL_NEED_DATA_FRAME_NEXT,
INPUT_CONTROL_SEEK_FRAME_PREVIOUS,
INPUT_CONTROL_SET_RENDERER,

Loading…
Cancel
Save