diff options
45 files changed, 286 insertions, 162 deletions
@@ -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 @@ -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": { @@ -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 |