summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2025-05-04 14:53:21 -0400
committerAndrew Opalach <andrew@akon.city> 2025-05-04 14:53:21 -0400
commitefbe4b52a22ea3c4458401a7acbce89a5bf29299 (patch)
tree0f2935024ed1b9166f739f9ed2222da485077316
parent2ce402fa41508d45e2d30b56502c00ef92efff7b (diff)
downloadcamu-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>
-rw-r--r--flake.lock12
-rw-r--r--flake.nix8
-rw-r--r--meson.build6
-rw-r--r--src/buffer/audio.c2
-rw-r--r--src/cache/entry.c3
-rw-r--r--src/cache/entry.h6
-rw-r--r--src/cache/handle.c22
-rw-r--r--src/cache/handle.h5
-rw-r--r--src/cache/handler.h2
-rw-r--r--src/cache/handlers/cdio.c3
-rw-r--r--src/cache/handlers/cdio.h2
-rw-r--r--src/cache/handlers/file.c3
-rw-r--r--src/cache/handlers/http.c3
-rw-r--r--src/cache/handlers/http.h2
-rw-r--r--src/cache/threaded_waits.c26
-rw-r--r--src/cache/threaded_waits.h22
-rw-r--r--src/cache/wait.h2
-rw-r--r--src/codec/codec.h2
-rw-r--r--src/codec/ffmpeg/avio.c10
-rw-r--r--src/codec/ffmpeg/common.c2
-rw-r--r--src/codec/ffmpeg/demuxer.c14
-rw-r--r--src/codec/stb_image/server.c3
-rw-r--r--src/liana/handlers/codec_server.c2
-rw-r--r--src/liana/process.c10
-rw-r--r--src/libsink/sink.c8
-rw-r--r--subprojects/packagefiles/libcue/55.diff51
-rw-r--r--subprojects/packagefiles/libcue/composer_rem.diff34
-rw-r--r--tests/cue_splitter.c192
-rw-r--r--tests/meson.build4
29 files changed, 384 insertions, 77 deletions
diff --git a/flake.lock b/flake.lock
index 23dd477..89311ee 100644
--- a/flake.lock
+++ b/flake.lock
@@ -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": {
diff --git a/flake.nix b/flake.nix
index 1dd7ea3..0c49aff 100644
--- a/flake.nix
+++ b/flake.nix
@@ -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)