This is version 5.2
This commit is contained in:
@@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7.0.0
|
- uses: actions/checkout@v7.0.1
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: sudo apt-get -y --no-install-recommends install libpipewire-0.3-dev libplist-utils xmltoman libpopt-dev libconfig-dev libasound2-dev avahi-daemon libavahi-client-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev libgcrypt-dev uuid-dev
|
run: sudo apt-get -y --no-install-recommends install libpipewire-0.3-dev libplist-utils xmltoman libpopt-dev libconfig-dev libasound2-dev avahi-daemon libavahi-client-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev libgcrypt-dev uuid-dev
|
||||||
- name: Configure
|
- name: Configure
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7.0.0
|
- uses: actions/checkout@v7.0.1
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: sudo apt-get -y --no-install-recommends install libglib2.0-dev libplist-utils xmltoman libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libmosquitto-dev avahi-daemon libavahi-client-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev libgcrypt-dev
|
run: sudo apt-get -y --no-install-recommends install libglib2.0-dev libplist-utils xmltoman libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libmosquitto-dev avahi-daemon libavahi-client-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev libgcrypt-dev
|
||||||
- name: Configure
|
- name: Configure
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7.0.0
|
- uses: actions/checkout@v7.0.1
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: sudo apt-get -y --no-install-recommends install libpipewire-0.3-dev libplist-utils xmltoman libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev libgcrypt-dev
|
run: sudo apt-get -y --no-install-recommends install libpipewire-0.3-dev libplist-utils xmltoman libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev libgcrypt-dev
|
||||||
- name: Configure
|
- name: Configure
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7.0.0
|
- uses: actions/checkout@v7.0.1
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: sudo apt-get -y --no-install-recommends install libpipewire-0.3-dev libplist-utils xmltoman libpopt-dev libdaemon-dev libconfig-dev libasound2-dev libao-dev libjack-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev
|
run: sudo apt-get -y --no-install-recommends install libpipewire-0.3-dev libplist-utils xmltoman libpopt-dev libdaemon-dev libconfig-dev libasound2-dev libao-dev libjack-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev
|
||||||
- name: Configure
|
- name: Configure
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7.0.0
|
- uses: actions/checkout@v7.0.1
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: |
|
run: |
|
||||||
brew install automake
|
brew install automake
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7.0.0
|
- uses: actions/checkout@v7.0.1
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev
|
run: sudo apt-get -y --no-install-recommends install xmltoman libpopt-dev libconfig-dev libasound2-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev
|
||||||
- name: Configure
|
- name: Configure
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7.0.0
|
- uses: actions/checkout@v7.0.1
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: sudo apt-get -y --no-install-recommends install xmltoman libpipewire-0.3-dev libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libavutil-dev libavcodec-dev libavformat-dev
|
run: sudo apt-get -y --no-install-recommends install xmltoman libpipewire-0.3-dev libpopt-dev libconfig-dev libasound2-dev libao-dev libjack-dev libglib2.0-dev libmosquitto-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev libavutil-dev libavcodec-dev libavformat-dev
|
||||||
- name: Configure
|
- name: Configure
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ jobs:
|
|||||||
NQPTP_BRANCH=${{ needs.docker-vars.outputs.nqptp_branch }}
|
NQPTP_BRANCH=${{ needs.docker-vars.outputs.nqptp_branch }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout shairport sync repo
|
- name: Checkout shairport sync repo
|
||||||
uses: actions/checkout@v7.0.0
|
uses: actions/checkout@v7.0.1
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Login to Docker Registry
|
- name: Login to Docker Registry
|
||||||
uses: docker/login-action@v4.2.0
|
uses: docker/login-action@v4.5.1
|
||||||
with:
|
with:
|
||||||
registry: ${{ secrets.DOCKER_REGISTRY }}
|
registry: ${{ secrets.DOCKER_REGISTRY }}
|
||||||
username: ${{ secrets.DOCKER_REGISTRY_USER }}
|
username: ${{ secrets.DOCKER_REGISTRY_USER }}
|
||||||
@@ -67,13 +67,13 @@ jobs:
|
|||||||
if: needs.docker-vars.outputs.push_docker_image == 'true'
|
if: needs.docker-vars.outputs.push_docker_image == 'true'
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v4.1.0
|
uses: docker/setup-qemu-action@v4.2.0
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v4.1.0
|
uses: docker/setup-buildx-action@v4.2.0
|
||||||
|
|
||||||
- name: Build and push ${{ matrix.name }}
|
- name: Build and push ${{ matrix.name }}
|
||||||
uses: docker/build-push-action@v7.2.0
|
uses: docker/build-push-action@v7.3.0
|
||||||
env:
|
env:
|
||||||
registry_and_name: ${{ secrets.DOCKER_REGISTRY }}/${{ secrets.DOCKER_IMAGE_NAME }}
|
registry_and_name: ${{ secrets.DOCKER_REGISTRY }}/${{ secrets.DOCKER_IMAGE_NAME }}
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v10.3.0
|
- uses: actions/stale@v10.4.0
|
||||||
with:
|
with:
|
||||||
stale-issue-message: 'This issue has been inactive for 28 days so will be closed 7 days from now. To prevent this, please remove the "stale" label or post a comment.'
|
stale-issue-message: 'This issue has been inactive for 28 days so will be closed 7 days from now. To prevent this, please remove the "stale" label or post a comment.'
|
||||||
stale-pr-message: 'This PR has been inactive for 28 days so will be closed 7 days from now. To prevent this, please remove the "stale" label or post a comment.'
|
stale-pr-message: 'This PR has been inactive for 28 days so will be closed 7 days from now. To prevent this, please remove the "stale" label or post a comment.'
|
||||||
|
|||||||
@@ -14,4 +14,4 @@ The configuration file – which contains lots of documentation – is on your s
|
|||||||
placed at `/etc/shairport-sync.conf.sample` (`/usr/local/etc/shairport-sync.conf.sample` on FreeBSD).
|
placed at `/etc/shairport-sync.conf.sample` (`/usr/local/etc/shairport-sync.conf.sample` on FreeBSD).
|
||||||
You can also view an online version [here](../scripts/shairport-sync.conf).
|
You can also view an online version [here](../scripts/shairport-sync.conf).
|
||||||
|
|
||||||
Build configuration flags are discussed [here](CONFIGURATION%20FLAGS.md).
|
Build configuration flags are discussed [here](../CONFIGURATION%20FLAGS.md).
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ The configuration file is typically at `/etc/shairport-sync.conf` or `/usr/local
|
|||||||
You should also remove any of the following service files that may be present:
|
You should also remove any of the following service files that may be present:
|
||||||
* `/etc/systemd/system/shairport-sync.service`
|
* `/etc/systemd/system/shairport-sync.service`
|
||||||
* `/etc/systemd/user/shairport-sync.service`
|
* `/etc/systemd/user/shairport-sync.service`
|
||||||
|
* `/usr/lib/systemd/system/shairport-sync.service`
|
||||||
* `/lib/systemd/system/shairport-sync.service`
|
* `/lib/systemd/system/shairport-sync.service`
|
||||||
* `/lib/systemd/user/shairport-sync.service`
|
* `/lib/systemd/user/shairport-sync.service`
|
||||||
* `/etc/dbus-1/system.d/shairport-sync-dbus.conf`
|
* `/etc/dbus-1/system.d/shairport-sync-dbus.conf`
|
||||||
@@ -59,6 +60,11 @@ You should also remove any of the following service files that may be present:
|
|||||||
|
|
||||||
New service files will be installed if necessary at the `# make install` stage.
|
New service files will be installed if necessary at the `# make install` stage.
|
||||||
|
|
||||||
|
When you have finished removing the service files, perform the following command:
|
||||||
|
```
|
||||||
|
# systemctl daemon-reload
|
||||||
|
```
|
||||||
|
|
||||||
(In FreeBSD, there is no need to remove the file at `/usr/local/etc/rc.d/shairport-sync` – it's always replaced in the `make install` step.)
|
(In FreeBSD, there is no need to remove the file at `/usr/local/etc/rc.d/shairport-sync` – it's always replaced in the `make install` step.)
|
||||||
#### Reboot after Cleaning Up
|
#### Reboot after Cleaning Up
|
||||||
If you removed any installations of Shairport Sync or any of its service or configuration files in the last three steps, you should reboot.
|
If you removed any installations of Shairport Sync or any of its service or configuration files in the last three steps, you should reboot.
|
||||||
@@ -127,15 +133,13 @@ Enable and start the `avahi-daemon` service.
|
|||||||
# systemctl start avahi-daemon
|
# systemctl start avahi-daemon
|
||||||
```
|
```
|
||||||
### FreeBSD
|
### FreeBSD
|
||||||
This is for FreeBSD 14.3. First, update everything:
|
This is for FreeBSD 15.1. First, update everything:
|
||||||
```
|
```
|
||||||
# freebsd-update fetch
|
|
||||||
# freebsd-update install
|
|
||||||
# pkg update
|
# pkg update
|
||||||
# pkg upgrade
|
# pkg upgrade
|
||||||
```
|
```
|
||||||
Next, install the Avahi subsystem.
|
Next, install the Avahi subsystem.
|
||||||
FYI, `avahi-app` is chosen because it doesn’t require X11. If you are using a GUI with your FreeBSD system, select `avahi` rather than `avahi-app`.
|
If you are using a GUI with your FreeBSD system, select `avahi` rather than `avahi-app`.
|
||||||
`nss_mdns` is included to allow FreeBSD to resolve mDNS-originated addresses – it's not actually needed by Shairport Sync. Thanks to [reidransom](https://gist.github.com/reidransom/6033227) for this.
|
`nss_mdns` is included to allow FreeBSD to resolve mDNS-originated addresses – it's not actually needed by Shairport Sync. Thanks to [reidransom](https://gist.github.com/reidransom/6033227) for this.
|
||||||
```
|
```
|
||||||
# pkg install avahi-app nss_mdns
|
# pkg install avahi-app nss_mdns
|
||||||
@@ -156,7 +160,7 @@ Next, install the packages that are needed for Shairport Sync and NQPTP. If you
|
|||||||
If you are building Shairport Sync for AirPlay 2, install the following packages:
|
If you are building Shairport Sync for AirPlay 2, install the following packages:
|
||||||
```
|
```
|
||||||
# pkg install git autotools pkgconf popt libconfig openssl alsa-utils libsoxr \
|
# pkg install git autotools pkgconf popt libconfig openssl alsa-utils libsoxr \
|
||||||
libplist libsodium ffmpeg libuuid vim
|
libplist libsodium libgcrypt ffmpeg libuuid vim
|
||||||
```
|
```
|
||||||
|
|
||||||
If you are building classic Shairport Sync, the list of packages is shorter:
|
If you are building classic Shairport Sync, the list of packages is shorter:
|
||||||
@@ -181,12 +185,22 @@ Download Shairport Sync and configure, compile and install it. Before executing
|
|||||||
* You can actually omit `--with-ffmpeg` when building classic Shairport Sync, but it is not recommended. While you'll save space (you can omit the FFMpeg libraries in the build and run-time environments), transcoding, e.g. from 44,100 to 48,000 frames per second, will not be available and less well-maintained and less secure decoders will be used.
|
* You can actually omit `--with-ffmpeg` when building classic Shairport Sync, but it is not recommended. While you'll save space (you can omit the FFMpeg libraries in the build and run-time environments), transcoding, e.g. from 44,100 to 48,000 frames per second, will not be available and less well-maintained and less secure decoders will be used.
|
||||||
* **Extra Features:** If you wish to add extra features, for example an extra audio backend, take a look at the [configuration flags](CONFIGURATION%20FLAGS.md). For this walkthrough, though, please do not change too much!
|
* **Extra Features:** If you wish to add extra features, for example an extra audio backend, take a look at the [configuration flags](CONFIGURATION%20FLAGS.md). For this walkthrough, though, please do not change too much!
|
||||||
|
|
||||||
|
If you are building Shairport Sync for the first time, clone its git repository and move into it:
|
||||||
```
|
```
|
||||||
$ git clone https://github.com/mikebrady/shairport-sync.git
|
$ git clone https://github.com/mikebrady/shairport-sync.git
|
||||||
$ cd shairport-sync
|
$ cd shairport-sync
|
||||||
|
```
|
||||||
|
Otherwise, if you have previously cloned the Shairport Sync git repository to the local directory `shairport-sync`, move into that directory and pull the updates:
|
||||||
|
```
|
||||||
|
$ cd shairport-sync
|
||||||
|
$ git pull
|
||||||
|
```
|
||||||
|
Next, perform the following steps:
|
||||||
|
```
|
||||||
$ autoreconf -fi # about 1.5 minutes on a Raspberry Pi B
|
$ autoreconf -fi # about 1.5 minutes on a Raspberry Pi B
|
||||||
$ ./configure --sysconfdir=/etc --with-alsa \
|
$ ./configure --sysconfdir=/etc --with-alsa \
|
||||||
--with-soxr --with-avahi --with-ssl=openssl --with-systemd-startup --with-airplay-2
|
--with-soxr --with-avahi --with-ssl=openssl --with-systemd-startup --with-airplay-2
|
||||||
|
$ make clean # only necessary when updating, to remove outdated older object files
|
||||||
$ make # just over 7 minutes on a Raspberry Pi B
|
$ make # just over 7 minutes on a Raspberry Pi B
|
||||||
# make install
|
# make install
|
||||||
```
|
```
|
||||||
|
|||||||
+2
-1
@@ -31,7 +31,8 @@ shairport_sync_SOURCES = shairport.c bonjour_strings.c mdns.c common.c rtp.c \
|
|||||||
player.c audio.c loudness.c activity_monitor.c \
|
player.c audio.c loudness.c activity_monitor.c \
|
||||||
rtsp.c \
|
rtsp.c \
|
||||||
utilities/debug.c utilities/network_utilities.c utilities/rtsp_message_utilities.c \
|
utilities/debug.c utilities/network_utilities.c utilities/rtsp_message_utilities.c \
|
||||||
utilities/string_utilities.c
|
utilities/string_utilities.c \
|
||||||
|
utilities/exit.c
|
||||||
|
|
||||||
if BUILD_FOR_DARWIN
|
if BUILD_FOR_DARWIN
|
||||||
AM_CXXFLAGS = -I/usr/local/include -Wno-multichar -Wall -Wextra -Wno-deprecated-declarations -pthread -DSYSCONFDIR=\"$(sysconfdir)\"
|
AM_CXXFLAGS = -I/usr/local/include -Wno-multichar -Wall -Wextra -Wno-deprecated-declarations -pthread -DSYSCONFDIR=\"$(sysconfdir)\"
|
||||||
|
|||||||
+1
-1
@@ -131,7 +131,7 @@ void activity_monitor_signify_activity(int active) {
|
|||||||
pthread_mutex_unlock(&activity_monitor_mutex);
|
pthread_mutex_unlock(&activity_monitor_mutex);
|
||||||
}
|
}
|
||||||
// lock the mutex again to send a signal
|
// lock the mutex again to send a signal
|
||||||
pthread_cleanup_debug_mutex_lock(&activity_monitor_mutex, 10000, 4);
|
pthread_mutex_lock_and_cleanup_push(&activity_monitor_mutex);
|
||||||
pthread_cond_signal(&activity_monitor_cv);
|
pthread_cond_signal(&activity_monitor_cv);
|
||||||
pthread_cleanup_pop(1); // release the mutex
|
pthread_cleanup_pop(1); // release the mutex
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -344,8 +344,7 @@ void *rtp_buffered_audio_processor(void *arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (finished == 0) {
|
if (finished == 0) {
|
||||||
pthread_cleanup_debug_mutex_lock(&conn->flush_mutex, 25000,
|
pthread_mutex_lock_and_cleanup_push(&conn->flush_mutex);
|
||||||
4); // 25 ms is a long time to wait!
|
|
||||||
if (blocks_read != 0) {
|
if (blocks_read != 0) {
|
||||||
if (conn->ap2_immediate_flush_requested != 0) {
|
if (conn->ap2_immediate_flush_requested != 0) {
|
||||||
if (ap2_immediate_flush_requested == 0) {
|
if (ap2_immediate_flush_requested == 0) {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ void decodeAndLogPlist(plist_t plist_to_log) {
|
|||||||
// number if okay
|
// number if okay
|
||||||
ssize_t ap2_event_port_send_message(rtsp_conn_info *conn, char *data, size_t data_length) {
|
ssize_t ap2_event_port_send_message(rtsp_conn_info *conn, char *data, size_t data_length) {
|
||||||
ssize_t result = -1; // assume a problem
|
ssize_t result = -1; // assume a problem
|
||||||
debug_mutex_lock(&conn->event_sender_mutex, 1000000, 4);
|
pthread_mutex_lock(&conn->event_sender_mutex);
|
||||||
pthread_cleanup_push(mutex_unlock, &conn->event_sender_mutex);
|
pthread_cleanup_push(mutex_unlock, &conn->event_sender_mutex);
|
||||||
if (conn->event_channel_fd != 0) {
|
if (conn->event_channel_fd != 0) {
|
||||||
result = write_encrypted(conn->event_channel_fd, &conn->ap2_pairing_context.event_cipher_bundle,
|
result = write_encrypted(conn->event_channel_fd, &conn->ap2_pairing_context.event_cipher_bundle,
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ void ap2_event_receiver_cleanup_handler(void *arg) {
|
|||||||
// this is here to ensure it's only performed once during a teardown of a ptp stream
|
// this is here to ensure it's only performed once during a teardown of a ptp stream
|
||||||
send_ssnc_metadata('disc', conn->client_ip_string, strlen(conn->client_ip_string), 1);
|
send_ssnc_metadata('disc', conn->client_ip_string, strlen(conn->client_ip_string), 1);
|
||||||
#endif
|
#endif
|
||||||
debug_mutex_lock(&conn->event_sender_mutex, 1000000, 4);
|
pthread_mutex_lock(&conn->event_sender_mutex);
|
||||||
pthread_cleanup_push(mutex_unlock, &conn->event_sender_mutex);
|
pthread_cleanup_push(mutex_unlock, &conn->event_sender_mutex);
|
||||||
safe_socket_close(&conn->event_channel_fd);
|
safe_socket_close(&conn->event_channel_fd);
|
||||||
pthread_cleanup_pop(1); // unlock the mutex
|
pthread_cleanup_pop(1); // unlock the mutex
|
||||||
@@ -65,7 +65,7 @@ void *ap2_event_receiver(void *arg) {
|
|||||||
memset(&remote_addr, 0, sizeof(remote_addr));
|
memset(&remote_addr, 0, sizeof(remote_addr));
|
||||||
socklen_t addr_size = sizeof(remote_addr);
|
socklen_t addr_size = sizeof(remote_addr);
|
||||||
|
|
||||||
debug_mutex_lock(&conn->event_sender_mutex, 1000000, 4);
|
pthread_mutex_lock(&conn->event_sender_mutex);
|
||||||
pthread_cleanup_push(mutex_unlock, &conn->event_sender_mutex);
|
pthread_cleanup_push(mutex_unlock, &conn->event_sender_mutex);
|
||||||
conn->event_channel_fd =
|
conn->event_channel_fd =
|
||||||
eintr_checked_accept(conn->event_socket, (struct sockaddr *)&remote_addr, &addr_size);
|
eintr_checked_accept(conn->event_socket, (struct sockaddr *)&remote_addr, &addr_size);
|
||||||
|
|||||||
+29
-30
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* libalsa output driver. This file is part of Shairport.
|
* libalsa output driver. This file is part of Shairport.
|
||||||
* Copyright (c) Muffinman, Skaman 2013
|
* Copyright (c) Muffinman, Skaman 2013
|
||||||
* Copyright (c) Mike Brady 2014--2025
|
* Copyright (c) Mike Brady 2014--2026
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person
|
* Permission is hereby granted, free of charge, to any person
|
||||||
@@ -212,9 +212,10 @@ void handle_unfixable_error(int errorCode) {
|
|||||||
if (config.cmd_unfixable) {
|
if (config.cmd_unfixable) {
|
||||||
command_execute(config.cmd_unfixable, messageString, 1);
|
command_execute(config.cmd_unfixable, messageString, 1);
|
||||||
} else {
|
} else {
|
||||||
die("An unrecoverable error, \"output_device_error_%d\", has been "
|
pthread_mutex_unlock(&alsa_mutex); // release the alsa mutex to allow a clean exit
|
||||||
"detected. Doing an emergency exit, as no run_this_if_an_unfixable_error_is_detected "
|
die("an unrecoverable error, \"output_device_error_%d\", has been "
|
||||||
"program.",
|
"detected. Doing an emergency exit, as no \"run_this_if_an_unfixable_error_is_detected\" "
|
||||||
|
"handler has been provided.",
|
||||||
errorCode);
|
errorCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -246,7 +247,7 @@ static int get_permissible_configuration_settings() {
|
|||||||
snd_pcm_hw_params_alloca(&local_alsa_params);
|
snd_pcm_hw_params_alloca(&local_alsa_params);
|
||||||
snd_pcm_info_t *local_alsa_info;
|
snd_pcm_info_t *local_alsa_info;
|
||||||
snd_pcm_info_alloca(&local_alsa_info);
|
snd_pcm_info_alloca(&local_alsa_info);
|
||||||
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 50000, 0);
|
pthread_mutex_lock_and_cleanup_push(&alsa_mutex);
|
||||||
snd_pcm_t *temporary_alsa_handle = NULL;
|
snd_pcm_t *temporary_alsa_handle = NULL;
|
||||||
ret = snd_pcm_open(&temporary_alsa_handle, alsa_out_dev, SND_PCM_STREAM_PLAYBACK, 0);
|
ret = snd_pcm_open(&temporary_alsa_handle, alsa_out_dev, SND_PCM_STREAM_PLAYBACK, 0);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
@@ -1061,7 +1062,7 @@ static int prepare_mixer() {
|
|||||||
|
|
||||||
// Now, start trying to initialise the alsa device with the settings
|
// Now, start trying to initialise the alsa device with the settings
|
||||||
// obtained
|
// obtained
|
||||||
pthread_cleanup_debug_mutex_lock(&alsa_mixer_mutex, 1000, 1);
|
pthread_mutex_lock_and_cleanup_push(&alsa_mixer_mutex);
|
||||||
if (open_mixer() == 0) {
|
if (open_mixer() == 0) {
|
||||||
if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, &alsa_mix_maxv) <
|
if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, &alsa_mix_maxv) <
|
||||||
0) {
|
0) {
|
||||||
@@ -1147,7 +1148,7 @@ static int prepare_mixer() {
|
|||||||
if (response == 0)
|
if (response == 0)
|
||||||
response = close_mixer();
|
response = close_mixer();
|
||||||
}
|
}
|
||||||
debug_mutex_unlock(&alsa_mixer_mutex, 3); // release the mutex
|
pthread_mutex_unlock(&alsa_mixer_mutex); // release the mutex
|
||||||
pthread_cleanup_pop(0);
|
pthread_cleanup_pop(0);
|
||||||
pthread_setcancelstate(oldState, NULL);
|
pthread_setcancelstate(oldState, NULL);
|
||||||
}
|
}
|
||||||
@@ -1593,7 +1594,7 @@ static int configure(int32_t requested_encoded_format, char **channel_map) {
|
|||||||
int response = 0;
|
int response = 0;
|
||||||
int oldState;
|
int oldState;
|
||||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
||||||
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 200000, 0);
|
pthread_mutex_lock_and_cleanup_push(&alsa_mutex);
|
||||||
if (current_encoded_output_format != requested_encoded_format) {
|
if (current_encoded_output_format != requested_encoded_format) {
|
||||||
if (current_encoded_output_format == 0)
|
if (current_encoded_output_format == 0)
|
||||||
debug(2, "alsa: setting output configuration to %s.",
|
debug(2, "alsa: setting output configuration to %s.",
|
||||||
@@ -1610,7 +1611,7 @@ static int configure(int32_t requested_encoded_format, char **channel_map) {
|
|||||||
if ((response == 0) && (channel_map != NULL)) {
|
if ((response == 0) && (channel_map != NULL)) {
|
||||||
*channel_map = get_channel_map_str();
|
*channel_map = get_channel_map_str();
|
||||||
}
|
}
|
||||||
debug_mutex_unlock(&alsa_mutex, 0);
|
pthread_mutex_unlock(&alsa_mutex);
|
||||||
pthread_cleanup_pop(0);
|
pthread_cleanup_pop(0);
|
||||||
pthread_setcancelstate(oldState, NULL);
|
pthread_setcancelstate(oldState, NULL);
|
||||||
if (response != 0)
|
if (response != 0)
|
||||||
@@ -1623,7 +1624,7 @@ static void deinit(void) {
|
|||||||
int oldState;
|
int oldState;
|
||||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
||||||
debug(2, "audio_alsa deinit called.");
|
debug(2, "audio_alsa deinit called.");
|
||||||
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 1);
|
pthread_mutex_lock_and_cleanup_push(&alsa_mutex);
|
||||||
if (alsa_handle != NULL) {
|
if (alsa_handle != NULL) {
|
||||||
debug(3, "alsa: closing the output device.");
|
debug(3, "alsa: closing the output device.");
|
||||||
do_close();
|
do_close();
|
||||||
@@ -1647,7 +1648,7 @@ static int set_mute_state() {
|
|||||||
int response = 1; // some problem expected, e.g. no mixer or not allowed to use it or disconnected
|
int response = 1; // some problem expected, e.g. no mixer or not allowed to use it or disconnected
|
||||||
int oldState;
|
int oldState;
|
||||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
||||||
pthread_cleanup_debug_mutex_lock(&alsa_mixer_mutex, 10000, 0);
|
pthread_mutex_lock_and_cleanup_push(&alsa_mixer_mutex);
|
||||||
if ((alsa_backend_state != abm_disconnected) && (config.alsa_use_hardware_mute == 1) &&
|
if ((alsa_backend_state != abm_disconnected) && (config.alsa_use_hardware_mute == 1) &&
|
||||||
(open_mixer() == 0)) {
|
(open_mixer() == 0)) {
|
||||||
response = 0; // okay if actually using the mute facility
|
response = 0; // okay if actually using the mute facility
|
||||||
@@ -1674,7 +1675,7 @@ static int set_mute_state() {
|
|||||||
}
|
}
|
||||||
close_mixer();
|
close_mixer();
|
||||||
}
|
}
|
||||||
debug_mutex_unlock(&alsa_mixer_mutex, 4); // release the mutex
|
pthread_mutex_unlock(&alsa_mixer_mutex); // release the mutex
|
||||||
pthread_cleanup_pop(0); // release the mutex
|
pthread_cleanup_pop(0); // release the mutex
|
||||||
pthread_setcancelstate(oldState, NULL);
|
pthread_setcancelstate(oldState, NULL);
|
||||||
return response;
|
return response;
|
||||||
@@ -1910,11 +1911,11 @@ static int delay(long *the_delay) {
|
|||||||
snd_pcm_state_t state;
|
snd_pcm_state_t state;
|
||||||
|
|
||||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
||||||
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 0);
|
pthread_mutex_lock_and_cleanup_push(&alsa_mutex);
|
||||||
|
|
||||||
ret = delay_and_status(&state, &my_delay, NULL);
|
ret = delay_and_status(&state, &my_delay, NULL);
|
||||||
|
|
||||||
debug_mutex_unlock(&alsa_mutex, 0);
|
pthread_mutex_unlock(&alsa_mutex);
|
||||||
pthread_cleanup_pop(0);
|
pthread_cleanup_pop(0);
|
||||||
pthread_setcancelstate(oldState, NULL);
|
pthread_setcancelstate(oldState, NULL);
|
||||||
|
|
||||||
@@ -1941,7 +1942,7 @@ static int stats(uint64_t *raw_measurement_time, uint64_t *corrected_measurement
|
|||||||
snd_pcm_sframes_t my_delay = 0; // this initialisation is to silence a clang warning
|
snd_pcm_sframes_t my_delay = 0; // this initialisation is to silence a clang warning
|
||||||
|
|
||||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
||||||
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 0);
|
pthread_mutex_lock_and_cleanup_push(&alsa_mutex);
|
||||||
|
|
||||||
if (alsa_handle == NULL) {
|
if (alsa_handle == NULL) {
|
||||||
ret = alsa_handle_status;
|
ret = alsa_handle_status;
|
||||||
@@ -1958,7 +1959,7 @@ static int stats(uint64_t *raw_measurement_time, uint64_t *corrected_measurement
|
|||||||
frames_sent_break_occurred = 0; // reset it.
|
frames_sent_break_occurred = 0; // reset it.
|
||||||
if (frames_sent_to_dac != NULL)
|
if (frames_sent_to_dac != NULL)
|
||||||
*frames_sent_to_dac = frames_sent_for_playing;
|
*frames_sent_to_dac = frames_sent_for_playing;
|
||||||
debug_mutex_unlock(&alsa_mutex, 0);
|
pthread_mutex_unlock(&alsa_mutex);
|
||||||
pthread_cleanup_pop(0);
|
pthread_cleanup_pop(0);
|
||||||
pthread_setcancelstate(oldState, NULL);
|
pthread_setcancelstate(oldState, NULL);
|
||||||
uint64_t hd = my_delay; // note: snd_pcm_sframes_t is a long
|
uint64_t hd = my_delay; // note: snd_pcm_sframes_t is a long
|
||||||
@@ -1983,12 +1984,10 @@ static int do_play(void *buf, int samples) {
|
|||||||
if (ret == 0) { // will be non-zero if an error or a stall
|
if (ret == 0) { // will be non-zero if an error or a stall
|
||||||
// just check the state of the DAC
|
// just check the state of the DAC
|
||||||
|
|
||||||
if ((state != SND_PCM_STATE_PREPARED) && (state != SND_PCM_STATE_RUNNING) &&
|
if ((state != SND_PCM_STATE_PREPARED) && (state != SND_PCM_STATE_RUNNING)) {
|
||||||
(state != SND_PCM_STATE_XRUN)) {
|
debug(1, "alsa: DAC in unexpected state %s prior to writing.", snd_pcm_state_name(state));
|
||||||
debug(1, "alsa: DAC in odd SND_PCM_STATE_* %d prior to writing.", state);
|
|
||||||
}
|
}
|
||||||
if (state == SND_PCM_STATE_XRUN) {
|
if (state == SND_PCM_STATE_XRUN) {
|
||||||
debug(1, "alsa: DAC in SND_PCM_STATE_XRUN prior to writing.");
|
|
||||||
ret = snd_pcm_recover(alsa_handle, ret, 1);
|
ret = snd_pcm_recover(alsa_handle, ret, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2141,7 +2140,7 @@ static int play(void *buf, int samples, __attribute__((unused)) int sample_type,
|
|||||||
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 50000, 0);
|
pthread_mutex_lock_and_cleanup_push(&alsa_mutex);
|
||||||
|
|
||||||
if (alsa_backend_state == abm_disconnected) {
|
if (alsa_backend_state == abm_disconnected) {
|
||||||
ret = do_open();
|
ret = do_open();
|
||||||
@@ -2163,13 +2162,13 @@ static int play(void *buf, int samples, __attribute__((unused)) int sample_type,
|
|||||||
ret = do_play(buf, samples);
|
ret = do_play(buf, samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_mutex_unlock(&alsa_mutex, 0);
|
pthread_mutex_unlock(&alsa_mutex);
|
||||||
pthread_cleanup_pop(0); // release the mutex
|
pthread_cleanup_pop(0); // release the mutex
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void flush(void) {
|
static void flush(void) {
|
||||||
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 4);
|
pthread_mutex_lock_and_cleanup_push(&alsa_mutex);
|
||||||
if (alsa_backend_state != abm_disconnected) { // must be playing or connected...
|
if (alsa_backend_state != abm_disconnected) { // must be playing or connected...
|
||||||
// do nothing for a flush if config.keep_dac_busy is true
|
// do nothing for a flush if config.keep_dac_busy is true
|
||||||
if (config.keep_dac_busy == 0) {
|
if (config.keep_dac_busy == 0) {
|
||||||
@@ -2178,19 +2177,19 @@ static void flush(void) {
|
|||||||
} else {
|
} else {
|
||||||
debug(3, "alsa: flush() -- called on a disconnected alsa backend");
|
debug(3, "alsa: flush() -- called on a disconnected alsa backend");
|
||||||
}
|
}
|
||||||
debug_mutex_unlock(&alsa_mutex, 4);
|
pthread_mutex_unlock(&alsa_mutex);
|
||||||
pthread_cleanup_pop(0); // release the mutex
|
pthread_cleanup_pop(0); // release the mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stop(void) {
|
static void stop(void) {
|
||||||
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 4);
|
pthread_mutex_lock_and_cleanup_push(&alsa_mutex);
|
||||||
if (alsa_backend_state != abm_disconnected) { // must be playing or connected...
|
if (alsa_backend_state != abm_disconnected) { // must be playing or connected...
|
||||||
if (config.keep_dac_busy == 0) {
|
if (config.keep_dac_busy == 0) {
|
||||||
do_close();
|
do_close();
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
debug(3, "alsa: stop() -- called on a disconnected alsa backend");
|
debug(3, "alsa: stop() -- called on a disconnected alsa backend");
|
||||||
debug_mutex_unlock(&alsa_mutex, 4);
|
pthread_mutex_unlock(&alsa_mutex);
|
||||||
pthread_cleanup_pop(0); // release the mutex
|
pthread_cleanup_pop(0); // release the mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2200,7 +2199,7 @@ static void do_volume(double vol) { // caller is assumed to have the alsa_mutex
|
|||||||
int oldState;
|
int oldState;
|
||||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
||||||
set_volume = vol;
|
set_volume = vol;
|
||||||
pthread_cleanup_debug_mutex_lock(&alsa_mixer_mutex, 1000, 4);
|
pthread_mutex_lock_and_cleanup_push(&alsa_mixer_mutex);
|
||||||
if (volume_set_request && (open_mixer() == 0)) {
|
if (volume_set_request && (open_mixer() == 0)) {
|
||||||
if (has_softvol) {
|
if (has_softvol) {
|
||||||
if (ctl && elem_id) {
|
if (ctl && elem_id) {
|
||||||
@@ -2230,7 +2229,7 @@ static void do_volume(double vol) { // caller is assumed to have the alsa_mutex
|
|||||||
volume_set_request = 0; // any external request that has been made is now satisfied
|
volume_set_request = 0; // any external request that has been made is now satisfied
|
||||||
close_mixer();
|
close_mixer();
|
||||||
}
|
}
|
||||||
debug_mutex_unlock(&alsa_mixer_mutex, 4);
|
pthread_mutex_unlock(&alsa_mixer_mutex);
|
||||||
pthread_cleanup_pop(0); // release the mutex
|
pthread_cleanup_pop(0); // release the mutex
|
||||||
pthread_setcancelstate(oldState, NULL);
|
pthread_setcancelstate(oldState, NULL);
|
||||||
}
|
}
|
||||||
@@ -2303,7 +2302,7 @@ static void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg)
|
|||||||
"alsa_buffer_monitor_thread_code sleep was %.6f sec but request was for %.6f sec. "
|
"alsa_buffer_monitor_thread_code sleep was %.6f sec but request was for %.6f sec. "
|
||||||
"Disabling standby may not work properly!",
|
"Disabling standby may not work properly!",
|
||||||
sleep_time_actual_ns * 0.000000001, config.disable_standby_mode_silence_scan_interval);
|
sleep_time_actual_ns * 0.000000001, config.disable_standby_mode_silence_scan_interval);
|
||||||
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 200000, 0);
|
pthread_mutex_lock_and_cleanup_push(&alsa_mutex);
|
||||||
// check possible state transitions here
|
// check possible state transitions here
|
||||||
if ((alsa_backend_state == abm_disconnected) && (config.keep_dac_busy != 0)) {
|
if ((alsa_backend_state == abm_disconnected) && (config.keep_dac_busy != 0)) {
|
||||||
// open the dac and move to abm_connected mode
|
// open the dac and move to abm_connected mode
|
||||||
@@ -2409,7 +2408,7 @@ static void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug_mutex_unlock(&alsa_mutex, 0);
|
pthread_mutex_unlock(&alsa_mutex);
|
||||||
pthread_cleanup_pop(0); // release the mutex
|
pthread_cleanup_pop(0); // release the mutex
|
||||||
uint64_t tsb = get_absolute_time_in_ns();
|
uint64_t tsb = get_absolute_time_in_ns();
|
||||||
usleep(sleep_time_us); // has a cancellation point in it
|
usleep(sleep_time_us); // has a cancellation point in it
|
||||||
|
|||||||
+28
-28
@@ -39,7 +39,7 @@ static char channel_map_6[] = "FL FR BL BR FC LFE";
|
|||||||
static pthread_mutex_t sndio_mutex = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t sndio_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
static int current_encoded_output_format;
|
static int current_encoded_output_format;
|
||||||
static struct sio_hdl *hdl;
|
static struct sio_hdl *ohdl;
|
||||||
static const char *output_device_name;
|
static const char *output_device_name;
|
||||||
static unsigned int output_device_driver_bufsiz; // parameters for opening the output device
|
static unsigned int output_device_driver_bufsiz; // parameters for opening the output device
|
||||||
static unsigned int output_device_driver_round; // parameters for opening the output device
|
static unsigned int output_device_driver_round; // parameters for opening the output device
|
||||||
@@ -51,7 +51,7 @@ static size_t written;
|
|||||||
uint64_t time_of_last_onmove_cb;
|
uint64_t time_of_last_onmove_cb;
|
||||||
int at_least_one_onmove_cb_seen;
|
int at_least_one_onmove_cb_seen;
|
||||||
|
|
||||||
struct sio_par par;
|
struct sio_par opar;
|
||||||
|
|
||||||
struct sndio_formats {
|
struct sndio_formats {
|
||||||
sps_format_t sps_format;
|
sps_format_t sps_format;
|
||||||
@@ -84,7 +84,7 @@ static uint16_t permissible_configurations[SPS_RATE_HIGHEST + 1][SPS_FORMAT_HIGH
|
|||||||
static int get_permissible_configuration_settings() {
|
static int get_permissible_configuration_settings() {
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
uint64_t hto = get_absolute_time_in_ns();
|
uint64_t hto = get_absolute_time_in_ns();
|
||||||
pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1);
|
pthread_mutex_lock_and_cleanup_push(&sndio_mutex);
|
||||||
struct sio_hdl *hdl = sio_open(output_device_name, SIO_PLAY, 0);
|
struct sio_hdl *hdl = sio_open(output_device_name, SIO_PLAY, 0);
|
||||||
if (hdl != NULL) {
|
if (hdl != NULL) {
|
||||||
struct sio_cap cap;
|
struct sio_cap cap;
|
||||||
@@ -198,7 +198,7 @@ static int get_permissible_configuration_settings() {
|
|||||||
proposed_par.rate = sps_rate_actual_rate(r);
|
proposed_par.rate = sps_rate_actual_rate(r);
|
||||||
proposed_par.pchan = c;
|
proposed_par.pchan = c;
|
||||||
proposed_par.bits = format_info->bits;
|
proposed_par.bits = format_info->bits;
|
||||||
proposed_par.bps = SIO_BPS(par.bits);
|
proposed_par.bps = SIO_BPS(opar.bits);
|
||||||
proposed_par.le = format_info->le;
|
proposed_par.le = format_info->le;
|
||||||
proposed_par.sig = format_info->sig;
|
proposed_par.sig = format_info->sig;
|
||||||
if (sio_setpar(hdl, &proposed_par) == 1) {
|
if (sio_setpar(hdl, &proposed_par) == 1) {
|
||||||
@@ -283,7 +283,7 @@ static int configure(int32_t requested_encoded_format, char **channel_map) {
|
|||||||
|
|
||||||
if (is_running != 0) {
|
if (is_running != 0) {
|
||||||
debug(1, "sndio: the output device is running while changing configuration");
|
debug(1, "sndio: the output device is running while changing configuration");
|
||||||
if (sio_flush(hdl) != 1)
|
if (sio_flush(ohdl) != 1)
|
||||||
debug(1, "sndio: unable to flush");
|
debug(1, "sndio: unable to flush");
|
||||||
written = played = is_running = 0;
|
written = played = is_running = 0;
|
||||||
time_of_last_onmove_cb = 0;
|
time_of_last_onmove_cb = 0;
|
||||||
@@ -301,9 +301,9 @@ static int configure(int32_t requested_encoded_format, char **channel_map) {
|
|||||||
par.sig = format_info->sig;
|
par.sig = format_info->sig;
|
||||||
debug(3, "Requested %u/%u/%u/%u/%u (rate/bits/signed/le/channels)", par.rate, par.bits, par.sig,
|
debug(3, "Requested %u/%u/%u/%u/%u (rate/bits/signed/le/channels)", par.rate, par.bits, par.sig,
|
||||||
par.le, par.pchan);
|
par.le, par.pchan);
|
||||||
if (sio_setpar(hdl, &par) == 1) {
|
if (sio_setpar(ohdl, &par) == 1) {
|
||||||
struct sio_par apar;
|
struct sio_par apar;
|
||||||
if (sio_getpar(hdl, &apar) == 1) {
|
if (sio_getpar(ohdl, &apar) == 1) {
|
||||||
debug(3, "Got %u/%u/%u/%u/%u (rate/bits/signed/le/channels)", apar.rate, apar.bits,
|
debug(3, "Got %u/%u/%u/%u/%u (rate/bits/signed/le/channels)", apar.rate, apar.bits,
|
||||||
apar.sig, apar.le, apar.pchan);
|
apar.sig, apar.le, apar.pchan);
|
||||||
if ((apar.rate == par.rate) && (apar.pchan == par.pchan) && (apar.bits == par.bits) &&
|
if ((apar.rate == par.rate) && (apar.pchan == par.pchan) && (apar.bits == par.bits) &&
|
||||||
@@ -439,23 +439,23 @@ static int init(int argc, char **argv) {
|
|||||||
*/
|
*/
|
||||||
get_permissible_configuration_settings();
|
get_permissible_configuration_settings();
|
||||||
debug(2, "sndio: output device name is \"%s\".", output_device_name);
|
debug(2, "sndio: output device name is \"%s\".", output_device_name);
|
||||||
hdl = sio_open(output_device_name, SIO_PLAY, 0);
|
ohdl = sio_open(output_device_name, SIO_PLAY, 0);
|
||||||
if (!hdl)
|
if (!ohdl)
|
||||||
die("sndio: cannot open audio device");
|
die("sndio: cannot open audio device");
|
||||||
sio_onmove(hdl, onmove_cb, NULL);
|
sio_onmove(ohdl, onmove_cb, NULL);
|
||||||
// debug(1, "sndio: init done");
|
// debug(1, "sndio: init done");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void deinit() {
|
static void deinit() {
|
||||||
pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1);
|
pthread_mutex_lock_and_cleanup_push(&sndio_mutex);
|
||||||
if (hdl != NULL) {
|
if (ohdl != NULL) {
|
||||||
if (is_running != 0) {
|
if (is_running != 0) {
|
||||||
sio_flush(hdl);
|
sio_flush(ohdl);
|
||||||
is_running = 0;
|
is_running = 0;
|
||||||
}
|
}
|
||||||
sio_close(hdl);
|
sio_close(ohdl);
|
||||||
hdl = NULL;
|
ohdl = NULL;
|
||||||
}
|
}
|
||||||
pthread_cleanup_pop(1); // unlock the mutex
|
pthread_cleanup_pop(1); // unlock the mutex
|
||||||
}
|
}
|
||||||
@@ -465,10 +465,10 @@ static int play(void *buf, int frames, __attribute__((unused)) int sample_type,
|
|||||||
__attribute__((unused)) uint64_t playtime) {
|
__attribute__((unused)) uint64_t playtime) {
|
||||||
|
|
||||||
if (frames > 0) {
|
if (frames > 0) {
|
||||||
pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1);
|
pthread_mutex_lock_and_cleanup_push(&sndio_mutex);
|
||||||
if (is_running == 0) {
|
if (is_running == 0) {
|
||||||
if (hdl != NULL) {
|
if (ohdl != NULL) {
|
||||||
if (sio_start(hdl) != 1)
|
if (sio_start(ohdl) != 1)
|
||||||
debug(1, "sndio: unable to start");
|
debug(1, "sndio: unable to start");
|
||||||
is_running = 1;
|
is_running = 1;
|
||||||
written = played = 0;
|
written = played = 0;
|
||||||
@@ -478,7 +478,7 @@ static int play(void *buf, int frames, __attribute__((unused)) int sample_type,
|
|||||||
debug(1, "sndio: output device is not open for play!");
|
debug(1, "sndio: output device is not open for play!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
written += sio_write(hdl, buf, frames * framesize);
|
written += sio_write(ohdl, buf, frames * framesize);
|
||||||
pthread_cleanup_pop(1); // unlock the mutex
|
pthread_cleanup_pop(1); // unlock the mutex
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -486,10 +486,10 @@ static int play(void *buf, int frames, __attribute__((unused)) int sample_type,
|
|||||||
|
|
||||||
static void stop() {
|
static void stop() {
|
||||||
|
|
||||||
pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1);
|
pthread_mutex_lock_and_cleanup_push(&sndio_mutex);
|
||||||
if (hdl != NULL) {
|
if (ohdl != NULL) {
|
||||||
if (is_running != 0) {
|
if (is_running != 0) {
|
||||||
if (sio_flush(hdl) != 1)
|
if (sio_flush(ohdl) != 1)
|
||||||
debug(1, "sndio: unable to stop");
|
debug(1, "sndio: unable to stop");
|
||||||
written = played = is_running = 0;
|
written = played = is_running = 0;
|
||||||
} else {
|
} else {
|
||||||
@@ -509,7 +509,7 @@ int get_delay(long *delay) {
|
|||||||
// calculate the difference in time between now and when the last callback occurred,
|
// calculate the difference in time between now and when the last callback occurred,
|
||||||
// and use it to estimate the frames that would have been output
|
// and use it to estimate the frames that would have been output
|
||||||
uint64_t time_difference = get_absolute_time_in_ns() - time_of_last_onmove_cb;
|
uint64_t time_difference = get_absolute_time_in_ns() - time_of_last_onmove_cb;
|
||||||
uint64_t frame_difference = (time_difference * par.rate) / 1000000000;
|
uint64_t frame_difference = (time_difference * opar.rate) / 1000000000;
|
||||||
estimated_extra_frames_output = frame_difference;
|
estimated_extra_frames_output = frame_difference;
|
||||||
// sanity check -- total estimate can not exceed frames written.
|
// sanity check -- total estimate can not exceed frames written.
|
||||||
if ((estimated_extra_frames_output + played) > written / framesize) {
|
if ((estimated_extra_frames_output + played) > written / framesize) {
|
||||||
@@ -526,8 +526,8 @@ int get_delay(long *delay) {
|
|||||||
|
|
||||||
static int delay(long *delay) {
|
static int delay(long *delay) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1);
|
pthread_mutex_lock_and_cleanup_push(&sndio_mutex);
|
||||||
if (hdl != NULL) {
|
if (ohdl != NULL) {
|
||||||
if (is_running != 0) {
|
if (is_running != 0) {
|
||||||
result = get_delay(delay);
|
result = get_delay(delay);
|
||||||
} else {
|
} else {
|
||||||
@@ -543,10 +543,10 @@ static int delay(long *delay) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void flush() {
|
static void flush() {
|
||||||
pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1);
|
pthread_mutex_lock_and_cleanup_push(&sndio_mutex);
|
||||||
if (hdl != NULL) {
|
if (ohdl != NULL) {
|
||||||
if (is_running != 0) {
|
if (is_running != 0) {
|
||||||
if (sio_flush(hdl) != 1)
|
if (sio_flush(ohdl) != 1)
|
||||||
debug(1, "sndio: unable to flush");
|
debug(1, "sndio: unable to flush");
|
||||||
written = played = is_running = 0;
|
written = played = is_running = 0;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -131,11 +131,8 @@ void set_alsa_out_dev(char *);
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
config_t config_file_stuff;
|
config_t config_file_stuff;
|
||||||
int type_of_exit_cleanup;
|
|
||||||
uint64_t minimum_dac_queue_size;
|
uint64_t minimum_dac_queue_size;
|
||||||
|
|
||||||
pthread_mutex_t the_conn_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
|
|
||||||
unsigned int sps_format_sample_size_array[] = {
|
unsigned int sps_format_sample_size_array[] = {
|
||||||
0, // unknown
|
0, // unknown
|
||||||
1, 1, // S8, U8
|
1, 1, // S8, U8
|
||||||
@@ -233,8 +230,10 @@ const char *short_format_description(int32_t encoded_format) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// true if Shairport Sync is supposed to be sending output to the output device, false otherwise
|
// true if Shairport Sync is supposed to be sending output to the output device, false otherwise
|
||||||
static volatile int requested_connection_state_to_output = 1;
|
// static volatile int requested_connection_state_to_output = 1;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
// this stuff is to direct logging to syslog via libdaemon or directly
|
// this stuff is to direct logging to syslog via libdaemon or directly
|
||||||
// alternatively you can direct it to stderr using a command line option
|
// alternatively you can direct it to stderr using a command line option
|
||||||
|
|
||||||
@@ -291,7 +290,7 @@ int create_log_file(const char *path) {
|
|||||||
int flags = fcntl(fd, F_GETFL);
|
int flags = fcntl(fd, F_GETFL);
|
||||||
if (flags == -1) {
|
if (flags == -1) {
|
||||||
// strerror_r(errno, (char
|
// strerror_r(errno, (char
|
||||||
//*)errorstring, sizeof(errorstring));
|
// *)errorstring, sizeof(errorstring));
|
||||||
// debug(1, "create_log_file -- error %d (\"%s\") getting flags of pipe: \"%s\".",
|
// debug(1, "create_log_file -- error %d (\"%s\") getting flags of pipe: \"%s\".",
|
||||||
// errno,
|
// errno,
|
||||||
// (char *)errorstring, pathname);
|
// (char *)errorstring, pathname);
|
||||||
@@ -337,33 +336,12 @@ void log_to_syslog() {
|
|||||||
#else
|
#else
|
||||||
sps_log = syslog;
|
sps_log = syslog;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
shairport_cfg config;
|
shairport_cfg config;
|
||||||
|
|
||||||
sigset_t pselect_sigset;
|
|
||||||
|
|
||||||
// note -- don't use this to shutdown from dbus -- see its own code in dbus-service.c
|
|
||||||
void sps_shutdown(type_of_exit_type shutdown_type) { // TOE_normal, TOE_emergency
|
|
||||||
type_of_exit_cleanup = shutdown_type;
|
|
||||||
if (type_of_exit_cleanup == TOE_emergency) {
|
|
||||||
debug(1, "emergency shutdown requested");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
} else {
|
|
||||||
debug(1, "normal shutdown requested");
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int usleep_uncancellable(useconds_t usec) {
|
|
||||||
int response;
|
|
||||||
int oldState;
|
|
||||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
|
|
||||||
response = usleep(usec);
|
|
||||||
pthread_setcancelstate(oldState, NULL);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t UDPPortIndex = 0;
|
static uint16_t UDPPortIndex = 0;
|
||||||
|
|
||||||
void resetFreeUDPPort() {
|
void resetFreeUDPPort() {
|
||||||
@@ -524,9 +502,9 @@ uint16_t bind_UDP_port(int ip_family, const char *self_ip_address, uint32_t scop
|
|||||||
return sport;
|
return sport;
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_requested_connection_state_to_output() { return requested_connection_state_to_output; }
|
// int get_requested_connection_state_to_output() { return requested_connection_state_to_output; }
|
||||||
|
|
||||||
void set_requested_connection_state_to_output(int v) { requested_connection_state_to_output = v; }
|
// void set_requested_connection_state_to_output(int v) { requested_connection_state_to_output = v; }
|
||||||
|
|
||||||
void getErrorText(char *destinationString, size_t destinationStringLength) {
|
void getErrorText(char *destinationString, size_t destinationStringLength) {
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
@@ -1828,13 +1806,6 @@ uint64_t nctoh64(const uint8_t *p) {
|
|||||||
return vl;
|
return vl;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_t barrier_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
|
|
||||||
void memory_barrier() {
|
|
||||||
pthread_mutex_lock(&barrier_mutex);
|
|
||||||
pthread_mutex_unlock(&barrier_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sps_nanosleep(const time_t sec, const long nanosec) {
|
void sps_nanosleep(const time_t sec, const long nanosec) {
|
||||||
struct timespec req, rem;
|
struct timespec req, rem;
|
||||||
int result;
|
int result;
|
||||||
@@ -1849,98 +1820,6 @@ void sps_nanosleep(const time_t sec, const long nanosec) {
|
|||||||
nanosec, errno);
|
nanosec, errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mac OS X doesn't have pthread_mutex_timedlock
|
|
||||||
// Also note that timing must be relative to CLOCK_REALTIME
|
|
||||||
|
|
||||||
/*
|
|
||||||
#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
|
|
||||||
int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time) {
|
|
||||||
|
|
||||||
int oldState;
|
|
||||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
|
|
||||||
struct timespec timeoutTime;
|
|
||||||
uint64_t wait_until_time = dally_time * 1000; // to nanoseconds
|
|
||||||
uint64_t start_time = get_realtime_in_ns(); // this is from CLOCK_REALTIME
|
|
||||||
wait_until_time = wait_until_time + start_time;
|
|
||||||
uint64_t wait_until_sec = wait_until_time / 1000000000;
|
|
||||||
uint64_t wait_until_nsec = wait_until_time % 1000000000;
|
|
||||||
timeoutTime.tv_sec = wait_until_sec;
|
|
||||||
timeoutTime.tv_nsec = wait_until_nsec;
|
|
||||||
int r = pthread_mutex_timedlock(mutex, &timeoutTime);
|
|
||||||
pthread_setcancelstate(oldState, NULL);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef COMPILE_FOR_OSX
|
|
||||||
*/
|
|
||||||
int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time) {
|
|
||||||
|
|
||||||
// this would not be not pthread_cancellation safe because is contains a cancellation point
|
|
||||||
int oldState;
|
|
||||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
|
|
||||||
int time_to_wait = dally_time;
|
|
||||||
int r = pthread_mutex_trylock(mutex);
|
|
||||||
while ((r == EBUSY) && (time_to_wait > 0)) {
|
|
||||||
int st = time_to_wait;
|
|
||||||
if (st > 1000)
|
|
||||||
st = 1000;
|
|
||||||
sps_nanosleep(0, st * 1000); // this contains a cancellation point
|
|
||||||
time_to_wait -= st;
|
|
||||||
r = pthread_mutex_trylock(mutex);
|
|
||||||
}
|
|
||||||
pthread_setcancelstate(oldState, NULL);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
int _debug_mutex_lock(pthread_mutex_t *mutex, useconds_t dally_time, const char *mutexname,
|
|
||||||
const char *filename, const int line, int debuglevel) {
|
|
||||||
if ((debuglevel > debug_level()) || (debuglevel == 0))
|
|
||||||
return pthread_mutex_lock(mutex);
|
|
||||||
int oldState;
|
|
||||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
|
|
||||||
if (debuglevel != 0)
|
|
||||||
_debug(filename, line, 3, "mutex_lock \"%s\".", mutexname); // only if you really ask for it!
|
|
||||||
int result = sps_pthread_mutex_timedlock(mutex, dally_time);
|
|
||||||
if (result == ETIMEDOUT) {
|
|
||||||
_debug(
|
|
||||||
filename, line, debuglevel,
|
|
||||||
"mutex_lock \"%s\" failed to lock after %f ms -- now waiting unconditionally to lock it.",
|
|
||||||
mutexname, dally_time * 1E-3);
|
|
||||||
result = pthread_mutex_lock(mutex);
|
|
||||||
if (result == 0)
|
|
||||||
_debug(filename, line, debuglevel, " ...mutex_lock \"%s\" locked successfully.", mutexname);
|
|
||||||
else
|
|
||||||
_debug(filename, line, debuglevel, " ...mutex_lock \"%s\" exited with error code: %u",
|
|
||||||
mutexname, result);
|
|
||||||
}
|
|
||||||
pthread_setcancelstate(oldState, NULL);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _debug_mutex_unlock(pthread_mutex_t *mutex, const char *mutexname, const char *filename,
|
|
||||||
const int line, int debuglevel) {
|
|
||||||
if ((debuglevel > debug_level()) || (debuglevel == 0))
|
|
||||||
return pthread_mutex_unlock(mutex);
|
|
||||||
int oldState;
|
|
||||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
|
|
||||||
char dstring[1000];
|
|
||||||
char errstr[512];
|
|
||||||
memset(dstring, 0, sizeof(dstring));
|
|
||||||
snprintf(dstring, sizeof(dstring), "%s:%d", filename, line);
|
|
||||||
debug(debuglevel, "mutex_unlock \"%s\" at \"%s\".", mutexname, dstring);
|
|
||||||
int r = pthread_mutex_unlock(mutex);
|
|
||||||
if ((debuglevel != 0) && (r != 0)) {
|
|
||||||
if (strerror_r(r, errstr, sizeof(errstr)) == 0) {
|
|
||||||
debug(1, "error %d: \"%s\" unlocking mutex \"%s\" at \"%s\".", r, errstr, mutexname, dstring);
|
|
||||||
} else {
|
|
||||||
debug(1, "error %d: unlocking mutex \"%s\" at \"%s\".", r, mutexname, dstring);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pthread_setcancelstate(oldState, NULL);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
void malloc_cleanup(void *arg) {
|
void malloc_cleanup(void *arg) {
|
||||||
// the address of the malloc variable is passed in case a realloc is done as some time
|
// the address of the malloc variable is passed in case a realloc is done as some time
|
||||||
// debug(1, "malloc cleanup called.");
|
// debug(1, "malloc cleanup called.");
|
||||||
|
|||||||
@@ -52,11 +52,6 @@ typedef enum {
|
|||||||
} dbus_message_bus_t;
|
} dbus_message_bus_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
TOE_normal,
|
|
||||||
TOE_emergency,
|
|
||||||
} type_of_exit_type;
|
|
||||||
|
|
||||||
#define sps_extra_code_output_stalled 32768
|
#define sps_extra_code_output_stalled 32768
|
||||||
#define sps_extra_code_output_state_cannot_make_ready 32769
|
#define sps_extra_code_output_state_cannot_make_ready 32769
|
||||||
|
|
||||||
@@ -198,6 +193,8 @@ typedef struct {
|
|||||||
double resend_control_check_interval_time; // wait this long between making requests
|
double resend_control_check_interval_time; // wait this long between making requests
|
||||||
double resend_control_last_check_time; // if the packet is missing this close to the time of use,
|
double resend_control_last_check_time; // if the packet is missing this close to the time of use,
|
||||||
// give up
|
// give up
|
||||||
|
|
||||||
|
int get_plist_metadata; // set to non-zero to get richer plist metadata
|
||||||
pthread_mutex_t lock;
|
pthread_mutex_t lock;
|
||||||
config_t *cfg;
|
config_t *cfg;
|
||||||
int endianness;
|
int endianness;
|
||||||
@@ -438,9 +435,6 @@ typedef struct {
|
|||||||
unsigned int output_channel_map_size; // number of output channels
|
unsigned int output_channel_map_size; // number of output channels
|
||||||
|
|
||||||
#if defined(CONFIG_DBUS_INTERFACE) || defined(CONFIG_MPRIS_INTERFACE)
|
#if defined(CONFIG_DBUS_INTERFACE) || defined(CONFIG_MPRIS_INTERFACE)
|
||||||
GMainLoop *glib_worker_loop;
|
|
||||||
// for clean quitting from a dbus interface quit request (from the DBus or MPRIS interfaces)
|
|
||||||
int quit_requested_from_glib_mainloop; // remember that it initialised to zero.
|
|
||||||
dbus_message_bus_t dbus_default_message_bus;
|
dbus_message_bus_t dbus_default_message_bus;
|
||||||
|
|
||||||
#if defined(CONFIG_DBUS_INTERFACE)
|
#if defined(CONFIG_DBUS_INTERFACE)
|
||||||
@@ -458,18 +452,20 @@ uint32_t nctohl(const uint8_t *p); // read 4 characters from *p and do ntohl on
|
|||||||
uint16_t nctohs(const uint8_t *p); // read 2 characters from *p and do ntohs on them
|
uint16_t nctohs(const uint8_t *p); // read 2 characters from *p and do ntohs on them
|
||||||
uint64_t nctoh64(const uint8_t *p); // read 8 characters from *p to a uint64_t
|
uint64_t nctoh64(const uint8_t *p); // read 8 characters from *p to a uint64_t
|
||||||
|
|
||||||
void memory_barrier();
|
// void memory_barrier();
|
||||||
|
|
||||||
|
/*
|
||||||
void log_to_stderr(); // call this to direct logging to stderr;
|
void log_to_stderr(); // call this to direct logging to stderr;
|
||||||
void log_to_stdout(); // call this to direct logging to stdout;
|
void log_to_stdout(); // call this to direct logging to stdout;
|
||||||
void log_to_syslog(); // call this to direct logging to the system log;
|
void log_to_syslog(); // call this to direct logging to the system log;
|
||||||
void log_to_file(); // call this to direct logging to a file or (pre-existing) pipe;
|
void log_to_file(); // call this to direct logging to a file or (pre-existing) pipe;
|
||||||
|
*/
|
||||||
|
|
||||||
// true if Shairport Sync is supposed to be sending output to the output device, false otherwise
|
// true if Shairport Sync is supposed to be sending output to the output device, false otherwise
|
||||||
|
|
||||||
int get_requested_connection_state_to_output();
|
//int get_requested_connection_state_to_output();
|
||||||
|
|
||||||
void set_requested_connection_state_to_output(int v);
|
// void set_requested_connection_state_to_output(int v);
|
||||||
|
|
||||||
int try_to_open_pipe_for_writing(
|
int try_to_open_pipe_for_writing(
|
||||||
const char *pathname); // open it without blocking if it's not hooked up
|
const char *pathname); // open it without blocking if it's not hooked up
|
||||||
@@ -505,14 +501,6 @@ extern volatile int debuglev;
|
|||||||
MADEID(once_flag_, __LINE__) = 1; \
|
MADEID(once_flag_, __LINE__) = 1; \
|
||||||
}
|
}
|
||||||
|
|
||||||
// do X once, and then ignore repeated calls until they stop for more than one second
|
|
||||||
#define once_per_1_second_burst(X) \
|
|
||||||
static uint64_t MADEID(time_, __LINE__) = 0; \
|
|
||||||
int64_t MADEID(interval_, __LINE__) = get_absolute_time_in_ns() - MADEID(time_, __LINE__); \
|
|
||||||
if ((MADEID(time_, __LINE__) == 0) || (MADEID(interval_, __LINE__) > 1000000000L)) \
|
|
||||||
X; \
|
|
||||||
MADEID(time_, __LINE__) = get_absolute_time_in_ns()
|
|
||||||
|
|
||||||
void getErrorText(char *destinationString, size_t destinationStringLength);
|
void getErrorText(char *destinationString, size_t destinationStringLength);
|
||||||
|
|
||||||
uint8_t *base64_dec(char *input, int *outlen);
|
uint8_t *base64_dec(char *input, int *outlen);
|
||||||
@@ -558,7 +546,6 @@ uint32_t uatoi(const char *nptr);
|
|||||||
|
|
||||||
extern shairport_cfg config;
|
extern shairport_cfg config;
|
||||||
extern config_t config_file_stuff;
|
extern config_t config_file_stuff;
|
||||||
extern int type_of_exit_cleanup; // normal, emergency, dbus requested...
|
|
||||||
|
|
||||||
extern uint64_t minimum_dac_queue_size;
|
extern uint64_t minimum_dac_queue_size;
|
||||||
|
|
||||||
@@ -581,44 +568,11 @@ void command_set_volume(double volume);
|
|||||||
|
|
||||||
int mkpath(const char *path, mode_t mode);
|
int mkpath(const char *path, mode_t mode);
|
||||||
|
|
||||||
void sps_shutdown(type_of_exit_type shutdown_type); // TOE_normal, TOE_emergency, TOE_dbus
|
#define pthread_mutex_lock_and_cleanup_push(mu) \
|
||||||
|
if (pthread_mutex_lock(mu) == 0) \
|
||||||
|
pthread_cleanup_push(mutex_unlock, (void *)mu)
|
||||||
|
|
||||||
extern sigset_t pselect_sigset;
|
|
||||||
|
|
||||||
extern pthread_mutex_t the_conn_lock;
|
|
||||||
|
|
||||||
#define conn_lock(arg) \
|
|
||||||
pthread_mutex_lock(&the_conn_lock); \
|
|
||||||
arg; \
|
|
||||||
pthread_mutex_unlock(&the_conn_lock);
|
|
||||||
|
|
||||||
// wait for the specified time in microseconds -- it checks every 20 milliseconds
|
|
||||||
// int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time,
|
|
||||||
// const char *debugmessage, int debuglevel);
|
|
||||||
// wait for the specified time, checking every 20 milliseconds, and block if it can't acquire the
|
|
||||||
// lock
|
|
||||||
int _debug_mutex_lock(pthread_mutex_t *mutex, useconds_t dally_time, const char *mutexName,
|
|
||||||
const char *filename, const int line, int debuglevel);
|
|
||||||
|
|
||||||
#define debug_mutex_lock(mu, t, d) _debug_mutex_lock(mu, t, #mu, __FILE__, __LINE__, d)
|
|
||||||
|
|
||||||
int _debug_mutex_unlock(pthread_mutex_t *mutex, const char *mutexName, const char *filename,
|
|
||||||
const int line, int debuglevel);
|
|
||||||
|
|
||||||
#define debug_mutex_unlock(mu, d) _debug_mutex_unlock(mu, #mu, __FILE__, __LINE__, d)
|
|
||||||
|
|
||||||
void pthread_cleanup_debug_mutex_unlock(void *arg);
|
|
||||||
|
|
||||||
#define pthread_cleanup_debug_mutex_lock(mu, t, d) \
|
|
||||||
if (_debug_mutex_lock(mu, t, #mu, __FILE__, __LINE__, d) == 0) \
|
|
||||||
pthread_cleanup_push(pthread_cleanup_debug_mutex_unlock, (void *)mu)
|
|
||||||
|
|
||||||
#define config_lock \
|
|
||||||
if (pthread_mutex_trylock(&config.lock) != 0) { \
|
|
||||||
debug(1, "config_lock: cannot acquire config.lock"); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define config_unlock pthread_mutex_unlock(&config.lock)
|
|
||||||
|
|
||||||
int do_pthread_setname(pthread_t *restrict thread, const char *format, ...);
|
int do_pthread_setname(pthread_t *restrict thread, const char *format, ...);
|
||||||
|
|
||||||
@@ -640,7 +594,6 @@ char *get_version_string(); // mallocs a string space -- remember to free it aft
|
|||||||
int64_t generate_zero_frames(char *outp, size_t number_of_frames, int with_dither,
|
int64_t generate_zero_frames(char *outp, size_t number_of_frames, int with_dither,
|
||||||
int64_t random_number_in, uint32_t encoded_output_format);
|
int64_t random_number_in, uint32_t encoded_output_format);
|
||||||
|
|
||||||
void malloc_cleanup(void *arg);
|
|
||||||
|
|
||||||
int string_update_with_size(char **str, int *flag, char *s, size_t len);
|
int string_update_with_size(char **str, int *flag, char *s, size_t len);
|
||||||
|
|
||||||
@@ -652,10 +605,14 @@ int bind_socket_and_port(int type, int ip_family, const char *self_ip_address, u
|
|||||||
|
|
||||||
uint16_t bind_UDP_port(int ip_family, const char *self_ip_address, uint32_t scope_id, int *sock);
|
uint16_t bind_UDP_port(int ip_family, const char *self_ip_address, uint32_t scope_id, int *sock);
|
||||||
|
|
||||||
|
// for pthread_push and pop
|
||||||
|
// careful with the difference between cleanup and unlock!
|
||||||
|
|
||||||
|
void malloc_cleanup(void *arg);
|
||||||
void socket_cleanup(void *arg);
|
void socket_cleanup(void *arg);
|
||||||
void mutex_unlock(void *arg);
|
void mutex_unlock(void *arg);
|
||||||
void rwlock_unlock(void *arg);
|
|
||||||
void mutex_cleanup(void *arg);
|
void mutex_cleanup(void *arg);
|
||||||
|
void rwlock_unlock(void *arg);
|
||||||
void cv_cleanup(void *arg);
|
void cv_cleanup(void *arg);
|
||||||
void thread_cleanup(void *arg);
|
void thread_cleanup(void *arg);
|
||||||
#ifdef CONFIG_AIRPLAY_2
|
#ifdef CONFIG_AIRPLAY_2
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
# Process this file with autoconf to produce a configure script.
|
# Process this file with autoconf to produce a configure script.
|
||||||
|
|
||||||
AC_PREREQ([2.50])
|
AC_PREREQ([2.50])
|
||||||
AC_INIT([shairport-sync], [5.1], [4265913+mikebrady@users.noreply.github.com])
|
AC_INIT([shairport-sync], [5.2], [4265913+mikebrady@users.noreply.github.com])
|
||||||
: ${CFLAGS="-O3"}
|
: ${CFLAGS="-O3"}
|
||||||
: ${CXXFLAGS="-O3"}
|
: ${CXXFLAGS="-O3"}
|
||||||
AM_INIT_AUTOMAKE([subdir-objects])
|
AM_INIT_AUTOMAKE([subdir-objects])
|
||||||
|
|||||||
@@ -212,11 +212,7 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) {
|
|||||||
uint64_t start_time = get_absolute_time_in_ns();
|
uint64_t start_time = get_absolute_time_in_ns();
|
||||||
pthread_cleanup_push(addrinfo_cleanup, (void *)&res);
|
pthread_cleanup_push(addrinfo_cleanup, (void *)&res);
|
||||||
// only do this one at a time -- not sure it is necessary, but better safe than sorry
|
// only do this one at a time -- not sure it is necessary, but better safe than sorry
|
||||||
|
int mutex_reply = pthread_mutex_lock(&dacp_conversation_lock);
|
||||||
// int mutex_reply = sps_pthread_mutex_timedlock(&dacp_conversation_lock, 2000000, command,
|
|
||||||
// 1);
|
|
||||||
int mutex_reply = debug_mutex_lock(&dacp_conversation_lock, 2000000, 1);
|
|
||||||
// int mutex_reply = pthread_mutex_lock(&dacp_conversation_lock);
|
|
||||||
if (mutex_reply == 0) {
|
if (mutex_reply == 0) {
|
||||||
pthread_cleanup_push(mutex_lock_cleanup, (void *)&dacp_conversation_lock);
|
pthread_cleanup_push(mutex_lock_cleanup, (void *)&dacp_conversation_lock);
|
||||||
|
|
||||||
@@ -390,10 +386,10 @@ void relinquish_dacp_server_information(rtsp_conn_info *conn) {
|
|||||||
// as the conn's connection number
|
// as the conn's connection number
|
||||||
// this is to signify that the player has stopped, but only if another thread (with a different
|
// this is to signify that the player has stopped, but only if another thread (with a different
|
||||||
// index) hasn't already taken over the dacp service
|
// index) hasn't already taken over the dacp service
|
||||||
debug_mutex_lock(&dacp_server_information_lock, 500000, 4);
|
pthread_mutex_lock(&dacp_server_information_lock);
|
||||||
if (dacp_server.players_connection_thread_index == conn->connection_number)
|
if (dacp_server.players_connection_thread_index == conn->connection_number)
|
||||||
dacp_server.players_connection_thread_index = 0;
|
dacp_server.players_connection_thread_index = 0;
|
||||||
debug_mutex_unlock(&dacp_server_information_lock, 4);
|
pthread_mutex_unlock(&dacp_server_information_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
// this will be running on the thread of its caller, not of the conversation thread...
|
// this will be running on the thread of its caller, not of the conversation thread...
|
||||||
@@ -403,7 +399,7 @@ void relinquish_dacp_server_information(rtsp_conn_info *conn) {
|
|||||||
// Thus, we can keep the DACP port that might have previously been discovered
|
// Thus, we can keep the DACP port that might have previously been discovered
|
||||||
void set_dacp_server_information(rtsp_conn_info *conn) {
|
void set_dacp_server_information(rtsp_conn_info *conn) {
|
||||||
// debug(1, "set_dacp_server_information");
|
// debug(1, "set_dacp_server_information");
|
||||||
debug_mutex_lock(&dacp_server_information_lock, 500000, 2);
|
pthread_mutex_lock(&dacp_server_information_lock);
|
||||||
dacp_server.players_connection_thread_index = conn->connection_number;
|
dacp_server.players_connection_thread_index = conn->connection_number;
|
||||||
|
|
||||||
if ((conn->dacp_id == NULL) || (strcmp(conn->dacp_id, dacp_server.dacp_id) != 0)) {
|
if ((conn->dacp_id == NULL) || (strcmp(conn->dacp_id, dacp_server.dacp_id) != 0)) {
|
||||||
@@ -464,11 +460,11 @@ void set_dacp_server_information(rtsp_conn_info *conn) {
|
|||||||
|
|
||||||
debug(3, "set_dacp_server_information set active-remote id to %s.", dacp_server.active_remote_id);
|
debug(3, "set_dacp_server_information set active-remote id to %s.", dacp_server.active_remote_id);
|
||||||
pthread_cond_signal(&dacp_server_information_cv);
|
pthread_cond_signal(&dacp_server_information_cv);
|
||||||
debug_mutex_unlock(&dacp_server_information_lock, 3);
|
pthread_mutex_unlock(&dacp_server_information_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dacp_monitor_port_update_callback(const char *dacp_id, uint16_t port) {
|
void dacp_monitor_port_update_callback(const char *dacp_id, uint16_t port) {
|
||||||
debug_mutex_lock(&dacp_server_information_lock, 500000, 2);
|
pthread_mutex_lock(&dacp_server_information_lock);
|
||||||
debug(3,
|
debug(3,
|
||||||
"dacp_monitor_port_update_callback with Remote ID \"%s\", target ID \"%s\" and port "
|
"dacp_monitor_port_update_callback with Remote ID \"%s\", target ID \"%s\" and port "
|
||||||
"number %d.",
|
"number %d.",
|
||||||
@@ -493,7 +489,7 @@ void dacp_monitor_port_update_callback(const char *dacp_id, uint16_t port) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pthread_cond_signal(&dacp_server_information_cv);
|
pthread_cond_signal(&dacp_server_information_cv);
|
||||||
debug_mutex_unlock(&dacp_server_information_lock, 3);
|
pthread_mutex_unlock(&dacp_server_information_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
|
void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
|
||||||
@@ -510,7 +506,7 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
|
|||||||
while (1) {
|
while (1) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
int32_t the_volume;
|
int32_t the_volume;
|
||||||
pthread_cleanup_debug_mutex_lock(&dacp_server_information_lock, 500000, 4);
|
pthread_mutex_lock_and_cleanup_push(&dacp_server_information_lock);
|
||||||
if (dacp_server.scan_enable == 0) {
|
if (dacp_server.scan_enable == 0) {
|
||||||
metadata_hub_modify_prolog();
|
metadata_hub_modify_prolog();
|
||||||
int ch = (metadata_store.dacp_server_active != 0) ||
|
int ch = (metadata_store.dacp_server_active != 0) ||
|
||||||
|
|||||||
+2
-2
@@ -44,6 +44,7 @@
|
|||||||
#include "metadata/hub.h"
|
#include "metadata/hub.h"
|
||||||
|
|
||||||
#include "dbus-service.h"
|
#include "dbus-service.h"
|
||||||
|
#include "utilities/exit.h"
|
||||||
|
|
||||||
#ifdef CONFIG_CONVOLUTION
|
#ifdef CONFIG_CONVOLUTION
|
||||||
#include <FFTConvolver/convolver.h>
|
#include <FFTConvolver/convolver.h>
|
||||||
@@ -934,9 +935,8 @@ static gboolean on_handle_quit(ShairportSync *skeleton, GDBusMethodInvocation *i
|
|||||||
__attribute__((unused)) const gchar *command,
|
__attribute__((unused)) const gchar *command,
|
||||||
__attribute__((unused)) gpointer user_data) {
|
__attribute__((unused)) gpointer user_data) {
|
||||||
debug(1, ">> quit request...");
|
debug(1, ">> quit request...");
|
||||||
config.quit_requested_from_glib_mainloop = 1;
|
|
||||||
g_main_loop_quit(config.glib_worker_loop);
|
|
||||||
shairport_sync_complete_quit(skeleton, invocation);
|
shairport_sync_complete_quit(skeleton, invocation);
|
||||||
|
exit_request(EXIT_SUCCESS);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
.SH NAME
|
.SH NAME
|
||||||
shairport-sync \- AirPlay and AirPlay 2 Audio Player
|
shairport-sync \- AirPlay and AirPlay 2 Audio Player
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
\fBshairport-sync [-djvw]\fB [-a \fB\fIservice-name\fB | --name=\fB\fIservice-name\fB]\fB [-B \fB\fIcommand\fB | --onstart=\fB\fIcommand\fB]\fB [-c \fB\fIconfigurationfile\fB | --configfile=\fB\fIconfigurationfile\fB]\fB [-d | --daemon]\fB [--dbus-default-message-bus=\fB\fIbus\fB]\fB [-E \fB\fIcommand\fB | --onstop=\fB\fIcommand\fB]\fB [-g | --get-cover-art]\fB [-j | --justDaemoniseNoPIDFile]\fB [--logOutputLevel]\fB [--log-to-syslog]\fB [-L \fB\fIlatency\fB | --latency=\fB\fIlatency\fB]\fB [-m \fB\fIbackend\fB | --mdns=\fB\fIbackend\fB]\fB [-M | --metadata-enable]\fB [-o \fB\fIbackend\fB | --output=\fB\fIbackend\fB]\fB [-p \fB\fIport\fB | --port=\fB\fIport\fB]\fB [--password=\fB\fIsecret\fB]\fB [-r \fB\fIthreshold\fB | --resync=\fB\fIthreshold\fB]\fB [--service-type=\fB\fItype\fB]\fB [--statistics]\fB [-S \fB\fImode\fB | --stuffing=\fB\fImode\fB]\fB [-t \fB\fItimeout\fB | --timeout=\fB\fItimeout\fB]\fB [--tolerance=\fB\fIframes\fB]\fB [-v | --verbose]\fB [-w | --wait-cmd]\fB [-- \fB\fIaudio_backend_options\fB]\fB
|
\fBshairport-sync [-djvw]\fB [-a \fB\fIservice-name\fB | --name=\fB\fIservice-name\fB]\fB [-B \fB\fIcommand\fB | --onstart=\fB\fIcommand\fB]\fB [-c \fB\fIconfigurationfile\fB | --configfile=\fB\fIconfigurationfile\fB]\fB [-d | --daemon]\fB [--dbus-default-message-bus=\fB\fIbus\fB]\fB [-E \fB\fIcommand\fB | --onstop=\fB\fIcommand\fB]\fB [-g | --get-cover-art]\fB [-j | --justDaemoniseNoPIDFile]\fB [--logOutputLevel]\fB [-L \fB\fIlatency\fB | --latency=\fB\fIlatency\fB]\fB [-m \fB\fIbackend\fB | --mdns=\fB\fIbackend\fB]\fB [-M | --metadata-enable]\fB [-o \fB\fIbackend\fB | --output=\fB\fIbackend\fB]\fB [-p \fB\fIport\fB | --port=\fB\fIport\fB]\fB [--password=\fB\fIsecret\fB]\fB [-r \fB\fIthreshold\fB | --resync=\fB\fIthreshold\fB]\fB [--service-type=\fB\fItype\fB]\fB [--statistics]\fB [-S \fB\fImode\fB | --stuffing=\fB\fImode\fB]\fB [-t \fB\fItimeout\fB | --timeout=\fB\fItimeout\fB]\fB [--tolerance=\fB\fIframes\fB]\fB [-v | --verbose]\fB [-w | --wait-cmd]\fB [-- \fB\fIaudio_backend_options\fB]\fB
|
||||||
|
|
||||||
shairport-sync -X | --displayConfig\fB
|
shairport-sync -X | --displayConfig\fB
|
||||||
|
|
||||||
@@ -96,11 +96,6 @@ Kill the shairport-sync daemon and exit. (Requires that the daemon has written i
|
|||||||
\fB--logOutputLevel\f1
|
\fB--logOutputLevel\f1
|
||||||
Use this to log the volume level when the volume is changed. It may be useful if you are trying to determine a suitable value for the maximum volume level. Not available as a configuration file setting.
|
Use this to log the volume level when the volume is changed. It may be useful if you are trying to determine a suitable value for the maximum volume level. Not available as a configuration file setting.
|
||||||
.TP
|
.TP
|
||||||
\fB--log-to-syslog\f1
|
|
||||||
Warnings, error messages and messages are sent, by default, to \fISTDERR\f1. Use this option to route these messages to the \fBsyslog\f1 instead. This is intended for use when Shairport Sync is operating as a daemon.
|
|
||||||
|
|
||||||
See also \fB--displayConfig\f1.
|
|
||||||
.TP
|
|
||||||
\fB-L | --latency=\f1\fIlatency\f1
|
\fB-L | --latency=\f1\fIlatency\f1
|
||||||
Use this to set the \fIdefault latency\f1, in frames, for audio coming from an unidentified source or from an iTunes Version 9 or earlier source. The standard value for the \fIdefault latency\f1 is 88,200 frames, where there are 44,100 frames to the second.
|
Use this to set the \fIdefault latency\f1, in frames, for audio coming from an unidentified source or from an iTunes Version 9 or earlier source. The standard value for the \fIdefault latency\f1 is 88,200 frames, where there are 44,100 frames to the second.
|
||||||
|
|
||||||
@@ -131,7 +126,7 @@ Resynchronise if timings differ by more than \fIthreshold\f1 frames. If the outp
|
|||||||
On an AirPlay-2-capable Shairport Sync, if you need offer only the older "classic" AirPlay (sometimes called AirPlay 1) service, set \fB--service-type=classic\f1. A potential use of this would be for Apple Music on Windows, which is not compatible with the AirPlay 2 service offered by Shairport Sync, but which is compatible with the classic-only service. To offer AirPlay 2 service only, set \fB--service-type=airplay2\f1 (note that this requires NQPTP). Set \fB--service-type=auto\f1 to allow Shairport Sync to choose AirPlay 2 service if NQPTP is present or classic AirPlay service if not, with "(Classic)" appended to the default service name.
|
On an AirPlay-2-capable Shairport Sync, if you need offer only the older "classic" AirPlay (sometimes called AirPlay 1) service, set \fB--service-type=classic\f1. A potential use of this would be for Apple Music on Windows, which is not compatible with the AirPlay 2 service offered by Shairport Sync, but which is compatible with the classic-only service. To offer AirPlay 2 service only, set \fB--service-type=airplay2\f1 (note that this requires NQPTP). Set \fB--service-type=auto\f1 to allow Shairport Sync to choose AirPlay 2 service if NQPTP is present or classic AirPlay service if not, with "(Classic)" appended to the default service name.
|
||||||
.TP
|
.TP
|
||||||
\fB--statistics\f1
|
\fB--statistics\f1
|
||||||
Print some performance information to \fISTDERR\f1, or to \fBsyslog\f1 if the \fB--log-to-syslog\f1 command line option is also chosen.
|
Print some performance information to \fISTDERR\f1.
|
||||||
.TP
|
.TP
|
||||||
\fB-S \f1\fImode\f1\fB | --stuffing=\f1\fImode\f1
|
\fB-S \f1\fImode\f1\fB | --stuffing=\f1\fImode\f1
|
||||||
Interpolate ("stuff") the audio stream using the \fImode\f1. "Stuffing" refers to the process of adding or removing frames of audio to or from the stream sent to the output device in order to keep it synchronised with the player. The \fBvernier\f1 mode is normally almost completely inaudible and suitable for low-powered devices. An alternative mode, \fBsoxr\f1, is less obtrusive but requires much more processing power. For this mode, support for \fBlibsoxr\f1, the SoX Resampler Library, must be selected when \fBshairport-sync\f1 is built. The \fBbasic\f1 mode is also suitable for low-powered device, but \fBvernier\f1 is better. The default setting, \fBauto\f1, allows Shairport Sync to choose \fBsoxr\f1 mode if the system is powerful enough.
|
Interpolate ("stuff") the audio stream using the \fImode\f1. "Stuffing" refers to the process of adding or removing frames of audio to or from the stream sent to the output device in order to keep it synchronised with the player. The \fBvernier\f1 mode is normally almost completely inaudible and suitable for low-powered devices. An alternative mode, \fBsoxr\f1, is less obtrusive but requires much more processing power. For this mode, support for \fBlibsoxr\f1, the SoX Resampler Library, must be selected when \fBshairport-sync\f1 is built. The \fBbasic\f1 mode is also suitable for low-powered device, but \fBvernier\f1 is better. The default setting, \fBauto\f1, allows Shairport Sync to choose \fBsoxr\f1 mode if the system is powerful enough.
|
||||||
@@ -148,7 +143,7 @@ Allow playback to be up to \fIframes\f1 out of exact synchronization before atte
|
|||||||
Print version information and exit.
|
Print version information and exit.
|
||||||
.TP
|
.TP
|
||||||
\fB-v | --verbose\f1
|
\fB-v | --verbose\f1
|
||||||
Print debug information to the \fISTDERR\f1, or to \fBsyslog\f1 if the \fB--log-to-syslog\f1 command line option is also chosen. Repeat up to three times (i.e. \fB-vv\f1 or \fB-vvv\f1) for more detail. You should use \fB-vvv\f1 very sparingly -- it is really noisy.
|
Print debug information to the \fISTDERR\f1. Repeat up to three times (i.e. \fB-vv\f1 or \fB-vvv\f1) for more detail. You should use \fB-vvv\f1 very sparingly -- it is really noisy.
|
||||||
.TP
|
.TP
|
||||||
\fB-w | --wait-cmd\f1
|
\fB-w | --wait-cmd\f1
|
||||||
Wait for commands specified using \fB-B\f1 or \fB-E\f1 to complete before continuing execution.
|
Wait for commands specified using \fB-B\f1 or \fB-E\f1 to complete before continuing execution.
|
||||||
|
|||||||
@@ -45,7 +45,6 @@
|
|||||||
<opt>[-g | --get-cover-art]</opt>
|
<opt>[-g | --get-cover-art]</opt>
|
||||||
<opt>[-j | --justDaemoniseNoPIDFile]</opt>
|
<opt>[-j | --justDaemoniseNoPIDFile]</opt>
|
||||||
<opt>[--logOutputLevel]</opt>
|
<opt>[--logOutputLevel]</opt>
|
||||||
<opt>[--log-to-syslog]</opt>
|
|
||||||
<opt>[-L </opt><arg>latency</arg><opt> | --latency=</opt><arg>latency</arg><opt>]</opt>
|
<opt>[-L </opt><arg>latency</arg><opt> | --latency=</opt><arg>latency</arg><opt>]</opt>
|
||||||
<opt>[-m </opt><arg>backend</arg><opt> | --mdns=</opt><arg>backend</arg><opt>]</opt>
|
<opt>[-m </opt><arg>backend</arg><opt> | --mdns=</opt><arg>backend</arg><opt>]</opt>
|
||||||
<opt>[-M | --metadata-enable]</opt>
|
<opt>[-M | --metadata-enable]</opt>
|
||||||
@@ -269,16 +268,6 @@
|
|||||||
</optdesc>
|
</optdesc>
|
||||||
</option>
|
</option>
|
||||||
|
|
||||||
<option>
|
|
||||||
<p><opt>--log-to-syslog</opt></p>
|
|
||||||
<optdesc><p>
|
|
||||||
Warnings, error messages and messages are sent, by default, to <file>STDERR</file>.
|
|
||||||
Use this option to route these messages to the <opt>syslog</opt> instead.
|
|
||||||
This is intended for use when Shairport Sync is operating as a daemon.
|
|
||||||
</p><p>See also <opt>--displayConfig</opt>.</p>
|
|
||||||
</optdesc>
|
|
||||||
</option>
|
|
||||||
|
|
||||||
<option>
|
<option>
|
||||||
<p><opt>-L | --latency=</opt><arg>latency</arg></p>
|
<p><opt>-L | --latency=</opt><arg>latency</arg></p>
|
||||||
<optdesc><p>
|
<optdesc><p>
|
||||||
@@ -381,7 +370,7 @@
|
|||||||
<option>
|
<option>
|
||||||
<p><opt>--statistics</opt></p>
|
<p><opt>--statistics</opt></p>
|
||||||
<optdesc><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>.
|
||||||
</p></optdesc>
|
</p></optdesc>
|
||||||
</option>
|
</option>
|
||||||
|
|
||||||
@@ -446,7 +435,7 @@
|
|||||||
<option>
|
<option>
|
||||||
<p><opt>-v | --verbose</opt></p>
|
<p><opt>-v | --verbose</opt></p>
|
||||||
<optdesc><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>.
|
||||||
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.
|
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>
|
</p></optdesc>
|
||||||
</option>
|
</option>
|
||||||
|
|||||||
+3
-3
@@ -81,11 +81,11 @@ int pc_queue_add_item(pc_queue *the_queue, const void *the_stuff, int block) {
|
|||||||
int rc;
|
int rc;
|
||||||
if (the_queue) {
|
if (the_queue) {
|
||||||
if (block == 0) {
|
if (block == 0) {
|
||||||
rc = debug_mutex_lock(&the_queue->pc_queue_lock, 10000, 4);
|
rc = pthread_mutex_lock(&the_queue->pc_queue_lock);
|
||||||
if (rc == EBUSY)
|
if (rc == EBUSY)
|
||||||
return EBUSY;
|
return EBUSY;
|
||||||
} else
|
} else
|
||||||
rc = debug_mutex_lock(&the_queue->pc_queue_lock, 50000, 4);
|
rc = pthread_mutex_lock(&the_queue->pc_queue_lock);
|
||||||
if (rc)
|
if (rc)
|
||||||
debug(1, "Error %d (\"%s\") locking for pc_queue_add_item. Block is %d.", rc, strerror(rc),
|
debug(1, "Error %d (\"%s\") locking for pc_queue_add_item. Block is %d.", rc, strerror(rc),
|
||||||
block);
|
block);
|
||||||
@@ -137,7 +137,7 @@ int pc_queue_add_item(pc_queue *the_queue, const void *the_stuff, int block) {
|
|||||||
int pc_queue_get_item(pc_queue *the_queue, void *the_stuff) {
|
int pc_queue_get_item(pc_queue *the_queue, void *the_stuff) {
|
||||||
int rc;
|
int rc;
|
||||||
if (the_queue) {
|
if (the_queue) {
|
||||||
rc = debug_mutex_lock(&the_queue->pc_queue_lock, 50000, 4);
|
rc = pthread_mutex_lock(&the_queue->pc_queue_lock);
|
||||||
if (rc)
|
if (rc)
|
||||||
debug(1, "metadata queue \"%s\": error locking for pc_queue_get_item", the_queue->name);
|
debug(1, "metadata queue \"%s\": error locking for pc_queue_get_item", the_queue->name);
|
||||||
pthread_cleanup_push(pc_queue_cleanup_handler, (void *)the_queue);
|
pthread_cleanup_push(pc_queue_cleanup_handler, (void *)the_queue);
|
||||||
|
|||||||
+2
-2
@@ -41,6 +41,7 @@
|
|||||||
|
|
||||||
#include "metadata/hub.h"
|
#include "metadata/hub.h"
|
||||||
#include "mpris-service.h"
|
#include "mpris-service.h"
|
||||||
|
#include "utilities/exit.h"
|
||||||
|
|
||||||
static guint ownerID = 0;
|
static guint ownerID = 0;
|
||||||
static GBusType mpris_bus_type = G_BUS_TYPE_SYSTEM; // default is the dbus system message bus
|
static GBusType mpris_bus_type = G_BUS_TYPE_SYSTEM; // default is the dbus system message bus
|
||||||
@@ -234,9 +235,8 @@ void mpris_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused)
|
|||||||
static gboolean on_handle_quit(MediaPlayer2 *skeleton, GDBusMethodInvocation *invocation,
|
static gboolean on_handle_quit(MediaPlayer2 *skeleton, GDBusMethodInvocation *invocation,
|
||||||
__attribute__((unused)) gpointer user_data) {
|
__attribute__((unused)) gpointer user_data) {
|
||||||
debug(1, ">> quit request...");
|
debug(1, ">> quit request...");
|
||||||
config.quit_requested_from_glib_mainloop = 1;
|
|
||||||
g_main_loop_quit(config.glib_worker_loop);
|
|
||||||
media_player2_complete_quit(skeleton, invocation);
|
media_player2_complete_quit(skeleton, invocation);
|
||||||
|
exit_request(EXIT_SUCCESS);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#include <ctype.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -70,7 +71,8 @@ void on_message(__attribute__((unused)) struct mosquitto *mosq,
|
|||||||
char *commands[] = {"command", "beginff", "beginrew", "mutetoggle",
|
char *commands[] = {"command", "beginff", "beginrew", "mutetoggle",
|
||||||
"nextitem", "previtem", "pause", "playpause",
|
"nextitem", "previtem", "pause", "playpause",
|
||||||
"play", "stop", "playresume", "shuffle_songs",
|
"play", "stop", "playresume", "shuffle_songs",
|
||||||
"volumedown", "volumeup", "disconnect", NULL};
|
"volumedown", "volumeup", "disconnect", "queue_next",
|
||||||
|
NULL};
|
||||||
|
|
||||||
int it = 0;
|
int it = 0;
|
||||||
|
|
||||||
@@ -82,6 +84,25 @@ void on_message(__attribute__((unused)) struct mosquitto *mosq,
|
|||||||
if (strcmp(commands[it], "disconnect") == 0) {
|
if (strcmp(commands[it], "disconnect") == 0) {
|
||||||
debug(2, "[MQTT]: Disconnect Command: %s\n", commands[it]);
|
debug(2, "[MQTT]: Disconnect Command: %s\n", commands[it]);
|
||||||
stop_play(); // stop any current session and don't replace it
|
stop_play(); // stop any current session and don't replace it
|
||||||
|
} else if (strcmp(commands[it], "queue_next") == 0) {
|
||||||
|
// payload is "queue_next <track_id>", where <track_id> is the hex track_id
|
||||||
|
// string as published by shairport-sync itself (see the "mper"/"track_id" handling
|
||||||
|
// above), e.g. "queue_next 1A2B3C4D5E6F7"
|
||||||
|
char *track_id = payload + strlen(commands[it]);
|
||||||
|
while (*track_id == ' ' || *track_id == '\t')
|
||||||
|
track_id++;
|
||||||
|
size_t track_id_len = strlen(track_id);
|
||||||
|
while (track_id_len > 0 && isspace((unsigned char)track_id[track_id_len - 1]))
|
||||||
|
track_id[--track_id_len] = '\0';
|
||||||
|
if (track_id_len == 0) {
|
||||||
|
warn("[MQTT]: queue_next command received with no track_id -- ignoring.");
|
||||||
|
} else {
|
||||||
|
char dacp_command[256];
|
||||||
|
snprintf(dacp_command, sizeof(dacp_command),
|
||||||
|
"cue?command=add&query='dmap.persistentid:0x%s'&mode=3", track_id);
|
||||||
|
debug(2, "[MQTT]: Queue Next Command: %s\n", dacp_command);
|
||||||
|
send_simple_dacp_command(dacp_command);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
debug(2, "[MQTT]: DACP Command: %s\n", commands[it]);
|
debug(2, "[MQTT]: DACP Command: %s\n", commands[it]);
|
||||||
#ifdef CONFIG_DACP_CLIENT
|
#ifdef CONFIG_DACP_CLIENT
|
||||||
@@ -301,7 +322,6 @@ void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t le
|
|||||||
if (config.mqtt_publish_parsed) {
|
if (config.mqtt_publish_parsed) {
|
||||||
if (type == 'core') {
|
if (type == 'core') {
|
||||||
int32_t r;
|
int32_t r;
|
||||||
uint64_t trackid;
|
|
||||||
char trackidstring[32];
|
char trackidstring[32];
|
||||||
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
@@ -321,8 +341,12 @@ void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t le
|
|||||||
mqtt_publish("title", data, length);
|
mqtt_publish("title", data, length);
|
||||||
break;
|
break;
|
||||||
case 'mper':
|
case 'mper':
|
||||||
trackid = *(uint64_t *)(data);
|
// publish the raw persistent-id bytes as hex, in the order received --
|
||||||
r = snprintf(trackidstring, sizeof(trackidstring), "%" PRIX64 "", trackid);
|
// no byte-order or integer conversion, so this can't reverse the value.
|
||||||
|
r = 0;
|
||||||
|
for (uint32_t i = 0; i < length && r < (int32_t)sizeof(trackidstring) - 2; i++)
|
||||||
|
r += snprintf(trackidstring + r, sizeof(trackidstring) - (size_t)r, "%02X",
|
||||||
|
(unsigned char)data[i]);
|
||||||
mqtt_publish("track_id", trackidstring, r);
|
mqtt_publish("track_id", trackidstring, r);
|
||||||
}
|
}
|
||||||
} else if (type == 'ssnc') {
|
} else if (type == 'ssnc') {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Modifications for audio synchronisation, AirPlay 2
|
* Modifications for audio synchronisation, AirPlay 2
|
||||||
* and related work, copyright (c) Mike Brady 2014--2025
|
* and related work, copyright (c) Mike Brady 2014--2026
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person
|
* Permission is hereby granted, free of charge, to any person
|
||||||
@@ -332,7 +332,7 @@ static void free_audio_buffers(rtsp_conn_info *conn) {
|
|||||||
int first_possibly_missing_frame = -1;
|
int first_possibly_missing_frame = -1;
|
||||||
|
|
||||||
void reset_buffer(rtsp_conn_info *conn) {
|
void reset_buffer(rtsp_conn_info *conn) {
|
||||||
pthread_cleanup_debug_mutex_lock(&conn->ab_mutex, 30000, 0);
|
pthread_mutex_lock_and_cleanup_push(&conn->ab_mutex);
|
||||||
ab_resync(conn);
|
ab_resync(conn);
|
||||||
pthread_cleanup_pop(1);
|
pthread_cleanup_pop(1);
|
||||||
#if CONFIG_FFMPEG
|
#if CONFIG_FFMPEG
|
||||||
@@ -349,7 +349,7 @@ void reset_buffer(rtsp_conn_info *conn) {
|
|||||||
|
|
||||||
size_t get_audio_buffer_occupancy(rtsp_conn_info *conn) {
|
size_t get_audio_buffer_occupancy(rtsp_conn_info *conn) {
|
||||||
size_t response = 0;
|
size_t response = 0;
|
||||||
pthread_cleanup_debug_mutex_lock(&conn->ab_mutex, 30000, 0);
|
pthread_mutex_lock_and_cleanup_push(&conn->ab_mutex);
|
||||||
if (conn->ab_synced) {
|
if (conn->ab_synced) {
|
||||||
int16_t occ =
|
int16_t occ =
|
||||||
conn->ab_write - conn->ab_read; // will be zero or positive if read and write are within
|
conn->ab_write - conn->ab_read; // will be zero or positive if read and write are within
|
||||||
@@ -1412,7 +1412,7 @@ seq_t get_revised_seqno(rtsp_conn_info *conn, uint32_t timestamp) {
|
|||||||
// go back through the buffers to find the first buffer following a buffer that predates
|
// go back through the buffers to find the first buffer following a buffer that predates
|
||||||
// the given timestamp, if any.
|
// the given timestamp, if any.
|
||||||
seq_t revised_seqno = conn->ab_write;
|
seq_t revised_seqno = conn->ab_write;
|
||||||
pthread_cleanup_debug_mutex_lock(&conn->ab_mutex, 30000, 0);
|
pthread_mutex_lock_and_cleanup_push(&conn->ab_mutex);
|
||||||
int older_seqno_found = 0;
|
int older_seqno_found = 0;
|
||||||
while ((older_seqno_found == 0) && (revised_seqno != conn->ab_read)) {
|
while ((older_seqno_found == 0) && (revised_seqno != conn->ab_read)) {
|
||||||
revised_seqno--;
|
revised_seqno--;
|
||||||
@@ -1478,18 +1478,18 @@ uint32_t player_put_packet(uint32_t ssrc, seq_t seqno, uint32_t actual_timestamp
|
|||||||
|
|
||||||
// ignore a request to flush that has been made before the first packet...
|
// ignore a request to flush that has been made before the first packet...
|
||||||
if (conn->packet_count == 0) {
|
if (conn->packet_count == 0) {
|
||||||
debug_mutex_lock(&conn->flush_mutex, 1000, 4);
|
pthread_mutex_lock(&conn->flush_mutex);
|
||||||
conn->flush_requested = 0;
|
conn->flush_requested = 0;
|
||||||
conn->flush_rtp_timestamp = 0;
|
conn->flush_rtp_timestamp = 0;
|
||||||
debug_mutex_unlock(&conn->flush_mutex, 4);
|
pthread_mutex_unlock(&conn->flush_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_cleanup_debug_mutex_lock(&conn->ab_mutex, 30000, 0);
|
pthread_mutex_lock_and_cleanup_push(&conn->ab_mutex);
|
||||||
uint64_t time_now = get_absolute_time_in_ns();
|
uint64_t time_now = get_absolute_time_in_ns();
|
||||||
conn->packet_count++;
|
conn->packet_count++;
|
||||||
conn->packet_count_since_flush++;
|
conn->packet_count_since_flush++;
|
||||||
conn->time_of_last_audio_packet = time_now;
|
conn->time_of_last_audio_packet = time_now;
|
||||||
if (conn->connection_state_to_output) { // if we are supposed to be processing these packets
|
// if (conn->connection_state_to_output) { // if we are supposed to be processing these packets
|
||||||
abuf_t *abuf = 0;
|
abuf_t *abuf = 0;
|
||||||
if (!conn->ab_synced) {
|
if (!conn->ab_synced) {
|
||||||
conn->ab_write = seqno;
|
conn->ab_write = seqno;
|
||||||
@@ -1795,7 +1795,7 @@ uint32_t player_put_packet(uint32_t ssrc, seq_t seqno, uint32_t actual_timestamp
|
|||||||
if (number_of_missing_frames == 0)
|
if (number_of_missing_frames == 0)
|
||||||
first_possibly_missing_frame = conn->ab_write;
|
first_possibly_missing_frame = conn->ab_write;
|
||||||
}
|
}
|
||||||
}
|
// } // remove this
|
||||||
pthread_cleanup_pop(1);
|
pthread_cleanup_pop(1);
|
||||||
// debug_mutex_unlock(&conn->ab_mutex, 0);
|
// debug_mutex_unlock(&conn->ab_mutex, 0);
|
||||||
return input_packets_used;
|
return input_packets_used;
|
||||||
@@ -2044,7 +2044,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn, int resync_requested) {
|
|||||||
abuf_t *curframe = NULL;
|
abuf_t *curframe = NULL;
|
||||||
int notified_buffer_empty = 0; // diagnostic only
|
int notified_buffer_empty = 0; // diagnostic only
|
||||||
|
|
||||||
pthread_cleanup_debug_mutex_lock(&conn->ab_mutex, 30000, 0);
|
pthread_mutex_lock_and_cleanup_push(&conn->ab_mutex);
|
||||||
|
|
||||||
int wait;
|
int wait;
|
||||||
long dac_delay = 0; // long because alsa returns a long
|
long dac_delay = 0; // long because alsa returns a long
|
||||||
@@ -2059,6 +2059,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn, int resync_requested) {
|
|||||||
// we must have timing information before we can do anything here
|
// we must have timing information before we can do anything here
|
||||||
if ((have_timestamp_timing_information(conn)) && (conn->input_format_is_valid != 0)) {
|
if ((have_timestamp_timing_information(conn)) && (conn->input_format_is_valid != 0)) {
|
||||||
|
|
||||||
|
/*
|
||||||
int rco = get_requested_connection_state_to_output();
|
int rco = get_requested_connection_state_to_output();
|
||||||
|
|
||||||
if (conn->connection_state_to_output != rco) {
|
if (conn->connection_state_to_output != rco) {
|
||||||
@@ -2066,23 +2067,23 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn, int resync_requested) {
|
|||||||
// change happening
|
// change happening
|
||||||
if (conn->connection_state_to_output == 0) { // going off
|
if (conn->connection_state_to_output == 0) { // going off
|
||||||
debug(2, "request flush because connection_state_to_output is off");
|
debug(2, "request flush because connection_state_to_output is off");
|
||||||
debug_mutex_lock(&conn->flush_mutex, 1000, 1);
|
pthread_mutex_lock(&conn->flush_mutex);
|
||||||
conn->flush_requested = 1;
|
conn->flush_requested = 1;
|
||||||
conn->flush_rtp_timestamp = 0;
|
conn->flush_rtp_timestamp = 0;
|
||||||
debug_mutex_unlock(&conn->flush_mutex, 3);
|
pthread_mutex_unlock(&conn->flush_mutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
if (config.output->is_running)
|
if (config.output->is_running)
|
||||||
if (config.output->is_running() != 0) { // if the back end isn't running for any reason
|
if (config.output->is_running() != 0) { // if the back end isn't running for any reason
|
||||||
debug(2, "request flush because back end is not running");
|
debug(2, "request flush because back end is not running");
|
||||||
debug_mutex_lock(&conn->flush_mutex, 1000, 0);
|
pthread_mutex_lock(&conn->flush_mutex);
|
||||||
conn->flush_requested = 1;
|
conn->flush_requested = 1;
|
||||||
conn->flush_rtp_timestamp = 0;
|
conn->flush_rtp_timestamp = 0;
|
||||||
debug_mutex_unlock(&conn->flush_mutex, 0);
|
pthread_mutex_unlock(&conn->flush_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_cleanup_debug_mutex_lock(&conn->flush_mutex, 1000, 0);
|
pthread_mutex_lock_and_cleanup_push(&conn->flush_mutex);
|
||||||
if (conn->flush_requested == 1) {
|
if (conn->flush_requested == 1) {
|
||||||
if (conn->flush_output_flushed == 0) {
|
if (conn->flush_output_flushed == 0) {
|
||||||
#if CONFIG_FFMPEG
|
#if CONFIG_FFMPEG
|
||||||
@@ -3557,7 +3558,7 @@ void *player_thread_func(void *arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
conn->session_corrections = 0;
|
conn->session_corrections = 0;
|
||||||
conn->connection_state_to_output = get_requested_connection_state_to_output();
|
// conn->connection_state_to_output = get_requested_connection_state_to_output();
|
||||||
|
|
||||||
int number_of_statistics, oldest_statistic, newest_statistic;
|
int number_of_statistics, oldest_statistic, newest_statistic;
|
||||||
uint32_t frames_since_last_stats_logged = 0;
|
uint32_t frames_since_last_stats_logged = 0;
|
||||||
@@ -4625,20 +4626,18 @@ void *player_thread_func(void *arg) {
|
|||||||
// Apply convolution
|
// Apply convolution
|
||||||
// First, have we got the right convolution setup?
|
// First, have we got the right convolution setup?
|
||||||
|
|
||||||
|
static int convolver_error_notified = 0;
|
||||||
static int convolver_is_valid = 0;
|
static int convolver_is_valid = 0;
|
||||||
static size_t current_convolver_block_size = 0;
|
// static size_t current_convolver_block_size = 0;
|
||||||
static unsigned int current_convolver_rate = 0;
|
static unsigned int current_convolver_rate = 0;
|
||||||
static unsigned int current_convolver_channels = 0;
|
static unsigned int current_convolver_channels = 0;
|
||||||
static double current_convolver_maximum_length_in_seconds = 0;
|
static double current_convolver_maximum_length_in_seconds = 0;
|
||||||
|
|
||||||
if (config.convolution_enabled) {
|
if (config.convolution_enabled) {
|
||||||
if (
|
if (
|
||||||
// if any of these are true, we need to create a new convolver
|
// if any of these are true, we need to create a new convolver
|
||||||
// (conn->convolver_is_valid == 0) ||
|
(current_convolver_rate != RATE_FROM_ENCODED_FORMAT(config.current_output_configuration)) ||
|
||||||
(current_convolver_block_size != inframe->length) ||
|
(current_convolver_channels != CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration)) ||
|
||||||
(current_convolver_rate != conn->input_rate) ||
|
|
||||||
!((current_convolver_channels == 1) ||
|
|
||||||
(current_convolver_channels == conn->input_num_channels)) ||
|
|
||||||
(current_convolver_maximum_length_in_seconds !=
|
(current_convolver_maximum_length_in_seconds !=
|
||||||
config.convolution_max_length_in_seconds) ||
|
config.convolution_max_length_in_seconds) ||
|
||||||
(config.convolution_ir_files_updated == 1)) {
|
(config.convolution_ir_files_updated == 1)) {
|
||||||
@@ -4646,13 +4645,12 @@ void *player_thread_func(void *arg) {
|
|||||||
// look for a convolution ir file with a matching rate and channel count
|
// look for a convolution ir file with a matching rate and channel count
|
||||||
|
|
||||||
convolver_is_valid = 0; // declare any current convolver as invalid
|
convolver_is_valid = 0; // declare any current convolver as invalid
|
||||||
current_convolver_block_size = inframe->length;
|
current_convolver_rate = RATE_FROM_ENCODED_FORMAT(config.current_output_configuration);
|
||||||
current_convolver_rate = conn->input_rate;
|
current_convolver_channels = CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration);
|
||||||
current_convolver_channels = conn->input_num_channels;
|
|
||||||
current_convolver_maximum_length_in_seconds =
|
current_convolver_maximum_length_in_seconds =
|
||||||
config.convolution_max_length_in_seconds;
|
config.convolution_max_length_in_seconds;
|
||||||
config.convolution_ir_files_updated = 0;
|
config.convolution_ir_files_updated = 0;
|
||||||
debug(2, "try to initialise a %u/%u convolver.", current_convolver_rate,
|
debug(3, "looking for a %u/%u finite impulse response file.", current_convolver_rate,
|
||||||
current_convolver_channels);
|
current_convolver_channels);
|
||||||
char *convolver_file_found = NULL;
|
char *convolver_file_found = NULL;
|
||||||
unsigned int ir = 0;
|
unsigned int ir = 0;
|
||||||
@@ -4667,55 +4665,58 @@ void *player_thread_func(void *arg) {
|
|||||||
ir++;
|
ir++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if no luck, try for a single-channel IR file
|
// if no luck, try for a single-channel IR file
|
||||||
if (convolver_file_found == NULL) {
|
if (convolver_file_found == NULL) {
|
||||||
current_convolver_channels = 1;
|
|
||||||
ir = 0;
|
ir = 0;
|
||||||
|
debug(3, "looking for a %u/1 finite impulse response file.", current_convolver_rate);
|
||||||
while ((ir < config.convolution_ir_file_count) &&
|
while ((ir < config.convolution_ir_file_count) &&
|
||||||
(convolver_file_found == NULL)) {
|
(convolver_file_found == NULL)) {
|
||||||
if ((config.convolution_ir_files[ir].samplerate ==
|
if ((config.convolution_ir_files[ir].samplerate ==
|
||||||
current_convolver_rate) &&
|
current_convolver_rate) &&
|
||||||
(config.convolution_ir_files[ir].channels ==
|
(config.convolution_ir_files[ir].channels == 1)) {
|
||||||
current_convolver_channels)) {
|
|
||||||
convolver_file_found = config.convolution_ir_files[ir].filename;
|
convolver_file_found = config.convolution_ir_files[ir].filename;
|
||||||
} else {
|
} else {
|
||||||
ir++;
|
ir++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (convolver_file_found != NULL) {
|
||||||
|
debug(1, "The %u/1 finite impulse response file \"%s\" will be used for convolution.", current_convolver_rate,
|
||||||
|
convolver_file_found);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug(1, "The %u/%u finite impulse response file \"%s\" will be used for convolution.", current_convolver_rate,
|
||||||
|
current_convolver_channels, convolver_file_found);
|
||||||
}
|
}
|
||||||
if (convolver_file_found != NULL) {
|
if (convolver_file_found != NULL) {
|
||||||
// we have an apparently suitable convolution ir file, so lets initialise
|
// we have an apparently suitable convolution ir file, so lets initialise
|
||||||
// a convolver
|
// a convolver
|
||||||
convolver_is_valid = convolver_init(
|
convolver_is_valid = convolver_init(
|
||||||
convolver_file_found, conn->input_num_channels,
|
convolver_file_found, current_convolver_channels,
|
||||||
config.convolution_max_length_in_seconds, inframe->length);
|
config.convolution_max_length_in_seconds, 1024); // power of 2 suggested for the block length
|
||||||
convolver_wait_for_all();
|
convolver_wait_for_all();
|
||||||
// if (convolver_is_valid)
|
if ((convolver_is_valid == 0) && (convolver_error_notified == 0)) {
|
||||||
// debug(1, "convolver_init for %u channels was successful.",
|
debug(1, "can not initialise a %u/%u convolver from the \"%s\" finite impulse response file.", current_convolver_rate,
|
||||||
// conn->input_num_channels); convolver_is_valid = convolver_init(
|
current_convolver_channels, convolver_file_found);
|
||||||
// convolver_file_found, conn->input_num_channels,
|
convolver_error_notified = 1;
|
||||||
// config.convolution_max_length_in_seconds, inframe->length);
|
}
|
||||||
|
} else if (convolver_error_notified == 0) {
|
||||||
|
debug(1, "Convolution is disabled because a suitable %u/%u or %u/1 finite impulse response file can not be found.", current_convolver_rate, current_convolver_channels, current_convolver_rate);
|
||||||
|
convolver_error_notified = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (convolver_is_valid == 0)
|
|
||||||
debug(1, "can not initialise a %u/%u convolver.", current_convolver_rate,
|
|
||||||
conn->input_num_channels);
|
|
||||||
else
|
|
||||||
debug(1, "convolver: \"%s\".", convolver_file_found);
|
|
||||||
}
|
}
|
||||||
if (convolver_is_valid != 0) {
|
if (convolver_is_valid != 0) {
|
||||||
for (j = 0; j < conn->input_num_channels; j++) {
|
for (j = 0; j < current_convolver_channels; j++) {
|
||||||
// convolver_process(j, fbufs[j], inframe->length);
|
|
||||||
convolver_process(j, fbufs[j], inframe->length);
|
convolver_process(j, fbufs[j], inframe->length);
|
||||||
}
|
}
|
||||||
convolver_wait_for_all();
|
convolver_wait_for_all();
|
||||||
|
convolver_error_notified = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply convolution gain even if no convolution is done...
|
// apply convolution gain even if no convolution is done...
|
||||||
float gain = pow(10.0, config.convolution_gain / 20.0);
|
float gain = pow(10.0, config.convolution_gain / 20.0);
|
||||||
for (i = 0; i < inframe->length; ++i) {
|
for (i = 0; i < inframe->length; ++i) {
|
||||||
for (j = 0; j < conn->input_num_channels; j++) {
|
for (j = 0; j < current_convolver_channels; j++) {
|
||||||
float output_level_db = 0.0;
|
float output_level_db = 0.0;
|
||||||
if (fbufs[j][i] < 0.0)
|
if (fbufs[j][i] < 0.0)
|
||||||
output_level_db = 20 * log10(fbufs[j][i] / (float)INT32_MIN * 1.0);
|
output_level_db = 20 * log10(fbufs[j][i] / (float)INT32_MIN * 1.0);
|
||||||
@@ -4732,17 +4733,16 @@ void *player_thread_func(void *arg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
if (conn->do_loudness) {
|
if (conn->do_loudness) {
|
||||||
loudness_process_blocks((float *)fbufs, inframe->length,
|
loudness_process_blocks((float *)fbufs, inframe->length,
|
||||||
conn->input_num_channels,
|
CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration),
|
||||||
(float)conn->fix_volume / 65536);
|
(float)conn->fix_volume / 65536);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interleave and convert back to int32_t
|
// Interleave and convert back to int32_t
|
||||||
for (i = 0; i < inframe->length; i++) {
|
for (i = 0; i < inframe->length; i++) {
|
||||||
for (j = 0; j < conn->input_num_channels; j++) {
|
for (j = 0; j < CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration); j++) {
|
||||||
tbuf32[conn->input_num_channels * i + j] = fbufs[j][i];
|
tbuf32[conn->input_num_channels * i + j] = fbufs[j][i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5017,7 +5017,7 @@ static void player_send_volume_metadata(uint8_t vol_mode_both, double airplay_vo
|
|||||||
}
|
}
|
||||||
|
|
||||||
void player_volume_without_notification(double airplay_volume, rtsp_conn_info *conn) {
|
void player_volume_without_notification(double airplay_volume, rtsp_conn_info *conn) {
|
||||||
debug_mutex_lock(&conn->volume_control_mutex, 5000, 4);
|
pthread_mutex_lock(&conn->volume_control_mutex);
|
||||||
// first, see if we are hw only, sw only, both with hw attenuation on the top or both with sw
|
// first, see if we are hw only, sw only, both with hw attenuation on the top or both with sw
|
||||||
// attenuation on top
|
// attenuation on top
|
||||||
|
|
||||||
@@ -5235,7 +5235,7 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c
|
|||||||
// here, store the volume for possible use in the future
|
// here, store the volume for possible use in the future
|
||||||
config.airplay_volume = airplay_volume;
|
config.airplay_volume = airplay_volume;
|
||||||
conn->own_airplay_volume = airplay_volume;
|
conn->own_airplay_volume = airplay_volume;
|
||||||
debug_mutex_unlock(&conn->volume_control_mutex, 4);
|
pthread_mutex_unlock(&conn->volume_control_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void player_volume(double airplay_volume, rtsp_conn_info *conn) {
|
void player_volume(double airplay_volume, rtsp_conn_info *conn) {
|
||||||
@@ -5246,11 +5246,11 @@ void player_volume(double airplay_volume, rtsp_conn_info *conn) {
|
|||||||
void do_flush(uint32_t timestamp, rtsp_conn_info *conn) {
|
void do_flush(uint32_t timestamp, rtsp_conn_info *conn) {
|
||||||
|
|
||||||
debug(3, "do_flush: flush to %u.", timestamp);
|
debug(3, "do_flush: flush to %u.", timestamp);
|
||||||
debug_mutex_lock(&conn->flush_mutex, 1000, 1);
|
pthread_mutex_lock(&conn->flush_mutex);
|
||||||
conn->flush_requested = 1;
|
conn->flush_requested = 1;
|
||||||
conn->flush_rtp_timestamp = timestamp; // flush all packets up to, but not including, this one.
|
conn->flush_rtp_timestamp = timestamp; // flush all packets up to, but not including, this one.
|
||||||
reset_input_flow_metrics(conn);
|
reset_input_flow_metrics(conn);
|
||||||
debug_mutex_unlock(&conn->flush_mutex, 3);
|
pthread_mutex_unlock(&conn->flush_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void player_flush(uint32_t timestamp, rtsp_conn_info *conn) {
|
void player_flush(uint32_t timestamp, rtsp_conn_info *conn) {
|
||||||
@@ -5280,7 +5280,7 @@ int player_play(rtsp_conn_info *conn) {
|
|||||||
// access to the output device (i.e. knowing that it should not be in
|
// access to the output device (i.e. knowing that it should not be in
|
||||||
// use by another program at this time).
|
// use by another program at this time).
|
||||||
|
|
||||||
pthread_cleanup_debug_mutex_lock(&conn->player_create_delete_mutex, 5000, 1);
|
pthread_mutex_lock_and_cleanup_push(&conn->player_create_delete_mutex);
|
||||||
if (conn->player_thread == NULL) {
|
if (conn->player_thread == NULL) {
|
||||||
pthread_t *pt = malloc(sizeof(pthread_t));
|
pthread_t *pt = malloc(sizeof(pthread_t));
|
||||||
if (pt == NULL)
|
if (pt == NULL)
|
||||||
@@ -5307,7 +5307,7 @@ int player_stop(rtsp_conn_info *conn) {
|
|||||||
// note -- this may be called from another connection thread.
|
// note -- this may be called from another connection thread.
|
||||||
debug(2, "Connection %d: player_stop.", conn->connection_number);
|
debug(2, "Connection %d: player_stop.", conn->connection_number);
|
||||||
int response = 0; // okay
|
int response = 0; // okay
|
||||||
pthread_cleanup_debug_mutex_lock(&conn->player_create_delete_mutex, 5000, 4);
|
pthread_mutex_lock_and_cleanup_push(&conn->player_create_delete_mutex);
|
||||||
pthread_t *pt = conn->player_thread;
|
pthread_t *pt = conn->player_thread;
|
||||||
if (pt) {
|
if (pt) {
|
||||||
debug(3, "player_thread cancel...");
|
debug(3, "player_thread cancel...");
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ typedef struct {
|
|||||||
alac_file *decoder_info;
|
alac_file *decoder_info;
|
||||||
uint64_t packet_count;
|
uint64_t packet_count;
|
||||||
uint64_t packet_count_since_flush;
|
uint64_t packet_count_since_flush;
|
||||||
int connection_state_to_output;
|
// int connection_state_to_output;
|
||||||
uint64_t first_packet_time_to_play;
|
uint64_t first_packet_time_to_play;
|
||||||
int64_t time_since_play_started; // nanoseconds
|
int64_t time_since_play_started; // nanoseconds
|
||||||
// stats
|
// stats
|
||||||
|
|||||||
@@ -487,7 +487,7 @@ void *rtp_control_receiver(void *arg) {
|
|||||||
|
|
||||||
sync_rtp_timestamp = sync_rtp_timestamp - conn->latency;
|
sync_rtp_timestamp = sync_rtp_timestamp - conn->latency;
|
||||||
|
|
||||||
debug_mutex_lock(&conn->reference_time_mutex, 1000, 0);
|
pthread_mutex_lock(&conn->reference_time_mutex);
|
||||||
|
|
||||||
if (conn->initial_reference_time == 0) {
|
if (conn->initial_reference_time == 0) {
|
||||||
if (conn->packet_count_since_flush > 0) {
|
if (conn->packet_count_since_flush > 0) {
|
||||||
@@ -535,7 +535,7 @@ void *rtp_control_receiver(void *arg) {
|
|||||||
conn->anchor_remote_info_is_valid = 1;
|
conn->anchor_remote_info_is_valid = 1;
|
||||||
|
|
||||||
conn->latency_delayed_timestamp = rtp_timestamp_less_latency;
|
conn->latency_delayed_timestamp = rtp_timestamp_less_latency;
|
||||||
debug_mutex_unlock(&conn->reference_time_mutex, 0);
|
pthread_mutex_unlock(&conn->reference_time_mutex);
|
||||||
|
|
||||||
conn->reference_to_previous_time_difference =
|
conn->reference_to_previous_time_difference =
|
||||||
remote_time_of_sync - old_remote_reference_time;
|
remote_time_of_sync - old_remote_reference_time;
|
||||||
@@ -1111,11 +1111,11 @@ void rtp_setup(SOCKADDR *local, SOCKADDR *remote, uint16_t cport, uint16_t tport
|
|||||||
}
|
}
|
||||||
|
|
||||||
void reset_ntp_anchor_info(rtsp_conn_info *conn) {
|
void reset_ntp_anchor_info(rtsp_conn_info *conn) {
|
||||||
debug_mutex_lock(&conn->reference_time_mutex, 1000, 1);
|
pthread_mutex_lock(&conn->reference_time_mutex);
|
||||||
conn->anchor_remote_info_is_valid = 0;
|
conn->anchor_remote_info_is_valid = 0;
|
||||||
conn->anchor_rtptime = 0;
|
conn->anchor_rtptime = 0;
|
||||||
conn->anchor_time = 0;
|
conn->anchor_time = 0;
|
||||||
debug_mutex_unlock(&conn->reference_time_mutex, 3);
|
pthread_mutex_unlock(&conn->reference_time_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
int have_ntp_timing_information(rtsp_conn_info *conn) {
|
int have_ntp_timing_information(rtsp_conn_info *conn) {
|
||||||
@@ -1132,7 +1132,7 @@ int frame_to_ntp_local_time(uint32_t timestamp, uint64_t *time, rtsp_conn_info *
|
|||||||
// a zero result is good
|
// a zero result is good
|
||||||
if (conn->anchor_remote_info_is_valid == 0)
|
if (conn->anchor_remote_info_is_valid == 0)
|
||||||
debug(1, "no anchor information");
|
debug(1, "no anchor information");
|
||||||
debug_mutex_lock(&conn->reference_time_mutex, 1000, 0);
|
pthread_mutex_lock(&conn->reference_time_mutex);
|
||||||
int result = -1;
|
int result = -1;
|
||||||
if (conn->anchor_remote_info_is_valid != 0) {
|
if (conn->anchor_remote_info_is_valid != 0) {
|
||||||
uint64_t remote_time_of_timestamp;
|
uint64_t remote_time_of_timestamp;
|
||||||
@@ -1151,13 +1151,13 @@ int frame_to_ntp_local_time(uint32_t timestamp, uint64_t *time, rtsp_conn_info *
|
|||||||
*time = remote_time_of_timestamp - local_to_remote_time_difference_now(conn);
|
*time = remote_time_of_timestamp - local_to_remote_time_difference_now(conn);
|
||||||
result = 0;
|
result = 0;
|
||||||
}
|
}
|
||||||
debug_mutex_unlock(&conn->reference_time_mutex, 0);
|
pthread_mutex_unlock(&conn->reference_time_mutex);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int local_ntp_time_to_frame(uint64_t time, uint32_t *frame, rtsp_conn_info *conn) {
|
int local_ntp_time_to_frame(uint64_t time, uint32_t *frame, rtsp_conn_info *conn) {
|
||||||
// a zero result is good
|
// a zero result is good
|
||||||
debug_mutex_lock(&conn->reference_time_mutex, 1000, 0);
|
pthread_mutex_lock(&conn->reference_time_mutex);
|
||||||
int result = -1;
|
int result = -1;
|
||||||
if (conn->anchor_remote_info_is_valid != 0) {
|
if (conn->anchor_remote_info_is_valid != 0) {
|
||||||
// first, get from [local] time to remote time.
|
// first, get from [local] time to remote time.
|
||||||
@@ -1176,7 +1176,7 @@ int local_ntp_time_to_frame(uint64_t time, uint32_t *frame, rtsp_conn_info *conn
|
|||||||
*frame = new_frame;
|
*frame = new_frame;
|
||||||
result = 0;
|
result = 0;
|
||||||
}
|
}
|
||||||
debug_mutex_unlock(&conn->reference_time_mutex, 0);
|
pthread_mutex_unlock(&conn->reference_time_mutex);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ int RTSP_connection_index = 1;
|
|||||||
// keep track of the threads we have spawned so we can join() them
|
// keep track of the threads we have spawned so we can join() them
|
||||||
static int nconns = 0;
|
static int nconns = 0;
|
||||||
static void track_thread(rtsp_conn_info *conn) {
|
static void track_thread(rtsp_conn_info *conn) {
|
||||||
debug_mutex_lock(&conns_lock, 1000000, 4);
|
pthread_mutex_lock(&conns_lock);
|
||||||
// look for an empty slot first
|
// look for an empty slot first
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int found = 0;
|
int found = 0;
|
||||||
@@ -170,7 +170,7 @@ static void track_thread(rtsp_conn_info *conn) {
|
|||||||
die("could not reallocate memory for conns");
|
die("could not reallocate memory for conns");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug_mutex_unlock(&conns_lock, 4);
|
pthread_mutex_unlock(&conns_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: connection numbers start at 1, so an except_this_one value of zero means "all threads"
|
// note: connection numbers start at 1, so an except_this_one value of zero means "all threads"
|
||||||
@@ -178,7 +178,7 @@ void cancel_all_RTSP_threads(airplay_stream_c stream_category, int except_this_o
|
|||||||
// if the stream category is unspecified_stream_category
|
// if the stream category is unspecified_stream_category
|
||||||
// all categories are elegible for cancellation
|
// all categories are elegible for cancellation
|
||||||
// otherwise just the category itself
|
// otherwise just the category itself
|
||||||
debug_mutex_lock(&conns_lock, 1000000, 3);
|
pthread_mutex_lock(&conns_lock);
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < nconns; i++) {
|
for (i = 0; i < nconns; i++) {
|
||||||
if ((conns[i] != NULL) && (conns[i]->running != 0) &&
|
if ((conns[i] != NULL) && (conns[i]->running != 0) &&
|
||||||
@@ -203,7 +203,7 @@ void cancel_all_RTSP_threads(airplay_stream_c stream_category, int except_this_o
|
|||||||
conns[i] = NULL;
|
conns[i] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug_mutex_unlock(&conns_lock, 3);
|
pthread_mutex_unlock(&conns_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
int old_connection_count = -1;
|
int old_connection_count = -1;
|
||||||
@@ -214,7 +214,7 @@ void cleanup_threads(void) {
|
|||||||
int i;
|
int i;
|
||||||
int connection_count = 0;
|
int connection_count = 0;
|
||||||
// debug(2, "culling threads.");
|
// debug(2, "culling threads.");
|
||||||
debug_mutex_lock(&conns_lock, 1000000, 4);
|
pthread_mutex_lock(&conns_lock);
|
||||||
for (i = 0; i < nconns; i++) {
|
for (i = 0; i < nconns; i++) {
|
||||||
if ((conns[i] != NULL) && (conns[i]->running == 0)) {
|
if ((conns[i] != NULL) && (conns[i]->running == 0)) {
|
||||||
debug(4, "found RTSP connection thread %d in a non-running state.",
|
debug(4, "found RTSP connection thread %d in a non-running state.",
|
||||||
@@ -230,7 +230,7 @@ void cleanup_threads(void) {
|
|||||||
connection_count++;
|
connection_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug_mutex_unlock(&conns_lock, 4);
|
pthread_mutex_unlock(&conns_lock);
|
||||||
|
|
||||||
if (old_connection_count != connection_count) {
|
if (old_connection_count != connection_count) {
|
||||||
if (connection_count == 0) {
|
if (connection_count == 0) {
|
||||||
@@ -247,7 +247,7 @@ void cleanup_threads(void) {
|
|||||||
int terminate_conn(int connection_number) {
|
int terminate_conn(int connection_number) {
|
||||||
// this will look for a connection by number, cancel it, join it and delete it.
|
// this will look for a connection by number, cancel it, join it and delete it.
|
||||||
int found = 0;
|
int found = 0;
|
||||||
debug_mutex_lock(&conns_lock, 1000000, 4);
|
pthread_mutex_lock(&conns_lock);
|
||||||
// look for an empty slot first
|
// look for an empty slot first
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while ((i < nconns) && (found == 0)) {
|
while ((i < nconns) && (found == 0)) {
|
||||||
@@ -259,7 +259,7 @@ int terminate_conn(int connection_number) {
|
|||||||
} else
|
} else
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
debug_mutex_unlock(&conns_lock, 4);
|
pthread_mutex_unlock(&conns_lock);
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1341,7 +1341,7 @@ void handle_flushbuffered(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
|
|||||||
debug(4, "flushUntilTS is %" PRId64 ".", flushUntilTS);
|
debug(4, "flushUntilTS is %" PRId64 ".", flushUntilTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_mutex_lock(&conn->flush_mutex, 1000, 4);
|
pthread_mutex_lock(&conn->flush_mutex);
|
||||||
|
|
||||||
if (flushFromValid == 0) {
|
if (flushFromValid == 0) {
|
||||||
// an immediate flush is requested
|
// an immediate flush is requested
|
||||||
@@ -1387,7 +1387,7 @@ void handle_flushbuffered(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_mutex_unlock(&conn->flush_mutex, 4);
|
pthread_mutex_unlock(&conn->flush_mutex);
|
||||||
plist_free(messagePlist);
|
plist_free(messagePlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1465,7 +1465,7 @@ void handle_setrateanchori(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
|
|||||||
uint64_t rate;
|
uint64_t rate;
|
||||||
plist_get_uint_val(item, &rate);
|
plist_get_uint_val(item, &rate);
|
||||||
debug(3, "anchor rate 0x%016" PRIx64 ".", rate);
|
debug(3, "anchor rate 0x%016" PRIx64 ".", rate);
|
||||||
pthread_cleanup_debug_mutex_lock(&conn->flush_mutex, 1000, 4);
|
pthread_mutex_lock_and_cleanup_push(&conn->flush_mutex);
|
||||||
conn->ap2_rate = rate;
|
conn->ap2_rate = rate;
|
||||||
if ((rate & 1) != 0) {
|
if ((rate & 1) != 0) {
|
||||||
ptp_send_control_message_string(
|
ptp_send_control_message_string(
|
||||||
@@ -4168,14 +4168,14 @@ static void *rtsp_conversation_thread_func(void *pconn) {
|
|||||||
|
|
||||||
// check to see if a conn has been zeroed
|
// check to see if a conn has been zeroed
|
||||||
|
|
||||||
debug_mutex_lock(&conns_lock, 1000000, 4);
|
pthread_mutex_lock(&conns_lock);
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < nconns; i++) {
|
for (i = 0; i < nconns; i++) {
|
||||||
if ((conns[i] != NULL) && (conns[i]->connection_number == 0)) {
|
if ((conns[i] != NULL) && (conns[i]->connection_number == 0)) {
|
||||||
debug(1, "conns[%d] has a Connection Number of 0!", i);
|
debug(1, "conns[%d] has a Connection Number of 0!", i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug_mutex_unlock(&conns_lock, 4);
|
pthread_mutex_unlock(&conns_lock);
|
||||||
|
|
||||||
reply = rtsp_read_request(conn, &req);
|
reply = rtsp_read_request(conn, &req);
|
||||||
if (reply == rtsp_read_request_response_ok) {
|
if (reply == rtsp_read_request_response_ok) {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ general =
|
|||||||
// The default is "auto", which means that the AirPlay 2 service will be provided if NQPTP is present and "classic" AirPlay will be provided otherwise, with "(Classic)" appended to the service name (see above).
|
// The default is "auto", which means that the AirPlay 2 service will be provided if NQPTP is present and "classic" AirPlay will be provided otherwise, with "(Classic)" appended to the service name (see above).
|
||||||
// service_type = "auto"; // This can be "auto", "airplay2" or "classic".
|
// service_type = "auto"; // This can be "auto", "airplay2" or "classic".
|
||||||
// The interpolation setting below controls how Shairport Sync adds or removes frames of audio to keep in sync.
|
// The interpolation setting below controls how Shairport Sync adds or removes frames of audio to keep in sync.
|
||||||
// "auto" (default) measures the processor's floating point speed and chooses "soxr" if available and it is fast enough. Otherwise, "vernier" is selected.
|
// "auto" (default) measures the processor's floating point speed and chooses "soxr" if available and if the processor is fast enough. Otherwise, "vernier" is selected.
|
||||||
// "vernier" recodes a packet of frames to a new packet containing more or fewer frames. This is recommended for low powered devices.
|
// "vernier" recodes a packet of frames to a new packet containing more or fewer frames. This is recommended for low powered devices.
|
||||||
// "soxr" uses the SoX library to recode a packet of frames to a new packet containing more or fewer frames. This needs a processor with fast floating point capability.
|
// "soxr" uses the SoX library to recode a packet of frames to a new packet containing more or fewer frames. This needs a processor with fast floating point capability.
|
||||||
// "basic" causes the simple removal or insertion of frames in a packet of frames. Not recommended.
|
// "basic" causes the simple removal or insertion of frames in a packet of frames. Not recommended.
|
||||||
@@ -106,13 +106,18 @@ sessioncontrol =
|
|||||||
// run_this_before_play_begins = "/full/path/to/application and args"; // make sure the application has executable permission. If it's a script, include the shebang (#!/bin/...) on the first line
|
// run_this_before_play_begins = "/full/path/to/application and args"; // make sure the application has executable permission. If it's a script, include the shebang (#!/bin/...) on the first line
|
||||||
// run_this_after_play_ends = "/full/path/to/application and args"; // make sure the application has executable permission. If it's a script, include the shebang (#!/bin/...) on the first line
|
// run_this_after_play_ends = "/full/path/to/application and args"; // make sure the application has executable permission. If it's a script, include the shebang (#!/bin/...) on the first line
|
||||||
|
|
||||||
// run_this_if_an_unfixable_error_is_detected = "/full/path/to/application and args"; // if a problem occurs that can't be cleared by Shairport Sync itself, hook a program on here to deal with it.
|
// run_this_if_an_unfixable_error_is_detected = "/full/path/to/application and args"; // if a problem occurs that can't be cleared by Shairport Sync itself, hook a handler program on here to deal with it.
|
||||||
// An error code-string is passed as the last argument.
|
// An error code-string is passed as the last argument.
|
||||||
|
// Shairport Sync will always wait for this handler to complete.
|
||||||
|
// Shairport Sync will not exit automatically after executing this handler. To exit, the handler must do it itself, perhaps via a script with "/usr/bin/kill $PPID" as its last line.
|
||||||
// Many of these "unfixable" problems are caused by malfunctioning output devices, and sometimes it is necessary to restart the whole device to clear the problem.
|
// Many of these "unfixable" problems are caused by malfunctioning output devices, and sometimes it is necessary to restart the whole device to clear the problem.
|
||||||
// You could hook on a program to do this automatically, but beware -- the device may then power off and restart without warning!
|
// You could hook on a program to do this automatically, but beware -- the device may then power off and restart without warning!
|
||||||
// wait_for_completion = "no"; // set to "yes" to get Shairport Sync to wait until the "run_this..." applications have terminated before continuing
|
|
||||||
|
|
||||||
// allow_session_interruption = "no"; // set to "yes" to allow another device to interrupt Shairport Sync while it's playing from an existing audio source
|
// wait_for_completion = "no"; // set to "yes" to get Shairport Sync to wait until the "run_this..." applications have terminated before continuing,
|
||||||
|
// except the "run_this_if_an_unfixable_error_is_detected" handler, which will always wait for completion, irrespective of this setting.
|
||||||
|
|
||||||
|
// allow_session_interruption = "no"; // (Classic AirPlay only) Set to "yes" to allow another device to interrupt Shairport Sync while it's playing from an existing audio source
|
||||||
|
|
||||||
// session_timeout = 60; // wait for this number of seconds after a source disappears before terminating the session and becoming available again.
|
// session_timeout = 60; // wait for this number of seconds after a source disappears before terminating the session and becoming available again.
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -294,16 +299,19 @@ ao =
|
|||||||
dsp =
|
dsp =
|
||||||
{
|
{
|
||||||
//////////////////////////////////////////
|
//////////////////////////////////////////
|
||||||
// This convolution filter can be used with inpulse responses files
|
// This convolution filter can be used with finite impulse response (IR) files
|
||||||
// to apply almost any correction to the audio signal, like frequency and phase correction.
|
// to apply almost any correction to the audio signal, like frequency and phase correction.
|
||||||
// For example you could measure (with a good microphone and a sweep-sine) the frequency response of your speakers + room,
|
// For example you could measure (with a good microphone and a sweep-sine) the frequency response of your speakers + room,
|
||||||
// and apply a correction to get a flat response curve.
|
// and apply a correction to get a flat response curve.
|
||||||
// Long impulse response files require lots of floating-point processing!
|
// Long impulse response files require lots of floating-point processing!
|
||||||
|
//
|
||||||
|
// Convolution is applied to each output channel at the output rate.
|
||||||
|
// Thus, your IR files must match the output rate and match the output channel count or are mono.
|
||||||
//////////////////////////////////////////
|
//////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// convolution_enabled = "no"; // Set this to "yes" to activate the convolution filter.
|
// convolution_enabled = "no"; // Set this to "yes" to activate the convolution filter.
|
||||||
// convolution_thread_pool_size = 1; // Number of CPU threads that can work on convolution at the same time.
|
// convolution_thread_pool_size = 1; // Number of CPU threads that can work on convolution at the same time.
|
||||||
// convolution_ir_files = "<comma-separated list of full path names to impulse response files, for sample rates of 44100 and 48000 -- stereo or mono only>";
|
// convolution_ir_files = "<comma-separated list of full path names to impulse response files, with sample rates that match output rates and with channel counts that match output channel counts or are mono>";
|
||||||
// convolution_gain = -4.0; // Static gain applied after convolution, between -60 and +18 dB. Useful for preventing clipping or amplifying low convolution output levels
|
// convolution_gain = -4.0; // Static gain applied after convolution, between -60 and +18 dB. Useful for preventing clipping or amplifying low convolution output levels
|
||||||
// convolution_max_length_in_seconds = 1.0; // Truncate the input file to this length in order to save CPU.
|
// convolution_max_length_in_seconds = 1.0; // Truncate the input file to this length in order to save CPU.
|
||||||
//
|
//
|
||||||
@@ -383,7 +391,6 @@ mqtt =
|
|||||||
diagnostics =
|
diagnostics =
|
||||||
{
|
{
|
||||||
// disable_resend_requests = "no"; // set this to yes to stop Shairport Sync from requesting the retransmission of missing packets. Default is "no".
|
// disable_resend_requests = "no"; // set this to yes to stop Shairport Sync from requesting the retransmission of missing packets. Default is "no".
|
||||||
// log_output_to = "syslog"; // set this to "syslog" (default), "stderr" or "stdout" or a file or pipe path to specify were all logs, statistics and diagnostic messages are written to. If there's anything wrong with the file spec, output will be to "stderr".
|
|
||||||
// statistics = "no"; // set to "yes" to print statistics in the log
|
// statistics = "no"; // set to "yes" to print statistics in the log
|
||||||
// log_verbosity = 0; // "0" means no debug verbosity, "3" is most verbose.
|
// log_verbosity = 0; // "0" means no debug verbosity, "3" is most verbose.
|
||||||
// log_show_file_and_line = "yes"; // set this to yes if you want the file and line number of the message source in the log file
|
// log_show_file_and_line = "yes"; // set this to yes if you want the file and line number of the message source in the log file
|
||||||
@@ -391,4 +398,5 @@ diagnostics =
|
|||||||
// log_show_time_since_last_message = "yes"; // set this to yes if you want the time since the last debug message in the debug message -- seconds down to nanoseconds
|
// log_show_time_since_last_message = "yes"; // set this to yes if you want the time since the last debug message in the debug message -- seconds down to nanoseconds
|
||||||
// drop_this_fraction_of_audio_packets = 0.0; // use this to simulate a noisy network where this fraction of UDP packets are lost in transmission. E.g. a value of 0.001 would mean an average of 0.1% of packets are lost, which is actually quite a high figure.
|
// drop_this_fraction_of_audio_packets = 0.0; // use this to simulate a noisy network where this fraction of UDP packets are lost in transmission. E.g. a value of 0.001 would mean an average of 0.1% of packets are lost, which is actually quite a high figure.
|
||||||
// retain_cover_art = "no"; // artwork is deleted when its corresponding track has been played. Set this to "yes" to retain all artwork permanently. Warning -- your directory might fill up.
|
// retain_cover_art = "no"; // artwork is deleted when its corresponding track has been played. Set this to "yes" to retain all artwork permanently. Warning -- your directory might fill up.
|
||||||
|
// get_plist_metadata = "no"; // set this temporary setting to "yes" to get the new richer metadata stream.
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ rcvar="shairport_sync_enable"
|
|||||||
# This seems to be necessary in FreeBSD, as the startup system doesn't seem to like dashes ("-") in service names.
|
# This seems to be necessary in FreeBSD, as the startup system doesn't seem to like dashes ("-") in service names.
|
||||||
|
|
||||||
pidfile="/var/run/shairport-sync.pid"
|
pidfile="/var/run/shairport-sync.pid"
|
||||||
apptodaemonise="/usr/local/bin/shairport-sync --log-to-syslog"
|
apptodaemonise="/usr/local/bin/shairport-sync"
|
||||||
|
|
||||||
command="/usr/sbin/daemon"
|
command="/usr/sbin/daemon"
|
||||||
# -S log to syslog; -P store the supervisor PID; -u to specify the user to run the service under
|
# -S log to syslog; -P store the supervisor PID; -u to specify the user to run the service under
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ StartLimitIntervalSec=300
|
|||||||
StartLimitBurst=5
|
StartLimitBurst=5
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=@prefix@/bin/shairport-sync --log-to-syslog
|
ExecStart=@prefix@/bin/shairport-sync
|
||||||
User=shairport-sync
|
User=shairport-sync
|
||||||
Group=shairport-sync
|
Group=shairport-sync
|
||||||
LimitRTPRIO=5
|
LimitRTPRIO=5
|
||||||
|
|||||||
+69
-42
@@ -83,6 +83,7 @@
|
|||||||
#include "rtp.h"
|
#include "rtp.h"
|
||||||
#include "rtsp.h"
|
#include "rtsp.h"
|
||||||
#include "utilities/string_utilities.h"
|
#include "utilities/string_utilities.h"
|
||||||
|
#include "utilities/exit.h"
|
||||||
|
|
||||||
#if defined(CONFIG_DACP_CLIENT)
|
#if defined(CONFIG_DACP_CLIENT)
|
||||||
#include "dacp.h"
|
#include "dacp.h"
|
||||||
@@ -140,6 +141,10 @@ int this_is_the_daemon_process = 0;
|
|||||||
|
|
||||||
pthread_t rtsp_listener_thread;
|
pthread_t rtsp_listener_thread;
|
||||||
|
|
||||||
|
#if defined(CONFIG_DBUS_INTERFACE) || defined(CONFIG_MPRIS_INTERFACE)
|
||||||
|
GMainLoop *glib_worker_loop = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
int killOption = 0;
|
int killOption = 0;
|
||||||
int daemonisewith = 0;
|
int daemonisewith = 0;
|
||||||
int daemonisewithout = 0;
|
int daemonisewithout = 0;
|
||||||
@@ -453,7 +458,7 @@ int parse_options(int argc, char **argv) {
|
|||||||
break;
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
inform("Warning: the option -u is no longer needed and is deprecated. Debug and statistics "
|
inform("Warning: the option -u is no longer needed and is deprecated. Debug and statistics "
|
||||||
"output to STDERR is now the default. Use \"--log-to-syslog\" to revert.");
|
"output to STDERR is now the default.");
|
||||||
break;
|
break;
|
||||||
case 'D':
|
case 'D':
|
||||||
inform("Warning: the option -D or --disconnectFromOutput is deprecated.");
|
inform("Warning: the option -D or --disconnectFromOutput is deprecated.");
|
||||||
@@ -489,6 +494,7 @@ int parse_options(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (c < -1) {
|
if (c < -1) {
|
||||||
|
debug(1, "Oops");
|
||||||
die("%s: %s", poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(c));
|
die("%s: %s", poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,17 +509,16 @@ int parse_options(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (log_to_syslog_selected) {
|
if (log_to_syslog_selected) {
|
||||||
// if this was the first command line argument, it'll already have been chosen
|
inform("the diagnostic \"log-to-syslog\" command_line_option is obsolete and is ignored. All logging is to STDERR, which is directed to the system log when Shairport Sync is running as a service.");
|
||||||
if (log_to_syslog_select_is_first_command_line_argument == 0) {
|
/*
|
||||||
inform("Suggestion: make \"--log-to-syslog\" the first command line argument to ensure "
|
|
||||||
"messages go to the syslog right from the beginning.");
|
|
||||||
}
|
|
||||||
#ifdef CONFIG_LIBDAEMON
|
#ifdef CONFIG_LIBDAEMON
|
||||||
log_to_default = 0; // a specific log output modality has been selected.
|
log_to_default = 0; // a specific log output modality has been selected.
|
||||||
#endif
|
#endif
|
||||||
log_to_syslog();
|
log_to_syslog();
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef CONFIG_LIBDAEMON
|
#ifdef CONFIG_LIBDAEMON
|
||||||
if ((daemonisewith) && (daemonisewithout))
|
if ((daemonisewith) && (daemonisewithout))
|
||||||
die("Select either daemonize_with_pid_file or daemonize_without_pid_file -- you have selected "
|
die("Select either daemonize_with_pid_file or daemonize_without_pid_file -- you have selected "
|
||||||
@@ -574,9 +579,6 @@ int parse_options(int argc, char **argv) {
|
|||||||
#endif
|
#endif
|
||||||
config.firmware_version = strdup(PACKAGE_VERSION);
|
config.firmware_version = strdup(PACKAGE_VERSION);
|
||||||
|
|
||||||
free(config.firmware_version);
|
|
||||||
config.firmware_version = strdup("5.2");
|
|
||||||
|
|
||||||
#ifdef CONFIG_METADATA
|
#ifdef CONFIG_METADATA
|
||||||
/* Get the metadata setting. */
|
/* Get the metadata setting. */
|
||||||
config.metadata_enabled = 1; // if metadata support is included, then enable it by default
|
config.metadata_enabled = 1; // if metadata support is included, then enable it by default
|
||||||
@@ -771,6 +773,17 @@ int parse_options(int argc, char **argv) {
|
|||||||
"inclusive.",
|
"inclusive.",
|
||||||
value);
|
value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config_lookup_string(config.cfg, "diagnostics.get_plist_metadata", &str)) {
|
||||||
|
if (strcasecmp(str, "no") == 0)
|
||||||
|
config.get_plist_metadata = 0;
|
||||||
|
else if (strcasecmp(str, "yes") == 0)
|
||||||
|
config.get_plist_metadata = 1;
|
||||||
|
else
|
||||||
|
die("Invalid \"get_plist_metadata\" option choice \"%s\". It should be \"yes\" or "
|
||||||
|
"\"no\"",
|
||||||
|
str);
|
||||||
|
}
|
||||||
|
|
||||||
/* Get the verbosity setting. */
|
/* Get the verbosity setting. */
|
||||||
if (config_lookup_int(config.cfg, "diagnostics.log_verbosity", &value)) {
|
if (config_lookup_int(config.cfg, "diagnostics.log_verbosity", &value)) {
|
||||||
@@ -860,6 +873,7 @@ int parse_options(int argc, char **argv) {
|
|||||||
|
|
||||||
/* Get the diagnostics output default. */
|
/* Get the diagnostics output default. */
|
||||||
if (config_lookup_string(config.cfg, "diagnostics.log_output_to", &str)) {
|
if (config_lookup_string(config.cfg, "diagnostics.log_output_to", &str)) {
|
||||||
|
/*
|
||||||
#ifdef CONFIG_LIBDAEMON
|
#ifdef CONFIG_LIBDAEMON
|
||||||
log_to_default = 0; // a specific log output modality has been selected.
|
log_to_default = 0; // a specific log output modality has been selected.
|
||||||
#endif
|
#endif
|
||||||
@@ -874,7 +888,11 @@ int parse_options(int argc, char **argv) {
|
|||||||
config.log_fd = -1;
|
config.log_fd = -1;
|
||||||
log_to_file();
|
log_to_file();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
warn("the diagnostic \"log_output_to\" setting is obsolete and is ignored. All logging is to STDERR, which is directed to the system log when Shairport Sync is running as a service.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Get the ignore_volume_control setting. */
|
/* Get the ignore_volume_control setting. */
|
||||||
if (config_lookup_string(config.cfg, "general.ignore_volume_control", &str)) {
|
if (config_lookup_string(config.cfg, "general.ignore_volume_control", &str)) {
|
||||||
if (strcasecmp(str, "no") == 0)
|
if (strcasecmp(str, "no") == 0)
|
||||||
@@ -1630,17 +1648,22 @@ int parse_options(int argc, char **argv) {
|
|||||||
// provides much more information, so should be the default for AirPlay 2
|
// provides much more information, so should be the default for AirPlay 2
|
||||||
|
|
||||||
if (config.metadata_enabled != 0) {
|
if (config.metadata_enabled != 0) {
|
||||||
config.airplay_features |=
|
if (config.get_plist_metadata != 0) {
|
||||||
(uint64_t)1 << 50; // metadata in a binary plist, including more state information
|
config.airplay_features |=
|
||||||
// config.airplay_features |= ((uint64_t)1 << 15) | ((uint64_t)1 << 16) | ((uint64_t)1 << 17);
|
(uint64_t)1 << 50; // richer metadata in a binary plist, including more state information
|
||||||
// // older metadata flags artwork, progress and text respectively
|
} else {
|
||||||
|
// older metadata flags artwork, progress and text respectively
|
||||||
|
config.airplay_features |= (((uint64_t)1 << 16) | ((uint64_t)1 << 17));
|
||||||
|
if (config.get_coverart != 0)
|
||||||
|
config.airplay_features |= ((uint64_t)1 << 15);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// now generate the fex field
|
// now generate the fex field
|
||||||
uint8_t fexbytes[8];
|
uint8_t fexbytes[8];
|
||||||
uint64_t temp = config.airplay_features;
|
uint64_t temp = config.airplay_features;
|
||||||
debug(2, "airplay_features are %" PRIx64 ".", temp);
|
debug(4, "airplay_features are %" PRIx64 ".", temp);
|
||||||
for (i = 0; i < 8; i++) {
|
for (i = 0; i < 8; i++) {
|
||||||
fexbytes[i] = temp & 0xff;
|
fexbytes[i] = temp & 0xff;
|
||||||
temp = temp >> 8;
|
temp = temp >> 8;
|
||||||
@@ -1841,7 +1864,7 @@ GThread *glib_worker_thread = NULL;
|
|||||||
gpointer glib_worker_thread_function(__attribute__((unused)) gpointer data) {
|
gpointer glib_worker_thread_function(__attribute__((unused)) gpointer data) {
|
||||||
|
|
||||||
// use the default global-default main context
|
// use the default global-default main context
|
||||||
config.glib_worker_loop = g_main_loop_new(NULL, FALSE);
|
glib_worker_loop = g_main_loop_new(NULL, FALSE);
|
||||||
|
|
||||||
// debug(1, "glib worker thread started.");
|
// debug(1, "glib worker thread started.");
|
||||||
|
|
||||||
@@ -1856,7 +1879,7 @@ gpointer glib_worker_thread_function(__attribute__((unused)) gpointer data) {
|
|||||||
|
|
||||||
// debug(1, "g_main_loop_run start.");
|
// debug(1, "g_main_loop_run start.");
|
||||||
|
|
||||||
g_main_loop_run(config.glib_worker_loop);
|
g_main_loop_run(glib_worker_loop);
|
||||||
|
|
||||||
// debug(1, "g_main_loop_run exit.");
|
// debug(1, "g_main_loop_run exit.");
|
||||||
|
|
||||||
@@ -1872,11 +1895,7 @@ gpointer glib_worker_thread_function(__attribute__((unused)) gpointer data) {
|
|||||||
// debug(1, "stopped D-Bus service");
|
// debug(1, "stopped D-Bus service");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
g_main_loop_unref(config.glib_worker_loop);
|
g_main_loop_unref(glib_worker_loop);
|
||||||
if (config.quit_requested_from_glib_mainloop != 0) {
|
|
||||||
debug(2, "glib_mainloop_thread_function asking for exit");
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1893,17 +1912,15 @@ const char *pid_file_proc(void) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void exit_rtsp_listener() {
|
void exit_rtsp_listener() {
|
||||||
debug(3, "exit_rtsp_listener begins");
|
debug(3, "exit_rtsp_listener begins");
|
||||||
if (type_of_exit_cleanup != TOE_emergency) {
|
pthread_cancel(rtsp_listener_thread);
|
||||||
pthread_cancel(rtsp_listener_thread);
|
pthread_join(rtsp_listener_thread, NULL); // not sure you need this
|
||||||
pthread_join(rtsp_listener_thread, NULL); // not sure you need this
|
|
||||||
}
|
|
||||||
debug(2, "exit_rtsp_listener ends");
|
debug(2, "exit_rtsp_listener ends");
|
||||||
}
|
}
|
||||||
|
|
||||||
void exit_function() {
|
void exit_function() {
|
||||||
if (type_of_exit_cleanup != TOE_emergency) {
|
|
||||||
// the following is to ensure that if libdaemon has been included
|
// the following is to ensure that if libdaemon has been included
|
||||||
// that most of this code will be skipped when the parent process is exiting
|
// that most of this code will be skipped when the parent process is exiting
|
||||||
// exec
|
// exec
|
||||||
@@ -1932,10 +1949,15 @@ void exit_function() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CONFIG_DBUS_INTERFACE) || defined(CONFIG_MPRIS_INTERFACE)
|
#if defined(CONFIG_DBUS_INTERFACE) || defined(CONFIG_MPRIS_INTERFACE)
|
||||||
if ((glib_worker_thread != NULL) && (config.quit_requested_from_glib_mainloop == 0)) {
|
if (glib_worker_loop != NULL) { // may not have been initialised
|
||||||
g_main_loop_quit(config.glib_worker_loop);
|
g_main_loop_quit(glib_worker_loop);
|
||||||
debug(2, "GMainLoop stop requested");
|
debug(2, "GMainLoop stop requested");
|
||||||
}
|
}
|
||||||
|
if (glib_worker_thread != NULL) {
|
||||||
|
g_thread_join(glib_worker_thread);
|
||||||
|
debug(1, "GLib worker thread joined");
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_METADATA_HUB
|
#ifdef CONFIG_METADATA_HUB
|
||||||
@@ -2038,6 +2060,8 @@ void exit_function() {
|
|||||||
|
|
||||||
#ifdef CONFIG_LIBDAEMON
|
#ifdef CONFIG_LIBDAEMON
|
||||||
if (this_is_the_daemon_process) { // this is the daemon that is exiting
|
if (this_is_the_daemon_process) { // this is the daemon that is exiting
|
||||||
|
mdns_unregister(); // once the dacp handler is done and all player threrads are done it should
|
||||||
|
// be safe
|
||||||
debug(1, "libdaemon daemon process exit");
|
debug(1, "libdaemon daemon process exit");
|
||||||
} else {
|
} else {
|
||||||
if (config.daemonise)
|
if (config.daemonise)
|
||||||
@@ -2046,13 +2070,10 @@ void exit_function() {
|
|||||||
debug(1, "normal exit");
|
debug(1, "normal exit");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
mdns_unregister(); // once the dacp handler is done and all player threrads are done it should
|
mdns_unregister(); // once the dacp handler is done and all player threads are done it should
|
||||||
// be safe
|
// be safe
|
||||||
debug(2, "normal exit");
|
debug(2, "normal exit");
|
||||||
#endif
|
#endif
|
||||||
} else {
|
|
||||||
debug(1, "emergency exit");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// for removing zombie script processes
|
// for removing zombie script processes
|
||||||
@@ -2069,12 +2090,12 @@ void handle_sigchld(__attribute__((unused)) int sig) {
|
|||||||
// for clean exits
|
// for clean exits
|
||||||
void intHandler(__attribute__((unused)) int k) {
|
void intHandler(__attribute__((unused)) int k) {
|
||||||
debug(2, "exit on SIGINT");
|
debug(2, "exit on SIGINT");
|
||||||
exit(EXIT_SUCCESS);
|
exit_request(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void termHandler(__attribute__((unused)) int k) {
|
void termHandler(__attribute__((unused)) int k) {
|
||||||
debug(2, "exit on SIGTERM");
|
debug(2, "exit on SIGTERM");
|
||||||
exit(EXIT_SUCCESS);
|
exit_request(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _display_config(const char *filename, const int linenumber, __attribute__((unused)) int argc,
|
void _display_config(const char *filename, const int linenumber, __attribute__((unused)) int argc,
|
||||||
@@ -2336,9 +2357,10 @@ const char *av_channel_layout_name(uint64_t channel_layout) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
exit_init(); // initialise the exit handler to give us a clean safe exit on request
|
||||||
// initialise debug messages stuff -- level 0, no elapsed time, relative time, file and line
|
// initialise debug messages stuff -- level 0, no elapsed time, relative time, file and line
|
||||||
// debug_init(int level, int show_elapsed_time, int show_relative_time, int show_file_and_line)
|
// debug_init(int level, int show_elapsed_time, int show_relative_time, int show_file_and_line)
|
||||||
debug_init(0, 0, 1, 1);
|
debug_init(0, 0, 1, 1, exit_request);
|
||||||
memset(&config, 0, sizeof(config)); // also clears all strings, BTW
|
memset(&config, 0, sizeof(config)); // also clears all strings, BTW
|
||||||
/* Check if we are called with -V or --version parameter */
|
/* Check if we are called with -V or --version parameter */
|
||||||
if (argc >= 2 && ((strcmp(argv[1], "-V") == 0) || (strcmp(argv[1], "--version") == 0))) {
|
if (argc >= 2 && ((strcmp(argv[1], "-V") == 0) || (strcmp(argv[1], "--version") == 0))) {
|
||||||
@@ -2381,13 +2403,15 @@ int main(int argc, char **argv) {
|
|||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if we are called with -log-to-syslog */
|
/*
|
||||||
|
// Check if we are called with -log-to-syslog
|
||||||
if (argc >= 2 && (strcmp(argv[1], "--log-to-syslog") == 0)) {
|
if (argc >= 2 && (strcmp(argv[1], "--log-to-syslog") == 0)) {
|
||||||
log_to_syslog_select_is_first_command_line_argument = 1;
|
log_to_syslog_select_is_first_command_line_argument = 1;
|
||||||
log_to_syslog();
|
log_to_syslog();
|
||||||
} else {
|
} else {
|
||||||
log_to_stderr();
|
log_to_stderr();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
pid = getpid();
|
pid = getpid();
|
||||||
config.log_fd = -1;
|
config.log_fd = -1;
|
||||||
@@ -2398,7 +2422,6 @@ int main(int argc, char **argv) {
|
|||||||
setlogmask(LOG_UPTO(LOG_DEBUG));
|
setlogmask(LOG_UPTO(LOG_DEBUG));
|
||||||
openlog(NULL, 0, LOG_DAEMON);
|
openlog(NULL, 0, LOG_DAEMON);
|
||||||
#endif
|
#endif
|
||||||
type_of_exit_cleanup = TOE_normal; // what kind of exit cleanup needed
|
|
||||||
debug(1, "adding the exit function");
|
debug(1, "adding the exit function");
|
||||||
atexit(exit_function);
|
atexit(exit_function);
|
||||||
|
|
||||||
@@ -2460,8 +2483,8 @@ int main(int argc, char **argv) {
|
|||||||
config.packet_stuffing = ST_vernier; // you need to explicitly ask for "basic" (ST_basic)
|
config.packet_stuffing = ST_vernier; // you need to explicitly ask for "basic" (ST_basic)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
set_requested_connection_state_to_output(
|
// set_requested_connection_state_to_output(
|
||||||
1); // we expect to be able to connect to the output device
|
// 1); // we expect to be able to connect to the output device
|
||||||
config.audio_backend_buffer_desired_length = 0.15; // seconds
|
config.audio_backend_buffer_desired_length = 0.15; // seconds
|
||||||
config.audio_decoded_buffer_desired_length = 0.75; // seconds
|
config.audio_decoded_buffer_desired_length = 0.75; // seconds
|
||||||
config.udp_port_base = 6001;
|
config.udp_port_base = 6001;
|
||||||
@@ -3355,6 +3378,10 @@ int main(int argc, char **argv) {
|
|||||||
// you'll see two threads named "listener" or whatever...
|
// you'll see two threads named "listener" or whatever...
|
||||||
named_pthread_create(&rtsp_listener_thread, NULL, &rtsp_listen_loop, NULL, "listener");
|
named_pthread_create(&rtsp_listener_thread, NULL, &rtsp_listen_loop, NULL, "listener");
|
||||||
atexit(exit_rtsp_listener);
|
atexit(exit_rtsp_listener);
|
||||||
pthread_join(rtsp_listener_thread, NULL);
|
|
||||||
|
// wait forever...
|
||||||
|
while (1) {
|
||||||
|
usleep(1000000);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
ssize_t buffered_read(buffered_tcp_desc *descriptor, void *buf, size_t count,
|
ssize_t buffered_read(buffered_tcp_desc *descriptor, void *buf, size_t count,
|
||||||
size_t *bytes_remaining) {
|
size_t *bytes_remaining) {
|
||||||
ssize_t response = -1;
|
ssize_t response = -1;
|
||||||
if (debug_mutex_lock(&descriptor->mutex, 50000, 4) != 0)
|
if (pthread_mutex_lock(&descriptor->mutex) != 0)
|
||||||
debug(1, "problem with mutex");
|
debug(1, "problem with mutex");
|
||||||
pthread_cleanup_push(mutex_unlock, (void *)&descriptor->mutex);
|
pthread_cleanup_push(mutex_unlock, (void *)&descriptor->mutex);
|
||||||
// wipe the slate dlean before reading...
|
// wipe the slate dlean before reading...
|
||||||
@@ -116,7 +116,7 @@ void *buffered_tcp_reader(void *arg) {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
int have_time_to_sleep = 0;
|
int have_time_to_sleep = 0;
|
||||||
if (debug_mutex_lock(&descriptor->mutex, 500000, 4) != 0)
|
if (pthread_mutex_lock(&descriptor->mutex) != 0)
|
||||||
debug(1, "problem with mutex");
|
debug(1, "problem with mutex");
|
||||||
pthread_cleanup_push(mutex_unlock, (void *)&descriptor->mutex);
|
pthread_cleanup_push(mutex_unlock, (void *)&descriptor->mutex);
|
||||||
while (descriptor->buffer_occupancy == descriptor->buffer_max_size) {
|
while (descriptor->buffer_occupancy == descriptor->buffer_max_size) {
|
||||||
@@ -150,7 +150,7 @@ void *buffered_tcp_reader(void *arg) {
|
|||||||
nread = recv(fd, descriptor->eoq, bytes_to_request, 0);
|
nread = recv(fd, descriptor->eoq, bytes_to_request, 0);
|
||||||
// debug(1, "Received %d bytes for a buffer size of %d bytes.",nread,
|
// debug(1, "Received %d bytes for a buffer size of %d bytes.",nread,
|
||||||
// descriptor->buffer_occupancy + nread);
|
// descriptor->buffer_occupancy + nread);
|
||||||
if (debug_mutex_lock(&descriptor->mutex, 50000, 4) != 0)
|
if (pthread_mutex_lock(&descriptor->mutex) != 0)
|
||||||
debug(1, "problem with not empty mutex");
|
debug(1, "problem with not empty mutex");
|
||||||
pthread_cleanup_push(mutex_unlock, (void *)&descriptor->mutex);
|
pthread_cleanup_push(mutex_unlock, (void *)&descriptor->mutex);
|
||||||
if (nread < 0) {
|
if (nread < 0) {
|
||||||
|
|||||||
+13
-2
@@ -24,6 +24,7 @@ SOFTWARE.
|
|||||||
|
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include <ctype.h> // for isprint()
|
#include <ctype.h> // for isprint()
|
||||||
|
#include <unistd.h> // for usleep()
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
@@ -41,6 +42,8 @@ int debugger_show_file_and_line = 1;
|
|||||||
static uint64_t ns_time_at_startup = 0;
|
static uint64_t ns_time_at_startup = 0;
|
||||||
static uint64_t ns_time_at_last_debug_message;
|
static uint64_t ns_time_at_last_debug_message;
|
||||||
|
|
||||||
|
void (*local_exit_requester)(const int exit_status) = NULL;
|
||||||
|
|
||||||
// always lock use this when accessing the ns_time_at_last_debug_message
|
// always lock use this when accessing the ns_time_at_last_debug_message
|
||||||
static pthread_mutex_t debug_timing_lock = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t debug_timing_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
@@ -56,13 +59,14 @@ uint64_t debug_get_absolute_time_in_ns() {
|
|||||||
return time_now_ns;
|
return time_now_ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
void debug_init(int level, int show_elapsed_time, int show_relative_time, int show_file_and_line) {
|
void debug_init(int level, int show_elapsed_time, int show_relative_time, int show_file_and_line, void (*exit_requester)(const int exit_status)) {
|
||||||
ns_time_at_startup = debug_get_absolute_time_in_ns();
|
ns_time_at_startup = debug_get_absolute_time_in_ns();
|
||||||
ns_time_at_last_debug_message = ns_time_at_startup;
|
ns_time_at_last_debug_message = ns_time_at_startup;
|
||||||
debuglev = level;
|
debuglev = level;
|
||||||
debugger_show_elapsed_time = show_elapsed_time;
|
debugger_show_elapsed_time = show_elapsed_time;
|
||||||
debugger_show_relative_time = show_relative_time;
|
debugger_show_relative_time = show_relative_time;
|
||||||
debugger_show_file_and_line = show_file_and_line;
|
debugger_show_file_and_line = show_file_and_line;
|
||||||
|
local_exit_requester = exit_requester;
|
||||||
}
|
}
|
||||||
|
|
||||||
int debug_level() { return debuglev; };
|
int debug_level() { return debuglev; };
|
||||||
@@ -143,7 +147,14 @@ void _die(const char *filename, const int linenumber, const char *format, ...) {
|
|||||||
// syslog(LOG_ERR, "%s", b);
|
// syslog(LOG_ERR, "%s", b);
|
||||||
fprintf(stderr, "%s\n", b);
|
fprintf(stderr, "%s\n", b);
|
||||||
pthread_setcancelstate(oldState, NULL);
|
pthread_setcancelstate(oldState, NULL);
|
||||||
exit(EXIT_FAILURE);
|
if (local_exit_requester != NULL) {
|
||||||
|
local_exit_requester(EXIT_FAILURE);
|
||||||
|
// wait for the exit request to be honoured
|
||||||
|
usleep(1000000);
|
||||||
|
// if not, head for the hills
|
||||||
|
fprintf(stderr, "fatal error: exit cleanup could not be completed.\n");
|
||||||
|
_Exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _warn(const char *filename, const int linenumber, const char *format, ...) {
|
void _warn(const char *filename, const int linenumber, const char *format, ...) {
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2023--2025 Mike Brady 4265913+mikebrady@users.noreply.github.com
|
Copyright (c) 2023--2026 Mike Brady 4265913+mikebrady@users.noreply.github.com
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -38,7 +38,7 @@ SOFTWARE.
|
|||||||
|
|
||||||
// level 0 is no messages, level 3 is most messages
|
// level 0 is no messages, level 3 is most messages
|
||||||
EXTERNC void debug_init(int level, int show_elapsed_time, int show_relative_time,
|
EXTERNC void debug_init(int level, int show_elapsed_time, int show_relative_time,
|
||||||
int show_file_and_line);
|
int show_file_and_line, void (*exit_requester)(const int exit_status));
|
||||||
|
|
||||||
EXTERNC int debug_level();
|
EXTERNC int debug_level();
|
||||||
EXTERNC void set_debug_level(int level);
|
EXTERNC void set_debug_level(int level);
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023--2026 Mike Brady 4265913+mikebrady@users.noreply.github.com
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// exit_init() starts a thread what waits for the exit_request_flag to
|
||||||
|
// be set to non-zero.
|
||||||
|
// It then runs the standard exit(exit_status) to
|
||||||
|
// terminate the program, running all the atexit handlers first.
|
||||||
|
|
||||||
|
// exit_request() can be called from anywhere in the program
|
||||||
|
// including a SIG handler and
|
||||||
|
// the thread will take care of terminating the program cleanly.
|
||||||
|
// pass in EXIT_SUCCESS or EXIT_FAILURE in the request.
|
||||||
|
|
||||||
|
#include <signal.h> // for sig_atomic_t
|
||||||
|
#include <stdlib.h> // for EXIT_SUCCESS
|
||||||
|
#include <string.h> // for memset
|
||||||
|
#include <unistd.h> // for usleep
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "exit.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
volatile sig_atomic_t exit_request_flag = 0;
|
||||||
|
volatile sig_atomic_t exit_status = EXIT_SUCCESS;
|
||||||
|
|
||||||
|
pthread_t exit_manager_thread;
|
||||||
|
|
||||||
|
void *exit_manager(__attribute__((unused)) void *arg) {
|
||||||
|
while(exit_request_flag == 0) {
|
||||||
|
usleep(100000);
|
||||||
|
}
|
||||||
|
exit(exit_status);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void exit_init() {
|
||||||
|
memset(&exit_manager_thread, 0, sizeof(pthread_t));
|
||||||
|
named_pthread_create(&exit_manager_thread, NULL, &exit_manager, NULL, "exit_manager");
|
||||||
|
}
|
||||||
|
|
||||||
|
void exit_request(const int exit_status_requested) {
|
||||||
|
exit_status = exit_status_requested; // EXIT_SUCCESS or EXIT_FAILURE
|
||||||
|
exit_request_flag = 1; // ask for exit
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023--2026 Mike Brady 4265913+mikebrady@users.noreply.github.com
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
void exit_init();
|
||||||
|
void exit_request(const int exit_status); // call this to ask for the program to be exited.
|
||||||
|
// pass in EXIT_SUCCESS or EXIT_FAILURE.
|
||||||
@@ -52,7 +52,7 @@ int _safe_socket_close(const char *filename, const int linenumber, int *sockfd)
|
|||||||
int result = 0;
|
int result = 0;
|
||||||
int oldstate;
|
int oldstate;
|
||||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
|
||||||
debug_mutex_lock(&safe_socket_lock, 1000000, 1);
|
pthread_mutex_lock(&safe_socket_lock);
|
||||||
if (*sockfd == 0) {
|
if (*sockfd == 0) {
|
||||||
_debug(filename, linenumber, 1, "_safe_socket_close: socket is zero!");
|
_debug(filename, linenumber, 1, "_safe_socket_close: socket is zero!");
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ int _safe_socket_close(const char *filename, const int linenumber, int *sockfd)
|
|||||||
} else {
|
} else {
|
||||||
_debug(filename, linenumber, 1, "_safe_socket_close: socket already closed!");
|
_debug(filename, linenumber, 1, "_safe_socket_close: socket already closed!");
|
||||||
}
|
}
|
||||||
debug_mutex_unlock(&safe_socket_lock, 4);
|
pthread_mutex_unlock(&safe_socket_lock);
|
||||||
pthread_setcancelstate(oldstate, NULL);
|
pthread_setcancelstate(oldstate, NULL);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ static pthread_mutex_t reference_counter_lock = PTHREAD_MUTEX_INITIALIZER;
|
|||||||
static int msg_indexes = 1;
|
static int msg_indexes = 1;
|
||||||
|
|
||||||
void msg_retain(rtsp_message *msg) {
|
void msg_retain(rtsp_message *msg) {
|
||||||
int rc = debug_mutex_lock(&reference_counter_lock, 500000, 4);
|
int rc = pthread_mutex_lock(&reference_counter_lock);
|
||||||
if (rc)
|
if (rc)
|
||||||
debug(1, "Error %d locking reference counter lock", rc);
|
debug(1, "Error %d locking reference counter lock", rc);
|
||||||
if (msg > (rtsp_message *)0x00010000) {
|
if (msg > (rtsp_message *)0x00010000) {
|
||||||
@@ -63,7 +63,7 @@ void msg_retain(rtsp_message *msg) {
|
|||||||
|
|
||||||
rtsp_message *msg_init(void) {
|
rtsp_message *msg_init(void) {
|
||||||
// no thread cancellation points here
|
// no thread cancellation points here
|
||||||
int rc = debug_mutex_lock(&reference_counter_lock, 500000, 4);
|
int rc = pthread_mutex_lock(&reference_counter_lock);
|
||||||
if (rc)
|
if (rc)
|
||||||
debug(1, "Error %d locking reference counter lock", rc);
|
debug(1, "Error %d locking reference counter lock", rc);
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ void _debug_print_msg_headers(rtsp_conn_info *conn, const char *filename, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void msg_free(rtsp_message **msgh) {
|
void msg_free(rtsp_message **msgh) {
|
||||||
debug_mutex_lock(&reference_counter_lock, 1000, 0);
|
pthread_mutex_lock(&reference_counter_lock);
|
||||||
if (*msgh > (rtsp_message *)0x00010000) {
|
if (*msgh > (rtsp_message *)0x00010000) {
|
||||||
rtsp_message *msg = *msgh;
|
rtsp_message *msg = *msgh;
|
||||||
msg->referenceCount--;
|
msg->referenceCount--;
|
||||||
@@ -161,7 +161,7 @@ void msg_free(rtsp_message **msgh) {
|
|||||||
"%" PRIxPTR ".",
|
"%" PRIxPTR ".",
|
||||||
(uintptr_t)*msgh);
|
(uintptr_t)*msgh);
|
||||||
}
|
}
|
||||||
debug_mutex_unlock(&reference_counter_lock, 0);
|
pthread_mutex_unlock(&reference_counter_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
int msg_handle_line(rtsp_message **pmsg, char *line) {
|
int msg_handle_line(rtsp_message **pmsg, char *line) {
|
||||||
|
|||||||
Reference in New Issue
Block a user