diff options
Diffstat (limited to 'player')
-rw-r--r-- | player/client.c | 50 | ||||
-rw-r--r-- | player/loadfile.c | 11 |
2 files changed, 56 insertions, 5 deletions
diff --git a/player/client.c b/player/client.c index 815c64c005..460632805f 100644 --- a/player/client.c +++ b/player/client.c @@ -102,6 +102,7 @@ struct observe_property { union m_option_value value; uint64_t value_ret_ts; // logical timestamp of value returned to user union m_option_value value_ret; + bool waiting_for_hook; // flag for draining old property changes on a hook }; struct mpv_handle { @@ -140,6 +141,7 @@ struct mpv_handle { size_t async_counter; // pending other async events bool choked; // recovering from queue overflow bool destroying; // pending destruction; no API accesses allowed + bool hook_pending; // hook events are returned after draining properties struct observe_property **properties; int num_properties; @@ -847,6 +849,27 @@ int mpv_request_event(mpv_handle *ctx, mpv_event_id event, int enable) return 0; } +// Set waiting_for_hook==true for all possibly pending properties. +static void set_wait_for_hook_flags(mpv_handle *ctx) +{ + for (int n = 0; n < ctx->num_properties; n++) { + struct observe_property *prop = ctx->properties[n]; + + if (prop->value_ret_ts != prop->change_ts) + prop->waiting_for_hook = true; + } +} + +// Return whether any property still has waiting_for_hook set. +static bool check_for_for_hook_flags(mpv_handle *ctx) +{ + for (int n = 0; n < ctx->num_properties; n++) { + if (ctx->properties[n]->waiting_for_hook) + return true; + } + return false; +} + mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout) { mpv_event *event = ctx->cur_event; @@ -874,8 +897,24 @@ mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout) event->event_id = MPV_EVENT_QUEUE_OVERFLOW; break; } - if (ctx->num_events) { - *event = ctx->events[ctx->first_event]; + struct mpv_event *ev = + ctx->num_events ? &ctx->events[ctx->first_event] : NULL; + if (ev && ev->event_id == MPV_EVENT_HOOK) { + // Give old property notifications priority over hooks. This is a + // guarantee given to clients to simplify their logic. New property + // changes after this are treated normally, so + if (!ctx->hook_pending) { + ctx->hook_pending = true; + set_wait_for_hook_flags(ctx); + } + if (check_for_for_hook_flags(ctx)) { + ev = NULL; // delay + } else { + ctx->hook_pending = false; + } + } + if (ev) { + *event = *ev; ctx->first_event = (ctx->first_event + 1) % ctx->max_events; ctx->num_events--; talloc_steal(event, event->data); @@ -1608,7 +1647,7 @@ static void send_client_property_changes(struct mpv_handle *ctx) ctx->async_counter -= 1; prop_unref(prop); - // Set of observed properties was changed or something similar + // Set if observed properties was changed or something similar // => start over, retry next time. if (cur_ts != ctx->properties_change_ts || ctx->destroying) { m_option_free(type, &val); @@ -1638,10 +1677,14 @@ static void send_client_property_changes(struct mpv_handle *ctx) changed = true; } + if (prop->waiting_for_hook) + ctx->new_property_events = true; // make sure to wakeup + // Avoid retriggering the change event if the property didn't change, // and the previous value was actually returned to the client. if (!changed && prop->value_ret_ts == prop->value_ts) { prop->value_ret_ts = prop->change_ts; // no change => no event + prop->waiting_for_hook = false; } else { ctx->new_property_events = true; } @@ -1704,6 +1747,7 @@ static bool gen_property_change_event(struct mpv_handle *ctx) prop->value_ret_ts != prop->value_ts) // other value than last time? { prop->value_ret_ts = prop->value_ts; + prop->waiting_for_hook = false; prop_unref(ctx->cur_property); ctx->cur_property = prop; prop->refcount += 1; diff --git a/player/loadfile.c b/player/loadfile.c index d2bdb47ccd..9eb58c0f4d 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -931,7 +931,8 @@ static void process_hooks(struct MPContext *mpctx, char *name) while (!mp_hook_test_completion(mpctx, name)) { mp_idle(mpctx); - // We have no idea what blocks a hook, so just do a full abort. + // We have no idea what blocks a hook, so just do a full abort. This + // does nothing for hooks that happen outside of playback. if (mpctx->stop_play) mp_abort_playback_async(mpctx); } @@ -1374,13 +1375,17 @@ static void play_current_file(struct MPContext *mpctx) double playback_start = -1e100; assert(mpctx->stop_play); + mpctx->stop_play = 0; + + process_hooks(mpctx, "on_before_start_file"); + if (mpctx->stop_play) + return; mp_notify(mpctx, MPV_EVENT_START_FILE, NULL); mp_cancel_reset(mpctx->playback_abort); mpctx->error_playing = MPV_ERROR_LOADING_FAILED; - mpctx->stop_play = 0; mpctx->filename = NULL; mpctx->shown_aframes = 0; mpctx->shown_vframes = 0; @@ -1701,6 +1706,8 @@ terminate_playback: } assert(mpctx->stop_play); + + process_hooks(mpctx, "on_after_end_file"); } // Determine the next file to play. Note that if this function returns non-NULL, |