From e0fcbf0910b52a6e66eb733a2850ec58a53cf0e3 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Mon, 23 Jun 2025 17:49:11 -0400 Subject: Improve FFmpeg hwaccel fallback, VR emulation - Optimize sink seeking with single video frame. - Small cleanups. Signed-off-by: Andrew Opalach --- src/buffer/audio.c | 2 +- src/buffer/peak_buffer.c | 24 ++++++++++++------------ src/buffer/peak_buffer.h | 12 ++++++------ src/buffer/video.c | 39 ++++++++++++++++++++++----------------- src/buffer/video_null.h | 4 ++-- 5 files changed, 43 insertions(+), 38 deletions(-) (limited to 'src/buffer') diff --git a/src/buffer/audio.c b/src/buffer/audio.c index fd395e0..699b05e 100644 --- a/src/buffer/audio.c +++ b/src/buffer/audio.c @@ -12,7 +12,7 @@ #define BUFFER_SIZE 8.0 #define BUFFER_MARK_MIN 3.25 // Must be a most half of the buffer size. -#define BUFFER_MARK_BUFFERED 0.25 +#define BUFFER_MARK_BUFFERED 0.35 #ifdef CAMU_AUDIO_BUFFER_FADE #define FADE_STEP(fmt) (2.50f / (fmt)->sample_rate) diff --git a/src/buffer/peak_buffer.c b/src/buffer/peak_buffer.c index c1fe254..854595a 100644 --- a/src/buffer/peak_buffer.c +++ b/src/buffer/peak_buffer.c @@ -4,29 +4,29 @@ // We use ptrdiff_t here instead of nn_buffer's size_t just to map better to the types in audio_buffer. -void camu_peak_buffer_init(struct camu_peak_buffer *buf, ptrdiff_t size) +void camu_peak_buffer_init(struct camu_peak_buffer *peak, ptrdiff_t size) { - nn_buffer_init(&buf->buf); - nn_buffer_ensure_space(&buf->buf, (size_t)size); + nn_buffer_init(&peak->buffer); + nn_buffer_ensure_space(&peak->buffer, (size_t)size); } -void camu_peak_buffer_push(struct camu_peak_buffer *buf, u8 *data, ptrdiff_t size) +void camu_peak_buffer_push(struct camu_peak_buffer *peak, u8 *data, ptrdiff_t size) { - nn_buffer_append(&buf->buf, data, (size_t)size); + nn_buffer_append(&peak->buffer, data, (size_t)size); } -ptrdiff_t camu_peak_buffer_get_size(struct camu_peak_buffer *buf) +ptrdiff_t camu_peak_buffer_get_size(struct camu_peak_buffer *peak) { - return (ptrdiff_t)nn_buffer_get_size(&buf->buf); + return (ptrdiff_t)peak->buffer.size; } -u8 *camu_peak_buffer_flush(struct camu_peak_buffer *buf) +u8 *camu_peak_buffer_flush(struct camu_peak_buffer *peak) { - nn_buffer_set_size(&buf->buf, 0); - return nn_buffer_get_ptr(&buf->buf, 0); + peak->buffer.size = 0; + return nn_buffer_get_ptr(&peak->buffer, 0); } -void camu_peak_buffer_free(struct camu_peak_buffer *buf) +void camu_peak_buffer_free(struct camu_peak_buffer *peak) { - nn_buffer_free(&buf->buf); + nn_buffer_free(&peak->buffer); } diff --git a/src/buffer/peak_buffer.h b/src/buffer/peak_buffer.h index 73465ec..2a68e9c 100644 --- a/src/buffer/peak_buffer.h +++ b/src/buffer/peak_buffer.h @@ -4,11 +4,11 @@ #include struct camu_peak_buffer { - struct nn_buffer buf; + struct nn_buffer buffer; }; -void camu_peak_buffer_init(struct camu_peak_buffer *buf, ptrdiff_t size); -void camu_peak_buffer_push(struct camu_peak_buffer *buf, u8 *data, ptrdiff_t size); -ptrdiff_t camu_peak_buffer_get_size(struct camu_peak_buffer *buf); -u8 *camu_peak_buffer_flush(struct camu_peak_buffer *buf); -void camu_peak_buffer_free(struct camu_peak_buffer *buf); +void camu_peak_buffer_init(struct camu_peak_buffer *peak, ptrdiff_t size); +void camu_peak_buffer_push(struct camu_peak_buffer *peak, u8 *data, ptrdiff_t size); +ptrdiff_t camu_peak_buffer_get_size(struct camu_peak_buffer *peak); +u8 *camu_peak_buffer_flush(struct camu_peak_buffer *peak); +void camu_peak_buffer_free(struct camu_peak_buffer *peak); diff --git a/src/buffer/video.c b/src/buffer/video.c index 9a33540..8bedbf6 100644 --- a/src/buffer/video.c +++ b/src/buffer/video.c @@ -10,10 +10,15 @@ #include "common.h" #include "common_internal.h" -#define BUFFER_MARK_LOW ((1.0 / 24.0) * 2) -#define BUFFER_MARK_BUFFERED ((1.0 / 24.0) * 4) // Must be >1. -#define BUFFER_MARK_HIGH ((1.0 / 24.0) * 6) -#define BUFFER_MARK_RESET (BUFFER_MARK_HIGH * 2.0) +// This used to scale based on avg_frame_duration, but that's actually +// quite incompatible with very low frame rates. +// MARK_LOW is considered directly after reading a frame, so in other words, +// it will trigger at the point where there is about (LOW+1) frames left. Even at +// something like 240fps that's still ~12 whole ms to uncork and produce a new frame. +#define BUFFER_MARK_LOW 2 +#define BUFFER_MARK_BUFFERED 5 // Must be >1. +#define BUFFER_MARK_HIGH 7 +#define BUFFER_MARK_RESET (BUFFER_MARK_HIGH * 2) bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *clock) { @@ -70,7 +75,7 @@ bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_code camu_video_format_copy(req, in); if (fmt->width > 0 && fmt->height > 0 && #ifdef CAMU_VIDEO_BUFFER_REQUIRE_RGB - (fmt->format != CAMU_PIXEL_FORMAT_RGBA && fmt->format != CAMU_PIXEL_FORMAT_RGB) + (fmt->format != CAMU_PIXEL_FORMAT_RGBA && fmt->format != CAMU_PIXEL_FORMAT_RGB32) #else (fmt->format == CAMU_PIXEL_FORMAT_PAL8) #endif @@ -79,8 +84,10 @@ bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_code req->width = fmt->width; req->height = fmt->height; #ifdef CAMU_HAVE_FFMPEG - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt->format); - req->format = (desc->flags & AV_PIX_FMT_FLAG_ALPHA) ? CAMU_PIXEL_FORMAT_RGBA : CAMU_PIXEL_FORMAT_RGB; + //const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get((enum AVPixelFormat)fmt->format); + //bool has_alpha = (desc->flags & AV_PIX_FMT_FLAG_ALPHA) != 0; + //req->format = CAMU_PIXEL_FORMAT_RGB32; + req->format = CAMU_PIXEL_FORMAT_RGBA; buf->scaler = camu_ff_scaler_create(); if (!buf->scaler->init(buf->scaler, &buf->fmt)) { // Scaler will be freed in video_buffer_free(). @@ -116,8 +123,7 @@ static void after_push_internal(struct camu_video_buffer *buf) // If a reset where to happen from the this thread, the latest read() // frame could be freed before it was used. s32 count = buf->queue->count(buf->queue); - f64 have = count * buf->avg_frame_duration; - if (!buf->buffered && (buf->single_frame || have >= BUFFER_MARK_BUFFERED)) { + if (!buf->buffered && (buf->single_frame || count >= BUFFER_MARK_BUFFERED)) { // Preserve order of: set flow -> flush -> callback, for single frames. if (buf->single_frame) { al_atomic_store(u8)(&buf->flow, FLUSHED, AL_ATOMIC_RELAXED); @@ -125,9 +131,9 @@ static void after_push_internal(struct camu_video_buffer *buf) } buf->buffered = true; buf->buffered_with_one_frame = count == 1; - log_debug("Buffered (mark: %.2fs).", have); + log_debug("Buffered (mark: %.2fs).", count * buf->avg_frame_duration); buf->callback(buf->userdata, CAMU_BUFFER_BUFFERED); - } else if (have >= BUFFER_MARK_HIGH) { + } else if (count >= BUFFER_MARK_HIGH) { buf->callback(buf->userdata, CAMU_BUFFER_CORK); } } @@ -157,8 +163,8 @@ static bool push_av_frame_internal(struct camu_video_buffer *buf, AVFrame *frame void camu_video_buffer_push(struct camu_video_buffer *buf, struct camu_codec_frame *frame) { - // A single frame will be sent again after a seek, discard it here (for now). if (buf->single_frame && buf->buffered) { + log_warn("Unexpected duplicate frame received."); camu_codec_frame_discard(frame); return; } @@ -198,10 +204,9 @@ void camu_video_buffer_flush(struct camu_video_buffer *buf) buf->queue->flush(buf->queue); if (!buf->buffered) { s32 count = buf->queue->count(buf->queue); - f64 have = count * buf->avg_frame_duration; buf->buffered = true; buf->buffered_with_one_frame = count == 1; - log_debug("Buffered (mark: %.2fs).", have); + log_debug("Buffered (mark: %.2fs).", count * buf->avg_frame_duration); buf->callback(buf->userdata, CAMU_BUFFER_BUFFERED); } } @@ -247,10 +252,10 @@ bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out, bool *weig al_atomic_store(u8)(&buf->flow, SIGNALED, AL_ATOMIC_RELEASE); } else if (flow == FLOWING) { // Never attempt to uncork if flow = FLUSHED. - f64 have = buf->queue->count(buf->queue) * buf->avg_frame_duration; - if (have <= BUFFER_MARK_LOW) { + s32 count = buf->queue->count(buf->queue); + if (count <= BUFFER_MARK_LOW) { buf->callback(buf->userdata, CAMU_BUFFER_UNCORK); - } else if (have >= BUFFER_MARK_RESET) { + } else if (count >= BUFFER_MARK_RESET) { log_warn("Buffer overflow, resetting."); ret = CAMU_QUEUE_ERR; // Don't use the frame written to out. buf->queue->reset(buf->queue); diff --git a/src/buffer/video_null.h b/src/buffer/video_null.h index 15d2aba..06b284c 100644 --- a/src/buffer/video_null.h +++ b/src/buffer/video_null.h @@ -7,6 +7,7 @@ #include "clock.h" struct camu_video_buffer { + struct camu_codec_stream *stream; bool single_frame; f64 avg_frame_duration; #ifdef CAMU_SCREEN_THREADED @@ -37,9 +38,8 @@ static bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_cl 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; + buf->stream = stream; return true; } -- cgit v1.2.3-101-g0448