Compare commits

...
36 Commits
Author SHA1 Message Date
nils 73264f4534 Add ohm, an OpenHome Songcast sender audio backend
Set variables for a Docker build (used by another workflow). / Set variables required for docker build. (push) Successful in 28s
Build and conditionally push docker image / docker-vars (push) Successful in 28s
Build and conditionally push docker image / Build and conditionally push docker image (SHAIRPORT_SYNC_BRANCH=. NQPTP_BRANCH=${{ needs.docker-vars.outputs.nqptp_branch }} , ./docker/Dockerfile, main) (push) Failing after 31m53s
Build and conditionally push docker image / Build and conditionally push docker image (SHAIRPORT_SYNC_BRANCH=. , ./docker/classic/Dockerfile, classic) (push) Failing after 32m4s
Streams AirPlay audio to OpenHome/Songcast receivers as OHU unicast PCM
(16/24-bit). Sends are paced on the PTP master clock so receivers slave
to the AirPlay group clock through their buffer-fill servo; MediaLatency
carries the phase, matching the wire behaviour of Linn's own sender
(zero flags and timestamps). delay() reports the newest frame's playtime
lead so shairport feeds a receiver latency ahead. Handles Join, Listen,
Leave and Resend with a frame history, and forwards track metadata and
metatext from the metadata hub.
2026-08-28 23:48:23 +02:00
Mike Brady fadd43d06e Merge branch 'development' to 'master' 5.2.2.
Summary of changes:
* If the output rate is different to the input rate (e.g. 48,000 vs 44,100), look for the deepest output bit depth so that the transcoder has the maximum dynamic range available to minimise transcoding inaccuracies.
* Fix a bug whereby the audio backend latency offset was mishandled in AirPlay 2 operation.
* Fix an output format selection bug.
* Update the README reference to the MPRIS specification to the latest version.
* Remove the explicit `-` for `xxd` to read from `STDIN`. It's not necessary, and removing it makes it easier to package Shairport Sync on other build systems, such as OpenWrt.
2026-08-22 08:59:45 +01:00
Mike BradyandGitHub 6e10eca884 Update MPRIS specification reference in documentation [skip ci]
Updated README reference to the latest MPRIS specification version, acknowledging Dmitry Batin for the contribution.
2026-08-21 12:05:57 +01:00
Mike BradyandGitHub 5e170a67a8 Merge pull request #2255 from demosspro/patch-1
Update README.md to link to latest MPRIS information.
2026-08-21 12:00:19 +01:00
DMITRY BATINandGitHub 3ba7dfea6f Update README.md
Old link not working. Add a valid link to MPRIS spec.
- [v ] Documentation update
2026-08-21 10:56:47 +03:00
Mike BradyandGitHub 6cdd0eb794 Update release notes for version 5.3-dev-21 [skip ci]
Added enhancement regarding output rate and bit depth for transcoding.
2026-08-20 15:51:25 +01:00
Mike Brady 6966a74c82 If the output rate is different to the input rate, look for the deepest output bit depth, to give the transcoder the chance to minimise transcoding inaccuracies. 2026-08-20 15:47:37 +01:00
Mike BradyandGitHub c4e17b60ef Update release notes for version 5.3-dev-19 [skip ci]
Added a bug fix for audio backend latency offset handling in AirPlay 2 operation, including background information and acknowledgments.
2026-08-20 11:13:42 +01:00
Mike Brady b69e01cdf9 Correct the treatment of audio backend latency offset in Realtime streams. Fix a warning if the offset is too negative and that would require frames to be played before they could possibly arrive. 2026-08-20 10:55:30 +01:00
Mike BradyandGitHub d8165c1143 Merge pull request #2253 from prhodey/fix/deferred-airplay2-latency [skip ci]
Defer buffered AirPlay 2 latency offset conversion. (It needs a further little adjustment to handle Realtime streams, but that will be done in the next commit.)
2026-08-20 10:48:35 +01:00
prhodey 4df0145372 Defer buffered AirPlay 2 latency offset conversion 2026-08-19 20:10:50 -04:00
Mike Brady 08af668a5d Remove reference to log_to_syslog() if libdaemon support is included. 2026-07-29 12:15:36 +02:00
Mike Brady 42bd29d6da This is version 5.2 2026-07-28 17:15:07 +01:00
Mike Brady d6ac53bf4c Bug fix, docker-compose updates, statistics readoout improvement. 2026-07-02 14:17:53 +01:00
Mike Brady 5bd5f9c1d0 Merge branch 'master' of github.com:mikebrady/shairport-sync
Online update.
2026-06-27 18:52:36 +01:00
Mike Brady 1718f7b5af Add Jack deprecation note to the configuration file. 2026-06-22 18:53:51 +01:00
Mike Brady 368f3696c3 Add note about new --with-metadata- configuration settings. 2026-06-22 18:39:13 +01:00
Mike BradyandGitHub 4d0e9bb6c8 Remove markdown test from Bug Report template [skip ci]
Removed markdown test section from Bug Report template.
2026-06-22 16:55:05 +01:00
Mike BradyandGitHub 83961baad9 Add markdown test section to Bug Report template [skip ci]
Added a markdown test section to the Bug Report template.
2026-06-22 16:53:45 +01:00
Mike Brady d08637d3d6 Merge branch 'development'
Update of configuration flag documentation.
2026-06-22 13:09:06 +01:00
Mike Brady a6231e089c Merge branch 'development'
Update settings document.
2026-06-22 13:05:53 +01:00
Mike Brady c454867a04 Merge branch 'development' 2026-06-22 11:54:59 +01:00
Mike BradyandGitHub befe1d4a1a Fix comment for systemd-dev installation command [skip ci] 2026-05-01 15:52:01 +01:00
Mike Brady eed1b9f0b9 Fix missing configuration mapping parameter in PulseAudio backend initialisation. 2026-04-27 12:43:51 +01:00
Mike BradyandGitHub 930e523757 Update instructions for systemd-dev installation [skip ci]
Added caution regarding installing systemd-dev on backported systems.
2026-04-27 09:42:49 +01:00
Mike Brady 6ef48e59e6 5.0.3 2026-04-25 11:22:18 +01:00
Mike BradyandGitHub 06e824be0c Merge pull request #2192 from mikebrady/revert-2188-mqtt-inform-publish-errors
Revert "mqtt: promote publish-error log paths to inform() for debuggability"

This needs to be done to the `development` branch.
2026-04-20 10:10:36 +01:00
Mike BradyandGitHub 32458c08aa Revert "mqtt: promote publish-error log paths to inform() for debuggability" 2026-04-20 10:09:44 +01:00
Mike BradyandGitHub 4ecd42eda3 Merge pull request #2188 from harreh3iesh/mqtt-inform-publish-errors
MQTT: promote publish-error log paths to inform() for debuggability
2026-04-20 10:09:29 +01:00
harreh3iesh 2ee44830d3 mqtt: promote publish-error log paths to inform() for debuggability
The two error branches in mqtt_publish() logged at debug(1), which is
invisible at default syslog verbosity. A silently-dropped MQTT publish
is the exact failure mode users report as 'MQTT just stopped working'
with no errors anywhere in the logs. Raising these to inform() makes
such drops legible at default verbosity and includes the target topic
plus mosquitto_strerror() text in the non-NO_CONN branch so the
diagnostic actually names the failing call site.
2026-04-14 20:48:56 -04:00
Mike Brady 28eb383155 Fix a D-Bus Test Client compilation bug; turn off lots of less relevant debug messages; change how blocks of audio very far in the future are handled; add pw=true to the Bonjour information for classic AirPlay when built for AirPlay 2. 2026-03-28 18:42:12 +00:00
Mike BradyandGitHub dc7a048bd3 Update macOS version for build job 2026-03-27 08:14:30 +00:00
Mike BradyandGitHub dc4c334adf Update version number to 5.0.1 in configure.ac [skip ci] 2026-02-27 15:15:10 +00:00
Mike Brady 1230285491 5.0.1 2026-02-27 14:57:28 +00:00
Mike BradyandGitHub c956d763ca Fix punctuation and formatting in BUILD.md [skip ci] 2026-02-14 18:26:02 +00:00
Mike BradyandGitHub 7421421848 Refactor Docker workflow for tag handling 2026-02-14 12:54:47 +00:00
14 changed files with 1623 additions and 3784 deletions
+4 -1
View File
@@ -1,4 +1,7 @@
.github .github
documents documents
docker/Dockerfile docker/Dockerfile
docker/classic/Dockerfile docker/classic/Dockerfile
reference
ohm.txt
out-ohm
+1 -1
View File
@@ -44,7 +44,7 @@ Shairport Sync offers AirPlay 2 support for audio sources on:
- Transcoding: the output will be transcoded if necessary (e.g. from 44,100 to 48,000 fps) to match the output device's rate. - Transcoding: the output will be transcoded if necessary (e.g. from 44,100 to 48,000 fps) to match the output device's rate.
- Mixdown: mixdown to fewer output channels (e.g. 5.1 to stereo) is automatic but can be controlled. - Mixdown: mixdown to fewer output channels (e.g. 5.1 to stereo) is automatic but can be controlled.
- Shairport Sync can revert to "classic" AirPlay if necessary. - Shairport Sync can revert to "classic" AirPlay if necessary.
- Devices running Shairport Sync in AirPlay 2 mode can be [added](https://github.com/mikebrady/shairport-sync/blob/development/ADDINGTOHOME.md) to the Home app. - Devices running Shairport Sync in AirPlay 2 mode can be [added](https://github.com/mikebrady/shairport-sync/blob/master/ADDINGTOHOME.md) to the Home app.
- Shairport Sync can be built to support classic AirPlay (aka "AirPlay 1") only. Classic Airplay offers only one format, ALAC/S16/44100/2, but output transcoding is available to, for example, 48000 fps. - Shairport Sync can be built to support classic AirPlay (aka "AirPlay 1") only. Classic Airplay offers only one format, ALAC/S16/44100/2, but output transcoding is available to, for example, 48000 fps.
## What Does Not Work ## What Does Not Work
+1
View File
@@ -89,6 +89,7 @@ If you are building classic Shairport Sync, the list of packages is shorter:
libpopt-dev libconfig-dev libasound2-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev \ libpopt-dev libconfig-dev libasound2-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev \
libavutil-dev libavcodec-dev libavformat-dev libavutil-dev libavcodec-dev libavformat-dev
``` ```
Building on Ubuntu 24.10 or Debian 13 ("Trixie") and later – and possibly on other distributions – requires `systemd-dev`. Be very careful here if you are using backports -- there is a [report](https://github.com/mikebrady/shairport-sync/issues/2195#issuecomment-4325326002) that installing `systemd-dev` on a backported system can severely damage it. Building on Ubuntu 24.10 or Debian 13 ("Trixie") and later – and possibly on other distributions – requires `systemd-dev`. Be very careful here if you are using backports -- there is a [report](https://github.com/mikebrady/shairport-sync/issues/2195#issuecomment-4325326002) that installing `systemd-dev` on a backported system can severely damage it.
``` ```
$ apt install --dry-run --no-install-recommends systemd-dev $ apt install --dry-run --no-install-recommends systemd-dev
+4
View File
@@ -112,6 +112,10 @@ if USE_DUMMY
shairport_sync_SOURCES += audio_dummy.c shairport_sync_SOURCES += audio_dummy.c
endif endif
if USE_OHM
shairport_sync_SOURCES += audio_ohm.c
endif
if USE_AO if USE_AO
shairport_sync_SOURCES += audio_ao.c shairport_sync_SOURCES += audio_ao.c
endif endif
+3 -3
View File
@@ -16,15 +16,15 @@ Shairport Sync does not support AirPlay video or photo streaming.
* Next Steps and Advanced Topics are [here](ADVANCED%20TOPICS/README.md). * Next Steps and Advanced Topics are [here](ADVANCED%20TOPICS/README.md).
* Runtime settings are documented [here](scripts/shairport-sync.conf). * Runtime settings are documented [here](scripts/shairport-sync.conf).
* Build configuration options are detailed in [CONFIGURATION FLAGS.md](CONFIGURATION%20FLAGS.md). * Build configuration options are detailed in [CONFIGURATION FLAGS.md](CONFIGURATION%20FLAGS.md).
* The `man` page, detailing command line options, is [here](https://raw.githack.com/mikebrady/shairport-sync/development/man/shairport-sync.1.xml). * The `man` page, detailing command line options, is [here](https://raw.githack.com/mikebrady/shairport-sync/master/man/shairport-sync.1.xml).
* Some advanced topics are developed in [ADVANCED TOPICS](https://github.com/mikebrady/shairport-sync/tree/master/ADVANCED%20TOPICS). * Some advanced topics and developed in [ADVANCED TOPICS](https://github.com/mikebrady/shairport-sync/tree/master/ADVANCED%20TOPICS).
# Features # Features
* Outputs AirPlay audio to [ALSA](https://www.alsa-project.org/wiki/Main_Page), [sndio](http://www.sndio.org), [PipeWire](https://pipewire.org), [PulseAudio](https://www.freedesktop.org/wiki/Software/PulseAudio/), to a unix pipe or to `STDOUT`. It also has limited support for [libao](https://xiph.org/ao/). * Outputs AirPlay audio to [ALSA](https://www.alsa-project.org/wiki/Main_Page), [sndio](http://www.sndio.org), [PipeWire](https://pipewire.org), [PulseAudio](https://www.freedesktop.org/wiki/Software/PulseAudio/), to a unix pipe or to `STDOUT`. It also has limited support for [libao](https://xiph.org/ao/).
* Metadata — Shairport Sync can deliver metadata supplied by the source, such as Album Name, Artist Name, Cover Art, etc. through a pipe or UDP socket to a recipient application program — see https://github.com/mikebrady/shairport-sync-metadata-reader for a sample recipient. Sources that supply metadata include iTunes and the Music app in macOS and iOS. * Metadata — Shairport Sync can deliver metadata supplied by the source, such as Album Name, Artist Name, Cover Art, etc. through a pipe or UDP socket to a recipient application program — see https://github.com/mikebrady/shairport-sync-metadata-reader for a sample recipient. Sources that supply metadata include iTunes and the Music app in macOS and iOS.
* An interface to [MQTT](https://en.wikipedia.org/wiki/MQTT), a popular protocol for Inter Process Communication, Machine-to-Machine, Internet of Things and Home Automation projects. The interface provides access to metadata and artwork, with remote control for Classic AirPlay clients. * An interface to [MQTT](https://en.wikipedia.org/wiki/MQTT), a popular protocol for Inter Process Communication, Machine-to-Machine, Internet of Things and Home Automation projects. The interface provides access to metadata and artwork, with remote control for Classic AirPlay clients.
* Digital Signal Processing facilities – please see the [DSP Wiki Page Guide](https://github.com/mikebrady/shairport-sync/wiki/Digital-Signal-Processing-with-Shairport-Sync). (Thanks to [Yann Pomarède](https://github.com/yannpom) for the code and to [Paul Wieland](https://github.com/PaulWieland) for the guide.) * Digital Signal Processing facilities – please see the [DSP Wiki Page Guide](https://github.com/mikebrady/shairport-sync/wiki/Digital-Signal-Processing-with-Shairport-Sync). (Thanks to [Yann Pomarède](https://github.com/yannpom) for the code and to [Paul Wieland](https://github.com/PaulWieland) for the guide.)
* An [MPRIS](https://specifications.freedesktop.org/mpris-spec/2.2/)-like interface, partially complete and very functional, including access to metadata and artwork, and partial remote control for Classic AirPlay clients. * An [MPRIS](https://specifications.freedesktop.org/mpris/latest/)-like interface, partially complete and very functional, including access to metadata and artwork, and partial remote control for Classic AirPlay clients.
* A native D-Bus interface, including access to metadata, artwork and system settings. Remote control is also available for Classic AirPlay clients. * A native D-Bus interface, including access to metadata, artwork and system settings. Remote control is also available for Classic AirPlay clients.
* Better Volume Control — Shairport Sync offers finer control at very top and very bottom of the volume range. See http://tangentsoft.net/audio/atten.html for a good discussion of audio "attenuators", upon which volume control in Shairport Sync is modelled. See also the diagram of the volume transfer function in the documents folder. In addition, Shairport Sync can offer an extended volume control range on devices with a restricted range. * Better Volume Control — Shairport Sync offers finer control at very top and very bottom of the volume range. See http://tangentsoft.net/audio/atten.html for a good discussion of audio "attenuators", upon which volume control in Shairport Sync is modelled. See also the diagram of the volume transfer function in the documents folder. In addition, Shairport Sync can offer an extended volume control range on devices with a restricted range.
* Flexible output rates, formats and channels with built-in transcoding. * Flexible output rates, formats and channels with built-in transcoding.
File diff suppressed because it is too large Load Diff
+10 -4
View File
@@ -62,6 +62,9 @@ extern audio_output audio_pipe;
#ifdef CONFIG_STDOUT #ifdef CONFIG_STDOUT
extern audio_output audio_stdout; extern audio_output audio_stdout;
#endif #endif
#ifdef CONFIG_OHM
extern audio_output audio_ohm;
#endif
static audio_output *outputs[] = { static audio_output *outputs[] = {
#ifdef CONFIG_ALSA #ifdef CONFIG_ALSA
@@ -93,6 +96,9 @@ static audio_output *outputs[] = {
#endif #endif
#ifdef CONFIG_DUMMY #ifdef CONFIG_DUMMY
&audio_dummy, &audio_dummy,
#endif
#ifdef CONFIG_OHM
&audio_ohm,
#endif #endif
NULL}; NULL};
@@ -994,7 +1000,7 @@ int32_t search_for_suitable_configuration(unsigned int channels, unsigned int ra
} }
#ifdef CONFIG_FFMPEG #ifdef CONFIG_FFMPEG
if (local_format == SPS_FORMAT_UNKNOWN) { if (local_format == SPS_FORMAT_UNKNOWN) {
debug(3, "check for the next highest rate above %u with %u channels.", rate, debug(3, "check for the next highest rate above %u with %u channels and the best format.", rate,
local_channels); local_channels);
unsigned int rate_pointer = 0; unsigned int rate_pointer = 0;
while ((rate_pointer < sizeof(rates) / sizeof(unsigned int)) && while ((rate_pointer < sizeof(rates) / sizeof(unsigned int)) &&
@@ -1002,7 +1008,7 @@ int32_t search_for_suitable_configuration(unsigned int channels, unsigned int ra
local_rate = rates[rate_pointer]; local_rate = rates[rate_pointer];
if (local_rate > rate) { if (local_rate > rate) {
local_format = check_configuration_with_formats( local_format = check_configuration_with_formats(
local_channels, local_rate, (sps_format_t)format, check_configuration); local_channels, local_rate, SPS_FORMAT_S32, check_configuration);
} }
if (local_format == SPS_FORMAT_UNKNOWN) { if (local_format == SPS_FORMAT_UNKNOWN) {
rate_pointer++; rate_pointer++;
@@ -1012,13 +1018,13 @@ int32_t search_for_suitable_configuration(unsigned int channels, unsigned int ra
if (local_format == SPS_FORMAT_UNKNOWN) { if (local_format == SPS_FORMAT_UNKNOWN) {
int rate_pointer = (int)(sizeof(rates) / sizeof(unsigned int) - 1); int rate_pointer = (int)(sizeof(rates) / sizeof(unsigned int) - 1);
debug(3, "check for the next lowest rate below %u with %u channels.", rate, debug(3, "check for the next lowest rate below %u with %u channels and the best format.", rate,
local_channels); local_channels);
while ((rate_pointer >= 0) && (local_format == SPS_FORMAT_UNKNOWN)) { while ((rate_pointer >= 0) && (local_format == SPS_FORMAT_UNKNOWN)) {
local_rate = rates[rate_pointer]; local_rate = rates[rate_pointer];
if (local_rate < rate) { if (local_rate < rate) {
local_format = check_configuration_with_formats( local_format = check_configuration_with_formats(
local_channels, local_rate, (sps_format_t)format, check_configuration); local_channels, local_rate, SPS_FORMAT_S32, check_configuration);
} }
if (local_format == SPS_FORMAT_UNKNOWN) { if (local_format == SPS_FORMAT_UNKNOWN) {
rate_pointer--; rate_pointer--;
+1493
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -768,8 +768,6 @@ void stream_write_cb(pa_stream *local_stream, size_t requested_bytes,
pthread_cleanup_pop(1); // release the mutex pthread_cleanup_pop(1); // release the mutex
if (ret != 0) if (ret != 0)
debug(1, "error writing to pa buffer"); debug(1, "error writing to pa buffer");
// debug(1,"<<<Frames requested %d, written to pa: %d, corked status:
// %d.",requested_bytes/4,bytes_transferred/4,pa_stream_is_corked(local_stream));
} }
void stream_success_cb(__attribute__((unused)) pa_stream *local_stream, void stream_success_cb(__attribute__((unused)) pa_stream *local_stream,
+7 -1
View File
@@ -1,7 +1,7 @@
# Process this file with autoconf to produce a configure script. # Process this file with autoconf to produce a configure script.
AC_PREREQ([2.50]) AC_PREREQ([2.50])
AC_INIT([shairport-sync], [5.3-dev], [4265913+mikebrady@users.noreply.github.com]) AC_INIT([shairport-sync], [5.2.2], [4265913+mikebrady@users.noreply.github.com])
: ${CFLAGS="-O3"} : ${CFLAGS="-O3"}
: ${CXXFLAGS="-O3"} : ${CXXFLAGS="-O3"}
AM_INIT_AUTOMAKE([subdir-objects]) AM_INIT_AUTOMAKE([subdir-objects])
@@ -98,6 +98,12 @@ if test "x$with_pipe" = "xyes" ; then
fi fi
AM_CONDITIONAL([USE_PIPE], [test "x$with_pipe" = "xyes" ]) AM_CONDITIONAL([USE_PIPE], [test "x$with_pipe" = "xyes" ])
AC_ARG_WITH([ohm],[AS_HELP_STRING([--with-ohm],[build the OpenHome Songcast OHM/OHU unicast sender audio backend into Shairport Sync])])
if test "x$with_ohm" = "xyes" ; then
AC_DEFINE([CONFIG_OHM], 1, [Include the OpenHome Songcast OHM/OHU unicast sender audio backend.])
fi
AM_CONDITIONAL([USE_OHM], [test "x$with_ohm" = "xyes"])
# Check to see if we should include the System V initscript # Check to see if we should include the System V initscript
AC_ARG_WITH([systemv-startup],[AS_HELP_STRING([--with-systemv-startup],[install a System V startup script during a make install])]) AC_ARG_WITH([systemv-startup],[AS_HELP_STRING([--with-systemv-startup],[install a System V startup script during a make install])])
+66
View File
@@ -0,0 +1,66 @@
# Minimal cross-build of shairport-sync with the OHM Songcast sender backend,
# for linux/arm64 (Raspberry Pi OS aarch64). Produces a glibc binary compatible
# with Debian bullseye (glibc 2.31) and later (e.g. bookworm).
#
# Usage from the repo root:
# docker buildx build --platform linux/arm64 --target binary \
# -f docker/Dockerfile.ohm --output type=local,dest=./out-ohm .
#
# Then copy ./out-ohm/shairport-sync to the Pi.
#
# FFmpeg is taken from Debian packages (libavcodec-dev etc.) rather than built
# from source, to keep the cross-build fast under emulation.
ARG DEBIAN_VERSION=bullseye
FROM --platform=linux/arm64 debian:${DEBIAN_VERSION} AS builder
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
autoconf \
automake \
libtool \
pkg-config \
git \
ca-certificates \
xxd \
libasound2-dev \
libplist-dev \
libplist-utils \
libssl-dev \
libsodium-dev \
libgcrypt-dev \
uuid-dev \
libavcodec-dev \
libavformat-dev \
libswresample-dev \
libavutil-dev \
libavahi-client-dev \
avahi-daemon \
libdbus-1-dev \
libglib2.0-dev \
libconfig-dev \
libpopt-dev \
libsndfile-dev \
libsoxr-dev \
xmltoman \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /shairport-sync
COPY . .
WORKDIR /shairport-sync/build
RUN autoreconf -i ../
RUN ../configure --sysconfdir=/etc --with-alsa \
--with-avahi --with-ssl=openssl \
--with-airplay-2 --with-ffmpeg --with-metadata \
--with-ohm --with-dummy --with-pipe --with-stdout \
--with-dbus-interface --with-mpris-interface \
--with-convolution --with-soxr
RUN make -j"$(nproc)"
# Target that yields just the binary on the host filesystem via --output.
FROM --platform=linux/arm64 scratch AS binary
COPY --from=builder /shairport-sync/build/shairport-sync /shairport-sync
+1 -1
View File
@@ -4,7 +4,7 @@
#include "rtsp.h" #include "rtsp.h"
#include <pthread.h> #include <pthread.h>
#define number_of_watchers 2 #define number_of_watchers 3
typedef enum { typedef enum {
PS_NOT_AVAILABLE = 0, PS_NOT_AVAILABLE = 0,
+31 -47
View File
@@ -1488,8 +1488,11 @@ int get_ptp_anchor_local_time_info(rtsp_conn_info *conn, uint32_t *anchorRTP,
} }
if (conn->last_anchor_info_is_valid != 0) { if (conn->last_anchor_info_is_valid != 0) {
if (anchorRTP != NULL) if (anchorRTP != NULL) {
*anchorRTP = conn->last_anchor_rtptime; // Use the current rate in case the stream format has changed.
int32_t added_latency = (int32_t)(config.audio_backend_latency_offset * conn->input_rate);
*anchorRTP = conn->last_anchor_rtptime - added_latency;
}
if (anchorLocalTime != NULL) if (anchorLocalTime != NULL)
*anchorLocalTime = conn->last_anchor_local_time; *anchorLocalTime = conn->last_anchor_local_time;
} }
@@ -1616,6 +1619,7 @@ int32_t decipher_player_put_packet(uint8_t *ciphered_audio_alt, ssize_t nread,
} }
void *rtp_ap2_control_receiver(void *arg) { void *rtp_ap2_control_receiver(void *arg) {
const int32_t ap2_realttime_stream_latency_fudge_factor = 11025; // seems to bring everything into sync
// #include <syscall.h> // #include <syscall.h>
// debug(1, "rtp_ap2_control_receiver PID %d", syscall(SYS_gettid)); // debug(1, "rtp_ap2_control_receiver PID %d", syscall(SYS_gettid));
pthread_cleanup_push(rtp_ap2_control_handler_cleanup_handler, arg); pthread_cleanup_push(rtp_ap2_control_handler_cleanup_handler, arg);
@@ -1674,22 +1678,6 @@ void *rtp_ap2_control_receiver(void *arg) {
switch (packet[1]) { switch (packet[1]) {
case 215: // code 215, effectively an anchoring announcement case 215: // code 215, effectively an anchoring announcement
{ {
// struct timespec tnr;
// clock_gettime(CLOCK_REALTIME, &tnr);
// uint64_t local_realtime_now = timespec_to_ns(&tnr);
/*
char obf[4096];
char *obfp = obf;
int obfc;
for (obfc=0;obfc<nread;obfc++) {
snprintf(obfp, 3, "%02X", packet[obfc]);
obfp+=2;
};
*obfp=0;
debug(1,"AP2 Timing Control Received: \"%s\"",obf);
*/
uint64_t remote_packet_time_ns = nctoh64(packet + 8); uint64_t remote_packet_time_ns = nctoh64(packet + 8);
check64conversion("remote_packet_time_ns", packet + 8, remote_packet_time_ns); check64conversion("remote_packet_time_ns", packet + 8, remote_packet_time_ns);
uint64_t clock_id = nctoh64(packet + 20); uint64_t clock_id = nctoh64(packet + 20);
@@ -1699,49 +1687,45 @@ void *rtp_ap2_control_receiver(void *arg) {
// debug(1,"remote_packet_time_ns: %" PRIx64 ", local_realtime_now_ns: %" PRIx64 // debug(1,"remote_packet_time_ns: %" PRIx64 ", local_realtime_now_ns: %" PRIx64
// ".", remote_packet_time_ns, local_realtime_now); // ".", remote_packet_time_ns, local_realtime_now);
uint32_t frame_1 = uint32_t frame_1 =
nctohl(packet + 4); // this seems to be the frame with latency of 77165 included nctohl(packet + 4); // this seems to be the frame with latency of 77175 included
check32conversion("frame_1", packet + 4, frame_1); check32conversion("frame_1", packet + 4, frame_1);
uint32_t frame_2 = uint32_t frame_2 =
nctohl(packet + 16); // this seems to be the frame the time refers to nctohl(packet + 16); // this seems to be the frame the time refers to
check32conversion("frame_2", packet + 16, frame_2); check32conversion("frame_2", packet + 16, frame_2);
// this just updates the anchor information contained in the packet // this updates the anchor information contained in the packet
// the frame and its remote time int32_t stream_specified_latency = frame_2 - frame_1; // this is the latency expected
// add in the audio_backend_latency_offset; if (stream_specified_latency != 77175)
int32_t notified_latency = frame_2 - frame_1; debug(1, "Stream-specified latency is %d frames. Normally it is 77175.", stream_specified_latency);
if (notified_latency != 77175) int32_t net_source_latency = stream_specified_latency + ap2_realttime_stream_latency_fudge_factor;
debug(1, "Notified latency is %d frames.", notified_latency);
int32_t added_latency =
(int32_t)(config.audio_backend_latency_offset * conn->input_rate);
// the actual latency is the notified latency plus the fixed latency + the added
// latency
int32_t net_latency = // Now to accommodate a backend buffer of the desired length.
notified_latency + 11035 + // Note that it's in input-rate frames, not output-rate frames!
added_latency; // this is the latency between incoming frames and the DAC
net_latency = net_latency - (int32_t)(config.audio_backend_buffer_desired_length * net_source_latency = net_source_latency - (int32_t)(config.audio_backend_buffer_desired_length *
conn->input_rate); conn->input_rate);
// debug(1, "Net latency is %d frames.", net_latency);
// Now we want to check the user-specified latency offset.
// We want to warn the user if they have asked for a negative latency that is too great --
// one that would require packets to arrive before they actually do,
// (which is about two seconds before they are to be played).
int32_t net_latency = net_source_latency + (int32_t)(config.audio_backend_latency_offset * conn->input_rate);
if (net_latency <= 0) { if (net_latency <= 0) {
if (conn->latency_warning_issued == 0) { if (conn->latency_warning_issued == 0) {
warn("The stream latency (%f seconds) it too short to accommodate an offset of " warn("The stream latency (%g seconds) is too short to accommodate an audio backend latency offset of "
"%f " "%g seconds and a backend buffer of %g seconds. The audio_backend_latency_offset has been set to zero.",
"seconds and a backend buffer of %f seconds.", ((stream_specified_latency + ap2_realttime_stream_latency_fudge_factor) * 1.0) / conn->input_rate,
((notified_latency + 11035) * 1.0) / conn->input_rate,
config.audio_backend_latency_offset, config.audio_backend_latency_offset,
config.audio_backend_buffer_desired_length); config.audio_backend_buffer_desired_length);
warn("(FYI the stream latency needed would be %f seconds.)", config.audio_backend_latency_offset = 0.0;
config.audio_backend_buffer_desired_length - net_latency = net_source_latency;
config.audio_backend_latency_offset);
conn->latency_warning_issued = 1; conn->latency_warning_issued = 1;
} }
conn->latency = notified_latency + 11035;
} else {
conn->latency = notified_latency + 11035 + added_latency;
} }
conn->latency = net_latency; // this is the time window within which packets can be accepted without being too late
set_ptp_anchor_info(conn, clock_id, frame_1 - 11035 - added_latency, set_ptp_anchor_info(conn, clock_id, frame_1 - ap2_realttime_stream_latency_fudge_factor, remote_packet_time_ns);
remote_packet_time_ns);
if (conn->anchor_clock != clock_id) { if (conn->anchor_clock != clock_id) {
debug(2, "Connection %d: Change Anchor Clock: %" PRIx64 ".", debug(2, "Connection %d: Change Anchor Clock: %" PRIx64 ".",
conn->connection_number, clock_id); conn->connection_number, clock_id);
+2 -5
View File
@@ -1453,11 +1453,8 @@ void handle_setrateanchori(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
// debug(1, "anchor rtpTime is %" PRId64 ".", rtpTime); // debug(1, "anchor rtpTime is %" PRId64 ".", rtpTime);
uint32_t anchorRTPTime = rtpTime; uint32_t anchorRTPTime = rtpTime;
int32_t added_latency = (int32_t)(config.audio_backend_latency_offset * conn->input_rate); // Store the raw anchor; apply the latency offset when it is used.
// debug(1,"anchorRTPTime: %" PRIu32 ", added latency: %" PRId32 ".", anchorRTPTime, set_ptp_anchor_info(conn, conn->networkTimeTimelineID, anchorRTPTime, anchorTimeNanoseconds);
// added_latency);
set_ptp_anchor_info(conn, conn->networkTimeTimelineID, anchorRTPTime - added_latency,
anchorTimeNanoseconds);
} }
item = plist_dict_get_item(messagePlist, "rate"); item = plist_dict_get_item(messagePlist, "rate");