vlc | branch: master | Denis Charmet <t...@videolan.org> | Wed May 2 18:19:25 2018 +0200| [2369e1a5df89bdba76ec9e1ad10e0d74cdd446ab] | committer: Thomas Guillem
clock: add vlc_clock Co-authored-by: Rémi Denis-Courmont <r...@remlab.net> Co-authored-by: Thomas Guillem <tho...@gllm.fr> Co-authored-by: Jean-Baptiste Kempf <j...@videolan.org> Signed-off-by: Thomas Guillem <tho...@gllm.fr> > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=2369e1a5df89bdba76ec9e1ad10e0d74cdd446ab --- src/Makefile.am | 2 + src/clock/clock.c | 567 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/clock/clock.h | 134 +++++++++++++ 3 files changed, 703 insertions(+) diff --git a/src/Makefile.am b/src/Makefile.am index 5e1a448cca..9c6fcd4e4b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -262,6 +262,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/decoder_helpers.c \ @@ -275,6 +276,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..3fae6c9161 --- /dev/null +++ b/src/clock/clock.c @@ -0,0 +1,567 @@ +/***************************************************************************** + * clock.c: Output modules synchronisation clock + ***************************************************************************** + * Copyright (C) 2018-2019 VLC authors, VideoLAN and Videolabs SAS + * + * 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 <vlc_aout.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; + + unsigned rc; + + /** + * Linear function + * system = pts * coeff / rate + offset + */ + clock_point_t last; + average_t coeff_avg; /* Moving average to smooth out the instant coeff */ + double rate; + double coeff; + vlc_tick_t offset; + vlc_tick_t delay; + + vlc_tick_t pause_date; + + clock_point_t wait_sync_ref; /* When the master */ + clock_point_t first_pcr; + vlc_tick_t output_dejitter; /* Delay used to absorb the output clock jitter */ + vlc_tick_t input_dejitter; /* Delay used to absorb the input jitter */ + bool abort; +}; + +struct vlc_clock_t +{ + vlc_tick_t (*update)(vlc_clock_t *clock, vlc_tick_t system_now, + vlc_tick_t pts, double rate); + void (*reset)(vlc_clock_t *clock); + vlc_tick_t (*set_delay)(vlc_clock_t *clock, vlc_tick_t delay); + void (*set_dejitter)(vlc_clock_t *clock, vlc_tick_t delay); + vlc_tick_t (*to_system_locked)(vlc_clock_t *clock, vlc_tick_t system_now, + vlc_tick_t pts, double rate); + + vlc_clock_main_t *owner; + vlc_tick_t delay; + vlc_tick_t dejitter; +}; + +static vlc_tick_t main_stream_to_system(vlc_clock_main_t *main_clock, + vlc_tick_t pts) +{ + if (main_clock->offset == VLC_TICK_INVALID) + return VLC_TICK_INVALID; + return (vlc_tick_t) + (pts * main_clock->coeff / main_clock->rate + main_clock->offset); +} + +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_TICK_INVALID; + + main_clock->wait_sync_ref = + main_clock->last = clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID); + vlc_cond_broadcast(&main_clock->cond); +} + +static vlc_tick_t vlc_clock_master_update(vlc_clock_t *clock, + vlc_tick_t system_now, + vlc_tick_t pts, double rate) +{ + vlc_clock_main_t *main_clock = clock->owner; + + if (unlikely(pts == VLC_TICK_INVALID || system_now == VLC_TICK_INVALID)) + return VLC_TICK_INVALID; + pts += clock->delay; + + vlc_mutex_lock(&main_clock->lock); + + if (main_clock->offset != VLC_TICK_INVALID && pts != main_clock->last.stream) + { + /* We have a reference so we can update coeff */ + double instant_coeff = (system_now - main_clock->last.system) + / (double)(pts - main_clock->last.stream); + if (rate == main_clock->rate) + { + instant_coeff *= rate; + AvgUpdate(&main_clock->coeff_avg, instant_coeff); + main_clock->coeff = AvgGet(&main_clock->coeff_avg); + } + } + else + main_clock->wait_sync_ref = + clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID); + + main_clock->offset = system_now - pts * main_clock->coeff / rate; + + main_clock->last = clock_point_Create(system_now, pts); + + main_clock->rate = rate; + 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); + + assert(main_clock->delay <= 0); + assert(clock->delay >= 0); + /* Move the delay from the slaves to the master */ + if (clock->delay != 0 && main_clock->delay != 0) + { + vlc_tick_t delta = clock->delay + main_clock->delay; + if (delta > 0) + { + clock->delay = delta; + main_clock->delay = 0; + } + else + { + clock->delay = 0; + main_clock->delay = delta; + } + } + + vlc_mutex_unlock(&main_clock->lock); +} + +static vlc_tick_t vlc_clock_master_set_delay(vlc_clock_t *clock, vlc_tick_t delay) +{ + vlc_clock_main_t *main_clock = clock->owner; + vlc_mutex_lock(&main_clock->lock); + + vlc_tick_t delta = delay - clock->delay; + + if (delta > 0) + { + /* The master clock is delayed */ + main_clock->delay = 0; + clock->delay = delay; + } + else + { + /* Delay all slaves clock instead of advancing the master one */ + main_clock->delay = delta; + } + + assert(main_clock->delay <= 0); + assert(clock->delay >= 0); + + vlc_cond_broadcast(&main_clock->cond); + vlc_mutex_unlock(&main_clock->lock); + return delta; +} + +static void vlc_clock_master_set_dejitter(vlc_clock_t *clock, vlc_tick_t delay) +{ + vlc_clock_main_t *main_clock = clock->owner; + + vlc_mutex_lock(&main_clock->lock); + main_clock->output_dejitter = delay; + vlc_mutex_unlock(&main_clock->lock); +} + +static vlc_tick_t +vlc_clock_monotonic_to_system_locked(vlc_clock_t *clock, vlc_tick_t now, + vlc_tick_t pts, double rate) +{ + vlc_clock_main_t *main_clock = clock->owner; + + if (main_clock->wait_sync_ref.system == VLC_TICK_INVALID) + { + /* XXX: This input_delay calculation is needed until we (finally) get + * ride of the input clock. This code is adapted from input_clock.c and + * is used to introduce the same delay than the input clock (first PTS + * - first PCR). */ + const vlc_tick_t pcr_delay = + main_clock->first_pcr.system == VLC_TICK_INVALID ? 0 : + (pts - main_clock->first_pcr.stream) / rate + + main_clock->first_pcr.system - now; + + const vlc_tick_t input_delay = main_clock->input_dejitter + pcr_delay; + + const vlc_tick_t delay = + __MAX(input_delay, main_clock->output_dejitter); + + main_clock->wait_sync_ref = clock_point_Create(now + delay, pts); + } + return (pts - main_clock->wait_sync_ref.stream) / rate + + main_clock->wait_sync_ref.system; +} + +static vlc_tick_t vlc_clock_slave_to_system_locked(vlc_clock_t *clock, + vlc_tick_t now, + vlc_tick_t pts, double rate) +{ + vlc_clock_main_t *main_clock = clock->owner; + if (main_clock->pause_date != VLC_TICK_INVALID) + return INT64_MAX; + + vlc_tick_t system = main_stream_to_system(main_clock, pts); + if (system == VLC_TICK_INVALID) + { + /* We don't have a master sync point, let's fallback to a monotonic ref + * point */ + system = vlc_clock_monotonic_to_system_locked(clock, now, pts, rate); + } + + return system + (clock->delay - main_clock->delay) * rate; +} + +static vlc_tick_t vlc_clock_master_to_system_locked(vlc_clock_t *clock, + vlc_tick_t now, + vlc_tick_t pts, double rate) +{ + vlc_clock_main_t *main_clock = clock->owner; + vlc_tick_t system = main_stream_to_system(main_clock, pts); + if (system == VLC_TICK_INVALID) + { + /* We don't have a master sync point, let's fallback to a monotonic ref + * point */ + system = vlc_clock_monotonic_to_system_locked(clock, now, pts, rate); + } + + return system + clock->delay * rate; +} + +static vlc_tick_t vlc_clock_slave_update(vlc_clock_t *clock, + vlc_tick_t system_now, + vlc_tick_t pts, double 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, pts, rate); + vlc_mutex_unlock(&main_clock->lock); + return computed != INT64_MAX ? computed - system_now : VLC_TICK_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_TICK_INVALID, VLC_TICK_INVALID); + vlc_mutex_unlock(&main_clock->lock); +} + +static vlc_tick_t vlc_clock_slave_set_delay(vlc_clock_t *clock, vlc_tick_t delay) +{ + vlc_clock_main_t *main_clock = clock->owner; + vlc_mutex_lock(&main_clock->lock); + + clock->delay = delay; + + vlc_cond_broadcast(&main_clock->cond); + vlc_mutex_unlock(&main_clock->lock); + return 0; +} + +int vlc_clock_Wait(vlc_clock_t *clock, vlc_tick_t system_now, vlc_tick_t pts, + double rate, vlc_tick_t max_duration) +{ + vlc_clock_main_t *main_clock = clock->owner; + vlc_mutex_lock(&main_clock->lock); + const vlc_tick_t max_deadline = + max_duration > 0 ? system_now + max_duration : INT64_MAX; + while (!main_clock->abort) + { + vlc_tick_t deadline; + if (main_clock->pause_date != VLC_TICK_INVALID) + deadline = INT64_MAX; + else + deadline = clock->to_system_locked(clock, system_now, pts, rate); + 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) +{ + VLC_UNUSED(clock); + VLC_UNUSED(delay); +} + + +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; + main_clock->rc = 1; + + main_clock->coeff = 1.0f; + main_clock->rate = 1.0f; + main_clock->offset = VLC_TICK_INVALID; + main_clock->delay = 0; + + main_clock->first_pcr = + clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID); + main_clock->wait_sync_ref = main_clock->last = + clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID); + + main_clock->pause_date = VLC_TICK_INVALID; + main_clock->input_dejitter = DEFAULT_PTS_DELAY; + main_clock->output_dejitter = AOUT_MAX_PTS_ADVANCE * 2; + 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); + main_clock->first_pcr = + clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID); + vlc_mutex_unlock(&main_clock->lock); +} + +void vlc_clock_main_SetFirstPcr(vlc_clock_main_t *main_clock, + vlc_tick_t system_now, vlc_tick_t pts) +{ + vlc_mutex_lock(&main_clock->lock); + if (main_clock->first_pcr.system == VLC_TICK_INVALID) + { + main_clock->first_pcr = clock_point_Create(system_now, pts); + main_clock->wait_sync_ref = + clock_point_Create(VLC_TICK_INVALID, VLC_TICK_INVALID); + } + vlc_mutex_unlock(&main_clock->lock); +} + +void vlc_clock_main_SetInputDejitter(vlc_clock_main_t *main_clock, + vlc_tick_t delay) +{ + vlc_mutex_lock(&main_clock->lock); + main_clock->input_dejitter = delay; + vlc_mutex_unlock(&main_clock->lock); +} + +void vlc_clock_main_ChangePause(vlc_clock_main_t *main_clock, vlc_tick_t now, + bool paused) +{ + vlc_mutex_lock(&main_clock->lock); + assert(paused == (main_clock->pause_date == VLC_TICK_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 + */ + const vlc_tick_t delay = now - main_clock->pause_date; + if (main_clock->offset != VLC_TICK_INVALID) + { + main_clock->last.system += delay; + main_clock->offset += delay; + } + if (main_clock->first_pcr.system != VLC_TICK_INVALID) + main_clock->first_pcr.system += delay; + if (main_clock->wait_sync_ref.system != VLC_TICK_INVALID) + main_clock->wait_sync_ref.system += delay; + main_clock->pause_date = VLC_TICK_INVALID; + vlc_cond_broadcast(&main_clock->cond); + } + vlc_mutex_unlock(&main_clock->lock); +} + +void vlc_clock_main_Delete(vlc_clock_main_t *main_clock) +{ + vlc_mutex_destroy(&main_clock->lock); + vlc_cond_destroy(&main_clock->cond); + assert(main_clock->rc == 1); + free(main_clock); +} + +vlc_tick_t vlc_clock_Update(vlc_clock_t *clock, vlc_tick_t system_now, + vlc_tick_t pts, double rate) +{ + return clock->update(clock, system_now, pts, rate); +} + +void vlc_clock_Reset(vlc_clock_t *clock) +{ + clock->reset(clock); +} + +vlc_tick_t vlc_clock_SetDelay(vlc_clock_t *clock, vlc_tick_t delay) +{ + return clock->set_delay(clock, delay); +} + +vlc_tick_t vlc_clock_ConvertToSystem(vlc_clock_t *clock, vlc_tick_t system_now, + vlc_tick_t pts, double rate) +{ + vlc_clock_main_t *main_clock = clock->owner; + vlc_mutex_lock(&main_clock->lock); + vlc_tick_t system = clock->to_system_locked(clock, system_now, pts, rate); + vlc_mutex_unlock(&main_clock->lock); + return system; +} + +void vlc_clock_ConvertArrayToSystem(vlc_clock_t *clock, vlc_tick_t system_now, + vlc_tick_t *pts_array, size_t pts_count, + double rate) +{ + vlc_clock_main_t *main_clock = clock->owner; + vlc_mutex_lock(&main_clock->lock); + for (size_t i = 0; i < pts_count; ++i) + pts_array[i] = clock->to_system_locked(clock, system_now, pts_array[i], + rate); + vlc_mutex_unlock(&main_clock->lock); +} + +void vlc_clock_SetDejitter(vlc_clock_t *clock, vlc_tick_t delay) +{ + clock->set_dejitter(clock, delay); +} + +static void vlc_clock_set_master_callbacks(vlc_clock_t *clock) +{ + clock->update = vlc_clock_master_update; + clock->reset = vlc_clock_master_reset; + clock->set_delay = vlc_clock_master_set_delay; + clock->set_dejitter = vlc_clock_master_set_dejitter; + clock->to_system_locked = vlc_clock_master_to_system_locked; +} + +static void vlc_clock_set_slave_callbacks(vlc_clock_t *clock) +{ + clock->update = vlc_clock_slave_update; + clock->reset = vlc_clock_slave_reset; + clock->set_delay = vlc_clock_slave_set_delay; + clock->set_dejitter = vlc_clock_slave_set_dejitter; + clock->to_system_locked = vlc_clock_slave_to_system_locked; +} + +vlc_clock_t *vlc_clock_main_CreateMaster(vlc_clock_main_t *main_clock) +{ + vlc_clock_t *clock = malloc(sizeof(vlc_clock_t)); + if (clock == NULL) + return NULL; + + clock->owner = main_clock; + clock->delay = 0; + + vlc_mutex_lock(&main_clock->lock); + if (main_clock->master != NULL) + { + vlc_clock_master_reset(main_clock->master); + vlc_clock_set_slave_callbacks(main_clock->master); + } + vlc_clock_set_master_callbacks(clock); + main_clock->master = clock; + main_clock->rc++; + vlc_mutex_unlock(&main_clock->lock); + + return clock; +} + +vlc_clock_t *vlc_clock_main_CreateSlave(vlc_clock_main_t *main_clock) +{ + vlc_clock_t *clock = malloc(sizeof(vlc_clock_t)); + if (clock == NULL) + return NULL; + + clock->owner = main_clock; + clock->delay = 0; + + vlc_mutex_lock(&main_clock->lock); + vlc_clock_set_slave_callbacks(clock); + main_clock->rc++; + vlc_mutex_unlock(&main_clock->lock); + + return clock; +} + +vlc_clock_t *vlc_clock_CreateSlave(const vlc_clock_t *clock) +{ + return vlc_clock_main_CreateSlave(clock->owner); +} + +void vlc_clock_main_SetMaster(vlc_clock_main_t *main_clock, vlc_clock_t *clock) +{ + vlc_mutex_lock(&main_clock->lock); + if (main_clock->master != NULL) + { + vlc_clock_master_reset(main_clock->master); + vlc_clock_set_slave_callbacks(main_clock->master); + } + vlc_clock_set_master_callbacks(clock); + main_clock->master = clock; + 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; + } + main_clock->rc--; + 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..0088d89ebe --- /dev/null +++ b/src/clock/clock.h @@ -0,0 +1,134 @@ +/***************************************************************************** + * clock.h: Output modules synchronisation clock + ***************************************************************************** + * Copyright (C) 2018-2019 VLC authors, VideoLAN and Videolabs SAS + * + * 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 VLC_CLOCK_H +#define VLC_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_clock); + +/** + * Abort all the pending vlc_clock_Wait + */ +void vlc_clock_main_Abort(vlc_clock_main_t *main_clock); + +/** + * Reset the vlc_clock_main_t + */ +void vlc_clock_main_Reset(vlc_clock_main_t *main_clock); + +void vlc_clock_main_SetFirstPcr(vlc_clock_main_t *main_clock, + vlc_tick_t system_now, vlc_tick_t pts); +void vlc_clock_main_SetInputDejitter(vlc_clock_main_t *main_clock, + vlc_tick_t delay); + +/** + * This function allows changing the pause status. + */ +void vlc_clock_main_ChangePause(vlc_clock_main_t *clock, vlc_tick_t system_now, + bool paused); + +/** + * This function set the allocated interface as the master making the current + * master if any a slave. + */ +void vlc_clock_main_SetMaster(vlc_clock_main_t *main_clock, vlc_clock_t *clock); + +/** + * This function creates a new master vlc_clock_t interface + * + * You must use vlc_clock_Delete to free it. + */ +vlc_clock_t *vlc_clock_main_CreateMaster(vlc_clock_main_t *main_clock); + +/** + * This function creates a new slave vlc_clock_t interface + * + * You must use vlc_clock_Delete to free it. + */ +vlc_clock_t *vlc_clock_main_CreateSlave(vlc_clock_main_t *main_clock); + +/** + * This function creates a new slave vlc_clock_t interface + * + * You must use vlc_clock_Delete to free it. + */ +vlc_clock_t *vlc_clock_CreateSlave(const vlc_clock_t *clock); + +/** + * This function free the resources allocated by vlc_clock*Create*() + */ +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 system_now, + vlc_tick_t pts, double rate); + +/** + * This function resets the clock drift + */ +void vlc_clock_Reset(vlc_clock_t *clock); + +/** + * This functions change the clock delay + * + * It returns the amount of time the clock owner need to wait in order to reach + * the time introduced by the new positive delay. + */ +vlc_tick_t vlc_clock_SetDelay(vlc_clock_t *clock, vlc_tick_t pts_delay); + +/** + * Wait for a timestamp expressed in stream time + */ +int vlc_clock_Wait(vlc_clock_t *clock, vlc_tick_t system_now, vlc_tick_t pts, + double rate, 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 system_now, + vlc_tick_t pts, double rate); + +/** + * This functon converts an array of timestamp from stream to system + */ +void vlc_clock_ConvertArrayToSystem(vlc_clock_t *clock, vlc_tick_t system_now, + vlc_tick_t *pts_array, size_t pts_count, + double rate); + +/** + * This function sets the dejitter delay to absorb the clock jitter + * + * Also used as the maximum delay before the synchro is considered to kick in. + */ +void vlc_clock_SetDejitter(vlc_clock_t *clock, vlc_tick_t delay); + +#endif /*VLC_CLOCK_H*/ _______________________________________________ vlc-commits mailing list vlc-commits@videolan.org https://mailman.videolan.org/listinfo/vlc-commits