5.0.3
This commit is contained in:
@@ -59,7 +59,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Login to Docker Registry
|
||||
uses: docker/login-action@v4.0.0
|
||||
uses: docker/login-action@v4.1.0
|
||||
with:
|
||||
registry: ${{ secrets.DOCKER_REGISTRY }}
|
||||
username: ${{ secrets.DOCKER_REGISTRY_USER }}
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
uses: docker/setup-buildx-action@v4.0.0
|
||||
|
||||
- name: Build and push ${{ matrix.name }}
|
||||
uses: docker/build-push-action@v7.0.0
|
||||
uses: docker/build-push-action@v7.1.0
|
||||
env:
|
||||
registry_and_name: ${{ secrets.DOCKER_REGISTRY }}/${{ secrets.DOCKER_IMAGE_NAME }}
|
||||
with:
|
||||
|
||||
@@ -1,7 +1,37 @@
|
||||
/*
|
||||
* Convolver Thread Pool. This file is part of Shairport Sync
|
||||
* Copyright (c) Mike Brady 2026
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "ConvolverThreadPool.h"
|
||||
#include "FFTConvolver.h"
|
||||
#include "config.h"
|
||||
|
||||
extern "C" void _debug(const char *filename, const int linenumber, int level, const char *format,
|
||||
...);
|
||||
#define debug(...) _debug(__FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
ConvolverThreadPool::ConvolverThreadPool()
|
||||
: _convolvers(), _threads(), _taskQueue(), _queueMutex(), _condition(), _completionCV(),
|
||||
_stop(false), _activeTasks(0) {}
|
||||
@@ -105,6 +135,16 @@ void ConvolverThreadPool::waitForAll() {
|
||||
_completionCV.wait(lock, [this]() { return _taskQueue.empty() && _activeTasks == 0; });
|
||||
}
|
||||
|
||||
void ConvolverThreadPool::clearState(size_t convolverId) {
|
||||
// Do the replacement assertion check first, and then wait for all tasks to stop
|
||||
if (convolverId < _convolvers.size()) {
|
||||
waitForAll();
|
||||
} else {
|
||||
debug(1, "assert(convolverId < _convolvers.size()) failed, with convolverId: %u and _convolvers.size(): %u.", convolverId, _convolvers.size());
|
||||
}
|
||||
}
|
||||
|
||||
/* this is the old version
|
||||
void ConvolverThreadPool::clearState(size_t convolverId) {
|
||||
assert(convolverId < _convolvers.size());
|
||||
|
||||
@@ -113,6 +153,7 @@ void ConvolverThreadPool::clearState(size_t convolverId) {
|
||||
|
||||
// _convolvers[convolverId]->clearState();
|
||||
}
|
||||
*/
|
||||
|
||||
void ConvolverThreadPool::clearAllStates() {
|
||||
// Make sure no tasks are running before clearing state
|
||||
|
||||
@@ -1,4 +1,28 @@
|
||||
|
||||
/*
|
||||
* Convolver. This file is part of Shairport Sync
|
||||
* Copyright (c) Mike Brady 2026
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "convolver.h"
|
||||
#include "ConvolverThreadPool.h"
|
||||
@@ -22,8 +46,8 @@ void convolver_pool_init(size_t numThreads, size_t numConvolvers) {
|
||||
if (!pool.init(numThreads, numConvolvers)) {
|
||||
debug(1, "failed to initialize thread pool!");
|
||||
} else {
|
||||
debug(1, "thread pool initialized with %u threads and %u convolvers.", numThreads,
|
||||
numConvolvers);
|
||||
debug(1, "thread pool initialized with %u thread%s and %u convolver%s.", numThreads, numThreads == 1 ? "" : "s",
|
||||
numConvolvers, numConvolvers == 1 ? "" : "s");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -235,6 +235,7 @@ void *rtp_buffered_audio_processor(void *arg) {
|
||||
uint16_t data_len;
|
||||
// here we read from the buffer that our thread has been reading
|
||||
|
||||
// debug(1,"read a block");
|
||||
size_t bytes_remaining_in_buffer;
|
||||
nread =
|
||||
read_sized_block(buffered_audio, &data_len, sizeof(data_len), &bytes_remaining_in_buffer);
|
||||
@@ -249,6 +250,7 @@ void *rtp_buffered_audio_processor(void *arg) {
|
||||
// get the block itself
|
||||
// debug(1,"buffered audio packet of size %u detected.", data_len - 2);
|
||||
nread = read_sized_block(buffered_audio, packet, data_len - 2, &bytes_remaining_in_buffer);
|
||||
// debug(1,"block read");
|
||||
|
||||
// diagnostic
|
||||
if ((conn->ap2_audio_buffer_minimum_size < 0) ||
|
||||
@@ -442,7 +444,7 @@ void *rtp_buffered_audio_processor(void *arg) {
|
||||
// to decode it and pass it to the player
|
||||
if (new_audio_block_needed == 0) {
|
||||
// is there space in the player thread's buffer system?
|
||||
size_t player_buffer_occupancy = get_audio_buffer_occupancy(conn);
|
||||
// size_t player_buffer_occupancy = get_audio_buffer_occupancy(conn);
|
||||
// debug(1,"player buffer size and occupancy: %u and %u", player_buffer_size,
|
||||
// player_buffer_occupancy);
|
||||
|
||||
@@ -451,17 +453,41 @@ void *rtp_buffered_audio_processor(void *arg) {
|
||||
// and send it to the player. Otherwise, keep the block and sleep for a while.
|
||||
|
||||
// calculate if there is room in the decoded audio buffer...
|
||||
int audio_decoded_buffer_below_desired_length = ((1.0 * player_buffer_occupancy * conn->frames_per_packet) / conn->input_rate) <= config.audio_decoded_buffer_desired_length;
|
||||
|
||||
// debug(1, "frames buffered: %f seconds, desired length: %f seconds.", (1.0 * player_buffer_occupancy * conn->frames_per_packet) / conn->input_rate, config.audio_decoded_buffer_desired_length);
|
||||
|
||||
// int audio_decoded_buffer_below_desired_length = ((1.0 * player_buffer_occupancy * conn->frames_per_packet) / conn->input_rate) <= config.audio_decoded_buffer_desired_length;
|
||||
uint64_t buffer_should_be_time;
|
||||
|
||||
int have_valid_time = (frame_to_local_time(timestamp, &buffer_should_be_time, conn) == 0);
|
||||
|
||||
// calculate the lead time to make sure it's not too early...
|
||||
int64_t lead_time = buffer_should_be_time - get_absolute_time_in_ns();
|
||||
|
||||
if ((play_enabled != 0) && (have_valid_time != 0) &&
|
||||
(audio_decoded_buffer_below_desired_length != 0) &&
|
||||
(lead_time * 1E-9 < (config.audio_decoded_buffer_desired_length + 0.1))) {
|
||||
|
||||
|
||||
// debug(1,"play_enabled: %d, have_valid_time: %d, audio_decoded_buffer_below_desired_length: %d, lead_time * 1E-9: %f, (config.audio_decoded_buffer_desired_length + 0.1): %f, player_buffer_occupancy: %zu",
|
||||
// play_enabled, have_valid_time, audio_decoded_buffer_below_desired_length, lead_time * 1E-9, (config.audio_decoded_buffer_desired_length + 0.1), player_buffer_occupancy
|
||||
// );
|
||||
|
||||
// A slight problem here is that counting the number of buffers may not be sufficient,
|
||||
// because the actual device may be
|
||||
// taking data in large quantities at a single time.
|
||||
|
||||
// So we just have to ensure that there
|
||||
// is enough of a lead time maintained for sufficient audio to be available to prevent
|
||||
// the device from under-running.
|
||||
|
||||
// If means that the Shairport Sync player might riun out of audio occasionally, but
|
||||
// as long as the device has enough in its buffer, everything is fine.
|
||||
|
||||
// But it also means that Shairport Sync's buffers must be sufficient to hold all the
|
||||
// entire lead-time's amount of audio in case the device has a zero-sized buffer.
|
||||
|
||||
if ((play_enabled != 0) && (have_valid_time != 0)
|
||||
// (audio_decoded_buffer_below_desired_length != 0) &&
|
||||
&& (lead_time * 1E-9 < (config.audio_decoded_buffer_desired_length + 0.1))
|
||||
// && (audio_decoded_buffer_below_desired_length != 0)
|
||||
) {
|
||||
|
||||
very_early_packets_signalled = 0; //reset very early packet warning signaller
|
||||
|
||||
@@ -639,8 +665,7 @@ void *rtp_buffered_audio_processor(void *arg) {
|
||||
debug(1, "incoming frame suddenly (?) has a lead time of %f seconds, with a desired decoded buffer length of %f.", 1.0 * lead_time * 1E-9, config.audio_decoded_buffer_desired_length);
|
||||
very_early_packets_signalled = 1;
|
||||
}
|
||||
|
||||
usleep(20000); // wait for a while
|
||||
usleep(((1000000 * conn->frames_per_packet) / conn->input_rate) * 2); // wait for approximately the length of two packets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -985,7 +985,7 @@ int32_t search_for_suitable_configuration(unsigned int channels, unsigned int ra
|
||||
}
|
||||
#ifdef CONFIG_FFMPEG
|
||||
if (local_format == SPS_FORMAT_UNKNOWN) {
|
||||
debug(3, "check for an next highest rate above %u with %u channels.", rate,
|
||||
debug(3, "check for the next highest rate above %u with %u channels.", rate,
|
||||
local_channels);
|
||||
unsigned int rate_pointer = 0;
|
||||
while ((rate_pointer < sizeof(rates) / sizeof(unsigned int)) &&
|
||||
@@ -1003,7 +1003,7 @@ int32_t search_for_suitable_configuration(unsigned int channels, unsigned int ra
|
||||
|
||||
if (local_format == SPS_FORMAT_UNKNOWN) {
|
||||
int rate_pointer = (int)(sizeof(rates) / sizeof(unsigned int) - 1);
|
||||
debug(3, "check for an next lowest rate below %u with %u channels.", rate,
|
||||
debug(3, "check for the next lowest rate below %u with %u channels.", rate,
|
||||
local_channels);
|
||||
while ((rate_pointer >= 0) && (local_format == SPS_FORMAT_UNKNOWN)) {
|
||||
local_rate = rates[rate_pointer];
|
||||
|
||||
@@ -1041,32 +1041,49 @@ uint8_t *rsa_apply(uint8_t *input, int inlen, int *outlen, int mode) {
|
||||
#endif
|
||||
|
||||
int config_lookup_non_empty_string(const config_t *cfg, const char *path, const char **value) {
|
||||
int response = config_lookup_string(cfg, path, value);
|
||||
if (response == CONFIG_TRUE) {
|
||||
if ((value != NULL) && ((*value == NULL) || (*value[0] == 0))) {
|
||||
warn("The \"%s\" parameter is an empty string and has been ignored.", path);
|
||||
response = CONFIG_FALSE;
|
||||
int response = CONFIG_FALSE;
|
||||
config_setting_t *s = config_lookup(cfg, path);
|
||||
if (s != NULL) {
|
||||
// the setting exists, but might not be a string
|
||||
if (config_setting_type(s) == CONFIG_TYPE_STRING) {
|
||||
if (value != NULL) {
|
||||
*value = config_setting_get_string(s);
|
||||
response = CONFIG_TRUE;
|
||||
// the string might be empty...
|
||||
if ((*value == NULL) || (*value[0] == 0)) {
|
||||
warn("The \"%s\" parameter is an empty string and has been ignored.", path);
|
||||
response = CONFIG_FALSE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
warn("the \"%s\" parameter is not a string, as required, and has been ignored.", path);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
int config_set_lookup_bool(config_t *cfg, char *where, int *dst) {
|
||||
const char *str = 0;
|
||||
if (config_lookup_string(cfg, where, &str)) {
|
||||
if (strcasecmp(str, "no") == 0) {
|
||||
(*dst) = 0;
|
||||
return 1;
|
||||
} else if (strcasecmp(str, "yes") == 0) {
|
||||
(*dst) = 1;
|
||||
return 1;
|
||||
int config_set_lookup_bool(config_t *cfg, const char *where, int *dst) {
|
||||
const char *str = NULL;
|
||||
int response = CONFIG_FALSE;
|
||||
config_setting_t *s = config_lookup(cfg, where);
|
||||
if (s != NULL) {
|
||||
if (config_setting_type(s) == CONFIG_TYPE_STRING) {
|
||||
str = config_setting_get_string(s);
|
||||
if (strcasecmp(str, "no") == 0) {
|
||||
(*dst) = 0;
|
||||
response = CONFIG_TRUE;
|
||||
} else if (strcasecmp(str, "yes") == 0) {
|
||||
(*dst) = 1;
|
||||
response = CONFIG_TRUE;
|
||||
} else {
|
||||
die("invalid boolean parameter \"%s\" option choice \"%s\". It should be \"yes\" or \"no\"", where, str);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
die("Invalid %s option choice \"%s\". It should be \"yes\" or \"no\"", where, str);
|
||||
return 0;
|
||||
warn("the \"%s\" parameter is not a string with a value of \"yes\" or \"no\", as required, and has been ignored.", where);
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
// remember to free the returned array of strings.
|
||||
|
||||
@@ -561,7 +561,7 @@ extern int type_of_exit_cleanup; // normal, emergency, dbus requested...
|
||||
extern uint64_t minimum_dac_queue_size;
|
||||
|
||||
int config_lookup_non_empty_string(const config_t *cfg, const char *path, const char **value);
|
||||
int config_set_lookup_bool(config_t *cfg, char *where, int *dst);
|
||||
int config_set_lookup_bool(config_t *cfg, const char *where, int *dst);
|
||||
int check_string_or_list_setting(config_setting_t *setting, const char *item);
|
||||
int check_int_or_list_setting(config_setting_t *setting, const int item);
|
||||
|
||||
|
||||
+14
-17
@@ -1,7 +1,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ([2.50])
|
||||
AC_INIT([shairport-sync], [5.0.2], [4265913+mikebrady@users.noreply.github.com])
|
||||
AC_INIT([shairport-sync], [5.0.3], [4265913+mikebrady@users.noreply.github.com])
|
||||
: ${CFLAGS="-O3"}
|
||||
: ${CXXFLAGS="-O3"}
|
||||
AM_INIT_AUTOMAKE([subdir-objects])
|
||||
@@ -228,8 +228,8 @@ if test "x$with_avahi" = "xyes" ; then
|
||||
AC_DEFINE([CONFIG_AVAHI], 1, [Include Avahi-based mDNS support.])
|
||||
AC_CHECK_LIB([avahi-client], [avahi_client_new], , AC_MSG_ERROR(Avahi support requires the avahi-client library!))
|
||||
AC_CHECK_LIB([avahi-common],[avahi_strerror], , AC_MSG_ERROR(Avahi support requires the avahi-common library!))
|
||||
systemd_after_args="${systemd_after_args} avahi-daemon.service"
|
||||
systemd_requires_args="${systemd_requires_args} avahi-daemon.service"
|
||||
systemd_after_args="${systemd_after_args}${systemd_after_args:+ }avahi-daemon.service"
|
||||
systemd_requires_args="${systemd_requires_args}${systemd_requires_args:+ }avahi-daemon.service"
|
||||
fi
|
||||
AM_CONDITIONAL([USE_AVAHI], [test "x$with_avahi" = "xyes"])
|
||||
|
||||
@@ -412,6 +412,7 @@ AM_CONDITIONAL([USE_METADATA], [test "x$with_metadata" = "xyes"])
|
||||
# Look for AirPlay 2 flag
|
||||
AC_ARG_WITH(airplay-2, [AS_HELP_STRING([--with-airplay-2],[Build for AirPlay 2])])
|
||||
if test "x$with_airplay_2" = "xyes" ; then
|
||||
using_ffmpeg=true
|
||||
AC_DEFINE([CONFIG_AIRPLAY_2], 1, [Build for AirPlay 2])
|
||||
AC_CHECK_PROG(PLISTUTIL_CHECK,plistutil,yes)
|
||||
AS_IF([test x"$PLISTUTIL_CHECK" != x"yes"], [AC_MSG_ERROR([plistutil can not be found. Please install plistutil for building for AirPlay 2. Maybe it's in the apt package libplist-utils.])])
|
||||
@@ -443,6 +444,8 @@ if test "x$with_airplay_2" = "xyes" ; then
|
||||
)
|
||||
])
|
||||
AC_CHECK_LIB([uuid],[uuid_generate], [], [AC_MSG_ERROR([AirPlay 2 support requires the uuid library -- uuid-dev suggested])])
|
||||
systemd_after_args="${systemd_after_args}${systemd_after_args:+ }nqptp.service"
|
||||
systemd_requires_args="${systemd_requires_args}${systemd_requires_args:+ }nqptp.service"
|
||||
fi
|
||||
AM_CONDITIONAL([USE_AIRPLAY_2], [test "x$with_airplay_2" = "xyes"])
|
||||
|
||||
@@ -455,7 +458,7 @@ if test "x$with_ffmpeg" = "xyes" -o "x$with_airplay_2" = "xyes" ; then
|
||||
PKG_CHECK_MODULES(
|
||||
[libavutil], [libavutil],
|
||||
[CFLAGS="${libavutil_CFLAGS} ${CFLAGS}" LIBS="${libavutil_LIBS} ${LIBS}"],
|
||||
[AC_MSG_ERROR(AirPlay 2 support requires libavutil -- libavutil-dev suggested)]
|
||||
[AC_MSG_ERROR(FFmpeg requires libavutil -- libavutil-dev suggested)]
|
||||
)
|
||||
PKG_CHECK_MODULES(
|
||||
[libavcodec], [libavcodec],
|
||||
@@ -463,7 +466,7 @@ if test "x$with_ffmpeg" = "xyes" -o "x$with_airplay_2" = "xyes" ; then
|
||||
[AC_CHECK_LIB(
|
||||
[avcodec], [avcodec_find_decoder],
|
||||
[],
|
||||
[AC_MSG_ERROR(AirPlay 2 support requires libavcodec -- libavcodec-dev suggested)]
|
||||
[AC_MSG_ERROR(FFmpeg requires libavcodec -- libavcodec-dev suggested)]
|
||||
)]
|
||||
)
|
||||
PKG_CHECK_MODULES(
|
||||
@@ -472,7 +475,7 @@ if test "x$with_ffmpeg" = "xyes" -o "x$with_airplay_2" = "xyes" ; then
|
||||
[AC_CHECK_LIB(
|
||||
[avformat], [avformat_new_stream],
|
||||
[],
|
||||
[AC_MSG_ERROR([AirPlay 2 support requires libavformat -- libavformat-dev suggested])]
|
||||
[AC_MSG_ERROR([FFmpeg requires libavformat -- libavformat-dev suggested])]
|
||||
)]
|
||||
)
|
||||
PKG_CHECK_MODULES(
|
||||
@@ -481,16 +484,7 @@ if test "x$with_ffmpeg" = "xyes" -o "x$with_airplay_2" = "xyes" ; then
|
||||
[AC_CHECK_LIB(
|
||||
[swresample], [swr_convert],
|
||||
[],
|
||||
[AC_MSG_ERROR([AirPlay 2 support requires libswresample -- libswresample-dev suggested])]
|
||||
)]
|
||||
)
|
||||
PKG_CHECK_MODULES(
|
||||
[libuuid], [uuid],
|
||||
[CFLAGS="${libuuid_CFLAGS} ${CFLAGS}" LIBS="${libuuid_LIBS} ${LIBS}"],
|
||||
[AC_CHECK_LIB(
|
||||
[uuid], [uuid_generate],
|
||||
[],
|
||||
[AC_MSG_ERROR([AirPlay 2 support requires the uuid library -- uuid-dev suggested])]
|
||||
[AC_MSG_ERROR([FFmpeg requires libswresample -- libswresample-dev suggested])]
|
||||
)]
|
||||
)
|
||||
else
|
||||
@@ -505,8 +499,11 @@ AM_CONDITIONAL([USE_HAMMERTON], [test $using_ffmpeg = false])
|
||||
# Look for Apple ALAC flag
|
||||
AC_ARG_WITH(apple-alac,[AS_HELP_STRING([--with-apple-alac],[include support for the Apple ALAC decoder (deprecated)])])
|
||||
if test "x${with_apple_alac}" = "xyes" ; then
|
||||
if test "x${with_airplay_2}" = "xyes" ; then
|
||||
AC_MSG_ERROR([the Apple ALAC Decoder (deprecated) can not be used in AirPlay 2.])
|
||||
fi
|
||||
if test $using_ffmpeg = true ; then
|
||||
AC_MSG_RESULT([Note: the FFMPEG decoder, included in the build, supercedes the Apple ALAC decoder, which is deprecated. Consider omitting the Apple ALAC decoder.])
|
||||
AC_MSG_WARN([the FFMPEG decoder, included in the build, supercedes the Apple ALAC Decoder, which is deprecated. Consider omitting the Apple ALAC Decoder.])
|
||||
fi
|
||||
AC_DEFINE([CONFIG_APPLE_ALAC], 1, [Include support for using the Apple ALAC Decoder (deprecated)])
|
||||
if test "x${with_pkg_config}" = xyes ; then
|
||||
|
||||
+8
-1
@@ -737,6 +737,12 @@ gboolean notify_alacdecoder_callback(ShairportSync *skeleton,
|
||||
__attribute__((unused)) gpointer user_data) {
|
||||
char *th = (char *)shairport_sync_get_alacdecoder(skeleton);
|
||||
|
||||
#ifdef CONFIG_AIRPLAY_2
|
||||
if (strcasecmp(th, "ffmpeg") != 0) {
|
||||
warn(" This request, to set the decoder to \"%s\", is ignored. For AirPlay 2, the FFmpeg decoder is always used.",
|
||||
th);
|
||||
}
|
||||
#else
|
||||
if ((strcasecmp(th, "hammerton") == 0) &&
|
||||
((config.decoders_supported & (1 << decoder_hammerton)) != 0))
|
||||
config.decoder_in_use = 1 << decoder_hammerton;
|
||||
@@ -747,12 +753,13 @@ gboolean notify_alacdecoder_callback(ShairportSync *skeleton,
|
||||
((config.decoders_supported & (1 << decoder_ffmpeg_alac)) != 0))
|
||||
config.decoder_in_use = 1 << decoder_ffmpeg_alac;
|
||||
else {
|
||||
warn("An unrecognised or unsupported ALAC decoder: \"%s\" was requested via D-Bus interface. "
|
||||
warn("An unrecognised or unsupported decoder: \"%s\" was requested via D-Bus interface. "
|
||||
"(Possibly "
|
||||
"support for this decoder was not compiled "
|
||||
"into this version of Shairport Sync.)",
|
||||
th);
|
||||
}
|
||||
#endif
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -2687,7 +2687,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn, int resync_requested) {
|
||||
// wait if the buffer is empty
|
||||
if ((conn->ab_synced != 0) && (conn->ab_read == conn->ab_write)) { // the buffer is empty!
|
||||
if (notified_buffer_empty == 0) {
|
||||
debug(2, "Connection %d: Buffer Empty", conn->connection_number);
|
||||
debug(4, "Connection %d: Buffer Empty", conn->connection_number);
|
||||
notified_buffer_empty = 1;
|
||||
// reset_input_flow_metrics(conn); // don't do a full flush parameters reset
|
||||
// conn->initial_reference_time = 0;
|
||||
|
||||
@@ -1513,9 +1513,12 @@ int frame_to_ptp_local_time(uint32_t timestamp, uint64_t *time, rtsp_conn_info *
|
||||
int32_t frame_difference = timestamp - anchor_rtptime;
|
||||
int64_t time_difference = frame_difference;
|
||||
time_difference = time_difference * 1000000000;
|
||||
if (conn->input_rate == 0)
|
||||
die("conn->input_rate is zero!");
|
||||
time_difference = time_difference / conn->input_rate;
|
||||
if (conn->input_rate == 0) {
|
||||
debug(1,"in a call to frame_to_ptp_local_time, conn->input_rate is zero!");
|
||||
time_difference = 0;
|
||||
} else {
|
||||
time_difference = time_difference / conn->input_rate;
|
||||
}
|
||||
uint64_t ltime = anchor_local_time + time_difference;
|
||||
*time = ltime;
|
||||
result = 0;
|
||||
|
||||
@@ -2828,6 +2828,8 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
|
||||
plist_array_append_item(addresses, plist_new_string(conn->self_ip_string));
|
||||
// debug(1,"self ip: \"%s\"", conn->self_ip_string);
|
||||
|
||||
int oldState;
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
|
||||
struct ifaddrs *addrs, *iap;
|
||||
getifaddrs(&addrs);
|
||||
for (iap = addrs; iap != NULL; iap = iap->ifa_next) {
|
||||
@@ -2862,6 +2864,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
|
||||
}
|
||||
}
|
||||
freeifaddrs(addrs);
|
||||
pthread_setcancelstate(oldState, NULL);
|
||||
|
||||
// debug(1,"initial timing peer command: \"%s\".", timing_list_message);
|
||||
// ptp_send_control_message_string(timing_list_message);
|
||||
|
||||
+40
-18
@@ -195,13 +195,12 @@ int has_fltp_capable_aac_decoder(void) {
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SOXR
|
||||
pthread_t soxr_time_check_thread;
|
||||
int soxr_time_check_thread_started = 0;
|
||||
pthread_t *soxr_time_check_thread = NULL;
|
||||
void *soxr_time_check(__attribute__((unused)) void *arg) {
|
||||
// this just checks how long it takes to process adding and subtracing a frame
|
||||
// from a buffer at 44100
|
||||
// #include <syscall.h>
|
||||
// debug(1, "soxr_time_check PID %d", syscall(SYS_gettid));
|
||||
// #include <syscall.h>
|
||||
// debug(1, "soxr_time_check PID %ld", syscall(SYS_gettid));
|
||||
|
||||
const int buffer_length = 352;
|
||||
int32_t inbuffer[buffer_length * 2];
|
||||
@@ -221,7 +220,7 @@ void *soxr_time_check(__attribute__((unused)) void *arg) {
|
||||
(uint64_t)1500000000 + soxr_start_time; // loop for a second and a half, max -- no need to be
|
||||
// able to cancel it, do _don't even try_!
|
||||
while (get_absolute_time_in_ns() < loop_until_time) {
|
||||
|
||||
pthread_testcancel();
|
||||
number_of_iterations++;
|
||||
for (i = 0; i < buffer_length; i++) {
|
||||
double w = sin(i * (frequency + number_of_iterations * 2) * 2 * M_PI / 44100);
|
||||
@@ -230,6 +229,9 @@ void *soxr_time_check(__attribute__((unused)) void *arg) {
|
||||
inbuffer[i * 2 + 1] = wint;
|
||||
}
|
||||
|
||||
int oldState;
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
||||
|
||||
soxr_io_spec_t io_spec;
|
||||
io_spec.itype = SOXR_INT32_I;
|
||||
io_spec.otype = SOXR_INT32_I;
|
||||
@@ -256,6 +258,8 @@ void *soxr_time_check(__attribute__((unused)) void *arg) {
|
||||
outbuffer, buffer_length - 1, &odone, // Output.
|
||||
&io_spec, // Input, output and transfer spec.
|
||||
NULL, NULL); // Default configuration.
|
||||
|
||||
pthread_setcancelstate(oldState, NULL);
|
||||
}
|
||||
|
||||
int64_t soxr_execution_time =
|
||||
@@ -271,8 +275,8 @@ void *soxr_time_check(__attribute__((unused)) void *arg) {
|
||||
debug(1, "No soxr-timing iterations performed, so \"vernier\" iteration will be used.");
|
||||
config.soxr_delay_index = 0; // used as a flag
|
||||
}
|
||||
debug(2, "soxr_delay: %d nanoseconds, soxr_delay_threshold: %d milliseconds.",
|
||||
config.soxr_delay_index, config.soxr_delay_threshold / 1000000);
|
||||
debug(2, "soxr_delay maximum permissible: %d milliseconds, actual: %f milliseconds.",
|
||||
config.soxr_delay_threshold / 1000000, 1E-6 * config.soxr_delay_index);
|
||||
if ((config.packet_stuffing == ST_soxr) &&
|
||||
(config.soxr_delay_index > config.soxr_delay_threshold))
|
||||
inform("Note: this device may be too slow for \"soxr\" interpolation. Consider choosing the "
|
||||
@@ -1856,7 +1860,6 @@ void exit_function() {
|
||||
(config.daemonise == 0)) { // if this is the daemon process that is exiting or it's not
|
||||
// actually daemonised at all
|
||||
#endif
|
||||
debug(2, "exit function called...");
|
||||
/*
|
||||
Actually, there is no terminate_mqtt() function.
|
||||
#ifdef CONFIG_MQTT
|
||||
@@ -1902,12 +1905,16 @@ void exit_function() {
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SOXR
|
||||
// be careful -- not sure if the thread can be cancelled cleanly, so wait for it to shut down
|
||||
if (soxr_time_check_thread_started != 0) {
|
||||
debug(2, "Waiting for SoXr timecheck to terminate...");
|
||||
pthread_join(soxr_time_check_thread, NULL);
|
||||
soxr_time_check_thread_started = 0;
|
||||
debug(2, "Waiting for SoXr timecheck to terminate done");
|
||||
{
|
||||
int oldState;
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
||||
if (soxr_time_check_thread != NULL) {
|
||||
pthread_cancel(*soxr_time_check_thread);
|
||||
pthread_join(*soxr_time_check_thread, NULL);
|
||||
free(soxr_time_check_thread);
|
||||
soxr_time_check_thread = NULL;
|
||||
}
|
||||
pthread_setcancelstate(oldState, NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1994,7 +2001,7 @@ void exit_function() {
|
||||
#else
|
||||
mdns_unregister(); // once the dacp handler is done and all player threrads are done it should
|
||||
// be safe
|
||||
debug(1, "normal exit");
|
||||
debug(2, "normal exit");
|
||||
#endif
|
||||
} else {
|
||||
debug(1, "emergency exit");
|
||||
@@ -2346,6 +2353,7 @@ int main(int argc, char **argv) {
|
||||
openlog(NULL, 0, LOG_DAEMON);
|
||||
#endif
|
||||
type_of_exit_cleanup = TOE_normal; // what kind of exit cleanup needed
|
||||
debug(1, "adding the exit function");
|
||||
atexit(exit_function);
|
||||
|
||||
// get a device id -- the first non-local MAC address
|
||||
@@ -2957,7 +2965,9 @@ int main(int argc, char **argv) {
|
||||
config.output_channel_map[i] = strdup(channel_id);
|
||||
debug(2, "output channel %d is \"%s\".", i, config.output_channel_map[i]);
|
||||
} else {
|
||||
warn("channel \"%s\" is not recognised -- output channel %d will be silent.",
|
||||
|
||||
|
||||
warn("during channel mapping, \"%s\" was not recognised as a channel name -- as a result, output channel %d will be silent.",
|
||||
channel_id, i);
|
||||
config.output_channel_map[i] = strdup("--");
|
||||
}
|
||||
@@ -3153,8 +3163,20 @@ int main(int argc, char **argv) {
|
||||
debug(option_print_level, "loudness reference level is %f", config.loudness_reference_volume_db);
|
||||
|
||||
#ifdef CONFIG_SOXR
|
||||
named_pthread_create(&soxr_time_check_thread, NULL, &soxr_time_check, NULL, "soxr_checker");
|
||||
soxr_time_check_thread_started = 1;
|
||||
|
||||
{
|
||||
int oldState;
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
||||
soxr_time_check_thread = malloc(sizeof(pthread_t));
|
||||
if (soxr_time_check_thread != NULL) {
|
||||
named_pthread_create(soxr_time_check_thread, NULL, &soxr_time_check, NULL, "soxr_checker");
|
||||
} else {
|
||||
debug(1,"couldn't get memory to start the soxr_checker");
|
||||
}
|
||||
pthread_setcancelstate(oldState, NULL); // make this un-cancellable
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FFMPEG
|
||||
|
||||
Reference in New Issue
Block a user