summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/buffer/clock.c3
-rw-r--r--src/buffer/video.c6
-rw-r--r--src/codec/ffmpeg/decoder.c14
-rw-r--r--src/codec/ffmpeg/decoder.h2
-rw-r--r--src/codec/ffmpeg/meson.build28
-rw-r--r--src/codec/ffmpeg/resampler.c1
-rw-r--r--src/fruits/cmc/ui/pane_search.c2
-rw-r--r--src/fruits/cmv/cmv.c2
-rw-r--r--src/liana/client.c8
-rw-r--r--src/liana/handlers/codec_client.c2
-rw-r--r--src/liana/list.h2
-rw-r--r--src/liana/vcr.c8
-rw-r--r--src/libsink/sink.c61
-rw-r--r--src/libsink/sink.h1
-rw-r--r--src/render/meson.build22
-rw-r--r--src/render/queue_libplacebo.c24
-rw-r--r--src/render/queue_libplacebo.h1
-rw-r--r--src/render/renderer.h1
-rw-r--r--src/render/renderer_libplacebo.c27
-rw-r--r--src/screen/screen.c76
-rw-r--r--src/screen/screen.h2
-rw-r--r--src/server/server.c1
-rw-r--r--src/sink/desktop.c5
23 files changed, 211 insertions, 88 deletions
diff --git a/src/buffer/clock.c b/src/buffer/clock.c
index b0b4ac9..1ca2a49 100644
--- a/src/buffer/clock.c
+++ b/src/buffer/clock.c
@@ -44,7 +44,8 @@ void camu_clock_seek(struct camu_clock *clock, f64 base, u64 target)
al_atomic_store(f64)(&clock->tick, tick, AL_ATOMIC_RELAXED);
}
} else {
- al_atomic_store(f64)(&clock->pause, PAUSED, AL_ATOMIC_RELAXED);
+ // The value of clock->pause cannot be touched here. A reconnecting entry
+ // may still be relying on a clock paused callback for sync.
clock->paused_at = 0.0;
}
}
diff --git a/src/buffer/video.c b/src/buffer/video.c
index 1f2aa3c..ab1503a 100644
--- a/src/buffer/video.c
+++ b/src/buffer/video.c
@@ -12,9 +12,9 @@
#include "../codec/ffmpeg/scaler.h"
#endif
-#define BUFFER_MARK_LOW ((1.0 / 30.0) * 4)
-#define BUFFER_MARK_BUFFERED ((1.0 / 30.0) * 6) // Must be >1.
-#define BUFFER_MARK_HIGH ((1.0 / 30.0) * 8)
+#define BUFFER_MARK_LOW ((1.0 / 24.0) * 3)
+#define BUFFER_MARK_BUFFERED ((1.0 / 24.0) * 4) // Must be >1.
+#define BUFFER_MARK_HIGH ((1.0 / 24.0) * 6)
#define BUFFER_MARK_RESET (BUFFER_MARK_HIGH * 2.0)
bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *clock)
diff --git a/src/codec/ffmpeg/decoder.c b/src/codec/ffmpeg/decoder.c
index 47b5b01..5f4de67 100644
--- a/src/codec/ffmpeg/decoder.c
+++ b/src/codec/ffmpeg/decoder.c
@@ -90,7 +90,8 @@ static s32 init_hwdevice_context(struct camu_ff_decoder *av, AVCodecContext *con
}
context->hw_device_ctx = av_buffer_ref(av->hw_context);
- context->extra_hw_frames = 40;
+ // Note that context->extra_hw_frames has the ability to cause corruption.
+ context->extra_hw_frames = 18;
al_log_info("ff_decoder", "Using %s hardware decoding.", av_hwdevice_get_type_name(av->hw_device_type));
@@ -237,13 +238,14 @@ static bool ff_decoder_init(struct camu_decoder *dec, struct camu_renderer *rend
#ifdef CAMU_FF_DECODER_HWACCEL
if (attempt_hwdec && av->hw_device_type != AV_HWDEVICE_TYPE_NONE) {
//av->codec_context->codec_id = codec->id;
- if (init_hwdevice_context(av, av->codec_context) < 0) {
+ if (init_hwdevice_context(av, av->codec_context) == 0) {
+ av->renderer = renderer;
+ av->codec_context->opaque = av;
+ av->codec_context->get_buffer2 = get_buffer2;
+ av->codec_context->get_format = get_hw_format;
+ } else {
av->hw_device_type = AV_HWDEVICE_TYPE_NONE;
}
- av->renderer = renderer;
- av->codec_context->opaque = av;
- av->codec_context->get_buffer2 = get_buffer2;
- av->codec_context->get_format = get_hw_format;
}
#else
(void)renderer;
diff --git a/src/codec/ffmpeg/decoder.h b/src/codec/ffmpeg/decoder.h
index 4aa3d32..f698a6d 100644
--- a/src/codec/ffmpeg/decoder.h
+++ b/src/codec/ffmpeg/decoder.h
@@ -13,12 +13,14 @@ struct camu_ff_decoder {
struct camu_decoder dec;
AVCodecContext *codec_context;
struct camu_renderer *renderer;
+#ifdef CAMU_FF_DECODER_HWACCEL
array(const AVCodec *) supported_hw_codecs;
array(enum AVHWDeviceType) supported_hw_devices;
AVBufferRef *hw_context;
enum AVHWDeviceType hw_device_type;
enum AVPixelFormat hw_pix_fmt;
bool use_frames_context;
+#endif
void (*callback)(void *, struct camu_codec_frame *);
void *userdata;
};
diff --git a/src/codec/ffmpeg/meson.build b/src/codec/ffmpeg/meson.build
index cd6c7d3..dfbf624 100644
--- a/src/codec/ffmpeg/meson.build
+++ b/src/codec/ffmpeg/meson.build
@@ -27,16 +27,16 @@ ffmpeg_client_deps = []
libavutil = dependency('libavutil', required: false)
libavformat = dependency('libavformat', required: false)
libavcodec = dependency('libavcodec', required: false)
-libavdevice = dependency('libavdevice', required: false)
+#libavdevice = dependency('libavdevice', required: false)
libswresample = dependency('libswresample', required: false)
libswscale = dependency('libswscale', required: false)
-if not (libavutil.found() and libavformat.found() and libavcodec.found() and libavdevice.found() and libswresample.found() and libswscale.found())
+if not (libavutil.found() and libavformat.found() and libavcodec.found() and libswresample.found() and libswscale.found())
ffmpeg_proj = subproject('ffmpeg', required: false)
if ffmpeg_proj.found()
libavutil = ffmpeg_proj.get_variable('avutil')
libavformat = ffmpeg_proj.get_variable('avformat')
libavcodec = ffmpeg_proj.get_variable('avcodec')
- libavdevice = ffmpeg_proj.get_variable('avdevice')
+ #libavdevice = ffmpeg_proj.get_variable('avdevice')
libswresample = ffmpeg_proj.get_variable('swresample')
libswscale = ffmpeg_proj.get_variable('swscale')
# Only used to set include dirs for FFmpeg.
@@ -56,18 +56,16 @@ else
ffmpeg_version_string = run_command('sh', join_paths(meson.current_source_dir(), 'version.sh'), check: true).stdout().strip()
endif
-# This is probably meaningless compared to libav* versions, it's just something I wanted.
-ffmpeg_args += ['-DFFMPEG_VERSION="' + ffmpeg_version_string + '"']
-
-soxr = compiler.find_library('soxr', required: false)
-if soxr.found()
- ffmpeg_client_deps += [soxr]
- ffmpeg_args += ['-DCAMU_HAVE_SOXR']
-endif
-
-if libavutil.found() and libavformat.found() and libavcodec.found() and libavdevice.found() and libswresample.found() and libswscale.found()
- ffmpeg_server_deps += [libavutil, libavformat, libavcodec, libavdevice]
- ffmpeg_client_deps += [libavutil, libavformat, libavcodec, libavdevice, libswresample, libswscale]
+if libavutil.found() and libavformat.found() and libavcodec.found() and libswresample.found() and libswscale.found()
+ # This is probably meaningless compared to libav* versions, it's just something I wanted.
+ ffmpeg_args += ['-DFFMPEG_VERSION="' + ffmpeg_version_string + '"']
+ ffmpeg_server_deps += [libavutil, libavformat, libavcodec]
+ ffmpeg_client_deps += [libavutil, libavformat, libavcodec, libswresample, libswscale]
+ soxr = compiler.find_library('soxr', required: false)
+ if soxr.found()
+ ffmpeg_client_deps += [soxr]
+ ffmpeg_args += ['-DCAMU_HAVE_SOXR']
+ endif
codec_server_deps += [
declare_dependency(sources: ffmpeg_server_src,
dependencies: [ffmpeg_deps, ffmpeg_server_deps], compile_args: ffmpeg_args)
diff --git a/src/codec/ffmpeg/resampler.c b/src/codec/ffmpeg/resampler.c
index d2c8f62..61e8c9e 100644
--- a/src/codec/ffmpeg/resampler.c
+++ b/src/codec/ffmpeg/resampler.c
@@ -26,6 +26,7 @@ static bool ff_resampler_init(struct camu_resampler *resamp, struct camu_resampl
#ifdef CAMU_HAVE_SOXR
// Slower but more accurate resampler.
av_opt_set_int(av->resample_context, "resampler", SWR_ENGINE_SOXR, 0);
+ al_log_info("ff_resampler", "Using SoX resampler.");
#endif
if (swr_init(av->resample_context) != 0) {
diff --git a/src/fruits/cmc/ui/pane_search.c b/src/fruits/cmc/ui/pane_search.c
index c1b47ed..e75caa5 100644
--- a/src/fruits/cmc/ui/pane_search.c
+++ b/src/fruits/cmc/ui/pane_search.c
@@ -127,7 +127,7 @@ bool cmc_sp_handle_input(struct cmc_ui *ui, struct ncinput *input)
if (handle_text_input(tab, input)) {
notcurses_cursor_disable(ui->nc);
str query;
- al_wstr_to_str(&tab->input_text, &query);
+ al_str_from_wstr(&query, &tab->input_text);
camu_client_create_search(&ui->c->client, &al_str_c("youtube"), &query);
al_str_free(&query);
}
diff --git a/src/fruits/cmv/cmv.c b/src/fruits/cmv/cmv.c
index f9c3b82..6285c39 100644
--- a/src/fruits/cmv/cmv.c
+++ b/src/fruits/cmv/cmv.c
@@ -246,7 +246,7 @@ s32 main(s32 argc, char *cargv[])
for (s32 i = 0; i < argc; i++) {
str arg;
#ifdef NAUNET_ON_WINDOWS
- if (!al_wstr_to_str(&al_wstr_cr(wargv[i]), &arg)) {
+ if (!al_str_from_wstr(&arg, &al_wstr_cr(wargv[i]))) {
al_log_error("cmv", "Failed to parse argument #%i.", i);
continue;
}
diff --git a/src/liana/client.c b/src/liana/client.c
index d83ef48..54b5584 100644
--- a/src/liana/client.c
+++ b/src/liana/client.c
@@ -23,6 +23,11 @@ static void data_packet_callback(void *userdata, struct nn_packet_stream *stream
lia_vcr_push_packet(&client->vcr, packet);
}
+static s32 stream_compare(const void *a, const void *b)
+{
+ return (s32)((struct camu_codec_stream *)a)->type - (s32)((struct camu_codec_stream *)b)->type;
+}
+
static void parse_info_packet(struct lia_client *client, struct nn_packet *packet)
{
str liana;
@@ -90,6 +95,9 @@ static void parse_info_packet(struct lia_client *client, struct nn_packet *packe
al_array_push(client->streams, stream);
}
+ // Video streams have to come before subtitle streams.
+ al_array_sort(client->streams, struct camu_codec_stream, stream_compare);
+
bool have_audio = false;
bool have_video = false;
bool have_subs = false;
diff --git a/src/liana/handlers/codec_client.c b/src/liana/handlers/codec_client.c
index 8a86799..6f7e9f4 100644
--- a/src/liana/handlers/codec_client.c
+++ b/src/liana/handlers/codec_client.c
@@ -127,7 +127,7 @@ static bool codec_client_handle_packet(struct lia_client_handler *handler, struc
AVRational time_base = stream->av.stream->time;
pkt->pts += av_rescale_q(stream->duration, AV_TIME_BASE_Q, time_base);
#else
- av_packet_unref(pkt);
+ av_packet_free_side_data(pkt);
av_packet_free(&pkt);
#endif
break;
diff --git a/src/liana/list.h b/src/liana/list.h
index 86064af..a60790a 100644
--- a/src/liana/list.h
+++ b/src/liana/list.h
@@ -7,7 +7,7 @@
#define LIANA_SEQUENCE_ANY -1
#define LIANA_TIMESTAMP_INVALID ((u64)-1)
-#define LIANA_BASE_DELAY 450000Lu // 450ms
+#define LIANA_BASE_DELAY 500000Lu // 500ms
#define LIANA_BASE_PING 125000Lu // 125ms
#define LIANA_PAUSE_DELAY LIANA_BASE_PING
#define LIANA_DELAY_IGNORE 0Lu
diff --git a/src/liana/vcr.c b/src/liana/vcr.c
index 70fb7b6..e06ff8e 100644
--- a/src/liana/vcr.c
+++ b/src/liana/vcr.c
@@ -3,7 +3,7 @@
#include "vcr.h"
#include "handler.h"
-#define VCR_BUFFER_BUFFERED MB(24)
+#define VCR_BUFFER_BUFFERED MB(8)
enum {
VCR_EXPAND_UNTOUCHED = 0,
@@ -124,6 +124,10 @@ static nn_thread_result NNWT_THREADCALL vcr_track_thread(void *userdata)
continue;
}
+ index++; // Count of packets consumed.
+ nn_packet_stream_return_packets(vcr->data, al_array_offset(track->cache.cache, 0), index);
+ al_array_remove_range(track->cache.cache, 0, index);
+
nn_packet_cache_unlock(&track->cache);
// Wait for uncork.
@@ -147,7 +151,7 @@ static nn_thread_result NNWT_THREADCALL vcr_track_thread(void *userdata)
if (corked) {
// The cache is already unlocked here.
- index++;
+ index = 0;
} else {
#ifndef VCR_BUFFER_WHOLE_FILE
al_assert(index == packets);
diff --git a/src/libsink/sink.c b/src/libsink/sink.c
index 62bb941..3528e6b 100644
--- a/src/libsink/sink.c
+++ b/src/libsink/sink.c
@@ -308,8 +308,6 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd)
if (sink->video.state == SINK_PLAYING) {
sink->callback(sink->userdata, CAMU_SINK_STOP, CAMU_SINK_VIDEO, NULL);
sink->video.state = SINK_PAUSED;
- } else {
- sink->callback(sink->userdata, CAMU_SINK_REFRESH_VIDEO, 0, NULL);
}
break;
#endif
@@ -641,6 +639,12 @@ void add_audio_if_set_and_buffered(struct camu_sink_entry *entry)
.op = VIDEO_EMPTY(entry) || entry->buffers_paused ? STOP : START,
.value.i = CAMU_SINK_VIDEO
});
+ // This is the only place to clear the screen if skipping from a video
+ // to an audio-only entry.
+ if (VIDEO_EMPTY(entry)) {
+ struct camu_sink *sink = entry->sink;
+ sink->callback(sink->userdata, CAMU_SINK_REFRESH_VIDEO, 0, NULL);
+ }
}
#endif
queue_cmd(entry->sink, (struct camu_sink_cmd){
@@ -654,6 +658,7 @@ void add_audio_if_set_and_buffered(struct camu_sink_entry *entry)
#ifndef CAMU_SINK_NO_VIDEO
void add_video_if_set_and_buffered(struct camu_sink_entry *entry)
{
+ // Single frames will be added/removed with ended set.
al_assert(entry->video.state != BUFFER_INIT &&
(entry->video.state != BUFFER_QUEUED) &&
(entry->video.state != BUFFER_ADDED));
@@ -676,7 +681,7 @@ void add_video_if_set_and_buffered(struct camu_sink_entry *entry)
add_entry_audio_buffer(entry);
}
- // This entry could be in previous.
+ // This entry could be in previous, see note in add_audio_if_set_and_buffered().
maybe_remove_previous(entry->sink);
queue_cmd(entry->sink, (struct camu_sink_cmd){
@@ -753,6 +758,9 @@ static void switch_to(struct camu_sink *sink, struct camu_sink_entry *target)
static void pause_and_swap_to(struct camu_sink *sink, struct camu_sink_entry *target, u64 at)
{
struct camu_sink_entry *current = sink->current;
+#ifdef CAMU_SINK_TRACE
+ al_log_info("sink", "pause_and_swap_to(%llx, %llu), current: %llx", target, at, current);
+#endif
al_assert(target != current);
if (!current || current->ended) {
switch_to(sink, target);
@@ -875,12 +883,13 @@ static void video_buffer_callback(void *userdata, u8 op)
break;
case CAMU_BUFFER_EOF: {
bool swapped = false;
+ bool single_frame = VIDEO_IS_SINGLE_FRAME(entry);
nn_mutex_lock(&sink->mutex);
al_log_info("sink", "Video EOF.");
if (!AUDIO_EMPTY(entry)) {
camu_audio_buffer_set_no_video(&entry->audio.buf, true);
}
- if (!VIDEO_IS_SINGLE_FRAME(entry)) {
+ if (!single_frame) {
if (entry->video.state == BUFFER_ADDED) {
remove_entry_video_buffer(sink, entry);
}
@@ -891,7 +900,7 @@ static void video_buffer_callback(void *userdata, u8 op)
}
}
nn_mutex_unlock(&sink->mutex);
- if (!swapped) {
+ if (!swapped && !single_frame) {
queue_cmd(sink, (struct camu_sink_cmd){
.op = STOP,
.value.i = CAMU_SINK_VIDEO
@@ -1086,6 +1095,10 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
nn_mutex_lock(&sink->mutex);
+#ifdef CAMU_SINK_TRACE
+ al_log_info("sink", "remove_buffers(%s), entry == current: %s", BOOLSTR(reconnect), BOOLSTR(entry == sink->current));
+#endif
+
if (reconnect) {
if (entry == sink->current) {
sink->reconnecting = entry;
@@ -1177,6 +1190,9 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
case LIANA_CLIENT_RESUME_AT: {
struct lia_timing *time = (struct lia_timing *)opaque;
nn_mutex_lock(&sink->mutex);
+#ifdef CAMU_SINK_TRACE
+ al_log_info("sink", "resume_at(%llu, %llu)", time->seek_pos, time->at);
+#endif
#if defined LIANA_LIST_SCUFFED_LOOP && !defined CAMU_SINK_NO_VIDEO
struct camu_video_buffer *buf = &entry->video.buf;
if (time->seek_pos == 0 && buf->last_pts >= 0.0) {
@@ -1704,11 +1720,39 @@ void camu_sink_seek(struct camu_sink *sink, f64 precent)
});
}
+// @TODO: We need at standard way to get the entries pts even when paused.
+static f64 tmp_get_entry_pts(struct camu_sink_entry *entry)
+{
+ f64 pts = camu_clock_get_pts(&entry->clock, 0.0, false);
+ if (pts == -1.0) {
+#ifndef CAMU_SINK_NO_VIDEO
+ pts = al_atomic_load(f64)(&entry->video.buf.pts, AL_ATOMIC_RELAXED);
+#endif
+ }
+ return pts;
+}
+
+void camu_sink_relative_seek(struct camu_sink *sink, f64 offset)
+{
+ nn_mutex_lock(&sink->mutex);
+ struct camu_sink_entry *current = sink->current;
+ nn_mutex_unlock(&sink->mutex);
+ if (!current) return;
+ f64 pts = tmp_get_entry_pts(current) + offset;
+ f64 duration = current->client.duration / 1000000.0;
+ queue_cmd(sink, (struct camu_sink_cmd){
+ .op = SEEK,
+ .value.f = pts / duration,
+ .opaque = current
+ });
+}
+
void camu_sink_reseek(struct camu_sink *sink)
{
nn_mutex_lock(&sink->mutex);
struct camu_sink_entry *current = sink->current;
nn_mutex_unlock(&sink->mutex);
+ if (!current) return;
queue_cmd(sink, (struct camu_sink_cmd){
.op = RESEEK,
.opaque = current
@@ -1743,12 +1787,7 @@ void camu_sink_status(struct camu_sink *sink)
al_log_info("sink", "Nothing playing.");
return;
}
- f64 pts = camu_clock_get_pts(&current->clock, 0.0, false);
- if (pts == -1.0) {
-#ifndef CAMU_SINK_NO_VIDEO
- pts = al_atomic_load(f64)(&current->video.buf.pts, AL_ATOMIC_RELAXED);
-#endif
- }
+ f64 pts = tmp_get_entry_pts(current);
f64 duration = current->client.duration / 1000000.0;
s32 text = 0;
u32 minute = (u32)(pts / 60);
diff --git a/src/libsink/sink.h b/src/libsink/sink.h
index 86c6a04..4e97f3a 100644
--- a/src/libsink/sink.h
+++ b/src/libsink/sink.h
@@ -119,6 +119,7 @@ void camu_sink_skip(struct camu_sink *sink, s32 n);
void camu_sink_shuffle(struct camu_sink *sink);
void camu_sink_toggle_pause(struct camu_sink *sink);
void camu_sink_seek(struct camu_sink *sink, f64 pos);
+void camu_sink_relative_seek(struct camu_sink *sink, f64 offset);
void camu_sink_reseek(struct camu_sink *sink);
void camu_sink_unset(struct camu_sink *sink);
void camu_sink_set_volume(struct camu_sink *sink, f32 volume);
diff --git a/src/render/meson.build b/src/render/meson.build
index f0fd2fd..1dd2dce 100644
--- a/src/render/meson.build
+++ b/src/render/meson.build
@@ -95,7 +95,7 @@ elif get_option('renderer') == 'libplacebo'
spirv_cross_c_shared
]
meson.override_dependency('spirv-cross-c-shared', spirv_cross_c_shared)
- libplacebo_opts += ['shaderc=enabled', 'd3d11=enabled', 'vulkan=enabled', 'opengl=disabled']
+ libplacebo_opts += ['shaderc=enabled', 'd3d11=enabled', 'vulkan=disabled', 'opengl=disabled']
else
libplacebo_opts += ['shaderc=disabled', 'opengl=enabled', 'vulkan=disabled', 'd3d11=disabled']
endif
@@ -122,6 +122,13 @@ if not libass.found()
'cache-build=disabled'
])
endif
+ # libass(e46aede):meson.build:84
+ # fontconfig(72b9a48):meson.build:28
+ freetype = dependency('freetype2',
+ version: '>=9.17.3',
+ default_options: [
+ 'harfbuzz=disabled'
+ ])
# libass(e46aede):meson.build:90
fribidi = dependency('fribidi',
version: '>=0.19.1',
@@ -130,13 +137,6 @@ if not libass.found()
'tests=false',
'bin=false'
])
- # libass(e46aede):meson.build:84
- # fontconfig(72b9a48):meson.build:28
- freetype = dependency('freetype2',
- version: '>=9.17.3',
- default_options: [
- 'harfbuzz=disabled'
- ])
# freetype2(42608f7):meson.build:346
harfbuz = dependency('harfbuzz',
version: '>=2.0.0',
@@ -145,7 +145,11 @@ if not libass.found()
'tests=disabled',
'introspection=disabled',
'docs=disabled',
- 'utilities=disabled'
+ 'utilities=disabled',
+ 'glib=disabled',
+ 'gobject=disabled',
+ 'cairo=disabled',
+ 'chafa=disabled'
])
libass = dependency('libass', default_options: libass_opts)
endif
diff --git a/src/render/queue_libplacebo.c b/src/render/queue_libplacebo.c
index d078cbc..65a713e 100644
--- a/src/render/queue_libplacebo.c
+++ b/src/render/queue_libplacebo.c
@@ -31,6 +31,7 @@ static bool queue_lp_configure_subtitiles(struct camu_frame_queue *queue, u32 wi
AVCodecParameters *codecpar = stream->av.stream->codecpar;
ass_process_codec_private(lq->ass_track, (const char *)codecpar->extradata,
codecpar->extradata_size);
+ nn_mutex_init(&lq->subtitle_lock);
lq->have_subtitles = true;
return true;
}
@@ -130,6 +131,7 @@ static void queue_lp_push(struct camu_frame_queue *queue, struct camu_codec_fram
static struct camu_overlay_lp *create_subtitle_overlay(pl_gpu gpu, ASS_Image *ass_frame)
{
struct camu_overlay_lp *overlay = al_alloc_object(struct camu_overlay_lp);
+
for (; ass_frame; ass_frame = ass_frame->next) {
pl_tex tex = pl_tex_create(gpu, pl_tex_params(
.w = ass_frame->w,
@@ -196,6 +198,7 @@ static struct camu_overlay_lp *create_subtitle_overlay(pl_gpu gpu, ASS_Image *as
current->coords = PL_OVERLAY_COORDS_SRC_FRAME;
current->num_parts = 1;
}
+
return overlay;
}
@@ -215,6 +218,8 @@ static bool map_av_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *
));
}
+ // https://github.com/streetpea/chiaki-ng/blob/dd145ec2c802814e98fb833d3a9692169aa67315/gui/src/qmlmainwindow.cpp#L860
+ // https://github.com/streetpea/chiaki-ng/blob/dd145ec2c802814e98fb833d3a9692169aa67315/gui/src/qmlbackend.cpp#L786
if (!ok && frame->hw_frames_ctx) {
if (!lq->copy_frame_fallback) {
al_log_warn("frame_queue_libplacebo", "Falling back to software copy.");
@@ -225,7 +230,8 @@ static bool map_av_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *
lq->swframe->format = fmts[0];
av_free(fmts);
}
- ok = av_hwframe_transfer_data(lq->swframe, frame, 0) == 0;
+ s32 ret = av_hwframe_transfer_data(lq->swframe, frame, 0);
+ ok = ret == 0;
if (ok) {
av_frame_copy_props(lq->swframe, frame);
ok = pl_map_avframe_ex(gpu, out_frame, pl_avframe_params(
@@ -233,6 +239,8 @@ static bool map_av_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *
.tex = tex
));
av_frame_unref(lq->swframe);
+ } else {
+ al_log_error("frame_queue_libplacebo", "Failed to transfer hwframe from the device (%s).", av_err2str(ret));
}
}
@@ -240,13 +248,12 @@ static bool map_av_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *
if (ok && lq->have_subtitles) {
s64 now = av_rescale_q(frame->best_effort_timestamp, stream->time_base, (AVRational){ 1, 1000 });
- s32 change;
+ nn_mutex_lock(&lq->subtitle_lock);
+ s32 change; // 1 = different position, 2 = different content.
ASS_Image *ass_frame = ass_render_frame(lq->ass_renderer, lq->ass_track, now, &change);
if (ass_frame) {
struct camu_overlay_lp *overlay = NULL;
- if (lq->overlays.count > 0) {
- overlay = al_array_last(lq->overlays);
- }
+ if (lq->overlays.count > 0) overlay = al_array_last(lq->overlays);
if (!overlay || change > 0) {
overlay = create_subtitle_overlay(gpu, ass_frame);
overlay->lq = lq;
@@ -257,6 +264,7 @@ static bool map_av_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *
out_frame->num_overlays = overlay->num;
((struct pl_source_frame *)src)->frame_data = overlay;
}
+ nn_mutex_unlock(&lq->subtitle_lock);
}
av_frame_free(&frame);
@@ -284,6 +292,7 @@ static void unmap_av_frame(pl_gpu gpu, struct pl_frame *frame, const struct pl_s
}
al_free((struct pl_overlay_part *)current->parts);
}
+ al_free(overlay->overlays);
al_array_remove(lq->overlays, overlay);
al_free(overlay);
}
@@ -318,9 +327,11 @@ static void queue_lp_push_av_frame(struct camu_frame_queue *queue, AVFrame *fram
static void queue_lp_push_subtitle(struct camu_frame_queue *queue, struct camu_codec_packet *packet)
{
struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)queue;
- al_assert(lq->ass_track && lq->ass_renderer);
AVPacket *pkt = packet->av.pkt;
+ nn_mutex_lock(&lq->subtitle_lock);
+ al_assert(lq->ass_track && lq->ass_renderer);
ass_process_chunk(lq->ass_track, (const char *)pkt->data, pkt->size, pkt->pts, pkt->duration);
+ nn_mutex_unlock(&lq->subtitle_lock);
}
static void queue_lp_flush(struct camu_frame_queue *queue)
@@ -367,6 +378,7 @@ static void queue_lp_free(struct camu_frame_queue **queue)
if (lq->have_subtitles) {
ass_free_track(lq->ass_track);
ass_renderer_done(lq->ass_renderer);
+ nn_mutex_destroy(&lq->subtitle_lock);
}
if (lq->copy_frame_fallback) {
av_frame_free(&lq->swframe);
diff --git a/src/render/queue_libplacebo.h b/src/render/queue_libplacebo.h
index f87a34d..67d2479 100644
--- a/src/render/queue_libplacebo.h
+++ b/src/render/queue_libplacebo.h
@@ -24,6 +24,7 @@ struct camu_frame_queue_lp {
ASS_Library *ass;
ASS_Renderer *ass_renderer;
ASS_Track *ass_track;
+ struct nn_mutex subtitle_lock;
bool have_subtitles;
};
diff --git a/src/render/renderer.h b/src/render/renderer.h
index a4d3a53..b4863a0 100644
--- a/src/render/renderer.h
+++ b/src/render/renderer.h
@@ -47,6 +47,7 @@ struct camu_renderer {
void (*add_font)(struct camu_renderer *, struct camu_codec_stream *);
u32 (*get_latency)(struct camu_renderer *);
void (*resize)(struct camu_renderer *, u32 *, u32 *);
+ void (*set_fullscreen)(struct camu_renderer *, bool);
void (*render)(struct camu_renderer *, struct camu_screen *, bool);
#ifdef CAMU_HAVE_FFMPEG
s32 (*get_buffer2)(struct AVCodecContext *s, AVFrame *frame, s32 flags);
diff --git a/src/render/renderer_libplacebo.c b/src/render/renderer_libplacebo.c
index 8ff718e..a3a3109 100644
--- a/src/render/renderer_libplacebo.c
+++ b/src/render/renderer_libplacebo.c
@@ -8,6 +8,12 @@
#include "../screen/screen.h"
#include "../util/color_palette.h"
+#ifdef CAMU_RENDERER_DX11
+#include <nnwt/windows.h>
+#define COBJMACROS
+#include <libplacebo/d3d11.h>
+#endif
+
#include "renderer_libplacebo.h"
#include "queue_libplacebo.h"
@@ -35,6 +41,16 @@ static void renderer_lp_resize(struct camu_renderer *renderer, u32 *width, u32 *
}
}
+#ifdef CAMU_RENDERER_DX11
+static void renderer_lp_set_fullscreen(struct camu_renderer *renderer, bool fullscreen)
+{
+ struct camu_renderer_lp *lr = (struct camu_renderer_lp *)renderer;
+ IDXGISwapChain *d3d_swapchain = pl_d3d11_swapchain_unwrap(lr->swapchain);
+ IDXGISwapChain1_SetFullscreenState(d3d_swapchain, fullscreen, NULL);
+ IDXGISwapChain1_Release(d3d_swapchain);
+}
+#endif
+
static void log_callback(void *userdata, enum pl_log_level level, const char *message)
{
(void)userdata;
@@ -123,6 +139,7 @@ static bool renderer_lp_create_renderer(struct camu_renderer *renderer, u32 *wid
al_log_info("render_libplacebo", "Using Vulkan.");
#elif defined CAMU_RENDERER_DX11
+ (void)priv;
lr->d3d11 = pl_d3d11_create(lr->logger, pl_d3d11_params(
.debug = false
));
@@ -188,6 +205,7 @@ static bool renderer_lp_create_renderer(struct camu_renderer *renderer, u32 *wid
lr->params = pl_render_default_params;
//lr->params = pl_render_high_quality_params;
lr->params.deband_params = NULL;
+ //lr->params.frame_mixer = NULL;
// Clear manually so we can draw multiple images per frame.
lr->params.border = PL_CLEAR_SKIP;
@@ -232,7 +250,7 @@ static struct camu_frame_queue *renderer_lp_create_queue(struct camu_renderer *r
al_memset(&lq->params, 0, sizeof(struct pl_queue_params));
lq->params.interpolation_threshold = 0.01f;
lq->params.radius = pl_frame_mix_radius(&lr->params);
- lq->params.timeout = 0uL; // vsync
+ lq->params.timeout = 0;
lq->copy_frame_fallback = false;
lq->ass = lr->ass;
return queue;
@@ -347,7 +365,7 @@ static void renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
f64 tick = nn_get_tick();
f64 frame_time = tick - lr->last_render_tick;
if (frame_time > (1.0 / 24.0)) { // 24fps is totally arbitrary.
- al_log_info("render_libplacebo", "FRAME_TIME: %fs", frame_time);
+ al_log_info("render_libplacebo", "FRAME_TIME: %fs (%.2ffps)", frame_time, 1.0 / frame_time);
}
lr->last_render_tick = tick;
#endif
@@ -376,6 +394,11 @@ struct camu_renderer *camu_renderer_lp_create(void)
lr->r.add_font = renderer_lp_add_font;
lr->r.get_latency = renderer_lp_get_latency;
lr->r.resize = renderer_lp_resize;
+#ifdef CAMU_RENDERER_DX11
+ lr->r.set_fullscreen = renderer_lp_set_fullscreen;
+#else
+ lr->r.set_fullscreen = NULL;
+#endif
lr->r.render = renderer_lp_render;
lr->r.free = renderer_lp_free;
return (struct camu_renderer *)lr;
diff --git a/src/screen/screen.c b/src/screen/screen.c
index eb68278..d0926da 100644
--- a/src/screen/screen.c
+++ b/src/screen/screen.c
@@ -169,58 +169,68 @@ static bool scroll_callback(void *userdata, f64 y)
return false;
}
-static bool key_callback(void *userdata, u8 state, u8 button)
+static bool key_callback(void *userdata, u8 state, u16 button)
{
struct camu_screen *scr = (struct camu_screen *)userdata;
switch (state) {
case STELA_BUTTON_PRESSED:
- //al_printf("%x\n", button);
switch (button) {
- case 0x2a: // shift
+ case STELA_KEY_LEFT_SHIFT:
+ case STELA_KEY_RIGHT_SHIFT:
scr->flags |= CAMU_SCREEN_MOD_SHIFT;
break;
- case 0x1d: // control
+ case STELA_KEY_LEFT_CONTROL:
+ case STELA_KEY_RIGHT_CONTROL:
scr->flags |= CAMU_SCREEN_MOD_CONTROL;
break;
- case 0x10: // q
+ case STELA_KEY_Q:
scr->callback(scr->userdata, CAMU_SCREEN_CLOSE, NULL);
break;
- case 0x31: // n
- case 0x20: // d
- case 0x6a: // right arrow
- case 0x4d: // right arrow (wine?)
+ case STELA_KEY_RIGHT:
+ if (scr->flags & CAMU_SCREEN_MOD_CONTROL) {
+ f64 plus_ten = 10.0;
+ scr->callback(scr->userdata, CAMU_SCREEN_RELATIVE_SEEK, &plus_ten);
+ break;
+ }
+ // fallthrough
+ case STELA_KEY_D:
+ case STELA_KEY_N:
scr->callback(scr->userdata, CAMU_SCREEN_NEXT, NULL);
break;
- case 0x30: // b
- case 0x1e: // a
- case 0x69: // left arrow
- case 0x4b: // left arrow (wine?)
+ case STELA_KEY_LEFT:
+ if (scr->flags & CAMU_SCREEN_MOD_CONTROL) {
+ f64 minus_ten = -10.0;
+ scr->callback(scr->userdata, CAMU_SCREEN_RELATIVE_SEEK, &minus_ten);
+ break;
+ }
+ // fallthrough
+ case STELA_KEY_B:
+ case STELA_KEY_A:
scr->callback(scr->userdata, CAMU_SCREEN_PREVIOUS, NULL);
break;
- case 0x39: // spacebar
+ case STELA_KEY_SPACE:
scr->callback(scr->userdata, CAMU_SCREEN_TOGGLE_PAUSE, NULL);
break;
- case 0x1f: // s
+ case STELA_KEY_S:
scr->callback(scr->userdata, CAMU_SCREEN_SHUFFLE, NULL);
break;
- case 0x12: // e
+ case STELA_KEY_E:
scr->callback(scr->userdata, CAMU_SCREEN_RESEEK, NULL);
break;
- case 0x2f: { // v
+ case STELA_KEY_V: {
f32 volume = 1.0;
scr->callback(scr->userdata, CAMU_SCREEN_SET_VOLUME, &volume);
break;
- case 0xb: {
+ }
+ case STELA_KEY_0:
scr->callback(scr->userdata, CAMU_SCREEN_STATUS, NULL);
break;
- }
- }
#ifdef CAMU_SCREEN_DEBUG_KEY
- case 0x19: // p
+ case STELA_KEY_P:
scr->callback(scr->userdata, CAMU_SCREEN_DEBUG, NULL);
break;
#endif
- case 0x13: { // r
+ case STELA_KEY_R: {
struct camu_view *view = get_view_from_mouse_pos(scr);
if (view) {
if (scr->flags & CAMU_SCREEN_MOD_SHIFT) {
@@ -263,7 +273,7 @@ static bool key_callback(void *userdata, u8 state, u8 button)
}
break;
}
- case 0xd: { // +
+ case STELA_KEY_EQUAL: {
struct camu_view *view = get_view_from_mouse_pos(scr);
if (view) {
camu_view_zoom_simple(view, scr->width, scr->height, scr->width / 2.0, scr->height / 2.0, 0.05);
@@ -271,7 +281,7 @@ static bool key_callback(void *userdata, u8 state, u8 button)
}
break;
}
- case 0xc: { // -
+ case STELA_KEY_MINUS: {
struct camu_view *view = get_view_from_mouse_pos(scr);
if (view) {
camu_view_zoom_simple(view, scr->width, scr->height, scr->width / 2.0, scr->height / 2.0, -0.05);
@@ -285,10 +295,12 @@ static bool key_callback(void *userdata, u8 state, u8 button)
break;
case STELA_BUTTON_RELEASED:
switch (button) {
- case 0x2a: // shift
+ case STELA_KEY_LEFT_SHIFT:
+ case STELA_KEY_RIGHT_SHIFT:
scr->flags &= ~CAMU_SCREEN_MOD_SHIFT;
break;
- case 0x1d: // control
+ case STELA_KEY_LEFT_CONTROL:
+ case STELA_KEY_RIGHT_CONTROL:
scr->flags &= ~CAMU_SCREEN_MOD_CONTROL;
break;
}
@@ -298,14 +310,19 @@ static bool key_callback(void *userdata, u8 state, u8 button)
return false;
}
-static void key_immediate_callback(void *userdata, u8 state, u8 button)
+static void key_immediate_callback(void *userdata, u8 state, u16 button)
{
struct camu_screen *scr = (struct camu_screen *)userdata;
switch (state) {
case STELA_BUTTON_PRESSED:
switch (button) {
- case 0x21: { // f
- scr->window->toggle_fullscreen(scr->window);
+ case STELA_KEY_F: {
+ scr->fullscreen = !scr->fullscreen;
+ if (scr->renderer->set_fullscreen) {
+ scr->renderer->set_fullscreen(scr->renderer, scr->fullscreen);
+ } else {
+ scr->window->toggle_fullscreen(scr->window);
+ }
break;
}
}
@@ -364,6 +381,7 @@ bool camu_screen_create_window(struct camu_screen *scr, const char *name)
}
scr->width = scr->window->width;
scr->height = scr->window->height;
+ scr->fullscreen = false;
#ifdef STELA_EVENT_BUFFER
nn_thread_create(&scr->thread, event_thread, scr);
#endif
diff --git a/src/screen/screen.h b/src/screen/screen.h
index 2050d78..4b2e4c0 100644
--- a/src/screen/screen.h
+++ b/src/screen/screen.h
@@ -36,6 +36,7 @@ enum {
CAMU_SCREEN_SHUFFLE,
CAMU_SCREEN_TOGGLE_PAUSE,
CAMU_SCREEN_SEEK,
+ CAMU_SCREEN_RELATIVE_SEEK,
CAMU_SCREEN_RESEEK,
CAMU_SCREEN_SET_VOLUME,
CAMU_SCREEN_OFFSET_VOLUME,
@@ -62,6 +63,7 @@ struct camu_screen {
u32 flags;
u32 width;
u32 height;
+ bool fullscreen;
u64 last_click_ts;
f64 last_mouse_y;
f64 last_mouse_x;
diff --git a/src/server/server.c b/src/server/server.c
index d85d82c..10204df 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -684,6 +684,7 @@ static bool list_action_callback(void *userdata, struct nn_rpc_connection *conn,
s32 sequence = nn_packet_read_s32(packet);
u32 id = nn_packet_read_u32(packet);
f64 percent = nn_packet_read_f64(packet);
+ percent = CLAMP(percent, 0.0, 100.0);
lia_list_seek(list, sequence, id, percent);
break;
}
diff --git a/src/sink/desktop.c b/src/sink/desktop.c
index 61cad77..73a1667 100644
--- a/src/sink/desktop.c
+++ b/src/sink/desktop.c
@@ -55,6 +55,11 @@ static void screen_callback(void *userdata, u8 op, void *opaque)
camu_sink_seek(&c->sink, percent);
break;
}
+ case CAMU_SCREEN_RELATIVE_SEEK: {
+ f64 offset = *(f64 *)opaque;
+ camu_sink_relative_seek(&c->sink, offset);
+ break;
+ }
case CAMU_SCREEN_RESEEK:
camu_sink_reseek(&c->sink);
break;