[vlc-devel] [RFC PATCH 01/13] clock: add vlc_clock

Thomas Guillem thomas at gllm.fr
Thu Aug 30 14:00:36 CEST 2018


Thanks a lot for this review.

I'll check it with Denis later.

On Wed, Aug 29, 2018, at 11:10, Zhao Zhili wrote:
> 
> 
> On 2018年06月27日 20:41, Thomas Guillem wrote:
> > From: Denis Charmet <typx at videolan.org>
> >
> > ---
> >   src/Makefile.am            |   2 +
> >   src/clock/clock.c          | 526 +++++++++++++++++++++++++++++++++++++
> >   src/clock/clock.h          | 117 +++++++++
> >   src/clock/clock_internal.c |  55 ++--
> >   src/clock/clock_internal.h |  38 +--
> >   src/clock/input_clock.c    |  42 +--
> >   6 files changed, 717 insertions(+), 63 deletions(-)
> >   create mode 100644 src/clock/clock.c
> >   create mode 100644 src/clock/clock.h
> >
> > diff --git a/src/Makefile.am b/src/Makefile.am
> > index 897c557e55..7b84cca68c 100644
> > --- a/src/Makefile.am
> > +++ b/src/Makefile.am
> > @@ -233,6 +233,7 @@ libvlccore_la_SOURCES = \
> >   	input/access.c \
> >   	clock/clock_internal.c \
> >   	clock/input_clock.c \
> > +	clock/clock.c \
> >   	input/control.c \
> >   	input/decoder.c \
> >   	input/demux.c \
> > @@ -244,6 +245,7 @@ libvlccore_la_SOURCES = \
> >   	input/info.h \
> >   	input/meta.c \
> >   	clock/input_clock.h \
> > +	clock/clock.h \
> >   	clock/clock_internal.h \
> >   	input/decoder.h \
> >   	input/demux.h \
> > diff --git a/src/clock/clock.c b/src/clock/clock.c
> > new file mode 100644
> > index 0000000000..ad6b2152ea
> > --- /dev/null
> > +++ b/src/clock/clock.c
> > @@ -0,0 +1,526 @@
> > +/*****************************************************************************
> > + * clock.c: Ouptut modules synchronisation clock
> > + *****************************************************************************
> > + * Copyright (C) 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 <vlc_common.h>
> > +#include <assert.h>
> > +#include "clock.h"
> > +#include "clock_internal.h"
> > +
> > +struct vlc_clock_main_t
> > +{
> > +    vlc_mutex_t lock;
> > +    vlc_cond_t cond;
> > +
> > +    vlc_clock_t * master;
> > +
> > +    /* FIXME do we need to keep the slaves?*/
> > +    vlc_clock_t ** slaves;
> > +    int nslaves;
> > +
> > +    /**
> > +     * Linear function
> > +     * system = pts * coeff / rate + offset
> > +     */
> > +    clock_point_t last;
> > +    average_t coeff_avg; /* Moving average to smooth out the instant coeff */
> > +    float coeff;
> > +    float rate;
> > +    vlc_tick_t offset;
> > +    vlc_tick_t delay;
> > +    vlc_tick_t reset_date;
> > +
> > +    vlc_tick_t pause_date;
> > +
> > +    clock_point_t wait_sync_ref; /* When the master */
> > +    vlc_tick_t dejitter; /* Delay used to absorb the clock jitter */
> > +    bool abort;
> > +};
> > +
> > +struct vlc_clock_t
> > +{
> > +    vlc_tick_t (*update)(vlc_clock_t * clock, vlc_tick_t timestamp,
> > +                      vlc_tick_t system_now, float rate);
> > +    void (*reset)(vlc_clock_t * clock);
> > +    void (*pause)(vlc_clock_t * clock, bool paused, vlc_tick_t now);
> > +    void (*set_dejitter)(vlc_clock_t * clock, vlc_tick_t delay, int cr_avg);
> > +    vlc_tick_t (*to_system_locked)(vlc_clock_t * clock, vlc_tick_t now, vlc_tick_t pts);
> > +
> > +    vlc_clock_main_t * owner;
> > +    vlc_tick_t delay;
> > +};
> > +
> > +static vlc_tick_t main_system_to_stream(vlc_clock_main_t * main_clock,
> > +                                       vlc_tick_t system)
> > +{
> > +    if (unlikely(main_clock->offset == VLC_TS_INVALID))
> > +        return VLC_TS_INVALID;
> > +    return (vlc_tick_t) ((system - main_clock->offset) * main_clock->rate / main_clock->coeff);
> > +}
> > +
> > +static vlc_tick_t main_stream_to_system(vlc_clock_main_t * main_clock,
> > +                                       vlc_tick_t pts)
> > +{
> > +    if (unlikely(main_clock->offset == VLC_TS_INVALID))
> > +        return VLC_TS_INVALID;
> > +    return (vlc_tick_t) (pts * main_clock->coeff / main_clock->rate + main_clock->offset);
> > +}
> 
> I'm afraid int64_t to float to int64_t won't work well as float can only 
> represent a small integer precisely.
> main_clock->offset makes it worse since it isn't uncommon to be a large 
> value.
> 
> (vlc_tick_t)(pts * main_clock->coeff / main_clock->rate) + main_clock->offset
> 
> may be a little better, but the problem is still there.
> 
> 
> Some test results:
> 
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <time.h>
> #include <inttypes.h>
> 
> int main(int argc, char *argv[])
> {
>      struct timespec tp;
>      clock_gettime(CLOCK_MONOTONIC, &tp);
> 
>      int64_t t0 = tp.tv_sec * 1000000LL + tp.tv_nsec / 1000LL;
>      int64_t t1 = (int64_t)(0.0f + t0);
>      printf("t %" PRId64 ", t1 %" PRId64 ", diff %" PRId64 "\n", t0, t1, 
> t1 - t0);
>      return 0;
> }
> 
> $ uptime
>   17:09:09 up 9 days,  7:24, 17 users,  load average: 0.79, 1.10, 0.95
> $ ./demo
> t 804284916684, t1 804284923904, diff 7220
> $ ./demo
> t 804288780827, t1 804288790528, diff 9701
> $ ./demo
> t 804294212030, t1 804294230016, diff 17986
> $ ./demo
> t 804296484712, t1 804296458240, diff -26472
> $ ./demo
> t 804299796871, t1 804299800576, diff 3705
> $ ./demo
> t 804302052782, t1 804302028800, diff -23982
> 
> > +
> > +static void vlc_clock_main_reset(vlc_clock_main_t * main_clock)
> > +{
> > +    AvgReset(&main_clock->coeff_avg);
> > +    main_clock->coeff = 1.0f;
> > +    main_clock->rate = 1.0f;
> > +    main_clock->offset = VLC_TS_INVALID;
> > +
> > +    main_clock->wait_sync_ref =
> > +        main_clock->last = clock_point_Create(VLC_TS_INVALID, VLC_TS_INVALID);
> > +    vlc_cond_broadcast(&main_clock->cond);
> > +}
> > +
> > +static vlc_tick_t vlc_clock_master_update(vlc_clock_t * clock, vlc_tick_t pts,
> > +                                       vlc_tick_t system_now, float rate)
> > +{
> > +    vlc_clock_main_t * main_clock = clock->owner;
> > +
> > +    vlc_mutex_lock(&main_clock->lock);
> > +    assert(main_clock->pause_date == VLC_TS_INVALID);
> > +
> > +    if (main_clock->reset_date != VLC_TS_INVALID)
> > +    {
> > +        if (system_now < main_clock->reset_date)
> > +        {
> > +            vlc_mutex_unlock(&main_clock->lock);
> > +            return 0;
> > +        }
> > +        main_clock->reset_date = VLC_TS_INVALID;
> > +        vlc_clock_main_reset(main_clock);
> > +    }
> > +    main_clock->rate = rate;
> > +
> > +    if (unlikely(pts == VLC_TS_INVALID || system_now == VLC_TS_INVALID))
> > +    {
> > +        vlc_mutex_unlock(&main_clock->lock);
> > +        return VLC_TS_INVALID;
> > +    }
> > +
> > +    if (main_clock->offset != VLC_TS_INVALID && pts != main_clock->last.stream)
> > +    {
> > +        /* We have a reference so we can update coeff */
> > +        float instant_coeff =
> > +            ((float) (system_now - main_clock->last.system))/(pts - main_clock->last.stream);
> > +        AvgUpdate(&main_clock->coeff_avg, instant_coeff);
> > +        main_clock->coeff = AvgGet(&main_clock->coeff_avg);
> > +        /* TODO handle rate change?*/
> > +    }
> > +    else
> > +        main_clock->wait_sync_ref =
> > +            clock_point_Create(VLC_TS_INVALID, VLC_TS_INVALID);
> > +
> > +    main_clock->offset = system_now - pts * main_clock->coeff / rate;
> > +
> > +    if (pts != VLC_TS_INVALID && system_now != VLC_TS_INVALID)
> > +        main_clock->last = clock_point_Create(pts, system_now);
> > +
> > +    vlc_cond_broadcast(&main_clock->cond);
> > +    vlc_mutex_unlock(&main_clock->lock);
> > +    return 0;
> > +}
> > +
> > +static void vlc_clock_master_reset(vlc_clock_t * clock)
> > +{
> > +    vlc_clock_main_t * main_clock = clock->owner;
> > +
> > +    vlc_mutex_lock(&main_clock->lock);
> > +    vlc_clock_main_reset(main_clock);
> > +    vlc_mutex_unlock(&main_clock->lock);
> > +}
> > +
> > +static void vlc_clock_master_pause(vlc_clock_t * clock, bool paused, vlc_tick_t now)
> > +{
> > +    vlc_clock_main_t * main_clock = clock->owner;
> > +    vlc_mutex_lock(&main_clock->lock);
> > +    assert(paused == (main_clock->pause_date == VLC_TS_INVALID));
> > +
> > +    if (paused)
> > +        main_clock->pause_date = now;
> > +    else
> > +    {
> > +        /**
> > +         * Only apply a delay if the clock has a reference point to avoid
> > +         * messing up the timings if the stream was paused then seeked
> > +         */
> > +        if (main_clock->offset != VLC_TS_INVALID ||
> > +            (main_clock->wait_sync_ref.stream != VLC_TS_INVALID ||
> > +             main_clock->wait_sync_ref.system != VLC_TS_INVALID))
> > +        {
> > +            const vlc_tick_t delay = now - main_clock->pause_date;
> > +            main_clock->last.system += delay;
> > +            main_clock->offset += delay;
> > +        }
> > +        main_clock->pause_date = VLC_TS_INVALID;
> > +        vlc_cond_broadcast(&main_clock->cond);
> > +    }
> > +    vlc_mutex_unlock(&main_clock->lock);
> > +}
> > +
> > +static float vlc_clock_get_rate(vlc_clock_t * clock)
> > +{
> > +    float rate;
> > +    vlc_clock_main_t * main_clock = clock->owner;
> > +    vlc_mutex_lock(&main_clock->lock);
> > +
> > +    rate = main_clock->rate;
> > +
> > +    vlc_mutex_unlock(&main_clock->lock);
> > +
> > +    return rate;
> > +}
> > +
> > +static vlc_tick_t vlc_clock_to_stream(vlc_clock_t * clock, vlc_tick_t system)
> > +{
> > +    vlc_clock_main_t * main_clock = clock->owner;
> > +    vlc_tick_t pts;
> > +
> > +    vlc_mutex_lock(&main_clock->lock);
> > +    pts = main_system_to_stream(main_clock, system);
> > +    vlc_mutex_unlock(&main_clock->lock);
> > +    return pts;
> > +}
> > +
> > +static void vlc_clock_master_set_dejitter(vlc_clock_t * clock, vlc_tick_t delay, int cr_avg)
> > +{
> > +    VLC_UNUSED(cr_avg);
> > +    vlc_clock_main_t * main_clock = clock->owner;
> > +
> > +    vlc_mutex_lock(&main_clock->lock);
> > +    main_clock->dejitter = delay;
> > +    vlc_mutex_unlock(&main_clock->lock);
> > +}
> > +
> > +static vlc_tick_t vlc_clock_get_dejitter(vlc_clock_t * clock)
> > +{
> > +    vlc_clock_main_t * main_clock = clock->owner;
> > +    vlc_tick_t dejitter;
> > +
> > +    vlc_mutex_lock(&main_clock->lock);
> > +    dejitter = main_clock->dejitter;
> > +    vlc_mutex_unlock(&main_clock->lock);
> > +    return dejitter;
> > +}
> > +
> > +static vlc_tick_t vlc_clock_main_to_system_locked(vlc_clock_main_t * main_clock,
> > +                                               vlc_tick_t now, vlc_tick_t pts)
> > +{
> > +    vlc_tick_t system = main_stream_to_system(main_clock, pts);
> > +    if (system == VLC_TS_INVALID)
> > +    {
> > +        /**
> > +         * We don't have a master sync point, let's fallback to a monotonic
> > +         * ref point
> > +         */
> > +        if (main_clock->wait_sync_ref.stream == VLC_TS_INVALID ||
> > +            main_clock->wait_sync_ref.system == VLC_TS_INVALID)
> > +        {
> > +            main_clock->wait_sync_ref =
> > +                clock_point_Create(pts, now + main_clock->dejitter);
> > +        }
> > +        system = (pts - main_clock->wait_sync_ref.stream) / main_clock->rate;
> > +        system += main_clock->wait_sync_ref.system;
> > +    }
> > +    return system;
> > +}
> > +
> > +static vlc_tick_t vlc_clock_slave_to_system_locked(vlc_clock_t * clock, vlc_tick_t now,
> > +                                                vlc_tick_t pts)
> > +{
> > +    vlc_clock_main_t * main_clock = clock->owner;
> > +    if (main_clock->pause_date != VLC_TS_INVALID)
> > +        return INT64_MAX;
> > +
> > +    return vlc_clock_main_to_system_locked(main_clock, now, pts)
> > +         + clock->delay - main_clock->delay;
> > +}
> > +
> > +static vlc_tick_t vlc_clock_master_to_system_locked(vlc_clock_t * clock, vlc_tick_t now,
> > +                                                 vlc_tick_t pts)
> > +{
> > +    vlc_clock_main_t * main_clock = clock->owner;
> > +    assert(main_clock->pause_date == VLC_TS_INVALID);
> > +    return vlc_clock_main_to_system_locked(main_clock, now, pts);
> > +}
> > +
> > +static vlc_tick_t vlc_clock_slave_update(vlc_clock_t * clock, vlc_tick_t timestamp,
> > +                                      vlc_tick_t system_now, float rate)
> > +{
> > +    VLC_UNUSED(rate);
> > +    vlc_clock_main_t * main_clock = clock->owner;
> > +    vlc_mutex_lock(&main_clock->lock);
> > +    vlc_tick_t computed = clock->to_system_locked(clock, system_now, timestamp);
> > +    vlc_mutex_unlock(&main_clock->lock);
> > +    return (computed != INT64_MAX)?(computed - system_now):VLC_TS_INVALID;
> > +}
> > +
> > +static void vlc_clock_slave_reset(vlc_clock_t * clock)
> > +{
> > +    vlc_clock_main_t * main_clock = clock->owner;
> > +    vlc_mutex_lock(&main_clock->lock);
> > +    main_clock->wait_sync_ref = clock_point_Create(VLC_TS_INVALID, VLC_TS_INVALID);
> > +    vlc_mutex_unlock(&main_clock->lock);
> > +}
> > +
> > +static void vlc_clock_slave_pause(vlc_clock_t * clock, bool paused, vlc_tick_t now)
> > +{
> > +    VLC_UNUSED(clock);
> > +    VLC_UNUSED(paused);
> > +    VLC_UNUSED(now);
> > +}
> > +
> > +int vlc_clock_Wait(vlc_clock_t * clock, vlc_tick_t pts, vlc_tick_t max_duration)
> > +{
> > +    vlc_clock_main_t * main_clock = clock->owner;
> > +    vlc_mutex_lock(&main_clock->lock);
> > +    vlc_tick_t now = vlc_tick_now();
> > +    vlc_tick_t max_deadline = max_duration > 0 ? now + max_duration : INT64_MAX;
> > +    while (!main_clock->abort)
> > +    {
> > +        vlc_tick_t deadline;
> > +        if (main_clock->pause_date != VLC_TS_INVALID)
> > +            deadline = INT64_MAX;
> > +        else
> > +            deadline = clock->to_system_locked(clock, now, pts);
> > +        deadline = __MIN(deadline, max_deadline);
> > +
> > +        if (vlc_cond_timedwait(&main_clock->cond, &main_clock->lock, deadline))
> > +        {
> > +            vlc_mutex_unlock(&main_clock->lock);
> > +            return 0;
> > +        }
> > +    }
> > +    vlc_mutex_unlock(&main_clock->lock);
> > +    return 1;
> > +}
> > +
> > +static void vlc_clock_slave_set_dejitter(vlc_clock_t * clock, vlc_tick_t delay, int cr_avg)
> > +{
> > +    VLC_UNUSED(clock);
> > +    VLC_UNUSED(delay);
> > +    VLC_UNUSED(cr_avg);
> > +}
> > +
> > +
> > +vlc_clock_main_t * vlc_clock_main_New(void)
> > +{
> > +    vlc_clock_main_t * main_clock = malloc(sizeof(vlc_clock_main_t));
> > +
> > +    if (main_clock == NULL)
> > +        return NULL;
> > +
> > +    vlc_mutex_init(&main_clock->lock);
> > +    vlc_cond_init(&main_clock->cond);
> > +    main_clock->master = NULL;
> > +
> > +    TAB_INIT(main_clock->nslaves, main_clock->slaves);
> > +    main_clock->coeff = 1.0f;
> > +    main_clock->rate = 1.0f;
> > +    main_clock->offset = VLC_TS_INVALID;
> > +    main_clock->reset_date = VLC_TS_INVALID;
> > +    main_clock->delay = 0;
> > +
> > +    main_clock->wait_sync_ref =
> > +        main_clock->last = clock_point_Create(VLC_TS_INVALID, VLC_TS_INVALID);
> > +
> > +    main_clock->pause_date = VLC_TS_INVALID;
> > +    main_clock->dejitter = 200000;
> > +    main_clock->abort = false;
> > +
> > +    AvgInit(&main_clock->coeff_avg, 10);
> > +
> > +    return main_clock;
> > +}
> > +
> > +void vlc_clock_main_Abort(vlc_clock_main_t * main_clock)
> > +{
> > +    vlc_mutex_lock(&main_clock->lock);
> > +    main_clock->abort = true;
> > +    vlc_cond_broadcast(&main_clock->cond);
> > +
> > +    vlc_mutex_unlock(&main_clock->lock);
> > +}
> > +
> > +void vlc_clock_main_Reset(vlc_clock_main_t * main_clock)
> > +{
> > +    vlc_mutex_lock(&main_clock->lock);
> > +    vlc_clock_main_reset(main_clock);
> > +    vlc_mutex_unlock(&main_clock->lock);
> > +}
> > +
> > +void vlc_clock_main_Delete(vlc_clock_main_t * main_clock)
> > +{
> > +    while (main_clock->nslaves != 0)
> > +        vlc_clock_Delete(main_clock->slaves[0]);
> > +
> > +    if (main_clock->master != NULL)
> > +        vlc_clock_Delete(main_clock->master);
> > +
> > +    TAB_CLEAN(main_clock->nslaves, main_clock->slaves);
> > +    vlc_mutex_destroy(&main_clock->lock);
> > +    vlc_cond_destroy(&main_clock->cond);
> > +    free(main_clock);
> > +}
> > +
> > +vlc_tick_t vlc_clock_Update(vlc_clock_t * clock, vlc_tick_t timestamp,
> > +                         vlc_tick_t system_now, float rate)
> > +{
> > +    return clock->update(clock, timestamp, system_now, rate);
> > +}
> > +
> > +void vlc_clock_Reset(vlc_clock_t * clock)
> > +{
> > +    clock->reset(clock);
> > +}
> > +
> > +void vlc_clock_ChangePause(vlc_clock_t * clock, bool paused, vlc_tick_t system_now)
> > +{
> > +    clock->pause(clock, paused, system_now);
> > +}
> > +
> > +float vlc_clock_GetRate(vlc_clock_t * clock)
> > +{
> > +    return vlc_clock_get_rate(clock);
> > +}
> > +
> > +vlc_tick_t vlc_clock_ConvertToSystem(vlc_clock_t * clock, vlc_tick_t pts)
> > +{
> > +    vlc_clock_main_t * main_clock = clock->owner;
> > +    vlc_mutex_lock(&main_clock->lock);
> > +    vlc_tick_t system = clock->to_system_locked(clock, vlc_tick_now(), pts);
> > +    vlc_mutex_unlock(&main_clock->lock);
> > +    return system;
> > +}
> > +
> > +vlc_tick_t vlc_clock_ConvertToStream(vlc_clock_t * clock, vlc_tick_t system)
> > +{
> > +    return vlc_clock_to_stream(clock, system);
> > +}
> > +
> > +void vlc_clock_SetDejitter(vlc_clock_t * clock, vlc_tick_t delay, int cr_avg)
> > +{
> > +    clock->set_dejitter(clock, delay, cr_avg);
> > +}
> > +
> > +vlc_tick_t vlc_clock_GetDejitter(vlc_clock_t * clock)
> > +{
> > +    return vlc_clock_get_dejitter(clock);
> > +}
> > +
> > +static void vlc_clock_set_master_cbk(vlc_clock_t * clk)
> > +{
> > +
> > +    clk->update = vlc_clock_master_update;
> > +    clk->reset = vlc_clock_master_reset;
> > +    clk->pause = vlc_clock_master_pause;
> > +    clk->set_dejitter = vlc_clock_master_set_dejitter;
> > +    clk->to_system_locked = vlc_clock_master_to_system_locked;
> > +}
> > +
> > +static void vlc_clock_set_slave_cbk(vlc_clock_t * clk)
> > +{
> > +    clk->update = vlc_clock_slave_update;
> > +    clk->reset = vlc_clock_slave_reset;
> > +    clk->pause = vlc_clock_slave_pause;
> > +    clk->set_dejitter = vlc_clock_slave_set_dejitter;
> > +    clk->to_system_locked = vlc_clock_slave_to_system_locked;
> > +}
> > +
> > +vlc_clock_t * vlc_clock_NewMaster(vlc_clock_main_t * main_clock)
> > +{
> > +    vlc_clock_t * clk = malloc(sizeof(vlc_clock_t));
> > +    if (clk == NULL)
> > +        return NULL;
> > +
> > +    clk->owner = main_clock;
> > +    clk->delay = 0;
> > +
> > +    vlc_mutex_lock(&main_clock->lock);
> > +    if (main_clock->master != NULL)
> > +    {
> > +        vlc_clock_master_reset(main_clock->master);
> > +        vlc_clock_set_slave_cbk(main_clock->master);
> > +        TAB_APPEND(main_clock->nslaves, main_clock->slaves, main_clock->master);
> > +    }
> > +    vlc_clock_set_master_cbk(clk);
> > +    main_clock->master = clk;
> > +    vlc_mutex_unlock(&main_clock->lock);
> > +
> > +    return clk;
> > +}
> > +
> > +vlc_clock_t * vlc_clock_NewSlave(vlc_clock_main_t * main_clock)
> > +{
> > +    vlc_clock_t * clk = malloc(sizeof(vlc_clock_t));
> > +    if (clk == NULL)
> > +        return NULL;
> > +
> > +    clk->owner = main_clock;
> > +    clk->delay = 0;
> > +
> > +    vlc_mutex_lock(&main_clock->lock);
> > +    vlc_clock_set_slave_cbk(clk);
> > +    TAB_APPEND(main_clock->nslaves, main_clock->slaves, clk);
> > +    vlc_mutex_unlock(&main_clock->lock);
> > +
> > +    return clk;
> > +}
> > +
> > +void vlc_clock_SetMaster(vlc_clock_main_t * main_clock, vlc_clock_t * clk)
> > +{
> > +    vlc_mutex_lock(&main_clock->lock);
> > +    TAB_REMOVE(main_clock->nslaves, main_clock->slaves, clk);
> > +    if (main_clock->master != NULL)
> > +    {
> > +        vlc_clock_master_reset(main_clock->master);
> > +        vlc_clock_set_slave_cbk(main_clock->master);
> > +        TAB_APPEND(main_clock->nslaves, main_clock->slaves, main_clock->master);
> > +    }
> > +    vlc_clock_set_master_cbk(clk);
> > +    main_clock->master = clk;
> > +    vlc_mutex_unlock(&main_clock->lock);
> > +}
> > +
> > +
> > +void vlc_clock_Delete(vlc_clock_t * clock)
> > +{
> > +    vlc_clock_main_t * main_clock = clock->owner;
> > +    vlc_mutex_lock(&main_clock->lock);
> > +    if (clock == main_clock->master)
> > +    {
> > +        vlc_clock_main_reset(main_clock);
> > +        main_clock->master = NULL;
> > +    }
> > +    else
> > +        TAB_REMOVE(main_clock->nslaves, main_clock->slaves, clock);
> > +    vlc_mutex_unlock(&main_clock->lock);
> > +    free(clock);
> > +}
> > +
> > +
> > diff --git a/src/clock/clock.h b/src/clock/clock.h
> > new file mode 100644
> > index 0000000000..f5a4df54d0
> > --- /dev/null
> > +++ b/src/clock/clock.h
> > @@ -0,0 +1,117 @@
> > +/*****************************************************************************
> > + * clock.h: Ouptut modules synchronisation clock
> > + *****************************************************************************
> > + * Copyright (C) 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.
> > + *****************************************************************************/
> > +#ifndef LIBVLC_CLOCK_H
> > +#define LIBVLC_CLOCK_H
> > +
> > +typedef struct vlc_clock_main_t vlc_clock_main_t;
> > +typedef struct vlc_clock_t vlc_clock_t;
> > +
> > +/**
> > + * This function creates the vlc_clock_main_t of the program
> > + */
> > +vlc_clock_main_t * vlc_clock_main_New(void);
> > +
> > +/**
> > + * Destroy the clock main
> > + */
> > +void vlc_clock_main_Delete(vlc_clock_main_t * main);
> > +
> > +/**
> > + * Abort all the pending vlc_clock_Wait
> > + */
> > +void vlc_clock_main_Abort(vlc_clock_main_t * main);
> > +
> > +/**
> > + * Reset the vlc_clock_main_t
> > + */
> > +void vlc_clock_main_Reset(vlc_clock_main_t * main);
> > +
> > +/**
> > + * This function set the allocated interface as the master making the current
> > + * master if any a slave.
> > + */
> > +void vlc_clock_SetMaster(vlc_clock_main_t * main, vlc_clock_t * new_master);
> > +
> > +/**
> > + * This function creates a new slave vlc_clock_t interface.
> > + * You must use vlc_clock_Delete to free it.
> > + */
> > +vlc_clock_t * vlc_clock_NewMaster(vlc_clock_main_t * main);
> > +
> > +/**
> > + * This function creates a new slave vlc_clock_t interface.
> > + * You must use vlc_clock_Delete to free it.
> > + */
> > +vlc_clock_t * vlc_clock_NewSlave(vlc_clock_main_t * main);
> > +
> > +/**
> > + * This function free the resources allocated by vlc_clock_New
> > + */
> > +void vlc_clock_Delete(vlc_clock_t * clock);
> > +
> > +/**
> > + * This function will update the clock drift and returns the drift
> > + */
> > +vlc_tick_t vlc_clock_Update(vlc_clock_t * clock, vlc_tick_t pts,
> > +                         vlc_tick_t system_now, float rate);
> > +
> > +/**
> > + * This function resets the clock drift
> > + */
> > +void vlc_clock_Reset(vlc_clock_t * clock);
> > +
> > +/**
> > + * This function allows changing the pause status.
> > + */
> > +void vlc_clock_ChangePause(vlc_clock_t * clock, bool paused,
> > +                           vlc_tick_t sysem_now);
> > +
> > +/**
> > + * This function returns the current rate.
> > + */
> > +float vlc_clock_GetRate(vlc_clock_t * clock);
> > +
> > +/**
> > + * Wait for a timestamp expressed in stream time
> > + */
> > +int vlc_clock_Wait(vlc_clock_t * clock, vlc_tick_t pts, vlc_tick_t max_duration);
> > +
> > +/**
> > + * This function converts a timestamp from stream to system
> > + */
> > +vlc_tick_t vlc_clock_ConvertToSystem(vlc_clock_t * clock, vlc_tick_t pts);
> > +
> > +/**
> > + * This function converts a timestamp from system to stream time
> > + */
> > +vlc_tick_t vlc_clock_ConvertToStream(vlc_clock_t * clock, vlc_tick_t system);
> > +
> > +/**
> > + * This function sets the dejitter delay to absorb the clock jitter
> > + * also used as the ma1ximum delay before the synchro is considered to kick in
> > + */
> > +void vlc_clock_SetDejitter(vlc_clock_t * clock, vlc_tick_t delay, int cr_avg);
> > +
> > +/**
> > + * This function retrieves the synchronization delay
> > + */
> > +vlc_tick_t vlc_clock_GetDejitter(vlc_clock_t * clock);
> > +
> > +#endif /*LIBVLC_CLOCK_H*/
> > diff --git a/src/clock/clock_internal.c b/src/clock/clock_internal.c
> > index 49711cbf01..b3cf44a02a 100644
> > --- a/src/clock/clock_internal.c
> > +++ b/src/clock/clock_internal.c
> > @@ -30,47 +30,52 @@
> >   /*****************************************************************************
> >    * Long term average helpers
> >    *****************************************************************************/
> > -void AvgInit( average_t *p_avg, int i_divider )
> > +void AvgInit(average_t *avg, int range)
> >   {
> > -    p_avg->i_divider = i_divider;
> > -    AvgReset( p_avg );
> > +    avg->range = range;
> > +    AvgReset(avg);
> >   }
> >   
> > -void AvgClean( average_t *p_avg )
> > +void AvgClean(average_t * avg)
> >   {
> > -    VLC_UNUSED(p_avg);
> > +    VLC_UNUSED(avg);
> >   }
> >   
> > -void AvgReset( average_t *p_avg )
> > +void AvgReset(average_t *avg)
> >   {
> > -    p_avg->i_value = 0;
> > -    p_avg->i_residue = 0;
> > -    p_avg->i_count = 0;
> > +    avg->value = 0.0f;
> > +    avg->count = 0;
> >   }
> >   
> > -void AvgUpdate( average_t *p_avg, vlc_tick_t i_value )
> > +void AvgUpdate(average_t *avg, float value)
> >   {
> > -    const int i_f0 = __MIN( p_avg->i_divider - 1, p_avg->i_count );
> > -    const int i_f1 = p_avg->i_divider - i_f0;
> > +    const int new_value_weight = 1;
> > +    int average_weight;
> > +    int divider;
> > +    if (avg->count < avg->range)
> > +    {
> > +        average_weight = avg->count++;
> > +        divider = avg->count;
> > +    }
> > +    else
> > +    {
> > +        average_weight = avg->range - 1;
> > +        divider = avg->range;
> > +    }
> >   
> > -    const vlc_tick_t i_tmp = i_f0 * p_avg->i_value + i_f1 * i_value + p_avg->i_residue;
> > -
> > -    p_avg->i_value   = i_tmp / p_avg->i_divider;
> > -    p_avg->i_residue = i_tmp % p_avg->i_divider;
> > -
> > -    p_avg->i_count++;
> > +    const float tmp = average_weight * avg->value + new_value_weight * value;
> > +    avg->value = tmp / divider;
> >   }
> >   
> > -vlc_tick_t AvgGet( average_t *p_avg )
> > +float AvgGet(average_t *avg)
> >   {
> > -    return p_avg->i_value;
> > +    return avg->value;
> >   }
> >   
> > -void AvgRescale( average_t *p_avg, int i_divider )
> > +void AvgRescale(average_t *avg, int range)
> >   {
> > -    const vlc_tick_t i_tmp = p_avg->i_value * p_avg->i_divider + p_avg->i_residue;
> > +    const float tmp = avg->value * avg->range;
> >   
> > -    p_avg->i_divider = i_divider;
> > -    p_avg->i_value   = i_tmp / p_avg->i_divider;
> > -    p_avg->i_residue = i_tmp % p_avg->i_divider;
> > +    avg->range = range;
> > +    avg->value = tmp / avg->range;
> >   }
> > diff --git a/src/clock/clock_internal.h b/src/clock/clock_internal.h
> > index 21da59c075..eeaab8cdca 100644
> > --- a/src/clock/clock_internal.h
> > +++ b/src/clock/clock_internal.h
> > @@ -20,6 +20,8 @@
> >    * along with this program; if not, write to the Free Software Foundation,
> >    * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
> >    *****************************************************************************/
> > +#ifndef LIBVLC_CLOCK_INTERNAL_H
> > +#define LIBVLC_CLOCK_INTERNAL_H
> >   
> >   #include <vlc_common.h>
> >   
> > @@ -27,36 +29,38 @@
> >    * Structures
> >    *****************************************************************************/
> >   
> > - /**
> > - * This structure holds long term average
> > +/**
> > + * This structure holds long term moving average
> >    */
> >   typedef struct
> >   {
> > -    vlc_tick_t i_value;
> > -    int     i_residue;
> > -
> > -    int     i_count;
> > -    int     i_divider;
> > +    float value; /* The average value */
> > +    int count; /* The number of sample evaluated */
> > +    int range; /* The maximum range of sample on which we calculate the average*/
> >   } average_t;
> >   
> > -void    AvgInit( average_t *, int i_divider );
> > -void    AvgClean( average_t * );
> > +void AvgInit(average_t *, int range);
> > +void AvgClean(average_t *);
> > +
> > +void AvgReset(average_t *);
> > +
> > +/*  calculates (previous_average * (range - 1) + new_value)/range */
> > +void AvgUpdate(average_t *, float value);
> >   
> > -void    AvgReset( average_t * );
> > -void    AvgUpdate( average_t *, vlc_tick_t i_value );
> > -vlc_tick_t AvgGet( average_t * );
> > -void    AvgRescale( average_t *, int i_divider );
> > +float AvgGet(average_t *);
> > +void AvgRescale(average_t *, int range);
> >   
> >   /* */
> >   typedef struct
> >   {
> > -    vlc_tick_t i_stream;
> > -    vlc_tick_t i_system;
> > +    vlc_tick_t stream;
> > +    vlc_tick_t system;
> >   } clock_point_t;
> >   
> > -static inline clock_point_t clock_point_Create( vlc_tick_t i_stream, vlc_tick_t i_system )
> > +static inline clock_point_t clock_point_Create( vlc_tick_t stream, vlc_tick_t system )
> >   {
> > -    clock_point_t p = { .i_stream = i_stream, .i_system = i_system };
> > +    clock_point_t p = { .stream = stream, .system = system };
> >       return p;
> >   }
> >   
> > +#endif /* LIBVLC_CLOCK_INTERNAL_H */
> > diff --git a/src/clock/input_clock.c b/src/clock/input_clock.c
> > index 247d78ddfa..f20fa532a3 100644
> > --- a/src/clock/input_clock.c
> > +++ b/src/clock/input_clock.c
> > @@ -216,9 +216,9 @@ void input_clock_Update( input_clock_t *cl, vlc_object_t *p_log,
> >           /* */
> >           b_reset_reference= true;
> >       }
> > -    else if( cl->last.i_stream != VLC_TS_INVALID &&
> > -             ( (cl->last.i_stream - i_ck_stream) > CR_MAX_GAP ||
> > -               (cl->last.i_stream - i_ck_stream) < -CR_MAX_GAP ) )
> > +    else if( cl->last.stream != VLC_TS_INVALID &&
> > +             ( (cl->last.stream - i_ck_stream) > CR_MAX_GAP ||
> > +               (cl->last.stream - i_ck_stream) < -CR_MAX_GAP ) )
> >       {
> >           /* Stream discontinuity, for which we haven't received a
> >            * warning from the stream control facilities (dd-edited
> > @@ -265,7 +265,7 @@ void input_clock_Update( input_clock_t *cl, vlc_object_t *p_log,
> >           /* Try to bufferize more than necessary by reading
> >            * CR_BUFFERING_RATE/256 faster until we have CR_BUFFERING_TARGET.
> >            */
> > -        const vlc_tick_t i_duration = __MAX( i_ck_stream - cl->last.i_stream, 0 );
> > +        const vlc_tick_t i_duration = __MAX( i_ck_stream - cl->last.stream, 0 );
> >   
> >           cl->i_buffering_duration += ( i_duration * CR_BUFFERING_RATE + 255 ) / 256;
> >           if( cl->i_buffering_duration > CR_BUFFERING_TARGET )
> > @@ -316,7 +316,7 @@ void input_clock_ChangeRate( input_clock_t *cl, int i_rate )
> >       {
> >           /* Move the reference point (as if we were playing at the new rate
> >            * from the start */
> > -        cl->ref.i_system = cl->last.i_system - (cl->last.i_system - cl->ref.i_system) * i_rate / cl->i_rate;
> > +        cl->ref.system = cl->last.system - (cl->last.system - cl->ref.system) * i_rate / cl->i_rate;
> >       }
> >       cl->i_rate = i_rate;
> >   
> > @@ -337,8 +337,8 @@ void input_clock_ChangePause( input_clock_t *cl, bool b_paused, vlc_tick_t i_dat
> >   
> >           if( cl->b_has_reference && i_duration > 0 )
> >           {
> > -            cl->ref.i_system += i_duration;
> > -            cl->last.i_system += i_duration;
> > +            cl->ref.system += i_duration;
> > +            cl->last.system += i_duration;
> >           }
> >       }
> >       cl->i_pause_date = i_date;
> > @@ -358,7 +358,7 @@ vlc_tick_t input_clock_GetWakeup( input_clock_t *cl )
> >   
> >       /* Synchronized, we can wait */
> >       if( cl->b_has_reference )
> > -        i_wakeup = ClockStreamToSystem( cl, cl->last.i_stream + AvgGet( &cl->drift ) - cl->i_buffering_duration );
> > +        i_wakeup = ClockStreamToSystem( cl, cl->last.stream + AvgGet( &cl->drift ) - cl->i_buffering_duration );
> >   
> >       vlc_mutex_unlock( &cl->lock );
> >   
> > @@ -450,11 +450,11 @@ int input_clock_GetState( input_clock_t *cl,
> >           return VLC_EGENERIC;
> >       }
> >   
> > -    *pi_stream_start = cl->ref.i_stream;
> > -    *pi_system_start = cl->ref.i_system;
> > +    *pi_stream_start = cl->ref.stream;
> > +    *pi_system_start = cl->ref.system;
> >   
> > -    *pi_stream_duration = cl->last.i_stream - cl->ref.i_stream;
> > -    *pi_system_duration = cl->last.i_system - cl->ref.i_system;
> > +    *pi_stream_duration = cl->last.stream - cl->ref.stream;
> > +    *pi_system_duration = cl->last.system - cl->ref.system;
> >   
> >       vlc_mutex_unlock( &cl->lock );
> >   
> > @@ -469,7 +469,7 @@ void input_clock_ChangeSystemOrigin( input_clock_t *cl, bool b_absolute, vlc_tic
> >       vlc_tick_t i_offset;
> >       if( b_absolute )
> >       {
> > -        i_offset = i_system - cl->ref.i_system - ClockGetTsOffset( cl );
> > +        i_offset = i_system - cl->ref.system - ClockGetTsOffset( cl );
> >       }
> >       else
> >       {
> > @@ -481,8 +481,8 @@ void input_clock_ChangeSystemOrigin( input_clock_t *cl, bool b_absolute, vlc_tic
> >           i_offset = i_system - cl->i_external_clock;
> >       }
> >   
> > -    cl->ref.i_system += i_offset;
> > -    cl->last.i_system += i_offset;
> > +    cl->ref.system += i_offset;
> > +    cl->last.system += i_offset;
> >   
> >       vlc_mutex_unlock( &cl->lock );
> >   }
> > @@ -493,7 +493,7 @@ void input_clock_GetSystemOrigin( input_clock_t *cl, vlc_tick_t *pi_system, vlc_
> >   
> >       assert( cl->b_has_reference );
> >   
> > -    *pi_system = cl->ref.i_system;
> > +    *pi_system = cl->ref.system;
> >       if( pi_delay )
> >           *pi_delay  = cl->i_pts_delay;
> >   
> > @@ -534,7 +534,7 @@ void input_clock_SetJitter( input_clock_t *cl,
> >       if( i_cr_average < 10 )
> >           i_cr_average = 10;
> >   
> > -    if( cl->drift.i_divider != i_cr_average )
> > +    if( cl->drift.range != i_cr_average )
> >           AvgRescale( &cl->drift, i_cr_average );
> >   
> >       vlc_mutex_unlock( &cl->lock );
> > @@ -570,8 +570,8 @@ static vlc_tick_t ClockStreamToSystem( input_clock_t *cl, vlc_tick_t i_stream )
> >       if( !cl->b_has_reference )
> >           return VLC_TS_INVALID;
> >   
> > -    return ( i_stream - cl->ref.i_stream ) * cl->i_rate / INPUT_RATE_DEFAULT +
> > -           cl->ref.i_system;
> > +    return ( i_stream - cl->ref.stream ) * cl->i_rate / INPUT_RATE_DEFAULT +
> > +           cl->ref.system;
> >   }
> >   
> >   /*****************************************************************************
> > @@ -582,8 +582,8 @@ static vlc_tick_t ClockStreamToSystem( input_clock_t *cl, vlc_tick_t i_stream )
> >   static vlc_tick_t ClockSystemToStream( input_clock_t *cl, vlc_tick_t i_system )
> >   {
> >       assert( cl->b_has_reference );
> > -    return ( i_system - cl->ref.i_system ) * INPUT_RATE_DEFAULT / cl->i_rate +
> > -            cl->ref.i_stream;
> > +    return ( i_system - cl->ref.system ) * INPUT_RATE_DEFAULT / cl->i_rate +
> > +            cl->ref.stream;
> >   }
> >   
> >   /**
> 
> 
> 
> _______________________________________________
> 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