summaryrefslogtreecommitdiff
path: root/src/buffer
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2025-03-15 20:40:38 -0400
committerAndrew Opalach <andrew@akon.city> 2025-03-15 20:40:38 -0400
commit846bd3d1b7bcec3653aa9d662f559235c4689e1b (patch)
tree56906544460079681f46d23d48d76f1a8242ea75 /src/buffer
parent46d5660a2582323e1c9c33a56694c322bc21b7bc (diff)
downloadcamu-846bd3d1b7bcec3653aa9d662f559235c4689e1b.tar.gz
camu-846bd3d1b7bcec3653aa9d662f559235c4689e1b.tar.bz2
camu-846bd3d1b7bcec3653aa9d662f559235c4689e1b.zip
Sink refactor wip
Dangling target won't stay around. I have a couple of ideas about how to massively simplify the handling of ended entries. Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/buffer')
-rw-r--r--src/buffer/clock.c18
-rw-r--r--src/buffer/clock.h2
-rw-r--r--src/buffer/meson.build6
-rw-r--r--src/buffer/video.c82
-rw-r--r--src/buffer/video.h8
-rw-r--r--src/buffer/video_null.h98
6 files changed, 149 insertions, 65 deletions
diff --git a/src/buffer/clock.c b/src/buffer/clock.c
index 1ca2a49..a1a1eab 100644
--- a/src/buffer/clock.c
+++ b/src/buffer/clock.c
@@ -28,19 +28,20 @@ void camu_clock_set(struct camu_clock *clock, f64 base)
al_atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED);
al_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);
}
void camu_clock_seek(struct camu_clock *clock, f64 base, u64 target)
{
clock->base = base;
clock->offset = 0.0;
- f64 tick = nn_get_tick();
+ al_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);
} else {
- tick = calc_tick_offset(tick, nn_get_timestamp(), target);
+ f64 tick = calc_tick_offset(nn_get_tick(), nn_get_timestamp(), target);
al_atomic_store(f64)(&clock->tick, tick, AL_ATOMIC_RELAXED);
}
} else {
@@ -136,5 +137,16 @@ f64 camu_clock_get_pts(struct camu_clock *clock, f64 offset, bool allow_set)
}
}
- return (clock->base - clock->offset) + (current - tick) + offset;
+ f64 pts = (clock->base - clock->offset) + (current - tick);
+
+ if (allow_set) {
+ al_atomic_store(f64)(&clock->last_pts, pts, AL_ATOMIC_RELAXED);
+ }
+
+ return pts + offset;
+}
+
+f64 camu_clock_get_last_pts(struct camu_clock *clock)
+{
+ return al_atomic_load(f64)(&clock->last_pts, AL_ATOMIC_RELAXED);
}
diff --git a/src/buffer/clock.h b/src/buffer/clock.h
index b312a1c..57bdb7d 100644
--- a/src/buffer/clock.h
+++ b/src/buffer/clock.h
@@ -33,6 +33,7 @@ struct camu_clock {
atomic(f64) tick;
atomic(f64) pause;
f64 paused_at;
+ atomic(f64) last_pts;
void (*callback)(void *, u8);
void *userdata;
};
@@ -49,3 +50,4 @@ bool camu_clock_is_paused(struct camu_clock *clock);
f64 camu_clock_get_base_pts(struct camu_clock *clock);
f64 camu_clock_get_pts(struct camu_clock *clock, f64 offset, bool allow_set);
+f64 camu_clock_get_last_pts(struct camu_clock *clock);
diff --git a/src/buffer/meson.build b/src/buffer/meson.build
index 6301206..7eef600 100644
--- a/src/buffer/meson.build
+++ b/src/buffer/meson.build
@@ -1,9 +1,5 @@
buffer_src = ['audio.c', 'clock.c', 'peak_buffer.c']
-buffer_deps = []
-buffer_args = []
-
if not no_video
buffer_src += ['video.c']
endif
-
-buffer = declare_dependency(sources: buffer_src, dependencies: buffer_deps, compile_args: buffer_args)
+buffer = declare_dependency(sources: buffer_src)
diff --git a/src/buffer/video.c b/src/buffer/video.c
index ab1503a..ed56ff8 100644
--- a/src/buffer/video.c
+++ b/src/buffer/video.c
@@ -1,3 +1,4 @@
+#define AL_LOG_SECTION "video_buffer"
#include <al/log.h>
#include "video.h"
@@ -22,7 +23,6 @@ bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *cl
buf->clock = clock;
buf->latency = 0.0;
al_atomic_store(f64)(&buf->pts, -1.0, AL_ATOMIC_RELAXED);
- buf->last_pts = -1.0;
// The least confusing behavior for single_frame is that it can't be
// set if the buffer is empty.
buf->single_frame = false;
@@ -46,73 +46,54 @@ bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_code
buf->queue = renderer->create_queue(renderer);
buf->queue->buf = buf;
struct camu_video_format *fmt = &buf->stream->video.fmt;
+ const char *format_name = camu_pixel_format_name(fmt->format);
switch (stream->mode) {
case CAMU_NORMAL: {
- camu_video_format_copy(&buf->fmt.in, fmt);
-
buf->single_frame = true;
buf->avg_frame_duration = 0.0;
-
- const char *format_name = camu_pixel_format_name(fmt->format);
- if (!format_name) format_name = "unknown";
-
- al_log_info("video_buffer", "Stream: %s (%ux%u) %s.",
- format_name, fmt->width, fmt->height, "IMAGE");
-
+ info("Stream: %s (%ux%u) IMAGE.", format_name, fmt->width, fmt->height);
break;
}
#ifdef CAMU_HAVE_FFMPEG
case CAMU_FFMPEG_COMPAT: {
- AVCodecParameters *codecpar = stream->av.stream->codecpar;
AVRational frame_rate = stream->av.stream->avg_frame_rate;
-
- fmt->width = (u32)codecpar->width;
- fmt->height = (u32)codecpar->height;
- fmt->format = codecpar->format;
- camu_video_format_copy(&buf->fmt.in, fmt);
-
buf->single_frame = stream->duration == 0 || frame_rate.den == 0;
buf->avg_frame_duration = frame_rate.den > 0 ? av_q2d(av_inv_q(frame_rate)) : 0.0;
-
- const char *format_name = av_get_pix_fmt_name(fmt->format);
if (buf->single_frame) {
- al_log_info("video_buffer", "Stream: %s (%ux%u) %s.",
- format_name, fmt->width, fmt->height, "IMAGE");
+ info("Stream: %s (%ux%u) IMAGE.", format_name, fmt->width, fmt->height);
} else {
- al_log_info("video_buffer", "Stream: %s (%ux%u) %s %.3ffps.",
- format_name, fmt->width, fmt->height, "VIDEO", av_q2d(frame_rate));
+ info("Stream: %s (%ux%u) VIDEO %.3ffps.", format_name, fmt->width, fmt->height, av_q2d(frame_rate));
}
-
break;
}
#endif
}
-#ifdef CAMU_VIDEO_BUFFER_FORCE_SCALER
-#ifdef CAMU_HAVE_FFMPEG
+ struct camu_video_format *in = &buf->fmt.in;
+ struct camu_video_format *req = &buf->fmt.req;
+ camu_video_format_copy(in, fmt);
+#ifndef CAMU_VIDEO_BUFFER_FORCE_SCALER
+ camu_video_format_copy(req, in);
+#else
if (fmt->width > 0 && fmt->height > 0 &&
(fmt->format != CAMU_PIXEL_FORMAT_RGBA && fmt->format != CAMU_PIXEL_FORMAT_RGB)) {
buf->fmt.scaler_needed = true;
- buf->fmt.req.width = fmt->width;
- buf->fmt.req.height = fmt->height;
- buf->fmt.req.format = CAMU_PIXEL_FORMAT_RGBA;
+#ifndef CAMU_HAVE_FFMPEG
+ return false;
+#endif
+ req->width = fmt->width;
+ req->height = fmt->height;
+ req->format = CAMU_PIXEL_FORMAT_RGBA;
buf->scaler = camu_ff_scaler_create();
- if (buf->scaler->init(buf->scaler, &buf->fmt)) {
- const char *format_name = av_get_pix_fmt_name(buf->fmt.req.format);
- al_log_info("video_buffer", "Scaling to: %s (%ux%u).",
- format_name, buf->fmt.req.width, buf->fmt.req.height);
- } else {
+ if (!buf->scaler->init(buf->scaler, &buf->fmt)) {
// Scaler will be freed in video_buffer_free().
return false;
}
+ const char *req_format_name = camu_pixel_format_name(req->format);
+ info("Scaling to: %s (%ux%u).", req_format_name, req->width, req->height);
} else {
buf->fmt.scaler_needed = false;
- camu_video_format_copy(&buf->fmt.req, &buf->fmt.in);
+ camu_video_format_copy(req, in);
}
-#else
- return false;
-#endif
-#else
- camu_video_format_copy(&buf->fmt.req, &buf->fmt.in);
#endif
return true;
}
@@ -138,7 +119,6 @@ static void after_push_internal(struct camu_video_buffer *buf)
{
s32 count = buf->queue->count(buf->queue);
f64 have = count * buf->avg_frame_duration;
-
if (!buf->buffered && (buf->single_frame || have >= BUFFER_MARK_BUFFERED)) {
// Preserve order of: set flow -> flush -> callback, for single frames.
if (buf->single_frame) {
@@ -147,12 +127,10 @@ static void after_push_internal(struct camu_video_buffer *buf)
}
buf->buffered = true;
buf->buffered_with_one_frame = count == 1;
- al_log_debug("video_buffer", "Buffered (mark: %.2fs).", have);
+ debug("Buffered (mark: %.2fs).", have);
buf->callback(buf->userdata, CAMU_BUFFER_BUFFERED);
- }
-
- if (have >= BUFFER_MARK_RESET) {
- al_log_warn("video_buffer", "Buffer overflow, resetting.");
+ } else if (have >= BUFFER_MARK_RESET) {
+ warn("Buffer overflow, resetting.");
buf->queue->reset(buf->queue);
} else if (have >= BUFFER_MARK_HIGH) {
buf->callback(buf->userdata, CAMU_BUFFER_CORK);
@@ -167,7 +145,6 @@ static bool push_av_frame_internal(struct camu_video_buffer *buf, AVFrame *frame
f64 duration = camu_ff_frame_duration(frame) * av_q2d(stream->time_base);
f64 base_pts = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE);
if (!buf->single_frame && frame_is_late(buf->clock, base_pts, pts, duration)) {
- al_log_info("video_buffer", "Discarding late frame.");
return false;
}
if (base_pts == -1.0) al_atomic_store(f64)(&buf->pts, pts, AL_ATOMIC_RELEASE);
@@ -223,14 +200,14 @@ void camu_video_buffer_push_subtitle(struct camu_video_buffer *buf, struct camu_
// flush() always comes from the same thread as push().
void camu_video_buffer_flush(struct camu_video_buffer *buf)
{
- al_log_debug("video_buffer", "Flush requested.");
+ debug("Flush requested.");
al_atomic_store(u8)(&buf->flow, FLUSHED, AL_ATOMIC_RELAXED);
buf->queue->flush(buf->queue);
if (!buf->buffered) {
s32 count = buf->queue->count(buf->queue);
buf->buffered = true;
buf->buffered_with_one_frame = count == 1;
- al_log_debug("video_buffer", "Buffered (flush).");
+ debug("Buffered (flush).");
buf->callback(buf->userdata, CAMU_BUFFER_BUFFERED);
}
}
@@ -238,8 +215,7 @@ void camu_video_buffer_flush(struct camu_video_buffer *buf)
// Not thread-safe, must be called while the buffer is not being read from or written to.
void camu_video_buffer_reset(struct camu_video_buffer *buf)
{
- buf->last_pts = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE);
- al_atomic_store(f64)(&buf->pts, -1.0, AL_ATOMIC_RELEASE);
+ al_atomic_store(f64)(&buf->pts, -1.0, AL_ATOMIC_RELAXED);
if (buf->queue) buf->queue->reset(buf->queue);
buf->buffered = false;
al_atomic_store(u8)(&buf->flow, FLOWING, AL_ATOMIC_RELAXED);
@@ -269,7 +245,7 @@ bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out, bool *weig
// In our code that 1 frame buffer might be a video, in which case that behavior is erroneous.
bool eof = ret == CAMU_QUEUE_EOF || (buf->buffered_with_one_frame && ret == CAMU_QUEUE_OK);
if (flow == FLUSHED && eof) {
- al_log_debug("video_buffer", "Flushed.");
+ debug("Flushed.");
buf->callback(buf->userdata, CAMU_BUFFER_EOF);
al_atomic_store(u8)(&buf->flow, SIGNALED, AL_ATOMIC_RELEASE);
} else if (flow == FLOWING) {
@@ -284,7 +260,7 @@ bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out, bool *weig
*weighted = buf->weighted_read;
buf->weighted_read = false;
} else if (ret == CAMU_QUEUE_MORE) {
- al_log_warn("video_buffer", "Underrun.");
+ trace("Underrun.");
}
return ret == CAMU_QUEUE_OK || ret == CAMU_QUEUE_MORE;
diff --git a/src/buffer/video.h b/src/buffer/video.h
index c587af1..401fa48 100644
--- a/src/buffer/video.h
+++ b/src/buffer/video.h
@@ -8,8 +8,8 @@
#include "../render/renderer.h"
#include "../screen/view.h"
-#include "clock.h"
#include "frame_queue.h"
+#include "clock.h"
struct camu_video_buffer {
struct camu_codec_stream *stream;
@@ -17,7 +17,6 @@ struct camu_video_buffer {
struct camu_clock *clock;
atomic(f64) pts;
f64 latency;
- f64 last_pts;
bool single_frame;
f64 avg_frame_duration;
@@ -36,12 +35,13 @@ struct camu_video_buffer {
atomic(u8) flow;
- struct camu_view view;
-
#ifdef CAMU_SCREEN_THREADED
atomic(u8) ref;
#endif
+ // Previous view, set from screen.
+ struct camu_view view;
+
void (*callback)(void *, u8);
void *userdata;
};
diff --git a/src/buffer/video_null.h b/src/buffer/video_null.h
new file mode 100644
index 0000000..69da85f
--- /dev/null
+++ b/src/buffer/video_null.h
@@ -0,0 +1,98 @@
+#pragma once
+
+#include <al/atomic.h>
+
+#include "../codec/codec.h"
+
+#include "clock.h"
+
+struct camu_video_buffer {
+ f64 avg_frame_duration;
+#ifdef CAMU_SCREEN_THREADED
+ atomic(u8) ref;
+#endif
+ void (*callback)(void *, u8);
+ void *userdata;
+};
+
+struct camu_renderer {
+ void (*add_font)(struct camu_renderer *, struct camu_codec_stream *);
+ u32 (*get_latency)(struct camu_renderer *);
+};
+
+AL_UNUSED_FUNCTION_PUSH
+
+static bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *clock)
+{
+ (void)clock;
+ buf->avg_frame_duration = 0.0;
+#ifdef CAMU_SCREEN_THREADED
+ al_atomic_store(u8)(&buf->ref, 0, AL_ATOMIC_RELAXED);
+#endif
+ return true;
+}
+
+static bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_codec_stream *stream,
+ struct camu_renderer *renderer)
+{
+ (void)buf;
+ (void)stream;
+ (void)renderer;
+ return true;
+}
+
+static bool camu_video_buffer_configure_subtitles(struct camu_video_buffer *buf, struct camu_codec_stream *stream)
+{
+ (void)buf;
+ (void)stream;
+ return true;
+}
+
+static void camu_video_buffer_set_latency(struct camu_video_buffer *buf, s32 frames)
+{
+ (void)buf;
+ (void)frames;
+}
+
+static bool camu_video_buffer_is_single_frame(struct camu_video_buffer *buf)
+{
+ (void)buf;
+ return false;
+}
+
+static void camu_video_buffer_push(struct camu_video_buffer *buf, struct camu_codec_frame *frame)
+{
+ (void)buf;
+ camu_codec_frame_discard(frame);
+}
+
+static void camu_video_buffer_push_subtitle(struct camu_video_buffer *buf, struct camu_codec_packet *packet)
+{
+ (void)buf;
+ (void)packet;
+}
+
+static void camu_video_buffer_flush(struct camu_video_buffer *buf)
+{
+ (void)buf;
+}
+
+static void camu_video_buffer_reset(struct camu_video_buffer *buf)
+{
+ (void)buf;
+}
+
+static bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out, bool *weighted)
+{
+ (void)buf;
+ (void)out;
+ *weighted = false;
+ return false;
+}
+
+static void camu_video_buffer_free(struct camu_video_buffer *buf)
+{
+ (void)buf;
+}
+
+AL_UNUSED_FUNCTION_POP