diff options
| author | 2025-10-30 14:40:08 -0400 | |
|---|---|---|
| committer | 2025-10-30 14:40:08 -0400 | |
| commit | 90da3b27d939b3b7af1cf7fed10dfaaa7e271622 (patch) | |
| tree | f0cf96c76d5c83470d2c50dd69d2d3e39a503fab /src | |
| parent | 77837d7b63240c069b3351a919a7d2af670f46cd (diff) | |
| download | camu-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>
Diffstat (limited to 'src')
34 files changed, 189 insertions, 128 deletions
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; |