summaryrefslogtreecommitdiff
path: root/src/libsink
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsink')
-rw-r--r--src/libsink/sink.c147
1 files changed, 69 insertions, 78 deletions
diff --git a/src/libsink/sink.c b/src/libsink/sink.c
index 5038566..5bf9597 100644
--- a/src/libsink/sink.c
+++ b/src/libsink/sink.c
@@ -22,7 +22,7 @@ enum {
enum {
// Created.
- BUFFER_INIT,
+ BUFFER_INIT = 0,
// Set but not configured.
BUFFER_QUEUED,
// Ready to receive data.
@@ -115,14 +115,11 @@ static inline bool entry_video_buffer_held(struct camu_sink_entry *entry)
static void queue_cmd(struct camu_sink *sink, struct camu_sink_cmd cmd)
{
-#ifdef CAMU_SINK_NO_VIDEO
- if (cmd.value.i == CAMU_SINK_VIDEO) return;
-#endif
camu_queue_push(sink->queue, cmd);
nn_signal_send(&sink->queue_signal);
}
-static void request_video_refresh(struct camu_sink *sink)
+static void refresh_video_output(struct camu_sink *sink)
{
#ifndef CAMU_SINK_NO_VIDEO
sink->callback(sink->userdata, CAMU_SINK_REFRESH_VIDEO, 0, NULL);
@@ -207,7 +204,7 @@ static void remove_entry_video_buffer(struct camu_sink_entry *entry)
// It's possible for some of an entry's buffers to be ENDED while others are still ADDED.
// This means entry->ended and BUFFER_ENDED have two distinct considerations.
-// entry->ended: Completely ignored, needs special consideration in CLIENT_REMOVE_BUFFERS.
+// entry->ended: Completely ignored and needs special consideration in CLIENT_REMOVE_BUFFERS.
// BUFFER_ENDED: No-op'd in remove_entry_buffers() and add_or_queue_entry() but otherwise unchanged.
static void remove_entry_buffers(struct camu_sink_entry *entry)
{
@@ -584,7 +581,7 @@ static void maybe_add_to_previous(struct camu_sink *sink, struct camu_sink_entry
al_assert(!previous->ended);
// If none of the entry's buffers are added, we don't care about adding it to previous.
bool dangling_target = target == (struct camu_sink_entry *)0xb00b;
- if (dangling_target || target->ended || !(AUDIO_STATE(previous) == BUFFER_ADDED || VIDEO_STATE(previous) == BUFFER_ADDED)) {
+ if (dangling_target || target->ended || (AUDIO_STATE(previous) != BUFFER_ADDED && VIDEO_STATE(previous) != BUFFER_ADDED)) {
remove_entry_buffers(previous);
return;
}
@@ -608,7 +605,7 @@ static void do_add_entry(struct camu_sink_entry *entry)
});
if (VIDEO_EMPTY(entry)) {
// Clear the screen if skipping from a video to an audio-only entry.
- request_video_refresh(entry->sink);
+ refresh_video_output(entry->sink);
}
queue_cmd(entry->sink, (struct camu_sink_cmd){
.op = (AUDIO_EMPTY(entry) || entry->paused) ? STOP : START,
@@ -629,7 +626,7 @@ void add_audio_if_set_and_buffered(struct camu_sink_entry *entry)
break;
case BUFFER_SET_OR_BUFFERED:
AUDIO_STATE(entry) = BUFFER_ADDED;
- if (VIDEO_ADDED_OR_EMPTY(entry)) {
+ if (VIDEO_ADDED_OR_EMPTY(entry) || VIDEO_IS_SINGLE_FRAME(entry)) {
do_add_entry(entry);
}
break;
@@ -639,6 +636,7 @@ void add_audio_if_set_and_buffered(struct camu_sink_entry *entry)
void add_video_if_set_and_buffered(struct camu_sink_entry *entry)
{
// Single frame entries will be added/removed with ended set.
+ if (entry->ended) al_assert(VIDEO_IS_SINGLE_FRAME(entry));
al_assert(VIDEO_STATE(entry) != BUFFER_INIT);
al_assert(VIDEO_STATE(entry) != BUFFER_QUEUED);
al_assert(VIDEO_STATE(entry) != BUFFER_ADDED);
@@ -649,8 +647,9 @@ void add_video_if_set_and_buffered(struct camu_sink_entry *entry)
break;
case BUFFER_SET_OR_BUFFERED:
VIDEO_STATE(entry) = BUFFER_ADDED;
- if (VIDEO_IS_SINGLE_FRAME(entry)) add_entry_video_buffer(entry);
- if (AUDIO_ADDED_OR_EMPTY(entry)) {
+ if (VIDEO_IS_SINGLE_FRAME(entry)) {
+ add_entry_video_buffer(entry);
+ } else if (AUDIO_ADDED_OR_EMPTY(entry)) {
do_add_entry(entry);
}
break;
@@ -661,11 +660,12 @@ static void switch_to(struct camu_sink *sink, struct camu_sink_entry *target)
{
trace("switch_to("ENTRY_FMT"), current: "ENTRY_FMT".", ENTRY_ARG(target), ENTRY_ARG(sink->current));
- if (sink->current) {
- struct camu_sink_entry *current = sink->current;
+ bool ensure_removed = false;
+ struct camu_sink_entry *current = sink->current;
+ if (current) {
struct camu_sink_entry *detached = sink->detached;
al_assert(current != target);
- bool ensure_removed = detached || current->ended;
+ ensure_removed = detached || current->ended;
if (detached) {
al_assert(detached == current);
sink->detached = NULL;
@@ -676,43 +676,47 @@ static void switch_to(struct camu_sink *sink, struct camu_sink_entry *target)
} else if (!current->ended) {
maybe_add_to_previous(sink, current, target);
}
- if (ensure_removed) {
- if (VIDEO_IS_SINGLE_FRAME(current)) {
- remove_entry_video_buffer(current);
+ }
+
+ bool stop_video = false;
+ bool dangling_target = target == (struct camu_sink_entry *)0xb00b;
+ if (!dangling_target) {
+ if (!target->ended) {
+ remove_previous_if_contains(sink, target);
+ add_or_queue_entry(target);
+ } else {
+ if (VIDEO_IS_SINGLE_FRAME(target)) {
+ add_video_if_set_and_buffered(target);
}
- al_assert(AUDIO_NOT_ADDED(current));
- al_assert(VIDEO_NOT_ADDED(current));
+ stop_video = true;
}
}
- if (target == (struct camu_sink_entry *)0xb00b) {
- sink->current = NULL;
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = STOP,
- .value.i = CAMU_SINK_VIDEO
- });
- request_video_refresh(sink);
- return;
+ if (ensure_removed) {
+ if (VIDEO_IS_SINGLE_FRAME(current)) {
+ remove_entry_video_buffer(current);
+ }
+ al_assert(AUDIO_NOT_ADDED(current));
+ al_assert(VIDEO_NOT_ADDED(current));
}
- if (!target->ended) {
- remove_previous_if_contains(sink, target);
- add_or_queue_entry(target);
- } else {
- if (VIDEO_IS_SINGLE_FRAME(target)) {
- add_video_if_set_and_buffered(target);
- } else {
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = STOP,
- .value.i = CAMU_SINK_VIDEO
- });
+ if (!dangling_target) {
+ sink->current = target;
+ sink->current->audio.ignore_paused = false;
+ if (!sink->current->paused) {
+ camu_audio_buffer_resync(&sink->current->audio.buf);
}
+ } else {
+ sink->current = NULL;
+ stop_video = true;
}
- sink->current = target;
- sink->current->audio.ignore_paused = false;
- if (!sink->current->paused) {
- camu_audio_buffer_resync(&sink->current->audio.buf);
+ if (stop_video) {
+ queue_cmd(sink, (struct camu_sink_cmd){
+ .op = STOP,
+ .value.i = CAMU_SINK_VIDEO
+ });
+ refresh_video_output(sink);
}
}
@@ -798,7 +802,7 @@ static void audio_buffer_callback(void *userdata, u8 op)
nn_mutex_lock(&sink->lock);
// This buffer's state could be ADDED, SET_OR_BUFFERED, or CONFIGURED.
// Having threaded outputs means anything could have happened while waiting on the lock.
- // If we were waiting in CLIENT_REMOVE_BUFFERS, state could very well be CONFIGURED.
+ // If we were waiting in CLIENT_REMOVE_BUFFERS, state could very well be CONFIGURED here.
if (AUDIO_STATE(entry) == BUFFER_ADDED) {
remove_entry_audio_buffer(entry);
}
@@ -824,13 +828,12 @@ static void video_buffer_callback(void *userdata, u8 op)
struct camu_sink_entry *entry = (struct camu_sink_entry *)userdata;
struct camu_sink *sink = entry->sink;
switch (op) {
- case CAMU_BUFFER_BUFFERED: {
+ case CAMU_BUFFER_BUFFERED:
lia_vcr_set_buffered(entry->video.track);
nn_mutex_lock(&sink->lock);
add_video_if_set_and_buffered(entry);
nn_mutex_unlock(&sink->lock);
break;
- }
case CAMU_BUFFER_CORK:
lia_vcr_cork(entry->video.track);
break;
@@ -903,7 +906,8 @@ static void evaluate_and_set_buffer_params(struct camu_sink *sink, struct camu_s
if (!AUDIO_EMPTY(entry) && !ignore_video) {
f64 audio = camu_mixer_get_latency(sink->audio.mixer);
s32 frames = audio / entry->video.buf.avg_frame_duration;
- frames -= sink->video.renderer->get_latency(sink->video.renderer);
+ struct camu_renderer *renderer = sink->video.renderer;
+ if (renderer) frames -= renderer->get_latency(renderer);
camu_video_buffer_set_latency(&entry->video.buf, -frames);
}
// If we're local we don't have to worry about syncing audio-only entries.
@@ -915,7 +919,8 @@ static void evaluate_and_set_buffer_params(struct camu_sink *sink, struct camu_s
f64 audio = camu_mixer_get_latency(sink->audio.mixer);
if (!VIDEO_EMPTY(entry)) {
s32 frames = audio / entry->video.buf.avg_frame_duration;
- frames += sink->video.renderer->get_latency(sink->video.renderer);
+ struct camu_renderer *renderer = sink->video.renderer;
+ if (renderer) frames += renderer->get_latency(renderer);
camu_video_buffer_set_latency(&entry->video.buf, frames);
}
bool ignore_video = VIDEO_EMPTY(entry) || VIDEO_IS_SINGLE_FRAME(entry);
@@ -1062,7 +1067,7 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
if (!AUDIO_ENDED_OR_EMPTY(entry)) {
// Remove for re-add in CLIENT_RECONNECTED.
remove_entry_audio_buffer(entry);
- // Remove again for BUFFER_BUFFERED.
+ // Remove again for another add in BUFFER_BUFFERED.
remove_entry_audio_buffer(entry);
}
// Slight optimization. A duplicate frame will still be sent but discarded in the video buffer.
@@ -1187,8 +1192,7 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
}
// @TODO: Mark for removal here instead.
- bool removed;
- al_array_remove_checked(sink->entries, entry, removed);
+ bool removed = al_array_remove(sink->entries, entry);
remove_from_queue_by_opaque(sink, entry);
if (entry == sink->target) {
@@ -1230,17 +1234,6 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
}
}
-static const char *pause_op_name(u8 pause)
-{
- switch (pause) {
- case LIANA_PAUSE_NONE: return "PAUSE_NONE";
- case LIANA_PAUSE_RESUME: return "PAUSE_RESUME";
- case LIANA_PAUSE_PAUSE: return "PAUSE_PAUSE";
- case LIANA_PAUSE_BOTH: return "PAUSE_BOTH";
- default: al_assert_and_return(NULL);
- };
-}
-
static struct camu_sink_entry *create_entry(struct camu_sink *sink, u32 id)
{
struct camu_sink_entry *entry = al_alloc_object(struct camu_sink_entry);
@@ -1343,7 +1336,7 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn,
#ifdef CAMU_SINK_LOCAL
(void)at;
al_assert(entry != current);
- trace("set("ENTRY_FMT"), %s[local], created: %s.", ENTRY_ARG(entry), pause_op_name(pause), BOOLSTR(create));
+ trace("set("ENTRY_FMT"), %s[local], created: %s.", ENTRY_ARG(entry), lia_pause_op_name(pause), BOOLSTR(create));
if (current && !current->ended) {
current->audio.ignore_paused = true;
if (!current->paused) {
@@ -1360,7 +1353,7 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn,
#else
// For target to be set that must mean current is set, armed to pause, and not ended.
struct camu_sink_entry *prev_target = sink->target;
- trace("set("ENTRY_FMT"), %s, created: %s, target: "ENTRY_FMT".", ENTRY_ARG(entry), pause_op_name(pause), BOOLSTR(create), ENTRY_ARG(prev_target));
+ trace("set("ENTRY_FMT"), %s, created: %s, target: "ENTRY_FMT".", ENTRY_ARG(entry), lia_pause_op_name(pause), BOOLSTR(create), ENTRY_ARG(prev_target));
switch (pause) {
case LIANA_PAUSE_NONE:
if (prev_target) {
@@ -1452,7 +1445,7 @@ static bool pause_command_callback(void *userdata, struct nn_rpc_connection *con
if (!entry) goto out;
al_assert(entry->sequence == sequence);
nn_mutex_lock(&sink->lock);
- trace("pause("ENTRY_FMT"), %s, audio_state: %hhu, video_state: %hhu.", ENTRY_ARG(entry), pause_op_name(pause), AUDIO_STATE(entry), VIDEO_STATE(entry));
+ trace("pause("ENTRY_FMT"), %s, audio_state: %hhu, video_state: %hhu.", ENTRY_ARG(entry), lia_pause_op_name(pause), AUDIO_STATE(entry), VIDEO_STATE(entry));
#ifdef CAMU_SINK_LOCAL
(void)at;
sink_local_pause(sink, entry);
@@ -1508,7 +1501,7 @@ static bool seek_command_callback(void *userdata, struct nn_rpc_connection *conn
if (!entry) goto out;
trace("seek("ENTRY_FMT"), reset_id: %u.", ENTRY_ARG(entry), reset_id);
entry->reset_id = reset_id;
- // The rest of the seek is handled in client_callback()'s.
+ // The rest of the seek is handled in CLIENT_REMOVE_BUFFERS/RESUME_AT/RECONNECTED.
lia_client_seek(&entry->client, pos, at);
out:
@@ -1613,13 +1606,12 @@ void camu_sink_toggle_pause(struct camu_sink *sink)
nn_mutex_lock(&sink->lock);
struct camu_sink_entry *current = sink->current;
nn_mutex_unlock(&sink->lock);
- if (current) {
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = TOGGLE_PAUSE,
- .value.f = camu_clock_get_pts(&current->clock, 0.0, false),
- .opaque = current
- });
- }
+ if (!current) return;
+ queue_cmd(sink, (struct camu_sink_cmd){
+ .op = TOGGLE_PAUSE,
+ .value.f = camu_clock_get_pts(&current->clock, 0.0, false),
+ .opaque = current
+ });
}
void camu_sink_seek(struct camu_sink *sink, void *value, u8 mode)
@@ -1663,12 +1655,11 @@ void camu_sink_reseek(struct camu_sink *sink)
nn_mutex_lock(&sink->lock);
struct camu_sink_entry *current = sink->current;
nn_mutex_unlock(&sink->lock);
- if (current) {
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = RESEEK,
- .opaque = current
- });
- }
+ if (!current) return;
+ queue_cmd(sink, (struct camu_sink_cmd){
+ .op = RESEEK,
+ .opaque = current
+ });
}
void camu_sink_shuffle(struct camu_sink *sink)