summaryrefslogtreecommitdiff
path: root/src/buffer
diff options
context:
space:
mode:
Diffstat (limited to 'src/buffer')
-rw-r--r--src/buffer/audio.c11
-rw-r--r--src/buffer/clock.c77
-rw-r--r--src/buffer/clock.h5
-rw-r--r--src/buffer/video.c14
-rw-r--r--src/buffer/video.h2
5 files changed, 60 insertions, 49 deletions
diff --git a/src/buffer/audio.c b/src/buffer/audio.c
index a740111..998662b 100644
--- a/src/buffer/audio.c
+++ b/src/buffer/audio.c
@@ -10,12 +10,12 @@
#include "common.h"
#include "common_internal.h"
-#define BUFFER_SIZE 6.0
+#define BUFFER_SIZE 7.0
#define BUFFER_MARK_MIN 3.25 // Must be a most half of the buffer size.
#define BUFFER_MARK_BUFFERED 0.35
#ifdef CAMU_AUDIO_BUFFER_FADE
-#define FADE_STEP(fmt) (2.50f / (fmt)->sample_rate)
+#define FADE_STEP(fmt, down) ((down ? -2.5f : 2.5f) / (fmt)->sample_rate)
#define FADE_MIN 0.175f
#endif
@@ -436,10 +436,9 @@ ptrdiff_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, ptrdif
f32 step = 0.f;
if (buf->pause == PAUSE_FADING || buf->fade.volume != buf->volume.user) {
- step = FADE_STEP(fmt);
- step *= (buf->pause == PAUSE_FADING || buf->fade.volume > buf->volume.user) ? -1.f : 1.f;
- if (buf->volume.user > 1.f) step *= buf->volume.user;
- step *= MAX(buf->fade.volume, FADE_MIN);
+ bool down = (buf->pause == PAUSE_FADING || buf->volume.user < buf->fade.volume);
+ step = FADE_STEP(fmt, down) * MAX(buf->fade.volume, FADE_MIN) *
+ ((buf->volume.user > 1.f) ? buf->volume.user : 1.f); // Move faster at >100%.
}
if (step != 0.f || buf->fade.volume != 1.f) {
buf->fade.volume = apply_volume(data, req, fmt, buf->fade.volume, buf->volume.user, step);
diff --git a/src/buffer/clock.c b/src/buffer/clock.c
index 732185e..356ac56 100644
--- a/src/buffer/clock.c
+++ b/src/buffer/clock.c
@@ -7,6 +7,11 @@
#define RUNNING 0.0
#define PAUSED -DBL_MAX
+// @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.
+
void camu_clock_init(struct camu_clock *clock, void (*callback)(void *, u8), void *userdata)
{
clock->callback = callback;
@@ -22,28 +27,33 @@ static f64 calc_tick_offset(f64 tick, u64 now, u64 target)
}
}
+static 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);
+}
+
void camu_clock_set(struct camu_clock *clock, f64 base)
{
clock->base = base;
- clock->offset = 0.0;
- al_atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED);
- al_atomic_store(f64)(&clock->pause, PAUSED, AL_ATOMIC_RELAXED);
+ atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED);
+ atomic_store(f64)(&clock->pause, PAUSED, AL_ATOMIC_RELAXED);
clock->paused_at = 0.0;
- al_atomic_store(f64)(&clock->last_pts, base, AL_ATOMIC_RELAXED);
+ atomic_store(f64)(&clock->last_pts, base, AL_ATOMIC_RELAXED);
}
void camu_clock_seek(struct camu_clock *clock, f64 base, u64 target)
{
clock->base = base;
- clock->offset = 0.0;
- al_atomic_store(f64)(&clock->last_pts, base, AL_ATOMIC_RELAXED);
+ atomic_store(f64)(&clock->last_pts, base, AL_ATOMIC_RELAXED);
if (clock->paused_at == -1.0) {
// We are safe to directly edit the tick here.
if (target == 0) {
- al_atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED);
+ atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED);
} else {
- f64 tick = calc_tick_offset(nn_get_tick(), nn_get_timestamp(), target);
- al_atomic_store(f64)(&clock->tick, tick, AL_ATOMIC_RELAXED);
+ f64 tick = nn_get_tick();
+ tick = calc_tick_offset(tick, nn_get_timestamp(), target);
+ atomic_store(f64)(&clock->tick, tick, AL_ATOMIC_RELAXED);
}
} else {
// Don't touch clock->pause here for the sake of sync. This means,
@@ -57,28 +67,29 @@ void camu_clock_seek(struct camu_clock *clock, f64 base, u64 target)
}
}
+// Seek to 0 but include the time it took to perform the seek.
void camu_clock_loop(struct camu_clock *clock, f64 last_pts)
{
- // Seek to 0 but include the time it took to perform the seek.
- clock->offset += last_pts - clock->base;
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)
{
al_assert(clock->paused_at == -1.0);
- f64 tick = nn_get_tick();
+ f64 now = nn_get_tick(), tick = now;
if (target > 0) {
tick = calc_tick_offset(tick, nn_get_timestamp(), target);
- al_atomic_store(f64)(&clock->pause, tick, AL_ATOMIC_RELAXED);
- } else {
- al_atomic_store(f64)(&clock->pause, PAUSED, AL_ATOMIC_RELAXED);
}
+ atomic_store(f64)(&clock->pause, (tick > now) ? tick : -tick, AL_ATOMIC_RELAXED);
+
clock->paused_at = tick;
}
+// If late, this will be a catchup.
void camu_clock_resume(struct camu_clock *clock, u64 target)
{
al_assert(clock->paused_at != -1.0);
@@ -91,22 +102,23 @@ void camu_clock_resume(struct camu_clock *clock, u64 target)
if (clock->paused_at == 0.0) {
if (target == 0) {
// target = 0 can never be synced.
- al_atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED);
+ atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED);
} else {
- al_atomic_store(f64)(&clock->tick, tick, AL_ATOMIC_RELAXED);
+ atomic_store(f64)(&clock->tick, tick, AL_ATOMIC_RELAXED);
}
} else {
- clock->offset += tick - clock->paused_at;
+ f64 pause = atomic_load(f64)(&clock->pause, AL_ATOMIC_ACQUIRE);
+ offset_tick(clock, tick - ((pause != PAUSED) ? -pause : clock->paused_at));
}
- al_atomic_store(f64)(&clock->pause, RUNNING, AL_ATOMIC_RELAXED);
+ atomic_store(f64)(&clock->pause, RUNNING, AL_ATOMIC_RELEASE);
clock->paused_at = -1.0;
}
bool camu_clock_is_paused(struct camu_clock *clock)
{
- return al_atomic_load(f64)(&clock->pause, AL_ATOMIC_RELAXED) == PAUSED;
+ return atomic_load(f64)(&clock->pause, AL_ATOMIC_RELAXED) < 0.0;
}
f64 camu_clock_get_base_pts(struct camu_clock *clock)
@@ -116,35 +128,34 @@ f64 camu_clock_get_base_pts(struct camu_clock *clock)
f64 camu_clock_get_pts(struct camu_clock *clock, f64 latency, bool allow_set)
{
- f64 pause = al_atomic_load(f64)(&clock->pause, AL_ATOMIC_ACQUIRE);
- if (pause == PAUSED) return CAMU_PTS_PAUSED;
+ f64 pause = atomic_load(f64)(&clock->pause, AL_ATOMIC_RELAXED);
+ if (pause < 0.0) return CAMU_PTS_PAUSED;
f64 current = nn_get_tick();
-
- f64 tick = al_atomic_load(f64)(&clock->tick, AL_ATOMIC_RELAXED);
+ f64 tick = atomic_load(f64)(&clock->tick, AL_ATOMIC_RELAXED);
if (tick == -1.0) {
if (allow_set) {
- tick = al_atomic_compare_and_swap(f64)(&clock->tick, -1.0, current);
+ tick = atomic_compare_and_swap(f64)(&clock->tick, -1.0, current);
if (tick == -1.0) tick = current;
} else {
return CAMU_PTS_PAUSED;
}
}
+ f64 pts = clock->base + (current - tick);
+
bool signal_pause = false;
- if (pause > 0.0 && current > pause) { // pause > 0.0 means running or pause armed.
- current = pause;
- pause = al_atomic_compare_and_swap(f64)(&clock->pause, pause, PAUSED);
- if (pause != PAUSED) {
+ if (pause > 0.0 && current > pause) { // pause > 0.0 = RUNNING or armed for pause.
+ f64 paused_at = -current;
+ pause = atomic_compare_and_swap(f64)(&clock->pause, pause, paused_at);
+ if (pause != paused_at) {
clock->callback(clock->userdata, CAMU_CLOCK_PAUSED);
signal_pause = true;
}
}
- f64 pts = (clock->base - clock->offset) + (current - tick);
-
if (allow_set) {
- al_atomic_store(f64)(&clock->last_pts, pts, AL_ATOMIC_RELAXED);
+ atomic_store(f64)(&clock->last_pts, pts, AL_ATOMIC_RELAXED);
}
return signal_pause ? CAMU_PTS_SIGNAL_PAUSE : pts + latency;
@@ -152,5 +163,5 @@ f64 camu_clock_get_pts(struct camu_clock *clock, f64 latency, bool allow_set)
f64 camu_clock_get_last_pts(struct camu_clock *clock)
{
- return al_atomic_load(f64)(&clock->last_pts, AL_ATOMIC_RELAXED);
+ return atomic_load(f64)(&clock->last_pts, AL_ATOMIC_RELAXED);
}
diff --git a/src/buffer/clock.h b/src/buffer/clock.h
index 3209119..8324b6a 100644
--- a/src/buffer/clock.h
+++ b/src/buffer/clock.h
@@ -23,8 +23,8 @@
// Next/Prev:
// - User input -> swap
-#define CAMU_PTS_PAUSED 0xffffffffffffffff
-#define CAMU_PTS_SIGNAL_PAUSE 0x7fffffffffffffff
+#define CAMU_PTS_PAUSED ((f64)0xffffffffffffffff)
+#define CAMU_PTS_SIGNAL_PAUSE ((f64)0x7fffffffffffffff)
#define CAMU_PTS_CONSIDER_PAUSED(pts) (pts == CAMU_PTS_PAUSED || pts == CAMU_PTS_SIGNAL_PAUSE)
enum {
@@ -33,7 +33,6 @@ enum {
struct camu_clock {
f64 base;
- f64 offset;
atomic(f64) tick;
atomic(f64) pause;
f64 paused_at;
diff --git a/src/buffer/video.c b/src/buffer/video.c
index e5c38a6..5011e9d 100644
--- a/src/buffer/video.c
+++ b/src/buffer/video.c
@@ -30,6 +30,7 @@ bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *cl
buf->single_frame = true;
buf->queue = NULL;
buf->buffered = false;
+ buf->weighted_first_read = false;
al_atomic_store(u8)(&buf->flow, FLOWING, AL_ATOMIC_RELAXED);
#ifdef CAMU_SCREEN_THREADED
al_atomic_store(u8)(&buf->ref, 0, AL_ATOMIC_RELAXED);
@@ -63,13 +64,13 @@ bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_code
} else {
al_assert(frame_rate.den > 0);
buf->avg_frame_duration = av_q2d(av_inv_q(frame_rate));
+ buf->weighted_first_read = true;
log_info("Stream: %s (%ux%u) VIDEO %.3ffps.", format_name, fmt->width, fmt->height, av_q2d(frame_rate));
}
break;
}
#endif
}
- buf->weighted_read = !buf->single_frame;
struct camu_video_format *in = &buf->fmt.in;
struct camu_video_format *req = &buf->fmt.req;
camu_video_format_copy(in, fmt);
@@ -233,7 +234,8 @@ bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out, bool *weig
f64 base_pts = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE);
if (!buf->single_frame) {
- f64 pts = camu_clock_get_pts(buf->clock, buf->latency, !buf->weighted_read);
+ bool set_clock = !buf->weighted_first_read;
+ f64 pts = camu_clock_get_pts(buf->clock, buf->latency, set_clock);
if (!CAMU_PTS_CONSIDER_PAUSED(pts) && pts > base_pts) {
base_pts = pts;
}
@@ -268,15 +270,15 @@ bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out, bool *weig
}
if (ret == CAMU_QUEUE_OK) {
- if (UNLIKELY(buf->weighted_read)) {
- *weighted = buf->weighted_read;
- buf->weighted_read = false;
- }
// Only updating buf->pts on QUEUE_OK affects the behavior when we fall completely behind.
// Setting it here will result in laggily displaying out of date frames as they come in.
// Setting it on every read() will cause the video to freeze.
// This option ignores sync but is easier for the user to understand what's happening.
al_atomic_store(f64)(&buf->pts, base_pts, AL_ATOMIC_RELEASE);
+ if (UNLIKELY(buf->weighted_first_read)) {
+ *weighted = true;
+ buf->weighted_first_read = false;
+ }
} else if (ret == CAMU_QUEUE_MORE) {
log_trace("Underrun.");
}
diff --git a/src/buffer/video.h b/src/buffer/video.h
index fdd7d0b..20644bc 100644
--- a/src/buffer/video.h
+++ b/src/buffer/video.h
@@ -30,7 +30,7 @@ struct camu_video_buffer {
// Need to manually trigger EOF in read().
bool buffered_with_one_frame;
// Flush the renderer on this read and don't allow it to set the clock.
- bool weighted_read;
+ bool weighted_first_read;
atomic(u8) flow;