Clean up some of the wording and layout of the configuration file

Clean up and improve the way lead-in sillence is used to give a more accurate sync time to the first packet of audio.
Allow very good sync to occur with 0.3 seconds of lead-in silence.
Improve the use of the buffer offset.
Format some other files.
This commit is contained in:
Mike Brady
2020-05-18 16:55:09 +01:00
parent 012dd26c77
commit 54d761ff7d
23 changed files with 907 additions and 903 deletions
-1
View File
@@ -27,7 +27,6 @@
*
*/
#ifndef __ALAC__DECOMP_H
#define __ALAC__DECOMP_H
+6 -4
View File
@@ -194,14 +194,14 @@ void parse_general_audio_options(void) {
config.audio_backend_latency_offset = dvalue;
}
/* Check if the length of the silent lead-in ia set to \"auto\". */
if (config_lookup_string(config.cfg, "general.audio_backend_silent_lead_in_time", &str)) {
if (strcasecmp(str, "auto") == 0) {
config.audio_backend_silent_lead_in_time_auto = 1;
} else {
if (config.audio_backend_silent_lead_in_time_auto == 1)
warn("Invalid audio_backend_silent_lead_in_time \"%s\". It should be \"auto\" or the lead-in time in seconds. "
warn("Invalid audio_backend_silent_lead_in_time \"%s\". It should be \"auto\" or the "
"lead-in time in seconds. "
"It remains set to \"auto\". Note: numbers should not be placed in quotes.",
str);
else
@@ -216,11 +216,13 @@ void parse_general_audio_options(void) {
if ((dvalue < 0.0) || (dvalue > 4)) {
if (config.audio_backend_silent_lead_in_time_auto == 1)
warn("Invalid audio_backend_silent_lead_in_time \"%f\". It "
"must be between 0.0 and 4.0 seconds. Omit the setting to use the automatic value. The setting remains at \"auto\".",
"must be between 0.0 and 4.0 seconds. Omit the setting to use the automatic value. "
"The setting remains at \"auto\".",
dvalue);
else
warn("Invalid audio_backend_silent_lead_in_time \"%f\". It "
"must be between 0.0 and 4.0 seconds. Omit the setting to use the automatic value. It remains set to %f.",
"must be between 0.0 and 4.0 seconds. Omit the setting to use the automatic value. "
"It remains set to %f.",
dvalue, config.audio_backend_silent_lead_in_time);
} else {
config.audio_backend_silent_lead_in_time = dvalue;
+29 -21
View File
@@ -337,7 +337,8 @@ void actual_close_alsa_device() {
if (alsa_handle) {
int derr;
if ((derr = snd_pcm_hw_free(alsa_handle)))
debug(1, "Error %d (\"%s\") freeing the output device hardware while "
debug(1,
"Error %d (\"%s\") freeing the output device hardware while "
"closing it.",
derr, snd_strerror(derr));
@@ -353,7 +354,10 @@ void actual_close_alsa_device() {
// The lowest rate that the DAC is capable of is chosen.
unsigned int auto_speed_output_rates[] = {
44100, 88200, 176400, 352800,
44100,
88200,
176400,
352800,
};
// This array is of all the formats known to Shairport Sync, in order of the SPS_FORMAT definitions,
@@ -702,7 +706,8 @@ int actual_open_alsa_device(int do_auto_setup) {
buffer_size);
}
*/
debug(1, "The alsa buffer is smaller (%lu bytes) than the desired backend "
debug(1,
"The alsa buffer is smaller (%lu bytes) than the desired backend "
"buffer "
"length (%ld) you have chosen.",
actual_buffer_length, config.audio_backend_buffer_desired_length);
@@ -880,8 +885,8 @@ int prepare_mixer() {
// obtained
pthread_cleanup_debug_mutex_lock(&alsa_mixer_mutex, 1000, 1);
if (open_mixer() == 1) {
if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv,
&alsa_mix_maxv) < 0)
if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, &alsa_mix_maxv) <
0)
debug(1, "Can't read mixer's [linear] min and max volumes.");
else {
if (snd_mixer_selem_get_playback_dB_range(alsa_mix_elem, &alsa_mix_mindb,
@@ -926,10 +931,10 @@ int prepare_mixer() {
snd_ctl_elem_id_set_name(elem_id, alsa_mix_ctrl);
if (snd_ctl_get_dB_range(ctl, elem_id, &alsa_mix_mindb, &alsa_mix_maxdb) == 0) {
debug(1, "alsa: hardware mixer \"%s\" selected, with dB volume "
debug(1,
"alsa: hardware mixer \"%s\" selected, with dB volume "
"from %f to %f.",
alsa_mix_ctrl, (1.0 * alsa_mix_mindb) / 100.0,
(1.0 * alsa_mix_maxdb) / 100.0);
alsa_mix_ctrl, (1.0 * alsa_mix_mindb) / 100.0, (1.0 * alsa_mix_maxdb) / 100.0);
has_softvol = 1;
audio_alsa.volume = &volume; // insert the volume function now
// we know it can do dB stuff
@@ -974,10 +979,7 @@ int prepare_mixer() {
return response;
}
int alsa_device_init() {
return prepare_mixer();
}
int alsa_device_init() { return prepare_mixer(); }
static int init(int argc, char **argv) {
// for debugging
@@ -1135,8 +1137,10 @@ static int init(int argc, char **argv) {
"\"S16\", \"S24\", \"S24_LE\", \"S24_BE\", "
"\"S24_3LE\", \"S24_3BE\" or "
"\"S32\", \"S32_LE\", \"S32_BE\". It remains set to \"%s\".",
str, config.output_format_auto_requested == 1 ? "auto" : sps_format_description_string(
config.output_format));
str,
config.output_format_auto_requested == 1
? "auto"
: sps_format_description_string(config.output_format));
}
}
@@ -1286,9 +1290,9 @@ static int init(int argc, char **argv) {
warn("Invalid use_precision_timing option choice \"%s\". It should be "
"\"yes\", \"auto\" or \"no\". "
"It remains set to \"%s\".",
config.use_precision_timing == YNA_NO ? "no" : config.use_precision_timing == YNA_AUTO
? "auto"
: "yes");
config.use_precision_timing == YNA_NO
? "no"
: config.use_precision_timing == YNA_AUTO ? "auto" : "yes");
}
}
@@ -1519,12 +1523,14 @@ int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
if (((update_timestamp_ns - stall_monitor_start_time) > stall_monitor_error_threshold) ||
((time_now_ns - stall_monitor_start_time) > stall_monitor_error_threshold)) {
debug(2, "DAC seems to have stalled with time_now_ns: %" PRIX64
debug(2,
"DAC seems to have stalled with time_now_ns: %" PRIX64
", update_timestamp_ns: %" PRIX64 ", stall_monitor_start_time %" PRIX64
", stall_monitor_error_threshold %" PRIX64 ".",
time_now_ns, update_timestamp_ns, stall_monitor_start_time,
stall_monitor_error_threshold);
debug(2, "DAC seems to have stalled with time_now: %lx,%lx"
debug(2,
"DAC seems to have stalled with time_now: %lx,%lx"
", update_timestamp: %lx,%lx, stall_monitor_start_time %" PRIX64
", stall_monitor_error_threshold %" PRIX64 ".",
tn.tv_sec, tn.tv_nsec, update_timestamp.tv_sec, update_timestamp.tv_nsec,
@@ -1692,7 +1698,8 @@ int do_play(void *buf, int samples) {
}
}
} else {
debug(1, "alsa: device status returns fault status %d and SND_PCM_STATE_* "
debug(1,
"alsa: device status returns fault status %d and SND_PCM_STATE_* "
"%d for play.",
ret, state);
frame_index = 0;
@@ -2007,7 +2014,8 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) {
error_count++;
char errorstring[1024];
strerror_r(-ret, (char *)errorstring, sizeof(errorstring));
debug(2, "alsa: alsa_buffer_monitor_thread_code error %d (\"%s\") writing %d samples "
debug(2,
"alsa: alsa_buffer_monitor_thread_code error %d (\"%s\") writing %d samples "
"to alsa device -- %d errors in %d trials.",
ret, (char *)errorstring, frames_of_silence, error_count, frame_count);
if ((error_count > 40) && (frame_count < 100)) {
+7 -26
View File
@@ -89,14 +89,9 @@ typedef struct soxr_quality {
const char *name;
} soxr_quality_t;
static soxr_quality_t soxr_quality_table[] = {
{ SOXR_VHQ, "very high" },
{ SOXR_HQ, "high" },
{ SOXR_MQ, "medium" },
{ SOXR_LQ, "low" },
{ SOXR_QQ, "quick" },
{ -1, NULL }
};
static soxr_quality_t soxr_quality_table[] = {{SOXR_VHQ, "very high"}, {SOXR_HQ, "high"},
{SOXR_MQ, "medium"}, {SOXR_LQ, "low"},
{SOXR_QQ, "quick"}, {-1, NULL}};
static int parse_soxr_quality_name(const char *name) {
for (soxr_quality_t *s = soxr_quality_table; s->name != NULL; ++s) {
@@ -122,8 +117,7 @@ static inline sample_t sample_conv(short sample) {
return ((sample < 0) ? (-1.0 * sample / SHRT_MIN) : (1.0 * sample / SHRT_MAX));
}
static void deinterleave(const char *interleaved_input_buffer,
sample_t *jack_output_buffer[],
static void deinterleave(const char *interleaved_input_buffer, sample_t *jack_output_buffer[],
jack_nframes_t offset, jack_nframes_t nframes) {
jack_nframes_t f;
// We're dealing with 16bit audio here:
@@ -353,8 +347,7 @@ void jack_deinit() {
#endif
}
void jack_start(int i_sample_rate,
__attribute__((unused)) int i_sample_format) {
void jack_start(int i_sample_rate, __attribute__((unused)) int i_sample_format) {
// Nothing to do, JACK client has already been set up at jack_init().
// Also, we have no say over the sample rate or sample format of JACK,
// We convert the 16bit samples to float, and die if the sample rate is != 44k1 without soxr.
@@ -365,13 +358,7 @@ void jack_start(int i_sample_rate,
soxr_delete(soxr);
}
soxr_error_t e = NULL;
soxr = soxr_create(i_sample_rate,
sample_rate,
NPORTS,
&e,
&io_spec,
&quality_spec,
NULL);
soxr = soxr_create(i_sample_rate, sample_rate, NPORTS, &e, &io_spec, &quality_spec, NULL);
if (!soxr) {
die("Unable to create soxr resampler for JACK: %s", e);
}
@@ -428,13 +415,7 @@ int play(void *buf, int samples) {
size_t i_done, o_done;
soxr_error_t e;
while (samples > 0 && thisbuf > 0) {
e = soxr_process(soxr,
(soxr_in_t)in,
samples,
&i_done,
(soxr_out_t)out,
thisbuf,
&o_done);
e = soxr_process(soxr, (soxr_in_t)in, samples, &i_done, (soxr_out_t)out, thisbuf, &o_done);
if (e)
die("Error during soxr process: %s", e);
+2 -1
View File
@@ -24,7 +24,8 @@ static void write_callback(struct SoundIoOutStream *outstream, int frame_count_m
int fill_bytes = soundio_ring_buffer_fill_count(ring_buffer);
int fill_count = fill_bytes / outstream->bytes_per_frame;
debug(3, "[--->>] frame_count_min: %d , frame_count_max: %d , fill_bytes: %d , fill_count: %d , "
debug(3,
"[--->>] frame_count_min: %d , frame_count_max: %d , fill_bytes: %d , fill_count: %d , "
"outstream->bytes_per_frame: %d",
frame_count_min, frame_count_max, fill_bytes, fill_count, outstream->bytes_per_frame);
+4 -2
View File
@@ -1578,9 +1578,11 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, sps_format_t f
return previous_random_number;
}
// This will check the incoming string "s" of length "len" with the existing NUL-terminated string "str" and update "flag" accordingly.
// This will check the incoming string "s" of length "len" with the existing NUL-terminated string
// "str" and update "flag" accordingly.
// Note: if the incoming string length is zero, then the a NULL is used; i.e. no zero-length strings are stored.
// Note: if the incoming string length is zero, then the a NULL is used; i.e. no zero-length strings
// are stored.
// If the strings are different, the str is free'd and replaced by a pointer
// to a newly strdup'd string and the flag is set
+10 -9
View File
@@ -100,10 +100,13 @@ typedef enum {
const char *sps_format_description_string(sps_format_t format);
typedef struct {
double missing_port_dacp_scan_interval_seconds; // if no DACP port number can be found, check at these intervals
double resend_control_first_check_time; // wait this long before asking for a missing packet to be resent
double missing_port_dacp_scan_interval_seconds; // if no DACP port number can be found, check at
// these intervals
double resend_control_first_check_time; // wait this long before asking for a missing packet to be
// resent
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, give up
double resend_control_last_check_time; // if the packet is missing this close to the time of use,
// give up
pthread_mutex_t lock;
config_t *cfg;
int endianness;
@@ -212,15 +215,13 @@ typedef struct {
// buffer
double disable_standby_mode_silence_scan_interval; // check the threshold this often
double lead_in_silence_initial_period; // the size of the first block of silence to send to the DAC
int lead_in_silence_minimum_adjustments_to_make; // make at least this number of checks and timing adjustments
double lead_in_silence_adjustment_interval; // make checks and adjustments at this interval
double audio_backend_latency_offset; // this will be the offset in seconds to compensate for any
// fixed latency there might be in the audio path
int audio_backend_silent_lead_in_time_auto; // true if the lead-in time should be from as soon as packets are received
int audio_backend_silent_lead_in_time_auto; // true if the lead-in time should be from as soon as
// packets are received
double audio_backend_silent_lead_in_time; // the length of the silence that should precede a play.
uint32_t minimum_free_buffer_headroom; // when effective latency is calculated, ensure this number of buffers are unallocated
uint32_t minimum_free_buffer_headroom; // when effective latency is calculated, ensure this number
// of buffers are unallocated
double active_state_timeout; // the amount of time from when play ends to when the system leaves
// into the "active" mode.
uint32_t volume_range_db; // the range, in dB, from max dB to min dB. Zero means use the mixer's
+14 -13
View File
@@ -154,7 +154,7 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) {
// debug(1,"dacp_send_command: command is: \"%s\".",command);
if (dacp_server.port == 0) {
debug(2, "No DACP port specified yet");
debug(3, "No DACP port specified yet");
result = 490; // no port specified
} else {
@@ -222,7 +222,6 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) {
pthread_cleanup_push(connect_cleanup, (void *)&sockfd);
// debug(2, "dacp_send_command: open socket %d.",sockfd);
// This is for limiting the time to be spent waiting for a response.
struct timeval tv;
@@ -233,7 +232,6 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) {
if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof tv) == -1)
debug(1, "dacp_send_command: error %d setting send timeout.", errno);
// connect!
// debug(1, "DACP socket created.");
if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) {
@@ -418,7 +416,8 @@ void set_dacp_server_information(rtsp_conn_info *conn) {
// which return immediately with a 403 code if there are no changes.
dacp_server.always_use_revision_number_1 = 0;
char *p = strstr(conn->UserAgent, "forked-daapd");
if ((p != 0) && (p == conn->UserAgent)) {// must exist and be at the start of the UserAgent string
if ((p != 0) &&
(p == conn->UserAgent)) { // must exist and be at the start of the UserAgent string
dacp_server.always_use_revision_number_1 = 1;
}
@@ -505,14 +504,15 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
(metadata_store.advanced_dacp_server_active != 0);
metadata_store.dacp_server_active = 0;
metadata_store.advanced_dacp_server_active = 0;
debug(2,
debug(3,
"setting metadata_store.dacp_server_active and "
"metadata_store.advanced_dacp_server_active to 0 with an update "
"flag value of %d",
ch);
metadata_hub_modify_epilog(ch);
uint64_t time_to_wait_for_wakeup_ns = (uint64_t)(1000000000 * config.missing_port_dacp_scan_interval_seconds);
uint64_t time_to_wait_for_wakeup_ns =
(uint64_t)(1000000000 * config.missing_port_dacp_scan_interval_seconds);
#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
uint64_t time_of_wakeup_ns = get_absolute_time_in_ns() + time_to_wait_for_wakeup_ns;
@@ -534,11 +534,13 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
result = pthread_cond_timedwait(&dacp_server_information_cv, &dacp_server_information_lock,
&time_to_wait); // this is a pthread cancellation point
// debug(1, "result is %d, dacp_server.scan_enable is %d, time_to_wait_for_wakeup_ns is %" PRId64 ".", result, dacp_server.scan_enable, time_to_wait_for_wakeup_ns);
// debug(1, "result is %d, dacp_server.scan_enable is %d, time_to_wait_for_wakeup_ns is %"
// PRId64 ".", result, dacp_server.scan_enable, time_to_wait_for_wakeup_ns);
#endif
#ifdef COMPILE_FOR_OSX
result = pthread_cond_timedwait_relative_np(&dacp_server_information_cv, &dacp_server_information_lock, &time_to_wait);
result = pthread_cond_timedwait_relative_np(&dacp_server_information_cv,
&dacp_server_information_lock, &time_to_wait);
#endif
}
if (dacp_server.scan_enable == 1) {
@@ -547,7 +549,8 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
}
}
always_use_revision_number_1 = dacp_server.always_use_revision_number_1; // set this while access is locked
always_use_revision_number_1 =
dacp_server.always_use_revision_number_1; // set this while access is locked
result = dacp_get_volume(&the_volume); // just want the http code
pthread_cleanup_pop(1);
@@ -597,8 +600,8 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
else
idle_scan_count = 0;
debug(3, "Scan Result: %d, Bad Scan Count: %d, Idle Scan Count: %d.", result, bad_result_count,
idle_scan_count);
debug(3, "Scan Result: %d, Bad Scan Count: %d, Idle Scan Count: %d.", result,
bad_result_count, idle_scan_count);
/* not used
// decide if idle for too long
@@ -934,8 +937,6 @@ void dacp_monitor_start() {
rc = pthread_cond_init(&dacp_server_information_cv, NULL);
#endif
pthread_mutexattr_t mta;
rc = pthread_mutexattr_init(&mta);
+27 -24
View File
@@ -24,10 +24,10 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "config.h"
@@ -69,8 +69,8 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused))
shairport_sync_remote_control_set_client(shairportSyncRemoteControlSkeleton, argc->client_ip);
// although it's a DACP server, the server is in fact, part of the the AirPlay "client" (their term).
// although it's a DACP server, the server is in fact, part of the the AirPlay "client" (their
// term).
if (argc->dacp_server_active) {
shairport_sync_remote_control_set_available(shairportSyncRemoteControlSkeleton, TRUE);
} else {
@@ -166,7 +166,6 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused))
shairportSyncAdvancedRemoteControlSkeleton, response);
}
switch (argc->shuffle_status) {
case SS_NOT_AVAILABLE:
new_status = FALSE;
@@ -188,8 +187,8 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused))
// only set this if it's different
if (current_status != new_status) {
debug(3, "Shuffle State should be changed");
shairport_sync_advanced_remote_control_set_shuffle(
shairportSyncAdvancedRemoteControlSkeleton, new_status);
shairport_sync_advanced_remote_control_set_shuffle(shairportSyncAdvancedRemoteControlSkeleton,
new_status);
}
// Build the metadata array
@@ -369,7 +368,8 @@ static gboolean on_handle_volume_down(ShairportSyncRemoteControl *skeleton,
}
static gboolean on_handle_set_airplay_volume(ShairportSyncRemoteControl *skeleton,
GDBusMethodInvocation *invocation, const gdouble volume,
GDBusMethodInvocation *invocation,
const gdouble volume,
__attribute__((unused)) gpointer user_data) {
debug(2, "Set airplay volume to %.6f.", volume);
char command[256] = "";
@@ -379,8 +379,6 @@ static gboolean on_handle_set_airplay_volume(ShairportSyncRemoteControl *skeleto
return TRUE;
}
gboolean notify_elapsed_time_callback(ShairportSyncDiagnostics *skeleton,
__attribute__((unused)) gpointer user_data) {
// debug(1, "\"notify_elapsed_time_callback\" called.");
@@ -468,7 +466,8 @@ gboolean notify_convolution_callback(ShairportSync *skeleton,
if (shairport_sync_get_convolution(skeleton)) {
debug(1, ">> activating convolution");
config.convolution = 1;
config.convolver_valid = convolver_init(config.convolution_ir_file, config.convolution_max_length);
config.convolver_valid =
convolver_init(config.convolution_ir_file, config.convolution_max_length);
} else {
debug(1, ">> deactivating convolution");
config.convolution = 0;
@@ -506,25 +505,29 @@ gboolean notify_convolution_gain_callback(__attribute__((unused)) ShairportSync
#endif
#ifdef CONFIG_CONVOLUTION
gboolean notify_convolution_impulse_response_file_callback(ShairportSync *skeleton,
__attribute__((unused)) gpointer user_data) {
__attribute__((unused))
gpointer user_data) {
char *th = (char *)shairport_sync_get_convolution_impulse_response_file(skeleton);
if (config.convolution_ir_file)
free(config.convolution_ir_file);
config.convolution_ir_file = strdup(th);
debug(1, ">> setting configuration impulse response filter file to \"%s\".", config.convolution_ir_file);
config.convolver_valid = convolver_init(config.convolution_ir_file, config.convolution_max_length);
debug(1, ">> setting configuration impulse response filter file to \"%s\".",
config.convolution_ir_file);
config.convolver_valid =
convolver_init(config.convolution_ir_file, config.convolution_max_length);
return TRUE;
}
#else
gboolean notify_convolution_impulse_response_file_callback(__attribute__((unused)) ShairportSync *skeleton,
__attribute__((unused)) gpointer user_data) {
__attribute__((unused)) char *th = (char *)shairport_sync_get_convolution_impulse_response_file(skeleton);
gboolean notify_convolution_impulse_response_file_callback(__attribute__((unused))
ShairportSync *skeleton,
__attribute__((unused))
gpointer user_data) {
__attribute__((unused)) char *th =
(char *)shairport_sync_get_convolution_impulse_response_file(skeleton);
return TRUE;
}
#endif
gboolean notify_loudness_callback(ShairportSync *skeleton,
__attribute__((unused)) gpointer user_data) {
// debug(1, "\"notify_loudness_callback\" called.");
@@ -781,7 +784,6 @@ static gboolean on_handle_remote_command(ShairportSync *skeleton, GDBusMethodInv
return TRUE;
}
static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name,
__attribute__((unused)) gpointer user_data) {
@@ -823,8 +825,8 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name
G_CALLBACK(notify_convolution_gain_callback), NULL);
g_signal_connect(shairportSyncSkeleton, "notify::convolution-impulse-response-file",
G_CALLBACK(notify_convolution_impulse_response_file_callback), NULL);
g_signal_connect(shairportSyncSkeleton, "notify::loudness",
G_CALLBACK(notify_loudness_callback), NULL);
g_signal_connect(shairportSyncSkeleton, "notify::loudness", G_CALLBACK(notify_loudness_callback),
NULL);
g_signal_connect(shairportSyncSkeleton, "notify::loudness-threshold",
G_CALLBACK(notify_loudness_threshold_callback), NULL);
g_signal_connect(shairportSyncSkeleton, "notify::drift-tolerance",
@@ -879,7 +881,6 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name
g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-set-airplay-volume",
G_CALLBACK(on_handle_set_airplay_volume), NULL);
g_signal_connect(shairportSyncAdvancedRemoteControlSkeleton, "handle-set-volume",
G_CALLBACK(on_handle_set_volume), NULL);
@@ -975,9 +976,11 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name
shairport_sync_set_convolution(SHAIRPORT_SYNC(shairportSyncSkeleton), TRUE);
}
if (config.convolution_ir_file)
shairport_sync_set_convolution_impulse_response_file(SHAIRPORT_SYNC(shairportSyncSkeleton), config.convolution_ir_file);
shairport_sync_set_convolution_impulse_response_file(SHAIRPORT_SYNC(shairportSyncSkeleton),
config.convolution_ir_file);
// else
// shairport_sync_set_convolution_impulse_response_file(SHAIRPORT_SYNC(shairportSyncSkeleton), NULL);
// shairport_sync_set_convolution_impulse_response_file(SHAIRPORT_SYNC(shairportSyncSkeleton),
// NULL);
#endif
shairport_sync_set_version(SHAIRPORT_SYNC(shairportSyncSkeleton), PACKAGE_VERSION);
+1 -1
View File
@@ -24,8 +24,8 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "mdns.h"
#include "common.h"
#include "mdns.h"
#include <arpa/inet.h>
#include <dns_sd.h>
#include <stdlib.h>
+1 -1
View File
@@ -25,8 +25,8 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "mdns.h"
#include "common.h"
#include "mdns.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
+11 -10
View File
@@ -35,10 +35,10 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <inttypes.h>
#include "config.h"
@@ -232,8 +232,8 @@ char *metadata_write_image_file(const char *buf, int len) {
// if it exists, we're done
char *prefix = "cover-";
size_t pl = strlen(config.cover_art_cache_dir) + 1 + strlen(prefix) + strlen(img_md5_str) + 1 +
strlen(ext);
size_t pl = strlen(config.cover_art_cache_dir) + 1 + strlen(prefix) + strlen(img_md5_str) +
1 + strlen(ext);
path = malloc(pl + 1);
snprintf(path, pl + 1, "%s/%s%s.%s", config.cover_art_cache_dir, prefix, img_md5_str, ext);
@@ -321,8 +321,7 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
metadata_store.item_id_received = 1;
debug(2, "MH Item ID set to: \"%" PRIx64 "\"", metadata_store.item_id);
}
}
break;
} break;
case 'astm': {
uint32_t ui = ntohl(*(uint32_t *)data);
debug(2, "MH Song Time seen: \"%u\" of length %u.", ui, length);
@@ -331,8 +330,7 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
metadata_store.songtime_in_milliseconds_changed = 1;
debug(2, "MH Song Time set to: \"%u\"", metadata_store.songtime_in_milliseconds);
}
}
break;
} break;
case 'asal':
cs = strndup(data, length);
if (string_update(&metadata_store.album_name, &metadata_store.album_name_changed, cs)) {
@@ -467,7 +465,8 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
metadata_hub_modify_prolog();
debug(2, "MH Picture received, length %u bytes.", length);
char uri[2048];
if ((length > 16) && (strcmp(config.cover_art_cache_dir,"")!=0)) { // if it's okay to write the file
if ((length > 16) &&
(strcmp(config.cover_art_cache_dir, "") != 0)) { // if it's okay to write the file
char *pathname = metadata_write_image_file(data, length);
snprintf(uri, sizeof(uri), "file://%s", pathname);
free(pathname);
@@ -526,14 +525,16 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
break;
case 'pbeg':
metadata_hub_modify_prolog();
changed = ((metadata_store.player_state != PS_PLAYING) || (metadata_store.player_thread_active == 0));
changed = ((metadata_store.player_state != PS_PLAYING) ||
(metadata_store.player_thread_active == 0));
metadata_store.player_state = PS_PLAYING;
metadata_store.player_thread_active = 1;
metadata_hub_modify_epilog(changed);
break;
case 'pend':
metadata_hub_modify_prolog();
changed = ((metadata_store.player_state != PS_STOPPED) || (metadata_store.player_thread_active == 1));
changed = ((metadata_store.player_state != PS_STOPPED) ||
(metadata_store.player_thread_active == 1));
metadata_store.player_state = PS_STOPPED;
metadata_store.player_thread_active = 0;
metadata_hub_modify_epilog(changed);
+5 -4
View File
@@ -23,9 +23,9 @@
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "config.h"
@@ -64,7 +64,8 @@ double mpris_volume_to_airplay_volume(double sp) {
void mpris_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused)) void *userdata) {
// debug(1, "MPRIS metadata watcher called");
char response[100];
media_player2_player_set_volume(mprisPlayerPlayerSkeleton, airplay_volume_to_mpris_volume(argc->airplay_volume));
media_player2_player_set_volume(mprisPlayerPlayerSkeleton,
airplay_volume_to_mpris_volume(argc->airplay_volume));
switch (argc->repeat_status) {
case RS_NOT_AVAILABLE:
strcpy(response, "Not Available");
@@ -175,7 +176,8 @@ void mpris_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused)
// Add in the Track ID based on the 'mper' metadata if it is non-zero
if (argc->item_id != 0) {
char trackidstring[128];
snprintf(trackidstring, sizeof(trackidstring), "/org/gnome/ShairportSync/%" PRIX64 "", argc->item_id);
snprintf(trackidstring, sizeof(trackidstring), "/org/gnome/ShairportSync/%" PRIX64 "",
argc->item_id);
GVariant *trackid = g_variant_new("o", trackidstring);
g_variant_builder_add(dict_builder, "{sv}", "mpris:trackid", trackid);
}
@@ -334,7 +336,6 @@ static void on_mpris_name_acquired(GDBusConnection *connection, const gchar *nam
g_signal_connect(mprisPlayerPlayerSkeleton, "handle-set-volume", G_CALLBACK(on_handle_set_volume),
NULL);
add_metadata_watcher(mpris_metadata_watcher, NULL);
debug(1, "MPRIS service started at \"%s\" on the %s bus.", name,
+247 -237
View File
@@ -91,6 +91,14 @@
#include "activity_monitor.h"
// make the first audio packet deliberately early to bias the sync error of
// the very first packet, making the error more likely to be too early
// rather than too late. It it's too early,
// a delay exactly compensating for it can be sent just before the
// first packet. This should exactly compensate for the error.
int64_t first_frame_early_bias = 8;
// default buffer size
// needs to be a power of 2 because of the way BUFIDX(seqno) works
//#define BUFFER_FRAMES 512
@@ -123,8 +131,11 @@ static void ab_resync(rtsp_conn_info *conn) {
for (i = 0; i < BUFFER_FRAMES; i++) {
conn->audio_buffer[i].ready = 0;
conn->audio_buffer[i].resend_request_number = 0;
conn->audio_buffer[i].resend_time = 0; // this is either zero or the time the last resend was requested.
conn->audio_buffer[i].initialisation_time = 0; // this is either the time the packet was received or the time it was noticed the packet was missing.
conn->audio_buffer[i].resend_time =
0; // this is either zero or the time the last resend was requested.
conn->audio_buffer[i].initialisation_time =
0; // this is either the time the packet was received or the time it was noticed the packet
// was missing.
conn->audio_buffer[i].sequence_number = 0;
}
conn->ab_synced = 0;
@@ -323,7 +334,8 @@ int audio_packet_decode(short *dest, int *destlen, uint8_t *buf, int len, rtsp_c
}
if (outsize > maximum_possible_outsize) {
debug(2, "Output from alac_decode larger (%d bytes, not frames) than expected (%d bytes) -- "
debug(2,
"Output from alac_decode larger (%d bytes, not frames) than expected (%d bytes) -- "
"truncated, but buffer overflow possible! Encrypted = %d.",
outsize, maximum_possible_outsize, conn->stream.encrypted);
reply = -1; // output packet is the wrong size
@@ -331,7 +343,8 @@ int audio_packet_decode(short *dest, int *destlen, uint8_t *buf, int len, rtsp_c
*destlen = outsize / conn->input_bytes_per_frame;
if ((outsize % conn->input_bytes_per_frame) != 0)
debug(1, "Number of audio frames (%d) does not correspond exactly to the number of bytes (%d) "
debug(1,
"Number of audio frames (%d) does not correspond exactly to the number of bytes (%d) "
"and the audio frame size (%d).",
*destlen, outsize, conn->input_bytes_per_frame);
return reply;
@@ -486,7 +499,8 @@ void player_put_packet(seq_t seqno, uint32_t actual_timestamp, uint8_t *data, in
if ((conn->flush_rtp_timestamp != 0) && (actual_timestamp != conn->flush_rtp_timestamp) &&
(modulo_32_offset(actual_timestamp, conn->flush_rtp_timestamp) <
conn->input_rate * 10)) { // if it's less than 10 seconds
debug(3, "Dropping flushed packet in player_put_packet, seqno %u, timestamp %" PRIu32
debug(3,
"Dropping flushed packet in player_put_packet, seqno %u, timestamp %" PRIu32
", flushing to "
"timestamp: %" PRIu32 ".",
seqno, actual_timestamp, conn->flush_rtp_timestamp);
@@ -523,7 +537,8 @@ void player_put_packet(seq_t seqno, uint32_t actual_timestamp, uint8_t *data, in
abuf = conn->audio_buffer + BUFIDX(seq_sum(conn->ab_write, i));
abuf->ready = 0; // to be sure, to be sure
abuf->resend_request_number = 0;
abuf->initialisation_time = time_now; // this represents when the packet was noticed to be missing
abuf->initialisation_time =
time_now; // this represents when the packet was noticed to be missing
abuf->status = 1 << 0; // signifying missing
abuf->resend_time = 0;
abuf->given_timestamp = 0;
@@ -534,7 +549,8 @@ void player_put_packet(seq_t seqno, uint32_t actual_timestamp, uint8_t *data, in
// rtp_request_resend(ab_write, gap);
// resend_requests++;
conn->ab_write = SUCCESSOR(seqno);
} else if (seq_order(conn->ab_read, seqno, conn->ab_read)) { // older than expected but not too late
} else if (seq_order(conn->ab_read, seqno,
conn->ab_read)) { // older than expected but not too late
conn->late_packets++;
abuf = conn->audio_buffer + BUFIDX(seqno);
} else { // too late.
@@ -567,15 +583,21 @@ void player_put_packet(seq_t seqno, uint32_t actual_timestamp, uint8_t *data, in
// resend checks
{
uint64_t minimum_wait_time = (uint64_t)(config.resend_control_first_check_time * (uint64_t)1000000000);
uint64_t resend_repeat_interval = (uint64_t)(config.resend_control_check_interval_time * (uint64_t)1000000000);
uint64_t minimum_remaining_time = (uint64_t)((config.resend_control_last_check_time + config.audio_backend_buffer_desired_length)* (uint64_t)1000000000);
uint64_t minimum_wait_time =
(uint64_t)(config.resend_control_first_check_time * (uint64_t)1000000000);
uint64_t resend_repeat_interval =
(uint64_t)(config.resend_control_check_interval_time * (uint64_t)1000000000);
uint64_t minimum_remaining_time = (uint64_t)(
(config.resend_control_last_check_time + config.audio_backend_buffer_desired_length) *
(uint64_t)1000000000);
uint64_t latency_time = (uint64_t)(conn->latency * (uint64_t)1000000000);
latency_time = latency_time / (uint64_t)conn->input_rate;
int x; // this is the first frame to be checked
// if we detected a first empty frame before and if it's still in the buffer!
if ((first_possibly_missing_frame >= 0) && (position_in_modulo_uint16_t_buffer(first_possibly_missing_frame, conn->ab_read, conn->ab_write, NULL))) {
if ((first_possibly_missing_frame >= 0) &&
(position_in_modulo_uint16_t_buffer(first_possibly_missing_frame, conn->ab_read,
conn->ab_write, NULL))) {
x = first_possibly_missing_frame;
} else {
x = conn->ab_read;
@@ -592,10 +614,17 @@ void player_put_packet(seq_t seqno, uint32_t actual_timestamp, uint8_t *data, in
if (first_possibly_missing_frame < 0)
first_possibly_missing_frame = x;
number_of_missing_frames++;
// debug(1, "frame %u's initialisation_time is 0x%" PRIx64 ", latency_time is 0x%" PRIx64 ", time_now is 0x%" PRIx64 ", minimum_remaining_time is 0x%" PRIx64 ".", x, check_buf->initialisation_time, latency_time, time_now, minimum_remaining_time);
int too_late = ((check_buf->initialisation_time < (time_now - latency_time)) || ((check_buf->initialisation_time - (time_now - latency_time)) < minimum_remaining_time));
// debug(1, "frame %u's initialisation_time is 0x%" PRIx64 ", latency_time is 0x%"
// PRIx64 ", time_now is 0x%" PRIx64 ", minimum_remaining_time is 0x%" PRIx64 ".", x,
// check_buf->initialisation_time, latency_time, time_now, minimum_remaining_time);
int too_late = ((check_buf->initialisation_time < (time_now - latency_time)) ||
((check_buf->initialisation_time - (time_now - latency_time)) <
minimum_remaining_time));
int too_early = ((time_now - check_buf->initialisation_time) < minimum_wait_time);
int too_soon_after_last_request = ((check_buf->resend_time != 0) && ((time_now - check_buf->resend_time) < resend_repeat_interval)); // time_now can never be less than the time_tag
int too_soon_after_last_request =
((check_buf->resend_time != 0) &&
((time_now - check_buf->resend_time) <
resend_repeat_interval)); // time_now can never be less than the time_tag
if (too_late)
check_buf->status |= 1 << 2; // too late
@@ -617,21 +646,25 @@ void player_put_packet(seq_t seqno, uint32_t actual_timestamp, uint8_t *data, in
} else {
missing_frame_run_count++;
}
check_buf->resend_time = time_now; // setting the time to now because we are definitely going to take action
check_buf->resend_time = time_now; // setting the time to now because we are
// definitely going to take action
check_buf->resend_request_number++;
debug(3,"Frame %d is missing with ab_read of %u and ab_write of %u.", x, conn->ab_read, conn->ab_write);
debug(3, "Frame %d is missing with ab_read of %u and ab_write of %u.", x,
conn->ab_read, conn->ab_write);
}
// if (too_late) {
// debug(1,"too late to get missing frame %u.", x);
// }
}
// if (number_of_missing_frames != 0)
// debug(1,"check with x = %u, ab_read = %u, ab_write = %u, first_possibly_missing_frame = %d.", x, conn->ab_read, conn->ab_write, first_possibly_missing_frame);
// debug(1,"check with x = %u, ab_read = %u, ab_write = %u, first_possibly_missing_frame
// = %d.", x, conn->ab_read, conn->ab_write, first_possibly_missing_frame);
x = (x + 1) & 0xffff;
if (((check_buf->ready) || (x == conn->ab_write)) && (missing_frame_run_count > 0)) {
// send a resend request
if (missing_frame_run_count > 1)
debug(2,"request resend of %d packets starting at seqno %u.", missing_frame_run_count, start_of_missing_frame_run);
debug(2, "request resend of %d packets starting at seqno %u.",
missing_frame_run_count, start_of_missing_frame_run);
if (config.disable_resend_requests == 0) {
debug_mutex_unlock(&conn->ab_mutex, 3);
rtp_request_resend(start_of_missing_frame_run, missing_frame_run_count, conn);
@@ -660,15 +693,18 @@ int32_t rand_in_range(int32_t exclusive_range_limit) {
return sp >> 32;
}
int get_and_check_effective_latency(rtsp_conn_info *conn, uint32_t *effective_latency, double offset_time) {
// check that the overall effective latency remains positive and is not greater than the capacity of the buffers
// return 0 if okay, -1 if the latency would be negative, +1 if it would be too large
int get_and_check_effective_latency(rtsp_conn_info *conn, uint32_t *effective_latency,
double offset_time) {
// check that the overall effective latency remains positive and is not greater than the capacity
// of the buffers return 0 if okay, -1 if the latency would be negative, +1 if it would be too
// large
int result = 0;
if (offset_time >= 0.0) {
uint32_t latency_addition = (uint32_t)(offset_time * conn->input_rate);
// keep about one second of buffers back
if ((*effective_latency + latency_addition) <= (conn->max_frames_per_packet * (BUFFER_FRAMES - config.minimum_free_buffer_headroom)))
if ((*effective_latency + latency_addition) <=
(conn->max_frames_per_packet * (BUFFER_FRAMES - config.minimum_free_buffer_headroom)))
*effective_latency += latency_addition;
else
result = 1;
@@ -920,7 +956,8 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
int wait;
long dac_delay = 0; // long because alsa returns a long
int have_sent_prefiller_silence = 0; // set to true when we have sent at least one silent frame to the DAC
int have_sent_prefiller_silence =
0; // set to true when we have sent at least one silent frame to the DAC
pthread_cleanup_push(buffer_get_frame_cleanup_handler,
(void *)conn); // undo what's been done so far
@@ -958,6 +995,8 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
conn->first_packet_time_to_play = 0;
conn->time_since_play_started = 0;
conn->flush_requested = 0;
have_sent_prefiller_silence = 0;
dac_delay = 0;
}
debug_mutex_unlock(&conn->flush_mutex, 0);
@@ -990,7 +1029,8 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
(curframe->given_timestamp != conn->flush_rtp_timestamp) &&
(modulo_32_offset(curframe->given_timestamp, conn->flush_rtp_timestamp) <
conn->input_rate * 10)) { // if it's less than ten seconds
debug(3, "Dropping flushed packet in buffer_get_frame, seqno %u, timestamp %" PRIu32
debug(3,
"Dropping flushed packet in buffer_get_frame, seqno %u, timestamp %" PRIu32
", flushing to "
"timestamp: %" PRIu32 ".",
curframe->sequence_number, curframe->given_timestamp, conn->flush_rtp_timestamp);
@@ -1013,29 +1053,14 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
notified_buffer_empty = 0; // at least one buffer now -- diagnostic only.
if (conn->ab_buffering) { // if we are getting packets but not yet forwarding them to the
// player
// int have_sent_prefiller_silence = 1; // set to true when we have sent at least one silent frame to the DAC
/*
int64_t reference_timestamp;
uint64_t reference_timestamp_time, remote_reference_timestamp_time;
get_reference_timestamp_stuff(&reference_timestamp, &reference_timestamp_time,
&remote_reference_timestamp_time, conn);
reference_timestamp *= conn->output_sample_ratio;
*/
if (conn->first_packet_timestamp == 0) { // if this is the very first packet
// debug(1,"First frame seen, time %u, with %d
// frames...",curframe->timestamp,seq_diff(ab_read, ab_write));
if (have_timestamp_timing_information(conn)) { // if we have a reference time
// debug(1,"First frame seen with timestamp...");
conn->first_packet_timestamp =
curframe->given_timestamp; // we will keep buffering until we are
// supposed to start playing this
// have_sent_prefiller_silence = 0;
// debug(1, "First packet timestamp is %" PRId64 ".", conn->first_packet_timestamp);
// say we have started playing here
#ifdef CONFIG_METADATA
// say we have started receiving frames here
debug(2, "pffr");
send_ssnc_metadata(
'pffr', NULL, 0,
@@ -1046,9 +1071,8 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
// We will send packets of silence from now until that time and then we will send the
// first packet, which will be followed by the subsequent packets.
// we will get a fix every second or so, which will be stored as a pair consisting of
// the time when the packet with a particular timestamp should be played, neglecting
// latencies, etc.
// every second or so, we get a reference on when a particular packet should be
// played.
// It probably won't be the timestamp of our first packet, however, so we might have
// to do some calculations.
@@ -1061,29 +1085,30 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
// that the audio back end has a latency of 100 ms, we would
// ask for the first packet to be emitted 100 ms earlier than it should, i.e. -4410
// frames, so that when it got through the audio back end,
// if would be in sync. To do this, we would give it a latency offset of -100 ms, i.e.
// -4410 frames.
// debug(1, "Output sample ratio is %d", conn->output_sample_ratio);
// what we are asking for here is "what is the local time at which time the calculated
// frame should be played"
// if would be in sync. To do this, we would give it a latency offset of -0.1 sec,
// i.e. -4410 frames.
uint64_t should_be_time;
uint32_t effective_latency = conn->latency;
get_and_check_effective_latency(conn, &effective_latency,config.audio_backend_latency_offset);
// we are ignoring the returned status because it will be captured on subsequent frames, below.
get_and_check_effective_latency(conn, &effective_latency,
config.audio_backend_latency_offset);
// we are ignoring the returned status because it will be captured on subsequent
// frames, below.
frame_to_local_time(conn->first_packet_timestamp + effective_latency, // this will go modulo 2^32
&should_be_time,
conn);
// what we are asking for here is "what is the local time at which time the calculated
// frame should be played"
frame_to_local_time(conn->first_packet_timestamp +
effective_latency, // this will go modulo 2^32
&should_be_time, conn);
conn->first_packet_time_to_play = should_be_time;
if (local_time_now > conn->first_packet_time_to_play) {
uint64_t lateness = local_time_now - conn->first_packet_time_to_play;
debug(3, "First packet is %" PRIu64 " nanoseconds late! Flushing 0.5 seconds",
debug(2, "First packet is %" PRIu64 " nanoseconds late! Flushing 0.5 seconds",
lateness);
do_flush(conn->first_packet_timestamp + 5 * 4410, conn);
}
@@ -1096,18 +1121,24 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
uint64_t should_be_time;
uint32_t effective_latency = conn->latency;
switch (get_and_check_effective_latency(conn, &effective_latency,config.audio_backend_latency_offset)) {
switch (get_and_check_effective_latency(conn, &effective_latency,
config.audio_backend_latency_offset)) {
case -1:
if (conn->unachievable_audio_backend_latency_offset_notified == 0) {
warn("Negative latency! A latency of %d frames requested by the player, when combined with an audio_backend_latency_offset of %f seconds, would make the overall latency negative. The audio_backend_latency_offset setting is ignored.", conn->latency, config.audio_backend_latency_offset);
warn("Negative latency! A latency of %d frames requested by the player, when "
"combined with an audio_backend_latency_offset of %f seconds, would make the "
"overall latency negative. The audio_backend_latency_offset setting is "
"ignored.",
conn->latency, config.audio_backend_latency_offset);
config.audio_backend_latency_offset = 0; // set it to zero
conn->unachievable_audio_backend_latency_offset_notified = 1;
};
// effective_latency = 0 ;
break;
case 1:
if (conn->unachievable_audio_backend_latency_offset_notified == 0) {
warn("An audio_backend_latency_offset of %f seconds may exceed the frame buffering capacity -- the setting is ignored.", config.audio_backend_latency_offset);
warn("An audio_backend_latency_offset of %f seconds may exceed the frame buffering "
"capacity -- the setting is ignored.",
config.audio_backend_latency_offset);
config.audio_backend_latency_offset = 0; // set it to zero;
conn->unachievable_audio_backend_latency_offset_notified = 1;
};
@@ -1116,169 +1147,91 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
break;
}
frame_to_local_time(conn->first_packet_timestamp + effective_latency, // this will go modulo 2^32
&should_be_time,
conn);
frame_to_local_time(conn->first_packet_timestamp +
effective_latency, // this will go modulo 2^32
&should_be_time, conn);
conn->first_packet_time_to_play = should_be_time;
// we want the frames of silence sent at the start to be fairly large in case the output
// device's minimum buffer size is large. But they can't be greater than the silent
// lead_in time
// which is either the agreed latency or the silent lead-in time specified by the
// setting
// In fact, if should be some fraction of them, to allow for adjustment.
int64_t adjustment_interval_frames = (int64_t)(config.lead_in_silence_adjustment_interval * conn->input_rate);
int64_t initial_silence_frames = (int64_t)(config.lead_in_silence_initial_period * conn->input_rate);
int64_t max_dac_delay = effective_latency;
if (config.audio_backend_silent_lead_in_time_auto == 0)
max_dac_delay =
(int64_t)(config.audio_backend_silent_lead_in_time * conn->input_rate);
// should check that the audio_backend_silent_lead_in_time is greater than the desired_buffer_size
int64_t filler_size = adjustment_interval_frames;
if (have_sent_prefiller_silence == 0) {
int64_t delay_before_adjustments = max_dac_delay - config.lead_in_silence_minimum_adjustments_to_make * adjustment_interval_frames;
if (delay_before_adjustments > initial_silence_frames)
filler_size = initial_silence_frames;
else
filler_size = max_dac_delay / (config.lead_in_silence_minimum_adjustments_to_make + 1);
// debug(1,"Initial filler size: %" PRId64 " frames.", filler_size);
}
if (local_time_now > conn->first_packet_time_to_play) {
uint64_t lateness = local_time_now - conn->first_packet_time_to_play;
debug(3, "Gone past starting time by %" PRIu64 " nanoseconds.", lateness);
debug(2, "Gone past starting time by %" PRIu64 " nanoseconds.", lateness);
// have_sent_prefiller_silence = 1;
conn->ab_buffering = 0;
// we've gone past the time...
// debug(1,"Run past the exact start time by %llu frames, with time now of %llx, fpttp
// of %llx and dac_delay of %d and %d packets;
// flush.",(((tn-conn->first_packet_time_to_play)*config.output_rate)>>32)+dac_delay,tn,conn->first_packet_time_to_play,dac_delay,seq_diff(ab_read,
// ab_write));
/*
if (config.output->flush)
config.output->flush();
ab_resync(conn);
conn->first_packet_timestamp = 0;
conn->first_packet_time_to_play = 0;
conn->time_since_play_started = 0;
*/
} else {
// do some calculations
int64_t lead_time = conn->first_packet_time_to_play - local_time_now;
if ((config.audio_backend_silent_lead_in_time_auto == 1) || (lead_time <= (int64_t)(config.audio_backend_silent_lead_in_time * (int64_t)1000000000))) {
// debug(1,"Lead time: %" PRId64 ".", lead_time);
// debug(1,"Checking");
if (config.output->delay) {
// conn->first_packet_time_to_play is definitely later than local_time_now
if ((config.audio_backend_silent_lead_in_time_auto == 1) ||
(lead_time <=
(int64_t)(config.audio_backend_silent_lead_in_time * (int64_t)1000000000))) {
debug(3, "Lead time: %" PRId64 ".", lead_time);
if (config.output->delay) { // if the output device has a delay function
debug(3, "Checking");
int resp = 0;
dac_delay = 0;
if (have_sent_prefiller_silence != 0)
resp = config.output->delay(&dac_delay);
if (resp == 0) {
int64_t gross_frame_gap =
((conn->first_packet_time_to_play - local_time_now) * config.output_rate) / 1000000000;
((conn->first_packet_time_to_play - local_time_now) * config.output_rate) /
1000000000;
int64_t exact_frame_gap = gross_frame_gap - dac_delay;
// debug(1,"Exact and gross frame gaps are %" PRId64 " and %" PRId64 " frames,
// and the dac delay is %ld.", exact_frame_gap, gross_frame_gap, dac_delay);
if (exact_frame_gap < 0) {
// we've gone past the time...
// debug(1,"Run past time.");
int64_t frames_needed_to_maintain_desired_buffer =
(int64_t)(config.audio_backend_buffer_desired_length * config.output_rate) -
dac_delay;
// below, remember that exact_frame_gap and
// frames_needed_to_maintain_desired_buffer could both be negative
int64_t fs = frames_needed_to_maintain_desired_buffer;
// this might happen if a big clock adjustment was made at just the wrong
// time.
debug(1, "Run a bit past the exact start time by %" PRId64
" frames with a DAC delay of %ld frames.",
-exact_frame_gap, dac_delay);
if (config.output->flush)
config.output->flush();
ab_resync(conn);
conn->first_packet_timestamp = 0;
conn->first_packet_time_to_play = 0;
} else {
int64_t fs = filler_size;
if (fs > (max_dac_delay - dac_delay))
fs = max_dac_delay - dac_delay;
if (fs < 0) {
// this could happen if the dac delay mysteriously grows between samples,
// which could happen in a transition between having no interpolation and
// having interpolated buffer numbers.
// this will happen benignly if standby is being prevented, because a
// thread in the alsa back end will be stuffing frames of silence in there
// to keep it busy
debug(3,
"frame size (fs) < 0 with max_dac_delay of %lld and dac_delay of %ld",
max_dac_delay, dac_delay);
fs = 0;
// if there isn't enough time to have the desired buffer size
if (exact_frame_gap <= frames_needed_to_maintain_desired_buffer) {
fs = conn->max_frames_per_packet * 2;
}
if ((exact_frame_gap <= fs) ||
(exact_frame_gap <= conn->max_frames_per_packet * 2)) {
// if we are very close to the end of buffering, i.e. within to frame-lengths,
// add the remaining silence needed and end buffering
if (exact_frame_gap <= conn->max_frames_per_packet * 2) {
fs = exact_frame_gap;
// debug(1,"Exact frame gap is %llu; play %d frames of silence. Dac_delay is
// %d,
// with %d packets, ab_read is %04x, ab_write is
// %04x.",exact_frame_gap,fs,dac_delay,seq_diff(ab_read,
// ab_write),ab_read,ab_write);
if (fs > first_frame_early_bias)
fs = fs - first_frame_early_bias; // deliberately make the first packet a tiny bit early so that the player may compensate for it at the last minute
conn->ab_buffering = 0;
}
void *silence;
// if (fs==0)
// debug(2,"Zero length silence buffer needed with gross_frame_gap of %lld
// and
// dac_delay of %lld.",gross_frame_gap,dac_delay);
// the fs (number of frames of silence to play) can be zero in the DAC doesn't
// start
// outputting frames for a while -- it could get loaded up but not start
// responding
// for many milliseconds.
if (fs > 0) {
silence = malloc(conn->output_bytes_per_frame * fs);
if (silence == NULL)
debug(1, "Failed to allocate %d byte silence buffer.", fs);
else {
conn->previous_random_number = generate_zero_frames(
silence, fs, config.output_format, conn->enable_dither,
conn->previous_random_number);
// debug(1,"Frames to start: %llu, DAC delay %d, buffer: %d
// packets.",exact_frame_gap,dac_delay,seq_diff(conn->ab_read,
// conn->ab_write, conn->ab_read));
// generate frames of silence with dither if necessary
conn->previous_random_number =
generate_zero_frames(silence, fs, config.output_format,
conn->enable_dither, conn->previous_random_number);
config.output->play(silence, fs);
// debug(1,"Sent %" PRId64 " frames of silence",fs);
debug(3, "Sent %" PRId64 " frames of silence", fs);
free(silence);
have_sent_prefiller_silence = 1;
}
}
have_sent_prefiller_silence =
1; // even if we haven't sent silence because it's zero frames long...
}
} else {
if ((resp == sps_extra_code_output_stalled) &&
(conn->unfixable_error_reported == 0)) {
if (resp == sps_extra_code_output_stalled) {
if (conn->unfixable_error_reported == 0) {
conn->unfixable_error_reported = 1;
if (config.cmd_unfixable) {
command_execute(config.cmd_unfixable, "output_device_stalled", 1);
} else {
warn(
"an unrecoverable error, \"output_device_stalled\", has been detected.",
warn("an unrecoverable error, \"output_device_stalled\", has been "
"detected.",
conn->connection_number);
}
}
} else {
debug(2, "Unexpected response to getting dac delay: %d.", resp);
}
}
} else {
// no delay function on back end -- just send the prefiller silence
// debug(1,"Back end has no delay function.");
// debug(2,"Back end has no delay function.");
// send the appropriate prefiller here...
void *silence;
@@ -1353,39 +1306,49 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
uint64_t time_to_play;
uint32_t effective_latency = conn->latency;
switch (get_and_check_effective_latency(conn, &effective_latency,config.audio_backend_latency_offset - config.audio_backend_buffer_desired_length)) {
switch (get_and_check_effective_latency(conn, &effective_latency,
config.audio_backend_latency_offset -
config.audio_backend_buffer_desired_length)) {
case -1:
// this means that the latency is negative, i.e. the packet must be played before its time. This is
// clearly a mistake, and can arise if the combination of a large negative latency offset and the desired buffer length are greater than the
// latency chosen by the player. For example if the latency is 88200 frames (2 seconds), the audio_backend_latency_offset is -1.9 and the audio_backend_buffer_desired_length is 0.2
// the effective latency is -0.1, so the frame must be played 0.1 seconds before it is time-tagged for, and in reality before it arrives at the player.
// this means that the latency is negative, i.e. the packet must be played before its
// time. This is clearly a mistake, and can arise if the combination of a large negative
// latency offset and the desired buffer length are greater than the latency chosen by the
// player. For example if the latency is 88200 frames (2 seconds), the
// audio_backend_latency_offset is -1.9 and the audio_backend_buffer_desired_length is 0.2
// the effective latency is -0.1, so the frame must be played 0.1 seconds before it is
// time-tagged for, and in reality before it arrives at the player.
// to deal with the problem, rather than block the packets, we'll just let 'em go...
//
if (conn->unachievable_audio_backend_latency_offset_notified == 0) {
warn("Negative latency! A latency of %d frames requested by the player, when combined with an audio_backend_latency_offset of %f seconds an audio_backend_buffer_desired_length of %f seconds, would make the overall latency negative. No latency is used. Synchronisation may fail.", conn->latency, config.audio_backend_latency_offset, config.audio_backend_buffer_desired_length);
warn("Negative latency! A latency of %d frames requested by the player, when combined "
"with an audio_backend_latency_offset of %f seconds an "
"audio_backend_buffer_desired_length of %f seconds, would make the overall "
"latency negative. No latency is used. Synchronisation may fail.",
conn->latency, config.audio_backend_latency_offset,
config.audio_backend_buffer_desired_length);
conn->unachievable_audio_backend_latency_offset_notified = 1;
};
time_to_play = local_time_now; // pretend the frame should be played now...
break;
case 1:
if (conn->unachievable_audio_backend_latency_offset_notified == 0) {
warn("Latency too long! An audio_backend_latency_offset of %f seconds combined with an audio_backend_buffer_desired_length of %f seconds may exceed the frame buffering capacity.", config.audio_backend_latency_offset, config.audio_backend_buffer_desired_length);
warn("Latency too long! An audio_backend_latency_offset of %f seconds combined with an "
"audio_backend_buffer_desired_length of %f seconds may exceed the frame "
"buffering capacity.",
config.audio_backend_latency_offset, config.audio_backend_buffer_desired_length);
conn->unachievable_audio_backend_latency_offset_notified = 1;
};
time_to_play = local_time_now; // pretend the frame should be played now...
break;
default:
frame_to_local_time(curframe->given_timestamp + effective_latency, // this will go modulo 2^32
&time_to_play,
conn);
frame_to_local_time(curframe->given_timestamp +
effective_latency, // this will go modulo 2^32
&time_to_play, conn);
break;
}
if (local_time_now >= time_to_play) {
do_wait = 0;
}
@@ -1574,8 +1537,7 @@ int stuff_buffer_soxr_32(int32_t *inptr, int32_t *scratchBuffer, int length,
// mean and variance calculations from "online_variance" algorithm at
// https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Online_algorithm
double soxr_execution_time =
(get_absolute_time_in_ns() - soxr_start_time) * 0.000000001;
double soxr_execution_time = (get_absolute_time_in_ns() - soxr_start_time) * 0.000000001;
// debug(1,"soxr_execution_time_us: %10.1f",soxr_execution_time_us);
if (soxr_execution_time > longest_soxr_execution_time)
longest_soxr_execution_time = soxr_execution_time;
@@ -1628,7 +1590,8 @@ int stuff_buffer_soxr_32(int32_t *inptr, int32_t *scratchBuffer, int length,
}
if (packets_processed % 1250 == 0) {
debug(3, "soxr_oneshot execution time in nanoseconds: mean, standard deviation and max "
debug(3,
"soxr_oneshot execution time in nanoseconds: mean, standard deviation and max "
"for %" PRId32 " interpolations in the last "
"1250 packets. %10.6f, %10.6f, %10.6f.",
stat_n, stat_mean, stat_n <= 1 ? 0.0 : sqrtf(stat_M2 / (stat_n - 1)),
@@ -1826,6 +1789,7 @@ void *player_thread_func(void *arg) {
stats_t statistics[trend_interval];
int number_of_statistics, oldest_statistic, newest_statistic;
int at_least_one_frame_seen = 0;
int at_least_one_frame_seen_this_session = 0;
int64_t tsum_of_sync_errors, tsum_of_corrections, tsum_of_insertions_and_deletions,
tsum_of_drifts;
int64_t previous_sync_error = 0, previous_correction = 0;
@@ -1920,17 +1884,17 @@ void *player_thread_func(void *arg) {
// if ((input_rate!=config.output_rate) || (input_bit_depth!=output_bit_depth)) {
// debug(1,"Define tbuf of length
// %d.",output_bytes_per_frame*(max_frames_per_packet*output_sample_ratio+max_frame_size_change));
conn->tbuf =
malloc(sizeof(int32_t) * 2 * (conn->max_frames_per_packet * conn->output_sample_ratio +
conn->max_frame_size_change));
conn->tbuf = malloc(
sizeof(int32_t) * 2 *
(conn->max_frames_per_packet * conn->output_sample_ratio + conn->max_frame_size_change));
if (conn->tbuf == NULL)
die("Failed to allocate memory for the transition buffer.");
// initialise this, because soxr stuffing might be chosen later
conn->sbuf =
malloc(sizeof(int32_t) * 2 * (conn->max_frames_per_packet * conn->output_sample_ratio +
conn->max_frame_size_change));
conn->sbuf = malloc(
sizeof(int32_t) * 2 *
(conn->max_frames_per_packet * conn->output_sample_ratio + conn->max_frame_size_change));
if (conn->sbuf == NULL)
die("Failed to allocate memory for the sbuf buffer.");
@@ -2040,8 +2004,11 @@ void *player_thread_func(void *arg) {
// debug(3, "Play frame %d.", play_number);
conn->play_number_after_flush++;
if (inframe->given_timestamp == 0) {
debug(1, "Player has supplied a silent frame, (possibly frame %u) for play number %d, status 0x%X after %u resend requests.",
SUCCESSOR(conn->last_seqno_read), play_number, inframe->status, inframe->resend_request_number);
debug(1,
"Player has supplied a silent frame, (possibly frame %u) for play number %d, "
"status 0x%X after %u resend requests.",
SUCCESSOR(conn->last_seqno_read), play_number, inframe->status,
inframe->resend_request_number);
conn->last_seqno_read = (SUCCESSOR(conn->last_seqno_read) &
0xffff); // manage the packet out of sequence minder
@@ -2162,8 +2129,6 @@ void *player_thread_func(void *arg) {
// If it's late, we remove an audio frame from this frame to bring a subsequent frame
// forward in time
at_least_one_frame_seen = 1;
// now, go back as far as the total latency less, say, 100 ms, and check the presence of
// frames from then onwards
@@ -2194,7 +2159,8 @@ void *player_thread_func(void *arg) {
// should be negated. Conversion is
// positive uint64_t to int64_t, thus
// okay.
td_in_frames = (td * config.output_rate) / 1000000000 ; // use the absolute td value for the present. Types okay
td_in_frames = (td * config.output_rate) /
1000000000; // use the absolute td value for the present. Types okay
td_in_frames = -td_in_frames;
td = -td; // should be okay, as the range of values should be very small w.r.t 64 bits
}
@@ -2213,7 +2179,8 @@ void *player_thread_func(void *arg) {
SUCCESSOR(conn->last_seqno_read); // int32_t from seq_t, i.e. uint16_t, so okay.
if (inframe->sequence_number !=
conn->last_seqno_read) { // seq_t, ei.e. uint16_t and int32_t, so okay
debug(2, "Player: packets out of sequence: expected: %u, got: %u, with ab_read: %u "
debug(2,
"Player: packets out of sequence: expected: %u, got: %u, with ab_read: %u "
"and ab_write: %u.",
conn->last_seqno_read, inframe->sequence_number, conn->ab_read, conn->ab_write);
conn->last_seqno_read = inframe->sequence_number; // reset warning...
@@ -2306,7 +2273,48 @@ void *player_thread_func(void *arg) {
(int64_t)(config.audio_backend_latency_offset *
config.output_rate)); // int64_t from int64_t - int32_t, so okay
// debug(1,"%" PRId64 "",sync_error,inbuflength);
if (at_least_one_frame_seen_this_session == 0) {
at_least_one_frame_seen_this_session = 1;
// debug(2,"first frame real sync error (positive --> late): %" PRId64 " frames.", sync_error);
// this is a sneaky attempt to make a final adjustment to the timing of the first packet
// the very first packet generally has a first_frame_early_bias subtracted from its timing
// to make it more likely that it will be early than late,
// making it possible to compensate for it be adding a few frames of silence.
// debug(2,"first frame real sync error (positive --> late): %" PRId64 " frames.", sync_error);
// remove the bias when reporting the error to make it the true error
debug(2,"first frame sync error (positive --> late): %" PRId64 " frames, %.3f mS at %d frames per second output.", sync_error+first_frame_early_bias, (1000.0*(sync_error+first_frame_early_bias))/config.output_rate, config.output_rate);
// if the packet is early, add the frames needed to put it in sync.
if (sync_error < 0) {
size_t final_adjustment_length_sized = -sync_error;
char *final_adjustment_silence = malloc(conn->output_bytes_per_frame * final_adjustment_length_sized);
if (final_adjustment_silence) {
conn->previous_random_number =
generate_zero_frames(final_adjustment_silence, final_adjustment_length_sized, config.output_format,
conn->enable_dither, conn->previous_random_number);
int final_adjustment = -sync_error;
final_adjustment = final_adjustment - first_frame_early_bias;
debug(2, "final sync adjustment: %d frames.", final_adjustment);
config.output->play(final_adjustment_silence, final_adjustment_length_sized);
free(final_adjustment_silence);
} else {
warn("Failed to allocate memory for a final_adjustment_silence buffer of %d frames for a "
"sync error of %d frames.",
final_adjustment_length_sized, sync_error);
}
sync_error = 0; // say the error was fixed!
}
}
at_least_one_frame_seen = 1;
// not too sure if abs() is implemented for int64_t, so we'll do it manually
int64_t abs_sync_error = sync_error;
@@ -2416,7 +2424,9 @@ void *player_thread_func(void *arg) {
if ((local_time_now) && (conn->first_packet_time_to_play) &&
(local_time_now >= conn->first_packet_time_to_play)) {
int64_t tp = (local_time_now - conn->first_packet_time_to_play) / 1000000000; // seconds int64_t from uint64_t which is always positive, so ok
int64_t tp =
(local_time_now - conn->first_packet_time_to_play) /
1000000000; // seconds int64_t from uint64_t which is always positive, so ok
if (tp < 5)
amount_to_stuff = 0; // wait at least five seconds
@@ -2435,17 +2445,18 @@ void *player_thread_func(void *arg) {
// Apply DSP here
// check the state of loudness and convolution flags here and don't change them for the frame
// check the state of loudness and convolution flags here and don't change them for
// the frame
int do_loudness = config.loudness;
#ifdef CONFIG_CONVOLUTION
int do_convolution = 0;
if ((config.convolution) && (config.convolver_valid))
do_convolution = 1;
// we will apply the convolution gain if convolution is enabled, even if there is no valid convolution happening
// we will apply the convolution gain if convolution is enabled, even if there is no
// valid convolution happening
int convolution_is_enabled = 0;
if (config.convolution)
@@ -2704,7 +2715,8 @@ void *player_thread_func(void *arg) {
if ((config.output->delay)) {
if (config.no_sync == 0) {
inform("%*.2f," /* Sync error in milliseconds */
inform(
"%*.2f," /* Sync error in milliseconds */
"%*.1f," /* net correction in ppm */
"%*.1f," /* corrections in ppm */
"%*d," /* total packets */
@@ -2721,21 +2733,19 @@ void *player_thread_func(void *arg) {
"%*.2f," /* source clock drift */
"%*d," /* source clock drift sample count */
"%*.2f", /* rough calculated correction in ppm */
10,
1000 * moving_average_sync_error / config.output_rate, 10,
moving_average_correction * 1000000 / (352 * conn->output_sample_ratio),
10, moving_average_insertions_plus_deletions * 1000000 /
10, 1000 * moving_average_sync_error / config.output_rate, 10,
moving_average_correction * 1000000 / (352 * conn->output_sample_ratio), 10,
moving_average_insertions_plus_deletions * 1000000 /
(352 * conn->output_sample_ratio),
12, play_number, 7, conn->missing_packets, 7, conn->late_packets, 7,
conn->too_late_packets, 7, conn->resend_requests, 7,
minimum_dac_queue_size, 5, minimum_buffer_occupancy, 5,
maximum_buffer_occupancy, 11, conn->remote_frame_rate, 11,
conn->input_frame_rate, 11, conn->frame_rate, 10,
conn->too_late_packets, 7, conn->resend_requests, 7, minimum_dac_queue_size,
5, minimum_buffer_occupancy, 5, maximum_buffer_occupancy, 11,
conn->remote_frame_rate, 11, conn->input_frame_rate, 11, conn->frame_rate, 10,
(conn->local_to_remote_time_gradient - 1.0) * 1000000, 6,
conn->local_to_remote_time_gradient_sample_count, 10,
(conn->frame_rate > 0.0)
? ((conn->frame_rate -
conn->remote_frame_rate * conn->output_sample_ratio *
? ((conn->frame_rate - conn->remote_frame_rate *
conn->output_sample_ratio *
conn->local_to_remote_time_gradient) *
1000000) /
conn->frame_rate
@@ -2754,10 +2764,9 @@ void *player_thread_func(void *arg) {
"%*.2f," /* source actual (average) frame rate */
"%*.2f," /* source clock drift */
"%*d", /* source clock drift sample count */
10,
1000 * moving_average_sync_error / config.output_rate, 12, play_number, 7,
conn->missing_packets, 7, conn->late_packets, 7, conn->too_late_packets, 7,
conn->resend_requests, 7, minimum_dac_queue_size, 5,
10, 1000 * moving_average_sync_error / config.output_rate, 12, play_number,
7, conn->missing_packets, 7, conn->late_packets, 7, conn->too_late_packets,
7, conn->resend_requests, 7, minimum_dac_queue_size, 5,
minimum_buffer_occupancy, 5, maximum_buffer_occupancy, 11,
conn->remote_frame_rate, 11, conn->input_frame_rate, 10,
(conn->local_to_remote_time_gradient - 1.0) * 1000000, 6,
@@ -2776,10 +2785,9 @@ void *player_thread_func(void *arg) {
"%*.2f," /* source actual (average) frame rate */
"%*.2f," /* source clock drift */
"%*d", /* source clock drift sample count */
10,
1000 * moving_average_sync_error / config.output_rate, 12, play_number, 7,
conn->missing_packets, 7, conn->late_packets, 7, conn->too_late_packets, 7,
conn->resend_requests, 5, minimum_buffer_occupancy, 5,
10, 1000 * moving_average_sync_error / config.output_rate, 12, play_number,
7, conn->missing_packets, 7, conn->late_packets, 7, conn->too_late_packets,
7, conn->resend_requests, 5, minimum_buffer_occupancy, 5,
maximum_buffer_occupancy, 11, conn->remote_frame_rate, 11,
conn->input_frame_rate, 10,
(conn->local_to_remote_time_gradient - 1.0) * 1000000, 6,
@@ -2879,12 +2887,14 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c
if (airplay_volume == -144.0) {
if ((config.output->mute) && (config.output->mute(1) == 0))
debug(2, "player_volume_without_notification: volume mode is %d, airplay_volume is %f, "
debug(2,
"player_volume_without_notification: volume mode is %d, airplay_volume is %f, "
"hardware mute is enabled.",
volume_mode, airplay_volume);
else {
conn->software_mute_enabled = 1;
debug(2, "player_volume_without_notification: volume mode is %d, airplay_volume is %f, "
debug(2,
"player_volume_without_notification: volume mode is %d, airplay_volume is %f, "
"software mute is enabled.",
volume_mode, airplay_volume);
}
@@ -2997,7 +3007,8 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c
memset(dv, 0, 128);
if (volume_mode == vol_both) {
// normalise the maximum output to the hardware device's max output
snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, (scaled_attenuation - max_db + hw_max_db) / 100.0,
snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume,
(scaled_attenuation - max_db + hw_max_db) / 100.0,
(min_db - max_db + hw_max_db) / 100.0, (max_db - max_db + hw_max_db) / 100.0);
} else {
snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, scaled_attenuation / 100.0,
@@ -3007,12 +3018,12 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c
}
#endif
if (config.output->mute)
config.output->mute(0);
conn->software_mute_enabled = 0;
debug(2, "player_volume_without_notification: volume mode is %d, airplay volume is %f, "
debug(2,
"player_volume_without_notification: volume mode is %d, airplay volume is %f, "
"software_attenuation: %f, hardware_attenuation: %f, muting "
"is disabled.",
volume_mode, airplay_volume, software_attenuation, hardware_attenuation);
@@ -3032,7 +3043,6 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c
}
#endif
// here, store the volume for possible use in the future
config.airplay_volume = airplay_volume;
debug_mutex_unlock(&conn->volume_control_mutex, 3);
+2 -1
View File
@@ -43,7 +43,8 @@ typedef struct audio_buffer_entry { // decoded audio packets
uint16_t resend_request_number;
signed short *data;
seq_t sequence_number;
uint64_t initialisation_time; // the time the packet was added or the time it was noticed the packet was missing
uint64_t initialisation_time; // the time the packet was added or the time it was noticed the
// packet was missing
uint64_t resend_time; // time of last resend request or zero
uint32_t given_timestamp; // for debugging and checking
int length; // the length of the decoded data
+18 -13
View File
@@ -74,9 +74,13 @@ uint64_t local_to_remote_time_difference_now(rtsp_conn_info *conn) {
uint64_t result = conn->local_to_remote_time_difference;
if (conn->local_to_remote_time_gradient >= 1.0) {
result = conn->local_to_remote_time_difference + (uint64_t)((conn->local_to_remote_time_gradient - 1.0) * time_since_last_local_to_remote_time_difference_measurement);
result = conn->local_to_remote_time_difference +
(uint64_t)((conn->local_to_remote_time_gradient - 1.0) *
time_since_last_local_to_remote_time_difference_measurement);
} else {
result = conn->local_to_remote_time_difference - (uint64_t)((1.0 - conn->local_to_remote_time_gradient) * time_since_last_local_to_remote_time_difference_measurement);
result = conn->local_to_remote_time_difference -
(uint64_t)((1.0 - conn->local_to_remote_time_gradient) *
time_since_last_local_to_remote_time_difference_measurement);
}
return result;
}
@@ -111,8 +115,7 @@ void *rtp_audio_receiver(void *arg) {
uint64_t local_time_now_ns = get_absolute_time_in_ns();
if (time_of_previous_packet_ns) {
float time_interval_us =
(local_time_now_ns - time_of_previous_packet_ns) * 0.001;
float time_interval_us = (local_time_now_ns - time_of_previous_packet_ns) * 0.001;
time_of_previous_packet_ns = local_time_now_ns;
if (time_interval_us > longest_packet_time_interval_us)
longest_packet_time_interval_us = time_interval_us;
@@ -121,7 +124,8 @@ void *rtp_audio_receiver(void *arg) {
stat_mean += stat_delta / stat_n;
stat_M2 += stat_delta * (time_interval_us - stat_mean);
if (stat_n % 2500 == 0) {
debug(2, "Packet reception interval stats: mean, standard deviation and max for the last "
debug(2,
"Packet reception interval stats: mean, standard deviation and max for the last "
"2,500 packets in microseconds: %10.1f, %10.1f, %10.1f.",
stat_mean, sqrtf(stat_M2 / (stat_n - 1)), longest_packet_time_interval_us);
stat_n = 0;
@@ -354,7 +358,8 @@ void *rtp_control_receiver(void *arg) {
if (la != conn->latency) {
conn->latency = la;
debug(3, "New latency detected: %" PRIu32 ", sync latency: %" PRIu32
debug(3,
"New latency detected: %" PRIu32 ", sync latency: %" PRIu32
", minimum latency: %" PRIu32 ", maximum "
"latency: %" PRIu32 ", fixed offset: %" PRIu32 ".",
la, sync_rtp_timestamp - rtp_timestamp_less_latency, conn->minimum_latency,
@@ -777,7 +782,9 @@ void *rtp_timing_receiver(void *arg) {
// debug(1,"local to remote time gradient is %12.2f ppm, based on %d
// samples.",conn->local_to_remote_time_gradient*1000000,sample_count);
} else {
debug(2, "Time ping turnaround time: " PRIu64 " ns -- it looks like a timing ping was lost.",
debug(2,
"Time ping turnaround time: %" PRIu64
" ns -- it looks like a timing ping was lost.",
return_time);
}
} else {
@@ -1178,22 +1185,20 @@ void rtp_request_resend(seq_t first, uint32_t count, rtsp_conn_info *conn) {
(struct sockaddr *)&conn->rtp_client_control_socket, msgsize) == -1) {
char em[1024];
strerror_r(errno, em, sizeof(em));
debug(2, "Error %d using sendto to request a resend: \"%s\".",
errno, em);
debug(2, "Error %d using sendto to request a resend: \"%s\".", errno, em);
conn->rtp_time_of_last_resend_request_error_ns = time_of_sending_ns;
} else {
conn->rtp_time_of_last_resend_request_error_ns = 0;
}
} else {
debug(
3,
"Dropping resend request packet to simulate a bad network. Backing off for 0.3 "
debug(3, "Dropping resend request packet to simulate a bad network. Backing off for 0.3 "
"second.");
conn->rtp_time_of_last_resend_request_error_ns = time_of_sending_ns;
}
} else {
debug(1, "Suppressing a resend request due to a resend sendto error in the last 0.3 seconds.");
debug(1,
"Suppressing a resend request due to a resend sendto error in the last 0.3 seconds.");
}
} else {
// if (!request_sent) {
+21 -12
View File
@@ -286,14 +286,16 @@ void *player_watchdog_thread_code(void *arg) {
uint64_t last_watchdog_bark_time = conn->watchdog_bark_time;
debug_mutex_unlock(&conn->watchdog_mutex, 0);
if (last_watchdog_bark_time != 0) {
uint64_t time_since_last_bark = (get_absolute_time_in_ns() - last_watchdog_bark_time) / 1000000000;
uint64_t time_since_last_bark =
(get_absolute_time_in_ns() - last_watchdog_bark_time) / 1000000000;
uint64_t ct = config.timeout; // go from int to 64-bit int
if (time_since_last_bark >= ct) {
conn->watchdog_barks++;
if (conn->watchdog_barks == 1) {
// debuglev = 3; // tell us everything.
debug(1, "Connection %d: As Yeats almost said, \"Too long a silence / can make a stone "
debug(1,
"Connection %d: As Yeats almost said, \"Too long a silence / can make a stone "
"of the heart\".",
conn->connection_number);
conn->stop = 1;
@@ -614,7 +616,8 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes
if (errno == EINTR)
continue;
if (errno == EAGAIN) {
debug(1, "Connection %d: getting Error 11 -- EAGAIN from a blocking read!", conn->connection_number);
debug(1, "Connection %d: getting Error 11 -- EAGAIN from a blocking read!",
conn->connection_number);
continue;
}
if (errno != ECONNRESET) {
@@ -644,7 +647,8 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes
msg_size = msg_handle_line(the_packet, buf);
if (!(*the_packet)) {
debug(1,"Connection %d: rtsp_read_request can't find an RTSP header.", conn->connection_number);
debug(1, "Connection %d: rtsp_read_request can't find an RTSP header.",
conn->connection_number);
reply = rtsp_read_request_response_bad_packet;
goto shutdown;
}
@@ -851,7 +855,8 @@ void handle_options(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *
rtsp_message *resp) {
debug(3, "Connection %d: OPTIONS", conn->connection_number);
resp->respcode = 200;
msg_add_header(resp, "Public", "ANNOUNCE, SETUP, RECORD, "
msg_add_header(resp, "Public",
"ANNOUNCE, SETUP, RECORD, "
"PAUSE, FLUSH, TEARDOWN, "
"OPTIONS, GET_PARAMETER, SET_PARAMETER");
}
@@ -994,7 +999,8 @@ void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
msg_add_header(resp, "Session", "1");
resp->respcode = 200; // it all worked out okay
debug(1, "Connection %d: SETUP DACP-ID \"%s\" from %s to %s with UDP ports Control: "
debug(1,
"Connection %d: SETUP DACP-ID \"%s\" from %s to %s with UDP ports Control: "
"%d, Timing: %d and Audio: %d.",
conn->connection_number, conn->dacp_id, &conn->client_ip_string,
&conn->self_ip_string, conn->local_control_port, conn->local_timing_port,
@@ -2370,8 +2376,9 @@ static void *rtsp_conversation_thread_func(void *pconn) {
if (strcmp(req->method, "OPTIONS") !=
0) // the options message is very common, so don't log it until level 3
debug_level = 2;
debug(debug_level, "Connection %d: Received an RTSP Packet of type \"%s\":",
conn->connection_number, req->method),
debug(debug_level,
"Connection %d: Received an RTSP Packet of type \"%s\":", conn->connection_number,
req->method),
debug_print_msg_headers(debug_level, req);
apple_challenge(conn->fd, req, resp);
@@ -2420,7 +2427,8 @@ static void *rtsp_conversation_thread_func(void *pconn) {
if (conn->stop == 0) {
int err = msg_write_response(conn->fd, resp);
if (err) {
debug(1, "Connection %d: Unable to write an RTSP message response. Terminating the "
debug(1,
"Connection %d: Unable to write an RTSP message response. Terminating the "
"connection.",
conn->connection_number);
struct linger so_linger;
@@ -2474,10 +2482,11 @@ static void *rtsp_conversation_thread_func(void *pconn) {
if (reply == -1) {
char errorstring[1024];
strerror_r(errno, (char *)errorstring, sizeof(errorstring));
debug(1, "rtsp_read_request_response_bad_packet write response error %d: \"%s\".", errno, (char *)errorstring);
debug(1, "rtsp_read_request_response_bad_packet write response error %d: \"%s\".", errno,
(char *)errorstring);
} else if (reply != (ssize_t)strlen(response_text)) {
debug(1, "rtsp_read_request_response_bad_packet write %d bytes requested but %d written.", strlen(response_text),
reply);
debug(1, "rtsp_read_request_response_bad_packet write %d bytes requested but %d written.",
strlen(response_text), reply);
}
} else {
debug(1, "Connection %d: rtsp_read_request error %d, packet ignored.",
+28 -17
View File
@@ -18,11 +18,20 @@ general =
// interpolation = "auto"; // aka "stuffing". Default is "auto". Alternatives are "basic" or "soxr". Choose "soxr" only if you have a reasonably fast processor and Shairport Sync has been built with "soxr" support.
// output_backend = "alsa"; // Run "shairport-sync -h" to get a list of all output_backends, e.g. "alsa", "pipe", "stdout". The default is the first one.
// mdns_backend = "avahi"; // Run "shairport-sync -h" to get a list of all mdns_backends. The default is the first one.
// interface = "name"; // Use this advanced setting to specify the interface on which Shairport Sync should provide its service. Leave it commented out to get the default, which is to select the interface(s) automatically.
// port = 5000; // Listen for service requests on this port
// udp_port_base = 6001; // start allocating UDP ports from this port number when needed
// udp_port_range = 10; // look for free ports in this number of places, starting at the UDP port base. Allow at least 10, though only three are needed in a steady state.
// regtype = "_raop._tcp"; // Use this advanced setting to set the service type and transport to be advertised by Zeroconf/Bonjour. Default is "_raop._tcp".
// drift_tolerance_in_seconds = 0.002; // allow a timing error of this number of seconds of drift away from exact synchronisation before attempting to correct it
// resync_threshold_in_seconds = 0.050; // a synchronisation error greater than this number of seconds will cause resynchronisation; 0 disables it
// playback_mode = "stereo"; // This can be "stereo", "mono", "reverse stereo", "both left" or "both right". Default is "stereo".
// alac_decoder = "hammerton"; // This can be "hammerton" or "apple". This advanced setting allows you to choose
// the original Shairport decoder by David Hammerton or the Apple Lossless Audio Codec (ALAC) decoder written by Apple.
// If you build Shairport Sync with the flag --with-apple-alac, the Apple ALAC decoder will be chosen by default.
// ignore_volume_control = "no"; // set this to "yes" if you want the volume to be at 100% no matter what the source's volume control is set to.
// volume_range_db = 60 ; // use this advanced setting to set the range, in dB, you want between the maximum volume and the minimum volume. Range is 30 to 150 dB. Leave it commented out to use mixer's native range.
// volume_max_db = 0.0 ; // use this advanced setting, which must have a decimal point in it, to set the maximum volume, in dB, you wish to use.
@@ -36,25 +45,23 @@ general =
// The desired AirPlay volume is appended to the end of the command line – leave a space if you want it treated as an extra argument.
// AirPlay volume goes from 0 to -30 and -144 means "mute".
// regtype = "_raop._tcp"; // Use this advanced setting to set the service type and transport to be advertised by Zeroconf/Bonjour. Default is "_raop._tcp".
// playback_mode = "stereo"; // This can be "stereo", "mono", "reverse stereo", "both left" or "both right". Default is "stereo".
// alac_decoder = "hammerton"; // This can be "hammerton" or "apple". This advanced setting allows you to choose
// the original Shairport decoder by David Hammerton or the Apple Lossless Audio Codec (ALAC) decoder written by Apple.
// If you build Shairport Sync with the flag --with-apple-alac, the Apple ALAC decoder will be chosen by default.
// interface = "name"; // Use this advanced setting to specify the interface on which Shairport Sync should provide its service. Leave it commented out to get the default, which is to select the interface(s) automatically.
// audio_backend_latency_offset_in_seconds = 0.0; // This is added to the latency requested by the player to delay or advance the output by a fixed amount. Use it, for example, to compensate for a fixed delay in the audio back end. E.g. if the output device, e.g. a soundbar, takes 100 ms to process audio, set this to -0.1 to deliver the audio to the output device 100 ms early, allowing it time to process the audio and output it perfectly in sync.
// audio_backend_buffer_desired_length_in_seconds = 0.2; // If set too small, buffer underflow occurs on low-powered machines. Too long and the response time to volume changes becomes annoying. Default is 0.2 seconds in the alsa backend, 0.35 seconds in the pa backend and 1.0 seconds otherwise.
// audio_backend_latency_offset_in_seconds = 0.0; // This is added to the latency requested by the player to delay or advance the output by a fixed amount.
// Use it, for example, to compensate for a fixed delay in the audio back end.
// E.g. if the output device, e.g. a soundbar, takes 100 ms to process audio, set this to -0.1 to deliver the audio
// to the output device 100 ms early, allowing it time to process the audio and output it perfectly in sync.
// audio_backend_buffer_desired_length_in_seconds = 0.2; // If set too small, buffer underflow occurs on low-powered machines.
// Too long and the response time to volume changes becomes annoying.
// Default is 0.2 seconds in the alsa backend, 0.35 seconds in the pa backend and 1.0 seconds otherwise.
// audio_backend_buffer_interpolation_threshold_in_seconds = 0.075; // Advanced feature. If the buffer size drops below this, stop using time-consuming interpolation like soxr to avoid dropouts due to underrun.
// audio_backend_silent_lead_in_time = "auto"; // This optional advanced setting, either "auto" or a positive number, sets the length of the period of silence that precedes the start of the audio. The default is "auto" -- the silent lead-in starts as soon as the player starts sending packets. Values greater than the latency are ignored. Values that are too low will affect initial synchronisation.
// audio_backend_silent_lead_in_initial_period = 0.5; // This optional advanced setting sets the length, in seconds, of the first "block" of silence sent to the DAC -- some DACs need a fairly substantial block to prevent lots of initial underrun.
// audio_backend_silent_lead_in_minimum_adjustments_to_make = 3; // This optional advanced setting sets the minimum number of times Shairport Sync will readjust the length of the silent lead in to ensure that the audio starts at precisely the correct time.
// audio_backend_silent_lead_in_adjustment_interval = 0.1; // This optional advanced setting sets the interval, in seconds, between adjustments of the silent lead in to ensure that the audio starts at precisely the correct time.
// audio_backend_silent_lead_in_time = "auto"; // This optional advanced setting, either "auto" or a positive number, sets the length of the period of silence that precedes the start of the audio.
// The default is "auto" -- the silent lead-in starts as soon as the player starts sending packets.
// Values greater than the latency are ignored. Values that are too low will affect initial synchronisation.
// dbus_service_bus = "system"; // The Shairport Sync dbus interface, if selected at compilation, will appear
// as "org.gnome.ShairportSync" on the whichever bus you specify here: "system" (default) or "session".
// mpris_service_bus = "system"; // The Shairport Sync mpris interface, if selected at compilation, will appear
// as "org.gnome.ShairportSync" on the whichever bus you specify here: "system" (default) or "session".
// resend_control_first_check_time = 0.10; // Use this optional advanced setting to set the wait time in seconds before deciding a packet is missing.
// resend_control_check_interval_time = 0.25; // Use this optional advanced setting to set the time in seconds between requests for a missing packet.
// resend_control_last_check_time = 0.10; // Use this optional advanced setting to set the latest time, in seconds, by which the last check should be done before the estimated time of a missing packet's transfer to the output buffer.
@@ -64,14 +71,14 @@ general =
// Advanced parameters for controlling how Shairport Sync stays active and how it runs a session
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_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
// "active" state starts when play begins and ends when the active_state_timeout has elapsed after play ends, unless another play session starts before the timeout has fully elapsed.
// run_this_before_entering_active_state = "/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_exiting_active_state = "/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
// active_state_timeout = 10.0; // wait for this number of seconds after play ends before leaving the active state, unless another play session begins.
// 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_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. An error code-string is passed as the last argument.
// 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!
@@ -91,15 +98,19 @@ alsa =
// output_device = "default"; // the name of the alsa output device. Use "shairport-sync -h" to discover the names of ALSA hardware devices. Use "alsamixer" or "aplay" to find out the names of devices, mixers, etc.
// mixer_control_name = "PCM"; // the name of the mixer to use to adjust output volume. If not specified, volume in adjusted in software.
// mixer_device = "default"; // the mixer_device default is whatever the output_device is. Normally you wouldn't have to use this.
// output_rate = "auto"; // can be "auto", 44100, 88200, 176400 or 352800, but the device must have the capability.
// output_format = "auto"; // can be "auto", "U8", "S8", "S16", "S16_LE", "S16_BE", "S24", "S24_LE", "S24_BE", "S24_3LE", "S24_3BE", "S32", "S32_LE" or "S32_BE" but the device must have the capability. Except where stated using (*LE or *BE), endianness matches that of the processor.
// disable_synchronization = "no"; // Set to "yes" to disable synchronization. Default is "no".
// disable_synchronization = "no"; // Set to "yes" to disable synchronization. Default is "no" This is really meant for troubleshooting.
// period_size = <number>; // Use this optional advanced setting to set the alsa period size near to this value
// buffer_size = <number>; // Use this optional advanced setting to set the alsa buffer size near to this value
// use_mmap_if_available = "yes"; // Use this optional advanced setting to control whether MMAP-based output is used to communicate with the DAC. Default is "yes"
// use_hardware_mute_if_available = "no"; // Use this optional advanced setting to control whether the hardware in the DAC is used for muting. Default is "no", for compatibility with other audio players.
// maximum_stall_time = 0.200; // Use this optional advanced setting to control how long to wait for data to be consumed by the output device before considering it an error. It should never approach 200 ms.
// use_precision_timing = "auto"; // Use this optional advanced setting to control how Shairport Sync gathers timing information. When set to "auto", if the output device is a real hardware device, precision timing will be used. Choose "no" for more compatible standard timing, choose "yes" to force the use of precision timing, which may cause problems.
// disable_standby_mode = "never"; // This setting prevents the DAC from entering the standby mode. Some DACs make small "popping" noises when they go in and out of standby mode. Settings can be: "always", "auto" or "never". Default is "never", but only for backwards compatibility. The "auto" setting prevents entry to standby mode while Shairport Sync is in the "active" mode. You can use "yes" instead of "always" and "no" instead of "never".
// disable_standby_mode_silence_threshold = 0.040; // Use this optional advanced setting to control how little audio should remain in the output buffer before the disable_standby code should start sending silence to the output device.
// disable_standby_mode_silence_scan_interval = 0.004; // Use this optional advanced setting to control how often the amount of audio remaining in the output buffer should be checked.
+16 -10
View File
@@ -186,11 +186,10 @@ int main(int argc, char *argv[]) {
g_signal_connect(proxy4, "notify::volume", G_CALLBACK(notify_volume_callback),
"ShairportSync.AdvancedRemoteControl");
g_print("Starting test...\n");
g_print("Using the RemoteControl interface, play for five seconds, pause for five seconds and then resume play...\n");
g_print("Using the RemoteControl interface, play for five seconds, pause for five seconds and "
"then resume play...\n");
g_print("Play...\n");
shairport_sync_remote_control_call_play(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), NULL, NULL, 0);
sleep(5);
@@ -200,24 +199,31 @@ int main(int argc, char *argv[]) {
g_print("Play...\n");
shairport_sync_remote_control_call_play(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), NULL, NULL, 0);
sleep(5);
g_print("Using the RemoteControl interface, set AirPlay Volume (range -30 to 0) to -30, -20, -10, 0 and -15 for five seconds each...\n");
g_print("Using the RemoteControl interface, set AirPlay Volume (range -30 to 0) to -30, -20, "
"-10, 0 and -15 for five seconds each...\n");
g_print("Set AirPlay Volume (range -30 to 0) to -30\n");
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), -30, NULL, NULL, 0);
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), -30,
NULL, NULL, 0);
sleep(5);
g_print("Set AirPlay Volume (range -30 to 0) to -20\n");
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), -20, NULL, NULL, 0);
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), -20,
NULL, NULL, 0);
sleep(5);
g_print("Set AirPlay Volume (range -30 to 0) to -10\n");
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), -10, NULL, NULL, 0);
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), -10,
NULL, NULL, 0);
sleep(5);
g_print("Set AirPlay Volume (range -30 to 0) to -0\n");
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), 0, NULL, NULL, 0);
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), 0,
NULL, NULL, 0);
sleep(5);
g_print("Set AirPlay Volume (range -30 to 0) to -15\n");
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), -15, NULL, NULL, 0);
shairport_sync_remote_control_call_set_airplay_volume(SHAIRPORT_SYNC_REMOTE_CONTROL(proxy3), -15,
NULL, NULL, 0);
sleep(5);
g_print("Using the AdvancedRemoteControl interface, set Volume to 20%%, 100%%, 40%% and 60%% for five seconds each...\n");
g_print("Using the AdvancedRemoteControl interface, set Volume to 20%%, 100%%, 40%% and 60%% for "
"five seconds each...\n");
g_print("Set Volume to 20%%\n");
shairport_sync_advanced_remote_control_call_set_volume(
SHAIRPORT_SYNC_ADVANCED_REMOTE_CONTROL(proxy4), 20, NULL, NULL, 0);
+37 -76
View File
@@ -145,7 +145,8 @@ void *soxr_time_check(__attribute__((unused)) void *arg) {
int number_of_iterations = 0;
uint64_t soxr_start_time = get_absolute_time_in_ns();
uint64_t loop_until_time =
(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_!
(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) {
number_of_iterations++;
@@ -184,8 +185,7 @@ void *soxr_time_check(__attribute__((unused)) void *arg) {
NULL, NULL); // Default configuration.
}
double soxr_execution_time_ns =
(get_absolute_time_in_ns() - soxr_start_time) * 1.0;
double soxr_execution_time_ns = (get_absolute_time_in_ns() - soxr_start_time) * 1.0;
// free(outbuffer);
// free(inbuffer);
config.soxr_delay_index = (int)(0.9 + soxr_execution_time_ns / (number_of_iterations * 1000000));
@@ -388,7 +388,8 @@ int parse_options(int argc, char **argv) {
config.resyncthreshold = 1.0 * fResyncthreshold / 44100;
config.tolerance = 1.0 * fTolerance / 44100;
config.audio_backend_silent_lead_in_time_auto = 1; // start outputting silence as soon as packets start arriving
config.audio_backend_silent_lead_in_time_auto =
1; // start outputting silence as soon as packets start arriving
config.airplay_volume = -18.0; // if no volume is ever set, default to initial default value if
// nothing else comes in first.
config.fixedLatencyOffset = 11025; // this sounds like it works properly.
@@ -400,16 +401,22 @@ int parse_options(int argc, char **argv) {
config.volume_range_hw_priority =
0; // if combining software and hardware volume control, give the software priority
// i.e. when reducing volume, reduce the sw first before reducing the software.
// this is because some hw mixers mute at the bottom of their range, and they don't always advertise
// this fact
config.resend_control_first_check_time = 0.10; // wait this many seconds before requesting the resending of a missing packet
config.resend_control_check_interval_time = 0.25; // wait this many seconds before again requesting the resending of a missing packet
config.resend_control_last_check_time = 0.10; // give up if the packet is still missing this close to when it's needed
config.missing_port_dacp_scan_interval_seconds = 2.0; // check at this interval if no DACP port number is known
// this is because some hw mixers mute at the bottom of their range, and they don't always
// advertise this fact
config.resend_control_first_check_time =
0.10; // wait this many seconds before requesting the resending of a missing packet
config.resend_control_check_interval_time =
0.25; // wait this many seconds before again requesting the resending of a missing packet
config.resend_control_last_check_time =
0.10; // give up if the packet is still missing this close to when it's needed
config.missing_port_dacp_scan_interval_seconds =
2.0; // check at this interval if no DACP port number is known
config.minimum_free_buffer_headroom = 125; // leave approximately one second's worth of buffers free after calculating the effective latency.
// e.g. if we have 1024 buffers or 352 frames = 8.17 seconds and we have a nominal latency of 2.0 seconds
// then we can add an offset of 5.17 seconds and still leave a second's worth of buffers for unexpected circumstances
config.minimum_free_buffer_headroom = 125; // leave approximately one second's worth of buffers
// free after calculating the effective latency.
// e.g. if we have 1024 buffers or 352 frames = 8.17 seconds and we have a nominal latency of 2.0
// seconds then we can add an offset of 5.17 seconds and still leave a second's worth of buffers
// for unexpected circumstances
#ifdef CONFIG_METADATA_HUB
config.cover_art_cache_dir = "/tmp/shairport-sync/.cache/coverart";
@@ -420,7 +427,8 @@ int parse_options(int argc, char **argv) {
config.scan_max_bad_response_count =
5; // number of successive bad results to ignore before giving up
// config.scan_max_inactive_count =
// (365 * 24 * 60 * 60) / config.scan_interval_when_inactive; // number of scans to do before stopping if
// (365 * 24 * 60 * 60) / config.scan_interval_when_inactive; // number of scans to do before
// stopping if
// not made active again (not used)
#endif
@@ -772,10 +780,8 @@ int parse_options(int argc, char **argv) {
die("Invalid alac_decoder option choice \"%s\". It should be \"hammerton\" or \"apple\"");
}
/* Get the resend control settings. */
if (config_lookup_float(config.cfg, "general.resend_control_first_check_time",
&dvalue)) {
if (config_lookup_float(config.cfg, "general.resend_control_first_check_time", &dvalue)) {
if ((dvalue >= 0.0) && (dvalue <= 3.0))
config.resend_control_first_check_time = dvalue;
else
@@ -786,8 +792,7 @@ int parse_options(int argc, char **argv) {
dvalue, config.resend_control_first_check_time);
}
if (config_lookup_float(config.cfg, "general.resend_control_check_interval_time",
&dvalue)) {
if (config_lookup_float(config.cfg, "general.resend_control_check_interval_time", &dvalue)) {
if ((dvalue >= 0.0) && (dvalue <= 3.0))
config.resend_control_check_interval_time = dvalue;
else
@@ -798,8 +803,7 @@ int parse_options(int argc, char **argv) {
dvalue, config.resend_control_check_interval_time);
}
if (config_lookup_float(config.cfg, "general.resend_control_last_check_time",
&dvalue)) {
if (config_lookup_float(config.cfg, "general.resend_control_last_check_time", &dvalue)) {
if ((dvalue >= 0.0) && (dvalue <= 3.0))
config.resend_control_last_check_time = dvalue;
else
@@ -826,40 +830,6 @@ int parse_options(int argc, char **argv) {
if (config_lookup_int(config.cfg, "latencies.default", &value))
config.userSuppliedLatency = value;
/* Get the lead-in silence settings. */
if (config_lookup_float(config.cfg, "general.audio_backend_silent_lead_in_initial_period",
&dvalue)) {
if ((dvalue >= 0.0) && (dvalue <= 3.0))
config.lead_in_silence_initial_period = dvalue;
else
warn("Invalid general audio_backend_silent_lead_in_initial_period setting \"%f\". It should "
"be "
"between 0.0 and 3.0, "
"inclusive. The setting remains at %f seconds.",
dvalue, config.lead_in_silence_initial_period);
}
if (config_lookup_int(config.cfg, "general.audio_backend_silent_lead_in_minimum_adjustments_to_make", &value)) {
if (value < 0)
die("Invalid audio_backend_silent_lead_in_minimum_adjustments_to_make range \"%d\". It must be positive. The setting remains at %d.",
value, config.lead_in_silence_minimum_adjustments_to_make);
else
config.lead_in_silence_minimum_adjustments_to_make = value;
}
if (config_lookup_float(config.cfg, "general.audio_backend_silent_lead_in_adjustment_interval",
&dvalue)) {
if ((dvalue >= 0.0) && (dvalue <= 3.0))
config.lead_in_silence_adjustment_interval = dvalue;
else
warn("Invalid general audio_backend_silent_lead_in_adjustment_interval setting \"%f\". It should "
"be "
"between 0.0 and 3.0, "
"inclusive. The setting remains at %f seconds.",
dvalue, config.lead_in_silence_adjustment_interval);
}
#ifdef CONFIG_METADATA
/* Get the metadata setting. */
config.metadata_enabled = 1; // if metadata support is included, then enable it by default
@@ -1014,7 +984,8 @@ int parse_options(int argc, char **argv) {
if (config_lookup_string(config.cfg, "dsp.convolution_ir_file", &str)) {
config.convolution_ir_file = strdup(str);
config.convolver_valid = convolver_init(config.convolution_ir_file, config.convolution_max_length);
config.convolver_valid =
convolver_init(config.convolution_ir_file, config.convolution_max_length);
}
if (config.convolution && config.convolution_ir_file == NULL) {
@@ -1224,7 +1195,8 @@ int parse_options(int argc, char **argv) {
#else
/* Check if we are called with -d or --daemon or -j or justDaemoniseNoPIDFile options*/
if ((daemonisewith != 0) || (daemonisewithout != 0)) {
fprintf(stderr, "%s was built without libdaemon, so does not support daemonisation using the "
fprintf(stderr,
"%s was built without libdaemon, so does not support daemonisation using the "
"-d, --daemon, -j or --justDaemoniseNoPIDFile options\n",
config.appName);
exit(EXIT_FAILURE);
@@ -1248,8 +1220,6 @@ int parse_options(int argc, char **argv) {
char hostname[100];
gethostname(hostname, 100);
char *i0;
if (raw_service_name == NULL)
i0 = strdup("%H"); // this is the default it the Service Name wasn't specified
@@ -1320,7 +1290,6 @@ const char *pid_file_proc(void) {
}
#endif
void exit_function() {
// the following is to ensure that if libdaemon has been included
@@ -1384,7 +1353,6 @@ Actually, there is no stop_mpris_service() function.
pthread_join(soxr_time_check_thread, NULL);
#endif
if (conns)
free(conns); // make sure the connections have been deleted first
@@ -1421,7 +1389,8 @@ Actually, there is no stop_mpris_service() function.
void handle_sigchld(__attribute__((unused)) int sig) {
int saved_errno = errno;
while (waitpid((pid_t)(-1), 0, WNOHANG) > 0) {}
while (waitpid((pid_t)(-1), 0, WNOHANG) > 0) {
}
errno = saved_errno;
}
@@ -1532,9 +1501,6 @@ int main(int argc, char **argv) {
config.output_rate_auto_requested = 1; // default auto select format
config.decoders_supported =
1 << decoder_hammerton; // David Hammerton's decoder supported by default
config.lead_in_silence_initial_period = 0.5; // the size of the first block of silence to send to the DAC
config.lead_in_silence_minimum_adjustments_to_make = 3; // make at least this number of checks and timing adjustments
config.lead_in_silence_adjustment_interval = 0.025; // make checks and adjustments at this interval
#ifdef CONFIG_APPLE_ALAC
config.decoders_supported += 1 << decoder_apple_alac;
config.use_apple_decoder = 1; // use the ALAC decoder by default if support has been included
@@ -1766,7 +1732,8 @@ int main(int argc, char **argv) {
/* Print out options */
debug(1, "disable resend requests is %s.", config.disable_resend_requests ? "on" : "off");
debug(1, "diagnostic_drop_packet_fraction is %f. A value of 0.0 means no packets will be dropped "
debug(1,
"diagnostic_drop_packet_fraction is %f. A value of 0.0 means no packets will be dropped "
"deliberately.",
config.diagnostic_drop_packet_fraction);
debug(1, "statistics_requester status is %d.", config.statistics_requested);
@@ -1790,8 +1757,8 @@ int main(int argc, char **argv) {
debug(1, "mdns backend \"%s\".", config.mdns_name);
debug(2, "userSuppliedLatency is %d.", config.userSuppliedLatency);
debug(1, "interpolation setting is \"%s\".",
config.packet_stuffing == ST_basic ? "basic" : config.packet_stuffing == ST_soxr ? "soxr"
: "auto");
config.packet_stuffing == ST_basic ? "basic"
: config.packet_stuffing == ST_soxr ? "soxr" : "auto");
debug(1, "interpolation soxr_delay_threshold is %d.", config.soxr_delay_threshold);
debug(1, "resync time is %f seconds.", config.resyncthreshold);
debug(1, "allow a session to be interrupted: %d.", config.allow_session_interruption);
@@ -1805,7 +1772,8 @@ int main(int argc, char **argv) {
debug(1, "volume_max_db is not set");
debug(1, "volume range in dB (zero means use the range specified by the mixer): %u.",
config.volume_range_db);
debug(1, "volume_range_combined_hardware_priority (1 means hardware mixer attenuation is used "
debug(1,
"volume_range_combined_hardware_priority (1 means hardware mixer attenuation is used "
"first) is %d.",
config.volume_range_hw_priority);
debug(1, "playback_mode is %d (0-stereo, 1-mono, 1-reverse_stereo, 2-both_left, 3-both_right).",
@@ -1830,13 +1798,6 @@ int main(int argc, char **argv) {
else
debug(1, "audio backend silence lead-in time is %f seconds.",
config.audio_backend_silent_lead_in_time);
debug(1, "audio backend silent lead-in initial period is %f seconds.",
config.lead_in_silence_initial_period);
debug(1, "audio backend silent lead-in minimum adjustments to make is %d.", config.lead_in_silence_minimum_adjustments_to_make);
debug(1, "audio backend silent lead-in adjustment interval is %f seconds.",
config.lead_in_silence_adjustment_interval);
debug(1, "zeroconf regtype is \"%s\".", config.regtype);
debug(1, "decoders_supported field is %d.", config.decoders_supported);
debug(1, "use_apple_decoder is %d.", config.use_apple_decoder);
+2 -1
View File
@@ -1737,7 +1737,8 @@ void mdnsd_stop(struct mdnsd *s) {
assert(s != NULL);
struct timeval tv = {
.tv_sec = 0, .tv_usec = 500 * 1000,
.tv_sec = 0,
.tv_usec = 500 * 1000,
};
s->stop_flag = 1;