#endif
NULL};
-audio_output *audio_get_output(char *name) {
+audio_output *audio_get_output(const char *name) {
audio_output **out;
// default to the first
} audio_output;
-audio_output *audio_get_output(char *name);
+audio_output *audio_get_output(const char *name);
void audio_ls_outputs(void);
void parse_general_audio_options(void);
// "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO
// open for reading."
- fd = try_to_open_pipe_for_writing(pipename);
- // we check that it's not a "real" error. From the "man 2 open" page:
- // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO
- // open for reading." Which is okay.
- if ((fd == -1) && (errno != ENXIO)) {
- char errorstring[1024];
- strerror_r(errno, (char *)errorstring, sizeof(errorstring));
- debug(1, "audio_pipe start -- error %d (\"%s\") opening pipe: \"%s\".", errno,
- (char *)errorstring, pipename);
- warn("can not open audio pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno,
- (char *)errorstring, pipename);
- }
+ fd = try_to_open_pipe_for_writing(pipename);
+ // we check that it's not a "real" error. From the "man 2 open" page:
+ // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO
+ // open for reading." Which is okay.
+ if ((fd == -1) && (errno != ENXIO)) {
+ char errorstring[1024];
+ strerror_r(errno, (char *)errorstring, sizeof(errorstring));
+ debug(1, "audio_pipe start -- error %d (\"%s\") opening pipe: \"%s\".", errno,
+ (char *)errorstring, pipename);
+ warn("can not open audio pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno,
+ (char *)errorstring, pipename);
+ }
}
static int play(void *buf, int samples) {
// if the file is not open, try to open it.
char errorstring[1024];
if (fd == -1) {
- fd = try_to_open_pipe_for_writing(pipename);
+ fd = try_to_open_pipe_for_writing(pipename);
}
// if it's got a reader, write to it.
if (fd > 0) {
- //int rc = non_blocking_write(fd, buf, samples * 4);
+ // int rc = non_blocking_write(fd, buf, samples * 4);
int rc = write(fd, buf, samples * 4);
if ((rc < 0) && (errno != EPIPE)) {
strerror_r(errno, (char *)errorstring, 1024);
- debug(1, "audio_pip play: error %d writing to the pipe named \"%s\": \"%s\".", errno, pipename, errorstring);
+ debug(1, "audio_pip play: error %d writing to the pipe named \"%s\": \"%s\".", errno,
+ pipename, errorstring);
}
}
return 0;
static void stop(void) {
// Don't close the pipe just because a play session has stopped.
-
}
static int init(int argc, char **argv) {
#include "common.h"
#include <assert.h>
#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
#include <memory.h>
#include <poll.h>
#include <popt.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
-#include <fcntl.h>
-#include <libgen.h>
#ifdef COMPILE_FOR_OSX
#include <CoreServices/CoreServices.h>
fprintf(stdout, "%s\n", s);
}
-int create_log_file(const char* path) {
- int fd = -1;
- if (path != NULL) {
- char *dirc = strdup(path);
- if (dirc) {
- char *dname = dirname(dirc);
- // create the directory, if necessary
- int result = 0;
- if (dname) {
- char *pdir = realpath(dname, NULL); // will return a NULL if the directory doesn't exist
- if (pdir == NULL) {
- mode_t oldumask = umask(000);
- result = mkpath(dname, 0777);
- umask(oldumask);
- } else {
- free(pdir);
- }
- if ((result == 0) || (result == -EEXIST)) {
- // now open the file
- fd = open(path, O_WRONLY | O_NONBLOCK | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- if ((fd == -1) && (errno == EEXIST))
- fd = open(path, O_WRONLY | O_APPEND | O_NONBLOCK);
-
- if (fd >= 0) {
- // now we switch to blocking mode
- int flags = fcntl(fd, F_GETFL);
- if (flags == -1) {
-// strerror_r(errno, (char *)errorstring, sizeof(errorstring));
-// debug(1, "create_log_file -- error %d (\"%s\") getting flags of pipe: \"%s\".", errno,
-// (char *)errorstring, pathname);
- } else {
- flags = fcntl(fd, F_SETFL,flags & ~O_NONBLOCK);
-// if (flags == -1) {
-// strerror_r(errno, (char *)errorstring, sizeof(errorstring));
-// debug(1, "create_log_file -- error %d (\"%s\") unsetting NONBLOCK of pipe: \"%s\".", errno,
-// (char *)errorstring, pathname);
- }
- }
- }
- }
- free(dirc);
- }
- }
- return fd;
+int create_log_file(const char *path) {
+ int fd = -1;
+ if (path != NULL) {
+ char *dirc = strdup(path);
+ if (dirc) {
+ char *dname = dirname(dirc);
+ // create the directory, if necessary
+ int result = 0;
+ if (dname) {
+ char *pdir = realpath(dname, NULL); // will return a NULL if the directory doesn't exist
+ if (pdir == NULL) {
+ mode_t oldumask = umask(000);
+ result = mkpath(dname, 0777);
+ umask(oldumask);
+ } else {
+ free(pdir);
+ }
+ if ((result == 0) || (result == -EEXIST)) {
+ // now open the file
+ fd = open(path, O_WRONLY | O_NONBLOCK | O_CREAT | O_EXCL,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if ((fd == -1) && (errno == EEXIST))
+ fd = open(path, O_WRONLY | O_APPEND | O_NONBLOCK);
+
+ if (fd >= 0) {
+ // now we switch to blocking mode
+ int flags = fcntl(fd, F_GETFL);
+ if (flags == -1) {
+ // strerror_r(errno, (char
+ //*)errorstring, sizeof(errorstring)); debug(1, "create_log_file -- error %d (\"%s\")
+ //getting flags of pipe: \"%s\".", errno, (char *)errorstring, pathname);
+ } else {
+ flags = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+ // if (flags == -1) {
+ // strerror_r(errno,
+ //(char *)errorstring, sizeof(errorstring)); debug(1, "create_log_file -- error %d
+ //(\"%s\") unsetting NONBLOCK of pipe: \"%s\".", errno, (char *)errorstring,
+ //pathname);
+ }
+ }
+ }
+ }
+ free(dirc);
+ }
+ }
+ return fd;
}
void do_sps_log_to_fd(__attribute__((unused)) int prio, const char *t, ...) {
- char s[1024];
- va_list args;
- va_start(args, t);
- vsnprintf(s, sizeof(s), t, args);
- va_end(args);
- if (config.log_fd == -1)
- config.log_fd = create_log_file(config.log_file_path);
- if (config.log_fd >= 0) {
- dprintf(config.log_fd, "%s\n", s);
+ char s[1024];
+ va_list args;
+ va_start(args, t);
+ vsnprintf(s, sizeof(s), t, args);
+ va_end(args);
+ if (config.log_fd == -1)
+ config.log_fd = create_log_file(config.log_file_path);
+ if (config.log_fd >= 0) {
+ dprintf(config.log_fd, "%s\n", s);
} else if (errno != ENXIO) { // maybe there is a pipe there but not hooked up
- fprintf(stderr, "%s\n", s);
+ fprintf(stderr, "%s\n", s);
}
}
void log_to_file() { sps_log = do_sps_log_to_fd; }
void log_to_syslog() {
#ifdef CONFIG_LIBDAEMON
- sps_log = daemon_log;
+ sps_log = daemon_log;
#else
- sps_log = syslog;
+ sps_log = syslog;
#endif
}
1.0 * time_since_last_debug_message / 1000000000, filename,
linenumber, " *fatal error: ");
} else {
- strncpy(b, "fatal error: ", sizeof(b));
- s = b+strlen(b);
+ strncpy(b, "fatal error: ", sizeof(b));
+ s = b + strlen(b);
}
va_list args;
va_start(args, format);
1.0 * time_since_last_debug_message / 1000000000, filename,
linenumber, " *warning: ");
} else {
- strncpy(b, "warning: ", sizeof(b));
- s = b+strlen(b);
+ strncpy(b, "warning: ", sizeof(b));
+ s = b + strlen(b);
}
va_list args;
va_start(args, format);
return time_now_ns;
}
-int try_to_open_pipe_for_writing(const char* pathname) {
- // tries to open the pipe in non-blocking mode first.
- // if it succeeds, it sets it to blocking.
- // if not, it returns -1.
+int try_to_open_pipe_for_writing(const char *pathname) {
+ // tries to open the pipe in non-blocking mode first.
+ // if it succeeds, it sets it to blocking.
+ // if not, it returns -1.
- int fdis = open(pathname, O_WRONLY | O_NONBLOCK); // open it in non blocking mode first
+ int fdis = open(pathname, O_WRONLY | O_NONBLOCK); // open it in non blocking mode first
// we check that it's not a "real" error. From the "man 2 open" page:
// "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO
// This is checked by the caller.
if (fdis >= 0) {
- // now we switch to blocking mode
- int flags = fcntl(fdis, F_GETFL);
- if (flags == -1) {
- char errorstring[1024];
- strerror_r(errno, (char *)errorstring, sizeof(errorstring));
- debug(1, "try_to_open_pipe -- error %d (\"%s\") getting flags of pipe: \"%s\".", errno,
- (char *)errorstring, pathname);
- } else {
- flags = fcntl(fdis, F_SETFL,flags & ~O_NONBLOCK);
- if (flags == -1) {
- char errorstring[1024];
- strerror_r(errno, (char *)errorstring, sizeof(errorstring));
- debug(1, "try_to_open_pipe -- error %d (\"%s\") unsetting NONBLOCK of pipe: \"%s\".", errno,
- (char *)errorstring, pathname);
- }
- }
+ // now we switch to blocking mode
+ int flags = fcntl(fdis, F_GETFL);
+ if (flags == -1) {
+ char errorstring[1024];
+ strerror_r(errno, (char *)errorstring, sizeof(errorstring));
+ debug(1, "try_to_open_pipe -- error %d (\"%s\") getting flags of pipe: \"%s\".", errno,
+ (char *)errorstring, pathname);
+ } else {
+ flags = fcntl(fdis, F_SETFL, flags & ~O_NONBLOCK);
+ if (flags == -1) {
+ char errorstring[1024];
+ strerror_r(errno, (char *)errorstring, sizeof(errorstring));
+ debug(1, "try_to_open_pipe -- error %d (\"%s\") unsetting NONBLOCK of pipe: \"%s\".", errno,
+ (char *)errorstring, pathname);
+ }
+ }
}
- return fdis;
+ return fdis;
}
/* from
}
// from https://stackoverflow.com/questions/13663617/memdup-function-in-c, with thanks
-void* memdup(const void* mem, size_t size) {
- void* out = malloc(size);
+void *memdup(const void *mem, size_t size) {
+ void *out = malloc(size);
- if(out != NULL)
- memcpy(out, mem, size);
+ if (out != NULL)
+ memcpy(out, mem, size);
- return out;
+ return out;
}
char *pidfile;
#endif
- int log_fd; // file descriptor of the file or pipe to log stuff to.
- char *log_file_path; // path to file or pipe to log to, if any
+ int log_fd; // file descriptor of the file or pipe to log stuff to.
+ char *log_file_path; // path to file or pipe to log to, if any
int logOutputLevel; // log output level
int debugger_show_elapsed_time; // in the debug message, display the time since startup
int debugger_show_relative_time; // in the debug message, display the time since the last one
int jack_soxr_resample_quality;
#endif
#endif
- void *gradients; // a linked list of the clock gradients discovered for all DACP IDs
- // can't use IP numbers as they might be given to different devices
- // can't get hold of MAC addresses.
- // can't define the nvll linked list struct here
+ void *gradients; // a linked list of the clock gradients discovered for all DACP IDs
+ // can't use IP numbers as they might be given to different devices
+ // can't get hold of MAC addresses.
+ // can't define the nvll linked list struct here
} shairport_cfg;
// accessors to config for multi-thread access
void log_to_stderr(); // call this to direct logging to stderr;
void log_to_stdout(); // call this to direct logging to stdout;
void log_to_syslog(); // call this to direct logging to the system log;
-void log_to_file(); // call this to direct logging to a file or (pre-existing) pipe;
-
-
+void log_to_file(); // call this to direct logging to a file or (pre-existing) pipe;
// true if Shairport Sync is supposed to be sending output to the output device, false otherwise
void set_requested_connection_state_to_output(int v);
-int try_to_open_pipe_for_writing(const char* pathname); // open it without blocking if it's not hooked up
+int try_to_open_pipe_for_writing(
+ const char *pathname); // open it without blocking if it's not hooked up
/* from
* http://coding.debuntu.org/c-implementing-str_replace-replace-all-occurrences-substring#comment-722
int string_update_with_size(char **str, int *flag, char *s, size_t len);
// from https://stackoverflow.com/questions/13663617/memdup-function-in-c, with thanks
-void* memdup(const void* mem, size_t size);
+void *memdup(const void *mem, size_t size);
#endif // _COMMON_H
// debug(1, "locking metadata hub for writing");
if (pthread_rwlock_trywrlock(&metadata_hub_re_lock) != 0) {
if (last_metadata_hub_modify_prolog_file)
- debug(2, "Metadata_hub write lock at \"%s:%d\" is already taken at \"%s:%d\" -- must wait.", filename, linenumber, last_metadata_hub_modify_prolog_file, last_metadata_hub_modify_prolog_line);
+ debug(2, "Metadata_hub write lock at \"%s:%d\" is already taken at \"%s:%d\" -- must wait.",
+ filename, linenumber, last_metadata_hub_modify_prolog_file,
+ last_metadata_hub_modify_prolog_line);
else
- debug(2, "Metadata_hub write lock is already taken by unknown -- must wait.");
+ debug(2, "Metadata_hub write lock is already taken by unknown -- must wait.");
metadata_hub_re_lock_access_is_delayed = 0;
pthread_rwlock_wrlock(&metadata_hub_re_lock);
debug(2, "Okay -- acquired the metadata_hub write lock at \"%s:%d\".", filename, linenumber);
} else {
- if (last_metadata_hub_modify_prolog_file) {
- free(last_metadata_hub_modify_prolog_file);
- }
- last_metadata_hub_modify_prolog_file = strdup(filename);
- last_metadata_hub_modify_prolog_line = linenumber;
+ if (last_metadata_hub_modify_prolog_file) {
+ free(last_metadata_hub_modify_prolog_file);
+ }
+ last_metadata_hub_modify_prolog_file = strdup(filename);
+ last_metadata_hub_modify_prolog_line = linenumber;
// debug(3, "Metadata_hub write lock acquired.");
}
metadata_hub_re_lock_access_is_delayed = 0;
run_metadata_watchers();
}
if (metadata_hub_re_lock_access_is_delayed) {
- if (last_metadata_hub_modify_prolog_file) {
- debug(1, "Metadata_hub write lock taken at \"%s:%d\" is freed at \"%s:%d\".", last_metadata_hub_modify_prolog_file, last_metadata_hub_modify_prolog_line, filename, linenumber);
- free(last_metadata_hub_modify_prolog_file);
- last_metadata_hub_modify_prolog_file = NULL;
- } else {
- debug(1, "Metadata_hub write lock taken at an unknown place is freed at \"%s:%d\".", filename, linenumber);
- }
+ if (last_metadata_hub_modify_prolog_file) {
+ debug(1, "Metadata_hub write lock taken at \"%s:%d\" is freed at \"%s:%d\".",
+ last_metadata_hub_modify_prolog_file, last_metadata_hub_modify_prolog_line, filename,
+ linenumber);
+ free(last_metadata_hub_modify_prolog_file);
+ last_metadata_hub_modify_prolog_file = NULL;
+ } else {
+ debug(1, "Metadata_hub write lock taken at an unknown place is freed at \"%s:%d\".", filename,
+ linenumber);
+ }
}
pthread_rwlock_unlock(&metadata_hub_re_lock);
// debug(3, "Metadata_hub write lock unlocked.");
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
- // make this uncancellable
- int oldState;
- pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
+ if ((length > 16) &&
+ (strcmp(config.cover_art_cache_dir, "") != 0)) { // if it's okay to write the file
+ // make this uncancellable
+ int oldState;
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
char *pathname = metadata_write_image_file(data, length);
snprintf(uri, sizeof(uri), "file://%s", pathname);
free(pathname);
changed = 1;
else
changed = 0;
-// pthread_cleanup_pop(0); // don't remove the lock -- it'll have been done
+ // pthread_cleanup_pop(0); // don't remove the lock -- it'll have been done
break;
case 'clip':
cs = strndup(data, length);
// these functions lock and unlock the read-write mutex on the metadata hub and run the watchers
// afterwards
void _metadata_hub_modify_prolog(const char *filename, const int linenumber);
-void _metadata_hub_modify_epilog(int modified, const char *filename, const int linenumber); // set to true if modifications occurred, 0 otherwise
+void _metadata_hub_modify_epilog(
+ int modified, const char *filename,
+ const int linenumber); // set to true if modifications occurred, 0 otherwise
/*
// these are for safe reading
*/
#define metadata_hub_modify_prolog(void) _metadata_hub_modify_prolog(__FILE__, __LINE__)
-#define metadata_hub_modify_epilog(modified) _metadata_hub_modify_epilog(modified, __FILE__, __LINE__)
+#define metadata_hub_modify_epilog(modified) \
+ _metadata_hub_modify_epilog(modified, __FILE__, __LINE__)
#define metadata_hub_read_prolog(void) _metadata_hub_read_prolog(__FILE__, __LINE__)
#define metadata_hub_read_epilog(void) _metadata_hub_modify_epilog(__FILE__, __LINE__)
conn->ab_buffering = 1;
}
-
// given starting and ending points as unsigned 16-bit integers running modulo 2^16, returns the
// position of x in the interval in *pos
// returns true if x is actually within the buffer
return response;
}
-static inline seq_t SUCCESSOR(seq_t x) {
- return x + 1;
-}
+static inline seq_t SUCCESSOR(seq_t x) { return x + 1; }
// a minus b
int16_t seq_diff(seq_t a, seq_t b) {
- int16_t response;
- seq_t diff = a - b;
- seq_t invdiff = b - a;
- if (diff < invdiff)
- response = diff;
- else
- response = -invdiff;
+ int16_t response;
+ seq_t diff = a - b;
+ seq_t invdiff = b - a;
+ if (diff < invdiff)
+ response = diff;
+ else
+ response = -invdiff;
return response;
}
-static inline seq_t seq_sum(seq_t a, seq_t b) {
- return a + b;
-}
+static inline seq_t seq_sum(seq_t a, seq_t b) { return a + b; }
// This orders u and v by picking the smaller of the two modulo differences
// in unsigned modulo arithmetic and setting the sign of the result accordingly.
// If that ever happens and there is a real ambiguity in the application,
// the modulo chosen is too small.
-
int64_t int64_mod_difference(const uint64_t u, const uint64_t v, const uint64_t modulo) {
- int64_t response;
- uint64_t diff = (u - v) % modulo;
- uint64_t invdiff = (v - u) % modulo;
- if (diff < invdiff)
- response = diff;
- else
- response = -invdiff;
- return response;
+ int64_t response;
+ uint64_t diff = (u - v) % modulo;
+ uint64_t invdiff = (v - u) % modulo;
+ if (diff < invdiff)
+ response = diff;
+ else
+ response = -invdiff;
+ return response;
}
void reset_input_flow_metrics(rtsp_conn_info *conn) {
conn->packet_count_since_flush++;
conn->time_of_last_audio_packet = time_now;
if (conn->connection_state_to_output) { // if we are supposed to be processing these packets
- abuf_t *abuf = 0;
- if (!conn->ab_synced) {
- // if this is the first packet...
- debug(3, "syncing to seqno %u.", seqno);
- conn->ab_write = seqno;
- conn->ab_read = seqno;
- conn->ab_synced = 1;
- }
- int16_t write_point_gap = seq_diff(seqno,conn->ab_write); // this is the difference between
- // the incoming packet number and the packet number that was expected.
- if (write_point_gap == 0) { // if this is the expected packet (which could be the first packet...)
- if (conn->input_frame_rate_starting_point_is_valid == 0) {
- if ((conn->packet_count_since_flush >= 500) && (conn->packet_count_since_flush <= 510)) {
- conn->frames_inward_measurement_start_time = time_now;
- conn->frames_inward_frames_received_at_measurement_start_time = actual_timestamp;
- conn->input_frame_rate_starting_point_is_valid = 1; // valid now
- }
- }
- conn->frames_inward_measurement_time = time_now;
- conn->frames_inward_frames_received_at_measurement_time = actual_timestamp;
- abuf = conn->audio_buffer + BUFIDX(seqno);
- conn->ab_write = SUCCESSOR(seqno); // move the write pointer to the next free space
- } else if (write_point_gap > 0) { // newer than expected
- // initialise the frames in between
- int i;
- for (i = 0; i < write_point_gap; i++) {
- 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->status = 1 << 0; // signifying missing
- abuf->resend_time = 0;
- abuf->given_timestamp = 0;
- abuf->sequence_number = 0;
- }
- abuf = conn->audio_buffer + BUFIDX(seqno);
- conn->ab_write = SUCCESSOR(seqno);
- } else if (seq_diff(seqno,conn->ab_read) > 0) { // older than expected but still not too late
- conn->late_packets++;
- abuf = conn->audio_buffer + BUFIDX(seqno);
- } else { // too late.
- conn->too_late_packets++;
- }
-
- if (abuf) {
- int datalen = conn->max_frames_per_packet;
- abuf->initialisation_time = time_now;
- abuf->resend_time = 0;
- if (audio_packet_decode(abuf->data, &datalen, data, len, conn) == 0) {
- abuf->ready = 1;
- abuf->status = 0; // signifying that it was received
- abuf->length = datalen;
- abuf->given_timestamp = actual_timestamp;
- abuf->sequence_number = seqno;
- } else {
- debug(1, "Bad audio packet detected and discarded.");
- abuf->ready = 0;
- abuf->status = 1 << 1; // bad packet, discarded
- abuf->resend_request_number = 0;
- abuf->given_timestamp = 0;
- abuf->sequence_number = 0;
- }
- }
-
- int rc = pthread_cond_signal(&conn->flowcontrol);
- if (rc)
- debug(1, "Error signalling flowcontrol.");
-
- // 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 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))) {
- x = first_possibly_missing_frame;
- } else {
- x = conn->ab_read;
- }
-
- first_possibly_missing_frame = -1; // has not been set
-
- int missing_frame_run_count = 0;
- int start_of_missing_frame_run = -1;
- int number_of_missing_frames = 0;
- while (x != conn->ab_write) {
- abuf_t *check_buf = conn->audio_buffer + BUFIDX(x);
- if (!check_buf->ready) {
- 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));
- 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
-
- if (too_late)
- check_buf->status |= 1 << 2; // too late
- else
- check_buf->status &= 0xFF - (1 << 2); // not too late
- if (too_early)
- check_buf->status |= 1 << 3; // too early
- else
- check_buf->status &= 0xFF - (1 << 3); // not too early
- if (too_soon_after_last_request)
- check_buf->status |= 1 << 4; // too soon after last request
- else
- check_buf->status &= 0xFF - (1 << 4); // not too soon after last request
-
- if ((!too_soon_after_last_request) && (!too_late) && (!too_early)) {
- if (start_of_missing_frame_run == -1) {
- start_of_missing_frame_run = x;
- missing_frame_run_count = 1;
- } 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_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);
- }
- // 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);
- 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(3, "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);
- debug_mutex_lock(&conn->ab_mutex, 20000, 1);
- conn->resend_requests++;
- }
- start_of_missing_frame_run = -1;
- missing_frame_run_count = 0;
- }
- }
- if (number_of_missing_frames == 0)
- first_possibly_missing_frame = conn->ab_write;
- }
+ abuf_t *abuf = 0;
+ if (!conn->ab_synced) {
+ // if this is the first packet...
+ debug(3, "syncing to seqno %u.", seqno);
+ conn->ab_write = seqno;
+ conn->ab_read = seqno;
+ conn->ab_synced = 1;
+ }
+ int16_t write_point_gap = seq_diff(seqno, conn->ab_write); // this is the difference between
+ // the incoming packet number and the packet number that was expected.
+ if (write_point_gap ==
+ 0) { // if this is the expected packet (which could be the first packet...)
+ if (conn->input_frame_rate_starting_point_is_valid == 0) {
+ if ((conn->packet_count_since_flush >= 500) && (conn->packet_count_since_flush <= 510)) {
+ conn->frames_inward_measurement_start_time = time_now;
+ conn->frames_inward_frames_received_at_measurement_start_time = actual_timestamp;
+ conn->input_frame_rate_starting_point_is_valid = 1; // valid now
+ }
+ }
+ conn->frames_inward_measurement_time = time_now;
+ conn->frames_inward_frames_received_at_measurement_time = actual_timestamp;
+ abuf = conn->audio_buffer + BUFIDX(seqno);
+ conn->ab_write = SUCCESSOR(seqno); // move the write pointer to the next free space
+ } else if (write_point_gap > 0) { // newer than expected
+ // initialise the frames in between
+ int i;
+ for (i = 0; i < write_point_gap; i++) {
+ 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->status = 1 << 0; // signifying missing
+ abuf->resend_time = 0;
+ abuf->given_timestamp = 0;
+ abuf->sequence_number = 0;
+ }
+ abuf = conn->audio_buffer + BUFIDX(seqno);
+ conn->ab_write = SUCCESSOR(seqno);
+ } else if (seq_diff(seqno, conn->ab_read) > 0) { // older than expected but still not too late
+ conn->late_packets++;
+ abuf = conn->audio_buffer + BUFIDX(seqno);
+ } else { // too late.
+ conn->too_late_packets++;
+ }
+
+ if (abuf) {
+ int datalen = conn->max_frames_per_packet;
+ abuf->initialisation_time = time_now;
+ abuf->resend_time = 0;
+ if (audio_packet_decode(abuf->data, &datalen, data, len, conn) == 0) {
+ abuf->ready = 1;
+ abuf->status = 0; // signifying that it was received
+ abuf->length = datalen;
+ abuf->given_timestamp = actual_timestamp;
+ abuf->sequence_number = seqno;
+ } else {
+ debug(1, "Bad audio packet detected and discarded.");
+ abuf->ready = 0;
+ abuf->status = 1 << 1; // bad packet, discarded
+ abuf->resend_request_number = 0;
+ abuf->given_timestamp = 0;
+ abuf->sequence_number = 0;
+ }
+ }
+
+ int rc = pthread_cond_signal(&conn->flowcontrol);
+ if (rc)
+ debug(1, "Error signalling flowcontrol.");
+
+ // 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 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))) {
+ x = first_possibly_missing_frame;
+ } else {
+ x = conn->ab_read;
+ }
+
+ first_possibly_missing_frame = -1; // has not been set
+
+ int missing_frame_run_count = 0;
+ int start_of_missing_frame_run = -1;
+ int number_of_missing_frames = 0;
+ while (x != conn->ab_write) {
+ abuf_t *check_buf = conn->audio_buffer + BUFIDX(x);
+ if (!check_buf->ready) {
+ 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));
+ 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
+
+ if (too_late)
+ check_buf->status |= 1 << 2; // too late
+ else
+ check_buf->status &= 0xFF - (1 << 2); // not too late
+ if (too_early)
+ check_buf->status |= 1 << 3; // too early
+ else
+ check_buf->status &= 0xFF - (1 << 3); // not too early
+ if (too_soon_after_last_request)
+ check_buf->status |= 1 << 4; // too soon after last request
+ else
+ check_buf->status &= 0xFF - (1 << 4); // not too soon after last request
+
+ if ((!too_soon_after_last_request) && (!too_late) && (!too_early)) {
+ if (start_of_missing_frame_run == -1) {
+ start_of_missing_frame_run = x;
+ missing_frame_run_count = 1;
+ } 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_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);
+ }
+ // 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);
+ 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(3, "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);
+ debug_mutex_lock(&conn->ab_mutex, 20000, 1);
+ conn->resend_requests++;
+ }
+ start_of_missing_frame_run = -1;
+ missing_frame_run_count = 0;
+ }
+ }
+ if (number_of_missing_frames == 0)
+ first_possibly_missing_frame = conn->ab_write;
+ }
}
debug_mutex_unlock(&conn->ab_mutex, 0);
}
debug_mutex_lock(&conn->flush_mutex, 1000, 0);
if (conn->flush_requested == 1) {
- if (conn->flush_output_flushed == 0)
- if (config.output->flush) {
- config.output->flush(); // no cancellation points
- debug(2, "flush request: flush output device.");
+ if (conn->flush_output_flushed == 0)
+ if (config.output->flush) {
+ config.output->flush(); // no cancellation points
+ debug(2, "flush request: flush output device.");
}
conn->flush_output_flushed = 1;
}
- // now check to see it the flush request is for frames in the buffer or not
- // if the first_packet_timestamp is zero, don't check
- int flush_needed = 0;
- int drop_request = 0;
- if ((conn->flush_requested == 1) && (conn->flush_rtp_timestamp == 0)) {
- debug(1, "flush request: flush frame 0 -- flush assumed to be needed.");
- flush_needed = 1;
- drop_request = 1;
- } else if (conn->flush_rtp_timestamp != 0) {
- if ((conn->ab_synced) && ((conn->ab_write - conn->ab_read) > 0)) {
- abuf_t *firstPacket = conn->audio_buffer + BUFIDX(conn->ab_read);
- abuf_t *lastPacket = conn->audio_buffer + BUFIDX(conn->ab_write - 1);
- if ((firstPacket != NULL) && (firstPacket->ready)) {
- // discard flushes more than 10 seconds into the future -- they are probably bogus
- uint32_t first_frame_in_buffer = firstPacket->given_timestamp;
- int32_t offset_from_first_frame = (int32_t)(conn->flush_rtp_timestamp - first_frame_in_buffer);
- if (offset_from_first_frame > (int)conn->input_rate * 10) {
- debug(1, "flush request: sanity check -- flush frame %u is too far into the future from the first frame %u -- discarded.", conn->flush_rtp_timestamp, first_frame_in_buffer);
- drop_request = 1;
- } else {
- if ((lastPacket != NULL) && (lastPacket->ready)) {
- // we have enough information to check if the flush is needed or can be discarded
- uint32_t last_frame_in_buffer = lastPacket->given_timestamp + lastPacket->length - 1;
- // now we have to work out if the flush frame is in the buffer
- // if it is later than the end of the buffer, flush everything and keep the request active.
- // if it is in the buffer, we need to flush part of the buffer. Actually we flush the entire buffer and drop the request.
- // if it is before the buffer, no flush is needed. Drop the request.
- if (offset_from_first_frame > 0) {
- int32_t offset_to_last_frame = (int32_t)(last_frame_in_buffer - conn->flush_rtp_timestamp);
- if (offset_to_last_frame >= 0) {
- debug(2,"flush request: flush frame %u active -- buffer contains %u frames, from %u to %u", conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer, last_frame_in_buffer);
- drop_request = 1;
- flush_needed = 1;
- } else {
- debug(2,"flush request: flush frame %u pending -- buffer contains %u frames, from %u to %u", conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer, last_frame_in_buffer);
- flush_needed = 1;
- }
- } else {
- debug(2,"flush request: flush frame %u expired -- buffer contains %u frames, from %u to %u", conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer, last_frame_in_buffer);
- drop_request = 1;
- }
- }
- }
- }
- } else {
- debug(3, "flush request: flush frame %u -- buffer not synced or empty: synced: %d, ab_read: %u, ab_write: %u", conn->flush_rtp_timestamp, conn->ab_synced, conn->ab_read, conn->ab_write);
- // leave flush request pending and don't do a buffer flush, because there isn't one
- }
- }
- if (flush_needed) {
- debug(2, "flush request: flush done.");
- ab_resync(conn); // no cancellation points
- conn->first_packet_timestamp = 0;
- conn->first_packet_time_to_play = 0;
- conn->time_since_play_started = 0;
- have_sent_prefiller_silence = 0;
- dac_delay = 0;
- }
- if (drop_request) {
- debug(2, "flush request: request dropped.");
- conn->flush_requested = 0;
- conn->flush_rtp_timestamp = 0;
- conn->flush_output_flushed = 0;
- }
+ // now check to see it the flush request is for frames in the buffer or not
+ // if the first_packet_timestamp is zero, don't check
+ int flush_needed = 0;
+ int drop_request = 0;
+ if ((conn->flush_requested == 1) && (conn->flush_rtp_timestamp == 0)) {
+ debug(1, "flush request: flush frame 0 -- flush assumed to be needed.");
+ flush_needed = 1;
+ drop_request = 1;
+ } else if (conn->flush_rtp_timestamp != 0) {
+ if ((conn->ab_synced) && ((conn->ab_write - conn->ab_read) > 0)) {
+ abuf_t *firstPacket = conn->audio_buffer + BUFIDX(conn->ab_read);
+ abuf_t *lastPacket = conn->audio_buffer + BUFIDX(conn->ab_write - 1);
+ if ((firstPacket != NULL) && (firstPacket->ready)) {
+ // discard flushes more than 10 seconds into the future -- they are probably bogus
+ uint32_t first_frame_in_buffer = firstPacket->given_timestamp;
+ int32_t offset_from_first_frame =
+ (int32_t)(conn->flush_rtp_timestamp - first_frame_in_buffer);
+ if (offset_from_first_frame > (int)conn->input_rate * 10) {
+ debug(1,
+ "flush request: sanity check -- flush frame %u is too far into the future from "
+ "the first frame %u -- discarded.",
+ conn->flush_rtp_timestamp, first_frame_in_buffer);
+ drop_request = 1;
+ } else {
+ if ((lastPacket != NULL) && (lastPacket->ready)) {
+ // we have enough information to check if the flush is needed or can be discarded
+ uint32_t last_frame_in_buffer = lastPacket->given_timestamp + lastPacket->length - 1;
+ // now we have to work out if the flush frame is in the buffer
+ // if it is later than the end of the buffer, flush everything and keep the request
+ // active. if it is in the buffer, we need to flush part of the buffer. Actually we
+ // flush the entire buffer and drop the request. if it is before the buffer, no flush
+ // is needed. Drop the request.
+ if (offset_from_first_frame > 0) {
+ int32_t offset_to_last_frame =
+ (int32_t)(last_frame_in_buffer - conn->flush_rtp_timestamp);
+ if (offset_to_last_frame >= 0) {
+ debug(2,
+ "flush request: flush frame %u active -- buffer contains %u frames, from "
+ "%u to %u",
+ conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1,
+ first_frame_in_buffer, last_frame_in_buffer);
+ drop_request = 1;
+ flush_needed = 1;
+ } else {
+ debug(2,
+ "flush request: flush frame %u pending -- buffer contains %u frames, from "
+ "%u to %u",
+ conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1,
+ first_frame_in_buffer, last_frame_in_buffer);
+ flush_needed = 1;
+ }
+ } else {
+ debug(2,
+ "flush request: flush frame %u expired -- buffer contains %u frames, from %u "
+ "to %u",
+ conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1,
+ first_frame_in_buffer, last_frame_in_buffer);
+ drop_request = 1;
+ }
+ }
+ }
+ }
+ } else {
+ debug(3,
+ "flush request: flush frame %u -- buffer not synced or empty: synced: %d, ab_read: "
+ "%u, ab_write: %u",
+ conn->flush_rtp_timestamp, conn->ab_synced, conn->ab_read, conn->ab_write);
+ // leave flush request pending and don't do a buffer flush, because there isn't one
+ }
+ }
+ if (flush_needed) {
+ debug(2, "flush request: flush done.");
+ ab_resync(conn); // no cancellation points
+ conn->first_packet_timestamp = 0;
+ conn->first_packet_time_to_play = 0;
+ conn->time_since_play_started = 0;
+ have_sent_prefiller_silence = 0;
+ dac_delay = 0;
+ }
+ if (drop_request) {
+ debug(2, "flush request: request dropped.");
+ conn->flush_requested = 0;
+ conn->flush_rtp_timestamp = 0;
+ conn->flush_output_flushed = 0;
+ }
debug_mutex_unlock(&conn->flush_mutex, 0);
if (conn->ab_synced) {
curframe = conn->audio_buffer + BUFIDX(conn->ab_read);
}
}
-
if ((curframe) && (curframe->ready)) {
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
// Here, calculate when we should start playing. We need to know when to allow the
// packets to be sent to the player.
-
// every second or so, we get a reference on when a particular packet should be
// played.
&should_be_time, conn);
conn->first_packet_time_to_play = should_be_time;
- debug(2,"first_packet_time set for frame %u.", conn->first_packet_timestamp);
-
+ debug(2, "first_packet_time set for frame %u.", conn->first_packet_timestamp);
if (local_time_now > conn->first_packet_time_to_play) {
uint64_t lateness = local_time_now - conn->first_packet_time_to_play;
}
}
+ if (conn->first_packet_time_to_play != 0) {
+ // Now that we know the timing of the first packet...
+ if (config.output->delay) {
+ // and that the output device is capable of synchronization...
+ // We may send packets of
+ // silence from now until the time the first audio packet should be sent
+ // and then we will send the first packet, which will be followed by
+ // the subsequent packets.
+ // here, we figure out whether and what silence to send.
- if (conn->first_packet_time_to_play != 0) {
- // Now that we know the timing of the first packet...
- if (config.output->delay) {
- // and that the output device is capable of synchronization...
-
- // We may send packets of
- // silence from now until the time the first audio packet should be sent
- // and then we will send the first packet, which will be followed by
- // the subsequent packets.
- // here, we figure out whether and what silence to send.
-
- 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)) {
- 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);
- config.audio_backend_latency_offset = 0; // set it to zero
- conn->unachievable_audio_backend_latency_offset_notified = 1;
- };
- 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);
- config.audio_backend_latency_offset = 0; // set it to zero;
- conn->unachievable_audio_backend_latency_offset_notified = 1;
- };
- break;
- default:
- break;
- }
-
- // readjust first packet time to play
- frame_to_local_time(conn->first_packet_timestamp +
- effective_latency, // this will go modulo 2^32
- &should_be_time, conn);
-
- int64_t change_in_should_be_time = (int64_t)(should_be_time - conn->first_packet_time_to_play);
-
- if (fabs(0.000001*change_in_should_be_time) > 0.001) // the clock drift estimation might be nudging the estimate, and we can ignore this unless if's more than a microsecond
- debug(2,"Change in estimated first_packet_time: %8.4f milliseconds.", 0.000001*change_in_should_be_time);
-
- 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(2, "Gone past starting time by %" PRIu64 " nanoseconds.", lateness);
- conn->ab_buffering = 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 " nanoseconds.", lead_time);
- int resp = 0;
- dac_delay = 0;
- if (have_sent_prefiller_silence != 0)
- resp = config.output->delay(&dac_delay); // we know the output device must have a delay function
- if (resp == 0) {
- int64_t gross_frame_gap =
- ((conn->first_packet_time_to_play - local_time_now) * config.output_rate) /
- 1000000000;
- int64_t exact_frame_gap = gross_frame_gap - dac_delay;
- 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;
-
- // 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 we are very close to the end of buffering, i.e. within two frame-lengths,
- // add the remaining silence needed and end buffering
- if (exact_frame_gap <= conn->max_frames_per_packet * 2) {
- fs = exact_frame_gap;
- 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) {
- silence = malloc(conn->output_bytes_per_frame * fs);
- if (silence == NULL)
- debug(1, "Failed to allocate %d byte silence buffer.", fs);
- else {
- // 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);
- free(silence);
- have_sent_prefiller_silence = 1;
- }
- }
- } else {
-
- 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.",
- conn->connection_number);
- }
- }
- } else {
- debug(2, "Unexpected response to getting dac delay: %d.", resp);
- }
- }
- }
- }
- } else {
- // if the output device doesn't have a delay, we simply send the lead-in
- int64_t lead_time = conn->first_packet_time_to_play - local_time_now; // negative if we are late
- void *silence;
- int64_t frame_gap = (lead_time * config.output_rate) / 1000000000;
- // debug(1,"%d frames needed.",frame_gap);
- while (frame_gap > 0) {
- ssize_t fs = config.output_rate / 10;
- if (fs > frame_gap)
- fs = frame_gap;
-
- silence = malloc(conn->output_bytes_per_frame * fs);
- if (silence == NULL)
- debug(1, "Failed to allocate %d frame silence buffer.", fs);
- else {
- // debug(1, "No delay function -- outputting %d frames of silence.", fs);
- conn->previous_random_number =
- generate_zero_frames(silence, fs, config.output_format,
- conn->enable_dither, conn->previous_random_number);
- config.output->play(silence, fs);
- free(silence);
- }
- frame_gap -= fs;
- }
- conn->ab_buffering = 0;
- }
- }
+ 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)) {
+ 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);
+ config.audio_backend_latency_offset = 0; // set it to zero
+ conn->unachievable_audio_backend_latency_offset_notified = 1;
+ };
+ 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);
+ config.audio_backend_latency_offset = 0; // set it to zero;
+ conn->unachievable_audio_backend_latency_offset_notified = 1;
+ };
+ break;
+ default:
+ break;
+ }
+
+ // readjust first packet time to play
+ frame_to_local_time(conn->first_packet_timestamp +
+ effective_latency, // this will go modulo 2^32
+ &should_be_time, conn);
+
+ int64_t change_in_should_be_time =
+ (int64_t)(should_be_time - conn->first_packet_time_to_play);
+
+ if (fabs(0.000001 * change_in_should_be_time) >
+ 0.001) // the clock drift estimation might be nudging the estimate, and we can
+ // ignore this unless if's more than a microsecond
+ debug(2, "Change in estimated first_packet_time: %8.4f milliseconds.",
+ 0.000001 * change_in_should_be_time);
+
+ 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(2, "Gone past starting time by %" PRIu64 " nanoseconds.", lateness);
+ conn->ab_buffering = 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 " nanoseconds.", lead_time);
+ int resp = 0;
+ dac_delay = 0;
+ if (have_sent_prefiller_silence != 0)
+ resp = config.output->delay(
+ &dac_delay); // we know the output device must have a delay function
+ if (resp == 0) {
+ int64_t gross_frame_gap =
+ ((conn->first_packet_time_to_play - local_time_now) * config.output_rate) /
+ 1000000000;
+ int64_t exact_frame_gap = gross_frame_gap - dac_delay;
+ 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;
+
+ // 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 we are very close to the end of buffering, i.e. within two frame-lengths,
+ // add the remaining silence needed and end buffering
+ if (exact_frame_gap <= conn->max_frames_per_packet * 2) {
+ fs = exact_frame_gap;
+ 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) {
+ silence = malloc(conn->output_bytes_per_frame * fs);
+ if (silence == NULL)
+ debug(1, "Failed to allocate %d byte silence buffer.", fs);
+ else {
+ // 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);
+ free(silence);
+ have_sent_prefiller_silence = 1;
+ }
+ }
+ } else {
+
+ 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.",
+ conn->connection_number);
+ }
+ }
+ } else {
+ debug(2, "Unexpected response to getting dac delay: %d.", resp);
+ }
+ }
+ }
+ }
+ } else {
+ // if the output device doesn't have a delay, we simply send the lead-in
+ int64_t lead_time =
+ conn->first_packet_time_to_play - local_time_now; // negative if we are late
+ void *silence;
+ int64_t frame_gap = (lead_time * config.output_rate) / 1000000000;
+ // debug(1,"%d frames needed.",frame_gap);
+ while (frame_gap > 0) {
+ ssize_t fs = config.output_rate / 10;
+ if (fs > frame_gap)
+ fs = frame_gap;
+
+ silence = malloc(conn->output_bytes_per_frame * fs);
+ if (silence == NULL)
+ debug(1, "Failed to allocate %d frame silence buffer.", fs);
+ else {
+ // debug(1, "No delay function -- outputting %d frames of silence.", fs);
+ conn->previous_random_number =
+ generate_zero_frames(silence, fs, config.output_format, conn->enable_dither,
+ conn->previous_random_number);
+ config.output->play(silence, fs);
+ free(silence);
+ }
+ frame_gap -= fs;
+ }
+ conn->ab_buffering = 0;
+ }
+ }
#ifdef CONFIG_METADATA
- if (conn->ab_buffering == 0) {
- debug(2, "prsm");
- send_ssnc_metadata('prsm', NULL, 0,
- 0); // "resume", but don't wait if the queue is locked
- }
-#endif
+ if (conn->ab_buffering == 0) {
+ debug(2, "prsm");
+ send_ssnc_metadata('prsm', NULL, 0,
+ 0); // "resume", but don't wait if the queue is locked
}
+#endif
}
}
+ }
// Here, we work out whether to release a packet or wait
// We release a packet when the time is right.
}
if (conn->statistics) {
- free(conn->statistics);
- conn->statistics = NULL;
+ free(conn->statistics);
+ conn->statistics = NULL;
}
free_audio_buffers(conn);
if (conn->stream.type == ast_apple_lossless)
conn->first_packet_timestamp = 0;
conn->flush_requested = 0;
conn->flush_output_flushed = 0; // only send a flush command to the output device once
- conn->flush_rtp_timestamp = 0; // it seems this number has a special significance -- it seems to
- // be used as a null operand, so we'll use it like that too
+ conn->flush_rtp_timestamp = 0; // it seems this number has a special significance -- it seems to
+ // be used as a null operand, so we'll use it like that too
conn->fix_volume = 0x10000;
if (conn->latency == 0) {
int sync_error_out_of_bounds =
0; // number of times in a row that there's been a serious sync error
- conn->statistics = malloc(sizeof(stats_t)*trend_interval);
+ conn->statistics = malloc(sizeof(stats_t) * trend_interval);
if (conn->statistics == NULL)
- die("Failed to allocate a statistics buffer");
+ die("Failed to allocate a statistics buffer");
conn->framesProcessedInThisEpoch = 0;
conn->framesGeneratedInThisEpoch = 0;
"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); // manage the packet out of sequence minder
+ conn->last_seqno_read =
+ SUCCESSOR(conn->last_seqno_read); // manage the packet out of sequence minder
void *silence = malloc(conn->output_bytes_per_frame * conn->max_frames_per_packet *
conn->output_sample_ratio);
die("Shairport Sync only supports 16 bit input");
}
-
- at_least_one_frame_seen = 1;
+ at_least_one_frame_seen = 1;
// We have a frame of data. We need to see if we want to add or remove a frame from it to
// keep in sync.
}
}
- conn->buffer_occupancy =
- seq_diff(conn->ab_write, conn->ab_read); // int32_t from int16_t
+ conn->buffer_occupancy = seq_diff(conn->ab_write, conn->ab_read); // int32_t from int16_t
if (conn->buffer_occupancy < minimum_buffer_occupancy)
minimum_buffer_occupancy = conn->buffer_occupancy;
if (conn->buffer_occupancy > maximum_buffer_occupancy)
maximum_buffer_occupancy = conn->buffer_occupancy;
-
-
-
// here, we want to check (a) if we are meant to do synchronisation,
// (b) if we have a delay procedure, (c) if we can get the delay.
if (resp == 0) { // no error
current_delay = l_delay;
if (l_delay >= 0)
- current_delay = l_delay;
+ current_delay = l_delay;
else {
debug(2, "Underrun of %ld frames reported, but ignored.", l_delay);
current_delay =
local_time_to_frame(local_time_now, &should_be_frame_32, conn);
// int64_t should_be_frame = ((int64_t)should_be_frame_32) * conn->output_sample_ratio;
- int64_t delay = int64_mod_difference(should_be_frame_32 * conn->output_sample_ratio, nt - current_delay, UINT32_MAX * conn->output_sample_ratio);
+ int64_t delay =
+ int64_mod_difference(should_be_frame_32 * conn->output_sample_ratio,
+ nt - current_delay, UINT32_MAX * conn->output_sample_ratio);
- //int64_t delay = should_be_frame - (nt - current_delay); // all int64_t
+ // int64_t delay = should_be_frame - (nt - current_delay); // all int64_t
// the original frame numbers are unsigned 32-bit integers that roll over modulo 2^32
- // hence these delay figures will be unsigned numbers that roll over modulo 2^32 * conn->output_sample_ratio;
- // therefore, calculating the delay must be done in the light of possible rollover
-
-
+ // hence these delay figures will be unsigned numbers that roll over modulo 2^32 *
+ // conn->output_sample_ratio; therefore, calculating the delay must be done in the light
+ // of possible rollover
sync_error =
delay - ((int64_t)conn->latency * conn->output_sample_ratio +
config.output_rate)); // int64_t from int64_t - int32_t, so okay
if (at_least_one_frame_seen_this_session == 0) {
- at_least_one_frame_seen_this_session = 1;
+ at_least_one_frame_seen_this_session = 1;
- // debug(2,"first frame real sync error (positive --> late): %" PRId64 " frames.", sync_error);
+ // 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
+ // 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.
+ // 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);
+ // 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
+ // 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);
+ 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);
+ 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: %" PRId64 " silent frames added with a bias of %" PRId64 " frames.", -sync_error, first_frame_early_bias);
+ debug(2,
+ "final sync adjustment: %" PRId64
+ " silent frames added with a bias of %" PRId64 " frames.",
+ -sync_error, first_frame_early_bias);
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 "
+ 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);
}
// if there is no delay procedure, then we should be sending the packet
// to the output at the time determined by
// the packet's time to play + requested latency + requested offset.
-/*
- // This is just for checking during development
+ /*
+ // This is just for checking during
+ development
- uint32_t should_be_frame_32;
- local_time_to_frame(local_time_now, &should_be_frame_32, conn);
- // int64_t should_be_frame = ((int64_t)should_be_frame_32) * conn->output_sample_ratio;
+ uint32_t should_be_frame_32;
+ local_time_to_frame(local_time_now, &should_be_frame_32, conn);
+ // int64_t should_be_frame = ((int64_t)should_be_frame_32) *
+ conn->output_sample_ratio;
- int32_t ilatency = (int32_t)((config.audio_backend_latency_offset - config.audio_backend_buffer_desired_length) * conn->input_rate) + conn->latency;
- if (ilatency < 0)
- debug(1,"incorrect latency %d.", ilatency);
+ int32_t ilatency = (int32_t)((config.audio_backend_latency_offset -
+ config.audio_backend_buffer_desired_length) * conn->input_rate) + conn->latency; if
+ (ilatency < 0) debug(1,"incorrect latency %d.", ilatency);
- int32_t idelay = (int32_t)(should_be_frame_32 - inframe->given_timestamp);
+ int32_t idelay = (int32_t)(should_be_frame_32 - inframe->given_timestamp);
- idelay = idelay - ilatency;
+ idelay = idelay - ilatency;
- debug(2,"delay is %d input frames.", idelay);
-*/
+ debug(2,"delay is %d input frames.", idelay);
+ */
// if this is the first frame, see if it's close to when it's supposed to be
// release, which will be its time plus latency and any offset_time
if (at_least_one_frame_seen_this_session == 0) {
- at_least_one_frame_seen_this_session = 1;
-
-
+ at_least_one_frame_seen_this_session = 1;
}
play_samples =
"%*.2f," /* source actual (average) frame rate */
"%*.2f," /* source clock drift */
"%*d", /* source clock drift sample count */
- 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,
+ 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,
conn->local_to_remote_time_gradient_sample_count);
}
inform("No frames received in the last sampling interval.");
}
}
- minimum_dac_queue_size = UINT64_MAX; // hack reset
+ minimum_dac_queue_size = UINT64_MAX; // hack reset
maximum_buffer_occupancy = INT32_MIN; // can't be less than this
minimum_buffer_occupancy = INT32_MAX; // can't be more than this
at_least_one_frame_seen = 0;
// here, send the 'pvol' metadata message when the airplay volume information
// is being used by shairport sync to control the output volume
char dv[128];
- 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,
- (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,
- min_db / 100.0, max_db / 100.0);
- }
- send_ssnc_metadata('pvol', dv, strlen(dv), 1);
+ 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,
+ (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,
+ min_db / 100.0, max_db / 100.0);
+ }
+ send_ssnc_metadata('pvol', dv, strlen(dv), 1);
#endif
if (config.output->mute)
else {
// here, send the 'pvol' metadata message when the airplay volume information
// is being used by shairport sync to control the output volume
- char dv[128];
- memset(dv, 0, 128);
- snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, 0.0, 0.0, 0.0);
- send_ssnc_metadata('pvol', dv, strlen(dv), 1);
+ char dv[128];
+ memset(dv, 0, 128);
+ snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, 0.0, 0.0, 0.0);
+ send_ssnc_metadata('pvol', dv, strlen(dv), 1);
}
#endif
debug(3, "player_flush");
do_flush(timestamp, conn);
#ifdef CONFIG_METADATA
- debug(2, "pfls");
- char numbuf[32];
- snprintf(numbuf, sizeof(numbuf),"%u",timestamp);
- send_ssnc_metadata('pfls', numbuf, strlen(numbuf), 1); // contains cancellation points
+ debug(2, "pfls");
+ char numbuf[32];
+ snprintf(numbuf, sizeof(numbuf), "%u", timestamp);
+ send_ssnc_metadata('pfls', numbuf, strlen(numbuf), 1); // contains cancellation points
#endif
}
#include "audio.h"
#define time_ping_history_power_of_two 7
-#define time_ping_history (1 << time_ping_history_power_of_two) // 2^7 is 128. At 1 per three seconds, approximately six minutes of records
+#define time_ping_history \
+ (1 << time_ping_history_power_of_two) // 2^7 is 128. At 1 per three seconds, approximately six
+ // minutes of records
typedef struct time_ping_record {
uint64_t dispersion;
#include <unistd.h>
struct Nvll {
- char* name;
- double value;
- struct Nvll *next;
+ char *name;
+ double value;
+ struct Nvll *next;
};
typedef struct Nvll nvll;
obfp += 2;
};
*obfp = 0;
-
-
+
+
// get raw timestamp information
// I think that a good way to understand these timestamps is that
// (1) the rtlt below is the timestamp of the frame that should be playing at the
// Thus, (3) the latency can be calculated by subtracting the second from the
// first.
// There must be more to it -- there something missing.
-
+
// In addition, it seems that if the value of the short represented by the second
// pair of bytes in the packet is 7
// then an extra time lag is expected to be added, presumably by
// the AirPort Express.
-
+
// Best guess is that this delay is 11,025 frames.
-
+
uint32_t rtlt = nctohl(&packet[4]); // raw timestamp less latency
uint32_t rt = nctohl(&packet[16]); // raw timestamp
-
+
uint32_t fl = nctohs(&packet[2]); //
-
+
debug(1,"Sync Packet of %d bytes received: \"%s\", flags: %d, timestamps %u and %u,
giving a latency of %d frames.",plen,obf,fl,rt,rtlt,rt-rtlt);
//debug(1,"Monotonic timestamps are: %" PRId64 " and %" PRId64 "
// walk down the list of DACP / gradient pairs, if any
nvll *gradients = config.gradients;
if (conn->dacp_id)
- while ((gradients) && (strcasecmp((const char *)&conn->client_ip_string,gradients->name) != 0))
- gradients = gradients->next;
+ while ((gradients) && (strcasecmp((const char *)&conn->client_ip_string, gradients->name) != 0))
+ gradients = gradients->next;
- // if gradients comes out of this non-null, it is pointing to the DACP and it's last-known gradient
+ // if gradients comes out of this non-null, it is pointing to the DACP and it's last-known
+ // gradient
if (gradients) {
- gradients->value = conn->local_to_remote_time_gradient;
- // debug(1,"Updating a drift of %.2f ppm for \"%s\".", (conn->local_to_remote_time_gradient - 1.0)*1000000, gradients->name);
+ gradients->value = conn->local_to_remote_time_gradient;
+ // debug(1,"Updating a drift of %.2f ppm for \"%s\".", (conn->local_to_remote_time_gradient
+ // - 1.0)*1000000, gradients->name);
} else {
- nvll *new_entry = (nvll*)malloc(sizeof(nvll));
- if (new_entry) {
- new_entry->name = strdup((const char *)&conn->client_ip_string);
- new_entry->value = conn->local_to_remote_time_gradient;
- new_entry->next = config.gradients;
- config.gradients = new_entry;
- // debug(1,"Setting a new drift of %.2f ppm for \"%s\".", (conn->local_to_remote_time_gradient - 1.0)*1000000, new_entry->name);
- }
+ nvll *new_entry = (nvll *)malloc(sizeof(nvll));
+ if (new_entry) {
+ new_entry->name = strdup((const char *)&conn->client_ip_string);
+ new_entry->value = conn->local_to_remote_time_gradient;
+ new_entry->next = config.gradients;
+ config.gradients = new_entry;
+ // debug(1,"Setting a new drift of %.2f ppm for \"%s\".", (conn->local_to_remote_time_gradient
+ // - 1.0)*1000000, new_entry->name);
+ }
}
debug(3, "Cancel Timing Requester.");
conn->local_to_remote_time_gradient = 1.0; // initial value.
// walk down the list of DACP / gradient pairs, if any
nvll *gradients = config.gradients;
- while ((gradients) && (strcasecmp((const char *)&conn->client_ip_string,gradients->name) != 0))
- gradients = gradients->next;
+ while ((gradients) && (strcasecmp((const char *)&conn->client_ip_string, gradients->name) != 0))
+ gradients = gradients->next;
// if gradients comes out of this non-null, it is pointing to the IP and it's last-known gradient
if (gradients) {
- conn->local_to_remote_time_gradient = gradients->value;
- // debug(1,"Using a stored drift of %.2f ppm for \"%s\".", (conn->local_to_remote_time_gradient - 1.0)*1000000, gradients->name);
+ conn->local_to_remote_time_gradient = gradients->value;
+ // debug(1,"Using a stored drift of %.2f ppm for \"%s\".", (conn->local_to_remote_time_gradient
+ // - 1.0)*1000000, gradients->name);
}
// calculate diffusion factor
// be the nth root of diffusion_expansion_factor
// where n is the number of elements in the array
- const double diffusion_expansion_factor = 10;
- double log_of_multiplier = log10(diffusion_expansion_factor)/time_ping_history;
- double multiplier = pow(10,log_of_multiplier);
+ const double diffusion_expansion_factor = 10;
+ double log_of_multiplier = log10(diffusion_expansion_factor) / time_ping_history;
+ double multiplier = pow(10, log_of_multiplier);
uint64_t dispersion_factor = (uint64_t)(multiplier * 100);
// debug(1,"dispersion factor is %" PRIu64 ".", dispersion_factor);
-
// uint64_t first_local_to_remote_time_difference_time;
// uint64_t l2rtd = 0;
int sequence_number = 0;
if (packet[1] == 0xd3) { // timing reply
return_time = arrival_time - conn->departure_time;
- debug(3,"clock synchronisation request: return time is %8.3f milliseconds.",0.000001*return_time);
+ debug(3, "clock synchronisation request: return time is %8.3f milliseconds.",
+ 0.000001 * return_time);
if (return_time < 200000000) { // must be less than 0.2 seconds
// distant_receive_time =
// conn->time_pings[cc].dispersion * pow(2.14,
// 1.0/conn->time_ping_count);
if (conn->time_pings[cc].dispersion > UINT64_MAX / dispersion_factor)
- debug(1,"dispersion factor is too large at %" PRIu64 ".");
+ debug(1, "dispersion factor is too large at %" PRIu64 ".");
else
- conn->time_pings[cc].dispersion =
- (conn->time_pings[cc].dispersion * dispersion_factor) /
- 100; // make the dispersions 'age' by this rational factor
+ conn->time_pings[cc].dispersion =
+ (conn->time_pings[cc].dispersion * dispersion_factor) /
+ 100; // make the dispersions 'age' by this rational factor
}
// these are used for doing a least squares calculation to get the drift
conn->time_pings[0].local_time = arrival_time;
if ((conn->time_pings[cc].chosen) &&
(conn->time_pings[cc].sequence_number >
(settling_time / 3))) { // wait for a approximate settling time
- // have to scale them down so that the sum, possibly over every term in the array, doesn't overflow
+ // have to scale them down so that the sum, possibly over
+ // every term in the array, doesn't overflow
y_bar += (conn->time_pings[cc].remote_time >> time_ping_history_power_of_two);
x_bar += (conn->time_pings[cc].local_time >> time_ping_history_power_of_two);
sample_count++;
y_bar = y_bar / sample_count;
x_bar = x_bar / sample_count;
-
-
int64_t xid, yid;
double mtl, mbl;
mtl = 0;
conn->local_to_remote_time_gradient = mtl / mbl;
else {
// conn->local_to_remote_time_gradient = 1.0;
- debug(1,"mbl is zero. Drift remains at %.2f ppm.", (conn->local_to_remote_time_gradient - 1.0)*1000000);
+ debug(1, "mbl is zero. Drift remains at %.2f ppm.",
+ (conn->local_to_remote_time_gradient - 1.0) * 1000000);
}
- // scale the numbers back up
- uint64_t ybf = y_bar << time_ping_history_power_of_two;
- uint64_t xbf = x_bar << time_ping_history_power_of_two;
+ // scale the numbers back up
+ uint64_t ybf = y_bar << time_ping_history_power_of_two;
+ uint64_t xbf = x_bar << time_ping_history_power_of_two;
- conn->local_to_remote_time_difference =
- ybf - xbf; // make this the new local-to-remote-time-difference
- conn->local_to_remote_time_difference_measurement_time = xbf;
+ conn->local_to_remote_time_difference =
+ ybf - xbf; // make this the new local-to-remote-time-difference
+ conn->local_to_remote_time_difference_measurement_time = xbf;
} else {
- debug(3,"not enough samples to estimate drift -- remaining at %.2f ppm.", (conn->local_to_remote_time_gradient - 1.0)*1000000);
+ debug(3, "not enough samples to estimate drift -- remaining at %.2f ppm.",
+ (conn->local_to_remote_time_gradient - 1.0) * 1000000);
// conn->local_to_remote_time_gradient = 1.0;
}
// debug(1,"local to remote time gradient is %12.2f ppm, based on %d
pthread_cond_t pc_queue_item_added_signal;
pthread_cond_t pc_queue_item_removed_signal;
char *name;
- size_t item_size; // number of bytes in each item
+ size_t item_size; // number of bytes in each item
uint32_t count; // number of items in the queue
uint32_t capacity; // maximum number of items
uint32_t toq; // first item to take
rtsp_message *carrier;
} metadata_package;
-void pc_queue_init(pc_queue *the_queue, char *items, size_t item_size, uint32_t number_of_items, const char* name) {
- if (name)
- debug(2, "Creating metadata queue \"%s\".", name);
- else
- debug(1, "Creating an unnamed metadata queue.");
+void pc_queue_init(pc_queue *the_queue, char *items, size_t item_size, uint32_t number_of_items,
+ const char *name) {
+ if (name)
+ debug(2, "Creating metadata queue \"%s\".", name);
+ else
+ debug(1, "Creating an unnamed metadata queue.");
pthread_mutex_init(&the_queue->pc_queue_lock, NULL);
pthread_cond_init(&the_queue->pc_queue_item_added_signal, NULL);
pthread_cond_init(&the_queue->pc_queue_item_removed_signal, NULL);
the_queue->toq = 0;
the_queue->eoq = 0;
if (name == NULL)
- the_queue->name = NULL;
+ the_queue->name = NULL;
else
- the_queue->name = strdup(name);
+ the_queue->name = strdup(name);
}
void pc_queue_delete(pc_queue *the_queue) {
- if (the_queue->name)
- debug(2, "Deleting metadata queue \"%s\".", the_queue->name);
- else
- debug(1, "Deleting an unnamed metadata queue.");
- if (the_queue->name != NULL)
- free(the_queue->name);
- // debug(2, "destroying pc_queue_item_removed_signal");
+ if (the_queue->name)
+ debug(2, "Deleting metadata queue \"%s\".", the_queue->name);
+ else
+ debug(1, "Deleting an unnamed metadata queue.");
+ if (the_queue->name != NULL)
+ free(the_queue->name);
+ // debug(2, "destroying pc_queue_item_removed_signal");
pthread_cond_destroy(&the_queue->pc_queue_item_removed_signal);
- // debug(2, "destroying pc_queue_item_added_signal");
+ // debug(2, "destroying pc_queue_item_added_signal");
pthread_cond_destroy(&the_queue->pc_queue_item_added_signal);
- // debug(2, "destroying pc_queue_lock");
+ // debug(2, "destroying pc_queue_lock");
pthread_mutex_destroy(&the_queue->pc_queue_lock);
- // debug(2, "destroying signals and locks done");
+ // debug(2, "destroying signals and locks done");
}
int send_metadata(uint32_t type, uint32_t code, char *data, uint32_t length, rtsp_message *carrier,
}
int pc_queue_add_item(pc_queue *the_queue, const void *the_stuff, int block) {
- int response = 0;
+ int response = 0;
int rc;
if (the_queue) {
if (block == 0) {
// leave this out if you want this to return if the queue is already full
// irrespective of the block flag.
/*
- while (the_queue->count == the_queue->capacity) {
- rc = pthread_cond_wait(&the_queue->pc_queue_item_removed_signal, &the_queue->pc_queue_lock);
- if (rc)
- debug(1, "Error waiting for item to be removed");
- }
- */
+ while (the_queue->count == the_queue->capacity) {
+ rc = pthread_cond_wait(&the_queue->pc_queue_item_removed_signal,
+ &the_queue->pc_queue_lock); if (rc) debug(1, "Error waiting for item to be removed");
+ }
+ */
if (the_queue->count < the_queue->capacity) {
- uint32_t i = the_queue->eoq;
- void *p = the_queue->items + the_queue->item_size * i;
- // void * p = &the_queue->qbase + the_queue->item_size*the_queue->eoq;
- memcpy(p, the_stuff, the_queue->item_size);
-
- // update the pointer
- i++;
- if (i == the_queue->capacity)
- // fold pointer if necessary
- i = 0;
- the_queue->eoq = i;
- the_queue->count++;
- //debug(2,"metadata queue+ \"%s\" %d/%d.", the_queue->name, the_queue->count, the_queue->capacity);
- if (the_queue->count == the_queue->capacity)
- debug(3, "metadata queue \"%s\": is now full with %d items in it!", the_queue->name, the_queue->count);
- rc = pthread_cond_signal(&the_queue->pc_queue_item_added_signal);
- if (rc)
- debug(1, "metadata queue \"%s\": error signalling after pc_queue_add_item", the_queue->name);
+ uint32_t i = the_queue->eoq;
+ void *p = the_queue->items + the_queue->item_size * i;
+ // void * p = &the_queue->qbase + the_queue->item_size*the_queue->eoq;
+ memcpy(p, the_stuff, the_queue->item_size);
+
+ // update the pointer
+ i++;
+ if (i == the_queue->capacity)
+ // fold pointer if necessary
+ i = 0;
+ the_queue->eoq = i;
+ the_queue->count++;
+ // debug(2,"metadata queue+ \"%s\" %d/%d.", the_queue->name, the_queue->count,
+ // the_queue->capacity);
+ if (the_queue->count == the_queue->capacity)
+ debug(3, "metadata queue \"%s\": is now full with %d items in it!", the_queue->name,
+ the_queue->count);
+ rc = pthread_cond_signal(&the_queue->pc_queue_item_added_signal);
+ if (rc)
+ debug(1, "metadata queue \"%s\": error signalling after pc_queue_add_item",
+ the_queue->name);
} else {
- response = EWOULDBLOCK; // a bit arbitrary, this.
- debug(3,"metadata queue \"%s\": is already full with %d items in it. Not adding this item to the queue.", the_queue->name, the_queue->count);
+ response = EWOULDBLOCK; // a bit arbitrary, this.
+ debug(3,
+ "metadata queue \"%s\": is already full with %d items in it. Not adding this item to "
+ "the queue.",
+ the_queue->name, the_queue->count);
}
pthread_cleanup_pop(1); // unlock the queue lock.
} else {
i = 0;
the_queue->toq = i;
the_queue->count--;
- debug(3,"metadata queue- \"%s\" %d/%d.", the_queue->name, the_queue->count, the_queue->capacity);
+ debug(3, "metadata queue- \"%s\" %d/%d.", the_queue->name, the_queue->count,
+ the_queue->capacity);
rc = pthread_cond_signal(&the_queue->pc_queue_item_removed_signal);
if (rc)
debug(1, "metadata queue \"%s\": error signalling after pc_queue_get_item", the_queue->name);
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 "
- "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);
debug(1, "Error %d locking reference counter lock");
if (msg > (rtsp_message *)0x00010000) {
msg->referenceCount++;
- debug(3,"msg_free increment reference counter message %d to %d.", msg->index_number, msg->referenceCount);
+ debug(3, "msg_free increment reference counter message %d to %d.", msg->index_number,
+ 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)
memset(msg, 0, sizeof(rtsp_message));
msg->referenceCount = 1; // from now on, any access to this must be protected with the lock
msg->index_number = msg_indexes++;
- debug(3,"msg_init message %d", msg->index_number);
+ debug(3, "msg_init message %d", msg->index_number);
} else {
die("msg_init -- can not allocate memory for rtsp_message %d.", msg_indexes);
}
if (*msgh > (rtsp_message *)0x00010000) {
rtsp_message *msg = *msgh;
msg->referenceCount--;
- if (msg->referenceCount)
- debug(3,"msg_free decrement reference counter message %d to %d", msg->index_number, msg->referenceCount);
+ if (msg->referenceCount)
+ debug(3, "msg_free decrement reference counter message %d to %d", msg->index_number,
+ msg->referenceCount);
if (msg->referenceCount == 0) {
unsigned int i;
for (i = 0; i < msg->nheaders; i++) {
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
- debug(3,"msg_free freed message %d", msg->index_number);
+ debug(3, "msg_free freed message %d", msg->index_number);
free(msg);
} else {
// debug(1,"msg_free item %d -- decrement reference to
}
fail:
- debug(3,"msg_handle_line fail");
+ debug(3, "msg_handle_line fail");
msg_free(pmsg);
*pmsg = NULL;
return 0;
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) {
goto shutdown;
}
-/* // this outputs the message received
- {
- void *pt = malloc(nread+1);
- memset(pt, 0, nread+1);
- memcpy(pt, buf + inbuf, nread);
- debug(1, "Incoming string on port: \"%s\"",pt);
- free(pt);
- }
-*/
+ /* // this outputs the message received
+ {
+ void *pt = malloc(nread+1);
+ memset(pt, 0, nread+1);
+ memcpy(pt, buf + inbuf, nread);
+ debug(1, "Incoming string on port: \"%s\"",pt);
+ free(pt);
+ }
+ */
inbuf += nread;
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;
}
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,
rtptime = uatoi(p + 1); // unsigned integer -- up to 2^32-1
}
}
-// debug(1,"RTSP Flush Requested: %u.",rtptime);
+ // debug(1,"RTSP Flush Requested: %u.",rtptime);
-// the following is now done better by the player_flush routine as a 'pfls'
-/*
-#ifdef CONFIG_METADATA
- if (p)
- send_metadata('ssnc', 'flsr', p + 1, strlen(p + 1), req, 1);
- else
- send_metadata('ssnc', 'flsr', NULL, 0, NULL, 0);
-#endif
-*/
+ // the following is now done better by the player_flush routine as a 'pfls'
+ /*
+ #ifdef CONFIG_METADATA
+ if (p)
+ send_metadata('ssnc', 'flsr', p + 1, strlen(p + 1), req, 1);
+ else
+ send_metadata('ssnc', 'flsr', NULL, 0, NULL, 0);
+ #endif
+ */
player_flush(rtptime, conn); // will not crash even it there is no player thread.
resp->respcode = 200;
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: "
- "%d, Timing: %d and Audio: %d.",
+ 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,
conn->local_audio_port);
static int fd = -1;
// static int dirty = 0;
-
-
pc_queue metadata_queue;
#define metadata_queue_size 500
metadata_package metadata_queue_items[metadata_queue_size];
metadata_package metadata_multicast_queue_items[metadata_queue_size];
pthread_t metadata_multicast_thread;
-
void metadata_create_multicast_socket(void) {
if (config.metadata_enabled == 0)
return;
free(metadata_sockmsg);
}
-
void metadata_open(void) {
if (config.metadata_enabled == 0)
return;
}
void metadata_multicast_process(uint32_t type, uint32_t code, char *data, uint32_t length) {
- // debug(1, "Process multicast metadata with type %x, code %x and length %u.", type, code, length);
+ // debug(1, "Process multicast metadata with type %x, code %x and length %u.", type, code,
+ // length);
if (metadata_sock >= 0 && length < config.metadata_sockmsglength - 8) {
char *ptr = metadata_sockmsg;
uint32_t v;
debug(1, "Error encoding base64 data.");
// debug(1,"Remaining count: %d ret: %d, outbuf_size:
// %d.",remaining_count,ret,outbuf_size);
- //ret = non_blocking_write(fd, outbuf, outbuf_size);
+ // ret = non_blocking_write(fd, outbuf, outbuf_size);
ret = write(fd, outbuf, outbuf_size);
if (ret < 0) {
// debug(1,"metadata_process error %d exit 3",ret);
}
}
snprintf(thestring, 1024, "</item>\n");
- //ret = non_blocking_write(fd, thestring, strlen(thestring));
+ // ret = non_blocking_write(fd, thestring, strlen(thestring));
ret = write(fd, thestring, strlen(thestring));
if (ret < 0) {
// debug(1,"metadata_process error %d exit 5",ret);
pc_queue_get_item(&metadata_queue, &pack);
pthread_cleanup_push(metadata_pack_cleanup_function, (void *)&pack);
if (config.metadata_enabled) {
- if (pack.carrier) {
- debug(3, " pipe: type %x, code %x, length %u, message %d.", pack.type, pack.code, pack.length, pack.carrier->index_number);
- } else {
- debug(3, " pipe: type %x, code %x, length %u.", pack.type, pack.code, pack.length);
- }
+ if (pack.carrier) {
+ debug(3, " pipe: type %x, code %x, length %u, message %d.", pack.type, pack.code,
+ pack.length, pack.carrier->index_number);
+ } else {
+ debug(3, " pipe: type %x, code %x, length %u.", pack.type, pack.code, pack.length);
+ }
metadata_process(pack.type, pack.code, pack.data, pack.length);
debug(3, " pipe: done.");
}
void *metadata_multicast_thread_function(__attribute__((unused)) void *ignore) {
// create a pc_queue for passing information to a threaded metadata handler
- pc_queue_init(&metadata_multicast_queue, (char *)&metadata_multicast_queue_items, sizeof(metadata_package),
- metadata_multicast_queue_size, "multicast");
+ pc_queue_init(&metadata_multicast_queue, (char *)&metadata_multicast_queue_items,
+ sizeof(metadata_package), metadata_multicast_queue_size, "multicast");
metadata_create_multicast_socket();
metadata_package pack;
pthread_cleanup_push(metadata_multicast_thread_cleanup_function, NULL);
pc_queue_get_item(&metadata_multicast_queue, &pack);
pthread_cleanup_push(metadata_pack_cleanup_function, (void *)&pack);
if (config.metadata_enabled) {
- if (pack.carrier) {
- debug(3, " multicast: type %x, code %x, length %u, message %d.", pack.type, pack.code, pack.length, pack.carrier->index_number);
- } else {
- debug(3, " multicast: type %x, code %x, length %u.", pack.type, pack.code, pack.length);
- }
+ if (pack.carrier) {
+ debug(3,
+ " multicast: type "
+ "%x, code %x, length %u, message %d.",
+ pack.type, pack.code, pack.length, pack.carrier->index_number);
+ } else {
+ debug(3,
+ " multicast: type "
+ "%x, code %x, length %u.",
+ pack.type, pack.code, pack.length);
+ }
metadata_multicast_process(pack.type, pack.code, pack.data, pack.length);
- debug(3, " multicast: done.");
+ debug(3,
+ " multicast: done.");
}
pthread_cleanup_pop(1);
}
pthread_exit(NULL);
}
-
#ifdef CONFIG_METADATA_HUB
-void metadata_hub_close(void) {
-}
+void metadata_hub_close(void) {}
void metadata_hub_thread_cleanup_function(__attribute__((unused)) void *arg) {
// debug(2, "metadata_hub_thread_cleanup_function called");
// we check that it's not a "real" error. From the "man 2 open" page:
// "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO
// open for reading." Which is okay.
- if ((fd == -1) && (errno != ENXIO)) {
- char errorstring[1024];
- strerror_r(errno, (char *)errorstring, sizeof(errorstring));
- debug(1, "metadata_hub_thread_function -- error %d (\"%s\") opening pipe: \"%s\".", errno,
- (char *)errorstring, path);
- warn("can not open metadata pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno,
- (char *)errorstring, path);
- }
+ if ((fd == -1) && (errno != ENXIO)) {
+ char errorstring[1024];
+ strerror_r(errno, (char *)errorstring, sizeof(errorstring));
+ debug(1, "metadata_hub_thread_function -- error %d (\"%s\") opening pipe: \"%s\".", errno,
+ (char *)errorstring, path);
+ warn("can not open metadata pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno,
+ (char *)errorstring, path);
+ }
free(path);
// create a pc_queue for passing information to a threaded metadata handler
while (1) {
pc_queue_get_item(&metadata_hub_queue, &pack);
pthread_cleanup_push(metadata_pack_cleanup_function, (void *)&pack);
- if (pack.carrier) {
- debug(3, " hub: type %x, code %x, length %u, message %d.", pack.type, pack.code, pack.length, pack.carrier->index_number);
- } else {
- debug(3, " hub: type %x, code %x, length %u.", pack.type, pack.code, pack.length);
- }
- metadata_hub_process_metadata(pack.type, pack.code, pack.data, pack.length);
- debug(3, " hub: done.");
+ if (pack.carrier) {
+ debug(3, " hub: type %x, code %x, length %u, message %d.", pack.type,
+ pack.code, pack.length, pack.carrier->index_number);
+ } else {
+ debug(3, " hub: type %x, code %x, length %u.", pack.type, pack.code,
+ pack.length);
+ }
+ metadata_hub_process_metadata(pack.type, pack.code, pack.data, pack.length);
+ debug(3, " hub: done.");
pthread_cleanup_pop(1);
}
pthread_cleanup_pop(1); // will never happen
#endif
#ifdef CONFIG_MQTT
-void metadata_mqtt_close(void) {
-}
+void metadata_mqtt_close(void) {}
void metadata_mqtt_thread_cleanup_function(__attribute__((unused)) void *arg) {
// debug(2, "metadata_mqtt_thread_cleanup_function called");
while (1) {
pc_queue_get_item(&metadata_mqtt_queue, &pack);
pthread_cleanup_push(metadata_pack_cleanup_function, (void *)&pack);
- if (config.mqtt_enabled) {
- if (pack.carrier) {
- debug(3, " mqtt: type %x, code %x, length %u, message %d.", pack.type, pack.code, pack.length, pack.carrier->index_number);
- } else {
- debug(3, " mqtt: type %x, code %x, length %u.", pack.type, pack.code, pack.length);
- }
- mqtt_process_metadata(pack.type, pack.code, pack.data, pack.length);
- debug(3, " mqtt: done.");
- }
+ if (config.mqtt_enabled) {
+ if (pack.carrier) {
+ debug(3,
+ " mqtt: type %x, code %x, length %u, message "
+ "%d.",
+ pack.type, pack.code, pack.length, pack.carrier->index_number);
+ } else {
+ debug(3, " mqtt: type %x, code %x, length %u.",
+ pack.type, pack.code, pack.length);
+ }
+ mqtt_process_metadata(pack.type, pack.code, pack.data, pack.length);
+ debug(3, " mqtt: done.");
+ }
pthread_cleanup_pop(1);
}
}
}
-int send_metadata_to_queue(pc_queue* queue, uint32_t type, uint32_t code, char *data, uint32_t length, rtsp_message *carrier,
- int block) {
+int send_metadata_to_queue(pc_queue *queue, uint32_t type, uint32_t code, char *data,
+ uint32_t length, rtsp_message *carrier, int block) {
// parameters: type, code, pointer to data or NULL, length of data or NULL,
// the rtsp_message or
if (pack.carrier) {
msg_retain(pack.carrier);
} else {
- if (data)
- pack.data = memdup(data,length); // only if it's not a null
+ if (data)
+ pack.data = memdup(data, length); // only if it's not a null
}
int rc = pc_queue_add_item(queue, &pack, block);
if (rc != 0) {
if (pack.carrier) {
- if (rc == EWOULDBLOCK)
- debug(2, "metadata queue \"%s\" full, dropping message item: type %x, code %x, data %x, length %u, message %d.", queue->name, pack.type, pack.code, pack.data, pack.length, pack.carrier->index_number);
+ if (rc == EWOULDBLOCK)
+ debug(2,
+ "metadata queue \"%s\" full, dropping message item: type %x, code %x, data %x, "
+ "length %u, message %d.",
+ queue->name, pack.type, pack.code, pack.data, pack.length,
+ pack.carrier->index_number);
msg_free(&pack.carrier);
} else {
- if (rc == EWOULDBLOCK)
- debug(2, "metadata queue \"%s\" full, dropping data item: type %x, code %x, data %x, length %u.", queue->name, pack.type, pack.code, pack.data, pack.length);
- if (pack.data)
- free(pack.data);
+ if (rc == EWOULDBLOCK)
+ debug(
+ 2,
+ "metadata queue \"%s\" full, dropping data item: type %x, code %x, data %x, length %u.",
+ queue->name, pack.type, pack.code, pack.data, pack.length);
+ if (pack.data)
+ free(pack.data);
}
}
return rc;
int send_metadata(uint32_t type, uint32_t code, char *data, uint32_t length, rtsp_message *carrier,
int block) {
- int rc;
- rc = send_metadata_to_queue(&metadata_queue, type, code, data, length, carrier, block);
+ int rc;
+ rc = send_metadata_to_queue(&metadata_queue, type, code, data, length, carrier, block);
#ifdef CONFIG_METADATA_HUB
- rc = send_metadata_to_queue(&metadata_hub_queue, type, code, data, length, carrier, block);
+ rc = send_metadata_to_queue(&metadata_hub_queue, type, code, data, length, carrier, block);
#endif
#ifdef CONFIG_MQTT
- rc = send_metadata_to_queue(&metadata_mqtt_queue, type, code, data, length, carrier, block);
+ rc = send_metadata_to_queue(&metadata_mqtt_queue, type, code, data, length, carrier, block);
#endif
- return rc;
+ return rc;
}
-
-
-
static void handle_set_parameter_metadata(__attribute__((unused)) rtsp_conn_info *conn,
rtsp_message *req,
__attribute__((unused)) rtsp_message *resp) {
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
unsigned int i = 0;
unsigned int max_param = sizeof(conn->stream.fmtp) / sizeof(conn->stream.fmtp[0]);
- char* found;
+ char *found;
while ((found = strsep(&pfmtp, " \t")) != NULL && i < max_param) {
conn->stream.fmtp[i++] = atoi(found);
}
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);
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"
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.",
debug(2, "Connection %d: new connection from %s:%u to self at %s:%u.",
conn->connection_number, remote_ip4, rport, ip4, tport);
}
- #ifdef AF_INET6
+#ifdef AF_INET6
if (local_info->SAFAMILY == AF_INET6) {
// IPv6:
debug(2, "Connection %d: new connection from [%s]:%u to self at [%s]:%u.",
conn->connection_number, remote_ip6, rport, ip6, tport);
}
- #endif
+#endif
} else {
debug(1, "Error figuring out Shairport Sync's own IP number.");
#endif
#include "activity_monitor.h"
+#include "audio.h"
#include "common.h"
#include "rtp.h"
#include "rtsp.h"
char configuration_file_path[4096 + 1];
char actual_configuration_file_path[4096 + 1];
+char first_backend_name[256];
+
void print_version(void) {
char *version_string = get_version_string();
if (version_string) {
debug(1, "Can't print version string!");
}
}
-
#ifdef CONFIG_SOXR
pthread_t soxr_time_check_thread;
void *soxr_time_check(__attribute__((unused)) void *arg) {
// for unexpected circumstances
#ifdef CONFIG_METADATA
- /* Get the metadata setting. */
- config.metadata_enabled = 1; // if metadata support is included, then enable it by default
- config.get_coverart = 1; // if metadata support is included, then enable it by default
+ /* Get the metadata setting. */
+ config.metadata_enabled = 1; // if metadata support is included, then enable it by default
+ config.get_coverart = 1; // if metadata support is included, then enable it by default
#endif
#ifdef CONFIG_CONVOLUTION
- config.convolution_max_length = 8192;
+ config.convolution_max_length = 8192;
#endif
- config.loudness_reference_volume_db = -20;
+ config.loudness_reference_volume_db = -20;
#ifdef CONFIG_METADATA_HUB
config.cover_art_cache_dir = "/tmp/shairport-sync/.cache/coverart";
} else if (strcasecmp(str, "stderr") == 0) {
log_to_stderr();
} else {
- config.log_file_path = (char *)str;
- config.log_fd = -1;
- log_to_file();
+ config.log_file_path = (char *)str;
+ config.log_fd = -1;
+ log_to_file();
}
}
/* Get the ignore_volume_control setting. */
void exit_function() {
- if (emergency_exit == 0) {
- // the following is to ensure that if libdaemon has been included
- // that most of this code will be skipped when the parent process is exiting
- // exec
+ if (emergency_exit == 0) {
+ // the following is to ensure that if libdaemon has been included
+ // that most of this code will be skipped when the parent process is exiting
+ // exec
#ifdef CONFIG_LIBDAEMON
- if ((this_is_the_daemon_process) || (config.daemonise == 0)) { // if this is the daemon process that is exiting or it's not actually deamonised at all
-#endif
- debug(2, "exit function called...");
- /*
- Actually, there is no terminate_mqtt() function.
- #ifdef CONFIG_MQTT
- if (config.mqtt_enabled) {
- terminate_mqtt();
- }
- #endif
- */
+ if ((this_is_the_daemon_process) ||
+ (config.daemonise == 0)) { // if this is the daemon process that is exiting or it's not
+ // actually deamonised at all
+#endif
+ debug(2, "exit function called...");
+ /*
+ Actually, there is no terminate_mqtt() function.
+ #ifdef CONFIG_MQTT
+ if (config.mqtt_enabled) {
+ terminate_mqtt();
+ }
+ #endif
+ */
#if defined(CONFIG_DBUS_INTERFACE) || defined(CONFIG_MPRIS_INTERFACE)
- /*
- Actually, there is no stop_mpris_service() function.
- #ifdef CONFIG_MPRIS_INTERFACE
- stop_mpris_service();
- #endif
- */
+ /*
+ Actually, there is no stop_mpris_service() function.
+ #ifdef CONFIG_MPRIS_INTERFACE
+ stop_mpris_service();
+ #endif
+ */
#ifdef CONFIG_DBUS_INTERFACE
- stop_dbus_service();
+ stop_dbus_service();
#endif
- if (g_main_loop) {
- debug(2, "Stopping DBUS Loop Thread");
- g_main_loop_quit(g_main_loop);
- pthread_join(dbus_thread, NULL);
- }
+ if (g_main_loop) {
+ debug(2, "Stopping DBUS Loop Thread");
+ g_main_loop_quit(g_main_loop);
+ pthread_join(dbus_thread, NULL);
+ }
#endif
#ifdef CONFIG_DACP_CLIENT
- debug(2, "Stopping DACP Monitor");
- dacp_monitor_stop();
+ debug(2, "Stopping DACP Monitor");
+ dacp_monitor_stop();
#endif
#ifdef CONFIG_METADATA_HUB
- debug(2, "Stopping metadata hub");
- metadata_hub_stop();
+ debug(2, "Stopping metadata hub");
+ metadata_hub_stop();
#endif
#ifdef CONFIG_METADATA
- metadata_stop(); // close down the metadata pipe
+ metadata_stop(); // close down the metadata pipe
#endif
- activity_monitor_stop(0);
+ activity_monitor_stop(0);
- if ((config.output) && (config.output->deinit)) {
- debug(2, "Deinitialise the audio backend.");
- config.output->deinit();
- }
+ if ((config.output) && (config.output->deinit)) {
+ debug(2, "Deinitialise the audio backend.");
+ config.output->deinit();
+ }
#ifdef CONFIG_SOXR
- // be careful -- not sure if the thread can be cancelled cleanly, so wait for it to shut down
- pthread_join(soxr_time_check_thread, NULL);
+ // be careful -- not sure if the thread can be cancelled cleanly, so wait for it to shut down
+ pthread_join(soxr_time_check_thread, NULL);
#endif
- if (conns)
- free(conns); // make sure the connections have been deleted first
+ if (conns)
+ free(conns); // make sure the connections have been deleted first
- if (config.service_name)
- free(config.service_name);
+ if (config.service_name)
+ free(config.service_name);
#ifdef CONFIG_CONVOLUTION
- if (config.convolution_ir_file)
- free(config.convolution_ir_file);
+ if (config.convolution_ir_file)
+ free(config.convolution_ir_file);
#endif
- if (config.regtype)
- free(config.regtype);
+ if (config.regtype)
+ free(config.regtype);
#ifdef CONFIG_LIBDAEMON
- if (this_is_the_daemon_process) {
- daemon_retval_send(0);
- daemon_pid_file_remove();
- daemon_signal_done();
- if (config.computed_piddir)
- free(config.computed_piddir);
- }
- }
+ if (this_is_the_daemon_process) {
+ daemon_retval_send(0);
+ daemon_pid_file_remove();
+ daemon_signal_done();
+ if (config.computed_piddir)
+ free(config.computed_piddir);
+ }
+ }
#endif
- if (config.cfg)
- config_destroy(config.cfg);
- if (config.appName)
- free(config.appName);
- // probably should be freeing malloc'ed memory here, including strdup-created strings...
-
+ if (config.cfg)
+ config_destroy(config.cfg);
+ if (config.appName)
+ free(config.appName);
+ // probably should be freeing malloc'ed memory here, including strdup-created strings...
#ifdef CONFIG_LIBDAEMON
- if (this_is_the_daemon_process) { // this is the daemon that is exiting
- debug(1,"libdaemon daemon exit");
- } else {
- if (config.daemonise)
- debug(1,"libdaemon parent exit");
- else
- debug(1,"exit");
- }
+ if (this_is_the_daemon_process) { // this is the daemon that is exiting
+ debug(1, "libdaemon daemon exit");
+ } else {
+ if (config.daemonise)
+ debug(1, "libdaemon parent exit");
+ else
+ debug(1, "exit");
+ }
#else
- debug(1,"exit");
+ debug(1, "exit");
#endif
- } else {
- debug(1,"emergency exit");
- }
+ } else {
+ debug(1, "emergency exit");
+ }
}
// for removing zombie script processes
}
void main_thread_cleanup_handler(__attribute__((unused)) void *arg) {
- debug(2,"main thread cleanup handler called");
+ debug(2, "main thread cleanup handler called");
exit(EXIT_SUCCESS);
}
-
int main(int argc, char **argv) {
/* Check if we are called with -V or --version parameter */
if (argc >= 2 && ((strcmp(argv[1], "-V") == 0) || (strcmp(argv[1], "--version") == 0))) {
#ifdef CONFIG_LIBDAEMON
pid = getpid();
#endif
- config.log_fd = -1;
+ config.log_fd = -1;
conns = NULL; // no connections active
memset((void *)&main_thread_id, 0, sizeof(main_thread_id));
memset(&config, 0, sizeof(config)); // also clears all strings, BTW
setlogmask(LOG_UPTO(LOG_DEBUG));
openlog(NULL, 0, LOG_DAEMON);
#endif
- emergency_exit = 0; // what to do or skip in the exit_function
+ emergency_exit = 0; // what to do or skip in the exit_function
atexit(exit_function);
// set defaults
// set non-zero / non-NULL default values here
// but note that audio back ends also have a chance to set defaults
+ // get the first output backend in the list and make it the default
+ audio_output *first_backend = audio_get_output(NULL);
+ if (first_backend == NULL) {
+ die("No audio backend found! Check your build of Shairport Sync.");
+ } else {
+ strncpy(first_backend_name, first_backend->name, sizeof(first_backend_name) - 1);
+ config.output_name = first_backend_name;
+ }
+
strcpy(configuration_file_path, SYSCONFDIR);
// strcat(configuration_file_path, "/shairport-sync"); // thinking about adding a special
// shairport-sync directory
if (errno == ENOENT)
daemon_log(LOG_WARNING, "Failed to kill %s daemon: PID file not found.", config.appName);
else
- daemon_log(LOG_WARNING, "Failed to kill %s daemon: \"%s\", errno %u.", config.appName, strerror(errno), errno);
+ daemon_log(LOG_WARNING, "Failed to kill %s daemon: \"%s\", errno %u.", config.appName,
+ strerror(errno), errno);
} else {
- // debug(1,"Successfully killed the %s daemon.", config.appName);
+ // debug(1,"Successfully killed the %s daemon.", config.appName);
if (daemon_pid_file_remove() == 0)
debug(2, "killed the %s daemon.", config.appName);
else
- daemon_log(LOG_WARNING, "killed the %s deamon, but cannot remove old PID file: \"%s\", errno %u.", config.appName, strerror(errno), errno);
+ daemon_log(LOG_WARNING,
+ "killed the %s deamon, but cannot remove old PID file: \"%s\", errno %u.",
+ config.appName, strerror(errno), errno);
}
return ret < 0 ? 1 : 0;
#else
case 0:
break;
case 1:
- daemon_log(LOG_ERR,
- "the %s daemon failed to launch: could not close open file descriptors after forking.", config.appName);
+ daemon_log(
+ LOG_ERR,
+ "the %s daemon failed to launch: could not close open file descriptors after forking.",
+ config.appName);
break;
case 2:
- daemon_log(LOG_ERR, "the %s daemon failed to launch: could not create PID file.", config.appName);
+ daemon_log(LOG_ERR, "the %s daemon failed to launch: could not create PID file.",
+ config.appName);
break;
case 3:
- daemon_log(LOG_ERR, "the %s daemon failed to launch: could not create or access PID directory.", config.appName);
+ daemon_log(LOG_ERR,
+ "the %s daemon failed to launch: could not create or access PID directory.",
+ config.appName);
break;
default:
daemon_log(LOG_ERR, "the %s daemon failed to launch, error %i.", config.appName, ret);
#endif
debug(1, "Started!");
- // stop a pipe signal from killing the program
- signal(SIGPIPE, SIG_IGN);
+ // stop a pipe signal from killing the program
+ signal(SIGPIPE, SIG_IGN);
// install a zombie process reaper
// see: http://www.microhowto.info/howto/reap_zombie_processes_using_a_sigchld_handler.html