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

Zhao Zhili quinkblack at foxmail.com
Wed Aug 29 11:10:55 CEST 2018



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;
>   }
>   
>   /**





More information about the vlc-devel mailing list