Merge branch 'development'
This is 4.3.3 release.
This commit is contained in:
@@ -17,7 +17,6 @@ jobs:
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
brew install automake
|
||||
brew install openssl
|
||||
brew install popt
|
||||
brew install libconfig
|
||||
brew install libao
|
||||
|
||||
+9
-5
@@ -1,5 +1,9 @@
|
||||
# AirPlay 2
|
||||
Shairport Sync offers AirPlay 2 support for audio sources on iOS devices, Macs from macOS 10.15 (Catalina) onwards, HomePod minis and Apple TVs.
|
||||
**Shairport Sync** (as of [v4.1](https://github.com/mikebrady/shairport-sync/releases/tag/4.1) and newer) offers **AirPlay 2** support for audio sources on:
|
||||
- iOS devices,
|
||||
- Macs from macOS 10.15 (Catalina) onwards,
|
||||
- HomePod minis,
|
||||
- and Apple TVs.
|
||||
|
||||
## What Works
|
||||
- AirPlay 2 audio for iOS, HomePod mini, AppleTV and Mac players.
|
||||
@@ -18,7 +22,7 @@ Shairport Sync offers AirPlay 2 support for audio sources on iOS devices, Macs f
|
||||
|
||||
## General
|
||||
Shairport Sync uses a companion application called [NQPTP](https://github.com/mikebrady/nqptp) ("Not Quite PTP")
|
||||
for timing and synchronisation in AirPlay 2. NQPTP must run as `root` and must have exclusive access to ports `319` and `320`.
|
||||
for timing and synchronisation in AirPlay 2. NQPTP must have exclusive access to ports `319` and `320`.
|
||||
|
||||
Lossless and High Definition Lossless material is transcoded to AAC before it reaches Shairport Sync.
|
||||
|
||||
@@ -26,12 +30,12 @@ Lossless and High Definition Lossless material is transcoded to AAC before it re
|
||||
AirPlay 2 support needs a slightly more powerful CPU for decoding and synchronisation and more memory for bigger buffers and larger libraries. A system with the power of a Raspberry Pi 2 or Raspberry Pi Zero 2 W, or better, is recommended.
|
||||
|
||||
Here are some guidelines:
|
||||
* Full access, including `root` privileges, to a system at least as powerful as a Raspberry Pi 2 or a Raspberry Pi Zero 2 W.
|
||||
* Full access, including network capabilities or `root` privileges, to a system at least as powerful as a Raspberry Pi 2 or a Raspberry Pi Zero 2 W.
|
||||
* Ports 319 and 320 must be free to use (i.e. they must not be in use by another service such as a PTP service) and must not be blocked by a firewall.
|
||||
* An up-to-date Linux or FreeBSD. This is important, as some of the libraries must be the latest available.
|
||||
* An up-to-date system. This is important, as some of the libraries must be the latest available.
|
||||
* Shairport Sync will not run in AirPlay 2 mode on a Mac because NQPTP, on which it relies, needs ports 319 and 320, which are already used by macOS.
|
||||
* A version of the [FFmpeg](https://www.ffmpeg.org) library with an AAC decoder capable of decoding Floating Planar -- `fltp` -- material. There is a guide [here](TROUBLESHOOTING.md#aac-decoder-issues-airplay-2-only) to help you find out if your system has it.
|
||||
* An audio output, for example an ALSA device (or `sndio` in FreeBSD). The device must be capable of running at 44,100 frames per second. You can use [`sps-alsa-explore`](https://github.com/mikebrady/sps-alsa-explore) to test the suitability of hardware ALSA audio devices on your device.
|
||||
* An audio output. The device must be capable of running at 44,100 frames per second. You can use [`sps-alsa-explore`](https://github.com/mikebrady/sps-alsa-explore) to test the suitability of hardware ALSA audio devices on your device.
|
||||
Other backends continue to work as with "classic" Shairport Sync.
|
||||
- Multiple instances of the AirPlay 2 version of Shairport Sync can not be hosted on the same system. It seems that AirPlay 2 clients are confused by having multiple AirPlay 2 players at the same IP addresses.
|
||||
|
||||
|
||||
@@ -49,11 +49,11 @@ If you are building classic Shairport Sync, the list of packages is shorter:
|
||||
# apt-get install --no-install-recommends build-essential git autoconf automake libtool \
|
||||
libpopt-dev libconfig-dev libasound2-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev
|
||||
```
|
||||
### Fedora
|
||||
For AirPlay 2 operation, _before you install the libraries_, please ensure the you have [enabled](https://docs.fedoraproject.org/en-US/quick-docs/setup_rpmfusion) RPM Fusion software repositories at least to the "Free" level. If this is not done, FFmpeg libraries will be installed that lack a suitable AAC decoder, preventing Shairport Sync from working in AirPlay 2 mode.
|
||||
### Fedora (Fedora 40)
|
||||
For AirPlay 2 operation, _before you install the libraries_, please ensure the you have [enabled](https://docs.fedoraproject.org/en-US/quick-docs/rpmfusion-setup) RPM Fusion software repositories to the "Nonfree" level. If this is not done, the FFmpeg libraries will lack a suitable AAC decoder, preventing Shairport Sync from working in AirPlay 2 mode.
|
||||
```
|
||||
# yum update
|
||||
# yum install make automake gcc gcc-c++ \
|
||||
# yum install --allowerasing make automake gcc gcc-c++ \
|
||||
git autoconf automake avahi-devel libconfig-devel openssl-devel popt-devel soxr-devel \
|
||||
ffmpeg ffmpeg-devel libplist-devel libsodium-devel libgcrypt-devel libuuid-devel vim-common \
|
||||
alsa-lib-devel
|
||||
@@ -122,7 +122,7 @@ Download, install, enable and start NQPTP from [here](https://github.com/mikebra
|
||||
|
||||
### Shairport Sync
|
||||
#### Build and Install
|
||||
Download Shairport Sync, configure, compile and install it. Before executing the commands, please note the following:
|
||||
Download Shairport Sync, branch and configure, compile and install it. Before executing the commands, please note the following:
|
||||
|
||||
* If building for FreeBSD, replace `--with-systemd` with `--with-os=freebsd --with-freebsd-service`.
|
||||
* Omit the `--with-airplay-2` from the `./configure` options if you are building classic Shairport Sync.
|
||||
|
||||
+122
-79
@@ -5,18 +5,18 @@ If your car audio has an AUX input, you can get AirPlay in your car using Shairp
|
||||
|
||||
The basic idea is to use a small Linux computer to create an isolated WiFi network (a "car network") and run Shairport Sync on it to provide an AirPlay service. An iPhone or an iPad with cellular capability can simultaneously connect to internet radio, YouTube, Apple Music, Spotify, etc. over the cellular network and send AirPlay audio through the car network to the AirPlay service provided by Shairport Sync. This sends the audio to the computer's DAC which is connected to the AUX input of your car audio.
|
||||
|
||||
Note that Android devices can not, so far, do this trick of using the two networks simultaneously.
|
||||
Please note that Android phones and tablets can not, so far, do this trick of using the two networks simultaneously.
|
||||
|
||||
## Example
|
||||
|
||||
**Important Note** This guide can not be used for the latest (time of writing: October 2023) version of Rasberry Pi OS (Bookworm) because the `dhcpcd` package is no longer part of the system. It does work for Rasberry Pi OS (Bullseye)
|
||||
If you are updating an existing installation, please refer to the [updating](#updating) section below.
|
||||
|
||||
In this example, a Raspberry Pi Zero 2 W and a Pimoroni PHAT DAC are used. Shairport Sync will be built for AirPlay 2 operation, but you can build it for "classic" AirPlay (aka AirPlay 1) operation if you prefer. A Pi Zero W is powerful enough for classic AirPlay.
|
||||
|
||||
Please note that some of the details of setting up networks are specific to the version of Linux used.
|
||||
|
||||
### Prepare the initial SD Image
|
||||
* Download Raspberry Pi OS Bullseye (Lite) and install it onto an SD Card using `Raspberry Pi Imager`. The Lite version is preferable to the Desktop version as it doesn't include a sound server like PulseAudio or PipeWire that can prevent direct access to the audio output device.
|
||||
* Download Raspberry Pi OS (Lite) and install it onto an SD Card using `Raspberry Pi Imager`. The Lite version is preferable to the Desktop version as it doesn't include a sound server like PulseAudio or PipeWire that can prevent direct access to the audio output device.
|
||||
* Before writing the image to the card, use the Settings control on `Raspberry Pi Imager` to set hostname, enable SSH and provide a username and password to use while building the system. Similarly, you can specify a wireless network the Pi will connect to while building the system. Later on, the Pi will be configured to start its own isolated network.
|
||||
* The next few steps are to add the overlay needed for the sound card. This may not be necessary in your case, but in this example a Pimoroni PHAT is being used. If you do not need to add an overlay, skip these steps.
|
||||
* Mount the card on a Linux machine. Two drives should appear – a `boot` drive and a `rootfs` drive.
|
||||
@@ -28,7 +28,7 @@ Please note that some of the details of setting up networks are specific to the
|
||||
* Close the file and carefully dismount and eject the two drives. *Be sure to dismount and eject the drives properly; otherwise they may be corrupted.*
|
||||
* Remove the SD card from the Linux machine, insert it into the Pi and reboot.
|
||||
|
||||
After a short time, the Pi should appear on your network – it may take a minute or so. To check, try to `ping` it at the `<hostname>.local`, e.g. if the hostname is `bmw` then use `$ ping bmw.local`. Once it has appeared, you can SSH into it and configure it.
|
||||
After a short time, the Pi should appear on your network – it may take a couple of minutes. To check, try to `ping` it at the `<hostname>.local`, e.g. if the hostname is `bmw` then use `$ ping bmw.local`. Once it has appeared, you can SSH into it and configure it.
|
||||
|
||||
### Boot, Configure, Update
|
||||
The first thing to do on a Pi would be to use the `raspi-config` tool to expand the file system to use the entire card. Next, do the usual update and upgrade:
|
||||
@@ -57,7 +57,7 @@ Skip this section if you are building classic Shairport Sync – NQPTP is not ne
|
||||
Download, install, enable and start NQPTP from [here](https://github.com/mikebrady/nqptp) following the guide for Linux.
|
||||
|
||||
#### Shairport Sync
|
||||
Download Shairport Sync, check out the `development` branch and configure, compile and install it.
|
||||
Download Shairport Sync, configure, compile and install it.
|
||||
|
||||
* Omit the `--with-airplay-2` from the `./configure` options if you are building classic Shairport Sync.
|
||||
|
||||
@@ -69,11 +69,10 @@ $ ./configure --sysconfdir=/etc --with-alsa \
|
||||
--with-soxr --with-avahi --with-ssl=openssl --with-systemd --with-airplay-2
|
||||
$ make
|
||||
# make install
|
||||
# systemctl enable shairport-sync
|
||||
```
|
||||
The `autoreconf` step may take quite a while – please be patient!
|
||||
|
||||
**Note:** *Do not* enable Shairport Sync to start automatically at boot time – later on in this installation, we will arrange for it to start after the network has been set up.
|
||||
|
||||
### Configure Shairport Sync
|
||||
Here are the important options for the Shairport Sync configuration file at `/etc/shairport-sync.conf`:
|
||||
```
|
||||
@@ -91,18 +90,22 @@ alsa =
|
||||
};
|
||||
|
||||
```
|
||||
Two `general` settings are worth noting. First, the option to ignore the sending device's volume control is enabled. This means that the car audio's volume control is the only one that affects the audio volume. Of course this is a matter of personal preference.
|
||||
Second, the maximum output offered by the DAC to the AUX port of the car audio can be reduced if it is overloading the input circuits. Again, that's a matter for personal selection and adjustment.
|
||||
Two `general` settings are worth noting.
|
||||
1. First, the option to ignore the sending device's volume control is enabled. This means that the car audio's volume control is the only one that affects the audio volume. This is a matter of personal preference.
|
||||
|
||||
2. Second, the maximum output offered by the DAC to the AUX port of the car audio can be reduced if it is overloading the car audio's input circuits and causing distortion. Again, that's a matter for personal selection and adjustment.
|
||||
|
||||
The `alsa` settings are for the Pimoroni PHAT – it does not have a hardware mixer, so no `mixer_control_name` is given.
|
||||
|
||||
Note that the DAC's 32-bit capability is automatically selected if available, so there is no need to set it here. Similarly, since `soxr` support is included in the build, `soxr` interpolation will be automatically enabled if the device is fast enough.
|
||||
The DAC's 32-bit capability is automatically selected if available, so there is no need to set it here. Similarly, since `soxr` support is included in the build, `soxr` interpolation will be automatically enabled if the device is fast enough.
|
||||
|
||||
### Extra Packages
|
||||
A number of packages to enable the Pi to work as a WiFi base station are needed:
|
||||
```
|
||||
# apt-get install hostapd isc-dhcp-server
|
||||
# apt install --no-install-recommends hostapd isc-dhcp-server
|
||||
```
|
||||
(The installer will get errors trying to set up both of these services; the errors can be ignored.)
|
||||
|
||||
Disable both of these services from starting at boot time (this is because we will launch them sequentially later on):
|
||||
```
|
||||
# systemctl unmask hostapd
|
||||
@@ -157,103 +160,143 @@ INTERFACESv4="wlan0"
|
||||
INTERFACESv6=""
|
||||
```
|
||||
### Set up the Startup Sequence
|
||||
Configure the startup sequence by adding commands to `/etc/rc.local` to start `hostapd` and the `dhcp` server and then to start `shairport-sync` automatically after startup. Its contents should look like this:
|
||||
Configure the startup sequence by adding commands to `/etc/rc.local` to start `hostapd` and the `dhcp` automatically after startup. Its contents should look like this:
|
||||
```
|
||||
#!/bin/sh -e
|
||||
#
|
||||
# rc.local
|
||||
#
|
||||
# This script is executed at the end of each multiuser runlevel.
|
||||
# Make sure that the script will "exit 0" on success or any other
|
||||
# value on error.
|
||||
#
|
||||
# In order to enable or disable this script just change the execution
|
||||
# bits.
|
||||
#
|
||||
# By default this script does nothing.
|
||||
|
||||
/sbin/iw dev wlan0 set power_save off
|
||||
/usr/sbin/hostapd -B -P /run/hostapd.pid /etc/hostapd/hostapd.conf
|
||||
/sbin/ip addr add 10.0.10.1/24 dev wlan0
|
||||
/bin/sleep 1
|
||||
/bin/systemctl start isc-dhcp-server
|
||||
/bin/sleep 2
|
||||
/bin/systemctl start shairport-sync
|
||||
# Shairport Sync is automatically started as a service on startup.
|
||||
|
||||
exit 0
|
||||
# If MODE is set to RUN, the system will start the WiFi access point.
|
||||
|
||||
# If MODE is set to anything else, e.g. DEV, the system will not start the WiFi access point.
|
||||
# Instead, you can connect the system to a network.
|
||||
# If it still has the WiFi credentials of the last WiFi network it connected to,
|
||||
# it can connect to it automatically.
|
||||
|
||||
MODE=RUN
|
||||
|
||||
/bin/sleep 2 # may be necessary while wlan0 becomes available
|
||||
/sbin/iw dev wlan0 set power_save off # always do this
|
||||
|
||||
if test $MODE = RUN ; then
|
||||
|
||||
# If script execution gets in here, it starts the WiFi access point.
|
||||
/usr/sbin/hostapd -B -P /run/hostapd.pid /etc/hostapd/hostapd.conf
|
||||
/sbin/ip addr add 10.0.10.1/24 dev wlan0
|
||||
/bin/systemctl start isc-dhcp-server
|
||||
|
||||
else
|
||||
|
||||
# If script execution gets in here, it starts services needed for normal operation.
|
||||
/bin/systemctl start systemd-timesyncd || :
|
||||
/bin/systemctl start dhcpcd || /bin/systemctl start NetworkManager || :
|
||||
|
||||
fi
|
||||
|
||||
exit 0 # normal exit here
|
||||
```
|
||||
As you can see, the effect of these commands is to start the WiFi transmitter, give the base station the IP address `10.0.10.1`, start a DHCP server and finally start the Shairport Sync service.
|
||||
|
||||
#### Disable Unused Services
|
||||
These optional steps have been tested on a Raspberry Pi only -- they have not been tested on other systems.
|
||||
Some services are not necessary for this setup and can be disabled as follows:
|
||||
```
|
||||
# systemctl disable keyboard-setup
|
||||
# systemctl disable triggerhappy
|
||||
# systemctl disable dphys-swapfile
|
||||
```
|
||||
#### Optional: Read-only mode – Raspberry Pi Specific
|
||||
This optional step is applicable to a Raspberry Pi only. Run `sudo raspi-config` and then choose `Performance Options` > `Overlay Filesystem` and choose to enable the overlay filesystem, and to set the boot partition to be write-protected. (The idea here is that this offers more protection against files being corrupted by the sudden removal of power.)
|
||||
|
||||
### Final Steps
|
||||
Up to now, if you reboot the Pi, it will reconnect to your WiFi network, ignoring the instructions and settings you have given it to act as a base station. That is because the `wlan0` interface is still under the control of the `dhcpcd` service. So, the final step is to instruct the `dhcpcd` service not to manage `wlan0`. To do this, edit `/etc/dhcpcd.conf` and insert the following line at the start:
|
||||
|
||||
You now need to disable some services; that is, you need to stop them starting automatically on power-up. This is because they either interfere with the system's operation in WiFi Access Point mode, or because they won't work when the system isn't connected to the Internet. Only one of the `NetworkManager` and the `dhcpcd` service will be present in your system, but it's no harm to try to disable both.
|
||||
```
|
||||
denyinterfaces wlan0
|
||||
# systemctl disable dhcpcd
|
||||
# systemctl disable NetworkManager
|
||||
# systemctl disable wpa_supplicant
|
||||
# systemctl disable systemd-timesyncd
|
||||
```
|
||||
From this point on, at least on the Raspberry Pi, if you reboot the machine, it will not reconnect to your network. Instead, it will act as the WiFi base station you have configured with `hostapd` and `isc-dhcp-server`.
|
||||
|
||||
### Optimise startup time – Raspberry Pi Specific
|
||||
|
||||
This is applicable to a Raspberry Pi only. Some of it may be applicable to other systems, but it has not been tested on them.
|
||||
|
||||
There are quite a few services that are not necessary for this setup. Disabling them can improve startup time. Running these commands disables them:
|
||||
Lastly, note that the WiFi credentials you used initially to connect to your network (e.g. your home network) will have been stored in the system in plain text. This is convenient for when you want to reconnect to update (see later), but if you prefer to delete them, they will be in `/etc/wpa_supplicant/wpa_supplicant.conf`
|
||||
|
||||
When you are finished, carefully power down the machine before unplugging it from power:
|
||||
```
|
||||
sudo systemctl disable systemd-timesyncd.service
|
||||
sudo systemctl disable keyboard-setup.service
|
||||
sudo systemctl disable triggerhappy.service
|
||||
sudo systemctl disable dhcpcd.service
|
||||
sudo systemctl disable wpa_supplicant.service
|
||||
sudo systemctl disable dphys-swapfile.service
|
||||
sudo systemctl disable networking.service
|
||||
# poweroff
|
||||
```
|
||||
|
||||
### Read-only mode – Raspberry Pi Specific
|
||||
Run `sudo raspi-config` and then choose `Performance Options` > `Overlay Filesystem` and choose to enable the overlay filesystem, and to set the boot partition to be write-protected.
|
||||
|
||||
### Ready
|
||||
Install the Raspberry Pi in your car. It should be powered from a source that is switched off when you leave the car, otherwise the slight current drain will eventually flatten the car's battery.
|
||||
|
||||
When the power source is switched on, typically when you start the car, it will take maybe a minute for the system to boot up.
|
||||
When the power source is switched on -- typically when you start the car -- it will take around 35 seconds for the system to become available (timing based on a Raspberry Pi Zero 2 W running Bookworm).
|
||||
|
||||
### Enjoy!
|
||||
|
||||
---
|
||||
|
||||
## Updating
|
||||
From time to time, you may wish to update this installation. However, in order to update Shairport Sync, you must reconnect the system to a network that can access the internet. The easiest thing is to temporarily reconnect to the network you used when you created the system. To do that, you have to temporarily undo the "Final Steps" and some of the "Raspberry Pi Specific" steps you used. This will enable you to connect your device back to the network it was created on. You should then be able to update the operating system and libraries in the normal way and then update Shairport Sync.
|
||||
From time to time, you may wish to update this installation. Assuming you haven't deleted your original WiFi network credentials, the easiest thing is to temporarily reconnect to the network you used when you created the system. You can then update the operating system and libraries in the normal way and then update Shairport Sync.
|
||||
|
||||
So, take the following steps:
|
||||
However, if you're *upgrading* the operating system to e.g. from Bullseye to Bookworm, the names and index numbers of the output devices may change, and the names of the mixer controls may also change. You can use [`sps-alsa-explore`](https://github.com/mikebrady/sps-alsa-explore) to discover device names and mixer names.
|
||||
|
||||
1. If it's a Raspberry Pi and you have enabled the Read-only mode, you must take the device out of Read-only mode:
|
||||
#### Exit Raspberry Pi Read-Only Mode
|
||||
If it's a Raspberry Pi and you have optionally enabled the read-only mode, you must take the device out of Read-only mode:
|
||||
Run `sudo raspi-config` and then choose `Performance Options` > `Overlay Filesystem` and choose to disable the overlay filesystem and to set the boot partition not to be write-protected. This is so that changes can be written to the file system; you can make the filesystem read-only again later. Save the changes and reboot the system.
|
||||
#### Undo Optimisations
|
||||
If you have disabled any of the services listed in the [Disable Unused Services](#disable-unused-services) section, you should re-enable them. (But *do not* re-eneable `NetworkManager`, `dhcpcd`, `wpa_supplicant` or `systemd-timesyncd` -- they are handled specially by the startup script.)
|
||||
#### Perform Legacy Updates
|
||||
Over time, the arrangements by which the system is prepared for operation has changed to make it easier to revert to normal operation when necessary for maintenance, updates, etc. A small number of the old settings need to be changed to bring them up to date with the present arrangements. Once the required changes have been made, your system will be ready for the update process detailed below. Here are those legacy changes you need to make, just once:
|
||||
|
||||
2. If you have disabled the `dhcpcd`, `wpa_supplicant` or `systemd-timesyncd` services as suggested in the "Optimise startup time -- Raspberry Pi Specific" section, you need to temporarily re-enable them:
|
||||
`# systemctl enable dhcpcd.service`
|
||||
`# systemctl enable wpa_supplicant.service`
|
||||
`# systemctl enable systemd-timesyncd.service`
|
||||
Reboot.
|
||||
1. If there is a file called `/etc/dhcpcd.conf` and if the first line reads:
|
||||
```
|
||||
denyinterfaces wlan0
|
||||
```
|
||||
then delete that line -- it is no longer needed and will cause problems in future if it remains there.
|
||||
|
||||
If the file `/etc/dhcpcd.conf` doesn't exist, or if the first line is not `denyinterfaces wlan0`, then you don't need to do anything.
|
||||
|
||||
2. Replace the contents of the file `/etc/rc.local` with the new contents given [above](#set-up-the-startup-sequence).
|
||||
|
||||
3. To allow your device to reconnect to the network it was created on, edit `/etc/dhcpcd.conf` and comment out the following line at the start:
|
||||
`denyinterfaces wlan0`
|
||||
so that it looks like this:
|
||||
`# denyinterfaces wlan0`
|
||||
From this point on, if you reboot the machine, it will connect to the network it was configured on, i.e. the network you used when you set it up for the first time. This is because the name and password of the network it was created on would have been placed in `/etc/wpa_supplicant/wpa_supplicant` when the system was initially configured and will still be there.
|
||||
3. Disable a number of services as follows. Only one of the `NetworkManager` and the `dhcpcd` service will be present in your system, but it's no harm to try to disable both.
|
||||
```
|
||||
# systemctl disable dhcpcd
|
||||
# systemctl disable NetworkManager
|
||||
# systemctl disable wpa_supplicant
|
||||
# systemctl disable systemd-timesyncd
|
||||
```
|
||||
4. Enable the `shairport-sync` service itself:
|
||||
```
|
||||
# systemctl enable shairport-sync
|
||||
```
|
||||
Once you have made these one-off legacy updates, you can proceed to the next stage -- performing the update.
|
||||
### Performing the Update
|
||||
To update, take the following steps:
|
||||
#### Temporarily reconnect to a network and update
|
||||
1. Edit the startup script in `/etc/rc.local` to so that the system is no longer in the RUN mode.
|
||||
To do that, change line 15 so that it goes from this:
|
||||
```
|
||||
MODE=RUN
|
||||
```
|
||||
to this:
|
||||
```
|
||||
MODE=DEV
|
||||
```
|
||||
Do not be tempted to insert any spaces anywhere -- Unix scripting syntax is very strict!
|
||||
|
||||
4. Reboot and do Normal Updating
|
||||
2. Save and close the file and reboot. From this point on, the system will start normally and can be connected to a network. If it still has the WiFi credentials of the last network it was connected to, then it could automatically reconnect.
|
||||
|
||||
You can perform updates in the normal way. When you are finished, you need to undo the temporary changes you made to the setup, as follows:
|
||||
The system is now ready for updating in the normal way.
|
||||
#### Revert to normal operation
|
||||
When you are finished updating, you need to put the system back into its RUN mode, as follows:
|
||||
|
||||
5. If you had temporarily re-enabled services that are normally disabled, then it's time to disable them again:
|
||||
`# systemctl disable dhcpcd.service`
|
||||
`# systemctl disable wpa_supplicant.service`
|
||||
`# systemctl disable systemd-timesyncd.service`
|
||||
1. Edit the startup script in `/etc/rc.local` to so that the MODE variable is set to RUN.
|
||||
To do that, change line 15 so that it goes from this:
|
||||
```
|
||||
MODE=DEV
|
||||
```
|
||||
to this:
|
||||
```
|
||||
MODE=RUN
|
||||
```
|
||||
Once again, do insert any spaces anywhere.
|
||||
|
||||
6. To re-enable the system to create its own network, edit `/etc/dhcpcd.conf` and uncomment the line that you had temporarily commented out at the start of the update. Change:
|
||||
`# denyinterfaces wlan0`
|
||||
so that it looks like this:
|
||||
`denyinterfaces wlan0`
|
||||
|
||||
7. Reboot. The system should start as it would if it was in the car.
|
||||
|
||||
8. If the device is a Raspberry Pi and you wish to make the file system read-only, connect to the system, run `sudo raspi-config` and then choose `Performance Options` > `Overlay Filesystem`. In there, choose to enable the overlay filesystem, and to set the boot partition to be write-protected. Do a final reboot and check that everyting is in order.
|
||||
|
||||
2. Save and close the file and reboot. The system should start as it would if it was in the car.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
MQTT in Shairport Sync
|
||||
====
|
||||
# MQTT in Shairport Sync
|
||||
|
||||
To enable Shairport Sync to act as an MQTT publisher, you need to:
|
||||
1. Install the mosquitto library:
|
||||
```
|
||||
# apt install libmosquitto-dev
|
||||
```
|
||||
2. Add the configuration flag `--with-mqtt-client` to the list of parameters to the `./configure...` command, for example:
|
||||
2. Add the configuration flag `--with-mqtt-client` to the list of parameters to the `./configure...` command. For example:
|
||||
```
|
||||
$ ./configure --with-mqtt-client --sysconfdir=/etc --with-alsa --with-avahi --with-ssl=openssl --with-systemd
|
||||
```
|
||||
@@ -16,82 +16,71 @@ $ shairport-sync -V
|
||||
3.3.8-OpenSSL-Avahi-ALSA-metadata-mqtt-sysconfdir:/etc
|
||||
```
|
||||
|
||||
Setting Up MQTT Publishing
|
||||
====
|
||||
**Note:** [The Docker image](https://hub.docker.com/r/mikebrady/shairport-sync) will have MQTT support enabled by default.
|
||||
|
||||
This is a rough guide on the setup of MQTT publishing in ShairPort-Sync. The MQTT service listens for and publishes metadata generated by the airplay source and shairport-sync.
|
||||
## Setting Up MQTT Publishing
|
||||
|
||||
Below is a simple example of configuring shairport-sync to send parsed metadata to a MQTT server under the topic "shairport"
|
||||
This is a rough guide on the setup of MQTT publishing in ShairPort Sync. The MQTT service listens for and publishes metadata generated by the AirPlay source and Shairport Sync.
|
||||
|
||||
Example Configuration
|
||||
----
|
||||
### Example Configuration
|
||||
|
||||
Shairport-sync is configured below to published parsed metadata information and album-art to a MQTT server.
|
||||
In the following example, Shairport Sync is configured to publish parsed metadata information and album-art to a MQTT server under the topic "shairport".
|
||||
|
||||
```xml
|
||||
|
||||
metadata =
|
||||
{
|
||||
enabled = "yes"; // set this to yes to get Shairport Sync to solicit metadata from the source and to pass it on via a pipe
|
||||
include_cover_art = "yes"; // set to "yes" to get Shairport Sync to solicit cover art from the source and pass it via the pipe. You must also set "enabled" to "yes".
|
||||
cover_art_cache_directory = "/tmp/shairport-sync/.cache/coverart"; // artwork will be stored in this directory if the dbus or MPRIS interfaces are enabled or if the MQTT client is in use. Set it to "" to prevent caching, which may be useful on some systems
|
||||
enabled = "yes"; // Set this to yes to get Shairport Sync to solicit metadata from the source and to pass it on via a pipe.
|
||||
include_cover_art = "yes"; // Set to "yes" to get Shairport Sync to solicit cover art from the source and pass it via the pipe. You must also set "enabled" to "yes".
|
||||
cover_art_cache_directory = "/tmp/shairport-sync/.cache/coverart"; // Artwork will be stored in this directory if the dbus or MPRIS interfaces are enabled or if the MQTT client is in use. Set it to "" to prevent caching, which may be useful on some systems.
|
||||
pipe_name = "/tmp/shairport-sync-metadata";
|
||||
pipe_timeout = 5000; // wait for this number of milliseconds for a blocked pipe to unblock before giving up
|
||||
pipe_timeout = 5000; // Wait for this number of milliseconds for a blocked pipe to unblock before giving up.
|
||||
};
|
||||
|
||||
|
||||
mqtt =
|
||||
{
|
||||
enabled = "yes"; // set this to yes to enable the mqtt-metadata-service
|
||||
hostname = "192.168.1.111"; // Hostname of the MQTT Broker
|
||||
port = 1883; // Port on the MQTT Broker to connect to
|
||||
username = "username"; //set this to a string to your username in order to enable username authentication
|
||||
password = "password"; //set this to a string you your password in order to enable username & password authentication
|
||||
topic = "shairport"; //MQTT topic where this instance of shairport-sync should publish. If not set, the general.name value is used.
|
||||
// publish_raw = "no"; //whether to publish all available metadata under the codes given in the 'metadata' docs.
|
||||
publish_parsed = "yes"; //whether to publish a small (but useful) subset of metadata under human-understandable topics
|
||||
publish_cover = "yes"; //whether to publish the cover over mqtt in binary form. This may lead to a bit of load on the broker
|
||||
// enable_remote = "no"; //whether to remote control via MQTT. RC is available under `topic`/remote.
|
||||
enabled = "yes"; // Set this to yes to enable the mqtt-metadata-service.
|
||||
hostname = "192.168.1.111"; // Hostname of the MQTT Broker.
|
||||
port = 1883; // Port on the MQTT Broker to connect to.
|
||||
username = "username"; // Set this to your MQTT user's username in order to enable username authentication.
|
||||
password = "password"; // Set this to your MQTT user's password in order to enable username & password authentication.
|
||||
topic = "shairport"; // MQTT topic where this instance of Shairport Sync should publish. If not set, the general.name value is used.
|
||||
// publish_raw = "no"; // Whether to publish all available metadata under the codes given in the 'metadata' docs.
|
||||
publish_parsed = "yes"; // Whether to publish a small (but useful) subset of metadata under human-understandable topics.
|
||||
publish_cover = "yes"; // Whether to publish the cover over MQTT in binary form. This may lead to a bit of load on the broker.
|
||||
// enable_remote = "no"; // Whether to remote control via MQTT. RC is available under `topic`/remote.
|
||||
};
|
||||
```
|
||||
**Important:** Either `publish_raw`, `publish_parsed`, or `publish_cover` need to be set in the MQTT configuration. Otherwise, no messages will be published.
|
||||
|
||||
Import Notes
|
||||
----
|
||||
## Overall Active States
|
||||
|
||||
Publish Options
|
||||
`active_start` and `active_end` represent a stable on/off flag for the current AirPlay session.
|
||||
|
||||
One needs to set either `publish_raw`, `publish_parsed` or `publish_cover` in the MQTT setup or no messages will be published.
|
||||
`active_start` is plublished when any new AirPlay session begins.
|
||||
|
||||
|
||||
Overall Active State of Stream
|
||||
|
||||
`active_start` and `active_end` represent a stable on/off flag for the current airplay session.
|
||||
|
||||
`active_start` is plublished when any new airplay session begins
|
||||
|
||||
`active_end` will fire after a configured timeout period unless the airplay stream is resumed.
|
||||
`active_end` will fire after a configured timeout period unless the AirPlay stream is resumed.
|
||||
|
||||
```xml
|
||||
sessioncontrol =
|
||||
{
|
||||
// "active" state starts when play begins and ends when the active_state_timeout has elapsed after play ends, unless another play session starts before the timeout has fully elapsed.
|
||||
// "active" state starts when play begins, and ends when the active_state_timeout has elapsed after play ends, unless another play session starts before the timeout has fully elapsed.
|
||||
active_state_timeout = 30.0;
|
||||
};
|
||||
```
|
||||
|
||||
MetaData Parsing
|
||||
----
|
||||
## Metadata Parsing
|
||||
|
||||
Additional details regarding the metadata can be found at https://github.com/mikebrady/shairport-sync-metadata-reader.
|
||||
|
||||
Additional details regarding the metadata can be found at [https://github.com/mikebrady/shairport-sync-metadata-reader]
|
||||
|
||||
Meta data is generated by both the stream source (iOS, itunes, etc) and by shairport-sync itself. This data is coded as two 4-character codes to identify each piece of data, the `type` and the `code`.
|
||||
Metadata is generated by both the stream source (iOS, iTunes, etc.) and by Shairport Sync itself. This data is coded as two 4-character codes to identify each piece of data, the `type` and the `code`.
|
||||
|
||||
The first 4-character code, called the `type`, is either:
|
||||
* `core` for all the regular metadadata coming from iTunes, etc., or
|
||||
* `ssnc` (for 'shairport-sync') for all metadata coming from Shairport Sync itself, such as start/end delimiters, etc.
|
||||
|
||||
* For `core` metadata, the second 4-character code is the 4-character metadata code that comes from iTunes etc. See, for example, https://code.google.com/p/ytrack/wiki/DMAP for information about the significance of the codes. The original data supplied by the source, if any, follows, and is encoded in base64 format. The length of the data is also provided.
|
||||
Additionally:
|
||||
* For `core` metadata, the second 4-character code is the 4-character metadata code that comes from iTunes, etc. See, for example, https://code.google.com/p/ytrack/wiki/DMAP for information about the significance of the codes. The original data supplied by the source, if any, follows, and is encoded in base64 format. The length of the data is also provided.
|
||||
* For `ssnc` metadata, the second 4-character code is used to distinguish the messages. Cover art, coming from the source, is not tagged in the same way as other metadata, it seems, so is sent as an `ssnc` type metadata message with the code `PICT`. Progress information, similarly, is not tagged like other source-originated metadata, so it is sent as an `ssnc` type with the code `prgr`.
|
||||
|
||||
Here are some of the `core` codes commonly passed from the source:
|
||||
@@ -128,14 +117,12 @@ Here are the 'ssnc' codes defined so far:
|
||||
* `stal` -- this is an error message meaning that reception of a large piece of metadata, usually a large picture, has stalled; bad things may happen.
|
||||
* `svip` -- the payload is the IP address of the server, i.e. shairport-sync. Can be an IPv4 or an IPv6 address.
|
||||
|
||||
### Parsed Messages
|
||||
|
||||
Parsed Messages
|
||||
----
|
||||
|
||||
The MQTT service can parse the above raw messages into a subset of human-readable topics that include,
|
||||
The MQTT service can parse the above raw messages into a subset of human-readable topics that include:
|
||||
|
||||
* `active_remote_id` -- Active Remote ID
|
||||
* `artist` -- text of artist name
|
||||
* `artist` -- text of artist name
|
||||
* `album` -- text of album name
|
||||
* `client_ip` -- IP address of the connected client
|
||||
* `client_device_id` -- Client advertised Device ID
|
||||
@@ -145,50 +132,45 @@ The MQTT service can parse the above raw messages into a subset of human-readabl
|
||||
* `dacp_id` -- DACP ID
|
||||
* `format` -- ??
|
||||
* `genre` -- text of genre
|
||||
* `server_ip` -- IP address of shairport-sync that the client is connected to
|
||||
* `songalbum` --
|
||||
* `server_ip` -- IP address of Shairport Sync that the client is connected to
|
||||
* `songalbum` --
|
||||
* `title` -- text of song title
|
||||
* `volume` -- The volume is sent as a string -- "airplay_volume,volume,lowest_volume,highest_volume", where "volume", "lowest_volume" and "highest_volume" are given in dB. (see above)
|
||||
|
||||
and empty messages (`--`) at the following topics are published.
|
||||
Additionally, empty messages (`--`) at the following topics are published.
|
||||
|
||||
* `play_start` -- fired at the begining of every song
|
||||
* `play_end` -- fired at the end of every song
|
||||
* `play_flush` -- fired when song is skipped or on positional change
|
||||
* `play_resume` -- fired when song play resumes from pause
|
||||
* `active_start` -- fired when a new active airplay session begins
|
||||
* `active_start` -- fired when a new active AirPlay session begins
|
||||
* `active_end` -- fired after a configured timeout period after the stream ends (unless a new stream begins)
|
||||
|
||||
|
||||
|
||||
## Consuming MQTT Data
|
||||
|
||||
Users will find examples on how to consume the MQTT data in various home automation projects. If a user has an interesting use please raise a new issue to suggest adding it to the guide, or simply fork the development branch and create a pull-request.
|
||||
MQTT provides users with the flexibility to consume the MQTT data in various home automation projects. If you have an interesting use, please raise a new issue to suggest adding it to the guide, or simply fork the development branch and create a pull request.
|
||||
|
||||
### Home Assistant Variable Templates
|
||||
### [Home Assistant](https://www.home-assistant.io/) Examples
|
||||
|
||||
Examples of consuming "parsed" MQTT data in [Home Assistant](https://www.home-assistant.io/)
|
||||
The `active_start` and `active_end` have good potential use as triggers to turn on and off various connect receivers/zones. "payload_off" is set to prevent accidental triggering.
|
||||
The `active_start` and `active_end` have good potential use as triggers to turn on and off various connected receivers/zones. Note that `payload_off` is set to prevent accidental triggering.
|
||||
|
||||
```yml
|
||||
binary_sensor:
|
||||
|
||||
- platform: mqtt
|
||||
name: "shairport active start"
|
||||
state_topic: "shairport/active_start"
|
||||
payload_on: "--"
|
||||
payload_off: "OFF"
|
||||
off_delay: 300
|
||||
|
||||
- platform: mqtt
|
||||
name: "shairport active end"
|
||||
state_topic: "shairport/active_end"
|
||||
payload_on: "--"
|
||||
payload_off: "OFF"
|
||||
off_delay: 300
|
||||
mqtt:
|
||||
- binary_sensor:
|
||||
name: "shairport active start"
|
||||
state_topic: "shairport/active_start"
|
||||
payload_on: "--"
|
||||
payload_off: "OFF"
|
||||
off_delay: 300
|
||||
- binary_sensor:
|
||||
name: "shairport active end"
|
||||
state_topic: "shairport/active_end"
|
||||
payload_on: "--"
|
||||
payload_off: "OFF"
|
||||
off_delay: 300
|
||||
```
|
||||
|
||||
Below parsed data is saved into the Home Assistant database as sensor data. Please note the conversion of the volume from dB to percentage.
|
||||
In the below example, the parsed data is saved into the Home Assistant database as sensor data. Please note the conversion of the volume from dB to percentage.
|
||||
|
||||
```yml
|
||||
mqtt:
|
||||
|
||||
+13
-6
@@ -1,6 +1,6 @@
|
||||
ARFLAGS = cr
|
||||
|
||||
man_MANS = $(top_srcdir)/man/shairport-sync.7
|
||||
man_MANS = $(top_srcdir)/man/shairport-sync.1
|
||||
|
||||
lib_pair_ap_a_CFLAGS = -Wall -g -DCONFIG_GCRYPT -pthread
|
||||
lib_tinyhttp_a_CFLAGS = -pthread
|
||||
@@ -34,6 +34,7 @@ if BUILD_FOR_OPENBSD
|
||||
else
|
||||
AM_CXXFLAGS = -fno-common -Wno-multichar -Wall -Wextra -Wno-clobbered -Wno-psabi -pthread -DSYSCONFDIR=\"$(sysconfdir)\"
|
||||
AM_CFLAGS = -fno-common -Wno-multichar -Wall -Wextra -Wno-clobbered -Wno-psabi -pthread -DSYSCONFDIR=\"$(sysconfdir)\"
|
||||
shairport_sync_SOURCES += scripts/shairport-sync.service scripts/shairport-sync.service-avahi scripts/shairport-sync
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@@ -211,8 +212,6 @@ shairport_sync_mpris_test_client_SOURCES = shairport-sync-mpris-test-client.c
|
||||
shairport_sync_mpris_test_client_LDADD = lib_mpris_interface.a
|
||||
endif
|
||||
|
||||
if INSTALL_CONFIG_FILES
|
||||
|
||||
CONFIG_FILE_INSTALL_TARGET = config-file-install-local
|
||||
$(CONFIG_FILE_INSTALL_TARGET): scripts/shairport-sync.conf
|
||||
install -d $(DESTDIR)$(sysconfdir)
|
||||
@@ -257,14 +256,16 @@ $(MPRIS_POLICY_INSTALL_TARGET): $(MPRIS_POLICY_FILE)
|
||||
|
||||
endif # USE_MPRIS
|
||||
|
||||
endif # INSTALL_CONFIG_FILES
|
||||
|
||||
INSTALL_GROUP_TARGET = install-group-local
|
||||
|
||||
$(INSTALL_GROUP_TARGET):
|
||||
getent group shairport-sync &>/dev/null || groupadd -r shairport-sync &>/dev/null
|
||||
|
||||
if INSTALL_CREATE_USER_GROUP
|
||||
INSTALL_USER_TARGET = install-user-local
|
||||
else
|
||||
INSTALL_USER_TARGET =
|
||||
endif
|
||||
|
||||
$(INSTALL_USER_TARGET): $(INSTALL_GROUP_TARGET)
|
||||
getent passwd shairport-sync &>/dev/null || useradd -r -M -g shairport-sync -s /usr/sbin/nologin -G audio shairport-sync &>/dev/null
|
||||
@@ -329,10 +330,16 @@ $(INSTALL_CYGWIN_TARGET): scripts/shairport-sync-config
|
||||
|
||||
endif # INSTALL_CYGWIN_SERVICE
|
||||
|
||||
install-exec-hook: $(CONFIG_FILE_INSTALL_TARGET) \
|
||||
install-config-files: $(CONFIG_FILE_INSTALL_TARGET) \
|
||||
$(DBUS_POLICY_INSTALL_TARGET) \
|
||||
$(MPRIS_POLICY_INSTALL_TARGET) \
|
||||
$(INSTALL_SYSTEMV_TARGET) \
|
||||
$(INSTALL_SYSTEMD_TARGET) \
|
||||
$(INSTALL_FREEBSD_TARGET) \
|
||||
$(INSTALL_CYGWIN_TARGET)
|
||||
|
||||
if INSTALL_CONFIG_FILES
|
||||
|
||||
install-exec-hook: install-config-files
|
||||
|
||||
endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Shairport Sync
|
||||
Shairport Sync is an [AirPlay](https://www.pocket-lint.com/speakers/news/apple/144646-apple-airplay-2-vs-airplay-what-s-the-difference) audio player for Linux and FreeBSD. It plays audio streamed from Apple devices and from AirPlay sources such as [OwnTone](https://github.com/owntone/owntone-server) (formerly `forked-daapd`).
|
||||
Shairport Sync is an [AirPlay](https://www.pocket-lint.com/speakers/news/apple/144646-apple-airplay-2-vs-airplay-what-s-the-difference) audio player for Linux, FreeBSD and OpenBSD. It plays audio streamed from Apple devices and from AirPlay sources such as [OwnTone](https://github.com/owntone/owntone-server) (formerly `forked-daapd`).
|
||||
|
||||
Shairport Sync can be built as an AirPlay 2 player (with [some limitations](AIRPLAY2.md#features-and-limitations)) or as "classic" Shairport Sync – a player for the older, but still supported, AirPlay (aka "AirPlay 1") protocol.
|
||||
|
||||
@@ -33,7 +33,7 @@ Some features require configuration at build time – see [CONFIGURATION FLAGS.m
|
||||
# Status
|
||||
Shairport Sync was designed to [run best](ADVANCED%20TOPICS/GetTheBest.md) on stable, dedicated, stand-alone low-power "headless" systems with ALSA as the audio system and with a decent CD-quality Digital to Analog Converter (DAC).
|
||||
|
||||
Shairport Sync runs on recent (2018 onwards) Linux systems and FreeBSD from 12.1 onwards. It requires a system with the power of a Raspberry Pi 2 or a Pi Zero 2 or better.
|
||||
Shairport Sync runs on recent (2018 onwards) Linux systems, FreeBSD from 12.1 onwards and OpenBSD. It requires a system with the power of a Raspberry Pi 2 or a Pi Zero 2 or better.
|
||||
|
||||
Classic Shairport Sync runs on a wider variety of Linux sytems, including OpenWrt and Cygwin and it also runs on OpenBSD. Many embedded devices are powerful enough to power classic Shairport Sync.
|
||||
|
||||
@@ -57,7 +57,7 @@ Thanks to everyone who has supported and improved Shairport Sync over the years.
|
||||
The audio that Shairport Sync receives is sent to the computer's sound system, to a named unix pipe or to `STDOUT`. By far the best sound system to use is ALSA. This is because ALSA can give direct access to the Digital to Analog Converter (DAC) hardware of the machine. Audio samples can be sent through ALSA directly to the DAC, maximising fidelity, and accurate timing information can be obtained from the DAC, maximising synchronisation. Direct access to hardware is given through ALSA devices with names beginning with `hw:`.
|
||||
|
||||
## Synchronised Audio
|
||||
Shairport Sync offers *full audio synchronisation*. Full audio synchronisation means that audio is played on the output device at exactly the time specified by the audio source. To accomplish this, Shairport Sync needs access to audio systems – such as ALSA on Linux and `sndio` on FreeBSD – that provide very accurate timing information about audio being streamed to output devices. Ideally, Shairport Sync should have direct access to the output device used, which should be a real sound card capable of working with 44,100, 88,200 or 176,400 samples per second, interleaved PCM stereo of 8, 16, 24 or 32 bits. Using the ALSA sound system, Shairport Sync will choose the greatest bit depth available at 44,100 samples per second, resorting to multiples of 44,100 if it is not available. You'll get a message in the log if there's a problem. With all other sound systems, a sample rate of 44,100 is chosen with a bit depth of 16 bit.
|
||||
Shairport Sync offers *full audio synchronisation*. Full audio synchronisation means that audio is played on the output device at exactly the time specified by the audio source. To accomplish this, Shairport Sync needs access to audio systems – such as ALSA on Linux and `sndio` on FreeBSD and OpenBSD – that provide very accurate timing information about audio being streamed to output devices. Ideally, Shairport Sync should have direct access to the output device used, which should be a real sound card capable of working with 44,100, 88,200 or 176,400 samples per second, interleaved PCM stereo of 8, 16, 24 or 32 bits. Using the ALSA sound system, Shairport Sync will choose the greatest bit depth available at 44,100 samples per second, resorting to multiples of 44,100 if it is not available. You'll get a message in the log if there's a problem. With all other sound systems, a sample rate of 44,100 is chosen with a bit depth of 16 bit.
|
||||
|
||||
Shairport Sync works well with PulseAudio, a widely used sound server found on many desktop Linuxes. While the timing information is not as accurate as that of ALSA or `sndio`, it is often impractical to remove or disable PulseAudio.
|
||||
|
||||
|
||||
+4
-3
@@ -938,9 +938,9 @@ static int prepare_mixer() {
|
||||
} else {
|
||||
// use the linear scale and do the db conversion ourselves
|
||||
warn("The hardware mixer specified -- \"%s\" -- does not have "
|
||||
"a dB volume scale.",
|
||||
"a dB volume scale, and so can not be used by Shairport Sync.",
|
||||
alsa_mix_ctrl);
|
||||
|
||||
/*
|
||||
if ((response = snd_ctl_open(&ctl, alsa_mix_dev, 0)) < 0) {
|
||||
warn("Cannot open control \"%s\"", alsa_mix_dev);
|
||||
}
|
||||
@@ -961,9 +961,10 @@ static int prepare_mixer() {
|
||||
// we know it can do dB stuff
|
||||
audio_alsa.parameters = ¶meters; // likewise the parameters stuff
|
||||
} else {
|
||||
debug(1, "Cannot get the dB range from the volume control \"%s\"", alsa_mix_ctrl);
|
||||
debug(1, "Cannot get a dB range from the volume control \"%s\"", alsa_mix_ctrl);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
if (((config.alsa_use_hardware_mute == 1) &&
|
||||
|
||||
+2
-2
@@ -41,7 +41,7 @@
|
||||
#define RATE 44100
|
||||
|
||||
// Four seconds buffer -- should be plenty
|
||||
#define buffer_allocation 44100 * 4 * 2 * 2
|
||||
#define buffer_allocation RATE * 4 * 2 * 2
|
||||
|
||||
static pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
@@ -399,7 +399,7 @@ void stream_write_cb(pa_stream *stream, size_t requested_bytes,
|
||||
ret = pa_stream_begin_write(stream, (void **)&buffer, &bytes_we_can_transfer);
|
||||
if ((ret == 0) && (buffer != NULL)) {
|
||||
if (bytes_we_can_transfer <= (size_t)(audio_umb - audio_toq)) {
|
||||
// the bytes are all in a row in the audo buffer
|
||||
// the bytes are all in a row in the audio buffer
|
||||
memcpy(buffer, audio_toq, bytes_we_can_transfer);
|
||||
audio_toq += bytes_we_can_transfer;
|
||||
ret = pa_stream_write(stream, buffer, bytes_we_can_transfer, NULL, 0LL, PA_SEEK_RELATIVE);
|
||||
|
||||
@@ -100,6 +100,12 @@
|
||||
#include <mbedtls/md.h>
|
||||
#include <mbedtls/version.h>
|
||||
#include <mbedtls/x509.h>
|
||||
|
||||
#if MBEDTLS_VERSION_MAJOR == 3
|
||||
#define MBEDTLS_PRIVATE_V3_ONLY(_q) MBEDTLS_PRIVATE(_q)
|
||||
#else
|
||||
#define MBEDTLS_PRIVATE_V3_ONLY(_q) _q
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LIBDAEMON
|
||||
@@ -910,8 +916,14 @@ uint8_t *rsa_apply(uint8_t *input, int inlen, int *outlen, int mode) {
|
||||
|
||||
mbedtls_pk_init(&pkctx);
|
||||
|
||||
#if MBEDTLS_VERSION_MAJOR == 3
|
||||
rc = mbedtls_pk_parse_key(&pkctx, (unsigned char *)super_secret_key, sizeof(super_secret_key),
|
||||
NULL, 0, mbedtls_ctr_drbg_random, &ctr_drbg);
|
||||
#else
|
||||
rc = mbedtls_pk_parse_key(&pkctx, (unsigned char *)super_secret_key, sizeof(super_secret_key),
|
||||
NULL, 0);
|
||||
|
||||
#endif
|
||||
if (rc != 0)
|
||||
debug(1, "Error %d reading the private key.", rc);
|
||||
|
||||
@@ -920,19 +932,29 @@ uint8_t *rsa_apply(uint8_t *input, int inlen, int *outlen, int mode) {
|
||||
|
||||
switch (mode) {
|
||||
case RSA_MODE_AUTH:
|
||||
mbedtls_rsa_set_padding(trsa, MBEDTLS_RSA_PKCS_V15, MBEDTLS_MD_NONE);
|
||||
outbuf = malloc(trsa->len);
|
||||
mbedtls_rsa_set_padding(trsa, MBEDTLS_RSA_PKCS_V15, MBEDTLS_MD_NONE);
|
||||
outbuf = malloc(trsa->MBEDTLS_PRIVATE_V3_ONLY(len));
|
||||
#if MBEDTLS_VERSION_MAJOR == 3
|
||||
rc = mbedtls_rsa_pkcs1_encrypt(trsa, mbedtls_ctr_drbg_random, &ctr_drbg,
|
||||
inlen, input, outbuf);
|
||||
#else
|
||||
rc = mbedtls_rsa_pkcs1_encrypt(trsa, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PRIVATE,
|
||||
inlen, input, outbuf);
|
||||
#endif
|
||||
if (rc != 0)
|
||||
debug(1, "mbedtls_pk_encrypt error %d.", rc);
|
||||
*outlen = trsa->len;
|
||||
*outlen = trsa->MBEDTLS_PRIVATE_V3_ONLY(len);
|
||||
break;
|
||||
case RSA_MODE_KEY:
|
||||
mbedtls_rsa_set_padding(trsa, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA1);
|
||||
outbuf = malloc(trsa->len);
|
||||
outbuf = malloc(trsa->MBEDTLS_PRIVATE_V3_ONLY(len));
|
||||
#if MBEDTLS_VERSION_MAJOR == 3
|
||||
rc = mbedtls_rsa_pkcs1_decrypt(trsa, mbedtls_ctr_drbg_random, &ctr_drbg,
|
||||
&olen, input, outbuf, trsa->MBEDTLS_PRIVATE_V3_ONLY(len));
|
||||
#else
|
||||
rc = mbedtls_rsa_pkcs1_decrypt(trsa, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PRIVATE,
|
||||
&olen, input, outbuf, trsa->len);
|
||||
#endif
|
||||
if (rc != 0)
|
||||
debug(1, "mbedtls_pk_decrypt error %d.", rc);
|
||||
*outlen = olen;
|
||||
|
||||
+16
-6
@@ -2,7 +2,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ([2.50])
|
||||
AC_INIT([shairport-sync], [4.3.2], [4265913+mikebrady@users.noreply.github.com])
|
||||
AC_INIT([shairport-sync], [4.3.3], [4265913+mikebrady@users.noreply.github.com])
|
||||
AM_INIT_AUTOMAKE([subdir-objects])
|
||||
AC_CONFIG_SRCDIR([shairport.c])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
@@ -68,8 +68,7 @@ AC_CHECK_LIB([m],[exp], , AC_MSG_ERROR(maths library needed))
|
||||
if test "x${with_pkg_config}" = xyes ; then
|
||||
PKG_CHECK_MODULES(
|
||||
[popt], [popt],
|
||||
[CFLAGS="${popt_CFLAGS} ${CFLAGS}"
|
||||
LIBS="${popt_LIBS} ${LIBS}"], AC_MSG_ERROR(libpopt needed))
|
||||
[CFLAGS="${popt_CFLAGS} ${CFLAGS}" LIBS="${popt_LIBS} ${LIBS}"], AC_MSG_ERROR(libpopt needed))
|
||||
else
|
||||
AC_CHECK_LIB([popt],[poptGetContext], , AC_MSG_ERROR(libpopt needed))
|
||||
fi
|
||||
@@ -115,11 +114,16 @@ if test "x$with_external_mdns" = xyes ; then
|
||||
fi
|
||||
AM_CONDITIONAL([USE_EXTERNAL_MDNS], [test "x$with_external_mdns" = "xyes" ])
|
||||
|
||||
# Check if we want to create user/group (default to yes):
|
||||
|
||||
AC_ARG_WITH([create-user-group],[AS_HELP_STRING([--with-create-user-group],[create user/group during a make install])], ,[with_with_create_user_group=yes])
|
||||
AM_CONDITIONAL([INSTALL_CREATE_USER_GROUP], [test "x$with_with_create_user_group" = "xyes"])
|
||||
|
||||
# Add the libconfig package
|
||||
if test "x${with_pkg_config}" = xyes ; then
|
||||
PKG_CHECK_MODULES(
|
||||
[libconfig], [libconfig],
|
||||
[LIBS="${libconfig_LIBS} ${LIBS}"], AC_MSG_ERROR([libconfig library needed]))
|
||||
[CFLAGS="${libconfig_CFLAGS} ${CFLAGS}" LIBS="${libconfig_LIBS} ${LIBS}"], AC_MSG_ERROR([libconfig library needed]))
|
||||
else
|
||||
AC_CHECK_LIB([config],[config_init], , AC_MSG_ERROR([libconfig library needed]))
|
||||
fi
|
||||
@@ -176,7 +180,7 @@ if test "x${with_ssl}" = xopenssl ; then
|
||||
if test "x${with_pkg_config}" = xyes ; then
|
||||
PKG_CHECK_MODULES(
|
||||
[SSL], [libssl,libcrypto],
|
||||
[LIBS="${SSL_LIBS} ${LIBS}"])
|
||||
[CFLAGS="${SSL_CFLAGS} ${CFLAGS}" LIBS="${SSL_LIBS} ${LIBS}"])
|
||||
else
|
||||
AC_CHECK_LIB([crypto], [main], , AC_MSG_ERROR(libcrypto selected but the library cannot be found!))
|
||||
AC_CHECK_LIB([ssl], [main], , AC_MSG_ERROR(libssl selected but the library cannot be found!))
|
||||
@@ -283,7 +287,13 @@ AM_CONDITIONAL([USE_SNDIO], [test "x$with_sndio" = "xyes"])
|
||||
AC_ARG_WITH(ao, [AS_HELP_STRING([--with-ao],[choose AO (Audio Output?) API support. N.B. no synchronisation -- so underflow or overflow is inevitable!])])
|
||||
if test "x$with_ao" = "xyes" ; then
|
||||
AC_DEFINE([CONFIG_AO], 1, [Include an AO-compatible audio backend.])
|
||||
AC_CHECK_LIB([ao], [ao_initialize], , AC_MSG_ERROR(AO support requires the ao library -- libao-dev suggested))
|
||||
if test "x${with_pkg_config}" = xyes ; then
|
||||
PKG_CHECK_MODULES(
|
||||
[AO], [ao > 1.0.0],
|
||||
[CFLAGS="${AO_CFLAGS} ${CFLAGS}" LIBS="${AO_LIBS} ${LIBS}"], [AC_MSG_ERROR(AO support requires the ao library -- libao-dev suggested)])
|
||||
else
|
||||
AC_CHECK_LIB([ao], [ao_initialize], , AC_MSG_ERROR(AO support requires the ao library -- libao-dev suggested))
|
||||
fi
|
||||
fi
|
||||
AM_CONDITIONAL([USE_AO], [test "x$with_ao" = "xyes"])
|
||||
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
#define COMPILE_FOR_FREEBSD 1
|
||||
#endif
|
||||
|
||||
#if defined(__OpenBSD__)
|
||||
#define COMPILE_FOR_OPENBSD 1
|
||||
#endif
|
||||
|
||||
// struct sockaddr_in6 is bigger than struct sockaddr. derp
|
||||
#ifdef AF_INET6
|
||||
#define SOCKADDR struct sockaddr_storage
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ RUN apk -U add \
|
||||
|
||||
# Copy build files.
|
||||
COPY --from=builder /shairport-sync/build/install/usr/local/bin/shairport-sync /usr/local/bin/shairport-sync
|
||||
COPY --from=builder /shairport-sync/build/install/usr/local/share/man/man7 /usr/share/man/man7
|
||||
COPY --from=builder /shairport-sync/build/install/usr/local/share/man/man1 /usr/share/man/man1
|
||||
COPY --from=builder /nqptp/nqptp /usr/local/bin/nqptp
|
||||
COPY --from=builder /usr/local/lib/libalac.* /usr/local/lib/
|
||||
COPY --from=builder /shairport-sync/build/install/etc/shairport-sync.conf /etc/
|
||||
|
||||
@@ -75,7 +75,7 @@ RUN apk -U add \
|
||||
|
||||
# Copy build files.
|
||||
COPY --from=builder /shairport-sync/build/install/usr/local/bin/shairport-sync /usr/local/bin/shairport-sync
|
||||
COPY --from=builder /shairport-sync/build/install/usr/local/share/man/man7 /usr/share/man/man7
|
||||
COPY --from=builder /shairport-sync/build/install/usr/local/share/man/man1 /usr/share/man/man1
|
||||
COPY --from=builder /usr/local/lib/libalac.* /usr/local/lib/
|
||||
COPY --from=builder /shairport-sync/build/install/etc/shairport-sync.conf /etc/
|
||||
COPY --from=builder /shairport-sync/build/install/etc/shairport-sync.conf.sample /etc/
|
||||
|
||||
@@ -6,10 +6,11 @@ services:
|
||||
network_mode: host
|
||||
restart: unless-stopped
|
||||
# environment:
|
||||
# S6_KEEP_ENV: 1 # Allow S6 to pass environment variables from compose file
|
||||
# PULSE_SERVER: unix:/tmp/pulseaudio.socket # Path for PulseAudio socket
|
||||
# PULSE_COOKIE: /tmp/pulseaudio.cookie # Path for PulseAudio cookie
|
||||
devices:
|
||||
- "/dev/snd" # ALSA device
|
||||
- "/dev/snd" # ALSA device, omit if using PulseAudio
|
||||
# volumes:
|
||||
# - ./volumes/shairport-sync/shairport-sync.conf:/etc/shairport-sync.conf # Customised Shairport Sync configuration file.
|
||||
# - /run/user/1000/pulse/native:/tmp/pulseaudio.socket # PulseAudio socket when using that backend
|
||||
|
||||
+6
-6
@@ -1,11 +1,11 @@
|
||||
shairport-sync.7: shairport-sync.7.xml
|
||||
xmltoman shairport-sync.7.xml > shairport-sync.7
|
||||
all: shairport-sync.1 shairport-sync.html
|
||||
|
||||
shairport-sync.html: shairport-sync.7.xml
|
||||
xsltproc xmltoman.xsl shairport-sync.7.xml > shairport-sync.html
|
||||
%.1: %.1.xml
|
||||
xmltoman $*.1.xml > $*.tmp && mv $*.tmp $*.1
|
||||
|
||||
all: shairport-sync.7 shairport-sync.html
|
||||
%.html: %.1.xml
|
||||
xsltproc xmltoman.xsl $*.1.xml > $*.tmp && mv $*.tmp $*.html
|
||||
|
||||
clean:
|
||||
rm shairport-sync.7
|
||||
rm shairport-sync.1
|
||||
rm shairport-sync.html
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.TH shairport-sync 7 User Manuals
|
||||
.TH shairport-sync 1 User Manuals
|
||||
.SH NAME
|
||||
shairport-sync \- AirPlay and AirPlay 2 Audio Player
|
||||
.SH SYNOPSIS
|
||||
@@ -29,7 +29,7 @@
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
-->
|
||||
|
||||
<manpage name="shairport-sync" section="7" desc="AirPlay and AirPlay 2 Audio Player">
|
||||
<manpage name="shairport-sync" section="1" desc="AirPlay and AirPlay 2 Audio Player">
|
||||
|
||||
<synopsis>
|
||||
<!--
|
||||
@@ -353,7 +353,7 @@
|
||||
<option>
|
||||
<p><opt>--statistics</opt></p>
|
||||
<optdesc><p>
|
||||
Print some performance information to <file>STDERR</file>, or to <opt>syslog</opt> if the <opt>-log-to-syslog</opt> command line option is also chosen.
|
||||
Print some performance information to <file>STDERR</file>, or to <opt>syslog</opt> if the <opt>--log-to-syslog</opt> command line option is also chosen.
|
||||
</p></optdesc>
|
||||
</option>
|
||||
|
||||
@@ -416,7 +416,7 @@
|
||||
<option>
|
||||
<p><opt>-v | --verbose</opt></p>
|
||||
<optdesc><p>
|
||||
Print debug information to the <file>STDERR</file>, or to <opt>syslog</opt> if the <opt>-log-to-syslog</opt> command line option is also chosen.
|
||||
Print debug information to the <file>STDERR</file>, or to <opt>syslog</opt> if the <opt>--log-to-syslog</opt> command line option is also chosen.
|
||||
Repeat up to three times (i.e. <opt>-vv</opt> or <opt>-vvv</opt>) for more detail. You should use <opt>-vvv</opt> very sparingly -- it is really noisy.
|
||||
</p></optdesc>
|
||||
</option>
|
||||
@@ -33,13 +33,13 @@ typedef struct {
|
||||
|
||||
/*
|
||||
#define MDNS_RECORD_WITH_METADATA \
|
||||
"tp=UDP", "sm=false", "ek=1", "et=0,1", "cn=0,1", "ch=2", METADATA_EXPRESSION, "ss=16", \
|
||||
"tp=UDP", "sm=false", "ek=1", "et=0,1", "cn=0,1", "ch=2", METADATA_EXPRESSION, "ss=16", \
|
||||
"sr=44100", "vn=3", "txtvers=1", config.password ? "pw=true" : "pw=false"
|
||||
*/
|
||||
#define MDNS_RECORD_WITH_METADATA \
|
||||
"sf=0x4", "fv=76400.10", "am=ShairportSync", "vs=105.1", "tp=TCP,UDP", "vn=65537", \
|
||||
METADATA_EXPRESSION, "ss=16", "sr=44100", "da=true", "sv=false", "et=0,1", "ek=1", "cn=0,1", \
|
||||
"ch=2", "txtvers=1", config.password ? "pw=true" : "pw=false"
|
||||
METADATA_EXPRESSION, "ss=16", "sr=44100", "da=true", "sv=false", "et=0,1", "ek=1", \
|
||||
"cn=0,1", "ch=2", "txtvers=1", config.password ? "pw=true" : "pw=false"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -52,7 +52,7 @@ typedef struct {
|
||||
|
||||
#define MDNS_RECORD_WITHOUT_METADATA \
|
||||
"sf=0x4", "fv=76400.10", "am=ShairportSync", "vs=105.1", "tp=TCP,UDP", "vn=65537", "ss=16", \
|
||||
"sr=44100", "da=true", "sv=false", "et=0,1", "ek=1", "cn=0,1", "ch=2", "txtvers=1", \
|
||||
"sr=44100", "da=true", "sv=false", "et=0,1", "ek=1", "cn=0,1", "ch=2", "txtvers=1", \
|
||||
config.password ? "pw=true" : "pw=false"
|
||||
|
||||
#endif // _MDNS_H
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
|
||||
#ifdef CONFIG_MBEDTLS
|
||||
#include <mbedtls/aes.h>
|
||||
#include <mbedtls/havege.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_POLARSSL
|
||||
@@ -2057,9 +2056,9 @@ void *player_thread_func(void *arg) {
|
||||
int64_t tsum_of_sync_errors, tsum_of_corrections, tsum_of_insertions_and_deletions,
|
||||
tsum_of_drifts;
|
||||
int64_t previous_sync_error = 0, previous_correction = 0;
|
||||
uint64_t minimum_dac_queue_size;
|
||||
int32_t minimum_buffer_occupancy;
|
||||
int32_t maximum_buffer_occupancy;
|
||||
uint64_t minimum_dac_queue_size = 0;
|
||||
int32_t minimum_buffer_occupancy = 0;
|
||||
int32_t maximum_buffer_occupancy = 0;
|
||||
|
||||
#ifdef CONFIG_AIRPLAY_2
|
||||
conn->ap2_audio_buffer_minimum_size = -1;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
#ifdef CONFIG_MBEDTLS
|
||||
#include <mbedtls/aes.h>
|
||||
#include <mbedtls/havege.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_POLARSSL
|
||||
|
||||
+22
-10
@@ -35,8 +35,10 @@
|
||||
#include <sys/mman.h>
|
||||
#ifdef COMPILE_FOR_FREEBSD
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netdb.h>
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include "common.h"
|
||||
#include "ptp-utilities.h"
|
||||
@@ -222,28 +224,38 @@ void ptp_send_control_message_string(const char *msg) {
|
||||
msg);
|
||||
debug(2, "Send control message to NQPTP: \"%s\"", full_message);
|
||||
int s;
|
||||
unsigned short port = htons(NQPTP_CONTROL_PORT);
|
||||
struct sockaddr_in server;
|
||||
int ret;
|
||||
struct addrinfo hints, *info;
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
/* nqptp is only controllable via localhost */
|
||||
char portstr[20];
|
||||
snprintf(portstr, 20, "%d", NQPTP_CONTROL_PORT);
|
||||
|
||||
ret = getaddrinfo("localhost", portstr, &hints, &info);
|
||||
if (ret) {
|
||||
die("getaddrinfo: %s", gai_strerror(ret));
|
||||
}
|
||||
|
||||
/* Create a datagram socket in the internet domain and use the
|
||||
* default protocol (UDP).
|
||||
*/
|
||||
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
if ((s = socket(info->ai_family, info->ai_socktype, 0)) < 0) {
|
||||
die("Can't open a socket to NQPTP");
|
||||
}
|
||||
|
||||
/* Set up the server name */
|
||||
server.sin_family = AF_INET; /* Internet Domain */
|
||||
server.sin_port = port; /* Server Port */
|
||||
server.sin_addr.s_addr = 0; /* Server's Address */
|
||||
|
||||
/* Send the message in buf to the server */
|
||||
if (sendto(s, full_message, full_message_size, 0, (struct sockaddr *)&server, sizeof(server)) <
|
||||
if (sendto(s, full_message, full_message_size, 0, info->ai_addr, info->ai_addrlen) <
|
||||
0) {
|
||||
die("error sending timing_peer_list to NQPTP");
|
||||
}
|
||||
/* Deallocate the socket */
|
||||
close(s);
|
||||
freeaddrinfo(info);
|
||||
|
||||
/* deallocate the message string */
|
||||
free(full_message);
|
||||
|
||||
@@ -271,7 +271,6 @@ void build_bonjour_strings(__attribute((unused)) rtsp_conn_info *conn) {
|
||||
snprintf(pkString, sizeof(pkString), "pk=");
|
||||
pkString_make(pkString + strlen("pk="), sizeof(pkString) - strlen("pk="),
|
||||
config.airplay_device_id);
|
||||
|
||||
txt_records[entry_number++] = "cn=0,1";
|
||||
txt_records[entry_number++] = "da=true";
|
||||
txt_records[entry_number++] = "et=0,1";
|
||||
@@ -594,7 +593,8 @@ int get_play_lock(rtsp_conn_info *conn, int allow_session_interruption) {
|
||||
principal_conn->connection_number);
|
||||
} else if (allow_session_interruption != 0) {
|
||||
rtsp_conn_info *previous_principal_conn = principal_conn;
|
||||
principal_conn = NULL; // no longer the principal conn
|
||||
// important -- demote the principal conn before cancelling it
|
||||
principal_conn = NULL;
|
||||
pthread_cancel(previous_principal_conn->thread);
|
||||
// the previous principal thread will block on the principal conn lock when exiting
|
||||
// so it's important not to wait for it here, e.g. don't put in a pthread_join here.
|
||||
@@ -707,7 +707,7 @@ void cleanup_threads(void) {
|
||||
conns[i] = NULL;
|
||||
}
|
||||
if (conns[i] != NULL) {
|
||||
debug(2, "Airplay Volume for connection %d is %.6f.", conns[i]->connection_number,
|
||||
debug(3, "Airplay Volume for connection %d is %.6f.", conns[i]->connection_number,
|
||||
suggested_volume(conns[i]));
|
||||
connection_count++;
|
||||
}
|
||||
@@ -723,7 +723,7 @@ void cleanup_threads(void) {
|
||||
debug(2, "%d active connections.", connection_count);
|
||||
old_connection_count = connection_count;
|
||||
}
|
||||
debug(2, "Airplay Volume for new connections is %.6f.", suggested_volume(NULL));
|
||||
debug(3, "Airplay Volume for new connections is %.6f.", suggested_volume(NULL));
|
||||
}
|
||||
|
||||
// park a null at the line ending, and return the next line pointer
|
||||
@@ -952,7 +952,7 @@ void add_flush_request(int flushNow, uint32_t flushFromSeq, uint32_t flushFromTS
|
||||
if ((u == NULL) || ((u->flushNow == 0) && (flushNow != 0)) ||
|
||||
(flushFromSeq < u->flushFromSeq) ||
|
||||
((flushFromSeq == u->flushFromSeq) && (flushFromTS < u->flushFromTS))) {
|
||||
flush_request_t *n = (flush_request_t *)calloc(sizeof(flush_request_t), 1);
|
||||
flush_request_t *n = (flush_request_t *)calloc(1, sizeof(flush_request_t));
|
||||
n->flushNow = flushNow;
|
||||
n->flushFromSeq = flushFromSeq;
|
||||
n->flushFromTS = flushFromTS;
|
||||
@@ -1532,7 +1532,7 @@ int msg_write_response(rtsp_conn_info *conn, rtsp_message *resp) {
|
||||
// Here, if there's content, write the Content-Length header ...
|
||||
|
||||
if (resp->contentlength) {
|
||||
debug(2, "Responding with content of length %d", resp->contentlength);
|
||||
debug(3, "Responding with content of length %d", resp->contentlength);
|
||||
n = snprintf(p, pktfree, "Content-Length: %d\r\n", resp->contentlength);
|
||||
pktfree -= n;
|
||||
p += n;
|
||||
@@ -1674,7 +1674,7 @@ void handle_record(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
|
||||
|
||||
void handle_get_info(__attribute((unused)) rtsp_conn_info *conn, rtsp_message *req,
|
||||
rtsp_message *resp) {
|
||||
debug_log_rtsp_message(2, "GET /info:", req);
|
||||
debug_log_rtsp_message(3, "GET /info:", req);
|
||||
if (rtsp_message_contains_plist(req)) { // it's stage one
|
||||
// get version of AirPlay -- it might be too old. Not using it yet.
|
||||
char *hdr = msg_get_header(req, "User-Agent");
|
||||
@@ -1683,10 +1683,51 @@ void handle_get_info(__attribute((unused)) rtsp_conn_info *conn, rtsp_message *r
|
||||
hdr = hdr + strlen("AirPlay/");
|
||||
// double airplay_version = 0.0;
|
||||
// airplay_version = atof(hdr);
|
||||
debug(2, "Connection %d: GET_INFO: Source AirPlay Version is: %s.", conn->connection_number,
|
||||
debug(3, "Connection %d: GET_INFO: Source AirPlay Version is: %s.", conn->connection_number,
|
||||
hdr);
|
||||
}
|
||||
}
|
||||
|
||||
// In Stage 1, look for the DACP and Active-Remote
|
||||
char *ar = msg_get_header(req, "Active-Remote");
|
||||
if (ar) {
|
||||
debug(3, "Connection %d: GET /info -- Active-Remote string seen: \"%s\".",
|
||||
conn->connection_number, ar);
|
||||
// get the active remote
|
||||
if (conn->dacp_active_remote) // this is in case SETUP was previously called
|
||||
free(conn->dacp_active_remote);
|
||||
conn->dacp_active_remote = strdup(ar);
|
||||
#ifdef CONFIG_METADATA
|
||||
send_metadata('ssnc', 'acre', ar, strlen(ar), req, 1);
|
||||
#endif
|
||||
} else {
|
||||
debug(3, "Connection %d: GET /info -- doesn't include Active-Remote information.",
|
||||
conn->connection_number);
|
||||
if (conn->dacp_active_remote) { // this is in case GET /info was previously called
|
||||
free(conn->dacp_active_remote);
|
||||
conn->dacp_active_remote = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ar = msg_get_header(req, "DACP-ID");
|
||||
if (ar) {
|
||||
debug(3, "Connection %d: GET /info -- DACP-ID string seen: \"%s\".", conn->connection_number,
|
||||
ar);
|
||||
if (conn->dacp_id) // this is in case SETUP was previously called
|
||||
free(conn->dacp_id);
|
||||
conn->dacp_id = strdup(ar);
|
||||
#ifdef CONFIG_METADATA
|
||||
send_metadata('ssnc', 'daid', ar, strlen(ar), req, 1);
|
||||
#endif
|
||||
} else {
|
||||
debug(3, "Connection %d: GET /info -- doesn't include DACP-ID string information.",
|
||||
conn->connection_number);
|
||||
if (conn->dacp_id) { // this is in case GET /info was previously called
|
||||
free(conn->dacp_id);
|
||||
conn->dacp_id = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
plist_t info_plist = NULL;
|
||||
plist_from_memory(req->content, req->contentlength, &info_plist);
|
||||
|
||||
@@ -1706,7 +1747,7 @@ void handle_get_info(__attribute((unused)) rtsp_conn_info *conn, rtsp_message *r
|
||||
debug(1, "GET /info Stage 1: first item in qualifier array not a string");
|
||||
goto user_fail;
|
||||
}
|
||||
debug(2, "GET /info Stage 1: qualifier: %s", qualifier_array_val_cstr);
|
||||
debug(3, "GET /info Stage 1: qualifier: %s", qualifier_array_val_cstr);
|
||||
plist_free(info_plist);
|
||||
free(qualifier_array_val_cstr);
|
||||
|
||||
@@ -1793,7 +1834,7 @@ void handle_get_info(__attribute((unused)) rtsp_conn_info *conn, rtsp_message *r
|
||||
free(qualifier_response_data);
|
||||
}
|
||||
msg_add_header(resp, "Content-Type", "application/x-apple-binary-plist");
|
||||
debug_log_rtsp_message(2, "GET /info Stage 1 Response:", resp);
|
||||
debug_log_rtsp_message(3, "GET /info Stage 1 Response:", resp);
|
||||
resp->respcode = 200;
|
||||
return;
|
||||
|
||||
@@ -1818,7 +1859,7 @@ void handle_get_info(__attribute((unused)) rtsp_conn_info *conn, rtsp_message *r
|
||||
plist_to_bin(response_plist, &resp->content, &resp->contentlength);
|
||||
plist_free(response_plist);
|
||||
msg_add_header(resp, "Content-Type", "application/x-apple-binary-plist");
|
||||
debug_log_rtsp_message(2, "GET /info Stage 2 Response", resp);
|
||||
debug_log_rtsp_message(3, "GET /info Stage 2 Response", resp);
|
||||
resp->respcode = 200;
|
||||
return;
|
||||
}
|
||||
@@ -2036,9 +2077,9 @@ void handle_setrateanchori(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
|
||||
}
|
||||
|
||||
void handle_get(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
|
||||
debug(2, "Connection %d: GET %s :: Content-Length %d", conn->connection_number, req->path,
|
||||
debug(3, "Connection %d: GET %s :: Content-Length %d", conn->connection_number, req->path,
|
||||
req->contentlength);
|
||||
debug_log_rtsp_message(2, "GET request", req);
|
||||
debug_log_rtsp_message(3, "GET request", req);
|
||||
if (strcmp(req->path, "/info") == 0) {
|
||||
handle_get_info(conn, req, resp);
|
||||
} else {
|
||||
@@ -2201,7 +2242,7 @@ void handle_pair_verify(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *r
|
||||
uint8_t *body = NULL;
|
||||
size_t body_len = 0;
|
||||
struct pair_result *result;
|
||||
debug(2, "Connection %d: pair-verify Content-Length %d", conn->connection_number,
|
||||
debug(3, "Connection %d: pair-verify Content-Length %d", conn->connection_number,
|
||||
req->contentlength);
|
||||
|
||||
if (!conn->ap2_pairing_context.verify_ctx) {
|
||||
@@ -2237,7 +2278,7 @@ out:
|
||||
resp->contentlength = body_len;
|
||||
if (body)
|
||||
msg_add_header(resp, "Content-Type", "application/octet-stream");
|
||||
debug_log_rtsp_message(2, "pair-verify response", resp);
|
||||
debug_log_rtsp_message(3, "pair-verify response", resp);
|
||||
}
|
||||
|
||||
void handle_pair_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
|
||||
@@ -2283,7 +2324,7 @@ out:
|
||||
resp->contentlength = body_len;
|
||||
if (body)
|
||||
msg_add_header(resp, "Content-Type", "application/octet-stream");
|
||||
debug_log_rtsp_message(2, "pair-setup response", resp);
|
||||
debug_log_rtsp_message(3, "pair-setup response", resp);
|
||||
}
|
||||
|
||||
void handle_fp_setup(__attribute__((unused)) rtsp_conn_info *conn, rtsp_message *req,
|
||||
@@ -2426,9 +2467,9 @@ void handle_configure(rtsp_conn_info *conn __attribute__((unused)),
|
||||
|
||||
void handle_feedback(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *req,
|
||||
__attribute__((unused)) rtsp_message *resp) {
|
||||
debug(2, "Connection %d: POST %s Content-Length %d", conn->connection_number, req->path,
|
||||
debug(3, "Connection %d: POST %s Content-Length %d", conn->connection_number, req->path,
|
||||
req->contentlength);
|
||||
debug_log_rtsp_message(2, NULL, req);
|
||||
debug_log_rtsp_message(3, NULL, req);
|
||||
if (conn->airplay_stream_category == remote_control_stream) {
|
||||
plist_t array_plist = plist_new_array();
|
||||
|
||||
@@ -2439,7 +2480,7 @@ void handle_feedback(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message
|
||||
plist_free(response_plist);
|
||||
|
||||
msg_add_header(resp, "Content-Type", "application/x-apple-binary-plist");
|
||||
debug_log_rtsp_message(2, "FEEDBACK response (remote_control_stream):", resp);
|
||||
debug_log_rtsp_message(3, "FEEDBACK response (remote_control_stream):", resp);
|
||||
}
|
||||
|
||||
/* not finished yet
|
||||
@@ -2467,7 +2508,7 @@ void handle_command(__attribute__((unused)) rtsp_conn_info *conn, rtsp_message *
|
||||
__attribute__((unused)) rtsp_message *resp) {
|
||||
debug(2, "Connection %d: POST %s Content-Length %d", conn->connection_number, req->path,
|
||||
req->contentlength);
|
||||
debug_log_rtsp_message(2, NULL, req);
|
||||
debug_log_rtsp_message(3, NULL, req);
|
||||
if (rtsp_message_contains_plist(req)) {
|
||||
plist_t command_dict = NULL;
|
||||
plist_from_memory(req->content, req->contentlength, &command_dict);
|
||||
@@ -2579,9 +2620,9 @@ void handle_post(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
|
||||
}
|
||||
|
||||
void handle_setpeers(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
|
||||
debug(2, "Connection %d: SETPEERS %s Content-Length %d", conn->connection_number, req->path,
|
||||
debug(3, "Connection %d: SETPEERS %s Content-Length %d", conn->connection_number, req->path,
|
||||
req->contentlength);
|
||||
debug_log_rtsp_message(2, "SETPEERS request", req);
|
||||
debug_log_rtsp_message(3, "SETPEERS request", req);
|
||||
/*
|
||||
char timing_list_message[4096];
|
||||
timing_list_message[0] = 'T';
|
||||
@@ -2715,7 +2756,7 @@ void teardown_phase_two(rtsp_conn_info *conn) {
|
||||
}
|
||||
|
||||
// only update these things if you're (still) the principal conn
|
||||
pthread_rwlock_rdlock(&principal_conn_lock); // don't let the principal_conn be changed
|
||||
pthread_rwlock_wrlock(&principal_conn_lock); // don't let the principal_conn be changed
|
||||
pthread_cleanup_push(rwlock_unlock, (void *)&principal_conn_lock);
|
||||
if (principal_conn == conn) {
|
||||
if (conn->airplay_stream_category == ptp_stream) {
|
||||
@@ -2781,7 +2822,7 @@ void teardown(rtsp_conn_info *conn) {
|
||||
}
|
||||
|
||||
// only update these things if you're (still) the principal conn
|
||||
pthread_rwlock_rdlock(&principal_conn_lock); // don't let the principal_conn be changed
|
||||
pthread_rwlock_wrlock(&principal_conn_lock); // don't let the principal_conn be changed
|
||||
pthread_cleanup_push(rwlock_unlock, (void *)&principal_conn_lock);
|
||||
if (principal_conn == conn) {
|
||||
#ifdef CONFIG_AIRPLAY_2
|
||||
@@ -2867,7 +2908,7 @@ static void check_and_send_plist_metadata(plist_t messagePlist, const char *plis
|
||||
void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
|
||||
int err;
|
||||
debug(2, "Connection %d: SETUP (AirPlay 2)", conn->connection_number);
|
||||
debug_log_rtsp_message(2, "SETUP (AirPlay 2) SETUP incoming message", req);
|
||||
debug_log_rtsp_message(3, "SETUP (AirPlay 2) SETUP incoming message", req);
|
||||
|
||||
plist_t messagePlist = plist_from_rtsp_content(req);
|
||||
plist_t setupResponsePlist = plist_new_dict();
|
||||
@@ -2964,7 +3005,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
|
||||
0) // it should be open already, but just in case it isn't...
|
||||
die("Can not access the NQPTP service. Has it stopped running?");
|
||||
// clear_ptp_clock();
|
||||
debug_log_rtsp_message(2, "SETUP \"PTP\" message", req);
|
||||
debug_log_rtsp_message(3, "SETUP \"PTP\" message", req);
|
||||
plist_t groupUUID = plist_dict_get_item(messagePlist, "groupUUID");
|
||||
if (groupUUID) {
|
||||
char *gid = NULL;
|
||||
@@ -3043,7 +3084,8 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
|
||||
// debug(1, "Interface index %d, name: \"%s\"",if_nametoindex(iap->ifa_name),
|
||||
// iap->ifa_name);
|
||||
if ((iap->ifa_addr) && (iap->ifa_netmask) && (iap->ifa_flags & IFF_UP) &&
|
||||
((iap->ifa_flags & IFF_LOOPBACK) == 0)) {
|
||||
((iap->ifa_flags & IFF_LOOPBACK) == 0) &&
|
||||
(config.interface == NULL || (strcmp(config.interface, iap->ifa_name) == 0))) {
|
||||
char buf[INET6_ADDRSTRLEN + 1]; // +1 for a NUL
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if (iap->ifa_addr->sa_family == AF_INET6) {
|
||||
@@ -3450,7 +3492,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
|
||||
plist_free(messagePlist);
|
||||
if (clientNameString != NULL)
|
||||
free(clientNameString);
|
||||
debug_log_rtsp_message(2, " SETUP response", resp);
|
||||
debug_log_rtsp_message(3, " SETUP response", resp);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5218,7 +5260,7 @@ static void *rtsp_conversation_thread_func(void *pconn) {
|
||||
#endif
|
||||
|
||||
while (conn->stop == 0) {
|
||||
int debug_level = 2; // for printing the request and response
|
||||
int debug_level = 3; // for printing the request and response
|
||||
|
||||
// check to see if a conn has been zeroed
|
||||
|
||||
@@ -5301,7 +5343,7 @@ static void *rtsp_conversation_thread_func(void *pconn) {
|
||||
obfp += 2;
|
||||
};
|
||||
*obfp = 0;
|
||||
debug(2, "Content: \"%s\".", obf);
|
||||
debug(dl, "Content: \"%s\".", obf);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5585,7 +5627,7 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) {
|
||||
pthread_cleanup_push(malloc_cleanup, conn);
|
||||
memset(conn, 0, sizeof(rtsp_conn_info));
|
||||
conn->connection_number = RTSP_connection_index++;
|
||||
debug(2, "Connection %d is at: 0x%" PRIxPTR ".", conn->connection_number, conn);
|
||||
// debug(2, "Connection %d is at: 0x%" PRIxPTR ".", conn->connection_number, conn);
|
||||
#ifdef CONFIG_AIRPLAY_2
|
||||
conn->airplay_type = ap_2; // changed if an ANNOUNCE is received
|
||||
conn->timing_type = ts_ptp; // changed if an ANNOUNCE is received
|
||||
@@ -5600,7 +5642,7 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) {
|
||||
} else {
|
||||
size_of_reply = sizeof(SOCKADDR);
|
||||
if (getsockname(conn->fd, (struct sockaddr *)&conn->local, &size_of_reply) == 0) {
|
||||
|
||||
#ifndef COMPILE_FOR_OPENBSD
|
||||
// Thanks to https://holmeshe.me/network-essentials-setsockopt-SO_KEEPALIVE/ for this.
|
||||
|
||||
// turn on keepalive stuff -- wait for keepidle + (keepcnt * keepinttvl time) seconds
|
||||
@@ -5635,6 +5677,7 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) {
|
||||
sizeof(keepAliveInterval))) {
|
||||
debug(1, "can't set the keepidle missing count interval");
|
||||
};
|
||||
#endif
|
||||
|
||||
// initialise the connection info
|
||||
void *client_addr = NULL, *self_addr = NULL;
|
||||
@@ -5692,7 +5735,7 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) {
|
||||
} while (1);
|
||||
pthread_cleanup_pop(1); // should never happen
|
||||
} else {
|
||||
warn("could not establish a service on port %d -- program terminating. Is another instance of "
|
||||
die("could not establish a service on port %d -- program terminating. Is another instance of "
|
||||
"Shairport Sync running on this device?",
|
||||
config.port);
|
||||
}
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ getent passwd %{name} &> /dev/null || useradd --system -c "%{name} User" \
|
||||
%files
|
||||
%config(noreplace) /etc/shairport-sync.conf
|
||||
/usr/bin/shairport-sync
|
||||
/usr/share/man/man7/shairport-sync.7.gz
|
||||
/usr/share/man/man1/shairport-sync.1.gz
|
||||
%{_unitdir}/%{name}.service
|
||||
%doc README.md RELEASENOTES.md TROUBLESHOOTING.md
|
||||
%license LICENSES
|
||||
|
||||
+51
-5
@@ -291,7 +291,7 @@ void usage(char *progname) {
|
||||
printf(" -c, --configfile=FILE Read configuration settings from FILE. Default is %s.\n", configuration_file_path);
|
||||
printf(" -a, --name=NAME Set service name. Default is the hostname with first letter capitalised.\n");
|
||||
printf(" --password=PASSWORD Require PASSWORD to connect. Default is no password. (Classic AirPlay only.)\n");
|
||||
printf(" -p, --port=PORT Set RTSP listening port. Default 5000; 7000 for AirPlay 2./\n");
|
||||
printf(" -p, --port=PORT Set RTSP listening port. Default 5000; 7000 for AirPlay 2.\n");
|
||||
printf(" -L, --latency=FRAMES [Deprecated] Set the latency for audio sent from an unknown device.\n");
|
||||
printf(" The default is to set it automatically.\n");
|
||||
printf(" -S, --stuffing=MODE Set how to adjust current latency to match desired latency, where:\n");
|
||||
@@ -1207,8 +1207,8 @@ int parse_options(int argc, char **argv) {
|
||||
|
||||
} else {
|
||||
if (config_error_type(&config_file_stuff) == CONFIG_ERR_FILE_IO)
|
||||
debug(2, "Error reading configuration file \"%s\": \"%s\".",
|
||||
config_error_file(&config_file_stuff), config_error_text(&config_file_stuff));
|
||||
die("Error reading configuration file \"%s\": \"%s\".",
|
||||
config_file_real_path, config_error_text(&config_file_stuff));
|
||||
else {
|
||||
die("Line %d of the configuration file \"%s\":\n%s", config_error_line(&config_file_stuff),
|
||||
config_error_file(&config_file_stuff), config_error_text(&config_file_stuff));
|
||||
@@ -1929,6 +1929,12 @@ void _display_config(const char *filename, const int linenumber, __attribute__((
|
||||
#define display_config(argc, argv) _display_config(__FILE__, __LINE__, argc, argv)
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
#ifdef COMPILE_FOR_OPENBSD
|
||||
/* Start with the superset of all potentially required promises. */
|
||||
if (pledge("stdio rpath wpath cpath dpath inet unix dns proc exec audio", NULL) == -1)
|
||||
die("pledge: %s", strerror(errno));
|
||||
#endif
|
||||
|
||||
memset(&config, 0, sizeof(config)); // also clears all strings, BTW
|
||||
/* Check if we are called with -V or --version parameter */
|
||||
if (argc >= 2 && ((strcmp(argv[1], "-V") == 0) || (strcmp(argv[1], "--version") == 0))) {
|
||||
@@ -2102,6 +2108,16 @@ int main(int argc, char **argv) {
|
||||
// parse arguments into config -- needed to locate pid_dir
|
||||
int audio_arg = parse_options(argc, argv);
|
||||
|
||||
#ifdef COMPILE_FOR_OPENBSD
|
||||
/* Any command to be executed at runtime? */
|
||||
int run_cmds =
|
||||
config.cmd_active_start != NULL ||
|
||||
config.cmd_active_stop != NULL ||
|
||||
config.cmd_set_volume != NULL ||
|
||||
config.cmd_start != NULL ||
|
||||
config.cmd_stop != NULL;
|
||||
#endif
|
||||
|
||||
// mDNS supports maximum of 63-character names (we append 13).
|
||||
if (strlen(config.service_name) > 50) {
|
||||
warn("The service name \"%s\" is too long (max 50 characters) and has been truncated.",
|
||||
@@ -2237,6 +2253,16 @@ int main(int argc, char **argv) {
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef COMPILE_FOR_OPENBSD
|
||||
/* Past daemon(3)'s double fork(2). */
|
||||
|
||||
/* Only user-defined commands are executed. */
|
||||
if (!run_cmds)
|
||||
/* Drop "proc exec". */
|
||||
if (pledge("stdio rpath wpath cpath dpath inet unix dns audio", NULL) == -1)
|
||||
die("pledge: %s", strerror(errno));
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AIRPLAY_2
|
||||
|
||||
if (has_fltp_capable_aac_decoder() == 0) {
|
||||
@@ -2352,6 +2378,26 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
config.output->init(argc - audio_arg, argv + audio_arg);
|
||||
|
||||
#ifdef COMPILE_FOR_OPENBSD
|
||||
/* Past first and last sio_open(3), sndio(7) only needs "audio". */
|
||||
|
||||
# ifdef CONFIG_METADATA
|
||||
/* Only coverart cache is created.
|
||||
* Only metadata pipe is special. */
|
||||
if (!config.metadata_enabled)
|
||||
# endif
|
||||
{
|
||||
/* Drop "cpath dpath". */
|
||||
if (run_cmds) {
|
||||
if (pledge("stdio rpath wpath inet unix dns proc exec audio", NULL) == -1)
|
||||
die("pledge: %s", strerror(errno));
|
||||
} else {
|
||||
if (pledge("stdio rpath wpath inet unix dns audio", NULL) == -1)
|
||||
die("pledge: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// pthread_cleanup_push(main_cleanup_handler, NULL);
|
||||
|
||||
// daemon_log(LOG_NOTICE, "startup");
|
||||
@@ -2429,7 +2475,7 @@ int main(int argc, char **argv) {
|
||||
debug(1, "allow a session to be interrupted: %d.", config.allow_session_interruption);
|
||||
debug(1, "busy timeout time is %d.", config.timeout);
|
||||
debug(1, "drift tolerance is %f seconds.", config.tolerance);
|
||||
debug(1, "password is \"%s\".", strnull(config.password));
|
||||
debug(1, "password is %s.", config.password == NULL ? "not set" : "set (omitted)");
|
||||
debug(1, "default airplay volume is: %.6f.", config.default_airplay_volume);
|
||||
debug(1, "high threshold airplay volume is: %.6f.", config.high_threshold_airplay_volume);
|
||||
if (config.limit_to_high_volume_threshold_time_in_minutes == 0)
|
||||
@@ -2491,7 +2537,7 @@ int main(int argc, char **argv) {
|
||||
#ifdef CONFIG_METADATA
|
||||
debug(1, "metadata enabled is %d.", config.metadata_enabled);
|
||||
debug(1, "metadata pipename is \"%s\".", config.metadata_pipename);
|
||||
debug(1, "metadata socket address is \"%s\" port %d.", config.metadata_sockaddr,
|
||||
debug(1, "metadata socket address is \"%s\" port %d.", strnull(config.metadata_sockaddr),
|
||||
config.metadata_sockport);
|
||||
debug(1, "metadata socket packet size is \"%d\".", config.metadata_sockmsglength);
|
||||
debug(1, "get-coverart is %d.", config.get_coverart);
|
||||
|
||||
Reference in New Issue
Block a user