mirror of
https://github.com/morgan9e/UxPlay
synced 2026-04-14 00:04:13 +09:00
detect if client is offline using feedback instead of ntp signal
This commit is contained in:
18
README.html
18
README.html
@@ -1159,16 +1159,16 @@ in which uxplay was put into the background). To quit, use
|
||||
<code>ctrl-C fg ctrl-C</code> to terminate the image viewer, bring
|
||||
<code>uxplay</code> into the foreground, and terminate it too.</p>
|
||||
<p><strong>-reset n</strong> sets a limit of <em>n</em> consecutive
|
||||
timeout failures of the client to respond to ntp requests from the
|
||||
server (these are sent every 3 seconds to check if the client is still
|
||||
present, and synchronize with it). After <em>n</em> failures, the client
|
||||
will be presumed to be offline, and the connection will be reset to
|
||||
allow a new connection. The default value of <em>n</em> is 5; the value
|
||||
<em>n</em> = 0 means “no limit” on timeouts.</p>
|
||||
failures of the client to send feedback requests (these “heartbeat
|
||||
signals” are sent by the client once per second to ask for a response
|
||||
showing that the server is still online). After <em>n</em> missing
|
||||
signals, the client will be presumed to be offline, and the connection
|
||||
will be reset to allow a new connection. The default value of <em>n</em>
|
||||
is 15 seconds; the value <em>n</em> = 0 means “no limit”.</p>
|
||||
<p><strong>-nofreeze</strong> closes the video window after a reset due
|
||||
to ntp timeout (default is to leave window open to allow a smoother
|
||||
reconection to the same client). This option may be useful in fullscreen
|
||||
mode.</p>
|
||||
to client going offline (default is to leave window open to allow a
|
||||
smoother reconection to the same client). This option may be useful in
|
||||
fullscreen mode.</p>
|
||||
<p><strong>-nc</strong> maintains previous UxPlay < 1.45 behavior
|
||||
that does <strong>not close</strong> the video window when the the
|
||||
client sends the “Stop Mirroring” signal. <em>This option is currently
|
||||
|
||||
12
README.md
12
README.md
@@ -1179,14 +1179,14 @@ uxplay was put into the background). To quit, use `ctrl-C fg ctrl-C` to
|
||||
terminate the image viewer, bring `uxplay` into the foreground, and
|
||||
terminate it too.
|
||||
|
||||
**-reset n** sets a limit of *n* consecutive timeout failures of the
|
||||
client to respond to ntp requests from the server (these are sent every
|
||||
3 seconds to check if the client is still present, and synchronize with
|
||||
it). After *n* failures, the client will be presumed to be offline, and
|
||||
**-reset n** sets a limit of *n* consecutive failures of the
|
||||
client to send feedback requests (these "heartbeat signals" are sent by the client
|
||||
once per second to ask for a response showing that the server is still online).
|
||||
After *n* missing signals, the client will be presumed to be offline, and
|
||||
the connection will be reset to allow a new connection. The default
|
||||
value of *n* is 5; the value *n* = 0 means "no limit" on timeouts.
|
||||
value of *n* is 15 seconds; the value *n* = 0 means "no limit".
|
||||
|
||||
**-nofreeze** closes the video window after a reset due to ntp timeout
|
||||
**-nofreeze** closes the video window after a reset due to client going offline
|
||||
(default is to leave window open to allow a smoother reconection to the
|
||||
same client). This option may be useful in fullscreen mode.
|
||||
|
||||
|
||||
18
README.txt
18
README.txt
@@ -1180,16 +1180,16 @@ uxplay was put into the background). To quit, use `ctrl-C fg ctrl-C` to
|
||||
terminate the image viewer, bring `uxplay` into the foreground, and
|
||||
terminate it too.
|
||||
|
||||
**-reset n** sets a limit of *n* consecutive timeout failures of the
|
||||
client to respond to ntp requests from the server (these are sent every
|
||||
3 seconds to check if the client is still present, and synchronize with
|
||||
it). After *n* failures, the client will be presumed to be offline, and
|
||||
the connection will be reset to allow a new connection. The default
|
||||
value of *n* is 5; the value *n* = 0 means "no limit" on timeouts.
|
||||
**-reset n** sets a limit of *n* consecutive failures of the client to
|
||||
send feedback requests (these "heartbeat signals" are sent by the client
|
||||
once per second to ask for a response showing that the server is still
|
||||
online). After *n* missing signals, the client will be presumed to be
|
||||
offline, and the connection will be reset to allow a new connection. The
|
||||
default value of *n* is 15 seconds; the value *n* = 0 means "no limit".
|
||||
|
||||
**-nofreeze** closes the video window after a reset due to ntp timeout
|
||||
(default is to leave window open to allow a smoother reconection to the
|
||||
same client). This option may be useful in fullscreen mode.
|
||||
**-nofreeze** closes the video window after a reset due to client going
|
||||
offline (default is to leave window open to allow a smoother reconection
|
||||
to the same client). This option may be useful in fullscreen mode.
|
||||
|
||||
**-nc** maintains previous UxPlay \< 1.45 behavior that does **not
|
||||
close** the video window when the the client sends the "Stop Mirroring"
|
||||
|
||||
@@ -64,7 +64,6 @@ struct raop_s {
|
||||
uint8_t clientFPSdata;
|
||||
|
||||
int audio_delay_micros;
|
||||
int max_ntp_timeouts;
|
||||
|
||||
/* for temporary storage of pin during pair-pin start */
|
||||
unsigned short pin;
|
||||
@@ -554,7 +553,6 @@ raop_init(raop_callbacks_t *callbacks) {
|
||||
/* initialize switch for display of client's streaming data records */
|
||||
raop->clientFPSdata = 0;
|
||||
|
||||
raop->max_ntp_timeouts = 0;
|
||||
raop->audio_delay_micros = 250000;
|
||||
|
||||
raop->hls_support = false;
|
||||
@@ -662,9 +660,6 @@ int raop_set_plist(raop_t *raop, const char *plist_item, const int value) {
|
||||
} else if (strcmp(plist_item, "clientFPSdata") == 0) {
|
||||
raop->clientFPSdata = (value ? 1 : 0);
|
||||
if ((int) raop->clientFPSdata != value) retval = 1;
|
||||
} else if (strcmp(plist_item, "max_ntp_timeouts") == 0) {
|
||||
raop->max_ntp_timeouts = (value > 0 ? value : 0);
|
||||
if (raop->max_ntp_timeouts != value) retval = 1;
|
||||
} else if (strcmp(plist_item, "audio_delay_micros") == 0) {
|
||||
if (value >= 0 && value <= 10 * SECOND_IN_USECS) {
|
||||
raop->audio_delay_micros = value;
|
||||
|
||||
@@ -67,11 +67,12 @@ struct raop_callbacks_s {
|
||||
void (*video_process)(void *cls, raop_ntp_t *ntp, video_decode_struct *data);
|
||||
void (*video_pause)(void *cls);
|
||||
void (*video_resume)(void *cls);
|
||||
void (*conn_feedback) (void *cls);
|
||||
|
||||
/* Optional but recommended callback functions */
|
||||
void (*conn_init)(void *cls);
|
||||
void (*conn_destroy)(void *cls);
|
||||
void (*conn_reset) (void *cls, int timeouts, bool reset_video);
|
||||
void (*conn_reset) (void *cls);
|
||||
void (*conn_teardown)(void *cls, bool *teardown_96, bool *teardown_110 );
|
||||
void (*audio_flush)(void *cls);
|
||||
void (*video_flush)(void *cls);
|
||||
|
||||
@@ -742,7 +742,7 @@ raop_handler_setup(raop_conn_t *conn,
|
||||
}
|
||||
conn->raop_ntp = raop_ntp_init(conn->raop->logger, &conn->raop->callbacks, remote,
|
||||
conn->remotelen, (unsigned short) timing_rport, &time_protocol);
|
||||
raop_ntp_start(conn->raop_ntp, &timing_lport, conn->raop->max_ntp_timeouts);
|
||||
raop_ntp_start(conn->raop_ntp, &timing_lport);
|
||||
conn->raop_rtp = raop_rtp_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_ntp,
|
||||
remote, conn->remotelen, aeskey, aesiv);
|
||||
conn->raop_rtp_mirror = raop_rtp_mirror_init(conn->raop->logger, &conn->raop->callbacks,
|
||||
@@ -983,6 +983,8 @@ raop_handler_feedback(raop_conn_t *conn,
|
||||
char **response_data, int *response_datalen)
|
||||
{
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "raop_handler_feedback");
|
||||
/* register receipt of client's "heartbeat" signal */
|
||||
conn->raop->callbacks.conn_feedback(conn->raop->callbacks.cls);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -58,8 +58,6 @@ struct raop_ntp_s {
|
||||
logger_t *logger;
|
||||
raop_callbacks_t callbacks;
|
||||
|
||||
int max_ntp_timeouts;
|
||||
|
||||
thread_handle_t thread;
|
||||
mutex_handle_t run_mutex;
|
||||
|
||||
@@ -94,6 +92,8 @@ struct raop_ntp_s {
|
||||
int tsock;
|
||||
|
||||
timing_protocol_t time_protocol;
|
||||
bool client_time_received;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -153,6 +153,7 @@ raop_ntp_t *raop_ntp_init(logger_t *logger, raop_callbacks_t *callbacks, const c
|
||||
raop_ntp->logger = logger;
|
||||
memcpy(&raop_ntp->callbacks, callbacks, sizeof(raop_callbacks_t));
|
||||
raop_ntp->timing_rport = timing_rport;
|
||||
raop_ntp->client_time_received = false;
|
||||
|
||||
if (raop_ntp_parse_remote(raop_ntp, remote, remote_addr_len) < 0) {
|
||||
free(raop_ntp);
|
||||
@@ -274,8 +275,6 @@ raop_ntp_thread(void *arg)
|
||||
};
|
||||
raop_ntp_data_t data_sorted[RAOP_NTP_DATA_COUNT];
|
||||
const unsigned two_pow_n[RAOP_NTP_DATA_COUNT] = {2, 4, 8, 16, 32, 64, 128, 256};
|
||||
int timeout_counter = 0;
|
||||
bool conn_reset = false;
|
||||
bool logger_debug = (logger_get_level(raop_ntp->logger) >= LOGGER_DEBUG);
|
||||
|
||||
while (1) {
|
||||
@@ -308,20 +307,15 @@ raop_ntp_thread(void *arg)
|
||||
// Read response
|
||||
response_len = recvfrom(raop_ntp->tsock, (char *)response, sizeof(response), 0, NULL, NULL);
|
||||
if (response_len < 0) {
|
||||
timeout_counter++;
|
||||
char time[30];
|
||||
int level = (timeout_counter == 1 ? LOGGER_DEBUG : LOGGER_ERR);
|
||||
ntp_timestamp_to_time(send_time, time, sizeof(time));
|
||||
logger_log(raop_ntp->logger, level, "raop_ntp receive timeout %d (limit %d) (request sent %s)",
|
||||
timeout_counter, raop_ntp->max_ntp_timeouts, time);
|
||||
if (timeout_counter == raop_ntp->max_ntp_timeouts) {
|
||||
conn_reset = true; /* client is no longer responding */
|
||||
break;
|
||||
}
|
||||
logger_log(raop_ntp->logger, LOGGER_DEBUG , "raop_ntp receive timeout (request sent %s)", time);
|
||||
} else {
|
||||
if (!raop_ntp->client_time_received) {
|
||||
raop_ntp->client_time_received = true;
|
||||
}
|
||||
//local time of the server when the NTP response packet returns
|
||||
int64_t t3 = (int64_t) raop_ntp_get_local_time(raop_ntp);
|
||||
timeout_counter = 0;
|
||||
|
||||
// Local time of the server when the NTP request packet leaves the server
|
||||
int64_t t0 = (int64_t) byteutils_get_ntp_timestamp(response, 8);
|
||||
@@ -391,15 +385,11 @@ raop_ntp_thread(void *arg)
|
||||
MUTEX_UNLOCK(raop_ntp->run_mutex);
|
||||
|
||||
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp exiting thread");
|
||||
if (conn_reset && raop_ntp->callbacks.conn_reset) {
|
||||
const bool video_reset = false; /* leave "frozen video" in place */
|
||||
raop_ntp->callbacks.conn_reset(raop_ntp->callbacks.cls, timeout_counter, video_reset);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
raop_ntp_start(raop_ntp_t *raop_ntp, unsigned short *timing_lport, int max_ntp_timeouts)
|
||||
raop_ntp_start(raop_ntp_t *raop_ntp, unsigned short *timing_lport)
|
||||
{
|
||||
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp starting time");
|
||||
int use_ipv6 = 0;
|
||||
@@ -407,7 +397,6 @@ raop_ntp_start(raop_ntp_t *raop_ntp, unsigned short *timing_lport, int max_ntp_t
|
||||
assert(raop_ntp);
|
||||
assert(timing_lport);
|
||||
|
||||
raop_ntp->max_ntp_timeouts = max_ntp_timeouts;
|
||||
raop_ntp->timing_lport = *timing_lport;
|
||||
|
||||
MUTEX_LOCK(raop_ntp->run_mutex);
|
||||
|
||||
@@ -27,7 +27,7 @@ typedef struct raop_ntp_s raop_ntp_t;
|
||||
|
||||
typedef enum timing_protocol_e { NTP, TP_NONE, TP_OTHER, TP_UNSPECIFIED } timing_protocol_t;
|
||||
|
||||
void raop_ntp_start(raop_ntp_t *raop_ntp, unsigned short *timing_lport, int max_ntp_timeouts);
|
||||
void raop_ntp_start(raop_ntp_t *raop_ntp, unsigned short *timing_lport);
|
||||
|
||||
void raop_ntp_stop(raop_ntp_t *raop_ntp);
|
||||
|
||||
|
||||
@@ -804,9 +804,8 @@ raop_rtp_mirror_thread(void *arg)
|
||||
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
|
||||
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror exiting TCP thread");
|
||||
if (conn_reset && raop_rtp_mirror->callbacks.conn_reset) {
|
||||
const bool video_reset = false; /* leave "frozen video" showing */
|
||||
raop_rtp_mirror->callbacks.conn_reset(raop_rtp_mirror->callbacks.cls, 0, video_reset);
|
||||
if (conn_reset&& raop_rtp_mirror->callbacks.conn_reset) {
|
||||
raop_rtp_mirror->callbacks.conn_reset(raop_rtp_mirror->callbacks.cls);
|
||||
}
|
||||
|
||||
if (unsupported_codec) {
|
||||
|
||||
2
uxplay.1
2
uxplay.1
@@ -106,7 +106,7 @@ UxPlay 1.71: An open\-source AirPlay mirroring (+ audio streaming) server:
|
||||
.TP
|
||||
\fB\-ca\fI fn \fR In Airplay Audio (ALAC) mode, write cover-art to file fn.
|
||||
.TP
|
||||
\fB\-reset\fR n Reset after 3n seconds client silence (default 5, 0=never).
|
||||
\fB\-reset\fR n Reset after n seconds client silence (default n=15, 0=never).
|
||||
.TP
|
||||
\fB\-nofreeze\fR Do NOT leave frozen screen in place after reset.
|
||||
.TP
|
||||
|
||||
58
uxplay.cpp
58
uxplay.cpp
@@ -70,7 +70,7 @@
|
||||
#define DEFAULT_DEBUG_LOG false
|
||||
#define LOWEST_ALLOWED_PORT 1024
|
||||
#define HIGHEST_PORT 65535
|
||||
#define NTP_TIMEOUT_LIMIT 5
|
||||
#define MISSED_FEEDBACK_LIMIT 15
|
||||
#define BT709_FIX "capssetter caps=\"video/x-h264, colorimetry=bt709\""
|
||||
#define SRGB_FIX " ! video/x-raw,colorimetry=sRGB,format=RGB ! "
|
||||
#ifdef FULL_RANGE_RGB_FIX
|
||||
@@ -104,7 +104,6 @@ static std::string video_parser = "h264parse";
|
||||
static std::string video_decoder = "decodebin";
|
||||
static std::string video_converter = "videoconvert";
|
||||
static bool show_client_FPS_data = false;
|
||||
static unsigned int max_ntp_timeouts = NTP_TIMEOUT_LIMIT;
|
||||
static FILE *video_dumpfile = NULL;
|
||||
static std::string video_dumpfile_name = "videodump";
|
||||
static int video_dump_limit = 0;
|
||||
@@ -156,6 +155,8 @@ static std::string url = "";
|
||||
static guint gst_x11_window_id = 0;
|
||||
static guint gst_hls_position_id = 0;
|
||||
static bool preserve_connections = false;
|
||||
static guint missed_feedback_limit = MISSED_FEEDBACK_LIMIT;
|
||||
static guint missed_feedback = 0;
|
||||
|
||||
/* logging */
|
||||
|
||||
@@ -365,6 +366,28 @@ static void dump_video_to_file(unsigned char *data, int datalen) {
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean feedback_callback(gpointer loop) {
|
||||
if (open_connections) {
|
||||
if (missed_feedback_limit && missed_feedback > missed_feedback_limit) {
|
||||
LOGI("***ERROR lost connection with client (network problem?)");
|
||||
LOGI("%u missed client feedback signals exceeds limit of %u", missed_feedback, missed_feedback_limit);
|
||||
LOGI(" Sometimes the network connection may recover after a longer delay:\n"
|
||||
" the default limit n = %d seconds, can be changed with the \"-reset n\" option", MISSED_FEEDBACK_LIMIT);
|
||||
if (!nofreeze) {
|
||||
close_window = false; /* leave "frozen" window open if reset_video is false */
|
||||
}
|
||||
raop_stop(raop);
|
||||
reset_loop = true;
|
||||
} else if (missed_feedback > 2) {
|
||||
LOGE("%u missed client feedback signals (expected once per second); client may be offline", missed_feedback);
|
||||
}
|
||||
missed_feedback++;
|
||||
} else {
|
||||
missed_feedback = 0;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean reset_callback(gpointer loop) {
|
||||
if (reset_loop) {
|
||||
g_main_loop_quit((GMainLoop *) loop);
|
||||
@@ -435,6 +458,8 @@ static void main_loop() {
|
||||
gst_bus_watch_id[i] = (guint) video_renderer_listen((void *)loop, i);
|
||||
}
|
||||
}
|
||||
missed_feedback = 0;
|
||||
guint feedback_watch_id = g_timeout_add_seconds(1, (GSourceFunc) feedback_callback, (gpointer) loop);
|
||||
guint reset_watch_id = g_timeout_add(100, (GSourceFunc) reset_callback, (gpointer) loop);
|
||||
guint video_reset_watch_id = g_timeout_add(100, (GSourceFunc) video_reset_callback, (gpointer) loop);
|
||||
guint sigterm_watch_id = g_unix_signal_add(SIGTERM, (GSourceFunc) sigterm_callback, (gpointer) loop);
|
||||
@@ -449,6 +474,7 @@ static void main_loop() {
|
||||
if (sigterm_watch_id > 0) g_source_remove(sigterm_watch_id);
|
||||
if (reset_watch_id > 0) g_source_remove(reset_watch_id);
|
||||
if (video_reset_watch_id > 0) g_source_remove(video_reset_watch_id);
|
||||
if (feedback_watch_id > 0) g_source_remove(feedback_watch_id);
|
||||
g_main_loop_unref(loop);
|
||||
}
|
||||
|
||||
@@ -657,7 +683,7 @@ static void print_info (char *name) {
|
||||
printf("-as 0 (or -a) Turn audio off, streamed video only\n");
|
||||
printf("-al x Audio latency in seconds (default 0.25) reported to client.\n");
|
||||
printf("-ca <fn> In Airplay Audio (ALAC) mode, write cover-art to file <fn>\n");
|
||||
printf("-reset n Reset after 3n seconds client silence (default %d, 0=never)\n", NTP_TIMEOUT_LIMIT);
|
||||
printf("-reset n Reset after n seconds of client silence (default n=%d, 0=never)\n", MISSED_FEEDBACK_LIMIT);
|
||||
printf("-nofreeze Do NOT leave frozen screen in place after reset\n");
|
||||
printf("-nc Do NOT Close video window when client stops mirroring\n");
|
||||
printf("-nohold Drop current connection when new client connects.\n");
|
||||
@@ -739,7 +765,7 @@ static bool get_value (const char *str, unsigned int *n) {
|
||||
static bool get_ports (int nports, std::string option, const char * value, unsigned short * const port) {
|
||||
/*valid entries are comma-separated values port_1,port_2,...,port_r, 0 < r <= nports */
|
||||
/*where ports are distinct, and are in the allowed range. */
|
||||
/*missing values are consecutive to last given value (at least one value needed). */
|
||||
/*missed values are consecutive to last given value (at least one value needed). */
|
||||
char *end;
|
||||
unsigned long l;
|
||||
std::size_t pos;
|
||||
@@ -1020,9 +1046,11 @@ static void parse_arguments (int argc, char *argv[]) {
|
||||
} else if (arg == "-FPSdata") {
|
||||
show_client_FPS_data = true;
|
||||
} else if (arg == "-reset") {
|
||||
max_ntp_timeouts = 0;
|
||||
if (!get_value(argv[++i], &max_ntp_timeouts)) {
|
||||
fprintf(stderr, "invalid \"-reset %s\"; -reset n must have n >= 0, default n = %d\n", argv[i], NTP_TIMEOUT_LIMIT);
|
||||
/* now using feedback (every 1 sec ) instead of ntp timeouts (every 3 secs) to detect offline client and reset connections */
|
||||
fprintf(stderr,"*** NOTE CHANGE: -reset n now means reset n seconds (not 3n seconds) after client goes offline\n");
|
||||
missed_feedback_limit = 0;
|
||||
if (!get_value(argv[++i], &missed_feedback_limit)) {
|
||||
fprintf(stderr, "invalid \"-reset %s\"; -reset n must have n >= 0, default n = %d seconds\n", argv[i], MISSED_FEEDBACK_LIMIT);
|
||||
exit(1);
|
||||
}
|
||||
} else if (arg == "-vdmp") {
|
||||
@@ -1587,15 +1615,15 @@ extern "C" void conn_destroy (void *cls) {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void conn_reset (void *cls, int timeouts, bool reset_video) {
|
||||
extern "C" void conn_feedback (void *cls) {
|
||||
/* received client heartbeat signal: connection still exists */
|
||||
missed_feedback = 0;
|
||||
}
|
||||
|
||||
extern "C" void conn_reset (void *cls) {
|
||||
LOGI("***ERROR lost connection with client (network problem?)");
|
||||
if (timeouts) {
|
||||
LOGI(" Client no-response limit of %d timeouts (%d seconds) reached:", timeouts, 3*timeouts);
|
||||
LOGI(" Sometimes the network connection may recover after a longer delay:\n"
|
||||
" the default timeout limit n = %d can be changed with the \"-reset n\" option", NTP_TIMEOUT_LIMIT);
|
||||
}
|
||||
if (!nofreeze) {
|
||||
close_window = reset_video; /* leave "frozen" window open if reset_video is false */
|
||||
close_window = false; /* leave "frozen" window open */
|
||||
}
|
||||
raop_stop(raop);
|
||||
reset_loop = true;
|
||||
@@ -1931,6 +1959,7 @@ static int start_raop_server (unsigned short display[5], unsigned short tcp[3],
|
||||
raop_cbs.conn_init = conn_init;
|
||||
raop_cbs.conn_destroy = conn_destroy;
|
||||
raop_cbs.conn_reset = conn_reset;
|
||||
raop_cbs.conn_feedback = conn_feedback;
|
||||
raop_cbs.conn_teardown = conn_teardown;
|
||||
raop_cbs.audio_process = audio_process;
|
||||
raop_cbs.video_process = video_process;
|
||||
@@ -1981,7 +2010,6 @@ static int start_raop_server (unsigned short display[5], unsigned short tcp[3],
|
||||
if (display[4]) raop_set_plist(raop, "overscanned", (int) display[4]);
|
||||
|
||||
if (show_client_FPS_data) raop_set_plist(raop, "clientFPSdata", 1);
|
||||
raop_set_plist(raop, "max_ntp_timeouts", max_ntp_timeouts);
|
||||
if (audiodelay >= 0) raop_set_plist(raop, "audio_delay_micros", audiodelay);
|
||||
if (require_password) raop_set_plist(raop, "pin", (int) pin);
|
||||
if (hls_support) raop_set_plist(raop, "hls", 1);
|
||||
|
||||
Reference in New Issue
Block a user