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"); }