[vlc-devel] [PATCH 08/13] player: add the timer API
Alexandre Janniaux
ajanni at videolabs.io
Wed Aug 21 17:05:37 CEST 2019
Hi,
Some comments inline.
On Wed, Aug 21, 2019 at 04:13:59PM +0200, Thomas Guillem wrote:
> Any interface or control modules could request a timer from the player. This
> player timer is like the player event listener except that:
>
> - It is only used to receive time update
>
> - The timer is not locked by the player lock. Indeed the player lock can be
> too "slow" (it can be recursive, it is used by the playlist, and is it held
> when sending all events). So it's not a good idea to hold this lock for
> every frame/sample updates.
>
> - The minimum delay between each updates can be configured: it avoids to flood
> the UI when playing a media file with very high fps or very low audio sample
> size.
>
> The time updated is the output time, unlike the on_position_changed event that
> use the input time. It can fixes a very big delay between the UI time widgets
> and the outputted content (depending on the audio output module, this delay
> could be close to 2seconds).
>
> The vlc_player_timer_value struct is used by timer update callbacks. This
> public struct hold all the informations to interpolate a time at a given date.
> It could be done with the vlc_player_timer_value_Interpolate() helper. In this
> way, it is now possible to get the last player time without holding any locks.
>
> There are two timer types:
>
> - Source: update are sent only when a frame or a sample is outputted. Users of
> this timer should take into account that the delay between each updates is
> not regular and can be up to 1seconds (depending of the input). In that
> case, they should use their own timer (from their mainloop) and use
> vlc_player_timer_value_Interpolate() to get the last time.
>
> - Regular: The player will spawn a vlc timer (likely an other thread) that
> will send regular time updates. It can be used by control/interfaces that
> don't have any mainloop.
> ---
> include/vlc_player.h | 140 ++++++++++++++++++
> src/Makefile.am | 1 +
> src/libvlccore.sym | 3 +
> src/player/input.c | 55 ++++++-
> src/player/player.c | 12 ++
> src/player/player.h | 43 ++++++
> src/player/timer.c | 341 +++++++++++++++++++++++++++++++++++++++++++
> 7 files changed, 594 insertions(+), 1 deletion(-)
> create mode 100644 src/player/timer.c
>
> diff --git a/include/vlc_player.h b/include/vlc_player.h
> index 401eafd047..8fdf314b17 100644
> --- a/include/vlc_player.h
> +++ b/include/vlc_player.h
> @@ -3055,6 +3055,146 @@ vlc_player_RemoveListener(vlc_player_t *player,
>
> /** @} vlc_player__events */
>
> +/**
> + * @defgroup vlc_player__timer Player timer
> + * @{
> + */
> +
> +/**
> + * Player timer opaque structure.
> + */
> +typedef struct vlc_player_timer_id vlc_player_timer_id;
> +
> +/*
> + * Player timer creation type
> + *
> + * @see vlc_player_AddTimer()
> + */
> +enum vlc_player_timer_type
> +{
> + /**
> + * Get notified only when the time is updated by the input or output
> + * source. The input source is the 'demux' or the 'access_demux'. The
> + * output source are audio and video outputs: an update is received each
> + * time a video frame is displayed or an audio sample is written. The delay
> + * between each updates may depend on the input and source type (it can be
> + * every 5ms, 30ms, 1s or 10s...). The user of this timer may need to
> + * update the position at a higher frequency from its own mainloop via
> + * vlc_player_timer_value_Interpolate().
> + */
> + VLC_PLAYER_TIMER_TYPE_SOURCE,
> + /** Spawn a timer to get regular time updates. */
> + VLC_PLAYER_TIMER_TYPE_REGULAR,
> +};
> +
> +/**
> + * Player timer state
> + *
> + * @see vlc_player_timer_cbs.on_update
> + */
> +enum vlc_player_timer_state
> +{
> + /* Normal state, the player is playing */
> + VLC_PLAYER_TIMER_STATE_PLAYING,
> + /* The player is paused */
> + VLC_PLAYER_TIMER_STATE_PAUSED,
> + /* A discontinuity occurred, likely caused by seek from the user. */
> + VLC_PLAYER_TIMER_STATE_DISCONTINUITY,
> +};
> +
> +/**
> + * Player timer value
> + *
> + * @see vlc_player_timer_cbs.on_update
> + */
> +struct vlc_player_timer_value
> +{
> + /** Position in the range [0.0f;1.0] */
Maybe recall that it can be not available if the media has no duration.
(VLC_TICK_INVALID if following what's next) ?
> + float position;
> + /** Rate of the player */
> + double rate;
> + /** Valid time > 0 or VLC_TICK_INVALID */
> + vlc_tick_t ts;
> + /** Valid length > 0 or VLC_TICK_INVALID */
> + vlc_tick_t length;
> + /** System date of this record (always valid), if the timer type is
> + * VLC_PLAYER_TIMER_TYPE_SOURCE, this date can be in the future. If the
> + * timer type is VLC_PLAYER_TIMER_TYPE_REGULAR, this date is
> + * vlc_tick_now(). */
> + vlc_tick_t system_date;
> +};
> +
> +/**
> + * Player timer callbacks
> + *
> + * @see vlc_player_AddTimer
> + */
> +struct vlc_player_timer_cbs
> +{
> + /**
> + * Called when the state or the time changed.
> + *
> + * @warning The player is not locked from this callback. It is forbidden
> + * to call any player functions from here.
> + *
> + * @param timer the timer created by vlc_player_AddTimer()
> + * @param state PLAYING, PAUSED or DISCONTINUITY
> + * @param value always valid, the time corresponding to the state
> + * @param data opaque pointer set by vlc_player_AddTimer()
> + */
> + void (*on_update)(enum vlc_player_timer_state state,
> + const struct vlc_player_timer_value *value, void *data);
> +};
> +
> +/**
> + * Add a timer in order to get times updates
> + *
> + * @param player player instance (locked or not)
> + * @param type SOURCE or REGULAR, see vlc_player_timer_type
> + * @param delay if the type is SOURCE, it corresponds to the minimum delay
> + * between each updates, use it to avoid flood from too many source updates,
> + * set it to VLC_TICK_INVALID to receive all updates. If type is REGULAR, it
> + * corresponds to the delay between each updates and it must be superior to
> + * 10ms.
> + * @param cbs pointer to a vlc_player_timer_cbs structure, the structure must
> + * be valid during the lifetime of the player
> + * @param cbs_data opaque pointer used by the callbacks
> + * @return a valid vlc_player_timer_id or NULL in case of memory allocation
> + * error
> + */
> +VLC_API vlc_player_timer_id *
> +vlc_player_AddTimer(vlc_player_t *player, enum vlc_player_timer_type type,
> + vlc_tick_t delay,
delay might be renamed into period (as it is relevant to update frequency
for REGULAR update).
> + const struct vlc_player_timer_cbs *cbs, void *data);
> +
> +/**
> + * Remove a player timer
> + *
> + * @param player player instance (locked or not)
> + * @param timer timer created by vlc_player_AddTimer()
> + */
> +VLC_API void
> +vlc_player_RemoveTimer(vlc_player_t *player, vlc_player_timer_id *timer);
> +
> +/**
> + * Interpolate the last timer value to now
> + *
> + * @param value time update obtained via the vlc_player_timer_cbs.on_update()
> + * callback
> + * @param system_now current system date
> + * @param player_rate rate of the player
> + * @param out_ts pointer where to set the interpolated ts
> + * @param out_pos pointer where to set the interpolated position
> + * @return VLC_SUCCESS in case of success, an error in the interpolated ts is
> + * negative (could happen during the buffering step)
> + */
> +VLC_API int
> +vlc_player_timer_value_Interpolate(const struct vlc_player_timer_value *value,
> + vlc_tick_t system_now,
> + vlc_tick_t *out_ts, float *out_pos);
Why not another `vlc_player_timer_value` for output ?
---
<-- Review ends here
> +
> +/** @} vlc_player__timer */
> +
> /** @} vlc_player */
>
> #endif
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 9f0b6df13a..e91fff6a3f 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -265,6 +265,7 @@ libvlccore_la_SOURCES = \
> player/player.c \
> player/player.h \
> player/input.c \
> + player/timer.c \
> player/track.c \
> player/title.c \
> player/aout.c \
> diff --git a/src/libvlccore.sym b/src/libvlccore.sym
> index f278fe6e9d..8e266b41df 100644
> --- a/src/libvlccore.sym
> +++ b/src/libvlccore.sym
> @@ -766,6 +766,7 @@ vlc_thumbnailer_Cancel
> vlc_thumbnailer_Release
> vlc_player_AddAssociatedMedia
> vlc_player_AddListener
> +vlc_player_AddTimer
> vlc_player_aout_AddListener
> vlc_player_aout_EnableFilter
> vlc_player_aout_GetVolume
> @@ -824,6 +825,7 @@ vlc_player_Pause
> vlc_player_program_Delete
> vlc_player_program_Dup
> vlc_player_RemoveListener
> +vlc_player_RemoveTimer
> vlc_player_RestartEsId
> vlc_player_Resume
> vlc_player_SeekByPos
> @@ -860,6 +862,7 @@ vlc_player_SetTeletextTransparency
> vlc_player_SetTrackCategoryEnabled
> vlc_player_Start
> vlc_player_Stop
> +vlc_player_timer_value_Interpolate
> vlc_player_title_list_GetAt
> vlc_player_title_list_GetCount
> vlc_player_title_list_Hold
> diff --git a/src/player/input.c b/src/player/input.c
> index d63b2ff59c..4ffe59522c 100644
> --- a/src/player/input.c
> +++ b/src/player/input.c
> @@ -60,12 +60,24 @@ vlc_player_input_HandleAtoBLoop(struct vlc_player_input *input, vlc_tick_t time,
> vlc_tick_t
> vlc_player_input_GetTime(struct vlc_player_input *input)
> {
> + vlc_player_t *player = input->player;
> + vlc_tick_t ts;
> +
> + if (input == player->input
> + && vlc_player_GetTimerPoint(player, vlc_tick_now(), &ts, NULL) == 0)
> + return ts;
> return input->time;
> }
>
> float
> vlc_player_input_GetPos(struct vlc_player_input *input)
> {
> + vlc_player_t *player = input->player;
> + float pos;
> +
> + if (input == player->input
> + && vlc_player_GetTimerPoint(player, vlc_tick_now(), NULL, &pos) == 0)
> + return pos;
> return input->position;
> }
>
> @@ -172,6 +184,11 @@ vlc_player_input_HandleState(struct vlc_player_input *input,
> break;
> case VLC_PLAYER_STATE_STOPPING:
> input->started = false;
> +
> + vlc_player_UpdateTimerSource(player, NULL, false, -1, -1,
> + VLC_TICK_INVALID, VLC_TICK_INVALID,
> + VLC_TICK_INVALID);
> +
> if (input == player->input)
> player->input = NULL;
>
> @@ -183,8 +200,17 @@ vlc_player_input_HandleState(struct vlc_player_input *input,
> }
> send_event = !player->started;
> break;
> - case VLC_PLAYER_STATE_STARTED:
> case VLC_PLAYER_STATE_PLAYING:
> + assert(state_date != VLC_TICK_INVALID);
> + if (input->pause_date != VLC_TICK_INVALID)
> + {
> + vlc_player_UpdateTimerState(player,
> + VLC_PLAYER_TIMER_STATE_PLAYING,
> + input->pause_date, state_date);
> + input->pause_date = VLC_TICK_INVALID;
> + }
> + /* fallthrough */
> + case VLC_PLAYER_STATE_STARTED:
> if (player->started &&
> player->global_state == VLC_PLAYER_STATE_PLAYING)
> send_event = false;
> @@ -192,6 +218,11 @@ vlc_player_input_HandleState(struct vlc_player_input *input,
>
> case VLC_PLAYER_STATE_PAUSED:
> assert(player->started && input->started);
> + assert(state_date != VLC_TICK_INVALID);
> + input->pause_date = state_date;
> +
> + vlc_player_UpdateTimerState(player, VLC_PLAYER_TIMER_STATE_PAUSED,
> + input->pause_date, state_date);
> break;
> default:
> vlc_assert_unreachable();
> @@ -573,6 +604,17 @@ input_thread_Events(input_thread_t *input_thread,
>
> assert(input_thread == input->thread);
>
> + /* No player lock for this event */
> + if (event->type == INPUT_EVENT_OUTPUT_CLOCK)
> + {
> + vlc_player_UpdateTimerSource(player, event->output_clock.id,
> + event->output_clock.master, -1,
> + event->output_clock.rate,
> + event->output_clock.ts, VLC_TICK_INVALID,
> + event->output_clock.system_ts);
> + return;
> + }
> +
> vlc_mutex_lock(&player->lock);
>
> switch (event->type)
> @@ -594,12 +636,15 @@ input_thread_Events(input_thread_t *input_thread,
> break;
> }
> case INPUT_EVENT_TIMES:
> + {
> + bool changed = false;
> if (event->times.ms != VLC_TICK_INVALID
> && (input->time != event->times.ms
> || input->position != event->times.percentage))
> {
> input->time = event->times.ms;
> input->position = event->times.percentage;
> + changed = true;
> vlc_player_SendEvent(player, on_position_changed,
> input->time, input->position);
>
> @@ -609,8 +654,15 @@ input_thread_Events(input_thread_t *input_thread,
> {
> input->length = event->times.length;
> vlc_player_SendEvent(player, on_length_changed, input->length);
> + changed = true;
> }
> + if (changed)
> + vlc_player_UpdateTimerSource(player, NULL, false,
> + input->position, input->rate,
> + input->time, input->length,
> + vlc_tick_now());
> break;
> + }
> case INPUT_EVENT_PROGRAM:
> vlc_player_input_HandleProgramEvent(input, &event->program);
> break;
> @@ -694,6 +746,7 @@ vlc_player_input_New(vlc_player_t *player, input_item_t *item)
> input->rate = 1.f;
> input->capabilities = 0;
> input->length = input->time = VLC_TICK_INVALID;
> + input->pause_date = VLC_TICK_INVALID;
> input->position = 0.f;
>
> input->recording = false;
> diff --git a/src/player/player.c b/src/player/player.c
> index b23b7ae212..51b2af2093 100644
> --- a/src/player/player.c
> +++ b/src/player/player.c
> @@ -893,6 +893,10 @@ vlc_player_SelectPrevChapter(vlc_player_t *player)
> void
> vlc_player_Lock(vlc_player_t *player)
> {
> + /* The timer lock should not be held (possible lock-order-inversion), cf.
> + * vlc_player_timer_cbs.on_update documentation */
> + assert(!vlc_mutex_marked(&player->timer_lock));
> +
> vlc_mutex_lock(&player->lock);
> }
>
> @@ -1788,6 +1792,7 @@ vlc_player_InitLocks(vlc_player_t *player, enum vlc_player_lock_type lock_type)
> else
> vlc_mutex_init(&player->lock);
>
> + vlc_mutex_init(&player->timer_lock);
> vlc_mutex_init(&player->vout_listeners_lock);
> vlc_mutex_init(&player->aout_listeners_lock);
> vlc_cond_init(&player->start_delay_cond);
> @@ -1798,6 +1803,7 @@ static void
> vlc_player_DestroyLocks(vlc_player_t *player)
> {
> vlc_mutex_destroy(&player->lock);
> + vlc_mutex_destroy(&player->timer_lock);
> vlc_mutex_destroy(&player->vout_listeners_lock);
> vlc_mutex_destroy(&player->aout_listeners_lock);
> vlc_cond_destroy(&player->start_delay_cond);
> @@ -1818,6 +1824,8 @@ vlc_player_Delete(vlc_player_t *player)
> assert(vlc_list_is_empty(&player->listeners));
> assert(vlc_list_is_empty(&player->vout_listeners));
> assert(vlc_list_is_empty(&player->aout_listeners));
> + assert(vlc_list_is_empty(&player->source_timers));
> + assert(vlc_list_is_empty(&player->regular_timers));
>
> vlc_mutex_unlock(&player->lock);
>
> @@ -1855,6 +1863,8 @@ vlc_player_New(vlc_object_t *parent, enum vlc_player_lock_type lock_type,
> vlc_list_init(&player->listeners);
> vlc_list_init(&player->vout_listeners);
> vlc_list_init(&player->aout_listeners);
> + vlc_list_init(&player->source_timers);
> + vlc_list_init(&player->regular_timers);
> vlc_list_init(&player->destructor.inputs);
> vlc_list_init(&player->destructor.stopping_inputs);
> vlc_list_init(&player->destructor.joinable_inputs);
> @@ -1876,6 +1886,8 @@ vlc_player_New(vlc_object_t *parent, enum vlc_player_lock_type lock_type,
> player->next_media_requested = false;
> player->next_media = NULL;
>
> + player->timer_value.system_date = VLC_TICK_INVALID;
> +
> #define VAR_CREATE(var, flag) do { \
> if (var_Create(player, var, flag) != VLC_SUCCESS) \
> goto error; \
> diff --git a/src/player/player.h b/src/player/player.h
> index 576bc57110..43c260e30a 100644
> --- a/src/player/player.h
> +++ b/src/player/player.h
> @@ -65,6 +65,8 @@ struct vlc_player_input
> float position;
> vlc_tick_t time;
>
> + vlc_tick_t pause_date;
> +
> bool recording;
>
> float signal_quality;
> @@ -107,6 +109,18 @@ struct vlc_player_listener_id
> struct vlc_list node;
> };
>
> +struct vlc_player_timer_id
> +{
> + enum vlc_player_timer_type type;
> + vlc_tick_t delay;
> + vlc_tick_t last_update_date;
> +
> + const struct vlc_player_timer_cbs *cbs;
> + void *data;
> +
> + struct vlc_list node;
> +};
> +
> struct vlc_player_vout_listener_id
> {
> const struct vlc_player_vout_cbs *cbs;
> @@ -125,6 +139,7 @@ struct vlc_player_t
> {
> struct vlc_object_t obj;
> vlc_mutex_t lock;
> + vlc_mutex_t timer_lock;
> vlc_mutex_t aout_listeners_lock;
> vlc_mutex_t vout_listeners_lock;
> vlc_cond_t start_delay_cond;
> @@ -142,6 +157,12 @@ struct vlc_player_t
> struct vlc_list aout_listeners;
> struct vlc_list vout_listeners;
>
> + struct vlc_list source_timers;
> + struct vlc_list regular_timers;
> + vlc_timer_t system_timer;
> + void *timer_source;
> + struct vlc_player_timer_value timer_value;
> +
> input_resource_t *resource;
> vlc_renderer_item_t *renderer;
>
> @@ -312,6 +333,28 @@ void
> vlc_player_input_HandleState(struct vlc_player_input *, enum vlc_player_state,
> vlc_tick_t state_date);
>
> +struct vlc_player_timer_value
> +vlc_player_input_GetTimerValue(struct vlc_player_input *input);
> +
> +/*
> + * player_timer.c
> +*/
> +
> +void
> +vlc_player_UpdateTimerState(vlc_player_t *player,
> + enum vlc_player_timer_state state,
> + vlc_tick_t interpolate_date, vlc_tick_t system_date);
> +
> +void
> +vlc_player_UpdateTimerSource(vlc_player_t *player, void *source,
> + bool source_is_master, float position, double rate,
> + vlc_tick_t ts, vlc_tick_t length,
> + vlc_tick_t system_date);
> +
> +int
> +vlc_player_GetTimerPoint(vlc_player_t *player, vlc_tick_t system_now,
> + vlc_tick_t *out_ts, float *out_pos);
> +
> /*
> * player_vout.c
> */
> diff --git a/src/player/timer.c b/src/player/timer.c
> new file mode 100644
> index 0000000000..baa5f4d5e8
> --- /dev/null
> +++ b/src/player/timer.c
> @@ -0,0 +1,341 @@
> +/*****************************************************************************
> + * player.c: Player interface
> + *****************************************************************************
> + * Copyright © 2018 VLC authors and VideoLAN
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License as published by
> + * the Free Software Foundation; either version 2.1 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program; if not, write to the Free Software Foundation,
> + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
> + *****************************************************************************/
> +
> +#ifdef HAVE_CONFIG_H
> +# include "config.h"
> +#endif
> +
> +#include <limits.h>
> +
> +#include "player.h"
> +
> +static void
> +vlc_player_ScheduleRegularTimer(vlc_player_t *player)
> +{
> + vlc_tick_t next_deadline = INT64_MAX;
> +
> + vlc_player_timer_id *timer;
> + vlc_list_foreach(timer, &player->regular_timers, node)
> + {
> + assert(timer->last_update_date != VLC_TICK_INVALID);
> + const vlc_tick_t timer_deadline = timer->delay + timer->last_update_date;
> + if (timer_deadline < next_deadline)
> + next_deadline = timer_deadline;
> + }
> + assert(next_deadline != INT64_MAX);
> + vlc_timer_schedule(player->system_timer, true, next_deadline, 0);
> +}
> +
> +static void
> +vlc_player_RegularTimerCallback(void *data)
> +{
> + vlc_player_t *player = data;
> +
> + static const vlc_tick_t epsilon = VLC_TICK_FROM_MS(2);
> + const vlc_tick_t system_now = vlc_tick_now();
> +
> + vlc_mutex_lock(&player->timer_lock);
> +
> + vlc_tick_t new_ts = VLC_TICK_0; /* Fallback to 0 in case of failure */
> + float new_pos = 0;
> + vlc_player_timer_value_Interpolate(&player->timer_value, system_now,
> + &new_ts, &new_pos);
> + const struct vlc_player_timer_value value =
> + {
> + .position = new_pos,
> + .rate = player->timer_value.rate,
> + .ts = new_ts,
> + .length = player->timer_value.length,
> + .system_date = system_now,
> + };
> +
> + vlc_player_timer_id *timer;
> + vlc_list_foreach(timer, &player->regular_timers, node)
> + {
> + const vlc_tick_t timer_deadline =
> + timer->last_update_date == VLC_TICK_INVALID ? system_now
> + : timer->delay + timer->last_update_date;
> +
> + if (timer_deadline >= system_now - epsilon
> + && timer_deadline <= system_now + epsilon)
> + {
> + timer->cbs->on_update(VLC_PLAYER_TIMER_STATE_PLAYING, &value,
> + timer->data);
> + timer->last_update_date = value.system_date;
> + }
> + }
> +
> + vlc_player_ScheduleRegularTimer(player);
> +
> + vlc_mutex_unlock(&player->timer_lock);
> +}
> +
> +static void
> +vlc_player_UpdateTimersLocked(vlc_player_t *player, bool force_update,
> + enum vlc_player_timer_state state,
> + const struct vlc_player_timer_value *value)
> +{
> + vlc_player_timer_id *timer;
> +
> + vlc_list_foreach(timer, &player->source_timers, node)
> + {
> + /* Respect refresh delay of the timer */
> + if (!force_update && timer->delay != VLC_TICK_INVALID
> + && timer->last_update_date != VLC_TICK_INVALID
> + && value->system_date - timer->last_update_date < timer->delay)
> + continue;
> + timer->cbs->on_update(state, value, timer->data);
> + timer->last_update_date = value->system_date;
> + }
> +
> + if (!vlc_list_is_empty(&player->regular_timers))
> + {
> + vlc_timer_disarm(player->system_timer);
> +
> + vlc_list_foreach(timer, &player->regular_timers, node)
> + {
> + if (!force_update && state == VLC_PLAYER_TIMER_STATE_PLAYING
> + && timer->last_update_date != VLC_TICK_INVALID)
> + continue;
> + timer->cbs->on_update(state, value, timer->data);
> + timer->last_update_date = value->system_date;
> + }
> +
> + if (state == VLC_PLAYER_TIMER_STATE_PLAYING)
> + vlc_player_ScheduleRegularTimer(player);
> + }
> +}
> +
> +static void
> +vlc_player_UpdateTimerStateLocked(vlc_player_t *player,
> + enum vlc_player_timer_state state,
> + vlc_tick_t interpolate_date,
> + vlc_tick_t system_date)
> +{
> + assert(interpolate_date != VLC_TICK_INVALID
> + && system_date != VLC_TICK_INVALID);
> +
> + vlc_tick_t new_ts = VLC_TICK_0; /* Fallback to 0 in case of failure */
> + float new_pos = 0;
> + vlc_player_timer_value_Interpolate(&player->timer_value, interpolate_date,
> + &new_ts, &new_pos);
> +
> + const struct vlc_player_timer_value value =
> + {
> + .position = new_pos,
> + .rate = player->timer_value.rate,
> + .ts = new_ts,
> + .length = player->timer_value.length,
> + .system_date = system_date,
> + };
> +
> + vlc_player_UpdateTimersLocked(player, true, state, &value);
> +}
> +
> +void
> +vlc_player_UpdateTimerState(vlc_player_t *player,
> + enum vlc_player_timer_state state,
> + vlc_tick_t pause_date, vlc_tick_t system_date)
> +{
> + vlc_mutex_lock(&player->timer_lock);
> + vlc_player_UpdateTimerStateLocked(player, state, pause_date, system_date);
> + vlc_mutex_unlock(&player->timer_lock);
> +}
> +
> +void
> +vlc_player_UpdateTimerSource(vlc_player_t *player, void *source,
> + bool source_is_master, float position, double rate,
> + vlc_tick_t ts, vlc_tick_t length,
> + vlc_tick_t system_date)
> +{
> + vlc_mutex_lock(&player->timer_lock);
> +
> + if (system_date != VLC_TICK_INVALID)
> + {
> + /* Source priority:
> + * 1/ source != NULL + master (from the master ES track)
> + * 2/ source != NULL (from the first ES track updated)
> + * 3/ source == NULL (from the input)
> + */
> + if (player->timer_source == NULL)
> + player->timer_source = source;
> + else if (source_is_master)
> + player->timer_source = source;
> +
> + bool update = false, force_update = false;
> +
> + if (source == NULL)
> + {
> + /* Only valid for input sources */
> + if (player->timer_value.length != length)
> + {
> + player->timer_value.length = length;
> + update = force_update = true;
> + }
> + /* Will likely be overridden by non input source */
> + player->timer_value.position = position;
> + }
> +
> + if (player->timer_source == source)
> + {
> + update = ts != VLC_TICK_INVALID;
> + force_update = player->timer_value.rate != rate;
> +
> + player->timer_value.rate = rate;
> + player->timer_value.ts = ts;
> + player->timer_value.system_date = system_date;
> +
> + if (player->timer_value.length != VLC_TICK_INVALID)
> + player->timer_value.position = player->timer_value.ts
> + / (double) player->timer_value.length;
> + }
> +
> + if (update)
> + vlc_player_UpdateTimersLocked(player, force_update,
> + VLC_PLAYER_TIMER_STATE_PLAYING,
> + &player->timer_value);
> + }
> + else if (player->timer_source == source
> + && player->timer_value.system_date != VLC_TICK_INVALID)
> + {
> + const vlc_tick_t system_now = vlc_tick_now();
> + vlc_player_UpdateTimerStateLocked(player,
> + VLC_PLAYER_TIMER_STATE_DISCONTINUITY,
> + system_now, system_now);
> +
> + player->timer_source = NULL;
> + player->timer_value.system_date = VLC_TICK_INVALID;
> + }
> +
> + vlc_mutex_unlock(&player->timer_lock);
> +}
> +
> +int
> +vlc_player_GetTimerPoint(vlc_player_t *player, vlc_tick_t system_now,
> + vlc_tick_t *out_ts, float *out_pos)
> +{
> + vlc_mutex_lock(&player->timer_lock);
> + if (player->timer_value.system_date == VLC_TICK_INVALID)
> + {
> + vlc_mutex_unlock(&player->timer_lock);
> + return VLC_EGENERIC;
> + }
> + int ret = vlc_player_timer_value_Interpolate(&player->timer_value,
> + system_now, out_ts, out_pos);
> +
> + vlc_mutex_unlock(&player->timer_lock);
> + return ret;
> +}
> +
> +vlc_player_timer_id *
> +vlc_player_AddTimer(vlc_player_t *player, enum vlc_player_timer_type type,
> + vlc_tick_t delay,
> + const struct vlc_player_timer_cbs *cbs, void *data)
> +{
> + assert(delay >= 0 || delay == VLC_TICK_INVALID);
> + assert(cbs && cbs->on_update);
> + assert(type == VLC_PLAYER_TIMER_TYPE_SOURCE
> + || type == VLC_PLAYER_TIMER_TYPE_REGULAR);
> +
> + struct vlc_player_timer_id *timer = malloc(sizeof(*timer));
> + if (!timer)
> + return NULL;
> + timer->type = type;
> + timer->delay = delay;
> + timer->last_update_date = VLC_TICK_INVALID;
> + timer->cbs = cbs;
> + timer->data = data;
> +
> + vlc_mutex_lock(&player->timer_lock);
> +
> + if (type == VLC_PLAYER_TIMER_TYPE_REGULAR)
> + {
> + assert(delay >= VLC_TICK_FROM_MS(10));
> +
> + if (vlc_list_is_empty(&player->regular_timers)
> + && vlc_timer_create(&player->system_timer,
> + vlc_player_RegularTimerCallback, player) != 0)
> + {
> + free(timer);
> + vlc_mutex_unlock(&player->timer_lock);
> + return NULL;
> + }
> + vlc_list_append(&timer->node, &player->regular_timers);
> + }
> + else
> + vlc_list_append(&timer->node, &player->source_timers);
> +
> + vlc_mutex_unlock(&player->timer_lock);
> +
> + return timer;
> +}
> +
> +void
> +vlc_player_RemoveTimer(vlc_player_t *player, vlc_player_timer_id *timer)
> +{
> + assert(timer);
> +
> + vlc_mutex_lock(&player->timer_lock);
> + vlc_list_remove(&timer->node);
> +
> + if (timer->type == VLC_PLAYER_TIMER_TYPE_REGULAR
> + && vlc_list_is_empty(&player->regular_timers))
> + vlc_timer_destroy(player->system_timer);
> +
> + vlc_mutex_unlock(&player->timer_lock);
> +
> + free(timer);
> +}
> +
> +int
> +vlc_player_timer_value_Interpolate(const struct vlc_player_timer_value *value,
> + vlc_tick_t system_now,
> + vlc_tick_t *out_ts, float *out_pos)
> +{
> + assert(value);
> + assert(system_now > 0);
> + assert(out_ts || out_pos);
> +
> + const vlc_tick_t drift = (system_now - value->system_date) * value->rate;
> + vlc_tick_t ts = value->ts;
> + float pos = value->position;
> +
> + if (ts != VLC_TICK_INVALID)
> + {
> + ts += drift;
> + if (ts < 0)
> + return VLC_EGENERIC;
> + }
> + if (value->length != VLC_TICK_INVALID)
> + {
> + pos += drift / (float) value->length;
> + if (pos > 1.f)
> + pos = 1.f;
> + if (ts > value->length)
> + ts = value->length;
> + }
> +
> + if (out_ts)
> + *out_ts = ts;
> + if (out_pos)
> + *out_pos = pos;
> +
> + return VLC_SUCCESS;
> +}
> --
> 2.20.1
>
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
More information about the vlc-devel
mailing list