[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