From 24b58a516e6bacfdf59aac422411c2f1fcf4ffb2 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Wed, 1 Jan 2025 16:04:59 -0500 Subject: Optimizations based on video loop performance - Support nn_packet_stream direct mode - Hook up FFmpeg hardware accelerated decoding - Refactor VCR - Reduce locking when returning packets to a packet pool Signed-off-by: Andrew Opalach --- src/buffer/audio.c | 8 ++++---- src/buffer/clock.c | 18 ++++++++++++------ src/buffer/clock.h | 2 +- src/buffer/video.c | 39 ++++++++++++++++++++++++++++++--------- src/buffer/video.h | 6 +++++- 5 files changed, 52 insertions(+), 21 deletions(-) (limited to 'src/buffer') diff --git a/src/buffer/audio.c b/src/buffer/audio.c index 7bb2c4c..d438d25 100644 --- a/src/buffer/audio.c +++ b/src/buffer/audio.c @@ -9,9 +9,9 @@ #include "common_internal.h" #include "volume.h" -#define BUFFER_SIZE 9.0 -#define BUFFER_MARK_MIN 4.3 // Must be a most half of the buffer size. -#define BUFFER_MARK_BUFFERED 2.0 +#define BUFFER_SIZE 4.0 +#define BUFFER_MARK_MIN 1.75 // Must be a most half of the buffer size. +#define BUFFER_MARK_BUFFERED 1.0 #ifdef CAMU_AUDIO_BUFFER_FADE #define FADE_STEP(fmt) (1.75f / (fmt)->sample_rate) @@ -291,7 +291,7 @@ size_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, size_t re } f64 base = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE); - f64 pts = camu_clock_get_pts(buf->clock, buf->latency); + f64 pts = camu_clock_get_pts(buf->clock, buf->latency, false); size_t ret, signal = req; size_t have = al_ring_buffer_occupied(&buf->rb); #ifdef CAMU_AUDIO_BUFFER_FADE diff --git a/src/buffer/clock.c b/src/buffer/clock.c index 33118aa..b711a27 100644 --- a/src/buffer/clock.c +++ b/src/buffer/clock.c @@ -57,6 +57,7 @@ void camu_clock_offset(struct camu_clock *clock, f64 amount) void camu_clock_pause(struct camu_clock *clock, u64 target) { al_assert(clock->paused_at == -1.0); + f64 tick = nn_get_tick(); if (target > 0) { tick = calc_tick_offset(tick, nn_get_timestamp(), target); @@ -64,19 +65,19 @@ void camu_clock_pause(struct camu_clock *clock, u64 target) } else { al_atomic_store(f64)(&clock->pause, PAUSED, AL_ATOMIC_RELAXED); } + clock->paused_at = tick; } void camu_clock_resume(struct camu_clock *clock, u64 target) { al_assert(clock->paused_at != -1.0); + f64 tick = nn_get_tick(); - //if (pause != PAUSED && tick < pause) { - // tick = pause; - //} else if (target > 0) { if (target > 0) { tick = calc_tick_offset(tick, nn_get_timestamp(), target); } + if (clock->paused_at == 0.0) { if (target == 0) { // target = 0 can never be synced. @@ -87,6 +88,7 @@ void camu_clock_resume(struct camu_clock *clock, u64 target) } else { clock->offset += tick - clock->paused_at; } + al_atomic_store(f64)(&clock->pause, RUNNING, AL_ATOMIC_RELAXED); clock->paused_at = -1.0; } @@ -106,7 +108,7 @@ f64 camu_clock_get_base_pts(struct camu_clock *clock) return clock->base; } -f64 camu_clock_get_pts(struct camu_clock *clock, f64 offset) +f64 camu_clock_get_pts(struct camu_clock *clock, f64 offset, bool skip_set) { f64 pause = al_atomic_load(f64)(&clock->pause, AL_ATOMIC_ACQUIRE); if (pause == PAUSED) return -1.0; @@ -115,8 +117,12 @@ f64 camu_clock_get_pts(struct camu_clock *clock, f64 offset) f64 tick = al_atomic_load(f64)(&clock->tick, AL_ATOMIC_RELAXED); if (tick == -1.0) { - tick = al_atomic_compare_and_swap(f64)(&clock->tick, -1.0, current); - if (tick == -1.0) tick = current; + if (skip_set) { + return -1.0; + } else { + tick = al_atomic_compare_and_swap(f64)(&clock->tick, -1.0, current); + if (tick == -1.0) tick = current; + } } // pause > 0.0 means running or pause armed. diff --git a/src/buffer/clock.h b/src/buffer/clock.h index 86eba5e..edad287 100644 --- a/src/buffer/clock.h +++ b/src/buffer/clock.h @@ -48,4 +48,4 @@ bool camu_clock_is_armed(struct camu_clock *clock); 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); +f64 camu_clock_get_pts(struct camu_clock *clock, f64 offset, bool skip_set); diff --git a/src/buffer/video.c b/src/buffer/video.c index fefb307..e2b6f8e 100644 --- a/src/buffer/video.c +++ b/src/buffer/video.c @@ -10,22 +10,25 @@ #endif #endif -#define BUFFER_MARK_LOW ((1.0 / 30.0) * 6) -#define BUFFER_MARK_BUFFERED ((1.0 / 30.0) * 8) -#define BUFFER_MARK_HIGH ((1.0 / 30.0) * 12) +#define BUFFER_MARK_LOW ((1.0 / 30.0) * 4) +#define BUFFER_MARK_BUFFERED ((1.0 / 30.0) * 5) +#define BUFFER_MARK_HIGH ((1.0 / 30.0) * 10) #define BUFFER_MARK_RESET (BUFFER_MARK_HIGH * 2.0) bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *clock) { + 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 unless the buffer is not empty. buf->single_frame = false; buf->avg_frame_duration = 0.0; buf->queue = NULL; buf->buffered = false; + buf->weighted_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); @@ -43,6 +46,7 @@ bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_code struct camu_video_format *fmt = &buf->stream->video.fmt; switch (stream->mode) { case CAMU_NORMAL: { + buf->single_frame = true; camu_video_format_copy(&buf->fmt.in, fmt); const char *format_name = camu_pixel_format_name(fmt->format); if (!format_name) format_name = "unknown"; @@ -122,16 +126,24 @@ bool camu_video_buffer_is_single_frame(struct camu_video_buffer *buf) static void after_push_internal(struct camu_video_buffer *buf) { f64 have = buf->queue->count(buf->queue) * buf->avg_frame_duration; + if (!buf->buffered && (buf->single_frame || have >= BUFFER_MARK_BUFFERED)) { - al_log_debug("video_buffer", "Buffered (mark: %.2fs).", have); // Preserve order of: flush -> callback -> set flow, for single frames. if (buf->single_frame) buf->queue->flush(buf->queue); + + al_log_debug("video_buffer", "Buffered (mark: %.2fs).", have); buf->callback(buf->userdata, CAMU_BUFFER_BUFFERED); buf->buffered = true; + if (buf->single_frame) al_atomic_store(u8)(&buf->flow, FLUSHED, AL_ATOMIC_RELAXED); } - if (have >= BUFFER_MARK_RESET) buf->queue->reset(buf->queue); - else if (have >= BUFFER_MARK_HIGH) buf->callback(buf->userdata, CAMU_BUFFER_CORK); + + if (have >= BUFFER_MARK_RESET) { + al_log_warn("video_buffer", "Buffer overflow, resetting."); + buf->queue->reset(buf->queue); + } else if (have >= BUFFER_MARK_HIGH) { + buf->callback(buf->userdata, CAMU_BUFFER_CORK); + } } static inline bool frame_is_late(struct camu_clock *clock, f64 base, f64 pts, f64 duration) @@ -218,17 +230,19 @@ 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) { - al_atomic_store(f64)(&buf->pts, -1.0, AL_ATOMIC_RELAXED); + buf->last_pts = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE); + al_atomic_store(f64)(&buf->pts, -1.0, AL_ATOMIC_RELEASE); + // pl_queue_pts_offset for looping? if (buf->queue) buf->queue->reset(buf->queue); buf->buffered = false; al_atomic_store(u8)(&buf->flow, FLOWING, AL_ATOMIC_RELAXED); } -bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out) +bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out, bool *weighted) { f64 base = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE); if (!buf->single_frame) { - f64 pts = camu_clock_get_pts(buf->clock, buf->latency); + f64 pts = camu_clock_get_pts(buf->clock, buf->latency, buf->weighted_read); if (pts > base) { base = pts; al_atomic_store(f64)(&buf->pts, base, AL_ATOMIC_RELEASE); @@ -241,6 +255,7 @@ bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out) al_atomic_store(u8)(&buf->flow, ERRORED, AL_ATOMIC_RELEASE); return false; } + if (flow == FLUSHED && (ret == CAMU_QUEUE_EOF || (buf->single_frame && ret == CAMU_QUEUE_OK))) { al_log_debug("video_buffer", "Flushed."); buf->callback(buf->userdata, CAMU_BUFFER_EOF); @@ -251,6 +266,12 @@ bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out) buf->callback(buf->userdata, CAMU_BUFFER_UNCORK); } } + + if (ret == CAMU_QUEUE_OK) { + *weighted = buf->weighted_read; + buf->weighted_read = false; + } + return ret == CAMU_QUEUE_OK || ret == CAMU_QUEUE_MORE; } diff --git a/src/buffer/video.h b/src/buffer/video.h index f6f4e28..5313f75 100644 --- a/src/buffer/video.h +++ b/src/buffer/video.h @@ -17,6 +17,7 @@ struct camu_video_buffer { struct camu_clock *clock; f64 latency; atomic(f64) pts; + f64 last_pts; bool single_frame; f64 avg_frame_duration; @@ -28,6 +29,9 @@ struct camu_video_buffer { struct camu_frame_queue *queue; bool buffered; + // Flush the render pipeline on this read and don't allow + // it to set the clock. + bool weighted_read; atomic(u8) flow; @@ -55,5 +59,5 @@ void camu_video_buffer_push_subtitle(struct camu_video_buffer *buf, AVPacket *pk #endif void camu_video_buffer_flush(struct camu_video_buffer *buf); void camu_video_buffer_reset(struct camu_video_buffer *buf); -bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out); +bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out, bool *weighted); void camu_video_buffer_free(struct camu_video_buffer *buf); -- cgit v1.2.3-101-g0448