diff --git a/include/vlc_player.h b/include/vlc_player.h index ce533874af..46f5b15906 100644 --- a/include/vlc_player.h +++ b/include/vlc_player.h @@ -477,6 +477,11 @@ vlc_player_Resume(vlc_player_t *player); /** * Pause and display the next video frame * + * @note Works only on streams that can pause.. + * + * @note listen to the vlc_player_cbs.on_next_frame_status to be notified when + * the next frame is displayed. + * * @param player locked player instance */ VLC_API void @@ -3317,6 +3322,19 @@ struct vlc_player_cbs void (*on_stopping_current_media)(vlc_player_t *player, input_item_t *current_media, enum vlc_player_media_stopping_reason stopping_reason, void *data); + + /** + * Called when the next frame, following a call to + * `vlc_player_NextVideoFrame()`, is displayed. + * + * @see vlc_player_NextVideoFrame() + * + * @param player locked player instance + * @param status 0 in case of success, -EAGAIN on first call (paused), + * -EBUSY in case of video error, -ENOTSUP if can't pause, + * -EINVAL in case of invalid state + */ + void (*on_next_frame_status)(vlc_player_t *player, int status, void *data); }; /** diff --git a/po/POTFILES.in b/po/POTFILES.in index cdb7be4f7b..e6bb36cb62 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -115,6 +115,7 @@ src/network/tls.c src/network/udp.c src/player/osd.c src/player/player.c +src/player/input.c src/stream_output/sap.c src/stream_output/stream_output.c src/stream_output/stream_output.h diff --git a/src/input/decoder.c b/src/input/decoder.c index dec2b9b059..54e88f6154 100644 --- a/src/input/decoder.c +++ b/src/input/decoder.c @@ -2732,19 +2732,24 @@ void vlc_input_decoder_Wait( vlc_input_decoder_t *p_owner ) void vlc_input_decoder_FrameNext( vlc_input_decoder_t *p_owner ) { assert( p_owner->paused ); + assert( p_owner->cat == VIDEO_ES ); vlc_fifo_Lock( p_owner->p_fifo ); - p_owner->frames_countdown++; - vlc_fifo_Signal( p_owner->p_fifo ); - vlc_fifo_Unlock( p_owner->p_fifo ); - vlc_fifo_Lock(p_owner->p_fifo); - if( p_owner->cat == VIDEO_ES ) + if( p_owner->video.vout == NULL ) { - if( p_owner->video.vout ) - vout_NextPicture( p_owner->video.vout ); + decoder_Notify( p_owner, frame_next_status, -EBUSY ); + vlc_fifo_Unlock( p_owner->p_fifo ); + return; } - vlc_fifo_Unlock(p_owner->p_fifo); + + p_owner->frames_countdown++; + vlc_fifo_Signal( p_owner->p_fifo ); + + vout_NextPicture( p_owner->video.vout ); + /* TODO: it should be notified from the vout */ + decoder_Notify( p_owner, frame_next_status, 0 ); + vlc_fifo_Unlock( p_owner->p_fifo ); } size_t vlc_input_decoder_GetFifoSize( vlc_input_decoder_t *p_owner ) diff --git a/src/input/decoder.h b/src/input/decoder.h index e2be45c1ec..78484c39e0 100644 --- a/src/input/decoder.h +++ b/src/input/decoder.h @@ -52,7 +52,8 @@ struct vlc_input_decoder_callbacks { void *userdata); void (*on_new_audio_stats)(vlc_input_decoder_t *decoder, unsigned decoded, unsigned lost, unsigned played, void *userdata); - + void (*frame_next_status)(vlc_input_decoder_t *decoder, int status, + void *userdata); /* requests */ int (*get_attachments)(vlc_input_decoder_t *decoder, input_attachment_t ***ppp_attachment, diff --git a/src/input/es_out.c b/src/input/es_out.c index d0f44da813..84c7ee86d1 100644 --- a/src/input/es_out.c +++ b/src/input/es_out.c @@ -502,6 +502,27 @@ decoder_on_new_audio_stats(vlc_input_decoder_t *decoder, unsigned decoded, unsig memory_order_relaxed); } +static void +decoder_frame_next_status(vlc_input_decoder_t *decoder, int status, + 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; + + const struct vlc_input_event event = { + .type = INPUT_EVENT_FRAME_NEXT_STATUS, + .frame_next_status = status, + }; + + input_SendEvent(p_sys->p_input, &event); +} + static int decoder_get_attachments(vlc_input_decoder_t *decoder, input_attachment_t ***ppp_attachment, @@ -526,6 +547,7 @@ static const struct vlc_input_decoder_callbacks decoder_cbs = { .on_thumbnail_ready = decoder_on_thumbnail_ready, .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, .get_attachments = decoder_get_attachments, }; diff --git a/src/input/input.c b/src/input/input.c index e1a6f107e8..fe75f9fb37 100644 --- a/src/input/input.c +++ b/src/input/input.c @@ -2316,6 +2316,14 @@ static bool Control( input_thread_t *p_input, } case INPUT_CONTROL_SET_FRAME_NEXT: + if (!priv->master->b_can_pause) + { + input_SendEvent(p_input, &(struct vlc_input_event) { + .type = INPUT_EVENT_FRAME_NEXT_STATUS, + .frame_next_status = -ENOTSUP, + }); + break; + } if( priv->i_state == PAUSE_S ) { es_out_SetFrameNext( priv->p_es_out ); @@ -2323,10 +2331,17 @@ static bool Control( input_thread_t *p_input, else if( priv->i_state == PLAYING_S ) { ControlPause( p_input, i_control_date ); + input_SendEvent(p_input, &(struct vlc_input_event) { + .type = INPUT_EVENT_FRAME_NEXT_STATUS, + .frame_next_status = -EAGAIN, + }); } else { - msg_Err( p_input, "invalid state for frame next" ); + input_SendEvent(p_input, &(struct vlc_input_event) { + .type = INPUT_EVENT_FRAME_NEXT_STATUS, + .frame_next_status = -EINVAL, + }); } b_force_update = true; break; diff --git a/src/input/input_internal.h b/src/input/input_internal.h index 62505c17c3..3505876cc5 100644 --- a/src/input/input_internal.h +++ b/src/input/input_internal.h @@ -159,6 +159,9 @@ typedef enum input_event_type_e /* Mouse event */ INPUT_EVENT_MOUSE_LEFT, + + /* frame-next status */ + INPUT_EVENT_FRAME_NEXT_STATUS, } input_event_type_e; #define VLC_INPUT_CAPABILITIES_SEEKABLE (1<<0) @@ -347,6 +350,8 @@ struct vlc_input_event struct vlc_input_event_attachments attachments; /* INPUT_EVENT_NAV_FAILED */ int nav_type; + /* INPUT_EVENT_FRAME_NEXT_STATUS */ + int frame_next_status; }; }; diff --git a/src/player/input.c b/src/player/input.c index c946f33e48..6f0b6d8060 100644 --- a/src/player/input.c +++ b/src/player/input.c @@ -24,6 +24,7 @@ #include #include +#include #include "player.h" struct vlc_player_track_priv * @@ -878,6 +879,51 @@ vlc_player_input_MouseFallback(struct vlc_player_input *input) vlc_player_TogglePause(player); } +static void +vlc_player_DisplayFrameError(vlc_player_t *player, + const char *title, const char *title_error, + int status) +{ + static const char ebusy[] = {N_("no video found")}; + static const char enotusp[] = {N_("can't pause/seek/pace")}; + static const char einval[] = {N_("invalid state")}; + + switch (status) + { + case 0: + vlc_player_osd_Message(player, title); + break; + case -EAGAIN: + break; + case -EBUSY: + vlc_dialog_display_error(player, title_error, ebusy); + break; + case -ENOTSUP: + vlc_dialog_display_error(player, title_error, enotusp); + break; + default: + case -EINVAL: + vlc_dialog_display_error(player, title_error, einval); + break; + } +} + +static void +vlc_player_input_FrameNextStatus(struct vlc_player_input *input, int status) +{ + vlc_player_t *player = input->player; + + unsigned count; + vlc_player_SendEventCount(player, on_next_frame_status, count, status); + + /* Don't display errors if status is handled by the player user */ + if (count != 0) + return; + + vlc_player_DisplayFrameError(player, _("Next frame"), + _("Next frame error"), status); +} + static bool input_thread_Events(input_thread_t *input_thread, const struct vlc_input_event *event, void *user_data) @@ -1100,6 +1146,10 @@ input_thread_Events(input_thread_t *input_thread, case INPUT_EVENT_MOUSE_LEFT: vlc_player_input_MouseFallback(input); break; + case INPUT_EVENT_FRAME_NEXT_STATUS: + vlc_player_input_FrameNextStatus(input, + event->frame_next_status); + break; default: handled = false; break; diff --git a/src/player/player.c b/src/player/player.c index 9cfef641a7..3aca063495 100644 --- a/src/player/player.c +++ b/src/player/player.c @@ -1334,10 +1334,8 @@ vlc_player_NextVideoFrame(vlc_player_t *player) struct vlc_player_input *input = vlc_player_get_input_locked(player); if (!input) return; - int ret = input_ControlPushHelper(input->thread, - INPUT_CONTROL_SET_FRAME_NEXT, NULL); - if (ret == VLC_SUCCESS) - vlc_player_osd_Message(player, _("Next frame")); + input_ControlPushHelper(input->thread, + INPUT_CONTROL_SET_FRAME_NEXT, NULL); } enum vlc_player_state