[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