Version 2.8.1 preliminary
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
Installing to Cygwin
|
||||
----
|
||||
|
||||
This is based on installing onto a fresh default installation of Cygwin 2.4.1 (64-bit installation) running in Windows 10
|
||||
inside VMWare Fusion on a Mac.
|
||||
|
||||
Note: to enable Cygwin to resolve domain names, it was found necessary to go to Windows `Control Panel` > `Network and Internet` > `Network Connections` >
|
||||
`Ethernet0` > `Properties` > `Internet Protocol Version 4 (TCP/IPv4)` > `Properties`, select `Use the following DNS server addresses`
|
||||
and enter valid DNS server addresses, e.g. `8.8.8.8`.
|
||||
|
||||
* Use `Cygwin Setup` to install the following packages:
|
||||
* `pkg-config`
|
||||
* `autoconf`
|
||||
* `automake`
|
||||
* `clang`
|
||||
* `libdaemon-devel`
|
||||
* `popt-devel`
|
||||
* `make`
|
||||
* `libao-devel`
|
||||
* `openssl-devel`
|
||||
* `libtool`
|
||||
* `git`
|
||||
* `wget` for convenience,
|
||||
* `flex` for compiling `libconfig`
|
||||
* `bison` for compiling `libconfig`
|
||||
|
||||
* Download, configure, compile and install `libconfig`.
|
||||
|
||||
For this, if your home directory name has a space in it, which happens by default with Cygwin, you should
|
||||
temporarily rename it to a single word, e.g. `Mike Brady` -> `mike`. This is to allow the `make install` script to work properly.
|
||||
```
|
||||
$ wget http://www.hyperrealm.com/libconfig/libconfig-1.5.tar.gz
|
||||
$ tar -xvzf libconfig-1.5.tar.gz
|
||||
$ cd libconfig-1.5
|
||||
$ ./configure
|
||||
$ make
|
||||
$ make install
|
||||
```
|
||||
* Next, download, configure and compile Shairport Sync:
|
||||
```
|
||||
$ git clone https://github.com/mikebrady/shairport-sync.git
|
||||
$ cd shairport-sync
|
||||
$ autoreconf -fi
|
||||
$ PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./configure --with-ao --with-ssl=openssl
|
||||
$ make
|
||||
```
|
||||
* That's it. There should be a `shairport-sync.exe` file in your directory.
|
||||
@@ -1,6 +1,6 @@
|
||||
Shairport Sync
|
||||
=============
|
||||
Shairport Sync is an AirPlay audio player — it plays audio streamed from iTunes, iOS devices and other AirPlay sources such as Quicktime Player, ForkedDaapd among others.
|
||||
Shairport Sync is an AirPlay audio player — it plays audio streamed from iTunes, iOS devices and other AirPlay sources such as Quicktime Player and ForkedDaapd, among others.
|
||||
Audio played by a Shairport Sync-powered device stays synchronised with the source and hence with similar devices playing the same source. In this way, synchronised multi-room audio is possible without difficulty. (Hence the name Shairport Sync, BTW.)
|
||||
|
||||
Shairport Sync does not support AirPlay video or photo streaming.
|
||||
@@ -13,11 +13,11 @@ Shairport Sync works by using timing information and timestamps present in data
|
||||
|
||||
The latency to be used is specified by the source when it negotiates with Shairport Sync. Most sources set a latency of 88,200 frames — exactly two seconds. Recent versions of iTunes and forkedDaapd use a latency of 99,577 frames.
|
||||
|
||||
Timestamps are measured relative to the source computer's clock – the `source clock`, but timing must be done relative to the clock of the computer running Shairport Sync – the `local clock`. The source and local clocks are synchronised by Shairport Sync, usually to within a fraction of a millisecond, using a variant of NTP synchronisation protocols.
|
||||
Timestamps are measured relative to the source computer's clock – the `source clock`, but timing must be done relative to the clock of the computer running Shairport Sync – the `local clock`. This is done by synchronising the two clocks, usually to within a fraction of a millisecond, using a variant of NTP synchronisation protocols.
|
||||
|
||||
To maintain the exact latency required, if an output device is running slow relative to the source, Shairport Sync will delete frames of audio to allow the device to keep up. If the output device is running fast, Shairport Sync will insert frames to keep time. The number of frames inserted or deleted is so small as to be almost inaudible on normal audio material. Frames are inserted or deleted as necessary at pseudorandom intervals. Alternatively, with `libsoxr` support, Shairport Sync can resample the audio feed to ensure the output device can keep up. This is less obtrusive than insertion and deletion but requires a good deal of processing power — most embedded devices probably can't support it. The process of insertion/deletion or resampling is rather inelegantly called “stuffing”.
|
||||
|
||||
Shairport Sync is a pretty substantial rewrite of the fantastic work done in Shairport 1.0 by James Laird and others — please see https://github.com/abrasive/shairport/blob/master/README.md#contributors-to-version-1x for a list of the contributors to Shairport 1.x and Shairport 0.x. From a "heritage" point of view, Shairport Sync is a fork of Shairport 1.0.
|
||||
Shairport Sync is a substantial rewrite of the fantastic work done in Shairport 1.0 by James Laird and others — please see https://github.com/abrasive/shairport/blob/master/README.md#contributors-to-version-1x for a list of the contributors to Shairport 1.x and Shairport 0.x. From a "heritage" point of view, Shairport Sync is a fork of Shairport 1.0.
|
||||
|
||||
Shairport Sync is mainly designed for `alsa` and thus for Linux, although since `alsa` has been ported to FreeBSD, Shairport Sync runs in FreeBSD too. It must have direct access to the output device, which must be a real sound card capable of working with 44,100 samples per second interleaved PCM stereo (you'll get a message in the logfile if there's a problem).
|
||||
|
||||
@@ -51,7 +51,7 @@ Note: Historically, Shairport Sync has taken its settings from command line argu
|
||||
|
||||
Building And Installing
|
||||
---------------------
|
||||
If you wish to install Shairport Sync on OpenWrt, Arch or Fedora platforms, please follow the appropriate instructions below. Limited support is also available for MAc OS X. Otherwise follow the General Build Instructions. Then, when the program has been installed, refer to the section on Configuring Shairport Sync that follows.
|
||||
If you wish to install Shairport Sync on OpenWrt, Arch or Fedora platforms, please follow the appropriate instructions below. Limited support is also available for Mac OS X. Otherwise follow the General Build Instructions. Then, when the program has been installed, refer to the section on Configuring Shairport Sync that follows.
|
||||
|
||||
**Note**
|
||||
|
||||
@@ -61,9 +61,6 @@ The following procedures will install the shairport-sync application into your s
|
||||
**Ubuntu:**
|
||||
Personal Package Archives for Shairport Sync master and development branches are available at https://launchpad.net/~dantheperson. A `shairport-sync` installer package is available in Ubuntu 16.04, currently in its alpha phase.
|
||||
|
||||
**Debian:**
|
||||
`shairport-sync` is in the Debian archive and is scheduled for release with Debian Stretch (9): https://tracker.debian.org/shairport-sync. A backport for Debian Jessie (8) may be provided given enough demand.
|
||||
|
||||
**OpenWrt:**
|
||||
There is a Shairport Sync package in OpenWrt `trunk`. Also, there's an OpenWrt package at https://github.com/mikebrady/shairport-sync-for-openwrt, including one that builds back to `Barrier Breaker`.
|
||||
|
||||
@@ -99,6 +96,9 @@ You may have to manually create the directory `/var/shairport-sync` for the inst
|
||||
```
|
||||
Sincere thanks to all package contributors!
|
||||
|
||||
**Cygwin**
|
||||
Please see the guide at [CYGWIN.md](https://github.com/mikebrady/shairport-sync/blob/master/CYGWIN.md).
|
||||
|
||||
**General Build Instructions**
|
||||
|
||||
To build Shairport Sync from sources on Debian, Ubuntu, Raspbian, etc. follow these instructions.
|
||||
@@ -126,7 +126,7 @@ Debian, Ubuntu and Raspbian users can get the basics with:
|
||||
- `apt-get install avahi-daemon libavahi-client-dev` if you want to use Avahi (recommended).
|
||||
- `apt-get install libssl-dev` if you want to use OpenSSL and libcrypto, or use PolarSSL otherwise.
|
||||
- `apt-get install libpolarssl-dev` if you want to use PolarSSL, or use OpenSSL/libcrypto otherwise.
|
||||
- `apt-get install libsoxr-dev` if you want support for libsoxr-based resampling. This library is in many recent distributions, including Jessie and Raspioan Jessie; if not, instructions for how to build it from source for Raspian/Debian Wheezy are available at [LIBSOXR.md](https://github.com/mikebrady/shairport-sync/blob/development/LIBSOXR.md).
|
||||
- `apt-get install libsoxr-dev` if you want support for libsoxr-based resampling. This library is in many recent distributions, including Jessie and Raspioan Jessie; if not, instructions for how to build it from source for Raspian/Debian Wheezy are available at [LIBSOXR.md](https://github.com/mikebrady/shairport-sync/blob/master/LIBSOXR.md).
|
||||
|
||||
Download Shairport Sync:
|
||||
|
||||
@@ -424,3 +424,8 @@ For reference, a drift of one second per day is approximately 11.57 ppm. Left un
|
||||
It's not unusual to have resend requests, late packets and even missing packets if some part of the connection to the Shairport Sync device is over WiFi. Late packets can sometimes be asked for and received multiple times. Sometimes late packets are sent and arrive too late, but have already been sent and received in time, so weren't needed anyway...
|
||||
|
||||
"Min DAC queue size" is the minimum size the queue of samples in the output device's hardware buffer was measured at. It is meant to stand at 0.15 seconds = 6,615 samples, and will go low if the processor is very busy. If it goes below about 2,000 then it's a sign that the processor can't really keep up.
|
||||
|
||||
Troubleshooting
|
||||
---------------
|
||||
Please refer to [TROUBLESHOOTING](https://github.com/mikebrady/shairport-sync/blob/master/TROUBLESHOOTING.md) for a few hints, contributed by users.
|
||||
|
||||
|
||||
+21
-2
@@ -1,3 +1,22 @@
|
||||
Version 2.8.1 – Stable Version
|
||||
----
|
||||
Version 2.8.1 is derived from development version 2.9.2 and contains important bug fixes.
|
||||
|
||||
For full details, please refer to the release notes here, back as far as 2.9.1.
|
||||
|
||||
Version 2.9.2 – Development Version
|
||||
----
|
||||
Version 2.9.2 focusses on further bug fixes and stability improvements.
|
||||
* Enhanced stability: an important bug has been fixed in the handling of missing audio frames – i.e. what happens when a frame of audio is truly missing, after all attempts to fetch it have been unsuccessful. The bug would cause Shairport Sync to do an unnecessary resynchronisation, or, if resync was turned off, to jump out of sync. This is a long-standing bug – thanks to [Jörg Krause](https://github.com/joerg-krause) for identifying it.
|
||||
* An extra diagnostic has been added which gives the mean, standard deviation and maximum values for inter-packet reception time on the audio port. It may be useful for exploring line quality.
|
||||
|
||||
Version 2.9.1 – Development Version
|
||||
----
|
||||
Version 2.9.1 focusses on bug fixes and stability improvements.
|
||||
* Stability improvements are concentrated on what happens when a play sessions ends and is followed immediately by a new session. This happens in iOS 9.2 when you click to the next track or to the previous track. It also happens playing YouTube videos when a Mac's System Audio is routed through AirPlay. Thanks to [Tim Curtis](https://github.com/moodeaudio) for help with these issues.
|
||||
* A workaround for an apparent flushing issue in TuneBlade has been included. Thanks to [gibman](https://github.com/gibman) for reporting this issue.
|
||||
* A number of bug fixes have been made to `configure.ac` – thanks to [Jörg Krause](https://github.com/joerg-krause).
|
||||
|
||||
Version 2.8 – Stable Version
|
||||
----
|
||||
Version 2.8 is derived from version 2.7.10 with slight documentation updates. Here is a summary of changes between the last stable version – 2.6 – and this version. For full details, refer to the release notes here, back as far as 2.7.
|
||||
@@ -17,8 +36,8 @@ Version 2.8 is derived from version 2.7.10 with slight documentation updates. He
|
||||
* Disabled picture sending if pictures haven’t been asked for.
|
||||
|
||||
**Bug fixes**
|
||||
* Fixed a bug that prevented Shairport Sync from correctly setting the hardware mixer volume if it had been altered externally.
|
||||
* Modified the shutdown behaviour so that a shutdown followed immediately by a play request is handled properly. This was causing iOS 9.2 sometimes to drop the Airplay link between tunes.
|
||||
* Fixed a bug that prevented Shairport Sync from correctly setting the hardware mixer volume if it had been altered externally. Thanks to [Tim Curtis](https://github.com/moodeaudio) for help with these issues.
|
||||
* Modified the shutdown behaviour so that a shutdown followed immediately by a play request is handled better. This was causing iOS 9.2 sometimes to drop the Airplay link between tunes.
|
||||
* Fixed a data-alignment bug that would cause a crash in certain circumstances on ARM processors with metadata enabled.
|
||||
* Corrected the names for a few settings tags.
|
||||
* Fixed some typos and misspellings.
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
Troubleshooting
|
||||
-----
|
||||
The installation and setup of Shairport Sync is straightforward on recent Linux distributions. Issues can occasionally arise caused by problems elsewhere in the system, typically WiFi reception and/or the WiFi adapter settings, the network, the router, firewall settings or some more esoteric audio interfaces.
|
||||
|
||||
In this brief document will be listed some problems and some solutions.
|
||||
|
||||
Before starting, ensure that your software is up-to-date.
|
||||
|
||||
### WiFi adapter running in power-saving / low-power mode
|
||||
|
||||
**Problem**
|
||||
|
||||
Shairport Sync is installed and running, but sometimes it disappears from the network, and sometimes it suffers from long dropouts.
|
||||
|
||||
**Possible Cause**
|
||||
|
||||
This can be caused by lots of things, but one of them is that the WiFi adapter may be set to run in a low-power or power-saving mode. If it's not busy, then after a while it goes into a low-power mode. This is bad as the device needs to be always connected to the network to provide the AirPlay service. You need to turn off power-saving mode. How you do this varies with platform and with WiFi adapter – internet search is your friend. Here, for instance, is the command for the C.H.I.P. from Next Thing Co, which has built in WiFi and Linux and has the `iw` command installed:
|
||||
|
||||
```
|
||||
iw dev wlan0 set power_save off
|
||||
```
|
||||
|
||||
There are some more details in some the closed issues on this repository.
|
||||
|
||||
### UFW firewall blocking connections on Raspbian (Raspberry Pi)
|
||||
|
||||
**Problem**
|
||||
|
||||
You have installed Shairport Sync successfully, the deamon is running, you can see it from your remote terminal but you are unable to play a song.
|
||||
|
||||
**Before you change anything to your configuration**
|
||||
|
||||
- Type the following command:
|
||||
|
||||
`sudo ufw disable`
|
||||
|
||||
- Try to launch a song from your remote device on the Shairport-sync one, if this works, proceed to the next step and follow the ones described below, in the solution section.
|
||||
|
||||
- Enable UFW through the following command:
|
||||
|
||||
`sudo ufw enable`
|
||||
|
||||
**Solution**
|
||||
|
||||
You have to allow connections to your Pi from remote devices. To do so, after re-enabling UFW (see last step of the previous section), enter the following commands in shell:
|
||||
|
||||
```
|
||||
sudo ufw allow from 192.168.1.1/16 to any port 3689 proto tcp
|
||||
sudo ufw allow from 192.168.1.1/16 to any port 5353
|
||||
sudo ufw allow from 192.168.1.1/16 to any port 5000:5005 proto tcp
|
||||
sudo ufw allow from 192.168.1.1/16 to any port 6000:6005 proto udp
|
||||
sudo ufw allow from 192.168.1.1/16 to any port 35000:65535 proto udp
|
||||
```
|
||||
|
||||
You may have to change the IP adresses range depending on your own local network settings.
|
||||
|
||||
You can check UFW config by typing `sudo ufw status` in shell. Please make sure that UFW is active, especially if you have deactivated it previously for testing purpose.
|
||||
|
||||
Run your song from your remote device. Enjoy !
|
||||
|
||||
### Stuttering audio on certain USB DACs (such as the Creative Soundblaster MP3+)
|
||||
|
||||
**Problem**
|
||||
When using a USB DAC on a Raspberry Pi audio plays fine through other methods (such as through mpd, mopidy, mplayer or aplay) but when streamed to Shairport Sync regular dropouts or stutters are heard.
|
||||
|
||||
**Possible Cause**
|
||||
There is a suspicion (although this is not 100% confirmed) that this is a fun latency/timing issue related to a combination of
|
||||
- The Raspberry Pi's ethernet itself being a USB device resulting in shared bandwidth/interrupts with USB DACs
|
||||
- Shairport Sync continually checking the latency of the USB DAC to maintain synchronisation of audio
|
||||
- Quirky USB DACs (already known to be problematic on the Raspberry Pi more info available [here](https://www.raspberrypi.org/documentation/hardware/raspberrypi/usb/README.md#knownissues)
|
||||
For more discussion on this issue see [issue 167](https://github.com/mikebrady/shairport-sync/issues/167) or read on for the quick fix!
|
||||
|
||||
**Possible Solution**
|
||||
To get nice smooth audio first check the details of your USB DAC by either using 'aplay -l' which will give you output something like this:
|
||||
````
|
||||
**** List of PLAYBACK Hardware Devices ****
|
||||
card 0: ALSA [bcm2835 ALSA], device 0: bcm2835 ALSA [bcm2835 ALSA]
|
||||
Subdevices: 8/8
|
||||
Subdevice #0: subdevice #0
|
||||
Subdevice #1: subdevice #1
|
||||
Subdevice #2: subdevice #2
|
||||
Subdevice #3: subdevice #3
|
||||
Subdevice #4: subdevice #4
|
||||
Subdevice #5: subdevice #5
|
||||
Subdevice #6: subdevice #6
|
||||
Subdevice #7: subdevice #7
|
||||
card 0: ALSA [bcm2835 ALSA], device 1: bcm2835 ALSA [bcm2835 IEC958/HDMI]
|
||||
Subdevices: 1/1
|
||||
Subdevice #0: subdevice #0
|
||||
card 1: MP3 [Sound Blaster MP3+], device 0: USB Audio [USB Audio]
|
||||
Subdevices: 0/1
|
||||
Subdevice #0: subdevice #0
|
||||
````
|
||||
|
||||
or look at your exisiting '/etc/asound.conf' file, which may look something like this
|
||||
|
||||
````
|
||||
pcm.!default {
|
||||
type hw
|
||||
card 1
|
||||
}
|
||||
ctl.!default {
|
||||
type hw
|
||||
card 1
|
||||
}
|
||||
````
|
||||
The important information you want is the card number which in this case is 1.
|
||||
|
||||
Now modify your 'etc/asound.conf' file (or create one if it doesn't exist) using the following template substituting the 'pcm "hw:1"' and 'card 1' sections with the card number of your device
|
||||
|
||||
````
|
||||
pcm.!default {
|
||||
type plug
|
||||
slave.pcm {
|
||||
type dmix
|
||||
ipc_key 1024
|
||||
slave {
|
||||
pcm "hw:1"
|
||||
rate 48000 # this line is only needed for USB DACs which only support 48khz
|
||||
period_time 0
|
||||
period_size 1920
|
||||
buffer_size 19200
|
||||
}
|
||||
}
|
||||
}
|
||||
ctl.!default {
|
||||
type hw
|
||||
card 1
|
||||
}
|
||||
````
|
||||
This sets the default alsa audio device to be the USB DAC via a dmixer plugin (which can be used by multiple applications at once) using a modified period and buffer size and optionally mix to 48khz.
|
||||
|
||||
This will then be used by default by Shairport-Sync and any other applications using alsa.
|
||||
|
||||
Note that some distributions (such as Volumio 2) don't use an asound.conf file by default, they instead specificy the hardware details directly in '/etc/mpd.conf' files so some more in-depth modification is needed to override this.
|
||||
|
||||
(Note: not tested by Mike B.)
|
||||
@@ -710,6 +710,7 @@ void alac_decode_frame(alac_file *alac,
|
||||
unsigned char *inbuffer,
|
||||
void *outbuffer, int *outputsize)
|
||||
{
|
||||
int outbuffer_allocation_size = *outputsize; // initial value
|
||||
int channels;
|
||||
int32_t outputsamples = alac->setinfo_max_samples_per_frame;
|
||||
|
||||
@@ -720,6 +721,11 @@ void alac_decode_frame(alac_file *alac,
|
||||
channels = readbits(alac, 3);
|
||||
|
||||
*outputsize = outputsamples * alac->bytespersample;
|
||||
if (*outputsize>outbuffer_allocation_size) {
|
||||
fprintf(stderr, "FIXME: Not enough space if the output buffer for audio frame - E1.\n");
|
||||
*outputsize = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
switch(channels)
|
||||
{
|
||||
@@ -751,6 +757,12 @@ void alac_decode_frame(alac_file *alac,
|
||||
* as a 32bit integer */
|
||||
outputsamples = readbits(alac, 32);
|
||||
*outputsize = outputsamples * alac->bytespersample;
|
||||
if (*outputsize>outbuffer_allocation_size) {
|
||||
fprintf(stderr, "FIXME: Not enough space if the output buffer for audio frame - E2.\n");
|
||||
*outputsize = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
readsamplesize = alac->setinfo_sample_size - (uncompressed_bytes * 8);
|
||||
@@ -928,6 +940,11 @@ void alac_decode_frame(alac_file *alac,
|
||||
* as a 32bit integer */
|
||||
outputsamples = readbits(alac, 32);
|
||||
*outputsize = outputsamples * alac->bytespersample;
|
||||
if (*outputsize>outbuffer_allocation_size) {
|
||||
fprintf(stderr, "FIXME: Not enough space if the output buffer for audio frame - E3.\n");
|
||||
*outputsize = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
readsamplesize = alac->setinfo_sample_size - (uncompressed_bytes * 8) + 1;
|
||||
|
||||
+82
-65
@@ -97,7 +97,7 @@ static void help(void) {
|
||||
|
||||
int open_mixer() {
|
||||
if (hardware_mixer) {
|
||||
debug(3, "Open Mixer");
|
||||
debug(2, "Open Mixer");
|
||||
int ret = 0;
|
||||
snd_mixer_selem_id_alloca(&alsa_mix_sid);
|
||||
snd_mixer_selem_id_set_index(alsa_mix_sid, alsa_mix_index);
|
||||
@@ -122,7 +122,8 @@ int open_mixer() {
|
||||
}
|
||||
|
||||
static int init(int argc, char **argv) {
|
||||
// debug(1,"audio_alsa init called.");
|
||||
pthread_mutex_lock(&alsa_mutex);
|
||||
// debug(2,"audio_alsa init called.");
|
||||
const char *str;
|
||||
int value;
|
||||
|
||||
@@ -221,75 +222,78 @@ static int init(int argc, char **argv) {
|
||||
|
||||
debug(1, "Output device name is \"%s\".", alsa_out_dev);
|
||||
|
||||
if (!hardware_mixer)
|
||||
return 0;
|
||||
if (hardware_mixer) {
|
||||
|
||||
if (alsa_mix_dev == NULL)
|
||||
alsa_mix_dev = alsa_out_dev;
|
||||
|
||||
if (alsa_mix_dev == NULL)
|
||||
alsa_mix_dev = alsa_out_dev;
|
||||
// Open mixer
|
||||
|
||||
// Open mixer
|
||||
open_mixer();
|
||||
|
||||
open_mixer();
|
||||
if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv,
|
||||
&alsa_mix_maxv) < 0)
|
||||
debug(1, "Can't read mixer's [linear] min and max volumes.");
|
||||
else {
|
||||
if (snd_mixer_selem_get_playback_dB_range(alsa_mix_elem, &alsa_mix_mindb,
|
||||
&alsa_mix_maxdb) == 0) {
|
||||
|
||||
if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv,
|
||||
&alsa_mix_maxv) < 0)
|
||||
debug(1, "Can't read mixer's [linear] min and max volumes.");
|
||||
else {
|
||||
if (snd_mixer_selem_get_playback_dB_range(alsa_mix_elem, &alsa_mix_mindb,
|
||||
&alsa_mix_maxdb) == 0) {
|
||||
|
||||
audio_alsa.volume =
|
||||
&volume; // insert the volume function now we know it can do dB stuff
|
||||
audio_alsa.parameters = ¶meters; // likewise the parameters stuff
|
||||
if (alsa_mix_mindb == SND_CTL_TLV_DB_GAIN_MUTE) {
|
||||
// Raspberry Pi does this
|
||||
debug(1, "Lowest dB value is a mute.");
|
||||
if (snd_mixer_selem_ask_playback_vol_dB(
|
||||
alsa_mix_elem, alsa_mix_minv + 1, &alsa_mix_mindb) == 0)
|
||||
debug(1, "Can't get dB value corresponding to a \"volume\" of 1.");
|
||||
audio_alsa.volume =
|
||||
&volume; // insert the volume function now we know it can do dB stuff
|
||||
audio_alsa.parameters = ¶meters; // likewise the parameters stuff
|
||||
if (alsa_mix_mindb == SND_CTL_TLV_DB_GAIN_MUTE) {
|
||||
// Raspberry Pi does this
|
||||
debug(1, "Lowest dB value is a mute.");
|
||||
if (snd_mixer_selem_ask_playback_vol_dB(
|
||||
alsa_mix_elem, alsa_mix_minv + 1, &alsa_mix_mindb) == 0)
|
||||
debug(1, "Can't get dB value corresponding to a \"volume\" of 1.");
|
||||
}
|
||||
debug(1, "Hardware mixer has dB volume from %f to %f.",
|
||||
(1.0 * alsa_mix_mindb) / 100.0, (1.0 * alsa_mix_maxdb) / 100.0);
|
||||
} else {
|
||||
// use the linear scale and do the db conversion ourselves
|
||||
debug(1, "note: the hardware mixer specified -- \"%s\" -- does not have "
|
||||
"a dB volume scale, so it can't be used.",
|
||||
alsa_mix_ctrl);
|
||||
/*
|
||||
debug(1, "Min and max volumes are %d and
|
||||
%d.",alsa_mix_minv,alsa_mix_maxv);
|
||||
alsa_mix_maxdb = 0;
|
||||
if ((alsa_mix_maxv!=0) && (alsa_mix_minv!=0))
|
||||
alsa_mix_mindb =
|
||||
-20*100*(log10(alsa_mix_maxv*1.0)-log10(alsa_mix_minv*1.0));
|
||||
else if (alsa_mix_maxv!=0)
|
||||
alsa_mix_mindb = -20*100*log10(alsa_mix_maxv*1.0);
|
||||
audio_alsa.volume = &linear_volume; // insert the linear volume function
|
||||
audio_alsa.parameters = ¶meters; // likewise the parameters stuff
|
||||
debug(1,"Max and min dB calculated are %d and
|
||||
%d.",alsa_mix_maxdb,alsa_mix_mindb);
|
||||
*/
|
||||
}
|
||||
debug(1, "Hardware mixer has dB volume from %f to %f.",
|
||||
(1.0 * alsa_mix_mindb) / 100.0, (1.0 * alsa_mix_maxdb) / 100.0);
|
||||
} else {
|
||||
// use the linear scale and do the db conversion ourselves
|
||||
debug(1, "note: the hardware mixer specified -- \"%s\" -- does not have "
|
||||
"a dB volume scale, so it can't be used.",
|
||||
alsa_mix_ctrl);
|
||||
/*
|
||||
debug(1, "Min and max volumes are %d and
|
||||
%d.",alsa_mix_minv,alsa_mix_maxv);
|
||||
alsa_mix_maxdb = 0;
|
||||
if ((alsa_mix_maxv!=0) && (alsa_mix_minv!=0))
|
||||
alsa_mix_mindb =
|
||||
-20*100*(log10(alsa_mix_maxv*1.0)-log10(alsa_mix_minv*1.0));
|
||||
else if (alsa_mix_maxv!=0)
|
||||
alsa_mix_mindb = -20*100*log10(alsa_mix_maxv*1.0);
|
||||
audio_alsa.volume = &linear_volume; // insert the linear volume function
|
||||
audio_alsa.parameters = ¶meters; // likewise the parameters stuff
|
||||
debug(1,"Max and min dB calculated are %d and
|
||||
%d.",alsa_mix_maxdb,alsa_mix_mindb);
|
||||
*/
|
||||
}
|
||||
}
|
||||
if (snd_mixer_selem_has_playback_switch(alsa_mix_elem)) {
|
||||
audio_alsa.mute =
|
||||
&mute; // insert the mute function now we know it can do muting stuff
|
||||
debug(1, "Has mute ability.");
|
||||
}
|
||||
if (snd_mixer_selem_has_playback_switch(alsa_mix_elem)) {
|
||||
audio_alsa.mute =
|
||||
&mute; // insert the mute function now we know it can do muting stuff
|
||||
debug(1, "Has mute ability.");
|
||||
}
|
||||
|
||||
snd_mixer_close(alsa_mix_handle);
|
||||
snd_mixer_close(alsa_mix_handle);
|
||||
}
|
||||
alsa_mix_handle = NULL;
|
||||
pthread_mutex_unlock(&alsa_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void deinit(void) {
|
||||
// debug(2,"audio_alsa deinit called.");
|
||||
stop();
|
||||
if (alsa_mix_handle) {
|
||||
if (hardware_mixer && alsa_mix_handle) {
|
||||
snd_mixer_close(alsa_mix_handle);
|
||||
}
|
||||
}
|
||||
|
||||
int open_alsa_device(void) {
|
||||
|
||||
const snd_pcm_uframes_t minimal_buffer_headroom =
|
||||
352 * 2; // we accept this much headroom in the hardware buffer, but we'll
|
||||
// accept less
|
||||
@@ -392,12 +396,14 @@ int open_alsa_device(void) {
|
||||
}
|
||||
|
||||
static void start(int sample_rate) {
|
||||
// debug(2,"audio_alsa start called.");
|
||||
if (sample_rate != 44100)
|
||||
die("Unexpected sample rate %d -- only 44,100 supported!", sample_rate);
|
||||
desired_sample_rate = sample_rate; // must be a variable
|
||||
}
|
||||
|
||||
static uint32_t delay() {
|
||||
// debug(3,"audio_alsa delay called.");
|
||||
if (alsa_handle == NULL) {
|
||||
return 0;
|
||||
} else {
|
||||
@@ -435,10 +441,13 @@ static uint32_t delay() {
|
||||
}
|
||||
|
||||
static void play(short buf[], int samples) {
|
||||
// debug(3,"audio_alsa play called.");
|
||||
int ret = 0;
|
||||
if (alsa_handle == NULL) {
|
||||
pthread_mutex_lock(&alsa_mutex);
|
||||
ret = open_alsa_device();
|
||||
open_mixer();
|
||||
pthread_mutex_unlock(&alsa_mutex);
|
||||
if ((ret == 0) && (audio_alsa.volume))
|
||||
audio_alsa.volume(set_volume);
|
||||
}
|
||||
@@ -467,8 +476,10 @@ static void play(short buf[], int samples) {
|
||||
}
|
||||
|
||||
static void flush(void) {
|
||||
// debug(2,"audio_alsa flush called.");
|
||||
pthread_mutex_lock(&alsa_mutex);
|
||||
int derr;
|
||||
if (alsa_mix_handle) {
|
||||
if (hardware_mixer && alsa_mix_handle) {
|
||||
snd_mixer_close(alsa_mix_handle);
|
||||
alsa_mix_handle = NULL;
|
||||
}
|
||||
@@ -495,16 +506,17 @@ static void flush(void) {
|
||||
snd_pcm_close(alsa_handle);
|
||||
alsa_handle = NULL;
|
||||
}
|
||||
pthread_mutex_unlock(&alsa_mutex);
|
||||
}
|
||||
|
||||
static void stop(void) {
|
||||
if (alsa_handle != 0)
|
||||
// when we want to stop, we want the alsa device
|
||||
// to be closed immediately -- we may even be killing the thread, so we
|
||||
// don't wish to wait
|
||||
// so we should flush first
|
||||
flush(); // flush will also close the device
|
||||
// close_alsa_device();
|
||||
// debug(2,"audio_alsa stop called.");
|
||||
// when we want to stop, we want the alsa device
|
||||
// to be closed immediately -- we may even be killing the thread, so we
|
||||
// don't wish to wait
|
||||
// so we should flush first
|
||||
flush(); // flush will also close the device
|
||||
// close_alsa_device();
|
||||
}
|
||||
|
||||
static void parameters(audio_parameters *info) {
|
||||
@@ -513,9 +525,10 @@ static void parameters(audio_parameters *info) {
|
||||
}
|
||||
|
||||
static void volume(double vol) {
|
||||
debug(2, "Setting volume db to %f.", vol);
|
||||
pthread_mutex_lock(&alsa_mutex);
|
||||
debug(2, "Setting volume db to %f.", vol);
|
||||
set_volume = vol;
|
||||
if (alsa_mix_handle) {
|
||||
if (hardware_mixer && alsa_mix_handle) {
|
||||
if (snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, vol, 0) != 0) {
|
||||
debug(1, "Can't set playback volume accurately to %f dB.", vol);
|
||||
if (snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, vol, -1) != 0)
|
||||
@@ -523,12 +536,13 @@ static void volume(double vol) {
|
||||
die("Failed to set playback dB volume");
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&alsa_mutex);
|
||||
}
|
||||
|
||||
static void linear_volume(double vol) {
|
||||
debug(2, "Setting linear volume to %f.", vol);
|
||||
set_volume = vol;
|
||||
if (alsa_mix_handle) {
|
||||
if (hardware_mixer && alsa_mix_handle) {
|
||||
double linear_volume = pow(10, vol);
|
||||
// debug(1,"Linear volume is %f.",linear_volume);
|
||||
long int_vol =
|
||||
@@ -542,7 +556,9 @@ static void linear_volume(double vol) {
|
||||
}
|
||||
|
||||
static void mute(int do_mute) {
|
||||
if (alsa_mix_handle) {
|
||||
pthread_mutex_lock(&alsa_mutex);
|
||||
// debug(2,"audio_alsa mute called.");
|
||||
if (hardware_mixer && alsa_mix_handle) {
|
||||
if (do_mute) {
|
||||
// debug(1,"Mute");
|
||||
snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, 0);
|
||||
@@ -551,4 +567,5 @@ static void mute(int do_mute) {
|
||||
snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, 1);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&alsa_mutex);
|
||||
}
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ([2.50])
|
||||
AC_INIT([shairport-sync], [2.8.0], [mikebrady@eircom.net])
|
||||
AC_INIT([shairport-sync], [2.8.1], [mikebrady@eircom.net])
|
||||
AM_INIT_AUTOMAKE
|
||||
AC_CONFIG_SRCDIR([shairport.c])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
@@ -114,7 +114,7 @@ AC_ARG_WITH(ssl, [ choose --with-ssl=openssl or --with-ssl=polarssl for encrypti
|
||||
AC_MSG_ERROR(choose either "openssl" or "polarssl" encryption)
|
||||
fi
|
||||
if test "x${with_ssl}" = xopenssl ; then
|
||||
if test "x${with_pkg_config}" = x1 ; then
|
||||
if test "x${with_pkg_config}" = xyes ; then
|
||||
PKG_CHECK_MODULES(
|
||||
[SSL], [libssl,libcrypto],
|
||||
[LIBS="${SSL_LIBS} ${LIBS}"
|
||||
@@ -169,7 +169,7 @@ AC_ARG_WITH(alsa, [ --with-alsa = choose ALSA API support (GNU/Linux only)],
|
||||
HAS_ALSA=1
|
||||
AM_CONDITIONAL([USE_ALSA], [test 0])
|
||||
AC_DEFINE([CONFIG_ALSA], 1, [Needed by the compiler.])
|
||||
if test "x${with_pkg_config}" = x1 ; then
|
||||
if test "x${with_pkg_config}" = xyes ; then
|
||||
PKG_CHECK_MODULES(
|
||||
[ALSA], [alsa],
|
||||
[LIBS="${ALSA_LIBS} ${LIBS}"
|
||||
|
||||
@@ -221,11 +221,12 @@ static inline int seq32_order(uint32_t a, uint32_t b) {
|
||||
return (C & 0x80000000) == 0;
|
||||
}
|
||||
|
||||
static void alac_decode(short *dest, uint8_t *buf, int len) {
|
||||
static int alac_decode(short *dest, uint8_t *buf, int len) {
|
||||
unsigned char packet[MAX_PACKET];
|
||||
unsigned char packetp[MAX_PACKET];
|
||||
assert(len <= MAX_PACKET);
|
||||
int outsize;
|
||||
int reply = 0; //everything okay
|
||||
int outsize=FRAME_BYTES(frame_size); // the size it should be
|
||||
|
||||
if (encrypted) {
|
||||
unsigned char iv[16];
|
||||
@@ -244,11 +245,13 @@ static void alac_decode(short *dest, uint8_t *buf, int len) {
|
||||
}
|
||||
if (outsize!=FRAME_BYTES(frame_size)) {
|
||||
if(outsize<FRAME_BYTES(frame_size)) {
|
||||
debug(1,"Output from alac_decode is smaller than expected. Encrypted = %d.",encrypted);
|
||||
debug(2,"Output from alac_decode is smaller than expected. Encrypted = %d.",encrypted);
|
||||
} else {
|
||||
debug(1,"OUtput from alac_decode larger than expected -- truncated, but buffer overflow possible! Encrypted = %d.",encrypted);
|
||||
debug(2,"Output from alac_decode larger than expected -- truncated, but buffer overflow possible! Encrypted = %d.",encrypted);
|
||||
}
|
||||
reply = -1; // output frame is the wrong size
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
static int init_decoder(int32_t fmtp[12]) {
|
||||
@@ -298,6 +301,14 @@ static void free_buffer(void) {
|
||||
|
||||
void player_put_packet(seq_t seqno, uint32_t timestamp, uint8_t *data, int len) {
|
||||
|
||||
// ignore a request to flush that has been made before the first packet...
|
||||
if (packet_count==0) {
|
||||
pthread_mutex_lock(&flush_mutex);
|
||||
flush_requested = 0;
|
||||
flush_rtp_timestamp = 0;
|
||||
pthread_mutex_unlock(&flush_mutex);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&ab_mutex);
|
||||
packet_count++;
|
||||
time_of_last_audio_packet = get_absolute_time_in_fp();
|
||||
@@ -363,10 +374,16 @@ void player_put_packet(seq_t seqno, uint32_t timestamp, uint8_t *data, int len)
|
||||
// pthread_mutex_unlock(&ab_mutex);
|
||||
|
||||
if (abuf) {
|
||||
alac_decode(abuf->data, data, len);
|
||||
abuf->ready = 1;
|
||||
abuf->timestamp = timestamp;
|
||||
abuf->sequence_number = seqno;
|
||||
if (alac_decode(abuf->data, data, len)==0) {
|
||||
abuf->ready = 1;
|
||||
abuf->timestamp = timestamp;
|
||||
abuf->sequence_number = seqno;
|
||||
} else {
|
||||
debug(1,"Bad audio packet detected and discarded.");
|
||||
abuf->ready = 0;
|
||||
abuf->timestamp = 0;
|
||||
abuf->sequence_number = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// pthread_mutex_lock(&ab_mutex);
|
||||
@@ -664,8 +681,9 @@ static abuf_t *buffer_get_frame(void) {
|
||||
// The packet time + (latency + latency offset - backend_buffer_length).
|
||||
// Note: the last three items are expressed in frames and must be converted to time.
|
||||
|
||||
int do_wait = 1;
|
||||
int do_wait = 0; // don't wait unless we can really prove we must
|
||||
if ((ab_synced) && (curframe) && (curframe->ready) && (curframe->timestamp)) {
|
||||
do_wait = 1; // if the current frame exists and is ready, then wait unless it's time to let it go...
|
||||
uint32_t reference_timestamp;
|
||||
uint64_t reference_timestamp_time,remote_reference_timestamp_time;
|
||||
get_reference_timestamp_stuff(&reference_timestamp, &reference_timestamp_time, &remote_reference_timestamp_time);
|
||||
@@ -749,7 +767,7 @@ static abuf_t *buffer_get_frame(void) {
|
||||
}
|
||||
|
||||
if (!curframe->ready) {
|
||||
// debug(1, " %d. Supplying a silent frame.", read);
|
||||
// debug(1, "Supplying a silent frame for frame %u", read);
|
||||
missing_packets++;
|
||||
memset(curframe->data, 0, FRAME_BYTES(frame_size));
|
||||
curframe->timestamp = 0;
|
||||
@@ -893,12 +911,13 @@ typedef struct stats { // statistics for running averages
|
||||
} stats_t;
|
||||
|
||||
static void *player_thread_func(void *arg) {
|
||||
int threads_stop = 0;
|
||||
struct inter_threads_record itr;
|
||||
itr.please_stop = 0;
|
||||
// create and start the timing, control and audio receiver threads
|
||||
pthread_t rtp_audio_thread, rtp_control_thread, rtp_timing_thread;
|
||||
pthread_create(&rtp_audio_thread, NULL, &rtp_audio_receiver, (void *)&threads_stop);
|
||||
pthread_create(&rtp_control_thread, NULL, &rtp_control_receiver, (void *)&threads_stop);
|
||||
pthread_create(&rtp_timing_thread, NULL, &rtp_timing_receiver, (void *)&threads_stop);
|
||||
pthread_create(&rtp_audio_thread, NULL, &rtp_audio_receiver, (void *)&itr);
|
||||
pthread_create(&rtp_control_thread, NULL, &rtp_control_receiver, (void *)&itr);
|
||||
pthread_create(&rtp_timing_thread, NULL, &rtp_timing_receiver, (void *)&itr);
|
||||
|
||||
session_corrections = 0;
|
||||
play_segment_reference_frame = 0; // zero signals that we are not in a play segment
|
||||
@@ -1228,10 +1247,11 @@ static void *player_thread_func(void *arg) {
|
||||
}
|
||||
if (config.output->stop)
|
||||
config.output->stop();
|
||||
usleep(100000); // allow this time to (?) allow the alsa subsystem to finish cleaning up after itself. 50 ms seems too short
|
||||
free(outbuf);
|
||||
free(silence);
|
||||
debug(1,"Shut down audio, control and timing threads");
|
||||
// usleep(1000000);
|
||||
itr.please_stop = 1;
|
||||
pthread_kill(rtp_audio_thread, SIGUSR1);
|
||||
pthread_kill(rtp_control_thread, SIGUSR1);
|
||||
pthread_kill(rtp_timing_thread, SIGUSR1);
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <memory.h>
|
||||
#include <math.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
@@ -47,11 +48,6 @@
|
||||
#include <linux/in6.h>
|
||||
#endif
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t seconds;
|
||||
uint32_t fraction;
|
||||
} ntp_timestamp;
|
||||
|
||||
typedef struct time_ping_record {
|
||||
uint64_t local_to_remote_difference;
|
||||
uint64_t dispersion;
|
||||
@@ -96,16 +92,49 @@ static pthread_mutex_t reference_time_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
uint64_t static local_to_remote_time_difference; // used to switch between local and remote clocks
|
||||
|
||||
void *rtp_audio_receiver(void *arg) {
|
||||
// we inherit the signal mask (SIGUSR1)
|
||||
debug(2, "Audio receiver -- Server RTP thread starting.");
|
||||
|
||||
int *stop = arg; // when set to 1, we should stop
|
||||
// we inherit the signal mask (SIGUSR1)
|
||||
struct inter_threads_record *itr = arg;
|
||||
|
||||
int32_t last_seqno = -1;
|
||||
uint8_t packet[2048], *pktp;
|
||||
|
||||
uint64_t time_of_previous_packet_fp = 0;
|
||||
float longest_packet_time_interval_us = 0.0;
|
||||
|
||||
// mean and variance calculations from "online_variance" algorithm at https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Online_algorithm
|
||||
|
||||
int32_t stat_n = 0;
|
||||
float stat_mean = 0.0;
|
||||
float stat_M2 = 0.0;
|
||||
|
||||
ssize_t nread;
|
||||
while (*stop==0) {
|
||||
while (itr->please_stop==0) {
|
||||
nread = recv(audio_socket, packet, sizeof(packet), 0);
|
||||
|
||||
uint64_t local_time_now_fp = get_absolute_time_in_fp();
|
||||
if (time_of_previous_packet_fp) {
|
||||
float time_interval_us = (((local_time_now_fp - time_of_previous_packet_fp)*1000000)>>32)*1.0;
|
||||
time_of_previous_packet_fp = local_time_now_fp;
|
||||
if (time_interval_us>longest_packet_time_interval_us)
|
||||
longest_packet_time_interval_us=time_interval_us;
|
||||
stat_n+=1;
|
||||
float stat_delta = time_interval_us - stat_mean;
|
||||
stat_mean += stat_delta/stat_n;
|
||||
stat_M2 += stat_delta*(time_interval_us - stat_mean);
|
||||
if (stat_n % 2500 == 0) {
|
||||
debug(3,"Packet reception intervals: mean, standard deviation and max for the last 2,500 packets in microseconds: %10.1f, %10.1f, %10.1f.",stat_mean, sqrtf(stat_M2 / (stat_n-1)),longest_packet_time_interval_us);
|
||||
stat_n = 0;
|
||||
stat_mean = 0.0;
|
||||
stat_M2 = 0.0;
|
||||
time_of_previous_packet_fp = 0;
|
||||
longest_packet_time_interval_us = 0.0;
|
||||
}
|
||||
} else {
|
||||
time_of_previous_packet_fp = local_time_now_fp;
|
||||
}
|
||||
|
||||
if (nread < 0)
|
||||
break;
|
||||
|
||||
@@ -124,8 +153,8 @@ void *rtp_audio_receiver(void *arg) {
|
||||
last_seqno = seqno;
|
||||
else {
|
||||
last_seqno = (last_seqno + 1) & 0xffff;
|
||||
if (seqno != last_seqno)
|
||||
debug(2, "RTP: Packets out of sequence: expected: %d, got %d.", last_seqno, seqno);
|
||||
//if (seqno != last_seqno)
|
||||
// debug(3, "RTP: Packets out of sequence: expected: %d, got %d.", last_seqno, seqno);
|
||||
last_seqno = seqno; // reset warning...
|
||||
}
|
||||
uint32_t timestamp = ntohl(*(unsigned long *)(pktp + 4));
|
||||
@@ -160,7 +189,8 @@ void *rtp_audio_receiver(void *arg) {
|
||||
void *rtp_control_receiver(void *arg) {
|
||||
// we inherit the signal mask (SIGUSR1)
|
||||
|
||||
int *stop = arg; // when set to 1, we should stop
|
||||
debug(2, "Control receiver -- Server RTP thread starting.");
|
||||
struct inter_threads_record *itr = arg;
|
||||
|
||||
reference_timestamp = 0; // nothing valid received yet
|
||||
uint8_t packet[2048], *pktp;
|
||||
@@ -168,7 +198,7 @@ void *rtp_control_receiver(void *arg) {
|
||||
uint64_t remote_time_of_sync, local_time_now, remote_time_now;
|
||||
uint32_t sync_rtp_timestamp, rtp_timestamp_less_latency;
|
||||
ssize_t nread;
|
||||
while (*stop==0) {
|
||||
while (itr->please_stop==0) {
|
||||
nread = recv(control_socket, packet, sizeof(packet), 0);
|
||||
local_time_now = get_absolute_time_in_fp();
|
||||
// clock_gettime(CLOCK_MONOTONIC,&tn);
|
||||
@@ -260,6 +290,7 @@ void *rtp_control_receiver(void *arg) {
|
||||
}
|
||||
|
||||
void *rtp_timing_sender(void *arg) {
|
||||
debug(2, "Timing sender thread starting.");
|
||||
int *stop = arg; // the parameter points to this request to stop thing
|
||||
struct timing_request {
|
||||
char leader;
|
||||
@@ -315,9 +346,10 @@ void *rtp_timing_sender(void *arg) {
|
||||
}
|
||||
|
||||
void *rtp_timing_receiver(void *arg) {
|
||||
debug(2, "Timing receiver -- Server RTP thread starting.");
|
||||
// we inherit the signal mask (SIGUSR1)
|
||||
|
||||
int *stop = arg; // when set to 1, we should stop
|
||||
struct inter_threads_record *itr = arg;
|
||||
|
||||
uint8_t packet[2048], *pktp;
|
||||
ssize_t nread;
|
||||
@@ -335,7 +367,7 @@ void *rtp_timing_receiver(void *arg) {
|
||||
uint64_t first_local_to_remote_time_difference = 0;
|
||||
uint64_t first_local_to_remote_time_difference_time;
|
||||
uint64_t l2rtd = 0;
|
||||
while (*stop==0) {
|
||||
while (itr->please_stop==0) {
|
||||
nread = recv(timing_socket, packet, sizeof(packet), 0);
|
||||
arrival_time = get_absolute_time_in_fp();
|
||||
// clock_gettime(CLOCK_MONOTONIC,&att);
|
||||
@@ -702,7 +734,7 @@ void rtp_shutdown(void) {
|
||||
void rtp_request_resend(seq_t first, uint32_t count) {
|
||||
if (running) {
|
||||
//if (!request_sent) {
|
||||
debug(2, "requesting resend of %d packets starting at %u.", count, first);
|
||||
debug(3, "requesting resend of %d packets starting at %u.", count, first);
|
||||
// request_sent = 1;
|
||||
//}
|
||||
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
#include "player.h"
|
||||
|
||||
typedef struct inter_threads_record {
|
||||
uint32_t please_stop;
|
||||
} inter_threads_record;
|
||||
|
||||
void *rtp_audio_receiver(void *arg);
|
||||
void *rtp_control_receiver(void *arg);
|
||||
void *rtp_timing_receiver(void *arg);
|
||||
|
||||
@@ -73,6 +73,7 @@ enum rtsp_read_request_response {
|
||||
};
|
||||
|
||||
// Mike Brady's part...
|
||||
static pthread_mutex_t barrier_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_mutex_t play_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// every time we want to retain or release a reference count, lock it with this
|
||||
@@ -81,9 +82,9 @@ static pthread_mutex_t reference_counter_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// only one thread is allowed to use the player at once.
|
||||
// it monitors the request variable (at least when interrupted)
|
||||
static pthread_mutex_t playing_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
//static pthread_mutex_t playing_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
// static int please_shutdown = 0;
|
||||
static pthread_t playing_thread = 0;
|
||||
// static pthread_t playing_thread = 0;
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
@@ -95,6 +96,14 @@ typedef struct {
|
||||
pthread_t player_thread;
|
||||
} rtsp_conn_info;
|
||||
|
||||
static rtsp_conn_info *playing_conn = NULL; // the data structure representing the connection that has the player.
|
||||
static rtsp_conn_info **conns = NULL;
|
||||
|
||||
void memory_barrier() {
|
||||
pthread_mutex_lock(&barrier_mutex);
|
||||
pthread_mutex_unlock(&barrier_mutex);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_METADATA
|
||||
typedef struct {
|
||||
pthread_mutex_t pc_queue_lock;
|
||||
@@ -236,10 +245,14 @@ int pc_queue_get_item(pc_queue *the_queue, void *the_stuff) {
|
||||
|
||||
// determine if we are the currently playing thread
|
||||
static inline int rtsp_playing(void) {
|
||||
if (pthread_mutex_trylock(&playing_mutex)) {
|
||||
return pthread_equal(playing_thread, pthread_self());
|
||||
if (pthread_mutex_trylock(&play_lock)) {
|
||||
// if playing_mutex is locked...
|
||||
// return 0 if the threads are different, non-zero if the threads are the same
|
||||
return pthread_equal(playing_conn->thread, pthread_self());
|
||||
} else {
|
||||
pthread_mutex_unlock(&playing_mutex);
|
||||
// you actually acquired the playing_mutex, implying that there is no currently playing thread
|
||||
// so unlock it return 0, implying you are not playing
|
||||
pthread_mutex_unlock(&play_lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -250,27 +263,21 @@ void rtsp_request_shutdown_stream(void) {
|
||||
0); // i.e. ask all playing threads to stop
|
||||
}
|
||||
|
||||
static void rtsp_take_player(void) {
|
||||
if (rtsp_playing())
|
||||
return;
|
||||
//static void rtsp_take_player(void) {
|
||||
// if (rtsp_playing())
|
||||
// return;
|
||||
|
||||
if (pthread_mutex_trylock(&playing_mutex)) {
|
||||
debug(1, "Request to all other playing threads to stop.");
|
||||
ask_other_rtsp_conversation_threads_to_stop(
|
||||
pthread_self()); // all threads apart from self
|
||||
pthread_mutex_lock(&playing_mutex);
|
||||
}
|
||||
playing_thread =
|
||||
pthread_self(); // make us the currently-playing thread (why?)
|
||||
}
|
||||
|
||||
void rtsp_shutdown_stream(void) {
|
||||
rtsp_take_player();
|
||||
pthread_mutex_unlock(&playing_mutex);
|
||||
}
|
||||
// if (pthread_mutex_trylock(&playing_mutex)) {
|
||||
// debug(1, "Request to all other playing threads to stop.");
|
||||
// ask_other_rtsp_conversation_threads_to_stop(
|
||||
// pthread_self()); // all threads apart from self
|
||||
// pthread_mutex_lock(&playing_mutex);
|
||||
// }
|
||||
// playing_thread =
|
||||
// pthread_self(); // make us the currently-playing thread (why?)
|
||||
//}
|
||||
|
||||
// keep track of the threads we have spawned so we can join() them
|
||||
static rtsp_conn_info **conns = NULL;
|
||||
static int nconns = 0;
|
||||
static void track_thread(rtsp_conn_info *conn) {
|
||||
conns = realloc(conns, sizeof(rtsp_conn_info *) * (nconns + 1));
|
||||
@@ -281,12 +288,12 @@ static void track_thread(rtsp_conn_info *conn) {
|
||||
static void cleanup_threads(void) {
|
||||
void *retval;
|
||||
int i;
|
||||
debug(2, "culling threads.");
|
||||
// debug(2, "culling threads.");
|
||||
for (i = 0; i < nconns;) {
|
||||
if (conns[i]->running == 0) {
|
||||
pthread_join(conns[i]->thread, &retval);
|
||||
free(conns[i]);
|
||||
debug(2, "one joined...");
|
||||
debug(2, "one thread joined...");
|
||||
nconns--;
|
||||
if (nconns)
|
||||
conns[i] = conns[nconns];
|
||||
@@ -481,6 +488,7 @@ rtsp_read_request(rtsp_conn_info *conn, rtsp_message **the_packet) {
|
||||
int msg_size = -1;
|
||||
|
||||
while (msg_size < 0) {
|
||||
memory_barrier();
|
||||
if (conn->stop != 0) {
|
||||
debug(1, "RTSP shutdown requested.");
|
||||
reply = rtsp_read_request_response_shutdown_requested;
|
||||
@@ -488,7 +496,7 @@ rtsp_read_request(rtsp_conn_info *conn, rtsp_message **the_packet) {
|
||||
}
|
||||
nread = read(conn->fd, buf + inbuf, buflen - inbuf);
|
||||
if (!nread) {
|
||||
debug(1, "RTSP connection closed.");
|
||||
debug(2, "RTSP connection closed.");
|
||||
reply = rtsp_read_request_response_shutdown_requested;
|
||||
goto shutdown;
|
||||
}
|
||||
@@ -794,7 +802,7 @@ static void handle_setup(rtsp_conn_info *conn, rtsp_message *req,
|
||||
p = strchr(p, '=') + 1;
|
||||
tport = atoi(p);
|
||||
|
||||
rtsp_take_player();
|
||||
// rtsp_take_player();
|
||||
rtp_setup(&conn->remote, cport, tport, active_remote, &lsport, &lcport,
|
||||
<port);
|
||||
if (!lsport)
|
||||
@@ -1325,9 +1333,31 @@ static void handle_set_parameter(rtsp_conn_info *conn, rtsp_message *req,
|
||||
|
||||
static void handle_announce(rtsp_conn_info *conn, rtsp_message *req,
|
||||
rtsp_message *resp) {
|
||||
int have_the_player = 0;
|
||||
|
||||
// interrupt session if permitted
|
||||
if ((config.allow_session_interruption == 1) ||
|
||||
(pthread_mutex_trylock(&play_lock) == 0)) {
|
||||
if (pthread_mutex_trylock(&play_lock) == 0) {
|
||||
have_the_player = 1;
|
||||
} else {
|
||||
if (config.allow_session_interruption == 1) {
|
||||
// some other thread has the player ... ask it to relinquish the thread
|
||||
if (playing_conn) {
|
||||
playing_conn->stop = 1;
|
||||
memory_barrier();
|
||||
pthread_kill(playing_conn->thread, SIGUSR1);
|
||||
} else {
|
||||
die("Non existent the_playing_conn with play_lock enabled.");
|
||||
}
|
||||
usleep(1000000); // here, it is possible for other connections to come in and nab the player.
|
||||
debug(1,"Try to get the player now");
|
||||
//pthread_mutex_lock(&play_lock);
|
||||
if (pthread_mutex_trylock(&play_lock) == 0)
|
||||
have_the_player = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (have_the_player) {
|
||||
playing_conn = conn; // the present connection is now playing
|
||||
resp->respcode = 456; // 456 - Header Field Not Valid for Resource
|
||||
char *paesiv = NULL;
|
||||
char *prsaaeskey = NULL;
|
||||
@@ -1686,22 +1716,25 @@ static void *rtsp_conversation_thread_func(void *pconn) {
|
||||
}
|
||||
} while (reply != rtsp_read_request_response_shutdown_requested);
|
||||
|
||||
debug(1, "Now closing RTSP connection.");
|
||||
if (conn->fd > 0)
|
||||
close(conn->fd);
|
||||
debug(1, "Closing down RTSP conversation thread...");
|
||||
if (rtsp_playing()) {
|
||||
player_stop(&conn->player_thread); // might be less noisy doing this first
|
||||
rtp_shutdown();
|
||||
// usleep(400000); // let an angel pass...
|
||||
pthread_mutex_unlock(&play_lock);
|
||||
pthread_mutex_unlock(&playing_mutex);
|
||||
} else {
|
||||
debug(1, "This RTSP conversation thread doesn't think it's playing for a "
|
||||
"close RTSP connection.");
|
||||
}
|
||||
conn->running = 0;
|
||||
if (conn->fd > 0)
|
||||
close(conn->fd);
|
||||
if (auth_nonce)
|
||||
free(auth_nonce);
|
||||
conn->running = 0;
|
||||
debug(2, "Now terminating RTSP conversation thread.");
|
||||
// pthread_mutex_unlock(&playing_mutex);
|
||||
// usleep(1000000);
|
||||
// } // else {
|
||||
//debug(1, "This RTSP conversation thread doesn't think it's playing for a "
|
||||
// "close RTSP connection.");
|
||||
// }
|
||||
debug(2, "RTSP conversation thread terminated.");
|
||||
// please_shutdown = 0;
|
||||
return NULL;
|
||||
}
|
||||
@@ -1839,6 +1872,7 @@ void rtsp_listen_loop(void) {
|
||||
perror("failed to accept connection");
|
||||
free(conn);
|
||||
} else {
|
||||
usleep(500000);
|
||||
pthread_t rtsp_conversation_thread;
|
||||
ret = pthread_create(&rtsp_conversation_thread, NULL,
|
||||
rtsp_conversation_thread_func, conn);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define _RTSP_H
|
||||
|
||||
void rtsp_listen_loop(void);
|
||||
void rtsp_shutdown_stream(void);
|
||||
// void rtsp_shutdown_stream(void);
|
||||
void rtsp_request_shutdown_stream(void);
|
||||
|
||||
|
||||
|
||||
@@ -1,209 +0,0 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
2785806419BB488D00C0B34D /* alac.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = alac.c; sourceTree = "<group>"; };
|
||||
2785806519BB488D00C0B34D /* alac.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = alac.h; sourceTree = "<group>"; };
|
||||
2785806619BB488D00C0B34D /* audio.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = audio.c; sourceTree = "<group>"; };
|
||||
2785806719BB488D00C0B34D /* audio.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = audio.h; sourceTree = "<group>"; };
|
||||
2785806819BB488D00C0B34D /* audio_alsa.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = audio_alsa.c; sourceTree = "<group>"; };
|
||||
2785806919BB488D00C0B34D /* audio_ao.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = audio_ao.c; sourceTree = "<group>"; };
|
||||
2785806A19BB488D00C0B34D /* audio_dummy.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = audio_dummy.c; sourceTree = "<group>"; };
|
||||
2785806B19BB488D00C0B34D /* audio_pipe.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = audio_pipe.c; sourceTree = "<group>"; };
|
||||
2785806C19BB488D00C0B34D /* audio_pulse.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = audio_pulse.c; sourceTree = "<group>"; };
|
||||
2785806D19BB488D00C0B34D /* audio_sndio.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = audio_sndio.c; sourceTree = "<group>"; };
|
||||
2785806E19BB488D00C0B34D /* AUTHORS */ = {isa = PBXFileReference; lastKnownFileType = text; path = AUTHORS; sourceTree = "<group>"; };
|
||||
2785807119BB488D00C0B34D /* common.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = common.c; sourceTree = "<group>"; };
|
||||
2785807219BB488D00C0B34D /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = "<group>"; };
|
||||
2785807C19BB488D00C0B34D /* configure.ac */ = {isa = PBXFileReference; lastKnownFileType = text; path = configure.ac; sourceTree = "<group>"; };
|
||||
2785807D19BB488D00C0B34D /* COPYING */ = {isa = PBXFileReference; lastKnownFileType = text; path = COPYING; sourceTree = "<group>"; };
|
||||
2785807F19BB488D00C0B34D /* documents */ = {isa = PBXFileReference; lastKnownFileType = folder; path = documents; sourceTree = "<group>"; };
|
||||
2785808019BB488D00C0B34D /* INSTALL */ = {isa = PBXFileReference; lastKnownFileType = text; path = INSTALL; sourceTree = "<group>"; };
|
||||
2785808219BB488D00C0B34D /* LICENSES */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSES; sourceTree = "<group>"; };
|
||||
2785808419BB488D00C0B34D /* Makefile.am */ = {isa = PBXFileReference; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
|
||||
2785808619BB488D00C0B34D /* mdns.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mdns.c; sourceTree = "<group>"; };
|
||||
2785808719BB488D00C0B34D /* mdns.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mdns.h; sourceTree = "<group>"; };
|
||||
2785808819BB488D00C0B34D /* mdns_avahi.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mdns_avahi.c; sourceTree = "<group>"; };
|
||||
2785808919BB488D00C0B34D /* mdns_dns_sd.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mdns_dns_sd.c; sourceTree = "<group>"; };
|
||||
2785808A19BB488D00C0B34D /* mdns_external.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mdns_external.c; sourceTree = "<group>"; };
|
||||
2785808B19BB488D00C0B34D /* mdns_tinysvcmdns.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mdns_tinysvcmdns.c; sourceTree = "<group>"; };
|
||||
2785808D19BB488D00C0B34D /* NEWS */ = {isa = PBXFileReference; lastKnownFileType = text; path = NEWS; sourceTree = "<group>"; };
|
||||
2785808E19BB488D00C0B34D /* player.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = player.c; sourceTree = "<group>"; };
|
||||
2785808F19BB488D00C0B34D /* player.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = player.h; sourceTree = "<group>"; };
|
||||
2785809019BB488D00C0B34D /* README */ = {isa = PBXFileReference; lastKnownFileType = text; path = README; sourceTree = "<group>"; };
|
||||
2785809119BB488D00C0B34D /* README.md */ = {isa = PBXFileReference; lastKnownFileType = text; path = README.md; sourceTree = "<group>"; };
|
||||
2785809219BB488D00C0B34D /* rtp.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = rtp.c; sourceTree = "<group>"; };
|
||||
2785809319BB488D00C0B34D /* rtp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = rtp.h; sourceTree = "<group>"; };
|
||||
2785809419BB488D00C0B34D /* rtsp.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = rtsp.c; sourceTree = "<group>"; };
|
||||
2785809519BB488D00C0B34D /* rtsp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = rtsp.h; sourceTree = "<group>"; };
|
||||
2785809619BB488D00C0B34D /* scripts */ = {isa = PBXFileReference; lastKnownFileType = folder; path = scripts; sourceTree = "<group>"; };
|
||||
2785809719BB488D00C0B34D /* shairport-sync.xcworkspace */ = {isa = PBXFileReference; lastKnownFileType = wrapper.workspace; path = "shairport-sync.xcworkspace"; sourceTree = "<group>"; };
|
||||
2785809819BB488D00C0B34D /* shairport.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = shairport.c; sourceTree = "<group>"; };
|
||||
2785809A19BB488D00C0B34D /* tinysvcmdns.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = tinysvcmdns.c; sourceTree = "<group>"; };
|
||||
2785809B19BB488D00C0B34D /* tinysvcmdns.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = tinysvcmdns.h; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
2785805919BB488D00C0B34D = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2785806419BB488D00C0B34D /* alac.c */,
|
||||
2785806519BB488D00C0B34D /* alac.h */,
|
||||
2785806619BB488D00C0B34D /* audio.c */,
|
||||
2785806719BB488D00C0B34D /* audio.h */,
|
||||
2785806819BB488D00C0B34D /* audio_alsa.c */,
|
||||
2785806919BB488D00C0B34D /* audio_ao.c */,
|
||||
2785806A19BB488D00C0B34D /* audio_dummy.c */,
|
||||
2785806B19BB488D00C0B34D /* audio_pipe.c */,
|
||||
2785806C19BB488D00C0B34D /* audio_pulse.c */,
|
||||
2785806D19BB488D00C0B34D /* audio_sndio.c */,
|
||||
2785806E19BB488D00C0B34D /* AUTHORS */,
|
||||
2785807119BB488D00C0B34D /* common.c */,
|
||||
2785807219BB488D00C0B34D /* common.h */,
|
||||
2785807C19BB488D00C0B34D /* configure.ac */,
|
||||
2785807D19BB488D00C0B34D /* COPYING */,
|
||||
2785807F19BB488D00C0B34D /* documents */,
|
||||
2785808019BB488D00C0B34D /* INSTALL */,
|
||||
2785808219BB488D00C0B34D /* LICENSES */,
|
||||
2785808419BB488D00C0B34D /* Makefile.am */,
|
||||
2785808619BB488D00C0B34D /* mdns.c */,
|
||||
2785808719BB488D00C0B34D /* mdns.h */,
|
||||
2785808819BB488D00C0B34D /* mdns_avahi.c */,
|
||||
2785808919BB488D00C0B34D /* mdns_dns_sd.c */,
|
||||
2785808A19BB488D00C0B34D /* mdns_external.c */,
|
||||
2785808B19BB488D00C0B34D /* mdns_tinysvcmdns.c */,
|
||||
2785808D19BB488D00C0B34D /* NEWS */,
|
||||
2785808E19BB488D00C0B34D /* player.c */,
|
||||
2785808F19BB488D00C0B34D /* player.h */,
|
||||
2785809019BB488D00C0B34D /* README */,
|
||||
2785809119BB488D00C0B34D /* README.md */,
|
||||
2785809219BB488D00C0B34D /* rtp.c */,
|
||||
2785809319BB488D00C0B34D /* rtp.h */,
|
||||
2785809419BB488D00C0B34D /* rtsp.c */,
|
||||
2785809519BB488D00C0B34D /* rtsp.h */,
|
||||
2785809619BB488D00C0B34D /* scripts */,
|
||||
2785809719BB488D00C0B34D /* shairport-sync.xcworkspace */,
|
||||
2785809819BB488D00C0B34D /* shairport.c */,
|
||||
2785809A19BB488D00C0B34D /* tinysvcmdns.c */,
|
||||
2785809B19BB488D00C0B34D /* tinysvcmdns.h */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXLegacyTarget section */
|
||||
2785805E19BB488D00C0B34D /* shairport-sync */ = {
|
||||
isa = PBXLegacyTarget;
|
||||
buildArgumentsString = "$(ACTION)";
|
||||
buildConfigurationList = 2785809C19BB488D00C0B34D /* Build configuration list for PBXLegacyTarget "shairport-sync" */;
|
||||
buildPhases = (
|
||||
);
|
||||
buildToolPath = /usr/bin/make;
|
||||
buildWorkingDirectory = "/Users/mike/Documents/git/shairport-sync";
|
||||
dependencies = (
|
||||
);
|
||||
name = "shairport-sync";
|
||||
passBuildSettingsInEnvironment = 1;
|
||||
productName = "shairport-sync";
|
||||
};
|
||||
/* End PBXLegacyTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
2785805A19BB488D00C0B34D /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0510;
|
||||
};
|
||||
buildConfigurationList = 2785805D19BB488D00C0B34D /* Build configuration list for PBXProject "shairport-sync" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 2785805919BB488D00C0B34D;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
2785805E19BB488D00C0B34D /* shairport-sync */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
2785805B19BB488D00C0B34D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = NO;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
2785805C19BB488D00C0B34D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
2785809D19BB488D00C0B34D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUGGING_SYMBOLS = YES;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
OTHER_CFLAGS = "";
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_NAME = "shairport-sync";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
2785809E19BB488D00C0B34D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
OTHER_CFLAGS = "";
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_NAME = "shairport-sync";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
2785805D19BB488D00C0B34D /* Build configuration list for PBXProject "shairport-sync" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
2785805B19BB488D00C0B34D /* Debug */,
|
||||
2785805C19BB488D00C0B34D /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
2785809C19BB488D00C0B34D /* Build configuration list for PBXLegacyTarget "shairport-sync" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
2785809D19BB488D00C0B34D /* Debug */,
|
||||
2785809E19BB488D00C0B34D /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 2785805A19BB488D00C0B34D /* Project object */;
|
||||
}
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0510"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2785805E19BB488D00C0B34D"
|
||||
BuildableName = "shairport-sync"
|
||||
BlueprintName = "shairport-sync"
|
||||
ReferencedContainer = "container:shairport-sync.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
allowLocationSimulation = "NO">
|
||||
<PathRunnable
|
||||
FilePath = "/Users/mike/Documents/git/shairport-sync/shairport-sync">
|
||||
</PathRunnable>
|
||||
<CommandLineArguments>
|
||||
<CommandLineArgument
|
||||
argument = "-v"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
<AdditionalOptions>
|
||||
<AdditionalOption
|
||||
key = "DYLD_INSERT_LIBRARIES"
|
||||
value = "/usr/lib/libgmalloc.dylib"
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
<AdditionalOption
|
||||
key = "OBJC_PRINT_EXCEPTIONS"
|
||||
value = "YES"
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
<AdditionalOption
|
||||
key = "USERBREAK"
|
||||
value = "1"
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
<AdditionalOption
|
||||
key = "MallocGuardEdges"
|
||||
value = ""
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
<AdditionalOption
|
||||
key = "MallocScribble"
|
||||
value = ""
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>shairport-sync.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>2785805E19BB488D00C0B34D</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:shairport-sync.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
|
||||
<false/>
|
||||
<key>IDESourceControlProjectIdentifier</key>
|
||||
<string>FE3806DD-2182-4590-A83E-FEB5D071A2C5</string>
|
||||
<key>IDESourceControlProjectName</key>
|
||||
<string>shairport-sync</string>
|
||||
<key>IDESourceControlProjectOriginsDictionary</key>
|
||||
<dict>
|
||||
<key>DE7E3213834F1D8A89C0AEFB5901CF451E3668FA</key>
|
||||
<string>ssh://github.com/mikebrady/shairport-sync.git</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectPath</key>
|
||||
<string>shairport-sync.xcworkspace</string>
|
||||
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
||||
<dict>
|
||||
<key>DE7E3213834F1D8A89C0AEFB5901CF451E3668FA</key>
|
||||
<string>..</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectURL</key>
|
||||
<string>ssh://github.com/mikebrady/shairport-sync.git</string>
|
||||
<key>IDESourceControlProjectVersion</key>
|
||||
<integer>111</integer>
|
||||
<key>IDESourceControlProjectWCCIdentifier</key>
|
||||
<string>DE7E3213834F1D8A89C0AEFB5901CF451E3668FA</string>
|
||||
<key>IDESourceControlProjectWCConfigurations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>DE7E3213834F1D8A89C0AEFB5901CF451E3668FA</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>shairport-sync</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildLocationStyle</key>
|
||||
<string>UseAppPreferences</string>
|
||||
<key>CustomBuildLocationType</key>
|
||||
<string>RelativeToDerivedData</string>
|
||||
<key>DerivedDataLocationStyle</key>
|
||||
<string>Default</string>
|
||||
<key>HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges</key>
|
||||
<true/>
|
||||
<key>IssueFilterStyle</key>
|
||||
<string>ShowActiveSchemeOnly</string>
|
||||
<key>LiveSourceIssuesEnabled</key>
|
||||
<true/>
|
||||
<key>SnapshotAutomaticallyBeforeSignificantChanges</key>
|
||||
<true/>
|
||||
<key>SnapshotLocationStyle</key>
|
||||
<string>Default</string>
|
||||
</dict>
|
||||
</plist>
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bucket
|
||||
type = "0"
|
||||
version = "2.0">
|
||||
<Breakpoints>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.SymbolicBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "Yes"
|
||||
symbolName = "SIGUSR1"
|
||||
moduleName = "">
|
||||
<Actions>
|
||||
<BreakpointActionProxy
|
||||
ActionExtensionID = "Xcode.BreakpointAction.DebuggerCommand">
|
||||
<ActionContent
|
||||
consoleCommand = "process handle SIGUSR1 -n true -p true -s false">
|
||||
</ActionContent>
|
||||
</BreakpointActionProxy>
|
||||
</Actions>
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
</Bucket>
|
||||
+1
-1
@@ -70,7 +70,7 @@ void shairport_shutdown() {
|
||||
return;
|
||||
shutting_down = 1;
|
||||
mdns_unregister();
|
||||
rtsp_shutdown_stream();
|
||||
rtsp_request_shutdown_stream();
|
||||
if (config.output)
|
||||
config.output->deinit();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user