diff --git a/DOCS/man/en/lua.rst b/DOCS/man/en/lua.rst index b84984e6ce..338ffec6af 100644 --- a/DOCS/man/en/lua.rst +++ b/DOCS/man/en/lua.rst @@ -365,6 +365,25 @@ List of events ``pause`` Playback was paused. + Has the following event fields: + + ``real_paused`` + Current playback pause state as boolean. + + ``user_paused`` + User requested pause state. + + ``by_command`` + If the action was triggered by an input command (or via an user key + binding). It's false if it was an automatic action. + + ``by_cache`` + If the action was triggered by a low (or recovering) cache state. + + ``by_keep_open`` + If the pausing was triggered because the end of playback was reached, + and the "keep-open" option is enabled, 0 otherwise. + ``unpause`` Playback was unpaused. diff --git a/libmpv/client.h b/libmpv/client.h index a37afdbcb5..04fc94663d 100644 --- a/libmpv/client.h +++ b/libmpv/client.h @@ -889,6 +889,32 @@ typedef struct mpv_event_log_message { const char *text; } mpv_event_log_message; +typedef struct mpv_event_pause_reason { + /** + * Actual pause state (0 or 1) + */ + int real_paused; + /** + * User requested pause state (0 or 1) + */ + int user_paused; + /** + * 1 if the action was triggered by an input command (or via an user key + * binding), 0 otherwise. + */ + int by_command; + /** + * 1 if the action was triggered by a low (or recovering) cache state, + * 0 otherwise. + */ + int by_cache; + /** + * 1 if the pausing was triggered because the end of playback was reached, + * and the "keep-open" option is enabled, 0 otherwise. + */ + int by_keep_open; +} mpv_event_pause_reason; + typedef struct mpv_event_script_input_dispatch { /** * Arbitrary integer value that was provided as argument to the @@ -938,6 +964,8 @@ typedef struct mpv_event { * The meaning and contents of data member depend on the event_id: * MPV_EVENT_GET_PROPERTY_REPLY: mpv_event_property* * MPV_EVENT_LOG_MESSAGE: mpv_event_log_message* + * MPV_EVENT_PAUSE: mpv_event_pause_reason* + * MPV_EVENT_UNPAUSE: mpv_event_pause_reason* * MPV_EVENT_SCRIPT_INPUT_DISPATCH: mpv_event_script_input_dispatch* * MPV_EVENT_CLIENT_MESSAGE: mpv_event_client_message* * other: NULL diff --git a/player/client.c b/player/client.c index eaf91c45a8..886ae5b0c8 100644 --- a/player/client.c +++ b/player/client.c @@ -324,23 +324,37 @@ static void status_reply(struct mpv_handle *ctx, int event, send_reply(ctx, userdata, &reply); } +// set ev->data to a new copy of the original data +static void dup_event_data(struct mpv_event *ev) +{ + switch (ev->event_id) { + case MPV_EVENT_PAUSE: + case MPV_EVENT_UNPAUSE: + ev->data = talloc_memdup(NULL, ev->data, sizeof(mpv_event_pause_reason)); + break; + default: + // Doesn't use events with memory allocation. + if (ev->data) + abort(); + } +} + void mp_client_broadcast_event(struct MPContext *mpctx, int event, void *data) { struct mp_client_api *clients = mpctx->clients; - struct mpv_event event_data = { - .event_id = event, - .data = data, - }; - pthread_mutex_lock(&clients->lock); - for (int n = 0; n < clients->num_clients; n++) + for (int n = 0; n < clients->num_clients; n++) { + struct mpv_event event_data = { + .event_id = event, + .data = data, + }; + dup_event_data(&event_data); send_event(clients->clients[n], &event_data); + } pthread_mutex_unlock(&clients->lock); - - talloc_free(data); } int mp_client_send_event(struct MPContext *mpctx, const char *client_name, diff --git a/player/command.c b/player/command.c index bb63b8863a..11a5422add 100644 --- a/player/command.c +++ b/player/command.c @@ -877,9 +877,9 @@ static int mp_property_pause(m_option_t *prop, int action, void *arg, if (action == M_PROPERTY_SET) { if (*(int *)arg) { - pause_player(mpctx); + pause_player(mpctx, PAUSE_BY_COMMAND); } else { - unpause_player(mpctx); + unpause_player(mpctx, PAUSE_BY_COMMAND); } return M_PROPERTY_OK; } @@ -3369,12 +3369,12 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) } if (cmd->flags & MP_PAUSING) - pause_player(mpctx); + pause_player(mpctx, PAUSE_BY_COMMAND); if (cmd->flags & MP_PAUSING_TOGGLE) { if (opts->pause) - unpause_player(mpctx); + unpause_player(mpctx, PAUSE_BY_COMMAND); else - pause_player(mpctx); + pause_player(mpctx, PAUSE_BY_COMMAND); } } diff --git a/player/core.h b/player/core.h index 9048685827..58c1e3793c 100644 --- a/player/core.h +++ b/player/core.h @@ -97,6 +97,11 @@ enum seek_type { MPSEEK_FACTOR, }; +// A bit-mask would be best, but I preferred not to use that with the client API. +#define PAUSE_BY_COMMAND ((mpv_event_pause_reason){.by_command=1}) +#define PAUSE_BY_CACHE ((mpv_event_pause_reason){.by_cache=1}) +#define PAUSE_BY_KEEP_OPEN ((mpv_event_pause_reason){.by_keep_open=1}) + struct track { enum stream_type type; @@ -420,8 +425,8 @@ void set_osd_function(struct MPContext *mpctx, int osd_function); void set_osd_subtitle(struct MPContext *mpctx, const char *text); // playloop.c -void pause_player(struct MPContext *mpctx); -void unpause_player(struct MPContext *mpctx); +void pause_player(struct MPContext *mpctx, mpv_event_pause_reason reason); +void unpause_player(struct MPContext *mpctx, mpv_event_pause_reason reason); void add_step_frame(struct MPContext *mpctx, int dir); void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount, int exact, bool immediate); diff --git a/player/loadfile.c b/player/loadfile.c index b5b42fe67c..90b2484ccf 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -1339,7 +1339,7 @@ goto_reopen_demuxer: ; get_relative_time(mpctx); // reset current delta if (mpctx->opts->pause) - pause_player(mpctx); + pause_player(mpctx, PAUSE_BY_COMMAND); mp_notify(mpctx, MPV_EVENT_PLAYBACK_START, NULL); diff --git a/player/lua.c b/player/lua.c index edd01c0c73..826de209ed 100644 --- a/player/lua.c +++ b/player/lua.c @@ -442,6 +442,22 @@ static int script_wait_event(lua_State *L) lua_setfield(L, -2, "args"); // event break; } + case MPV_EVENT_PAUSE: + case MPV_EVENT_UNPAUSE: + { + mpv_event_pause_reason *msg = event->data; + lua_pushboolean(L, msg->real_paused); + lua_setfield(L, -2, "real_paused"); + lua_pushboolean(L, msg->user_paused); + lua_setfield(L, -2, "user_paused"); + lua_pushboolean(L, msg->by_command); + lua_setfield(L, -2, "by_command"); + lua_pushboolean(L, msg->by_cache); + lua_setfield(L, -2, "by_cache"); + lua_pushboolean(L, msg->by_keep_open); + lua_setfield(L, -2, "by_keep_open"); + break; + } default: ; } diff --git a/player/playloop.c b/player/playloop.c index 87236a34f0..6b072b7414 100644 --- a/player/playloop.c +++ b/player/playloop.c @@ -78,17 +78,15 @@ static const char av_desync_help_text[] = "If none of this helps you, file a bug report.\n\n"; -void pause_player(struct MPContext *mpctx) +void pause_player(struct MPContext *mpctx, mpv_event_pause_reason reason) { - mp_notify(mpctx, MPV_EVENT_PAUSE, NULL); - mpctx->opts->pause = 1; if (mpctx->video_out) vo_control(mpctx->video_out, VOCTRL_RESTORE_SCREENSAVER, NULL); if (mpctx->paused) - return; + goto end; mpctx->paused = true; mpctx->step_frames = 0; mpctx->time_frame -= get_relative_time(mpctx); @@ -107,22 +105,25 @@ void pause_player(struct MPContext *mpctx) if (!mpctx->opts->quiet) MP_SMODE(mpctx, "ID_PAUSED\n"); + +end: + reason.user_paused = !!mpctx->opts->pause; + reason.real_paused = !!mpctx->paused; + mp_notify(mpctx, MPV_EVENT_UNPAUSE, &reason); } -void unpause_player(struct MPContext *mpctx) +void unpause_player(struct MPContext *mpctx, mpv_event_pause_reason reason) { - mp_notify(mpctx, MPV_EVENT_UNPAUSE, NULL); - mpctx->opts->pause = 0; if (mpctx->video_out && mpctx->opts->stop_screensaver) vo_control(mpctx->video_out, VOCTRL_KILL_SCREENSAVER, NULL); if (!mpctx->paused) - return; + goto end; // Don't actually unpause while cache is loading. if (mpctx->paused_for_cache) - return; + goto end; mpctx->paused = false; mpctx->osd_function = 0; @@ -131,6 +132,11 @@ void unpause_player(struct MPContext *mpctx) if (mpctx->video_out && mpctx->d_video && mpctx->video_out->config_ok) vo_control(mpctx->video_out, VOCTRL_RESUME, NULL); // resume video (void)get_relative_time(mpctx); // ignore time that passed during pause + +end: + reason.user_paused = !!mpctx->opts->pause; + reason.real_paused = !!mpctx->paused; + mp_notify(mpctx, MPV_EVENT_UNPAUSE, &reason); } static void draw_osd(struct MPContext *mpctx) @@ -159,12 +165,12 @@ void add_step_frame(struct MPContext *mpctx, int dir) return; if (dir > 0) { mpctx->step_frames += 1; - unpause_player(mpctx); + unpause_player(mpctx, PAUSE_BY_COMMAND); } else if (dir < 0) { if (!mpctx->backstep_active && !mpctx->hrseek_active) { mpctx->backstep_active = true; mpctx->backstep_start_seek_ts = mpctx->vo_pts_history_seek_ts; - pause_player(mpctx); + pause_player(mpctx, PAUSE_BY_COMMAND); } } } @@ -684,14 +690,14 @@ static void handle_pause_on_low_cache(struct MPContext *mpctx) if (cache < 0 || cache >= opts->stream_cache_min_percent || idle) { mpctx->paused_for_cache = false; if (!opts->pause) - unpause_player(mpctx); + unpause_player(mpctx, PAUSE_BY_CACHE); } } else { if (cache >= 0 && cache <= opts->stream_cache_pause && !idle && opts->stream_cache_pause < opts->stream_cache_min_percent) { bool prev_paused_user = opts->pause; - pause_player(mpctx); + pause_player(mpctx, PAUSE_BY_CACHE); mpctx->paused_for_cache = true; opts->pause = prev_paused_user; } @@ -857,7 +863,7 @@ static void handle_keep_open(struct MPContext *mpctx) if (opts->keep_open && mpctx->stop_play == AT_END_OF_FILE) { mpctx->stop_play = KEEP_PLAYING; mpctx->playback_pts = mpctx->last_vo_pts; - pause_player(mpctx); + pause_player(mpctx, PAUSE_BY_KEEP_OPEN); } } @@ -1261,7 +1267,7 @@ void run_playloop(struct MPContext *mpctx) if (new_frame_shown) mpctx->step_frames--; if (mpctx->step_frames == 0) - pause_player(mpctx); + pause_player(mpctx, PAUSE_BY_COMMAND); } }