diff options
Diffstat (limited to 'src/liana')
| -rw-r--r-- | src/liana/client.c | 4 | ||||
| -rw-r--r-- | src/liana/list.c | 200 | ||||
| -rw-r--r-- | src/liana/list.h | 1 | ||||
| -rw-r--r-- | src/liana/server.c | 8 | ||||
| -rw-r--r-- | src/liana/vcr.c | 10 | ||||
| -rw-r--r-- | src/liana/vcr.h | 4 |
6 files changed, 125 insertions, 102 deletions
diff --git a/src/liana/client.c b/src/liana/client.c index 0edc262..59de366 100644 --- a/src/liana/client.c +++ b/src/liana/client.c @@ -143,7 +143,7 @@ static void packet_sent_callback(void *userdata, struct aki_packet *packet) static void connection_callback(void *userdata, struct aki_packet_stream *stream) { struct lia_client *client = (struct lia_client *)userdata; - lia_vcr_start(&client->vcr, client->loop); + lia_vcr_start(&client->vcr); stream->packet_sent_callback = packet_sent_callback; struct aki_packet *packet = aki_packet_create(); aki_packet_write_u16(packet, client->id); @@ -192,7 +192,7 @@ void lia_client_connect(struct lia_client *client, struct aki_event_loop *loop, client->pos = pos; client->mask = 0; client->reconnect = false; - lia_vcr_init(&client->vcr, &client->data); + lia_vcr_init(&client->vcr, client->loop, &client->data); al_str_clone(&client->addr, addr); client->port = port; if (!aki_packet_stream_init(&client->data, type, connection_callback, connection_closed_callback, client)) { diff --git a/src/liana/list.c b/src/liana/list.c index 94e3fa2..a192848 100644 --- a/src/liana/list.c +++ b/src/liana/list.c @@ -56,7 +56,7 @@ static void pump_queue(struct lia_list *list); static bool assume_ended(struct lia_list_entry *entry, u64 at) { - if (entry->duration == 0) return true; + if (entry->ended || entry->duration == 0) return true; if (entry->paused_at == LIANA_TIMESTAMP_INVALID && entry->start != LIANA_TIMESTAMP_INVALID) { if (entry->offset > entry->duration) { return true; @@ -179,35 +179,42 @@ static bool handle_add(struct lia_list *list, struct lia_list_entry *entry) return true; } +static void adjust_current(struct lia_list *list, struct lia_list_entry *previous) +{ + al_assert(list->current >= 0); + struct lia_list_cmd *cmd = list->cmd; + struct lia_list_entry *entry; + al_array_foreach(list->entries, i, entry) { + if (entry->opaque == previous->opaque) { + list->previous = -1; + if (i == (u32)list->current) return; + cmd->op = SKIPTO; + cmd->sequence = i; + cmd->i = list->current; + list->current = i; + break; + } + } + al_assert(cmd->op == SKIPTO && !entry->held); + pump_queue(list); +} -static void unset_all(struct lia_list *list, struct lia_list_entry *previous) +static void unset_all(struct lia_list *list) { + list->current = -1; + list->previous = -1; + list->queued = -1; struct lia_list_sink *sink; al_array_foreach(list->sinks, i, sink) { sink->set = -1; sink->queued = -1; } - if (previous) { - list->cmd->op = SKIPTO; - struct lia_list_entry *entry; - al_array_foreach(list->entries, i, entry) { - if (entry->opaque == previous->opaque) { - list->cmd->i = list->current; - list->current = i; - list->cmd->sequence = i; - break; - } - } - pump_queue(list); - } } static void handle_unset(struct lia_list *list) { - unset_all(list, NULL); + unset_all(list); list->current = list->entries.size - 1; - list->previous = -1; - list->queued = -1; list->idle = true; struct lia_list_sink *sink; al_array_foreach(list->sinks, i, sink) { @@ -223,31 +230,22 @@ static struct lia_list_entry *get_entry_from_sequence(struct lia_list *list, s32 } // TODO: -// - Think about what is means for an entry to be done. never put into a pause state? -// - clock_end()?? -// - Sink needs to handle case where entry gets queued but the list already expects it to be playing -// - It's possible to know if sink->current is done during a set command, synchronously. -// So, check that when queueing an entry. -// - Can clock be ended during a queue command in any other case? -// - In the simplest case of our only operation being skip, how could client's become desynced? -// - Then with toggle pause -// - Is it safe to assert paused state on the client. -// - Do queued -// - Do seek //if (current->start != LIANA_TIMESTAMP_INVALID && current->start > ts - LIANA_BASE_PING) { static bool handle_skipto(struct lia_list *list, s32 sequence, s32 index) { if (index == list->current) return true; if (sequence == LIANA_SEQUENCE_ANY) sequence = list->current; - if (sequence == list->previous) { - // This can happen but almost certainly won't be expected behavior. + if (sequence != list->current) { + // Skipping from an entry other than current is not handled and will cause very + // confusing errors. On top of likely resulting in unexpected behavior. return true; } struct lia_list_entry *current = get_entry_from_sequence(list, sequence); struct lia_list_entry *target = get_entry_from_sequence(list, index); if (!current || !target) return true; + al_assert(current != target); if (!entry_load_and_get_duration(list, target)) { return false; } @@ -256,43 +254,51 @@ static bool handle_skipto(struct lia_list *list, s32 sequence, s32 index) u64 at = now + LIANA_BASE_DELAY; u8 pause; - // This should only happen if `start` has never been set. - if (target->start == LIANA_TIMESTAMP_INVALID && target->paused_at == LIANA_TIMESTAMP_INVALID) { - target->start = at; - } - + // Resume target if it's held. if (target->held) { + al_assert(target->paused_at != LIANA_TIMESTAMP_INVALID); target->paused_at = LIANA_TIMESTAMP_INVALID; target->held = false; } al_assert(!current->held); - bool ended = assume_ended(current, now); + // These are not equivalent to current/target->ended. + bool current_ended = assume_ended(current, now); bool target_ended = assume_ended(target, now); - al_log_info("list", "ended: %d, target_ended: %d", ended, target_ended); - if (ended || current->paused_at == LIANA_TIMESTAMP_INVALID) { - if (!ended) { - current->paused_at = at; - current->offset += current->paused_at - current->start; - current->start = LIANA_TIMESTAMP_INVALID; - current->held = true; - } + + // Current is ended or paused. + if (current_ended || current->paused_at != LIANA_TIMESTAMP_INVALID) { if (target_ended || target->paused_at != LIANA_TIMESTAMP_INVALID) { - pause = !ended ? LIANA_PAUSE_PAUSE : LIANA_PAUSE_NONE; - } else { //if (target->paused_at == LIANA_TIMESTAMP_INVALID) { + // Swap entries and ignore their clocks. + pause = LIANA_PAUSE_NONE; + } else { + // Swap entries and resume target. target->start = at; - pause = !ended ? LIANA_PAUSE_BOTH : LIANA_PAUSE_RESUME; + pause = LIANA_PAUSE_RESUME; } - } else { + } else { // Current is playing. if (target_ended || target->paused_at != LIANA_TIMESTAMP_INVALID) { - pause = LIANA_PAUSE_NONE; - } else { //if (target->paused_at == LIANA_TIMESTAMP_INVALID) { + // Pause-swap current, don't touch target's clock. + pause = LIANA_PAUSE_PAUSE; + } else { + // Pause-swap current and resume target. target->start = at; - pause = LIANA_PAUSE_RESUME; + pause = LIANA_PAUSE_BOTH; } } + // Pause current and set the held flag indicating it should be resumed + // if it becomes the target of a skip. + if (!current_ended && current->paused_at == LIANA_TIMESTAMP_INVALID) { + current->paused_at = at; + current->offset += current->paused_at - current->start; + current->start = LIANA_TIMESTAMP_INVALID; + current->held = true; + } + + al_log_info("list", "pause: %u, held: %s.", pause, BOOLSTR(current->held)); + list->current = index; list->previous = sequence; list->idle = false; @@ -301,6 +307,7 @@ static bool handle_skipto(struct lia_list *list, s32 sequence, s32 index) .at = at, .seek_pos = target->offset, .pause = pause, + .previous_ended = current_ended, .ended = target_ended }; @@ -318,7 +325,11 @@ static bool handle_skipto(struct lia_list *list, s32 sequence, s32 index) static bool handle_skip(struct lia_list *list, s32 sequence, s32 n) { if (sequence == LIANA_SEQUENCE_ANY) sequence = list->current; - return handle_skipto(list, sequence, sequence + n); + struct lia_list_cmd *cmd = list->cmd; + cmd->op = SKIPTO; + cmd->sequence = sequence; + cmd->i = sequence + n; + return handle_skipto(list, cmd->sequence, cmd->i); } static void handle_toggle_pause(struct lia_list *list, s32 sequence, f64 pts) @@ -383,45 +394,53 @@ static void handle_seek(struct lia_list *list, s32 sequence, f64 percent) } } -static void handle_end(struct lia_list *list, s32 sequence) +static bool handle_end(struct lia_list *list, s32 sequence) { al_assert(sequence != LIANA_SEQUENCE_ANY); - if (sequence != list->current) return; + s32 size = (s32)list->entries.size; - s32 next = sequence + 1; - struct lia_list_entry *current = al_array_at(list->entries, list->current); - if (current->ended) { + struct lia_list_entry *entry = al_array_at(list->entries, sequence); + if (entry->ended) { al_log_warn("list", "Got end() from an already ended resource, ignoring."); - return; + return true; } - current->offset = current->duration; - current->ended = true; - if (list->queued >= 0) { - list->current = list->queued; - list->previous = sequence; - list->queued = -1; - struct lia_list_sink *sink; - al_array_foreach(list->sinks, i, sink) { - sink->queued = -1; - } - al_log_info("list", "Now playing: %ls.", AL_WSTR_PRINTF(¤t->name)); - } else if (next < size) { - struct lia_list_cmd *cmd = al_alloc_object(struct lia_list_cmd); - cmd->op = SKIPTO; - cmd->sequence = sequence; - cmd->i = next; - al_array_push(list->queue, cmd); - } else { - list->idle = true; - struct lia_list_sink *sink; - al_array_foreach(list->sinks, i, sink) { - sink->set = -1; + entry->ended = true; + entry->offset = entry->duration; + + if (sequence == list->current) { + s32 next = sequence + 1; + if (list->queued >= 0) { + list->current = list->queued; + list->previous = sequence; + list->queued = -1; + struct lia_list_sink *sink; + al_array_foreach(list->sinks, i, sink) { + sink->queued = -1; + } + struct lia_list_entry *current = al_array_at(list->entries, list->current); + al_log_info("list", "Now playing: %ls.", AL_WSTR_PRINTF(¤t->name)); + } else if (next < size) { + struct lia_list_cmd *cmd = list->cmd; + cmd->op = SKIPTO; + cmd->sequence = sequence; + cmd->i = next; + pump_queue(list); + return false; + } else { + list->idle = true; + struct lia_list_sink *sink; + al_array_foreach(list->sinks, i, sink) { + sink->set = -1; + } } } + + return true; } static void handle_reverse(struct lia_list *list) { + if (list->current == -1) return; struct lia_list_entry *previous = al_array_at(list->entries, list->current); u32 size = list->entries.size; for (u32 i = 0; i < size; i++) { @@ -429,18 +448,20 @@ static void handle_reverse(struct lia_list *list) if (tail <= i) break; SWAP(al_array_at(list->entries, i), al_array_at(list->entries, tail)); } - unset_all(list, previous); + adjust_current(list, previous); } static void handle_sort(struct lia_list *list) { + if (list->current == -1) return; struct lia_list_entry *previous = al_array_at(list->entries, list->current); al_array_sort(list->entries, struct lia_list_entry *, camu_db_compare); - unset_all(list, previous); + adjust_current(list, previous); } static void handle_shuffle(struct lia_list *list) { + if (list->current == -1) return; struct lia_list_entry *previous = al_array_at(list->entries, list->current); u32 size = list->entries.size; if (size == 0) return; @@ -448,21 +469,19 @@ static void handle_shuffle(struct lia_list *list) u32 j = i + al_rand() / (AL_RAND_MAX / (size - i) + 1); SWAP(al_array_at(list->entries, i), al_array_at(list->entries, j)); } - unset_all(list, previous); + adjust_current(list, previous); } static void handle_clear(struct lia_list *list) { + unset_all(list); + list->idle = true; struct lia_list_entry *entry; al_array_foreach(list->entries, i, entry) { al_wstr_free(&entry->name); al_free(entry); } list->entries.size = 0; - unset_all(list, NULL); - list->current = -1; - list->previous = -1; - list->idle = true; } void pump_queue(struct lia_list *list) @@ -506,7 +525,10 @@ void pump_queue(struct lia_list *list) handle_seek(list, cmd->sequence, cmd->f); break; case END: - handle_end(list, cmd->sequence); + if (!handle_end(list, cmd->sequence)) { + // End was converted to a skip. + return; + } break; case REVERSE: handle_reverse(list); diff --git a/src/liana/list.h b/src/liana/list.h index 7a24104..6443db6 100644 --- a/src/liana/list.h +++ b/src/liana/list.h @@ -45,6 +45,7 @@ struct lia_timing { u64 at; u64 seek_pos; u8 pause; + bool previous_ended; bool ended; }; diff --git a/src/liana/server.c b/src/liana/server.c index 93ee643..e03b8ef 100644 --- a/src/liana/server.c +++ b/src/liana/server.c @@ -237,8 +237,8 @@ static void packet_callback(void *userdata, struct aki_packet_stream *stream, st conn->node = node; conn->stream = stream; conn->packet = packet; - aki_signal_init(&conn->signal, signal_callback, conn); - aki_signal_start(&conn->signal, server->loop); + aki_signal_init(&conn->signal, server->loop, signal_callback, conn); + aki_signal_start(&conn->signal); cch_entry_get_handle(node->entry, &conn->handle); conn->handler = lia_handler_by_name(cch_entry_get_liana(node->entry))->create_server_handler(); conn->errored = false; @@ -315,8 +315,8 @@ static void duration_signal_callback(void *userdata) void lia_node_get_duration(struct lia_node *node) { - aki_signal_init(&node->signal, duration_signal_callback, node); - aki_signal_start(&node->signal, node->server->loop); + aki_signal_init(&node->signal, node->server->loop, duration_signal_callback, node); + aki_signal_start(&node->signal); cch_entry_get_handle(node->entry, &node->handle); node->handler = lia_handler_by_name(cch_entry_get_liana(node->entry))->create_server_handler(); aki_thread_create(&node->thread, init_duration_thread, node); diff --git a/src/liana/vcr.c b/src/liana/vcr.c index 571fe4e..435fcba 100644 --- a/src/liana/vcr.c +++ b/src/liana/vcr.c @@ -3,7 +3,7 @@ #include "vcr.h" #include "handler.h" -#define VCR_BUFFER_BUFFERED MB(12) +#define VCR_BUFFER_BUFFERED MB(8) enum { VCR_EXPAND_UNTOUCHED = 0, @@ -28,7 +28,7 @@ static void reset_metrics(struct lia_vcr *vcr) vcr->metric.last_report_ts = 0; } -void lia_vcr_init(struct lia_vcr *vcr, struct aki_packet_stream *data) +void lia_vcr_init(struct lia_vcr *vcr, struct aki_event_loop *loop, struct aki_packet_stream *data) { al_array_init(vcr->tracks); al_atomic_store(u64)(&vcr->count, 0, AL_ATOMIC_RELAXED); @@ -36,13 +36,13 @@ void lia_vcr_init(struct lia_vcr *vcr, struct aki_packet_stream *data) vcr->mark.low = 0; vcr->expand = VCR_EXPAND_UNTOUCHED; vcr->data = data; - aki_signal_init(&vcr->signal, signal_callback, vcr); + aki_signal_init(&vcr->signal, loop, signal_callback, vcr); reset_metrics(vcr); } -void lia_vcr_start(struct lia_vcr *vcr, struct aki_event_loop *loop) +void lia_vcr_start(struct lia_vcr *vcr) { - aki_signal_start(&vcr->signal, loop); + aki_signal_start(&vcr->signal); } static aki_thread_result AKI_THREADCALL vcr_track_thread(void *userdata) diff --git a/src/liana/vcr.h b/src/liana/vcr.h index fc7fb32..8041d96 100644 --- a/src/liana/vcr.h +++ b/src/liana/vcr.h @@ -40,8 +40,8 @@ struct lia_vcr { } metric; }; -void lia_vcr_init(struct lia_vcr *vcr, struct aki_packet_stream *data); -void lia_vcr_start(struct lia_vcr *vcr, struct aki_event_loop *loop); +void lia_vcr_init(struct lia_vcr *vcr, struct aki_event_loop *loop, struct aki_packet_stream *data); +void lia_vcr_start(struct lia_vcr *vcr); void lia_vcr_add_track(struct lia_vcr *vcr, struct lia_vcr_track *track); bool lia_vcr_is_empty(struct lia_vcr *vcr); void lia_vcr_push_packet(struct lia_vcr *vcr, struct aki_packet *packet); |