summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/buffer/audio.c6
-rw-r--r--src/buffer/video.c120
-rw-r--r--src/buffer/video.h15
-rw-r--r--src/codec/codec.h30
-rw-r--r--src/codec/ffmpeg/scaler.c61
-rw-r--r--src/codec/ffmpeg/scaler.h19
-rw-r--r--src/codec/stb_image/impl.c12
-rw-r--r--src/liana/client.c7
-rw-r--r--src/liana/handlers/codec_client.c2
-rw-r--r--src/liana/handlers/codec_server.c12
-rw-r--r--src/liana/list.c2
-rw-r--r--src/libsink/sink.c15
-rw-r--r--src/mixer/audio_miniaudio.c3
-rw-r--r--src/render/queue_tiger.c43
-rw-r--r--src/render/queue_tiger.h3
-rw-r--r--src/render/renderer.h1
-rw-r--r--src/render/renderer_libplacebo.c8
-rw-r--r--src/render/renderer_tiger.c9
-rw-r--r--src/screen/screen.c12
19 files changed, 244 insertions, 136 deletions
diff --git a/src/buffer/audio.c b/src/buffer/audio.c
index f3e0b74..113ea1d 100644
--- a/src/buffer/audio.c
+++ b/src/buffer/audio.c
@@ -9,9 +9,9 @@
#include "common_internal.h"
#include "volume.h"
-#define BUFFER_USEC (6 * 1000000L)
+#define BUFFER_SIZE (6 * 1000000L)
#define BUFFER_MARK_MIN (2.6 * 1000000L) // Must be a most half of the buffer size.
-#define BUFFER_MARK_BUFFERED (1.5 * 1000000L)
+#define BUFFER_MARK_BUFFERED (2.5 * 1000000L)
#ifdef CAMU_AUDIO_BUFFER_FADE
#define FADE_STEP(fmt) (1.1f / (fmt)->sample_rate)
@@ -75,7 +75,7 @@ bool camu_audio_buffer_configure(struct camu_audio_buffer *buf, struct camu_code
in_format_name, buf->fmt.in.channel_count, buf->fmt.in.sample_rate,
req_format_name, buf->fmt.req.channel_count, buf->fmt.req.sample_rate);
- buf->size = camu_audio_format_usec_to_bytes(&buf->fmt.req, BUFFER_USEC);
+ buf->size = camu_audio_format_usec_to_bytes(&buf->fmt.req, BUFFER_SIZE);
buf->data = (u8 *)al_malloc(buf->size);
al_ring_buffer_init(&buf->rb, buf->data, buf->size);
diff --git a/src/buffer/video.c b/src/buffer/video.c
index 04a9dbf..0ffc00d 100644
--- a/src/buffer/video.c
+++ b/src/buffer/video.c
@@ -4,10 +4,16 @@
#include "common.h"
#include "common_internal.h"
-#define BUFFER_WATERMARK_LOW 6 // frames.
-#define BUFFER_WATERMARK_BUFFERED 4
-#define BUFFER_WATERMARK_HIGH 10
-#define BUFFER_WATERMARK_RESET BUFFER_WATERMARK_HIGH + 10
+#ifdef CAMU_VIDEO_BUFFER_FORCE_SCALER
+#ifdef CAMU_HAVE_FFMPEG
+#include "../codec/ffmpeg/scaler.h"
+#endif
+#endif
+
+#define BUFFER_MARK_LOW 0.199998
+#define BUFFER_MARK_BUFFERED 0.133332
+#define BUFFER_MARK_HIGH 0.33333
+#define BUFFER_MARK_RESET (BUFFER_MARK_HIGH * 2.0)
bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *clock, struct camu_renderer *renderer)
{
@@ -29,53 +35,73 @@ bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *cl
bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_codec_stream *stream)
{
buf->stream = stream;
+ struct camu_video_format *fmt = &buf->stream->video.fmt;
switch (stream->mode) {
case CAMU_NORMAL: {
- s32 width = buf->stream->video.width;
- s32 height = buf->stream->video.height;
+ 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(buf->stream->video.format);
- al_log_info("video_buffer", "Stream: %s (%dx%d) %s.", format_name, width, height, "IMAGE");
+
+ const char *format_name = camu_pixel_format_name(fmt->format);
+ al_log_info("video_buffer", "Stream: %s (%dx%d) %s.",
+ format_name, fmt->width, fmt->height, "IMAGE");
+
break;
}
#ifdef CAMU_HAVE_FFMPEG
case CAMU_FFMPEG_COMPAT: {
- s32 width = stream->av.stream->codecpar->width;
- s32 height = stream->av.stream->codecpar->height;
- buf->fmt.in_width = buf->stream->video.width = width;
- buf->fmt.in_height = buf->stream->video.height = height;
- buf->fmt.in_format = stream->av.stream->codecpar->format;
- buf->single_frame = stream->av.stream->duration == 0 ||
- stream->av.stream->avg_frame_rate.den == 0;
- AVRational frame_rate = buf->single_frame ? ((AVRational){ 0, 1 })
- : stream->av.stream->avg_frame_rate;
- buf->avg_frame_duration = av_q2d(av_inv_q(frame_rate));
- const char *format_name = av_get_pix_fmt_name(buf->fmt.in_format);
- al_log_info("video_buffer", "Stream: %s (%dx%d) %s %.2ffps.", format_name,
- width, height, buf->single_frame ? "IMAGE" : "VIDEO", av_q2d(frame_rate));
+ AVCodecParameters *codecpar = stream->av.stream->codecpar;
+ s64 duration = stream->av.stream->duration;
+ AVRational frame_rate = stream->av.stream->avg_frame_rate;
+
+ fmt->width = codecpar->width;
+ fmt->height = codecpar->height;
+ fmt->format = codecpar->format;
+ camu_video_format_copy(&buf->fmt.in, fmt);
+
+ buf->single_frame = duration == 0 || frame_rate.den == 0;
+ buf->avg_frame_duration = buf->single_frame ? 0.0 : av_q2d(av_inv_q(frame_rate));
+
+ const char *format_name = av_get_pix_fmt_name(buf->fmt.in.format);
+ al_log_info("video_buffer", "Stream: %s (%dx%d) %s %.2ffps.",
+ format_name, fmt->width, fmt->height, buf->single_frame ? "IMAGE" : "VIDEO", av_q2d(frame_rate));
+
+ break;
+ }
+#endif
+ }
#ifdef CAMU_VIDEO_BUFFER_FORCE_SCALER
- buf->fmt.req_width = width;
- buf->fmt.req_height = height;
- buf->fmt.req_format = AV_PIX_FMT_RGBA;
- if (camu_ff_scaler_init(&buf->scale, &buf->fmt) && buf->fmt.scaler_needed) {
- format_name = av_get_pix_fmt_name(buf->fmt.req_format);
- al_log_info("video_buffer", "Scaling to: %s (%dx%d)", format_name,
- buf->fmt.req_width, buf->fmt.req_height);
+#ifdef CAMU_HAVE_FFMPEG
+ if (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;
+ buf->scale = camu_ff_scaler_create();
+ if (buf->fmt.scaler_needed && buf->scale->init(buf->scale, &buf->fmt)) {
+ const char *format_name = av_get_pix_fmt_name(buf->fmt.req.format);
+ al_log_info("video_buffer", "Scaling to: %s (%dx%d).",
+ format_name, buf->fmt.req.width, buf->fmt.req.height);
} else {
return false;
}
-#endif
- break;
+ } else {
+ buf->fmt.scaler_needed = false;
+ camu_video_format_copy(&buf->fmt.req, &buf->fmt.in);
}
+#else
+ return false;
+#endif
+#else
+ camu_video_format_copy(&buf->fmt.req, &buf->fmt.in);
#endif
- }
return true;
}
-void camu_video_buffer_set_latency(struct camu_video_buffer *buf, f64 latency)
+void camu_video_buffer_set_latency(struct camu_video_buffer *buf, s32 frames)
{
- buf->latency = latency;
+ buf->latency = frames * buf->avg_frame_duration;
}
bool camu_video_buffer_is_single_frame(struct camu_video_buffer *buf)
@@ -85,17 +111,17 @@ bool camu_video_buffer_is_single_frame(struct camu_video_buffer *buf)
static void after_push_internal(struct camu_video_buffer *buf)
{
- s32 count = buf->queue->count(buf->queue);
- if (!buf->buffered && (buf->single_frame || count >= BUFFER_WATERMARK_BUFFERED)) {
- al_log_debug("video_buffer", "Buffered (watermark: %d frames).", count);
+ 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);
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 (count >= BUFFER_WATERMARK_RESET) buf->queue->reset(buf->queue);
- else if (count >= BUFFER_WATERMARK_HIGH) buf->callback(buf->userdata, CAMU_BUFFER_CORK);
+ if (have >= BUFFER_MARK_RESET) buf->queue->reset(buf->queue);
+ else if (have >= BUFFER_MARK_HIGH) buf->callback(buf->userdata, CAMU_BUFFER_CORK);
}
#ifdef CAMU_HAVE_FFMPEG
@@ -113,11 +139,11 @@ static bool push_av_frame_internal(struct camu_video_buffer *buf, AVFrame *frame
if (buf->pts == -1.0) buf->pts = pts;
#ifdef CAMU_VIDEO_BUFFER_FORCE_SCALER
if (buf->fmt.scaler_needed) {
- if (!camu_ff_scaler_scale(&buf->scale, (const u8 **)frame->data, frame->linesize)) {
- return;
+ if (!buf->scale->scale(buf->scale, (const u8 **)frame->data, frame->linesize)) {
+ return false;
}
av_frame_free(&frame);
- frame = av_frame_clone(buf->scale.frame);
+ frame = av_frame_clone(buf->scale->get_frame(buf->scale));
}
#endif
if (frame) frame->opaque = buf;
@@ -163,7 +189,8 @@ void camu_video_buffer_flush(struct camu_video_buffer *buf)
{
buf->queue->flush(buf->queue);
if (!buf->buffered) {
- al_log_debug("video_buffer", "Buffered (watermark: %d frames).", buf->queue->count(buf->queue));
+ f64 have = buf->queue->count(buf->queue) * buf->avg_frame_duration;
+ al_log_debug("video_buffer", "Buffered (mark: %.2fs).", have);
buf->callback(buf->userdata, CAMU_BUFFER_BUFFERED);
buf->buffered = true;
}
@@ -186,8 +213,11 @@ bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out)
buf->callback(buf->userdata, CAMU_BUFFER_EOF);
al_atomic_store(u8)(&buf->flow, SIGNALED, AL_ATOMIC_RELEASE);
al_log_debug("video_buffer", "Flushed.");
- } else if (flow == FLOWING && buf->queue->count(buf->queue) <= BUFFER_WATERMARK_LOW) {
- buf->callback(buf->userdata, CAMU_BUFFER_UNCORK);
+ } else if (flow == FLOWING) {
+ f64 have = buf->queue->count(buf->queue) * buf->avg_frame_duration;
+ if (have <= BUFFER_MARK_LOW) {
+ buf->callback(buf->userdata, CAMU_BUFFER_UNCORK);
+ }
}
return ret == CAMU_QUEUE_OK || ret == CAMU_QUEUE_MORE;
}
@@ -195,7 +225,7 @@ bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out)
void camu_video_buffer_free(struct camu_video_buffer *buf)
{
#ifdef CAMU_VIDEO_BUFFER_FORCE_SCALER
- if (buf->fmt.scaler_needed) camu_ff_scaler_close(&buf->scale);
+ if (buf->fmt.scaler_needed) buf->scale->free(&buf->scale);
#endif
buf->queue->free(&buf->queue);
}
diff --git a/src/buffer/video.h b/src/buffer/video.h
index 0e8bfff..0b48361 100644
--- a/src/buffer/video.h
+++ b/src/buffer/video.h
@@ -8,9 +8,6 @@
#include "../render/renderer.h"
#include "../screen/screen.h"
#include "../screen/view.h"
-#ifdef CAMU_VIDEO_BUFFER_FORCE_SCALER
-#endif
-#include "../codec/ffmpeg/scaler.h"
#include "clock.h"
#include "frame_queue.h"
@@ -23,23 +20,23 @@ struct camu_video_buffer {
f64 pts;
bool single_frame;
- f32 avg_frame_duration;
+ f64 avg_frame_duration;
+ struct camu_scaler_format fmt;
#ifdef CAMU_VIDEO_BUFFER_FORCE_SCALER
- struct camu_ff_scaler scale;
+ struct camu_scaler *scale;
#endif
- // TODO: Terrible
- struct camu_ff_scale_fmt fmt;
struct camu_frame_queue *queue;
bool buffered;
atomic(u8) flow;
+ struct camu_view view;
+
#ifdef CAMU_SCREEN_THREADED
atomic(u8) ref;
#endif
- struct camu_view view;
void (*callback)(void *, u8);
void *userdata;
@@ -47,7 +44,7 @@ struct camu_video_buffer {
bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *clock, struct camu_renderer *renderer);
bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_codec_stream *stream);
-void camu_video_buffer_set_latency(struct camu_video_buffer *buf, f64 latency);
+void camu_video_buffer_set_latency(struct camu_video_buffer *buf, s32 frames);
bool camu_video_buffer_is_single_frame(struct camu_video_buffer *buf);
void camu_video_buffer_push(struct camu_video_buffer *buf, struct camu_codec_frame *frame);
void camu_video_buffer_reset(struct camu_video_buffer *buf);
diff --git a/src/codec/codec.h b/src/codec/codec.h
index 9528522..68f45f6 100644
--- a/src/codec/codec.h
+++ b/src/codec/codec.h
@@ -112,14 +112,18 @@ struct camu_audio_format {
s32 channel_count;
};
+struct camu_video_format {
+ s32 width;
+ s32 height;
+ s32 format;
+};
+
struct camu_codec_stream {
u8 mode;
u8 type;
u64 duration;
struct {
- s32 width;
- s32 height;
- s32 format;
+ struct camu_video_format fmt;
} video;
struct {
struct camu_audio_format fmt;
@@ -192,6 +196,19 @@ struct camu_resampler {
void (*free)(struct camu_resampler **);
};
+struct camu_scaler_format {
+ bool scaler_needed;
+ struct camu_video_format in;
+ struct camu_video_format req;
+};
+
+struct camu_scaler {
+ bool (*init)(struct camu_scaler *, struct camu_scaler_format *);
+ bool (*scale)(struct camu_scaler *, const u8 **in_slice, s32 *in_strides);
+ AVFrame *(*get_frame)(struct camu_scaler *);
+ void (*free)(struct camu_scaler **);
+};
+
AL_UNUSED_FUNCTION_PUSH
static inline bool camu_resampler_format_matches(struct camu_resampler_format *fmt)
@@ -279,6 +296,13 @@ static inline f64 camu_audio_format_bytes_to_sec(struct camu_audio_format *fmt,
return samples / (f64)fmt->sample_rate;
}
+static inline void camu_video_format_copy(struct camu_video_format *dest, struct camu_video_format *src)
+{
+ dest->width = src->width;
+ dest->height = src->height;
+ dest->format = src->format;
+}
+
static const char *camu_pixel_format_name(s32 format)
{
switch (format) {
diff --git a/src/codec/ffmpeg/scaler.c b/src/codec/ffmpeg/scaler.c
index 9b5c5e0..d233268 100644
--- a/src/codec/ffmpeg/scaler.c
+++ b/src/codec/ffmpeg/scaler.c
@@ -34,51 +34,64 @@ static AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, s32 width, s32 height)
return frame;
}
-bool camu_ff_scaler_init(struct camu_ff_scaler *scale, struct camu_ff_scale_fmt *fmt)
+static bool ff_scaler_init(struct camu_scaler *scale, struct camu_scaler_format *fmt)
{
- al_memset(scale, 0, sizeof(struct camu_ff_scaler));
+ struct camu_ff_scaler *av = (struct camu_ff_scaler *)scale;
+ al_assert(fmt->scaler_needed);
- if (fmt->in_width == fmt->req_width && fmt->in_height == fmt->req_height &&
- fmt->in_format == fmt->req_format) {
- fmt->scaler_needed = false;
- return true;
- }
+ av->fmt = *fmt;
s32 flags = SWS_BITEXACT;
- scale->scaler_context = sws_getContext(fmt->in_width, fmt->in_height, fmt->in_format,
- fmt->req_width, fmt->req_height, fmt->req_format, flags, NULL, NULL, NULL);
- if (!scale->scaler_context) {
+ av->scaler_context = sws_getContext(fmt->in.width, fmt->in.height, fmt->in.format,
+ fmt->req.width, fmt->req.height, fmt->req.format, flags, NULL, NULL, NULL);
+ if (!av->scaler_context) {
al_log_error("ff_scaler", "Failed to create scaler context.");
return false;
}
- scale->frame = alloc_picture(fmt->req_format, fmt->req_width, fmt->req_height);
- if (!scale->frame) {
+ av->frame = alloc_picture(fmt->req.format, fmt->req.width, fmt->req.height);
+ if (!av->frame) {
al_log_error("ff_scaler", "Failed to allocate frame.");
return false;
}
- fmt->scaler_needed = true;
-
- scale->fmt = *fmt;
-
return true;
}
-bool camu_ff_scaler_scale(struct camu_ff_scaler *scale, const u8 **in_slice, s32 *in_strides)
+static bool ff_scaler_scale(struct camu_scaler *scale, const u8 **in_slice, s32 *in_strides)
{
- s32 ret = sws_scale(scale->scaler_context, in_slice, in_strides, 0, scale->fmt.in_height,
- scale->frame->data, scale->frame->linesize);
- if (ret != scale->fmt.req_height) {
+ struct camu_ff_scaler *av = (struct camu_ff_scaler *)scale;
+ s32 ret = sws_scale(av->scaler_context, in_slice, in_strides, 0, av->fmt.in.height,
+ av->frame->data, av->frame->linesize);
+ if (ret != av->fmt.req.height) {
al_log_error("ff_scaler", "Failed to scale frame (%s).", av_err2str(ret));
return false;
}
return true;
}
-void camu_ff_scaler_close(struct camu_ff_scaler *scale)
+static AVFrame *ff_scaler_get_frame(struct camu_scaler *scale)
+{
+ struct camu_ff_scaler *av = (struct camu_ff_scaler *)scale;
+ return av->frame;
+}
+
+static void ff_scaler_free(struct camu_scaler **scale)
+{
+ struct camu_ff_scaler *av = (struct camu_ff_scaler *)*scale;
+ av_freep(&av->frame->data[0]);
+ av_frame_free(&av->frame);
+ sws_freeContext(av->scaler_context);
+ al_free(av);
+ *scale = NULL;
+}
+
+struct camu_scaler *camu_ff_scaler_create(void)
{
- av_freep(&scale->frame->data[0]);
- av_frame_free(&scale->frame);
- sws_freeContext(scale->scaler_context);
+ struct camu_ff_scaler *av = al_alloc_object(struct camu_ff_scaler);
+ av->scale.init = ff_scaler_init;
+ av->scale.scale = ff_scaler_scale;
+ av->scale.get_frame = ff_scaler_get_frame;
+ av->scale.free = ff_scaler_free;
+ return (struct camu_scaler *)av;
}
diff --git a/src/codec/ffmpeg/scaler.h b/src/codec/ffmpeg/scaler.h
index 3e53fbb..a4effb3 100644
--- a/src/codec/ffmpeg/scaler.h
+++ b/src/codec/ffmpeg/scaler.h
@@ -1,24 +1,15 @@
#pragma once
-#include <libswscale/swscale.h>
#include <al/types.h>
+#include <libswscale/swscale.h>
-struct camu_ff_scale_fmt {
- s32 in_width;
- s32 in_height;
- enum AVPixelFormat in_format;
- s32 req_width;
- s32 req_height;
- enum AVPixelFormat req_format;
- bool scaler_needed;
-};
+#include "../codec.h"
struct camu_ff_scaler {
+ struct camu_scaler scale;
struct SwsContext *scaler_context;
- struct camu_ff_scale_fmt fmt;
+ struct camu_scaler_format fmt;
AVFrame *frame;
};
-bool camu_ff_scaler_init(struct camu_ff_scaler *scale, struct camu_ff_scale_fmt *fmt);
-bool camu_ff_scaler_scale(struct camu_ff_scaler *scale, const u8 **in_slice, s32 *in_strides);
-void camu_ff_scaler_close(struct camu_ff_scaler *scale);
+struct camu_scaler *camu_ff_scaler_create(void);
diff --git a/src/codec/stb_image/impl.c b/src/codec/stb_image/impl.c
index 98d9e2c..ac1cbc5 100644
--- a/src/codec/stb_image/impl.c
+++ b/src/codec/stb_image/impl.c
@@ -40,9 +40,10 @@ static bool stbi_demuxer_init(struct camu_demuxer *demux, struct cch_handle *han
struct camu_codec_stream stream = { 0 };
stream.mode = CAMU_NORMAL;
stream.type = CAMU_STREAM_VIDEO;
- stream.video.width = w;
- stream.video.height = h;
- stream.video.format = camu_pixel_format_from_channels(channels);
+ struct camu_video_format *fmt = &stream.video.fmt;
+ fmt->width = w;
+ fmt->height = h;
+ fmt->format = camu_pixel_format_from_channels(channels);
al_array_push(stb->demux.streams, stream);
@@ -106,7 +107,8 @@ static s32 stbi_decoder_push(struct camu_decoder *dec, struct camu_codec_packet
u8 *data = aki_buffer_get_ptr(packet->buffer, 0);
u8 *pixels = stbi_load_from_memory(data, packet->buffer->size, &w, &h, &channels, 0);
if (!pixels) return CAMU_ERR_ERROR;
- al_assert(w == stb->stream->video.width && h == stb->stream->video.height);
+ struct camu_video_format *fmt = &stb->stream->video.fmt;
+ al_assert(w == fmt->width && h == fmt->height);
struct camu_codec_frame *frame = al_alloc_object(struct camu_codec_frame);
frame->mode = CAMU_NORMAL;
@@ -115,7 +117,7 @@ static s32 stbi_decoder_push(struct camu_decoder *dec, struct camu_codec_packet
frame->video.width = w;
frame->video.height = h;
frame->format = camu_pixel_format_from_channels(channels);
- al_assert(frame->format == stb->stream->video.format);
+ al_assert(frame->format == fmt->format);
frame->pts = 0.0;
stb->callback(stb->userdata, frame);
diff --git a/src/liana/client.c b/src/liana/client.c
index 5e33086..5d770c0 100644
--- a/src/liana/client.c
+++ b/src/liana/client.c
@@ -39,9 +39,10 @@ static void parse_info_packet(struct lia_client *client, struct aki_packet *pack
av_channel_layout_default(&fmt->channel_layout, fmt->channel_count);
#endif
} else if (type == CAMU_STREAM_VIDEO) {
- track->stream.video.width = aki_packet_read_s32(packet);
- track->stream.video.height = aki_packet_read_s32(packet);
- track->stream.video.format = aki_packet_read_s32(packet);
+ struct camu_video_format *fmt = &track->stream.video.fmt;
+ fmt->width = aki_packet_read_s32(packet);
+ fmt->height = aki_packet_read_s32(packet);
+ fmt->format = aki_packet_read_s32(packet);
}
track->index = 0;
break;
diff --git a/src/liana/handlers/codec_client.c b/src/liana/handlers/codec_client.c
index 050cc6f..d420c59 100644
--- a/src/liana/handlers/codec_client.c
+++ b/src/liana/handlers/codec_client.c
@@ -1,6 +1,8 @@
#include "../../codec/codec.h"
+#ifdef CAMU_HAVE_FFMPEG
#include "../../codec/ffmpeg/decoder.h"
#include "../../codec/ffmpeg/packet_ext.h"
+#endif
#include "../../codec/stb_image/decoder.h"
#include "../../codec/spng/decoder.h"
#include "../../codec/wuffs/decoder.h"
diff --git a/src/liana/handlers/codec_server.c b/src/liana/handlers/codec_server.c
index 07fafdf..58da2e6 100644
--- a/src/liana/handlers/codec_server.c
+++ b/src/liana/handlers/codec_server.c
@@ -1,6 +1,8 @@
#include "../../codec/codec.h"
+#ifdef CAMU_HAVE_FFMPEG
#include "../../codec/ffmpeg/demuxer.h"
#include "../../codec/ffmpeg/packet_ext.h"
+#endif
#include "../../codec/stb_image/demuxer.h"
#include "../../codec/spng/demuxer.h"
#include "../../codec/wuffs/demuxer.h"
@@ -41,12 +43,14 @@ static void codec_server_write_info(struct lia_server_handler *handler, struct a
al_array_foreach_ptr(codec->demux->streams, i, stream) {
aki_packet_write_u8(packet, stream->mode);
switch (stream->mode) {
- case CAMU_NORMAL:
+ case CAMU_NORMAL: {
aki_packet_write_u8(packet, stream->type);
- aki_packet_write_s32(packet, stream->video.width);
- aki_packet_write_s32(packet, stream->video.height);
- aki_packet_write_s32(packet, stream->video.format);
+ struct camu_video_format *fmt = &stream->video.fmt;
+ aki_packet_write_s32(packet, fmt->width);
+ aki_packet_write_s32(packet, fmt->height);
+ aki_packet_write_s32(packet, fmt->format);
break;
+ }
#ifdef CAMU_HAVE_FFMPEG
case CAMU_FFMPEG_COMPAT:
aki_packet_write_av_codec_id(packet, stream->av.stream->codecpar->codec_id);
diff --git a/src/liana/list.c b/src/liana/list.c
index 9698497..970d247 100644
--- a/src/liana/list.c
+++ b/src/liana/list.c
@@ -3,8 +3,6 @@
#include <al/lib.h>
#include <al/log.h>
-#include "../libsink/common.h"
-
#include "list.h"
#include "list_cmp.h"
diff --git a/src/libsink/sink.c b/src/libsink/sink.c
index ff9f4f2..da8f47f 100644
--- a/src/libsink/sink.c
+++ b/src/libsink/sink.c
@@ -472,13 +472,20 @@ static void evaluate_latency(struct camu_sink *sink, struct camu_sink_entry *ent
#ifndef CAMU_SINK_NO_VIDEO
// Entry has both audio and video configured.
if (!BUFFER_EMPTY(&entry->audio) && !BUFFER_EMPTY(&entry->video)) {
- camu_video_buffer_set_latency(&entry->video.buf, -camu_mixer_get_latency(sink->audio.mixer));
+ f64 audio = camu_mixer_get_latency(sink->audio.mixer);
+ s32 frames = audio / entry->video.buf.avg_frame_duration;
+ frames -= sink->video.renderer->get_latency(sink->video.renderer);
+ camu_video_buffer_set_latency(&entry->video.buf, -frames);
}
#endif
#else
// To sync clients with differing audio latencies our only option is to factor the mixer
- // latency into the audio buffer.
- camu_audio_buffer_set_latency(&entry->audio.buf, camu_mixer_get_latency(sink->audio.mixer));
+ // latency directly into the audio buffer.
+ f64 audio = camu_mixer_get_latency(sink->audio.mixer);
+ s32 frames = audio / entry->video.buf.avg_frame_duration;
+ frames += sink->video.renderer->get_latency(sink->video.renderer);
+ camu_video_buffer_set_latency(&entry->video.buf, frames);
+ camu_audio_buffer_set_latency(&entry->audio.buf, audio);
#endif
}
@@ -823,6 +830,8 @@ static bool set_command_callback(void *userdata, struct aki_rpc_connection *conn
entry->ended = ended;
+ if (entry == sink->current) goto out;
+
if (op == LIANA_SINK_BUFFER) {
goto out;
} else if (op == LIANA_SINK_BUFFER_AND_QUEUE) {
diff --git a/src/mixer/audio_miniaudio.c b/src/mixer/audio_miniaudio.c
index 4e29e0a..5695a31 100644
--- a/src/mixer/audio_miniaudio.c
+++ b/src/mixer/audio_miniaudio.c
@@ -175,7 +175,8 @@ static bool audio_miniaudio_configure_stream(struct camu_audio *audio, void *opa
static u64 audio_miniaudio_get_latency(struct camu_audio *audio)
{
struct camu_audio_miniaudio *ma = (struct camu_audio_miniaudio *)audio;
- return (ma->device.playback.internalPeriodSizeInFrames * ma->device.playback.internalPeriods) /
+ u32 periods = ma->device.playback.internalPeriods;
+ return (ma->device.playback.internalPeriodSizeInFrames * periods) /
(ma->device.playback.internalSampleRate / 1000000.0);
}
diff --git a/src/render/queue_tiger.c b/src/render/queue_tiger.c
index b1c34ca..bbc2b6e 100644
--- a/src/render/queue_tiger.c
+++ b/src/render/queue_tiger.c
@@ -3,7 +3,7 @@
#include "queue_tiger.h"
-static void upload_texture(struct camu_frame_queue_tiger *tq, struct camu_codec_frame *frame)
+static void upload_texture(struct camu_frame_queue_tiger *tq, u8 *data, s32 width, s32 height, s32 format, s32 linesize)
{
glGenTextures(1, &tq->texture_id);
glActiveTexture(GL_TEXTURE0);
@@ -15,19 +15,16 @@ static void upload_texture(struct camu_frame_queue_tiger *tq, struct camu_codec_
//if (frame->flags & CAMU_FRAME_DEVICE_ALLOCATED) {
// glBindBuffer(GL_PIXEL_UNPACK_BUFFER, tq->tr->texture_buffer);
//}
- //if (linesize) {
- // glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize);
- //}
- //glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- switch (frame->format) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 0);
+ switch (format) {
case CAMU_PIXEL_FORMAT_RGBA:
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, frame->width, frame->height, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, frame->data);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height,
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
break;
case CAMU_PIXEL_FORMAT_RGB:
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, frame->width, frame->height, 0,
- GL_RGB, GL_UNSIGNED_BYTE, frame->data);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height,
+ 0, GL_RGB, GL_UNSIGNED_BYTE, data);
break;
default:
al_assert(false);
@@ -47,6 +44,7 @@ static void queue_tiger_push_av_frame(struct camu_frame_queue *queue, AVFrame *f
{
struct camu_frame_queue_tiger *tq = (struct camu_frame_queue_tiger *)queue;
(void)pts;
+ tq->queued_av_frame = frame;
}
#endif
@@ -68,10 +66,30 @@ static u8 queue_tiger_read(struct camu_frame_queue *queue, f64 pts, void *out)
(void)pts;
if (tq->queued_frame) {
struct camu_codec_frame *frame = tq->queued_frame;
- upload_texture(tq, frame);
+ s32 linesize = frame->video.width;
+ upload_texture(tq, frame->data, frame->video.width, frame->video.height, frame->format, linesize);
+ camu_codec_frame_discard(frame);
tq->queued_frame = NULL;
tq->uploaded = true;
}
+#ifdef CAMU_HAVE_FFMPEG
+ if (tq->queued_av_frame) {
+ AVFrame *frame = tq->queued_av_frame;
+ s32 linesize = frame->linesize[0];
+ switch (frame->format) {
+ case CAMU_PIXEL_FORMAT_RGBA:
+ linesize /= 4;
+ break;
+ case CAMU_PIXEL_FORMAT_RGB:
+ linesize /= 3;
+ break;
+ }
+ upload_texture(tq, frame->data[0], frame->width, frame->height, frame->format, linesize);
+ av_frame_free(&frame);
+ tq->queued_av_frame = NULL;
+ tq->uploaded = true;
+ }
+#endif
if (!tq->uploaded) return CAMU_QUEUE_MORE;
*((GLuint *)out) = tq->texture_id;
return CAMU_QUEUE_OK;
@@ -95,6 +113,7 @@ struct camu_frame_queue *camu_frame_queue_tiger_create(void)
{
struct camu_frame_queue_tiger *tq = al_alloc_object(struct camu_frame_queue_tiger);
tq->queued_frame = NULL;
+ tq->queued_av_frame = NULL;
tq->uploaded = false;
tq->q.push = queue_tiger_push;
#ifdef CAMU_HAVE_FFMPEG
diff --git a/src/render/queue_tiger.h b/src/render/queue_tiger.h
index a90d8c7..af0e229 100644
--- a/src/render/queue_tiger.h
+++ b/src/render/queue_tiger.h
@@ -9,6 +9,9 @@ struct camu_frame_queue_tiger {
GLuint texture_id;
struct camu_renderer_tiger *tr;
struct camu_codec_frame *queued_frame;
+#ifdef CAMU_HAVE_FFMPEG
+ struct AVFrame *queued_av_frame;
+#endif
bool uploaded;
};
diff --git a/src/render/renderer.h b/src/render/renderer.h
index 5b1d342..dcd74c1 100644
--- a/src/render/renderer.h
+++ b/src/render/renderer.h
@@ -31,6 +31,7 @@ struct camu_renderer {
#endif
void *priv);
struct camu_frame_queue *(*create_queue)(struct camu_renderer *);
+ u32 (*get_latency)(struct camu_renderer *);
void (*resize)(struct camu_renderer *, s32 *, s32 *);
void (*render)(struct camu_renderer *, struct camu_screen *);
#ifdef CAMU_HAVE_FFMPEG
diff --git a/src/render/renderer_libplacebo.c b/src/render/renderer_libplacebo.c
index 8dde7f5..4f64fc2 100644
--- a/src/render/renderer_libplacebo.c
+++ b/src/render/renderer_libplacebo.c
@@ -116,6 +116,7 @@ static bool renderer_lp_create_renderer(struct camu_renderer *renderer, s32 *wid
}
lr->swapchain = pl_opengl_create_swapchain(lr->gl, pl_opengl_swapchain_params(
+ .max_swapchain_depth = 2,
.swap_buffers = gl_swap_buffers,
.priv = priv
));
@@ -184,6 +185,12 @@ static struct camu_frame_queue *renderer_lp_create_queue(struct camu_renderer *r
return queue;
}
+static u32 renderer_lp_get_latency(struct camu_renderer *renderer)
+{
+ struct camu_renderer_lp *lr = (struct camu_renderer_lp *)renderer;
+ return (u32)pl_swapchain_latency(lr->swapchain);
+}
+
static void renderer_lp_render(struct camu_renderer *renderer, struct camu_screen *scr)
{
struct camu_renderer_lp *lr = (struct camu_renderer_lp *)renderer;
@@ -257,6 +264,7 @@ struct camu_renderer *camu_renderer_lp_create(void)
struct camu_renderer_lp *lr = al_alloc_object(struct camu_renderer_lp);
lr->r.create_renderer = renderer_lp_create_renderer;
lr->r.create_queue = renderer_lp_create_queue;
+ lr->r.get_latency = renderer_lp_get_latency;
lr->r.resize = renderer_lp_resize;
lr->r.render = renderer_lp_render;
lr->r.free = renderer_lp_free;
diff --git a/src/render/renderer_tiger.c b/src/render/renderer_tiger.c
index 7127891..74d9238 100644
--- a/src/render/renderer_tiger.c
+++ b/src/render/renderer_tiger.c
@@ -174,6 +174,12 @@ static struct camu_frame_queue *renderer_tiger_create_queue(struct camu_renderer
#endif
}
+static u32 renderer_tiger_get_latency(struct camu_renderer *renderer)
+{
+ (void)renderer;
+ return 0;
+}
+
static void set_uniform(struct camu_renderer_tiger *tr, const GLchar *name,
u8 type, void *value)
{
@@ -229,7 +235,7 @@ static void renderer_tiger_render(struct camu_renderer *renderer, struct camu_sc
glActiveTexture(GL_TEXTURE0);
#ifdef CAMU_SCREEN_THREADED
- if (al_atomic_bool_load(&scr->queued, AL_ATOMIC_RELAXED)) {
+ if (al_atomic_load(u8)(&scr->queued, AL_ATOMIC_RELAXED)) {
camu_screen_run_queue(scr);
}
#endif
@@ -297,6 +303,7 @@ struct camu_renderer *camu_renderer_tiger_create(void)
struct camu_renderer_tiger *tr = al_alloc_object(struct camu_renderer_tiger);
tr->r.create_renderer = renderer_tiger_create_renderer;
tr->r.create_queue = renderer_tiger_create_queue;
+ tr->r.get_latency = renderer_tiger_get_latency;
tr->r.resize = renderer_tiger_resize;
tr->r.render = renderer_tiger_render;
tr->r.get_device_ptr = renderer_tiger_get_device_ptr;
diff --git a/src/screen/screen.c b/src/screen/screen.c
index afc6b9b..30c77d6 100644
--- a/src/screen/screen.c
+++ b/src/screen/screen.c
@@ -1,8 +1,6 @@
#include <al/log.h>
#include <aki/thread.h>
-#include "../liana/list.h"
-
#include "view.h"
#include "screen.h"
@@ -325,19 +323,19 @@ static void add_buffer_internal(struct camu_screen *scr, struct camu_video_buffe
struct camu_screen_video video = {
.buf = buf
};
- struct camu_codec_stream *stream = buf->stream;
+ struct camu_video_format *fmt = &buf->fmt.req;
if (buf->view.mode != CAMU_VIEW_NONE) {
video.view = buf->view;
#if 0
} else if (!al_array_is_empty(scr->videos)) {
video.view = al_array_last(scr->videos).view;
- video.view.width = stream->video.width;
- video.view.height = stream->video.height;
+ video.view.width = fmt->width;
+ video.view.height = fmt->height;
#endif
} else {
video.view.mode = CAMU_DEFAULT_VIEW;
- video.view.width = stream->video.width;
- video.view.height = stream->video.height;
+ video.view.width = fmt->width;
+ video.view.height = fmt->height;
video.view.stretch = 1.0;
video.view.rotation = CAMU_VIEW_ROTATION_0;
video.view.zindex = 0;