diff options
| author | 2025-05-04 14:53:21 -0400 | |
|---|---|---|
| committer | 2025-05-04 14:53:21 -0400 | |
| commit | efbe4b52a22ea3c4458401a7acbce89a5bf29299 (patch) | |
| tree | 0f2935024ed1b9166f739f9ed2222da485077316 | |
| parent | 2ce402fa41508d45e2d30b56502c00ef92efff7b (diff) | |
| download | camu-efbe4b52a22ea3c4458401a7acbce89a5bf29299.tar.gz camu-efbe4b52a22ea3c4458401a7acbce89a5bf29299.tar.bz2 camu-efbe4b52a22ea3c4458401a7acbce89a5bf29299.zip | |
Prepare for cch_handle_write(), cue testing
Signed-off-by: Andrew Opalach <andrew@akon.city>
29 files changed, 384 insertions, 77 deletions
@@ -23,11 +23,11 @@ ] }, "locked": { - "lastModified": 1745987135, - "narHash": "sha256-8Up4QPuMZEJBU0eefAY+nUe7DYKQQzvaHnMpNdwRgKA=", + "lastModified": 1746177088, + "narHash": "sha256-hmHKl4meWr6ryzqQAwRD3+3Ihfb/Y/0CbK+WnE+oa6Q=", "owner": "nix-community", "repo": "home-manager", - "rev": "d2b3e6c83d457aa0e7f9344c61c3fed32bad0f7e", + "rev": "f045bd46b73c3b0ed4e46cdb6036b3d5823d7dee", "type": "github" }, "original": { @@ -91,11 +91,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1745930157, - "narHash": "sha256-y3h3NLnzRSiUkYpnfvnS669zWZLoqqI6NprtLQ+5dck=", + "lastModified": 1746141548, + "narHash": "sha256-IgBWhX7A2oJmZFIrpRuMnw5RAufVnfvOgHWgIdds+hc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "46e634be05ce9dc6d4db8e664515ba10b78151ae", + "rev": "f02fddb8acef29a8b32f10a335d44828d7825b78", "type": "github" }, "original": { @@ -27,6 +27,13 @@ ./subprojects/packagefiles/libplacebo/info_priv_hash.diff ]; }); + libcue = super.libcue.overrideAttrs (old: { + patches = [ + ./subprojects/packagefiles/libcue/55.diff + # https://github.com/lipnitsk/libcue/pull/4 + ./subprojects/packagefiles/libcue/composer_rem.diff + ]; + }); }) ]; forAllSupportedSystems = function: @@ -108,6 +115,7 @@ soxr libcdio libcdio-paranoia + libcue libdvdcss libdvdread libdvdnav diff --git a/meson.build b/meson.build index e12e909..16a6619 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,7 @@ -project('camu', ['c', 'cpp'], version: '0.01', - default_options: ['warning_level=2', 'c_std=c99']) +project('camu', 'c', version: '0.01', + meson_version: '>=0.63.0', + default_options: ['warning_level=2', 'c_std=c99'], + license: 'GPL-3.0-only') compiler = meson.get_compiler('c') cmake = import('cmake') diff --git a/src/buffer/audio.c b/src/buffer/audio.c index 28209d7..37aff4a 100644 --- a/src/buffer/audio.c +++ b/src/buffer/audio.c @@ -307,7 +307,7 @@ ptrdiff_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, ptrdif if (!buf->ignore_desync && al_atomic_load(u32)(&buf->unpause, AL_ATOMIC_ACQUIRE) > 0) { // Queuing multiple resyncs before resuming the stream will cause pops! - log_info("Forcing resync."); + log_debug("Forcing resync."); buf->pause = PAUSE_PAUSED; al_atomic_sub(u32)(&buf->unpause, 1, AL_ATOMIC_RELEASE); } diff --git a/src/cache/entry.c b/src/cache/entry.c index 0bace31..183278a 100644 --- a/src/cache/entry.c +++ b/src/cache/entry.c @@ -9,8 +9,6 @@ bool cch_entry_get_handle(struct cch_entry *entry, struct cch_handle *handle) handle->wait.disabled = false; nn_cond_init(&handle->wait.cond); nn_mutex_init(&handle->wait.lock); - nn_mutex_lock(&entry->mutex); - nn_mutex_unlock(&entry->mutex); return true; } @@ -31,7 +29,6 @@ void cch_entry_free(struct cch_entry **entry) { (*entry)->handler->free(&(*entry)->handler); (*entry)->backing->free(&(*entry)->backing); - nn_mutex_destroy(&(*entry)->mutex); al_free(*entry); *entry = NULL; } diff --git a/src/cache/entry.h b/src/cache/entry.h index eb98c2f..c74870d 100644 --- a/src/cache/entry.h +++ b/src/cache/entry.h @@ -32,6 +32,11 @@ [x] get/set_size() no longer make sense. [x] // @TODO: Unknown size is unhandled in backings. - Handle keeps reading but will eventually have a wait cut short after a backing finalize. + [ ] Separate cch_handler from cch_entry. + - Move waits to entry? + - Create cache entry with argument like file, cue, http, hls, etc. + - Handlers created internally, and run on different step. + [ ] Maybe rename cch_handler. [ ] Rethink liana field on handler. - Data format hint on backing (codec or raw+description). - Codec hints (priority)? @@ -54,7 +59,6 @@ struct cch_entry { struct cch_chapter *chapter; struct cch_backing *backing; struct cch_handler *handler; - struct nn_mutex mutex; }; bool cch_entry_get_handle(struct cch_entry *entry, struct cch_handle *handle); diff --git a/src/cache/handle.c b/src/cache/handle.c index 1ec0f47..74db306 100644 --- a/src/cache/handle.c +++ b/src/cache/handle.c @@ -19,7 +19,7 @@ static off_t wait_for_size(struct cch_handle *handle, struct cch_handler *handle return backing->get_size_if_known(backing); } -s32 cch_handle_read(struct cch_handle *handle, u8 *buf, s32 size) +s32 cch_handle_read(struct cch_handle *handle, u8 *buf, s32 count) { struct cch_handler *handler = handle->entry->handler; struct cch_backing *backing = handle->entry->backing; @@ -30,18 +30,18 @@ s32 cch_handle_read(struct cch_handle *handle, u8 *buf, s32 size) if (filesize > 0) { if (handle->pointer >= filesize) { return CAMU_ERR_EOF; - } else if (handle->pointer + size >= filesize) { - size = filesize - handle->pointer; + } else if (handle->pointer + count >= filesize) { + count = filesize - handle->pointer; } } - al_assert(size >= 0); + al_assert(count >= 0); handle->wait.start = handle->pointer; - handle->wait.end = handle->pointer + size; + handle->wait.end = handle->pointer + count; if (!handler->wait_for_range(handler, &handle->wait)) { log_trace("Wait for range (%zd-%zd) failed.", handle->wait.start, handle->wait.end); return CAMU_ERR_EOF; } - size_t available = (size_t)size; + size_t available = (size_t)count; if (backing->mode == CACHE_BACKING_MAPPED) { u8 *ptr = backing->get_ptr(backing, handle->pointer, &available); if (ptr && available > 0) { @@ -52,10 +52,18 @@ s32 cch_handle_read(struct cch_handle *handle, u8 *buf, s32 size) backing->read(backing, buf, handle->pointer, &available); } handle->pointer += available; - log_trace("read(%u), pointer: %zd.", size, handle->pointer); + log_trace("read(%u), pointer: %zd.", count, handle->pointer); return (available > 0) ? (s32)available : CAMU_ERR_EOF; } +s32 cch_handle_write(struct cch_handle *handle, const u8 *buf, s32 count) +{ + (void)handle; + (void)buf; + (void)count; + return -1; +} + off_t cch_handle_seek(struct cch_handle *handle, off_t offset, s32 whence) { struct cch_handler *handler = handle->entry->handler; diff --git a/src/cache/handle.h b/src/cache/handle.h index 0052c02..d42cd5a 100644 --- a/src/cache/handle.h +++ b/src/cache/handle.h @@ -9,10 +9,11 @@ struct cch_entry; struct cch_handle { struct cch_entry *entry; off_t pointer; - struct cch_handler_wait wait; + struct cch_wait wait; }; -s32 cch_handle_read(struct cch_handle *handle, u8 *buf, s32 size); +s32 cch_handle_read(struct cch_handle *handle, u8 *buf, s32 count); +s32 cch_handle_write(struct cch_handle *handle, const u8 *buf, s32 count); off_t cch_handle_seek(struct cch_handle *handle, off_t offset, s32 whence); bool cch_handle_can_seek(struct cch_handle *handle); void cch_handle_disable(struct cch_handle *handle); diff --git a/src/cache/handler.h b/src/cache/handler.h index 0a64573..4051449 100644 --- a/src/cache/handler.h +++ b/src/cache/handler.h @@ -8,7 +8,7 @@ struct cch_handler { bool (*can_seek)(struct cch_handler *); void (*maybe_spawn_worker)(struct cch_handler *, size_t); - bool (*wait_for_range)(struct cch_handler *, struct cch_handler_wait *); + bool (*wait_for_range)(struct cch_handler *, struct cch_wait *); void (*free)(struct cch_handler **); struct cch_entry *entry; str liana; diff --git a/src/cache/handlers/cdio.c b/src/cache/handlers/cdio.c index 9a5e698..ba7898f 100644 --- a/src/cache/handlers/cdio.c +++ b/src/cache/handlers/cdio.c @@ -99,7 +99,7 @@ static void handler_cdio_maybe_spawn_worker(struct cch_handler *handler, size_t nn_thread_create(&cdio->thread, cd_read_thread, cdio); } -static bool handler_cdio_wait_for_range(struct cch_handler *handler, struct cch_handler_wait *wait) +static bool handler_cdio_wait_for_range(struct cch_handler *handler, struct cch_wait *wait) { struct cch_handler_cdio *cdio = (struct cch_handler_cdio *)handler; return cch_threaded_wait_for_range(&cdio->waits, cdio->backing, wait); @@ -192,7 +192,6 @@ struct cch_entry *cch_handler_cdio_create(void) struct cch_entry *entry = al_alloc_object(struct cch_entry); entry->backing = backing; entry->handler = (struct cch_handler *)cdio; - nn_mutex_init(&entry->mutex); entry->handler->can_seek = handler_cdio_can_seek; entry->handler->maybe_spawn_worker = handler_cdio_maybe_spawn_worker; entry->handler->wait_for_range = handler_cdio_wait_for_range; diff --git a/src/cache/handlers/cdio.h b/src/cache/handlers/cdio.h index 650d2e5..a2734ca 100644 --- a/src/cache/handlers/cdio.h +++ b/src/cache/handlers/cdio.h @@ -17,7 +17,7 @@ struct cch_handler_cdio { struct nn_buffer buffer; atomic(s32) running; struct nn_thread thread; - struct cch_handler_waits waits; + struct cch_threaded_waits waits; }; struct cch_entry *cch_handler_cdio_create(void); diff --git a/src/cache/handlers/file.c b/src/cache/handlers/file.c index cd78a2c..345cdca 100644 --- a/src/cache/handlers/file.c +++ b/src/cache/handlers/file.c @@ -15,7 +15,7 @@ static void handler_file_maybe_spawn_worker(struct cch_handler *handler, size_t (void)index; } -static bool handler_file_wait_for_range(struct cch_handler *handler, struct cch_handler_wait *wait) +static bool handler_file_wait_for_range(struct cch_handler *handler, struct cch_wait *wait) { (void)handler; (void)wait; @@ -39,7 +39,6 @@ struct cch_entry *cch_handler_file_create(str *path, str *liana) struct cch_entry *entry = al_alloc_object(struct cch_entry); entry->backing = backing; entry->handler = (struct cch_handler *)file; - nn_mutex_init(&entry->mutex); entry->handler->can_seek = handler_file_can_seek; entry->handler->maybe_spawn_worker = handler_file_maybe_spawn_worker; entry->handler->wait_for_range = handler_file_wait_for_range; diff --git a/src/cache/handlers/http.c b/src/cache/handlers/http.c index 02f656b..e48490b 100644 --- a/src/cache/handlers/http.c +++ b/src/cache/handlers/http.c @@ -85,7 +85,7 @@ static void handler_http_maybe_spawn_worker(struct cch_handler *handler, size_t log_debug("Spawning worker for %.*s.", al_str_x(&http->url)); } -static bool handler_http_wait_for_range(struct cch_handler *handler, struct cch_handler_wait *wait) +static bool handler_http_wait_for_range(struct cch_handler *handler, struct cch_wait *wait) { struct cch_handler_http *http = (struct cch_handler_http *)handler; return cch_threaded_wait_for_range(&http->waits, http->backing, wait); @@ -116,7 +116,6 @@ struct cch_entry *cch_handler_http_create(str *url, struct nn_event_loop *loop) struct cch_entry *entry = al_alloc_object(struct cch_entry); entry->backing = backing; entry->handler = (struct cch_handler *)http; - nn_mutex_init(&entry->mutex); entry->handler->can_seek = handler_http_can_seek; entry->handler->maybe_spawn_worker = handler_http_maybe_spawn_worker; entry->handler->wait_for_range = handler_http_wait_for_range; diff --git a/src/cache/handlers/http.h b/src/cache/handlers/http.h index 2b87db9..93efdae 100644 --- a/src/cache/handlers/http.h +++ b/src/cache/handlers/http.h @@ -15,7 +15,7 @@ struct cch_handler_http { str url; off_t pointer; array(struct nn_http) requests; - struct cch_handler_waits waits; + struct cch_threaded_waits waits; }; struct cch_entry *cch_handler_http_create(str *url, struct nn_event_loop *loop); diff --git a/src/cache/threaded_waits.c b/src/cache/threaded_waits.c index 3acbef1..1626466 100644 --- a/src/cache/threaded_waits.c +++ b/src/cache/threaded_waits.c @@ -1,13 +1,13 @@ #include "threaded_waits.h" -void cch_threaded_waits_init(struct cch_handler_waits *thw) +void cch_threaded_waits_init(struct cch_threaded_waits *thw) { thw->disabled = false; nn_mutex_init(&thw->lock); al_array_init(thw->active); } -static bool wait_range_satisfied(struct cch_backing *backing, struct cch_handler_wait *wait) +static bool wait_range_satisfied(struct cch_backing *backing, struct cch_wait *wait) { if (wait->end < 0 || (backing->size > 0 && wait->end > backing->size)) { return true; @@ -21,8 +21,8 @@ static bool wait_range_satisfied(struct cch_backing *backing, struct cch_handler return false; } -bool cch_threaded_wait_for_range(struct cch_handler_waits *thw, struct cch_backing *backing, - struct cch_handler_wait *wait) +bool cch_threaded_wait_for_range(struct cch_threaded_waits *thw, struct cch_backing *backing, + struct cch_wait *wait) { off_t size = backing->get_size_if_known(backing); @@ -59,10 +59,10 @@ bool cch_threaded_wait_for_range(struct cch_handler_waits *thw, struct cch_backi return !canceled; } -void cch_threaded_waits_signal_anys(struct cch_handler_waits *thw) +void cch_threaded_waits_signal_anys(struct cch_threaded_waits *thw) { nn_mutex_lock(&thw->lock); - struct cch_handler_wait *wait; + struct cch_wait *wait; al_array_foreach_rev(thw->active, i, wait) { if (wait->end < 0) { // This will be considered a successful wait. @@ -75,11 +75,11 @@ void cch_threaded_waits_signal_anys(struct cch_handler_waits *thw) nn_mutex_unlock(&thw->lock); } -void cch_threaded_waits_evaluate(struct cch_handler_waits *thw, struct cch_backing *backing) +void cch_threaded_waits_evaluate(struct cch_threaded_waits *thw, struct cch_backing *backing) { nn_mutex_lock(&thw->lock); backing->lock(backing); - struct cch_handler_wait *wait; + struct cch_wait *wait; al_array_foreach_rev(thw->active, i, wait) { if (wait_range_satisfied(backing, wait)) { al_array_remove_at(thw->active, i); @@ -92,7 +92,7 @@ void cch_threaded_waits_evaluate(struct cch_handler_waits *thw, struct cch_backi nn_mutex_unlock(&thw->lock); } -void cch_threaded_wait_disable(struct cch_handler_wait *wait) +void cch_threaded_wait_disable(struct cch_wait *wait) { // If this wait is in active, cond_is_waiting() will be true. // So, signaling cond while holding the lock here will ensure it's removed before @@ -105,14 +105,14 @@ void cch_threaded_wait_disable(struct cch_handler_wait *wait) nn_mutex_unlock(&wait->lock); } -void cch_threaded_wait_enable(struct cch_handler_wait *wait) +void cch_threaded_wait_enable(struct cch_wait *wait) { nn_mutex_lock(&wait->lock); wait->disabled = false; nn_mutex_unlock(&wait->lock); } -void cch_threaded_waits_disable_all(struct cch_handler_waits *thw) +void cch_threaded_waits_disable_all(struct cch_threaded_waits *thw) { nn_mutex_lock(&thw->lock); @@ -124,7 +124,7 @@ void cch_threaded_waits_disable_all(struct cch_handler_waits *thw) // Disallow any further waits. thw->disabled = true; - struct cch_handler_wait *wait; + struct cch_wait *wait; al_array_foreach_rev(thw->active, i, wait) { nn_mutex_lock(&wait->lock); wait->disabled = true; @@ -135,7 +135,7 @@ void cch_threaded_waits_disable_all(struct cch_handler_waits *thw) nn_mutex_unlock(&thw->lock); } -void cch_threaded_waits_close(struct cch_handler_waits *thw) +void cch_threaded_waits_close(struct cch_threaded_waits *thw) { nn_mutex_destroy(&thw->lock); al_array_free(thw->active); diff --git a/src/cache/threaded_waits.h b/src/cache/threaded_waits.h index 1f41df2..fd9a8e6 100644 --- a/src/cache/threaded_waits.h +++ b/src/cache/threaded_waits.h @@ -6,18 +6,18 @@ #include "backing.h" #include "wait.h" -struct cch_handler_waits { +struct cch_threaded_waits { bool disabled; struct nn_mutex lock; - array(struct cch_handler_wait *) active; + array(struct cch_wait *) active; }; -void cch_threaded_waits_init(struct cch_handler_waits *thw); -bool cch_threaded_wait_for_range(struct cch_handler_waits *thw, struct cch_backing *backing, - struct cch_handler_wait *wait); -void cch_threaded_waits_signal_anys(struct cch_handler_waits *thw); -void cch_threaded_waits_evaluate(struct cch_handler_waits *thw, struct cch_backing *backing); -void cch_threaded_wait_disable(struct cch_handler_wait *wait); -void cch_threaded_wait_enable(struct cch_handler_wait *wait); -void cch_threaded_waits_disable_all(struct cch_handler_waits *thw); -void cch_threaded_waits_close(struct cch_handler_waits *thw); +void cch_threaded_waits_init(struct cch_threaded_waits *thw); +bool cch_threaded_wait_for_range(struct cch_threaded_waits *thw, struct cch_backing *backing, + struct cch_wait *wait); +void cch_threaded_waits_signal_anys(struct cch_threaded_waits *thw); +void cch_threaded_waits_evaluate(struct cch_threaded_waits *thw, struct cch_backing *backing); +void cch_threaded_wait_disable(struct cch_wait *wait); +void cch_threaded_wait_enable(struct cch_wait *wait); +void cch_threaded_waits_disable_all(struct cch_threaded_waits *thw); +void cch_threaded_waits_close(struct cch_threaded_waits *thw); diff --git a/src/cache/wait.h b/src/cache/wait.h index 143e2b9..7422bc8 100644 --- a/src/cache/wait.h +++ b/src/cache/wait.h @@ -2,7 +2,7 @@ #include <nnwt/thread.h> -struct cch_handler_wait { +struct cch_wait { off_t start, end; bool disabled; struct nn_cond cond; diff --git a/src/codec/codec.h b/src/codec/codec.h index 1c7c21f..48f83c7 100644 --- a/src/codec/codec.h +++ b/src/codec/codec.h @@ -186,7 +186,7 @@ struct camu_demuxer { bool (*init)(struct camu_demuxer *, struct cch_handle *); s32 (*get_packet)(struct camu_demuxer *, struct camu_codec_packet *); u64 (*get_duration)(struct camu_demuxer *); - bool (*seek)(struct camu_demuxer *, u64); + bool (*seek)(struct camu_demuxer *, u64, bool); void (*free)(struct camu_demuxer **); array(struct camu_codec_stream) streams; u32 subscribed; diff --git a/src/codec/ffmpeg/avio.c b/src/codec/ffmpeg/avio.c index 855a4de..154152e 100644 --- a/src/codec/ffmpeg/avio.c +++ b/src/codec/ffmpeg/avio.c @@ -2,10 +2,16 @@ #include "../../cache/handle.h" -s32 camu_avio_read(void *data, u8 *buf, s32 size) +s32 camu_avio_read(void *data, u8 *buf, s32 count) { struct cch_handle *handle = (struct cch_handle *)data; - return cch_handle_read(handle, buf, size); + return cch_handle_read(handle, buf, count); +} + +s32 camu_avio_write(void *data, const u8 *buf, s32 count) +{ + struct cch_handle *handle = (struct cch_handle *)data; + return cch_handle_write(handle, buf, count); } s64 camu_avio_seek(void *data, s64 offset, s32 whence) diff --git a/src/codec/ffmpeg/common.c b/src/codec/ffmpeg/common.c index d96dc76..259ba90 100644 --- a/src/codec/ffmpeg/common.c +++ b/src/codec/ffmpeg/common.c @@ -38,7 +38,7 @@ static void av_log_callback(void *userdata, s32 level, const char *fmt, va_list log_context.pos += ret; if ((log_context.pos > 0 && *(offset - 1) == '\n') || log_context.pos >= AV_LOG_MAX_CHUNK) { - if (level <= AV_LOG_INFO) { + if (level < AV_LOG_WARNING) { log_info(log_context.buf); } else { log_debug(log_context.buf); diff --git a/src/codec/ffmpeg/demuxer.c b/src/codec/ffmpeg/demuxer.c index d45a970..c5ca20c 100644 --- a/src/codec/ffmpeg/demuxer.c +++ b/src/codec/ffmpeg/demuxer.c @@ -95,7 +95,7 @@ static bool ff_demuxer_init(struct camu_demuxer *demux, struct cch_handle *handl if (stream->duration < 0) { stream->duration = av_rescale_q(default_duration, AV_TIME_BASE_Q, stream->time_base); al_assert(stream->duration >= 0); - log_warn("Stream #%u has an invalid duration, defaulting to %.3fs.", + log_debug("Stream #%u has an invalid duration, defaulting to %.3fs.", i, stream->duration * av_q2d(stream->time_base)); } duration = (u64)av_rescale_q(stream->duration, stream->time_base, AV_TIME_BASE_Q); @@ -162,19 +162,23 @@ static u64 ff_demuxer_get_duration(struct camu_demuxer *demux) return av->duration; } -static bool ff_demuxer_seek(struct camu_demuxer *demux, u64 pos) +static bool ff_demuxer_seek(struct camu_demuxer *demux, u64 pos, bool bytes) { struct camu_ff_demuxer *av = (struct camu_ff_demuxer *)demux; - al_assert(pos <= INT64_MAX); - if (pos > av->duration) pos = av->duration; if (av->eof) { // avio_flush() here probably does nothing. avio_flush(av->io_context); avformat_flush(av->format_context); 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; s64 ts = (s64)pos; - if (avformat_seek_file(av->format_context, -1, INT64_MIN, ts, ts, 0) < 0) { + s32 flags = bytes ? AVSEEK_FLAG_BYTE : 0; + s32 ret = avformat_seek_file(av->format_context, -1, INT64_MIN, ts, ts, flags); + if (ret < 0) { + log_error("Failed to seek resource (%s).", av_err2str(ret)); return false; } return true; diff --git a/src/codec/stb_image/server.c b/src/codec/stb_image/server.c index e2d26e8..ba50360 100644 --- a/src/codec/stb_image/server.c +++ b/src/codec/stb_image/server.c @@ -58,10 +58,11 @@ static u64 stbi_demuxer_get_duration(struct camu_demuxer *demux) return 0; } -static bool stbi_demuxer_seek(struct camu_demuxer *demux, u64 pos) +static bool stbi_demuxer_seek(struct camu_demuxer *demux, u64 pos, bool bytes) { (void)demux; (void)pos; + (void)bytes; return true; } diff --git a/src/liana/handlers/codec_server.c b/src/liana/handlers/codec_server.c index 99db398..042c05b 100644 --- a/src/liana/handlers/codec_server.c +++ b/src/liana/handlers/codec_server.c @@ -98,7 +98,7 @@ static bool codec_server_seek(struct lia_server_handler *handler, u64 pos) #ifdef LIANA_SERVER_LOOP codec->pts_offset = 0; #endif - return codec->demux->seek(codec->demux, pos); + return codec->demux->seek(codec->demux, pos, false); } static void codec_server_step(struct lia_server_handler *handler) diff --git a/src/liana/process.c b/src/liana/process.c index 9a70210..0a9f030 100644 --- a/src/liana/process.c +++ b/src/liana/process.c @@ -50,18 +50,16 @@ bool lia_prepare_visual_data(struct cch_handle *handle, struct lia_visual_data * } struct camu_codec_stream *stream; - struct camu_codec_stream *selected = NULL; al_array_foreach_ptr(demux->streams, i, stream) { if (stream->type == CAMU_STREAM_AUDIO) { - selected = stream; + demux->subscribed = 1 << stream->index; break; } } - if (!selected) goto out; - demux->subscribed = 1 << selected->index; + if (demux->subscribed == 0) goto out; struct camu_resampler_format fmt; - AVCodecParameters *codecpar = selected->av.stream->codecpar; + AVCodecParameters *codecpar = stream->av.stream->codecpar; fmt.in.format = codecpar->format; fmt.req.format = CAMU_SAMPLE_FORMAT_S16; fmt.in.sample_rate = codecpar->sample_rate; @@ -78,7 +76,7 @@ bool lia_prepare_visual_data(struct cch_handle *handle, struct lia_visual_data * nn_buffer_init(&raw.buf); raw.index = 0; - if (!dec->init(dec, NULL, selected, data_callback, &raw)) { + if (!dec->init(dec, NULL, stream, data_callback, &raw)) { goto out; } diff --git a/src/libsink/sink.c b/src/libsink/sink.c index b9b3aa6..c95096d 100644 --- a/src/libsink/sink.c +++ b/src/libsink/sink.c @@ -748,7 +748,7 @@ static void pause_and_swap_to(struct camu_sink *sink, struct camu_sink_entry *ta static bool end_entry_and_advance_queue(struct camu_sink *sink, struct camu_sink_entry *entry) { - log_info("Entry ("ENTRY_FMT") ended.", ENTRY_ARG(entry)); + log_debug("Entry ("ENTRY_FMT") ended.", ENTRY_ARG(entry)); remove_previous_if_contains(sink, entry); #ifdef CAMU_SINK_ONESHOT sink->callback(sink->userdata, CAMU_SINK_MOCK_CLOSE, 0, NULL); @@ -807,7 +807,7 @@ static void audio_buffer_callback(void *userdata, u8 op) nn_mutex_unlock(&sink->lock); break; case CAMU_BUFFER_EOF: - log_info("Audio EOF."); + log_debug("Audio EOF."); nn_mutex_lock(&sink->lock); // Having threaded outputs means anything could have happened while waiting // on the lock above. If we were locked in CLIENT_REMOVE_BUFFERS, state could very @@ -849,7 +849,7 @@ static void video_buffer_callback(void *userdata, u8 op) lia_vcr_uncork(entry->video.track); break; case CAMU_BUFFER_EOF: - log_info("Video EOF."); + log_debug("Video EOF."); bool single_frame = VIDEO_IS_SINGLE_FRAME(entry); bool swapped = false; nn_mutex_lock(&sink->lock); @@ -1234,7 +1234,7 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str lia_client_free(&entry->client); camu_audio_buffer_free(&entry->audio.buf); camu_video_buffer_free(&entry->video.buf); - log_info("Entry ("ENTRY_FMT") closed by %s.", ENTRY_ARG(entry), removed ? "force" : "cleanup"); + log_debug("Entry ("ENTRY_FMT") closed by %s.", ENTRY_ARG(entry), removed ? "force" : "cleanup"); al_free(entry); break; diff --git a/subprojects/packagefiles/libcue/55.diff b/subprojects/packagefiles/libcue/55.diff new file mode 100644 index 0000000..7c19579 --- /dev/null +++ b/subprojects/packagefiles/libcue/55.diff @@ -0,0 +1,51 @@ +diff --git a/cue_parser.y b/cue_parser.y +index bb3e181..3616908 100644 +--- a/cue_parser.y ++++ b/cue_parser.y +@@ -130,7 +130,7 @@ Cd *cue_parse_string(const char*); + %% + + cuefile +- : new_cd global_statements track_list ++ : new_cd global_statements track_list end_cd + ; + + new_cd +@@ -316,6 +316,11 @@ rem_item + | REPLAYGAIN_TRACK_GAIN + | REPLAYGAIN_TRACK_PEAK + ; ++ ++end_cd ++ : /* empty */ ++ | '\n' end_cd ++ ; + %% + + /* lexer interface */ +diff --git a/t/noncompliant.c b/t/noncompliant.c +index a8d07a1..fb1dcd4 100644 +--- a/t/noncompliant.c ++++ b/t/noncompliant.c +@@ -21,7 +21,7 @@ static char cue[] = + "PERFORMER \"The Specials\"\n" + "INDEX 00 02:47:74\n" + "FILE \"The Specials - Singles - 02 - Rudi, A Message To You.flac\" FLAC\n" +- "INDEX 01 00:00:00\n"; ++ "INDEX 01 00:00:00\n\n\n\n\n\n"; + + static char* cue_test() + { +diff --git a/t/single_idx_00.c b/t/single_idx_00.c +index b1b1c57..6eab3d3 100644 +--- a/t/single_idx_00.c ++++ b/t/single_idx_00.c +@@ -23,7 +23,7 @@ static char cue[] = + "TITLE \"Helicopter\"\n" + "PERFORMER \"Bloc Party\"\n" + "INDEX 00 07:42:69\n" +- "INDEX 01 07:44:69\n"; ++ "INDEX 01 07:44:69"; + + static char* cue_test() + { diff --git a/subprojects/packagefiles/libcue/composer_rem.diff b/subprojects/packagefiles/libcue/composer_rem.diff new file mode 100644 index 0000000..696ea17 --- /dev/null +++ b/subprojects/packagefiles/libcue/composer_rem.diff @@ -0,0 +1,34 @@ +diff --git a/cue_parser.y b/cue_parser.y +index bb3e181..a9a7b2b 100644 +--- a/cue_parser.y ++++ b/cue_parser.y +@@ -122,7 +122,8 @@ Cd *cue_parse_string(const char*); + /* REM */ + %type <ival> rem_item + %token <ival> DATE +-%token <ival> XXX_GENRE /* parsed in REM but stored in CD-TEXT */ ++%token <ival> XXX_GENRE /* XXX = parsed in REM but stored in CD-TEXT */ ++%token <ival> XXX_COMPOSER + %token <ival> REPLAYGAIN_ALBUM_GAIN + %token <ival> REPLAYGAIN_ALBUM_PEAK + %token <ival> REPLAYGAIN_TRACK_GAIN +@@ -307,6 +308,7 @@ time + rem + : rem_item STRING '\n' { rem_set($1, $2, rem); } + | XXX_GENRE STRING '\n' { cdtext_set($1, $2, cdtext); } ++ | XXX_COMPOSER STRING '\n' { cdtext_set($1, $2, cdtext); } + ; + + rem_item +diff --git a/cue_scanner.l b/cue_scanner.l +index feb7778..0bdb68f 100644 +--- a/cue_scanner.l ++++ b/cue_scanner.l +@@ -102,6 +102,7 @@ REM { BEGIN(REM); /* exclusive rules for special exceptions */ } + + <REM>DATE { BEGIN(NAME); yylval.ival = REM_DATE; return DATE; } + <REM>GENRE { BEGIN(NAME); yylval.ival = PTI_GENRE; return XXX_GENRE; } ++<REM>COMPOSER { BEGIN(NAME); yylval.ival = PTI_COMPOSER; return XXX_COMPOSER; } + <REM>REPLAYGAIN_ALBUM_GAIN { BEGIN(RPG); yylval.ival = REM_REPLAYGAIN_ALBUM_GAIN; + return REPLAYGAIN_ALBUM_GAIN; } + <REM>REPLAYGAIN_ALBUM_PEAK { BEGIN(RPG); yylval.ival = REM_REPLAYGAIN_ALBUM_PEAK; diff --git a/tests/cue_splitter.c b/tests/cue_splitter.c new file mode 100644 index 0000000..f34ec83 --- /dev/null +++ b/tests/cue_splitter.c @@ -0,0 +1,192 @@ +#define AL_LOG_SECTION "cue_splitter" +#include <al/log.h> +#include <al/lib.h> +#include <nnwt/file.h> +#include <libcue.h> + +#include "../src/codec/codec.h" +#include "../src/codec/ffmpeg/demuxer.h" +#include "../src/codec/ffmpeg/decoder.h" +#include "../src/codec/ffmpeg/encoder.h" + +struct worker_info { + str filename; + off_t start; + off_t length; + struct cch_handle handle; + struct camu_demuxer *demux; + struct camu_decoder *dec; + struct camu_encoder *enc; + struct cch_entry *output; +}; + +static void data_callback(void *userdata, struct camu_codec_frame *frame) +{ + struct worker_info *info = (struct worker_info *)userdata; + (void)frame; +} + +static nn_thread_result NNWT_THREADCALL worker_thread(void *userdata) +{ + struct worker_info *info = (struct worker_info *)userdata; + + if (!info->demux->init(info->demux, &info->handle)) { + goto err; + } + // @TOOD: Check AVFMT_NO_BYTE_SEEK. + + struct camu_codec_stream *stream; + al_array_foreach_ptr(info->demux->streams, i, stream) { + if (stream->type == CAMU_STREAM_AUDIO) { + info->demux->subscribed = 1 << stream->index; + break; + } + } + if (info->demux->subscribed == 0) { + log_error("File has no audio stream."); + goto err; + } + + if (!info->dec->init(info->dec, NULL, stream, data_callback, info)) { + goto err; + } + + AVPacket *pkt = av_packet_alloc(); + struct camu_codec_packet packet = { .av.pkt = pkt }; + s32 status; + do { + status = info->demux->get_packet(info->demux, &packet); + if (status == CAMU_OK) { + info->dec->push_av_packet(info->dec, pkt); + info->dec->process(info->dec); + av_packet_unref(pkt); + } + } while (status == CAMU_OK); + info->dec->flush(info->dec); + + return 0; +err: + log_error("Worker \"%.*s\" failed.", al_str_x(&info->filename)); + return 0; +} + +static void dump_cdtext(Cdtext *text) +{ + al_printf(" CDTEXT:\n"); + if (cdtext_get(PTI_TITLE, text)) al_printf(" TITLE: %s\n", cdtext_get(PTI_TITLE, text)); + if (cdtext_get(PTI_PERFORMER, text)) al_printf(" PERFORMER: %s\n", cdtext_get(PTI_PERFORMER, text)); + if (cdtext_get(PTI_SONGWRITER, text)) al_printf(" SONGWRITER: %s\n", cdtext_get(PTI_SONGWRITER, text)); + if (cdtext_get(PTI_COMPOSER, text)) al_printf(" COMPOSER: %s\n", cdtext_get(PTI_COMPOSER, text)); + if (cdtext_get(PTI_ARRANGER, text)) al_printf(" ARRANGER: %s\n", cdtext_get(PTI_ARRANGER, text)); + if (cdtext_get(PTI_MESSAGE, text)) al_printf(" MESSAGE: %s\n", cdtext_get(PTI_MESSAGE, text)); + if (cdtext_get(PTI_DISC_ID, text)) al_printf(" DISC_ID: %s\n", cdtext_get(PTI_DISC_ID, text)); + if (cdtext_get(PTI_GENRE, text)) al_printf(" GENRE: %s\n", cdtext_get(PTI_GENRE, text)); +} + +static void dump_rem(Rem *rem, bool track) +{ + if (!(rem_get(REM_DATE, rem) || + (track && (rem_get(REM_REPLAYGAIN_TRACK_GAIN, rem) || rem_get(REM_REPLAYGAIN_TRACK_GAIN, rem))) || + (!track && (rem_get(REM_REPLAYGAIN_ALBUM_GAIN, rem) || rem_get(REM_REPLAYGAIN_ALBUM_GAIN, rem))))) { + return; + } + al_printf(" REM:\n"); + if (rem_get(REM_DATE, rem)) { + al_printf(" DATE: %s\n", rem_get(REM_DATE, rem)); + } + if (track) { + if (rem_get(REM_REPLAYGAIN_TRACK_GAIN, rem)) { + al_printf(" REPLAYGAIN_TRACK_GAIN: %s\n", rem_get(REM_REPLAYGAIN_TRACK_GAIN, rem)); + } + if (rem_get(REM_REPLAYGAIN_TRACK_PEAK, rem)) { + al_printf(" REPLAYGAIN_TRACK_GAIN: %s\n", rem_get(REM_REPLAYGAIN_TRACK_PEAK, rem)); + } + } else { + if (rem_get(REM_REPLAYGAIN_ALBUM_GAIN, rem)) { + al_printf(" REPLAYGAIN_ALBUM_GAIN: %s\n", rem_get(REM_REPLAYGAIN_ALBUM_GAIN, rem)); + } + if (rem_get(REM_REPLAYGAIN_ALBUM_PEAK, rem)) { + al_printf(" REPLAYGAIN_ALBUM_GAIN: %s\n", rem_get(REM_REPLAYGAIN_ALBUM_PEAK, rem)); + } + } +} + +s32 main(s32 argc, char *argv[]) +{ + if (argc < 2 || argc > 3) { + al_printf("Usage: %s [sheet.cue] <output_dir> <comma,separated,track,numbers>\n", argv[0]); + return EXIT_FAILURE; + } + + str input_path = al_str_cr(argv[1]); + struct nn_file input; + if (!nn_file_open(&input, &input_path, NNWT_FILE_READONLY)) { + al_printf("File \%.*s\" doesn't exist.\n", al_str_x(&input_path)); + return EXIT_FAILURE; + } + + str output_dir = al_str_null(); + if (argc == 3) { + output_dir = al_str_cr(argv[2]); + if (!nn_dir_exists(&output_dir)) { + if (!nn_dir_create(&output_dir)) { + al_printf("Directory \"%.*s\" doesn't exist and failed to be created.\n", al_str_x(&output_dir)); + return EXIT_FAILURE; + } + } + } + + char *cue_string; + nn_file_read_as_c_str(&input, &cue_string); + Cd *cue = cue_parse_string(cue_string); + s32 tracks = cd_get_ntrack(cue); + al_printf("Cue sheet loaded with %d tracks.\n", tracks); + Cdtext *text = cd_get_cdtext(cue); + if (text) dump_cdtext(text); + Rem *rem = cd_get_rem(cue); + if (rem) dump_rem(rem, false); + for (s32 i = 1; i <= tracks; i++) { + Track *track = cd_get_track(cue, i); + al_printf("Filename: %s\n", track_get_filename(track)); + al_printf(" Start: %ld\n", track_get_start(track)); + al_printf(" Length: %ld\n", track_get_length(track)); + al_printf(" Pre-gap: %ld\n", track_get_zero_pre(track)); + al_printf(" Post-gap: %ld\n", track_get_zero_post(track)); + al_printf(" Mode: %d\n", track_get_mode(track)); + al_printf(" SubMode: %d\n", track_get_sub_mode(track)); + if (track_get_isrc(track)) { + al_printf(" ISRC: %s\n", track_get_isrc(track)); + } + text = track_get_cdtext(track); + if (text) dump_cdtext(text); + Rem *rem = track_get_rem(track); + if (rem) dump_rem(rem, true); + } + + // One cache entry to file/files, multiple handles shouldn't be an issue. + // Per thread: get_handle() -> demuxer -> encoder -> muxer + if (output_dir.length != 0) { + struct worker_info *workers = al_malloc(sizeof(struct worker_info) * tracks); + for (s32 i = 1; i <= tracks; i++) { + struct worker_info *info = &workers[i - 1]; + Track *track = cd_get_track(cue, i); + Cdtext *text = track_get_cdtext(track); + const char *title = cdtext_get(PTI_TITLE, text); + u32 id_len = al_strlen(title) + 11; + al_str_sized(&info->filename, id_len); + al_snprintf(info->filename.data, id_len, "%02d - %s.flac", i, title); + // We need to allocate an extra byte when working with snprintf. + info->filename.length = id_len - 1; + str output_path; + al_str_clone(&output_path, &output_dir); + al_str_cat(&output_path, &al_str_c("/")); + al_str_cat(&output_path, &info->filename); + // start and length from libcue ignore all gaps. + info->start = track_get_start(track); + info->length = track_get_length(track); + al_printf("start: %zd, length: %zd, %.*s\n", info->start, info->length, al_str_x(&output_path)); + } + } + + return EXIT_SUCCESS; +} diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 0000000..191ccc1 --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,4 @@ +cue_splitter_src = ['cue_splitter.c'] +libcue = dependency('libcue') +cue_splitter_deps = [common_deps, cache, codecs_server, codecs_client, libcue] +executable('cue_splitter', cue_splitter_src, dependencies: cue_splitter_deps, install: false) |