diff options
Diffstat (limited to 'src/buffer/clock.c')
| -rw-r--r-- | src/buffer/clock.c | 118 |
1 files changed, 71 insertions, 47 deletions
diff --git a/src/buffer/clock.c b/src/buffer/clock.c index 356ac56..86d6739 100644 --- a/src/buffer/clock.c +++ b/src/buffer/clock.c @@ -1,16 +1,20 @@ -#include <nnwt/thread.h> +#include <al/math.h> +#include <nnwt/time.h> #include "../liana/common.h" #include "clock.h" -#define RUNNING 0.0 -#define PAUSED -DBL_MAX +// clock->tick +#define SET (-HUGE_VAL) -// @TODO: -// Stop throwing away precision before we need to. -// Assume calc_tick_offset() could give a negative result at any point. -// Use a tick offset to set local buffer latency in a way that makes more sense. +// clock->pause +#define RUNNING HUGE_VAL +#define PAUSED (-HUGE_VAL) + +// clock->paused_at +#define NOT_STARTED HUGE_VAL +#define NOT_PAUSED (-HUGE_VAL) void camu_clock_init(struct camu_clock *clock, void (*callback)(void *, u8), void *userdata) { @@ -18,16 +22,16 @@ void camu_clock_init(struct camu_clock *clock, void (*callback)(void *, u8), voi clock->userdata = userdata; } -static f64 calc_tick_offset(f64 tick, u64 now, u64 target) +static inline f64 calc_tick_offset(f64 tick, u64 now, u64 target) { if (now > target) { - return tick - (now - target) / 1000000.0; + return tick - ((now - target) / 1000000.0); } else { - return tick + (target - now) / 1000000.0; + return tick + ((target - now) / 1000000.0); } } -static void offset_tick(struct camu_clock *clock, f64 diff) +static inline void offset_tick(struct camu_clock *clock, f64 diff) { diff += atomic_load(f64)(&clock->tick, AL_ATOMIC_ACQUIRE); atomic_store(f64)(&clock->tick, diff, AL_ATOMIC_RELEASE); @@ -36,20 +40,27 @@ static void offset_tick(struct camu_clock *clock, f64 diff) void camu_clock_set(struct camu_clock *clock, f64 base) { clock->base = base; - atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED); + atomic_store(f64)(&clock->tick, SET, AL_ATOMIC_RELAXED); atomic_store(f64)(&clock->pause, PAUSED, AL_ATOMIC_RELAXED); - clock->paused_at = 0.0; + atomic_store(bool)(&clock->external_pause, false, AL_ATOMIC_RELAXED); + clock->paused_at = NOT_STARTED; atomic_store(f64)(&clock->last_pts, base, AL_ATOMIC_RELAXED); } +// Setting an offset will unconditionally break sync. Only used in local mode. +void camu_clock_offset(struct camu_clock *clock, f64 offset) +{ + clock->offset = offset; +} + void camu_clock_seek(struct camu_clock *clock, f64 base, u64 target) { clock->base = base; atomic_store(f64)(&clock->last_pts, base, AL_ATOMIC_RELAXED); - if (clock->paused_at == -1.0) { + if (clock->paused_at == NOT_PAUSED) { // We are safe to directly edit the tick here. if (target == 0) { - atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED); + atomic_store(f64)(&clock->tick, SET, AL_ATOMIC_RELAXED); } else { f64 tick = nn_get_tick(); tick = calc_tick_offset(tick, nn_get_timestamp(), target); @@ -63,62 +74,73 @@ void camu_clock_seek(struct camu_clock *clock, f64 base, u64 target) // Likely confusing an entry's buffers and causing excessive catchup or delay. // We mitigate this sink-side by immediately switching to a potential target in // CLIENT_REMOVE_BUFFERS, which is always called before an entry is seeked. - clock->paused_at = 0.0; + clock->paused_at = NOT_STARTED; } } -// Seek to 0 but include the time it took to perform the seek. -void camu_clock_loop(struct camu_clock *clock, f64 last_pts) -{ - clock->base = 0.0; - offset_tick(clock, last_pts - clock->base); -} - // If late, pause immediately and on resume() there will be a long delay. -void camu_clock_pause(struct camu_clock *clock, u64 target) +bool camu_clock_pause(struct camu_clock *clock, u64 target) { - al_assert(clock->paused_at == -1.0); + al_assert(clock->paused_at == NOT_PAUSED); - f64 now = nn_get_tick(), tick = now; + f64 tick = atomic_load(f64)(&clock->tick, AL_ATOMIC_RELAXED); + + f64 current = nn_get_tick(), pause = current; if (target > 0) { - tick = calc_tick_offset(tick, nn_get_timestamp(), target); + pause = calc_tick_offset(pause, nn_get_timestamp(), target); } - atomic_store(f64)(&clock->pause, (tick > now) ? tick : -tick, AL_ATOMIC_RELAXED); + clock->paused_at = pause; - clock->paused_at = tick; + if ((tick != SET && tick >= current) || pause <= current) { // Immediate pause. + // This could be -0.0, meaning 0.0 has to be considered paused by get_pts(). + atomic_store(f64)(&clock->pause, -pause, AL_ATOMIC_RELAXED); + return true; + } else { // Arm for pause. + // This can never be 0.0. + al_assert(pause != 0.0); + atomic_store(f64)(&clock->pause, pause, AL_ATOMIC_RELAXED); + return false; + } +} + +void camu_clock_external_pause(struct camu_clock *clock) +{ + atomic_store(bool)(&clock->external_pause, true, AL_ATOMIC_RELAXED); } // If late, this will be a catchup. void camu_clock_resume(struct camu_clock *clock, u64 target) { - al_assert(clock->paused_at != -1.0); + al_assert(clock->paused_at != NOT_PAUSED); + + atomic_store(bool)(&clock->external_pause, false, AL_ATOMIC_RELAXED); f64 tick = nn_get_tick(); if (target > 0) { tick = calc_tick_offset(tick, nn_get_timestamp(), target); } - if (clock->paused_at == 0.0) { + if (clock->paused_at == NOT_STARTED) { if (target == 0) { // target = 0 can never be synced. - atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED); + atomic_store(f64)(&clock->tick, SET, AL_ATOMIC_RELAXED); } else { atomic_store(f64)(&clock->tick, tick, AL_ATOMIC_RELAXED); } } else { f64 pause = atomic_load(f64)(&clock->pause, AL_ATOMIC_ACQUIRE); - offset_tick(clock, tick - ((pause != PAUSED) ? -pause : clock->paused_at)); + al_assert(pause != PAUSED); + /* Using pause instead of clock->paused_at is incorrect for sync. + if (pause < 0.0) pause = -pause; + offset_tick(clock, tick - pause); + */ + offset_tick(clock, tick - clock->paused_at); } - atomic_store(f64)(&clock->pause, RUNNING, AL_ATOMIC_RELEASE); - - clock->paused_at = -1.0; -} + clock->paused_at = NOT_PAUSED; -bool camu_clock_is_paused(struct camu_clock *clock) -{ - return atomic_load(f64)(&clock->pause, AL_ATOMIC_RELAXED) < 0.0; + atomic_store(f64)(&clock->pause, RUNNING, AL_ATOMIC_RELEASE); } f64 camu_clock_get_base_pts(struct camu_clock *clock) @@ -126,26 +148,28 @@ f64 camu_clock_get_base_pts(struct camu_clock *clock) return clock->base; } -f64 camu_clock_get_pts(struct camu_clock *clock, f64 latency, bool allow_set) +f64 camu_clock_get_pts(struct camu_clock *clock, f64 latency, bool allow_set, bool *armed_for_pause) { + // Treat pause = 0.0 or -0.0 as paused. f64 pause = atomic_load(f64)(&clock->pause, AL_ATOMIC_RELAXED); - if (pause < 0.0) return CAMU_PTS_PAUSED; + if (pause <= 0.0) return CAMU_PTS_PAUSED; f64 current = nn_get_tick(); f64 tick = atomic_load(f64)(&clock->tick, AL_ATOMIC_RELAXED); - if (tick == -1.0) { + if (tick == SET) { if (allow_set) { - tick = atomic_compare_and_swap(f64)(&clock->tick, -1.0, current); - if (tick == -1.0) tick = current; + tick = atomic_compare_and_swap(f64)(&clock->tick, SET, current); + if (tick == SET) tick = current; } else { return CAMU_PTS_PAUSED; } } - f64 pts = clock->base + (current - tick); + f64 pts = (clock->base - clock->offset) + (current - tick); bool signal_pause = false; - if (pause > 0.0 && current > pause) { // pause > 0.0 = RUNNING or armed for pause. + *armed_for_pause = pause != RUNNING || atomic_load(bool)(&clock->external_pause, AL_ATOMIC_RELAXED); + if (pause > 0.0 && current > pause) { f64 paused_at = -current; pause = atomic_compare_and_swap(f64)(&clock->pause, pause, paused_at); if (pause != paused_at) { |