diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/buffer/audio.c | 6 | ||||
| -rw-r--r-- | src/buffer/video.c | 120 | ||||
| -rw-r--r-- | src/buffer/video.h | 15 | ||||
| -rw-r--r-- | src/codec/codec.h | 30 | ||||
| -rw-r--r-- | src/codec/ffmpeg/scaler.c | 61 | ||||
| -rw-r--r-- | src/codec/ffmpeg/scaler.h | 19 | ||||
| -rw-r--r-- | src/codec/stb_image/impl.c | 12 | ||||
| -rw-r--r-- | src/liana/client.c | 7 | ||||
| -rw-r--r-- | src/liana/handlers/codec_client.c | 2 | ||||
| -rw-r--r-- | src/liana/handlers/codec_server.c | 12 | ||||
| -rw-r--r-- | src/liana/list.c | 2 | ||||
| -rw-r--r-- | src/libsink/sink.c | 15 | ||||
| -rw-r--r-- | src/mixer/audio_miniaudio.c | 3 | ||||
| -rw-r--r-- | src/render/queue_tiger.c | 43 | ||||
| -rw-r--r-- | src/render/queue_tiger.h | 3 | ||||
| -rw-r--r-- | src/render/renderer.h | 1 | ||||
| -rw-r--r-- | src/render/renderer_libplacebo.c | 8 | ||||
| -rw-r--r-- | src/render/renderer_tiger.c | 9 | ||||
| -rw-r--r-- | src/screen/screen.c | 12 |
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; |