summaryrefslogtreecommitdiff
path: root/src/liana
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2025-11-22 14:22:11 -0500
committerAndrew Opalach <andrew@akon.city> 2025-11-22 14:22:11 -0500
commit0d6d13425015d78606232874498327cabcb0e4e2 (patch)
tree34682f9e9602117dac6db5f7de8c67d1207135c4 /src/liana
parentd4ea79a8622b6bf03555f186aeb1f5fa9283721f (diff)
downloadcamu-0d6d13425015d78606232874498327cabcb0e4e2.tar.gz
camu-0d6d13425015d78606232874498327cabcb0e4e2.tar.bz2
camu-0d6d13425015d78606232874498327cabcb0e4e2.zip
Fixes and cleanup around synced gapless
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/liana')
-rw-r--r--src/liana/client.c16
-rw-r--r--src/liana/common.h3
-rw-r--r--src/liana/list.c445
-rw-r--r--src/liana/list.h26
-rw-r--r--src/liana/list_cmp.h4
-rw-r--r--src/liana/vcr.c2
6 files changed, 245 insertions, 251 deletions
diff --git a/src/liana/client.c b/src/liana/client.c
index 9f7940f..380ed26 100644
--- a/src/liana/client.c
+++ b/src/liana/client.c
@@ -43,7 +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);
- stream.codec_info = camu_codec_info_by_name(&codec); // @TODO: NULL unhandled.
+ stream.codec_info = camu_codec_info_by_name(&codec);
u8 mode = nn_packet_read_u8(packet);
u8 type = nn_packet_read_u8(packet);
u64 duration = nn_packet_read_u64(packet);
@@ -70,9 +70,9 @@ static void collect_streams(struct lia_client *client, struct nn_packet *packet)
#ifdef CAMU_HAVE_FFMPEG
case CAMU_FFMPEG_COMPAT: {
enum AVCodecID codec_id = nn_packet_read_av_codec_id(packet);
- const AVCodec *codec = avcodec_find_decoder(codec_id);
+ const AVCodec *av_codec = avcodec_find_decoder(codec_id);
AVFormatContext *format_context = avformat_alloc_context();
- stream.av.stream = nn_packet_read_av_stream(format_context, codec, packet);
+ stream.av.stream = nn_packet_read_av_stream(format_context, av_codec, packet);
switch (type) {
case CAMU_STREAM_ATTACHMENT:
// Assume all the data we need is in the AVStream object.
@@ -108,6 +108,14 @@ static void collect_streams(struct lia_client *client, struct nn_packet *packet)
stream.type = type;
stream.duration = duration;
stream.index = index;
+ if (!stream.codec_info) {
+#ifdef CAMU_HAVE_FFMPEG
+ if (stream.mode == CAMU_FFMPEG_COMPAT) {
+ avformat_free_context(stream.av.format_context);
+ }
+#endif
+ continue;
+ }
al_array_push(client->streams, stream);
}
// Video streams have to come before subtitle streams.
@@ -219,7 +227,7 @@ static bool connection_callback(void *userdata, struct nn_packet_stream *stream)
// we still want to call RESUME_AT here.
struct lia_timing time = {
.at = client->at,
- .seek_pos = client->pos,
+ .pos = client->pos,
.pause = LIANA_PAUSE_NONE
};
client->callback(client->userdata, LIANA_CLIENT_RESUME_AT, NULL, &time);
diff --git a/src/liana/common.h b/src/liana/common.h
new file mode 100644
index 0000000..ec6d50a
--- /dev/null
+++ b/src/liana/common.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#define LIANA_TIMESTAMP_INVALID ((u64)-1)
diff --git a/src/liana/list.c b/src/liana/list.c
index 2430a9c..b47a30c 100644
--- a/src/liana/list.c
+++ b/src/liana/list.c
@@ -35,8 +35,8 @@ void lia_list_init(struct lia_list *list, str *name)
{
al_str_clone(&list->name, name);
list->current = -1;
- list->queued = -1;
list->idle = true;
+ list->closed = false;
list->increment = 0;
al_array_init(list->entries);
al_array_init(list->sinks);
@@ -69,18 +69,45 @@ static inline void entry_free(struct lia_list_entry *entry)
al_free(entry);
}
+static inline void entry_ref(struct lia_list *list, struct lia_list_entry *entry)
+{
+ list->callback(list->userdata, LIANA_REF_ENTRY, entry, NULL);
+}
+
+static inline void entry_unref(struct lia_list *list, struct lia_list_entry *entry)
+{
+ list->callback(list->userdata, LIANA_UNREF_ENTRY, entry, NULL);
+}
+
+static void unref_all_entries(struct lia_list *list, s32 trigger)
+{
+ struct lia_list_entry *entry;
+ s32 sequence;
+ al_array_foreach(list->entries, i, entry) {
+ sequence = (s32)i;
+ if (sequence != list->current && sequence != trigger) {
+ entry_unref(list, entry);
+ }
+ }
+}
+
static bool entry_load_and_get_duration(struct lia_list *list, struct lia_list_entry *entry, s32 sequence, bool *error)
{
- u8 status;
- list->callback(list->userdata, LIANA_LOAD_ENTRY, entry, &status);
- if (status == LIANA_ENTRY_ERRORED) {
+ u8 last_load = entry->load;
+ list->callback(list->userdata, LIANA_LOAD_ENTRY, entry, &entry->load);
+ if (entry->load == LIANA_ENTRY_ERRORED) {
// If sequence is <0 that must mean entry is not yet added to list->entries.
if (sequence >= 0) {
al_array_remove_at(list->entries, (u32)sequence);
+ // @TODO: Consider this when implementing list_remove().
+ // In the case of remove sequence 0, causing a skip to sequence 1, and sequence 1
+ // fails to load, do we properly move to -1 and an idle list.
if (list->current > sequence) {
- // @TODO: Consider this when implementing list_remove().
- // In the case of remove sequence 0, causing a skip to sequence 1, and sequence 1
- // fails to load, do we properly move to -1 and an idle list.
+ struct lia_list_sink *sink;
+ al_array_foreach(list->sinks, i, sink) {
+ al_assert(sink->set == list->current);
+ sink->set--;
+ }
list->current--;
}
struct lia_list_cmd *cmd = list->cmd;
@@ -102,35 +129,26 @@ static bool entry_load_and_get_duration(struct lia_list *list, struct lia_list_e
return false;
}
*error = false;
- if (status == LIANA_ENTRY_LOADED) {
+ if (entry->load == LIANA_ENTRY_LOADED) {
+ if (last_load != LIANA_ENTRY_LOADED) { // Newly loaded entry.
+ unref_all_entries(list, sequence);
+ }
list->callback(list->userdata, LIANA_GET_ENTRY_DURATION, entry, &entry->duration);
return true;
}
return false;
}
-static inline void entry_ref(struct lia_list *list, struct lia_list_entry *entry)
-{
- list->callback(list->userdata, LIANA_REF_ENTRY, entry, NULL);
-}
-
-static inline void entry_unref(struct lia_list *list, struct lia_list_entry *entry)
-{
- list->callback(list->userdata, LIANA_UNREF_ENTRY, entry, NULL);
-}
-
-static void unref_all_entries(struct lia_list *list)
+static void unload_all_entires(struct lia_list *list)
{
- if (list->current < 0) return;
struct lia_list_entry *entry;
al_array_foreach(list->entries, i, entry) {
- if (i != (u32)list->current) entry_unref(list, entry);
+ entry_unload(list, entry);
}
}
static inline void sink_set_entry(struct lia_list_sink *sink, struct lia_list_entry *entry, s32 sequence, struct lia_timing *time)
{
- sink->set = sequence;
sink->callback(sink->userdata, LIANA_SINK_SET, entry, sequence, time);
}
@@ -149,38 +167,32 @@ static inline void sink_unset_entry(struct lia_list_sink *sink)
sink->callback(sink->userdata, LIANA_SINK_UNSET, NULL, -1, NULL);
}
-static void list_set_current(struct lia_list *list, struct lia_list_entry *entry, s32 sequence, struct lia_timing *time)
+static bool list_set_current(struct lia_list *list, struct lia_list_entry *entry, s32 sequence)
{
+ list->idle = false;
entry_ref(list, entry);
+ al_assert(list->current != sequence);
list->current = sequence;
- list->idle = false;
- // @TODO: This is incorrect and unfinished.
- // Skipping back and forth between 2 entries will unload everything else.
- unref_all_entries(list);
- struct lia_list_sink *sink;
- al_array_foreach(list->sinks, i, sink) {
- sink_set_entry(sink, entry, sequence, time);
- }
list_signal_meta(list, entry, LIANA_META_CURRENT_CHANGED);
+ return true;
}
static void pump_queue(struct lia_list *list);
static bool handle_add_sink(struct lia_list *list, struct lia_list_sink *sink)
{
- // The list being idle is not equivalent to current being unset.
+ // The list being idle is not equivalent to current = -1.
if (list->current >= 0) {
struct lia_list_entry *current = al_array_at(list->entries, list->current);
- u64 now = nn_get_timestamp();
u8 pause;
u64 at = LIANA_TIMESTAMP_INVALID;
- u64 seek_pos = current->offset;
+ u64 pos = current->offset;
if (current->paused_at != LIANA_TIMESTAMP_INVALID) {
pause = LIANA_PAUSE_NONE;
} else {
- at = now + LIANA_BASE_DELAY;
+ at = nn_get_timestamp() + LIANA_BASE_DELAY;
if (at > current->start && at - current->start > LIANA_BASE_DELAY) {
- seek_pos += at - current->start;
+ pos += at - current->start;
} else {
at = current->start;
}
@@ -192,19 +204,23 @@ static bool handle_add_sink(struct lia_list *list, struct lia_list_sink *sink)
}
struct lia_timing time = {
.at = at,
- .seek_pos = seek_pos,
- .pause = pause,
- .ended = current->ended
+ .pos = pos,
+ .pause = pause
};
+ sink->set = list->current;
sink_set_entry(sink, current, list->current, &time);
}
al_array_push(list->sinks, sink);
return true;
}
+
static void handle_remove_sink(struct lia_list *list, void *userdata)
{
struct lia_list_cmd *cmd = list->cmd;
+ // handle_remove_sink() runs immediately, so if there's an ADD_SINK
+ // queued for this sink, remove it. It should only ever be possible to
+ // have one queued ADD_SINK for each sink.
if (cmd && cmd->op == ADD_SINK && cmd->sink->userdata == userdata) {
al_free(cmd->sink);
al_free(cmd);
@@ -224,9 +240,12 @@ static void handle_remove_sink(struct lia_list *list, void *userdata)
if (sink->userdata == userdata) {
al_array_remove_at(list->sinks, i);
al_free(sink);
- return;
+ break;
}
}
+ if (list->closed && !list->sinks.count) {
+ unload_all_entires(list);
+ }
}
static struct lia_list_entry *get_entry_from_sequence(struct lia_list *list, s32 sequence)
@@ -248,33 +267,11 @@ static s32 get_sequence_from_entry_id(struct lia_list *list, u32 id)
return -1;
}
-static bool handle_skipto(struct lia_list *list, s32 sequence, s32 index)
+static u8 skipto_entry(struct lia_list_entry *current, struct lia_list_entry *target, u64 at)
{
- if (sequence == LIANA_SEQUENCE_ANY) sequence = list->current;
- if (sequence < 0) return true; // list->current = -1
- if (sequence == index) return true;
- if (sequence != list->current) {
- log_warn("Discarding out of date skip().");
- return true;
- }
-
- struct lia_list_entry *current = get_entry_from_sequence(list, sequence);
- struct lia_list_entry *target = get_entry_from_sequence(list, index);
- al_assert(current && !current->held && current != target);
- if (!target) return true;
- bool error;
- if (!entry_load_and_get_duration(list, target, index, &error)) {
- // index might point to a different entry after an error.
- if (error) pump_queue(list);
- return false;
- }
+ al_assert(at != LIANA_TIMESTAMP_INVALID);
- u64 now = nn_get_timestamp();
- u64 at = now + LIANA_BASE_DELAY;
- u8 pause;
-
- // Resume target if it's held.
- if (target->held) {
+ if (target->held) { // Resume target if it's held.
al_assert(target->paused_at != LIANA_TIMESTAMP_INVALID);
target->paused_at = LIANA_TIMESTAMP_INVALID;
target->start = at;
@@ -287,6 +284,8 @@ static bool handle_skipto(struct lia_list *list, s32 sequence, s32 index)
al_assert(target->duration == 0 || target->paused_at != LIANA_TIMESTAMP_INVALID);
}
+ u8 pause;
+
// If current->duration = LIANA_TIMESTAMP_INVALID, handling of a static entry happens on the sink.
if (current->duration == 0 || current->paused_at != LIANA_TIMESTAMP_INVALID) {
if (target->duration == 0 || target->paused_at != LIANA_TIMESTAMP_INVALID) {
@@ -310,6 +309,7 @@ static bool handle_skipto(struct lia_list *list, s32 sequence, s32 index)
// Pause current to be resumed if it becomes the target of a skip (hold).
if (current->duration != 0 && current->paused_at == LIANA_TIMESTAMP_INVALID) {
+ al_assert(current->start != LIANA_TIMESTAMP_INVALID);
current->paused_at = at;
if (current->paused_at < current->start) {
current->paused_at = current->start;
@@ -319,28 +319,70 @@ static bool handle_skipto(struct lia_list *list, s32 sequence, s32 index)
current->held = true;
}
- log_trace("skipto(#%u-#%u): pause: %hhu, held: %s.", current->id, target->id, pause, BOOLSTR(current->held));
+ return pause;
+}
+
+static bool handle_skipto(struct lia_list *list, s32 sequence, s32 index)
+{
+ if (sequence == LIANA_SEQUENCE_ANY) {
+ sequence = list->current;
+ }
+ if (sequence < 0) return true; // list->current = -1
+ if (sequence == index) return true;
+ if (sequence != list->current) {
+ log_warn("Discarding out of date skip().");
+ return true;
+ }
+
+ struct lia_list_entry *current = get_entry_from_sequence(list, sequence);
+ struct lia_list_entry *target = get_entry_from_sequence(list, index);
+ al_assert(current && !current->held && current != target);
+ if (!target) return true;
+ bool error;
+ if (!entry_load_and_get_duration(list, target, index, &error)) {
+ if (error) {
+ // index might point to a different entry after an error.
+ return handle_skipto(list, sequence, index);
+ }
+ return false;
+ }
+
+ u64 at = nn_get_timestamp() + LIANA_PAUSE_DELAY;
+ u64 pos = target->offset;
+ u8 pause = skipto_entry(current, target, at);
+
+ log_trace("skipto(#%u-#%u): pause: %s, held: %s.", current->id, target->id,
+ lia_pause_op_name(pause), BOOLSTR(current->held));
struct lia_timing time = {
.at = at,
- .seek_pos = target->offset,
- .pause = pause,
- .ended = target->ended
+ .pos = pos,
+ .pause = pause
};
- list_set_current(list, target, index, &time);
+ struct lia_list_sink *sink;
+ al_array_foreach(list->sinks, i, sink) {
+ al_assert(sink->set != index);
+ sink->set = index;
+ sink_set_entry(sink, target, index, &time);
+ }
+
+ return list_set_current(list, target, index);
+}
- return true;
+static bool handle_skip(struct lia_list *list, s32 sequence, s32 n)
+{
+ if (sequence == LIANA_SEQUENCE_ANY) sequence = list->current;
+ if (sequence < 0) return true; // list->current = -1
+ return handle_skipto(list, sequence, sequence + n);
}
static bool handle_add(struct lia_list *list, struct lia_list_entry *entry)
{
- // @TODO: This is an easy spot to preload an entry.
- // Just fire an entry_load_and_get_duration() but ignore the immediate result.
if (list->idle) {
bool error;
if (!entry_load_and_get_duration(list, entry, -1, &error)) {
- // We passed a sequence of -1 so, on error, do not touch list->entries.
+ // We gave a sequence of -1 so, on error, don't add to list->entries.
return error;
}
}
@@ -350,16 +392,18 @@ static bool handle_add(struct lia_list *list, struct lia_list_entry *entry)
entry->start = nn_get_timestamp() + LIANA_BASE_DELAY;
struct lia_timing time = {
.at = entry->start,
- .seek_pos = entry->offset,
- .pause = LIANA_PAUSE_RESUME,
- .ended = false
+ .pos = entry->offset,
+ .pause = LIANA_PAUSE_RESUME
};
- list_set_current(list, entry, list->current + 1, &time);
- } else { // Immediately skip to the added 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);
+ struct lia_list_sink *sink;
+ al_array_foreach(list->sinks, i, sink) {
+ al_assert(sink->set == -1);
+ sink->set = 0;
+ sink_set_entry(sink, entry, 0, &time);
+ }
+ return list_set_current(list, entry, 0);
+ } else { // Skip to the added entry.
+ // This is done via SKIPTO for consistency. Ended entries must still be held.
struct lia_list_cmd *cmd = list->cmd;
cmd->op = SKIPTO;
cmd->sequence = list->current;
@@ -367,20 +411,16 @@ static bool handle_add(struct lia_list *list, struct lia_list_entry *entry)
return handle_skipto(list, cmd->sequence, cmd->arg0.i);
}
}
+ al_assert(list->current != -1);
return true;
}
-static bool handle_skip(struct lia_list *list, s32 sequence, s32 n)
-{
- if (sequence == LIANA_SEQUENCE_ANY) sequence = list->current;
- if (sequence < 0) return true; // list->current = -1
- return handle_skipto(list, sequence, sequence + n);
-}
-
static void handle_toggle_pause(struct lia_list *list, s32 sequence, f64 pts)
{
- if (sequence == LIANA_SEQUENCE_ANY) sequence = list->current;
- if (sequence < 0) return; // list->current = -1
+ if (sequence == LIANA_SEQUENCE_ANY) {
+ sequence = list->current;
+ }
+ if (sequence < 0) return;
if (sequence != list->current) {
log_warn("Discarding out of date toggle_pause().");
return;
@@ -395,7 +435,7 @@ static void handle_toggle_pause(struct lia_list *list, s32 sequence, f64 pts)
u64 at;
switch (pause) {
- case LIANA_PAUSE_PAUSE:
+ case LIANA_PAUSE_PAUSE: {
al_assert(entry->start != LIANA_TIMESTAMP_INVALID);
entry->paused_at = now + LIANA_PAUSE_DELAY;
if (entry->paused_at < entry->start) {
@@ -405,26 +445,32 @@ static void handle_toggle_pause(struct lia_list *list, s32 sequence, f64 pts)
entry->start = LIANA_TIMESTAMP_INVALID;
at = entry->paused_at;
break;
- case LIANA_PAUSE_RESUME:
+ }
+ case LIANA_PAUSE_RESUME: {
al_assert(entry->start == LIANA_TIMESTAMP_INVALID);
- entry->paused_at = LIANA_TIMESTAMP_INVALID;
entry->start = now + LIANA_PAUSE_DELAY;
+ entry->paused_at = LIANA_TIMESTAMP_INVALID;
at = entry->start;
break;
}
+ }
log_trace("toggle_pause(#%u): pts: %f, pause: %hhu.", entry->id, pts, pause);
struct lia_timing time = {
.at = at,
- .seek_pos = LIANA_TIMESTAMP_INVALID,
- .pause = pause,
- .ended = entry->ended
+ .pos = entry->offset,
+ .pause = pause
};
struct lia_list_sink *sink;
al_array_foreach(list->sinks, i, sink) {
- sink_pause_entry(sink, entry, sequence, &time);
+ if (sequence == list->current && sink->set != sequence) {
+ sink->set = sequence;
+ sink_set_entry(sink, entry, sequence, &time);
+ } else {
+ sink_pause_entry(sink, entry, sequence, &time);
+ }
}
list_signal_meta(list, entry, LIANA_META_ENTRY_PAUSED);
@@ -448,52 +494,50 @@ static void handle_seek(struct lia_list *list, s32 sequence, u32 id, u64 pos)
}
pos = CLAMP(pos, (u64)0, entry->duration);
- u64 now = nn_get_timestamp();
- u64 at = now + LIANA_BASE_DELAY;
- u8 pause = (entry->paused_at == LIANA_TIMESTAMP_INVALID) ? LIANA_PAUSE_RESUME : LIANA_PAUSE_NONE;
-
- entry->ended = false;
- entry->reset_id = get_incremental_id(list);
- entry->offset = pos;
- if (pause == LIANA_PAUSE_RESUME) {
+ u64 at = nn_get_timestamp() + LIANA_BASE_DELAY;
+ u8 pause;
+ if (entry->paused_at == LIANA_TIMESTAMP_INVALID) {
entry->start = at;
+ pause = LIANA_PAUSE_RESUME;
+ } else {
+ pause = LIANA_PAUSE_NONE;
}
- list->idle = false;
+ entry->ended = false;
+ entry->offset = pos;
+ entry->reset_token = get_incremental_id(list);
log_trace("seek(#%u): pos: %f, pause: %hhu.", entry->id, pos / 1000000.0, pause);
+ list->idle = false;
+
struct lia_timing time = {
.at = at,
- .seek_pos = pos,
- .pause = pause,
- .ended = entry->ended
+ .pos = pos,
+ .pause = pause
};
struct lia_list_sink *sink;
al_array_foreach(list->sinks, i, sink) {
if (sequence == list->current && sink->set != sequence) {
- // Entry might not be set if the sink was added after it ended.
+ sink->set = sequence;
sink_set_entry(sink, entry, sequence, &time);
+ } else {
+ sink_seek_entry(sink, entry, sequence, &time);
}
- sink_seek_entry(sink, entry, sequence, &time);
}
list_signal_meta(list, entry, LIANA_META_ENTRY_SEEKED);
}
-static bool handle_end(struct lia_list *list, u32 id, u32 reset_id)
+static bool handle_end(struct lia_list *list, u32 id, u32 reset_token)
{
s32 sequence = get_sequence_from_entry_id(list, id);
if (sequence < 0) return true;
- struct lia_list_entry *entry = get_entry_from_sequence(list, sequence);
- // @TODO: Looping.
- // Main issue is rolling back an entry that skipped onto queued
- // before it's looping state was synced. If we track which sink END
- // is coming from, we could probably handle it then.
+ struct lia_list_entry *entry = get_entry_from_sequence(list, sequence);
- if (reset_id != entry->reset_id) {
+ if (reset_token != entry->reset_token) {
log_warn("Got end() with out of order or incorrect reset id, ignoring.");
return true;
}
@@ -521,17 +565,7 @@ static bool handle_end(struct lia_list *list, u32 id, u32 reset_id)
s32 size = (s32)list->entries.count;
if (sequence == list->current) {
s32 next = sequence + 1;
- if (list->queued >= 0) {
- struct lia_list_entry *queued = al_array_at(list->entries, list->queued);
- struct lia_list_sink *sink;
- al_array_foreach(list->sinks, i, sink) {
- sink->queued = -1;
- sink->set = list->queued;
- }
- list->current = list->queued;
- list->queued = -1;
- list_signal_meta(list, queued, LIANA_META_CURRENT_CHANGED);
- } else if (next < size) {
+ if (next < size) {
struct lia_list_cmd *cmd = list->cmd;
cmd->op = SKIPTO;
cmd->sequence = sequence;
@@ -545,9 +579,10 @@ static bool handle_end(struct lia_list *list, u32 id, u32 reset_id)
return true;
}
-static bool adjust_current(struct lia_list *list, struct lia_list_entry *previous)
+static bool adjust_for_order_change(struct lia_list *list, struct lia_list_entry *previous)
{
al_assert(list->current >= 0);
+ list_signal_meta(list, previous, LIANA_META_ORDER_PROBABLY_CHANGED);
struct lia_list_cmd *cmd = list->cmd;
struct lia_list_entry *entry = NULL;
al_array_foreach(list->entries, i, entry) {
@@ -558,12 +593,16 @@ static bool adjust_current(struct lia_list *list, struct lia_list_entry *previou
cmd->op = SKIPTO;
cmd->sequence = i;
cmd->arg0.i = list->current;
+ struct lia_list_sink *sink;
+ al_array_foreach(list->sinks, j, sink) {
+ al_assert(sink->set == list->current);
+ sink->set = i;
+ }
list->current = i;
break;
}
}
- al_assert(entry && cmd->op == SKIPTO);
- list_signal_meta(list, previous, LIANA_META_ORDER_CHANGED);
+ al_assert(cmd->op == SKIPTO);
return handle_skipto(list, cmd->sequence, cmd->arg0.i);
}
@@ -577,7 +616,8 @@ static bool handle_reverse(struct lia_list *list)
if (tail <= i) break;
SWAP(al_array_at(list->entries, i), al_array_at(list->entries, tail));
}
- return adjust_current(list, previous);
+ log_trace("reverse()");
+ return adjust_for_order_change(list, previous);
}
static bool handle_sort(struct lia_list *list)
@@ -585,14 +625,14 @@ static bool handle_sort(struct lia_list *list)
if (list->current < 0) return true;
struct lia_list_entry *previous = al_array_at(list->entries, list->current);
al_array_sort(list->entries, struct lia_list_entry *, camu_db_compare);
- return adjust_current(list, previous);
+ log_trace("sort()");
+ return adjust_for_order_change(list, previous);
}
static bool handle_shuffle(struct lia_list *list)
{
- if (list->current < 0) return true;
u32 size = list->entries.count;
- if (size <= 1) return false;
+ if (list->current < 0 || size < 2) return true;
struct lia_list_entry *previous = al_array_at(list->entries, list->current);
/* https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
for i from 0 to n−2 do
@@ -609,21 +649,37 @@ static bool handle_shuffle(struct lia_list *list)
SWAP(al_array_at(list->entries, i), al_array_at(list->entries, j));
}
}
- return adjust_current(list, previous);
+ log_trace("shuffle()");
+ return adjust_for_order_change(list, previous);
}
-static void handle_unset(struct lia_list *list)
+static void unset_current(struct lia_list *list)
{
- // @TODO: Unset behavior (flag on list):
- // SKIP: Based on previous current.
- // ADD: Skip to added entry.
- // SEEK: Set and seek previous current.
- // Explicitly ignore all other events.
struct lia_list_sink *sink;
al_array_foreach(list->sinks, i, sink) {
+ if (sink->set >= 0) {
+ sink_unset_entry(sink);
+ }
sink->set = -1;
- sink_unset_entry(sink);
}
+ list->idle = true;
+}
+
+static void handle_unset(struct lia_list *list)
+{
+ unset_current(list);
+}
+
+static void handle_clear(struct lia_list *list)
+{
+ unset_current(list);
+ list->current = -1;
+ unload_all_entires(list);
+ struct lia_list_entry *entry;
+ al_array_foreach(list->entries, i, entry) {
+ entry_free(entry);
+ }
+ list->entries.count = 0;
}
static void run_queue(struct lia_list *list)
@@ -693,6 +749,7 @@ static void run_queue(struct lia_list *list)
handle_unset(list);
break;
case CLEAR:
+ handle_clear(list);
break;
}
al_free(cmd);
@@ -714,7 +771,6 @@ void lia_list_add_sink(struct lia_list *list, void (*callback)(void *, u8, struc
{
struct lia_list_sink *sink = al_alloc_object(struct lia_list_sink);
sink->set = -1;
- sink->queued = -1;
sink->callback = callback;
sink->userdata = userdata;
struct lia_list_cmd *cmd = al_alloc_object(struct lia_list_cmd);
@@ -731,19 +787,20 @@ void lia_list_remove_sink(struct lia_list *list, void *userdata)
handle_remove_sink(list, userdata);
}
-void lia_list_add(struct lia_list *list, void *opaque, u64 duration, str *brief)
+void lia_list_add(struct lia_list *list, str *brief, void *opaque, u64 duration, u8 load)
{
struct lia_list_entry *entry = al_alloc_object(struct lia_list_entry);
entry->opaque = opaque;
+ al_str_clone(&entry->brief, brief);
entry->id = get_incremental_id(list);
entry->start = LIANA_TIMESTAMP_INVALID;
entry->paused_at = LIANA_TIMESTAMP_INVALID;
entry->offset = 0;
+ entry->duration = duration;
+ entry->load = load;
entry->held = false;
entry->ended = false;
- entry->reset_id = get_incremental_id(list);
- entry->duration = duration;
- al_str_clone(&entry->brief, brief);
+ entry->reset_token = get_incremental_id(list);
entry->list = list;
struct lia_list_cmd *cmd = al_alloc_object(struct lia_list_cmd);
cmd->op = ADD;
@@ -795,12 +852,12 @@ void lia_list_seek(struct lia_list *list, s32 sequence, u32 id, u64 pos)
pump_queue(list);
}
-void lia_list_end(struct lia_list *list, u32 id, u32 reset_id)
+void lia_list_end(struct lia_list *list, u32 id, u32 reset_token)
{
struct lia_list_cmd *cmd = al_alloc_object(struct lia_list_cmd);
cmd->op = END;
cmd->arg0.u = id;
- cmd->arg1.u = reset_id;
+ cmd->arg1.u = reset_token;
al_array_push(list->command_queue, cmd);
pump_queue(list);
}
@@ -837,7 +894,6 @@ void lia_list_unset(struct lia_list *list)
pump_queue(list);
}
-/*
void lia_list_clear(struct lia_list *list)
{
struct lia_list_cmd *cmd = al_alloc_object(struct lia_list_cmd);
@@ -845,20 +901,18 @@ void lia_list_clear(struct lia_list *list)
al_array_push(list->command_queue, cmd);
pump_queue(list);
}
-*/
void lia_list_close(struct lia_list *list)
{
- // @TODO: Consider sinks being in use.
- // Delay until list->sinks is empty.
- struct lia_list_entry *entry;
- al_array_foreach(list->entries, i, entry) {
- entry_unload(list, entry);
+ list->closed = true;
+ if (!list->sinks.count) {
+ unload_all_entires(list);
}
}
void lia_list_free(struct lia_list *list)
{
+ al_assert(list->closed);
struct lia_list_cmd *cmd;
al_array_foreach(list->command_queue, i, cmd) {
al_free(cmd);
@@ -877,74 +931,3 @@ void lia_list_free(struct lia_list *list)
al_array_free(list->sinks);
al_str_free(&list->name);
}
-
-/*
-static void buffer_ahead(struct lia_list *list)
-{
- s32 size = (s32)list->entries.count;
- if (list->current >= 0 && list->current + 1 < size) {
- s32 ahead = list->current + 1;
- for (s32 i = ahead; i < MIN(ahead + LIANA_BUFFER_AHEAD, size); i++) {
- struct lia_list_entry *entry = al_array_at(list->entries, i);
- struct lia_timing time = {
- .at = LIANA_TIMESTAMP_INVALID,
- .seek_pos = entry->offset,
- .pause = LIANA_PAUSE_NONE,
- .ended = false
- };
- struct lia_list_sink *sink;
- al_array_foreach(list->sinks, i, sink) {
- sink->callback(sink->userdata, LIANA_SINK_BUFFER, entry, i, &time);
- }
- }
- }
-}
-
-static void set_queued(struct lia_list *list)
-{
- if (list->queued < 0) {
- return;
- }
-
- struct lia_list_entry *queued = al_array_at(list->entries, list->queued);
-
- bool error;
- struct lia_list_entry *current = al_array_at(list->entries, list->current);
- if (!entry_load_and_get_duration(list, current, list->current, &error)) {
- return;
- }
-
- queued->start = current->start + (current->duration - current->offset);
- u8 pause = (queued->paused_at == LIANA_TIMESTAMP_INVALID) ? LIANA_PAUSE_RESUME : LIANA_PAUSE_NONE;
- struct lia_timing time = {
- .at = queued->start,
- .seek_pos = queued->offset,
- .pause = pause
- };
-
- struct lia_list_sink *sink;
- al_array_foreach(list->sinks, i, sink) {
- if (sink->queued != list->queued) {
- sink->queued = list->queued;
- sink->callback(sink->userdata, LIANA_SINK_BUFFER_AND_QUEUE, queued, list->queued, &time);
- }
- }
-}
-
-static void evaluate_queued(struct lia_list *list)
-{
- s32 size = (s32)list->entries.count;
- s32 next = list->current + 1;
- if (next >= size || next == list->queued) {
- return;
- }
-
- bool error;
- struct lia_list_entry *queued = al_array_at(list->entries, next);
- if (!entry_load_and_get_duration(list, queued, next, &error)) {
- return;
- }
-
- list->queued = next;
-}
-*/
diff --git a/src/liana/list.h b/src/liana/list.h
index 0f09465..61dcb1d 100644
--- a/src/liana/list.h
+++ b/src/liana/list.h
@@ -4,10 +4,11 @@
#include <al/wstr.h>
#include <al/array.h>
+#include "common.h"
+
#define LIANA_SEQUENCE_ANY -1
-#define LIANA_TIMESTAMP_INVALID ((u64)-1)
-#define LIANA_BASE_DELAY 600000u // 600ms
+#define LIANA_BASE_DELAY 450000u // 450ms
#define LIANA_BASE_PING 125000u // 125ms
#define LIANA_PAUSE_DELAY LIANA_BASE_PING
#define LIANA_DELAY_IGNORE 0u
@@ -52,7 +53,7 @@ enum {
LIANA_META_ADDED_ENTRY = 0,
LIANA_META_REMOVED_ENTRY,
LIANA_META_CURRENT_CHANGED,
- LIANA_META_ORDER_CHANGED,
+ LIANA_META_ORDER_PROBABLY_CHANGED,
LIANA_META_ENTRY_PAUSED,
LIANA_META_ENTRY_SEEKED,
LIANA_META_ENTRY_ERRORED
@@ -67,28 +68,27 @@ enum {
struct lia_timing {
u64 at;
- u64 seek_pos;
+ u64 pos;
u8 pause;
- bool ended;
};
struct lia_list_entry {
void *opaque;
+ str brief;
u32 id;
u64 start;
u64 paused_at;
u64 offset;
+ u64 duration;
+ u8 load;
bool held;
bool ended;
- u32 reset_id;
- u64 duration;
- str brief;
+ u32 reset_token;
struct lia_list *list;
};
struct lia_list_sink {
s32 set;
- s32 queued;
void (*callback)(void *, u8, struct lia_list_entry *, s32, struct lia_timing *);
void *userdata;
};
@@ -106,8 +106,8 @@ struct lia_list_cmd {
struct lia_list {
str name;
s32 current;
- s32 queued;
bool idle;
+ bool closed;
u32 increment;
array(struct lia_list_entry *) entries;
array(struct lia_list_sink *) sinks;
@@ -135,18 +135,18 @@ void lia_list_pump(struct lia_list *list);
void lia_list_add_sink(struct lia_list *list, void (*callback)(void *, u8, struct lia_list_entry *, s32, struct lia_timing *), void *userdata);
void lia_list_remove_sink(struct lia_list *list, void *userdata);
-void lia_list_add(struct lia_list *list, void *opaque, u64 duration, str *brief);
+void lia_list_add(struct lia_list *list, str *brief, void *opaque, u64 duration, u8 load);
void lia_list_unset(struct lia_list *list);
void lia_list_skipto(struct lia_list *list, s32 sequence, s32 i);
void lia_list_skip(struct lia_list *list, s32 sequence, s32 n);
void lia_list_toggle_pause(struct lia_list *list, s32 sequence, f64 pts);
void lia_list_seek(struct lia_list *list, s32 sequence, u32 id, u64 pos);
-void lia_list_end(struct lia_list *list, u32 id, u32 reset_id);
+void lia_list_end(struct lia_list *list, u32 id, u32 reset_token);
void lia_list_reverse(struct lia_list *list);
void lia_list_sort(struct lia_list *list);
void lia_list_shuffle(struct lia_list *list);
-//void lia_list_clear(struct lia_list *list);
+void lia_list_clear(struct lia_list *list);
void lia_list_close(struct lia_list *list);
void lia_list_free(struct lia_list *list);
diff --git a/src/liana/list_cmp.h b/src/liana/list_cmp.h
index 30f6e7d..6bc7c69 100644
--- a/src/liana/list_cmp.h
+++ b/src/liana/list_cmp.h
@@ -55,8 +55,8 @@ static s32 camu_db_compare(const void *a, const void *b)
if (a_index > b_index) return 1;
else if (a_index < b_index) return -1;
} else {
- if (a_id > b_id) return -1;
- else if (a_id < b_id) return 1;
+ if (a_id > b_id) return 1;
+ else if (a_id < b_id) return -1;
}
return 0;
}
diff --git a/src/liana/vcr.c b/src/liana/vcr.c
index 32c33cf..13082f5 100644
--- a/src/liana/vcr.c
+++ b/src/liana/vcr.c
@@ -5,7 +5,7 @@
#include "handlers/handler.h"
#include "vcr.h"
-#include "list.h"
+#include "common.h"
#define VCR_BUFFER_BUFFERED MB(4)
#define VCR_BUFFER_GROW_FACTOR 8