Add an "active" mode that can time out after a period -- 10 seconds by default -- after play ends.
This commit is contained in:
+99
-90
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Activity Monitor
|
||||
*
|
||||
*
|
||||
* Contains code to run an activity flag and associated timer
|
||||
* A pthread implements a simple state machine with three states,
|
||||
* A pthread implements a simple state machine with three states,
|
||||
* "idle", "active" and "timing out".
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* This file is part of Shairport Sync.
|
||||
* Copyright (c) Mike Brady 2019
|
||||
* All rights reserved.
|
||||
@@ -31,41 +31,43 @@
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "activity_monitor.h"
|
||||
#include "common.h"
|
||||
#include "rtsp.h"
|
||||
#include "activity_monitor.h"
|
||||
|
||||
enum am_state {am_inactive, am_active, am_timing_out} state;
|
||||
enum ps_state {ps_inactive, ps_active} player_state;
|
||||
enum am_state { am_inactive, am_active, am_timing_out } state;
|
||||
enum ps_state { ps_inactive, ps_active } player_state;
|
||||
|
||||
pthread_t activity_monitor_thread;
|
||||
pthread_mutex_t activity_monitor_mutex;
|
||||
pthread_cond_t activity_monitor_cv;
|
||||
|
||||
void going_active(int block) {
|
||||
// debug(1, "activity_monitor: state transitioning to \"active\" with%s blocking", block ? "" : "out");
|
||||
// debug(1, "activity_monitor: state transitioning to \"active\" with%s blocking", block ? "" :
|
||||
// "out");
|
||||
if (config.cmd_active_start)
|
||||
command_execute(config.cmd_active_start, "", block);
|
||||
#ifdef CONFIG_METADATA
|
||||
debug(2, "abeg"); // active mode begin
|
||||
send_ssnc_metadata('pend', NULL, 0, 1); // contains cancellation points
|
||||
debug(2, "abeg"); // active mode begin
|
||||
send_ssnc_metadata('pend', NULL, 0, 1); // contains cancellation points
|
||||
#endif
|
||||
}
|
||||
|
||||
void going_inactive(int block) {
|
||||
// debug(1, "activity_monitor: state transitioning to \"inactive\" with%s blocking", block ? "" : "out");
|
||||
// debug(1, "activity_monitor: state transitioning to \"inactive\" with%s blocking", block ? "" :
|
||||
// "out");
|
||||
if (config.cmd_active_stop)
|
||||
command_execute(config.cmd_active_stop, "", block);
|
||||
#ifdef CONFIG_METADATA
|
||||
debug(2, "aend"); // active mode end
|
||||
send_ssnc_metadata('pend', NULL, 0, 1); // contains cancellation points
|
||||
debug(2, "aend"); // active mode end
|
||||
send_ssnc_metadata('pend', NULL, 0, 1); // contains cancellation points
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -83,17 +85,20 @@ void activity_monitor_signify_activity(int active) {
|
||||
// The reason for all this is that we might want to perform the attached scripts
|
||||
// and wait for them to complete before continuing. If they were perfomed in the
|
||||
// activity monitor thread, then we coldn't wait for them to complete.
|
||||
|
||||
|
||||
// Thus, the only time the thread will execute a going_... function is when a non-zero
|
||||
// timeout actually matures.
|
||||
|
||||
|
||||
if ((state == am_inactive) && (player_state == ps_active)) {
|
||||
going_active(config.cmd_blocking); // note -- will be executed with the mutex locked, but that's okay
|
||||
} else if ((state == am_active) && (player_state == ps_inactive) && (config.active_mode_timeout == 0.0)) {
|
||||
going_inactive(config.cmd_blocking); // note -- will be executed with the mutex locked, but that's okay
|
||||
going_active(
|
||||
config.cmd_blocking); // note -- will be executed with the mutex locked, but that's okay
|
||||
} else if ((state == am_active) && (player_state == ps_inactive) &&
|
||||
(config.active_mode_timeout == 0.0)) {
|
||||
going_inactive(
|
||||
config.cmd_blocking); // note -- will be executed with the mutex locked, but that's okay
|
||||
}
|
||||
|
||||
pthread_cond_signal(&activity_monitor_cv);
|
||||
|
||||
pthread_cond_signal(&activity_monitor_cv);
|
||||
pthread_mutex_unlock(&activity_monitor_mutex);
|
||||
}
|
||||
|
||||
@@ -115,7 +120,7 @@ void *activity_monitor_thread_code(void *arg) {
|
||||
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); // can't do this in OS X, and don't need it.
|
||||
rc = pthread_cond_init(&activity_monitor_cv, &attr);
|
||||
pthread_condattr_destroy(&attr);
|
||||
|
||||
|
||||
#endif
|
||||
#ifdef COMPILE_FOR_OSX
|
||||
rc = pthread_cond_init(&activity_monitor_cv, NULL);
|
||||
@@ -123,84 +128,89 @@ void *activity_monitor_thread_code(void *arg) {
|
||||
if (rc)
|
||||
die("activity_monitor: error %d initialising activity_monitor_cv.");
|
||||
pthread_cleanup_push(activity_thread_cleanup_handler, arg);
|
||||
|
||||
|
||||
uint64_t sec;
|
||||
uint64_t nsec;
|
||||
int rc;
|
||||
struct timespec time_for_wait;
|
||||
|
||||
|
||||
state = am_inactive;
|
||||
player_state = ps_inactive;
|
||||
|
||||
|
||||
pthread_mutex_lock(&activity_monitor_mutex);
|
||||
do {
|
||||
switch (state) {
|
||||
case am_inactive:
|
||||
// debug(1,"am_state: am_inactive");
|
||||
while (player_state != ps_active)
|
||||
pthread_cond_wait(&activity_monitor_cv, &activity_monitor_mutex);
|
||||
state = am_active;
|
||||
// going_active(); // this is done in activity_monitor_signify_activity
|
||||
case am_inactive:
|
||||
// debug(1,"am_state: am_inactive");
|
||||
while (player_state != ps_active)
|
||||
pthread_cond_wait(&activity_monitor_cv, &activity_monitor_mutex);
|
||||
state = am_active;
|
||||
// going_active(); // this is done in activity_monitor_signify_activity
|
||||
break;
|
||||
case am_active:
|
||||
// debug(1,"am_state: am_active");
|
||||
while (player_state != ps_inactive)
|
||||
pthread_cond_wait(&activity_monitor_cv, &activity_monitor_mutex);
|
||||
if (config.active_mode_timeout == 0.0) {
|
||||
state = am_inactive;
|
||||
// going_inactive(); // this is done in activity_monitor_signify_activity
|
||||
} else {
|
||||
state = am_timing_out;
|
||||
|
||||
uint64_t time_to_wait_for_wakeup_fp = (uint64_t)(config.active_mode_timeout *1000000); // resolution of microseconds
|
||||
time_to_wait_for_wakeup_fp = time_to_wait_for_wakeup_fp << 32;
|
||||
time_to_wait_for_wakeup_fp = time_to_wait_for_wakeup_fp / 1000000;
|
||||
|
||||
#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
|
||||
uint64_t time_of_wakeup_fp = get_absolute_time_in_fp() + time_to_wait_for_wakeup_fp;
|
||||
sec = time_of_wakeup_fp >> 32;
|
||||
nsec = ((time_of_wakeup_fp & 0xffffffff) * 1000000000) >> 32;
|
||||
time_for_wait.tv_sec = sec;
|
||||
time_for_wait.tv_nsec = nsec;
|
||||
#endif
|
||||
#ifdef COMPILE_FOR_OSX
|
||||
sec = time_to_wait_for_wakeup_fp >> 32;
|
||||
nsec = ((time_to_wait_for_wakeup_fp & 0xffffffff) * 1000000000) >> 32;
|
||||
time_for_wait.tv_sec = sec;
|
||||
time_for_wait.tv_nsec = nsec;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case am_timing_out:
|
||||
// debug(1,"am_state: am_timing_out");
|
||||
rc = 0;
|
||||
while ((player_state != ps_active) && (rc != ETIMEDOUT)) {
|
||||
#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
|
||||
rc = pthread_cond_timedwait(&activity_monitor_cv, &activity_monitor_mutex, &time_for_wait); // this is a pthread cancellation point
|
||||
#endif
|
||||
#ifdef COMPILE_FOR_OSX
|
||||
rc = pthread_cond_timedwait_relative_np(&activity_monitor_cv, &activity_monitor_mutex, &time_for_wait);
|
||||
#endif
|
||||
}
|
||||
if (player_state == ps_active)
|
||||
state = am_active; // player has gone active -- do nothing, because it's still active
|
||||
else if (rc == ETIMEDOUT) {
|
||||
state = am_inactive;
|
||||
pthread_mutex_unlock(&activity_monitor_mutex);
|
||||
going_inactive(0); // don't wait for completion -- it makes no sense
|
||||
pthread_mutex_lock(&activity_monitor_mutex);
|
||||
} else {
|
||||
// activity monitor was woken up in the state am_timing_out, but not by a timeout and player is not in ps_active state
|
||||
debug(1, "activity monitor was woken up in the state am_timing_out, but didn't change state");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
debug(1,"activity monitor in an illegal state!");
|
||||
case am_active:
|
||||
// debug(1,"am_state: am_active");
|
||||
while (player_state != ps_inactive)
|
||||
pthread_cond_wait(&activity_monitor_cv, &activity_monitor_mutex);
|
||||
if (config.active_mode_timeout == 0.0) {
|
||||
state = am_inactive;
|
||||
// going_inactive(); // this is done in activity_monitor_signify_activity
|
||||
} else {
|
||||
state = am_timing_out;
|
||||
|
||||
uint64_t time_to_wait_for_wakeup_fp =
|
||||
(uint64_t)(config.active_mode_timeout * 1000000); // resolution of microseconds
|
||||
time_to_wait_for_wakeup_fp = time_to_wait_for_wakeup_fp << 32;
|
||||
time_to_wait_for_wakeup_fp = time_to_wait_for_wakeup_fp / 1000000;
|
||||
|
||||
#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
|
||||
uint64_t time_of_wakeup_fp = get_absolute_time_in_fp() + time_to_wait_for_wakeup_fp;
|
||||
sec = time_of_wakeup_fp >> 32;
|
||||
nsec = ((time_of_wakeup_fp & 0xffffffff) * 1000000000) >> 32;
|
||||
time_for_wait.tv_sec = sec;
|
||||
time_for_wait.tv_nsec = nsec;
|
||||
#endif
|
||||
#ifdef COMPILE_FOR_OSX
|
||||
sec = time_to_wait_for_wakeup_fp >> 32;
|
||||
nsec = ((time_to_wait_for_wakeup_fp & 0xffffffff) * 1000000000) >> 32;
|
||||
time_for_wait.tv_sec = sec;
|
||||
time_for_wait.tv_nsec = nsec;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case am_timing_out:
|
||||
// debug(1,"am_state: am_timing_out");
|
||||
rc = 0;
|
||||
while ((player_state != ps_active) && (rc != ETIMEDOUT)) {
|
||||
#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
|
||||
rc = pthread_cond_timedwait(&activity_monitor_cv, &activity_monitor_mutex,
|
||||
&time_for_wait); // this is a pthread cancellation point
|
||||
#endif
|
||||
#ifdef COMPILE_FOR_OSX
|
||||
rc = pthread_cond_timedwait_relative_np(&activity_monitor_cv, &activity_monitor_mutex,
|
||||
&time_for_wait);
|
||||
#endif
|
||||
}
|
||||
if (player_state == ps_active)
|
||||
state = am_active; // player has gone active -- do nothing, because it's still active
|
||||
else if (rc == ETIMEDOUT) {
|
||||
state = am_inactive;
|
||||
pthread_mutex_unlock(&activity_monitor_mutex);
|
||||
going_inactive(0); // don't wait for completion -- it makes no sense
|
||||
pthread_mutex_lock(&activity_monitor_mutex);
|
||||
} else {
|
||||
// activity monitor was woken up in the state am_timing_out, but not by a timeout and player
|
||||
// is not in ps_active state
|
||||
debug(1,
|
||||
"activity monitor was woken up in the state am_timing_out, but didn't change state");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
debug(1, "activity monitor in an illegal state!");
|
||||
state = am_inactive;
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
pthread_mutex_unlock(&activity_monitor_mutex);
|
||||
pthread_mutex_unlock(&activity_monitor_mutex);
|
||||
pthread_cleanup_pop(0); // should never happen
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
@@ -211,9 +221,8 @@ void activity_monitor_start() {
|
||||
}
|
||||
|
||||
void activity_monitor_stop() {
|
||||
debug(1,"activity_monitor_stop start...");
|
||||
debug(1, "activity_monitor_stop start...");
|
||||
pthread_cancel(activity_monitor_thread);
|
||||
pthread_join(activity_monitor_thread,NULL);
|
||||
debug(1,"activity_monitor_stop complete");
|
||||
pthread_join(activity_monitor_thread, NULL);
|
||||
debug(1, "activity_monitor_stop complete");
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,8 @@ typedef struct {
|
||||
void (*start)(int sample_rate, int sample_format);
|
||||
|
||||
// block of samples
|
||||
int (*preflight)(void *buf, int samples); // say you are about to send these samples (before interpolation)
|
||||
int (*preflight)(void *buf,
|
||||
int samples); // say you are about to send these samples (before interpolation)
|
||||
int (*play)(void *buf, int samples);
|
||||
void (*stop)(void);
|
||||
|
||||
|
||||
+17
-13
@@ -469,8 +469,9 @@ int actual_open_alsa_device(void) {
|
||||
buffer_size);
|
||||
}
|
||||
*/
|
||||
debug(1, "The alsa buffer is smaller (%lu bytes) than the desired backend buffer "
|
||||
"length (%ld) you have chosen.",
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -633,13 +634,15 @@ static int init(int argc, char **argv) {
|
||||
config.audio_backend_buffer_interpolation_threshold_in_seconds =
|
||||
0.120; // below this, basic interpolation will be used to save time.
|
||||
config.alsa_maximum_stall_time = 0.200; // 200 milliseconds -- if it takes longer, it's a problem
|
||||
config.audio_backend_silence_threshold = 0.040; //start sending silent frames if the delay goes below this time
|
||||
config.audio_backend_silence_scan_interval = 0.004; //check silence threshold this often
|
||||
config.audio_backend_silence_threshold =
|
||||
0.040; // start sending silent frames if the delay goes below this time
|
||||
config.audio_backend_silence_scan_interval = 0.004; // check silence threshold this often
|
||||
|
||||
stall_monitor_error_threshold =
|
||||
(uint64_t)1000000 * config.alsa_maximum_stall_time; // stall time max to microseconds;
|
||||
stall_monitor_error_threshold = (stall_monitor_error_threshold << 32) / 1000000; // now in fp form
|
||||
debug(1, "stall_monitor_error_threshold is 0x%" PRIx64 ", with alsa_maximum_stall_time of %f sec.",
|
||||
debug(1,
|
||||
"stall_monitor_error_threshold is 0x%" PRIx64 ", with alsa_maximum_stall_time of %f sec.",
|
||||
stall_monitor_error_threshold, config.alsa_maximum_stall_time);
|
||||
|
||||
stall_monitor_start_time = 0;
|
||||
@@ -1047,9 +1050,10 @@ int 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
|
||||
", update_timestamp_ns: %" PRIX64 ", stall_monitor_start_time %" PRIX64
|
||||
", stall_monitor_error_threshold %" 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);
|
||||
ret = sps_extra_code_output_stalled;
|
||||
@@ -1264,8 +1268,8 @@ static void flush(void) {
|
||||
int preflight(__attribute__((unused)) void *buf, __attribute__((unused)) int samples) {
|
||||
uint64_t time_now =
|
||||
get_absolute_time_in_fp(); // this is to regulate access by the silence filler thread
|
||||
uint64_t standoff_time = 60; // milliseconds
|
||||
standoff_time = (standoff_time << 32)/1000;
|
||||
uint64_t standoff_time = 60; // milliseconds
|
||||
standoff_time = (standoff_time << 32) / 1000;
|
||||
most_recent_write_time = time_now + standoff_time;
|
||||
return 0;
|
||||
}
|
||||
@@ -1467,7 +1471,7 @@ void *alsa_buffer_monitor_thread_code(void *arg) {
|
||||
|
||||
int sleep_time_ms = (int)(config.audio_backend_silence_scan_interval * 1000);
|
||||
long buffer_size_threshold = (long)(config.audio_backend_silence_threshold * desired_sample_rate);
|
||||
|
||||
|
||||
uint64_t sleep_time_in_fp = sleep_time_ms;
|
||||
sleep_time_in_fp = sleep_time_in_fp << 32;
|
||||
sleep_time_in_fp = sleep_time_in_fp / 1000;
|
||||
@@ -1489,8 +1493,8 @@ void *alsa_buffer_monitor_thread_code(void *arg) {
|
||||
|
||||
if ((most_recent_write_time == 0) || (present_time > most_recent_write_time)) {
|
||||
|
||||
// ((present_time > most_recent_write_time) &&
|
||||
// ((present_time - most_recent_write_time) > (sleep_time_in_fp)))) {
|
||||
// ((present_time > most_recent_write_time) &&
|
||||
// ((present_time - most_recent_write_time) > (sleep_time_in_fp)))) {
|
||||
reply = delay(&buffer_size);
|
||||
if (reply != 0) {
|
||||
buffer_size = 0;
|
||||
|
||||
+3
-3
@@ -27,7 +27,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <jack/jack.h>
|
||||
@@ -336,8 +335,9 @@ int jack_init(__attribute__((unused)) int argc, __attribute__((unused)) char **a
|
||||
* we should try. */
|
||||
if (config_lookup_int(config.cfg, "jack.auto_client_open_interval", &value)) {
|
||||
if ((value < 0) || (value > 300))
|
||||
debug(1, "Invalid jack auto_client_open_interval \"%sd\". It should be between 0 and 300, "
|
||||
"default is %d.",
|
||||
debug(1,
|
||||
"Invalid jack auto_client_open_interval \"%sd\". It should be between 0 and 300, "
|
||||
"default is %d.",
|
||||
value, config.jack_auto_client_open_interval);
|
||||
else
|
||||
config.jack_auto_client_open_interval = value;
|
||||
|
||||
+3
-2
@@ -27,8 +27,9 @@ 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 , "
|
||||
"outstream->bytes_per_frame: %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);
|
||||
|
||||
if (frame_count_min > fill_count) {
|
||||
|
||||
@@ -114,7 +114,8 @@ uint16_t nextFreeUDPPort() {
|
||||
if (UDPPortIndex == 0)
|
||||
UDPPortIndex = config.udp_port_base;
|
||||
else if (UDPPortIndex == (config.udp_port_base + config.udp_port_range - 1))
|
||||
UDPPortIndex = config.udp_port_base + 3; // avoid wrapping back to the first three, as they can be assigned by resetFreeUDPPort without checking
|
||||
UDPPortIndex = config.udp_port_base + 3; // avoid wrapping back to the first three, as they can
|
||||
// be assigned by resetFreeUDPPort without checking
|
||||
else
|
||||
UDPPortIndex++;
|
||||
return UDPPortIndex;
|
||||
@@ -394,7 +395,7 @@ uint8_t *base64_dec(char *input, int *outlen) {
|
||||
|
||||
#ifdef CONFIG_OPENSSL
|
||||
char *base64_enc(uint8_t *input, int length) {
|
||||
int oldState;
|
||||
int oldState;
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
|
||||
BIO *bmem, *b64;
|
||||
BUF_MEM *bptr;
|
||||
@@ -421,7 +422,7 @@ char *base64_enc(uint8_t *input, int length) {
|
||||
}
|
||||
|
||||
uint8_t *base64_dec(char *input, int *outlen) {
|
||||
int oldState;
|
||||
int oldState;
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
|
||||
BIO *bmem, *b64;
|
||||
int inlen = strlen(input);
|
||||
@@ -479,7 +480,7 @@ static char super_secret_key[] =
|
||||
|
||||
#ifdef CONFIG_OPENSSL
|
||||
uint8_t *rsa_apply(uint8_t *input, int inlen, int *outlen, int mode) {
|
||||
int oldState;
|
||||
int oldState;
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
|
||||
RSA *rsa = NULL;
|
||||
if (!rsa) {
|
||||
@@ -794,7 +795,7 @@ void command_execute(const char *command, const char *extra_argument, const int
|
||||
void command_stop(void) {
|
||||
// this has a cancellation point if waiting is enabled
|
||||
if (config.cmd_stop)
|
||||
command_execute(config.cmd_stop,"",config.cmd_blocking);
|
||||
command_execute(config.cmd_stop, "", config.cmd_blocking);
|
||||
}
|
||||
|
||||
// this is for reading an unsigned 32 bit number, such as an RTP timestamp
|
||||
@@ -829,17 +830,17 @@ double flat_vol2attn(double vol, long max_db, long min_db) {
|
||||
|
||||
double vol2attn(double vol, long max_db, long min_db) {
|
||||
|
||||
// We use a little coordinate geometry to build a transfer function from the volume passed in to
|
||||
// the device's dynamic range. (See the diagram in the documents folder.) The x axis is the
|
||||
// "volume in" which will be from -30 to 0. The y axis will be the "volume out" which will be from
|
||||
// the bottom of the range to the top. We build the transfer function from one or more lines. We
|
||||
// characterise each line with two numbers: the first is where on x the line starts when y=0 (x
|
||||
// can be from 0 to -30); the second is where on y the line stops when when x is -30. thus, if the
|
||||
// line was characterised as {0,-30}, it would be an identity transfer. Assuming, for example, a
|
||||
// dynamic range of lv=-60 to hv=0 Typically we'll use three lines -- a three order transfer
|
||||
// function First: {0,30} giving a gentle slope -- the 30 comes from half the dynamic range
|
||||
// Second: {-5,-30-(lv+30)/2} giving a faster slope from y=0 at x=-12 to y=-42.5 at x=-30
|
||||
// Third: {-17,lv} giving a fast slope from y=0 at x=-19 to y=-60 at x=-30
|
||||
// We use a little coordinate geometry to build a transfer function from the volume passed in to
|
||||
// the device's dynamic range. (See the diagram in the documents folder.) The x axis is the
|
||||
// "volume in" which will be from -30 to 0. The y axis will be the "volume out" which will be from
|
||||
// the bottom of the range to the top. We build the transfer function from one or more lines. We
|
||||
// characterise each line with two numbers: the first is where on x the line starts when y=0 (x
|
||||
// can be from 0 to -30); the second is where on y the line stops when when x is -30. thus, if the
|
||||
// line was characterised as {0,-30}, it would be an identity transfer. Assuming, for example, a
|
||||
// dynamic range of lv=-60 to hv=0 Typically we'll use three lines -- a three order transfer
|
||||
// function First: {0,30} giving a gentle slope -- the 30 comes from half the dynamic range
|
||||
// Second: {-5,-30-(lv+30)/2} giving a faster slope from y=0 at x=-12 to y=-42.5 at x=-30
|
||||
// Third: {-17,lv} giving a fast slope from y=0 at x=-19 to y=-60 at x=-30
|
||||
|
||||
#define order 3
|
||||
|
||||
@@ -1151,8 +1152,9 @@ int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time,
|
||||
et = (et * 1000000) >> 32; // microseconds
|
||||
char errstr[1000];
|
||||
if (r == ETIMEDOUT)
|
||||
debug(debuglevel, "timed out waiting for a mutex, having waiting %f seconds, with a maximum "
|
||||
"waiting time of %d microseconds. \"%s\".",
|
||||
debug(debuglevel,
|
||||
"timed out waiting for a mutex, having waiting %f seconds, with a maximum "
|
||||
"waiting time of %d microseconds. \"%s\".",
|
||||
(1.0 * et) / 1000000, dally_time, debugmessage);
|
||||
else
|
||||
debug(debuglevel, "error %d: \"%s\" waiting for a mutex: \"%s\".", r,
|
||||
|
||||
@@ -172,15 +172,16 @@ typedef struct {
|
||||
double audio_backend_buffer_interpolation_threshold_in_seconds; // below this, soxr interpolation
|
||||
// will not occur -- it'll be
|
||||
// basic interpolation instead.
|
||||
double audio_backend_silence_threshold; // below this, silence will be added to the output buffer
|
||||
double audio_backend_silence_threshold; // below this, silence will be added to the output buffer
|
||||
double audio_backend_silence_scan_interval; // check the threshold this often
|
||||
|
||||
|
||||
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
|
||||
double audio_backend_silent_lead_in_time; // the length of the silence that should precede a play.
|
||||
double active_mode_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
|
||||
// native range.
|
||||
double active_mode_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
|
||||
// native range.
|
||||
enum sps_format_t output_format;
|
||||
enum volume_control_profile_type volume_control_profile;
|
||||
int output_rate;
|
||||
|
||||
@@ -111,7 +111,10 @@ static void response_code(void *opaque, int code) {
|
||||
}
|
||||
|
||||
static const struct http_funcs responseFuncs = {
|
||||
response_realloc, response_body, response_header, response_code,
|
||||
response_realloc,
|
||||
response_body,
|
||||
response_header,
|
||||
response_code,
|
||||
};
|
||||
|
||||
// static pthread_mutex_t dacp_conversation_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
@@ -324,9 +327,10 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) {
|
||||
// debug(1,"Sent command\"%s\" with a response body of size %d.",command,response.size);
|
||||
// debug(1,"dacp_conversation_lock released.");
|
||||
} else {
|
||||
debug(3, "dacp_send_command: could not acquire a lock on the dacp transmit/receive section "
|
||||
"when attempting to "
|
||||
"send the command \"%s\". Possible timeout?",
|
||||
debug(3,
|
||||
"dacp_send_command: could not acquire a lock on the dacp transmit/receive section "
|
||||
"when attempting to "
|
||||
"send the command \"%s\". Possible timeout?",
|
||||
command);
|
||||
response.code = 494; // This client is already busy
|
||||
}
|
||||
@@ -423,8 +427,9 @@ void set_dacp_server_information(rtsp_conn_info *conn) {
|
||||
|
||||
void dacp_monitor_port_update_callback(char *dacp_id, uint16_t port) {
|
||||
debug_mutex_lock(&dacp_server_information_lock, 500000, 2);
|
||||
debug(3, "dacp_monitor_port_update_callback with Remote ID \"%s\", target ID \"%s\" and port "
|
||||
"number %d.",
|
||||
debug(3,
|
||||
"dacp_monitor_port_update_callback with Remote ID \"%s\", target ID \"%s\" and port "
|
||||
"number %d.",
|
||||
dacp_id, dacp_server.dacp_id, port);
|
||||
if (strcmp(dacp_id, dacp_server.dacp_id) == 0) {
|
||||
dacp_server.port = port;
|
||||
@@ -472,8 +477,9 @@ 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, "setting dacp_server_active and advanced_dacp_server_active to 0 with an update "
|
||||
"flag value of %d",
|
||||
debug(2,
|
||||
"setting dacp_server_active and advanced_dacp_server_active to 0 with an update "
|
||||
"flag value of %d",
|
||||
ch);
|
||||
metadata_hub_modify_epilog(ch);
|
||||
while (dacp_server.scan_enable == 0) {
|
||||
|
||||
+3
-3
@@ -379,7 +379,7 @@ gboolean notify_verbosity_callback(ShairportSyncDiagnostics *skeleton,
|
||||
}
|
||||
|
||||
gboolean notify_disable_standby_callback(ShairportSync *skeleton,
|
||||
__attribute__((unused)) gpointer user_data) {
|
||||
__attribute__((unused)) gpointer user_data) {
|
||||
// debug(1, "\"notify_disable_standby_callback\" called.");
|
||||
if (shairport_sync_get_disable_standby(skeleton)) {
|
||||
debug(1, ">> activating disable standby");
|
||||
@@ -624,9 +624,9 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name
|
||||
G_CALLBACK(notify_alacdecoder_callback), NULL);
|
||||
g_signal_connect(shairportSyncSkeleton, "notify::volume-control-profile",
|
||||
G_CALLBACK(notify_volume_control_profile_callback), NULL);
|
||||
g_signal_connect(shairportSyncSkeleton, "notify::disable-standby",
|
||||
g_signal_connect(shairportSyncSkeleton, "notify::disable-standby",
|
||||
G_CALLBACK(notify_disable_standby_callback), NULL);
|
||||
g_signal_connect(shairportSyncSkeleton, "notify::loudness-filter-active",
|
||||
g_signal_connect(shairportSyncSkeleton, "notify::loudness-filter-active",
|
||||
G_CALLBACK(notify_loudness_filter_active_callback), NULL);
|
||||
g_signal_connect(shairportSyncSkeleton, "notify::loudness-threshold",
|
||||
G_CALLBACK(notify_loudness_threshold_callback), NULL);
|
||||
|
||||
+1
-1
@@ -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
@@ -24,8 +24,8 @@
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "mdns.h"
|
||||
#include "common.h"
|
||||
#include "mdns.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
|
||||
+1
-1
@@ -205,7 +205,7 @@ char *metadata_write_image_file(const char *buf, int len) {
|
||||
char *path = NULL; // this will be what is returned
|
||||
|
||||
uint8_t img_md5[16];
|
||||
// uint8_t ap_md5[16];
|
||||
// uint8_t ap_md5[16];
|
||||
|
||||
#ifdef CONFIG_SSL
|
||||
MD5_CTX ctx;
|
||||
|
||||
+1
-2
@@ -82,8 +82,7 @@ typedef struct metadata_bundle {
|
||||
|
||||
enum play_status_type
|
||||
player_state; // this is the state of the actual player itself, which can be a bit noisy.
|
||||
enum active_mode_type
|
||||
active_state;
|
||||
enum active_mode_type active_state;
|
||||
|
||||
int speaker_volume; // this is the actual speaker volume, allowing for the main volume and the
|
||||
// speaker volume control
|
||||
|
||||
@@ -285,16 +285,18 @@ static int alac_decode(short *dest, int *destlen, uint8_t *buf, int len, rtsp_co
|
||||
}
|
||||
|
||||
if (outsize > toutsize) {
|
||||
debug(2, "Output from alac_decode larger (%d bytes, not frames) than expected (%d bytes) -- "
|
||||
"truncated, but buffer overflow possible! Encrypted = %d.",
|
||||
debug(2,
|
||||
"Output from alac_decode larger (%d bytes, not frames) than expected (%d bytes) -- "
|
||||
"truncated, but buffer overflow possible! Encrypted = %d.",
|
||||
outsize, toutsize, conn->stream.encrypted);
|
||||
reply = -1; // output packet is the wrong size
|
||||
}
|
||||
|
||||
*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) "
|
||||
"and the audio frame size (%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;
|
||||
}
|
||||
@@ -454,9 +456,10 @@ 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
|
||||
", flushing to "
|
||||
"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);
|
||||
conn->initial_reference_time = 0;
|
||||
conn->initial_reference_timestamp = 0;
|
||||
@@ -849,9 +852,10 @@ 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
|
||||
", flushing to "
|
||||
"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);
|
||||
curframe->ready = 0;
|
||||
curframe->resend_level = 0;
|
||||
@@ -934,8 +938,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
|
||||
frame_to_local_time(conn->first_packet_timestamp + conn->latency +
|
||||
(uint32_t)(config.audio_backend_latency_offset *
|
||||
conn->input_rate), // this will go modulo 2^32
|
||||
&should_be_time,
|
||||
conn);
|
||||
&should_be_time, conn);
|
||||
|
||||
conn->first_packet_time_to_play = should_be_time;
|
||||
|
||||
@@ -956,8 +959,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
|
||||
frame_to_local_time(conn->first_packet_timestamp + conn->latency +
|
||||
(uint32_t)(config.audio_backend_latency_offset *
|
||||
conn->input_rate), // this should go modulo 2^32
|
||||
&should_be_time,
|
||||
conn);
|
||||
&should_be_time, conn);
|
||||
|
||||
conn->first_packet_time_to_play = should_be_time;
|
||||
|
||||
@@ -1028,8 +1030,9 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
|
||||
// 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.",
|
||||
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();
|
||||
@@ -1187,8 +1190,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
|
||||
(uint32_t)(config.audio_backend_latency_offset * conn->input_rate) -
|
||||
(uint32_t)(config.audio_backend_buffer_desired_length *
|
||||
conn->input_rate), // this will go modulo 2^32
|
||||
&time_to_play,
|
||||
conn);
|
||||
&time_to_play, conn);
|
||||
|
||||
if (local_time_now >= time_to_play) {
|
||||
do_wait = 0;
|
||||
@@ -1491,7 +1493,7 @@ void player_thread_cleanup_handler(void *arg) {
|
||||
}
|
||||
|
||||
void *player_thread_func(void *arg) {
|
||||
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
|
||||
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
|
||||
// pthread_cleanup_push(player_thread_initial_cleanup_handler, arg);
|
||||
conn->packet_count = 0;
|
||||
conn->packet_count_since_flush = 0;
|
||||
@@ -1667,17 +1669,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.");
|
||||
|
||||
@@ -1965,8 +1967,9 @@ 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 "
|
||||
"and ab_write: %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...
|
||||
}
|
||||
@@ -2191,7 +2194,7 @@ void *player_thread_func(void *arg) {
|
||||
#ifdef CONFIG_CONVOLUTION
|
||||
|| config.convolution
|
||||
#endif
|
||||
) {
|
||||
) {
|
||||
int32_t *tbuf32 = (int32_t *)conn->tbuf;
|
||||
float fbuf_l[inbuflength];
|
||||
float fbuf_r[inbuflength];
|
||||
@@ -2429,42 +2432,41 @@ void *player_thread_func(void *arg) {
|
||||
|
||||
if ((config.output->delay)) {
|
||||
if (config.no_sync == 0) {
|
||||
inform("%*.2f," /* Sync error in milliseconds */
|
||||
"%*.1f," /* net correction in ppm */
|
||||
"%*.1f," /* corrections in ppm */
|
||||
"%*d," /* total packets */
|
||||
"%*" PRIu64 "," /* missing packets */
|
||||
"%*" PRIu64 "," /* late packets */
|
||||
"%*" PRIu64 "," /* too late packets */
|
||||
"%*" PRIu64 "," /* resend requests */
|
||||
"%*" PRId64 "," /* min DAC queue size */
|
||||
"%*" PRId32 "," /* min buffer occupancy */
|
||||
"%*" PRId32 "," /* max buffer occupancy */
|
||||
"%*.2f," /* source nominal frame rate */
|
||||
"%*.2f," /* source actual (average) frame rate */
|
||||
"%*.2f," /* output frame rate */
|
||||
"%*.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 /
|
||||
(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->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->local_to_remote_time_gradient) *
|
||||
1000000) /
|
||||
conn->frame_rate
|
||||
: 0.0);
|
||||
inform(
|
||||
"%*.2f," /* Sync error in milliseconds */
|
||||
"%*.1f," /* net correction in ppm */
|
||||
"%*.1f," /* corrections in ppm */
|
||||
"%*d," /* total packets */
|
||||
"%*" PRIu64 "," /* missing packets */
|
||||
"%*" PRIu64 "," /* late packets */
|
||||
"%*" PRIu64 "," /* too late packets */
|
||||
"%*" PRIu64 "," /* resend requests */
|
||||
"%*" PRId64 "," /* min DAC queue size */
|
||||
"%*" PRId32 "," /* min buffer occupancy */
|
||||
"%*" PRId32 "," /* max buffer occupancy */
|
||||
"%*.2f," /* source nominal frame rate */
|
||||
"%*.2f," /* source actual (average) frame rate */
|
||||
"%*.2f," /* output frame rate */
|
||||
"%*.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 /
|
||||
(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->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->local_to_remote_time_gradient) *
|
||||
1000000) /
|
||||
conn->frame_rate
|
||||
: 0.0);
|
||||
} else {
|
||||
inform("%*.2f," /* Sync error in milliseconds */
|
||||
"%*d," /* total packets */
|
||||
@@ -2479,10 +2481,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,
|
||||
@@ -2501,10 +2502,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,
|
||||
@@ -2800,7 +2800,8 @@ int player_play(rtsp_conn_info *conn) {
|
||||
if (config.buffer_start_fill > BUFFER_FRAMES)
|
||||
die("specified buffer starting fill %d > buffer size %d", config.buffer_start_fill,
|
||||
BUFFER_FRAMES);
|
||||
activity_monitor_signify_activity(1); // active, and should be before play's command hook, command_start()
|
||||
activity_monitor_signify_activity(
|
||||
1); // active, and should be before play's command hook, command_start()
|
||||
command_start();
|
||||
pthread_t *pt = malloc(sizeof(pthread_t));
|
||||
if (pt == NULL)
|
||||
|
||||
@@ -137,8 +137,9 @@ 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 "
|
||||
"2,500 packets in microseconds: %10.1f, %10.1f, %10.1f.",
|
||||
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;
|
||||
stat_mean = 0.0;
|
||||
@@ -368,9 +369,10 @@ void *rtp_control_receiver(void *arg) {
|
||||
|
||||
if (la != conn->latency) {
|
||||
conn->latency = la;
|
||||
debug(3, "New latency detected: %" PRIu32 ", sync latency: %" PRIu32
|
||||
", minimum latency: %" PRIu32 ", maximum "
|
||||
"latency: %" PRIu32 ", fixed offset: %" 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,
|
||||
conn->maximum_latency, config.fixedLatencyOffset);
|
||||
}
|
||||
@@ -1226,8 +1228,9 @@ 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(1, "Error %d using sendto to an audio socket: \"%s\". Backing off for 1/16th of a "
|
||||
"second.",
|
||||
debug(1,
|
||||
"Error %d using sendto to an audio socket: \"%s\". Backing off for 1/16th of a "
|
||||
"second.",
|
||||
errno, em);
|
||||
conn->rtp_time_of_last_resend_request_error_fp = time_of_sending_fp;
|
||||
} else {
|
||||
|
||||
@@ -288,15 +288,16 @@ void *player_watchdog_thread_code(void *arg) {
|
||||
if (time_since_last_bark >= ct) {
|
||||
conn->watchdog_barks++;
|
||||
if (conn->watchdog_barks == 1) {
|
||||
debug(1, "Connection %d: As Yeats almost said, \"Too long a silence / can make a stone "
|
||||
"of the heart\".",
|
||||
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;
|
||||
pthread_cancel(conn->thread);
|
||||
} else if (conn->watchdog_barks == 3) {
|
||||
if ((config.cmd_unfixable) && (conn->unfixable_error_reported == 0)) {
|
||||
conn->unfixable_error_reported = 1;
|
||||
command_execute(config.cmd_unfixable, "unable_to_cancel_play_session",1);
|
||||
command_execute(config.cmd_unfixable, "unable_to_cancel_play_session", 1);
|
||||
} else {
|
||||
warn("an unrecoverable error, \"unable_to_cancel_play_session\", has been detected.",
|
||||
conn->connection_number);
|
||||
@@ -404,17 +405,17 @@ static char *nextline(char *in, int inbuf) {
|
||||
}
|
||||
|
||||
void msg_retain(rtsp_message *msg) {
|
||||
int rc = pthread_mutex_lock(&reference_counter_lock);
|
||||
if (rc)
|
||||
debug(1, "Error %d locking reference counter lock");
|
||||
if (msg > (rtsp_message *) 0x00010000) {
|
||||
int rc = pthread_mutex_lock(&reference_counter_lock);
|
||||
if (rc)
|
||||
debug(1, "Error %d locking reference counter lock");
|
||||
if (msg > (rtsp_message *)0x00010000) {
|
||||
msg->referenceCount++;
|
||||
// debug(1,"msg_retain -- item %d reference count %d.", msg->index_number, msg->referenceCount);
|
||||
rc = pthread_mutex_unlock(&reference_counter_lock);
|
||||
if (rc)
|
||||
debug(1, "Error %d unlocking reference counter lock");
|
||||
} else {
|
||||
debug(1, "invalid rtsp_message pointer 0x%x passed to retain", (uintptr_t) msg);
|
||||
debug(1, "invalid rtsp_message pointer 0x%x passed to retain", (uintptr_t)msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,13 +500,18 @@ void msg_free(rtsp_message **msgh) {
|
||||
uintptr_t index = (msg->index_number) & 0xFFFF;
|
||||
if (index == 0)
|
||||
index = 0x10000; // ensure it doesn't fold to zero.
|
||||
*msgh = (rtsp_message *)(index); // put a version of the index number of the freed message in here
|
||||
*msgh =
|
||||
(rtsp_message *)(index); // put a version of the index number of the freed message in here
|
||||
free(msg);
|
||||
} else {
|
||||
// debug(1,"msg_free item %d -- decrement reference to %d.",msg->index_number,msg->referenceCount);
|
||||
// debug(1,"msg_free item %d -- decrement reference to
|
||||
// %d.",msg->index_number,msg->referenceCount);
|
||||
}
|
||||
} else if (*msgh != NULL) {
|
||||
debug(1, "msg_free: error attempting to free an allocated but already-freed rtsp_message, number %d.",(uintptr_t)*msgh);
|
||||
debug(1,
|
||||
"msg_free: error attempting to free an allocated but already-freed rtsp_message, number "
|
||||
"%d.",
|
||||
(uintptr_t)*msgh);
|
||||
}
|
||||
debug_mutex_unlock(&reference_counter_lock, 0);
|
||||
}
|
||||
@@ -831,9 +837,10 @@ 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, "
|
||||
"PAUSE, FLUSH, TEARDOWN, "
|
||||
"OPTIONS, GET_PARAMETER, SET_PARAMETER");
|
||||
msg_add_header(resp, "Public",
|
||||
"ANNOUNCE, SETUP, RECORD, "
|
||||
"PAUSE, FLUSH, TEARDOWN, "
|
||||
"OPTIONS, GET_PARAMETER, SET_PARAMETER");
|
||||
}
|
||||
|
||||
void handle_teardown(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *req,
|
||||
@@ -1564,7 +1571,7 @@ static void handle_set_parameter(rtsp_conn_info *conn, rtsp_message *req, rtsp_m
|
||||
char *ct = msg_get_header(req, "Content-Type");
|
||||
|
||||
if (ct) {
|
||||
// debug(2, "SET_PARAMETER Content-Type:\"%s\".", ct);
|
||||
// debug(2, "SET_PARAMETER Content-Type:\"%s\".", ct);
|
||||
|
||||
#ifdef CONFIG_METADATA
|
||||
// It seems that the rtptime of the message is used as a kind of an ID that
|
||||
@@ -2009,7 +2016,6 @@ static int rtsp_auth(char **nonce, rtsp_message *req, rtsp_message *resp) {
|
||||
|
||||
uint8_t digest_urp[16], digest_mu[16], digest_total[16];
|
||||
|
||||
|
||||
#ifdef CONFIG_OPENSSL
|
||||
MD5_CTX ctx;
|
||||
|
||||
@@ -2244,8 +2250,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);
|
||||
@@ -2304,8 +2311,9 @@ 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 "
|
||||
"connection.",
|
||||
debug(1,
|
||||
"Connection %d: Unable to write an RTSP message response. Terminating the "
|
||||
"connection.",
|
||||
conn->connection_number);
|
||||
struct linger so_linger;
|
||||
so_linger.l_onoff = 1; // "true"
|
||||
@@ -2602,7 +2610,7 @@ void rtsp_listen_loop(void) {
|
||||
char errorstring[1024];
|
||||
strerror_r(ret, (char *)errorstring, sizeof(errorstring));
|
||||
die("Connection %d: cannot create an RTSP conversation thread. Error %d: \"%s\".",
|
||||
conn->connection_number, ret, (char *)errorstring);
|
||||
conn->connection_number, ret, (char *)errorstring);
|
||||
}
|
||||
debug(3, "Successfully created RTSP receiver thread %d.", conn->connection_number);
|
||||
conn->running = 1; // this must happen before the thread is tracked
|
||||
|
||||
+14
-11
@@ -59,11 +59,11 @@
|
||||
#include <glib.h>
|
||||
#endif
|
||||
|
||||
#include "activity_monitor.h"
|
||||
#include "common.h"
|
||||
#include "mdns.h"
|
||||
#include "rtp.h"
|
||||
#include "rtsp.h"
|
||||
#include "activity_monitor.h"
|
||||
|
||||
#if defined(CONFIG_DACP_CLIENT)
|
||||
#include "dacp.h"
|
||||
@@ -723,24 +723,26 @@ int parse_options(int argc, char **argv) {
|
||||
config.cmd_stop = (char *)str;
|
||||
}
|
||||
|
||||
if (config_lookup_string(config.cfg, "sessioncontrol.run_this_before_entering_active_mode", &str)) {
|
||||
if (config_lookup_string(config.cfg, "sessioncontrol.run_this_before_entering_active_mode",
|
||||
&str)) {
|
||||
config.cmd_active_start = (char *)str;
|
||||
}
|
||||
|
||||
if (config_lookup_string(config.cfg, "sessioncontrol.run_this_after_exiting_active_mode", &str)) {
|
||||
if (config_lookup_string(config.cfg, "sessioncontrol.run_this_after_exiting_active_mode",
|
||||
&str)) {
|
||||
config.cmd_active_stop = (char *)str;
|
||||
}
|
||||
|
||||
if (config_lookup_float(config.cfg, "sessioncontrol.active_mode_timeout", &dvalue)) {
|
||||
if (dvalue < 0.0)
|
||||
warn("Invalid value \"%f\" for sessioncontrol.active_mode_timeout. It must be positive. The default of %f will be used instead.",
|
||||
dvalue, config.active_mode_timeout);
|
||||
warn("Invalid value \"%f\" for sessioncontrol.active_mode_timeout. It must be positive. "
|
||||
"The default of %f will be used instead.",
|
||||
dvalue, config.active_mode_timeout);
|
||||
else
|
||||
config.active_mode_timeout = dvalue;
|
||||
}
|
||||
|
||||
|
||||
if (config_lookup_string(config.cfg,
|
||||
if (config_lookup_string(config.cfg,
|
||||
"sessioncontrol.run_this_if_an_unfixable_error_is_detected", &str)) {
|
||||
config.cmd_unfixable = (char *)str;
|
||||
}
|
||||
@@ -1181,7 +1183,7 @@ void main_cleanup_handler(__attribute__((unused)) void *arg) {
|
||||
#endif
|
||||
|
||||
activity_monitor_stop(0);
|
||||
|
||||
|
||||
if (config.output->deinit) {
|
||||
debug(1, "Deinitialise the audio backend.");
|
||||
config.output->deinit();
|
||||
@@ -1519,8 +1521,9 @@ int main(int argc, char **argv) {
|
||||
/* Print out options */
|
||||
debug(1, "log verbosity is %d.", debuglev);
|
||||
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 "
|
||||
"deliberately.",
|
||||
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);
|
||||
debug(1, "daemon status is %d.", config.daemonise);
|
||||
@@ -1599,7 +1602,7 @@ int main(int argc, char **argv) {
|
||||
uint8_t ap_md5[16];
|
||||
|
||||
#ifdef CONFIG_OPENSSL
|
||||
// can't imagine this is necessary, but here it is anyway
|
||||
// can't imagine this is necessary, but here it is anyway
|
||||
int oldState;
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
||||
MD5_CTX ctx;
|
||||
|
||||
+2
-1
@@ -1713,7 +1713,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;
|
||||
|
||||
Reference in New Issue
Block a user