From 1224a43bbde9ef2297c7ee638b1928a07488cc3d Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Mon, 3 Mar 2025 12:53:14 -0500 Subject: Fix vcr memory leak, subtitle upload/render sync - Some sink behavior fixes. - Fix clock sync when seeking. - Add relative seek bind. Signed-off-by: Andrew Opalach --- src/buffer/clock.c | 3 +- src/buffer/video.c | 6 ++-- src/codec/ffmpeg/decoder.c | 14 ++++---- src/codec/ffmpeg/decoder.h | 2 ++ src/codec/ffmpeg/meson.build | 28 +++++++-------- src/codec/ffmpeg/resampler.c | 1 + src/fruits/cmc/ui/pane_search.c | 2 +- src/fruits/cmv/cmv.c | 2 +- src/liana/client.c | 8 +++++ src/liana/handlers/codec_client.c | 2 +- src/liana/list.h | 2 +- src/liana/vcr.c | 8 +++-- src/libsink/sink.c | 61 +++++++++++++++++++++++++------ src/libsink/sink.h | 1 + src/render/meson.build | 22 +++++++----- src/render/queue_libplacebo.c | 24 +++++++++---- src/render/queue_libplacebo.h | 1 + src/render/renderer.h | 1 + src/render/renderer_libplacebo.c | 27 ++++++++++++-- src/screen/screen.c | 76 ++++++++++++++++++++++++--------------- src/screen/screen.h | 2 ++ src/server/server.c | 1 + src/sink/desktop.c | 5 +++ 23 files changed, 211 insertions(+), 88 deletions(-) (limited to 'src') 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(¤t->clock, 0.0, false); - if (pts == -1.0) { -#ifndef CAMU_SINK_NO_VIDEO - pts = al_atomic_load(f64)(¤t->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 +#define COBJMACROS +#include +#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; -- cgit v1.2.3-101-g0448