summaryrefslogtreecommitdiff
path: root/src/libsink
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsink')
-rw-r--r--src/libsink/common.h2
-rw-r--r--src/libsink/sink.c193
-rw-r--r--src/libsink/sink.h1
3 files changed, 122 insertions, 74 deletions
diff --git a/src/libsink/common.h b/src/libsink/common.h
index 0bb3ded..708b1bb 100644
--- a/src/libsink/common.h
+++ b/src/libsink/common.h
@@ -1,6 +1,6 @@
#pragma once
-#define CAMU_SINK_LOCAL
+//#define CAMU_SINK_LOCAL
enum {
CAMU_SINK_SET = 0,
diff --git a/src/libsink/sink.c b/src/libsink/sink.c
index 538515a..2caa973 100644
--- a/src/libsink/sink.c
+++ b/src/libsink/sink.c
@@ -10,6 +10,8 @@
#include "sink.h"
#include "common.h"
+#include "../render/renderer_libplacebo.h"
+
enum {
SINK_EMPTY = 0,
SINK_PAUSED,
@@ -36,7 +38,7 @@ enum {
CLOSE
};
-#define ENTRY_MAX_AGE 7
+#define ENTRY_MAX_AGE 5
#define BUFFER_EMPTY(buf) ((buf)->state == BUFFER_INIT || (buf)->state == BUFFER_QUEUED)
@@ -47,6 +49,13 @@ enum {
#endif
#define AUDIO_READY_OR_EMPTY(entry) (BUFFER_EMPTY(&(entry)->audio) || (entry)->audio.state == BUFFER_ADDED)
+#ifdef CAMU_SINK_NO_VIDEO
+#define VIDEO_REMOVED_OR_EMPTY(entry) true
+#else
+#define VIDEO_REMOVED_OR_EMPTY(entry) (BUFFER_EMPTY(&(entry)->video) || (entry)->video.state != BUFFER_ADDED)
+#endif
+#define AUDIO_REMOVED_OR_EMPTY(entry) (BUFFER_EMPTY(&(entry)->audio) || (entry)->audio.state != BUFFER_ADDED)
+
#if defined CAMU_SCREEN_THREADED && defined CAMU_MIXER_THREADED
#define BLOCKING_SLEEP(delay) aki_thread_sleep(delay)
#else
@@ -379,6 +388,36 @@ void add_video_if_set_and_buffered(struct camu_sink_entry *entry)
}
#endif
+static void switch_to(struct camu_sink *sink, struct camu_sink_entry *entry)
+{
+ if (sink->current) {
+ if (camu_clock_is_ended(&entry->clock)) {
+ remove_entry_buffers(sink, sink->current);
+ } else {
+ maybe_add_to_previous(sink, sink->current, entry);
+ }
+ }
+ set_or_queue_entry(entry);
+ sink->current = entry;
+}
+
+static bool end_entry_and_advance_queue(struct camu_sink *sink, struct camu_sink_entry *entry)
+{
+ al_log_info("sink", "Entry ended.");
+ camu_clock_end(&entry->clock);
+ if (sink->target) {
+ switch_to(sink, sink->target);
+ sink->target = NULL;
+ }
+ if (sink->queued) {
+ set_or_queue_entry(sink->queued);
+ sink->current = sink->queued;
+ sink->queued = NULL;
+ return true;
+ }
+ return false;
+}
+
static void audio_buffer_callback(void *userdata, u8 op)
{
struct camu_sink_entry *entry = (struct camu_sink_entry *)userdata;
@@ -386,9 +425,7 @@ static void audio_buffer_callback(void *userdata, u8 op)
switch (op) {
case CAMU_BUFFER_BUFFERED:
aki_mutex_lock(&sink->mutex);
- if (!entry->ended) {
- add_audio_if_set_and_buffered(entry);
- }
+ add_audio_if_set_and_buffered(entry);
aki_mutex_unlock(&sink->mutex);
break;
case CAMU_BUFFER_CORK:
@@ -410,20 +447,23 @@ static void audio_buffer_callback(void *userdata, u8 op)
case CAMU_BUFFER_EOF: {
lia_vcr_cork(entry->audio.track);
aki_mutex_lock(&sink->mutex);
+ al_log_info("sink", "Audio EOF.");
remove_entry_audio_buffer(sink, entry);
- camu_clock_end(&entry->clock);
- // EOF on the audio buffer unconditionally
- // advances the queue.
- if (sink->queued) {
- set_or_queue_entry(sink->queued);
- sink->current = sink->queued;
- sink->queued = NULL;
+ bool run_queue = VIDEO_REMOVED_OR_EMPTY(entry);
+#ifndef CAMU_SINK_NO_VIDEO
+ bool single_frame = camu_video_buffer_is_single_frame(&entry->video.buf);
+ run_queue = run_queue || single_frame;
+#endif
+ if (run_queue) {
+ end_entry_and_advance_queue(sink, entry);
}
aki_mutex_unlock(&sink->mutex);
- queue_cmd(sink, (struct camu_sink_cmd){
- .op = END,
- .value.i = entry->sequence
- });
+ if (run_queue) {
+ queue_cmd(sink, (struct camu_sink_cmd){
+ .op = END,
+ .value.i = entry->sequence
+ });
+ }
break;
}
}
@@ -436,11 +476,8 @@ static void video_buffer_callback(void *userdata, u8 op)
struct camu_sink *sink = entry->sink;
switch (op) {
case CAMU_BUFFER_BUFFERED: {
- bool single_frame = camu_video_buffer_is_single_frame(&entry->video.buf);
aki_mutex_lock(&sink->mutex);
- if (!entry->ended || single_frame) {
- add_video_if_set_and_buffered(entry);
- }
+ add_video_if_set_and_buffered(entry);
aki_mutex_unlock(&sink->mutex);
break;
}
@@ -455,19 +492,13 @@ static void video_buffer_callback(void *userdata, u8 op)
bool single_frame = camu_video_buffer_is_single_frame(&entry->video.buf);
bool swapped = false;
aki_mutex_lock(&sink->mutex);
+ al_log_info("sink", "Video EOF.");
if (!single_frame) {
remove_entry_video_buffer(sink, entry);
}
- // If this entry has no audio, advance the queue.
- bool run_queue = !single_frame && BUFFER_EMPTY(&entry->audio);
+ bool run_queue = !single_frame && AUDIO_REMOVED_OR_EMPTY(entry);
if (run_queue) {
- camu_clock_end(&entry->clock);
- if (sink->queued) {
- set_or_queue_entry(sink->queued);
- sink->current = sink->queued;
- sink->queued = NULL;
- swapped = true;
- }
+ swapped = end_entry_and_advance_queue(sink, entry);
}
aki_mutex_unlock(&sink->mutex);
if (!swapped) {
@@ -528,7 +559,7 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
switch (stream->type) {
case CAMU_STREAM_AUDIO:
entry->audio.track = (struct lia_vcr_track *)opaque;
- if (!camu_audio_buffer_configure(&entry->audio.buf, stream)) {
+ if (!camu_audio_buffer_configure(&entry->audio.buf, stream, sink->audio.mixer)) {
lia_client_disconnect(&entry->client);
}
aki_mutex_lock(&sink->mutex);
@@ -545,7 +576,7 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
#ifndef CAMU_SINK_NO_VIDEO
case CAMU_STREAM_VIDEO:
entry->video.track = (struct lia_vcr_track *)opaque;
- if (!camu_video_buffer_configure(&entry->video.buf, stream)) {
+ if (!camu_video_buffer_configure(&entry->video.buf, stream, sink->video.renderer)) {
lia_client_disconnect(&entry->client);
}
aki_mutex_lock(&sink->mutex);
@@ -559,6 +590,20 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
}
aki_mutex_unlock(&sink->mutex);
break;
+ case CAMU_STREAM_SUBTITLE:
+#ifdef CAMU_HAVE_FFMPEG
+ camu_video_buffer_configure_subtitles(&entry->video.buf, stream->av.stream->codecpar);
+#endif
+ break;
+ case CAMU_STREAM_ATTACHMENT: {
+#ifdef CAMU_HAVE_FFMPEG
+ struct camu_renderer_lp *lp = (struct camu_renderer_lp *)sink->video.renderer;
+ AVDictionaryEntry *entry = av_dict_get(stream->av.stream->metadata, "filename", NULL, 0);
+ AVCodecParameters *codecpar = stream->av.stream->codecpar;
+ ass_add_font(lp->ass, entry->value, (const char *)codecpar->extradata, codecpar->extradata_size);
+#endif
+ break;
+ }
#endif
}
break;
@@ -580,6 +625,18 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
}
break;
}
+ case LIANA_CLIENT_SUBTITLE: {
+ switch (stream->type) {
+ case CAMU_STREAM_SUBTITLE: {
+#ifdef CAMU_HAVE_FFMPEG
+ AVPacket *pkt = (AVPacket *)opaque;
+ camu_video_buffer_push_subtitle(&entry->video.buf, pkt);
+#endif
+ break;
+ }
+ }
+ break;
+ }
case LIANA_CLIENT_REMOVE_BUFFERS: {
aki_mutex_lock(&sink->mutex);
if (entry->audio.state == BUFFER_ADDED) {
@@ -588,7 +645,8 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
}
#ifndef CAMU_SINK_NO_VIDEO
bool reconnect = *(bool *)opaque;
- bool keep_video = reconnect && camu_video_buffer_is_single_frame(&entry->video.buf);
+ bool single_frame = camu_video_buffer_is_single_frame(&entry->video.buf);
+ bool keep_video = reconnect && single_frame;
if (!keep_video && entry->video.state == BUFFER_ADDED) {
sink->callback(sink->userdata, CAMU_SINK_REMOVE_BUFFER, CAMU_SINK_VIDEO, &entry->video.buf);
entry->video.state = BUFFER_SET_OR_BUFFERED;
@@ -599,7 +657,7 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
while (keep_video ? entry_audio_buffer_held(entry) : entry_buffers_held(entry)) {
BLOCKING_SLEEP(AKI_TS_FROM_USEC(2000));
}
- if (!keep_video) camu_video_buffer_reset(&entry->video.buf);
+ if (reconnect && !single_frame) camu_video_buffer_reset(&entry->video.buf);
#else
while (entry_buffers_held(entry)) {
BLOCKING_SLEEP(AKI_TS_FROM_USEC(2000));
@@ -634,8 +692,21 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
}
case LIANA_CLIENT_CLOSED: {
aki_mutex_lock(&sink->mutex);
- if (entry == sink->current) sink->current = NULL;
- if (entry == sink->queued) sink->queued = NULL;
+ if (entry == sink->target) {
+ if (sink->current) {
+ remove_entry_buffers(sink, sink->current);
+ sink->current = NULL;
+ }
+ sink->target = NULL;
+ } else if (entry == sink->current) {
+ sink->current = NULL;
+ if (sink->target) {
+ switch_to(sink, sink->target);
+ sink->target = NULL;
+ }
+ } else if (entry == sink->queued) {
+ sink->queued = NULL;
+ }
lia_client_free(&entry->client);
camu_audio_buffer_free(&entry->audio.buf);
#ifndef CAMU_SINK_NO_VIDEO
@@ -647,13 +718,13 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
if (rentry == entry) {
al_array_remove_at(sink->entries, i);
removed = true;
- al_log_debug("sink", "Entry closed by disconnect.");
+ al_log_info("sink", "Entry closed by disconnect.");
break;
}
}
al_free(entry);
if (!removed) {
- al_log_debug("sink", "Entry closed by cleanup.");
+ al_log_info("sink", "Entry closed by cleanup.");
}
aki_mutex_unlock(&sink->mutex);
break;
@@ -743,20 +814,6 @@ static void maybe_cleanup_old_entries(struct camu_sink *sink)
}
}
-static void switch_to(struct camu_sink *sink, struct camu_sink_entry *entry)
-{
- if (entry->ended || camu_clock_is_ended(&entry->clock)) {
- if (sink->current) {
- remove_entry_buffers(sink, sink->current);
- }
- } else {
- if (sink->current) {
- maybe_add_to_previous(sink, sink->current, entry);
- }
- }
- set_or_queue_entry(entry);
- sink->current = entry;
-}
static void clock_callback(void *userdata, u8 op)
{
@@ -790,13 +847,13 @@ static struct camu_sink_entry *get_entry_from_id(struct camu_sink *sink, u16 id,
entry->audio.state = BUFFER_INIT;
entry->audio.armed = false;
- camu_audio_buffer_init(&entry->audio.buf, &entry->clock, sink->audio.mixer);
+ camu_audio_buffer_init(&entry->audio.buf, &entry->clock);
entry->audio.buf.callback = audio_buffer_callback;
entry->audio.buf.userdata = entry;
#ifndef CAMU_SINK_NO_VIDEO
entry->video.state = BUFFER_INIT;
- camu_video_buffer_init(&entry->video.buf, &entry->clock, sink->video.renderer);
+ camu_video_buffer_init(&entry->video.buf, &entry->clock);
entry->video.buf.callback = video_buffer_callback;
entry->video.buf.userdata = entry;
#endif
@@ -842,6 +899,7 @@ static bool set_command_callback(void *userdata, struct aki_rpc_connection *conn
u64 seek_pos = aki_packet_read_u64(packet);
u8 pause = aki_packet_read_u8(packet);
bool ended = aki_packet_read_bool(packet);
+ (void)ended;
bool created;
struct camu_sink_entry *entry = get_entry_from_id(sink, node_id, &created);
@@ -851,9 +909,6 @@ static bool set_command_callback(void *userdata, struct aki_rpc_connection *conn
if (created) {
camu_clock_set(&entry->clock, seek_pos / 1000000.0);
- if (ended) {
- camu_clock_end(&entry->clock);
- }
struct camu_renderer *renderer = NULL;
#ifndef CAMU_SINK_NO_VIDEO
renderer = sink->video.renderer;
@@ -861,8 +916,6 @@ static bool set_command_callback(void *userdata, struct aki_rpc_connection *conn
lia_client_connect(&entry->client, sink->loop, sink->type, &addr, port, node_id, seek_pos, renderer);
}
- entry->ended = ended;
-
if (op == LIANA_SINK_BUFFER) {
goto out;
} else if (op == LIANA_SINK_BUFFER_AND_QUEUE) {
@@ -890,27 +943,25 @@ static bool set_command_callback(void *userdata, struct aki_rpc_connection *conn
al_log_warn("sink", "Ignoring target on NONE.");
sink->target = NULL;
}
- if (sink->current) {
- maybe_add_to_previous(sink, sink->current, entry);
+ if (entry == sink->current) {
+ break;
}
- set_or_queue_entry(entry);
- sink->current = entry;
+ switch_to(sink, entry);
break;
case LIANA_PAUSE_RESUME:
if (sink->target) {
al_log_warn("sink", "Ignoring target on RESUME.");
sink->target = NULL;
}
+ camu_clock_resume(&entry->clock, at);
if (entry == sink->current) {
camu_audio_buffer_unpause(&entry->audio.buf);
- } else if (sink->current) {
- maybe_add_to_previous(sink, sink->current, entry);
+ break;
}
- camu_clock_resume(&entry->clock, at);
- set_or_queue_entry(entry);
- sink->current = entry;
+ switch_to(sink, entry);
break;
case LIANA_PAUSE_PAUSE: {
+ // For target to be set that must mean that current is set and armed to pause.
struct camu_sink_entry *prev_target = sink->target;
if (prev_target) {
if (prev_target == entry) {
@@ -919,9 +970,8 @@ static bool set_command_callback(void *userdata, struct aki_rpc_connection *conn
sink->target = entry;
}
camu_clock_pause(&prev_target->clock, at);
- } else if (sink->current) {
- if (camu_clock_is_ended(&sink->current->clock)) {
- // Server thought we weren't done, be we are.
+ } else {
+ if (!sink->current || camu_clock_is_ended(&sink->current->clock)) {
switch_to(sink, entry);
} else {
sink->target = entry;
@@ -941,8 +991,8 @@ static bool set_command_callback(void *userdata, struct aki_rpc_connection *conn
camu_audio_buffer_unpause(&entry->audio.buf);
}
camu_clock_pause(&prev_target->clock, at);
- } else if (sink->current) {
- if (camu_clock_is_ended(&sink->current->clock)) {
+ } else {
+ if (!sink->current || camu_clock_is_ended(&sink->current->clock)) {
switch_to(sink, entry);
} else {
sink->target = entry;
@@ -1024,7 +1074,6 @@ static bool seek_command_callback(void *userdata, struct aki_rpc_connection *con
if (!current) goto out;
if (current->sequence == sequence) {
- current->ended = false;
#ifdef CAMU_SINK_LOCAL
(void)at;
lia_client_seek(&current->client, pos, 0);
diff --git a/src/libsink/sink.h b/src/libsink/sink.h
index 5feadf5..18ef134 100644
--- a/src/libsink/sink.h
+++ b/src/libsink/sink.h
@@ -42,7 +42,6 @@ struct camu_sink_entry {
u16 lru;
struct camu_clock clock;
struct lia_client client;
- bool ended;
struct {
u8 state;
bool armed;