summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2025-10-30 14:40:08 -0400
committerAndrew Opalach <andrew@akon.city> 2025-10-30 14:40:08 -0400
commit90da3b27d939b3b7af1cf7fed10dfaaa7e271622 (patch)
treef0cf96c76d5c83470d2c50dd69d2d3e39a503fab
parent77837d7b63240c069b3351a919a7d2af670f46cd (diff)
downloadcamu-90da3b27d939b3b7af1cf7fed10dfaaa7e271622.tar.gz
camu-90da3b27d939b3b7af1cf7fed10dfaaa7e271622.tar.bz2
camu-90da3b27d939b3b7af1cf7fed10dfaaa7e271622.zip
Fixes from testing sink change, document things
Signed-off-by: Andrew Opalach <andrew@akon.city>
-rw-r--r--TODO51
-rw-r--r--cross/wasm-emscripten.txt20
-rw-r--r--env/android_build.txt2
-rw-r--r--env/mingw_build.txt4
-rw-r--r--flake.lock30
-rw-r--r--flake.nix3
-rwxr-xr-xscripts/adb_logcat.sh2
-rw-r--r--src/buffer/clock.c14
-rw-r--r--src/cache/backings/file_common.h2
-rw-r--r--src/cache/handlers/http.c8
-rw-r--r--src/codec/codec.h46
-rw-r--r--src/codec/ffmpeg/demuxer.c31
-rw-r--r--src/codec/ffmpeg/packet_ext.c2
-rw-r--r--src/codec/stb_image.c14
-rw-r--r--src/fruits/cmc/cmc.c1
-rw-r--r--src/fruits/cmc/ui/ui.c1
-rw-r--r--src/fruits/cmc/ui/util/waveform.c2
-rw-r--r--src/fruits/cmsrv/cmsrv.c2
-rw-r--r--src/fruits/cmv/cmv.c11
-rw-r--r--src/fruits/common.h10
-rw-r--r--src/fruits/ctv/ctv.c8
-rw-r--r--src/fruits/droid/ctv/gradle/libs.versions.toml2
-rw-r--r--src/fruits/droid/ctv/gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--src/liana/client.c3
-rw-r--r--src/liana/handlers/codec.h6
-rw-r--r--src/liana/list.c11
-rw-r--r--src/liana/list.h2
-rw-r--r--src/liana/server.c9
-rw-r--r--src/liana/vcr.c3
-rw-r--r--src/libsink/desktop.c23
-rw-r--r--src/libsink/desktop.h5
-rw-r--r--src/libsink/input_simulator.c37
-rw-r--r--src/mixer/mixer.c7
-rw-r--r--src/mixer/mixer.h2
-rw-r--r--src/portal/py/modules/patreon.py1
-rw-r--r--src/portal/py/modules/twitter.py19
-rwxr-xr-xsrc/portal/scripts/run_py.sh2
-rw-r--r--src/screen/screen.c6
-rw-r--r--src/server/common.h11
-rw-r--r--src/server/server.c10
-rw-r--r--src/util/color_palette.c4
-rw-r--r--subprojects/libalabaster.wrap5
-rw-r--r--subprojects/libnaunet.wrap5
-rw-r--r--subprojects/miniaudio.wrap4
-rw-r--r--subprojects/stela.wrap5
45 files changed, 286 insertions, 162 deletions
diff --git a/TODO b/TODO
index d8d374b..af266a0 100644
--- a/TODO
+++ b/TODO
@@ -30,7 +30,14 @@
- [x] Small list refactor.
- [x] Commit libnaunet changes.
- [x] Consolidate spng, stb_img, and wuffs into one file with server/client ifdefs.
- - [ ] Sink errored buffer fixes.
+ - [x] Commit subproject changes.
+ - [-] Sink errored buffer improvement.
+ - [-] More checks for VIDEO_EMPTY() || VIDEO_ENDED() for better experience.
+ - This was an actual correctness issue.
+ - [x] Checking buffer states needs a lock, check asserts.
+ - [ ] Commit fixes.
+ - [ ] Commit changes.
+ - [ ] Header cleanup.
- [ ] Packet benchmark.
- Create packet, write bunch of ints, send to another thread, thread adds everything.
- [ ] Startup timing information.
@@ -39,6 +46,16 @@
- [ ] RPC header packing.
- [ ] Look for more obvious packet_stream/rpc optimizations.
+== Emscripten Sink
+ - [ ] WebSocket packet_stream.
+ - [ ] Server-side WebSocket and TCP.
+ - nn_multiplex handles WebSocket upgrade.
+ - [ ] Analyze thread-safety requirements of nn_signal usage.
+ - [ ] Sink
+ - [ ] VCR
+ - [ ] Should still be able to rely on miniaudio.
+ - [ ] MOMO should be comptabible with WebGL.
+
=== DMMN
- [ ] List lock.
- [ ] Reconnect/Reseek.
@@ -77,6 +94,9 @@
- [ ] HTTP timeout.
=== Liana
+ - [ ] Invalid connection handling.
+ - [ ] Clearest way to "cut off connection immediately".
+ - [ ] Max packet size (invalid header).
- [ ] Node connection pool.
- [ ] One "connection" object should suffice for retrieving duration, handling stream, and loading visual data for client.
@@ -87,6 +107,7 @@
- [ ] Internalize "packet cache" logic.
=== Codecs
+ - [ ] Negotiate codec with sinks.
- [ ] Better wuffs support.
- [ ] https://snisurset.net/code/abydos
@@ -103,11 +124,11 @@
- https://www.shadertoy.com/view/wtsXRX
=== Renderer
+ - [ ] queue_libplacebo rgb24 upload fails on ANV and Wine(wined3d), at least.
- [ ] vaapi fallback if Vulkan decoding not available.
- [ ] Command queue.
- [ ] Queue creation/deletion.
- [ ] Texture upload.
- - [ ] queue_libplacebo rgb24 upload fails on ANV.
- [ ] MOMO frame queue.
- [ ] Log when scaling flips from required to not and vice versa.
@@ -124,6 +145,14 @@
- [ ] Implement node paradigm.
- [ ] Allow cmv to act as a node.
+=== libalabaster
+ - [x] Thread-safe rand().
+ - [x] AL_THREAD_LOCAL_SRAND
+ - [ ] Scrap trying to use stdlib rand() and copy a prng algorithm from somewhere.
+ - Worrying about predicting the state in libc is actually dumb.
+ - https://prng.di.unimi.it/xoroshiro64starstar.c
+ - https://prng.di.unimi.it/
+
=== libnaunet
- [ ] Native Windows file backend.
- https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-win32_find_dataa
@@ -135,6 +164,10 @@
- [x] Xcursor
- [-] Fix vaapi on x11.
- It's actually Xwayland lacking dma import extension.
+ - [ ] Wayland xkb.
+ - https://wayland-book.com/seat/keyboard.html
+ - https://xkbcommon.org/doc/current/xkbcommon_8h.html#a638bf7a52db06f383ce5a4ef99dfe539
+ - https://github.com/glfw/glfw/blob/8e15281d34a8b9ee9271ccce38177a3d812456f8/src/wl_window.c#L1880
- [ ] Configurable depth and stencil buffer.
- [ ] Consider server-side transcode in terms of the build.
- If video decode specific GPU device wiring is needed, should it be in stela?
@@ -152,6 +185,7 @@
- [ ] Rename mutex variables to lock.
=== Tests
+ - [x] Sporadic buffer errors.
- [ ] Emulate slow packet stream connection.
- ev_set_io_collect_interval() in loop.c.
- [ ] Fast open -> shuffle spam -> disconnect deadlock.
@@ -160,6 +194,16 @@
- [ ] al_array_contains
- [ ] al_array_unsorted_search_by_key
+==== Test cases
+// BUFFER_QUEUED -> BUFFER_INIT in CLIENT_REMOVE_BUFFERS.
+// - Ended audio or video only-entry processes a seek right after being skipped from.
+// - In switch_to(), because it's ended, remove_entry_buffers() is not called on this entry.
+// - During the seek within CLIENT_REMOVE_BUFFERS (this point before this change),
+// we don't touch an empty buffer's state. An empty buffer that was QUEUED stays QUEUED.
+// - CLIENT_RECONNECTED has no effect because the entry is not current.
+// - Now, if this entry is ever switch_to()'d, add_or_queue_entry() will assert
+// because a buffer's state being QUEUED at that point is invalid.
+
=== Build
- [x] Allow forcing ffmpeg-compat.
- 3 levels? latest, 4, 3.
@@ -169,6 +213,7 @@
- [x] Don't require shaderc if libplacebo found.
- [x] Audio optional based on if libpulse/libasound are found.
- [x] libalabaster option for release build with asserts (al_assert only).
+ - [ ] Change git clone depth of subproject wraps based on build configuration.
- [ ] Web build.
=== External
@@ -184,6 +229,8 @@
- [ ] Re-write database using moves for resources.
==== Status
+ - [ ] Generate following list for pixiv and twitter, do twitter one-shots in twitter7/8.
+ - [ ] List all pixiv ids in database, compare against current following list.
- [ ] Merge run/* in some way that respects the file dates.
- [ ] Move original pixiv into proper DB.
- [ ] Verify whether pixiv_app returns updated date or created date.
diff --git a/cross/wasm-emscripten.txt b/cross/wasm-emscripten.txt
new file mode 100644
index 0000000..b8d8611
--- /dev/null
+++ b/cross/wasm-emscripten.txt
@@ -0,0 +1,20 @@
+[binaries]
+c = 'emcc'
+cpp = 'em++'
+ar = 'emar'
+
+# If pkgconfig is broken refer to this.
+# https://github.com/hartenfels/meson-emscripten-example/blob/380be33d396e536b872e88f95e62b93e2f16545c/cross/emscripten.cross
+pkgconfig = ['emmake', 'pkg-config']
+cmake = ['emcmake', 'cmake']
+
+exe_wrapper = 'node'
+
+[properties]
+needs_exe_wrapper = true
+
+[host_machine]
+system = 'wasm'
+cpu_family = 'wasm'
+cpu = 'wasm'
+endian = 'little'
diff --git a/env/android_build.txt b/env/android_build.txt
index 7855be7..c89cc1d 100644
--- a/env/android_build.txt
+++ b/env/android_build.txt
@@ -1,4 +1,4 @@
#! /usr/bin/env sh
meson setup build-android -Dserver=disabled -Drenderer-api=gl -Dwindow=glfm \
- -Dcodec-ffmpeg-version=8 -Dcodec-hwaccels=mediacodec \
+ -Dcodec-ffmpeg-version=8 -Dcodec-hwaccels=mediacodec -Dtests=disabled \
--cross-file ./cross/aarch64-linux-android.txt --default-library=static
diff --git a/env/mingw_build.txt b/env/mingw_build.txt
index e091f9a..178d2a0 100644
--- a/env/mingw_build.txt
+++ b/env/mingw_build.txt
@@ -1,12 +1,12 @@
meson setup build-mingw64-vulkan -Drenderer-api=vulkan -Drenderer=libplacebo -Dwindow=win32 \
-Dsink-only=false -Dserver=enabled -Dportal=disabled -Dclient=disabled \
- -Dcodecs=ffmpeg -Dcodec-ffmpeg-version=8 -Dcodec-hwaccels=vulkan -Dsubtitles=enabled \
+ -Dcodecs=ffmpeg,stb_image -Dcodec-ffmpeg-version=8 -Dcodec-hwaccels=vulkan -Dsubtitles=enabled \
-Dtests=disabled -Dwin32-compat=false --cross-file ./cross/x86_64-w64-mingw32.txt \
--default-library=static --buildtype=release --optimization=2 -Ddebug=false --strip
meson setup build-mingw64-dx11 -Drenderer-api=dx11 -Drenderer=libplacebo -Dwindow=win32 \
-Dsink-only=false -Dserver=enabled -Dportal=disabled -Dclient=disabled \
- -Dcodecs=ffmpeg -Dcodec-ffmpeg-version=8 -Dcodec-hwaccels=d3d11va -Dsubtitles=enabled \
+ -Dcodecs=ffmpeg,stb_image -Dcodec-ffmpeg-version=8 -Dcodec-hwaccels=d3d11va -Dsubtitles=enabled \
-Dtests=disabled -Dwin32-compat=false --cross-file ./cross/x86_64-w64-mingw32.txt \
--default-library=static --buildtype=release --optimization=s -Ddebug=false --strip
diff --git a/flake.lock b/flake.lock
index 7fba99f..d9e8395 100644
--- a/flake.lock
+++ b/flake.lock
@@ -41,11 +41,11 @@
]
},
"locked": {
- "lastModified": 1757385184,
- "narHash": "sha256-LCxtQn9ajvOgGRbQIRUJgfP7clMGGvV1SDW1HcSb0zk=",
+ "lastModified": 1761770832,
+ "narHash": "sha256-IQIWG6kHxnUpx5KEb9r0BROL3/R6UQ/30aO2oHncBA8=",
"owner": "nix-community",
"repo": "home-manager",
- "rev": "26993d87fd0d3b14f7667b74ad82235f120d986e",
+ "rev": "124b99dbd1594dbebdd575ac7142752ee96a98a0",
"type": "github"
},
"original": {
@@ -76,11 +76,11 @@
},
"nixos-hardware": {
"locked": {
- "lastModified": 1757103352,
- "narHash": "sha256-PtT7ix43ss8PONJ1VJw3f6t2yAoGH+q462Sn8lrmWmk=",
+ "lastModified": 1761824787,
+ "narHash": "sha256-cH0vVt83ZBafqJzwOl7IHTXo6KA1c1J+8qvMjfARMqI=",
"owner": "NixOS",
"repo": "nixos-hardware",
- "rev": "11b2a10c7be726321bb854403fdeec391e798bf0",
+ "rev": "e07b1618c3009195ffbd4749e9b5618511260557",
"type": "github"
},
"original": {
@@ -96,11 +96,11 @@
"nixpkgs": "nixpkgs_2"
},
"locked": {
- "lastModified": 1757427959,
- "narHash": "sha256-p0i07rLfAMzJWYfsjFOXEtIWeS1EGVxJaCi9gfyCwRE=",
+ "lastModified": 1761563673,
+ "narHash": "sha256-d+1TpVAmRjcNBfjZsh2yQSdwUfN7Xgz1blJ185g73+A=",
"owner": "nix-community",
"repo": "NixOS-WSL",
- "rev": "785f1b67b6c53de088f640f2a7da50ca4b2d7161",
+ "rev": "a518cf710e5ebb935518dc7ac98e07e7ee5014c3",
"type": "github"
},
"original": {
@@ -127,11 +127,11 @@
},
"nixpkgs_2": {
"locked": {
- "lastModified": 1757068644,
- "narHash": "sha256-NOrUtIhTkIIumj1E/Rsv1J37Yi3xGStISEo8tZm3KW4=",
+ "lastModified": 1761373498,
+ "narHash": "sha256-Q/uhWNvd7V7k1H1ZPMy/vkx3F8C13ZcdrKjO7Jv7v0c=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "8eb28adfa3dc4de28e792e3bf49fcf9007ca8ac9",
+ "rev": "6a08e6bb4e46ff7fcbb53d409b253f6bad8a28ce",
"type": "github"
},
"original": {
@@ -143,11 +143,11 @@
},
"nixpkgs_3": {
"locked": {
- "lastModified": 1757347588,
- "narHash": "sha256-tLdkkC6XnsY9EOZW9TlpesTclELy8W7lL2ClL+nma8o=",
+ "lastModified": 1761672384,
+ "narHash": "sha256-o9KF3DJL7g7iYMZq9SWgfS1BFlNbsm6xplRjVlOCkXI=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "b599843bad24621dcaa5ab60dac98f9b0eb1cabe",
+ "rev": "08dacfca559e1d7da38f3cf05f1f45ee9bfd213c",
"type": "github"
},
"original": {
diff --git a/flake.nix b/flake.nix
index d13c76c..aac6d50 100644
--- a/flake.nix
+++ b/flake.nix
@@ -121,6 +121,7 @@
ninja
meson
cmake
+ emscripten
wayland-scanner.bin
((python3.override { enableOptimizations = true; }).withPackages (python-pkgs: [
python-pkgs.setuptools
@@ -216,8 +217,6 @@
perl
asciidoctor
pandoc
- pkgs.nixgl.nixGLIntel
- pkgs.nixgl.nixVulkanIntel
mesa-demos
#makemkv
# mesa build
diff --git a/scripts/adb_logcat.sh b/scripts/adb_logcat.sh
index b0da2a0..64aac3d 100755
--- a/scripts/adb_logcat.sh
+++ b/scripts/adb_logcat.sh
@@ -1,2 +1,2 @@
#! /usr/bin/env sh
-adb logcat *:S CTV
+adb logcat *:S CTV DEBUG
diff --git a/src/buffer/clock.c b/src/buffer/clock.c
index 508a899..67a229b 100644
--- a/src/buffer/clock.c
+++ b/src/buffer/clock.c
@@ -44,13 +44,13 @@ void camu_clock_seek(struct camu_clock *clock, f64 base, u64 target)
al_atomic_store(f64)(&clock->tick, tick, AL_ATOMIC_RELAXED);
}
} else {
- // Don't touch clock->pause here for the sake of sync.
- // Theoretically, the sink could rely on a CLOCK_PAUSED event from an entry
- // even after it was seeked. That would ultimately maintain sync but the
- // clock would report the old position in get_pts() before triggering CLOCK_PAUSED,
- // likely confusing the audio/video buffers of an entry. This is mitigated in
- // CLIENT_REMOVE_BUFFERS by immediately switching to the target instead of waiting for
- // a clock_callback().
+ // Don't touch clock->pause here for the sake of sync. This means,
+ // theoretically, the sink could still rely on a CLOCK_PAUSED callback
+ // from an entry even after it was seeked. That would ultimately maintain sync but
+ // the clock would report the old position in get_pts() before triggering CLOCK_PAUSED.
+ // Likely confusing an entry's buffers and causing excessive catchup or delay.
+ // We mitigate this sink-side by immediately switching to a potential target in
+ // CLIENT_REMOVE_BUFFERS, if possible, which is always called before an entry is seeked.
clock->paused_at = 0.0;
}
}
diff --git a/src/cache/backings/file_common.h b/src/cache/backings/file_common.h
index 936babe..6650dd1 100644
--- a/src/cache/backings/file_common.h
+++ b/src/cache/backings/file_common.h
@@ -26,7 +26,7 @@ static void file_backing_finalize(struct cch_backing *backing)
static bool file_open_internal(struct cch_backing_file *file, str *path, size_t size)
{
- s32 flags = size != 0 ? NNWT_FILE_CREATE : NNWT_FILE_READONLY;
+ s32 flags = (size != 0) ? NNWT_FILE_CREATE : NNWT_FILE_READONLY;
if (!nn_file_open(&file->file, path, flags)) return false;
size_t filesize = file->file.size;
if (!size) {
diff --git a/src/cache/handlers/http.c b/src/cache/handlers/http.c
index 9e99b8a..f57d3eb 100644
--- a/src/cache/handlers/http.c
+++ b/src/cache/handlers/http.c
@@ -22,8 +22,6 @@ static void http_callback(void *userdata, u8 op, u8 *buf, void *opaque)
{
struct cch_handler_http *http = (struct cch_handler_http *)userdata;
switch (op) {
- case NNWT_HTTP_READ:
- al_assert(false);
case NNWT_HTTP_WRITE: {
size_t n = *(size_t *)opaque;
// If we got data before a Content-Length, assume the size is unknown.
@@ -55,7 +53,7 @@ static void http_callback(void *userdata, u8 op, u8 *buf, void *opaque)
log_debug("Redirect %ld.", response_code);
break;
}
- case NNWT_HTTP_FINISHED:
+ case NNWT_HTTP_FINISHED: {
http->backing->finalize(http->backing);
if (http->backing->get_size_if_known(http->backing) == 0) {
// The handle needs to consider that it could wake up from any
@@ -66,11 +64,13 @@ static void http_callback(void *userdata, u8 op, u8 *buf, void *opaque)
}
log_debug("Transfer finished.");
break;
+ }
case NNWT_HTTP_ERROR:
cch_threaded_waits_disable_all(&http->waits);
break;
+ case NNWT_HTTP_READ:
default:
- break;
+ al_assert_and_return();
}
}
diff --git a/src/codec/codec.h b/src/codec/codec.h
index 0829847..f32283e 100644
--- a/src/codec/codec.h
+++ b/src/codec/codec.h
@@ -18,12 +18,6 @@ enum {
CAMU_LANG_JAPANESE
};
-enum {
- CAMU_MASK_AUDIO = 1,
- CAMU_MASK_VIDEO = 1 << 1,
- CAMU_MASK_SUBTITLE = 1 << 2
-};
-
#ifdef CAMU_HAVE_FFMPEG
enum {
CAMU_OK = 0,
@@ -36,33 +30,31 @@ enum {
CAMU_SEEK_SIZE = AVSEEK_SIZE
};
+// Stream types must be >= 0 and < 7 because we use them as shifts for a u8 mask and 7
+// is reserved for STREAM_UNKNOWN which is different than AVMEDIA_TYPE_UNKNOWN(-1).
enum {
- CAMU_STREAM_UNKNOWN = AVMEDIA_TYPE_UNKNOWN,
CAMU_STREAM_VIDEO = AVMEDIA_TYPE_VIDEO, // 0
CAMU_STREAM_AUDIO = AVMEDIA_TYPE_AUDIO, // 1
CAMU_STREAM_SUBTITLE = AVMEDIA_TYPE_SUBTITLE,
- CAMU_STREAM_ATTACHMENT = AVMEDIA_TYPE_ATTACHMENT
+ CAMU_STREAM_ATTACHMENT = AVMEDIA_TYPE_ATTACHMENT,
+ CAMU_STREAM_UNKNOWN = 7
};
+AL_STATIC_ASSERT(stream_type_count, AVMEDIA_TYPE_NB, <, 7);
enum {
- CAMU_SAMPLE_FORMAT_NONE = AV_SAMPLE_FMT_NONE,
+ CAMU_SAMPLE_FORMAT_NONE = AV_SAMPLE_FMT_NONE, // -1
CAMU_SAMPLE_FORMAT_U8 = AV_SAMPLE_FMT_U8,
CAMU_SAMPLE_FORMAT_S16 = AV_SAMPLE_FMT_S16,
CAMU_SAMPLE_FORMAT_S32 = AV_SAMPLE_FMT_S32,
CAMU_SAMPLE_FORMAT_FLT = AV_SAMPLE_FMT_FLT,
- CAMU_SAMPLE_FORMAT_DBL = AV_SAMPLE_FMT_DBL,
CAMU_SAMPLE_FORMAT_U8P = AV_SAMPLE_FMT_U8P,
CAMU_SAMPLE_FORMAT_S16P = AV_SAMPLE_FMT_S16P,
CAMU_SAMPLE_FORMAT_S32P = AV_SAMPLE_FMT_S32P,
- CAMU_SAMPLE_FORMAT_FLTP = AV_SAMPLE_FMT_FLTP,
- CAMU_SAMPLE_FORMAT_DBLP = AV_SAMPLE_FMT_DBLP,
-#ifndef CAMU_OLD_FFMPEG
- CAMU_SAMPLE_FORMAT_S64 = AV_SAMPLE_FMT_S64,
- CAMU_SAMPLE_FORMAT_S64P = AV_SAMPLE_FMT_S64P
-#endif
+ CAMU_SAMPLE_FORMAT_FLTP = AV_SAMPLE_FMT_FLTP
};
enum {
+ CAMU_PIXEL_FORMAT_NONE = AV_PIX_FMT_NONE, // -1
CAMU_PIXEL_FORMAT_RGBA = AV_PIX_FMT_RGBA,
CAMU_PIXEL_FORMAT_RGB24 = AV_PIX_FMT_RGB24,
CAMU_PIXEL_FORMAT_RGB32 = AV_PIX_FMT_RGB32,
@@ -91,13 +83,13 @@ enum {
CAMU_SEEK_SIZE = 0x10000
};
-// Using values from FFmpeg for potentially less confusing errors.
+// Using values from FFmpeg for less confusing errors.
enum {
- CAMU_STREAM_UNKNOWN = -1,
CAMU_STREAM_VIDEO = 0,
CAMU_STREAM_AUDIO = 1,
CAMU_STREAM_SUBTITLE = 3,
- CAMU_STREAM_ATTACHMENT = 4
+ CAMU_STREAM_ATTACHMENT = 4,
+ CAMU_STREAM_UNKNOWN = 7
};
enum {
@@ -106,18 +98,15 @@ enum {
CAMU_SAMPLE_FORMAT_S16,
CAMU_SAMPLE_FORMAT_S32,
CAMU_SAMPLE_FORMAT_FLT,
- CAMU_SAMPLE_FORMAT_DBL,
CAMU_SAMPLE_FORMAT_U8P,
CAMU_SAMPLE_FORMAT_S16P,
CAMU_SAMPLE_FORMAT_S32P,
- CAMU_SAMPLE_FORMAT_FLTP,
- CAMU_SAMPLE_FORMAT_DBLP,
- CAMU_SAMPLE_FORMAT_S64,
- CAMU_SAMPLE_FORMAT_S64P
+ CAMU_SAMPLE_FORMAT_FLTP
};
enum {
- CAMU_PIXEL_FORMAT_RGBA = 0,
+ CAMU_PIXEL_FORMAT_NONE = -1,
+ CAMU_PIXEL_FORMAT_RGBA,
CAMU_PIXEL_FORMAT_RGB24,
CAMU_PIXEL_FORMAT_RGB32,
CAMU_PIXEL_FORMAT_GREYA,
@@ -344,11 +333,6 @@ static inline size_t camu_audio_format_bytes_per_sample(struct camu_audio_format
case CAMU_SAMPLE_FORMAT_FLT:
case CAMU_SAMPLE_FORMAT_FLTP:
return 4;
- case CAMU_SAMPLE_FORMAT_S64:
- case CAMU_SAMPLE_FORMAT_S64P:
- case CAMU_SAMPLE_FORMAT_DBL:
- case CAMU_SAMPLE_FORMAT_DBLP:
- return 8;
default:
al_assert_and_return(0);
}
@@ -419,7 +403,7 @@ static inline s32 camu_pixel_format_from_channels(s32 channels)
case 3: return CAMU_PIXEL_FORMAT_RGB24;
case 2: return CAMU_PIXEL_FORMAT_GREYA;
case 1: return CAMU_PIXEL_FORMAT_GREY;
- default: al_assert_and_return(CAMU_PIXEL_FORMAT_GREY);
+ default: al_assert_and_return(CAMU_PIXEL_FORMAT_NONE);
}
}
diff --git a/src/codec/ffmpeg/demuxer.c b/src/codec/ffmpeg/demuxer.c
index e5aa2f6..32068f3 100644
--- a/src/codec/ffmpeg/demuxer.c
+++ b/src/codec/ffmpeg/demuxer.c
@@ -89,6 +89,7 @@ static bool ff_demuxer_init(struct camu_demuxer *demux, struct cch_handle *handl
// @TODO:
// - 1 frame .gif.
// - No-duration video. (ex: data/test_resources/no_duration.mkv)
+ // - Incomplete video files with no duration. .temp.webm.
#define GUESS_STREAM_IS_IMAGE(stream) \
((stream->duration >= 0 && stream->nb_frames <= 1 && \
(stream->avg_frame_rate.den == 0 && stream->r_frame_rate.den > 0)) || \
@@ -105,6 +106,7 @@ static bool ff_demuxer_init(struct camu_demuxer *demux, struct cch_handle *handl
#endif
enum AVMediaType type = stream->codecpar->codec_type;
u64 duration = 0;
+ u8 mapped_type;
if (type == AVMEDIA_TYPE_AUDIO || type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_SUBTITLE) {
// Try to detect attached images.
if (type == AVMEDIA_TYPE_VIDEO && GUESS_STREAM_IS_IMAGE(stream)) {
@@ -117,15 +119,24 @@ static bool ff_demuxer_init(struct camu_demuxer *demux, struct cch_handle *handl
i, stream->duration * av_q2d(stream->time_base));
}
duration = (u64)av_rescale_q(stream->duration, stream->time_base, AV_TIME_BASE_Q);
- if (duration > av->duration) av->duration = duration;
+ if (duration > av->duration) {
+ av->duration = duration;
+ }
+ }
+ if (stream->start_time == AV_NOPTS_VALUE) {
+ stream->start_time = 0;
}
- if (stream->start_time == AV_NOPTS_VALUE) stream->start_time = 0;
- } else if (type != AVMEDIA_TYPE_ATTACHMENT) {
- type = AVMEDIA_TYPE_UNKNOWN; // Mapped to CAMU_STREAM_UNKNOWN.
+ mapped_type = type;
+ } else if (type == AVMEDIA_TYPE_ATTACHMENT) {
+ mapped_type = CAMU_STREAM_ATTACHMENT;
+ } else {
+ // CAMU_STREAM_UNKNOWN(7) is a different value than AVMEDIA_TYPE_UNKNOWN(-1) because
+ // we don't support negative stream types.
+ mapped_type = CAMU_STREAM_UNKNOWN;
}
al_array_push(av->demux.streams, ((struct camu_codec_stream){
.mode = CAMU_FFMPEG_COMPAT,
- .type = type,
+ .type = mapped_type,
.duration = duration,
.index = stream->index,
.av.stream = stream
@@ -184,12 +195,10 @@ static u64 ff_demuxer_get_duration(struct camu_demuxer *demux)
static bool ff_demuxer_seek(struct camu_demuxer *demux, u64 pos, bool bytes)
{
struct camu_ff_demuxer *av = (struct camu_ff_demuxer *)demux;
- if (av->eof) {
- // avio_flush() here probably does nothing.
- avio_flush(av->io_context);
- avformat_flush(av->format_context);
- av->eof = false;
- }
+ // avio_flush() here probably does nothing.
+ avio_flush(av->io_context);
+ avformat_flush(av->format_context);
+ if (av->eof) av->eof = false;
al_assert(pos <= INT64_MAX);
al_assert(!(bytes && av->format_context->flags & AVFMT_NO_BYTE_SEEK));
if (!bytes && pos > av->duration) pos = av->duration;
diff --git a/src/codec/ffmpeg/packet_ext.c b/src/codec/ffmpeg/packet_ext.c
index 6b66dd1..68f1b4c 100644
--- a/src/codec/ffmpeg/packet_ext.c
+++ b/src/codec/ffmpeg/packet_ext.c
@@ -221,7 +221,7 @@ void nn_packet_read_av_packet(struct nn_packet *packet, AVPacket *pkt)
enum AVPacketSideDataType type;
s64 storage_for_size;
NNWT_PACKET_READ_TYPE(packet, s64, storage_for_size);
- al_assert((sizeof(size_t) > 4 || storage_for_size <= INT32_MAX));
+ al_assert(sizeof(size_t) > 4 || storage_for_size <= INT32_MAX);
size_t size = (size_t)storage_for_size;
NNWT_PACKET_READ_DATA(packet, size, data);
NNWT_PACKET_READ_TYPE(packet, enum AVPacketSideDataType, type);
diff --git a/src/codec/stb_image.c b/src/codec/stb_image.c
index b4fabc2..1d790dd 100644
--- a/src/codec/stb_image.c
+++ b/src/codec/stb_image.c
@@ -2,6 +2,7 @@
#define STBI_MALLOC camu_page_alloc
#define STBI_FREE al_free
#define STBI_REALLOC camu_page_realloc
+#define STBI_ASSERT al_assert
#define STBI_NO_STDIO
#define STBI_NO_FAILURE_STRINGS
#define STB_IMAGE_IMPLEMENTATION
@@ -10,7 +11,7 @@
#include "stb_image.h"
#ifdef LIANA_SERVER
-#include "../../cache/entry.h"
+#include "../cache/entry.h"
static bool stbi_demuxer_init(struct camu_demuxer *demux, struct cch_handle *handle)
{
@@ -114,7 +115,14 @@ static s32 stbi_decoder_push(struct camu_decoder *dec, struct camu_codec_packet
s32 w, h, channels;
u8 *data = nn_buffer_get_ptr(packet->buffer, 0);
+#ifdef NAUNET_ON_WINDOWS
+#define STB_FORCE_RGBA
+#endif
+#ifdef STB_FORCE_RGBA
+ u8 *pixels = stbi_load_from_memory(data, packet->buffer->size, &w, &h, &channels, 4);
+#else
u8 *pixels = stbi_load_from_memory(data, packet->buffer->size, &w, &h, &channels, 0);
+#endif
if (!pixels) return CAMU_ERR_ERROR;
struct camu_video_format *fmt = &stb->stream->video.fmt;
@@ -126,8 +134,12 @@ static s32 stbi_decoder_push(struct camu_decoder *dec, struct camu_codec_packet
frame->data = pixels;
frame->video.width = (u32)w;
frame->video.height = (u32)h;
+#ifdef STB_FORCE_RGBA
+ frame->format = camu_pixel_format_from_channels(4);
+#else
frame->format = camu_pixel_format_from_channels(channels);
al_assert(frame->format == fmt->format);
+#endif
frame->pts = 0.0;
stb->callback(stb->userdata, frame);
diff --git a/src/fruits/cmc/cmc.c b/src/fruits/cmc/cmc.c
index 1baae8f..d95a2f6 100644
--- a/src/fruits/cmc/cmc.c
+++ b/src/fruits/cmc/cmc.c
@@ -94,6 +94,7 @@ static struct cmc_list *get_list_by_name(struct cmc *c, str *name)
static void client_callback(void *userdata, u8 op, void *opaque)
{
struct cmc *c = (struct cmc *)userdata;
+ // @TODO: String macro.
switch (op) {
case CAMU_CLIENT_LOGGED_IN: {
struct nn_packet *packet = (struct nn_packet *)opaque;
diff --git a/src/fruits/cmc/ui/ui.c b/src/fruits/cmc/ui/ui.c
index bc01a22..b51ec53 100644
--- a/src/fruits/cmc/ui/ui.c
+++ b/src/fruits/cmc/ui/ui.c
@@ -212,7 +212,6 @@ static bool relayout(struct cmc_ui *ui, struct ncplane *stdplane)
static s32 resize_cb(struct ncplane *p)
{
struct cmc_ui *ui = (struct cmc_ui *)ncplane_userptr(p);
- //al_assert(false);
ui->layed_out = relayout(ui, notcurses_stdplane(ui->nc));
if (ui->layed_out) {
cmc_ui_queue_render(ui);
diff --git a/src/fruits/cmc/ui/util/waveform.c b/src/fruits/cmc/ui/util/waveform.c
index a82aa45..bed34b7 100644
--- a/src/fruits/cmc/ui/util/waveform.c
+++ b/src/fruits/cmc/ui/util/waveform.c
@@ -197,7 +197,7 @@ void cmc_draw_waveform(struct cmc_list_entry *entry, u32 width, u32 height, bool
// buf[j][i][0] = 'R';
// masks_for_chars[j] &= ~RMS_BIT;
// }
-// u32 len = masks_for_chars[j] == 0 ? 2 : 5;
+// u32 len = (masks_for_chars[j] == 0) ? 2 : 5;
// al_memcpy(buf[j][i] + 1, octant_lut[masks_for_chars[j]], len);
// //slen[j] += len;
// }
diff --git a/src/fruits/cmsrv/cmsrv.c b/src/fruits/cmsrv/cmsrv.c
index 431c682..8e24701 100644
--- a/src/fruits/cmsrv/cmsrv.c
+++ b/src/fruits/cmsrv/cmsrv.c
@@ -70,7 +70,6 @@ static void render_timer_callback(void *userdata, struct nn_timer *timer)
struct cmsrv *s = (struct cmsrv *)userdata;
(void)timer;
cmsrv_ui_render(&s->ui);
- nn_timer_again(&s->render_timer);
}
static void input_poll_callback(void *userdata, s32 revents)
@@ -168,7 +167,6 @@ s32 main(s32 argc, char *argv[])
nn_poll_init(&s.input_poll, input_poll_callback, &s);
nn_poll_set(&s.input_poll, cmsrv_ui_get_input_fd(&s.ui), NNWT_POLL_READ);
nn_poll_start(&s.input_poll, &s.loop);
-
nn_timer_init(&s.render_timer, &s.loop, render_timer_callback, &s);
nn_timer_set_repeat(&s.render_timer, NNWT_TS_FROM_USEC(100000));
nn_timer_again(&s.render_timer);
diff --git a/src/fruits/cmv/cmv.c b/src/fruits/cmv/cmv.c
index 840eb6a..1fce028 100644
--- a/src/fruits/cmv/cmv.c
+++ b/src/fruits/cmv/cmv.c
@@ -8,7 +8,6 @@
#ifndef CAMU_SINK_ONLY
#include "../../server/server.h"
#endif
-#include "../../server/common.h"
#include "../../util/color_palette.h"
#ifdef CAMU_HAVE_FFMPEG
#include "../../codec/ffmpeg/common.h"
@@ -85,11 +84,11 @@ static bool parse_exe_name_params(str *exe_name, str *addr, struct lia_prefs *pr
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 |= CAMU_MASK_AUDIO;
+ prefs->enabled_mask |= 1 << CAMU_STREAM_AUDIO;
} else if (al_str_at(&value, 0) == 'v') {
- prefs->enabled_mask |= CAMU_MASK_VIDEO;
+ prefs->enabled_mask |= 1 << CAMU_STREAM_VIDEO;
} else if (al_str_at(&value, 0) == 's') {
- prefs->enabled_mask |= CAMU_MASK_SUBTITLE;
+ prefs->enabled_mask |= 1 << CAMU_STREAM_SUBTITLE;
}
al_str_free(&value);
@@ -99,9 +98,9 @@ static bool parse_exe_name_params(str *exe_name, str *addr, struct lia_prefs *pr
return true;
def:
#ifdef CAMU_HAVE_SUBTITLES
- prefs->enabled_mask = CAMU_MASK_AUDIO | CAMU_MASK_VIDEO | CAMU_MASK_SUBTITLE;
+ prefs->enabled_mask = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_VIDEO) | (1 << CAMU_STREAM_SUBTITLE);
#else
- prefs->enabled_mask = CAMU_MASK_AUDIO | CAMU_MASK_VIDEO;
+ prefs->enabled_mask = (1 << CAMU_STREAM_AUDIO) | (1 << CAMU_STREAM_VIDEO);
#endif
prefs->audio_lang = CAMU_LANG_JAPANESE;
prefs->subtitle_lang = CAMU_LANG_ENGLISH;
diff --git a/src/fruits/common.h b/src/fruits/common.h
index 1ff1b1e..1f7870d 100644
--- a/src/fruits/common.h
+++ b/src/fruits/common.h
@@ -13,8 +13,12 @@ static str CAMU_TEST_CONTROL_PATH = al_str_c("/tmp/camu_control_sock");
AL_IGNORE_WARNING_END
-//#define CAMU_TEST_TYPE NNWT_SOCKET_UNIX
-//#define CAMU_TEST_ADDR CAMU_TEST_PATH
-
+#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 7357177..45942e6 100644
--- a/src/fruits/ctv/ctv.c
+++ b/src/fruits/ctv/ctv.c
@@ -23,8 +23,12 @@ static nn_thread_result NNWT_THREADCALL event_loop_thread(void *userdata)
nn_event_loop_init(&c->loop);
struct lia_prefs *prefs = &c->desktop.sink.prefs;
- prefs->enabled_mask = CAMU_MASK_AUDIO | CAMU_MASK_VIDEO | CAMU_MASK_SUBTITLE;
- prefs->audio_lang = CAMU_LANG_ENGLISH;
+#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;
if (!camu_desktop_connect(&c->desktop, &al_str_c("ctv"), &c->loop, CAMU_TEST_TYPE, &CAMU_TEST_ADDR, CAMU_PORT)) {
diff --git a/src/fruits/droid/ctv/gradle/libs.versions.toml b/src/fruits/droid/ctv/gradle/libs.versions.toml
index 12e2469..1125570 100644
--- a/src/fruits/droid/ctv/gradle/libs.versions.toml
+++ b/src/fruits/droid/ctv/gradle/libs.versions.toml
@@ -1,5 +1,5 @@
[versions]
-agp = "8.10.1"
+agp = "8.12.3"
[libraries]
diff --git a/src/fruits/droid/ctv/gradle/wrapper/gradle-wrapper.properties b/src/fruits/droid/ctv/gradle/wrapper/gradle-wrapper.properties
index 6c7d7ac..75264fd 100644
--- a/src/fruits/droid/ctv/gradle/wrapper/gradle-wrapper.properties
+++ b/src/fruits/droid/ctv/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Wed Oct 30 14:10:25 EDT 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/src/liana/client.c b/src/liana/client.c
index d853acf..581bdba 100644
--- a/src/liana/client.c
+++ b/src/liana/client.c
@@ -43,8 +43,7 @@ static void collect_streams(struct lia_client *client, struct nn_packet *packet)
struct camu_codec_stream stream = { 0 };
str codec;
nn_packet_read_str(packet, &codec);
- // @TODO: NULL unhandled.
- stream.codec_info = camu_codec_info_by_name(&codec);
+ stream.codec_info = camu_codec_info_by_name(&codec); // @TODO: NULL unhandled.
u8 mode = nn_packet_read_u8(packet);
u8 type = nn_packet_read_u8(packet);
u64 duration = nn_packet_read_u64(packet);
diff --git a/src/liana/handlers/codec.h b/src/liana/handlers/codec.h
index f179190..097dd23 100644
--- a/src/liana/handlers/codec.h
+++ b/src/liana/handlers/codec.h
@@ -6,12 +6,6 @@
#include "handler.h"
-// FFmpeg Independence:
-// - [ ] CAMU_CODEC_* only for explicitly non-ffmpeg codecs.
-// - [ ] Map between av_guess_format() and CAMU_CODEC_
-// - [ ] Runtime selection.
-// - [ ] Audio support.
-
#ifdef LIANA_SERVER
struct lia_codec_server {
struct lia_server_handler handler;
diff --git a/src/liana/list.c b/src/liana/list.c
index 2193360..9ac7e87 100644
--- a/src/liana/list.c
+++ b/src/liana/list.c
@@ -121,11 +121,10 @@ static inline void entry_unref(struct lia_list *list, struct lia_list_entry *ent
static void unref_all_entries(struct lia_list *list)
{
+ if (list->current < 0) return;
struct lia_list_entry *entry;
al_array_foreach(list->entries, i, entry) {
- if (list->current >= 0 && i != (u32)list->current) {
- entry_unref(list, entry);
- }
+ if (i != (u32)list->current) entry_unref(list, entry);
}
}
@@ -356,7 +355,7 @@ static bool handle_add(struct lia_list *list, struct lia_list_entry *entry)
// Processing this through a SKIPTO is extremely important for consistency.
// We expect current is ended but it still must be held before moving to this entry.
// -2 cause we just added this entry above.
- al_assert((u32)list->current == (list->entries.count - 2));
+ al_assert((u32)list->current == list->entries.count - 2);
struct lia_list_cmd *cmd = list->cmd;
cmd->op = SKIPTO;
cmd->sequence = list->current;
@@ -597,12 +596,12 @@ static bool handle_shuffle(struct lia_list *list)
exchange a[i] and a[j]
*/
if (size == 2) {
- if (al_rand() % 2) {
+ if (al_random_int(0, 1) == 0) {
SWAP(al_array_at(list->entries, 0), al_array_at(list->entries, 1));
}
} else {
for (u32 i = 0; i < size - 2; i++) {
- u32 j = i + (al_rand() % (size - i));
+ u32 j = al_random_int(i, size - 1);
SWAP(al_array_at(list->entries, i), al_array_at(list->entries, j));
}
}
diff --git a/src/liana/list.h b/src/liana/list.h
index bfcd240..0f09465 100644
--- a/src/liana/list.h
+++ b/src/liana/list.h
@@ -124,7 +124,7 @@ static inline const char *lia_pause_op_name(u8 pause)
case LIANA_PAUSE_RESUME: return "PAUSE_RESUME";
case LIANA_PAUSE_PAUSE: return "PAUSE_PAUSE";
case LIANA_PAUSE_BOTH: return "PAUSE_BOTH";
- default: al_assert_and_return(NULL);
+ default: al_assert_and_return("");
}
}
diff --git a/src/liana/server.c b/src/liana/server.c
index db61e20..04ef54b 100644
--- a/src/liana/server.c
+++ b/src/liana/server.c
@@ -46,7 +46,9 @@ static void packet_pool_callback(void *userdata, struct nn_packet *packet)
{
struct lia_node_connection *conn = (struct lia_node_connection *)userdata;
if (!nn_packet_stream_send_packet(conn->stream, packet)) {
+ nn_packet_pool_lock(&conn->pool);
nn_packet_pool_return(&conn->pool, packet);
+ nn_packet_pool_unlock(&conn->pool);
}
}
@@ -92,9 +94,8 @@ static nn_thread_result NNWT_THREADCALL handler_thread(void *userdata)
static void discard_packet_callback(void *userdata, struct nn_packet_stream *stream, struct nn_packet *packet)
{
(void)userdata;
+ // @TODO: This should invalidate the connection instead of asserting.
nn_packet_stream_return_packet(stream, packet);
- // We should never be here. Although, we also shouldn't assert because
- // any erroneous connection can bring us here.
al_assert(false);
}
@@ -208,7 +209,9 @@ static void handle_connection(struct lia_node_connection *conn, struct nn_packet
al_assert(!conn->ref);
conn->ref = true;
- // Besides being wasteful, seeking to 0 on a new stream can skip data.
+ // If mask is already set, this is a reconnect. So, always seek to flush
+ // the handler. Otherwise and if seek_pos is 0, don't seek as seeking to 0
+ // on a new stream can skip data.
if (mask != 0 || seek_pos > 0) {
conn->seek_pos = seek_pos;
}
diff --git a/src/liana/vcr.c b/src/liana/vcr.c
index d8a1fc9..e95922b 100644
--- a/src/liana/vcr.c
+++ b/src/liana/vcr.c
@@ -6,6 +6,9 @@
#include "vcr.h"
#define VCR_BUFFER_BUFFERED MB(4)
+#define VCR_BUFFER_GROW_FACTOR 8
+#define VCR_BUFFER_LOW_OFFSET MB(1)
+AL_STATIC_ASSERT(buf_gt_low_offset, VCR_BUFFER_BUFFERED * VCR_BUFFER_GROW_FACTOR, >, VCR_BUFFER_LOW_OFFSET);
enum {
VCR_EXPAND_UNTOUCHED = 0,
diff --git a/src/libsink/desktop.c b/src/libsink/desktop.c
index 35c0b1b..780818c 100644
--- a/src/libsink/desktop.c
+++ b/src/libsink/desktop.c
@@ -63,25 +63,25 @@ static u8 sink_callback(void *userdata, u8 op, u8 type, void *opaque)
case CAMU_SINK_START:
switch (type) {
case CAMU_SINK_AUDIO:
+ log_info("Starting audio.");
camu_mixer_resume(mixer);
- log_info("Audio started.");
break;
case CAMU_SINK_VIDEO:
+ log_info("Starting video.");
camu_screen_set_state(scr, CAMU_SCREEN_PLAYING);
camu_screen_wake(scr);
- log_info("Video started.");
break;
}
break;
case CAMU_SINK_STOP:
switch (type) {
case CAMU_SINK_AUDIO:
+ log_info("Stopping audio.");
camu_mixer_pause(mixer);
- log_info("Audio stopped.");
break;
case CAMU_SINK_VIDEO:
+ log_info("Stopping video.");
camu_screen_set_state(scr, CAMU_SCREEN_PAUSED);
- log_info("Video stopped.");
break;
}
break;
@@ -143,7 +143,7 @@ static void log_sink_status(struct camu_desktop *c)
offset += al_snprintf(status + offset, sizeof(status) - offset, "]");
status[offset] = '\0';
- log_info("%s, window size: %ux%u, volume: %f", status, c->scr.width, c->scr.height, c->mixer.volume);
+ log_info("%s, window size: %ux%u, volume: %.2f", status, c->scr.width, c->scr.height, c->mixer.volume);
return;
notplaying:
log_info("Not playing, window size: %ux%u", c->scr.width, c->scr.height);
@@ -153,12 +153,17 @@ static void screen_callback(void *userdata, u8 op, void *opaque)
{
struct camu_desktop *c = (struct camu_desktop *)userdata;
switch (op) {
- case CAMU_SCREEN_SET_VOLUME:
- camu_mixer_set_volume(&c->mixer, *(f32 *)opaque);
+ case CAMU_SCREEN_SET_VOLUME: {
+ f32 volume = *(f32 *)opaque;
+ camu_mixer_set_volume(&c->mixer, volume);
+ log_info("Volume: %f.", volume);
break;
- case CAMU_SCREEN_OFFSET_VOLUME:
- camu_mixer_offset_volume(&c->mixer, *(f32 *)opaque);
+ }
+ case CAMU_SCREEN_OFFSET_VOLUME: {
+ f32 volume = camu_mixer_offset_volume(&c->mixer, *(f32 *)opaque);
+ log_info("Volume: %f.", volume);
break;
+ }
case CAMU_SCREEN_SKIP:
camu_sink_skip(&c->sink, *(s32 *)opaque);
break;
diff --git a/src/libsink/desktop.h b/src/libsink/desktop.h
index 279fd7b..3c66a22 100644
--- a/src/libsink/desktop.h
+++ b/src/libsink/desktop.h
@@ -4,9 +4,10 @@
#include <al/str.h>
#include <nnwt/event_loop.h>
-#include "../libsink/sink.h"
-
#include "../screen/screen.h"
+#include "../render/renderer.h"
+#include "../mixer/mixer.h"
+#include "../libsink/sink.h"
struct camu_desktop {
struct nn_event_loop *loop;
diff --git a/src/libsink/input_simulator.c b/src/libsink/input_simulator.c
index 871f0ce..19786af 100644
--- a/src/libsink/input_simulator.c
+++ b/src/libsink/input_simulator.c
@@ -1,7 +1,9 @@
#include "../screen/screen.h"
#ifdef CAMU_SCREEN_DEBUG_KEY
-#include <nnwt/thread.h>
+#define AL_LOG_SECTION "input_simulator"
+#include <al/log.h>
#include <al/random.h>
+#include <nnwt/thread.h>
#include "input_simulator.h"
@@ -10,10 +12,10 @@ static s32 quit = 1;
static struct nn_thread thread;
enum {
- SEEK,
SKIP,
BACKSKIP,
TOGGLE_PAUSE,
+ SEEK,
SHUFFLE,
MARK, // count
};
@@ -23,25 +25,38 @@ static nn_thread_result NNWT_THREADCALL input_simulation_thread(void *userdata)
struct camu_sink *sink = (struct camu_sink *)userdata;
nn_thread_set_name("input_simulator");
while (!quit) {
- nn_thread_sleep(NNWT_TS_FROM_USEC(60000));
- switch (al_rand() % MARK) {
- case SKIP:
- camu_sink_skip(sink, (al_rand() % 5));
+ //nn_thread_sleep(NNWT_TS_FROM_USEC(2000000 + al_random_int(0, 1750000)));
+ //nn_thread_sleep(NNWT_TS_FROM_USEC(60000));
+ nn_thread_sleep(NNWT_TS_FROM_USEC(30000));
+ switch (al_random_int(0, MARK - 1)) {
+ case SKIP: {
+ s32 n = al_random_int(1, 5);
+ log_debug("SKIP (n: %d).", n);
+ camu_sink_skip(sink, n);
break;
- case BACKSKIP:
- camu_sink_skip(sink, -(al_rand() % 5));
+ }
+ case BACKSKIP: {
+ s32 n = al_random_int(-5, -1);
+ log_debug("BACKSKIP (n: %d).", n);
+ camu_sink_skip(sink, n);
break;
- case SHUFFLE:
+ }
+ case SHUFFLE: {
+ log_debug("SHUFFLE.");
camu_sink_shuffle(sink);
break;
- case TOGGLE_PAUSE:
+ }
+ case TOGGLE_PAUSE: {
+ log_debug("TOGGLE_PAUSE.");
camu_sink_toggle_pause(sink);
break;
+ }
case SEEK: {
f64 pos = al_rand() / (f64)AL_RAND_MAX;
if (pos < 0.005) pos = 0.0;
if (pos > 0.995) pos = 1.0;
- else if (pos > 0.99) pos = .9999;
+ else if (pos > 0.99) pos = 0.9999;
+ log_debug("SEEK (pos: %f).", pos);
camu_sink_seek(sink, &pos, CAMU_SEEK_PERCENT);
break;
}
diff --git a/src/mixer/mixer.c b/src/mixer/mixer.c
index 7e62f8d..26471c9 100644
--- a/src/mixer/mixer.c
+++ b/src/mixer/mixer.c
@@ -105,7 +105,7 @@ void camu_mixer_set_volume(struct camu_mixer *mixer, f32 volume)
#endif
}
-void camu_mixer_offset_volume(struct camu_mixer *mixer, f32 amount)
+f32 camu_mixer_offset_volume(struct camu_mixer *mixer, f32 amount)
{
#ifdef CAMU_MIXER_THREADED
nn_mutex_lock(&mixer->mutex);
@@ -115,6 +115,7 @@ void camu_mixer_offset_volume(struct camu_mixer *mixer, f32 amount)
} else {
mixer->volume = MAX(0.f, mixer->volume + amount);
}
+ f32 volume = mixer->volume;
struct camu_audio_buffer *buf;
al_array_foreach(mixer->buffers, i, buf) {
camu_audio_buffer_set_volume(buf, mixer->volume);
@@ -122,6 +123,7 @@ void camu_mixer_offset_volume(struct camu_mixer *mixer, f32 amount)
#ifdef CAMU_MIXER_THREADED
nn_mutex_unlock(&mixer->mutex);
#endif
+ return volume;
}
f64 camu_mixer_get_latency(struct camu_mixer *mixer)
@@ -276,8 +278,7 @@ void camu_mixer_pause(struct camu_mixer *mixer)
al_atomic_store(bool)(&mixer->paused, true, AL_ATOMIC_RELEASE);
}
#ifdef CAMU_MIXER_THREADED
- // This assumes stop() blocks until the output actually stops.
- // That may not be the behavior we want to require.
+ // This requires that stop() blocks until the output actually stops.
run_queue_internal(mixer);
#endif
#ifdef CAMU_MIXER_THREADED_START_STOP
diff --git a/src/mixer/mixer.h b/src/mixer/mixer.h
index 7f443ea..d1a79b3 100644
--- a/src/mixer/mixer.h
+++ b/src/mixer/mixer.h
@@ -41,7 +41,7 @@ struct camu_mixer {
bool camu_mixer_init(struct camu_mixer *mixer, struct camu_audio *audio);
void camu_mixer_pick_format(struct camu_mixer *mixer, struct camu_resampler_format *fmt);
void camu_mixer_set_volume(struct camu_mixer *mixer, f32 volume);
-void camu_mixer_offset_volume(struct camu_mixer *mixer, f32 amount);
+f32 camu_mixer_offset_volume(struct camu_mixer *mixer, f32 amount);
f64 camu_mixer_get_latency(struct camu_mixer *mixer);
void camu_mixer_add_buffer(struct camu_mixer *mixer, struct camu_audio_buffer *buf);
void camu_mixer_remove_buffer(struct camu_mixer *mixer, struct camu_audio_buffer *buf);
diff --git a/src/portal/py/modules/patreon.py b/src/portal/py/modules/patreon.py
index bf2f0df..8ef9ad1 100644
--- a/src/portal/py/modules/patreon.py
+++ b/src/portal/py/modules/patreon.py
@@ -75,6 +75,7 @@ class PatreonUser(PatreonBase):
# image_file: https://www.patreon.com/posts/emma-again-29391136
# text_only: https://www.patreon.com/posts/hello-update-29391184
# tags
+ # Query doesn't complete.
def create_post(self, data: ParsedJson, hash: str) -> Optional[Post]:
#if 'access_rules' in data['relationships']:
diff --git a/src/portal/py/modules/twitter.py b/src/portal/py/modules/twitter.py
index f756d13..79e7f31 100644
--- a/src/portal/py/modules/twitter.py
+++ b/src/portal/py/modules/twitter.py
@@ -110,6 +110,25 @@ class TwitterModule(Module):
self.parser: QueryParser = QueryParser(self, 'user')
self.parser.add_command('user', TwitterUser)
# @TODO: Cookie import from browser.
+ self.headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; rv:128.0) Gecko/20100101 Firefox/128.0'
+ self.headers['Accept'] = '*/*'
+ self.headers['Accept-Language'] = 'en-US,en;q=0.5'
+ self.headers['Accept-Encoding'] = 'gzip, deflate, br, zstd'
+ self.headers['content-type'] = 'application/json'
+ self.headers['x-twitter-auth-type'] = 'OAuth2Session'
+ self.headers['x-csrf-token'] = ''
+ self.headers['x-twitter-client-language'] = 'en'
+ self.headers['x-twitter-active-user'] = 'yes'
+ self.headers['x-client-transaction-id'] = ''
+ self.headers['x-xp-forwarded-for'] = ''
+ self.headers['DNT'] = '1'
+ self.headers['Sec-Fetch-Dest'] = 'empty'
+ self.headers['Sec-Fetch-Mode'] = 'cors'
+ self.headers['Sec-Fetch-Site'] = 'same-origin'
+ self.headers['authorization'] = ''
+ self.headers['Connection'] = 'keep-alive'
+ self.headers['Cookie'] = ''
+ self.headers['TE'] = 'trailers'
#if cookies_path:
# cookie_jar = http.cookiejar.MozillaCookieJar()
# cookie_jar.load(filename=cookies_path, ignore_expires=True)
diff --git a/src/portal/scripts/run_py.sh b/src/portal/scripts/run_py.sh
index e745a72..87045ff 100755
--- a/src/portal/scripts/run_py.sh
+++ b/src/portal/scripts/run_py.sh
@@ -1,5 +1,5 @@
#! /usr/bin/env sh
-export CURL_CA_BUNDLE=/etc/ssl/certs/ca-bundle.crt
+#export CURL_CA_BUNDLE=/etc/ssl/certs/ca-bundle.crt
export PYTHONDONTWRITEBYTECODE=1
export PYTHONOPTIMIZE=2
export PYTHONPATH=$HOME/c/camu/src/portal/vendor/vendor
diff --git a/src/screen/screen.c b/src/screen/screen.c
index 7bd5d25..844076e 100644
--- a/src/screen/screen.c
+++ b/src/screen/screen.c
@@ -1,4 +1,5 @@
#define AL_LOG_SECTION "screen"
+//#define AL_LOG_ENABLE_TRACE
#include <al/log.h>
#include <al/math.h>
#include <nnwt/thread.h>
@@ -703,7 +704,9 @@ void camu_screen_clear(struct camu_screen *scr)
void camu_screen_set_state(struct camu_screen *scr, s32 state)
{
- // @TODO: What to do if a buffer is queued for removal and we pause here.
+ // If state = PAUSED, remove_buffer() relies on screen_wake().
+ // Unlike mixer_pause() there are no special considerations needed for
+ // the buffer queue at the point we set state to PAUSED.
al_atomic_store(s32)(&scr->state, state, AL_ATOMIC_RELEASE);
if (state == CAMU_SCREEN_PAUSED) {
al_atomic_add(u32)(&scr->force_refresh, 1, AL_ATOMIC_RELAXED);
@@ -747,6 +750,7 @@ bool camu_screen_tick(struct camu_screen *scr, bool *force)
force_refresh = al_atomic_load(u32)(&scr->force_refresh, AL_ATOMIC_ACQUIRE);
do_render |= force_refresh > 0;
*force = force_refresh > 0;
+ log_trace("tick (do_render: %s, force_refresh: %u).", BOOLSTR(do_render), force_refresh);
if (force_refresh > 0) {
al_atomic_sub(u32)(&scr->force_refresh, 1, AL_ATOMIC_RELEASE);
}
diff --git a/src/server/common.h b/src/server/common.h
index 5b27768..814b557 100644
--- a/src/server/common.h
+++ b/src/server/common.h
@@ -6,6 +6,13 @@
#define CAMU_MULTIPLEX_RPC 0x53
#define CAMU_MULTIPLEX_LIANA 0x85
+AL_STATIC_ASSERT(rpc_not_head, CAMU_MULTIPLEX_RPC, !=, 0x48); // HEAD
+AL_STATIC_ASSERT(rpc_not_get, CAMU_MULTIPLEX_RPC, !=, 0x47); // GET
+AL_STATIC_ASSERT(rpc_not_post, CAMU_MULTIPLEX_RPC, !=, 0x50); // POST
+AL_STATIC_ASSERT(liana_not_head, CAMU_MULTIPLEX_LIANA, !=, 0x48);
+AL_STATIC_ASSERT(liana_not_get, CAMU_MULTIPLEX_LIANA, !=, 0x47);
+AL_STATIC_ASSERT(liana_not_post, CAMU_MULTIPLEX_LIANA, !=, 0x50);
+
//#define CAMU_DIRECT_MODE
enum {
@@ -41,6 +48,6 @@ enum {
static inline bool camu_is_url(str *s, u32 i)
{
- return al_str_cmp(s, &al_str_c("https://"), i, 8) == 0
- || al_str_cmp(s, &al_str_c("http://"), i, 7) == 0;
+ return (al_str_cmp(s, &al_str_c("https://"), i, 8) == 0) ||
+ (al_str_cmp(s, &al_str_c("http://"), i, 7) == 0);
}
diff --git a/src/server/server.c b/src/server/server.c
index e76f6f4..6a5b22a 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -17,7 +17,7 @@
#include "common.h"
#include "db.h"
-#define RESOURCE_MAX_AGE 7
+#define RESOURCE_MAX_AGE 9
static struct camu_user *get_user_by_username(struct camu_server *server, str *username)
{
@@ -139,7 +139,7 @@ static bool identify_callback(void *userdata, struct nn_rpc_connection *conn,
}
client->user = user;
al_array_push(server->clients, client);
- log_info("User \"%.*s\" logged in.", al_str_x(&user->name));
+ log_info("User \'%.*s\' logged in.", al_str_x(&user->name));
write_initial_user_state(server, user, rpacket);
break;
}
@@ -152,7 +152,7 @@ static bool identify_callback(void *userdata, struct nn_rpc_connection *conn,
sink->server = server;
al_array_push(server->sinks, sink);
handle_toggle_sink(server, &al_str_c("default"), sink, true);
- log_info("Sink \"%.*s\" connected.", al_str_x(&name));
+ log_info("Sink \'%.*s\' connected.", al_str_x(&name));
break;
}
}
@@ -805,7 +805,7 @@ static void connection_closed_callback(void *userdata, struct nn_rpc_connection
struct camu_server_client *client;
al_array_foreach(server->clients, i, client) {
if (client->conn == conn) {
- log_info("User \"%.*s\" logged out.", al_str_x(&client->user->name));
+ log_info("User \'%.*s\' logged out.", al_str_x(&client->user->name));
cleanup_client(client);
al_array_remove_at(server->clients, i);
break;
@@ -815,7 +815,7 @@ static void connection_closed_callback(void *userdata, struct nn_rpc_connection
struct camu_server_sink *sink;
al_array_foreach(server->sinks, i, sink) {
if (sink->conn == conn) {
- log_info("Sink \"%.*s\" removed.", al_str_x(&sink->name));
+ log_info("Sink \'%.*s\' removed.", al_str_x(&sink->name));
struct lia_list *list;
al_array_foreach(server->lists, j, list) {
lia_list_remove_sink(list, sink);
diff --git a/src/util/color_palette.c b/src/util/color_palette.c
index 09c3ae3..bd923e8 100644
--- a/src/util/color_palette.c
+++ b/src/util/color_palette.c
@@ -82,7 +82,7 @@ bool camu_color_palette_init(str *path)
return false;
}
color = json_string_value(object);
- index = color[0] == '#' ? 1 : 0;
+ index = (color[0] == '#') ? 1 : 0;
value = (u32)al_str_to_long(&al_str_w((char *)color, index, 6), 16, &to_long_error);
if (!to_long_error) {
global_color_palette.colors[i] = value;
@@ -98,7 +98,7 @@ bool camu_color_palette_init(str *path)
return false;
}
color = json_string_value(object);
- index = color[0] == '#' ? 1 : 0;
+ index = (color[0] == '#') ? 1 : 0;
value = (u32)al_str_to_long(&al_str_w((char *)color, index, 6), 16, &to_long_error);
if (!to_long_error) {
global_color_palette.colors[i + 2] = value;
diff --git a/subprojects/libalabaster.wrap b/subprojects/libalabaster.wrap
index 8f26eb0..145c015 100644
--- a/subprojects/libalabaster.wrap
+++ b/subprojects/libalabaster.wrap
@@ -1,6 +1,5 @@
[wrap-git]
-directory = libalabaster-ed6fc17
+directory = libalabaster-0d1bc6c
url = https://git.akon.city/libalabaster.git
push-url = git@git.akon.city:libalabaster.git
-revision = ed6fc176a1ef6afd43eaa198c43eb813c3a81d99
-depth = 1
+revision = 0d1bc6cf7483d2a53c2d5f04d15a0ce3da85f29b
diff --git a/subprojects/libnaunet.wrap b/subprojects/libnaunet.wrap
index 6619f18..df923b0 100644
--- a/subprojects/libnaunet.wrap
+++ b/subprojects/libnaunet.wrap
@@ -1,6 +1,5 @@
[wrap-git]
-directory = libnaunet-ed40327
+directory = libnaunet-5edcdda
url = https://git.akon.city/libnaunet.git
push-url = git@git.akon.city:libnaunet.git
-revision = ed403279224444f9fb3bac68a1ab008f8e7dfd8c
-depth = 1
+revision = 5edcdda9efadd7c6ff5a47bef2897c0eb995057f
diff --git a/subprojects/miniaudio.wrap b/subprojects/miniaudio.wrap
index 7a4073d..8c89803 100644
--- a/subprojects/miniaudio.wrap
+++ b/subprojects/miniaudio.wrap
@@ -1,7 +1,7 @@
[wrap-git]
-directory = miniaudio-78cdb9c
+directory = miniaudio-0.11.23
url = https://github.com/mackron/miniaudio.git
-revision = 78cdb9c1cb1de054cf9c9b6202387eb2921158be
+revision = 0.11.23
depth = 1
patch_directory = miniaudio
diff_files = miniaudio/miniaudio_pulse_flush.diff, miniaudio/miniaudio_wasapi_drain_tweak.diff
diff --git a/subprojects/stela.wrap b/subprojects/stela.wrap
index e90a60e..7122113 100644
--- a/subprojects/stela.wrap
+++ b/subprojects/stela.wrap
@@ -1,6 +1,5 @@
[wrap-git]
-directory = stela-133eca8
+directory = stela-9d69eec
url = https://git.akon.city/stela.git
push-url = git@git.akon.city:stela.git
-revision = 133eca8c1fc95c5b0d093ce7b7331aac42feac5d
-depth = 1
+revision = 9d69eec9ab48f009a04b9ab710f57c85b240dd19