diff options
84 files changed, 1026 insertions, 1122 deletions
@@ -7,17 +7,20 @@ data/ docs/**/*.html env/msvc-root/ result +scripts/host_ip src/portal/cpy/portal.c src/portal/cpy/portal.h src/portal/py/config.py src/portal/vendor/* subprojects/BLAKE3-*/ subprojects/SPIRV-Cross-*/ +subprojects/SPIRV-Headers-*/ subprojects/SPIRV-Tools-*/ subprojects/c89atomic-*/ subprojects/c89atomic.wrap +subprojects/cairo-*/ subprojects/cairo.wrap -subprojects/cairo/ +subprojects/dav1d-*/ subprojects/expat-*/ subprojects/expat.wrap subprojects/ffmpeg-*/ @@ -35,6 +38,8 @@ subprojects/gperf.wrap subprojects/gperf/ subprojects/gvdb.wrap subprojects/harfbuzz-*/ +subprojects/icu.wrap +subprojects/icu/ subprojects/jansson-*/ subprojects/jansson.wrap subprojects/jsmn-*/ @@ -50,6 +55,7 @@ subprojects/libdvdnav-*/ subprojects/libdvdread-*/ subprojects/libev-4.33/ subprojects/libev.wrap +subprojects/libffi-*/ subprojects/libffi.wrap subprojects/libffi/ subprojects/libnaunet @@ -61,15 +67,20 @@ subprojects/libxml2-*/ subprojects/libxml2.wrap subprojects/linmath.h-*/ subprojects/miniaudio-*/ +subprojects/msvc-wine-*/ subprojects/msvc-wine/ subprojects/notcurses-*/ subprojects/packagecache/ subprojects/pcre2-*/ subprojects/pcre2.wrap +subprojects/proxy-libintl.wrap +subprojects/proxy-libintl/ subprojects/shaderc-*/ subprojects/stb-*/ subprojects/stela subprojects/stela-*/ +subprojects/sysprof.wrap +subprojects/sysprof/ subprojects/vulkan-headers-*/ subprojects/vulkan.wrap subprojects/wayland-protocols-*/ @@ -37,11 +37,17 @@ - [x] Checking buffer states needs a lock, check asserts. - [x] Commit fixes. - [x] Commit changes. + - [x] cache selection in meson_options + - [x] al_atomic_* -> atomic_* + - [ ] Remove entry->ended from sink. + - [ ] libnaunet zlib wrap + - [ ] Pass over logging. + - [ ] list_remove(). + - [ ] Do ntp like latency evaluation. + - [ ] packet_cache_v2. - [ ] Implement queued. - [ ] Command line argument test in libnaunet. - [ ] Header cleanup. - - [ ] Remove entry->ended from sink. - - [ ] packet_cache_v2. - [ ] https://github.com/JuliaStrings/utf8proc/blob/master/utf8proc.h - [ ] ustr for ui elements. - [ ] remove wstr. @@ -97,6 +103,7 @@ === Cache - See cache/entry.h. + - [ ] Don't use file_mapped for large files. - [ ] EOF when seeking forward in slow http source. - [ ] HTTP timeout. diff --git a/cross/x86_64-w64-msvc-clang.txt b/cross/x86_64-w64-msvc-clang.txt index f269f63..01188f3 100644 --- a/cross/x86_64-w64-msvc-clang.txt +++ b/cross/x86_64-w64-msvc-clang.txt @@ -11,12 +11,14 @@ pkg-config = '' exe_wrapper = 'wine64' [properties] -c_args = ['--target=x86_64-windows-msvc', '-fuse-ld=lld'] -cpp_args = ['--target=x86_64-windows-msvc', '-fuse-ld=lld'] ar = 'llvm-lib' nm = 'llvm-objdump' needs_exe_wrapper = true +[built-in options] +c_args = ['--target=x86_64-windows-msvc', '-fuse-ld=lld'] +cpp_args = ['--target=x86_64-windows-msvc', '-fuse-ld=lld'] + [host_machine] system = 'windows' cpu_family = 'x86_64' diff --git a/env/msvc_build.sh b/env/msvc_build.sh index ccc5625..ba73416 100644 --- a/env/msvc_build.sh +++ b/env/msvc_build.sh @@ -1,13 +1,17 @@ #! /usr/bin/env sh +# nix develop .#mingw64 # meson subprojects download msvc-wine -# ./vsdownload.py --dest env/msvc-root -# ./install.sh env/msvc-root +# cd subprojects/msvc-wine-32b504c/ +# ./vsdownload.py --dest ../../env/msvc-root +# ./install.sh ../../env/msvc-root -BIN=env/msvc-root/bin/x64 . subprojects/msvc-wine/msvcenv-native.sh -export INCLUDE="$HOME/c/camu/subprojects/vulkan-headers-1.4.313/include;/nix/store/jx7aqhh225lhxdxdbiw42mrf2awvpdsq-clang-19.1.7-lib/lib/clang/19/include;$INCLUDE" -CC=clang-cl CXX=clang-cl meson setup build-clang-cl -Drenderer-api=dx11 \ - -Dsink-only=false -Dserver=enabled -Dportal=disabled -Dclient=disabled \ - -Dcodecs=ffmpeg -Dcodec-hwaccel=enabled -Dsubtitles=enabled \ - -Dtests=disabled --cross-file ./cross/x86_64-w64-msvc-clang.txt --default-library=static --buildtype=release \ - --optimization=2 -Ddebug=false --strip -Db_vscrt=static_from_buildtype +export INCLUDE="$INCLUDE:$HOME/c/camu/subprojects/vulkan-headers-1.4.325/include" + +BIN=env/msvc-root/bin/x64 . subprojects/msvc-wine-32b504c/msvcenv-native.sh + +CC=clang-cl CXX=clang-cl meson setup build-clang-cl -Dwindow=win32 -Drenderer-api=dx11 \ + -Dsink-only=false -Dserver=enabled -Dportal=disabled -Dclient=disabled -Dsources=file \ + -Dcodecs=ffmpeg -Dcodec-ffmpeg-version=8 -Dcodec-hwaccels=d3d11va -Dsubtitles=enabled -Dtests=disabled \ + --cross-file ./cross/x86_64-w64-msvc-clang.txt --default-library=static -Db_vscrt=static_from_buildtype \ + --buildtype=release --optimization=s -Ddebug=false --strip @@ -41,11 +41,11 @@ ] }, "locked": { - "lastModified": 1763748372, - "narHash": "sha256-AUc78Qv3sWir0hvbmfXoZ7Jzq9VVL97l+sP9Jgms+JU=", + "lastModified": 1764510518, + "narHash": "sha256-hDFkciyjhjMLRC/qXCREWX1dgq8kopiuJdwXdfVlJuQ=", "owner": "nix-community", "repo": "home-manager", - "rev": "d10a9b16b2a3ee28433f3d1c603f4e9f1fecb8e1", + "rev": "83053e1d337f33e0b48250588006e4b9df2f0d9d", "type": "github" }, "original": { @@ -76,11 +76,11 @@ }, "nixos-hardware": { "locked": { - "lastModified": 1762847253, - "narHash": "sha256-BWWnUUT01lPwCWUvS0p6Px5UOBFeXJ8jR+ZdLX8IbrU=", + "lastModified": 1764440730, + "narHash": "sha256-ZlJTNLUKQRANlLDomuRWLBCH5792x+6XUJ4YdFRjtO4=", "owner": "NixOS", "repo": "nixos-hardware", - "rev": "899dc449bc6428b9ee6b3b8f771ca2b0ef945ab9", + "rev": "9154f4569b6cdfd3c595851a6ba51bfaa472d9f3", "type": "github" }, "original": { @@ -96,11 +96,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1763738061, - "narHash": "sha256-VpNRcInaj1MOya8NmcqhFmdO7KGO7SSZelJQmPl6HoQ=", + "lastModified": 1764072830, + "narHash": "sha256-ezkjlUCohD9o9c47Ey0/I4CamSS0QEORTqGvyGqMud0=", "owner": "nix-community", "repo": "NixOS-WSL", - "rev": "3bcc267c4e0efa023b98b9c5cfbe11b88ec2dc8f", + "rev": "c7832dd786175e20f2697179e0e03efadffe4201", "type": "github" }, "original": { @@ -127,11 +127,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1762977756, - "narHash": "sha256-4PqRErxfe+2toFJFgcRKZ0UI9NSIOJa+7RXVtBhy4KE=", + "lastModified": 1763678758, + "narHash": "sha256-+hBiJ+kG5IoffUOdlANKFflTT5nO3FrrR2CA3178Y5s=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c5ae371f1a6a7fd27823bc500d9390b38c05fa55", + "rev": "117cc7f94e8072499b0a7aa4c52084fa4e11cc9b", "type": "github" }, "original": { @@ -143,11 +143,11 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1763421233, - "narHash": "sha256-Stk9ZYRkGrnnpyJ4eqt9eQtdFWRRIvMxpNRf4sIegnw=", + "lastModified": 1764242076, + "narHash": "sha256-sKoIWfnijJ0+9e4wRvIgm/HgE27bzwQxcEmo2J/gNpI=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "89c2b2330e733d6cdb5eae7b899326930c2c0648", + "rev": "2fad6eac6077f03fe109c4d4eb171cf96791faa4", "type": "github" }, "original": { @@ -151,9 +151,11 @@ pipewire libpulseaudio libva + dav1d libplacebo xxHash lcms + libdovi shaderc glslang.dev libGL @@ -308,12 +310,12 @@ meson cmake gdb - wineWowPackages.staging + #wineWowPackages.staging # Use nixGLIntel to run wine - pkgs.nixgl.nixGLIntel - pkgs.nixgl.nixVulkanIntel - # clang-cl - clang + #pkgs.nixgl.nixGLIntel + #pkgs.nixgl.nixVulkanIntel + # clang-cl, .cc is the 'unwrapped' compiler. + clang.cc msitools lld libllvm @@ -340,6 +342,9 @@ (soxr.overrideAttrs (oldAttrs: { cmakeFlags = oldAttrs.cmakeFlags ++ [ "-DBUILD_STATIC_LIBS=ON" "-DBUILD_SHARED_LIBS=OFF" ]; })) + (dav1d.overrideAttrs (oldAttrs: { + mesonFlags = oldAttrs.mesonFlags ++ [ "--default-library=static" ]; + })) (openssl.override { static = true; }) xxHash (libiconv.override { enableStatic = true; enableShared = false; }) @@ -353,7 +358,7 @@ export SHELL="${pkgs.bashInteractive}/bin/bash" export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${pkgs.stdenv.cc.cc.lib}/lib" export XDG_DATA_DIRS="${pkgs.mesa}/share:$XDG_DATA_DIRS" - alias wine="${pkgs.nixgl.nixGLIntel}/bin/nixGLIntel wine" + #alias wine="${pkgs.nixgl.nixGLIntel}/bin/nixGLIntel wine" # Silence Wine logging and enable Esync by default. export WINEDEBUG=-all export WINEESYNC=1 @@ -401,7 +406,7 @@ androidComposition = androidenv.composeAndroidPackages { cmdLineToolsVersion = "13.0"; toolsVersion = "26.1.1"; - platformToolsVersion = "34.0.5"; + platformToolsVersion = "36.0.2"; buildToolsVersions = [ "34.0.0" ]; includeEmulator = false; emulatorVersion = "35.2.5"; diff --git a/meson_options.txt b/meson_options.txt index fb06ae9..70fc940 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,4 +1,5 @@ # Options +option('sources', type: 'array', choices: ['file', 'http', 'cdio', 'libdvd'], value: ['file', 'http']) option('codecs', type: 'array', choices: ['ffmpeg', 'stb_image', 'wuffs', 'spng'], value: ['ffmpeg', 'stb_image']) option('codec-hwaccels', type: 'array', choices: ['vulkan', 'vaapi', 'mediacodec', 'd3d11va', 'dxva2'], value: [], yield: true) option('codec-ffmpeg-version', type: 'combo', choices: ['system', '8', '7', '6', '5', '4', '3', '2'], value: 'system') diff --git a/scripts/cmv_add.sh b/scripts/cmv_add.sh index 92de535..4572b51 100755 --- a/scripts/cmv_add.sh +++ b/scripts/cmv_add.sh @@ -12,4 +12,4 @@ do fi CMD="$CMD""$EXP\n" done -echo -e "$CMD""X" | socat - UNIX-CONNECT:/tmp/camu_control_sock +echo -e "$CMD""X" | socat - UNIX-CONNECT:/tmp/camu_control diff --git a/scripts/encode_ip.py b/scripts/encode_ip.py new file mode 100755 index 0000000..9f24fd6 --- /dev/null +++ b/scripts/encode_ip.py @@ -0,0 +1,38 @@ +#! /usr/bin/env python3 + +import sys + +arg = sys.argv[1] + +# Invalid characters on Windows: \/:*?"<>| + +#edge_chars = ['¥','£','€','¢'] +#chars = ['¡', '"', '#', '$', '%', '&', "'", '(', ')', ',', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '—', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '}', '~'] +#chars.extend(edge_chars) +#shift_up = '+' +#shift_down = '-' +# +#c_struct = 'static char addr_char_map[] = { ' +#for i in range(0, 87): +# c_struct += f'[\'{chars[i] if chars[i] != '\'' else '\\\''}\'] = {str(i)}' +# if i != 86: +# c_struct += ', ' +#c_struct += ' };' +# +#s = '' +#for n in arg.split('.'): +# i = int(n, 10) +# if i >= 174: +# i -= 174 +# s += shift_down +# if i >= 87: +# i -= 87 +# else: +# s += shift_up +# s += chars[i] + +s = '' +for n in arg.split('.'): + s += '%02x' % int(n, 10) + +print(s) diff --git a/scripts/run_server.sh b/scripts/run_server.sh index 22945ed..f3c520b 100755 --- a/scripts/run_server.sh +++ b/scripts/run_server.sh @@ -1,5 +1,6 @@ #! /usr/bin/env sh source ../scripts/python_env +export CMSRV_IP=$(<../scripts/host_ip) # screen will use $SHELL. screen -c ../scripts/screenrc #valgrind --leak-check=no --show-error-list=yes --log-file=./server-valgrind.log ./src/fruits/cmsrv/cmsrv $@ diff --git a/scripts/run_valgrind.sh b/scripts/run_valgrind.sh index c49d561..7982184 100755 --- a/scripts/run_valgrind.sh +++ b/scripts/run_valgrind.sh @@ -1,6 +1,6 @@ #! /usr/bin/env sh source ../scripts/python_env #valgrind --leak-check=full --show-leak-kinds=definite ./src/fruits/cmv/cmv "$@" -valgrind --leak-check=full --show-leak-kinds=all ./src/fruits/cmv/cmv "$@" -#valgrind --leak-check=no --show-error-list=yes ./src/fruits/cmv/cmv "$@" +#valgrind --leak-check=full --show-leak-kinds=all ./src/fruits/cmv/cmv "$@" +valgrind --leak-check=no --show-error-list=yes ./src/fruits/cmv/cmv "$@" #valgrind --tool=callgrind ./src/fruits/cmv/cmv "$@" diff --git a/src/buffer/audio.c b/src/buffer/audio.c index a740111..998662b 100644 --- a/src/buffer/audio.c +++ b/src/buffer/audio.c @@ -10,12 +10,12 @@ #include "common.h" #include "common_internal.h" -#define BUFFER_SIZE 6.0 +#define BUFFER_SIZE 7.0 #define BUFFER_MARK_MIN 3.25 // Must be a most half of the buffer size. #define BUFFER_MARK_BUFFERED 0.35 #ifdef CAMU_AUDIO_BUFFER_FADE -#define FADE_STEP(fmt) (2.50f / (fmt)->sample_rate) +#define FADE_STEP(fmt, down) ((down ? -2.5f : 2.5f) / (fmt)->sample_rate) #define FADE_MIN 0.175f #endif @@ -436,10 +436,9 @@ ptrdiff_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, ptrdif f32 step = 0.f; if (buf->pause == PAUSE_FADING || buf->fade.volume != buf->volume.user) { - step = FADE_STEP(fmt); - step *= (buf->pause == PAUSE_FADING || buf->fade.volume > buf->volume.user) ? -1.f : 1.f; - if (buf->volume.user > 1.f) step *= buf->volume.user; - step *= MAX(buf->fade.volume, FADE_MIN); + bool down = (buf->pause == PAUSE_FADING || buf->volume.user < buf->fade.volume); + step = FADE_STEP(fmt, down) * MAX(buf->fade.volume, FADE_MIN) * + ((buf->volume.user > 1.f) ? buf->volume.user : 1.f); // Move faster at >100%. } if (step != 0.f || buf->fade.volume != 1.f) { buf->fade.volume = apply_volume(data, req, fmt, buf->fade.volume, buf->volume.user, step); diff --git a/src/buffer/clock.c b/src/buffer/clock.c index 732185e..356ac56 100644 --- a/src/buffer/clock.c +++ b/src/buffer/clock.c @@ -7,6 +7,11 @@ #define RUNNING 0.0 #define PAUSED -DBL_MAX +// @TODO: +// Stop throwing away precision before we need to. +// Assume calc_tick_offset() could give a negative result at any point. +// Use a tick offset to set local buffer latency in a way that makes more sense. + void camu_clock_init(struct camu_clock *clock, void (*callback)(void *, u8), void *userdata) { clock->callback = callback; @@ -22,28 +27,33 @@ static f64 calc_tick_offset(f64 tick, u64 now, u64 target) } } +static void offset_tick(struct camu_clock *clock, f64 diff) +{ + diff += atomic_load(f64)(&clock->tick, AL_ATOMIC_ACQUIRE); + atomic_store(f64)(&clock->tick, diff, AL_ATOMIC_RELEASE); +} + void camu_clock_set(struct camu_clock *clock, f64 base) { clock->base = base; - clock->offset = 0.0; - al_atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED); - al_atomic_store(f64)(&clock->pause, PAUSED, AL_ATOMIC_RELAXED); + atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED); + atomic_store(f64)(&clock->pause, PAUSED, AL_ATOMIC_RELAXED); clock->paused_at = 0.0; - al_atomic_store(f64)(&clock->last_pts, base, AL_ATOMIC_RELAXED); + atomic_store(f64)(&clock->last_pts, base, AL_ATOMIC_RELAXED); } void camu_clock_seek(struct camu_clock *clock, f64 base, u64 target) { clock->base = base; - clock->offset = 0.0; - al_atomic_store(f64)(&clock->last_pts, base, AL_ATOMIC_RELAXED); + atomic_store(f64)(&clock->last_pts, base, AL_ATOMIC_RELAXED); if (clock->paused_at == -1.0) { // We are safe to directly edit the tick here. if (target == 0) { - al_atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED); + atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED); } else { - f64 tick = calc_tick_offset(nn_get_tick(), nn_get_timestamp(), target); - al_atomic_store(f64)(&clock->tick, tick, AL_ATOMIC_RELAXED); + f64 tick = nn_get_tick(); + tick = calc_tick_offset(tick, nn_get_timestamp(), target); + atomic_store(f64)(&clock->tick, tick, AL_ATOMIC_RELAXED); } } else { // Don't touch clock->pause here for the sake of sync. This means, @@ -57,28 +67,29 @@ void camu_clock_seek(struct camu_clock *clock, f64 base, u64 target) } } +// Seek to 0 but include the time it took to perform the seek. void camu_clock_loop(struct camu_clock *clock, f64 last_pts) { - // Seek to 0 but include the time it took to perform the seek. - clock->offset += last_pts - clock->base; clock->base = 0.0; + offset_tick(clock, last_pts - clock->base); } +// If late, pause immediately and on resume() there will be a long delay. void camu_clock_pause(struct camu_clock *clock, u64 target) { al_assert(clock->paused_at == -1.0); - f64 tick = nn_get_tick(); + f64 now = nn_get_tick(), tick = now; if (target > 0) { tick = calc_tick_offset(tick, nn_get_timestamp(), target); - al_atomic_store(f64)(&clock->pause, tick, AL_ATOMIC_RELAXED); - } else { - al_atomic_store(f64)(&clock->pause, PAUSED, AL_ATOMIC_RELAXED); } + atomic_store(f64)(&clock->pause, (tick > now) ? tick : -tick, AL_ATOMIC_RELAXED); + clock->paused_at = tick; } +// If late, this will be a catchup. void camu_clock_resume(struct camu_clock *clock, u64 target) { al_assert(clock->paused_at != -1.0); @@ -91,22 +102,23 @@ void camu_clock_resume(struct camu_clock *clock, u64 target) if (clock->paused_at == 0.0) { if (target == 0) { // target = 0 can never be synced. - al_atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED); + atomic_store(f64)(&clock->tick, -1.0, AL_ATOMIC_RELAXED); } else { - al_atomic_store(f64)(&clock->tick, tick, AL_ATOMIC_RELAXED); + atomic_store(f64)(&clock->tick, tick, AL_ATOMIC_RELAXED); } } else { - clock->offset += tick - clock->paused_at; + f64 pause = atomic_load(f64)(&clock->pause, AL_ATOMIC_ACQUIRE); + offset_tick(clock, tick - ((pause != PAUSED) ? -pause : clock->paused_at)); } - al_atomic_store(f64)(&clock->pause, RUNNING, AL_ATOMIC_RELAXED); + atomic_store(f64)(&clock->pause, RUNNING, AL_ATOMIC_RELEASE); clock->paused_at = -1.0; } bool camu_clock_is_paused(struct camu_clock *clock) { - return al_atomic_load(f64)(&clock->pause, AL_ATOMIC_RELAXED) == PAUSED; + return atomic_load(f64)(&clock->pause, AL_ATOMIC_RELAXED) < 0.0; } f64 camu_clock_get_base_pts(struct camu_clock *clock) @@ -116,35 +128,34 @@ f64 camu_clock_get_base_pts(struct camu_clock *clock) f64 camu_clock_get_pts(struct camu_clock *clock, f64 latency, bool allow_set) { - f64 pause = al_atomic_load(f64)(&clock->pause, AL_ATOMIC_ACQUIRE); - if (pause == PAUSED) return CAMU_PTS_PAUSED; + f64 pause = atomic_load(f64)(&clock->pause, AL_ATOMIC_RELAXED); + if (pause < 0.0) return CAMU_PTS_PAUSED; f64 current = nn_get_tick(); - - f64 tick = al_atomic_load(f64)(&clock->tick, AL_ATOMIC_RELAXED); + f64 tick = atomic_load(f64)(&clock->tick, AL_ATOMIC_RELAXED); if (tick == -1.0) { if (allow_set) { - tick = al_atomic_compare_and_swap(f64)(&clock->tick, -1.0, current); + tick = atomic_compare_and_swap(f64)(&clock->tick, -1.0, current); if (tick == -1.0) tick = current; } else { return CAMU_PTS_PAUSED; } } + f64 pts = clock->base + (current - tick); + bool signal_pause = false; - if (pause > 0.0 && current > pause) { // pause > 0.0 means running or pause armed. - current = pause; - pause = al_atomic_compare_and_swap(f64)(&clock->pause, pause, PAUSED); - if (pause != PAUSED) { + if (pause > 0.0 && current > pause) { // pause > 0.0 = RUNNING or armed for pause. + f64 paused_at = -current; + pause = atomic_compare_and_swap(f64)(&clock->pause, pause, paused_at); + if (pause != paused_at) { clock->callback(clock->userdata, CAMU_CLOCK_PAUSED); signal_pause = true; } } - f64 pts = (clock->base - clock->offset) + (current - tick); - if (allow_set) { - al_atomic_store(f64)(&clock->last_pts, pts, AL_ATOMIC_RELAXED); + atomic_store(f64)(&clock->last_pts, pts, AL_ATOMIC_RELAXED); } return signal_pause ? CAMU_PTS_SIGNAL_PAUSE : pts + latency; @@ -152,5 +163,5 @@ f64 camu_clock_get_pts(struct camu_clock *clock, f64 latency, bool allow_set) f64 camu_clock_get_last_pts(struct camu_clock *clock) { - return al_atomic_load(f64)(&clock->last_pts, AL_ATOMIC_RELAXED); + return atomic_load(f64)(&clock->last_pts, AL_ATOMIC_RELAXED); } diff --git a/src/buffer/clock.h b/src/buffer/clock.h index 3209119..8324b6a 100644 --- a/src/buffer/clock.h +++ b/src/buffer/clock.h @@ -23,8 +23,8 @@ // Next/Prev: // - User input -> swap -#define CAMU_PTS_PAUSED 0xffffffffffffffff -#define CAMU_PTS_SIGNAL_PAUSE 0x7fffffffffffffff +#define CAMU_PTS_PAUSED ((f64)0xffffffffffffffff) +#define CAMU_PTS_SIGNAL_PAUSE ((f64)0x7fffffffffffffff) #define CAMU_PTS_CONSIDER_PAUSED(pts) (pts == CAMU_PTS_PAUSED || pts == CAMU_PTS_SIGNAL_PAUSE) enum { @@ -33,7 +33,6 @@ enum { struct camu_clock { f64 base; - f64 offset; atomic(f64) tick; atomic(f64) pause; f64 paused_at; diff --git a/src/buffer/video.c b/src/buffer/video.c index e5c38a6..5011e9d 100644 --- a/src/buffer/video.c +++ b/src/buffer/video.c @@ -30,6 +30,7 @@ bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *cl buf->single_frame = true; buf->queue = NULL; buf->buffered = false; + buf->weighted_first_read = false; al_atomic_store(u8)(&buf->flow, FLOWING, AL_ATOMIC_RELAXED); #ifdef CAMU_SCREEN_THREADED al_atomic_store(u8)(&buf->ref, 0, AL_ATOMIC_RELAXED); @@ -63,13 +64,13 @@ bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_code } else { al_assert(frame_rate.den > 0); buf->avg_frame_duration = av_q2d(av_inv_q(frame_rate)); + buf->weighted_first_read = true; log_info("Stream: %s (%ux%u) VIDEO %.3ffps.", format_name, fmt->width, fmt->height, av_q2d(frame_rate)); } break; } #endif } - buf->weighted_read = !buf->single_frame; struct camu_video_format *in = &buf->fmt.in; struct camu_video_format *req = &buf->fmt.req; camu_video_format_copy(in, fmt); @@ -233,7 +234,8 @@ bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out, bool *weig f64 base_pts = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE); if (!buf->single_frame) { - f64 pts = camu_clock_get_pts(buf->clock, buf->latency, !buf->weighted_read); + bool set_clock = !buf->weighted_first_read; + f64 pts = camu_clock_get_pts(buf->clock, buf->latency, set_clock); if (!CAMU_PTS_CONSIDER_PAUSED(pts) && pts > base_pts) { base_pts = pts; } @@ -268,15 +270,15 @@ bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out, bool *weig } if (ret == CAMU_QUEUE_OK) { - if (UNLIKELY(buf->weighted_read)) { - *weighted = buf->weighted_read; - buf->weighted_read = false; - } // Only updating buf->pts on QUEUE_OK affects the behavior when we fall completely behind. // Setting it here will result in laggily displaying out of date frames as they come in. // Setting it on every read() will cause the video to freeze. // This option ignores sync but is easier for the user to understand what's happening. al_atomic_store(f64)(&buf->pts, base_pts, AL_ATOMIC_RELEASE); + if (UNLIKELY(buf->weighted_first_read)) { + *weighted = true; + buf->weighted_first_read = false; + } } else if (ret == CAMU_QUEUE_MORE) { log_trace("Underrun."); } diff --git a/src/buffer/video.h b/src/buffer/video.h index fdd7d0b..20644bc 100644 --- a/src/buffer/video.h +++ b/src/buffer/video.h @@ -30,7 +30,7 @@ struct camu_video_buffer { // Need to manually trigger EOF in read(). bool buffered_with_one_frame; // Flush the renderer on this read and don't allow it to set the clock. - bool weighted_read; + bool weighted_first_read; atomic(u8) flow; diff --git a/src/cache/handlers/cdio.c b/src/cache/handlers/cdio.c index 29126c0..a9e3747 100644 --- a/src/cache/handlers/cdio.c +++ b/src/cache/handlers/cdio.c @@ -1,4 +1,5 @@ #define AL_LOG_SECTION "cache_cdio" +//#define AL_LOG_ENABLE_TRACE #include <al/log.h> #include "../../codec/codec.h" @@ -10,7 +11,7 @@ #include "cdio.h" -// 1 sector at a time to minimize skips +// 1 sector at a time to minimize skips. #define SECTORS_PER_STEP 1 #define BYTES_PER_STEP (SECTORS_PER_STEP * CDIO_CD_FRAMESIZE_RAW) @@ -18,13 +19,13 @@ static void cdio_log_messages(struct cch_handler_cdio *cdio) { char *error_messages = cdio_cddap_errors(cdio->drive); if (error_messages) { - log_error("\n%s", error_messages); + log_error("%s", error_messages); cdio_cddap_free_messages(error_messages); } char *messages = cdio_cddap_messages(cdio->drive); if (messages) { - log_info("\n%s", messages); + log_info("%s", messages); cdio_cddap_free_messages(messages); } } @@ -42,26 +43,21 @@ static nn_thread_result NNWT_THREADCALL cd_read_thread(void *userdata) for (;;) { if (!al_atomic_load(s32)(&cdio->running, AL_ATOMIC_RELAXED)) { - // We don't care about how complete the disc read was. + // We don't care about how complete the read was. return 0; } - // cdio_read() here can block for a very long time so we buffer each read - // before writing it to the backing. + // cdio_read() can block for a long time so we buffer each read before writing it to the backing. long ret = cdio_cddap_read(cdio->drive, nn_buffer_get_ptr(&cdio->buffer, 0), cdio->sector, SECTORS_PER_STEP); cdio_log_messages(cdio); al_assert(ret <= SECTORS_PER_STEP); off_t pointer = cdio->sector * CDIO_CD_FRAMESIZE_RAW; size_t size; u8 *ptr = cdio->backing->get_ptr(cdio->backing, pointer, &size); - if (size < BYTES_PER_STEP) { - log_error("Tried to write over expected size during normal operation."); - cdio->backing->unlock(cdio->backing); - break; - } + al_assert(size >= BYTES_PER_STEP); if (ret == SECTORS_PER_STEP) { al_memcpy(ptr, nn_buffer_get_ptr(&cdio->buffer, 0), BYTES_PER_STEP); } else if (ret == -10) { - log_error("I/O error, skipping sector."); + log_error("Disc read error, skipping sector."); al_memset(ptr, 0, BYTES_PER_STEP); ret = SECTORS_PER_STEP; } else if (ret < 0) { @@ -170,7 +166,8 @@ static bool open_cd_drive(struct cch_handler_cdio *cdio) lsn_t start = cdio_cddap_disc_firstsector(drive); lsn_t end = cdio_cddap_disc_lastsector(drive); - lsn_t offset = start; + lsn_t first_track_start = cdio_cddap_track_firstsector(drive, first_track); + lsn_t offset = first_track_start; s32 track = CDIO_INVALID_TRACK; while (offset < end) { track = cdio_cddap_sector_gettrack(drive, offset); @@ -178,6 +175,7 @@ static bool open_cd_drive(struct cch_handler_cdio *cdio) lsn_t track_start = cdio_cddap_track_firstsector(drive, track); lsn_t track_end = cdio_cddap_track_lastsector(drive, track); al_array_push(cdio->handler.entry->chapters, ((struct cch_chapter){ track_start, track_end })); + log_trace("Track #%u: %zd-%zd.", track, track_start, track_end); offset = track_end + 1; } // end is a valid index so add one framesize. diff --git a/src/cache/meson.build b/src/cache/meson.build index 1a198eb..61be949 100644 --- a/src/cache/meson.build +++ b/src/cache/meson.build @@ -8,37 +8,35 @@ cache_src = [ cache_deps = [] cache_args = [] -if naunet_has_mmap - cache_src += ['backings/file_mapped.c'] -else - cache_src += ['backings/file.c'] +if 'file' in get_option('sources') + if naunet_has_mmap + cache_src += ['backings/file_mapped.c'] + else + cache_src += ['backings/file.c'] + endif endif -if naunet_has_curl +if 'http' in get_option('sources') and naunet_has_curl cache_src += ['handlers/http.c'] endif -cache_have_cdio = false -if is_linux - libcdio_paranoia = dependency('libcdio_paranoia', required: false, allow_fallback: true) - libcdio_cdda = dependency('libcdio_cdda', required: false, allow_fallback: true) +if 'cdio' in get_option('sources') + libcdio_paranoia = dependency('libcdio_paranoia', allow_fallback: true) + libcdio_cdda = dependency('libcdio_cdda', allow_fallback: true) if libcdio_paranoia.found() and libcdio_cdda.found() cache_src += ['handlers/cdio.c'] cache_deps += [libcdio_paranoia, libcdio_cdda] cache_args += ['-DCACHE_HAVE_CDIO'] - cache_have_cdio = true endif endif -cache_have_libdvd = false -if is_linux - libdvdcss = dependency('libdvdcss', required: false, allow_fallback: true) - libdvdread = dependency('dvdread', required: false, allow_fallback: true) - libdvdnav = dependency('dvdnav', required: false, allow_fallback: true) +if 'libdvd' in get_option('sources') + libdvdcss = dependency('libdvdcss', allow_fallback: true) + libdvdread = dependency('dvdread', allow_fallback: true) + libdvdnav = dependency('dvdnav', allow_fallback: true) if libdvdcss.found() and libdvdread.found() and libdvdnav.found() cache_deps += [libdvdcss, libdvdread, libdvdnav] cache_args += ['-DCACHE_HAVE_LIBDVD'] - cache_have_libdvd = true endif endif diff --git a/src/codec/codec.h b/src/codec/codec.h index f32283e..6a9b654 100644 --- a/src/codec/codec.h +++ b/src/codec/codec.h @@ -428,7 +428,7 @@ static inline s32 camu_guess_format(str *short_name, str *ext, str *mime_type) (short_name && al_str_eq(short_name, &al_str_c("png"))) || (mime_type && al_str_eq(mime_type, &al_str_c("image/png")))) { return CAMU_CODEC_PNG; - } else if ((ext && (al_str_eq(ext, &al_str_c(".jpeg")) || al_str_eq(ext, &al_str_c(".jpg")))) || + } else if ((ext && (al_str_eq(ext, &al_str_c(".jpeg")) || al_str_eq(ext, &al_str_c(".jpg")) || al_str_eq(ext, &al_str_c(".jpe")) || al_str_eq(ext, &al_str_c(".jfif")))) || (short_name && al_str_eq(short_name, &al_str_c("jpeg"))) || (mime_type && al_str_eq(mime_type, &al_str_c("image/jpeg")))) { return CAMU_CODEC_JPEG; diff --git a/src/codec/codecs.c b/src/codec/codecs.c index dbec693..a373daf 100644 --- a/src/codec/codecs.c +++ b/src/codec/codecs.c @@ -83,6 +83,9 @@ struct camu_codec_info *camu_codec_info_by_type(s32 type) struct camu_codec_info *camu_codec_info_by_name(str *name) { + if (al_str_eq(name, &al_str_c("pcm_s16le"))) { + return (struct camu_codec_info *)0xcdda; + } for (u32 i = 0; i < ARRAY_SIZE(codec_information); i++) { if (al_str_eq(&codec_information[i].name, name)) { return &codec_information[i]; diff --git a/src/codec/ffmpeg/decoder.c b/src/codec/ffmpeg/decoder.c index 645a88a..7d50bca 100644 --- a/src/codec/ffmpeg/decoder.c +++ b/src/codec/ffmpeg/decoder.c @@ -114,9 +114,6 @@ static enum AVPixelFormat get_hw_format(AVCodecContext *context, const enum AVPi log_error("Failed to get %s HW surface format.", hwdevice_name); // Remake AVCodecContext as a software decoder. - // This might log an error like "Your platform doesn't support hardware accelerated AV1 decoding.". - // Even though the problem is really a lack of any decoder hardware OR software. - // This happens with wine + d3d11va. Maybe a build configuration issue or FFmpeg bug? AVCodecParameters *codecpar = av->codecpar; av->errored_hw_context = av->codec_context; alloc_codec_context_internal(av, codec, codecpar); diff --git a/src/codec/ffmpeg/meson.build b/src/codec/ffmpeg/meson.build index 6482426..e34cc92 100644 --- a/src/codec/ffmpeg/meson.build +++ b/src/codec/ffmpeg/meson.build @@ -11,7 +11,7 @@ ffmpeg_server_deps = [] ffmpeg_client_src = [ffmpeg_src, 'decoder.c', 'resampler.c', 'scaler.c'] ffmpeg_client_deps = [] -ffmpeg_force_fallback = false +ffmpeg_force_fallback = get_option('wrap_mode') == 'forcefallback' foreach dep: ['ffmpeg', 'libavutil', 'libavformat', 'libavcodec', 'libavdevice', 'libswresample', 'libswscale'] if dep in get_option('force_fallback_for') ffmpeg_force_fallback = true diff --git a/src/fruits/cmc/cmc.c b/src/fruits/cmc/cmc.c index 3d91ba6..f44f82f 100644 --- a/src/fruits/cmc/cmc.c +++ b/src/fruits/cmc/cmc.c @@ -6,8 +6,6 @@ #include "../../liana/list.h" #include "../../codec/packet_ext.h" -#include "../common.h" - #include "cmc.h" #include "ui/ui.h" @@ -281,9 +279,13 @@ s32 main(s32 argc, char *argv[]) camu_post_cache_init(&c.cache); al_array_init(c.searches); + str user = al_str_c("guest"); + char *CMC_USER = getenv("CMC_USER"); + if (CMC_USER) user = al_str_cr(CMC_USER); + c.client.callback = client_callback; c.client.userdata = &c; - if (!camu_client_login(&c.client, &al_str_c("andrew"), &c.loop, CAMU_TEST_TYPE, &CAMU_TEST_ADDR, CAMU_PORT)) { + if (!camu_client_login(&c.client, &user, &c.loop, NNWT_SOCKET_TCP, &al_str_c("127.0.0.1"), CAMU_PORT)) { goto err; } diff --git a/src/fruits/cmc/meson.build b/src/fruits/cmc/meson.build index de6d335..bb9d38d 100644 --- a/src/fruits/cmc/meson.build +++ b/src/fruits/cmc/meson.build @@ -20,7 +20,7 @@ if use_tui endif if is_windows and not is_msvc - cmc_args += ['-static', '-static-libgcc', '-static-libstdc++', '-municode', '-mwindows'] + cmc_args += ['-static', '-static-libgcc', '-static-libstdc++', '-mwindows', '-municode'] endif -executable('cmc', cmc_src, dependencies: cmc_deps, link_args: cmc_args, install: true) +executable('cmc', cmc_src, dependencies: cmc_deps, c_args: cmc_args, link_args: cmc_args, install: true) diff --git a/src/fruits/cmc/ui/util/waveform.c b/src/fruits/cmc/ui/util/waveform.c index bed34b7..624069e 100644 --- a/src/fruits/cmc/ui/util/waveform.c +++ b/src/fruits/cmc/ui/util/waveform.c @@ -22,8 +22,8 @@ // extra: #f6ac7f, #F5F4FA, #5b524b // left channel: n == 0, right channel: n == 1. #define COLOR_FOR_PIXEL(n, rms) rms ? \ - n == 0 ? nn_htonl(0xE4CAAF99) : nn_htonl(0xFF928FFF) : \ - n == 0 ? nn_htonl(0xF4EAE099) : nn_htonl(0xBC615EFF) + n == 0 ? nn_htonl(0xe4caaf99) : nn_htonl(0xff928fff) : \ + n == 0 ? nn_htonl(0xf4eae099) : nn_htonl(0xbc615eff) // ss: current sample, ns: next sample. #define HIT_WAVE(low, high, ss, ns) \ diff --git a/src/fruits/cmsrv/cmsrv.c b/src/fruits/cmsrv/cmsrv.c index ef66be8..babd5ef 100644 --- a/src/fruits/cmsrv/cmsrv.c +++ b/src/fruits/cmsrv/cmsrv.c @@ -1,8 +1,7 @@ -#define CMSRV_LOCAL_SOCKET -#define CMSRV_USE_UI - +#define AL_LOG_SECTION "cmsrv" #include <al/log.h> #include <nnwt/common.h> +#define CMSRV_LOCAL_SOCKET #ifdef CMSRV_LOCAL_SOCKET #include <nnwt/line_processor.h> #endif @@ -15,9 +14,9 @@ #include "../../cache/handlers/cdio.h" #endif -#include "../common.h" - +#ifdef CMSRV_USE_UI #include "ui.h" +#endif struct cmsrv { struct nn_event_loop loop; @@ -186,8 +185,13 @@ s32 main(s32 argc, char *argv[]) nn_signal_start(&s.quit_signal); signal(SIGINT, sigint_handler); + str addr = al_str_c("127.0.0.1"); + char *CMSRV_IP = getenv("CMSRV_IP"); + if (CMSRV_IP) addr = al_str_cr(CMSRV_IP); + log_info("Serving from %.*s.", al_str_x(&addr)); + camu_server_init(&s.server, &s.loop); - if (!camu_server_listen(&s.server, CAMU_TEST_TYPE, &CAMU_TEST_ADDR, CAMU_PORT)) { + if (!camu_server_listen(&s.server, NNWT_SOCKET_TCP, &addr, CAMU_PORT)) { return EXIT_FAILURE; } @@ -198,7 +202,7 @@ s32 main(s32 argc, char *argv[]) s.local.cli.userdata = &s; nn_line_processor_init(&s.local.cli, &al_str_c("\n")); nn_line_processor_open_socket(&s.local.cli, &s.local.sock); - if (nn_socket_bind(&s.local.sock, &CAMU_TEST_CONTROL_PATH, 0) && nn_socket_listen(&s.local.sock)) { + if (nn_socket_bind(&s.local.sock, &al_str_c("/tmp/camu_control"), 0) && nn_socket_listen(&s.local.sock)) { nn_line_processor_run(&s.local.cli, &s.loop); } #endif diff --git a/src/fruits/cmsrv/meson.build b/src/fruits/cmsrv/meson.build index b12d00d..bfeabe0 100644 --- a/src/fruits/cmsrv/meson.build +++ b/src/fruits/cmsrv/meson.build @@ -2,14 +2,14 @@ cmsrv_src = ['cmsrv.c'] cmsrv_deps = [common_deps, server] cmsrv_args = [] -use_tui = true -if use_tui +if use_tuis cmsrv_src += ['ui.c'] cmsrv_deps += [notcurses_core] + cmsrv_args += ['-DCMSRV_USE_UI'] endif if is_windows and not is_msvc - cmsrv_args += ['-static', '-static-libgcc', '-static-libstdc++', '-municode', '-mwindows'] + cmsrv_args += ['-static', '-static-libgcc', '-static-libstdc++', '-mwindows', '-municode'] endif -executable('cmsrv', cmsrv_src, dependencies: cmsrv_deps, link_args: cmsrv_args, install: true) +executable('cmsrv', cmsrv_src, dependencies: cmsrv_deps, c_args: cmsrv_args, link_args: cmsrv_args, install: true) diff --git a/src/fruits/cmsrv/ui.c b/src/fruits/cmsrv/ui.c index d96c09d..afbee58 100644 --- a/src/fruits/cmsrv/ui.c +++ b/src/fruits/cmsrv/ui.c @@ -29,6 +29,7 @@ bool cmsrv_ui_init(struct cmsrv_ui *ui, struct camu_server *server) ncplane_set_userptr(stdplane, ui); ui->pending_layout = true; al_array_init(ui->log.messages); + nn_mutex_init(&ui->log.lock); return true; } @@ -51,7 +52,9 @@ void cmsrv_ui_set_pane(struct cmsrv_ui *ui, u8 pane) void cmsrv_ui_push_message(struct cmsrv_ui *ui, char *message) { + nn_mutex_lock(&ui->log.lock); al_array_push(ui->log.messages, message); + nn_mutex_unlock(&ui->log.lock); } static void layout_log(struct cmsrv_ui *ui, struct ncplane *parent) @@ -80,6 +83,7 @@ static void render_log(struct cmsrv_ui *ui) u32 max_width = ncplane_dim_x(n) - 2; u32 max_height = ncplane_dim_y(n) - 2; + nn_mutex_lock(&ui->log.lock); u32 size = ui->log.messages.count; if (size > max_height) { u32 index = size - max_height; @@ -97,6 +101,7 @@ static void render_log(struct cmsrv_ui *ui) // messages will have wide characters. ncplane_putstr_yx(n, y, x, al_array_at(ui->log.messages, i)); } + nn_mutex_unlock(&ui->log.lock); u64 c = 0; ncchannels_set_fg_default(&c); @@ -251,4 +256,5 @@ void cmsrv_ui_close(struct cmsrv_ui *ui) al_free(message); } al_array_free(ui->log.messages); + nn_mutex_destroy(&ui->log.lock); } diff --git a/src/fruits/cmsrv/ui.h b/src/fruits/cmsrv/ui.h index ebf7c1a..3615d3b 100644 --- a/src/fruits/cmsrv/ui.h +++ b/src/fruits/cmsrv/ui.h @@ -19,6 +19,7 @@ struct cmsrv_ui { struct { struct ncplane *n; array(char *) messages; + struct nn_mutex lock; } log; struct { struct ncplane *n; diff --git a/src/fruits/cmv/cmv.c b/src/fruits/cmv/cmv.c index fe0fbd6..535bf72 100644 --- a/src/fruits/cmv/cmv.c +++ b/src/fruits/cmv/cmv.c @@ -13,8 +13,6 @@ #include "../../codec/ffmpeg/common.h" #endif -#include "../common.h" - #if !defined CAMU_SINK_ONLY && !defined NAUNET_ON_WINDOWS // https://unix.stackexchange.com/a/16884 #define PID_MAX_STR 7 // 0x400000 (2^22). @@ -46,13 +44,32 @@ static nn_thread_result NNWT_THREADCALL event_loop_thread(void *userdata) return 0; } -// sink-p12-108.52.160.112-aEN-v-sEN.exe -static bool parse_exe_name_params(str *exe_name, str *addr, struct lia_prefs *prefs) +static bool decode_addr(str *enc, str *addr) +{ + char value[4]; // max = 255. + for (u32 i = 0; i < 8; i += 2) { + str sub = al_str_substr(enc, i, i + 2); + bool error; + s64 l = al_str_to_long(&sub, 16, &error); + al_assert(l >= 0 && l <= 255); + if (error) return false; + al_snprintf(value, sizeof(value), "%hhu", (u8)l); + al_str_cat(addr, &al_str_cs(value)); + if (i != 6) { + al_str_cat(addr, &al_str_c(".")); + } + } + return true; +} + +// sink-p12-976502a7.exe +static bool parse_exe_name(str *exe_name, str *addr) { u32 index = al_str_rfind(exe_name, '/'); if (index == AL_STR_NO_POS) { - index = al_str_rfind(exe_name, '\\'); - if (index == AL_STR_NO_POS) goto def; + if ((index = al_str_rfind(exe_name, '\\')) == AL_STR_NO_POS) { + goto def; + } } str sub = al_str_substr(exe_name, index + 1, exe_name->length); @@ -62,51 +79,20 @@ static bool parse_exe_name_params(str *exe_name, str *addr, struct lia_prefs *pr sub = al_str_substr(&sub, 5, sub.length); // Skip version. - index = al_str_find(&sub, '-'); - if (index == AL_STR_NO_POS) goto def; + if ((index = al_str_find(&sub, '-')) == AL_STR_NO_POS) { + goto def; + } sub = al_str_substr(&sub, index + 1, sub.length); - index = al_str_find(&sub, '-'); - if (index == AL_STR_NO_POS) goto def; - al_str_clone(addr, &al_str_substr(&sub, 0, index)); - - for (;;) { - sub = al_str_substr(&sub, index + 1, sub.length); - index = al_str_find(&sub, '-'); - if (index == AL_STR_NO_POS) { - index = al_str_find(&sub, '.'); - if (index == AL_STR_NO_POS) { - index = sub.length; - } else { - sub.length = index; - } - } - - str value; - al_str_clone(&value, &al_str_substr(&sub, 0, index)); - al_str_to_lower(&value); - if (al_str_at(&value, 0) == 'a') { - prefs->enabled_mask |= 1 << CAMU_STREAM_AUDIO; - } else if (al_str_at(&value, 0) == 'v') { - prefs->enabled_mask |= 1 << CAMU_STREAM_VIDEO; - } else if (al_str_at(&value, 0) == 's') { - prefs->enabled_mask |= 1 << CAMU_STREAM_SUBTITLE; - } - al_str_free(&value); - - if (index == sub.length) break; + if ((index = al_str_find(&sub, '.')) != AL_STR_NO_POS) { + sub = al_str_substr(&sub, 0, index); } + al_str_from(addr, ""); + decode_addr(&sub, addr); + log_debug("Parsed address %.*s from exe filename.", al_str_x(addr)); return true; def: -#ifdef CAMU_HAVE_SUBTITLES - prefs->enabled_mask = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_VIDEO) | (1 << CAMU_STREAM_SUBTITLE); -#else - prefs->enabled_mask = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_VIDEO); -#endif - prefs->audio_lang = CAMU_LANG_JAPANESE; - prefs->subtitle_lang = CAMU_LANG_ENGLISH; - return false; } @@ -141,12 +127,12 @@ s32 window_system_main(u32 argc, str *argv, void *extra) u8 type; str addr; - if (!parse_exe_name_params(&argv[0], &addr, &c.desktop.sink.prefs)) { + if (!parse_exe_name(&argv[0], &addr)) { #ifndef CAMU_SINK_ONLY if (local) { #ifdef NAUNET_ON_WINDOWS - type = NNWT_SOCKET_TCP;; - al_str_clone(&addr, &CAMU_LOCALHOST); + type = NNWT_SOCKET_TCP; + al_str_clone(&addr, &al_str_c("127.0.0.1")); #else type = NNWT_SOCKET_UNIX; al_str_clone(&addr, &CMV_UNIX_PATH); @@ -155,17 +141,26 @@ s32 window_system_main(u32 argc, str *argv, void *extra) al_str_cat(&addr, &al_str_cs(pid_str)); #endif } else { - type = CAMU_TEST_TYPE; - al_str_clone(&addr, &CAMU_TEST_ADDR); + type = NNWT_SOCKET_TCP; + al_str_clone(&addr, &al_str_c("127.0.0.1")); } #else type = NNWT_SOCKET_TCP; - al_str_clone(&addr, &CAMU_TEST_ADDR); + al_str_clone(&addr, &al_str_c("127.0.0.1")); #endif } else { type = NNWT_SOCKET_TCP; } + struct lia_prefs *prefs = &c.desktop.sink.prefs; +#ifdef CAMU_HAVE_SUBTITLES + prefs->enabled = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_VIDEO) | (1 << CAMU_STREAM_SUBTITLE); +#else + prefs->enabled = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_VIDEO); +#endif + prefs->language.audio = CAMU_LANG_JAPANESE; + prefs->language.subtitles = CAMU_LANG_ENGLISH; + #ifndef CAMU_SINK_ONLY if (local) { camu_server_init(&c.server, &c.loop); diff --git a/src/fruits/cmv/meson.build b/src/fruits/cmv/meson.build index bfd2182..3e78f05 100644 --- a/src/fruits/cmv/meson.build +++ b/src/fruits/cmv/meson.build @@ -15,7 +15,7 @@ endif cmv_link_args = [] if is_windows and not is_msvc - cmv_link_args += ['-static', '-static-libgcc', '-static-libstdc++', '-municode', '-mwindows'] + cmv_link_args += ['-static', '-static-libgcc', '-static-libstdc++', '-mwindows', '-municode'] endif executable('cmv', cmv_src, dependencies: cmv_deps, c_args: cmv_args, link_args: cmv_link_args, install: true) diff --git a/src/fruits/common.h b/src/fruits/common.h deleted file mode 100644 index 66a72bc..0000000 --- a/src/fruits/common.h +++ /dev/null @@ -1,24 +0,0 @@ -#include <al/str.h> -#include <nnwt/socket.h> - -AL_IGNORE_WARNING("-Wunused-variable") - -static str CAMU_DB_PATH = al_str_c("/home/andrew/c/camu/data/camu_db_test"); - -static str CAMU_TEST_IP = al_str_c("108.52.160.112"); -static str CAMU_LOCALHOST = al_str_c("127.0.0.1"); - -static str CAMU_TEST_PATH = al_str_c("/tmp/camu_sock"); -static str CAMU_TEST_CONTROL_PATH = al_str_c("/tmp/camu_control_sock"); - -AL_IGNORE_WARNING_END - -#ifdef NAUNET_ON_WINDOWS -#define CAMU_TEST_TYPE NNWT_SOCKET_TCP -#define CAMU_TEST_ADDR CAMU_TEST_IP -#else -//#define CAMU_TEST_TYPE NNWT_SOCKET_UNIX -//#define CAMU_TEST_ADDR CAMU_TEST_PATH -#define CAMU_TEST_TYPE NNWT_SOCKET_TCP -#define CAMU_TEST_ADDR CAMU_TEST_IP -#endif diff --git a/src/fruits/ctv/ctv.c b/src/fruits/ctv/ctv.c index 45942e6..8b173b0 100644 --- a/src/fruits/ctv/ctv.c +++ b/src/fruits/ctv/ctv.c @@ -24,12 +24,12 @@ static nn_thread_result NNWT_THREADCALL event_loop_thread(void *userdata) struct lia_prefs *prefs = &c->desktop.sink.prefs; #ifdef CAMU_HAVE_SUBTITLES - prefs->enabled_mask = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_VIDEO) | (1 << CAMU_STREAM_SUBTITLE); + prefs->enabled = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_VIDEO) | (1 << CAMU_STREAM_SUBTITLE); #else - prefs->enabled_mask = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_VIDEO); + prefs->enabled = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_VIDEO); #endif - prefs->audio_lang = CAMU_LANG_JAPANESE; - prefs->subtitle_lang = CAMU_LANG_ENGLISH; + prefs->language.audio = CAMU_LANG_JAPANESE; + prefs->language.subtitles = CAMU_LANG_ENGLISH; if (!camu_desktop_connect(&c->desktop, &al_str_c("ctv"), &c->loop, CAMU_TEST_TYPE, &CAMU_TEST_ADDR, CAMU_PORT)) { return 0; diff --git a/src/fruits/meson.build b/src/fruits/meson.build index 37edd0b..c82d71d 100644 --- a/src/fruits/meson.build +++ b/src/fruits/meson.build @@ -1,4 +1,9 @@ -if (get_option('server').enabled() or get_option('client').enabled()) and not is_windows +use_tuis = true +if is_windows + use_tuis = false +endif + +if use_tuis and (get_option('server').enabled() or get_option('client').enabled()) notcurses = dependency('notcurses', required: false, allow_fallback: false) notcurses_core = dependency('notcurses-core', required: false, allow_fallback: false) if not notcurses.found() or not notcurses_core.found() diff --git a/src/liana/client.c b/src/liana/client.c index 380ed26..4d5689f 100644 --- a/src/liana/client.c +++ b/src/liana/client.c @@ -136,46 +136,52 @@ static void parse_info_packet(struct lia_client *client, struct nn_packet *packe nn_packet_read_str(packet, &handler); client->duration = nn_packet_read_u64(packet); collect_streams(client, packet); + if (client->streams.count == 0) { + log_warn("Resource has no streams."); + goto out; + } struct lia_prefs *prefs = &client->prefs; u8 selected = 0; - u32 accept_defaults = 0; + u8 accept_defaults = 0; for (; accept_defaults < 2; accept_defaults++) { struct camu_codec_stream *stream; al_array_foreach_ptr(client->streams, i, stream) { - u8 type_mask = 1 << stream->type; - if ((selected & type_mask) || !(prefs->enabled_mask & type_mask)) continue; + u8 type = stream->type; + if (type == CAMU_STREAM_SUBTITLE && client->mask == 0) { + log_warn("Ignoring subtitle-only resource."); + goto out; + } + if ((selected & (1 << type)) || !(prefs->enabled & (1 << type))) { + continue; + } const char *title = NULL; #ifdef CAMU_HAVE_FFMPEG if (stream->mode == CAMU_FFMPEG_COMPAT) { AVDictionary *metadata = stream->av.stream->metadata; const AVDictionaryEntry *title_entry = av_dict_get(metadata, "title", NULL, 0); - if (title_entry) title = title_entry->value; + if (title_entry) { + title = title_entry->value; + } const AVDictionaryEntry *lang_entry = av_dict_get(metadata, "language", NULL, 0); if (lang_entry && !accept_defaults) { u8 lang = CAMU_LANG_UNKNOWN; - str lang_str = al_str_cr(lang_entry->value); - if (al_str_eq(&lang_str, &al_str_c("eng"))) lang = CAMU_LANG_ENGLISH; - else if (al_str_eq(&lang_str, &al_str_c("jpn"))) lang = CAMU_LANG_JAPANESE; - switch (stream->type) { - case CAMU_STREAM_AUDIO: - if (prefs->audio_lang != lang) continue; - break; - case CAMU_STREAM_SUBTITLE: - if (prefs->subtitle_lang != lang) continue; - break; - default: - break; + str s = al_str_cr(lang_entry->value); + if (al_str_eq(&s, &al_str_c("eng"))) lang = CAMU_LANG_ENGLISH; + else if (al_str_eq(&s, &al_str_c("jpn"))) lang = CAMU_LANG_JAPANESE; + if ((type == CAMU_STREAM_AUDIO && lang != prefs->language.audio) || + (type == CAMU_STREAM_SUBTITLE && lang != prefs->language.subtitles)) { + continue; } + break; } } #endif if (title) { - log_info("Selected %s stream (index: %u, title: %s).", stream_type_to_str[stream->type], stream->index, title); + log_info("Selected %s stream (index: %u, title: %s).", stream_type_to_str[type], stream->index, title); } else { - log_info("Selected %s stream (index: %u).", stream_type_to_str[stream->type], stream->index); + log_info("Selected %s stream (index: %u).", stream_type_to_str[type], stream->index); } - selected |= type_mask; - client->mask |= 1 << stream->index; + selected |= (1 << type); struct lia_vcr_track *track = al_alloc_object(struct lia_vcr_track); track->stream = stream; track->client = lia_handler_by_name(&handler)->create_client_handler(); @@ -187,10 +193,12 @@ static void parse_info_packet(struct lia_client *client, struct nn_packet *packe // This will NOT attempt to select another stream. continue; } + client->mask |= 1 << stream->index; client->callback(client->userdata, LIANA_CLIENT_CONFIGURE, stream, track); lia_vcr_add_track(&client->vcr, track); } } +out: client->callback(client->userdata, LIANA_CLIENT_CONFIGURE_COMPLETE, NULL, NULL); } @@ -201,6 +209,7 @@ static void info_packet_callback(void *userdata, struct nn_packet_stream *stream parse_info_packet(client, packet); nn_packet_stream_return_packet(stream, packet); if (client->mask == 0 || lia_vcr_is_empty(&client->vcr)) { + log_warn("Discarding resource with no applicable streams."); al_assert(client->reconnect == RECONNECT_NONE); nn_packet_stream_disconnect(&client->data); return; diff --git a/src/liana/client.h b/src/liana/client.h index 40093e0..681b134 100644 --- a/src/liana/client.h +++ b/src/liana/client.h @@ -27,9 +27,11 @@ struct lia_reconnect_info { }; struct lia_prefs { - u8 enabled_mask; - s8 audio_lang; - s8 subtitle_lang; + u8 enabled; + struct { + u8 audio; + u8 subtitles; + } language; }; struct lia_client { diff --git a/src/liana/handlers/codec_client.c b/src/liana/handlers/codec_client.c index abb5b9a..c3dded1 100644 --- a/src/liana/handlers/codec_client.c +++ b/src/liana/handlers/codec_client.c @@ -33,7 +33,7 @@ static bool codec_client_init(struct lia_client_handler *handler, struct camu_re static bool push_av_packet(struct lia_codec_client *codec, AVPacket *pkt) { s32 ret = codec->dec->push_av_packet(codec->dec, pkt); - if (ret == AVERROR(EAGAIN)) { + while (ret == AVERROR(EAGAIN)) { codec->dec->process(codec->dec); ret = codec->dec->push_av_packet(codec->dec, pkt); } diff --git a/src/liana/list.c b/src/liana/list.c index b47a30c..253ba5f 100644 --- a/src/liana/list.c +++ b/src/liana/list.c @@ -91,6 +91,9 @@ static void unref_all_entries(struct lia_list *list, s32 trigger) } } +// @TODO: When implementing list_remove(), consider this case. +// remove() sequence 0 causes a skip to sequence 1 and sequence 1 fails to load. +// Do we properly move to list->current = -1 and an idle list? static bool entry_load_and_get_duration(struct lia_list *list, struct lia_list_entry *entry, s32 sequence, bool *error) { u8 last_load = entry->load; @@ -99,9 +102,6 @@ static bool entry_load_and_get_duration(struct lia_list *list, struct lia_list_e // If sequence is <0 that must mean entry is not yet added to list->entries. if (sequence >= 0) { al_array_remove_at(list->entries, (u32)sequence); - // @TODO: Consider this when implementing list_remove(). - // In the case of remove sequence 0, causing a skip to sequence 1, and sequence 1 - // fails to load, do we properly move to -1 and an idle list. if (list->current > sequence) { struct lia_list_sink *sink; al_array_foreach(list->sinks, i, sink) { @@ -111,6 +111,7 @@ static bool entry_load_and_get_duration(struct lia_list *list, struct lia_list_e list->current--; } struct lia_list_cmd *cmd = list->cmd; + al_assert(cmd); al_assert(cmd->sequence != sequence); if (cmd->sequence > sequence) { cmd->sequence--; @@ -141,6 +142,19 @@ static bool entry_load_and_get_duration(struct lia_list *list, struct lia_list_e static void unload_all_entires(struct lia_list *list) { + struct lia_list_cmd *cmd = list->cmd; + if (cmd && cmd->entry) { + al_assert(cmd->op == ADD); + entry_unload(list, cmd->entry); + list->cmd = NULL; + } + al_array_foreach_rev(list->command_queue, i, cmd) { + if (cmd->entry) { + al_assert(cmd->op == ADD); + entry_unload(list, cmd->entry); + al_array_remove_at(list->command_queue, i); + } + } struct lia_list_entry *entry; al_array_foreach(list->entries, i, entry) { entry_unload(list, entry); @@ -162,6 +176,7 @@ static inline void sink_pause_entry(struct lia_list_sink *sink, struct lia_list_ sink->callback(sink->userdata, LIANA_SINK_PAUSE, entry, sequence, time); } +// @TODO: This should probably give a sequence or ID. static inline void sink_unset_entry(struct lia_list_sink *sink) { sink->callback(sink->userdata, LIANA_SINK_UNSET, NULL, -1, NULL); @@ -341,13 +356,13 @@ static bool handle_skipto(struct lia_list *list, s32 sequence, s32 index) bool error; if (!entry_load_and_get_duration(list, target, index, &error)) { if (error) { - // index might point to a different entry after an error. + // On an error, sequence will address the same entry but index may not. return handle_skipto(list, sequence, index); } return false; } - u64 at = nn_get_timestamp() + LIANA_PAUSE_DELAY; + u64 at = nn_get_timestamp() + LIANA_BASE_PING; u64 pos = target->offset; u8 pause = skipto_entry(current, target, at); @@ -438,6 +453,7 @@ static void handle_toggle_pause(struct lia_list *list, s32 sequence, f64 pts) case LIANA_PAUSE_PAUSE: { al_assert(entry->start != LIANA_TIMESTAMP_INVALID); entry->paused_at = now + LIANA_PAUSE_DELAY; + // @TODO: There is no reason we couldn't pause during a pre-roll. if (entry->paused_at < entry->start) { entry->paused_at = entry->start; } @@ -603,6 +619,8 @@ static bool adjust_for_order_change(struct lia_list *list, struct lia_list_entry } } al_assert(cmd->op == SKIPTO); + // This SKIPTO being in-place is a consideration for the sequence of any + // commands that were queued after this one. return handle_skipto(list, cmd->sequence, cmd->arg0.i); } diff --git a/src/liana/list.h b/src/liana/list.h index 61dcb1d..c8b1d73 100644 --- a/src/liana/list.h +++ b/src/liana/list.h @@ -9,7 +9,7 @@ #define LIANA_SEQUENCE_ANY -1 #define LIANA_BASE_DELAY 450000u // 450ms -#define LIANA_BASE_PING 125000u // 125ms +#define LIANA_BASE_PING 150000u // 150ms #define LIANA_PAUSE_DELAY LIANA_BASE_PING #define LIANA_DELAY_IGNORE 0u @@ -100,7 +100,7 @@ struct lia_list_cmd { struct lia_list_entry *entry; s32 sequence; union { s32 i; u32 u; } arg0; - union { f64 f; u64 l; u32 u; } arg1; + union { u64 l; u32 u; f64 f; } arg1; }; struct lia_list { diff --git a/src/liana/meson.build b/src/liana/meson.build index 5c1cc42..40d4598 100644 --- a/src/liana/meson.build +++ b/src/liana/meson.build @@ -5,27 +5,28 @@ liana_server_src = [ 'handlers.c', 'process.c' ] + liana_client_src = [ 'client.c', 'vcr.c', 'handlers/codec_client.c', 'handlers.c' ] + liana_server_deps = [cache, codecs_server, codecs_client] # codecs_client for transcode. liana_client_deps = [codecs_client] -liana_args = [] -if cache_have_cdio +if 'cdio' in get_option('sources') liana_server_src += ['handlers/cdio_server.c'] liana_client_src += ['handlers/cdio_client.c'] endif -if cache_have_libdvd +if 'libdvd' in get_option('sources') liana_server_src += ['handlers/dvd_server.c'] liana_client_src += ['handlers/dvd_client.c'] endif liana_server = declare_dependency(sources: liana_server_src, - dependencies: liana_server_deps, compile_args: [liana_args, '-DLIANA_SERVER']) + dependencies: liana_server_deps, compile_args: ['-DLIANA_SERVER']) liana_client = declare_dependency(sources: liana_client_src, - dependencies: liana_client_deps, compile_args: [liana_args, '-DLIANA_CLIENT']) + dependencies: liana_client_deps, compile_args: ['-DLIANA_CLIENT']) diff --git a/src/liana/vcr.c b/src/liana/vcr.c index 13082f5..4fa548c 100644 --- a/src/liana/vcr.c +++ b/src/liana/vcr.c @@ -7,9 +7,9 @@ #include "vcr.h" #include "common.h" -#define VCR_BUFFER_BUFFERED MB(4) -#define VCR_BUFFER_GROW_FACTOR 8 -#define VCR_BUFFER_LOW_OFFSET MB(1) +#define VCR_BUFFER_BUFFERED MB(6LL) +#define VCR_BUFFER_GROW_FACTOR 8LL +#define VCR_BUFFER_LOW_OFFSET KB(500LL) AL_STATIC_ASSERT(buf_gt_low_offset, VCR_BUFFER_BUFFERED * VCR_BUFFER_GROW_FACTOR, >, VCR_BUFFER_LOW_OFFSET); enum { @@ -67,12 +67,12 @@ static void update_metrics(struct lia_vcr *vcr, u64 size) u64 diff = now - vcr->metrics.last_report_ts; u64 mark = now - vcr->metrics.last_report_mark; u64 frame = vcr->metrics.current_frame; - if (mark > 500000 || (diff > 2000000 && vcr->metrics.current_frame >= KB(500)) || (!size && frame > 0)) { + if (mark > 750000 || (diff > 5000000 && vcr->metrics.current_frame >= KB(500)) || (!size && frame > 0)) { al_assert(frame > 0); f32 kbps = (frame / 125.f) / (mark / 1000000.f); f32 average_kbps = vcr->metrics.average_kbps; average_kbps = average_kbps == 0.f ? kbps : (average_kbps + kbps) / 2.f; - f32 buffered = al_atomic_load(u64)(&vcr->count, AL_ATOMIC_RELAXED) / (f32)MB(1); + f32 buffered = atomic_load(u64)(&vcr->count, AL_ATOMIC_RELAXED) / (f32)MB(1); f32 capacity = vcr->mark.buffered / (f32)MB(1); log_info("Receiving packets at %.2fkbps (%.2f/%.2fMB).", average_kbps, buffered, capacity); vcr->metrics.average_kbps = average_kbps; @@ -88,9 +88,9 @@ void lia_vcr_init(struct lia_vcr *vcr, struct nn_event_loop *loop, struct nn_pac vcr->data = data; vcr->node_id = node_id; al_array_init(vcr->tracks); - al_atomic_store(u64)(&vcr->count, 0, AL_ATOMIC_RELAXED); + atomic_store(u64)(&vcr->count, 0, AL_ATOMIC_RELAXED); vcr->mark.buffered = VCR_BUFFER_BUFFERED; - al_atomic_store(u64)(&vcr->mark.low, 0, AL_ATOMIC_RELAXED); + atomic_store(u64)(&vcr->mark.low, 0, AL_ATOMIC_RELAXED); vcr->expand = VCR_EXPAND_UNTOUCHED; vcr->started = false; #ifndef CAMU_DIRECT_MODE @@ -149,10 +149,10 @@ static nn_thread_result NNWT_THREADCALL vcr_track_thread(void *userdata) if (packet) { // Check if we should uncork the packet stream. u32 size = nn_packet_get_size(packet); - u64 buffer = al_atomic_sub(u64)(&vcr->count, size, AL_ATOMIC_RELAXED); + u64 buffer = atomic_sub(u64)(&vcr->count, size, AL_ATOMIC_RELAXED); #ifndef CAMU_DIRECT_MODE - bool buffered = al_atomic_load(bool)(&track->buffered, AL_ATOMIC_RELAXED); - u64 low = al_atomic_load(u64)(&vcr->mark.low, AL_ATOMIC_RELAXED); + bool buffered = atomic_load(bool)(&track->buffered, AL_ATOMIC_RELAXED); + u64 low = atomic_load(u64)(&vcr->mark.low, AL_ATOMIC_RELAXED); if (buffered && (low && buffer <= low)) { nn_signal_send(&vcr->signal); } @@ -176,7 +176,7 @@ static nn_thread_result NNWT_THREADCALL vcr_track_thread(void *userdata) } // Take state again because handle_packet() could have caused the track to be corked. - state = al_atomic_load(s32)(&track->state, AL_ATOMIC_RELAXED); + state = atomic_load(s32)(&track->state, AL_ATOMIC_RELAXED); // We wait if corked (TRACK_STOPPED) or EOF. corked = (packet && state == VCR_TRACK_STOPPED) || !packet; @@ -199,7 +199,7 @@ static nn_thread_result NNWT_THREADCALL vcr_track_thread(void *userdata) nn_cond_wait(&track->cond, &track->mutex); // Check for possibly updated state. - state = al_atomic_load(s32)(&track->state, AL_ATOMIC_RELAXED); + state = atomic_load(s32)(&track->state, AL_ATOMIC_RELAXED); nn_mutex_unlock(&track->mutex); @@ -254,11 +254,11 @@ void lia_vcr_add_track(struct lia_vcr *vcr, struct lia_vcr_track *track) track->vcr = vcr; nn_cond_init(&track->cond); nn_mutex_init(&track->mutex); - al_atomic_store(bool)(&track->buffered, !VCR_TRACK_THREADED(track), AL_ATOMIC_RELAXED); + atomic_store(bool)(&track->buffered, !VCR_TRACK_THREADED(track), AL_ATOMIC_RELAXED); track->running = false; nn_packet_cache_init(&track->cache, 256); al_array_push(vcr->tracks, track); - al_atomic_store(s32)(&track->state, VCR_TRACK_RUNNING, AL_ATOMIC_RELAXED); + atomic_store(s32)(&track->state, VCR_TRACK_RUNNING, AL_ATOMIC_RELAXED); } bool lia_vcr_remove_track_by_stream(struct lia_vcr *vcr, struct camu_codec_stream *stream) @@ -298,13 +298,13 @@ static void cork_if_buffered(struct lia_vcr *vcr, u64 buffer) bool buffered = true; struct lia_vcr_track *track; al_array_foreach(vcr->tracks, i, track) { - buffered &= al_atomic_load(bool)(&track->buffered, AL_ATOMIC_RELAXED); + buffered &= atomic_load(bool)(&track->buffered, AL_ATOMIC_RELAXED); } if (buffered) { if (vcr->expand == VCR_EXPAND_UNTOUCHED) { vcr->mark.buffered = buffer * VCR_BUFFER_GROW_FACTOR; vcr->expand = VCR_EXPAND_GROWN; - log_info("Expanded buffer to size %.2fMB.", vcr->mark.buffered / (f32)MB(1)); + log_debug("Expanded buffer to size %.2fMB.", vcr->mark.buffered / (f32)MB(1)); return; } if (!vcr->corked) { @@ -316,7 +316,7 @@ static void cork_if_buffered(struct lia_vcr *vcr, u64 buffer) // Don't set low until after we corked so that vcr_track_thread() will never try uncorking // until we know what the low mark is. if (vcr->expand == VCR_EXPAND_GROWN) { - al_atomic_store(u64)(&vcr->mark.low, vcr->mark.buffered - VCR_BUFFER_LOW_OFFSET, AL_ATOMIC_RELAXED); + atomic_store(u64)(&vcr->mark.low, vcr->mark.buffered - VCR_BUFFER_LOW_OFFSET, AL_ATOMIC_RELAXED); vcr->expand = VCR_EXPAND_COMPLETE; } al_array_foreach(vcr->tracks, i, track) { @@ -342,9 +342,15 @@ void lia_vcr_push_packet(struct lia_vcr *vcr, struct nn_packet *packet) break; } if (VCR_TRACK_THREADED(track)) { - if (nn_packet_cache_send_packet(&track->cache, packet)) { - u64 buffer; - if ((buffer = al_atomic_add(u64)(&vcr->count, size, AL_ATOMIC_RELAXED)) >= vcr->mark.buffered) { + u64 buffer = 0; + bool can_send = nn_packet_cache_available(&track->cache); + if (can_send) { + buffer = atomic_add(u64)(&vcr->count, size, AL_ATOMIC_RELAXED); + nn_packet_cache_send_packet(&track->cache, packet); + } + nn_packet_cache_unlock(&track->cache); + if (can_send) { + if (buffer >= vcr->mark.buffered) { #ifndef CAMU_DIRECT_MODE cork_if_buffered(vcr, buffer); #endif @@ -360,12 +366,15 @@ void lia_vcr_push_packet(struct lia_vcr *vcr, struct nn_packet *packet) } case LIANA_PACKET_EOF: case LIANA_PACKET_ERROR: { + // @TODO: Should ERROR be passed down to LIANA_CLIENT_ERRORED? #ifndef CAMU_DIRECT_MODE update_metrics(vcr, 0); // Flush. #endif - // @TODO: Should ERROR be passed down to LIANA_CLIENT_ERRORED? al_array_foreach(vcr->tracks, i, track) { - nn_packet_cache_send_packet(&track->cache, NULL); + if (nn_packet_cache_available(&track->cache)) { + nn_packet_cache_send_packet(&track->cache, NULL); + } + nn_packet_cache_unlock(&track->cache); } #ifndef CAMU_DIRECT_MODE nn_signal_stop(&vcr->signal); @@ -373,7 +382,7 @@ void lia_vcr_push_packet(struct lia_vcr *vcr, struct nn_packet *packet) if (op == LIANA_PACKET_EOF) { log_info("Received EOF."); } else if (op == LIANA_PACKET_ERROR) { - log_warn("Forcing EOF because we got an error packet."); + log_warn("Forcing EOF due to an error packet."); } break; } @@ -386,17 +395,17 @@ void lia_vcr_push_packet(struct lia_vcr *vcr, struct nn_packet *packet) void lia_vcr_set_buffered(struct lia_vcr_track *track) { - al_atomic_store(bool)(&track->buffered, true, AL_ATOMIC_RELAXED); + atomic_store(bool)(&track->buffered, true, AL_ATOMIC_RELAXED); } void lia_vcr_cork(struct lia_vcr_track *track) { - al_atomic_store(s32)(&track->state, VCR_TRACK_STOPPED, AL_ATOMIC_RELAXED); + atomic_store(s32)(&track->state, VCR_TRACK_STOPPED, AL_ATOMIC_RELAXED); } void lia_vcr_uncork(struct lia_vcr_track *track) { - if (al_atomic_load(s32)(&track->state, AL_ATOMIC_RELAXED) != VCR_TRACK_STOPPED) { + if (atomic_load(s32)(&track->state, AL_ATOMIC_ACQUIRE) != VCR_TRACK_STOPPED) { // We will get here during normal operation. Early returning is historically // tricky in vcr_uncork(). If I'm understanding correctly, asserting that // cond_is_waiting() just below means we are safe. @@ -404,7 +413,7 @@ void lia_vcr_uncork(struct lia_vcr_track *track) } // Lock before setting track->state to avoid a race with cork(). nn_mutex_lock(&track->mutex); - al_atomic_store(s32)(&track->state, VCR_TRACK_RUNNING, AL_ATOMIC_RELAXED); + atomic_store(s32)(&track->state, VCR_TRACK_RUNNING, AL_ATOMIC_RELEASE); al_assert(nn_cond_is_waiting(&track->cond)); nn_cond_signal(&track->cond); nn_mutex_unlock(&track->mutex); @@ -416,7 +425,7 @@ static void vcr_track_close_internal(struct lia_vcr_track *track) // Calling packet_cache_disable() while holding the track mutex can very possibly deadlock. nn_packet_cache_disable(&track->cache); nn_mutex_lock(&track->mutex); - al_atomic_store(s32)(&track->state, VCR_TRACK_CLOSED, AL_ATOMIC_RELAXED); + atomic_store(s32)(&track->state, VCR_TRACK_CLOSED, AL_ATOMIC_RELAXED); if (nn_cond_is_waiting(&track->cond)) { nn_cond_signal(&track->cond); } @@ -451,17 +460,17 @@ void lia_vcr_flush(struct lia_vcr *vcr) struct lia_vcr_track *track; al_array_foreach(vcr->tracks, i, track) { if (VCR_TRACK_THREADED(track)) { - al_atomic_store(bool)(&track->buffered, false, AL_ATOMIC_RELAXED); + atomic_store(bool)(&track->buffered, false, AL_ATOMIC_RELAXED); track->client->flush(track->client); nn_packet_cache_enable(&track->cache); - al_atomic_store(s32)(&track->state, VCR_TRACK_RUNNING, AL_ATOMIC_RELAXED); + atomic_store(s32)(&track->state, VCR_TRACK_RUNNING, AL_ATOMIC_RELAXED); } else { track->client->flush(track->client); } } - al_atomic_store(u64)(&vcr->count, 0, AL_ATOMIC_RELAXED); + atomic_store(u64)(&vcr->count, 0, AL_ATOMIC_RELAXED); if (vcr->expand == VCR_EXPAND_COMPLETE) { - al_atomic_store(u64)(&vcr->mark.low, 0, AL_ATOMIC_RELAXED); + atomic_store(u64)(&vcr->mark.low, 0, AL_ATOMIC_RELAXED); vcr->expand = VCR_EXPAND_GROWN; } } diff --git a/src/libsink/desktop.c b/src/libsink/desktop.c index 8582cd4..af6fc50 100644 --- a/src/libsink/desktop.c +++ b/src/libsink/desktop.c @@ -208,14 +208,16 @@ bool camu_desktop_open(struct camu_desktop *c, const char *window_name) c->scr.callback = screen_callback; c->scr.userdata = c; if (!camu_screen_init(&c->scr) || !camu_screen_create_window(&c->scr, window_name)) { + log_error("Failed to create window."); return false; } #if defined CAMU_RENDERER_MOMO c->renderer = camu_renderer_momo_create(); #elif defined CAMU_RENDERER_LIBPLACEBO - c->renderer = camu_renderer_lp_create(); + c->renderer = camu_renderer_libplacebo_create(); #endif if (!camu_screen_create_renderer(&c->scr, c->renderer)) { + log_error("Failed to create renderer."); return false; } // Need to render twice for the window to show early. @@ -252,7 +254,10 @@ bool camu_desktop_tick(struct camu_desktop *c) { bool force; if (camu_screen_tick(&c->scr, &force) && !c->should_quit) { - c->renderer->render(c->renderer, &c->scr, force); + if (!c->renderer->render(c->renderer, &c->scr, force)) { + log_error("render() failed, forcing exit."); + c->should_quit = 1; + } } return !c->should_quit; } diff --git a/src/libsink/input_simulator.c b/src/libsink/input_simulator.c index 19786af..5b9d6e6 100644 --- a/src/libsink/input_simulator.c +++ b/src/libsink/input_simulator.c @@ -31,23 +31,23 @@ static nn_thread_result NNWT_THREADCALL input_simulation_thread(void *userdata) switch (al_random_int(0, MARK - 1)) { case SKIP: { s32 n = al_random_int(1, 5); - log_debug("SKIP (n: %d).", n); + log_info("SKIP (n: %d).", n); camu_sink_skip(sink, n); break; } case BACKSKIP: { s32 n = al_random_int(-5, -1); - log_debug("BACKSKIP (n: %d).", n); + log_info("BACKSKIP (n: %d).", n); camu_sink_skip(sink, n); break; } case SHUFFLE: { - log_debug("SHUFFLE."); + log_info("SHUFFLE."); camu_sink_shuffle(sink); break; } case TOGGLE_PAUSE: { - log_debug("TOGGLE_PAUSE."); + log_info("TOGGLE_PAUSE."); camu_sink_toggle_pause(sink); break; } @@ -56,7 +56,7 @@ static nn_thread_result NNWT_THREADCALL input_simulation_thread(void *userdata) if (pos < 0.005) pos = 0.0; if (pos > 0.995) pos = 1.0; else if (pos > 0.99) pos = 0.9999; - log_debug("SEEK (pos: %f).", pos); + log_info("SEEK (pos: %f).", pos); camu_sink_seek(sink, &pos, CAMU_SEEK_PERCENT); break; } diff --git a/src/libsink/sink.c b/src/libsink/sink.c index e9b4df4..fccbc3e 100644 --- a/src/libsink/sink.c +++ b/src/libsink/sink.c @@ -10,7 +10,7 @@ #include "sink.h" #include "common.h" -#define CAMU_SINK_LOCAL +//#define CAMU_SINK_LOCAL //#define CAMU_SINK_ONESHOT // Requested state of the sinks outputs. @@ -71,8 +71,7 @@ AL_STATIC_ASSERT(max_age_lt_lru, ENTRY_MAX_AGE, <, SINK_LRU_MAX); #define CONNECTION_NUMBER(id) ((id >> 48) & 0xffff) // Only sink->target can ever be 0xb00b. -#define OxbOOb ((struct camu_sink_entry *)0xb00b) -#define ENTRY_IS_VALID(entry) ((entry) && (entry) != OxbOOb) +#define ENTRY_IS_VALID(entry) ((entry) && (entry) != (struct camu_sink_entry *)0xb00b) // printf format for entries. #ifdef AL_DEBUG @@ -87,14 +86,19 @@ AL_STATIC_ASSERT(max_age_lt_lru, ENTRY_MAX_AGE, <, SINK_LRU_MAX); #define VIDEO_STATE(entry) ((entry)->video.state) // If a buffer is still INIT or QUEUED after the entry is configured, it's "empty". -// Also, if a buffer errors, it will be detached in CLIENT_REMOVE_BUFFERS. -// Empty is a state that cannot change while a buffer is being used (push()/read()). +// An ERRORED buffer will be DETACHED after CLIENT_REMOVE_BUFFERS and is then considered empty. +// Empty is a state that cannot change while a buffer could be in use (push()/read()). #define AUDIO_EMPTY(entry) (AUDIO_STATE(entry) <= BUFFER_DETACHED) #define VIDEO_EMPTY(entry) (VIDEO_STATE(entry) <= BUFFER_DETACHED) #define AUDIO_ENDED(entry) (AUDIO_STATE(entry) >= BUFFER_ENDED) #define VIDEO_ENDED(entry) (VIDEO_STATE(entry) >= BUFFER_ENDED) -#define ENTRY_ENDED(entry) ((AUDIO_EMPTY(entry) || AUDIO_ENDED(entry)) && (VIDEO_EMPTY(entry) || VIDEO_ENDED(entry))) +/* +#define ENTRY_ENDED(entry) \ + ((AUDIO_ENDED(entry) && (VIDEO_ENDED(entry) || VIDEO_EMPTY(entry) || VIDEO_IS_SINGLE_FRAME(entry))) || \ + (AUDIO_EMPTY(entry) && VIDEO_ENDED(entry))) +*/ +#define ENTRY_ENDED(entry) entry->ended // IGNORED = ENDED or EMPTY. #define AUDIO_ADDED_OR_IGNORED(entry) (AUDIO_STATE(entry) >= BUFFER_ADDED || AUDIO_EMPTY(entry)) @@ -113,6 +117,8 @@ AL_STATIC_ASSERT(max_age_lt_lru, ENTRY_MAX_AGE, <, SINK_LRU_MAX); #define BLOCKING_SLEEP(delay) nn_event_loop_sleep(sink->loop, delay) #endif +#define CMD(cmd, ...) ((struct camu_sink_cmd){ .op = cmd, __VA_ARGS__ }) + // Functions that might happen on separate threads. // CAMU_MIXER_THREADED: // audio_buffer_callback() @@ -127,7 +133,7 @@ AL_STATIC_ASSERT(max_age_lt_lru, ENTRY_MAX_AGE, <, SINK_LRU_MAX); static inline bool entry_audio_buffer_held(struct camu_sink_entry *entry) { #ifdef CAMU_MIXER_THREADED - return al_atomic_load(u8)(&entry->audio.buf.ref, AL_ATOMIC_RELAXED) == 1; + return atomic_load(u8)(&entry->audio.buf.ref, AL_ATOMIC_RELAXED) == 1; #else (void)entry; return false; @@ -137,19 +143,30 @@ static inline bool entry_audio_buffer_held(struct camu_sink_entry *entry) static inline bool entry_video_buffer_held(struct camu_sink_entry *entry) { #ifdef CAMU_SCREEN_THREADED - return al_atomic_load(u8)(&entry->video.buf.ref, AL_ATOMIC_RELAXED) == 1; + return atomic_load(u8)(&entry->video.buf.ref, AL_ATOMIC_RELAXED) == 1; #else (void)entry; return false; #endif } -static void queue_cmd(struct camu_sink *sink, struct camu_sink_cmd cmd) +static void queue_cmds(struct camu_sink *sink, u32 count, ...) { - camu_queue_push(sink->queue, cmd); + camu_queue_lock(sink->queue); + va_list cmds; + va_start(cmds, count); + for (u32 i = 0; i < count; i++) { + camu_queue_push(sink->queue, va_arg(cmds, struct camu_sink_cmd)); + } + camu_queue_unlock(sink->queue); nn_signal_send(&sink->queue_signal); } +static void queue_cmd(struct camu_sink *sink, struct camu_sink_cmd cmd) +{ + queue_cmds(sink, 1, cmd); +} + static void refresh_video_output(struct camu_sink *sink) { #ifndef CAMU_SINK_NO_VIDEO @@ -165,11 +182,7 @@ static inline void add_entry_audio_buffer(struct camu_sink_entry *entry) #ifdef CAMU_MIXER_THREADED_START_STOP sink->callback(sink->userdata, CAMU_SINK_ADD_BUFFER, CAMU_SINK_AUDIO, &entry->audio.buf); #else - queue_cmd(sink, (struct camu_sink_cmd){ - .op = ADD_BUFFER, - .value.i = CAMU_SINK_AUDIO, - .opaque = entry - }); + queue_cmd(sink, CMD(ADD_BUFFER, .v.u = CAMU_SINK_AUDIO, .opaque = entry)); #endif } @@ -194,11 +207,7 @@ static void remove_entry_audio_buffer(struct camu_sink_entry *entry) #ifdef CAMU_MIXER_THREADED_START_STOP sink->callback(sink->userdata, CAMU_SINK_REMOVE_BUFFER, CAMU_SINK_AUDIO, &entry->audio.buf); #else - queue_cmd(sink, (struct camu_sink_cmd){ - .op = REMOVE_BUFFER, - .value.i = CAMU_SINK_AUDIO, - .opaque = entry - }); + queue_cmd(sink, CMD(REMOVE_BUFFER, .v.u = CAMU_SINK_AUDIO, .opaque = entry)); #endif break; case BUFFER_SET_OR_BUFFERED: @@ -243,7 +252,6 @@ static void remove_entry_video_buffer(struct camu_sink_entry *entry) static void remove_entry_buffers(struct camu_sink_entry *entry) { log_trace("remove_entry_buffers("ENTRY_FMT"), audio_state: %hhu, video_state: %hhu.", ENTRY_ARG(entry), AUDIO_STATE(entry), VIDEO_STATE(entry)); - al_assert(!entry->ended); if (!AUDIO_ENDED(entry)) remove_entry_audio_buffer(entry); if (!VIDEO_ENDED(entry)) remove_entry_video_buffer(entry); } @@ -256,14 +264,15 @@ static void add_or_queue_entry(struct camu_sink_entry *entry) log_trace("add_or_queue_entry("ENTRY_FMT"), audio_state: %hhu, video_state: %hhu.", ENTRY_ARG(entry), AUDIO_STATE(entry), VIDEO_STATE(entry)); al_assert(AUDIO_STATE(entry) != BUFFER_QUEUED); al_assert(VIDEO_STATE(entry) != BUFFER_QUEUED); + // Either buffer could be DETACHED. if (AUDIO_STATE(entry) == BUFFER_INIT) { AUDIO_STATE(entry) = BUFFER_QUEUED; - } else if (!AUDIO_ENDED(entry)) { + } else if (!AUDIO_ENDED_OR_EMPTY(entry)) { add_audio_if_set_and_buffered(entry); } if (VIDEO_STATE(entry) == BUFFER_INIT) { VIDEO_STATE(entry) = BUFFER_QUEUED; - } else if (!VIDEO_ENDED(entry)) { + } else if (!VIDEO_ENDED_OR_EMPTY(entry)) { add_video_if_set_and_buffered(entry); } } @@ -279,14 +288,14 @@ static void maybe_disconnect_entry(struct camu_sink_entry *entry) } #ifdef CAMU_SINK_LOCAL -static void local_pause(struct camu_sink *sink, struct camu_sink_entry *entry) +static void local_entry_pause(struct camu_sink *sink, struct camu_sink_entry *entry) { if (!camu_clock_is_paused(&entry->clock)) { entry->paused = true; camu_clock_pause(&entry->clock, 0); log_info("Clock paused."); // Audio stop will be handled by a BUFFER_PAUSED callback. - if (!VIDEO_EMPTY(entry) && !VIDEO_IS_SINGLE_FRAME(entry) && sink->video.state == SINK_PLAYING) { + if (!VIDEO_ENDED_OR_EMPTY(entry) && !VIDEO_IS_SINGLE_FRAME(entry) && sink->video.state == SINK_PLAYING) { #ifndef CAMU_SINK_NO_VIDEO sink->callback(sink->userdata, CAMU_SINK_STOP, CAMU_SINK_VIDEO, NULL); #endif @@ -296,13 +305,13 @@ static void local_pause(struct camu_sink *sink, struct camu_sink_entry *entry) entry->paused = false; camu_clock_resume(&entry->clock, 0); log_info("Clock resumed."); - if (!VIDEO_EMPTY(entry) && !VIDEO_IS_SINGLE_FRAME(entry) && sink->video.state == SINK_PAUSED) { + if (!VIDEO_ENDED_OR_EMPTY(entry) && !VIDEO_IS_SINGLE_FRAME(entry) && sink->video.state == SINK_PAUSED) { #ifndef CAMU_SINK_NO_VIDEO sink->callback(sink->userdata, CAMU_SINK_START, CAMU_SINK_VIDEO, NULL); #endif sink->video.state = SINK_PLAYING; } - if (!AUDIO_EMPTY(entry) && sink->audio.state == SINK_PAUSED) { + if (!AUDIO_ENDED_OR_EMPTY(entry) && sink->audio.state == SINK_PAUSED) { sink->callback(sink->userdata, CAMU_SINK_START, CAMU_SINK_AUDIO, NULL); sink->audio.state = SINK_PLAYING; } @@ -310,6 +319,12 @@ static void local_pause(struct camu_sink *sink, struct camu_sink_entry *entry) } #endif +// sink->current could be NULL. +static inline struct camu_sink_entry *get_entry_for_command(struct camu_sink *sink) +{ + return ENTRY_IS_VALID(sink->target) ? sink->target : sink->current; +} + static inline s32 get_sequence_for_command(struct camu_sink_entry *entry) { // SEQUENCE_ANY resolves order on the server. @@ -329,7 +344,7 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd) { switch (cmd->op) { case START: { - switch (cmd->value.i) { + switch (cmd->v.u) { case CAMU_SINK_AUDIO: if (sink->audio.state == SINK_PAUSED) { sink->callback(sink->userdata, CAMU_SINK_START, CAMU_SINK_AUDIO, NULL); @@ -348,7 +363,7 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd) break; } case STOP: { - switch (cmd->value.i) { + switch (cmd->v.u) { case CAMU_SINK_AUDIO: if (sink->audio.state == SINK_PLAYING) { sink->callback(sink->userdata, CAMU_SINK_STOP, CAMU_SINK_AUDIO, NULL); @@ -368,7 +383,7 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd) } case ADD_BUFFER: { struct camu_sink_entry *entry = (struct camu_sink_entry *)cmd->opaque; - switch (cmd->value.i) { + switch (cmd->v.u) { case CAMU_SINK_AUDIO: sink->callback(sink->userdata, CAMU_SINK_ADD_BUFFER, CAMU_SINK_AUDIO, &entry->audio.buf); break; @@ -382,7 +397,7 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd) } case REMOVE_BUFFER: { struct camu_sink_entry *entry = (struct camu_sink_entry *)cmd->opaque; - switch (cmd->value.i) { + switch (cmd->v.u) { case CAMU_SINK_AUDIO: sink->callback(sink->userdata, CAMU_SINK_REMOVE_BUFFER, CAMU_SINK_AUDIO, &entry->audio.buf); break; @@ -395,7 +410,7 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd) break; } case CLEAR_BUFFERS: { - switch (cmd->value.i) { + switch (cmd->v.u) { case CAMU_SINK_AUDIO: sink->callback(sink->userdata, CAMU_SINK_CLEAR, CAMU_SINK_AUDIO, NULL); break; @@ -424,21 +439,21 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd) nn_packet_write_str(packet, &sink->default_list); nn_packet_write_u8(packet, CAMU_LIST_SKIP); nn_packet_write_s32(packet, get_sequence_for_command(entry)); - nn_packet_write_s32(packet, (s32)cmd->value.i); + nn_packet_write_s32(packet, (s32)cmd->v.i); nn_rpc_connection_command(sink->conn, packet, NULL, NULL); break; } case TOGGLE_PAUSE: { struct camu_sink_entry *entry = (struct camu_sink_entry *)cmd->opaque; #ifdef CAMU_SINK_LOCAL - if (!entry->held) local_pause(sink, entry); + if (!entry->held) local_entry_pause(sink, entry); #else if (!sink->conn) return; struct nn_packet *packet = nn_rpc_get_packet(&sink->client, CAMU_SERVER_LIST_ACTION); nn_packet_write_str(packet, &sink->default_list); nn_packet_write_u8(packet, CAMU_LIST_TOGGLE_PAUSE); nn_packet_write_s32(packet, get_sequence_for_command(entry)); - nn_packet_write_f64(packet, cmd->value.f); + nn_packet_write_f64(packet, cmd->v.f); nn_rpc_connection_command(sink->conn, packet, NULL, NULL); #endif break; @@ -451,7 +466,7 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd) nn_packet_write_u8(packet, CAMU_LIST_SEEK); nn_packet_write_s32(packet, entry->sequence); nn_packet_write_u32(packet, REMOTE_ENTRY_ID(entry->id)); - nn_packet_write_u64(packet, cmd->value.u); + nn_packet_write_u64(packet, cmd->v.u); nn_rpc_connection_command(sink->conn, packet, NULL, NULL); break; } @@ -476,7 +491,7 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd) nn_packet_write_str(packet, &sink->default_list); nn_packet_write_u8(packet, CAMU_LIST_END); nn_packet_write_u32(packet, REMOTE_ENTRY_ID(entry->id)); - nn_packet_write_u32(packet, (u32)cmd->value.u); + nn_packet_write_u32(packet, (u32)cmd->v.u); nn_rpc_connection_command(sink->conn, packet, NULL, NULL); break; } @@ -486,11 +501,11 @@ static void handle_sink_cmd(struct camu_sink *sink, struct camu_sink_cmd *cmd) static void queue_signal_callback(void *userdata) { struct camu_sink *sink = (struct camu_sink *)userdata; - u32 size; + u32 count; struct camu_sink_cmd cmd; for (;;) { - camu_queue_try_pop(sink->queue, size, cmd); - if (size == 0) break; + camu_queue_try_pop(sink->queue, count, cmd); + if (count == 0) break; handle_sink_cmd(sink, &cmd); } } @@ -498,7 +513,8 @@ static void queue_signal_callback(void *userdata) static void mixer_callback(void *userdata, u8 op) { struct camu_sink *sink = (struct camu_sink *)userdata; - // @TODO: Isn't the idea of MIXER_EMPTY to not STOP audio when we know the mixer will be empty? + // @TODO: Isn't the idea of MIXER_EMPTY to not explicitly STOP the audio in places we + // expect the mixer to be empty (rely on MIXER_EMPTY). if (op == CAMU_MIXER_EMPTY) { log_info("Mixer empty."); // Regardless of if we are checking an entry's state here, we have to sync with @@ -506,10 +522,7 @@ static void mixer_callback(void *userdata, u8 op) nn_mutex_lock(&sink->lock); // This feels a bit too loose. if (!(sink->current && AUDIO_STATE(sink->current) == BUFFER_ADDED)) { - queue_cmd(sink, (struct camu_sink_cmd){ - .op = STOP, - .value.i = CAMU_SINK_AUDIO - }); + queue_cmd(sink, CMD(STOP, .v.u = CAMU_SINK_AUDIO)); } nn_mutex_unlock(&sink->lock); } @@ -568,10 +581,7 @@ static void maybe_remove_previous(struct camu_sink *sink) // Cleanup entries from old connections. This is especially important to // keep reseek() from being overly wasteful. if (CONNECTION_NUMBER(previous->id) != sink->connection_number) { - queue_cmd(sink, (struct camu_sink_cmd){ - .op = EJECT_ENTRY, - .opaque = previous - }); + queue_cmd(sink, CMD(EJECT_ENTRY, .opaque = previous)); } } sink->previous.count = 0; @@ -600,10 +610,9 @@ static void maybe_add_to_previous(struct camu_sink *sink, struct camu_sink_entry { log_trace("maybe_add_to_previous("ENTRY_FMT", "ENTRY_FMT").", ENTRY_ARG(previous), ENTRY_ARG(target)); al_assert(previous != target); - al_assert(!previous->ended); - // If none of the entry's buffers are added, we don't care about adding it to previous. - bool dangling_target = target == OxbOOb; - if (dangling_target || target->ended || (AUDIO_STATE(previous) != BUFFER_ADDED && VIDEO_STATE(previous) != BUFFER_ADDED)) { + // If none of an entry's buffers are added, we don't care about adding it to previous. + bool dangling_target = target == (struct camu_sink_entry *)0xb00b; + if (dangling_target || ENTRY_ENDED(target) || (AUDIO_STATE(previous) != BUFFER_ADDED && VIDEO_STATE(previous) != BUFFER_ADDED)) { remove_entry_buffers(previous); return; } @@ -613,18 +622,14 @@ static void maybe_add_to_previous(struct camu_sink *sink, struct camu_sink_entry static void after_add_entry(struct camu_sink_entry *entry, bool skip_audio, bool skip_video) { maybe_remove_previous(entry->sink); - queue_cmd(entry->sink, (struct camu_sink_cmd){ - .op = (skip_video || entry->paused) ? STOP : START, - .value.i = CAMU_SINK_VIDEO - }); + queue_cmds(entry->sink, 2, + CMD((skip_video || entry->paused) ? STOP : START, .v.u = CAMU_SINK_VIDEO), + CMD((skip_audio || entry->paused) ? STOP : START, .v.u = CAMU_SINK_AUDIO) + ); if (VIDEO_ENDED_OR_EMPTY(entry)) { // Clear the screen if skipping from a video to an audio-only entry. refresh_video_output(entry->sink); } - queue_cmd(entry->sink, (struct camu_sink_cmd){ - .op = (skip_audio || entry->paused) ? STOP : START, - .value.i = CAMU_SINK_AUDIO - }); } // Call this after setting state to ADDED because this entry might be in previous. @@ -641,7 +646,7 @@ static void do_add_entry(struct camu_sink_entry *entry) void add_audio_if_set_and_buffered(struct camu_sink_entry *entry) { - al_assert(!entry->ended); + al_assert(!ENTRY_ENDED(entry)); al_assert(AUDIO_STATE(entry) != BUFFER_INIT); al_assert(AUDIO_STATE(entry) != BUFFER_QUEUED); al_assert(AUDIO_STATE(entry) != BUFFER_ADDED); @@ -662,7 +667,7 @@ void add_audio_if_set_and_buffered(struct camu_sink_entry *entry) void add_video_if_set_and_buffered(struct camu_sink_entry *entry) { // Single frame entries will be added/removed with ended set. - if (entry->ended) al_assert(VIDEO_IS_SINGLE_FRAME(entry)); + if (ENTRY_ENDED(entry)) al_assert(VIDEO_IS_SINGLE_FRAME(entry)); al_assert(VIDEO_STATE(entry) != BUFFER_INIT); al_assert(VIDEO_STATE(entry) != BUFFER_QUEUED); al_assert(VIDEO_STATE(entry) != BUFFER_ADDED); @@ -690,56 +695,34 @@ static void switch_to(struct camu_sink *sink, struct camu_sink_entry *target) { struct camu_sink_entry *current = sink->current; - log_trace("switch_to("ENTRY_FMT"(ended: %s)), current: "ENTRY_FMT"(ended: %s).", - ENTRY_ARG(target), BOOLSTR(ENTRY_IS_VALID(target) ? target->ended : false), - ENTRY_ARG(current), BOOLSTR(current ? current->ended : false)); + log_trace("switch_to("ENTRY_FMT"), current: "ENTRY_FMT".", ENTRY_ARG(target), ENTRY_ARG(current)); - bool ensure_removed = false; if (current) { struct camu_sink_entry *suspended = sink->suspended; al_assert(current != target); - ensure_removed = suspended || current->ended; if (suspended) { al_assert(suspended == current); - // A buffer's state being QUEUED should be impossible while it's - // entry is suspended. + // It should be impossible for a buffer to be QUEUED while it's entry is suspended. al_assert(AUDIO_STATE(suspended) != BUFFER_QUEUED); al_assert(VIDEO_STATE(suspended) != BUFFER_QUEUED); sink->suspended = NULL; log_warn("Unset suspended entry as a substitute for remove."); - } else if (!current->ended) { + } else { maybe_add_to_previous(sink, current, target); } } - // @TODO: Cleanup stop_video? and log_trace lengths. - bool stop_video = false; - bool dangling_target = target == OxbOOb; + bool dangling_target = target == (struct camu_sink_entry *)0xb00b; + bool stop_video = dangling_target; if (dangling_target) { log_trace("Ignored dangling target."); } else { - if (!target->ended) { - remove_previous_if_contains(sink, target); - add_or_queue_entry(target); - } else { - log_trace("Target ended in switch_to()."); - if (!VIDEO_ENDED_OR_EMPTY(target) && VIDEO_IS_SINGLE_FRAME(target)) { - add_video_if_set_and_buffered(target); - } - stop_video = true; - } - } - - if (ensure_removed) { - if (!VIDEO_ENDED_OR_EMPTY(current) && VIDEO_IS_SINGLE_FRAME(current)) { - remove_entry_video_buffer(current); - } - al_assert(AUDIO_STATE(current) != BUFFER_ADDED); - al_assert(VIDEO_STATE(current) != BUFFER_ADDED); + remove_previous_if_contains(sink, target); + add_or_queue_entry(target); + stop_video = VIDEO_ENDED_OR_EMPTY(target) || VIDEO_IS_SINGLE_FRAME(target); } if (dangling_target) { - stop_video = true; sink->current = NULL; } else { target->audio.ignore_paused = false; @@ -750,10 +733,7 @@ static void switch_to(struct camu_sink *sink, struct camu_sink_entry *target) } if (stop_video) { - queue_cmd(sink, (struct camu_sink_cmd){ - .op = STOP, - .value.i = CAMU_SINK_VIDEO - }); + queue_cmd(sink, CMD(STOP, .v.u = CAMU_SINK_VIDEO)); refresh_video_output(sink); } } @@ -781,17 +761,19 @@ static void pause_and_swap_to(struct camu_sink *sink, struct camu_sink_entry *ta al_assert(target != current); al_assert(!sink->target); // This is extra verbose because the order is important. - // 1. sink->target has to be set before calling clock_pause(). - // 2. current must still be paused even if it's ended. - // 3. In the immediate case, switch_to() has to come last. - if (current && !current->ended) { + // 1. sink->target has to be set before calling clock_pause(). + if (current && !ENTRY_ENDED(current)) { sink->target = target; } + // 2. current must still be paused, even if it's ended. if (current) { current->audio.ignore_paused = true; camu_clock_pause(¤t->clock, at); } - if (!current || current->ended) switch_to(sink, target); + // 3. In the immediate swap case, switch_to() has to come last. + if (!current || ENTRY_ENDED(current)) { + switch_to(sink, target); + } } #endif @@ -805,16 +787,14 @@ static bool end_entry_and_advance_queue(struct camu_sink *sink, struct camu_sink #endif // This entry's buffers cannot be added again until after a reset. entry->ended = true; - queue_cmd(sink, (struct camu_sink_cmd){ - .op = END, - .value.u = entry->reset_token, - .opaque = entry - }); + al_assert(ENTRY_ENDED(entry)); + queue_cmd(sink, CMD(END, .v.u = entry->reset_token, .opaque = entry)); #ifdef LIANA_LIST_SCUFFED_LOOP log_info("Looping."); return true; #endif if (sink->target) { + log_info("Buffers swapped on end() (Gapless if queued)."); switch_to(sink, sink->target); sink->target = NULL; return true; @@ -843,17 +823,18 @@ static void audio_buffer_callback(void *userdata, u8 op) nn_mutex_lock(&sink->lock); if (!entry->audio.ignore_paused && entry->paused) { log_info("Audio buffer paused."); - queue_cmd(sink, (struct camu_sink_cmd){ - .op = STOP, - .value.i = CAMU_SINK_AUDIO - }); + queue_cmd(sink, CMD(STOP, .v.u = CAMU_SINK_AUDIO)); } nn_mutex_unlock(&sink->lock); break; case CAMU_BUFFER_EOF: case CAMU_BUFFER_ERRORED: { bool error = op == CAMU_BUFFER_ERRORED; - log_debug(error ? "Audio buffer errored." : "Audio EOF."); + if (error) { + log_error("Audio buffer errored."); + } else { + log_debug("Audio EOF."); + } nn_mutex_lock(&sink->lock); // EOF and ERRORED come from the outputs read() thread. So, having threaded // outputs means anything could have happened while waiting on the lock above. @@ -894,7 +875,11 @@ static void video_buffer_callback(void *userdata, u8 op) case CAMU_BUFFER_EOF: case CAMU_BUFFER_ERRORED: { bool error = op == CAMU_BUFFER_ERRORED; - log_debug(error ? "Video buffer errored." : "Video EOF."); + if (error) { + log_error("Video buffer errored."); + } else { + log_debug("Video EOF."); + } nn_mutex_lock(&sink->lock); if (!AUDIO_EMPTY(entry)) { camu_audio_buffer_set_no_video(&entry->audio.buf, true); @@ -915,10 +900,7 @@ static void video_buffer_callback(void *userdata, u8 op) } nn_mutex_unlock(&sink->lock); if (!swapped) { - queue_cmd(sink, (struct camu_sink_cmd){ - .op = STOP, - .value.i = CAMU_SINK_VIDEO - }); + queue_cmd(sink, CMD(STOP, .v.u = CAMU_SINK_VIDEO)); } break; } @@ -937,10 +919,7 @@ static void clock_callback(void *userdata, u8 op) switch_to(sink, sink->target); sink->target = NULL; } else if (entry->paused) { - queue_cmd(entry->sink, (struct camu_sink_cmd){ - .op = STOP, - .value.i = CAMU_SINK_VIDEO - }); + queue_cmd(sink, CMD(STOP, .v.u = CAMU_SINK_VIDEO)); } } nn_mutex_unlock(&sink->lock); @@ -953,20 +932,11 @@ static void evaluate_and_set_buffer_params(struct camu_sink *sink, struct camu_s f64 avg_frame_duration = entry->video.buf.avg_frame_duration; #ifdef CAMU_SINK_LOCAL if (!AUDIO_EMPTY(entry) && !ignore_video) { - // Delay either the audio or video so we can start the clock as - // soon as possible while keeping A/V sync. f64 audio = camu_mixer_get_latency(sink->audio.mixer); struct camu_renderer *renderer = sink->video.renderer; f64 video = renderer->get_latency(renderer) * avg_frame_duration; - // If the audio buffer is delayed by less than the audio latency, data will be skipped. - f64 base = -audio; - if (audio > video) { - camu_audio_buffer_set_latency(&entry->audio.buf, base); - camu_video_buffer_set_latency(&entry->video.buf, base - (audio - video)); - } else if (video > audio) { - camu_audio_buffer_set_latency(&entry->audio.buf, base); - camu_video_buffer_set_latency(&entry->video.buf, base + (video - audio)); - } + camu_audio_buffer_set_latency(&entry->audio.buf, -audio); + camu_video_buffer_set_latency(&entry->video.buf, -video); } // If we're local we don't have to worry about syncing audio-only entries. camu_audio_buffer_set_ignore_desync(&entry->audio.buf, ignore_video); @@ -1013,6 +983,7 @@ static void remove_from_queue_by_opaque(struct camu_sink *sink, void *opaque) static void client_callback(void *userdata, u8 op, struct camu_codec_stream *stream, void *opaque) { struct camu_sink_entry *entry = (struct camu_sink_entry *)userdata; + al_assert(ENTRY_IS_VALID(entry)); struct camu_sink *sink = entry->sink; switch (op) { case LIANA_CLIENT_CONFIGURE: { @@ -1106,13 +1077,8 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str log_trace("remove_buffers("ENTRY_FMT", %s, %s), entry == current: %s.", ENTRY_ARG(entry), BOOLSTR(rec->reconnect), BOOLSTR(rec->unconfigured), BOOLSTR(entry == sink->current)); if (entry == sink->current) { - if (!entry->ended) { - // It should only be possible for a buffer to be INIT if entry is not current or ended. - // Ended case is: (audio or video buffer empty) -> entry switched off of -> entry ended -> switch_to()'d. - // - The empty buffer doesn't get re-QUEUED because the entry is ended. - al_assert(AUDIO_STATE(entry) != BUFFER_INIT); - al_assert(VIDEO_STATE(entry) != BUFFER_INIT); - } + al_assert(AUDIO_STATE(entry) != BUFFER_INIT); + al_assert(VIDEO_STATE(entry) != BUFFER_INIT); if (sink->target) { // This is necessary to avoid re-adding an entry with an in-between clock state. // See note in clock.c::camu_clock_seek(). @@ -1140,7 +1106,6 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str // Ignore unconfigured entries. if (rec->unconfigured) { - al_assert(!entry->ended); al_assert(AUDIO_EMPTY(entry) && VIDEO_EMPTY(entry)); nn_mutex_unlock(&sink->lock); return; @@ -1309,7 +1274,7 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str remove_from_queue_by_opaque(sink, entry); if (entry == sink->target) { - sink->target = OxbOOb; + sink->target = (struct camu_sink_entry *)0xb00b; log_warn("Attempting to handle a disconnected target."); } else if (entry == sink->current) { if (sink->suspended) { @@ -1322,10 +1287,7 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str } else { sink->current = NULL; if (removed) { // Don't stop video on exit. - queue_cmd(entry->sink, (struct camu_sink_cmd){ - .op = STOP, - .value.i = CAMU_SINK_VIDEO - }); + queue_cmd(sink, CMD(STOP, .v.u = CAMU_SINK_VIDEO)); } } // If current was never fully added we need to call this here. @@ -1401,9 +1363,12 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn, u8 op = nn_packet_read_u8(packet); if (op == LIANA_SINK_UNSET) { nn_mutex_lock(&sink->lock); - if (sink->current) { - maybe_disconnect_entry(sink->current); + struct camu_sink_entry *current = sink->current; + nn_mutex_unlock(&sink->lock); + if (current) { + maybe_disconnect_entry(current); } + nn_mutex_lock(&sink->lock); goto out; } @@ -1499,7 +1464,7 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn, case LIANA_PAUSE_PAUSE: if (prev_target) { al_assert(current); - al_assert(!current->ended); + al_assert(!ENTRY_ENDED(current)); al_assert(prev_target != entry); if (entry == current) { // pause_and_swap_to() negated. @@ -1507,7 +1472,7 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn, } else { sink->target = entry; } - if (prev_target != OxbOOb) { + if (prev_target != (struct camu_sink_entry *)0xb00b) { camu_clock_pause(&prev_target->clock, at); } } else { @@ -1518,7 +1483,7 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn, case LIANA_PAUSE_BOTH: if (prev_target) { al_assert(current); - al_assert(!current->ended); + al_assert(!ENTRY_ENDED(current)); al_assert(prev_target != entry); if (entry == current) { sink->target = NULL; @@ -1526,7 +1491,7 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn, } else { sink->target = entry; } - if (prev_target != OxbOOb) { + if (prev_target != (struct camu_sink_entry *)0xb00b) { camu_clock_pause(&prev_target->clock, at); } } else { @@ -1562,12 +1527,14 @@ static bool pause_command_callback(void *userdata, struct nn_rpc_connection *con struct camu_sink_entry *entry = get_entry_from_id(sink, id); if (!entry) goto out; - al_assert(entry->sequence == sequence); nn_mutex_lock(&sink->lock); - log_trace("pause("ENTRY_FMT"), %s, audio_state: %hhu, video_state: %hhu.", ENTRY_ARG(entry), lia_pause_op_name(pause), AUDIO_STATE(entry), VIDEO_STATE(entry)); + // As long as the list discards skips with a non-current sequence, this should hold true. + al_assert(entry->sequence == sequence); + log_trace("pause("ENTRY_FMT"), %s, audio_state: %hhu, video_state: %hhu.", + ENTRY_ARG(entry), lia_pause_op_name(pause), AUDIO_STATE(entry), VIDEO_STATE(entry)); #ifdef CAMU_SINK_LOCAL (void)at; - if (!entry->held) local_pause(sink, entry); + if (!entry->held) local_entry_pause(sink, entry); #else switch (pause) { case LIANA_PAUSE_PAUSE: { @@ -1582,18 +1549,12 @@ static bool pause_command_callback(void *userdata, struct nn_rpc_connection *con entry->paused = false; camu_clock_resume(&entry->clock, at); log_info("Clock resumed."); - if (!VIDEO_EMPTY(entry) && !VIDEO_IS_SINGLE_FRAME(entry)) { - queue_cmd(entry->sink, (struct camu_sink_cmd){ - .op = START, - .value.i = CAMU_SINK_VIDEO - }); + if (!VIDEO_ENDED_OR_EMPTY(entry) && !VIDEO_IS_SINGLE_FRAME(entry)) { + queue_cmd(sink, CMD(START, .v.u = CAMU_SINK_VIDEO)); } - if (!AUDIO_EMPTY(entry)) { + if (!AUDIO_ENDED_OR_EMPTY(entry)) { camu_audio_buffer_resync(&entry->audio.buf); - queue_cmd(entry->sink, (struct camu_sink_cmd){ - .op = START, - .value.i = CAMU_SINK_AUDIO - }); + queue_cmd(sink, CMD(START, .v.u = CAMU_SINK_AUDIO)); } break; } @@ -1615,7 +1576,6 @@ static bool seek_command_callback(void *userdata, struct nn_rpc_connection *conn u64 id = LOCAL_ENTRY_ID(sink, nn_packet_read_u32(packet)); s32 sequence = nn_packet_read_s32(packet); - (void)sequence; u64 at = nn_packet_read_u64(packet); u64 pos = nn_packet_read_u64(packet); u32 reset_token = nn_packet_read_u32(packet); @@ -1623,7 +1583,8 @@ static bool seek_command_callback(void *userdata, struct nn_rpc_connection *conn struct camu_sink_entry *entry = get_entry_from_id(sink, id); if (!entry) goto out; nn_mutex_lock(&sink->lock); - log_trace("seek("ENTRY_FMT"), reset_token: %u.", ENTRY_ARG(entry), reset_token); + entry->sequence = sequence; + log_trace("seek("ENTRY_FMT", %.2f), reset_token: %u.", ENTRY_ARG(entry), pos / 1000000.0, reset_token); entry->reset_token = reset_token; nn_mutex_unlock(&sink->lock); // The rest of the seek is handled in CLIENT_REMOVE_BUFFERS/RESUME_AT/RECONNECTED. @@ -1761,22 +1722,12 @@ void camu_sink_return_current(struct camu_sink *sink) nn_mutex_unlock(&sink->lock); } -// sink->current could be NULL. -static inline struct camu_sink_entry *get_entry_for_command(struct camu_sink *sink) -{ - return ENTRY_IS_VALID(sink->target) ? sink->target : sink->current; -} - void camu_sink_skip(struct camu_sink *sink, s32 n) { nn_mutex_lock(&sink->lock); struct camu_sink_entry *current = get_entry_for_command(sink); nn_mutex_unlock(&sink->lock); - queue_cmd(sink, (struct camu_sink_cmd){ - .op = SKIP, - .value.i = n, - .opaque = current - }); + queue_cmd(sink, CMD(SKIP, .v.i = n, .opaque = current)); } void camu_sink_toggle_pause(struct camu_sink *sink) @@ -1785,11 +1736,8 @@ void camu_sink_toggle_pause(struct camu_sink *sink) struct camu_sink_entry *current = get_entry_for_command(sink); nn_mutex_unlock(&sink->lock); if (!current) return; - queue_cmd(sink, (struct camu_sink_cmd){ - .op = TOGGLE_PAUSE, - .value.f = camu_clock_get_pts(¤t->clock, 0.0, false), - .opaque = current - }); + f64 pts = camu_clock_get_pts(¤t->clock, 0.0, false); + queue_cmd(sink, CMD(TOGGLE_PAUSE, .v.f = pts, .opaque = current)); } void camu_sink_seek(struct camu_sink *sink, void *value, u8 mode) @@ -1811,19 +1759,19 @@ void camu_sink_seek(struct camu_sink *sink, void *value, u8 mode) switch (mode) { case CAMU_SEEK_POS: { u64 pos = *(u64 *)value; - cmd.value.u = pos; + cmd.v.u = pos; break; } case CAMU_SEEK_RELATIVE: { f64 offset = *(f64 *)value; pts = MAX(pts + offset, 0.0); - cmd.value.u = (u64)(pts * 1000000); + cmd.v.u = (u64)(pts * 1000000); break; } case CAMU_SEEK_PERCENT: { // There's probably a way to lose less precision here. f64 percent = *(f64 *)value; - cmd.value.u = (u64)(duration * percent); + cmd.v.u = (u64)(duration * percent); break; } } @@ -1832,31 +1780,21 @@ void camu_sink_seek(struct camu_sink *sink, void *value, u8 mode) void camu_sink_reseek(struct camu_sink *sink) { - queue_cmd(sink, (struct camu_sink_cmd){ - .op = RESEEK - }); + queue_cmd(sink, CMD(RESEEK)); } void camu_sink_shuffle(struct camu_sink *sink) { - queue_cmd(sink, (struct camu_sink_cmd){ - .op = SHUFFLE - }); + queue_cmd(sink, CMD(SHUFFLE)); } void camu_sink_stop(struct camu_sink *sink) { - queue_cmd(sink, (struct camu_sink_cmd){ - .op = STOP, - .value.i = CAMU_SINK_AUDIO - }); - queue_cmd(sink, (struct camu_sink_cmd){ - .op = CLEAR_BUFFERS, - .value.i = CAMU_SINK_AUDIO - }); - queue_cmd(sink, (struct camu_sink_cmd){ - .op = CLOSE - }); + queue_cmds(sink, 3, + CMD(STOP, .v.u = CAMU_SINK_AUDIO), + CMD(CLEAR_BUFFERS, .v.u = CAMU_SINK_AUDIO), + CMD(CLOSE) + ); } void camu_sink_close(struct camu_sink *sink) diff --git a/src/libsink/sink.h b/src/libsink/sink.h index 5958829..1e8bbf1 100644 --- a/src/libsink/sink.h +++ b/src/libsink/sink.h @@ -56,7 +56,7 @@ struct camu_sink_entry { bool paused; struct { u8 state; - // Don't stop the audio output on BUFFER_PAUSED from this entry. + // Don't stop audio on a BUFFER_PAUSED from this entry. bool ignore_paused; struct camu_audio_buffer buf; struct lia_vcr_track *track; @@ -71,7 +71,7 @@ struct camu_sink_entry { struct camu_sink_cmd { u8 op; - union { s64 i; u64 u; f64 f; } value; + union { s64 i; u64 u; f64 f; } v; void *opaque; }; @@ -90,7 +90,7 @@ struct camu_sink { queue(struct camu_sink_cmd) queue; struct camu_sink_entry *current; struct camu_sink_entry *target; - // If an entry that was current was removed for a reconnect, it's "suspended". + // If current was removed for a reconnect, it's "suspended". struct camu_sink_entry *suspended; array(struct camu_sink_entry *) previous; array(struct camu_sink_entry *) entries; diff --git a/src/mixer/audio_miniaudio.c b/src/mixer/audio_miniaudio.c index 993bcca..c0e7cf4 100644 --- a/src/mixer/audio_miniaudio.c +++ b/src/mixer/audio_miniaudio.c @@ -34,19 +34,19 @@ static ma_backend backends[] = { #endif }; -static void *miniaudio_malloc(size_t size, void *userdata) +static void *ma_on_malloc(size_t size, void *userdata) { (void)userdata; return al_malloc(size); } -static void *miniaudio_realloc(void *ptr, size_t size, void *userdata) +static void *ma_on_realloc(void *ptr, size_t size, void *userdata) { (void)userdata; return al_realloc(ptr, size); } -static void miniaudio_free(void *ptr, void *userdata) +static void ma_on_free(void *ptr, void *userdata) { (void)userdata; al_free(ptr); @@ -54,12 +54,12 @@ static void miniaudio_free(void *ptr, void *userdata) static ma_allocation_callbacks alloc_callbacks = { .pUserData = NULL, - .onMalloc = miniaudio_malloc, - .onRealloc = miniaudio_realloc, - .onFree = miniaudio_free + .onMalloc = ma_on_malloc, + .onRealloc = ma_on_realloc, + .onFree = ma_on_free }; -static void miniaudio_log_callback(void *userdata, u32 level, const char *message) +static void log_callback(void *userdata, u32 level, const char *message) { (void)userdata; if (level == MA_LOG_LEVEL_ERROR) { @@ -71,14 +71,14 @@ static void miniaudio_log_callback(void *userdata, u32 level, const char *messag } } -static bool audio_miniaudio_init(struct camu_audio *audio, str *name) +static bool audio_ma_init(struct camu_audio *audio, str *name) { - struct camu_audio_miniaudio *ma = (struct camu_audio_miniaudio *)audio; + struct camu_audio_ma *ma = (struct camu_audio_ma *)audio; #ifdef CAMU_NO_MINIAUDIO_BACKENDS al_assert_and_return(false); #endif ma_log_init(&alloc_callbacks, &ma->log); - ma_log_register_callback(&ma->log, ma_log_callback_init(miniaudio_log_callback, NULL)); + ma_log_register_callback(&ma->log, ma_log_callback_init(log_callback, NULL)); ma->context_config = ma_context_config_init(); ma->context_config.pLog = &ma->log; #ifdef MA_ENABLE_PULSEAUDIO @@ -140,9 +140,9 @@ static s32 camu_sample_format_from_miniaudio(s32 fmt) } } -static void audio_miniaudio_pick_format(struct camu_audio *audio, struct camu_resampler_format *fmt) +static void audio_ma_pick_format(struct camu_audio *audio, struct camu_resampler_format *fmt) { - struct camu_audio_miniaudio *ma = (struct camu_audio_miniaudio *)audio; + struct camu_audio_ma *ma = (struct camu_audio_ma *)audio; fmt->req.format = camu_sample_format_from_miniaudio(ma->device.playback.format); fmt->req.sample_rate = ma->device.playback.internalSampleRate; u32 channels = ma->device.playback.channels; @@ -154,7 +154,7 @@ static void audio_miniaudio_pick_format(struct camu_audio *audio, struct camu_re static void data_callback(ma_device *device, void *output, const void *input, u32 frame_count) { - struct camu_audio_miniaudio *ma = (struct camu_audio_miniaudio *)device->pUserData; + struct camu_audio_ma *ma = (struct camu_audio_ma *)device->pUserData; (void)input; bool silence; ma->a.data_callback(ma->a.userdata, (u8 *)output, frame_count, &silence); @@ -170,9 +170,9 @@ static void data_callback(ma_device *device, void *output, const void *input, u3 #define DEFAULT_PERIOD_SIZE_IN_MILLISECONDS 48 #endif -static bool audio_miniaudio_configure_stream(struct camu_audio *audio, void *opaque) +static bool audio_ma_configure_stream(struct camu_audio *audio, void *opaque) { - struct camu_audio_miniaudio *ma = (struct camu_audio_miniaudio *)audio; + struct camu_audio_ma *ma = (struct camu_audio_ma *)audio; struct camu_ff_resample_fmt *fmt = (struct camu_ff_resample_fmt *)opaque; (void)fmt; ma->config = ma_device_config_init(ma_device_type_playback); @@ -225,9 +225,9 @@ static bool audio_miniaudio_configure_stream(struct camu_audio *audio, void *opa return true; } -static u64 audio_miniaudio_get_latency(struct camu_audio *audio) +static u64 audio_ma_get_latency(struct camu_audio *audio) { - struct camu_audio_miniaudio *ma = (struct camu_audio_miniaudio *)audio; + struct camu_audio_ma *ma = (struct camu_audio_ma *)audio; log_debug("periods: %u, period_size_in_frames: %u, sample_rate: %u.", ma->device.playback.internalPeriods, ma->device.playback.internalPeriodSizeInFrames, @@ -237,21 +237,21 @@ static u64 audio_miniaudio_get_latency(struct camu_audio *audio) (ma->device.playback.internalSampleRate / 1000000.0); } -static void audio_miniaudio_start(struct camu_audio *audio) +static void audio_ma_start(struct camu_audio *audio) { - struct camu_audio_miniaudio *ma = (struct camu_audio_miniaudio *)audio; + struct camu_audio_ma *ma = (struct camu_audio_ma *)audio; ma_device_start(&ma->device); } -static void audio_miniaudio_stop(struct camu_audio *audio) +static void audio_ma_stop(struct camu_audio *audio) { - struct camu_audio_miniaudio *ma = (struct camu_audio_miniaudio *)audio; + struct camu_audio_ma *ma = (struct camu_audio_ma *)audio; ma_device_stop(&ma->device); } -static void audio_miniaudio_free(struct camu_audio **audio) +static void audio_ma_free(struct camu_audio **audio) { - struct camu_audio_miniaudio *ma = (struct camu_audio_miniaudio *)*audio; + struct camu_audio_ma *ma = (struct camu_audio_ma *)*audio; ma_device_uninit(&ma->device); ma_context_uninit(&ma->context); #ifdef MA_ENABLE_PULSEAUDIO @@ -263,15 +263,15 @@ static void audio_miniaudio_free(struct camu_audio **audio) struct camu_audio *camu_audio_miniaudio_create(void) { - struct camu_audio_miniaudio *ma = al_alloc_object(struct camu_audio_miniaudio); + struct camu_audio_ma *ma = al_alloc_object(struct camu_audio_ma); ma->a = (struct camu_audio){ - .init = audio_miniaudio_init, - .pick_format = audio_miniaudio_pick_format, - .configure_stream = audio_miniaudio_configure_stream, - .get_latency = audio_miniaudio_get_latency, - .start = audio_miniaudio_start, - .stop = audio_miniaudio_stop, - .free = audio_miniaudio_free, + .init = audio_ma_init, + .pick_format = audio_ma_pick_format, + .configure_stream = audio_ma_configure_stream, + .get_latency = audio_ma_get_latency, + .start = audio_ma_start, + .stop = audio_ma_stop, + .free = audio_ma_free, .data_callback = NULL, .userdata = NULL }; diff --git a/src/mixer/audio_miniaudio.h b/src/mixer/audio_miniaudio.h index c067024..47e1088 100644 --- a/src/mixer/audio_miniaudio.h +++ b/src/mixer/audio_miniaudio.h @@ -9,7 +9,7 @@ #include "audio.h" -struct camu_audio_miniaudio { +struct camu_audio_ma { struct camu_audio a; ma_log log; ma_context_config context_config; diff --git a/src/portal/src/search.c b/src/portal/src/search.c index a1b7310..3f012bf 100644 --- a/src/portal/src/search.c +++ b/src/portal/src/search.c @@ -75,6 +75,7 @@ static nn_thread_result NNWT_THREADCALL queue_thread(void *userdata) // Defer python init. if (!(have_python = camu_python_init())) { log_error("Failed to initialize python."); + bridge->quit = 1; break; } } @@ -125,13 +126,15 @@ static nn_thread_result NNWT_THREADCALL queue_thread(void *userdata) } } } else { - log_info("Requested search doesn't exist (id: %d).", search->id); result.id = -1; + log_info("Requested search doesn't exist (id: %d).", cmd->id); } break; } } + camu_queue_lock(bridge->results); camu_queue_push(bridge->results, result); + camu_queue_unlock(bridge->results); } bridge->queue.count = 0; nn_signal_send(&bridge->results_signal); @@ -145,7 +148,9 @@ static nn_thread_result NNWT_THREADCALL queue_thread(void *userdata) result.callback = cmd->callback; result.userdata = cmd->userdata; result.id = -1; + camu_queue_lock(bridge->results); camu_queue_push(bridge->results, result); + camu_queue_unlock(bridge->results); } bridge->queue.count = 0; nn_signal_send(&bridge->results_signal); @@ -187,13 +192,18 @@ void camu_portal_init(struct camu_portal_bridge *bridge, struct camu_post_cache void camu_portal_create_search(struct camu_portal_bridge *bridge, str *module, str *query, void (*callback)(void *, void *, struct camu_portal_result *), void *userdata) { + nn_mutex_lock(&bridge->mutex); + if (bridge->quit) { + callback(bridge->userdata, userdata, NULL); + nn_mutex_unlock(&bridge->mutex); + return; + } struct camu_portal_cmd cmd = { 0 }; cmd.op = CAMU_CLIENT_CREATE_SEARCH; al_str_clone(&cmd.module, module); al_str_clone(&cmd.query, query); cmd.callback = callback; cmd.userdata = userdata; - nn_mutex_lock(&bridge->mutex); al_array_push(bridge->queue, cmd); if (nn_cond_is_waiting(&bridge->cond)) { nn_cond_signal(&bridge->cond); @@ -204,13 +214,18 @@ void camu_portal_create_search(struct camu_portal_bridge *bridge, str *module, s void camu_portal_get_page(struct camu_portal_bridge *bridge, s32 id, u32 num, void (*callback)(void *, void *, struct camu_portal_result *), void *userdata) { + nn_mutex_lock(&bridge->mutex); + if (bridge->quit) { + callback(bridge->userdata, userdata, NULL); + nn_mutex_unlock(&bridge->mutex); + return; + } struct camu_portal_cmd cmd = { 0 }; cmd.op = CAMU_CLIENT_GET_PAGE; cmd.id = id; cmd.num = num; cmd.callback = callback; cmd.userdata = userdata; - nn_mutex_lock(&bridge->mutex); al_array_push(bridge->queue, cmd); if (nn_cond_is_waiting(&bridge->cond)) { nn_cond_signal(&bridge->cond); diff --git a/src/render/meson.build b/src/render/meson.build index 157cd63..a7ccb58 100644 --- a/src/render/meson.build +++ b/src/render/meson.build @@ -9,11 +9,9 @@ if get_option('renderer') == 'momo' render_args += ['-DCAMU_RENDERER_MOMO'] elif get_option('renderer') == 'libplacebo' libplacebo_found = false - if 'libplacebo' not in get_option('force_fallback_for') + if 'libplacebo' not in get_option('force_fallback_for') and get_option('wrap_mode') != 'forcefallback' libplacebo = dependency('libplacebo', required: false, allow_fallback: false) - if libplacebo.found() - libplacebo_found = true - endif + libplacebo_found = libplacebo.found() endif if not libplacebo_found libplacebo_opts = ['warning_level=2', 'demos=false', 'tests=false', 'bench=false', 'fuzz=false', @@ -28,7 +26,7 @@ elif get_option('renderer') == 'libplacebo' if get_option('renderer-api') == 'vulkan' or get_option('renderer-api') == 'dx11' if get_option('renderer-compiler') == 'shaderc' shaderc_found = false - if 'shaderc' not in get_option('force_fallback_for') + if 'shaderc' not in get_option('force_fallback_for') and get_option('wrap_mode') != 'forcefallback' if is_windows shaderc = dependency('shaderc_combined', required: false, allow_fallback: false) else @@ -47,11 +45,14 @@ elif get_option('renderer') == 'libplacebo' shaderc_opts.add_cmake_defines({ 'SHADERC_SKIP_TESTS': true }) shaderc_opts.add_cmake_defines({ 'SHADERC_SKIP_EXAMPLES': true }) shaderc_opts.add_cmake_defines({ 'SHADERC_SKIP_COPYRIGHT_CHECK': true }) + # glslang shaderc_opts.add_cmake_defines({ 'ENABLE_GLSLANG_BINARIES': false }) - shaderc_opts.add_cmake_defines({ 'ENABLE_SPVREMAPPER': false }) shaderc_opts.add_cmake_defines({ 'ENABLE_HLSL': false }) + shaderc_opts.add_cmake_defines({ 'ENABLE_SPVREMAPPER': false }) + shaderc_opts.add_cmake_defines({ 'ENABLE_EXCEPTIONS': false }) spirv_optimizer = true shaderc_opts.add_cmake_defines({ 'ENABLE_OPT': spirv_optimizer }) + # spirv-tools shaderc_opts.add_cmake_defines({ 'ENABLE_EXCEPTIONS_ON_MSVC': false }) shaderc_proj = cmake.subproject('shaderc', options: shaderc_opts) shaderc = shaderc_proj.dependency('shaderc') @@ -66,9 +67,7 @@ elif get_option('renderer') == 'libplacebo' shaderc_proj.dependency('SPIRV-Tools-static') ] if spirv_optimizer - render_deps += [ - shaderc_proj.dependency('SPIRV-Tools-opt'), - ] + render_deps += [shaderc_proj.dependency('SPIRV-Tools-opt')] endif endif libplacebo_opts += ['shaderc=enabled', 'glslang=disabled'] @@ -77,19 +76,36 @@ elif get_option('renderer') == 'libplacebo' if glslang.found() render_deps += [glslang] else + # Currently does not build. + prefer_static = get_option('prefer_static') + spirv_optimizer = false + if spirv_optimizer + spirv_headers_proj = cmake.subproject('SPIRV-Headers') + spirv_headers = spirv_headers_proj.dependency('SPIRV-Headers') + spirv_tools_opts = cmake.subproject_options() + spirv_tools_opts.add_cmake_defines({ 'SPIRV-Headers_SOURCE_DIR': meson.source_root() + '/subprojects/SPIRV-Headers-b824a46' }) + spirv_tools_opts.add_cmake_defines({ 'ENABLE_EXCEPTIONS_ON_MSVC': false }) + spirv_tools_proj = cmake.subproject('SPIRV-Tools', options: spirv_tools_opts) + spirv_tools = spirv_tools_proj.dependency('SPIRV-Tools-static') + spirv_tools_opt = spirv_tools_proj.dependency('SPIRV-Tools-opt') + endif glslang_opts = cmake.subproject_options() glslang_opts.set_override_option('warning_level', '0') reltype = is_minsize ? 'MinSizeRel' : 'Release' glslang_opts.add_cmake_defines({ 'CMAKE_BUILD_TYPE': is_debug ? 'Debug' : reltype }) - spirv_optimizer = false + glslang_opts.add_cmake_defines({ 'ENABLE_GLSLANG_BINARIES': false }) + glslang_opts.add_cmake_defines({ 'ENABLE_HLSL': false }) + glslang_opts.add_cmake_defines({ 'ENABLE_SPVREMAPPER': false }) + glslang_opts.add_cmake_defines({ 'ENABLE_EXCEPTIONS': false }) glslang_opts.add_cmake_defines({ 'ENABLE_OPT': spirv_optimizer }) + glslang_opts.add_cmake_defines({ 'ALLOW_EXTERNAL_SPIRV_TOOLS': spirv_optimizer }) glslang_proj = cmake.subproject('glslang', options: glslang_opts) spirv = glslang_proj.dependency('SPIRV') glslang = glslang_proj.dependency('glslang') glslang_default_resource_limits = glslang_proj.dependency('glslang-default-resource-limits') - meson.override_dependency('SPIRV', spirv) - meson.override_dependency('glslang', glslang) - meson.override_dependency('glslang-default-resource-limits', glslang_default_resource_limits) + meson.override_dependency('SPIRV', spirv, static: prefer_static) + meson.override_dependency('glslang', glslang, static: prefer_static) + meson.override_dependency('glslang-default-resource-limits', glslang_default_resource_limits, static: prefer_static) render_deps += [ glslang_proj.dependency('MachineIndependent'), glslang_proj.dependency('OSDependent'), @@ -98,6 +114,13 @@ elif get_option('renderer') == 'libplacebo' glslang, glslang_default_resource_limits ] + if spirv_optimizer + render_deps += [ + spirv_tools, + spirv_headers, + spirv_tools_opt + ] + endif endif libplacebo_opts += ['glslang=enabled', 'shaderc=disabled'] endif @@ -140,11 +163,9 @@ endif if get_option('subtitles').enabled() libass_found = false - if 'libass' not in get_option('force_fallback_for') + if 'libass' not in get_option('force_fallback_for') and get_option('wrap_mode') != 'forcefallback' libass = dependency('libass', required: false, allow_fallback: false) - if libass.found() - libass_found = true - endif + libass_found = libass.found() endif if not libass_found libass_opts = ['test=disabled'] @@ -191,6 +212,7 @@ if get_option('subtitles').enabled() 'gobject=disabled', 'cairo=disabled', 'chafa=disabled', + 'icu=disabled', 'freetype=disabled', # This does not override yield, and yielding breaks the build if tests=enabled. # https://github.com/mesonbuild/meson/issues/5214 diff --git a/src/render/queue_libplacebo.c b/src/render/queue_libplacebo.c index c78562c..e3540c3 100644 --- a/src/render/queue_libplacebo.c +++ b/src/render/queue_libplacebo.c @@ -164,12 +164,12 @@ static struct camu_overlay *first_nonref_overlay(struct camu_frame_queue_lp *lq, static struct camu_overlay *create_subtitle_overlay(struct camu_overlay *prev, pl_gpu gpu, ASS_Image *ass_frame) { - u32 frame_count = 1; + u16 frame_count = 1; ASS_Image *head_ass_frame = ass_frame; - while (ass_frame->next) { frame_count++; ass_frame = ass_frame->next; } + while (ass_frame->next) { al_assert(frame_count < UINT16_MAX); frame_count++; ass_frame = ass_frame->next; } ass_frame = head_ass_frame; - u32 prev_alloc = prev ? prev->alloc : 0; + u16 prev_alloc = prev ? prev->alloc : 0; struct camu_overlay *overlay = camu_overlay_alloc(prev, frame_count, sizeof(struct pl_overlay) + sizeof(struct pl_overlay_part)); if (frame_count > prev_alloc) { @@ -225,9 +225,9 @@ static struct camu_overlay *create_subtitle_overlay(struct camu_overlay *prev, p }; u32 c = ass_frame->color; current_part->color[0] = (c >> 24) / 255.0; - current_part->color[1] = ((c >> 16) & 0xFF) / 255.0; - current_part->color[2] = ((c >> 8) & 0xFF) / 255.0; - current_part->color[3] = 1.0 - (c & 0xFF) / 255.0; + current_part->color[1] = ((c >> 16) & 0xff) / 255.0; + current_part->color[2] = ((c >> 8) & 0xff) / 255.0; + current_part->color[3] = 1.0 - (c & 0xff) / 255.0; current->parts = current_part; current->num_parts = 1; @@ -317,8 +317,9 @@ static bool map_av_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame * if (overlay) { overlay->ref++; out_frame->overlays = CAMU_OVERLAY_OFFSET(overlay, 0); - out_frame->num_overlays = overlay->num; ((struct pl_source_frame *)src)->frame_data = overlay; + } else { + out_frame->overlays = NULL; } } nn_mutex_unlock(&lq->subtitle_lock); diff --git a/src/render/renderer.h b/src/render/renderer.h index 3331baa..0eae5cb 100644 --- a/src/render/renderer.h +++ b/src/render/renderer.h @@ -30,12 +30,13 @@ enum { struct camu_overlay { u16 ref; - u32 alloc; + u16 alloc; s32 num; struct nn_mutex *lock; }; #define CAMU_OVERLAY_OFFSET(overlay, n) ((void *)(((u8 *)(overlay)) + sizeof(struct camu_overlay) + (n))) +#define CAMU_OVERLAY_FROM_HEAD(p) (struct camu_overlay *)(((u8 *)p) - sizeof(struct camu_overlay)) struct camu_frame_queue; struct camu_codec_stream; @@ -67,7 +68,7 @@ struct camu_renderer { u32 (*get_latency)(struct camu_renderer *); void (*resize)(struct camu_renderer *, u32 *, u32 *); void (*set)(struct camu_renderer *, u8, u8); - void (*render)(struct camu_renderer *, struct camu_screen *, bool); + bool (*render)(struct camu_renderer *, struct camu_screen *, bool); #ifdef CAMU_HAVE_FFMPEG s32 (*get_buffer2)(struct AVCodecContext *s, AVFrame *frame, s32 flags); void *opaque; diff --git a/src/render/renderer_libplacebo.c b/src/render/renderer_libplacebo.c index 3b69c64..01aa764 100644 --- a/src/render/renderer_libplacebo.c +++ b/src/render/renderer_libplacebo.c @@ -13,7 +13,8 @@ #include "queue_libplacebo.h" #ifdef AL_DEBUG -#define RENDERER_DEBUG 1 +// vkQueueSubmit2: VK_ERROR_VALIDATION_FAILED_EXT leads to an annoying deadlock. +#define RENDERER_DEBUG 0 #else #define RENDERER_DEBUG 0 #endif @@ -37,8 +38,11 @@ static void renderer_lp_resize(struct camu_renderer *renderer, u32 *width, u32 * struct camu_renderer_lp *lr = (struct camu_renderer_lp *)renderer; if (lr->swapchain) { if (lr->have_frame) { - pl_swapchain_submit_frame(lr->swapchain); - lr->have_frame = false; + if (pl_swapchain_submit_frame(lr->swapchain)) { + lr->have_frame = false; + } else { + log_error("Failed to submit frame on resize."); + } } pl_swapchain_resize_compat(lr->swapchain, width, height); } @@ -365,7 +369,9 @@ static inline intptr_t float_64_hash(f64 value) return hash; } -static void renderer_lp_render(struct camu_renderer *renderer, struct camu_screen *scr, bool force) + + +static bool renderer_lp_render(struct camu_renderer *renderer, struct camu_screen *scr, bool force) { struct camu_renderer_lp *lr = (struct camu_renderer_lp *)renderer; @@ -377,15 +383,14 @@ static void renderer_lp_render(struct camu_renderer *renderer, struct camu_scree if (!lr->have_frame) { struct pl_swapchain_frame frame; - if (!pl_swapchain_start_frame(lr->swapchain, &frame)) { - if (force) { - pl_swapchain_swap_buffers(lr->swapchain); - } - return; + if (pl_swapchain_start_frame(lr->swapchain, &frame)) { + pl_frame_from_swapchain(&lr->target, &frame); + pl_frame_clear_rgba(lr->gpu, &lr->target, clear_color); + lr->have_frame = true; + } else { + log_error("Failed to start frame, fatal."); + return false; } - pl_frame_from_swapchain(&lr->target, &frame); - pl_frame_clear_rgba(lr->gpu, &lr->target, clear_color); - lr->have_frame = true; } f64 mouse_x, mouse_y; @@ -405,11 +410,14 @@ static void renderer_lp_render(struct camu_renderer *renderer, struct camu_scree lr->params.hooks[0]->parameters[1].data->f = (f32)mouse_y; } + enum { + RESULT_SUBMIT = 1, + RESULT_SUBMIT_AND_WAIT = 1 << 1 + }; + + u8 result = 0; struct pl_frame *target = &lr->target; struct pl_frame_mix mix; - - bool do_gpu_finish = false; - struct camu_screen_video *video; while (scr->videos.count > 0) { bool any_eof = false; @@ -419,7 +427,7 @@ static void renderer_lp_render(struct camu_renderer *renderer, struct camu_scree // If mix.frames is NULL, read() returned QUEUE_MORE. if (mix.frames) { // weighted is only set when read() returns QUEUE_OK. - do_gpu_finish |= weighted; + result |= (1 | 1 << weighted); // Terrible hack. Lets us distinguish single frames with the same dimensions. // Tied to a libplacebo patch to consider info_priv in the hash. intptr_t hash = (intptr_t)video->buf; @@ -431,15 +439,22 @@ static void renderer_lp_render(struct camu_renderer *renderer, struct camu_scree hash += float_64_hash(video->view.fov); lr->params.hooks[0]->parameters[2].data->f = (f32)video->view.fov; } - lr->params.info_priv = (void *)hash; - target->crop = mix.frames[0]->crop; - if (!scr->subtitles_enabled) { - // This basically discards the subtitles of each frame before it's rendered. - // So, toggling the subtitles of a single frame (paused) won't work. - for (s32 j = 0; j < mix.num_frames; j++) { - ((struct pl_frame *)mix.frames[j])->num_overlays = 0; +#ifdef CAMU_HAVE_SUBTITLES + for (u32 j = 0; j < (u32)mix.num_frames; j++) { + struct pl_frame *mix_frame = (struct pl_frame *)mix.frames[j]; + if (!mix_frame->overlays) continue; + struct camu_overlay *overlay = CAMU_OVERLAY_FROM_HEAD(mix_frame->overlays); + if (!overlay->num) continue; + if (scr->subtitles_enabled) { + mix_frame->num_overlays = overlay->num; + hash += overlay->num; + } else { + mix_frame->num_overlays = 0; } } +#endif + lr->params.info_priv = (void *)hash; + target->crop = mix.frames[0]->crop; target->crop.x1 *= video->view.zoom / video->view.stretch; target->crop.y1 *= video->view.zoom * video->view.stretch; target->crop.x0 += video->view.x_offset; @@ -447,7 +462,14 @@ static void renderer_lp_render(struct camu_renderer *renderer, struct camu_scree target->crop.x1 += video->view.x_offset; target->crop.y1 += video->view.y_offset; target->rotation = video->view.rotation; - //lr->params.color_adjustment = pl_color_adjustment(.saturation = 0.0); + /* + //lr->params.color_map_params = pl_color_map_params(.inverse_tone_mapping = true); + if () { + target->color = pl_color_space_srgb; + target->color.hdr.max_luma = PL_COLOR_SDR_WHITE - 50.f; + target->color.hdr.min_luma = (PL_COLOR_SDR_WHITE - 50.f) / PL_COLOR_SDR_CONTRAST; + } + */ pl_render_image_mix(lr->renderer, &mix, target, &lr->params); } } else { @@ -468,32 +490,30 @@ static void renderer_lp_render(struct camu_renderer *renderer, struct camu_scree lr->params.hooks = NULL; lr->params.num_hooks = 0; - if (!scr->videos.count && !force) { - // Don't spin too hard on a potential error state. + // Considerations about the result of the loop above. + // 1. Any given call to video_buffer_read() may not produce a frame. + // 2. If a buffer signals EOF, by the time run_queue() happens at the bottom of the loop all + // previous buffers could have been queued for removal. Meaning, at this point, videos.count + // would be 0 while there could be data we want to display rendered to the frame. + if (!(result & RESULT_SUBMIT) && !force) { nn_thread_sleep(NNWT_TS_FROM_USEC(192)); - return; + return true; } - if (!pl_swapchain_submit_frame(lr->swapchain)) { + if (pl_swapchain_submit_frame(lr->swapchain)) { + lr->have_frame = false; + } else { log_error("Failed to submit frame, possibly fatal."); + return false; } - lr->have_frame = false; pl_swapchain_swap_buffers(lr->swapchain); - if (do_gpu_finish) { - // Block until render completes. + if (result & RESULT_SUBMIT_AND_WAIT) { pl_gpu_finish(lr->gpu); } -#if 0 - f64 tick = nn_get_tick(); - f64 frame_time = tick - lr->last_render_tick; - if (frame_time > (1.0 / 24.0)) { // 24fps is totally arbitrary. - log_info("FRAME_TIME: %fs (%.2ffps)", frame_time, 1.0 / frame_time); - } - lr->last_render_tick = tick; -#endif + return true; } void renderer_lp_free(struct camu_renderer **renderer) @@ -521,7 +541,7 @@ void renderer_lp_free(struct camu_renderer **renderer) *renderer = NULL; } -struct camu_renderer *camu_renderer_lp_create(void) +struct camu_renderer *camu_renderer_libplacebo_create(void) { struct camu_renderer_lp *lr = al_alloc_object(struct camu_renderer_lp); lr->r.create_renderer = renderer_lp_create_renderer; diff --git a/src/render/renderer_libplacebo.h b/src/render/renderer_libplacebo.h index 5d5cf9b..727e0b3 100644 --- a/src/render/renderer_libplacebo.h +++ b/src/render/renderer_libplacebo.h @@ -42,4 +42,4 @@ struct camu_renderer_lp { #endif }; -struct camu_renderer *camu_renderer_lp_create(void); +struct camu_renderer *camu_renderer_libplacebo_create(void); diff --git a/src/render/renderer_momo.c b/src/render/renderer_momo.c index 3a0e7ed..02ccaee 100644 --- a/src/render/renderer_momo.c +++ b/src/render/renderer_momo.c @@ -85,12 +85,13 @@ static u32 renderer_momo_get_latency(struct camu_renderer *renderer) return 0; } -static void renderer_momo_render(struct camu_renderer *renderer, struct camu_screen *scr, bool force) +static bool renderer_momo_render(struct camu_renderer *renderer, struct camu_screen *scr, bool force) { struct camu_renderer_momo *mr = (struct camu_renderer_momo *)renderer; (void)scr; (void)force; MOMO_Render(&mr->mmr); + return true; } static void renderer_momo_free(struct camu_renderer **renderer) diff --git a/src/screen/screen.c b/src/screen/screen.c index 844076e..f6833dd 100644 --- a/src/screen/screen.c +++ b/src/screen/screen.c @@ -259,8 +259,8 @@ static bool key_callback(void *userdata, u8 state, u16 button) break; case STELA_KEY_RIGHT: if (SCREEN_MOD1(scr)) { - f64 plus_five_seconds = 5.0; - scr->callback(scr->userdata, CAMU_SCREEN_RELATIVE_SEEK, &plus_five_seconds); + f64 plus_ten_seconds = 10.0; + scr->callback(scr->userdata, CAMU_SCREEN_RELATIVE_SEEK, &plus_ten_seconds); break; } // fallthrough @@ -276,8 +276,8 @@ static bool key_callback(void *userdata, u8 state, u16 button) f64 zero = 0.0; scr->callback(scr->userdata, CAMU_SCREEN_PERCENT_SEEK, &zero); } else { - f64 minus_five_seconds = -5.0; - scr->callback(scr->userdata, CAMU_SCREEN_RELATIVE_SEEK, &minus_five_seconds); + f64 minus_ten_seconds = -10.0; + scr->callback(scr->userdata, CAMU_SCREEN_RELATIVE_SEEK, &minus_ten_seconds); } break; } @@ -294,9 +294,11 @@ static bool key_callback(void *userdata, u8 state, u16 button) if (SCREEN_MOD1(scr)) { scr->callback(scr->userdata, CAMU_SCREEN_SHUFFLE, NULL); } else { +#ifdef CAMU_HAVE_SUBTITLES scr->subtitles_enabled = !scr->subtitles_enabled; log_info(scr->subtitles_enabled ? "Subtitles enabled." : "Subtitles disabled."); return true; +#endif } break; case STELA_KEY_E: @@ -484,7 +486,9 @@ bool camu_screen_init(struct camu_screen *scr) scr->flags = CAMU_SCREEN_ZOOM_PAN_SIMPLE; scr->scaling_disabled = false; scr->transparent_background = false; +#ifdef CAMU_HAVE_SUBTITLES scr->subtitles_enabled = true; +#endif scr->last_click_ts = SCREEN_INVALID_TS; scr->last_pointer_x = 0.0; scr->last_pointer_y = 0.0; diff --git a/src/server/server.c b/src/server/server.c index ae77ef0..59a0f7a 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -590,28 +590,31 @@ out: #ifdef CAMU_HAVE_PORTAL static void simple_search_portal_callback(void *userdata0, void *userdata1, struct camu_portal_result *result) { - struct camu_server *server = (struct camu_server *)userdata0; struct camu_resource *resource = (struct camu_resource *)userdata1; - switch (result->op) { - case CAMU_CLIENT_CREATE_SEARCH: - if (result->id == -1) { - break; - } - camu_portal_get_page(&server->bridge, result->id, 0, simple_search_portal_callback, resource); - return; - case CAMU_CLIENT_GET_PAGE: - if (result->id == -1 || !result->page || !result->page->list.count) { - break; + if (result) { + struct camu_server *server = (struct camu_server *)userdata0; + switch (result->op) { + case CAMU_CLIENT_CREATE_SEARCH: + if (result->id == -1) { + break; + } + camu_portal_get_page(&server->bridge, result->id, 0, simple_search_portal_callback, resource); + return; + case CAMU_CLIENT_GET_PAGE: + if (result->id == -1 || !result->page || !result->page->list.count) { + break; + } + resource->post = camu_post_cache_get(&server->cache, &al_array_at(result->page->list, 0)); + resource->type = CAMU_RESOURCE_PORTAL; + prepare_server_resource(server, resource); + process_pending(resource); + return; } - resource->post = camu_post_cache_get(&server->cache, &al_array_at(result->page->list, 0)); - resource->type = CAMU_RESOURCE_PORTAL; - prepare_server_resource(server, resource); - process_pending(resource); - return; } // No return is the error case. log_warn("Failed to process search request."); resource->load = LIANA_ENTRY_ERRORED; + process_pending(resource); } #endif @@ -684,6 +687,7 @@ static void handle_add_command(struct camu_server *server, struct lia_list *list al_str_from(&query, "link:"); al_str_cat(&query, &line); } + log_info("Processing search request: %.*s.", al_str_x(&query)); camu_portal_create_search(&server->bridge, &al_str_c("youtube"), &query, simple_search_portal_callback, resource); al_str_free(&query); diff --git a/src/util/queue.h b/src/util/queue.h index 17880e0..08b0a30 100644 --- a/src/util/queue.h +++ b/src/util/queue.h @@ -19,13 +19,6 @@ nn_mutex_unlock(&(q).mutex); \ } while (0) -#define camu_queue_count(q, r) \ - do { \ - nn_mutex_lock(&(q).mutex); \ - r = (q).a.count; \ - nn_mutex_unlock(&(q).mutex); \ - } while (0) - #define camu_queue_init(q) \ do { \ al_array_init((q).a); \ @@ -34,16 +27,7 @@ #define camu_queue_push(q, item) \ do { \ - nn_mutex_lock(&(q).mutex); \ al_array_push((q).a, item); \ - nn_mutex_unlock(&(q).mutex); \ - } while (0) - -#define camu_queue_pop(q, r) \ - do { \ - nn_mutex_lock(&(q).mutex); \ - al_array_pop_at((q).a, 0, r); \ - nn_mutex_unlock(&(q).mutex); \ } while (0) #define camu_queue_try_pop(q, s, r) \ diff --git a/subprojects/SPIRV-Cross.wrap b/subprojects/SPIRV-Cross.wrap index d522ec1..867f649 100644 --- a/subprojects/SPIRV-Cross.wrap +++ b/subprojects/SPIRV-Cross.wrap @@ -1,7 +1,7 @@ [wrap-git] -directory = SPIRV-Cross-adec7ac +directory = SPIRV-Cross-4f77a07 url = https://github.com/KhronosGroup/SPIRV-Cross.git -revision = adec7acbf41a988713cdb85f93f26c8ca5ea863e +revision = 4f77a07af0dcadd0f59d6eb78dc879abdd1d7400 depth = 1 method = cmake patch_directory = SPIRV-Cross diff --git a/subprojects/SPIRV-Headers.wrap b/subprojects/SPIRV-Headers.wrap new file mode 100644 index 0000000..06b420c --- /dev/null +++ b/subprojects/SPIRV-Headers.wrap @@ -0,0 +1,9 @@ +[wrap-git] +directory = SPIRV-Headers-b824a46 +url = https://github.com/KhronosGroup/SPIRV-Headers.git +revision = b824a462d4256d720bebb40e78b9eb8f78bbb305 +depth = 1 +method = cmake + +[provide] +dependency_names = SPIRV-Headers diff --git a/subprojects/dav1d.wrap b/subprojects/dav1d.wrap new file mode 100644 index 0000000..ed2d7a0 --- /dev/null +++ b/subprojects/dav1d.wrap @@ -0,0 +1,8 @@ +[wrap-git] +directory = dav1d-1.5.2 +url = https://code.videolan.org/videolan/dav1d.git +revision = 1.5.2 +depth = 1 + +[provide] +dav1d = dav1d_dep diff --git a/subprojects/ffmpeg3.wrap b/subprojects/ffmpeg3.wrap index 71d9908..64a316d 100644 --- a/subprojects/ffmpeg3.wrap +++ b/subprojects/ffmpeg3.wrap @@ -1,13 +1,13 @@ [wrap-file] -directory = ffmpeg-3.4.14 -#directory = ffmpeg-3.3.9 -source_url = https://ffmpeg.org/releases/ffmpeg-3.4.14.tar.gz -#source_url = https://ffmpeg.org/releases/ffmpeg-3.3.9.tar.gz -source_filename = ffmpeg-3.4.14.tar.gz -#source_filename = ffmpeg-3.3.9.tar.gz -source_hash = 61e255e824341e62fbc83365d84eb754fa558f53985d28ad610d13aeee895d5a -#source_hash = f79ef311f55303dbc692014d609ec44a609adcea6ded5f5507b6e220068521a3 +#directory = ffmpeg-3.4.14 +directory = ffmpeg-3.3.9 +#source_url = https://ffmpeg.org/releases/ffmpeg-3.4.14.tar.gz +source_url = https://ffmpeg.org/releases/ffmpeg-3.3.9.tar.gz +#source_filename = ffmpeg-3.4.14.tar.gz +source_filename = ffmpeg-3.3.9.tar.gz +#source_hash = 61e255e824341e62fbc83365d84eb754fa558f53985d28ad610d13aeee895d5a +source_hash = f79ef311f55303dbc692014d609ec44a609adcea6ded5f5507b6e220068521a3 patch_directory = ffmpeg -diff_files = ffmpeg/version_3.4.14.diff, ffmpeg/msvc_build_conform.diff, ffmpeg/ffmpeg.git-effadce6c756247ea8bae32dc13bb3e6f464f0eb.patch -#diff_files = ffmpeg/version_3.3.9.diff, ffmpeg/msvc_build_conform.diff, ffmpeg/ffmpeg.git-effadce6c756247ea8bae32dc13bb3e6f464f0eb.patch +#diff_files = ffmpeg/version_3.4.14.diff, ffmpeg/msvc_build_conform_old.diff, ffmpeg/ffmpeg.git-effadce6c756247ea8bae32dc13bb3e6f464f0eb.patch +diff_files = ffmpeg/version_3.3.9.diff, ffmpeg/msvc_build_conform_old.diff, ffmpeg/ffmpeg.git-effadce6c756247ea8bae32dc13bb3e6f464f0eb.patch diff --git a/subprojects/ffmpeg4.wrap b/subprojects/ffmpeg4.wrap index a9c4dc7..f2bde11 100644 --- a/subprojects/ffmpeg4.wrap +++ b/subprojects/ffmpeg4.wrap @@ -5,4 +5,4 @@ source_filename = ffmpeg-4.2.11.tar.gz source_hash = 93ad3689249984016c04cb0a0e02e5e22077a4d0204ca26bb604b0b7f0418eeb patch_directory = ffmpeg -diff_files = ffmpeg/version_4.2.11.diff, ffmpeg/msvc_build_conform.diff +diff_files = ffmpeg/version_4.2.11.diff, ffmpeg/msvc_build_conform_old.diff diff --git a/subprojects/ffmpeg5.wrap b/subprojects/ffmpeg5.wrap index 6ae62ca..6c063cf 100644 --- a/subprojects/ffmpeg5.wrap +++ b/subprojects/ffmpeg5.wrap @@ -1,8 +1,8 @@ [wrap-file] -directory = ffmpeg-5.1.7 -source_url = https://ffmpeg.org/releases/ffmpeg-5.1.7.tar.gz -source_filename = ffmpeg-5.1.7.tar.gz -source_hash = 75f25adaac2fe64f5c078b1ec3c89863353eb07f6e6a9e38898d79d2fc9527c8 +directory = ffmpeg-5.1.8 +source_url = https://ffmpeg.org/releases/ffmpeg-5.1.8.tar.gz +source_filename = ffmpeg-5.1.8.tar.gz +source_hash = 79781c24721427f1855e18b1bebea84f34e86f96e6ccb7a11be7e006d63215dc patch_directory = ffmpeg -diff_files = ffmpeg/version_5.1.7.diff, ffmpeg/msvc_build_conform.diff +diff_files = ffmpeg/version_5.1.8.diff, ffmpeg/msvc_build_conform.diff diff --git a/subprojects/ffmpeg6.wrap b/subprojects/ffmpeg6.wrap index 9c76e9c..4a6cdf0 100644 --- a/subprojects/ffmpeg6.wrap +++ b/subprojects/ffmpeg6.wrap @@ -1,8 +1,8 @@ [wrap-file] -directory = ffmpeg-6.1.3 -source_url = https://ffmpeg.org/releases/ffmpeg-6.1.3.tar.gz -source_filename = ffmpeg-6.1.3.tar.gz -source_hash = 3be466dfd1a312d752c20ece5b413248a5808fba3c967da0624bac48a4380857 +directory = ffmpeg-6.1.4 +source_url = https://ffmpeg.org/releases/ffmpeg-6.1.4.tar.gz +source_filename = ffmpeg-6.1.4.tar.gz +source_hash = e21c2624978f56b2030a0cc138cdf67f0b8888b471665907be16b598d85b98ad patch_directory = ffmpeg -diff_files = ffmpeg/version_6.1.3.diff, ffmpeg/msvc_build_conform.diff +diff_files = ffmpeg/version_6.1.4.diff, ffmpeg/msvc_build_conform.diff diff --git a/subprojects/ffmpeg7.wrap b/subprojects/ffmpeg7.wrap index 8187b07..adc4807 100644 --- a/subprojects/ffmpeg7.wrap +++ b/subprojects/ffmpeg7.wrap @@ -1,8 +1,8 @@ [wrap-file] -directory = ffmpeg-7.1.1 -source_url = https://ffmpeg.org/releases/ffmpeg-7.1.1.tar.gz -source_filename = ffmpeg-7.1.1.tar.gz -source_hash = 9a6e57a446b671012612aaeb9df5126794d5ac8f2015ca220934f99a6a4e0601 +directory = ffmpeg-7.1.3 +source_url = https://ffmpeg.org/releases/ffmpeg-7.1.3.tar.gz +source_filename = ffmpeg-7.1.3.tar.gz +source_hash = 81b452c5afe327a19a6a27e28ff09f6bf217b1d1fca988fc451c4717d8fe8ba7 patch_directory = ffmpeg -diff_files = ffmpeg/version_7.1.1.diff, ffmpeg/msvc_build_conform.diff +diff_files = ffmpeg/version_7.1.3.diff, ffmpeg/msvc_build_conform.diff diff --git a/subprojects/ffmpeg8.wrap b/subprojects/ffmpeg8.wrap index 2bc2d44..b91ff38 100644 --- a/subprojects/ffmpeg8.wrap +++ b/subprojects/ffmpeg8.wrap @@ -5,4 +5,4 @@ source_filename = ffmpeg-8.0.1.tar.gz source_hash = ed1cfade43aab7711c88937a0afc15b7b22efdc528c6ff074b4027c55a3c175c patch_directory = ffmpeg -diff_files = ffmpeg/version_8.0.1.diff +diff_files = ffmpeg/version_8.0.1.diff, ffmpeg/msvc_build_conform.diff diff --git a/subprojects/glslang.wrap b/subprojects/glslang.wrap index 7f4eae4..e06e2f6 100644 --- a/subprojects/glslang.wrap +++ b/subprojects/glslang.wrap @@ -1,6 +1,6 @@ [wrap-git] -directory = glslang-15.3.0 +directory = glslang-16.0.0 url = https://github.com/KhronosGroup/glslang.git -revision = 15.3.0 +revision = 16.0.0 depth = 1 method = cmake diff --git a/subprojects/libalabaster.wrap b/subprojects/libalabaster.wrap index 145c015..4b277ec 100644 --- a/subprojects/libalabaster.wrap +++ b/subprojects/libalabaster.wrap @@ -1,5 +1,5 @@ [wrap-git] -directory = libalabaster-0d1bc6c +directory = libalabaster-b5b7657 url = https://git.akon.city/libalabaster.git push-url = git@git.akon.city:libalabaster.git -revision = 0d1bc6cf7483d2a53c2d5f04d15a0ce3da85f29b +revision = b5b765781482a7b3ff6026ea71b4eb35a0a155d0 diff --git a/subprojects/libnaunet.wrap b/subprojects/libnaunet.wrap index df923b0..531910f 100644 --- a/subprojects/libnaunet.wrap +++ b/subprojects/libnaunet.wrap @@ -1,5 +1,5 @@ [wrap-git] -directory = libnaunet-5edcdda +directory = libnaunet-1e0f603 url = https://git.akon.city/libnaunet.git push-url = git@git.akon.city:libnaunet.git -revision = 5edcdda9efadd7c6ff5a47bef2897c0eb995057f +revision = 1e0f60341192c79bf49e84e6b187ffb7bfded252 diff --git a/subprojects/libplacebo.wrap b/subprojects/libplacebo.wrap index 9f29483..b84b3b6 100644 --- a/subprojects/libplacebo.wrap +++ b/subprojects/libplacebo.wrap @@ -1,7 +1,7 @@ [wrap-git] -directory = libplacebo-2e5a392 +directory = libplacebo-53719e4 url = https://code.videolan.org/videolan/libplacebo.git -revision = 2e5a392b7f1e4c25d5a3f931e253d71ab566757f +revision = 53719e45d264d90d422f51e1fa21a2957131bf1a clone-recursive = true depth = 1 diff --git a/subprojects/msvc-wine.wrap b/subprojects/msvc-wine.wrap index b80ca16..feeace3 100644 --- a/subprojects/msvc-wine.wrap +++ b/subprojects/msvc-wine.wrap @@ -1,7 +1,5 @@ [wrap-git] -directory = msvc-wine +directory = msvc-wine-32b504c url = https://github.com/mstorsjo/msvc-wine.git -revision = 44dc13b5e62ecc2373fbe7e4727a525001f403f4 +revision = 32b504c63b869681cda6824a20e30b74cb718432 depth = 1 -patch_directory = msvc-wine -diff_files = msvc-wine/msvc-wine-shebang-nixos.diff diff --git a/subprojects/packagefiles/ffmpeg/meson.build b/subprojects/packagefiles/ffmpeg/meson.build index 46f2d31..f813c8f 100644 --- a/subprojects/packagefiles/ffmpeg/meson.build +++ b/subprojects/packagefiles/ffmpeg/meson.build @@ -62,12 +62,24 @@ endif #'--extra-ldflags=-Lbuild/subprojects/libalabaster -l:libalabaster.a', #'--malloc-prefix=al_', -decoders = 'flac,mp3,mp3float,aac,opus,vorbis,h264,hevc,av1,vp9' +decoders = 'flac,mp3,mp3float,aac,opus,vorbis,h264,hevc,vp9' decoders += ',pcm_s32be,pcm_s32le,pcm_s24be,pcm_s24le,pcm_s16be,pcm_s16le' #decoders += ',pcm_f32be,pcm_f32le,pcm_s32le_planar,pcm_s16be_planar,pcm_s16le_planar' decoders += ',dca,eac3' decoders += ',pcm_dvd,mpegvideo,mpeg2video' #decoders += ',ass,srt' +if get_option('codec-hwaccels').length() == 0 + if not is_msvc + libdav1d = dependency('dav1d', required: false) + if libdav1d.found() + decoders += ',libdav1d' + extra_options += ['--enable-libdav1d'] + deps += [libdav1d] + endif + endif +else + decoders += ',av1' +endif demuxers = '' parsers = '' @@ -88,16 +100,22 @@ if not get_option('sink-only') endif endif -zlib = dependency('zlib', required: false) -if zlib.found() - extra_options += ['--enable-zlib'] - decoders += ',png,apng,mjpeg,jpeg2000,gif,' - if not get_option('sink-only') - demuxers += ',image_png_pipe,image2,mjpeg,image2pipe,image_jpeg_pipe,gif' - parsers += ',png,mjpeg,jpeg2000,gif' +ffmpeg_image_codecs = true +if ffmpeg_image_codecs + zlib = dependency('zlib', required: false) + if zlib.found() + extra_options += ['--enable-zlib'] + decoders += ',png,apng,mjpeg,jpeg2000,webp,gif,' + if not get_option('sink-only') + demuxers += ',image_png_pipe,image2,mjpeg,image2pipe,image_jpeg_pipe,image_webp_pipe,gif' + parsers += ',png,mjpeg,jpeg2000,webp,gif' + endif + deps += [zlib] + else + extra_options += ['--disable-zlib'] endif - deps += [zlib] endif +extra_options += ['--disable-xlib'] bsfs = '' if not get_option('sink-only') @@ -177,7 +195,12 @@ endif # Disable unused hwaccels. extra_options += [ + '--disable-amf', + '--disable-audiotoolbox', + '--disable-cuda-llvm', '--disable-cuvid', + '--disable-ffnvcodec', + '--disable-nvdec', '--disable-nvenc', '--disable-vdpau', '--disable-audiotoolbox', @@ -237,7 +260,6 @@ proj = ext_proj.add_project('configure', '--disable-manpages', '--disable-podpages', '--disable-txtpages', - '--disable-safe-bitstream-reader', '--disable-schannel', '--disable-network', '--disable-securetransport', diff --git a/subprojects/packagefiles/ffmpeg/msvc_build_conform.diff b/subprojects/packagefiles/ffmpeg/msvc_build_conform.diff index 3aff7e9..80c6c70 100644 --- a/subprojects/packagefiles/ffmpeg/msvc_build_conform.diff +++ b/subprojects/packagefiles/ffmpeg/msvc_build_conform.diff @@ -1,15 +1,26 @@ ---- a/configure 2025-03-02 19:22:08.000000000 -0500 -+++ b/configure 2025-05-17 18:43:08.854727216 -0400 -@@ -5184,7 +5184,7 @@ - DEPCCFLAGS=$_flags +--- a/configure 2025-11-19 21:31:28.000000000 -0500 ++++ b/configure 2025-11-24 12:35:16.905137447 -0500 +@@ -5299,8 +5299,8 @@ + arflags="rcD" + ar_o='$@' + else +- arflags="rc" +- ar_o='$@' ++ arflags="-nologo" ++ ar_o='-out:$@' fi - --if $ar 2>&1 | grep -q Microsoft; then -+if $ar 2>&1 | grep -q Microsoft || [ "$ar" == "llvm-lib" ]; then - arflags="-nologo" - ar_o='-out:$@' - elif $ar 2>&1 | grep -q "\[D\] "; then -@@ -5878,6 +5878,8 @@ + + # Treat unrecognized flags as errors on MSVC +@@ -5951,6 +5951,8 @@ + enabled x86_32 && check_ldflags -Wl,--large-address-aware + add_cppflags -DWIN32_LEAN_AND_MEAN + shlibdir_default="$bindir_default" ++ LIBPREF="" ++ LIBSUF=".lib" + SLIBPREF="" + SLIBSUF=".dll" + SLIBNAME_WITH_VERSION='$(SLIBPREF)$(FULLNAME)-$(LIBVERSION)$(SLIBSUF)' +@@ -6002,6 +6004,8 @@ enabled x86_32 && check_ldflags -LARGEADDRESSAWARE add_cppflags -DWIN32_LEAN_AND_MEAN shlibdir_default="$bindir_default" diff --git a/subprojects/packagefiles/ffmpeg/msvc_build_conform_old.diff b/subprojects/packagefiles/ffmpeg/msvc_build_conform_old.diff new file mode 100644 index 0000000..7811469 --- /dev/null +++ b/subprojects/packagefiles/ffmpeg/msvc_build_conform_old.diff @@ -0,0 +1,22 @@ +--- a/configure 2025-11-19 21:31:28.000000000 -0500 ++++ b/configure 2025-11-24 12:35:16.905137447 -0500 +@@ -5299,8 +5299,8 @@ + arflags="rcD" + ar_o='$@' + else +- arflags="rc" +- ar_o='$@' ++ arflags="-nologo" ++ ar_o='-out:$@' + fi + + # Treat unrecognized flags as errors on MSVC +@@ -5951,6 +5951,8 @@ + enabled x86_32 && check_ldflags -Wl,--large-address-aware + add_cppflags -DWIN32_LEAN_AND_MEAN + shlibdir_default="$bindir_default" ++ LIBPREF="" ++ LIBSUF=".lib" + SLIBPREF="" + SLIBSUF=".dll" + SLIBNAME_WITH_VERSION='$(SLIBPREF)$(FULLNAME)-$(LIBVERSION)$(SLIBSUF)' diff --git a/subprojects/packagefiles/ffmpeg/version_3.3.9.diff b/subprojects/packagefiles/ffmpeg/version_3.3.9.diff index 3887a2f..90911d5 100644 --- a/subprojects/packagefiles/ffmpeg/version_3.3.9.diff +++ b/subprojects/packagefiles/ffmpeg/version_3.3.9.diff @@ -1,5 +1,5 @@ ---- a/meson.build 2025-08-23 18:44:34.634014892 -0400 -+++ b/meson.build 2025-08-25 15:51:35.605178588 -0400 +--- a/meson.build 2025-11-30 11:51:43.747121512 -0500 ++++ b/meson.build 2025-11-30 12:55:07.519116860 -0500 @@ -1,4 +1,4 @@ -project('ffmpeg', 'c', version: '', meson_version: '>=0.57.0') +project('ffmpeg', 'c', version: '3.3.9', meson_version: '>=0.57.0') @@ -17,7 +17,40 @@ extra_options += ['--enable-static', '--disable-shared'] if is_windows -@@ -220,6 +216,15 @@ +@@ -68,18 +64,7 @@ + decoders += ',dca,eac3' + decoders += ',pcm_dvd,mpegvideo,mpeg2video' + #decoders += ',ass,srt' +-if get_option('codec-hwaccels').length() == 0 +- if not is_msvc +- libdav1d = dependency('dav1d', required: false) +- if libdav1d.found() +- decoders += ',libdav1d' +- extra_options += ['--enable-libdav1d'] +- deps += [libdav1d] +- endif +- endif +-else +- decoders += ',av1' +-endif ++decoders += ',av1' + + demuxers = '' + parsers = '' +@@ -195,12 +180,8 @@ + + # Disable unused hwaccels. + extra_options += [ +- '--disable-amf', + '--disable-audiotoolbox', +- '--disable-cuda-llvm', + '--disable-cuvid', +- '--disable-ffnvcodec', +- '--disable-nvdec', + '--disable-nvenc', + '--disable-vdpau', + '--disable-audiotoolbox', +@@ -243,6 +224,15 @@ args += ['-DCAMU_HAVE_SOXR'] endif @@ -33,7 +66,7 @@ ext_proj = import('unstable-external_project') proj = ext_proj.add_project('configure', -@@ -229,7 +234,6 @@ +@@ -252,7 +242,6 @@ '--incdir=@PREFIX@/@INCLUDEDIR@', '--enable-pic', '--disable-all', @@ -41,7 +74,7 @@ '--disable-everything', '--disable-programs', '--disable-doc', -@@ -255,11 +259,14 @@ +@@ -277,11 +266,14 @@ '--enable-swscale', '--disable-indevs', '--disable-outdevs', @@ -56,7 +89,7 @@ avutil = proj.dependency('avutil') avformat = proj.dependency('avformat') avcodec = proj.dependency('avcodec') -@@ -268,14 +275,14 @@ +@@ -290,14 +282,14 @@ swscale = proj.dependency('swscale') include = include_directories('.', 'build/') meson.override_dependency('libavutil', declare_dependency( diff --git a/subprojects/packagefiles/ffmpeg/version_4.2.11.diff b/subprojects/packagefiles/ffmpeg/version_4.2.11.diff index 27d5ceb..cce132d 100644 --- a/subprojects/packagefiles/ffmpeg/version_4.2.11.diff +++ b/subprojects/packagefiles/ffmpeg/version_4.2.11.diff @@ -1,12 +1,32 @@ ---- a/meson.build 2025-08-23 18:44:34.634014892 -0400 -+++ b/meson.build 2025-08-25 15:42:23.161139654 -0400 +--- a/meson.build 2025-11-30 11:51:43.747121512 -0500 ++++ b/meson.build 2025-11-30 12:52:39.981254464 -0500 @@ -1,4 +1,4 @@ -project('ffmpeg', 'c', version: '', meson_version: '>=0.57.0') +project('ffmpeg', 'c', version: '4.2.11', meson_version: '>=0.57.0') compiler = meson.get_compiler('c') -@@ -268,14 +268,14 @@ +@@ -68,18 +68,7 @@ + decoders += ',dca,eac3' + decoders += ',pcm_dvd,mpegvideo,mpeg2video' + #decoders += ',ass,srt' +-if get_option('codec-hwaccels').length() == 0 +- if not is_msvc +- libdav1d = dependency('dav1d', required: false) +- if libdav1d.found() +- decoders += ',libdav1d' +- extra_options += ['--enable-libdav1d'] +- deps += [libdav1d] +- endif +- endif +-else +- decoders += ',av1' +-endif ++decoders += ',av1' + + demuxers = '' + parsers = '' +@@ -290,14 +279,14 @@ swscale = proj.dependency('swscale') include = include_directories('.', 'build/') meson.override_dependency('libavutil', declare_dependency( diff --git a/subprojects/packagefiles/ffmpeg/version_5.1.8.diff b/subprojects/packagefiles/ffmpeg/version_5.1.8.diff new file mode 100644 index 0000000..784c8a1 --- /dev/null +++ b/subprojects/packagefiles/ffmpeg/version_5.1.8.diff @@ -0,0 +1,29 @@ +--- a/meson.build 2025-08-23 18:44:34.634014892 -0400 ++++ b/meson.build 2025-08-25 15:40:17.065798145 -0400 +@@ -1,4 +1,4 @@ +-project('ffmpeg', 'c', version: '', meson_version: '>=0.57.0') ++project('ffmpeg', 'c', version: '5.1.8', meson_version: '>=0.57.0') + + compiler = meson.get_compiler('c') + +@@ -268,14 +268,14 @@ + swscale = proj.dependency('swscale') + include = include_directories('.', 'build/') + meson.override_dependency('libavutil', declare_dependency( +- dependencies: [avutil, deps], compile_args: args, include_directories: include, version: '')) ++ dependencies: [avutil, deps], compile_args: args, include_directories: include, version: '57.28.100')) + meson.override_dependency('libavformat', declare_dependency( +- dependencies: [avformat], include_directories: include, version: '')) ++ dependencies: [avformat], include_directories: include, version: '59.27.100')) + meson.override_dependency('libavcodec', declare_dependency( +- dependencies: [avcodec], include_directories: include, version: '')) ++ dependencies: [avcodec], include_directories: include, version: '59.37.100')) + meson.override_dependency('libavdevice', declare_dependency( +- dependencies: [avdevice], include_directories: include, version: '')) ++ dependencies: [avdevice], include_directories: include, version: '59.7.100')) + meson.override_dependency('libswresample', declare_dependency( +- dependencies: [swresample], include_directories: include, version: '')) ++ dependencies: [swresample], include_directories: include, version: '4.7.100')) + meson.override_dependency('libswscale', declare_dependency( +- dependencies: [swscale], include_directories: include, version: '')) ++ dependencies: [swscale], include_directories: include, version: '6.7.100')) diff --git a/subprojects/packagefiles/ffmpeg/version_6.1.4.diff b/subprojects/packagefiles/ffmpeg/version_6.1.4.diff new file mode 100644 index 0000000..5f54fe5 --- /dev/null +++ b/subprojects/packagefiles/ffmpeg/version_6.1.4.diff @@ -0,0 +1,29 @@ +--- a/meson.build 2025-08-23 18:44:34.634014892 -0400 ++++ b/meson.build 2025-08-25 15:37:56.556303265 -0400 +@@ -1,4 +1,4 @@ +-project('ffmpeg', 'c', version: '', meson_version: '>=0.57.0') ++project('ffmpeg', 'c', version: '6.1.4', meson_version: '>=0.57.0') + + compiler = meson.get_compiler('c') + +@@ -268,14 +268,14 @@ + swscale = proj.dependency('swscale') + include = include_directories('.', 'build/') + meson.override_dependency('libavutil', declare_dependency( +- dependencies: [avutil, deps], compile_args: args, include_directories: include, version: '')) ++ dependencies: [avutil, deps], compile_args: args, include_directories: include, version: '58.29.100')) + meson.override_dependency('libavformat', declare_dependency( +- dependencies: [avformat], include_directories: include, version: '')) ++ dependencies: [avformat], include_directories: include, version: '60.16.100')) + meson.override_dependency('libavcodec', declare_dependency( +- dependencies: [avcodec], include_directories: include, version: '')) ++ dependencies: [avcodec], include_directories: include, version: '60.31.102')) + meson.override_dependency('libavdevice', declare_dependency( +- dependencies: [avdevice], include_directories: include, version: '')) ++ dependencies: [avdevice], include_directories: include, version: '60.3.100')) + meson.override_dependency('libswresample', declare_dependency( +- dependencies: [swresample], include_directories: include, version: '')) ++ dependencies: [swresample], include_directories: include, version: '4.12.100')) + meson.override_dependency('libswscale', declare_dependency( +- dependencies: [swscale], include_directories: include, version: '')) ++ dependencies: [swscale], include_directories: include, version: '7.5.100')) diff --git a/subprojects/packagefiles/ffmpeg/version_7.1.3.diff b/subprojects/packagefiles/ffmpeg/version_7.1.3.diff new file mode 100644 index 0000000..554ef02 --- /dev/null +++ b/subprojects/packagefiles/ffmpeg/version_7.1.3.diff @@ -0,0 +1,29 @@ +--- a/meson.build 2025-08-23 18:44:34.634014892 -0400 ++++ b/meson.build 2025-08-25 15:35:33.498781349 -0400 +@@ -1,4 +1,4 @@ +-project('ffmpeg', 'c', version: '', meson_version: '>=0.57.0') ++project('ffmpeg', 'c', version: '7.1.3', meson_version: '>=0.57.0') + + compiler = meson.get_compiler('c') + +@@ -268,14 +268,14 @@ + swscale = proj.dependency('swscale') + include = include_directories('.', 'build/') + meson.override_dependency('libavutil', declare_dependency( +- dependencies: [avutil, deps], compile_args: args, include_directories: include, version: '')) ++ dependencies: [avutil, deps], compile_args: args, include_directories: include, version: '59.39.100')) + meson.override_dependency('libavformat', declare_dependency( +- dependencies: [avformat], include_directories: include, version: '')) ++ dependencies: [avformat], include_directories: include, version: '61.7.100')) + meson.override_dependency('libavcodec', declare_dependency( +- dependencies: [avcodec], include_directories: include, version: '')) ++ dependencies: [avcodec], include_directories: include, version: '61.19.100')) + meson.override_dependency('libavdevice', declare_dependency( +- dependencies: [avdevice], include_directories: include, version: '')) ++ dependencies: [avdevice], include_directories: include, version: '61.3.100')) + meson.override_dependency('libswresample', declare_dependency( +- dependencies: [swresample], include_directories: include, version: '')) ++ dependencies: [swresample], include_directories: include, version: '5.3.100')) + meson.override_dependency('libswscale', declare_dependency( +- dependencies: [swscale], include_directories: include, version: '')) ++ dependencies: [swscale], include_directories: include, version: '8.3.100')) diff --git a/subprojects/packagefiles/miniaudio/meson.build b/subprojects/packagefiles/miniaudio/meson.build index 8522b5f..a2889f1 100644 --- a/subprojects/packagefiles/miniaudio/meson.build +++ b/subprojects/packagefiles/miniaudio/meson.build @@ -1,2 +1,2 @@ -project('miniaudio', 'c', version: 'git-78cdb9c') +project('miniaudio', 'c', version: '0.11.23') miniaudio = declare_dependency(include_directories: include_directories('.')) diff --git a/subprojects/packagefiles/msvc-wine/msvc-wine-shebang-nixos.diff b/subprojects/packagefiles/msvc-wine/msvc-wine-shebang-nixos.diff deleted file mode 100644 index 37295a9..0000000 --- a/subprojects/packagefiles/msvc-wine/msvc-wine-shebang-nixos.diff +++ /dev/null @@ -1,405 +0,0 @@ -https://github.com/mstorsjo/msvc-wine/pull/187 - -diff --git a/fixinclude b/fixinclude -index 4a8c733..bd5280c 100755 ---- a/fixinclude -+++ b/fixinclude -@@ -1,4 +1,4 @@ --#!/usr/bin/perl -+#!/usr/bin/env perl - # - # Copyright (c) 2019 Martin Storsjo - # -diff --git a/install.sh b/install.sh -index ac00d9f..039922f 100755 ---- a/install.sh -+++ b/install.sh -@@ -1,4 +1,4 @@ --#!/bin/sh -+#!/usr/bin/env sh - # - # Copyright (c) 2019 Martin Storsjo - # -diff --git a/lowercase b/lowercase -index ee10cc7..32902f7 100755 ---- a/lowercase -+++ b/lowercase -@@ -1,4 +1,4 @@ --#!/usr/bin/perl -+#!/usr/bin/env perl - # - # Copyright (c) 2019 Martin Storsjo - # -diff --git a/msvcenv-native.sh b/msvcenv-native.sh -index e83c687..f3a99f1 100755 ---- a/msvcenv-native.sh -+++ b/msvcenv-native.sh -@@ -1,4 +1,4 @@ --#!/bin/sh -+#!/usr/bin/env sh - # - # Copyright (c) 2019 Martin Storsjo - # -@@ -33,9 +33,9 @@ else - if [ ! -f "$ENV" ]; then - echo $ENV doesn\'t exist - else -- export INCLUDE="$(bash -c ". $ENV && /bin/echo \"\$INCLUDE\"" | sed s/z://g | sed 's/\\/\//g')" -- export LIB="$(bash -c ". $ENV && /bin/echo \"\$LIB\"" | sed s/z://g | sed 's/\\/\//g')" -- MSVCARCH="$(bash -c ". $ENV && /bin/echo \"\$ARCH\"")" -+ export INCLUDE="$(bash -c ". $ENV && /usr/bin/env echo \"\$INCLUDE\"" | sed s/z://g | sed 's/\\/\//g')" -+ export LIB="$(bash -c ". $ENV && /usr/bin/env echo \"\$LIB\"" | sed s/z://g | sed 's/\\/\//g')" -+ MSVCARCH="$(bash -c ". $ENV && /usr/bin/env echo \"\$ARCH\"")" - case $MSVCARCH in - x86) TARGET_ARCH=i686 ;; - x64) TARGET_ARCH=x86_64 ;; -diff --git a/test/test-asm.sh b/test/test-asm.sh -index 5774c02..ba9511b 100755 ---- a/test/test-asm.sh -+++ b/test/test-asm.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2023 Martin Storsjo - # -diff --git a/test/test-cl.sh b/test/test-cl.sh -index 250f092..41fe51b 100755 ---- a/test/test-cl.sh -+++ b/test/test-cl.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2023 Huang Qinjin - # -diff --git a/test/test-clang-cl-cmds.sh b/test/test-clang-cl-cmds.sh -index 885835b..9409d2b 100755 ---- a/test/test-clang-cl-cmds.sh -+++ b/test/test-clang-cl-cmds.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2023 Martin Storsjo - # -diff --git a/test/test-clang-cl.sh b/test/test-clang-cl.sh -index ba7f82d..f99b745 100755 ---- a/test/test-clang-cl.sh -+++ b/test/test-clang-cl.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2023 Martin Storsjo - # -diff --git a/test/test-cmake-clang-cl.sh b/test/test-cmake-clang-cl.sh -index 78485ab..4a6ce3f 100755 ---- a/test/test-cmake-clang-cl.sh -+++ b/test/test-cmake-clang-cl.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2023 Martin Storsjo - # -diff --git a/test/test-cmake.sh b/test/test-cmake.sh -index 6beface..994d07d 100755 ---- a/test/test-cmake.sh -+++ b/test/test-cmake.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2023 Huang Qinjin - # -diff --git a/test/test-dumpbin.sh b/test/test-dumpbin.sh -index 68608d4..dd3841d 100755 ---- a/test/test-dumpbin.sh -+++ b/test/test-dumpbin.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2023 Huang Qinjin - # -diff --git a/test/test-mc.sh b/test/test-mc.sh -index 836a210..2e7d8ab 100755 ---- a/test/test-mc.sh -+++ b/test/test-mc.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2024 Mark Harmstone - # -diff --git a/test/test-meson.sh b/test/test-meson.sh -index 68ac271..bfd6d6d 100755 ---- a/test/test-meson.sh -+++ b/test/test-meson.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2023 Martin Storsjo - # -diff --git a/test/test-midl.sh b/test/test-midl.sh -index 45d9f92..8f9319e 100755 ---- a/test/test-midl.sh -+++ b/test/test-midl.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2023 Martin Storsjo - # -diff --git a/test/test-msbuild.sh b/test/test-msbuild.sh -index 00f03c6..faf284a 100755 ---- a/test/test-msbuild.sh -+++ b/test/test-msbuild.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2024 Sergey Kvachonok - # -diff --git a/test/test-mt.sh b/test/test-mt.sh -index 5f1881b..eda11ab 100755 ---- a/test/test-mt.sh -+++ b/test/test-mt.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2023 Huang Qinjin - # -diff --git a/test/test-vcpkg.sh b/test/test-vcpkg.sh -index dc3d3ce..79ebe51 100755 ---- a/test/test-vcpkg.sh -+++ b/test/test-vcpkg.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2024 Huang Qinjin - # -diff --git a/test/test-wdk-msbuild.sh b/test/test-wdk-msbuild.sh -index b37b3de..90f4189 100755 ---- a/test/test-wdk-msbuild.sh -+++ b/test/test-wdk-msbuild.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2024 Sergey Kvachonok - # -diff --git a/test/test.sh b/test/test.sh -index 1825216..ad9f9c1 100755 ---- a/test/test.sh -+++ b/test/test.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2023 Huang Qinjin - # -diff --git a/vsdownload.py b/vsdownload.py -index d9b4dcf..7598eec 100755 ---- a/vsdownload.py -+++ b/vsdownload.py -@@ -1,4 +1,4 @@ --#!/usr/bin/python3 -+#!/usr/bin/env python3 - # - # Copyright (c) 2019 Martin Storsjo - # -diff --git a/wdk-download.sh b/wdk-download.sh -index e0ea631..7fa8bf0 100755 ---- a/wdk-download.sh -+++ b/wdk-download.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2024 Sergey Kvachonok - # -diff --git a/wrappers/armasm b/wrappers/armasm -index 3797431..bf8ea5a 100755 ---- a/wrappers/armasm -+++ b/wrappers/armasm -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2018 Martin Storsjo - # -diff --git a/wrappers/armasm64 b/wrappers/armasm64 -index fdfdb38..eb1553f 100755 ---- a/wrappers/armasm64 -+++ b/wrappers/armasm64 -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2018 Martin Storsjo - # -diff --git a/wrappers/cl b/wrappers/cl -index 35c1b23..6ca6a56 100755 ---- a/wrappers/cl -+++ b/wrappers/cl -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2018 Martin Storsjo - # -diff --git a/wrappers/cmd b/wrappers/cmd -index 88a39f0..a7ab236 100755 ---- a/wrappers/cmd -+++ b/wrappers/cmd -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2018 Martin Storsjo - # -diff --git a/wrappers/dumpbin b/wrappers/dumpbin -index c6f7f9e..18c5591 100755 ---- a/wrappers/dumpbin -+++ b/wrappers/dumpbin -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2018 Martin Storsjo - # -diff --git a/wrappers/findstr b/wrappers/findstr -index 1146022..136ac0e 100755 ---- a/wrappers/findstr -+++ b/wrappers/findstr -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2024 Huang Qinjin - # -diff --git a/wrappers/lib b/wrappers/lib -index 143da0c..3967675 100755 ---- a/wrappers/lib -+++ b/wrappers/lib -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2018 Martin Storsjo - # -diff --git a/wrappers/link b/wrappers/link -index dba1774..53920d4 100755 ---- a/wrappers/link -+++ b/wrappers/link -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2018 Martin Storsjo - # -diff --git a/wrappers/mc b/wrappers/mc -index c1c5d80..bf71882 100755 ---- a/wrappers/mc -+++ b/wrappers/mc -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2018 Martin Storsjo - # -diff --git a/wrappers/midl b/wrappers/midl -index dd34988..adddcfa 100755 ---- a/wrappers/midl -+++ b/wrappers/midl -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2018 Martin Storsjo - # -diff --git a/wrappers/ml b/wrappers/ml -index db671c7..a5d537b 100755 ---- a/wrappers/ml -+++ b/wrappers/ml -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2018 Martin Storsjo - # -diff --git a/wrappers/ml64 b/wrappers/ml64 -index 0708ebe..df65803 100755 ---- a/wrappers/ml64 -+++ b/wrappers/ml64 -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2018 Martin Storsjo - # -diff --git a/wrappers/msbuild b/wrappers/msbuild -index 65bbf8f..53fdd8f 100755 ---- a/wrappers/msbuild -+++ b/wrappers/msbuild -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2024 Sergey Kvachonok - # -diff --git a/wrappers/msvcenv.sh b/wrappers/msvcenv.sh -index 101e9bb..2d2a918 100755 ---- a/wrappers/msvcenv.sh -+++ b/wrappers/msvcenv.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2018 Martin Storsjo - # -diff --git a/wrappers/mt b/wrappers/mt -index edebd75..ed92f96 100755 ---- a/wrappers/mt -+++ b/wrappers/mt -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2018 Martin Storsjo - # -diff --git a/wrappers/nmake b/wrappers/nmake -index 88102d1..3eaf0f5 100755 ---- a/wrappers/nmake -+++ b/wrappers/nmake -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2018 Martin Storsjo - # -diff --git a/wrappers/rc b/wrappers/rc -index f7b8d97..7afb1ad 100755 ---- a/wrappers/rc -+++ b/wrappers/rc -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2018 Martin Storsjo - # -diff --git a/wrappers/wine-msvc.sh b/wrappers/wine-msvc.sh -index a267283..bb08891 100755 ---- a/wrappers/wine-msvc.sh -+++ b/wrappers/wine-msvc.sh -@@ -1,4 +1,4 @@ --#!/bin/bash -+#!/usr/bin/env bash - # - # Copyright (c) 2018 Martin Storsjo - # diff --git a/subprojects/packagefiles/wuffs/meson.build b/subprojects/packagefiles/wuffs/meson.build index c595249..5d877f6 100644 --- a/subprojects/packagefiles/wuffs/meson.build +++ b/subprojects/packagefiles/wuffs/meson.build @@ -1,2 +1,2 @@ -project('wuff', 'c', version: 'git-d662ef5') +project('wuff', 'c', version: 'git-1e2e58c') wuffs = declare_dependency(include_directories: include_directories('./release/c')) diff --git a/subprojects/shaderc.wrap b/subprojects/shaderc.wrap index d510ec9..efef095 100644 --- a/subprojects/shaderc.wrap +++ b/subprojects/shaderc.wrap @@ -1,7 +1,7 @@ [wrap-git] -directory = shaderc-v2025.4 +directory = shaderc-v2025.5 url = https://github.com/google/shaderc.git -revision = v2025.4 +revision = v2025.5 depth = 1 method = cmake patch_directory = shaderc diff --git a/subprojects/stela.wrap b/subprojects/stela.wrap index 7122113..fda63dd 100644 --- a/subprojects/stela.wrap +++ b/subprojects/stela.wrap @@ -1,5 +1,5 @@ [wrap-git] -directory = stela-9d69eec +directory = stela-22e9e4d url = https://git.akon.city/stela.git push-url = git@git.akon.city:stela.git -revision = 9d69eec9ab48f009a04b9ab710f57c85b240dd19 +revision = 22e9e4d6e2c2e8e5eea065e5b7cd04fa6626559c |