Tidy up some error messages about which backend is selected.
Tighten up memory allocatons in DACP.c.
Add a default name for the pipe backend: /tmp/shairport-sync-audio
Ensure the metadata pipe is created, if necessary, when the --with-metadata option is chosen without dbus or mpris
Make the first output backend in the list of backends the default and make its name the default output_name. Clang-format everything
Add the word 'jack' to the version string if Jack Audio support is compiled in.
Fix a flaw in resyncing -- a flush is set up but not triggered.
Open metadata and audio pipes with 666 permissions.
Ensure metadata and cover art are enabled if metadata support is included at compilation.
Reword some misleading error messages.
Set convolution defaults even if no configuration file is found.
#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);
static int fd = -1;
char *pipename = NULL;
+char *default_pipe_name = "/tmp/shairport-sync-audio";
static void start(__attribute__((unused)) int sample_rate,
__attribute__((unused)) int sample_format) {
// "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) {
if (config_lookup_string(config.cfg, "pipe.name", &str)) {
pipename = (char *)str;
}
-
- if ((pipename) && (strcasecmp(pipename, "STDOUT") == 0))
- die("Can't use \"pipe\" backend for STDOUT. Use the \"stdout\" backend instead.");
}
- if ((pipename == NULL) && (argc != 1))
- die("bad or missing argument(s) to pipe");
+ if (argc > 1)
+ die("too many command-line arguments to pipe");
if (argc == 1)
- pipename = strdup(argv[0]);
+ pipename = argv[0]; // command line argument has priority
+
+ if ((pipename) && (strcasecmp(pipename, "STDOUT") == 0))
+ die("Can't use \"pipe\" backend for STDOUT. Use the \"stdout\" backend instead.");
+
+ if (pipename == NULL)
+ pipename = default_pipe_name; // if none specified
// here, create the pipe
mode_t oldumask = umask(000);
#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
#ifdef CONFIG_SNDIO
strcat(version_string, "-sndio");
#endif
+#ifdef CONFIG_JACK
+ strcat(version_string, "-jack");
+#endif
#ifdef CONFIG_AO
strcat(version_string, "-ao");
#endif
}
// 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
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.50])
-AC_INIT([shairport-sync], [3.3.7rc2], [4265913+mikebrady@users.noreply.github.com])
+AC_INIT([shairport-sync], [3.3.7rc3], [4265913+mikebrady@users.noreply.github.com])
AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR([shairport.c])
AC_CONFIG_HEADERS([config.h])
AC_MSG_ERROR(choose "openssl", "mbedtls" or "polarssl" encryption)
fi
if test "x${with_ssl}" = xopenssl ; then
- AC_DEFINE([CONFIG_OPENSSL], 1, [Use the OpenSSL libraries for encryption and encoding and decoding])
+ AC_DEFINE([CONFIG_OPENSSL], 1, [Use the OpenSSL libraries for encryption and encoding and decoding])
if test "x${with_pkg_config}" = xyes ; then
PKG_CHECK_MODULES(
[SSL], [libssl,libcrypto],
AC_CHECK_LIB([ssl], [main], , AC_MSG_ERROR(libssl selected but the library cannot be found!))
fi
elif test "x${with_ssl}" = xmbedtls ; then
- AC_DEFINE([CONFIG_MBEDTLS], 1, [Use the mbed TLS libraries for encryption and encoding and decoding])
+ AC_DEFINE([CONFIG_MBEDTLS], 1, [Use the mbed TLS libraries for encryption and encoding and decoding])
AC_CHECK_LIB([mbedtls],[mbedtls_ssl_init],,
[AC_MSG_ERROR([mbed tls support requires the mbedtls library -- libmbedtls-dev suggested],1)])
AC_CHECK_LIB([mbedcrypto], [mbedtls_entropy_func],,
AC_CHECK_LIB([mbedx509], [mbedtls_pk_init],,
[AC_MSG_ERROR([mbed tls support requires the mbedx509 library -- libmbedx509-0 suggested],1)])
elif test "x${with_ssl}" = xpolarssl ; then
- AC_DEFINE([CONFIG_POLARSSL], 1, [Use the PolarSSL libraries for encryption and encoding and decoding])
+ AC_DEFINE([CONFIG_POLARSSL], 1, [Use the PolarSSL libraries for encryption and encoding and decoding])
AC_CHECK_LIB([polarssl],[ssl_init], , AC_MSG_ERROR(PolarSSL is selected but the library cannot be found and is deprecated. Consider selecting mbed TLS instead using --with-ssl=mbedtls.))
else
AC_MSG_ERROR(unknown option "${with_ssl}"." Please choose with "openssl", "mbedtls" or "polarssl")
int always_use_revision_number_1; // for dealing with forked-daapd;
uint32_t scope_id; // if it's an ipv6 connection, this will be its scope id
char ip_string[INET6_ADDRSTRLEN]; // the ip string pointing to the client
- uint32_t active_remote_id; // send this when you want to send remote control commands
+ char *active_remote_id; // send this when you want to send remote control commands
void *port_monitor_private_storage;
} dacp_server_record;
// debug(1,"DACP connect succeeded.");
snprintf(message, sizeof(message),
- "GET /ctrl-int/1/%s HTTP/1.1\r\nHost: %s:%u\r\nActive-Remote: %u\r\n\r\n",
+ "GET /ctrl-int/1/%s HTTP/1.1\r\nHost: %s:%u\r\nActive-Remote: %s\r\n\r\n",
command, dacp_server.ip_string, dacp_server.port,
dacp_server.active_remote_id);
// metadata_hub_modify_epilog(ch);
}
}
- dacp_server.active_remote_id = conn->dacp_active_remote; // even if the dacp_id remains the same,
- // the active remote will change.
- debug(3, "set_dacp_server_information set active-remote id to %" PRIu32 ".",
- dacp_server.active_remote_id);
+ if (dacp_server.active_remote_id)
+ free(dacp_server.active_remote_id);
+ dacp_server.active_remote_id =
+ strdup(conn->dacp_active_remote); // even if the dacp_id remains the same,
+ // the active remote will change.
+ debug(3, "set_dacp_server_information set active-remote id to %s.", dacp_server.active_remote_id);
pthread_cond_signal(&dacp_server_information_cv);
debug_mutex_unlock(&dacp_server_information_lock, 3);
}
"dacp_monitor_port_update_callback with Remote ID \"%s\", target ID \"%s\" and port "
"number %d.",
dacp_id, dacp_server.dacp_id, port);
- if (strcmp(dacp_id, dacp_server.dacp_id) == 0) {
- dacp_server.port = port;
- if (port == 0)
- dacp_server.scan_enable = 0;
- else {
- dacp_server.scan_enable = 1;
- // debug(2, "dacp_monitor_port_update_callback enables scan");
- }
- // metadata_hub_modify_prolog();
- // int ch = metadata_store.dacp_server_active != dacp_server.scan_enable;
- // metadata_store.dacp_server_active = dacp_server.scan_enable;
- // metadata_hub_modify_epilog(ch);
+ if ((dacp_id == NULL) || (dacp_server.dacp_id == NULL)) {
+ warn("dacp_id or dacp_server.dacp_id NULL detected");
} else {
- debug(1, "dacp port monitor reporting on an out-of-use remote.");
+ if (strcmp(dacp_id, dacp_server.dacp_id) == 0) {
+ dacp_server.port = port;
+ if (port == 0)
+ dacp_server.scan_enable = 0;
+ else {
+ dacp_server.scan_enable = 1;
+ // debug(2, "dacp_monitor_port_update_callback enables scan");
+ }
+ // metadata_hub_modify_prolog();
+ // int ch = metadata_store.dacp_server_active != dacp_server.scan_enable;
+ // metadata_store.dacp_server_active = dacp_server.scan_enable;
+ // metadata_hub_modify_epilog(ch);
+ } else {
+ debug(1, "dacp port monitor reporting on an out-of-use remote.");
+ }
}
pthread_cond_signal(&dacp_server_information_cv);
debug_mutex_unlock(&dacp_server_information_lock, 3);
pthread_mutex_destroy(&dacp_conversation_lock);
pthread_cond_destroy(&dacp_server_information_cv);
debug(3, "DACP Server Information Condition Variable destroyed.");
+ if (dacp_server.active_remote_id) {
+ free(dacp_server.active_remote_id);
+ dacp_server.active_remote_id = NULL;
+ }
}
}
// 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;
// could be one of many, so we need to know it
uint32_t self_scope_id; // if it's an ipv6 connection, this will be its scope
short connection_ip_family; // AF_INET / AF_INET6
- uint32_t client_active_remote; // used when you want to control the client...
SOCKADDR rtp_client_control_socket; // a socket pointing to the control port of the client
SOCKADDR rtp_client_timing_socket; // a socket pointing to the timing port of the client
char *dacp_id; // id of the client -- used to find the port to be used
// uint16_t dacp_port; // port on the client to send remote control messages to, else
// zero
- uint32_t dacp_active_remote; // key to send to the remote controller
- void *dapo_private_storage; // this is used for compatibility, if dacp stuff isn't enabled.
+ char *dacp_active_remote; // key to send to the remote controller
+ void *dapo_private_storage; // this is used for compatibility, if dacp stuff isn't enabled.
int enable_dither; // needed for filling silences before play actually starts
uint64_t dac_buffer_queue_minimum_length;
#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
* Copyright (c) James Laird 2013
* Modifications associated with audio synchronization, mutithreading and
- * metadata handling copyright (c) Mike Brady 2014-2019
+ * metadata handling copyright (c) Mike Brady 2014-2020
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
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;
debug(2, "Connection %d: SETUP -- Active-Remote string seen: \"%s\".",
conn->connection_number, ar);
// get the active remote
- char *p;
- conn->dacp_active_remote = strtoul(ar, &p, 10);
+ if (conn->dacp_active_remote) // this is in case SETUP was previously called
+ free(conn->dacp_active_remote);
+ conn->dacp_active_remote = strdup(ar);
#ifdef CONFIG_METADATA
send_metadata('ssnc', 'acre', ar, strlen(ar), req, 1);
#endif
} else {
debug(2, "Connection %d: SETUP -- Note: no Active-Remote information the SETUP Record.",
conn->connection_number);
- conn->dacp_active_remote = 0;
+ if (conn->dacp_active_remote) { // this is in case SETUP was previously called
+ free(conn->dacp_active_remote);
+ conn->dacp_active_remote = NULL;
+ }
}
ar = msg_get_header(req, "DACP-ID");
} else {
debug(2, "Connection %d: SETUP doesn't include DACP-ID string information.",
conn->connection_number);
- if (conn->dacp_id) // this is in case SETUP was previously called
+ if (conn->dacp_id) { // this is in case SETUP was previously called
free(conn->dacp_id);
- conn->dacp_id = NULL;
+ conn->dacp_id = NULL;
+ }
}
char *hdr = msg_get_header(req, "Transport");
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;
} else {
int buffer_size = METADATA_SNDBUF;
setsockopt(metadata_sock, SOL_SOCKET, SO_SNDBUF, &buffer_size, sizeof(buffer_size));
- fcntl(fd, F_SETFL, O_NONBLOCK);
bzero((char *)&metadata_sockaddr, sizeof(metadata_sockaddr));
metadata_sockaddr.sin_family = AF_INET;
metadata_sockaddr.sin_addr.s_addr = inet_addr(config.metadata_sockaddr);
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");
}
void *metadata_hub_thread_function(__attribute__((unused)) void *ignore) {
-
- // create the fifo, if necessary
- size_t pl = strlen(config.metadata_pipename) + 1;
- char *path = malloc(pl + 1);
- snprintf(path, pl + 1, "%s", config.metadata_pipename);
-
- mode_t oldumask = umask(000);
- if (mkfifo(path, 0666) && errno != EEXIST)
- die("Could not create metadata pipe \"%s\".", path);
- umask(oldumask);
- debug(1, "metadata pipe name is \"%s\".", path);
-
- // try to open it
- fd = try_to_open_pipe_for_writing(path);
- // 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);
- }
- free(path);
-
// create a pc_queue for passing information to a threaded metadata handler
pc_queue_init(&metadata_hub_queue, (char *)&metadata_hub_queue_items, sizeof(metadata_package),
metadata_hub_queue_size, "hub");
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);
}
#endif
void metadata_init(void) {
+
+ // create the metadata pipe, if necessary
+ size_t pl = strlen(config.metadata_pipename) + 1;
+ char *path = malloc(pl + 1);
+ snprintf(path, pl + 1, "%s", config.metadata_pipename);
+
+ mode_t oldumask = umask(000);
+ if (mkfifo(path, 0666) && errno != EEXIST)
+ die("Could not create metadata pipe \"%s\".", path);
+ umask(oldumask);
+ debug(1, "metadata pipe name is \"%s\".", path);
+
+ // try to open it
+ fd = try_to_open_pipe_for_writing(path);
+ // 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);
+ }
+ free(path);
+
int ret = pthread_create(&metadata_thread, NULL, metadata_thread_function, NULL);
if (ret)
debug(1, "Failed to create metadata thread!");
}
}
-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.");
// --with-pipe
pipe =
{
-// name = "/path/to/pipe"; // there is no default pipe name for the output
+// name = "/tmp/shairport-sync-audio"; // this is the default
};
// There are no configuration file parameters for the "stdout" audio back end. No interpolation is done.
#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
config.output = audio_get_output(config.output_name);
if (!config.output) {
- audio_ls_outputs();
- die("Invalid audio output specified!");
+ die("Invalid audio backend \"%s\" selected!",
+ config.output_name == NULL ? "<unspecified>" : config.output_name);
}
config.output->init(argc - audio_arg, argv + audio_arg);