summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2025-11-30 14:32:51 -0500
committerAndrew Opalach <andrew@akon.city> 2025-11-30 14:32:51 -0500
commitc8412bbedae0fce38db96833732e8ce904721e4c (patch)
tree4611046186c714513d042966ce97825df0fecf9e /src
parent0d6d13425015d78606232874498327cabcb0e4e2 (diff)
downloadcamu-c8412bbedae0fce38db96833732e8ce904721e4c.tar.gz
camu-c8412bbedae0fce38db96833732e8ce904721e4c.tar.bz2
camu-c8412bbedae0fce38db96833732e8ce904721e4c.zip
Build cleanup and fixes from sink testing
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src')
-rw-r--r--src/buffer/audio.c11
-rw-r--r--src/buffer/clock.c77
-rw-r--r--src/buffer/clock.h5
-rw-r--r--src/buffer/video.c14
-rw-r--r--src/buffer/video.h2
-rw-r--r--src/cache/handlers/cdio.c24
-rw-r--r--src/cache/meson.build30
-rw-r--r--src/codec/codec.h2
-rw-r--r--src/codec/codecs.c3
-rw-r--r--src/codec/ffmpeg/decoder.c3
-rw-r--r--src/codec/ffmpeg/meson.build2
-rw-r--r--src/fruits/cmc/cmc.c8
-rw-r--r--src/fruits/cmc/meson.build4
-rw-r--r--src/fruits/cmc/ui/util/waveform.c4
-rw-r--r--src/fruits/cmsrv/cmsrv.c18
-rw-r--r--src/fruits/cmsrv/meson.build8
-rw-r--r--src/fruits/cmsrv/ui.c6
-rw-r--r--src/fruits/cmsrv/ui.h1
-rw-r--r--src/fruits/cmv/cmv.c97
-rw-r--r--src/fruits/cmv/meson.build2
-rw-r--r--src/fruits/common.h24
-rw-r--r--src/fruits/ctv/ctv.c8
-rw-r--r--src/fruits/meson.build7
-rw-r--r--src/liana/client.c49
-rw-r--r--src/liana/client.h8
-rw-r--r--src/liana/handlers/codec_client.c2
-rw-r--r--src/liana/list.c28
-rw-r--r--src/liana/list.h4
-rw-r--r--src/liana/meson.build11
-rw-r--r--src/liana/vcr.c73
-rw-r--r--src/libsink/desktop.c9
-rw-r--r--src/libsink/input_simulator.c10
-rw-r--r--src/libsink/sink.c342
-rw-r--r--src/libsink/sink.h6
-rw-r--r--src/mixer/audio_miniaudio.c62
-rw-r--r--src/mixer/audio_miniaudio.h2
-rw-r--r--src/portal/src/search.c21
-rw-r--r--src/render/meson.build56
-rw-r--r--src/render/queue_libplacebo.c15
-rw-r--r--src/render/renderer.h5
-rw-r--r--src/render/renderer_libplacebo.c100
-rw-r--r--src/render/renderer_libplacebo.h2
-rw-r--r--src/render/renderer_momo.c3
-rw-r--r--src/screen/screen.c12
-rw-r--r--src/server/server.c36
-rw-r--r--src/util/queue.h16
46 files changed, 631 insertions, 601 deletions
diff --git a/src/buffer/audio.c b/src/buffer/audio.c
index a740111..998662b 100644
--- a/src/buffer/audio.c
+++ b/src/buffer/audio.c
@@ -10,12 +10,12 @@
#include "common.h"
#include "common_internal.h"
-#define BUFFER_SIZE 6.0
+#define BUFFER_SIZE 7.0
#define BUFFER_MARK_MIN 3.25 // Must be a most half of the buffer size.
#define BUFFER_MARK_BUFFERED 0.35
#ifdef CAMU_AUDIO_BUFFER_FADE
-#define FADE_STEP(fmt) (2.50f / (fmt)->sample_rate)
+#define FADE_STEP(fmt, down) ((down ? -2.5f : 2.5f) / (fmt)->sample_rate)
#define FADE_MIN 0.175f
#endif
@@ -436,10 +436,9 @@ ptrdiff_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, ptrdif
f32 step = 0.f;
if (buf->pause == PAUSE_FADING || buf->fade.volume != buf->volume.user) {
- step = FADE_STEP(fmt);
- step *= (buf->pause == PAUSE_FADING || buf->fade.volume > buf->volume.user) ? -1.f : 1.f;
- if (buf->volume.user > 1.f) step *= buf->volume.user;
- step *= MAX(buf->fade.volume, FADE_MIN);
+ bool down = (buf->pause == PAUSE_FADING || buf->volume.user < buf->fade.volume);
+ step = FADE_STEP(fmt, down) * MAX(buf->fade.volume, FADE_MIN) *
+ ((buf->volume.user > 1.f) ? buf->volume.user : 1.f); // Move faster at >100%.
}
if (step != 0.f || buf->fade.volume != 1.f) {
buf->fade.volume = apply_volume(data, req, fmt, buf->fade.volume, buf->volume.user, step);
diff --git a/src/buffer/clock.c b/src/buffer/clock.c
index 732185e..356ac56 100644
--- a/src/buffer/clock.c
+++ b/src/buffer/clock.c
@@ -7,6 +7,11 @@
#define RUNNING 0.0
#define PAUSED -DBL_MAX
+// @TODO:
+// Stop throwing away precision before we need to.
+// Assume calc_tick_offset() could give a negative result at any point.
+// Use a tick offset to set local buffer latency in a way that makes more sense.
+
void camu_clock_init(struct camu_clock *clock, void (*callback)(void *, u8), void *userdata)
{
clock->callback = callback;
@@ -22,28 +27,33 @@ static f64 calc_tick_offset(f64 tick, u64 now, u64 target)
}
}
+static void offset_tick(struct camu_clock *clock, f64 diff)
+{
+ diff += atomic_load(f64)(&clock->tick, AL_ATOMIC_ACQUIRE);
+ atomic_store(f64)(&clock->tick, diff, AL_ATOMIC_RELEASE);
+}
+
void camu_clock_set(struct camu_clock *clock, f64 base)
{
clock->base = base;
- clock->offset = 0.0;
- al_atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED);
- al_atomic_store(f64)(&clock->pause, PAUSED, AL_ATOMIC_RELAXED);
+ atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED);
+ 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);
+ 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;
- al_atomic_store(f64)(&clock->last_pts, base, AL_ATOMIC_RELAXED);
+ 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);
+ atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED);
} else {
- f64 tick = calc_tick_offset(nn_get_tick(), nn_get_timestamp(), target);
- al_atomic_store(f64)(&clock->tick, tick, AL_ATOMIC_RELAXED);
+ f64 tick = nn_get_tick();
+ tick = calc_tick_offset(tick, nn_get_timestamp(), target);
+ atomic_store(f64)(&clock->tick, tick, AL_ATOMIC_RELAXED);
}
} else {
// Don't touch clock->pause here for the sake of sync. This means,
@@ -57,28 +67,29 @@ void camu_clock_seek(struct camu_clock *clock, f64 base, u64 target)
}
}
+// Seek to 0 but include the time it took to perform the seek.
void camu_clock_loop(struct camu_clock *clock, f64 last_pts)
{
- // Seek to 0 but include the time it took to perform the seek.
- clock->offset += last_pts - clock->base;
clock->base = 0.0;
+ offset_tick(clock, last_pts - clock->base);
}
+// If late, pause immediately and on resume() there will be a long delay.
void camu_clock_pause(struct camu_clock *clock, u64 target)
{
al_assert(clock->paused_at == -1.0);
- f64 tick = nn_get_tick();
+ f64 now = nn_get_tick(), tick = now;
if (target > 0) {
tick = calc_tick_offset(tick, nn_get_timestamp(), target);
- al_atomic_store(f64)(&clock->pause, tick, AL_ATOMIC_RELAXED);
- } else {
- al_atomic_store(f64)(&clock->pause, PAUSED, AL_ATOMIC_RELAXED);
}
+ atomic_store(f64)(&clock->pause, (tick > now) ? tick : -tick, AL_ATOMIC_RELAXED);
+
clock->paused_at = tick;
}
+// If late, this will be a catchup.
void camu_clock_resume(struct camu_clock *clock, u64 target)
{
al_assert(clock->paused_at != -1.0);
@@ -91,22 +102,23 @@ void camu_clock_resume(struct camu_clock *clock, u64 target)
if (clock->paused_at == 0.0) {
if (target == 0) {
// target = 0 can never be synced.
- al_atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED);
+ atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED);
} else {
- al_atomic_store(f64)(&clock->tick, tick, AL_ATOMIC_RELAXED);
+ atomic_store(f64)(&clock->tick, tick, AL_ATOMIC_RELAXED);
}
} else {
- clock->offset += tick - clock->paused_at;
+ f64 pause = atomic_load(f64)(&clock->pause, AL_ATOMIC_ACQUIRE);
+ offset_tick(clock, tick - ((pause != PAUSED) ? -pause : clock->paused_at));
}
- al_atomic_store(f64)(&clock->pause, RUNNING, AL_ATOMIC_RELAXED);
+ atomic_store(f64)(&clock->pause, RUNNING, AL_ATOMIC_RELEASE);
clock->paused_at = -1.0;
}
bool camu_clock_is_paused(struct camu_clock *clock)
{
- return al_atomic_load(f64)(&clock->pause, AL_ATOMIC_RELAXED) == PAUSED;
+ return atomic_load(f64)(&clock->pause, AL_ATOMIC_RELAXED) < 0.0;
}
f64 camu_clock_get_base_pts(struct camu_clock *clock)
@@ -116,35 +128,34 @@ f64 camu_clock_get_base_pts(struct camu_clock *clock)
f64 camu_clock_get_pts(struct camu_clock *clock, f64 latency, bool allow_set)
{
- f64 pause = al_atomic_load(f64)(&clock->pause, AL_ATOMIC_ACQUIRE);
- if (pause == PAUSED) return CAMU_PTS_PAUSED;
+ f64 pause = atomic_load(f64)(&clock->pause, AL_ATOMIC_RELAXED);
+ if (pause < 0.0) return CAMU_PTS_PAUSED;
f64 current = nn_get_tick();
-
- f64 tick = al_atomic_load(f64)(&clock->tick, AL_ATOMIC_RELAXED);
+ f64 tick = atomic_load(f64)(&clock->tick, AL_ATOMIC_RELAXED);
if (tick == -1.0) {
if (allow_set) {
- tick = al_atomic_compare_and_swap(f64)(&clock->tick, -1.0, current);
+ tick = atomic_compare_and_swap(f64)(&clock->tick, -1.0, current);
if (tick == -1.0) tick = current;
} else {
return CAMU_PTS_PAUSED;
}
}
+ f64 pts = clock->base + (current - tick);
+
bool signal_pause = false;
- if (pause > 0.0 && current > pause) { // pause > 0.0 means running or pause armed.
- current = pause;
- pause = al_atomic_compare_and_swap(f64)(&clock->pause, pause, PAUSED);
- if (pause != PAUSED) {
+ if (pause > 0.0 && current > pause) { // pause > 0.0 = RUNNING or armed for pause.
+ f64 paused_at = -current;
+ pause = atomic_compare_and_swap(f64)(&clock->pause, pause, paused_at);
+ if (pause != paused_at) {
clock->callback(clock->userdata, CAMU_CLOCK_PAUSED);
signal_pause = true;
}
}
- f64 pts = (clock->base - clock->offset) + (current - tick);
-
if (allow_set) {
- al_atomic_store(f64)(&clock->last_pts, pts, AL_ATOMIC_RELAXED);
+ atomic_store(f64)(&clock->last_pts, pts, AL_ATOMIC_RELAXED);
}
return signal_pause ? CAMU_PTS_SIGNAL_PAUSE : pts + latency;
@@ -152,5 +163,5 @@ f64 camu_clock_get_pts(struct camu_clock *clock, f64 latency, bool allow_set)
f64 camu_clock_get_last_pts(struct camu_clock *clock)
{
- return al_atomic_load(f64)(&clock->last_pts, AL_ATOMIC_RELAXED);
+ return atomic_load(f64)(&clock->last_pts, AL_ATOMIC_RELAXED);
}
diff --git a/src/buffer/clock.h b/src/buffer/clock.h
index 3209119..8324b6a 100644
--- a/src/buffer/clock.h
+++ b/src/buffer/clock.h
@@ -23,8 +23,8 @@
// Next/Prev:
// - User input -> swap
-#define CAMU_PTS_PAUSED 0xffffffffffffffff
-#define CAMU_PTS_SIGNAL_PAUSE 0x7fffffffffffffff
+#define CAMU_PTS_PAUSED ((f64)0xffffffffffffffff)
+#define CAMU_PTS_SIGNAL_PAUSE ((f64)0x7fffffffffffffff)
#define CAMU_PTS_CONSIDER_PAUSED(pts) (pts == CAMU_PTS_PAUSED || pts == CAMU_PTS_SIGNAL_PAUSE)
enum {
@@ -33,7 +33,6 @@ enum {
struct camu_clock {
f64 base;
- f64 offset;
atomic(f64) tick;
atomic(f64) pause;
f64 paused_at;
diff --git a/src/buffer/video.c b/src/buffer/video.c
index e5c38a6..5011e9d 100644
--- a/src/buffer/video.c
+++ b/src/buffer/video.c
@@ -30,6 +30,7 @@ bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *cl
buf->single_frame = true;
buf->queue = NULL;
buf->buffered = false;
+ buf->weighted_first_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);
@@ -63,13 +64,13 @@ bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_code
} else {
al_assert(frame_rate.den > 0);
buf->avg_frame_duration = av_q2d(av_inv_q(frame_rate));
+ buf->weighted_first_read = true;
log_info("Stream: %s (%ux%u) VIDEO %.3ffps.", format_name, fmt->width, fmt->height, av_q2d(frame_rate));
}
break;
}
#endif
}
- buf->weighted_read = !buf->single_frame;
struct camu_video_format *in = &buf->fmt.in;
struct camu_video_format *req = &buf->fmt.req;
camu_video_format_copy(in, fmt);
@@ -233,7 +234,8 @@ bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out, bool *weig
f64 base_pts = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE);
if (!buf->single_frame) {
- f64 pts = camu_clock_get_pts(buf->clock, buf->latency, !buf->weighted_read);
+ bool set_clock = !buf->weighted_first_read;
+ f64 pts = camu_clock_get_pts(buf->clock, buf->latency, set_clock);
if (!CAMU_PTS_CONSIDER_PAUSED(pts) && pts > base_pts) {
base_pts = pts;
}
@@ -268,15 +270,15 @@ bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out, bool *weig
}
if (ret == CAMU_QUEUE_OK) {
- if (UNLIKELY(buf->weighted_read)) {
- *weighted = buf->weighted_read;
- buf->weighted_read = false;
- }
// Only updating buf->pts on QUEUE_OK affects the behavior when we fall completely behind.
// Setting it here will result in laggily displaying out of date frames as they come in.
// Setting it on every read() will cause the video to freeze.
// This option ignores sync but is easier for the user to understand what's happening.
al_atomic_store(f64)(&buf->pts, base_pts, AL_ATOMIC_RELEASE);
+ if (UNLIKELY(buf->weighted_first_read)) {
+ *weighted = true;
+ buf->weighted_first_read = false;
+ }
} else if (ret == CAMU_QUEUE_MORE) {
log_trace("Underrun.");
}
diff --git a/src/buffer/video.h b/src/buffer/video.h
index fdd7d0b..20644bc 100644
--- a/src/buffer/video.h
+++ b/src/buffer/video.h
@@ -30,7 +30,7 @@ struct camu_video_buffer {
// Need to manually trigger EOF in read().
bool buffered_with_one_frame;
// Flush the renderer on this read and don't allow it to set the clock.
- bool weighted_read;
+ bool weighted_first_read;
atomic(u8) flow;
diff --git a/src/cache/handlers/cdio.c b/src/cache/handlers/cdio.c
index 29126c0..a9e3747 100644
--- a/src/cache/handlers/cdio.c
+++ b/src/cache/handlers/cdio.c
@@ -1,4 +1,5 @@
#define AL_LOG_SECTION "cache_cdio"
+//#define AL_LOG_ENABLE_TRACE
#include <al/log.h>
#include "../../codec/codec.h"
@@ -10,7 +11,7 @@
#include "cdio.h"
-// 1 sector at a time to minimize skips
+// 1 sector at a time to minimize skips.
#define SECTORS_PER_STEP 1
#define BYTES_PER_STEP (SECTORS_PER_STEP * CDIO_CD_FRAMESIZE_RAW)
@@ -18,13 +19,13 @@ static void cdio_log_messages(struct cch_handler_cdio *cdio)
{
char *error_messages = cdio_cddap_errors(cdio->drive);
if (error_messages) {
- log_error("\n%s", error_messages);
+ log_error("%s", error_messages);
cdio_cddap_free_messages(error_messages);
}
char *messages = cdio_cddap_messages(cdio->drive);
if (messages) {
- log_info("\n%s", messages);
+ log_info("%s", messages);
cdio_cddap_free_messages(messages);
}
}
@@ -42,26 +43,21 @@ static nn_thread_result NNWT_THREADCALL cd_read_thread(void *userdata)
for (;;) {
if (!al_atomic_load(s32)(&cdio->running, AL_ATOMIC_RELAXED)) {
- // We don't care about how complete the disc read was.
+ // We don't care about how complete the read was.
return 0;
}
- // cdio_read() here can block for a very long time so we buffer each read
- // before writing it to the backing.
+ // cdio_read() can block for a long time so we buffer each read before writing it to the backing.
long ret = cdio_cddap_read(cdio->drive, nn_buffer_get_ptr(&cdio->buffer, 0), cdio->sector, SECTORS_PER_STEP);
cdio_log_messages(cdio);
al_assert(ret <= SECTORS_PER_STEP);
off_t pointer = cdio->sector * CDIO_CD_FRAMESIZE_RAW;
size_t size;
u8 *ptr = cdio->backing->get_ptr(cdio->backing, pointer, &size);
- if (size < BYTES_PER_STEP) {
- log_error("Tried to write over expected size during normal operation.");
- cdio->backing->unlock(cdio->backing);
- break;
- }
+ al_assert(size >= BYTES_PER_STEP);
if (ret == SECTORS_PER_STEP) {
al_memcpy(ptr, nn_buffer_get_ptr(&cdio->buffer, 0), BYTES_PER_STEP);
} else if (ret == -10) {
- log_error("I/O error, skipping sector.");
+ log_error("Disc read error, skipping sector.");
al_memset(ptr, 0, BYTES_PER_STEP);
ret = SECTORS_PER_STEP;
} else if (ret < 0) {
@@ -170,7 +166,8 @@ static bool open_cd_drive(struct cch_handler_cdio *cdio)
lsn_t start = cdio_cddap_disc_firstsector(drive);
lsn_t end = cdio_cddap_disc_lastsector(drive);
- lsn_t offset = start;
+ lsn_t first_track_start = cdio_cddap_track_firstsector(drive, first_track);
+ lsn_t offset = first_track_start;
s32 track = CDIO_INVALID_TRACK;
while (offset < end) {
track = cdio_cddap_sector_gettrack(drive, offset);
@@ -178,6 +175,7 @@ static bool open_cd_drive(struct cch_handler_cdio *cdio)
lsn_t track_start = cdio_cddap_track_firstsector(drive, track);
lsn_t track_end = cdio_cddap_track_lastsector(drive, track);
al_array_push(cdio->handler.entry->chapters, ((struct cch_chapter){ track_start, track_end }));
+ log_trace("Track #%u: %zd-%zd.", track, track_start, track_end);
offset = track_end + 1;
}
// end is a valid index so add one framesize.
diff --git a/src/cache/meson.build b/src/cache/meson.build
index 1a198eb..61be949 100644
--- a/src/cache/meson.build
+++ b/src/cache/meson.build
@@ -8,37 +8,35 @@ cache_src = [
cache_deps = []
cache_args = []
-if naunet_has_mmap
- cache_src += ['backings/file_mapped.c']
-else
- cache_src += ['backings/file.c']
+if 'file' in get_option('sources')
+ if naunet_has_mmap
+ cache_src += ['backings/file_mapped.c']
+ else
+ cache_src += ['backings/file.c']
+ endif
endif
-if naunet_has_curl
+if 'http' in get_option('sources') and naunet_has_curl
cache_src += ['handlers/http.c']
endif
-cache_have_cdio = false
-if is_linux
- libcdio_paranoia = dependency('libcdio_paranoia', required: false, allow_fallback: true)
- libcdio_cdda = dependency('libcdio_cdda', required: false, allow_fallback: true)
+if 'cdio' in get_option('sources')
+ libcdio_paranoia = dependency('libcdio_paranoia', allow_fallback: true)
+ libcdio_cdda = dependency('libcdio_cdda', allow_fallback: true)
if libcdio_paranoia.found() and libcdio_cdda.found()
cache_src += ['handlers/cdio.c']
cache_deps += [libcdio_paranoia, libcdio_cdda]
cache_args += ['-DCACHE_HAVE_CDIO']
- cache_have_cdio = true
endif
endif
-cache_have_libdvd = false
-if is_linux
- libdvdcss = dependency('libdvdcss', required: false, allow_fallback: true)
- libdvdread = dependency('dvdread', required: false, allow_fallback: true)
- libdvdnav = dependency('dvdnav', required: false, allow_fallback: true)
+if 'libdvd' in get_option('sources')
+ libdvdcss = dependency('libdvdcss', allow_fallback: true)
+ libdvdread = dependency('dvdread', allow_fallback: true)
+ libdvdnav = dependency('dvdnav', allow_fallback: true)
if libdvdcss.found() and libdvdread.found() and libdvdnav.found()
cache_deps += [libdvdcss, libdvdread, libdvdnav]
cache_args += ['-DCACHE_HAVE_LIBDVD']
- cache_have_libdvd = true
endif
endif
diff --git a/src/codec/codec.h b/src/codec/codec.h
index f32283e..6a9b654 100644
--- a/src/codec/codec.h
+++ b/src/codec/codec.h
@@ -428,7 +428,7 @@ static inline s32 camu_guess_format(str *short_name, str *ext, str *mime_type)
(short_name && al_str_eq(short_name, &al_str_c("png"))) ||
(mime_type && al_str_eq(mime_type, &al_str_c("image/png")))) {
return CAMU_CODEC_PNG;
- } else if ((ext && (al_str_eq(ext, &al_str_c(".jpeg")) || al_str_eq(ext, &al_str_c(".jpg")))) ||
+ } else if ((ext && (al_str_eq(ext, &al_str_c(".jpeg")) || al_str_eq(ext, &al_str_c(".jpg")) || al_str_eq(ext, &al_str_c(".jpe")) || al_str_eq(ext, &al_str_c(".jfif")))) ||
(short_name && al_str_eq(short_name, &al_str_c("jpeg"))) ||
(mime_type && al_str_eq(mime_type, &al_str_c("image/jpeg")))) {
return CAMU_CODEC_JPEG;
diff --git a/src/codec/codecs.c b/src/codec/codecs.c
index dbec693..a373daf 100644
--- a/src/codec/codecs.c
+++ b/src/codec/codecs.c
@@ -83,6 +83,9 @@ struct camu_codec_info *camu_codec_info_by_type(s32 type)
struct camu_codec_info *camu_codec_info_by_name(str *name)
{
+ if (al_str_eq(name, &al_str_c("pcm_s16le"))) {
+ return (struct camu_codec_info *)0xcdda;
+ }
for (u32 i = 0; i < ARRAY_SIZE(codec_information); i++) {
if (al_str_eq(&codec_information[i].name, name)) {
return &codec_information[i];
diff --git a/src/codec/ffmpeg/decoder.c b/src/codec/ffmpeg/decoder.c
index 645a88a..7d50bca 100644
--- a/src/codec/ffmpeg/decoder.c
+++ b/src/codec/ffmpeg/decoder.c
@@ -114,9 +114,6 @@ static enum AVPixelFormat get_hw_format(AVCodecContext *context, const enum AVPi
log_error("Failed to get %s HW surface format.", hwdevice_name);
// Remake AVCodecContext as a software decoder.
- // This might log an error like "Your platform doesn't support hardware accelerated AV1 decoding.".
- // Even though the problem is really a lack of any decoder hardware OR software.
- // This happens with wine + d3d11va. Maybe a build configuration issue or FFmpeg bug?
AVCodecParameters *codecpar = av->codecpar;
av->errored_hw_context = av->codec_context;
alloc_codec_context_internal(av, codec, codecpar);
diff --git a/src/codec/ffmpeg/meson.build b/src/codec/ffmpeg/meson.build
index 6482426..e34cc92 100644
--- a/src/codec/ffmpeg/meson.build
+++ b/src/codec/ffmpeg/meson.build
@@ -11,7 +11,7 @@ ffmpeg_server_deps = []
ffmpeg_client_src = [ffmpeg_src, 'decoder.c', 'resampler.c', 'scaler.c']
ffmpeg_client_deps = []
-ffmpeg_force_fallback = false
+ffmpeg_force_fallback = get_option('wrap_mode') == 'forcefallback'
foreach dep: ['ffmpeg', 'libavutil', 'libavformat', 'libavcodec', 'libavdevice', 'libswresample', 'libswscale']
if dep in get_option('force_fallback_for')
ffmpeg_force_fallback = true
diff --git a/src/fruits/cmc/cmc.c b/src/fruits/cmc/cmc.c
index 3d91ba6..f44f82f 100644
--- a/src/fruits/cmc/cmc.c
+++ b/src/fruits/cmc/cmc.c
@@ -6,8 +6,6 @@
#include "../../liana/list.h"
#include "../../codec/packet_ext.h"
-#include "../common.h"
-
#include "cmc.h"
#include "ui/ui.h"
@@ -281,9 +279,13 @@ s32 main(s32 argc, char *argv[])
camu_post_cache_init(&c.cache);
al_array_init(c.searches);
+ str user = al_str_c("guest");
+ char *CMC_USER = getenv("CMC_USER");
+ if (CMC_USER) user = al_str_cr(CMC_USER);
+
c.client.callback = client_callback;
c.client.userdata = &c;
- if (!camu_client_login(&c.client, &al_str_c("andrew"), &c.loop, CAMU_TEST_TYPE, &CAMU_TEST_ADDR, CAMU_PORT)) {
+ if (!camu_client_login(&c.client, &user, &c.loop, NNWT_SOCKET_TCP, &al_str_c("127.0.0.1"), CAMU_PORT)) {
goto err;
}
diff --git a/src/fruits/cmc/meson.build b/src/fruits/cmc/meson.build
index de6d335..bb9d38d 100644
--- a/src/fruits/cmc/meson.build
+++ b/src/fruits/cmc/meson.build
@@ -20,7 +20,7 @@ if use_tui
endif
if is_windows and not is_msvc
- cmc_args += ['-static', '-static-libgcc', '-static-libstdc++', '-municode', '-mwindows']
+ cmc_args += ['-static', '-static-libgcc', '-static-libstdc++', '-mwindows', '-municode']
endif
-executable('cmc', cmc_src, dependencies: cmc_deps, link_args: cmc_args, install: true)
+executable('cmc', cmc_src, dependencies: cmc_deps, c_args: cmc_args, link_args: cmc_args, install: true)
diff --git a/src/fruits/cmc/ui/util/waveform.c b/src/fruits/cmc/ui/util/waveform.c
index bed34b7..624069e 100644
--- a/src/fruits/cmc/ui/util/waveform.c
+++ b/src/fruits/cmc/ui/util/waveform.c
@@ -22,8 +22,8 @@
// extra: #f6ac7f, #F5F4FA, #5b524b
// left channel: n == 0, right channel: n == 1.
#define COLOR_FOR_PIXEL(n, rms) rms ? \
- n == 0 ? nn_htonl(0xE4CAAF99) : nn_htonl(0xFF928FFF) : \
- n == 0 ? nn_htonl(0xF4EAE099) : nn_htonl(0xBC615EFF)
+ n == 0 ? nn_htonl(0xe4caaf99) : nn_htonl(0xff928fff) : \
+ n == 0 ? nn_htonl(0xf4eae099) : nn_htonl(0xbc615eff)
// ss: current sample, ns: next sample.
#define HIT_WAVE(low, high, ss, ns) \
diff --git a/src/fruits/cmsrv/cmsrv.c b/src/fruits/cmsrv/cmsrv.c
index ef66be8..babd5ef 100644
--- a/src/fruits/cmsrv/cmsrv.c
+++ b/src/fruits/cmsrv/cmsrv.c
@@ -1,8 +1,7 @@
-#define CMSRV_LOCAL_SOCKET
-#define CMSRV_USE_UI
-
+#define AL_LOG_SECTION "cmsrv"
#include <al/log.h>
#include <nnwt/common.h>
+#define CMSRV_LOCAL_SOCKET
#ifdef CMSRV_LOCAL_SOCKET
#include <nnwt/line_processor.h>
#endif
@@ -15,9 +14,9 @@
#include "../../cache/handlers/cdio.h"
#endif
-#include "../common.h"
-
+#ifdef CMSRV_USE_UI
#include "ui.h"
+#endif
struct cmsrv {
struct nn_event_loop loop;
@@ -186,8 +185,13 @@ s32 main(s32 argc, char *argv[])
nn_signal_start(&s.quit_signal);
signal(SIGINT, sigint_handler);
+ str addr = al_str_c("127.0.0.1");
+ char *CMSRV_IP = getenv("CMSRV_IP");
+ if (CMSRV_IP) addr = al_str_cr(CMSRV_IP);
+ log_info("Serving from %.*s.", al_str_x(&addr));
+
camu_server_init(&s.server, &s.loop);
- if (!camu_server_listen(&s.server, CAMU_TEST_TYPE, &CAMU_TEST_ADDR, CAMU_PORT)) {
+ if (!camu_server_listen(&s.server, NNWT_SOCKET_TCP, &addr, CAMU_PORT)) {
return EXIT_FAILURE;
}
@@ -198,7 +202,7 @@ s32 main(s32 argc, char *argv[])
s.local.cli.userdata = &s;
nn_line_processor_init(&s.local.cli, &al_str_c("\n"));
nn_line_processor_open_socket(&s.local.cli, &s.local.sock);
- if (nn_socket_bind(&s.local.sock, &CAMU_TEST_CONTROL_PATH, 0) && nn_socket_listen(&s.local.sock)) {
+ if (nn_socket_bind(&s.local.sock, &al_str_c("/tmp/camu_control"), 0) && nn_socket_listen(&s.local.sock)) {
nn_line_processor_run(&s.local.cli, &s.loop);
}
#endif
diff --git a/src/fruits/cmsrv/meson.build b/src/fruits/cmsrv/meson.build
index b12d00d..bfeabe0 100644
--- a/src/fruits/cmsrv/meson.build
+++ b/src/fruits/cmsrv/meson.build
@@ -2,14 +2,14 @@ cmsrv_src = ['cmsrv.c']
cmsrv_deps = [common_deps, server]
cmsrv_args = []
-use_tui = true
-if use_tui
+if use_tuis
cmsrv_src += ['ui.c']
cmsrv_deps += [notcurses_core]
+ cmsrv_args += ['-DCMSRV_USE_UI']
endif
if is_windows and not is_msvc
- cmsrv_args += ['-static', '-static-libgcc', '-static-libstdc++', '-municode', '-mwindows']
+ cmsrv_args += ['-static', '-static-libgcc', '-static-libstdc++', '-mwindows', '-municode']
endif
-executable('cmsrv', cmsrv_src, dependencies: cmsrv_deps, link_args: cmsrv_args, install: true)
+executable('cmsrv', cmsrv_src, dependencies: cmsrv_deps, c_args: cmsrv_args, link_args: cmsrv_args, install: true)
diff --git a/src/fruits/cmsrv/ui.c b/src/fruits/cmsrv/ui.c
index d96c09d..afbee58 100644
--- a/src/fruits/cmsrv/ui.c
+++ b/src/fruits/cmsrv/ui.c
@@ -29,6 +29,7 @@ bool cmsrv_ui_init(struct cmsrv_ui *ui, struct camu_server *server)
ncplane_set_userptr(stdplane, ui);
ui->pending_layout = true;
al_array_init(ui->log.messages);
+ nn_mutex_init(&ui->log.lock);
return true;
}
@@ -51,7 +52,9 @@ void cmsrv_ui_set_pane(struct cmsrv_ui *ui, u8 pane)
void cmsrv_ui_push_message(struct cmsrv_ui *ui, char *message)
{
+ nn_mutex_lock(&ui->log.lock);
al_array_push(ui->log.messages, message);
+ nn_mutex_unlock(&ui->log.lock);
}
static void layout_log(struct cmsrv_ui *ui, struct ncplane *parent)
@@ -80,6 +83,7 @@ static void render_log(struct cmsrv_ui *ui)
u32 max_width = ncplane_dim_x(n) - 2;
u32 max_height = ncplane_dim_y(n) - 2;
+ nn_mutex_lock(&ui->log.lock);
u32 size = ui->log.messages.count;
if (size > max_height) {
u32 index = size - max_height;
@@ -97,6 +101,7 @@ static void render_log(struct cmsrv_ui *ui)
// messages will have wide characters.
ncplane_putstr_yx(n, y, x, al_array_at(ui->log.messages, i));
}
+ nn_mutex_unlock(&ui->log.lock);
u64 c = 0;
ncchannels_set_fg_default(&c);
@@ -251,4 +256,5 @@ void cmsrv_ui_close(struct cmsrv_ui *ui)
al_free(message);
}
al_array_free(ui->log.messages);
+ nn_mutex_destroy(&ui->log.lock);
}
diff --git a/src/fruits/cmsrv/ui.h b/src/fruits/cmsrv/ui.h
index ebf7c1a..3615d3b 100644
--- a/src/fruits/cmsrv/ui.h
+++ b/src/fruits/cmsrv/ui.h
@@ -19,6 +19,7 @@ struct cmsrv_ui {
struct {
struct ncplane *n;
array(char *) messages;
+ struct nn_mutex lock;
} log;
struct {
struct ncplane *n;
diff --git a/src/fruits/cmv/cmv.c b/src/fruits/cmv/cmv.c
index fe0fbd6..535bf72 100644
--- a/src/fruits/cmv/cmv.c
+++ b/src/fruits/cmv/cmv.c
@@ -13,8 +13,6 @@
#include "../../codec/ffmpeg/common.h"
#endif
-#include "../common.h"
-
#if !defined CAMU_SINK_ONLY && !defined NAUNET_ON_WINDOWS
// https://unix.stackexchange.com/a/16884
#define PID_MAX_STR 7 // 0x400000 (2^22).
@@ -46,13 +44,32 @@ static nn_thread_result NNWT_THREADCALL event_loop_thread(void *userdata)
return 0;
}
-// sink-p12-108.52.160.112-aEN-v-sEN.exe
-static bool parse_exe_name_params(str *exe_name, str *addr, struct lia_prefs *prefs)
+static bool decode_addr(str *enc, str *addr)
+{
+ char value[4]; // max = 255.
+ for (u32 i = 0; i < 8; i += 2) {
+ str sub = al_str_substr(enc, i, i + 2);
+ bool error;
+ s64 l = al_str_to_long(&sub, 16, &error);
+ al_assert(l >= 0 && l <= 255);
+ if (error) return false;
+ al_snprintf(value, sizeof(value), "%hhu", (u8)l);
+ al_str_cat(addr, &al_str_cs(value));
+ if (i != 6) {
+ al_str_cat(addr, &al_str_c("."));
+ }
+ }
+ return true;
+}
+
+// sink-p12-976502a7.exe
+static bool parse_exe_name(str *exe_name, str *addr)
{
u32 index = al_str_rfind(exe_name, '/');
if (index == AL_STR_NO_POS) {
- index = al_str_rfind(exe_name, '\\');
- if (index == AL_STR_NO_POS) goto def;
+ if ((index = al_str_rfind(exe_name, '\\')) == AL_STR_NO_POS) {
+ goto def;
+ }
}
str sub = al_str_substr(exe_name, index + 1, exe_name->length);
@@ -62,51 +79,20 @@ static bool parse_exe_name_params(str *exe_name, str *addr, struct lia_prefs *pr
sub = al_str_substr(&sub, 5, sub.length);
// Skip version.
- index = al_str_find(&sub, '-');
- if (index == AL_STR_NO_POS) goto def;
+ if ((index = al_str_find(&sub, '-')) == AL_STR_NO_POS) {
+ goto def;
+ }
sub = al_str_substr(&sub, index + 1, sub.length);
- index = al_str_find(&sub, '-');
- if (index == AL_STR_NO_POS) goto def;
- al_str_clone(addr, &al_str_substr(&sub, 0, index));
-
- for (;;) {
- sub = al_str_substr(&sub, index + 1, sub.length);
- index = al_str_find(&sub, '-');
- if (index == AL_STR_NO_POS) {
- index = al_str_find(&sub, '.');
- if (index == AL_STR_NO_POS) {
- index = sub.length;
- } else {
- sub.length = index;
- }
- }
-
- str value;
- al_str_clone(&value, &al_str_substr(&sub, 0, index));
- al_str_to_lower(&value);
- if (al_str_at(&value, 0) == 'a') {
- prefs->enabled_mask |= 1 << CAMU_STREAM_AUDIO;
- } else if (al_str_at(&value, 0) == 'v') {
- prefs->enabled_mask |= 1 << CAMU_STREAM_VIDEO;
- } else if (al_str_at(&value, 0) == 's') {
- prefs->enabled_mask |= 1 << CAMU_STREAM_SUBTITLE;
- }
- al_str_free(&value);
-
- if (index == sub.length) break;
+ if ((index = al_str_find(&sub, '.')) != AL_STR_NO_POS) {
+ sub = al_str_substr(&sub, 0, index);
}
+ al_str_from(addr, "");
+ decode_addr(&sub, addr);
+ log_debug("Parsed address %.*s from exe filename.", al_str_x(addr));
return true;
def:
-#ifdef CAMU_HAVE_SUBTITLES
- prefs->enabled_mask = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_VIDEO) | (1 << CAMU_STREAM_SUBTITLE);
-#else
- prefs->enabled_mask = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_VIDEO);
-#endif
- prefs->audio_lang = CAMU_LANG_JAPANESE;
- prefs->subtitle_lang = CAMU_LANG_ENGLISH;
-
return false;
}
@@ -141,12 +127,12 @@ s32 window_system_main(u32 argc, str *argv, void *extra)
u8 type;
str addr;
- if (!parse_exe_name_params(&argv[0], &addr, &c.desktop.sink.prefs)) {
+ if (!parse_exe_name(&argv[0], &addr)) {
#ifndef CAMU_SINK_ONLY
if (local) {
#ifdef NAUNET_ON_WINDOWS
- type = NNWT_SOCKET_TCP;;
- al_str_clone(&addr, &CAMU_LOCALHOST);
+ type = NNWT_SOCKET_TCP;
+ al_str_clone(&addr, &al_str_c("127.0.0.1"));
#else
type = NNWT_SOCKET_UNIX;
al_str_clone(&addr, &CMV_UNIX_PATH);
@@ -155,17 +141,26 @@ s32 window_system_main(u32 argc, str *argv, void *extra)
al_str_cat(&addr, &al_str_cs(pid_str));
#endif
} else {
- type = CAMU_TEST_TYPE;
- al_str_clone(&addr, &CAMU_TEST_ADDR);
+ type = NNWT_SOCKET_TCP;
+ al_str_clone(&addr, &al_str_c("127.0.0.1"));
}
#else
type = NNWT_SOCKET_TCP;
- al_str_clone(&addr, &CAMU_TEST_ADDR);
+ al_str_clone(&addr, &al_str_c("127.0.0.1"));
#endif
} else {
type = NNWT_SOCKET_TCP;
}
+ struct lia_prefs *prefs = &c.desktop.sink.prefs;
+#ifdef CAMU_HAVE_SUBTITLES
+ prefs->enabled = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_VIDEO) | (1 << CAMU_STREAM_SUBTITLE);
+#else
+ prefs->enabled = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_VIDEO);
+#endif
+ prefs->language.audio = CAMU_LANG_JAPANESE;
+ prefs->language.subtitles = CAMU_LANG_ENGLISH;
+
#ifndef CAMU_SINK_ONLY
if (local) {
camu_server_init(&c.server, &c.loop);
diff --git a/src/fruits/cmv/meson.build b/src/fruits/cmv/meson.build
index bfd2182..3e78f05 100644
--- a/src/fruits/cmv/meson.build
+++ b/src/fruits/cmv/meson.build
@@ -15,7 +15,7 @@ endif
cmv_link_args = []
if is_windows and not is_msvc
- cmv_link_args += ['-static', '-static-libgcc', '-static-libstdc++', '-municode', '-mwindows']
+ cmv_link_args += ['-static', '-static-libgcc', '-static-libstdc++', '-mwindows', '-municode']
endif
executable('cmv', cmv_src, dependencies: cmv_deps, c_args: cmv_args, link_args: cmv_link_args, install: true)
diff --git a/src/fruits/common.h b/src/fruits/common.h
deleted file mode 100644
index 66a72bc..0000000
--- a/src/fruits/common.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <al/str.h>
-#include <nnwt/socket.h>
-
-AL_IGNORE_WARNING("-Wunused-variable")
-
-static str CAMU_DB_PATH = al_str_c("/home/andrew/c/camu/data/camu_db_test");
-
-static str CAMU_TEST_IP = al_str_c("108.52.160.112");
-static str CAMU_LOCALHOST = al_str_c("127.0.0.1");
-
-static str CAMU_TEST_PATH = al_str_c("/tmp/camu_sock");
-static str CAMU_TEST_CONTROL_PATH = al_str_c("/tmp/camu_control_sock");
-
-AL_IGNORE_WARNING_END
-
-#ifdef NAUNET_ON_WINDOWS
-#define CAMU_TEST_TYPE NNWT_SOCKET_TCP
-#define CAMU_TEST_ADDR CAMU_TEST_IP
-#else
-//#define CAMU_TEST_TYPE NNWT_SOCKET_UNIX
-//#define CAMU_TEST_ADDR CAMU_TEST_PATH
-#define CAMU_TEST_TYPE NNWT_SOCKET_TCP
-#define CAMU_TEST_ADDR CAMU_TEST_IP
-#endif
diff --git a/src/fruits/ctv/ctv.c b/src/fruits/ctv/ctv.c
index 45942e6..8b173b0 100644
--- a/src/fruits/ctv/ctv.c
+++ b/src/fruits/ctv/ctv.c
@@ -24,12 +24,12 @@ static nn_thread_result NNWT_THREADCALL event_loop_thread(void *userdata)
struct lia_prefs *prefs = &c->desktop.sink.prefs;
#ifdef CAMU_HAVE_SUBTITLES
- prefs->enabled_mask = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_VIDEO) | (1 << CAMU_STREAM_SUBTITLE);
+ prefs->enabled = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_VIDEO) | (1 << CAMU_STREAM_SUBTITLE);
#else
- prefs->enabled_mask = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_VIDEO);
+ prefs->enabled = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_VIDEO);
#endif
- prefs->audio_lang = CAMU_LANG_JAPANESE;
- prefs->subtitle_lang = CAMU_LANG_ENGLISH;
+ prefs->language.audio = CAMU_LANG_JAPANESE;
+ prefs->language.subtitles = CAMU_LANG_ENGLISH;
if (!camu_desktop_connect(&c->desktop, &al_str_c("ctv"), &c->loop, CAMU_TEST_TYPE, &CAMU_TEST_ADDR, CAMU_PORT)) {
return 0;
diff --git a/src/fruits/meson.build b/src/fruits/meson.build
index 37edd0b..c82d71d 100644
--- a/src/fruits/meson.build
+++ b/src/fruits/meson.build
@@ -1,4 +1,9 @@
-if (get_option('server').enabled() or get_option('client').enabled()) and not is_windows
+use_tuis = true
+if is_windows
+ use_tuis = false
+endif
+
+if use_tuis and (get_option('server').enabled() or get_option('client').enabled())
notcurses = dependency('notcurses', required: false, allow_fallback: false)
notcurses_core = dependency('notcurses-core', required: false, allow_fallback: false)
if not notcurses.found() or not notcurses_core.found()
diff --git a/src/liana/client.c b/src/liana/client.c
index 380ed26..4d5689f 100644
--- a/src/liana/client.c
+++ b/src/liana/client.c
@@ -136,46 +136,52 @@ static void parse_info_packet(struct lia_client *client, struct nn_packet *packe
nn_packet_read_str(packet, &handler);
client->duration = nn_packet_read_u64(packet);
collect_streams(client, packet);
+ if (client->streams.count == 0) {
+ log_warn("Resource has no streams.");
+ goto out;
+ }
struct lia_prefs *prefs = &client->prefs;
u8 selected = 0;
- u32 accept_defaults = 0;
+ u8 accept_defaults = 0;
for (; accept_defaults < 2; accept_defaults++) {
struct camu_codec_stream *stream;
al_array_foreach_ptr(client->streams, i, stream) {
- u8 type_mask = 1 << stream->type;
- if ((selected & type_mask) || !(prefs->enabled_mask & type_mask)) continue;
+ u8 type = stream->type;
+ if (type == CAMU_STREAM_SUBTITLE && client->mask == 0) {
+ log_warn("Ignoring subtitle-only resource.");
+ goto out;
+ }
+ if ((selected & (1 << type)) || !(prefs->enabled & (1 << type))) {
+ continue;
+ }
const char *title = NULL;
#ifdef CAMU_HAVE_FFMPEG
if (stream->mode == CAMU_FFMPEG_COMPAT) {
AVDictionary *metadata = stream->av.stream->metadata;
const AVDictionaryEntry *title_entry = av_dict_get(metadata, "title", NULL, 0);
- if (title_entry) title = title_entry->value;
+ if (title_entry) {
+ title = title_entry->value;
+ }
const AVDictionaryEntry *lang_entry = av_dict_get(metadata, "language", NULL, 0);
if (lang_entry && !accept_defaults) {
u8 lang = CAMU_LANG_UNKNOWN;
- str lang_str = al_str_cr(lang_entry->value);
- if (al_str_eq(&lang_str, &al_str_c("eng"))) lang = CAMU_LANG_ENGLISH;
- else if (al_str_eq(&lang_str, &al_str_c("jpn"))) lang = CAMU_LANG_JAPANESE;
- switch (stream->type) {
- case CAMU_STREAM_AUDIO:
- if (prefs->audio_lang != lang) continue;
- break;
- case CAMU_STREAM_SUBTITLE:
- if (prefs->subtitle_lang != lang) continue;
- break;
- default:
- break;
+ str s = al_str_cr(lang_entry->value);
+ if (al_str_eq(&s, &al_str_c("eng"))) lang = CAMU_LANG_ENGLISH;
+ else if (al_str_eq(&s, &al_str_c("jpn"))) lang = CAMU_LANG_JAPANESE;
+ if ((type == CAMU_STREAM_AUDIO && lang != prefs->language.audio) ||
+ (type == CAMU_STREAM_SUBTITLE && lang != prefs->language.subtitles)) {
+ continue;
}
+ break;
}
}
#endif
if (title) {
- log_info("Selected %s stream (index: %u, title: %s).", stream_type_to_str[stream->type], stream->index, title);
+ log_info("Selected %s stream (index: %u, title: %s).", stream_type_to_str[type], stream->index, title);
} else {
- log_info("Selected %s stream (index: %u).", stream_type_to_str[stream->type], stream->index);
+ log_info("Selected %s stream (index: %u).", stream_type_to_str[type], stream->index);
}
- selected |= type_mask;
- client->mask |= 1 << stream->index;
+ selected |= (1 << type);
struct lia_vcr_track *track = al_alloc_object(struct lia_vcr_track);
track->stream = stream;
track->client = lia_handler_by_name(&handler)->create_client_handler();
@@ -187,10 +193,12 @@ static void parse_info_packet(struct lia_client *client, struct nn_packet *packe
// This will NOT attempt to select another stream.
continue;
}
+ client->mask |= 1 << stream->index;
client->callback(client->userdata, LIANA_CLIENT_CONFIGURE, stream, track);
lia_vcr_add_track(&client->vcr, track);
}
}
+out:
client->callback(client->userdata, LIANA_CLIENT_CONFIGURE_COMPLETE, NULL, NULL);
}
@@ -201,6 +209,7 @@ static void info_packet_callback(void *userdata, struct nn_packet_stream *stream
parse_info_packet(client, packet);
nn_packet_stream_return_packet(stream, packet);
if (client->mask == 0 || lia_vcr_is_empty(&client->vcr)) {
+ log_warn("Discarding resource with no applicable streams.");
al_assert(client->reconnect == RECONNECT_NONE);
nn_packet_stream_disconnect(&client->data);
return;
diff --git a/src/liana/client.h b/src/liana/client.h
index 40093e0..681b134 100644
--- a/src/liana/client.h
+++ b/src/liana/client.h
@@ -27,9 +27,11 @@ struct lia_reconnect_info {
};
struct lia_prefs {
- u8 enabled_mask;
- s8 audio_lang;
- s8 subtitle_lang;
+ u8 enabled;
+ struct {
+ u8 audio;
+ u8 subtitles;
+ } language;
};
struct lia_client {
diff --git a/src/liana/handlers/codec_client.c b/src/liana/handlers/codec_client.c
index abb5b9a..c3dded1 100644
--- a/src/liana/handlers/codec_client.c
+++ b/src/liana/handlers/codec_client.c
@@ -33,7 +33,7 @@ static bool codec_client_init(struct lia_client_handler *handler, struct camu_re
static bool push_av_packet(struct lia_codec_client *codec, AVPacket *pkt)
{
s32 ret = codec->dec->push_av_packet(codec->dec, pkt);
- if (ret == AVERROR(EAGAIN)) {
+ while (ret == AVERROR(EAGAIN)) {
codec->dec->process(codec->dec);
ret = codec->dec->push_av_packet(codec->dec, pkt);
}
diff --git a/src/liana/list.c b/src/liana/list.c
index b47a30c..253ba5f 100644
--- a/src/liana/list.c
+++ b/src/liana/list.c
@@ -91,6 +91,9 @@ static void unref_all_entries(struct lia_list *list, s32 trigger)
}
}
+// @TODO: When implementing list_remove(), consider this case.
+// remove() sequence 0 causes a skip to sequence 1 and sequence 1 fails to load.
+// Do we properly move to list->current = -1 and an idle list?
static bool entry_load_and_get_duration(struct lia_list *list, struct lia_list_entry *entry, s32 sequence, bool *error)
{
u8 last_load = entry->load;
@@ -99,9 +102,6 @@ static bool entry_load_and_get_duration(struct lia_list *list, struct lia_list_e
// If sequence is <0 that must mean entry is not yet added to list->entries.
if (sequence >= 0) {
al_array_remove_at(list->entries, (u32)sequence);
- // @TODO: Consider this when implementing list_remove().
- // In the case of remove sequence 0, causing a skip to sequence 1, and sequence 1
- // fails to load, do we properly move to -1 and an idle list.
if (list->current > sequence) {
struct lia_list_sink *sink;
al_array_foreach(list->sinks, i, sink) {
@@ -111,6 +111,7 @@ static bool entry_load_and_get_duration(struct lia_list *list, struct lia_list_e
list->current--;
}
struct lia_list_cmd *cmd = list->cmd;
+ al_assert(cmd);
al_assert(cmd->sequence != sequence);
if (cmd->sequence > sequence) {
cmd->sequence--;
@@ -141,6 +142,19 @@ static bool entry_load_and_get_duration(struct lia_list *list, struct lia_list_e
static void unload_all_entires(struct lia_list *list)
{
+ struct lia_list_cmd *cmd = list->cmd;
+ if (cmd && cmd->entry) {
+ al_assert(cmd->op == ADD);
+ entry_unload(list, cmd->entry);
+ list->cmd = NULL;
+ }
+ al_array_foreach_rev(list->command_queue, i, cmd) {
+ if (cmd->entry) {
+ al_assert(cmd->op == ADD);
+ entry_unload(list, cmd->entry);
+ al_array_remove_at(list->command_queue, i);
+ }
+ }
struct lia_list_entry *entry;
al_array_foreach(list->entries, i, entry) {
entry_unload(list, entry);
@@ -162,6 +176,7 @@ static inline void sink_pause_entry(struct lia_list_sink *sink, struct lia_list_
sink->callback(sink->userdata, LIANA_SINK_PAUSE, entry, sequence, time);
}
+// @TODO: This should probably give a sequence or ID.
static inline void sink_unset_entry(struct lia_list_sink *sink)
{
sink->callback(sink->userdata, LIANA_SINK_UNSET, NULL, -1, NULL);
@@ -341,13 +356,13 @@ static bool handle_skipto(struct lia_list *list, s32 sequence, s32 index)
bool error;
if (!entry_load_and_get_duration(list, target, index, &error)) {
if (error) {
- // index might point to a different entry after an error.
+ // On an error, sequence will address the same entry but index may not.
return handle_skipto(list, sequence, index);
}
return false;
}
- u64 at = nn_get_timestamp() + LIANA_PAUSE_DELAY;
+ u64 at = nn_get_timestamp() + LIANA_BASE_PING;
u64 pos = target->offset;
u8 pause = skipto_entry(current, target, at);
@@ -438,6 +453,7 @@ static void handle_toggle_pause(struct lia_list *list, s32 sequence, f64 pts)
case LIANA_PAUSE_PAUSE: {
al_assert(entry->start != LIANA_TIMESTAMP_INVALID);
entry->paused_at = now + LIANA_PAUSE_DELAY;
+ // @TODO: There is no reason we couldn't pause during a pre-roll.
if (entry->paused_at < entry->start) {
entry->paused_at = entry->start;
}
@@ -603,6 +619,8 @@ static bool adjust_for_order_change(struct lia_list *list, struct lia_list_entry
}
}
al_assert(cmd->op == SKIPTO);
+ // This SKIPTO being in-place is a consideration for the sequence of any
+ // commands that were queued after this one.
return handle_skipto(list, cmd->sequence, cmd->arg0.i);
}
diff --git a/src/liana/list.h b/src/liana/list.h
index 61dcb1d..c8b1d73 100644
--- a/src/liana/list.h
+++ b/src/liana/list.h
@@ -9,7 +9,7 @@
#define LIANA_SEQUENCE_ANY -1
#define LIANA_BASE_DELAY 450000u // 450ms
-#define LIANA_BASE_PING 125000u // 125ms
+#define LIANA_BASE_PING 150000u // 150ms
#define LIANA_PAUSE_DELAY LIANA_BASE_PING
#define LIANA_DELAY_IGNORE 0u
@@ -100,7 +100,7 @@ struct lia_list_cmd {
struct lia_list_entry *entry;
s32 sequence;
union { s32 i; u32 u; } arg0;
- union { f64 f; u64 l; u32 u; } arg1;
+ union { u64 l; u32 u; f64 f; } arg1;
};
struct lia_list {
diff --git a/src/liana/meson.build b/src/liana/meson.build
index 5c1cc42..40d4598 100644
--- a/src/liana/meson.build
+++ b/src/liana/meson.build
@@ -5,27 +5,28 @@ liana_server_src = [
'handlers.c',
'process.c'
]
+
liana_client_src = [
'client.c',
'vcr.c',
'handlers/codec_client.c',
'handlers.c'
]
+
liana_server_deps = [cache, codecs_server, codecs_client] # codecs_client for transcode.
liana_client_deps = [codecs_client]
-liana_args = []
-if cache_have_cdio
+if 'cdio' in get_option('sources')
liana_server_src += ['handlers/cdio_server.c']
liana_client_src += ['handlers/cdio_client.c']
endif
-if cache_have_libdvd
+if 'libdvd' in get_option('sources')
liana_server_src += ['handlers/dvd_server.c']
liana_client_src += ['handlers/dvd_client.c']
endif
liana_server = declare_dependency(sources: liana_server_src,
- dependencies: liana_server_deps, compile_args: [liana_args, '-DLIANA_SERVER'])
+ dependencies: liana_server_deps, compile_args: ['-DLIANA_SERVER'])
liana_client = declare_dependency(sources: liana_client_src,
- dependencies: liana_client_deps, compile_args: [liana_args, '-DLIANA_CLIENT'])
+ dependencies: liana_client_deps, compile_args: ['-DLIANA_CLIENT'])
diff --git a/src/liana/vcr.c b/src/liana/vcr.c
index 13082f5..4fa548c 100644
--- a/src/liana/vcr.c
+++ b/src/liana/vcr.c
@@ -7,9 +7,9 @@
#include "vcr.h"
#include "common.h"
-#define VCR_BUFFER_BUFFERED MB(4)
-#define VCR_BUFFER_GROW_FACTOR 8
-#define VCR_BUFFER_LOW_OFFSET MB(1)
+#define VCR_BUFFER_BUFFERED MB(6LL)
+#define VCR_BUFFER_GROW_FACTOR 8LL
+#define VCR_BUFFER_LOW_OFFSET KB(500LL)
AL_STATIC_ASSERT(buf_gt_low_offset, VCR_BUFFER_BUFFERED * VCR_BUFFER_GROW_FACTOR, >, VCR_BUFFER_LOW_OFFSET);
enum {
@@ -67,12 +67,12 @@ static void update_metrics(struct lia_vcr *vcr, u64 size)
u64 diff = now - vcr->metrics.last_report_ts;
u64 mark = now - vcr->metrics.last_report_mark;
u64 frame = vcr->metrics.current_frame;
- if (mark > 500000 || (diff > 2000000 && vcr->metrics.current_frame >= KB(500)) || (!size && frame > 0)) {
+ if (mark > 750000 || (diff > 5000000 && vcr->metrics.current_frame >= KB(500)) || (!size && frame > 0)) {
al_assert(frame > 0);
f32 kbps = (frame / 125.f) / (mark / 1000000.f);
f32 average_kbps = vcr->metrics.average_kbps;
average_kbps = average_kbps == 0.f ? kbps : (average_kbps + kbps) / 2.f;
- f32 buffered = al_atomic_load(u64)(&vcr->count, AL_ATOMIC_RELAXED) / (f32)MB(1);
+ f32 buffered = atomic_load(u64)(&vcr->count, AL_ATOMIC_RELAXED) / (f32)MB(1);
f32 capacity = vcr->mark.buffered / (f32)MB(1);
log_info("Receiving packets at %.2fkbps (%.2f/%.2fMB).", average_kbps, buffered, capacity);
vcr->metrics.average_kbps = average_kbps;
@@ -88,9 +88,9 @@ void lia_vcr_init(struct lia_vcr *vcr, struct nn_event_loop *loop, struct nn_pac
vcr->data = data;
vcr->node_id = node_id;
al_array_init(vcr->tracks);
- al_atomic_store(u64)(&vcr->count, 0, AL_ATOMIC_RELAXED);
+ atomic_store(u64)(&vcr->count, 0, AL_ATOMIC_RELAXED);
vcr->mark.buffered = VCR_BUFFER_BUFFERED;
- al_atomic_store(u64)(&vcr->mark.low, 0, AL_ATOMIC_RELAXED);
+ atomic_store(u64)(&vcr->mark.low, 0, AL_ATOMIC_RELAXED);
vcr->expand = VCR_EXPAND_UNTOUCHED;
vcr->started = false;
#ifndef CAMU_DIRECT_MODE
@@ -149,10 +149,10 @@ static nn_thread_result NNWT_THREADCALL vcr_track_thread(void *userdata)
if (packet) {
// Check if we should uncork the packet stream.
u32 size = nn_packet_get_size(packet);
- u64 buffer = al_atomic_sub(u64)(&vcr->count, size, AL_ATOMIC_RELAXED);
+ u64 buffer = atomic_sub(u64)(&vcr->count, size, AL_ATOMIC_RELAXED);
#ifndef CAMU_DIRECT_MODE
- bool buffered = al_atomic_load(bool)(&track->buffered, AL_ATOMIC_RELAXED);
- u64 low = al_atomic_load(u64)(&vcr->mark.low, AL_ATOMIC_RELAXED);
+ bool buffered = atomic_load(bool)(&track->buffered, AL_ATOMIC_RELAXED);
+ u64 low = atomic_load(u64)(&vcr->mark.low, AL_ATOMIC_RELAXED);
if (buffered && (low && buffer <= low)) {
nn_signal_send(&vcr->signal);
}
@@ -176,7 +176,7 @@ static nn_thread_result NNWT_THREADCALL vcr_track_thread(void *userdata)
}
// Take state again because handle_packet() could have caused the track to be corked.
- state = al_atomic_load(s32)(&track->state, AL_ATOMIC_RELAXED);
+ state = atomic_load(s32)(&track->state, AL_ATOMIC_RELAXED);
// We wait if corked (TRACK_STOPPED) or EOF.
corked = (packet && state == VCR_TRACK_STOPPED) || !packet;
@@ -199,7 +199,7 @@ static nn_thread_result NNWT_THREADCALL vcr_track_thread(void *userdata)
nn_cond_wait(&track->cond, &track->mutex);
// Check for possibly updated state.
- state = al_atomic_load(s32)(&track->state, AL_ATOMIC_RELAXED);
+ state = atomic_load(s32)(&track->state, AL_ATOMIC_RELAXED);
nn_mutex_unlock(&track->mutex);
@@ -254,11 +254,11 @@ void lia_vcr_add_track(struct lia_vcr *vcr, struct lia_vcr_track *track)
track->vcr = vcr;
nn_cond_init(&track->cond);
nn_mutex_init(&track->mutex);
- al_atomic_store(bool)(&track->buffered, !VCR_TRACK_THREADED(track), AL_ATOMIC_RELAXED);
+ atomic_store(bool)(&track->buffered, !VCR_TRACK_THREADED(track), AL_ATOMIC_RELAXED);
track->running = false;
nn_packet_cache_init(&track->cache, 256);
al_array_push(vcr->tracks, track);
- al_atomic_store(s32)(&track->state, VCR_TRACK_RUNNING, AL_ATOMIC_RELAXED);
+ atomic_store(s32)(&track->state, VCR_TRACK_RUNNING, AL_ATOMIC_RELAXED);
}
bool lia_vcr_remove_track_by_stream(struct lia_vcr *vcr, struct camu_codec_stream *stream)
@@ -298,13 +298,13 @@ static void cork_if_buffered(struct lia_vcr *vcr, u64 buffer)
bool buffered = true;
struct lia_vcr_track *track;
al_array_foreach(vcr->tracks, i, track) {
- buffered &= al_atomic_load(bool)(&track->buffered, AL_ATOMIC_RELAXED);
+ buffered &= atomic_load(bool)(&track->buffered, AL_ATOMIC_RELAXED);
}
if (buffered) {
if (vcr->expand == VCR_EXPAND_UNTOUCHED) {
vcr->mark.buffered = buffer * VCR_BUFFER_GROW_FACTOR;
vcr->expand = VCR_EXPAND_GROWN;
- log_info("Expanded buffer to size %.2fMB.", vcr->mark.buffered / (f32)MB(1));
+ log_debug("Expanded buffer to size %.2fMB.", vcr->mark.buffered / (f32)MB(1));
return;
}
if (!vcr->corked) {
@@ -316,7 +316,7 @@ static void cork_if_buffered(struct lia_vcr *vcr, u64 buffer)
// Don't set low until after we corked so that vcr_track_thread() will never try uncorking
// until we know what the low mark is.
if (vcr->expand == VCR_EXPAND_GROWN) {
- al_atomic_store(u64)(&vcr->mark.low, vcr->mark.buffered - VCR_BUFFER_LOW_OFFSET, AL_ATOMIC_RELAXED);
+ atomic_store(u64)(&vcr->mark.low, vcr->mark.buffered - VCR_BUFFER_LOW_OFFSET, AL_ATOMIC_RELAXED);
vcr->expand = VCR_EXPAND_COMPLETE;
}
al_array_foreach(vcr->tracks, i, track) {
@@ -342,9 +342,15 @@ void lia_vcr_push_packet(struct lia_vcr *vcr, struct nn_packet *packet)
break;
}
if (VCR_TRACK_THREADED(track)) {
- if (nn_packet_cache_send_packet(&track->cache, packet)) {
- u64 buffer;
- if ((buffer = al_atomic_add(u64)(&vcr->count, size, AL_ATOMIC_RELAXED)) >= vcr->mark.buffered) {
+ u64 buffer = 0;
+ bool can_send = nn_packet_cache_available(&track->cache);
+ if (can_send) {
+ buffer = atomic_add(u64)(&vcr->count, size, AL_ATOMIC_RELAXED);
+ nn_packet_cache_send_packet(&track->cache, packet);
+ }
+ nn_packet_cache_unlock(&track->cache);
+ if (can_send) {
+ if (buffer >= vcr->mark.buffered) {
#ifndef CAMU_DIRECT_MODE
cork_if_buffered(vcr, buffer);
#endif
@@ -360,12 +366,15 @@ void lia_vcr_push_packet(struct lia_vcr *vcr, struct nn_packet *packet)
}
case LIANA_PACKET_EOF:
case LIANA_PACKET_ERROR: {
+ // @TODO: Should ERROR be passed down to LIANA_CLIENT_ERRORED?
#ifndef CAMU_DIRECT_MODE
update_metrics(vcr, 0); // Flush.
#endif
- // @TODO: Should ERROR be passed down to LIANA_CLIENT_ERRORED?
al_array_foreach(vcr->tracks, i, track) {
- nn_packet_cache_send_packet(&track->cache, NULL);
+ if (nn_packet_cache_available(&track->cache)) {
+ nn_packet_cache_send_packet(&track->cache, NULL);
+ }
+ nn_packet_cache_unlock(&track->cache);
}
#ifndef CAMU_DIRECT_MODE
nn_signal_stop(&vcr->signal);
@@ -373,7 +382,7 @@ void lia_vcr_push_packet(struct lia_vcr *vcr, struct nn_packet *packet)
if (op == LIANA_PACKET_EOF) {
log_info("Received EOF.");
} else if (op == LIANA_PACKET_ERROR) {
- log_warn("Forcing EOF because we got an error packet.");
+ log_warn("Forcing EOF due to an error packet.");
}
break;
}
@@ -386,17 +395,17 @@ void lia_vcr_push_packet(struct lia_vcr *vcr, struct nn_packet *packet)
void lia_vcr_set_buffered(struct lia_vcr_track *track)
{
- al_atomic_store(bool)(&track->buffered, true, AL_ATOMIC_RELAXED);
+ atomic_store(bool)(&track->buffered, true, AL_ATOMIC_RELAXED);
}
void lia_vcr_cork(struct lia_vcr_track *track)
{
- al_atomic_store(s32)(&track->state, VCR_TRACK_STOPPED, AL_ATOMIC_RELAXED);
+ atomic_store(s32)(&track->state, VCR_TRACK_STOPPED, AL_ATOMIC_RELAXED);
}
void lia_vcr_uncork(struct lia_vcr_track *track)
{
- if (al_atomic_load(s32)(&track->state, AL_ATOMIC_RELAXED) != VCR_TRACK_STOPPED) {
+ if (atomic_load(s32)(&track->state, AL_ATOMIC_ACQUIRE) != VCR_TRACK_STOPPED) {
// We will get here during normal operation. Early returning is historically
// tricky in vcr_uncork(). If I'm understanding correctly, asserting that
// cond_is_waiting() just below means we are safe.
@@ -404,7 +413,7 @@ void lia_vcr_uncork(struct lia_vcr_track *track)
}
// Lock before setting track->state to avoid a race with cork().
nn_mutex_lock(&track->mutex);
- al_atomic_store(s32)(&track->state, VCR_TRACK_RUNNING, AL_ATOMIC_RELAXED);
+ atomic_store(s32)(&track->state, VCR_TRACK_RUNNING, AL_ATOMIC_RELEASE);
al_assert(nn_cond_is_waiting(&track->cond));
nn_cond_signal(&track->cond);
nn_mutex_unlock(&track->mutex);
@@ -416,7 +425,7 @@ static void vcr_track_close_internal(struct lia_vcr_track *track)
// Calling packet_cache_disable() while holding the track mutex can very possibly deadlock.
nn_packet_cache_disable(&track->cache);
nn_mutex_lock(&track->mutex);
- al_atomic_store(s32)(&track->state, VCR_TRACK_CLOSED, AL_ATOMIC_RELAXED);
+ atomic_store(s32)(&track->state, VCR_TRACK_CLOSED, AL_ATOMIC_RELAXED);
if (nn_cond_is_waiting(&track->cond)) {
nn_cond_signal(&track->cond);
}
@@ -451,17 +460,17 @@ void lia_vcr_flush(struct lia_vcr *vcr)
struct lia_vcr_track *track;
al_array_foreach(vcr->tracks, i, track) {
if (VCR_TRACK_THREADED(track)) {
- al_atomic_store(bool)(&track->buffered, false, AL_ATOMIC_RELAXED);
+ atomic_store(bool)(&track->buffered, false, AL_ATOMIC_RELAXED);
track->client->flush(track->client);
nn_packet_cache_enable(&track->cache);
- al_atomic_store(s32)(&track->state, VCR_TRACK_RUNNING, AL_ATOMIC_RELAXED);
+ atomic_store(s32)(&track->state, VCR_TRACK_RUNNING, AL_ATOMIC_RELAXED);
} else {
track->client->flush(track->client);
}
}
- al_atomic_store(u64)(&vcr->count, 0, AL_ATOMIC_RELAXED);
+ atomic_store(u64)(&vcr->count, 0, AL_ATOMIC_RELAXED);
if (vcr->expand == VCR_EXPAND_COMPLETE) {
- al_atomic_store(u64)(&vcr->mark.low, 0, AL_ATOMIC_RELAXED);
+ atomic_store(u64)(&vcr->mark.low, 0, AL_ATOMIC_RELAXED);
vcr->expand = VCR_EXPAND_GROWN;
}
}
diff --git a/src/libsink/desktop.c b/src/libsink/desktop.c
index 8582cd4..af6fc50 100644
--- a/src/libsink/desktop.c
+++ b/src/libsink/desktop.c
@@ -208,14 +208,16 @@ bool camu_desktop_open(struct camu_desktop *c, const char *window_name)
c->scr.callback = screen_callback;
c->scr.userdata = c;
if (!camu_screen_init(&c->scr) || !camu_screen_create_window(&c->scr, window_name)) {
+ log_error("Failed to create window.");
return false;
}
#if defined CAMU_RENDERER_MOMO
c->renderer = camu_renderer_momo_create();
#elif defined CAMU_RENDERER_LIBPLACEBO
- c->renderer = camu_renderer_lp_create();
+ c->renderer = camu_renderer_libplacebo_create();
#endif
if (!camu_screen_create_renderer(&c->scr, c->renderer)) {
+ log_error("Failed to create renderer.");
return false;
}
// Need to render twice for the window to show early.
@@ -252,7 +254,10 @@ bool camu_desktop_tick(struct camu_desktop *c)
{
bool force;
if (camu_screen_tick(&c->scr, &force) && !c->should_quit) {
- c->renderer->render(c->renderer, &c->scr, force);
+ if (!c->renderer->render(c->renderer, &c->scr, force)) {
+ log_error("render() failed, forcing exit.");
+ c->should_quit = 1;
+ }
}
return !c->should_quit;
}
diff --git a/src/libsink/input_simulator.c b/src/libsink/input_simulator.c
index 19786af..5b9d6e6 100644
--- a/src/libsink/input_simulator.c
+++ b/src/libsink/input_simulator.c
@@ -31,23 +31,23 @@ static nn_thread_result NNWT_THREADCALL input_simulation_thread(void *userdata)
switch (al_random_int(0, MARK - 1)) {
case SKIP: {
s32 n = al_random_int(1, 5);
- log_debug("SKIP (n: %d).", n);
+ log_info("SKIP (n: %d).", n);
camu_sink_skip(sink, n);
break;
}
case BACKSKIP: {
s32 n = al_random_int(-5, -1);
- log_debug("BACKSKIP (n: %d).", n);
+ log_info("BACKSKIP (n: %d).", n);
camu_sink_skip(sink, n);
break;
}
case SHUFFLE: {
- log_debug("SHUFFLE.");
+ log_info("SHUFFLE.");
camu_sink_shuffle(sink);
break;
}
case TOGGLE_PAUSE: {
- log_debug("TOGGLE_PAUSE.");
+ log_info("TOGGLE_PAUSE.");
camu_sink_toggle_pause(sink);
break;
}
@@ -56,7 +56,7 @@ static nn_thread_result NNWT_THREADCALL input_simulation_thread(void *userdata)
if (pos < 0.005) pos = 0.0;
if (pos > 0.995) pos = 1.0;
else if (pos > 0.99) pos = 0.9999;
- log_debug("SEEK (pos: %f).", pos);
+ log_info("SEEK (pos: %f).", pos);
camu_sink_seek(sink, &pos, CAMU_SEEK_PERCENT);
break;
}
diff --git a/src/libsink/sink.c b/src/libsink/sink.c
index e9b4df4..fccbc3e 100644
--- a/src/libsink/sink.c
+++ b/src/libsink/sink.c
@@ -10,7 +10,7 @@
#include "sink.h"
#include "common.h"
-#define CAMU_SINK_LOCAL
+//#define CAMU_SINK_LOCAL
//#define CAMU_SINK_ONESHOT
// Requested state of the sinks outputs.
@@ -71,8 +71,7 @@ AL_STATIC_ASSERT(max_age_lt_lru, ENTRY_MAX_AGE, <, SINK_LRU_MAX);
#define CONNECTION_NUMBER(id) ((id >> 48) & 0xffff)
// Only sink->target can ever be 0xb00b.
-#define OxbOOb ((struct camu_sink_entry *)0xb00b)
-#define ENTRY_IS_VALID(entry) ((entry) && (entry) != OxbOOb)
+#define ENTRY_IS_VALID(entry) ((entry) && (entry) != (struct camu_sink_entry *)0xb00b)
// printf format for entries.
#ifdef AL_DEBUG
@@ -87,14 +86,19 @@ AL_STATIC_ASSERT(max_age_lt_lru, ENTRY_MAX_AGE, <, SINK_LRU_MAX);
#define VIDEO_STATE(entry) ((entry)->video.state)
// If a buffer is still INIT or QUEUED after the entry is configured, it's "empty".
-// Also, if a buffer errors, it will be detached in CLIENT_REMOVE_BUFFERS.
-// Empty is a state that cannot change while a buffer is being used (push()/read()).
+// An ERRORED buffer will be DETACHED after CLIENT_REMOVE_BUFFERS and is then considered empty.
+// Empty is a state that cannot change while a buffer could be in use (push()/read()).
#define AUDIO_EMPTY(entry) (AUDIO_STATE(entry) <= BUFFER_DETACHED)
#define VIDEO_EMPTY(entry) (VIDEO_STATE(entry) <= BUFFER_DETACHED)
#define AUDIO_ENDED(entry) (AUDIO_STATE(entry) >= BUFFER_ENDED)
#define VIDEO_ENDED(entry) (VIDEO_STATE(entry) >= BUFFER_ENDED)
-#define ENTRY_ENDED(entry) ((AUDIO_EMPTY(entry) || AUDIO_ENDED(entry)) && (VIDEO_EMPTY(entry) || VIDEO_ENDED(entry)))
+/*
+#define ENTRY_ENDED(entry) \
+ ((AUDIO_ENDED(entry) && (VIDEO_ENDED(entry) || VIDEO_EMPTY(entry) || VIDEO_IS_SINGLE_FRAME(entry))) || \
+ (AUDIO_EMPTY(entry) && VIDEO_ENDED(entry)))
+*/
+#define ENTRY_ENDED(entry) entry->ended
// IGNORED = ENDED or EMPTY.
#define AUDIO_ADDED_OR_IGNORED(entry) (AUDIO_STATE(entry) >= BUFFER_ADDED || AUDIO_EMPTY(entry))
@@ -113,6 +117,8 @@ AL_STATIC_ASSERT(max_age_lt_lru, ENTRY_MAX_AGE, <, SINK_LRU_MAX);
#define BLOCKING_SLEEP(delay) nn_event_loop_sleep(sink->loop, delay)
#endif
+#define CMD(cmd, ...) ((struct camu_sink_cmd){ .op = cmd, __VA_ARGS__ })
+
// Functions that might happen on separate threads.
// CAMU_MIXER_THREADED:
// audio_buffer_callback()
@@ -127,7 +133,7 @@ AL_STATIC_ASSERT(max_age_lt_lru, ENTRY_MAX_AGE, <, SINK_LRU_MAX);
static inline bool entry_audio_buffer_held(struct camu_sink_entry *entry)
{
#ifdef CAMU_MIXER_THREADED
- return al_atomic_load(u8)(&entry->audio.buf.ref, AL_ATOMIC_RELAXED) == 1;
+ return atomic_load(u8)(&entry->audio.buf.ref, AL_ATOMIC_RELAXED) == 1;
#else
(void)entry;
return false;
@@ -137,19 +143,30 @@ static inline bool entry_audio_buffer_held(struct camu_sink_entry *entry)
static inline bool entry_video_buffer_held(struct camu_sink_entry *entry)
{
#ifdef CAMU_SCREEN_THREADED
- return al_atomic_load(u8)(&entry->video.buf.ref, AL_ATOMIC_RELAXED) == 1;
+ return atomic_load(u8)(&entry->video.buf.ref, AL_ATOMIC_RELAXED) == 1;
#else
(void)entry;
return false;
#endif
}
-static void queue_cmd(struct camu_sink *sink, struct camu_sink_cmd cmd)
+static void queue_cmds(struct camu_sink *sink, u32 count, ...)
{
- camu_queue_push(sink->queue, cmd);
+ camu_queue_lock(sink->queue);
+ va_list cmds;
+ va_start(cmds, count);
+ for (u32 i = 0; i < count; i++) {
+ camu_queue_push(sink->queue, va_arg(cmds, struct camu_sink_cmd));
+ }
+ camu_queue_unlock(sink->queue);
nn_signal_send(&sink->queue_signal);
}
+static void queue_cmd(struct camu_sink *sink, struct camu_sink_cmd cmd)
+{
+ queue_cmds(sink, 1, cmd);
+}
+
static void refresh_video_output(struct camu_sink *sink)
{
#ifndef CAMU_SINK_NO_VIDEO
@@ -165,11 +182,7 @@ static inline void add_entry_audio_buffer(struct camu_sink_entry *entry)
#ifdef CAMU_MIXER_THREADED_START_STOP
sink->callback(sink->userdata, CAMU_SINK_ADD_BUFFER, CAMU_SINK_AUDIO, &entry->audio.buf);
#else
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = ADD_BUFFER,
- .value.i = CAMU_SINK_AUDIO,
- .opaque = entry
- });
+ queue_cmd(sink, CMD(ADD_BUFFER, .v.u = CAMU_SINK_AUDIO, .opaque = entry));
#endif
}
@@ -194,11 +207,7 @@ static void remove_entry_audio_buffer(struct camu_sink_entry *entry)
#ifdef CAMU_MIXER_THREADED_START_STOP
sink->callback(sink->userdata, CAMU_SINK_REMOVE_BUFFER, CAMU_SINK_AUDIO, &entry->audio.buf);
#else
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = REMOVE_BUFFER,
- .value.i = CAMU_SINK_AUDIO,
- .opaque = entry
- });
+ queue_cmd(sink, CMD(REMOVE_BUFFER, .v.u = CAMU_SINK_AUDIO, .opaque = entry));
#endif
break;
case BUFFER_SET_OR_BUFFERED:
@@ -243,7 +252,6 @@ static void remove_entry_video_buffer(struct camu_sink_entry *entry)
static void remove_entry_buffers(struct camu_sink_entry *entry)
{
log_trace("remove_entry_buffers("ENTRY_FMT"), audio_state: %hhu, video_state: %hhu.", ENTRY_ARG(entry), AUDIO_STATE(entry), VIDEO_STATE(entry));
- al_assert(!entry->ended);
if (!AUDIO_ENDED(entry)) remove_entry_audio_buffer(entry);
if (!VIDEO_ENDED(entry)) remove_entry_video_buffer(entry);
}
@@ -256,14 +264,15 @@ static void add_or_queue_entry(struct camu_sink_entry *entry)
log_trace("add_or_queue_entry("ENTRY_FMT"), audio_state: %hhu, video_state: %hhu.", ENTRY_ARG(entry), AUDIO_STATE(entry), VIDEO_STATE(entry));
al_assert(AUDIO_STATE(entry) != BUFFER_QUEUED);
al_assert(VIDEO_STATE(entry) != BUFFER_QUEUED);
+ // Either buffer could be DETACHED.
if (AUDIO_STATE(entry) == BUFFER_INIT) {
AUDIO_STATE(entry) = BUFFER_QUEUED;
- } else if (!AUDIO_ENDED(entry)) {
+ } else if (!AUDIO_ENDED_OR_EMPTY(entry)) {
add_audio_if_set_and_buffered(entry);
}
if (VIDEO_STATE(entry) == BUFFER_INIT) {
VIDEO_STATE(entry) = BUFFER_QUEUED;
- } else if (!VIDEO_ENDED(entry)) {
+ } else if (!VIDEO_ENDED_OR_EMPTY(entry)) {
add_video_if_set_and_buffered(entry);
}
}
@@ -279,14 +288,14 @@ static void maybe_disconnect_entry(struct camu_sink_entry *entry)
}
#ifdef CAMU_SINK_LOCAL
-static void local_pause(struct camu_sink *sink, struct camu_sink_entry *entry)
+static void local_entry_pause(struct camu_sink *sink, struct camu_sink_entry *entry)
{
if (!camu_clock_is_paused(&entry->clock)) {
entry->paused = true;
camu_clock_pause(&entry->clock, 0);
log_info("Clock paused.");
// Audio stop will be handled by a BUFFER_PAUSED callback.
- if (!VIDEO_EMPTY(entry) && !VIDEO_IS_SINGLE_FRAME(entry) && sink->video.state == SINK_PLAYING) {
+ if (!VIDEO_ENDED_OR_EMPTY(entry) && !VIDEO_IS_SINGLE_FRAME(entry) && sink->video.state == SINK_PLAYING) {
#ifndef CAMU_SINK_NO_VIDEO
sink->callback(sink->userdata, CAMU_SINK_STOP, CAMU_SINK_VIDEO, NULL);
#endif
@@ -296,13 +305,13 @@ static void local_pause(struct camu_sink *sink, struct camu_sink_entry *entry)
entry->paused = false;
camu_clock_resume(&entry->clock, 0);
log_info("Clock resumed.");
- if (!VIDEO_EMPTY(entry) && !VIDEO_IS_SINGLE_FRAME(entry) && sink->video.state == SINK_PAUSED) {
+ if (!VIDEO_ENDED_OR_EMPTY(entry) && !VIDEO_IS_SINGLE_FRAME(entry) && sink->video.state == SINK_PAUSED) {
#ifndef CAMU_SINK_NO_VIDEO
sink->callback(sink->userdata, CAMU_SINK_START, CAMU_SINK_VIDEO, NULL);
#endif
sink->video.state = SINK_PLAYING;
}
- if (!AUDIO_EMPTY(entry) && sink->audio.state == SINK_PAUSED) {
+ if (!AUDIO_ENDED_OR_EMPTY(entry) && sink->audio.state == SINK_PAUSED) {
sink->callback(sink->userdata, CAMU_SINK_START, CAMU_SINK_AUDIO, NULL);
sink->audio.state = SINK_PLAYING;
}
@@ -310,6 +319,12 @@ static void local_pause(struct camu_sink *sink, struct camu_sink_entry *entry)
}
#endif
+// sink->current could be NULL.
+static inline struct camu_sink_entry *get_entry_for_command(struct camu_sink *sink)
+{
+ return ENTRY_IS_VALID(sink->target) ? sink->target : sink->current;
+}
+
static inline s32 get_sequence_for_command(struct camu_sink_entry *entry)
{
// SEQUENCE_ANY resolves order on the server.
@@ -329,7 +344,7 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd)
{
switch (cmd->op) {
case START: {
- switch (cmd->value.i) {
+ switch (cmd->v.u) {
case CAMU_SINK_AUDIO:
if (sink->audio.state == SINK_PAUSED) {
sink->callback(sink->userdata, CAMU_SINK_START, CAMU_SINK_AUDIO, NULL);
@@ -348,7 +363,7 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd)
break;
}
case STOP: {
- switch (cmd->value.i) {
+ switch (cmd->v.u) {
case CAMU_SINK_AUDIO:
if (sink->audio.state == SINK_PLAYING) {
sink->callback(sink->userdata, CAMU_SINK_STOP, CAMU_SINK_AUDIO, NULL);
@@ -368,7 +383,7 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd)
}
case ADD_BUFFER: {
struct camu_sink_entry *entry = (struct camu_sink_entry *)cmd->opaque;
- switch (cmd->value.i) {
+ switch (cmd->v.u) {
case CAMU_SINK_AUDIO:
sink->callback(sink->userdata, CAMU_SINK_ADD_BUFFER, CAMU_SINK_AUDIO, &entry->audio.buf);
break;
@@ -382,7 +397,7 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd)
}
case REMOVE_BUFFER: {
struct camu_sink_entry *entry = (struct camu_sink_entry *)cmd->opaque;
- switch (cmd->value.i) {
+ switch (cmd->v.u) {
case CAMU_SINK_AUDIO:
sink->callback(sink->userdata, CAMU_SINK_REMOVE_BUFFER, CAMU_SINK_AUDIO, &entry->audio.buf);
break;
@@ -395,7 +410,7 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd)
break;
}
case CLEAR_BUFFERS: {
- switch (cmd->value.i) {
+ switch (cmd->v.u) {
case CAMU_SINK_AUDIO:
sink->callback(sink->userdata, CAMU_SINK_CLEAR, CAMU_SINK_AUDIO, NULL);
break;
@@ -424,21 +439,21 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd)
nn_packet_write_str(packet, &sink->default_list);
nn_packet_write_u8(packet, CAMU_LIST_SKIP);
nn_packet_write_s32(packet, get_sequence_for_command(entry));
- nn_packet_write_s32(packet, (s32)cmd->value.i);
+ nn_packet_write_s32(packet, (s32)cmd->v.i);
nn_rpc_connection_command(sink->conn, packet, NULL, NULL);
break;
}
case TOGGLE_PAUSE: {
struct camu_sink_entry *entry = (struct camu_sink_entry *)cmd->opaque;
#ifdef CAMU_SINK_LOCAL
- if (!entry->held) local_pause(sink, entry);
+ if (!entry->held) local_entry_pause(sink, entry);
#else
if (!sink->conn) return;
struct nn_packet *packet = nn_rpc_get_packet(&sink->client, CAMU_SERVER_LIST_ACTION);
nn_packet_write_str(packet, &sink->default_list);
nn_packet_write_u8(packet, CAMU_LIST_TOGGLE_PAUSE);
nn_packet_write_s32(packet, get_sequence_for_command(entry));
- nn_packet_write_f64(packet, cmd->value.f);
+ nn_packet_write_f64(packet, cmd->v.f);
nn_rpc_connection_command(sink->conn, packet, NULL, NULL);
#endif
break;
@@ -451,7 +466,7 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd)
nn_packet_write_u8(packet, CAMU_LIST_SEEK);
nn_packet_write_s32(packet, entry->sequence);
nn_packet_write_u32(packet, REMOTE_ENTRY_ID(entry->id));
- nn_packet_write_u64(packet, cmd->value.u);
+ nn_packet_write_u64(packet, cmd->v.u);
nn_rpc_connection_command(sink->conn, packet, NULL, NULL);
break;
}
@@ -476,7 +491,7 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd)
nn_packet_write_str(packet, &sink->default_list);
nn_packet_write_u8(packet, CAMU_LIST_END);
nn_packet_write_u32(packet, REMOTE_ENTRY_ID(entry->id));
- nn_packet_write_u32(packet, (u32)cmd->value.u);
+ nn_packet_write_u32(packet, (u32)cmd->v.u);
nn_rpc_connection_command(sink->conn, packet, NULL, NULL);
break;
}
@@ -486,11 +501,11 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd)
static void queue_signal_callback(void *userdata)
{
struct camu_sink *sink = (struct camu_sink *)userdata;
- u32 size;
+ u32 count;
struct camu_sink_cmd cmd;
for (;;) {
- camu_queue_try_pop(sink->queue, size, cmd);
- if (size == 0) break;
+ camu_queue_try_pop(sink->queue, count, cmd);
+ if (count == 0) break;
handle_sink_cmd(sink, &cmd);
}
}
@@ -498,7 +513,8 @@ static void queue_signal_callback(void *userdata)
static void mixer_callback(void *userdata, u8 op)
{
struct camu_sink *sink = (struct camu_sink *)userdata;
- // @TODO: Isn't the idea of MIXER_EMPTY to not STOP audio when we know the mixer will be empty?
+ // @TODO: Isn't the idea of MIXER_EMPTY to not explicitly STOP the audio in places we
+ // expect the mixer to be empty (rely on MIXER_EMPTY).
if (op == CAMU_MIXER_EMPTY) {
log_info("Mixer empty.");
// Regardless of if we are checking an entry's state here, we have to sync with
@@ -506,10 +522,7 @@ static void mixer_callback(void *userdata, u8 op)
nn_mutex_lock(&sink->lock);
// This feels a bit too loose.
if (!(sink->current && AUDIO_STATE(sink->current) == BUFFER_ADDED)) {
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = STOP,
- .value.i = CAMU_SINK_AUDIO
- });
+ queue_cmd(sink, CMD(STOP, .v.u = CAMU_SINK_AUDIO));
}
nn_mutex_unlock(&sink->lock);
}
@@ -568,10 +581,7 @@ static void maybe_remove_previous(struct camu_sink *sink)
// Cleanup entries from old connections. This is especially important to
// keep reseek() from being overly wasteful.
if (CONNECTION_NUMBER(previous->id) != sink->connection_number) {
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = EJECT_ENTRY,
- .opaque = previous
- });
+ queue_cmd(sink, CMD(EJECT_ENTRY, .opaque = previous));
}
}
sink->previous.count = 0;
@@ -600,10 +610,9 @@ static void maybe_add_to_previous(struct camu_sink *sink, struct camu_sink_entry
{
log_trace("maybe_add_to_previous("ENTRY_FMT", "ENTRY_FMT").", ENTRY_ARG(previous), ENTRY_ARG(target));
al_assert(previous != target);
- al_assert(!previous->ended);
- // If none of the entry's buffers are added, we don't care about adding it to previous.
- bool dangling_target = target == OxbOOb;
- if (dangling_target || target->ended || (AUDIO_STATE(previous) != BUFFER_ADDED && VIDEO_STATE(previous) != BUFFER_ADDED)) {
+ // If none of an entry's buffers are added, we don't care about adding it to previous.
+ bool dangling_target = target == (struct camu_sink_entry *)0xb00b;
+ if (dangling_target || ENTRY_ENDED(target) || (AUDIO_STATE(previous) != BUFFER_ADDED && VIDEO_STATE(previous) != BUFFER_ADDED)) {
remove_entry_buffers(previous);
return;
}
@@ -613,18 +622,14 @@ static void maybe_add_to_previous(struct camu_sink *sink, struct camu_sink_entry
static void after_add_entry(struct camu_sink_entry *entry, bool skip_audio, bool skip_video)
{
maybe_remove_previous(entry->sink);
- queue_cmd(entry->sink, (struct camu_sink_cmd){
- .op = (skip_video || entry->paused) ? STOP : START,
- .value.i = CAMU_SINK_VIDEO
- });
+ queue_cmds(entry->sink, 2,
+ CMD((skip_video || entry->paused) ? STOP : START, .v.u = CAMU_SINK_VIDEO),
+ CMD((skip_audio || entry->paused) ? STOP : START, .v.u = CAMU_SINK_AUDIO)
+ );
if (VIDEO_ENDED_OR_EMPTY(entry)) {
// Clear the screen if skipping from a video to an audio-only entry.
refresh_video_output(entry->sink);
}
- queue_cmd(entry->sink, (struct camu_sink_cmd){
- .op = (skip_audio || entry->paused) ? STOP : START,
- .value.i = CAMU_SINK_AUDIO
- });
}
// Call this after setting state to ADDED because this entry might be in previous.
@@ -641,7 +646,7 @@ static void do_add_entry(struct camu_sink_entry *entry)
void add_audio_if_set_and_buffered(struct camu_sink_entry *entry)
{
- al_assert(!entry->ended);
+ al_assert(!ENTRY_ENDED(entry));
al_assert(AUDIO_STATE(entry) != BUFFER_INIT);
al_assert(AUDIO_STATE(entry) != BUFFER_QUEUED);
al_assert(AUDIO_STATE(entry) != BUFFER_ADDED);
@@ -662,7 +667,7 @@ void add_audio_if_set_and_buffered(struct camu_sink_entry *entry)
void add_video_if_set_and_buffered(struct camu_sink_entry *entry)
{
// Single frame entries will be added/removed with ended set.
- if (entry->ended) al_assert(VIDEO_IS_SINGLE_FRAME(entry));
+ if (ENTRY_ENDED(entry)) al_assert(VIDEO_IS_SINGLE_FRAME(entry));
al_assert(VIDEO_STATE(entry) != BUFFER_INIT);
al_assert(VIDEO_STATE(entry) != BUFFER_QUEUED);
al_assert(VIDEO_STATE(entry) != BUFFER_ADDED);
@@ -690,56 +695,34 @@ static void switch_to(struct camu_sink *sink, struct camu_sink_entry *target)
{
struct camu_sink_entry *current = sink->current;
- log_trace("switch_to("ENTRY_FMT"(ended: %s)), current: "ENTRY_FMT"(ended: %s).",
- ENTRY_ARG(target), BOOLSTR(ENTRY_IS_VALID(target) ? target->ended : false),
- ENTRY_ARG(current), BOOLSTR(current ? current->ended : false));
+ log_trace("switch_to("ENTRY_FMT"), current: "ENTRY_FMT".", ENTRY_ARG(target), ENTRY_ARG(current));
- bool ensure_removed = false;
if (current) {
struct camu_sink_entry *suspended = sink->suspended;
al_assert(current != target);
- ensure_removed = suspended || current->ended;
if (suspended) {
al_assert(suspended == current);
- // A buffer's state being QUEUED should be impossible while it's
- // entry is suspended.
+ // It should be impossible for a buffer to be QUEUED while it's entry is suspended.
al_assert(AUDIO_STATE(suspended) != BUFFER_QUEUED);
al_assert(VIDEO_STATE(suspended) != BUFFER_QUEUED);
sink->suspended = NULL;
log_warn("Unset suspended entry as a substitute for remove.");
- } else if (!current->ended) {
+ } else {
maybe_add_to_previous(sink, current, target);
}
}
- // @TODO: Cleanup stop_video? and log_trace lengths.
- bool stop_video = false;
- bool dangling_target = target == OxbOOb;
+ bool dangling_target = target == (struct camu_sink_entry *)0xb00b;
+ bool stop_video = dangling_target;
if (dangling_target) {
log_trace("Ignored dangling target.");
} else {
- if (!target->ended) {
- remove_previous_if_contains(sink, target);
- add_or_queue_entry(target);
- } else {
- log_trace("Target ended in switch_to().");
- if (!VIDEO_ENDED_OR_EMPTY(target) && VIDEO_IS_SINGLE_FRAME(target)) {
- add_video_if_set_and_buffered(target);
- }
- stop_video = true;
- }
- }
-
- if (ensure_removed) {
- if (!VIDEO_ENDED_OR_EMPTY(current) && VIDEO_IS_SINGLE_FRAME(current)) {
- remove_entry_video_buffer(current);
- }
- al_assert(AUDIO_STATE(current) != BUFFER_ADDED);
- al_assert(VIDEO_STATE(current) != BUFFER_ADDED);
+ remove_previous_if_contains(sink, target);
+ add_or_queue_entry(target);
+ stop_video = VIDEO_ENDED_OR_EMPTY(target) || VIDEO_IS_SINGLE_FRAME(target);
}
if (dangling_target) {
- stop_video = true;
sink->current = NULL;
} else {
target->audio.ignore_paused = false;
@@ -750,10 +733,7 @@ static void switch_to(struct camu_sink *sink, struct camu_sink_entry *target)
}
if (stop_video) {
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = STOP,
- .value.i = CAMU_SINK_VIDEO
- });
+ queue_cmd(sink, CMD(STOP, .v.u = CAMU_SINK_VIDEO));
refresh_video_output(sink);
}
}
@@ -781,17 +761,19 @@ static void pause_and_swap_to(struct camu_sink *sink, struct camu_sink_entry *ta
al_assert(target != current);
al_assert(!sink->target);
// This is extra verbose because the order is important.
- // 1. sink->target has to be set before calling clock_pause().
- // 2. current must still be paused even if it's ended.
- // 3. In the immediate case, switch_to() has to come last.
- if (current && !current->ended) {
+ // 1. sink->target has to be set before calling clock_pause().
+ if (current && !ENTRY_ENDED(current)) {
sink->target = target;
}
+ // 2. current must still be paused, even if it's ended.
if (current) {
current->audio.ignore_paused = true;
camu_clock_pause(&current->clock, at);
}
- if (!current || current->ended) switch_to(sink, target);
+ // 3. In the immediate swap case, switch_to() has to come last.
+ if (!current || ENTRY_ENDED(current)) {
+ switch_to(sink, target);
+ }
}
#endif
@@ -805,16 +787,14 @@ static bool end_entry_and_advance_queue(struct camu_sink *sink, struct camu_sink
#endif
// This entry's buffers cannot be added again until after a reset.
entry->ended = true;
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = END,
- .value.u = entry->reset_token,
- .opaque = entry
- });
+ al_assert(ENTRY_ENDED(entry));
+ queue_cmd(sink, CMD(END, .v.u = entry->reset_token, .opaque = entry));
#ifdef LIANA_LIST_SCUFFED_LOOP
log_info("Looping.");
return true;
#endif
if (sink->target) {
+ log_info("Buffers swapped on end() (Gapless if queued).");
switch_to(sink, sink->target);
sink->target = NULL;
return true;
@@ -843,17 +823,18 @@ static void audio_buffer_callback(void *userdata, u8 op)
nn_mutex_lock(&sink->lock);
if (!entry->audio.ignore_paused && entry->paused) {
log_info("Audio buffer paused.");
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = STOP,
- .value.i = CAMU_SINK_AUDIO
- });
+ queue_cmd(sink, CMD(STOP, .v.u = CAMU_SINK_AUDIO));
}
nn_mutex_unlock(&sink->lock);
break;
case CAMU_BUFFER_EOF:
case CAMU_BUFFER_ERRORED: {
bool error = op == CAMU_BUFFER_ERRORED;
- log_debug(error ? "Audio buffer errored." : "Audio EOF.");
+ if (error) {
+ log_error("Audio buffer errored.");
+ } else {
+ log_debug("Audio EOF.");
+ }
nn_mutex_lock(&sink->lock);
// EOF and ERRORED come from the outputs read() thread. So, having threaded
// outputs means anything could have happened while waiting on the lock above.
@@ -894,7 +875,11 @@ static void video_buffer_callback(void *userdata, u8 op)
case CAMU_BUFFER_EOF:
case CAMU_BUFFER_ERRORED: {
bool error = op == CAMU_BUFFER_ERRORED;
- log_debug(error ? "Video buffer errored." : "Video EOF.");
+ if (error) {
+ log_error("Video buffer errored.");
+ } else {
+ log_debug("Video EOF.");
+ }
nn_mutex_lock(&sink->lock);
if (!AUDIO_EMPTY(entry)) {
camu_audio_buffer_set_no_video(&entry->audio.buf, true);
@@ -915,10 +900,7 @@ static void video_buffer_callback(void *userdata, u8 op)
}
nn_mutex_unlock(&sink->lock);
if (!swapped) {
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = STOP,
- .value.i = CAMU_SINK_VIDEO
- });
+ queue_cmd(sink, CMD(STOP, .v.u = CAMU_SINK_VIDEO));
}
break;
}
@@ -937,10 +919,7 @@ static void clock_callback(void *userdata, u8 op)
switch_to(sink, sink->target);
sink->target = NULL;
} else if (entry->paused) {
- queue_cmd(entry->sink, (struct camu_sink_cmd){
- .op = STOP,
- .value.i = CAMU_SINK_VIDEO
- });
+ queue_cmd(sink, CMD(STOP, .v.u = CAMU_SINK_VIDEO));
}
}
nn_mutex_unlock(&sink->lock);
@@ -953,20 +932,11 @@ static void evaluate_and_set_buffer_params(struct camu_sink *sink, struct camu_s
f64 avg_frame_duration = entry->video.buf.avg_frame_duration;
#ifdef CAMU_SINK_LOCAL
if (!AUDIO_EMPTY(entry) && !ignore_video) {
- // Delay either the audio or video so we can start the clock as
- // soon as possible while keeping A/V sync.
f64 audio = camu_mixer_get_latency(sink->audio.mixer);
struct camu_renderer *renderer = sink->video.renderer;
f64 video = renderer->get_latency(renderer) * avg_frame_duration;
- // If the audio buffer is delayed by less than the audio latency, data will be skipped.
- f64 base = -audio;
- if (audio > video) {
- camu_audio_buffer_set_latency(&entry->audio.buf, base);
- camu_video_buffer_set_latency(&entry->video.buf, base - (audio - video));
- } else if (video > audio) {
- camu_audio_buffer_set_latency(&entry->audio.buf, base);
- camu_video_buffer_set_latency(&entry->video.buf, base + (video - audio));
- }
+ camu_audio_buffer_set_latency(&entry->audio.buf, -audio);
+ camu_video_buffer_set_latency(&entry->video.buf, -video);
}
// If we're local we don't have to worry about syncing audio-only entries.
camu_audio_buffer_set_ignore_desync(&entry->audio.buf, ignore_video);
@@ -1013,6 +983,7 @@ static void remove_from_queue_by_opaque(struct camu_sink *sink, void *opaque)
static void client_callback(void *userdata, u8 op, struct camu_codec_stream *stream, void *opaque)
{
struct camu_sink_entry *entry = (struct camu_sink_entry *)userdata;
+ al_assert(ENTRY_IS_VALID(entry));
struct camu_sink *sink = entry->sink;
switch (op) {
case LIANA_CLIENT_CONFIGURE: {
@@ -1106,13 +1077,8 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
log_trace("remove_buffers("ENTRY_FMT", %s, %s), entry == current: %s.", ENTRY_ARG(entry), BOOLSTR(rec->reconnect), BOOLSTR(rec->unconfigured), BOOLSTR(entry == sink->current));
if (entry == sink->current) {
- if (!entry->ended) {
- // It should only be possible for a buffer to be INIT if entry is not current or ended.
- // Ended case is: (audio or video buffer empty) -> entry switched off of -> entry ended -> switch_to()'d.
- // - The empty buffer doesn't get re-QUEUED because the entry is ended.
- al_assert(AUDIO_STATE(entry) != BUFFER_INIT);
- al_assert(VIDEO_STATE(entry) != BUFFER_INIT);
- }
+ al_assert(AUDIO_STATE(entry) != BUFFER_INIT);
+ al_assert(VIDEO_STATE(entry) != BUFFER_INIT);
if (sink->target) {
// This is necessary to avoid re-adding an entry with an in-between clock state.
// See note in clock.c::camu_clock_seek().
@@ -1140,7 +1106,6 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
// Ignore unconfigured entries.
if (rec->unconfigured) {
- al_assert(!entry->ended);
al_assert(AUDIO_EMPTY(entry) && VIDEO_EMPTY(entry));
nn_mutex_unlock(&sink->lock);
return;
@@ -1309,7 +1274,7 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
remove_from_queue_by_opaque(sink, entry);
if (entry == sink->target) {
- sink->target = OxbOOb;
+ sink->target = (struct camu_sink_entry *)0xb00b;
log_warn("Attempting to handle a disconnected target.");
} else if (entry == sink->current) {
if (sink->suspended) {
@@ -1322,10 +1287,7 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
} else {
sink->current = NULL;
if (removed) { // Don't stop video on exit.
- queue_cmd(entry->sink, (struct camu_sink_cmd){
- .op = STOP,
- .value.i = CAMU_SINK_VIDEO
- });
+ queue_cmd(sink, CMD(STOP, .v.u = CAMU_SINK_VIDEO));
}
}
// If current was never fully added we need to call this here.
@@ -1401,9 +1363,12 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn,
u8 op = nn_packet_read_u8(packet);
if (op == LIANA_SINK_UNSET) {
nn_mutex_lock(&sink->lock);
- if (sink->current) {
- maybe_disconnect_entry(sink->current);
+ struct camu_sink_entry *current = sink->current;
+ nn_mutex_unlock(&sink->lock);
+ if (current) {
+ maybe_disconnect_entry(current);
}
+ nn_mutex_lock(&sink->lock);
goto out;
}
@@ -1499,7 +1464,7 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn,
case LIANA_PAUSE_PAUSE:
if (prev_target) {
al_assert(current);
- al_assert(!current->ended);
+ al_assert(!ENTRY_ENDED(current));
al_assert(prev_target != entry);
if (entry == current) {
// pause_and_swap_to() negated.
@@ -1507,7 +1472,7 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn,
} else {
sink->target = entry;
}
- if (prev_target != OxbOOb) {
+ if (prev_target != (struct camu_sink_entry *)0xb00b) {
camu_clock_pause(&prev_target->clock, at);
}
} else {
@@ -1518,7 +1483,7 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn,
case LIANA_PAUSE_BOTH:
if (prev_target) {
al_assert(current);
- al_assert(!current->ended);
+ al_assert(!ENTRY_ENDED(current));
al_assert(prev_target != entry);
if (entry == current) {
sink->target = NULL;
@@ -1526,7 +1491,7 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn,
} else {
sink->target = entry;
}
- if (prev_target != OxbOOb) {
+ if (prev_target != (struct camu_sink_entry *)0xb00b) {
camu_clock_pause(&prev_target->clock, at);
}
} else {
@@ -1562,12 +1527,14 @@ static bool pause_command_callback(void *userdata, struct nn_rpc_connection *con
struct camu_sink_entry *entry = get_entry_from_id(sink, id);
if (!entry) goto out;
- al_assert(entry->sequence == sequence);
nn_mutex_lock(&sink->lock);
- log_trace("pause("ENTRY_FMT"), %s, audio_state: %hhu, video_state: %hhu.", ENTRY_ARG(entry), lia_pause_op_name(pause), AUDIO_STATE(entry), VIDEO_STATE(entry));
+ // As long as the list discards skips with a non-current sequence, this should hold true.
+ al_assert(entry->sequence == sequence);
+ log_trace("pause("ENTRY_FMT"), %s, audio_state: %hhu, video_state: %hhu.",
+ ENTRY_ARG(entry), lia_pause_op_name(pause), AUDIO_STATE(entry), VIDEO_STATE(entry));
#ifdef CAMU_SINK_LOCAL
(void)at;
- if (!entry->held) local_pause(sink, entry);
+ if (!entry->held) local_entry_pause(sink, entry);
#else
switch (pause) {
case LIANA_PAUSE_PAUSE: {
@@ -1582,18 +1549,12 @@ static bool pause_command_callback(void *userdata, struct nn_rpc_connection *con
entry->paused = false;
camu_clock_resume(&entry->clock, at);
log_info("Clock resumed.");
- if (!VIDEO_EMPTY(entry) && !VIDEO_IS_SINGLE_FRAME(entry)) {
- queue_cmd(entry->sink, (struct camu_sink_cmd){
- .op = START,
- .value.i = CAMU_SINK_VIDEO
- });
+ if (!VIDEO_ENDED_OR_EMPTY(entry) && !VIDEO_IS_SINGLE_FRAME(entry)) {
+ queue_cmd(sink, CMD(START, .v.u = CAMU_SINK_VIDEO));
}
- if (!AUDIO_EMPTY(entry)) {
+ if (!AUDIO_ENDED_OR_EMPTY(entry)) {
camu_audio_buffer_resync(&entry->audio.buf);
- queue_cmd(entry->sink, (struct camu_sink_cmd){
- .op = START,
- .value.i = CAMU_SINK_AUDIO
- });
+ queue_cmd(sink, CMD(START, .v.u = CAMU_SINK_AUDIO));
}
break;
}
@@ -1615,7 +1576,6 @@ static bool seek_command_callback(void *userdata, struct nn_rpc_connection *conn
u64 id = LOCAL_ENTRY_ID(sink, nn_packet_read_u32(packet));
s32 sequence = nn_packet_read_s32(packet);
- (void)sequence;
u64 at = nn_packet_read_u64(packet);
u64 pos = nn_packet_read_u64(packet);
u32 reset_token = nn_packet_read_u32(packet);
@@ -1623,7 +1583,8 @@ static bool seek_command_callback(void *userdata, struct nn_rpc_connection *conn
struct camu_sink_entry *entry = get_entry_from_id(sink, id);
if (!entry) goto out;
nn_mutex_lock(&sink->lock);
- log_trace("seek("ENTRY_FMT"), reset_token: %u.", ENTRY_ARG(entry), reset_token);
+ entry->sequence = sequence;
+ log_trace("seek("ENTRY_FMT", %.2f), reset_token: %u.", ENTRY_ARG(entry), pos / 1000000.0, reset_token);
entry->reset_token = reset_token;
nn_mutex_unlock(&sink->lock);
// The rest of the seek is handled in CLIENT_REMOVE_BUFFERS/RESUME_AT/RECONNECTED.
@@ -1761,22 +1722,12 @@ void camu_sink_return_current(struct camu_sink *sink)
nn_mutex_unlock(&sink->lock);
}
-// sink->current could be NULL.
-static inline struct camu_sink_entry *get_entry_for_command(struct camu_sink *sink)
-{
- return ENTRY_IS_VALID(sink->target) ? sink->target : sink->current;
-}
-
void camu_sink_skip(struct camu_sink *sink, s32 n)
{
nn_mutex_lock(&sink->lock);
struct camu_sink_entry *current = get_entry_for_command(sink);
nn_mutex_unlock(&sink->lock);
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = SKIP,
- .value.i = n,
- .opaque = current
- });
+ queue_cmd(sink, CMD(SKIP, .v.i = n, .opaque = current));
}
void camu_sink_toggle_pause(struct camu_sink *sink)
@@ -1785,11 +1736,8 @@ void camu_sink_toggle_pause(struct camu_sink *sink)
struct camu_sink_entry *current = get_entry_for_command(sink);
nn_mutex_unlock(&sink->lock);
if (!current) return;
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = TOGGLE_PAUSE,
- .value.f = camu_clock_get_pts(&current->clock, 0.0, false),
- .opaque = current
- });
+ f64 pts = camu_clock_get_pts(&current->clock, 0.0, false);
+ queue_cmd(sink, CMD(TOGGLE_PAUSE, .v.f = pts, .opaque = current));
}
void camu_sink_seek(struct camu_sink *sink, void *value, u8 mode)
@@ -1811,19 +1759,19 @@ void camu_sink_seek(struct camu_sink *sink, void *value, u8 mode)
switch (mode) {
case CAMU_SEEK_POS: {
u64 pos = *(u64 *)value;
- cmd.value.u = pos;
+ cmd.v.u = pos;
break;
}
case CAMU_SEEK_RELATIVE: {
f64 offset = *(f64 *)value;
pts = MAX(pts + offset, 0.0);
- cmd.value.u = (u64)(pts * 1000000);
+ cmd.v.u = (u64)(pts * 1000000);
break;
}
case CAMU_SEEK_PERCENT: {
// There's probably a way to lose less precision here.
f64 percent = *(f64 *)value;
- cmd.value.u = (u64)(duration * percent);
+ cmd.v.u = (u64)(duration * percent);
break;
}
}
@@ -1832,31 +1780,21 @@ void camu_sink_seek(struct camu_sink *sink, void *value, u8 mode)
void camu_sink_reseek(struct camu_sink *sink)
{
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = RESEEK
- });
+ queue_cmd(sink, CMD(RESEEK));
}
void camu_sink_shuffle(struct camu_sink *sink)
{
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = SHUFFLE
- });
+ queue_cmd(sink, CMD(SHUFFLE));
}
void camu_sink_stop(struct camu_sink *sink)
{
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = STOP,
- .value.i = CAMU_SINK_AUDIO
- });
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = CLEAR_BUFFERS,
- .value.i = CAMU_SINK_AUDIO
- });
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = CLOSE
- });
+ queue_cmds(sink, 3,
+ CMD(STOP, .v.u = CAMU_SINK_AUDIO),
+ CMD(CLEAR_BUFFERS, .v.u = CAMU_SINK_AUDIO),
+ CMD(CLOSE)
+ );
}
void camu_sink_close(struct camu_sink *sink)
diff --git a/src/libsink/sink.h b/src/libsink/sink.h
index 5958829..1e8bbf1 100644
--- a/src/libsink/sink.h
+++ b/src/libsink/sink.h
@@ -56,7 +56,7 @@ struct camu_sink_entry {
bool paused;
struct {
u8 state;
- // Don't stop the audio output on BUFFER_PAUSED from this entry.
+ // Don't stop audio on a BUFFER_PAUSED from this entry.
bool ignore_paused;
struct camu_audio_buffer buf;
struct lia_vcr_track *track;
@@ -71,7 +71,7 @@ struct camu_sink_entry {
struct camu_sink_cmd {
u8 op;
- union { s64 i; u64 u; f64 f; } value;
+ union { s64 i; u64 u; f64 f; } v;
void *opaque;
};
@@ -90,7 +90,7 @@ struct camu_sink {
queue(struct camu_sink_cmd) queue;
struct camu_sink_entry *current;
struct camu_sink_entry *target;
- // If an entry that was current was removed for a reconnect, it's "suspended".
+ // If current was removed for a reconnect, it's "suspended".
struct camu_sink_entry *suspended;
array(struct camu_sink_entry *) previous;
array(struct camu_sink_entry *) entries;
diff --git a/src/mixer/audio_miniaudio.c b/src/mixer/audio_miniaudio.c
index 993bcca..c0e7cf4 100644
--- a/src/mixer/audio_miniaudio.c
+++ b/src/mixer/audio_miniaudio.c
@@ -34,19 +34,19 @@ static ma_backend backends[] = {
#endif
};
-static void *miniaudio_malloc(size_t size, void *userdata)
+static void *ma_on_malloc(size_t size, void *userdata)
{
(void)userdata;
return al_malloc(size);
}
-static void *miniaudio_realloc(void *ptr, size_t size, void *userdata)
+static void *ma_on_realloc(void *ptr, size_t size, void *userdata)
{
(void)userdata;
return al_realloc(ptr, size);
}
-static void miniaudio_free(void *ptr, void *userdata)
+static void ma_on_free(void *ptr, void *userdata)
{
(void)userdata;
al_free(ptr);
@@ -54,12 +54,12 @@ static void miniaudio_free(void *ptr, void *userdata)
static ma_allocation_callbacks alloc_callbacks = {
.pUserData = NULL,
- .onMalloc = miniaudio_malloc,
- .onRealloc = miniaudio_realloc,
- .onFree = miniaudio_free
+ .onMalloc = ma_on_malloc,
+ .onRealloc = ma_on_realloc,
+ .onFree = ma_on_free
};
-static void miniaudio_log_callback(void *userdata, u32 level, const char *message)
+static void log_callback(void *userdata, u32 level, const char *message)
{
(void)userdata;
if (level == MA_LOG_LEVEL_ERROR) {
@@ -71,14 +71,14 @@ static void miniaudio_log_callback(void *userdata, u32 level, const char *messag
}
}
-static bool audio_miniaudio_init(struct camu_audio *audio, str *name)
+static bool audio_ma_init(struct camu_audio *audio, str *name)
{
- struct camu_audio_miniaudio *ma = (struct camu_audio_miniaudio *)audio;
+ struct camu_audio_ma *ma = (struct camu_audio_ma *)audio;
#ifdef CAMU_NO_MINIAUDIO_BACKENDS
al_assert_and_return(false);
#endif
ma_log_init(&alloc_callbacks, &ma->log);
- ma_log_register_callback(&ma->log, ma_log_callback_init(miniaudio_log_callback, NULL));
+ ma_log_register_callback(&ma->log, ma_log_callback_init(log_callback, NULL));
ma->context_config = ma_context_config_init();
ma->context_config.pLog = &ma->log;
#ifdef MA_ENABLE_PULSEAUDIO
@@ -140,9 +140,9 @@ static s32 camu_sample_format_from_miniaudio(s32 fmt)
}
}
-static void audio_miniaudio_pick_format(struct camu_audio *audio, struct camu_resampler_format *fmt)
+static void audio_ma_pick_format(struct camu_audio *audio, struct camu_resampler_format *fmt)
{
- struct camu_audio_miniaudio *ma = (struct camu_audio_miniaudio *)audio;
+ struct camu_audio_ma *ma = (struct camu_audio_ma *)audio;
fmt->req.format = camu_sample_format_from_miniaudio(ma->device.playback.format);
fmt->req.sample_rate = ma->device.playback.internalSampleRate;
u32 channels = ma->device.playback.channels;
@@ -154,7 +154,7 @@ static void audio_miniaudio_pick_format(struct camu_audio *audio, struct camu_re
static void data_callback(ma_device *device, void *output, const void *input, u32 frame_count)
{
- struct camu_audio_miniaudio *ma = (struct camu_audio_miniaudio *)device->pUserData;
+ struct camu_audio_ma *ma = (struct camu_audio_ma *)device->pUserData;
(void)input;
bool silence;
ma->a.data_callback(ma->a.userdata, (u8 *)output, frame_count, &silence);
@@ -170,9 +170,9 @@ static void data_callback(ma_device *device, void *output, const void *input, u3
#define DEFAULT_PERIOD_SIZE_IN_MILLISECONDS 48
#endif
-static bool audio_miniaudio_configure_stream(struct camu_audio *audio, void *opaque)
+static bool audio_ma_configure_stream(struct camu_audio *audio, void *opaque)
{
- struct camu_audio_miniaudio *ma = (struct camu_audio_miniaudio *)audio;
+ struct camu_audio_ma *ma = (struct camu_audio_ma *)audio;
struct camu_ff_resample_fmt *fmt = (struct camu_ff_resample_fmt *)opaque;
(void)fmt;
ma->config = ma_device_config_init(ma_device_type_playback);
@@ -225,9 +225,9 @@ static bool audio_miniaudio_configure_stream(struct camu_audio *audio, void *opa
return true;
}
-static u64 audio_miniaudio_get_latency(struct camu_audio *audio)
+static u64 audio_ma_get_latency(struct camu_audio *audio)
{
- struct camu_audio_miniaudio *ma = (struct camu_audio_miniaudio *)audio;
+ struct camu_audio_ma *ma = (struct camu_audio_ma *)audio;
log_debug("periods: %u, period_size_in_frames: %u, sample_rate: %u.",
ma->device.playback.internalPeriods,
ma->device.playback.internalPeriodSizeInFrames,
@@ -237,21 +237,21 @@ static u64 audio_miniaudio_get_latency(struct camu_audio *audio)
(ma->device.playback.internalSampleRate / 1000000.0);
}
-static void audio_miniaudio_start(struct camu_audio *audio)
+static void audio_ma_start(struct camu_audio *audio)
{
- struct camu_audio_miniaudio *ma = (struct camu_audio_miniaudio *)audio;
+ struct camu_audio_ma *ma = (struct camu_audio_ma *)audio;
ma_device_start(&ma->device);
}
-static void audio_miniaudio_stop(struct camu_audio *audio)
+static void audio_ma_stop(struct camu_audio *audio)
{
- struct camu_audio_miniaudio *ma = (struct camu_audio_miniaudio *)audio;
+ struct camu_audio_ma *ma = (struct camu_audio_ma *)audio;
ma_device_stop(&ma->device);
}
-static void audio_miniaudio_free(struct camu_audio **audio)
+static void audio_ma_free(struct camu_audio **audio)
{
- struct camu_audio_miniaudio *ma = (struct camu_audio_miniaudio *)*audio;
+ struct camu_audio_ma *ma = (struct camu_audio_ma *)*audio;
ma_device_uninit(&ma->device);
ma_context_uninit(&ma->context);
#ifdef MA_ENABLE_PULSEAUDIO
@@ -263,15 +263,15 @@ static void audio_miniaudio_free(struct camu_audio **audio)
struct camu_audio *camu_audio_miniaudio_create(void)
{
- struct camu_audio_miniaudio *ma = al_alloc_object(struct camu_audio_miniaudio);
+ struct camu_audio_ma *ma = al_alloc_object(struct camu_audio_ma);
ma->a = (struct camu_audio){
- .init = audio_miniaudio_init,
- .pick_format = audio_miniaudio_pick_format,
- .configure_stream = audio_miniaudio_configure_stream,
- .get_latency = audio_miniaudio_get_latency,
- .start = audio_miniaudio_start,
- .stop = audio_miniaudio_stop,
- .free = audio_miniaudio_free,
+ .init = audio_ma_init,
+ .pick_format = audio_ma_pick_format,
+ .configure_stream = audio_ma_configure_stream,
+ .get_latency = audio_ma_get_latency,
+ .start = audio_ma_start,
+ .stop = audio_ma_stop,
+ .free = audio_ma_free,
.data_callback = NULL,
.userdata = NULL
};
diff --git a/src/mixer/audio_miniaudio.h b/src/mixer/audio_miniaudio.h
index c067024..47e1088 100644
--- a/src/mixer/audio_miniaudio.h
+++ b/src/mixer/audio_miniaudio.h
@@ -9,7 +9,7 @@
#include "audio.h"
-struct camu_audio_miniaudio {
+struct camu_audio_ma {
struct camu_audio a;
ma_log log;
ma_context_config context_config;
diff --git a/src/portal/src/search.c b/src/portal/src/search.c
index a1b7310..3f012bf 100644
--- a/src/portal/src/search.c
+++ b/src/portal/src/search.c
@@ -75,6 +75,7 @@ static nn_thread_result NNWT_THREADCALL queue_thread(void *userdata)
// Defer python init.
if (!(have_python = camu_python_init())) {
log_error("Failed to initialize python.");
+ bridge->quit = 1;
break;
}
}
@@ -125,13 +126,15 @@ static nn_thread_result NNWT_THREADCALL queue_thread(void *userdata)
}
}
} else {
- log_info("Requested search doesn't exist (id: %d).", search->id);
result.id = -1;
+ log_info("Requested search doesn't exist (id: %d).", cmd->id);
}
break;
}
}
+ camu_queue_lock(bridge->results);
camu_queue_push(bridge->results, result);
+ camu_queue_unlock(bridge->results);
}
bridge->queue.count = 0;
nn_signal_send(&bridge->results_signal);
@@ -145,7 +148,9 @@ static nn_thread_result NNWT_THREADCALL queue_thread(void *userdata)
result.callback = cmd->callback;
result.userdata = cmd->userdata;
result.id = -1;
+ camu_queue_lock(bridge->results);
camu_queue_push(bridge->results, result);
+ camu_queue_unlock(bridge->results);
}
bridge->queue.count = 0;
nn_signal_send(&bridge->results_signal);
@@ -187,13 +192,18 @@ void camu_portal_init(struct camu_portal_bridge *bridge, struct camu_post_cache
void camu_portal_create_search(struct camu_portal_bridge *bridge, str *module, str *query,
void (*callback)(void *, void *, struct camu_portal_result *), void *userdata)
{
+ nn_mutex_lock(&bridge->mutex);
+ if (bridge->quit) {
+ callback(bridge->userdata, userdata, NULL);
+ nn_mutex_unlock(&bridge->mutex);
+ return;
+ }
struct camu_portal_cmd cmd = { 0 };
cmd.op = CAMU_CLIENT_CREATE_SEARCH;
al_str_clone(&cmd.module, module);
al_str_clone(&cmd.query, query);
cmd.callback = callback;
cmd.userdata = userdata;
- nn_mutex_lock(&bridge->mutex);
al_array_push(bridge->queue, cmd);
if (nn_cond_is_waiting(&bridge->cond)) {
nn_cond_signal(&bridge->cond);
@@ -204,13 +214,18 @@ void camu_portal_create_search(struct camu_portal_bridge *bridge, str *module, s
void camu_portal_get_page(struct camu_portal_bridge *bridge, s32 id, u32 num,
void (*callback)(void *, void *, struct camu_portal_result *), void *userdata)
{
+ nn_mutex_lock(&bridge->mutex);
+ if (bridge->quit) {
+ callback(bridge->userdata, userdata, NULL);
+ nn_mutex_unlock(&bridge->mutex);
+ return;
+ }
struct camu_portal_cmd cmd = { 0 };
cmd.op = CAMU_CLIENT_GET_PAGE;
cmd.id = id;
cmd.num = num;
cmd.callback = callback;
cmd.userdata = userdata;
- nn_mutex_lock(&bridge->mutex);
al_array_push(bridge->queue, cmd);
if (nn_cond_is_waiting(&bridge->cond)) {
nn_cond_signal(&bridge->cond);
diff --git a/src/render/meson.build b/src/render/meson.build
index 157cd63..a7ccb58 100644
--- a/src/render/meson.build
+++ b/src/render/meson.build
@@ -9,11 +9,9 @@ if get_option('renderer') == 'momo'
render_args += ['-DCAMU_RENDERER_MOMO']
elif get_option('renderer') == 'libplacebo'
libplacebo_found = false
- if 'libplacebo' not in get_option('force_fallback_for')
+ if 'libplacebo' not in get_option('force_fallback_for') and get_option('wrap_mode') != 'forcefallback'
libplacebo = dependency('libplacebo', required: false, allow_fallback: false)
- if libplacebo.found()
- libplacebo_found = true
- endif
+ libplacebo_found = libplacebo.found()
endif
if not libplacebo_found
libplacebo_opts = ['warning_level=2', 'demos=false', 'tests=false', 'bench=false', 'fuzz=false',
@@ -28,7 +26,7 @@ elif get_option('renderer') == 'libplacebo'
if get_option('renderer-api') == 'vulkan' or get_option('renderer-api') == 'dx11'
if get_option('renderer-compiler') == 'shaderc'
shaderc_found = false
- if 'shaderc' not in get_option('force_fallback_for')
+ if 'shaderc' not in get_option('force_fallback_for') and get_option('wrap_mode') != 'forcefallback'
if is_windows
shaderc = dependency('shaderc_combined', required: false, allow_fallback: false)
else
@@ -47,11 +45,14 @@ elif get_option('renderer') == 'libplacebo'
shaderc_opts.add_cmake_defines({ 'SHADERC_SKIP_TESTS': true })
shaderc_opts.add_cmake_defines({ 'SHADERC_SKIP_EXAMPLES': true })
shaderc_opts.add_cmake_defines({ 'SHADERC_SKIP_COPYRIGHT_CHECK': true })
+ # glslang
shaderc_opts.add_cmake_defines({ 'ENABLE_GLSLANG_BINARIES': false })
- shaderc_opts.add_cmake_defines({ 'ENABLE_SPVREMAPPER': false })
shaderc_opts.add_cmake_defines({ 'ENABLE_HLSL': false })
+ shaderc_opts.add_cmake_defines({ 'ENABLE_SPVREMAPPER': false })
+ shaderc_opts.add_cmake_defines({ 'ENABLE_EXCEPTIONS': false })
spirv_optimizer = true
shaderc_opts.add_cmake_defines({ 'ENABLE_OPT': spirv_optimizer })
+ # spirv-tools
shaderc_opts.add_cmake_defines({ 'ENABLE_EXCEPTIONS_ON_MSVC': false })
shaderc_proj = cmake.subproject('shaderc', options: shaderc_opts)
shaderc = shaderc_proj.dependency('shaderc')
@@ -66,9 +67,7 @@ elif get_option('renderer') == 'libplacebo'
shaderc_proj.dependency('SPIRV-Tools-static')
]
if spirv_optimizer
- render_deps += [
- shaderc_proj.dependency('SPIRV-Tools-opt'),
- ]
+ render_deps += [shaderc_proj.dependency('SPIRV-Tools-opt')]
endif
endif
libplacebo_opts += ['shaderc=enabled', 'glslang=disabled']
@@ -77,19 +76,36 @@ elif get_option('renderer') == 'libplacebo'
if glslang.found()
render_deps += [glslang]
else
+ # Currently does not build.
+ prefer_static = get_option('prefer_static')
+ spirv_optimizer = false
+ if spirv_optimizer
+ spirv_headers_proj = cmake.subproject('SPIRV-Headers')
+ spirv_headers = spirv_headers_proj.dependency('SPIRV-Headers')
+ spirv_tools_opts = cmake.subproject_options()
+ spirv_tools_opts.add_cmake_defines({ 'SPIRV-Headers_SOURCE_DIR': meson.source_root() + '/subprojects/SPIRV-Headers-b824a46' })
+ spirv_tools_opts.add_cmake_defines({ 'ENABLE_EXCEPTIONS_ON_MSVC': false })
+ spirv_tools_proj = cmake.subproject('SPIRV-Tools', options: spirv_tools_opts)
+ spirv_tools = spirv_tools_proj.dependency('SPIRV-Tools-static')
+ spirv_tools_opt = spirv_tools_proj.dependency('SPIRV-Tools-opt')
+ endif
glslang_opts = cmake.subproject_options()
glslang_opts.set_override_option('warning_level', '0')
reltype = is_minsize ? 'MinSizeRel' : 'Release'
glslang_opts.add_cmake_defines({ 'CMAKE_BUILD_TYPE': is_debug ? 'Debug' : reltype })
- spirv_optimizer = false
+ glslang_opts.add_cmake_defines({ 'ENABLE_GLSLANG_BINARIES': false })
+ glslang_opts.add_cmake_defines({ 'ENABLE_HLSL': false })
+ glslang_opts.add_cmake_defines({ 'ENABLE_SPVREMAPPER': false })
+ glslang_opts.add_cmake_defines({ 'ENABLE_EXCEPTIONS': false })
glslang_opts.add_cmake_defines({ 'ENABLE_OPT': spirv_optimizer })
+ glslang_opts.add_cmake_defines({ 'ALLOW_EXTERNAL_SPIRV_TOOLS': spirv_optimizer })
glslang_proj = cmake.subproject('glslang', options: glslang_opts)
spirv = glslang_proj.dependency('SPIRV')
glslang = glslang_proj.dependency('glslang')
glslang_default_resource_limits = glslang_proj.dependency('glslang-default-resource-limits')
- meson.override_dependency('SPIRV', spirv)
- meson.override_dependency('glslang', glslang)
- meson.override_dependency('glslang-default-resource-limits', glslang_default_resource_limits)
+ meson.override_dependency('SPIRV', spirv, static: prefer_static)
+ meson.override_dependency('glslang', glslang, static: prefer_static)
+ meson.override_dependency('glslang-default-resource-limits', glslang_default_resource_limits, static: prefer_static)
render_deps += [
glslang_proj.dependency('MachineIndependent'),
glslang_proj.dependency('OSDependent'),
@@ -98,6 +114,13 @@ elif get_option('renderer') == 'libplacebo'
glslang,
glslang_default_resource_limits
]
+ if spirv_optimizer
+ render_deps += [
+ spirv_tools,
+ spirv_headers,
+ spirv_tools_opt
+ ]
+ endif
endif
libplacebo_opts += ['glslang=enabled', 'shaderc=disabled']
endif
@@ -140,11 +163,9 @@ endif
if get_option('subtitles').enabled()
libass_found = false
- if 'libass' not in get_option('force_fallback_for')
+ if 'libass' not in get_option('force_fallback_for') and get_option('wrap_mode') != 'forcefallback'
libass = dependency('libass', required: false, allow_fallback: false)
- if libass.found()
- libass_found = true
- endif
+ libass_found = libass.found()
endif
if not libass_found
libass_opts = ['test=disabled']
@@ -191,6 +212,7 @@ if get_option('subtitles').enabled()
'gobject=disabled',
'cairo=disabled',
'chafa=disabled',
+ 'icu=disabled',
'freetype=disabled',
# This does not override yield, and yielding breaks the build if tests=enabled.
# https://github.com/mesonbuild/meson/issues/5214
diff --git a/src/render/queue_libplacebo.c b/src/render/queue_libplacebo.c
index c78562c..e3540c3 100644
--- a/src/render/queue_libplacebo.c
+++ b/src/render/queue_libplacebo.c
@@ -164,12 +164,12 @@ static struct camu_overlay *first_nonref_overlay(struct camu_frame_queue_lp *lq,
static struct camu_overlay *create_subtitle_overlay(struct camu_overlay *prev, pl_gpu gpu, ASS_Image *ass_frame)
{
- u32 frame_count = 1;
+ u16 frame_count = 1;
ASS_Image *head_ass_frame = ass_frame;
- while (ass_frame->next) { frame_count++; ass_frame = ass_frame->next; }
+ while (ass_frame->next) { al_assert(frame_count < UINT16_MAX); frame_count++; ass_frame = ass_frame->next; }
ass_frame = head_ass_frame;
- u32 prev_alloc = prev ? prev->alloc : 0;
+ u16 prev_alloc = prev ? prev->alloc : 0;
struct camu_overlay *overlay = camu_overlay_alloc(prev, frame_count,
sizeof(struct pl_overlay) + sizeof(struct pl_overlay_part));
if (frame_count > prev_alloc) {
@@ -225,9 +225,9 @@ static struct camu_overlay *create_subtitle_overlay(struct camu_overlay *prev, p
};
u32 c = ass_frame->color;
current_part->color[0] = (c >> 24) / 255.0;
- current_part->color[1] = ((c >> 16) & 0xFF) / 255.0;
- current_part->color[2] = ((c >> 8) & 0xFF) / 255.0;
- current_part->color[3] = 1.0 - (c & 0xFF) / 255.0;
+ current_part->color[1] = ((c >> 16) & 0xff) / 255.0;
+ current_part->color[2] = ((c >> 8) & 0xff) / 255.0;
+ current_part->color[3] = 1.0 - (c & 0xff) / 255.0;
current->parts = current_part;
current->num_parts = 1;
@@ -317,8 +317,9 @@ static bool map_av_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *
if (overlay) {
overlay->ref++;
out_frame->overlays = CAMU_OVERLAY_OFFSET(overlay, 0);
- out_frame->num_overlays = overlay->num;
((struct pl_source_frame *)src)->frame_data = overlay;
+ } else {
+ out_frame->overlays = NULL;
}
}
nn_mutex_unlock(&lq->subtitle_lock);
diff --git a/src/render/renderer.h b/src/render/renderer.h
index 3331baa..0eae5cb 100644
--- a/src/render/renderer.h
+++ b/src/render/renderer.h
@@ -30,12 +30,13 @@ enum {
struct camu_overlay {
u16 ref;
- u32 alloc;
+ u16 alloc;
s32 num;
struct nn_mutex *lock;
};
#define CAMU_OVERLAY_OFFSET(overlay, n) ((void *)(((u8 *)(overlay)) + sizeof(struct camu_overlay) + (n)))
+#define CAMU_OVERLAY_FROM_HEAD(p) (struct camu_overlay *)(((u8 *)p) - sizeof(struct camu_overlay))
struct camu_frame_queue;
struct camu_codec_stream;
@@ -67,7 +68,7 @@ struct camu_renderer {
u32 (*get_latency)(struct camu_renderer *);
void (*resize)(struct camu_renderer *, u32 *, u32 *);
void (*set)(struct camu_renderer *, u8, u8);
- void (*render)(struct camu_renderer *, struct camu_screen *, bool);
+ bool (*render)(struct camu_renderer *, struct camu_screen *, bool);
#ifdef CAMU_HAVE_FFMPEG
s32 (*get_buffer2)(struct AVCodecContext *s, AVFrame *frame, s32 flags);
void *opaque;
diff --git a/src/render/renderer_libplacebo.c b/src/render/renderer_libplacebo.c
index 3b69c64..01aa764 100644
--- a/src/render/renderer_libplacebo.c
+++ b/src/render/renderer_libplacebo.c
@@ -13,7 +13,8 @@
#include "queue_libplacebo.h"
#ifdef AL_DEBUG
-#define RENDERER_DEBUG 1
+// vkQueueSubmit2: VK_ERROR_VALIDATION_FAILED_EXT leads to an annoying deadlock.
+#define RENDERER_DEBUG 0
#else
#define RENDERER_DEBUG 0
#endif
@@ -37,8 +38,11 @@ static void renderer_lp_resize(struct camu_renderer *renderer, u32 *width, u32 *
struct camu_renderer_lp *lr = (struct camu_renderer_lp *)renderer;
if (lr->swapchain) {
if (lr->have_frame) {
- pl_swapchain_submit_frame(lr->swapchain);
- lr->have_frame = false;
+ if (pl_swapchain_submit_frame(lr->swapchain)) {
+ lr->have_frame = false;
+ } else {
+ log_error("Failed to submit frame on resize.");
+ }
}
pl_swapchain_resize_compat(lr->swapchain, width, height);
}
@@ -365,7 +369,9 @@ static inline intptr_t float_64_hash(f64 value)
return hash;
}
-static void renderer_lp_render(struct camu_renderer *renderer, struct camu_screen *scr, bool force)
+
+
+static bool renderer_lp_render(struct camu_renderer *renderer, struct camu_screen *scr, bool force)
{
struct camu_renderer_lp *lr = (struct camu_renderer_lp *)renderer;
@@ -377,15 +383,14 @@ static void renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
if (!lr->have_frame) {
struct pl_swapchain_frame frame;
- if (!pl_swapchain_start_frame(lr->swapchain, &frame)) {
- if (force) {
- pl_swapchain_swap_buffers(lr->swapchain);
- }
- return;
+ if (pl_swapchain_start_frame(lr->swapchain, &frame)) {
+ pl_frame_from_swapchain(&lr->target, &frame);
+ pl_frame_clear_rgba(lr->gpu, &lr->target, clear_color);
+ lr->have_frame = true;
+ } else {
+ log_error("Failed to start frame, fatal.");
+ return false;
}
- pl_frame_from_swapchain(&lr->target, &frame);
- pl_frame_clear_rgba(lr->gpu, &lr->target, clear_color);
- lr->have_frame = true;
}
f64 mouse_x, mouse_y;
@@ -405,11 +410,14 @@ static void renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
lr->params.hooks[0]->parameters[1].data->f = (f32)mouse_y;
}
+ enum {
+ RESULT_SUBMIT = 1,
+ RESULT_SUBMIT_AND_WAIT = 1 << 1
+ };
+
+ u8 result = 0;
struct pl_frame *target = &lr->target;
struct pl_frame_mix mix;
-
- bool do_gpu_finish = false;
-
struct camu_screen_video *video;
while (scr->videos.count > 0) {
bool any_eof = false;
@@ -419,7 +427,7 @@ static void renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
// If mix.frames is NULL, read() returned QUEUE_MORE.
if (mix.frames) {
// weighted is only set when read() returns QUEUE_OK.
- do_gpu_finish |= weighted;
+ result |= (1 | 1 << weighted);
// Terrible hack. Lets us distinguish single frames with the same dimensions.
// Tied to a libplacebo patch to consider info_priv in the hash.
intptr_t hash = (intptr_t)video->buf;
@@ -431,15 +439,22 @@ static void renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
hash += float_64_hash(video->view.fov);
lr->params.hooks[0]->parameters[2].data->f = (f32)video->view.fov;
}
- lr->params.info_priv = (void *)hash;
- target->crop = mix.frames[0]->crop;
- if (!scr->subtitles_enabled) {
- // This basically discards the subtitles of each frame before it's rendered.
- // So, toggling the subtitles of a single frame (paused) won't work.
- for (s32 j = 0; j < mix.num_frames; j++) {
- ((struct pl_frame *)mix.frames[j])->num_overlays = 0;
+#ifdef CAMU_HAVE_SUBTITLES
+ for (u32 j = 0; j < (u32)mix.num_frames; j++) {
+ struct pl_frame *mix_frame = (struct pl_frame *)mix.frames[j];
+ if (!mix_frame->overlays) continue;
+ struct camu_overlay *overlay = CAMU_OVERLAY_FROM_HEAD(mix_frame->overlays);
+ if (!overlay->num) continue;
+ if (scr->subtitles_enabled) {
+ mix_frame->num_overlays = overlay->num;
+ hash += overlay->num;
+ } else {
+ mix_frame->num_overlays = 0;
}
}
+#endif
+ lr->params.info_priv = (void *)hash;
+ target->crop = mix.frames[0]->crop;
target->crop.x1 *= video->view.zoom / video->view.stretch;
target->crop.y1 *= video->view.zoom * video->view.stretch;
target->crop.x0 += video->view.x_offset;
@@ -447,7 +462,14 @@ static void renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
target->crop.x1 += video->view.x_offset;
target->crop.y1 += video->view.y_offset;
target->rotation = video->view.rotation;
- //lr->params.color_adjustment = pl_color_adjustment(.saturation = 0.0);
+ /*
+ //lr->params.color_map_params = pl_color_map_params(.inverse_tone_mapping = true);
+ if () {
+ target->color = pl_color_space_srgb;
+ target->color.hdr.max_luma = PL_COLOR_SDR_WHITE - 50.f;
+ target->color.hdr.min_luma = (PL_COLOR_SDR_WHITE - 50.f) / PL_COLOR_SDR_CONTRAST;
+ }
+ */
pl_render_image_mix(lr->renderer, &mix, target, &lr->params);
}
} else {
@@ -468,32 +490,30 @@ static void renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
lr->params.hooks = NULL;
lr->params.num_hooks = 0;
- if (!scr->videos.count && !force) {
- // Don't spin too hard on a potential error state.
+ // Considerations about the result of the loop above.
+ // 1. Any given call to video_buffer_read() may not produce a frame.
+ // 2. If a buffer signals EOF, by the time run_queue() happens at the bottom of the loop all
+ // previous buffers could have been queued for removal. Meaning, at this point, videos.count
+ // would be 0 while there could be data we want to display rendered to the frame.
+ if (!(result & RESULT_SUBMIT) && !force) {
nn_thread_sleep(NNWT_TS_FROM_USEC(192));
- return;
+ return true;
}
- if (!pl_swapchain_submit_frame(lr->swapchain)) {
+ if (pl_swapchain_submit_frame(lr->swapchain)) {
+ lr->have_frame = false;
+ } else {
log_error("Failed to submit frame, possibly fatal.");
+ return false;
}
- lr->have_frame = false;
pl_swapchain_swap_buffers(lr->swapchain);
- if (do_gpu_finish) {
- // Block until render completes.
+ if (result & RESULT_SUBMIT_AND_WAIT) {
pl_gpu_finish(lr->gpu);
}
-#if 0
- f64 tick = nn_get_tick();
- f64 frame_time = tick - lr->last_render_tick;
- if (frame_time > (1.0 / 24.0)) { // 24fps is totally arbitrary.
- log_info("FRAME_TIME: %fs (%.2ffps)", frame_time, 1.0 / frame_time);
- }
- lr->last_render_tick = tick;
-#endif
+ return true;
}
void renderer_lp_free(struct camu_renderer **renderer)
@@ -521,7 +541,7 @@ void renderer_lp_free(struct camu_renderer **renderer)
*renderer = NULL;
}
-struct camu_renderer *camu_renderer_lp_create(void)
+struct camu_renderer *camu_renderer_libplacebo_create(void)
{
struct camu_renderer_lp *lr = al_alloc_object(struct camu_renderer_lp);
lr->r.create_renderer = renderer_lp_create_renderer;
diff --git a/src/render/renderer_libplacebo.h b/src/render/renderer_libplacebo.h
index 5d5cf9b..727e0b3 100644
--- a/src/render/renderer_libplacebo.h
+++ b/src/render/renderer_libplacebo.h
@@ -42,4 +42,4 @@ struct camu_renderer_lp {
#endif
};
-struct camu_renderer *camu_renderer_lp_create(void);
+struct camu_renderer *camu_renderer_libplacebo_create(void);
diff --git a/src/render/renderer_momo.c b/src/render/renderer_momo.c
index 3a0e7ed..02ccaee 100644
--- a/src/render/renderer_momo.c
+++ b/src/render/renderer_momo.c
@@ -85,12 +85,13 @@ static u32 renderer_momo_get_latency(struct camu_renderer *renderer)
return 0;
}
-static void renderer_momo_render(struct camu_renderer *renderer, struct camu_screen *scr, bool force)
+static bool renderer_momo_render(struct camu_renderer *renderer, struct camu_screen *scr, bool force)
{
struct camu_renderer_momo *mr = (struct camu_renderer_momo *)renderer;
(void)scr;
(void)force;
MOMO_Render(&mr->mmr);
+ return true;
}
static void renderer_momo_free(struct camu_renderer **renderer)
diff --git a/src/screen/screen.c b/src/screen/screen.c
index 844076e..f6833dd 100644
--- a/src/screen/screen.c
+++ b/src/screen/screen.c
@@ -259,8 +259,8 @@ static bool key_callback(void *userdata, u8 state, u16 button)
break;
case STELA_KEY_RIGHT:
if (SCREEN_MOD1(scr)) {
- f64 plus_five_seconds = 5.0;
- scr->callback(scr->userdata, CAMU_SCREEN_RELATIVE_SEEK, &plus_five_seconds);
+ f64 plus_ten_seconds = 10.0;
+ scr->callback(scr->userdata, CAMU_SCREEN_RELATIVE_SEEK, &plus_ten_seconds);
break;
}
// fallthrough
@@ -276,8 +276,8 @@ static bool key_callback(void *userdata, u8 state, u16 button)
f64 zero = 0.0;
scr->callback(scr->userdata, CAMU_SCREEN_PERCENT_SEEK, &zero);
} else {
- f64 minus_five_seconds = -5.0;
- scr->callback(scr->userdata, CAMU_SCREEN_RELATIVE_SEEK, &minus_five_seconds);
+ f64 minus_ten_seconds = -10.0;
+ scr->callback(scr->userdata, CAMU_SCREEN_RELATIVE_SEEK, &minus_ten_seconds);
}
break;
}
@@ -294,9 +294,11 @@ static bool key_callback(void *userdata, u8 state, u16 button)
if (SCREEN_MOD1(scr)) {
scr->callback(scr->userdata, CAMU_SCREEN_SHUFFLE, NULL);
} else {
+#ifdef CAMU_HAVE_SUBTITLES
scr->subtitles_enabled = !scr->subtitles_enabled;
log_info(scr->subtitles_enabled ? "Subtitles enabled." : "Subtitles disabled.");
return true;
+#endif
}
break;
case STELA_KEY_E:
@@ -484,7 +486,9 @@ bool camu_screen_init(struct camu_screen *scr)
scr->flags = CAMU_SCREEN_ZOOM_PAN_SIMPLE;
scr->scaling_disabled = false;
scr->transparent_background = false;
+#ifdef CAMU_HAVE_SUBTITLES
scr->subtitles_enabled = true;
+#endif
scr->last_click_ts = SCREEN_INVALID_TS;
scr->last_pointer_x = 0.0;
scr->last_pointer_y = 0.0;
diff --git a/src/server/server.c b/src/server/server.c
index ae77ef0..59a0f7a 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -590,28 +590,31 @@ out:
#ifdef CAMU_HAVE_PORTAL
static void simple_search_portal_callback(void *userdata0, void *userdata1, struct camu_portal_result *result)
{
- struct camu_server *server = (struct camu_server *)userdata0;
struct camu_resource *resource = (struct camu_resource *)userdata1;
- switch (result->op) {
- case CAMU_CLIENT_CREATE_SEARCH:
- if (result->id == -1) {
- break;
- }
- camu_portal_get_page(&server->bridge, result->id, 0, simple_search_portal_callback, resource);
- return;
- case CAMU_CLIENT_GET_PAGE:
- if (result->id == -1 || !result->page || !result->page->list.count) {
- break;
+ if (result) {
+ struct camu_server *server = (struct camu_server *)userdata0;
+ switch (result->op) {
+ case CAMU_CLIENT_CREATE_SEARCH:
+ if (result->id == -1) {
+ break;
+ }
+ camu_portal_get_page(&server->bridge, result->id, 0, simple_search_portal_callback, resource);
+ return;
+ case CAMU_CLIENT_GET_PAGE:
+ if (result->id == -1 || !result->page || !result->page->list.count) {
+ break;
+ }
+ resource->post = camu_post_cache_get(&server->cache, &al_array_at(result->page->list, 0));
+ resource->type = CAMU_RESOURCE_PORTAL;
+ prepare_server_resource(server, resource);
+ process_pending(resource);
+ return;
}
- resource->post = camu_post_cache_get(&server->cache, &al_array_at(result->page->list, 0));
- resource->type = CAMU_RESOURCE_PORTAL;
- prepare_server_resource(server, resource);
- process_pending(resource);
- return;
}
// No return is the error case.
log_warn("Failed to process search request.");
resource->load = LIANA_ENTRY_ERRORED;
+ process_pending(resource);
}
#endif
@@ -684,6 +687,7 @@ static void handle_add_command(struct camu_server *server, struct lia_list *list
al_str_from(&query, "link:");
al_str_cat(&query, &line);
}
+ log_info("Processing search request: %.*s.", al_str_x(&query));
camu_portal_create_search(&server->bridge, &al_str_c("youtube"),
&query, simple_search_portal_callback, resource);
al_str_free(&query);
diff --git a/src/util/queue.h b/src/util/queue.h
index 17880e0..08b0a30 100644
--- a/src/util/queue.h
+++ b/src/util/queue.h
@@ -19,13 +19,6 @@
nn_mutex_unlock(&(q).mutex); \
} while (0)
-#define camu_queue_count(q, r) \
- do { \
- nn_mutex_lock(&(q).mutex); \
- r = (q).a.count; \
- nn_mutex_unlock(&(q).mutex); \
- } while (0)
-
#define camu_queue_init(q) \
do { \
al_array_init((q).a); \
@@ -34,16 +27,7 @@
#define camu_queue_push(q, item) \
do { \
- nn_mutex_lock(&(q).mutex); \
al_array_push((q).a, item); \
- nn_mutex_unlock(&(q).mutex); \
- } while (0)
-
-#define camu_queue_pop(q, r) \
- do { \
- nn_mutex_lock(&(q).mutex); \
- al_array_pop_at((q).a, 0, r); \
- nn_mutex_unlock(&(q).mutex); \
} while (0)
#define camu_queue_try_pop(q, s, r) \