From c8025be7118d45850ecba2161f8129939bd84909 Mon Sep 17 00:00:00 2001
From: "F. Duncanh"
Date: Mon, 6 Feb 2023 14:15:52 -0500
Subject: [PATCH] v1.63: fix audio sync with client video in Audio-only mode
---
README.html | 42 ++++++++++++----------------
README.md | 25 ++++++++---------
README.txt | 39 +++++++++++---------------
lib/raop.c | 7 +++++
lib/raop_handlers.h | 13 +++++++--
lib/raop_rtp.c | 14 +++++-----
lib/raop_rtp.h | 4 +--
renderers/audio_renderer.h | 2 +-
renderers/audio_renderer_gstreamer.c | 16 ++++++-----
uxplay.1 | 6 ++--
uxplay.cpp | 28 ++++++++-----------
11 files changed, 98 insertions(+), 98 deletions(-)
diff --git a/README.html b/README.html
index cfb1616..d588998 100644
--- a/README.html
+++ b/README.html
@@ -1,6 +1,6 @@
UxPlay
-1.62: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix
+id="uxplay-1.63-airplay-mirror-and-airplay-audio-server-for-linux-macos-and-unix-now-also-runs-on-windows.">UxPlay
+1.63: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix
(now also runs on Windows).
Now
@@ -400,17 +400,11 @@ from sources like Apple Music in Audio-Only (ALAC) mode: run
a image viewer with an autoreload feature: an example is “feh”: run
“feh -R 1 <name>” in the foreground; terminate feh
and then Uxplay with “ctrl-C fg ctrl-C”.
-If you wish to listen in Audio-Only mode on the server while
-watching the client screen (for video or Apple Music song lyrics, etc.),
-the video on the client is delayed by about 5 seconds behind the the
-audio on the server (this is a Legacy mode issue: the client does
-not receive latency information to sync its video with audio played on
-the server). Since UxPlay-1.62, this can be corrected with the
-audio offset option -ao x with an
-x of about 5.0 (allowed values are decimal numbers between 0
-and 10.0 seconds); this workaround just delays playing of audio on the
-server by x seconds, so the effect of pausing or changing
-tracks on the client will also be delayed.
+In Audio-Only mode the server needs to specify a latency to the
+client so the client can show video in sync with audio played on the
+server. The default is 0.25 seconds: this can be changed with the
+-ao x.y option, where x.y is a decimal like 0.25 in the
+range [0.0, 10.0], with microsecond resolution.
One common problem involves GStreamer attempting to use
incorrectly-configured or absent accelerated hardware h264 video
@@ -786,17 +780,11 @@ parameters to be included with the audiosink name. (Some choices of
audiosink might not work on your system.)
-as 0 (or just -a) suppresses
playing of streamed audio, but displays streamed video.
--ao x.y adds an audio offset time in (decimal)
-seconds to Audio-only (ALAC) streams to allow synchronization of sound
-playing on the UxPlay server with video on the client which delays
-playing the audio by x.y seconds (a decimal number). In the
-AirPlay Legacy mode used by UxPlay, the client cannot obtain audio
-latency information from the server, and appears to assume a latency of
-about 5 seconds. This can be compensated for with offset values such as
--ao 5 (but the effect of a pause in play etc., on the
-client will also be delayed). The -ao option accepts values in the range
-[0,10], which it converts to a whole number of milliseconds (-ao 1.2345
-gives 1234 msec audio delay).
+-ao x.y specifies an audio latency) in (decimal)
+seconds in Audio-only (ALAC), that is reported to the client so it can
+synchronise its video with audio played on the server. Values in the
+range [0.0, 10.0] seconds are allowed, and will be converted to a whole
+number of microseconds. Default is 0.25 sec (250000 usec).
-ca filename provides a file (where
filename can include a full path) used for output of “cover
art” (from Apple Music, etc.,) in audio-only ALAC mode. This
@@ -1140,6 +1128,12 @@ as “SupportsLegacyPairing”) of the “features” plist code (reported to
the client by the AirPlay server) to be set. The “features” code and
other settings are set in UxPlay/lib/dnssdint.h.
Changelog
+1.63 2023-02-06 Corrected -ao option. Now allows audio latency
+reported to client to be changed from default of 0.25 sec (may not be
+necessary). Synchronisation of audio on server with video on client in
+audio-only ALAC mode now works, as sync=true is now used in the ALAC
+GStreamer pipeline. Internal change: all times are now given in
+nanoseconds.
1.62 2023-01-18 Added Audio-only mode time offset -ao x to allow user
synchronization of ALAC audio playing on the server with video, song
lyrics, etc. playing on the client. x = 5.0 appears to be optimal in
diff --git a/README.md b/README.md
index f895dbe..27b3287 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# UxPlay 1.62: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix (now also runs on Windows).
+# UxPlay 1.63: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix (now also runs on Windows).
### Now developed at the GitHub site [https://github.com/FDH2/UxPlay](https://github.com/FDH2/UxPlay) (where all user issues should be posted).
@@ -338,12 +338,9 @@ run "`uxplay -ca &`" in the background, then run a image viewer with an a
is "feh": run "``feh -R 1 ``"
in the foreground; terminate feh and then Uxplay with "`ctrl-C fg ctrl-C`".
-* If you wish to listen in Audio-Only mode on the server while watching the client screen (for video or Apple Music song lyrics, etc.),
-the video on the client is delayed by about 5 seconds behind the the audio on the server (_this is a Legacy mode issue: the client
-does not receive latency information to sync its video with audio played on the server_). Since UxPlay-1.62, this can be corrected with
-the **audio offset** option `-ao x` with an _x_ of about 5.0
-(allowed values are decimal numbers between 0 and 10.0 seconds); this workaround just delays playing of audio on the server by _x_ seconds, so the effect
-of pausing or changing tracks on the client will also be delayed.
+* In Audio-Only mode the server needs to specify a latency to the client so the client can show video in sync with audio played
+on the server. The default is 0.25 seconds: this can be changed with the `-ao x.y` option, where x.y is a decimal
+like 0.25 in the range [0.0, 10.0], with microsecond resolution.
**One common problem involves GStreamer
attempting to use incorrectly-configured or absent accelerated hardware h264
@@ -664,12 +661,9 @@ which will not work if a firewall is running.
**-as 0** (or just **-a**) suppresses playing of streamed audio, but displays streamed video.
-**-ao x.y** adds an audio offset time in (decimal) seconds to Audio-only (ALAC) streams to allow synchronization of sound
- playing on the UxPlay server with video on the client which delays playing the audio by _x.y_ seconds (a
- decimal number). In the AirPlay Legacy mode used by UxPlay, the client cannot obtain audio latency information
- from the server, and appears to assume a latency of about 5 seconds. This can be compensated for with offset values such
- as `-ao 5` (but the effect of a pause in play etc., on the client will also be delayed). The -ao option accepts
- values in the range [0,10], which it converts to a whole number of milliseconds (-ao 1.2345 gives 1234 msec audio delay).
+**-ao x.y** specifies an audio latency) in (decimal) seconds in Audio-only (ALAC), that is reported to the client so it
+ can synchronise its video with audio played on the server. Values in the range [0.0, 10.0] seconds are allowed, and
+ will be converted to a whole number of microseconds. Default is 0.25 sec (250000 usec).
**-ca _filename_** provides a file (where _filename_ can include a full path) used for output of "cover art"
(from Apple Music, _etc._,) in audio-only ALAC mode. This file is overwritten with the latest cover art as
@@ -945,6 +939,11 @@ tvOS 12.2.1); it seems that the use of "legacy" protocol just requires bit 27 (l
The "features" code and other settings are set in `UxPlay/lib/dnssdint.h`.
# Changelog
+1.63 2023-02-06 Corrected -ao option. Now allows audio latency reported to client to be changed
+ from default of 0.25 sec (may not be necessary). Synchronisation of audio on server
+ with video on client in audio-only ALAC mode now works, as sync=true is now used in
+ the ALAC GStreamer pipeline. Internal change: all times are now given in nanoseconds.
+
1.62 2023-01-18 Added Audio-only mode time offset -ao x to allow user synchronization of ALAC
audio playing on the server with video, song lyrics, etc. playing on the client.
x = 5.0 appears to be optimal in many cases. Quality fixes: cleanup in volume
diff --git a/README.txt b/README.txt
index b158d6f..1f26d2c 100644
--- a/README.txt
+++ b/README.txt
@@ -1,4 +1,4 @@
-# UxPlay 1.62: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix (now also runs on Windows).
+# UxPlay 1.63: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix (now also runs on Windows).
### Now developed at the GitHub site (where all user issues should be posted).
@@ -404,17 +404,11 @@ for help with this or other problems.
"`feh -R 1 `" in the foreground; terminate feh and then Uxplay
with "`ctrl-C fg ctrl-C`".
-- If you wish to listen in Audio-Only mode on the server while
- watching the client screen (for video or Apple Music song lyrics,
- etc.), the video on the client is delayed by about 5 seconds behind
- the the audio on the server (*this is a Legacy mode issue: the
- client does not receive latency information to sync its video with
- audio played on the server*). Since UxPlay-1.62, this can be
- corrected with the **audio offset** option `-ao x` with an *x* of
- about 5.0 (allowed values are decimal numbers between 0 and 10.0
- seconds); this workaround just delays playing of audio on the server
- by *x* seconds, so the effect of pausing or changing tracks on the
- client will also be delayed.
+- In Audio-Only mode the server needs to specify a latency to the
+ client so the client can show video in sync with audio played on the
+ server. The default is 0.25 seconds: this can be changed with the
+ `-ao x.y` option, where x.y is a decimal like 0.25 in the range
+ \[0.0, 10.0\], with microsecond resolution.
**One common problem involves GStreamer attempting to use
incorrectly-configured or absent accelerated hardware h264 video
@@ -808,16 +802,11 @@ name. (Some choices of audiosink might not work on your system.)
**-as 0** (or just **-a**) suppresses playing of streamed audio, but
displays streamed video.
-**-ao x.y** adds an audio offset time in (decimal) seconds to Audio-only
-(ALAC) streams to allow synchronization of sound playing on the UxPlay
-server with video on the client which delays playing the audio by *x.y*
-seconds (a decimal number). In the AirPlay Legacy mode used by UxPlay,
-the client cannot obtain audio latency information from the server, and
-appears to assume a latency of about 5 seconds. This can be compensated
-for with offset values such as `-ao 5` (but the effect of a pause in
-play etc., on the client will also be delayed). The -ao option accepts
-values in the range \[0,10\], which it converts to a whole number of
-milliseconds (-ao 1.2345 gives 1234 msec audio delay).
+**-ao x.y** specifies an audio latency) in (decimal) seconds in
+Audio-only (ALAC), that is reported to the client so it can synchronise
+its video with audio played on the server. Values in the range \[0.0,
+10.0\] seconds are allowed, and will be converted to a whole number of
+microseconds. Default is 0.25 sec (250000 usec).
**-ca *filename*** provides a file (where *filename* can include a full
path) used for output of "cover art" (from Apple Music, *etc.*,) in
@@ -1183,6 +1172,12 @@ other settings are set in `UxPlay/lib/dnssdint.h`.
# Changelog
+1.63 2023-02-06 Corrected -ao option. Now allows audio latency reported
+to client to be changed from default of 0.25 sec (may not be necessary).
+Synchronisation of audio on server with video on client in audio-only
+ALAC mode now works, as sync=true is now used in the ALAC GStreamer
+pipeline. Internal change: all times are now given in nanoseconds.
+
1.62 2023-01-18 Added Audio-only mode time offset -ao x to allow user
synchronization of ALAC audio playing on the server with video, song
lyrics, etc. playing on the client. x = 5.0 appears to be optimal in
diff --git a/lib/raop.c b/lib/raop.c
index d7a8a26..3137b63 100644
--- a/lib/raop.c
+++ b/lib/raop.c
@@ -60,6 +60,7 @@ struct raop_s {
uint8_t overscanned;
uint8_t clientFPSdata;
+ int audio_delay_micros;
int max_ntp_timeouts;
};
@@ -461,6 +462,7 @@ raop_init(int max_clients, raop_callbacks_t *callbacks) {
raop->clientFPSdata = 0;
raop->max_ntp_timeouts = 0;
+ raop->audio_delay_micros = 250000;
return raop;
}
@@ -519,6 +521,11 @@ int raop_set_plist(raop_t *raop, const char *plist_item, const int value) {
} 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;
+ }
+ if (raop->audio_delay_micros != value) retval = 1;
} else {
retval = -1;
}
diff --git a/lib/raop_handlers.h b/lib/raop_handlers.h
index 759c72f..a47bb21 100644
--- a/lib/raop_handlers.h
+++ b/lib/raop_handlers.h
@@ -20,6 +20,8 @@
#include
#include
#include
+#define AUDIO_SAMPLE_RATE 44100 /* all supported AirPlay audio format use this sample rate */
+#define SECOND_IN_USECS 1000000
typedef void (*raop_handler_t)(raop_conn_t *, http_request_t *,
http_response_t *, char **, int *);
@@ -500,7 +502,9 @@ raop_handler_setup(raop_conn_t *conn,
unsigned short cport = conn->raop->control_lport, dport = conn->raop->data_lport;
unsigned short remote_cport = 0;
unsigned char ct;
- unsigned int sr = 44100; /* all AirPlay audio formats supported so far have sample rate 44.1kHz */
+ unsigned int sr = AUDIO_SAMPLE_RATE; /* all AirPlay audio formats supported so far have sample rate 44.1kHz */
+ unsigned int ad = (unsigned int) (((uint64_t) conn->raop->audio_delay_micros) * AUDIO_SAMPLE_RATE / SECOND_IN_USECS);
+
uint64_t uint_val = 0;
plist_t req_stream_control_port_node = plist_dict_get_item(req_stream_node, "controlPort");
plist_get_uint_val(req_stream_control_port_node, &uint_val);
@@ -545,7 +549,7 @@ raop_handler_setup(raop_conn_t *conn,
}
if (conn->raop_rtp) {
- raop_rtp_start_audio(conn->raop_rtp, use_udp, &remote_cport, &cport, &dport, &ct, &sr);
+ raop_rtp_start_audio(conn->raop_rtp, use_udp, &remote_cport, &cport, &dport, &ct, &sr, &ad);
logger_log(conn->raop->logger, LOGGER_DEBUG, "RAOP initialized success");
} else {
logger_log(conn->raop->logger, LOGGER_ERR, "RAOP not initialized at SETUP, playing will fail!");
@@ -686,7 +690,10 @@ raop_handler_record(raop_conn_t *conn,
http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen)
{
+ char audio_latency[12];
+ unsigned int ad = (unsigned int) (((uint64_t) conn->raop->audio_delay_micros) * AUDIO_SAMPLE_RATE / SECOND_IN_USECS);
+ sprintf(audio_latency, "%u", ad);
logger_log(conn->raop->logger, LOGGER_DEBUG, "raop_handler_record");
- http_response_add_header(response, "Audio-Latency", "11025");
+ http_response_add_header(response, "Audio-Latency", audio_latency);
http_response_add_header(response, "Audio-Jack-Status", "connected; type=analog");
}
diff --git a/lib/raop_rtp.c b/lib/raop_rtp.c
index efed2bc..82570a0 100644
--- a/lib/raop_rtp.c
+++ b/lib/raop_rtp.c
@@ -54,6 +54,7 @@ struct raop_rtp_s {
// Time and sync
raop_ntp_t *ntp;
double rtp_clock_rate;
+ unsigned int audio_delay_rtp;
int64_t rtp_sync_offset;
raop_rtp_sync_data_t sync_data[RAOP_RTP_SYNC_DATA_COUNT];
int sync_data_index;
@@ -551,14 +552,12 @@ raop_rtp_thread_udp(void *arg)
uint64_t sync_ntp_raw = byteutils_get_long_be(packet, 8);
uint64_t sync_ntp_remote = raop_ntp_timestamp_to_nano_seconds(sync_ntp_raw, true);
uint64_t sync_ntp_local = raop_ntp_convert_remote_time(raop_rtp->ntp, sync_ntp_remote);
- int64_t shift;
+ int64_t shift = 0;
switch (raop_rtp->ct) {
- case 0x08: /*AAC-ELD */
- shift = -11025; /* 44100/4 */
+ case 0x02: /* ALAC audio-only mode */
break;
- case 0x02:
default:
- shift = 0; /* not needed for ALAC (audio only) */
+ shift -= (int64_t) raop_rtp->audio_delay_rtp; /* remove delay in Mirror mode */
break;
}
char *str = utils_data_to_string(packet, packetlen, 20);
@@ -710,8 +709,8 @@ raop_rtp_thread_udp(void *arg)
// Start rtp service, three udp ports
void
-raop_rtp_start_audio(raop_rtp_t *raop_rtp, int use_udp, unsigned short *control_rport,
- unsigned short *control_lport, unsigned short *data_lport, unsigned char *ct, unsigned int *sr)
+raop_rtp_start_audio(raop_rtp_t *raop_rtp, int use_udp, unsigned short *control_rport, unsigned short *control_lport,
+ unsigned short *data_lport, unsigned char *ct, unsigned int *sr, unsigned int *ad)
{
logger_log(raop_rtp->logger, LOGGER_INFO, "raop_rtp starting audio");
int use_ipv6 = 0;
@@ -726,6 +725,7 @@ raop_rtp_start_audio(raop_rtp_t *raop_rtp, int use_udp, unsigned short *control_
raop_rtp->ct = *ct;
raop_rtp->rtp_clock_rate = SECOND_IN_NSECS / *sr;
+ raop_rtp->audio_delay_rtp = *ad;
/* Initialize ports and sockets */
raop_rtp->control_lport = *control_lport;
diff --git a/lib/raop_rtp.h b/lib/raop_rtp.h
index a1ca7a3..87751b3 100644
--- a/lib/raop_rtp.h
+++ b/lib/raop_rtp.h
@@ -29,8 +29,8 @@ typedef struct raop_rtp_s raop_rtp_t;
raop_rtp_t *raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, const unsigned char *remote,
int remotelen, const unsigned char *aeskey, const unsigned char *aesiv);
-void raop_rtp_start_audio(raop_rtp_t *raop_rtp, int use_udp, unsigned short *control_rport,
- unsigned short *control_lport, unsigned short *data_lport, unsigned char *ct, unsigned int *sr);
+void raop_rtp_start_audio(raop_rtp_t *raop_rtp, int use_udp, unsigned short *control_rport, unsigned short *control_lport,
+ unsigned short *data_lport, unsigned char *ct, unsigned int *sr, unsigned int *ad);
void raop_rtp_set_volume(raop_rtp_t *raop_rtp, float volume);
void raop_rtp_set_metadata(raop_rtp_t *raop_rtp, const char *data, int datalen);
diff --git a/renderers/audio_renderer.h b/renderers/audio_renderer.h
index 00e39c4..adeea75 100644
--- a/renderers/audio_renderer.h
+++ b/renderers/audio_renderer.h
@@ -30,7 +30,7 @@ extern "C" {
#include "../lib/logger.h"
bool gstreamer_init();
-void audio_renderer_init(logger_t *logger, const char* audiosink, const char* audiodelay);
+void audio_renderer_init(logger_t *logger, const char* audiosink);
void audio_renderer_start(unsigned char* compression_type);
void audio_renderer_stop();
void audio_renderer_render_buffer(unsigned char* data, int *data_len, unsigned short *seqnum, uint64_t *ntp_time);
diff --git a/renderers/audio_renderer_gstreamer.c b/renderers/audio_renderer_gstreamer.c
index 9df50bb..239bc16 100644
--- a/renderers/audio_renderer_gstreamer.c
+++ b/renderers/audio_renderer_gstreamer.c
@@ -82,7 +82,7 @@ static GstClockTime gst_audio_pipeline_base_time = GST_CLOCK_TIME_NONE;
static logger_t *logger = NULL;
const char * format[NFORMATS];
-void audio_renderer_init(logger_t *render_logger, const char* audiosink, const char* audio_delay) {
+void audio_renderer_init(logger_t *render_logger, const char* audiosink) {
GError *error = NULL;
GstCaps *caps = NULL;
GstClock *clock = gst_system_clock_obtain();
@@ -101,11 +101,6 @@ void audio_renderer_init(logger_t *render_logger, const char* audiosink, const c
g_string_append(launch, "! avdec_aac ! ");
break;
case 1: /* ALAC */
- if (audio_delay[0]) {
- g_string_append(launch, "min-threshold-time=");
- g_string_append(launch, audio_delay);
- g_string_append(launch, "000000 ");
- }
g_string_append(launch, "! avdec_alac ! ");
break;
case 3: /*PCM*/
@@ -117,7 +112,14 @@ void audio_renderer_init(logger_t *render_logger, const char* audiosink, const c
g_string_append (launch, "audioresample ! "); /* wasapisink must resample from 44.1 kHz to 48 kHz */
g_string_append (launch, "volume name=volume ! level ! ");
g_string_append (launch, audiosink);
- g_string_append (launch, " sync=false");
+ switch(i) {
+ case 1: /*ALAC*/
+ g_string_append (launch, " sync=true");
+ break;
+ default:
+ g_string_append (launch, " sync=false");
+ break;
+ }
renderer_type[i]->pipeline = gst_parse_launch(launch->str, &error);
if (error) {
g_error ("gst_parse_launch error (audio %d):\n %s\n", i+1, error->message);
diff --git a/uxplay.1 b/uxplay.1
index 31be5dc..976c085 100644
--- a/uxplay.1
+++ b/uxplay.1
@@ -1,11 +1,11 @@
-.TH UXPLAY "1" "January 2023" "1.62" "User Commands"
+.TH UXPLAY "1" "February 2023" "1.63" "User Commands"
.SH NAME
uxplay \- start AirPlay server
.SH SYNOPSIS
.B uxplay
[\fI\,-n name\/\fR] [\fI\,-s wxh\/\fR] [\fI\,-p \/\fR[\fI\,n\/\fR]] [more \fI OPTIONS \/\fR ...]
.SH DESCRIPTION
-UxPlay 1.62: An open\-source AirPlay mirroring (+ audio streaming) server.
+UxPlay 1.63: An open\-source AirPlay mirroring (+ audio streaming) server.
.SH OPTIONS
.TP
.B
@@ -73,7 +73,7 @@ UxPlay 1.62: An open\-source AirPlay mirroring (+ audio streaming) server.
.TP
\fB\-as\fR 0 (or \fB\-a\fR) Turn audio off, streamed video only.
.TP
-\fB\-ao\fR x.y Audio offset time in seconds (default 0.0) in Audio-only mode.
+\fB\-ao\fR x.y Audio-only mode latency in seconds (default 0.25) used by client.
.TP
\fB\-ca\fI fn \fR In Airplay Audio (ALAC) mode, write cover-art to file fn.
.TP
diff --git a/uxplay.cpp b/uxplay.cpp
index 747257b..9bec039 100644
--- a/uxplay.cpp
+++ b/uxplay.cpp
@@ -51,8 +51,9 @@
#include "renderers/video_renderer.h"
#include "renderers/audio_renderer.h"
-#define VERSION "1.62"
+#define VERSION "1.63"
+#define SECOND_IN_USECS 1000000
#define DEFAULT_NAME "UxPlay"
#define DEFAULT_DEBUG_LOG false
#define LOWEST_ALLOWED_PORT 1024
@@ -73,7 +74,7 @@ static videoflip_t videoflip[2] = { NONE , NONE };
static bool use_video = true;
static unsigned char compression_type = 0;
static std::string audiosink = "autoaudiosink";
-static std::string audiodelay = "";
+static int audiodelay = -1;
static bool use_audio = true;
static bool new_window_closing_behavior = true;
static bool close_window;
@@ -387,7 +388,7 @@ static void print_info (char *name) {
printf(" some choices:pulsesink,alsasink,pipewiresink,jackaudiosink,\n");
printf(" osssink,oss4sink,osxaudiosink,wasapisink,directsoundsink.\n");
printf("-as 0 (or -a) Turn audio off, streamed video only\n");
- printf("-ao x.y Audio offset time in seconds (default 0.0) in Audio-only mode.\n");
+ printf("-ao x.y Audio-only mode latency in seconds (default 0.25) used by client.\n");
printf("-ca In Airplay Audio (ALAC) mode, write cover-art to file \n");
printf("-reset n Reset after 3n seconds client silence (default %d, 0=never)\n", NTP_TIMEOUT_LIMIT);
printf("-nc do Not Close video window when client stops mirroring\n");
@@ -741,20 +742,14 @@ static void parse_arguments (int argc, char *argv[]) {
int n;
char *end;
if (i < argc - 1 && *argv[i+1] != '-') {
- n = (int) (1000 * strtof(argv[++i], &end));
- if (*end == '\0' && n >=0 && n <= 10000) {
- audiodelay.erase();
- if (n > 0) {
- char* delay = new char[6];
- snprintf(delay, 6, "%d", n);
- audiodelay = delay;
- delete[] delay;
- }
+ n = (int) (strtof(argv[++i], &end) * SECOND_IN_USECS);
+ if (*end == '\0' && n >=0 && n <= 10 * SECOND_IN_USECS) {
+ audiodelay = n;
continue;
}
}
fprintf(stderr, "invalid argument -ao %s: must be a decimal time offset in seconds, range [0,10]\n"
- "(like 5 or 4.8, which will be converted to a whole number of milliseconds)\n", argv[i]);
+ "(like 5 or 4.8, which will be converted to a whole number of microseconds)\n", argv[i]);
exit(1);
} else {
fprintf(stderr, "unknown option %s, stopping\n",argv[i]);
@@ -1162,6 +1157,7 @@ int start_raop_server (unsigned short display[5], unsigned short tcp[3], unsigne
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);
/* network port selection (ports listed as "0" will be dynamically assigned) */
raop_set_tcp_ports(raop, tcp);
@@ -1284,10 +1280,10 @@ int main (int argc, char *argv[]) {
logger_set_level(render_logger, debug_log ? LOGGER_DEBUG : LOGGER_INFO);
if (use_audio) {
- if (audiodelay.c_str()[0]) {
- LOGI("Audio-only ALAC streams will be delayed by %s milliseconds", audiodelay.c_str());
+ if (audiodelay >= 0) {
+ LOGI("Audio-only ALAC streams will be delayed by %d microseconds", audiodelay);
}
- audio_renderer_init(render_logger, audiosink.c_str(), audiodelay.c_str());
+ audio_renderer_init(render_logger, audiosink.c_str());
} else {
LOGI("audio_disabled");
}