summaryrefslogtreecommitdiff
path: root/src/buffer/clock.c
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2026-04-13 17:36:54 -0400
committerAndrew Opalach <andrew@akon.city> 2026-04-13 17:36:54 -0400
commit4ceadc74f0086168fbc576032ba3e6f23af16e39 (patch)
treea542703d78f2b29ac1a756fdceec78e40860bab0 /src/buffer/clock.c
parent77b54c35bf9587450cd636e0d7df37e190e28bfb (diff)
downloadcamu-4ceadc74f0086168fbc576032ba3e6f23af16e39.tar.gz
camu-4ceadc74f0086168fbc576032ba3e6f23af16e39.tar.bz2
camu-4ceadc74f0086168fbc576032ba3e6f23af16e39.zip
Changes that went uncommitted for too long
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/buffer/clock.c')
-rw-r--r--src/buffer/clock.c118
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) {