summaryrefslogtreecommitdiff
path: root/src/libsink
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsink')
-rw-r--r--src/libsink/sink.c136
-rw-r--r--src/libsink/sink.h9
2 files changed, 70 insertions, 75 deletions
diff --git a/src/libsink/sink.c b/src/libsink/sink.c
index e5f9351..d3647f1 100644
--- a/src/libsink/sink.c
+++ b/src/libsink/sink.c
@@ -60,9 +60,9 @@ enum {
#define ENTRY_MAX_AGE 3
// printf format for entries.
-#if defined C89ATOMIC_64BIT
+#if defined AL_WE_64BIT
#define ENTRY_FMT "#%u(0x%llx)"
-#elif defined C89ATOMIC_32BIT
+#elif defined AL_WE_32BIT
#define ENTRY_FMT "#%u(0x%lx)"
#endif
#define ENTRY_ARG(entry) \
@@ -76,16 +76,16 @@ enum {
#define AUDIO_EMPTY(entry) BUFFER_EMPTY(&(entry)->audio)
#define VIDEO_EMPTY(entry) BUFFER_EMPTY(&(entry)->video)
+#define AUDIO_NOT_ADDED(entry) ((entry)->audio.state != BUFFER_ADDED)
+#define VIDEO_NOT_ADDED(entry) ((entry)->video.state != BUFFER_ADDED)
+
#define AUDIO_ADDED_OR_EMPTY(entry) ((entry)->audio.state == BUFFER_ADDED || BUFFER_EMPTY(&(entry)->audio))
#define VIDEO_ADDED_OR_EMPTY(entry) ((entry)->video.state == BUFFER_ADDED || BUFFER_EMPTY(&(entry)->video))
#define AUDIO_ENDED_OR_EMPTY(entry) ((entry)->audio.state == BUFFER_ENDED || BUFFER_EMPTY(&(entry)->audio))
#define VIDEO_ENDED_OR_EMPTY(entry) ((entry)->video.state == BUFFER_ENDED || BUFFER_EMPTY(&(entry)->video))
-#define AUDIO_NOT_ADDED(entry) ((entry)->audio.state != BUFFER_ADDED)
-#define VIDEO_NOT_ADDED(entry) ((entry)->video.state != BUFFER_ADDED)
-
-#define VIDEO_IS_SINGLE_FRAME(entry) camu_video_buffer_is_single_frame(&(entry)->video.buf)
+#define VIDEO_IS_SINGLE_FRAME(entry) (entry)->video.buf.single_frame
#if defined CAMU_SCREEN_THREADED && defined CAMU_MIXER_THREADED
#define BLOCKING_SLEEP(delay) nn_thread_sleep(delay)
@@ -126,6 +126,8 @@ static void request_video_refresh(struct camu_sink *sink)
{
#ifndef CAMU_SINK_NO_VIDEO
sink->callback(sink->userdata, CAMU_SINK_REFRESH_VIDEO, 0, NULL);
+#else
+ (void)sink;
#endif
}
@@ -148,16 +150,19 @@ static inline void add_entry_video_buffer(struct camu_sink_entry *entry)
#ifndef CAMU_SINK_NO_VIDEO
struct camu_sink *sink = entry->sink;
sink->callback(sink->userdata, CAMU_SINK_ADD_BUFFER, CAMU_SINK_VIDEO, &entry->video.buf);
+#else
+ (void)entry;
#endif
}
-static void remove_entry_audio_buffer(struct camu_sink *sink, struct camu_sink_entry *entry)
+static void remove_entry_audio_buffer(struct camu_sink_entry *entry)
{
al_assert(AUDIO_STATE(entry) != BUFFER_ENDED);
al_assert(AUDIO_STATE(entry) != BUFFER_INIT);
switch (AUDIO_STATE(entry)) {
case BUFFER_ADDED:
AUDIO_STATE(entry) = BUFFER_SET_OR_BUFFERED;
+ struct camu_sink *sink = entry->sink;
#ifdef CAMU_MIXER_THREADED_START_STOP
sink->callback(sink->userdata, CAMU_SINK_REMOVE_BUFFER, CAMU_SINK_AUDIO, &entry->audio.buf);
#else
@@ -177,7 +182,7 @@ static void remove_entry_audio_buffer(struct camu_sink *sink, struct camu_sink_e
}
}
-static void remove_entry_video_buffer(struct camu_sink *sink, struct camu_sink_entry *entry)
+static void remove_entry_video_buffer(struct camu_sink_entry *entry)
{
// Don't assert !entry->ended here because of single frame handling.
al_assert(VIDEO_STATE(entry) != BUFFER_ENDED);
@@ -186,6 +191,7 @@ static void remove_entry_video_buffer(struct camu_sink *sink, struct camu_sink_e
case BUFFER_ADDED:
VIDEO_STATE(entry) = BUFFER_SET_OR_BUFFERED;
#ifndef CAMU_SINK_NO_VIDEO
+ struct camu_sink *sink = entry->sink;
sink->callback(sink->userdata, CAMU_SINK_REMOVE_BUFFER, CAMU_SINK_VIDEO, &entry->video.buf);
#endif
break;
@@ -202,16 +208,15 @@ static void remove_entry_video_buffer(struct camu_sink *sink, struct camu_sink_e
// It's possible for some of an entries buffers to be ENDED while others are still
// ADDED and playing. We handle that by making remove_entry_buffers() and
// add_audio/video_if_set_and_buffered() no-ops for ENDED buffers.
-static void remove_entry_buffers(struct camu_sink *sink, struct camu_sink_entry *entry)
+static void remove_entry_buffers(struct camu_sink_entry *entry)
{
- trace("remove_entry_buffers("ENTRY_FMT"), audio_state: %hhu, video_state: %hhu.",
- ENTRY_ARG(entry), AUDIO_STATE(entry), VIDEO_STATE(entry));
+ trace("remove_entry_buffers("ENTRY_FMT"), audio_state: %hhu, video_state: %hhu.", ENTRY_ARG(entry), AUDIO_STATE(entry), VIDEO_STATE(entry));
al_assert(!entry->ended);
if (AUDIO_STATE(entry) != BUFFER_ENDED) {
- remove_entry_audio_buffer(sink, entry);
+ remove_entry_audio_buffer(entry);
}
if (VIDEO_STATE(entry) != BUFFER_ENDED) {
- remove_entry_video_buffer(sink, entry);
+ remove_entry_video_buffer(entry);
}
}
@@ -278,12 +283,9 @@ static inline s32 get_sequence_for_command(struct camu_sink *sink)
{
// SEQUENCE_ANY resolves order on the server.
s32 sequence = LIANA_SEQUENCE_ANY;
-#if 0
- // Setting an explicit sequence makes skip and pause act on "what you see".
- // This is unlikely to be expected behavior in the common case. The user might
- // feel like their input was eaten if skipping after a different skip happens
- // on the server but is yet to be reflected on their end.
- if (sink->target) {
+ // If we're local this could only lead to feeling like your inputs were eaten.
+#ifndef CAMU_SINK_LOCAL
+ if (sink->target && sink->target != (struct camu_sink_entry *)0xb00b) {
sequence = sink->target->sequence;
} else if (sink->current) {
sequence = sink->current->sequence;
@@ -463,14 +465,16 @@ static void queue_signal_callback(void *userdata)
static void mixer_callback(void *userdata, u8 op)
{
+ struct camu_sink *sink = (struct camu_sink *)userdata;
if (op == CAMU_MIXER_EMPTY) {
info("Mixer empty.");
#ifndef LIANA_LIST_SCUFFED_LOOP
- struct camu_sink *sink = (struct camu_sink *)userdata;
queue_cmd(sink, (struct camu_sink_cmd){
.op = STOP,
.value.i = CAMU_SINK_AUDIO
});
+#else
+ (void)sink;
#endif
}
}
@@ -548,7 +552,7 @@ static void maybe_remove_previous(struct camu_sink *sink)
trace("maybe_remove_previous(), previous_count: %u.", sink->previous.count);
struct camu_sink_entry *previous;
al_array_foreach(sink->previous, i, previous) {
- remove_entry_buffers(sink, previous);
+ remove_entry_buffers(previous);
}
sink->previous.count = 0;
}
@@ -577,14 +581,13 @@ static void maybe_remove_from_previous(struct camu_sink *sink, struct camu_sink_
static void maybe_add_to_previous(struct camu_sink *sink, struct camu_sink_entry *previous,
struct camu_sink_entry *target)
{
- trace("maybe_add_to_previous("ENTRY_FMT"), "ENTRY_FMT"), audio_state: %hhu, video_state: %hhu.",
- ENTRY_ARG(previous), ENTRY_ARG(target), AUDIO_STATE(previous), VIDEO_STATE(previous));
+ trace("maybe_add_to_previous("ENTRY_FMT", "ENTRY_FMT"), audio_state: %hhu, video_state: %hhu.", ENTRY_ARG(previous), ENTRY_ARG(target), AUDIO_STATE(previous), VIDEO_STATE(previous));
al_assert(previous != target);
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)) {
- remove_entry_buffers(sink, previous);
+ remove_entry_buffers(previous);
return;
}
al_array_push(sink->previous, previous);
@@ -602,7 +605,7 @@ static void do_add_entry(struct camu_sink_entry *entry)
}
maybe_remove_previous(entry->sink);
queue_cmd(entry->sink, (struct camu_sink_cmd){
- .op = VIDEO_EMPTY(entry) || VIDEO_IS_SINGLE_FRAME(entry) || entry->paused ? STOP : START,
+ .op = (VIDEO_EMPTY(entry) || VIDEO_IS_SINGLE_FRAME(entry) || entry->paused) ? STOP : START,
.value.i = CAMU_SINK_VIDEO
});
if (VIDEO_EMPTY(entry)) {
@@ -610,7 +613,7 @@ static void do_add_entry(struct camu_sink_entry *entry)
request_video_refresh(entry->sink);
}
queue_cmd(entry->sink, (struct camu_sink_cmd){
- .op = AUDIO_EMPTY(entry) || entry->paused ? STOP : START,
+ .op = (AUDIO_EMPTY(entry) || entry->paused) ? STOP : START,
.value.i = CAMU_SINK_AUDIO
});
}
@@ -650,9 +653,7 @@ void add_video_if_set_and_buffered(struct camu_sink_entry *entry)
VIDEO_STATE(entry) = BUFFER_SET_OR_BUFFERED;
} else if (VIDEO_STATE(entry) == BUFFER_SET_OR_BUFFERED) {
VIDEO_STATE(entry) = BUFFER_ADDED;
- if (VIDEO_IS_SINGLE_FRAME(entry)) {
- add_entry_video_buffer(entry);
- }
+ if (VIDEO_IS_SINGLE_FRAME(entry)) add_entry_video_buffer(entry);
if (AUDIO_ADDED_OR_EMPTY(entry)) {
do_add_entry(entry);
}
@@ -661,8 +662,7 @@ void add_video_if_set_and_buffered(struct camu_sink_entry *entry)
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));
+ 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;
@@ -677,7 +677,7 @@ static void switch_to(struct camu_sink *sink, struct camu_sink_entry *target)
}
if (ensure_removed) {
if (VIDEO_IS_SINGLE_FRAME(current)) {
- remove_entry_video_buffer(sink, current);
+ remove_entry_video_buffer(current);
}
al_assert(AUDIO_NOT_ADDED(current));
al_assert(VIDEO_NOT_ADDED(current));
@@ -719,8 +719,7 @@ static void switch_to(struct camu_sink *sink, struct camu_sink_entry *target)
static void pause_and_swap_to(struct camu_sink *sink, struct camu_sink_entry *target, u64 at)
{
struct camu_sink_entry *current = sink->current;
- trace("pause_and_swap_to("ENTRY_FMT"), %llu), current: "ENTRY_FMT".",
- ENTRY_ARG(target), at, ENTRY_ARG(current));
+ trace("pause_and_swap_to("ENTRY_FMT"), current: "ENTRY_FMT".", ENTRY_ARG(target), ENTRY_ARG(current));
al_assert(target != current);
if (!current || current->ended) {
switch_to(sink, target);
@@ -797,7 +796,7 @@ static void audio_buffer_callback(void *userdata, u8 op)
// Getting EOF on a buffer that isn't ADDED is very possible if the audio
// output is threaded. Even more if we had to wait on the lock above.
if (AUDIO_STATE(entry) == BUFFER_ADDED) {
- remove_entry_audio_buffer(sink, entry);
+ remove_entry_audio_buffer(entry);
}
// This assert likely doesn't matter due to the handling of the ENDED state.
al_assert(AUDIO_STATE(entry) == BUFFER_SET_OR_BUFFERED);
@@ -846,7 +845,7 @@ static void video_buffer_callback(void *userdata, u8 op)
}
if (!single_frame) {
if (VIDEO_STATE(entry) == BUFFER_ADDED) {
- remove_entry_video_buffer(sink, entry);
+ remove_entry_video_buffer(entry);
}
al_assert(VIDEO_STATE(entry) == BUFFER_SET_OR_BUFFERED);
VIDEO_STATE(entry) = BUFFER_ENDED;
@@ -879,8 +878,7 @@ static void clock_callback(void *userdata, u8 op)
struct camu_sink *sink = entry->sink;
if (op == CAMU_CLOCK_PAUSED) {
nn_mutex_lock(&sink->lock);
- trace("clock_callback(CAMU_CLOCK_PAUSED, "ENTRY_FMT"), target: "ENTRY_FMT".",
- ENTRY_ARG(entry), ENTRY_ARG(sink->target));
+ trace("clock_paused("ENTRY_FMT"), target: "ENTRY_FMT".", ENTRY_ARG(entry), ENTRY_ARG(sink->target));
if (entry == sink->current) {
if (sink->target) {
switch_to(sink, sink->target);
@@ -1040,8 +1038,7 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
nn_mutex_lock(&sink->lock);
- trace("remove_buffers("ENTRY_FMT", %s), entry == current: %s.",
- ENTRY_ARG(entry), BOOLSTR(reconnect), BOOLSTR(entry == sink->current));
+ trace("remove_buffers("ENTRY_FMT", %s), entry == current: %s.", ENTRY_ARG(entry), BOOLSTR(reconnect), BOOLSTR(entry == sink->current));
if (reconnect && entry == sink->current) {
sink->reconnecting = entry;
@@ -1053,11 +1050,11 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
remove_previous_if_contains(sink, entry);
if (AUDIO_STATE(entry) == BUFFER_ADDED) {
- remove_entry_audio_buffer(sink, entry);
+ remove_entry_audio_buffer(entry);
}
bool ignore_video = reconnect && VIDEO_IS_SINGLE_FRAME(entry);
if (!ignore_video && VIDEO_STATE(entry) == BUFFER_ADDED) {
- remove_entry_video_buffer(sink, entry);
+ remove_entry_video_buffer(entry);
}
// Resolve any queued REMOVE_BUFFER requests before blocking.
@@ -1107,8 +1104,10 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
}
case LIANA_CLIENT_RESUME_AT: {
struct lia_timing *time = (struct lia_timing *)opaque;
- trace("resume_at("ENTRY_FMT"), %llu, %llu), paused_at: %f.",
- ENTRY_ARG(entry), time->seek_pos, time->at, entry->clock.paused_at);
+ trace("resume_at("ENTRY_FMT"), paused_at: %f.", ENTRY_ARG(entry), entry->clock.paused_at);
+#ifdef CAMU_SINK_LOCAL
+ time->at = 0;
+#endif
nn_mutex_lock(&sink->lock);
#if defined LIANA_LIST_SCUFFED_LOOP
if (time->seek_pos == 0) {
@@ -1125,8 +1124,7 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
case LIANA_CLIENT_RECONNECTED: {
bool unconfigured = *(bool *)opaque;
nn_mutex_lock(&sink->lock);
- trace("reconnected("ENTRY_FMT"), reconnecting: "ENTRY_FMT".",
- ENTRY_ARG(entry), ENTRY_ARG(sink->reconnecting));
+ trace("reconnected("ENTRY_FMT"), reconnecting: "ENTRY_FMT".", ENTRY_ARG(entry), ENTRY_ARG(sink->reconnecting));
if (entry == sink->reconnecting) {
al_assert(entry == sink->current);
if (AUDIO_STATE(entry) > BUFFER_QUEUED) {
@@ -1172,7 +1170,7 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
if (VIDEO_STATE(entry) == BUFFER_ADDED) {
al_assert(VIDEO_IS_SINGLE_FRAME(entry));
- remove_entry_video_buffer(sink, entry);
+ remove_entry_video_buffer(entry);
while (entry_video_buffer_held(entry)) { BLOCKING_SLEEP(NNWT_TS_FROM_USEC(2000)); }
}
@@ -1220,6 +1218,17 @@ 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);
@@ -1242,9 +1251,9 @@ static struct camu_sink_entry *create_entry(struct camu_sink *sink, u32 id)
entry->video.buf.callback = video_buffer_callback;
entry->video.buf.userdata = entry;
- entry->client.renderer = sink->video.renderer;
entry->client.callback = client_callback;
entry->client.userdata = entry;
+ entry->client.renderer = sink->video.renderer;
entry->client.prefs = sink->prefs;
al_array_push(sink->entries, entry);
@@ -1298,14 +1307,11 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn,
u32 reset_id = nn_packet_read_u32(packet);
struct camu_sink_entry *current = sink->current;
- struct camu_sink_entry *prev_target = sink->target;
struct camu_sink_entry *entry = get_entry_from_id(sink, id);
bool create = !entry;
- if (create) {
- entry = create_entry(sink, id);
- }
+ if (create) entry = create_entry(sink, id);
entry->sequence = sequence;
- sink->lru = al_u16_inc_wrap(sink->lru);
+ sink->lru = al_u16_add_wrap(sink->lru, SINK_LRU_MAX);
entry->lru = sink->lru;
entry->reset_id = reset_id;
@@ -1322,9 +1328,6 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn,
lia_client_connect(&entry->client, sink->loop, sink->type, &addr, port, node_id, seek_pos);
}
- trace("set("ENTRY_FMT", %llu), created: %s, pause: %hhu, current: "ENTRY_FMT", target: "ENTRY_FMT".",
- ENTRY_ARG(entry), at, BOOLSTR(create), pause, ENTRY_ARG(current), ENTRY_ARG(prev_target));
-
if (op == LIANA_SINK_BUFFER) {
goto out;
} else if (op == LIANA_SINK_BUFFER_AND_QUEUE) {
@@ -1340,7 +1343,9 @@ 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));
if (current && !current->ended) {
current->audio.ignore_paused = true;
if (!current->paused) {
@@ -1356,6 +1361,8 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn,
switch_to(sink, entry);
#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));
switch (pause) {
case LIANA_PAUSE_NONE:
if (prev_target) {
@@ -1446,13 +1453,10 @@ static bool pause_command_callback(void *userdata, struct nn_rpc_connection *con
struct camu_sink_entry *entry = get_entry_from_id(sink, id);
if (!entry) goto out;
al_assert(entry->sequence == sequence);
-
nn_mutex_lock(&sink->lock);
-
- trace("pause("ENTRY_FMT", %llu), pause: %hhu, audio_state: %hhu, video_state: %hhu.",
- ENTRY_ARG(entry), at, pause, AUDIO_STATE(entry), VIDEO_STATE(entry));
-
+ trace("pause("ENTRY_FMT"), %s, audio_state: %hhu, video_state: %hhu.", ENTRY_ARG(entry), pause_op_name(pause), AUDIO_STATE(entry), VIDEO_STATE(entry));
#ifdef CAMU_SINK_LOCAL
+ (void)at;
sink_local_pause(sink, entry);
#else
switch (pause) {
@@ -1481,7 +1485,6 @@ static bool pause_command_callback(void *userdata, struct nn_rpc_connection *con
break;
}
#endif
-
nn_mutex_unlock(&sink->lock);
out:
@@ -1505,13 +1508,8 @@ static bool seek_command_callback(void *userdata, struct nn_rpc_connection *conn
struct camu_sink_entry *entry = get_entry_from_id(sink, id);
if (!entry) goto out;
- entry->reset_id = reset_id;
-
trace("seek("ENTRY_FMT"), reset_id: %u.", ENTRY_ARG(entry), reset_id);
-
-#ifdef CAMU_SINK_LOCAL
- at = 0;
-#endif
+ entry->reset_id = reset_id;
// The rest of the seek is handled in client_callback()'s.
lia_client_seek(&entry->client, pos, at);
@@ -1630,7 +1628,7 @@ void camu_sink_seek(struct camu_sink *sink, void *value, u8 mode)
{
nn_mutex_lock(&sink->lock);
struct camu_sink_entry *current = sink->current;
- u64 duration;
+ u64 duration = 0;
if (current) duration = current->client.duration;
f64 pts = camu_clock_get_last_pts(&current->clock);
nn_mutex_unlock(&sink->lock);
diff --git a/src/libsink/sink.h b/src/libsink/sink.h
index 32fdc26..d1bcd26 100644
--- a/src/libsink/sink.h
+++ b/src/libsink/sink.h
@@ -7,7 +7,7 @@
#include <nnwt/timer.h>
#include "../util/queue.h"
-
+#include "../liana/client.h"
#include "../buffer/clock.h"
#include "../buffer/audio.h"
#ifndef CAMU_SINK_NO_VIDEO
@@ -16,8 +16,6 @@
#include "../buffer/video_null.h"
#endif
-#include "../liana/client.h"
-
enum {
CAMU_SINK_AUDIO = 0,
CAMU_SINK_VIDEO
@@ -56,7 +54,7 @@ struct camu_sink_entry {
bool paused;
struct {
u8 state;
- // Don't stop the audio output when this entry is paused.
+ // Don't stop the audio output on BUFFER_PAUSED from this entry.
bool ignore_paused;
struct camu_audio_buffer buf;
struct lia_vcr_track *track;
@@ -87,7 +85,6 @@ struct camu_sink {
struct nn_timer reconnect_timer;
struct nn_signal queue_signal;
queue(struct camu_sink_cmd) queue;
- str default_list;
struct camu_sink_entry *current;
struct camu_sink_entry *queued;
struct camu_sink_entry *target;
@@ -104,7 +101,7 @@ struct camu_sink {
struct camu_renderer *renderer;
} video;
struct lia_prefs prefs;
- struct lia_server *local_server;
+ str default_list;
u8 (*callback)(void *, u8, u8, void *);
void *userdata;
};