diff options
| author | 2025-03-15 20:40:38 -0400 | |
|---|---|---|
| committer | 2025-03-15 20:40:38 -0400 | |
| commit | 846bd3d1b7bcec3653aa9d662f559235c4689e1b (patch) | |
| tree | 56906544460079681f46d23d48d76f1a8242ea75 /src/buffer | |
| parent | 46d5660a2582323e1c9c33a56694c322bc21b7bc (diff) | |
| download | camu-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.c | 18 | ||||
| -rw-r--r-- | src/buffer/clock.h | 2 | ||||
| -rw-r--r-- | src/buffer/meson.build | 6 | ||||
| -rw-r--r-- | src/buffer/video.c | 82 | ||||
| -rw-r--r-- | src/buffer/video.h | 8 | ||||
| -rw-r--r-- | src/buffer/video_null.h | 98 |
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 |