summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/buffer/clock.c11
-rw-r--r--src/buffer/clock.h1
-rw-r--r--src/cache/backings/file.c2
-rw-r--r--src/cache/backings/file_common.h14
-rw-r--r--src/cache/backings/file_mapped.c4
-rw-r--r--src/cache/handle.c3
-rw-r--r--src/cache/handlers/cdio.c9
-rw-r--r--src/cache/handlers/http.c14
-rw-r--r--src/cache/handlers/http.h2
-rw-r--r--src/codec/ffmpeg/common.c2
-rw-r--r--src/codec/ffmpeg/common.h2
-rw-r--r--src/codec/ffmpeg/encoder.c7
-rw-r--r--src/fruits/cmv/cmv.c38
-rw-r--r--src/fruits/cmv/meson.build13
-rw-r--r--src/fruits/ctv/ctv.c74
-rw-r--r--src/fruits/ctv/meson.build4
-rw-r--r--src/fruits/droid/ctv/app/src/main/AndroidManifest.xml28
-rw-r--r--src/liana/client.c2
-rw-r--r--src/liana/handlers/cdio_server.c8
-rw-r--r--src/liana/list.c2
-rw-r--r--src/liana/server.c21
-rw-r--r--src/liana/server.h4
-rw-r--r--src/liana/vcr.c14
-rw-r--r--src/libsink/desktop.c13
-rw-r--r--src/libsink/sink.c148
-rw-r--r--src/libsink/sink.h7
-rw-r--r--src/render/renderer.h2
-rw-r--r--src/render/renderer_libplacebo.c300
-rw-r--r--src/render/renderer_libplacebo.h23
-rw-r--r--src/render/shaders/osd.h73
-rw-r--r--src/render/shaders/osd_binds.h311
-rw-r--r--src/render/shaders/osd_header.h146
-rw-r--r--src/screen/screen.c208
-rw-r--r--src/screen/screen.h31
-rw-r--r--src/server/server.c29
-rw-r--r--src/server/server.h1
-rw-r--r--src/util/print_time.h24
-rw-r--r--subprojects/stela.wrap4
38 files changed, 1359 insertions, 240 deletions
diff --git a/src/buffer/clock.c b/src/buffer/clock.c
index fbe7ec5..19f721c 100644
--- a/src/buffer/clock.c
+++ b/src/buffer/clock.c
@@ -86,7 +86,8 @@ bool camu_clock_pause(struct camu_clock *clock, u64 target)
f64 tick = atomic_load(f64)(&clock->tick, AL_ATOMIC_RELAXED);
- f64 current = nn_get_tick(), pause = current;
+ f64 current = nn_get_tick();
+ f64 pause = current;
if (target > 0) {
pause = calc_tick_offset(pause, nn_get_timestamp(), target);
}
@@ -144,6 +145,14 @@ void camu_clock_resume(struct camu_clock *clock, u64 target)
atomic_store(f64)(&clock->pause, RUNNING, AL_ATOMIC_RELEASE);
}
+// @TODO: This only works for local sink.
+bool camu_clock_is_user_paused(struct camu_clock *clock)
+{
+ f64 tick = atomic_load(f64)(&clock->tick, AL_ATOMIC_RELAXED);
+ f64 pause = atomic_load(f64)(&clock->pause, AL_ATOMIC_RELAXED);
+ return tick != SET && pause <= 0.0;
+}
+
f64 camu_clock_get_base_pts(struct camu_clock *clock)
{
return clock->base;
diff --git a/src/buffer/clock.h b/src/buffer/clock.h
index eb3dcaf..e1a2d9e 100644
--- a/src/buffer/clock.h
+++ b/src/buffer/clock.h
@@ -53,6 +53,7 @@ bool camu_clock_pause(struct camu_clock *clock, u64 target);
void camu_clock_external_pause(struct camu_clock *clock);
void camu_clock_resume(struct camu_clock *clock, u64 target);
+bool camu_clock_is_user_paused(struct camu_clock *clock);
f64 camu_clock_get_base_pts(struct camu_clock *clock);
f64 camu_clock_get_pts(struct camu_clock *clock, f64 latency, bool allow_set, bool *armed_for_pause);
f64 camu_clock_get_last_pts(struct camu_clock *clock);
diff --git a/src/cache/backings/file.c b/src/cache/backings/file.c
index 06cc600..c98c8d5 100644
--- a/src/cache/backings/file.c
+++ b/src/cache/backings/file.c
@@ -63,7 +63,7 @@ static void file_backing_set_size(struct cch_backing *backing, off_t size, bool
struct cch_backing_file *file = (struct cch_backing_file *)backing;
nn_mutex_lock(&file->mutex);
file->backing.size = size;
- if (expand_to_size && (size_t)size > file->file.size) {
+ if (expand_to_size || (size > 0 && size < file->file.size)) {
nn_file_truncate(&file->file, size);
}
nn_mutex_unlock(&file->mutex);
diff --git a/src/cache/backings/file_common.h b/src/cache/backings/file_common.h
index 6650dd1..6dd4e96 100644
--- a/src/cache/backings/file_common.h
+++ b/src/cache/backings/file_common.h
@@ -27,7 +27,19 @@ static void file_backing_finalize(struct cch_backing *backing)
static bool file_open_internal(struct cch_backing_file *file, str *path, size_t size)
{
s32 flags = (size != 0) ? NNWT_FILE_CREATE : NNWT_FILE_READONLY;
- if (!nn_file_open(&file->file, path, flags)) return false;
+ if (al_str_cmp(path, &al_str_c("fd://"), 0, 5) == 0) {
+ str fd_str = al_str_substr(path, 5, path->length);
+ bool error;
+ s32 fd = (s32)al_str_to_long(&fd_str, 10, &error);
+ if (error) return false;
+ if (!nn_file_wrap_fd(&file->file, fd)) {
+ return false;
+ }
+ } else {
+ if (!nn_file_open(&file->file, path, flags)) {
+ return false;
+ }
+ }
size_t filesize = file->file.size;
if (!size) {
file->backing.size = filesize;
diff --git a/src/cache/backings/file_mapped.c b/src/cache/backings/file_mapped.c
index 7657a21..24ac005 100644
--- a/src/cache/backings/file_mapped.c
+++ b/src/cache/backings/file_mapped.c
@@ -46,14 +46,12 @@ static void file_backing_unlock(struct cch_backing *backing)
static void file_backing_set_size(struct cch_backing *backing, off_t size, bool expand_to_size)
{
struct cch_backing_file *file = (struct cch_backing_file *)backing;
- al_assert(expand_to_size || size == 0);
nn_mutex_lock(&file->mutex);
file->backing.size = size;
- if ((size_t)size > file->file.size) {
+ if (expand_to_size || (size > 0 && size < file->file.size)) {
nn_file_munmap(&file->file, file->u.map);
nn_file_truncate(&file->file, size);
file->u.map = nn_file_mmap(&file->file);
- file->file.size = size;
}
nn_mutex_unlock(&file->mutex);
}
diff --git a/src/cache/handle.c b/src/cache/handle.c
index 74db306..ebc3842 100644
--- a/src/cache/handle.c
+++ b/src/cache/handle.c
@@ -37,6 +37,7 @@ s32 cch_handle_read(struct cch_handle *handle, u8 *buf, s32 count)
al_assert(count >= 0);
handle->wait.start = handle->pointer;
handle->wait.end = handle->pointer + count;
+ log_trace("wait_for_range(%zd, %zd)", handle->wait.start, handle->wait.end);
if (!handler->wait_for_range(handler, &handle->wait)) {
log_trace("Wait for range (%zd-%zd) failed.", handle->wait.start, handle->wait.end);
return CAMU_ERR_EOF;
@@ -52,7 +53,7 @@ s32 cch_handle_read(struct cch_handle *handle, u8 *buf, s32 count)
backing->read(backing, buf, handle->pointer, &available);
}
handle->pointer += available;
- log_trace("read(%u), pointer: %zd.", count, handle->pointer);
+ log_trace("read(%u), avail: %zu, pointer: %zd.", count, available, handle->pointer);
return (available > 0) ? (s32)available : CAMU_ERR_EOF;
}
diff --git a/src/cache/handlers/cdio.c b/src/cache/handlers/cdio.c
index 8326868..90c5ce9 100644
--- a/src/cache/handlers/cdio.c
+++ b/src/cache/handlers/cdio.c
@@ -1,6 +1,7 @@
#define AL_LOG_SECTION "cache_cdio"
//#define AL_LOG_ENABLE_TRACE
#include <al/log.h>
+#include <nnwt/common.h>
#include "../../codec/codec.h"
@@ -51,9 +52,10 @@ static nn_thread_result NNWT_THREADCALL cd_read_thread(void *userdata)
cdio_log_messages(cdio);
al_assert(ret <= SECTORS_PER_STEP);
off_t pointer = cdio->sector * CDIO_CD_FRAMESIZE_RAW;
- size_t size;
+ size_t size = BYTES_PER_STEP;
u8 *ptr = cdio->backing->get_ptr(cdio->backing, pointer, &size);
al_assert(size >= BYTES_PER_STEP);
+ log_trace("cdio_read(), ret: %ld, size: %zu", ret, size);
if (ret == SECTORS_PER_STEP) {
al_memcpy(ptr, nn_buffer_get_ptr(&cdio->buffer, 0), BYTES_PER_STEP);
} else if (ret == -10) {
@@ -192,7 +194,10 @@ static bool open_cd_drive(struct cch_handler_cdio *cdio)
struct cch_entry *cch_handler_cdio_create(void)
{
- struct cch_backing *backing = cch_backing_file_create(&al_str_c("/tmp/camu_cd_data"), 4096);
+ str tmpfile;
+ al_str_clone(&tmpfile, &nn_temp_directory);
+ nn_path_append(&tmpfile, &al_str_c("camu_cd_data"));
+ struct cch_backing *backing = cch_backing_file_create(&tmpfile, 4096);
if (!backing) return NULL;
struct cch_handler_cdio *cdio = al_alloc_object(struct cch_handler_cdio);
cdio->backing = backing;
diff --git a/src/cache/handlers/http.c b/src/cache/handlers/http.c
index f57d3eb..4fe82e2 100644
--- a/src/cache/handlers/http.c
+++ b/src/cache/handlers/http.c
@@ -1,10 +1,12 @@
#define AL_LOG_SECTION "cache_http"
+#define AL_LOG_ENABLE_TRACE
#include <al/log.h>
+#include <nnwt/common.h>
#include "../../codec/codec.h"
#include "../backings/memory.h"
-//#include "../backings/file.h"
+#include "../backings/file.h"
#include "../threaded_waits.h"
#include "../wait.h"
@@ -78,11 +80,14 @@ static void handler_http_maybe_spawn_worker(struct cch_handler *handler, size_t
{
struct cch_handler_http *http = (struct cch_handler_http *)handler;
(void)index;
- al_array_push(http->requests, (struct nn_http){ 0 });
- struct nn_http *request = &al_array_last(http->requests);
+ struct nn_http *request = al_alloc_object(struct nn_http);
+ al_array_push(http->requests, request);
nn_http_init(request);
nn_http_set_url(request, &http->url);
nn_http_set_user_agent(request, &USER_AGENT);
+ if (index != 0) {
+ nn_http_set_range(request, index, 0);
+ }
nn_http_request_stream(request, NNWT_HTTP_GET, http->loop, http_callback, http);
log_debug("Spawning worker for %.*s.", al_str_x(&http->url));
}
@@ -104,7 +109,7 @@ static void handler_http_free(struct cch_handler **handler)
struct cch_handler_http *http = (struct cch_handler_http *)*handler;
cch_threaded_waits_disable_all(&http->waits);
struct nn_http *request;
- al_array_foreach_ptr(http->requests, i, request) {
+ al_array_foreach(http->requests, i, request) {
nn_http_close(request);
}
cch_threaded_waits_close(&http->waits);
@@ -115,6 +120,7 @@ static void handler_http_free(struct cch_handler **handler)
*handler = NULL;
}
+
struct cch_entry *cch_handler_http_create(str *url, struct nn_event_loop *loop)
{
struct cch_backing *backing = cch_backing_memory_create(1024 * 128);
diff --git a/src/cache/handlers/http.h b/src/cache/handlers/http.h
index 93efdae..3e0bfca 100644
--- a/src/cache/handlers/http.h
+++ b/src/cache/handlers/http.h
@@ -14,7 +14,7 @@ struct cch_handler_http {
struct nn_event_loop *loop;
str url;
off_t pointer;
- array(struct nn_http) requests;
+ array(struct nn_http *) requests;
struct cch_threaded_waits waits;
};
diff --git a/src/codec/ffmpeg/common.c b/src/codec/ffmpeg/common.c
index 5e10bcb..bda7a18 100644
--- a/src/codec/ffmpeg/common.c
+++ b/src/codec/ffmpeg/common.c
@@ -17,7 +17,7 @@ s64 camu_ff_get_frame_duration(AVFrame *frame)
}
// https://code.videolan.org/videolan/libplacebo/-/blob/a7a18af88ff0a17c04840dcb3246047bb6b46df3/src/include/libplacebo/utils/libav_internal.h#L854
-const u8 *camu_ff_get_stream_side_data(const AVStream *stream, enum AVPacketSideDataType type)
+const u8 *camu_ff_get_stream_side_data(AVStream *stream, enum AVPacketSideDataType type)
{
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 15, 100)
AVCodecParameters *codecpar = stream->codecpar;
diff --git a/src/codec/ffmpeg/common.h b/src/codec/ffmpeg/common.h
index b49ce88..5a0be3d 100644
--- a/src/codec/ffmpeg/common.h
+++ b/src/codec/ffmpeg/common.h
@@ -10,7 +10,7 @@
s64 camu_ff_get_frame_duration(AVFrame *frame);
-const u8 *camu_ff_get_stream_side_data(const AVStream *stream, enum AVPacketSideDataType type);
+const u8 *camu_ff_get_stream_side_data(AVStream *stream, enum AVPacketSideDataType type);
const u8 *camu_ff_get_frame_side_data(AVFrame *frame, enum AVFrameSideDataType type);
// userdata, level, fmt, args
diff --git a/src/codec/ffmpeg/encoder.c b/src/codec/ffmpeg/encoder.c
index 641acc5..7824461 100644
--- a/src/codec/ffmpeg/encoder.c
+++ b/src/codec/ffmpeg/encoder.c
@@ -44,10 +44,9 @@ bool camu_ff_encoder_init(struct camu_ff_encoder *enc, char *encoder_name, char
enc->codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
+ u32 kbps = bitrate / 1000;
AVDictionary *opts = NULL;
- char kbps[16];
- al_snprintf(kbps, sizeof(kbps), "%uk", bitrate / 1000);
- av_dict_set(&opts, "b", kbps, 0);
+ av_dict_set(&opts, "b", al_print_s32(kbps)->data, 0);
av_dict_set(&opts, "vbr", "off", 0);
av_dict_set(&opts, "compression_level", "10", 0);
av_dict_set(&opts, "frame_duration", "20", 0);
@@ -59,7 +58,7 @@ bool camu_ff_encoder_init(struct camu_ff_encoder *enc, char *encoder_name, char
}
av_dict_free(&opts);
- log_info("Encoder: %s (%s) %sbps.", codec->name,
+ log_info("Encoder: %s (%s) %ubps.", codec->name,
codec->long_name ? codec->long_name : codec->name, kbps);
AVCodecContext *out_codec = enc->codec_context;
diff --git a/src/fruits/cmv/cmv.c b/src/fruits/cmv/cmv.c
index 58c8e32..74e3555 100644
--- a/src/fruits/cmv/cmv.c
+++ b/src/fruits/cmv/cmv.c
@@ -53,6 +53,10 @@ static nn_thread_result NNWT_THREADCALL event_loop_thread(void *userdata)
static bool decode_addr(str *addr, str *enc)
{
+ if (al_str_find(enc, '.') != AL_STR_NO_POS) {
+ al_str_clone(addr, enc);
+ return true;
+ }
char value[4]; // max = 255.
for (u32 i = 0; i < 8; i += 2) {
str sub = al_str_substr(enc, i, i + 2);
@@ -75,40 +79,39 @@ static bool parse_exe_name(str *exe_name, str *addr)
u32 index = al_str_rfind(exe_name, '/');
if (index == AL_STR_NO_POS) {
if ((index = al_str_rfind(exe_name, '\\')) == AL_STR_NO_POS) {
- goto def;
+ return false;
}
}
str sub = al_str_substr(exe_name, index + 1, exe_name->length);
if (al_str_cmp(&sub, &al_str_c("sink-"), 0, 5) != 0) {
- goto def;
+ return false;
}
sub = al_str_substr(&sub, 5, sub.length);
// Skip version.
if ((index = al_str_find(&sub, '-')) == AL_STR_NO_POS) {
- goto def;
+ return false;
}
sub = al_str_substr(&sub, index + 1, sub.length);
if ((index = al_str_find(&sub, '.')) != AL_STR_NO_POS) {
sub = al_str_substr(&sub, 0, index);
}
- al_str_from(addr, "");
decode_addr(addr, &sub);
log_debug("Parsed address %.*s from exe filename.", al_str_x(addr));
return true;
-def:
- return false;
}
static struct cmv c = { 0 };
+#ifdef STELA_USE_PLATFORM_MAIN
+s32 stl_platform_main(u32 argc, str *argv, void *extra)
+#else
s32 window_system_main(u32 argc, str *argv, void *extra)
+#endif
{
- (void)extra;
-
bool local;
#ifdef CAMU_SINK_ONLY
local = argc > 1;
@@ -134,9 +137,9 @@ s32 window_system_main(u32 argc, str *argv, void *extra)
nn_event_loop_init(&c.loop);
u8 type;
- str addr = al_str_null();
+ str addr = AL_STR_EMPTY;
if (local) {
-#ifdef NAUNET_ON_WINDOWS
+#if defined NAUNET_ON_WINDOWS || defined NAUNET_ON_ANDROID
type = NNWT_SOCKET_TCP;
al_str_clone(&addr, &al_str_c("127.0.0.1"));
#else
@@ -151,8 +154,8 @@ s32 window_system_main(u32 argc, str *argv, void *extra)
} else {
type = NNWT_SOCKET_TCP;
char *SINK_IP = getenv("SINK_IP");
- al_str_from(&addr, "");
if (!SINK_IP || !decode_addr(&addr, &al_str_cr(SINK_IP))) {
+ local = true; // If no IP, default back to local.
al_str_cat(&addr, &al_str_c("127.0.0.1"));
}
}
@@ -228,11 +231,22 @@ s32 window_system_main(u32 argc, str *argv, void *extra)
#endif
nn_packet_free(packet);
}
+ if (extra) {
+ u32 start_index = *(u32 *)extra;
+ if (start_index != 0) {
+ camu_server_local_set_index(&c.server, start_index);
+ }
+ }
}
struct nn_thread thread;
nn_thread_create(&thread, event_loop_thread, &c);
+#ifdef STELA_USE_PLATFORM_MAIN
+ al_str_free(&addr);
+ return 0;
+#endif
+
while (camu_desktop_tick(&c.desktop)) {}
camu_desktop_stop(&c.desktop);
@@ -251,6 +265,7 @@ out:
return ret;
}
+#ifndef STELA_USE_PLATFORM_MAIN
static bool sigint_force = false;
static void sigint_handler(s32 signum)
{
@@ -314,3 +329,4 @@ s32 main(s32 argc, char *argv[])
return ret;
}
+#endif
diff --git a/src/fruits/cmv/meson.build b/src/fruits/cmv/meson.build
index ec27c81..301fa58 100644
--- a/src/fruits/cmv/meson.build
+++ b/src/fruits/cmv/meson.build
@@ -14,7 +14,10 @@ if use_tui
endif
cmv_link_args = []
-if is_windows
+if is_android
+ # https://github.com/google/ExoPlayer/issues/9933#issuecomment-1029775358
+ cmv_link_args += ['-Wl,-Bsymbolic', '-static-libstdc++']
+elif is_windows
windows = import('windows')
cmv_src += [windows.compile_resources('icon.rc')]
if not is_msvc
@@ -22,4 +25,10 @@ if is_windows
endif
endif
-executable('cmv', cmv_src, dependencies: cmv_deps, c_args: cmv_args, link_args: cmv_link_args, install: true)
+if is_android
+ cmv_args += ['-DAPPLICATION_NAME="CTV"']
+ shared_library('ctv', cmv_src, dependencies: cmv_deps, c_args: cmv_args, link_args: cmv_link_args)
+else
+ cmv_args += ['-DAPPLICATION_NAME="CMV"']
+ executable('cmv', cmv_src, dependencies: cmv_deps, c_args: cmv_args, link_args: cmv_link_args, install: true)
+endif
diff --git a/src/fruits/ctv/ctv.c b/src/fruits/ctv/ctv.c
deleted file mode 100644
index 2878600..0000000
--- a/src/fruits/ctv/ctv.c
+++ /dev/null
@@ -1,74 +0,0 @@
-#include <al/log.h>
-#include <android/log.h>
-
-#include "../../libsink/sink.h"
-#include "../../libsink/desktop.h"
-#ifdef CAMU_HAVE_FFMPEG
-#include "../../codec/ffmpeg/common.h"
-#endif
-
-#include "../common.h"
-
-struct ctv {
- struct nn_event_loop loop;
- struct camu_desktop desktop;
- struct nn_thread thread;
-};
-
-static nn_thread_result NNWT_THREADCALL event_loop_thread(void *userdata)
-{
- struct ctv *c = (struct ctv *)userdata;
- nn_thread_set_name("event_loop");
-
- nn_event_loop_init(&c->loop);
-
- struct camu_sink *sink = &c->desktop.sink;
- sink->local = true;
-
- struct lia_prefs *prefs = &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;
-
- if (!camu_desktop_connect(&c->desktop, &al_str_c("ctv"), &c->loop, NNWT_SOCKET_TCP, &al_str_c(""), CAMU_PORT)) {
- return 0;
- }
-
- nn_event_loop_run(&c->loop);
-
- return 0;
-}
-
-static s32 log_callback(void *userdata, u8 level, char *message)
-{
- (void)userdata;
- (void)level;
- __android_log_print(ANDROID_LOG_DEBUG, "CTV", "%*.*s", 0, AL_LOG_MESSAGE_SIZE, message);
- return al_strnlen(message, AL_LOG_MESSAGE_SIZE);
-}
-
-static struct ctv c = { 0 };
-
-s32 stl_platform_main(u32 argc, str *argv)
-{
- (void)argc;
- (void)argv;
-
- al_set_print(log_callback, NULL);
-
-#ifdef CAMU_HAVE_FFMPEG
- camu_ff_common_init();
-#endif
-
- if (!camu_desktop_open(&c.desktop, "ctv")) {
- return 0;
- }
-
- nn_thread_create(&c.thread, event_loop_thread, &c);
-
- return EXIT_SUCCESS;
-}
diff --git a/src/fruits/ctv/meson.build b/src/fruits/ctv/meson.build
deleted file mode 100644
index bf6f9e5..0000000
--- a/src/fruits/ctv/meson.build
+++ /dev/null
@@ -1,4 +0,0 @@
-ctv_src = ['ctv.c']
-ctv_deps = [common_deps, desktop]
-# https://github.com/google/ExoPlayer/issues/9933#issuecomment-1029775358
-shared_library('ctv', ctv_src, dependencies: ctv_deps, link_args: ['-Wl,-Bsymbolic', '-static-libstdc++'])
diff --git a/src/fruits/droid/ctv/app/src/main/AndroidManifest.xml b/src/fruits/droid/ctv/app/src/main/AndroidManifest.xml
index e957943..30a4d35 100644
--- a/src/fruits/droid/ctv/app/src/main/AndroidManifest.xml
+++ b/src/fruits/droid/ctv/app/src/main/AndroidManifest.xml
@@ -2,6 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
+ <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
+ <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-feature android:name="android.software.leanback"
android:required="false" />
<uses-feature android:name="android.hardware.touchscreen"
@@ -27,6 +31,28 @@
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
- </application>
+ <activity-alias
+ android:name=".api.file.OpenWithActivity"
+ android:exported="true"
+ android:targetActivity="android.app.NativeActivity">
+ <meta-data
+ android:name="android.app.lib_name"
+ android:value="ctv" />
+
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:scheme="content" />
+ <data android:scheme="file" />
+ <data android:scheme="http" />
+ <data android:mimeType="application/*" />
+ <data android:mimeType="audio/*" />
+ <data android:mimeType="image/*" />
+ <data android:mimeType="text/*" />
+ <data android:mimeType="video/*" />
+ </intent-filter>
+ </activity-alias>
+ </application>
</manifest>
diff --git a/src/liana/client.c b/src/liana/client.c
index 2b62612..1e873c6 100644
--- a/src/liana/client.c
+++ b/src/liana/client.c
@@ -138,7 +138,7 @@ 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) {
+ if (!client->streams.count) {
log_warn("Resource has no streams.");
goto out;
}
diff --git a/src/liana/handlers/cdio_server.c b/src/liana/handlers/cdio_server.c
index c67330d..65bd990 100644
--- a/src/liana/handlers/cdio_server.c
+++ b/src/liana/handlers/cdio_server.c
@@ -6,7 +6,7 @@
#include "cdio.h"
-#define SECTORS_PER_PACKET 8
+#define SAMPLES_PER_PACKET 128
static bool cdio_server_init(struct lia_server_handler *handler, struct cch_handle *handle)
{
@@ -75,7 +75,8 @@ static void cdio_server_step(struct lia_server_handler *handler)
static void cdio_server_write_packet(struct lia_server_handler *handler, struct nn_packet *packet)
{
struct lia_cdio_server *cdio = (struct lia_cdio_server *)handler;
- off_t size = CDIO_CD_FRAMESIZE_RAW * SECTORS_PER_PACKET;
+ size_t bps = camu_audio_format_bytes_per_sample(&cdio->fmt) * cdio->fmt.channel_count;
+ off_t size = bps * SAMPLES_PER_PACKET;
nn_buffer_ensure_space(&cdio->buffer, size);
s32 ret = cch_handle_read(cdio->handle, nn_buffer_get_ptr(&cdio->buffer, 0), size);
if (ret == CAMU_ERR_EOF) {
@@ -87,7 +88,8 @@ static void cdio_server_write_packet(struct lia_server_handler *handler, struct
nn_packet_write_u8(packet, LIANA_PACKET_DATA);
nn_packet_write_s32(packet, 0);
nn_packet_write_f64(packet, cdio->pts);
- s32 sample_count = ret / (camu_audio_format_bytes_per_sample(&cdio->fmt) * cdio->fmt.channel_count);
+ al_assert(ret % bps == 0);
+ s32 sample_count = ret / bps;
nn_packet_write_s32(packet, sample_count);
cdio->buffer.size = ret;
nn_packet_write_buffer(packet, &cdio->buffer);
diff --git a/src/liana/list.c b/src/liana/list.c
index d12caab..01c2ff3 100644
--- a/src/liana/list.c
+++ b/src/liana/list.c
@@ -712,7 +712,7 @@ static void handle_clear(struct lia_list *list)
static void run_queue(struct lia_list *list)
{
if (!list->cmd) {
- if (list->command_queue.count) {
+ if (list->command_queue.count > 0) {
al_array_pop_at(list->command_queue, 0, list->cmd);
} else {
return;
diff --git a/src/liana/server.c b/src/liana/server.c
index 5065425..791fe0e 100644
--- a/src/liana/server.c
+++ b/src/liana/server.c
@@ -124,7 +124,7 @@ static void free_connection(struct lia_node_connection *conn)
struct lia_node *node = conn->node;
al_assert(conn->handler);
conn->handler->free(&conn->handler);
- cch_entry_return_handle(conn->node->entry, &conn->handle);
+ cch_entry_return_handle(node->entry, &conn->handle);
nn_packet_pool_free(&conn->pool);
bool removed = al_array_remove(node->connections, conn);
al_assert(removed);
@@ -272,31 +272,32 @@ static void signal_callback(void *userdata)
nn_thread_join(&conn->thread);
nn_signal_stop(&conn->signal);
- al_array_remove(node->requests, conn);
struct nn_packet_stream *stream = conn->stream;
struct nn_packet *packet = conn->packet;
conn->packet = NULL;
+ if (packet) {
+ al_array_remove(node->requests, conn);
+ }
+
if (!packet || conn->errored) {
conn->handler->free(&conn->handler);
cch_entry_return_handle(node->entry, &conn->handle);
}
- if (!packet) {
- // Connection was closed before init was done.
+ if (!packet) { // Connection was closed before init was done.
+ al_free(conn);
if (should_free_node(node)) {
free_node(node);
}
nn_packet_stream_free(stream);
al_free(stream);
- al_free(conn);
} else if (conn->errored) {
// We must return the packet before disconnecting.
nn_packet_stream_return_packet(stream, packet);
- al_free(conn);
- conn = NULL;
demote_and_disconnect_stream(server, stream);
+ al_free(conn);
} else {
conn->id = get_incremental_id(server);
al_array_push(node->connections, conn);
@@ -337,10 +338,12 @@ static struct lia_node_connection *get_connection_from_id(struct lia_node *node,
static void pre_init_connection_closed_callback(void *userdata, struct nn_packet_stream *stream)
{
struct lia_node_connection *conn = (struct lia_node_connection *)userdata;
+ struct lia_node *node = conn->node;
struct nn_packet *packet = conn->packet;
// Checked in signal_callback and will signal to cleanup the connection.
conn->packet = NULL;
nn_packet_stream_return_packet(stream, packet);
+ al_array_remove(node->requests, conn);
}
static void packet_callback(void *userdata, struct nn_packet_stream *stream, struct nn_packet *packet)
@@ -439,6 +442,10 @@ static nn_thread_result NNWT_THREADCALL init_duration_thread(void *userdata)
{
struct lia_node *node = (struct lia_node *)userdata;
nn_thread_set_name("liana_init_dur");
+#if 0 // This is really bad for large resources and is just for testing.
+ lia_prepare_visual_data(&node->handle, &node->visual);
+ cch_handle_seek(&node->handle, 0, SEEK_SET);
+#endif
if (!node->handler->init(node->handler, &node->handle)) {
node->errored = true;
} else {
diff --git a/src/liana/server.h b/src/liana/server.h
index bafe95b..ec6f82e 100644
--- a/src/liana/server.h
+++ b/src/liana/server.h
@@ -7,6 +7,8 @@
#include "../cache/entry.h"
+#include "process.h"
+
//#define LIANA_SERVER_LOOP
struct lia_node_connection {
@@ -46,6 +48,8 @@ struct lia_node {
struct cch_handle handle;
struct nn_thread thread;
struct nn_signal signal;
+ // @TODO: This should probably be on camu_resource instead.
+ struct lia_visual_data visual;
};
struct lia_server {
diff --git a/src/liana/vcr.c b/src/liana/vcr.c
index f9dad7b..3a61e32 100644
--- a/src/liana/vcr.c
+++ b/src/liana/vcr.c
@@ -26,6 +26,7 @@ enum {
enum {
VCR_TRACK_RUNNING = 0,
VCR_TRACK_STOPPED,
+ VCR_TRACK_ERRORED,
VCR_TRACK_CLOSED
};
@@ -131,8 +132,8 @@ static nn_thread_result NNWT_THREADCALL vcr_track_thread(void *userdata)
nn_thread_set_priority(NNWT_THREAD_SCHED_FIFO, 32);
struct lia_vcr_track *track = (struct lia_vcr_track *)userdata;
struct lia_vcr *vcr = track->vcr;
- const char thread_name[16] = "\0"; // 16 = limit.
- al_snprintf((char *)thread_name, sizeof(thread_name), "vcr:%hu_%d", vcr->node_id, track->stream->index);
+ char thread_name[16] = "\0"; // 16 = limit.
+ al_snprintf(thread_name, sizeof(thread_name), "vcr:%hu_%d", vcr->node_id, track->stream->index);
nn_thread_set_name(thread_name);
bool corked;
@@ -177,12 +178,13 @@ static nn_thread_result NNWT_THREADCALL vcr_track_thread(void *userdata)
return_entire_cache(track);
track->cache.disabled = true;
nn_packet_cache_unlock(&track->cache);
+ // Let flush()/close() know we forcefully exited the thread.
+ track->state = VCR_TRACK_ERRORED;
nn_mutex_unlock(&track->lock);
return 0;
}
- if (!packet) {
- // Wait on EOF.
+ if (!packet) { // Wait on EOF.
track->state = VCR_TRACK_STOPPED;
}
@@ -410,6 +412,7 @@ void lia_vcr_set_buffered(struct lia_vcr_track *track)
// Meaning track->lock will be held.
void lia_vcr_cork(struct lia_vcr_track *track)
{
+ al_assert(track->state != VCR_TRACK_ERRORED);
track->state = VCR_TRACK_STOPPED;
}
@@ -422,6 +425,9 @@ void lia_vcr_uncork(struct lia_vcr_track *track)
// being held by flush(). flush() sets the state to CLOSED and signals the
// cond. Now nn_cond_is_waiting() is false at the point uncork() acquires the lock.
nn_mutex_lock(&track->lock);
+ // If a buffer is running under MARK_LOW, it will be continuously trying to uncork()
+ // the track. Meaning an erroring handle_packet() could happen at the same time as
+ // an uncork(). Causing track->state to be ERRORED after we acquire the lock here.
if (track->state != VCR_TRACK_STOPPED) {
nn_mutex_unlock(&track->lock);
return;
diff --git a/src/libsink/desktop.c b/src/libsink/desktop.c
index e9ccedb..2707f34 100644
--- a/src/libsink/desktop.c
+++ b/src/libsink/desktop.c
@@ -127,12 +127,11 @@ static void log_sink_status(struct camu_desktop *c)
u64 pts = camu_clock_get_last_pts(&current->clock) * 1000000u;
u64 duration = current->client.duration;
- pts = MIN(duration, pts);
bool paused = current->paused;
camu_sink_return_current(sink);
- u64 remaining = duration - pts;
+ u64 remaining = duration - MIN(pts, duration);
if (remaining == 0) {
goto notplaying;
}
@@ -162,14 +161,17 @@ static void screen_callback(void *userdata, u8 op, void *opaque)
volume = (c->mixer.volume == 0.f) ? 1.f : 0.f;
}
camu_mixer_set_volume(&c->mixer, volume);
- log_info("Volume: %f.", volume);
+ *(f32 *)opaque = volume;
break;
}
case CAMU_SCREEN_OFFSET_VOLUME: {
f32 volume = camu_mixer_offset_volume(&c->mixer, *(f32 *)opaque);
- log_info("Volume: %f.", volume);
+ *(f32 *)opaque = volume;
break;
}
+ case CAMU_SCREEN_ADD:
+ camu_sink_add(&c->sink, (str *)opaque);
+ break;
case CAMU_SCREEN_SKIP:
camu_sink_skip(&c->sink, *(s32 *)opaque);
break;
@@ -189,6 +191,9 @@ static void screen_callback(void *userdata, u8 op, void *opaque)
camu_sink_shuffle(&c->sink);
break;
case CAMU_SCREEN_STATUS:
+ camu_sink_status(&c->sink, (struct camu_osd *)opaque);
+ break;
+ case CAMU_SCREEN_PRINT:
log_sink_status(c);
break;
#ifdef CAMU_SCREEN_DEBUG_KEY
diff --git a/src/libsink/sink.c b/src/libsink/sink.c
index 783f242..5f61bd4 100644
--- a/src/libsink/sink.c
+++ b/src/libsink/sink.c
@@ -3,6 +3,7 @@
#include <al/log.h>
#include <al/random.h>
#include <nnwt/multiplex.h>
+#include <nnwt/time.h>
#include "../server/common.h"
#include "../buffer/common.h"
@@ -49,6 +50,7 @@ enum {
EJECT_ENTRY,
CLOSE,
// List actions.
+ ADD,
SKIP,
TOGGLE_PAUSE,
SEEK,
@@ -57,6 +59,13 @@ enum {
END
};
+// Status reporting.
+enum {
+ NOTIFY_EMPTY = 1,
+ NOTIFY_NOT_EMPTY = 1 << 1,
+ NOTIFY_SEEK = 1 << 2
+};
+
// Number of entries to keep buffered at one time.
#define ENTRY_MAX_AGE 4
#define SINK_LRU_MAX UINT16_MAX
@@ -375,6 +384,17 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd)
sink->callback(sink->userdata, CAMU_SINK_EXIT, 0, NULL);
return;
}
+ case ADD: {
+ if (!sink->connected) 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_ADD);
+ nn_packet_write_str(packet, (str *)cmd->v.p);
+ al_str_free((str *)cmd->v.p);
+ al_free(cmd->v.p);
+ nn_rpc_connection_command(sink->conn, packet, NULL, NULL);
+ break;
+ }
case SKIP: {
if (!sink->connected) return;
struct camu_sink_entry *entry = (struct camu_sink_entry *)cmd->opaque;
@@ -719,12 +739,18 @@ static void switch_to(struct camu_sink *sink, struct camu_sink_entry *target)
if (stop_video) {
queue_cmd(sink, CMD(STOP, .v.u = CAMU_SINK_VIDEO));
- if (dangling_target || VIDEO_ENDED_OR_EMPTY(target)) {
+ // Refresh on VIDEO_ENDED_OR_EMPTY(target) should be covered by after_add_entry().
+ if (dangling_target) {
refresh_video_output(sink);
}
}
sink->current = dangling_target ? NULL : target;
+
+ if (sink->current) {
+ sink->notify_status |= NOTIFY_NOT_EMPTY;
+ nn_timer_again(&sink->empty_timer);
+ }
}
static void pause_and_swap_to(struct camu_sink *sink, struct camu_sink_entry *target, u64 at)
@@ -1330,6 +1356,15 @@ static struct camu_sink_entry *get_entry_from_id(struct camu_sink *sink, u64 id)
return NULL;
}
+static void unset_current(struct camu_sink *sink)
+{
+ struct camu_sink_entry *current = sink->current;
+ nn_mutex_unlock(&sink->lock);
+ if (current) {
+ maybe_disconnect_entry(current);
+ }
+}
+
static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn,
struct nn_packet *packet, struct nn_packet *rpacket)
{
@@ -1339,11 +1374,7 @@ 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);
- struct camu_sink_entry *current = sink->current;
- nn_mutex_unlock(&sink->lock);
- if (current) {
- maybe_disconnect_entry(current);
- }
+ unset_current(sink);
nn_mutex_lock(&sink->lock);
goto out;
}
@@ -1378,6 +1409,13 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn,
// Don't lock before client_connect() or we could deadlock in CLIENT_CLOSED on a failed socket_connect().
nn_mutex_lock(&sink->lock);
+ // lia_client_connect() can fail and call connection_closed_callback() in-line. Meaning this entry
+ // could already be disconnected here.
+ if (!al_array_contains(sink->entries, entry)) {
+ unset_current(sink);
+ goto out;
+ }
+
struct camu_sink_entry *current = sink->current;
if (op == LIANA_SINK_BUFFER) {
@@ -1494,24 +1532,30 @@ static bool pause_command_callback(void *userdata, struct nn_rpc_connection *con
switch (pause) {
case LIANA_PAUSE_PAUSE: {
entry->paused = true;
- camu_clock_pause(&entry->clock, at);
+ if (camu_clock_pause(&entry->clock, at)) {
+ if (entry == sink->current) {
+ queue_cmd(sink, CMD(STOP, .v.u = CAMU_SINK_VIDEO));
+ }
+ }
log_info("Clock paused.");
// Audio will be stopped in a BUFFER_PAUSED callback.
- // Video will be stopped in a CLOCK_PAUSED callback.
+ // Video will be stopped above or in a CLOCK_PAUSED callback.
break;
}
case LIANA_PAUSE_RESUME: {
entry->paused = false;
camu_clock_resume(&entry->clock, at);
log_info("Clock resumed.");
- if (!VIDEO_ENDED_OR_EMPTY(entry) && !VIDEO_IS_STATIC(entry)) {
- queue_cmd(sink, CMD(START, .v.u = CAMU_SINK_VIDEO));
- }
- if (!AUDIO_ENDED_OR_EMPTY(entry)) {
- if (!sink->local) {
- camu_audio_buffer_resync(&entry->audio.buf);
+ if (entry == sink->current) {
+ if (!VIDEO_ENDED_OR_EMPTY(entry) && !VIDEO_IS_STATIC(entry)) {
+ queue_cmd(sink, CMD(START, .v.u = CAMU_SINK_VIDEO));
+ }
+ if (!AUDIO_ENDED_OR_EMPTY(entry)) {
+ if (!sink->local) {
+ camu_audio_buffer_resync(&entry->audio.buf);
+ }
+ queue_cmd(sink, CMD(START, .v.u = CAMU_SINK_AUDIO));
}
- queue_cmd(sink, CMD(START, .v.u = CAMU_SINK_AUDIO));
}
break;
}
@@ -1542,6 +1586,7 @@ static bool seek_command_callback(void *userdata, struct nn_rpc_connection *conn
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;
+ sink->notify_status |= NOTIFY_SEEK;
nn_mutex_unlock(&sink->lock);
// The rest of the seek is handled in CLIENT_REMOVE_BUFFERS/RESUME_AT/RECONNECTED.
lia_client_seek(&entry->client, pos, at);
@@ -1589,6 +1634,7 @@ static void connection_callback(void *userdata, struct nn_rpc_connection *conn)
if (sink->conn) al_assert(sink->conn == conn);
sink->conn = conn;
sink->connected = true;
+ refresh_video_output(sink); // For OSD.
sink->connection_number = al_u16_inc_wrap(sink->connection_number);
if (sink->connection_number == 0) sink->connection_number = 1;
identify_on_connection(sink);
@@ -1608,6 +1654,9 @@ static void reconnect_timer_callback(void *userdata, struct nn_timer *timer)
static void connection_closed_callback(void *userdata, struct nn_rpc_connection *conn)
{
struct camu_sink *sink = (struct camu_sink *)userdata;
+ // @TODO: This is broken for an immediately failing reconnect().
+ // If the nn_rpc_reconnect() in this function fails and recurses on connection_closed_callback(),
+ // sink->conn will be NULL and we will not attempt to reconnect.
bool reconnect = sink->conn != NULL;
bool disconnected = sink->conn && sink->connection_number > 0;
if (sink->conn) {
@@ -1628,6 +1677,18 @@ static void connection_closed_callback(void *userdata, struct nn_rpc_connection
}
}
+static void empty_timer_callback(void *userdata, struct nn_timer *timer)
+{
+ struct camu_sink *sink = (struct camu_sink *)userdata;
+ (void)timer;
+ if (!sink->current) {
+ sink->notify_status = NOTIFY_EMPTY;
+ refresh_video_output(sink);
+ nn_timer_stop(&sink->empty_timer);
+ }
+ nn_timer_set_repeat(&sink->empty_timer, NNWT_TS_FROM_USEC(1000000));
+}
+
bool camu_sink_init(struct camu_sink *sink, struct nn_event_loop *loop,
struct camu_mixer *mixer, struct camu_renderer *renderer)
{
@@ -1647,6 +1708,10 @@ bool camu_sink_init(struct camu_sink *sink, struct nn_event_loop *loop,
sink->suspended = NULL;
al_array_init(sink->previous);
al_array_init(sink->entries);
+ nn_timer_init(&sink->empty_timer, sink->loop, empty_timer_callback, sink);
+ nn_timer_set_repeat(&sink->empty_timer, NNWT_TS_FROM_USEC(100000));
+ nn_timer_again(&sink->empty_timer);
+ sink->notify_status = 0;
// Start high to exercise the wrapping path.
sink->lru = SINK_LRU_MAX - al_random_int(0, ENTRY_MAX_AGE);
mixer->callback = mixer_callback;
@@ -1690,6 +1755,13 @@ void camu_sink_return_current(struct camu_sink *sink)
nn_mutex_unlock(&sink->lock);
}
+void camu_sink_add(struct camu_sink *sink, str *path)
+{
+ str *copy = al_alloc_object(str);
+ al_str_clone(copy, path);
+ queue_cmd(sink, CMD(ADD, .v.p = copy));
+}
+
void camu_sink_skip(struct camu_sink *sink, s32 n)
{
nn_mutex_lock(&sink->lock);
@@ -1721,7 +1793,9 @@ void camu_sink_seek(struct camu_sink *sink, void *value, u8 mode)
pts = camu_clock_get_last_pts(&current->clock);
}
nn_mutex_unlock(&sink->lock);
- if (!current || duration == 0) return;
+ if (!current || duration == 0) {
+ return;
+ }
struct camu_sink_cmd cmd = {
.op = SEEK,
.opaque = current
@@ -1735,7 +1809,7 @@ void camu_sink_seek(struct camu_sink *sink, void *value, u8 mode)
case CAMU_SEEK_RELATIVE: {
f64 offset = *(f64 *)value;
pts = MAX(pts + offset, 0.0);
- cmd.v.u = (u64)(pts * 1000000);
+ cmd.v.u = (u64)(pts * 1000000.0);
break;
}
case CAMU_SEEK_PERCENT: {
@@ -1758,6 +1832,45 @@ void camu_sink_shuffle(struct camu_sink *sink)
queue_cmd(sink, CMD(SHUFFLE));
}
+void camu_sink_status(struct camu_sink *sink, struct camu_osd *osd)
+{
+ nn_mutex_lock(&sink->lock);
+ struct camu_sink_entry *current = sink->current;
+ osd->connecting = !sink->connected;
+ if (sink->notify_status & NOTIFY_EMPTY) {
+ sink->notify_status &= ~NOTIFY_EMPTY;
+ osd->show = true;
+ osd->shown_for |= CAMU_OSD_EMPTY;
+ }
+ if (sink->notify_status & NOTIFY_NOT_EMPTY) {
+ sink->notify_status &= ~NOTIFY_NOT_EMPTY;
+ if (osd->shown_for & CAMU_OSD_EMPTY) {
+ osd->shown_for &= ~CAMU_OSD_EMPTY;
+ osd->show = false;
+ }
+ }
+ if (sink->notify_status & NOTIFY_SEEK) {
+ sink->notify_status &= ~NOTIFY_SEEK;
+ if (!osd->show) {
+ osd->flash = CAMU_OSD_FLASH_FOR(0.75);
+ }
+ }
+ nn_mutex_unlock(&sink->lock);
+ if (current) {
+ osd->paused = camu_clock_is_user_paused(&current->clock);
+ bool armed_for_pause = false;
+ osd->pts = camu_clock_get_pts(&current->clock, 0.0, false, &armed_for_pause);
+ if (CAMU_PTS_CONSIDER_PAUSED(osd->pts)) {
+ osd->pts = camu_clock_get_last_pts(&current->clock);
+ }
+ osd->duration = current->client.duration;
+ } else {
+ osd->paused = false;
+ osd->pts = 0.0;
+ osd->duration = 0;
+ }
+}
+
void camu_sink_stop(struct camu_sink *sink)
{
queue_cmds(sink, 3,
@@ -1775,6 +1888,7 @@ void camu_sink_close(struct camu_sink *sink)
sink->conn = NULL; // Signal to connection_closed_callback() we're done.
nn_rpc_conn_disconnect(conn);
}
+ nn_timer_stop(&sink->empty_timer);
struct camu_sink_entry *entry;
al_array_foreach_rev(sink->entries, i, entry) {
al_array_remove_at(sink->entries, i);
diff --git a/src/libsink/sink.h b/src/libsink/sink.h
index acb6a51..6000b4f 100644
--- a/src/libsink/sink.h
+++ b/src/libsink/sink.h
@@ -8,6 +8,7 @@
#include "../util/queue.h"
#include "../liana/client.h"
+#include "../screen/screen.h"
#include "../buffer/clock.h"
#include "../buffer/audio.h"
#ifndef CAMU_SINK_NO_VIDEO
@@ -70,7 +71,7 @@ struct camu_sink_entry {
struct camu_sink_cmd {
u8 op;
- union { s64 i; u64 u; f64 f; } v;
+ union { s64 i; u64 u; f64 f; void *p; } v;
void *opaque;
};
@@ -95,6 +96,8 @@ struct camu_sink {
struct camu_sink_entry *suspended;
array(struct camu_sink_entry *) previous;
array(struct camu_sink_entry *) entries;
+ struct nn_timer empty_timer;
+ u8 notify_status;
u16 lru;
struct {
u8 state;
@@ -115,11 +118,13 @@ bool camu_sink_init(struct camu_sink *sink, struct nn_event_loop *loop,
bool camu_sink_connect(struct camu_sink *sink, str *name, u8 type, str *addr, u16 port);
struct camu_sink_entry *camu_sink_get_current(struct camu_sink *sink);
void camu_sink_return_current(struct camu_sink *sink);
+void camu_sink_add(struct camu_sink *sink, str *path);
void camu_sink_skip(struct camu_sink *sink, s32 n);
void camu_sink_toggle_pause(struct camu_sink *sink);
void camu_sink_seek(struct camu_sink *sink, void *value, u8 mode);
void camu_sink_reseek(struct camu_sink *sink);
void camu_sink_shuffle(struct camu_sink *sink);
+void camu_sink_status(struct camu_sink *sink, struct camu_osd *osd);
void camu_sink_stop(struct camu_sink *sink);
void camu_sink_close(struct camu_sink *sink);
void camu_sink_free(struct camu_sink *sink);
diff --git a/src/render/renderer.h b/src/render/renderer.h
index 0eae5cb..ffd590e 100644
--- a/src/render/renderer.h
+++ b/src/render/renderer.h
@@ -48,7 +48,7 @@ struct camu_renderer {
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL (*get_vk_proc_address)(VkInstance, const char *),
VkResult (*vk_create_surface)(void *, VkInstance, VkSurfaceKHR *),
VkResult (*vk_destroy_surface)(VkInstance, VkSurfaceKHR),
- const char *const *(*vk_get_extensions)(u32 *),
+ const char * const *(*vk_get_extensions)(u32 *),
#elif defined STELA_API_DX11
HWND win32_window,
#elif defined STELA_API_OPENGL
diff --git a/src/render/renderer_libplacebo.c b/src/render/renderer_libplacebo.c
index d6f95a8..8c28162 100644
--- a/src/render/renderer_libplacebo.c
+++ b/src/render/renderer_libplacebo.c
@@ -2,15 +2,21 @@
//#define AL_LOG_ENABLE_TRACE
#include <al/log.h>
#include <al/lib.h>
+#include <al/math.h>
#include <nnwt/file.h>
#include <nnwt/time.h>
+#include <libplacebo/shaders.h>
#include "../screen/screen.h"
#include "../util/color_palette.h"
+#include "../util/print_time.h"
#include "renderer_libplacebo.h"
#include "queue_libplacebo.h"
+#include "shaders/osd_header.h"
+#include "shaders/osd.h"
+#include "shaders/osd_binds.h"
#include "shaders/vr_video.h"
#ifdef AL_DEBUG
@@ -21,14 +27,18 @@
#endif
#define CRT_BACKGROUND
-#define CRT_BACKGROUND_COLOR (1.f / 7.5f)
-#define CRT_BACKGROUND_COLOR_ON (0.025f)
+#define CRT_BACKGROUND_COLOR(c4) do { \
+ c4[0] = 0.1333f; c4[1] = 0.1333f; c4[2] = 0.1333f; \
+} while (0)
+#define CRT_BACKGROUND_COLOR_ON(lr, c4) do { \
+ if (lr->params.background != PL_CLEAR_SKIP) { \
+ c4[0] = 0.025f; c4[1] = 0.025f; c4[2] = 0.025f; \
+ } else { \
+ c4[0] = 0.f; c4[1] = 0.f; c4[2] = 0.f; \
+ } \
+} while (0)
-#ifdef CRT_BACKGROUND
-static f32 clear_color[4] = { CRT_BACKGROUND_COLOR, CRT_BACKGROUND_COLOR, CRT_BACKGROUND_COLOR, 1.f };
-#else
static f32 clear_color[4] = { 0.f, 0.f, 0.f, 1.f };
-#endif
static struct pl_render_params default_params;
@@ -62,13 +72,14 @@ static void renderer_lp_set(struct camu_renderer *renderer, u8 option, u8 value)
{
struct camu_renderer_lp *lr = (struct camu_renderer_lp *)renderer;
switch (option) {
- case CAMU_RENDERER_FULLSCREEN:
+ case CAMU_RENDERER_FULLSCREEN: {
#ifdef STELA_API_DX11
IDXGISwapChain *d3d_swapchain = pl_d3d11_swapchain_unwrap(lr->swapchain);
IDXGISwapChain_SetFullscreenState(d3d_swapchain, value, NULL);
IDXGISwapChain_Release(d3d_swapchain);
#endif
break;
+ }
case CAMU_RENDERER_SCALING:
switch (value) {
case CAMU_SCALING_DEFAULT:
@@ -106,7 +117,7 @@ static void log_callback(void *userdata, enum pl_log_level level, const char *me
// will be shot anyway so allow it.
log_info(message);
} else {
- log_debug(message);
+ log_info(message);
}
}
@@ -136,7 +147,7 @@ static bool renderer_lp_create_renderer(struct camu_renderer *renderer, u32 *wid
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL (*get_vk_proc_address)(VkInstance, const char *),
VkResult (*vk_create_surface)(void *, VkInstance, VkSurfaceKHR *),
VkResult (*vk_destroy_surface)(VkInstance, VkSurfaceKHR),
- const char *const *(*vk_get_extensions)(u32 *),
+ const char * const *(*vk_get_extensions)(u32 *),
#elif defined STELA_API_DX11
HWND win32_window,
#elif defined STELA_API_OPENGL
@@ -174,7 +185,7 @@ static bool renderer_lp_create_renderer(struct camu_renderer *renderer, u32 *wid
lr->vk_destroy_surface = vk_destroy_surface;
u32 num;
- const char *const *extensions = vk_get_extensions(&num);
+ const char * const *extensions = vk_get_extensions(&num);
lr->vk_inst = pl_vk_inst_create(lr->logger, pl_vk_inst_params(
.debug = RENDERER_DEBUG,
.get_proc_addr = get_vk_proc_address,
@@ -229,7 +240,7 @@ static bool renderer_lp_create_renderer(struct camu_renderer *renderer, u32 *wid
// https://learn.microsoft.com/en-us/windows/win32/api/dxgi/ne-dxgi-dxgi_swap_chain_flag
// https://learn.microsoft.com/en-us/windows/win32/api/dxgi/ne-dxgi-dxgi_swap_effect
char *FLIP = getenv("SINK_DXGI_FLIP");
- if (!FLIP) FLIP = "0";
+ if (!FLIP) FLIP = "1";
lr->swapchain = pl_d3d11_create_swapchain(lr->d3d11, pl_d3d11_swapchain_params(
.window = win32_window,
.blit = al_strscmp(FLIP, "0") == 0,
@@ -280,6 +291,22 @@ static bool renderer_lp_create_renderer(struct camu_renderer *renderer, u32 *wid
log_info("Using OpenGL.");
#endif
+#ifdef USE_LIBPLACEBO_CACHE
+ lr->cache = pl_cache_create(pl_cache_params(
+ .log = lr->logger,
+ .max_total_size = MB(50)
+ ));
+ pl_gpu_set_cache(lr->gpu, lr->cache);
+ FILE *cache_file = fopen("sink.cache", "rb");
+ if (cache_file) {
+ pl_cache_load_file(lr->cache, cache_file);
+ //p->cache_sig = pl_cache_signature(p->cache);
+ fclose(cache_file);
+ }
+#endif
+
+ lr->dispatch = pl_dispatch_create(lr->gpu->log, lr->gpu);
+
lr->renderer = pl_renderer_create(lr->logger, lr->gpu);
pl_swapchain_resize_compat(lr->swapchain, width, height);
@@ -291,6 +318,7 @@ static bool renderer_lp_create_renderer(struct camu_renderer *renderer, u32 *wid
al_memset(&lr->params, 0, sizeof(struct pl_render_params));
lr->params = default_params;
+
lr->params.deband_params = NULL;
//lr->params.frame_mixer = NULL;
@@ -302,16 +330,79 @@ static bool renderer_lp_create_renderer(struct camu_renderer *renderer, u32 *wid
// Prioritize a more consistent image.
lr->params.correct_subpixel_offsets = true;
- //lr->params.distort_params = pl_distort_params(.unscaled = false);
- lr->vr_emulation_hook[0] = pl_mpv_user_shader_parse(lr->gpu, vr_video_shader, sizeof(vr_video_shader) - 1);
- /*
+ lr->osd.vars[0].var = pl_var_uvec2("screen");
+ lr->osd.vars[1].var = pl_var_uint("scale");
+ lr->osd.vars[2].var = pl_var_float("time");
+ lr->osd.vars[3].var = pl_var_uint("show_osd");
+ lr->osd.vars[4].var = pl_var_uint("connecting");
+ lr->osd.vars[5].var = pl_var_uint("paused");
+ lr->osd.vars[6].var = pl_var_uint("bar_mode");
+ lr->osd.vars[7].var = pl_var_float("bar");
+ lr->osd.vars[8].var = pl_var_float("dbar");
+ lr->osd.vars[9].var = pl_var_float("bbar");
+ lr->osd.vars[10].var = pl_var_uint("show_hour");
+ lr->osd.vars[11].var = pl_var_uint("negative");
+ static const char num_names[9][6] = {
+ "num1", "num2", "num3", "num4",
+ "num5", "num6", "num7", "num8",
+ "num9"
+ };
+ for (u32 i = 0; i < 9; i++) {
+ lr->osd.vars[12 + i].var = pl_var_uint(num_names[i]);
+ }
+ for (u32 i = 0; i < 21; i++) {
+ lr->osd.vars[i].dynamic = true;
+ }
+
+ lr->osd.attribs[0] = (struct pl_vertex_attrib){
+ .name = "pos",
+ .offset = 0,
+ .fmt = pl_find_vertex_fmt(lr->gpu, PL_FMT_FLOAT, 2)
+ };
+ lr->osd.attribs[1] = (struct pl_vertex_attrib){
+ .name = "coord",
+ .offset = sizeof(f32) * 2,
+ .fmt = pl_find_vertex_fmt(lr->gpu, PL_FMT_FLOAT, 2)
+ };
+
+ al_memcpy(lr->osd.vertices, (f32[]){
+ 1.f, 1.f, 1.f, 1.f,
+ -1.f, -1.f, 0.f, 0.f,
+ -1.f, 1.f, 0.f, 1.f,
+ -1.f, -1.f, 0.f, 0.f,
+ 1.f, -1.f, 1.f, 0.f,
+ 1.f, 1.f, 1.f, 1.f,
+ }, sizeof(f32) * 24);
+
+ lr->osd.prelude = osd_header_shader;
+ lr->osd.header = osd_shader;
+ lr->osd.binds = osd_binds_shader;
+#if 0
struct nn_file file;
- if (nn_file_open(&file, &al_str_c(""), 0)) {
+ if (nn_file_open(&file, &al_str_c("../src/render/shaders/osd_header.glsl"), 0)) {
+ char *c_str;
+ nn_file_read_as_c_str(&file, &c_str);
+ lr->osd.prelude = c_str;
+ nn_file_close(&file);
+ }
+ if (nn_file_open(&file, &al_str_c("../src/render/shaders/osd.glsl"), 0)) {
char *c_str;
- size_t length = nn_file_read_as_c_str(&file, &c_str);
- const struct pl_hook *hook = pl_mpv_user_shader_parse(lr->gpu, (const char *)c_str, length);
+ nn_file_read_as_c_str(&file, &c_str);
+ lr->osd.header = c_str;
+ nn_file_close(&file);
}
- */
+ if (nn_file_open(&file, &al_str_c("../src/render/shaders/osd_binds.glsl"), 0)) {
+ char *c_str;
+ nn_file_read_as_c_str(&file, &c_str);
+ lr->osd.binds = c_str;
+ nn_file_close(&file);
+ }
+#endif
+ lr->osd.tick_time = -1.0;
+ lr->osd.logged_fail = false;
+
+ lr->vr_init_failed = false;
+ lr->vr_emulation_hook[0] = NULL;
#ifdef CAMU_HAVE_FFMPEG
lr->r.get_buffer2 = pl_get_buffer2;
@@ -385,6 +476,125 @@ static inline intptr_t float_64_hash(f64 value)
return hash;
}
+#define ROUND_SECONDS(h, m, s, n) do { \
+ if (n >= 50) { \
+ if (s == 59) { \
+ if (m == 59) { \
+ h++; \
+ m = 0; \
+ } else m++; \
+ s = 0; \
+ } else s++; \
+ } \
+} while (0)
+
+static inline bool render_osd(struct camu_renderer_lp *lr, struct camu_screen *scr)
+{
+ f64 duration = scr->osd.duration / 1000000.0;
+ f32 bar, dbar, bbar;
+ f32 progress = ((duration != 0) ? scr->osd.pts / duration : 0.f);
+ if (scr->osd.bar_mode != CAMU_OSD_BAR_VOLUME) {
+ dbar = progress;
+ bbar = dbar; // @TODO: Should be buffered, once that's supported in the VCR.
+ } else {
+ dbar = 0.f;
+ bbar = 0.f;
+ }
+ bar = (scr->osd.bar_mode == CAMU_OSD_BAR_PROGRESS) ? progress : scr->osd.bar;
+ lr->osd.screen_size[0] = scr->width;
+ lr->osd.screen_size[1] = scr->height;
+ lr->osd.vars[0].data = lr->osd.screen_size;
+ u32 scale = 1;
+ lr->osd.vars[1].data = &scale;
+ f64 tick = nn_get_tick();
+ if (lr->osd.tick_time == -1.0) lr->osd.tick_time = tick;
+ lr->osd.time = (f32)(tick - lr->osd.tick_time);
+ lr->osd.vars[2].data = &lr->osd.time;
+ lr->osd.vars[3].data = &scr->osd.show;
+ lr->osd.vars[4].data = &scr->osd.connecting;
+ lr->osd.vars[5].data = &scr->osd.paused;
+ lr->osd.vars[6].data = &scr->osd.bar_mode;
+ lr->osd.vars[7].data = &bar;
+ lr->osd.vars[8].data = &dbar;
+ lr->osd.vars[9].data = &bbar;
+ u32 hours1, minutes1, seconds1, hundredths;
+ u32 show_hour;
+ show_hour = camu_split_time(scr->osd.duration, &hours1, &minutes1, &seconds1, &hundredths);
+ ROUND_SECONDS(hours1, minutes1, seconds1, hundredths);
+ lr->osd.vars[10].data = &show_hour;
+ u32 negative;
+ negative = scr->osd.pts < 0.0f;
+ lr->osd.vars[11].data = &negative;
+ lr->osd.vars[15].data = &hours1;
+ lr->osd.vars[16].data = &minutes1;
+ lr->osd.vars[17].data = &seconds1;
+ u32 hours2, minutes2, seconds2;
+ camu_split_time(fabs(scr->osd.pts) * 1000000u, &hours2, &minutes2, &seconds2, &hundredths);
+ // The trade-off of rounding PTS is that if we do, second 0 will be shown for only half a second.
+ // If we don't, more noticeably, PTS will never equal duration if duration rounds up.
+ // There's also the option of not rounding either.
+ ROUND_SECONDS(hours2, minutes2, seconds2, hundredths);
+ lr->osd.vars[12].data = &hours2;
+ lr->osd.vars[13].data = &minutes2;
+ lr->osd.vars[14].data = &seconds2;
+ u32 zero = 0;
+ lr->osd.vars[18].data = &zero;
+ lr->osd.vars[19].data = &zero;
+ lr->osd.vars[20].data = &zero;
+ if (scr->osd.bar_mode == CAMU_OSD_BAR_SEEK) {
+ u32 hours3, minutes3, seconds3;
+ camu_split_time(fabs(duration * bar) * 1000000u, &hours3, &minutes3, &seconds3, &hundredths);
+ ROUND_SECONDS(hours3, minutes3, seconds3, hundredths);
+ lr->osd.vars[18].data = &hours3;
+ lr->osd.vars[19].data = &minutes3;
+ lr->osd.vars[20].data = &seconds3;
+ } else {
+ f64 whole = floor(bar);
+ f64 frac = (bar - whole) * 100.0;
+ u32 num1, num2;
+ num1 = (u32)whole;
+ num2 = (u32)round(frac);
+ lr->osd.vars[19].data = &num1;
+ lr->osd.vars[20].data = &num2;
+ }
+
+ lr->osd.sh = pl_dispatch_begin(lr->dispatch);
+ pl_shader_custom(lr->osd.sh, &(struct pl_custom_shader){
+ .description = "osd",
+ .prelude = lr->osd.prelude,
+ .header = scr->osd.show_binds ? lr->osd.binds : lr->osd.header,
+ .body = "color = (show_osd == 0u) ? vec4(0.) : draw_osd();",
+ .output = PL_SHADER_SIG_COLOR,
+ .num_variables = 21,
+ .variables = lr->osd.vars
+ });
+
+ struct pl_color_repr repr = lr->frame.color_repr;
+ pl_shader_color_map_ex(lr->osd.sh, NULL, pl_color_map_args(
+ .src = pl_color_space_srgb,
+ .dst = lr->frame.color_space
+ ));
+ pl_shader_encode_color(lr->osd.sh, &repr);
+
+ struct pl_dispatch_vertex_params params = { 0 };
+ params = *pl_dispatch_vertex_params(
+ .shader = &lr->osd.sh,
+ .target = lr->frame.fbo,
+ .blend_params = &pl_alpha_overlay,
+ .vertex_attribs = lr->osd.attribs,
+ .num_vertex_attribs = 2,
+ .vertex_stride = sizeof(f32) * 4,
+ .vertex_position_idx = 0,
+ .vertex_coords = PL_COORDS_NORMALIZED,
+ .vertex_flipped = lr->frame.flipped,
+ .vertex_type = PL_PRIM_TRIANGLE_LIST,
+ .vertex_count = 6,
+ .vertex_data = lr->osd.vertices
+ );
+
+ return pl_dispatch_vertex(lr->dispatch, &params);
+}
+
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;
@@ -396,9 +606,8 @@ static bool renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
#endif
if (!lr->have_frame) {
- struct pl_swapchain_frame frame;
- if (pl_swapchain_start_frame(lr->swapchain, &frame)) {
- pl_frame_from_swapchain(&lr->target, &frame);
+ if (pl_swapchain_start_frame(lr->swapchain, &lr->frame)) {
+ pl_frame_from_swapchain(&lr->target, &lr->frame);
lr->have_frame = true;
#ifdef CRT_BACKGROUND
lr->frame_cleared = false;
@@ -413,7 +622,15 @@ static bool renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
}
f64 mouse_x, mouse_y;
- bool vr_emulation = scr->vr_emulation && lr->vr_emulation_hook[0];
+ bool vr_emulation = scr->vr_emulation && !lr->vr_init_failed;
+ if (vr_emulation && !lr->vr_emulation_hook[0]) {
+ lr->vr_emulation_hook[0] = pl_mpv_user_shader_parse(lr->gpu, vr_video_shader, sizeof(vr_video_shader) - 1);
+ lr->vr_init_failed = !lr->vr_emulation_hook[0];
+ if (lr->vr_init_failed) {
+ log_error("Failed to compile VR emulation shader.");
+ vr_emulation = false;
+ }
+ }
if (vr_emulation) {
mouse_x = (scr->last_pointer_x / scr->width) - (scr->vr_calibrate_x - 0.5);
mouse_y = (scr->last_pointer_y / scr->height) - (scr->vr_calibrate_y - 0.5);
@@ -484,8 +701,8 @@ static bool 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 = mix.frames[0]->rotation - video->view.rotation;
- /*
//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;
@@ -494,15 +711,7 @@ static bool renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
*/
#ifdef CRT_BACKGROUND
if (!lr->frame_cleared) {
- if (lr->params.background != PL_CLEAR_SKIP) {
- clear_color[0] = CRT_BACKGROUND_COLOR_ON;
- clear_color[1] = CRT_BACKGROUND_COLOR_ON;
- clear_color[2] = CRT_BACKGROUND_COLOR_ON;
- } else {
- clear_color[0] = 0.f;
- clear_color[1] = 0.f;
- clear_color[2] = 0.f;
- }
+ CRT_BACKGROUND_COLOR_ON(lr, clear_color);
pl_frame_clear_rgba(lr->gpu, &lr->target, clear_color);
lr->frame_cleared = true;
}
@@ -539,14 +748,20 @@ static bool renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
#ifdef CRT_BACKGROUND
if (!lr->frame_cleared) {
- clear_color[0] = CRT_BACKGROUND_COLOR;
- clear_color[1] = CRT_BACKGROUND_COLOR;
- clear_color[2] = CRT_BACKGROUND_COLOR;
+ CRT_BACKGROUND_COLOR(clear_color);
pl_frame_clear_rgba(lr->gpu, &lr->target, clear_color);
lr->frame_cleared = true;
}
#endif
+ if (scr->osd.show || (result & RESULT_WAIT)) {
+ al_assert(lr->osd.prelude && lr->osd.header && lr->osd.binds);
+ if (!render_osd(lr, scr) && !lr->osd.logged_fail) {
+ log_error("Failed to render OSD.");
+ lr->osd.logged_fail = true;
+ }
+ }
+
if (pl_swapchain_submit_frame(lr->swapchain)) {
lr->have_frame = false;
} else {
@@ -571,10 +786,25 @@ static bool renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
void renderer_lp_free(struct camu_renderer **renderer)
{
struct camu_renderer_lp *lr = (struct camu_renderer_lp *)*renderer;
+ if (lr->have_frame) {
+ pl_swapchain_submit_frame(lr->swapchain);
+ lr->have_frame = false;
+ }
#ifdef CAMU_HAVE_SUBTITLES
if (lr->ass) ass_library_done(lr->ass);
#endif
if (lr->vr_emulation_hook[0]) pl_mpv_user_shader_destroy(&lr->vr_emulation_hook[0]);
+#ifdef USE_LIBPLACEBO_CACHE
+ if (lr->cache) {
+ FILE *cache_file = fopen("sink.cache", "wb");
+ if (cache_file) {
+ pl_cache_save_file(lr->cache, cache_file);
+ fclose(cache_file);
+ }
+ pl_cache_destroy(&lr->cache);
+ }
+#endif
+ if (lr->dispatch) pl_dispatch_destroy(&lr->dispatch);
if (lr->renderer) pl_renderer_destroy(&lr->renderer);
if (lr->swapchain) pl_swapchain_destroy(&lr->swapchain);
#if defined STELA_API_VULKAN
diff --git a/src/render/renderer_libplacebo.h b/src/render/renderer_libplacebo.h
index 532963f..a8a5001 100644
--- a/src/render/renderer_libplacebo.h
+++ b/src/render/renderer_libplacebo.h
@@ -16,6 +16,8 @@
#include <ass/ass.h>
#endif
+//#define USE_LIBPLACEBO_CACHE
+
struct camu_renderer_lp {
struct camu_renderer r;
#if defined STELA_API_VULKAN
@@ -29,15 +31,34 @@ struct camu_renderer_lp {
pl_opengl gl;
#endif
pl_gpu gpu;
+#ifdef USE_LIBPLACEBO_CACHE
+ pl_cache cache;
+#endif
+ pl_dispatch dispatch;
pl_log logger;
pl_swapchain swapchain;
- struct pl_frame target;
+ struct pl_swapchain_frame frame;
bool have_frame;
bool frame_cleared;
+ struct pl_frame target;
pl_renderer renderer;
f64 last_swap_tick;
struct pl_render_params params;
+ struct {
+ char *prelude;
+ char *header;
+ char *binds;
+ pl_shader sh;
+ u32 screen_size[2];
+ f32 time;
+ struct pl_shader_var vars[21];
+ f32 vertices[24];
+ struct pl_vertex_attrib attribs[2];
+ f64 tick_time;
+ bool logged_fail;
+ } osd;
const struct pl_hook *vr_emulation_hook[1];
+ bool vr_init_failed;
#ifdef CAMU_HAVE_SUBTITLES
ASS_Library *ass;
#endif
diff --git a/src/render/shaders/osd.h b/src/render/shaders/osd.h
new file mode 100644
index 0000000..1109a50
--- /dev/null
+++ b/src/render/shaders/osd.h
@@ -0,0 +1,73 @@
+static char osd_shader[] = \
+"float text(vec2 uv, vec2 pos1, vec2 pos2) {\n"
+" float col = 0.;\n"
+" pp = pos1;\n"
+" if (connecting != 0u) {\n"
+" float t = mod(time,1.8);\n"
+" for (int i = 0; i < 3; i++) {\n"
+" col += char(ch_per,uv)*float(t >= float(i)*0.6);\n"
+" }\n"
+" } else if (paused != 0u) {\n"
+" col = char(ch_p,uv)+char(ch_a,uv)+char(ch_u,uv)+char(ch_s,uv)+char(ch_e,uv)+char(ch_d,uv);\n"
+" }\n"
+" pp = pos2;\n"
+" if (negative != 0u) {\n"
+" col += char(ch_dsh,uv);\n"
+" }\n"
+" if (show_hour != 0u) {\n"
+" col += integer(num1,uv)+char(ch_col,uv);\n"
+" }\n"
+" col += integer(num2,uv)+char(ch_col,uv)+integer(num3,uv)+char(ch_lsl,uv);\n"
+" if (show_hour != 0u) {\n"
+" col += integer(num4,uv)+char(ch_col,uv);\n"
+" }\n"
+" col += integer(num5,uv)+char(ch_col,uv)+integer(num6,uv);\n"
+" return col;\n"
+"}\n"
+"float text_over(vec2 uv, vec2 pos3) {\n"
+" float col = 0.;\n"
+" pp = pos3;\n"
+" if (show_hour != 0u && bar_mode == 1u) {\n"
+" col = integer(num7,uv)+char(ch_col,uv);\n"
+" }\n"
+" col += integer(num8,uv)+((bar_mode == 2u) ? char(ch_per,uv) : char(ch_col,uv))+integer(num9,uv);\n"
+" return col;\n"
+"}\n"
+"vec4 draw_osd() {\n"
+" vec2 sf = vec2(screen/scale);\n"
+" vec2 ss = sf/3.;\n"
+" vec2 c = vec2(coord.x,1.-coord.y);\n"
+" vec2 uv = c*ss;\n"
+" vec2 pos1 = vec2(8.,ss.y-4.-STRH(1.));\n"
+" float width1 = (show_hour != 0u) ? 17. : 11.;\n"
+" width1 += float(negative);\n"
+" vec2 pos2 = floor(vec2(ss.x/2.-STRW(width1)/2.,ss.y/9.));\n"
+" pos2.x -= STRW(0.5) * float(negative);\n"
+" float ov = fract(bar);\n"
+" ov = (-1.+(bar-ov)+float(c.x < ov))/1.5;\n"
+" vec4 color = mix(bcolor,mix(tcolor,vec4(0.7,0.45,0.,1.),ov),float(c.x < bar));\n"
+" float alpha = (c.x >= bar) ? 0.7 : 1.;\n"
+" color = mix(color,vec4(tcolor.xyz,alpha)+float(bar > dbar)*0.15,float(c.x < dbar));\n"
+" color = mix(color,vec4(tcolor.xyz,alpha-0.1)+float(bar > bbar)*0.15,float(c.x < bbar)*0.5);\n"
+" color *= float(uv.y <= 5.);\n"
+" if (paused != 0u) {\n"
+" color = mix(color,bcolor,in_box(uv,pos1-vec2(2.,2.),pos1+vec2(STRW(6.)+1.,STRH(1.)+1.)));\n"
+" }\n"
+" color = mix(color,bcolor,in_box(uv,pos2-vec2(3.,1.),pos2+vec2(STRW(width1)+2.,STRH(1.)+2.)));\n"
+" float pixel = text(uv,pos1,pos2);\n"
+" color = mix(color,tcolor,pixel);\n"
+" color -= pixel*float(mod(c.y*sf.y,3.) < 1.)*0.5;\n"
+" if (bar_mode != 0u) {\n"
+" ss = sf/2.;\n"
+" uv = c*ss;\n"
+" float width2 = (show_hour != 0u && bar_mode == 1u) ? 8. : 5.;\n"
+" vec2 pos3 = floor(vec2(ss.x/2.-STRW(width2)/2.,11.));\n"
+" float box = in_box(uv,pos3-vec2(2.,2.),pos3+vec2(STRW(width2)+1.,STRH(1.)+3.));\n"
+" color.xyz = mix(color.xyz,bcolor.xyz,box);\n"
+" color.w = color.w+(bcolor.w*box);\n"
+" pixel = text_over(uv,pos3);\n"
+" color = mix(color,tcolor,pixel);\n"
+" color -= pixel*float(mod(c.y*sf.y,2.) < 1.)*0.5;\n"
+" }\n"
+" return color;\n"
+"}\n";
diff --git a/src/render/shaders/osd_binds.h b/src/render/shaders/osd_binds.h
new file mode 100644
index 0000000..5d6806a
--- /dev/null
+++ b/src/render/shaders/osd_binds.h
@@ -0,0 +1,311 @@
+static char osd_binds_shader[] = \
+"float text(vec2 uv, vec2 pos) {\n"
+" float col = 0.;\n"
+" pp = vec2(pos.x,pos.y);\n"
+" col += char(ch_L,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_f,uv);\n"
+" col += char(ch_t,uv);\n"
+" col += char(ch_lsl,uv);\n"
+" col += char(ch_R,uv);\n"
+" col += char(ch_i,uv);\n"
+" col += char(ch_g,uv);\n"
+" col += char(ch_h,uv);\n"
+" col += char(ch_t,uv);\n"
+" col += char(ch_col,uv);\n"
+" spaces(1)\n"
+" col += char(ch_N,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_x,uv);\n"
+" col += char(ch_t,uv);\n"
+" col += char(ch_lsl,uv);\n"
+" col += char(ch_P,uv);\n"
+" col += char(ch_r,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_v,uv);\n"
+" col += char(ch_i,uv);\n"
+" col += char(ch_o,uv);\n"
+" col += char(ch_u,uv);\n"
+" col += char(ch_s,uv);\n"
+" col += char(ch_spc,uv);\n"
+" col += char(ch_lpa,uv);\n"
+" col += char(ch_C,uv);\n"
+" col += char(ch_t,uv);\n"
+" col += char(ch_r,uv);\n"
+" col += char(ch_l,uv);\n"
+" col += char(ch_col,uv);\n"
+" col += char(ch_spc,uv);\n"
+" col += char(ch_crs,uv);\n"
+" col += char(ch_lsl,uv);\n"
+" col += char(ch_dsh,uv);\n"
+" col += char(ch_1,uv);\n"
+" col += char(ch_0,uv);\n"
+" col += char(ch_s,uv);\n"
+" col += char(ch_rpa,uv);\n"
+" pos.y -= STRH(1.2);\n"
+" pp = vec2(pos.x,pos.y);\n"
+" col += char(ch_S,uv);\n"
+" col += char(ch_p,uv);\n"
+" col += char(ch_a,uv);\n"
+" col += char(ch_c,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_col,uv);\n"
+" spaces(6)\n"
+" col += char(ch_P,uv);\n"
+" col += char(ch_a,uv);\n"
+" col += char(ch_u,uv);\n"
+" col += char(ch_s,uv);\n"
+" col += char(ch_e,uv);\n"
+" pos.y -= STRH(1.2);\n"
+" pp = vec2(pos.x,pos.y);\n"
+" col += char(ch_M,uv);\n"
+" col += char(ch_col,uv);\n"
+" spaces(10)\n"
+" col += char(ch_M,uv);\n"
+" col += char(ch_u,uv);\n"
+" col += char(ch_t,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_spc,uv);\n"
+" col += char(ch_lpa,uv);\n"
+" col += char(ch_S,uv);\n"
+" col += char(ch_h,uv);\n"
+" col += char(ch_i,uv);\n"
+" col += char(ch_f,uv);\n"
+" col += char(ch_t,uv);\n"
+" col += char(ch_col,uv);\n"
+" col += char(ch_spc,uv);\n"
+" col += char(ch_1,uv);\n"
+" col += char(ch_0,uv);\n"
+" col += char(ch_0,uv);\n"
+" col += char(ch_pct,uv);\n"
+" col += char(ch_rpa,uv);\n"
+" pos.y -= STRH(1.2);\n"
+" pp = vec2(pos.x,pos.y);\n"
+" col += char(ch_T,uv);\n"
+" col += char(ch_a,uv);\n"
+" col += char(ch_b,uv);\n"
+" col += char(ch_col,uv);\n"
+" spaces(8)\n"
+" col += char(ch_T,uv);\n"
+" col += char(ch_o,uv);\n"
+" col += char(ch_g,uv);\n"
+" col += char(ch_g,uv);\n"
+" col += char(ch_l,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_spc,uv);\n"
+" col += char(ch_O,uv);\n"
+" col += char(ch_S,uv);\n"
+" col += char(ch_D,uv);\n"
+" pos.y -= STRH(1.2);\n"
+" pp = vec2(pos.x,pos.y);\n"
+" col += char(ch_C,uv);\n"
+" col += char(ch_t,uv);\n"
+" col += char(ch_r,uv);\n"
+" col += char(ch_l,uv);\n"
+" col += char(ch_col,uv);\n"
+" spaces(7)\n"
+" col += char(ch_S,uv);\n"
+" col += char(ch_h,uv);\n"
+" col += char(ch_o,uv);\n"
+" col += char(ch_w,uv);\n"
+" col += char(ch_spc,uv);\n"
+" col += char(ch_O,uv);\n"
+" col += char(ch_S,uv);\n"
+" col += char(ch_D,uv);\n"
+" col += char(ch_lsl,uv);\n"
+" col += char(ch_S,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_k,uv);\n"
+" col += char(ch_spc,uv);\n"
+" col += char(ch_lpa,uv);\n"
+" col += char(ch_C,uv);\n"
+" col += char(ch_l,uv);\n"
+" col += char(ch_i,uv);\n"
+" col += char(ch_c,uv);\n"
+" col += char(ch_k,uv);\n"
+" col += char(ch_rpa,uv);\n"
+" pos.y -= STRH(1.2);\n"
+" pp = vec2(pos.x,pos.y);\n"
+" col += char(ch_S,uv);\n"
+" col += char(ch_col,uv);\n"
+" spaces(10)\n"
+" col += char(ch_T,uv);\n"
+" col += char(ch_o,uv);\n"
+" col += char(ch_g,uv);\n"
+" col += char(ch_g,uv);\n"
+" col += char(ch_l,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_spc,uv);\n"
+" col += char(ch_S,uv);\n"
+" col += char(ch_u,uv);\n"
+" col += char(ch_b,uv);\n"
+" col += char(ch_t,uv);\n"
+" col += char(ch_i,uv);\n"
+" col += char(ch_t,uv);\n"
+" col += char(ch_l,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_s,uv);\n"
+" pos.y -= STRH(1.2);\n"
+" pp = vec2(pos.x,pos.y);\n"
+" col += char(ch_E,uv);\n"
+" col += char(ch_col,uv);\n"
+" spaces(10)\n"
+" col += char(ch_R,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_s,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_k,uv);\n"
+" pos.y -= STRH(1.2);\n"
+" pp = vec2(pos.x,pos.y);\n"
+" col += char(ch_crs,uv);\n"
+" col += char(ch_lsl,uv);\n"
+" col += char(ch_dsh,uv);\n"
+" col += char(ch_col,uv);\n"
+" spaces(8)\n"
+" col += char(ch_Z,uv);\n"
+" col += char(ch_o,uv);\n"
+" col += char(ch_o,uv);\n"
+" col += char(ch_m,uv);\n"
+" col += char(ch_spc,uv);\n"
+" col += char(ch_I,uv);\n"
+" col += char(ch_n,uv);\n"
+" col += char(ch_lsl,uv);\n"
+" col += char(ch_O,uv);\n"
+" col += char(ch_u,uv);\n"
+" col += char(ch_t,uv);\n"
+" pos.y -= STRH(1.2);\n"
+" pp = vec2(pos.x,pos.y);\n"
+" col += char(ch_N,uv);\n"
+" col += char(ch_col,uv);\n"
+" spaces(10)\n"
+" col += char(ch_D,uv);\n"
+" col += char(ch_i,uv);\n"
+" col += char(ch_s,uv);\n"
+" col += char(ch_a,uv);\n"
+" col += char(ch_b,uv);\n"
+" col += char(ch_l,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_spc,uv);\n"
+" col += char(ch_S,uv);\n"
+" col += char(ch_c,uv);\n"
+" col += char(ch_a,uv);\n"
+" col += char(ch_l,uv);\n"
+" col += char(ch_i,uv);\n"
+" col += char(ch_n,uv);\n"
+" col += char(ch_g,uv);\n"
+" pos.y -= STRH(1.2);\n"
+" pp = vec2(pos.x,pos.y);\n"
+" col += char(ch_C,uv);\n"
+" col += char(ch_col,uv);\n"
+" spaces(10)\n"
+" col += char(ch_T,uv);\n"
+" col += char(ch_r,uv);\n"
+" col += char(ch_a,uv);\n"
+" col += char(ch_n,uv);\n"
+" col += char(ch_s,uv);\n"
+" col += char(ch_p,uv);\n"
+" col += char(ch_a,uv);\n"
+" col += char(ch_r,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_n,uv);\n"
+" col += char(ch_t,uv);\n"
+" col += char(ch_spc,uv);\n"
+" col += char(ch_B,uv);\n"
+" col += char(ch_a,uv);\n"
+" col += char(ch_c,uv);\n"
+" col += char(ch_k,uv);\n"
+" col += char(ch_g,uv);\n"
+" col += char(ch_r,uv);\n"
+" col += char(ch_o,uv);\n"
+" col += char(ch_u,uv);\n"
+" col += char(ch_n,uv);\n"
+" col += char(ch_d,uv);\n"
+" pos.y -= STRH(1.2);\n"
+" pp = vec2(pos.x,pos.y);\n"
+" col += char(ch_V,uv);\n"
+" col += char(ch_col,uv);\n"
+" spaces(10)\n"
+" col += char(ch_V,uv);\n"
+" col += char(ch_R,uv);\n"
+" col += char(ch_spc,uv);\n"
+" col += char(ch_lpa,uv);\n"
+" col += char(ch_S,uv);\n"
+" col += char(ch_h,uv);\n"
+" col += char(ch_i,uv);\n"
+" col += char(ch_f,uv);\n"
+" col += char(ch_t,uv);\n"
+" col += char(ch_col,uv);\n"
+" col += char(ch_spc,uv);\n"
+" col += char(ch_3,uv);\n"
+" col += char(ch_6,uv);\n"
+" col += char(ch_0,uv);\n"
+" col += char(ch_dgr,uv);\n"
+" col += char(ch_com,uv);\n"
+" col += char(ch_spc,uv);\n"
+" col += char(ch_C,uv);\n"
+" col += char(ch_t,uv);\n"
+" col += char(ch_r,uv);\n"
+" col += char(ch_l,uv);\n"
+" col += char(ch_col,uv);\n"
+" col += char(ch_spc,uv);\n"
+" col += char(ch_C,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_n,uv);\n"
+" col += char(ch_t,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_r,uv);\n"
+" col += char(ch_rpa,uv);\n"
+" pos.y -= STRH(1.2);\n"
+" pp = vec2(pos.x,pos.y);\n"
+" col += char(ch_B,uv);\n"
+" col += char(ch_col,uv);\n"
+" spaces(10)\n"
+" col += char(ch_S,uv);\n"
+" col += char(ch_w,uv);\n"
+" col += char(ch_a,uv);\n"
+" col += char(ch_p,uv);\n"
+" col += char(ch_spc,uv);\n"
+" col += char(ch_E,uv);\n"
+" col += char(ch_y,uv);\n"
+" col += char(ch_e,uv);\n"
+" pos.y -= STRH(1.2);\n"
+" pp = vec2(pos.x,pos.y);\n"
+" col += char(ch_R,uv);\n"
+" col += char(ch_col,uv);\n"
+" spaces(10)\n"
+" col += char(ch_R,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_s,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_t,uv);\n"
+" col += char(ch_spc,uv);\n"
+" col += char(ch_V,uv);\n"
+" col += char(ch_i,uv);\n"
+" col += char(ch_e,uv);\n"
+" col += char(ch_w,uv);\n"
+" pos.y -= STRH(1.2);\n"
+" pp = vec2(pos.x,pos.y);\n"
+" col += char(ch_Q,uv);\n"
+" col += char(ch_col,uv);\n"
+" spaces(10)\n"
+" col += char(ch_Q,uv);\n"
+" col += char(ch_u,uv);\n"
+" col += char(ch_i,uv);\n"
+" col += char(ch_t,uv);\n"
+" return col;\n"
+"}\n"
+"vec4 draw_osd() {\n"
+" vec2 sf = vec2(screen/scale);\n"
+" vec2 ss = sf/2.;\n"
+" vec2 c = vec2(coord.x,1.-coord.y);\n"
+" vec2 uv = c*ss;\n"
+" vec2 pos = floor(vec2(ss.x/2.-STRW(43.)/2.,(ss.y/2.)+STRH(1.2*10.)));\n"
+" float pixel = text(uv, pos);\n"
+" float box = in_box(uv,pos-vec2(8.,STRH(1.2*13.)+8.),pos+vec2(STRW(43.)+8.,STRH(1.2)+8.));\n"
+" vec4 color = bcolor*box;\n"
+" color = mix(color,tcolor,pixel);\n"
+" color -= pixel*float(mod(c.y*sf.y,3.) < 1.)*0.5;\n"
+" return color;\n"
+"}\n";
diff --git a/src/render/shaders/osd_header.h b/src/render/shaders/osd_header.h
new file mode 100644
index 0000000..5ba6491
--- /dev/null
+++ b/src/render/shaders/osd_header.h
@@ -0,0 +1,146 @@
+static char osd_header_shader[] = \
+"#define ch_dgr uvec4(0x001824,0x241800,0x000000,0x000000)\n"
+"#define ch_spc uvec4(0x000000,0x000000,0x000000,0x000000)\n"
+"#define ch_exc uvec4(0x003078,0x787830,0x300030,0x300000)\n"
+"#define ch_quo uvec4(0x006666,0x662400,0x000000,0x000000)\n"
+"#define ch_hsh uvec4(0x006C6C,0xFE6C6C,0x6CFE6C,0x6C0000)\n"
+"#define ch_dol uvec4(0x30307C,0xC0C078,0x0C0CF8,0x303000)\n"
+"#define ch_pct uvec4(0x000000,0xC4CC18,0x3060CC,0x8C0000)\n"
+"#define ch_amp uvec4(0x0070D8,0xD870FA,0xDECCDC,0x760000)\n"
+"#define ch_apo uvec4(0x003030,0x306000,0x000000,0x000000)\n"
+"#define ch_lbr uvec4(0x000C18,0x306060,0x603018,0x0C0000)\n"
+"#define ch_rbr uvec4(0x006030,0x180C0C,0x0C1830,0x600000)\n"
+"#define ch_ast uvec4(0x000000,0x663CFF,0x3C6600,0x000000)\n"
+"#define ch_crs uvec4(0x000000,0x18187E,0x181800,0x000000)\n"
+"#define ch_com uvec4(0x000000,0x000000,0x000038,0x386000)\n"
+"#define ch_dsh uvec4(0x000000,0x0000FE,0x000000,0x000000)\n"
+"#define ch_per uvec4(0x000000,0x000000,0x000038,0x380000)\n"
+"#define ch_lsl uvec4(0x000002,0x060C18,0x3060C0,0x800000)\n"
+"#define ch_0 uvec4(0x007CC6,0xD6D6D6,0xD6D6C6,0x7C0000)\n"
+"#define ch_1 uvec4(0x001030,0xF03030,0x303030,0xFC0000)\n"
+"#define ch_2 uvec4(0x0078CC,0xCC0C18,0x3060CC,0xFC0000)\n"
+"#define ch_3 uvec4(0x0078CC,0x0C0C38,0x0C0CCC,0x780000)\n"
+"#define ch_4 uvec4(0x000C1C,0x3C6CCC,0xFE0C0C,0x1E0000)\n"
+"#define ch_5 uvec4(0x00FCC0,0xC0C0F8,0x0C0CCC,0x780000)\n"
+"#define ch_6 uvec4(0x003860,0xC0C0F8,0xCCCCCC,0x780000)\n"
+"#define ch_7 uvec4(0x00FEC6,0xC6060C,0x183030,0x300000)\n"
+"#define ch_8 uvec4(0x0078CC,0xCCEC78,0xDCCCCC,0x780000)\n"
+"#define ch_9 uvec4(0x0078CC,0xCCCC7C,0x181830,0x700000)\n"
+"#define ch_col uvec4(0x000000,0x383800,0x003838,0x000000)\n"
+"#define ch_scl uvec4(0x000000,0x383800,0x003838,0x183000)\n"
+"#define ch_les uvec4(0x000C18,0x3060C0,0x603018,0x0C0000)\n"
+"#define ch_equ uvec4(0x000000,0x007E00,0x7E0000,0x000000)\n"
+"#define ch_grt uvec4(0x006030,0x180C06,0x0C1830,0x600000)\n"
+"#define ch_que uvec4(0x0078CC,0x0C1830,0x300030,0x300000)\n"
+"#define ch_ats uvec4(0x007CC6,0xC6DEDE,0xDEC0C0,0x7C0000)\n"
+"#define ch_A uvec4(0x003078,0xCCCCCC,0xFCCCCC,0xCC0000)\n"
+"#define ch_B uvec4(0x00FC66,0x66667C,0x666666,0xFC0000)\n"
+"#define ch_C uvec4(0x003C66,0xC6C0C0,0xC0C666,0x3C0000)\n"
+"#define ch_D uvec4(0x00F86C,0x666666,0x66666C,0xF80000)\n"
+"#define ch_E uvec4(0x00FE62,0x60647C,0x646062,0xFE0000)\n"
+"#define ch_F uvec4(0x00FE66,0x62647C,0x646060,0xF00000)\n"
+"#define ch_G uvec4(0x003C66,0xC6C0C0,0xCEC666,0x3E0000)\n"
+"#define ch_H uvec4(0x00CCCC,0xCCCCFC,0xCCCCCC,0xCC0000)\n"
+"#define ch_I uvec4(0x007830,0x303030,0x303030,0x780000)\n"
+"#define ch_J uvec4(0x001E0C,0x0C0C0C,0xCCCCCC,0x780000)\n"
+"#define ch_K uvec4(0x00E666,0x6C6C78,0x6C6C66,0xE60000)\n"
+"#define ch_L uvec4(0x00F060,0x606060,0x626666,0xFE0000)\n"
+"#define ch_M uvec4(0x00C6EE,0xFEFED6,0xC6C6C6,0xC60000)\n"
+"#define ch_N uvec4(0x00C6C6,0xE6F6FE,0xDECEC6,0xC60000)\n"
+"#define ch_O uvec4(0x00386C,0xC6C6C6,0xC6C66C,0x380000)\n"
+"#define ch_P uvec4(0x00FC66,0x66667C,0x606060,0xF00000)\n"
+"#define ch_Q uvec4(0x00386C,0xC6C6C6,0xCEDE7C,0x0C1E00)\n"
+"#define ch_R uvec4(0x00FC66,0x66667C,0x6C6666,0xE60000)\n"
+"#define ch_S uvec4(0x0078CC,0xCCC070,0x18CCCC,0x780000)\n"
+"#define ch_T uvec4(0x00FCB4,0x303030,0x303030,0x780000)\n"
+"#define ch_U uvec4(0x00CCCC,0xCCCCCC,0xCCCCCC,0x780000)\n"
+"#define ch_V uvec4(0x00CCCC,0xCCCCCC,0xCCCC78,0x300000)\n"
+"#define ch_W uvec4(0x00C6C6,0xC6C6D6,0xD66C6C,0x6C0000)\n"
+"#define ch_X uvec4(0x00CCCC,0xCC7830,0x78CCCC,0xCC0000)\n"
+"#define ch_Y uvec4(0x00CCCC,0xCCCC78,0x303030,0x780000)\n"
+"#define ch_Z uvec4(0x00FECE,0x981830,0x6062C6,0xFE0000)\n"
+"#define ch_lsb uvec4(0x003C30,0x303030,0x303030,0x3C0000)\n"
+"#define ch_rsl uvec4(0x000080,0xC06030,0x180C06,0x020000)\n"
+"#define ch_rsb uvec4(0x003C0C,0x0C0C0C,0x0C0C0C,0x3C0000)\n"
+"#define ch_pow uvec4(0x10386C,0xC60000,0x000000,0x000000)\n"
+"#define ch_usc uvec4(0x000000,0x000000,0x000000,0x00FF00)\n"
+"#define ch_a uvec4(0x000000,0x00780C,0x7CCCCC,0x760000)\n"
+"#define ch_b uvec4(0x00E060,0x607C66,0x666666,0xDC0000)\n"
+"#define ch_c uvec4(0x000000,0x0078CC,0xC0C0CC,0x780000)\n"
+"#define ch_d uvec4(0x001C0C,0x0C7CCC,0xCCCCCC,0x760000)\n"
+"#define ch_e uvec4(0x000000,0x0078CC,0xFCC0CC,0x780000)\n"
+"#define ch_f uvec4(0x00386C,0x6060F8,0x606060,0xF00000)\n"
+"#define ch_g uvec4(0x000000,0x0076CC,0xCCCC7C,0x0CCC78)\n"
+"#define ch_h uvec4(0x00E060,0x606C76,0x666666,0xE60000)\n"
+"#define ch_i uvec4(0x001818,0x007818,0x181818,0x7E0000)\n"
+"#define ch_j uvec4(0x000C0C,0x003C0C,0x0C0C0C,0xCCCC78)\n"
+"#define ch_k uvec4(0x00E060,0x60666C,0x786C66,0xE60000)\n"
+"#define ch_l uvec4(0x007818,0x181818,0x181818,0x7E0000)\n"
+"#define ch_m uvec4(0x000000,0x00FCD6,0xD6D6D6,0xC60000)\n"
+"#define ch_n uvec4(0x000000,0x00F8CC,0xCCCCCC,0xCC0000)\n"
+"#define ch_o uvec4(0x000000,0x0078CC,0xCCCCCC,0x780000)\n"
+"#define ch_p uvec4(0x000000,0x00DC66,0x666666,0x7C60F0)\n"
+"#define ch_q uvec4(0x000000,0x0076CC,0xCCCCCC,0x7C0C1E)\n"
+"#define ch_r uvec4(0x000000,0x00EC6E,0x766060,0xF00000)\n"
+"#define ch_s uvec4(0x000000,0x0078CC,0x6018CC,0x780000)\n"
+"#define ch_t uvec4(0x000020,0x60FC60,0x60606C,0x380000)\n"
+"#define ch_u uvec4(0x000000,0x00CCCC,0xCCCCCC,0x760000)\n"
+"#define ch_v uvec4(0x000000,0x00CCCC,0xCCCC78,0x300000)\n"
+"#define ch_w uvec4(0x000000,0x00C6C6,0xD6D66C,0x6C0000)\n"
+"#define ch_x uvec4(0x000000,0x00C66C,0x38386C,0xC60000)\n"
+"#define ch_y uvec4(0x000000,0x006666,0x66663C,0x0C18F0)\n"
+"#define ch_z uvec4(0x000000,0x00FC8C,0x1860C4,0xFC0000)\n"
+"#define ch_lpa uvec4(0x001C30,0x3060C0,0x603030,0x1C0000)\n"
+"#define ch_bar uvec4(0x001818,0x181800,0x181818,0x180000)\n"
+"#define ch_rpa uvec4(0x00E030,0x30180C,0x183030,0xE00000)\n"
+"#define ch_tid uvec4(0x0073DA,0xCE0000,0x000000,0x000000)\n"
+"#define ch_lar uvec4(0x000000,0x10386C,0xC6C6FE,0x000000)\n"
+"#define spaces(n) for (int i = 0; i < n; i++) { col += char(ch_spc,uv); }\n"
+"#define STRW(c) (c*8.)\n"
+"#define STRH(c) (c*12.)\n"
+"const vec4 tcolor = vec4(0.78,0.76,0.76,1.);\n"
+"const vec4 bcolor = vec4(0.1,0.1,0.1,0.65);\n"
+"vec2 pp = vec2(0.);\n"
+"float in_box(vec2 v, vec2 bl, vec2 tr) {\n"
+" vec2 s = step(bl,v)-step(tr,v);\n"
+" return s.x*s.y;\n"
+"}\n"
+"float sprite(uvec4 spr, vec2 uv) {\n"
+" uint b = uint((7.-uv.x)+uv.y*8.);\n"
+" uvec4 bv = (spr>>min(uvec4(b-72u,b-48u,b-24u,b),31u))&1u;\n"
+#ifndef NAUNET_ON_ANDROID
+#define HAVE_UVEC_DOT
+#endif
+#ifdef HAVE_UVEC_DOT
+" return in_box(uv,vec2(0.),vec2(8.,12.))*float(dot(bv,uvec4(1u)));\n"
+#else
+" return in_box(uv,vec2(0.),vec2(8.,12.))*float(bv.x+bv.y+bv.z+bv.w);\n"
+#endif
+"}\n"
+"float char(uvec4 ch, vec2 uv) {\n"
+" float px = sprite(ch,floor(uv-pp));\n"
+" pp += vec2(8.,0.);\n"
+" return px;\n"
+"}\n"
+"uvec4 digit(uint d) {\n"
+" switch (d) {\n"
+" case 0: return ch_0;\n"
+" case 1: return ch_1;\n"
+" case 2: return ch_2;\n"
+" case 3: return ch_3;\n"
+" case 4: return ch_4;\n"
+" case 5: return ch_5;\n"
+" case 6: return ch_6;\n"
+" case 7: return ch_7;\n"
+" case 8: return ch_8;\n"
+" case 9:\n"
+" default: return ch_9;\n"
+" }\n"
+"}\n"
+"float integer(uint number, vec2 uv) {\n"
+" float r = 0.;\n"
+" for (int i = 1; i >= 0; i--) {\n"
+" r += char(digit(uint(mod(float(number)/max(1.,float(i)*10.),10.))),uv);\n"
+" }\n"
+" return r;\n"
+"}\n";
diff --git a/src/screen/screen.c b/src/screen/screen.c
index c0527c3..ae9b001 100644
--- a/src/screen/screen.c
+++ b/src/screen/screen.c
@@ -24,6 +24,8 @@
#define SCREEN_LAST_CLICK_WITHIN(ns) \
(((scr)->last_click_ts != SCREEN_INVALID_TS) && (nn_get_timestamp() - (scr)->last_click_ts <= ns))
+#define SCREEN_OSD_BAR_HEIGHT 15
+
static void should_close_callback(void *userdata)
{
struct camu_screen *scr = (struct camu_screen *)userdata;
@@ -62,15 +64,15 @@ static void resize_callback(void *userdata, u32 width, u32 height)
do_resize(scr, width, height);
}
+// @TODO: Incomplete.
static struct camu_view *get_view_from_mouse_pos(struct camu_screen *scr)
{
struct camu_view *view = NULL;
+ if (scr->videos.count > 0) {
+ view = &al_array_last(scr->videos).view;
+ }
struct camu_screen_video *video;
al_array_foreach_ptr(scr->videos, i, video) {
- if (!view) {
- view = &video->view;
- continue;
- }
if (view->zindex < video->view.zindex) {
view = &video->view;
}
@@ -85,21 +87,26 @@ static void seek_to_percent_at_pointer(struct camu_screen *scr, f64 x)
scr->callback(scr->userdata, CAMU_SCREEN_PERCENT_SEEK, &percent);
}
-static bool pointer_pos_callback(void *userdata, f64 x, f64 y)
+static bool pointer_callback(void *userdata, u8 type, f64 x, f64 y)
{
struct camu_screen *scr = (struct camu_screen *)userdata;
struct camu_view *view = get_view_from_mouse_pos(scr);
bool queue_refresh = false;
- if (SCREEN_IS_DRAGGING(scr)) {
-#ifdef CAMU_SCREEN_DRAG_SEEK
- u64 now = nn_get_timestamp();
- if (view && now - scr->last_seek_ts > 100000) {
- seek_to_percent_at_pointer(scr, x);
- scr->last_seek_ts = now;
+ bool have_pointer_move = scr->window->flags & STELA_WINDOW_SUPPORTS_POINTER_MOVE;
+ switch (type) {
+ case STELA_POINTER_POS:
+ if (scr->vr_emulation && have_pointer_move) {
+ break;
}
+ if (SCREEN_IS_DRAGGING(scr)) {
+#ifdef CAMU_SCREEN_DRAG_SEEK
+ u64 now = nn_get_timestamp();
+ if (view && now - scr->last_seek_ts > 100000) {
+ seek_to_percent_at_pointer(scr, x);
+ scr->last_seek_ts = now;
+ }
#else
- if (view) {
- if (SCREEN_ZOOM_MODE(scr, CAMU_SCREEN_ZOOM_PAN_SIMPLE)) {
+ if (view && !SCREEN_MOD1(scr) && SCREEN_ZOOM_MODE(scr, CAMU_SCREEN_ZOOM_PAN_SIMPLE)) {
f64 dx = x - scr->last_pointer_x;
f64 dy = y - scr->last_pointer_y;
if (camu_view_pan_simple(view, scr->width, scr->height, dx, dy)) {
@@ -110,11 +117,26 @@ static bool pointer_pos_callback(void *userdata, f64 x, f64 y)
scr->last_click_ts = SCREEN_INVALID_TS;
}
}
- }
#endif
+ }
+ if (SCREEN_MOD1(scr) && scr->osd.duration > 0) {
+ f64 dx = x - scr->last_pointer_x;
+ if (fabs(dx) >= 2.0 || y >= scr->height - SCREEN_OSD_BAR_HEIGHT) {
+ scr->osd.bar_mode = CAMU_OSD_BAR_SEEK;
+ }
+ }
+ scr->last_pointer_x = x;
+ scr->last_pointer_y = y;
+ break;
+ case STELA_POINTER_MOVE:
+ if (!scr->vr_emulation || !have_pointer_move) {
+ break;
+ }
+ scr->last_pointer_x += x * 0.75f;
+ scr->last_pointer_y += y * 0.75f;
+ queue_refresh = true;
+ break;
}
- scr->last_pointer_x = x;
- scr->last_pointer_y = y;
return queue_refresh;
}
@@ -169,7 +191,7 @@ static bool touch_callback(void *userdata, s32 index, u8 phase, f64 x, f64 y)
f64 lx = scr->last_touch_x[0], lx2 = scr->last_touch_x[1];
f64 ly = scr->last_touch_y[0], ly2 = scr->last_touch_y[1];
f64 dist = distance(lx, lx2, ly, ly2);
- f64 mx = x = (lx + lx2) / 2.0, my = (ly + ly2) / 2.0;
+ f64 mx = (lx + lx2) / 2.0, my = (ly + ly2) / 2.0;
bool refresh = false;
struct camu_view *view = get_view_from_mouse_pos(scr);
if (view) {
@@ -190,7 +212,7 @@ static bool touch_callback(void *userdata, s32 index, u8 phase, f64 x, f64 y)
scr->last_pointer_x = scr->last_touch_x[1];
scr->last_pointer_y = scr->last_touch_y[1];
}
- bool refresh = pointer_pos_callback(scr, x, y);
+ bool refresh = pointer_callback(scr, STELA_POINTER_POS, x, y);
if (index == 0) {
scr->last_touch_x[0] = x;
scr->last_touch_y[0] = y;
@@ -213,7 +235,12 @@ static bool touch_callback(void *userdata, s32 index, u8 phase, f64 x, f64 y)
if (SCREEN_LAST_CLICK_WITHIN(200000)) {
f64 third = scr->width / 3.0;
if (x >= third && x <= third * 2.0) {
- scr->callback(scr->userdata, CAMU_SCREEN_TOGGLE_PAUSE, NULL);
+ if (scr->vr_emulation) {
+ scr->vr_calibrate_x = scr->last_pointer_x / scr->width;
+ scr->vr_calibrate_y = scr->last_pointer_y / scr->height;
+ } else {
+ scr->callback(scr->userdata, CAMU_SCREEN_TOGGLE_PAUSE, NULL);
+ }
} else {
if (scr->touch_mode) {
#if CAMU_HAVE_SUBTITLES
@@ -246,7 +273,8 @@ static bool mouse_button_callback(void *userdata, u8 state, u8 button)
break;
case STELA_BUTTON_RELEASED:
scr->flags &= ~CAMU_SCREEN_DRAGGING;
- scr->window->set_cursor(scr->window, SCREEN_MOUSE_MOD(scr) ? STELA_CURSOR_CROSSHAIR : SCREEN_DEFAULT_CURSOR(scr));
+ bool want_crosshair = SCREEN_MOUSE_MOD(scr) && scr->fullscreen;
+ scr->window->set_cursor(scr->window, want_crosshair ? STELA_CURSOR_CROSSHAIR : SCREEN_DEFAULT_CURSOR(scr));
if (SCREEN_LAST_CLICK_WITHIN(200000)) {
if (SCREEN_MOD1(scr)) {
seek_to_percent_at_pointer(scr, scr->last_pointer_x);
@@ -295,6 +323,9 @@ bool scroll_callback(void *userdata, f64 y)
if (SCREEN_MOD1(scr)) {
f32 change = (f32)stl_scale_scroll(y, 40.0);
scr->callback(scr->userdata, CAMU_SCREEN_OFFSET_VOLUME, &change);
+ scr->osd.bar_mode = CAMU_OSD_BAR_VOLUME;
+ scr->osd.bar = change;
+ return true;
} else {
struct camu_view *view = get_view_from_mouse_pos(scr);
if (view) {
@@ -321,14 +352,21 @@ static bool key_callback(void *userdata, u8 state, u16 button)
scr->window->close(scr->window);
scr->callback(scr->userdata, CAMU_SCREEN_CLOSE, NULL);
break;
+ case STELA_KEY_SLASH:
+ scr->osd.show_binds = !scr->osd.show_binds;
+ break;
case STELA_KEY_RIGHT:
if (SCREEN_MOD1(scr)) {
- f64 plus_ten_seconds = 10.0;
- scr->callback(scr->userdata, CAMU_SCREEN_RELATIVE_SEEK, &plus_ten_seconds);
+ if (SCREEN_MOD2(scr)) {
+ f64 one_hundred = 100.0;
+ scr->callback(scr->userdata, CAMU_SCREEN_PERCENT_SEEK, &one_hundred);
+ } else {
+ f64 plus_ten_seconds = 10.0;
+ scr->callback(scr->userdata, CAMU_SCREEN_RELATIVE_SEEK, &plus_ten_seconds);
+ }
break;
}
// fallthrough
- case STELA_KEY_TAB:
case STELA_KEY_D: {
s32 n = 1;
scr->callback(scr->userdata, CAMU_SCREEN_SKIP, &n);
@@ -351,6 +389,31 @@ static bool key_callback(void *userdata, u8 state, u16 button)
scr->callback(scr->userdata, CAMU_SCREEN_SKIP, &n);
break;
}
+ case STELA_KEY_LEFT_CONTROL:
+ case STELA_KEY_RIGHT_CONTROL:
+ if (scr->osd.shown_for & CAMU_OSD_VOLUME) {
+ scr->osd.shown_for &= ~CAMU_OSD_VOLUME;
+ scr->osd.shown_for |= CAMU_OSD_SEEK;
+ } else if (!scr->osd.show) {
+ scr->osd.show = true;
+ scr->osd.shown_for |= CAMU_OSD_SEEK;
+ }
+ if (scr->last_pointer_y >= scr->height - SCREEN_OSD_BAR_HEIGHT) {
+ scr->osd.bar_mode = CAMU_OSD_BAR_SEEK;
+ }
+ break;
+ case STELA_KEY_TAB:
+ if (!(scr->osd.shown_for & CAMU_OSD_SEEK)) {
+ if (scr->osd.flash >= 0.0) {
+ scr->osd.flash = 0.0;
+ } else {
+ scr->osd.show = !scr->osd.show;
+ scr->osd.shown_for = 0;
+ atomic_add(u32)(&scr->force_refresh, 1, AL_ATOMIC_RELAXED);
+ }
+ return true;
+ }
+ break;
case STELA_KEY_SPACE:
scr->callback(scr->userdata, CAMU_SCREEN_TOGGLE_PAUSE, NULL);
break;
@@ -366,22 +429,22 @@ static bool key_callback(void *userdata, u8 state, u16 button)
}
break;
case STELA_KEY_E:
- if (scr->vr_emulation) {
- if (scr->vr_emulation == CAMU_VR_180) {
- scr->vr_left_eye = !scr->vr_left_eye;
- log_info(scr->vr_left_eye ? "Left eye." : "Right eye.");
- }
- } else {
- scr->callback(scr->userdata, CAMU_SCREEN_RESEEK, NULL);
- }
+ scr->callback(scr->userdata, CAMU_SCREEN_RESEEK, NULL);
break;
case STELA_KEY_M: {
- f32 volume = SCREEN_MOD1(scr) ? 1.f : -1.f;
+ f32 volume = SCREEN_MOD2(scr) ? 1.f : -1.f;
scr->callback(scr->userdata, CAMU_SCREEN_SET_VOLUME, &volume);
+ scr->osd.bar_mode = CAMU_OSD_BAR_VOLUME;
+ scr->osd.bar = volume;
+ scr->osd.flash = CAMU_OSD_FLASH_FOR(0.75);
+ if (!scr->osd.show) {
+ scr->osd.show = true;
+ scr->osd.shown_for |= CAMU_OSD_VOLUME;
+ }
break;
}
case STELA_KEY_0:
- scr->callback(scr->userdata, CAMU_SCREEN_STATUS, NULL);
+ scr->callback(scr->userdata, CAMU_SCREEN_PRINT, NULL);
break;
#ifdef CAMU_SCREEN_DEBUG_KEY
case STELA_KEY_P:
@@ -504,9 +567,14 @@ static bool key_callback(void *userdata, u8 state, u16 button)
scr->vr_emulation = CAMU_VR_DISABLED;
log_info("VR disabled.");
}
- if (view) {
- camu_view_calculate(view, scr->width, scr->height);
- }
+ if (view) camu_view_calculate(view, scr->width, scr->height);
+ }
+ break;
+ }
+ case STELA_KEY_B: {
+ if (scr->vr_emulation == CAMU_VR_180) {
+ scr->vr_left_eye = !scr->vr_left_eye;
+ log_info(scr->vr_left_eye ? "Left eye." : "Right eye.");
}
break;
}
@@ -515,6 +583,17 @@ static bool key_callback(void *userdata, u8 state, u16 button)
}
break;
case STELA_BUTTON_RELEASED:
+ switch (button) {
+ case STELA_KEY_LEFT_CONTROL:
+ case STELA_KEY_RIGHT_CONTROL:
+ if (scr->osd.shown_for & CAMU_OSD_SEEK) {
+ scr->osd.shown_for &= ~CAMU_OSD_SEEK;
+ scr->osd.show = false;
+ atomic_add(u32)(&scr->force_refresh, 1, AL_ATOMIC_RELAXED);
+ }
+ scr->osd.bar_mode = CAMU_OSD_BAR_PROGRESS;
+ return true;
+ }
break;
default:
break;
@@ -529,11 +608,10 @@ static bool key_immediate_callback(void *userdata, u8 state, u16 button)
case STELA_BUTTON_PRESSED:
switch (button) {
case STELA_KEY_F:
- scr->fullscreen = !scr->fullscreen;
// On X11 and Wayland, MOUSE2 will be forcefully released when entering fullscreen.
- if (!SCREEN_MOUSE_MOD(scr)) {
- scr->window->set_cursor(scr->window, scr->fullscreen ? STELA_CURSOR_HIDDEN : STELA_CURSOR_NORMAL);
- }
+ scr->fullscreen = !scr->fullscreen;
+ bool want_crosshair = SCREEN_MOUSE_MOD(scr) && scr->fullscreen;
+ scr->window->set_cursor(scr->window, want_crosshair ? STELA_CURSOR_CROSSHAIR : SCREEN_DEFAULT_CURSOR(scr));
//#ifdef STELA_API_DX11
#if 0 // Stela's window_win32 fullscreen mode (borderless) is likely more desirable here.
scr->renderer->set(scr->renderer, CAMU_RENDERER_FULLSCREEN, scr->fullscreen);
@@ -563,18 +641,29 @@ static bool key_immediate_callback(void *userdata, u8 state, u16 button)
return false;
}
+#ifdef STELA_DRAG_AND_DROP
+static void drag_and_drop_callback(void *userdata, str *path)
+{
+ struct camu_screen *scr = (struct camu_screen *)userdata;
+ scr->callback(scr->userdata, CAMU_SCREEN_ADD, path);
+}
+#endif
+
bool camu_screen_init(struct camu_screen *scr)
{
atomic_store(s32)(&scr->state, CAMU_SCREEN_PAUSED, AL_ATOMIC_RELAXED);
scr->window = stl_window_create();
scr->window->render_callback = render_callback;
scr->window->resize_callback = resize_callback;
- scr->window->pointer_pos_callback = pointer_pos_callback;
+ scr->window->pointer_callback = pointer_callback;
scr->window->touch_callback = touch_callback;
scr->window->scroll_callback = scroll_callback;
scr->window->mouse_button_callback = mouse_button_callback;
scr->window->key_callback = key_callback;
scr->window->key_immediate_callback = key_immediate_callback;
+#ifdef STELA_DRAG_AND_DROP
+ scr->window->drag_and_drop_callback = drag_and_drop_callback;
+#endif
scr->window->should_close_callback = should_close_callback;
scr->window->userdata = scr;
scr->renderer = NULL;
@@ -592,6 +681,9 @@ bool camu_screen_init(struct camu_screen *scr)
scr->last_seek_ts = 0;
#endif
scr->touch_mode = false;
+ scr->osd.show_binds = false;
+ scr->osd.bar = -1.0;
+ scr->osd.flash = -1.0;
scr->vr_emulation = CAMU_VR_DISABLED;
scr->vr_left_eye = false;
scr->vr_calibrate_x = 0.5;
@@ -849,7 +941,9 @@ bool camu_screen_tick(struct camu_screen *scr, bool *force)
{
s32 state = atomic_load(s32)(&scr->state, AL_ATOMIC_RELAXED);
u32 force_refresh = atomic_load(u32)(&scr->force_refresh, AL_ATOMIC_ACQUIRE);
- bool paused = !scr->vr_emulation && state == CAMU_SCREEN_PAUSED && force_refresh == 0;
+ bool paused = state == CAMU_SCREEN_PAUSED && force_refresh == 0;
+ paused &= !scr->osd.show;
+ paused &= !scr->vr_emulation;
bool do_render = camu_screen_poll(scr, paused) || !paused;
force_refresh = atomic_load(u32)(&scr->force_refresh, AL_ATOMIC_ACQUIRE);
do_render |= force_refresh > 0;
@@ -858,6 +952,36 @@ bool camu_screen_tick(struct camu_screen *scr, bool *force)
if (force_refresh > 0) {
atomic_sub(u32)(&scr->force_refresh, 1, AL_ATOMIC_RELEASE);
}
+ bool was_paused = scr->osd.paused;
+ scr->callback(scr->userdata, CAMU_SCREEN_STATUS, &scr->osd);
+ if (!SCREEN_MOD1(scr)) {
+ if (scr->osd.flash >= 0.0) {
+ if (nn_get_tick() >= scr->osd.flash) {
+ scr->osd.flash = -1.0;
+ scr->osd.shown_for = 0;
+ scr->osd.show = false;
+ scr->osd.bar_mode = CAMU_OSD_BAR_PROGRESS;
+ *force = true;
+ } else {
+ scr->osd.show = true;
+ }
+ }
+ }
+ if (scr->osd.bar_mode == CAMU_OSD_BAR_SEEK) {
+ f64 percent = scr->last_pointer_x / scr->width;
+ scr->osd.bar = CLAMP(percent, 0.0, 100.0);
+ }
+ if (was_paused != scr->osd.paused) {
+ if (scr->osd.paused && !scr->osd.show) {
+ scr->osd.show = true;
+ scr->osd.shown_for |= CAMU_OSD_PAUSE;
+ } else if (scr->osd.shown_for & CAMU_OSD_PAUSE) {
+ scr->osd.shown_for &= ~CAMU_OSD_PAUSE;
+ scr->osd.show = false;
+ }
+ *force = true;
+ }
+ *force |= scr->osd.show;
return do_render;
}
diff --git a/src/screen/screen.h b/src/screen/screen.h
index 0d40a02..c3dcd7d 100644
--- a/src/screen/screen.h
+++ b/src/screen/screen.h
@@ -39,6 +39,7 @@ enum {
enum {
CAMU_SCREEN_SET_VOLUME = 0, // f32
CAMU_SCREEN_OFFSET_VOLUME, // f32
+ CAMU_SCREEN_ADD, // str
CAMU_SCREEN_SKIP, // s32
CAMU_SCREEN_TOGGLE_PAUSE,
CAMU_SCREEN_PERCENT_SEEK, // f64
@@ -46,6 +47,7 @@ enum {
CAMU_SCREEN_RESEEK,
CAMU_SCREEN_SHUFFLE,
CAMU_SCREEN_STATUS,
+ CAMU_SCREEN_PRINT,
#ifdef CAMU_SCREEN_DEBUG_KEY
CAMU_SCREEN_DEBUG,
#endif
@@ -57,6 +59,34 @@ struct camu_screen_video {
struct camu_video_buffer *buf;
};
+enum {
+ CAMU_OSD_PAUSE = 1,
+ CAMU_OSD_SEEK = 1 << 1,
+ CAMU_OSD_VOLUME = 1 << 2,
+ CAMU_OSD_EMPTY = 1 << 3
+};
+
+enum {
+ CAMU_OSD_BAR_PROGRESS = 0,
+ CAMU_OSD_BAR_SEEK,
+ CAMU_OSD_BAR_VOLUME
+};
+
+#define CAMU_OSD_FLASH_FOR(s) (nn_get_tick() + s)
+
+struct camu_osd {
+ u32 show;
+ u8 shown_for;
+ bool show_binds;
+ u32 connecting;
+ u32 paused;
+ u32 bar_mode;
+ f64 bar;
+ f64 flash;
+ f64 pts;
+ u64 duration;
+};
+
struct camu_screen {
atomic(s32) state;
struct stl_window *window;
@@ -86,6 +116,7 @@ struct camu_screen {
u64 last_seek_ts;
#endif
bool touch_mode;
+ struct camu_osd osd;
u8 vr_emulation;
bool vr_left_eye;
f64 vr_calibrate_x;
diff --git a/src/server/server.c b/src/server/server.c
index c547b5d..7781c05 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -12,6 +12,7 @@
#ifdef CAMU_HAVE_PORTAL
#include "../portal/src/packet_ext.h"
#endif
+#include "../codec/packet_ext.h"
#include "server.h"
#include "common.h"
@@ -334,7 +335,11 @@ static struct cch_entry *entry_from_post(struct camu_server *server, struct camu
if (index <= post->media.count) {
struct camu_post_media *media = &al_array_at(post->media, index);
if (!al_str_is_empty(&media->url)) {
+#ifdef NAUNET_HAS_CURL
entry = cch_handler_http_create(&media->url, server->loop);
+#else
+ (void)server;
+#endif
}
}
if (!entry) {
@@ -528,6 +533,22 @@ static void client_portal_callback(void *userdata0, void *userdata1, struct camu
}
#endif
+static void maybe_send_visual_data(struct camu_server *server, struct nn_rpc_connection *conn, u32 node_id)
+{
+ struct camu_resource *resource = get_resource_by_node_id(server, node_id);
+ if (resource) {
+ struct lia_visual_data *visual = &resource->node->visual;
+ if (!visual->samples) return;
+ struct nn_packet *packet = nn_rpc_get_packet(conn->rpc, CAMU_CLIENT_VISUAL_DATA);
+ nn_packet_write_u32(packet, node_id);
+ nn_packet_write_audio_format(packet, &visual->fmt);
+ nn_packet_write_u32(packet, visual->size);
+ NNWT_PACKET_WRITE_DATA(packet, visual->samples, visual->size);
+ nn_packet_write_u32(packet, visual->count);
+ nn_rpc_connection_command(conn, packet, NULL, NULL);
+ }
+}
+
static bool client_command_callback(void *userdata, struct nn_rpc_connection *conn,
struct nn_packet *packet, struct nn_packet *rpacket)
{
@@ -577,8 +598,7 @@ static bool client_command_callback(void *userdata, struct nn_rpc_connection *co
#endif
case CAMU_CLIENT_REQUEST_VISUAL_DATA: {
u32 node_id = nn_packet_read_u32(packet);
- struct camu_resource *resource = get_resource_by_node_id(server, node_id);
- (void)resource;
+ maybe_send_visual_data(server, conn, node_id);
break;
}
}
@@ -1004,3 +1024,8 @@ void camu_server_local_add(struct camu_server *server, struct nn_packet *packet)
{
handle_add_command(server, al_array_last(server->lists), packet);
}
+
+void camu_server_local_set_index(struct camu_server *server, u32 index)
+{
+ lia_list_skipto(al_array_last(server->lists), LIANA_SEQUENCE_ANY, index);
+}
diff --git a/src/server/server.h b/src/server/server.h
index b1548f8..6552413 100644
--- a/src/server/server.h
+++ b/src/server/server.h
@@ -57,3 +57,4 @@ void camu_server_free(struct camu_server *server);
// Local compat.
void camu_server_local_add(struct camu_server *server, struct nn_packet *packet);
+void camu_server_local_set_index(struct camu_server *server, u32 index);
diff --git a/src/util/print_time.h b/src/util/print_time.h
index 89644b2..b6ab625 100644
--- a/src/util/print_time.h
+++ b/src/util/print_time.h
@@ -2,19 +2,29 @@
#include <al/lib.h>
+static inline bool camu_split_time(u64 nanoseconds, u32 *hours, u32 *minutes, u32 *seconds, u32 *hundredths)
+{
+ *hours = nanoseconds / 3600000000u;
+ *minutes = nanoseconds / 60000000u;
+ *seconds = nanoseconds / 1000000u;
+ *hundredths = nanoseconds / 10000u;
+ bool over_an_hour = *minutes >= 60u;
+ *hundredths -= *seconds * 100u;
+ *seconds -= *minutes * 60u;
+ *minutes -= *hours * 60u;
+ return over_an_hour;
+}
+
static inline s32 camu_print_time(char *buf, size_t size, u64 nanoseconds, bool always_show_hour, u32 hour_padding)
{
- u32 hours = nanoseconds / 3600000000u;
- u32 minutes = nanoseconds / 60000000u;
- bool show_hour = always_show_hour || minutes >= 60u;
- u32 seconds = nanoseconds / 1000000u;
- seconds -= minutes * 60u;
- minutes -= hours * 60u;
+ u32 hours, minutes, seconds, hundredths;
+ bool show_hour = camu_split_time(nanoseconds, &hours, &minutes, &seconds, &hundredths);
+ show_hour |= always_show_hour;
s32 offset = 0;
if (show_hour && hour_padding >= 2 && hour_padding <= 4) {
static const char *padding_table[] = { "%.2u:", "%.3u:", "%.4u:" };
offset += al_snprintf(buf, size, padding_table[hour_padding - 2u], hours);
}
- offset += al_snprintf(buf + offset, size - offset, "%.2u:%.2u", minutes, seconds);
+ offset += al_snprintf(buf + offset, size - offset, "%.2u:%.2u.%.2u", minutes, seconds, hundredths);
return offset;
}
diff --git a/subprojects/stela.wrap b/subprojects/stela.wrap
index 40cb968..bafd02c 100644
--- a/subprojects/stela.wrap
+++ b/subprojects/stela.wrap
@@ -1,5 +1,5 @@
[wrap-git]
-directory = stela-0c43dca
+directory = stela-9c4a7eb
url = https://git.akon.city/stela.git
push-url = git@git.akon.city:stela.git
-revision = 0c43dcac12f606c0ac46b5f87dd3de324ff9ef04
+revision = 9c4a7eb845126f068aaac0173453e803e5ea6bf8