From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 3 Sep 2020 08:53:13 +0000 (+0100) Subject: Make the first output backend in the list of backends the default and make its name... X-Git-Tag: 3.3.8~16^2~16 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ca56287254776bb5e3838258da57784d70132ab1;p=thirdparty%2Fshairport-sync.git Make the first output backend in the list of backends the default and make its name the default output_name. Clang-format everything --- diff --git a/audio.c b/audio.c index 23b1760d..0fe7b04f 100644 --- a/audio.c +++ b/audio.c @@ -89,7 +89,7 @@ static audio_output *outputs[] = { #endif NULL}; -audio_output *audio_get_output(char *name) { +audio_output *audio_get_output(const char *name) { audio_output **out; // default to the first diff --git a/audio.h b/audio.h index 856f184b..3413250c 100644 --- a/audio.h +++ b/audio.h @@ -54,7 +54,7 @@ typedef struct { } 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); diff --git a/audio_pipe.c b/audio_pipe.c index 45e0f060..05c98623 100644 --- a/audio_pipe.c +++ b/audio_pipe.c @@ -51,33 +51,34 @@ static void start(__attribute__((unused)) int sample_rate, // "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; @@ -85,7 +86,6 @@ static int play(void *buf, int samples) { static void stop(void) { // Don't close the pipe just because a play session has stopped. - } static int init(int argc, char **argv) { diff --git a/common.c b/common.c index dbb4bad0..c1deea32 100644 --- a/common.c +++ b/common.c @@ -29,6 +29,8 @@ #include "common.h" #include #include +#include +#include #include #include #include @@ -40,8 +42,6 @@ #include #include #include -#include -#include #ifdef COMPILE_FOR_OSX #include @@ -141,64 +141,66 @@ void do_sps_log_to_stdout(__attribute__((unused)) int prio, const char *t, ...) 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); } } @@ -207,9 +209,9 @@ void log_to_stdout() { sps_log = do_sps_log_to_stdout; } 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 } @@ -309,8 +311,8 @@ void _die(const char *filename, const int linenumber, const char *format, ...) { 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); @@ -339,8 +341,8 @@ void _warn(const char *filename, const int linenumber, const char *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); @@ -1134,12 +1136,12 @@ uint64_t get_absolute_time_in_ns() { 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 @@ -1147,24 +1149,24 @@ int try_to_open_pipe_for_writing(const char* pathname) { // 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 @@ -1710,11 +1712,11 @@ 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* 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; } diff --git a/common.h b/common.h index 1f5d1c87..68666ac9 100644 --- a/common.h +++ b/common.h @@ -182,8 +182,8 @@ typedef struct { 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 @@ -285,10 +285,10 @@ typedef struct { 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 @@ -303,9 +303,7 @@ void memory_barrier(); 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 @@ -313,7 +311,8 @@ int get_requested_connection_state_to_output(); 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 @@ -446,6 +445,6 @@ void malloc_cleanup(void *arg); 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 diff --git a/metadata_hub.c b/metadata_hub.c index 7b20ae18..a899574a 100644 --- a/metadata_hub.c +++ b/metadata_hub.c @@ -136,18 +136,20 @@ void _metadata_hub_modify_prolog(const char *filename, const int linenumber) { // 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; @@ -160,13 +162,16 @@ void _metadata_hub_modify_epilog(int modified, const char *filename, const int l 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."); @@ -506,10 +511,11 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin 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); @@ -524,7 +530,7 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin 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); diff --git a/metadata_hub.h b/metadata_hub.h index 760a848b..bdbbf321 100644 --- a/metadata_hub.h +++ b/metadata_hub.h @@ -149,7 +149,9 @@ void metadata_hub_release_track_artwork(void); // 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 @@ -158,7 +160,8 @@ void _metadata_hub_read_epilog(const char *filename, const int linenumber); */ #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__) diff --git a/player.c b/player.c index edca685f..78912e3b 100644 --- a/player.c +++ b/player.c @@ -143,7 +143,6 @@ static void ab_resync(rtsp_conn_info *conn) { 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 @@ -174,25 +173,21 @@ int position_in_modulo_uint16_t_buffer(uint16_t x, uint16_t start, uint16_t end, 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. @@ -215,16 +210,15 @@ static inline seq_t seq_sum(seq_t a, seq_t b) { // 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) { @@ -466,172 +460,173 @@ void player_put_packet(seq_t seqno, uint32_t actual_timestamp, uint8_t *data, in 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); } @@ -944,77 +939,98 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { 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); @@ -1037,7 +1053,6 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { } } - 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 @@ -1058,7 +1073,6 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { // 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. @@ -1093,8 +1107,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { &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; @@ -1105,172 +1118,180 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { } } + 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. @@ -1665,8 +1686,8 @@ void player_thread_cleanup_handler(void *arg) { } 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) @@ -1690,8 +1711,8 @@ void *player_thread_func(void *arg) { 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) { @@ -1902,9 +1923,9 @@ void *player_thread_func(void *arg) { 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; @@ -2002,7 +2023,8 @@ void *player_thread_func(void *arg) { "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); @@ -2112,8 +2134,7 @@ void *player_thread_func(void *arg) { 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. @@ -2166,8 +2187,7 @@ void *player_thread_func(void *arg) { } } - 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; @@ -2175,9 +2195,6 @@ void *player_thread_func(void *arg) { 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. @@ -2191,7 +2208,7 @@ void *player_thread_func(void *arg) { 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 = @@ -2229,15 +2246,16 @@ void *player_thread_func(void *arg) { 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 + @@ -2245,38 +2263,51 @@ void *player_thread_func(void *arg) { 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); } @@ -2560,30 +2591,30 @@ void *player_thread_func(void *arg) { // 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 = @@ -2779,11 +2810,10 @@ void *player_thread_func(void *arg) { "%*.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); } @@ -2791,7 +2821,7 @@ void *player_thread_func(void *arg) { 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; @@ -2997,17 +3027,17 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c // 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) @@ -3026,10 +3056,10 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c 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 @@ -3057,10 +3087,10 @@ void player_flush(uint32_t timestamp, rtsp_conn_info *conn) { 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 } diff --git a/player.h b/player.h index f6b86cd4..2b0c2c2e 100644 --- a/player.h +++ b/player.h @@ -25,7 +25,9 @@ #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; diff --git a/rtp.c b/rtp.c index e56c5454..c558645e 100644 --- a/rtp.c +++ b/rtp.c @@ -46,9 +46,9 @@ #include struct Nvll { - char* name; - double value; - struct Nvll *next; + char *name; + double value; + struct Nvll *next; }; typedef struct Nvll nvll; @@ -263,8 +263,8 @@ void *rtp_control_receiver(void *arg) { 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 @@ -275,19 +275,19 @@ void *rtp_control_receiver(void *arg) { // 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 " @@ -527,22 +527,25 @@ void rtp_timing_receiver_cleanup_handler(void *arg) { // 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."); @@ -574,13 +577,14 @@ void *rtp_timing_receiver(void *arg) { 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 @@ -591,13 +595,12 @@ void *rtp_timing_receiver(void *arg) { // 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; @@ -621,7 +624,8 @@ void *rtp_timing_receiver(void *arg) { 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 = @@ -671,11 +675,11 @@ void *rtp_timing_receiver(void *arg) { // 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; @@ -752,7 +756,8 @@ void *rtp_timing_receiver(void *arg) { 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++; @@ -762,8 +767,6 @@ void *rtp_timing_receiver(void *arg) { y_bar = y_bar / sample_count; x_bar = x_bar / sample_count; - - int64_t xid, yid; double mtl, mbl; mtl = 0; @@ -791,19 +794,21 @@ void *rtp_timing_receiver(void *arg) { 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 diff --git a/rtsp.c b/rtsp.c index 1f1cb9f9..af9e9706 100644 --- a/rtsp.c +++ b/rtsp.c @@ -115,7 +115,7 @@ typedef struct { 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 @@ -152,11 +152,12 @@ typedef struct { 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); @@ -167,25 +168,25 @@ void pc_queue_init(pc_queue *the_queue, char *items, size_t item_size, uint32_t 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, @@ -204,7 +205,7 @@ void pc_queue_cleanup_handler(void *arg) { } 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) { @@ -219,34 +220,39 @@ int pc_queue_add_item(pc_queue *the_queue, const void *the_stuff, int block) { // 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 { @@ -279,7 +285,8 @@ int pc_queue_get_item(pc_queue *the_queue, void *the_stuff) { 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); @@ -317,15 +324,17 @@ void *player_watchdog_thread_code(void *arg) { uint64_t last_watchdog_bark_time = conn->watchdog_bark_time; debug_mutex_unlock(&conn->watchdog_mutex, 0); if (last_watchdog_bark_time != 0) { - uint64_t time_since_last_bark = (get_absolute_time_in_ns() - last_watchdog_bark_time) / 1000000000; + uint64_t time_since_last_bark = + (get_absolute_time_in_ns() - last_watchdog_bark_time) / 1000000000; uint64_t ct = config.timeout; // go from int to 64-bit int if (time_since_last_bark >= ct) { conn->watchdog_barks++; if (conn->watchdog_barks == 1) { // debuglev = 3; // tell us everything. - debug(1, "Connection %d: As Yeats almost said, \"Too long a silence / can make a stone " - "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); @@ -446,7 +455,8 @@ void msg_retain(rtsp_message *msg) { 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) @@ -462,7 +472,7 @@ rtsp_message *msg_init(void) { 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); } @@ -526,8 +536,9 @@ void msg_free(rtsp_message **msgh) { 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++) { @@ -542,7 +553,7 @@ void msg_free(rtsp_message **msgh) { 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 @@ -607,7 +618,7 @@ int msg_handle_line(rtsp_message **pmsg, char *line) { } fail: - debug(3,"msg_handle_line fail"); + debug(3, "msg_handle_line fail"); msg_free(pmsg); *pmsg = NULL; return 0; @@ -650,7 +661,8 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes if (errno == EINTR) continue; if (errno == EAGAIN) { - debug(1, "Connection %d: getting Error 11 -- EAGAIN from a blocking read!", conn->connection_number); + debug(1, "Connection %d: getting Error 11 -- EAGAIN from a blocking read!", + conn->connection_number); continue; } if (errno != ECONNRESET) { @@ -663,15 +675,15 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes 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; @@ -680,7 +692,8 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes msg_size = msg_handle_line(the_packet, buf); if (!(*the_packet)) { - debug(1,"Connection %d: rtsp_read_request can't find an RTSP header.", conn->connection_number); + debug(1, "Connection %d: rtsp_read_request can't find an RTSP header.", + conn->connection_number); reply = rtsp_read_request_response_bad_packet; goto shutdown; } @@ -887,9 +900,10 @@ void handle_options(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message * rtsp_message *resp) { debug(3, "Connection %d: OPTIONS", conn->connection_number); resp->respcode = 200; - msg_add_header(resp, "Public", "ANNOUNCE, SETUP, RECORD, " - "PAUSE, FLUSH, TEARDOWN, " - "OPTIONS, GET_PARAMETER, SET_PARAMETER"); + msg_add_header(resp, "Public", + "ANNOUNCE, SETUP, RECORD, " + "PAUSE, FLUSH, TEARDOWN, " + "OPTIONS, GET_PARAMETER, SET_PARAMETER"); } void handle_teardown(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *req, @@ -929,17 +943,17 @@ void handle_flush(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { 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; @@ -1035,8 +1049,9 @@ void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { msg_add_header(resp, "Session", "1"); resp->respcode = 200; // it all worked out okay - debug(1, "Connection %d: SETUP DACP-ID \"%s\" from %s to %s with UDP ports Control: " - "%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); @@ -1265,8 +1280,6 @@ char *base64_encode_so(const unsigned char *data, size_t input_length, char *enc 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]; @@ -1294,7 +1307,6 @@ pc_queue metadata_multicast_queue; 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; @@ -1332,7 +1344,6 @@ void metadata_delete_multicast_socket(void) { free(metadata_sockmsg); } - void metadata_open(void) { if (config.metadata_enabled == 0) return; @@ -1354,7 +1365,8 @@ static void metadata_close(void) { } 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; @@ -1455,7 +1467,7 @@ void metadata_process(uint32_t type, uint32_t code, char *data, uint32_t length) 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); @@ -1473,7 +1485,7 @@ void metadata_process(uint32_t type, uint32_t code, char *data, uint32_t length) } } snprintf(thestring, 1024, "\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); @@ -1508,11 +1520,12 @@ void *metadata_thread_function(__attribute__((unused)) void *ignore) { 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."); } @@ -1530,8 +1543,8 @@ void metadata_multicast_thread_cleanup_function(__attribute__((unused)) void *ar 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); @@ -1539,13 +1552,20 @@ void *metadata_multicast_thread_function(__attribute__((unused)) void *ignore) { 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); } @@ -1553,10 +1573,8 @@ void *metadata_multicast_thread_function(__attribute__((unused)) void *ignore) { 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"); @@ -1582,14 +1600,14 @@ void *metadata_hub_thread_function(__attribute__((unused)) void *ignore) { // we check that it's not a "real" error. From the "man 2 open" page: // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO // open for reading." Which is okay. - if ((fd == -1) && (errno != ENXIO)) { - char errorstring[1024]; - strerror_r(errno, (char *)errorstring, sizeof(errorstring)); - debug(1, "metadata_hub_thread_function -- error %d (\"%s\") opening pipe: \"%s\".", errno, - (char *)errorstring, path); - warn("can not open metadata pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno, - (char *)errorstring, path); - } + if ((fd == -1) && (errno != ENXIO)) { + char errorstring[1024]; + strerror_r(errno, (char *)errorstring, sizeof(errorstring)); + debug(1, "metadata_hub_thread_function -- error %d (\"%s\") opening pipe: \"%s\".", errno, + (char *)errorstring, path); + warn("can not open metadata pipe -- error %d (\"%s\") opening pipe: \"%s\".", errno, + (char *)errorstring, path); + } free(path); // create a pc_queue for passing information to a threaded metadata handler @@ -1600,13 +1618,15 @@ void *metadata_hub_thread_function(__attribute__((unused)) void *ignore) { 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 @@ -1615,8 +1635,7 @@ void *metadata_hub_thread_function(__attribute__((unused)) void *ignore) { #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"); @@ -1634,15 +1653,19 @@ void *metadata_mqtt_thread_function(__attribute__((unused)) void *ignore) { 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); } @@ -1700,8 +1723,8 @@ void metadata_stop(void) { } } -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 @@ -1737,20 +1760,27 @@ int send_metadata_to_queue(pc_queue* queue, uint32_t type, uint32_t code, char * 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; @@ -1758,23 +1788,20 @@ int send_metadata_to_queue(pc_queue* queue, uint32_t type, uint32_t code, char * 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) { @@ -1838,7 +1865,7 @@ static void handle_set_parameter(rtsp_conn_info *conn, rtsp_message *req, rtsp_m char *ct = msg_get_header(req, "Content-Type"); if (ct) { -// debug(2, "SET_PARAMETER Content-Type:\"%s\".", ct); + // debug(2, "SET_PARAMETER Content-Type:\"%s\".", ct); #ifdef CONFIG_METADATA // It seems that the rtptime of the message is used as a kind of an ID that @@ -2136,7 +2163,7 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag 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); } @@ -2617,8 +2644,9 @@ static void *rtsp_conversation_thread_func(void *pconn) { if (strcmp(req->method, "OPTIONS") != 0) // the options message is very common, so don't log it until level 3 debug_level = 2; - debug(debug_level, "Connection %d: Received an RTSP Packet of type \"%s\":", - conn->connection_number, req->method), + debug(debug_level, + "Connection %d: Received an RTSP Packet of type \"%s\":", conn->connection_number, + req->method), debug_print_msg_headers(debug_level, req); apple_challenge(conn->fd, req, resp); @@ -2667,8 +2695,9 @@ static void *rtsp_conversation_thread_func(void *pconn) { if (conn->stop == 0) { int err = msg_write_response(conn->fd, resp); if (err) { - debug(1, "Connection %d: Unable to write an RTSP message response. Terminating the " - "connection.", + debug(1, + "Connection %d: Unable to write an RTSP message response. Terminating the " + "connection.", conn->connection_number); struct linger so_linger; so_linger.l_onoff = 1; // "true" @@ -2721,10 +2750,11 @@ static void *rtsp_conversation_thread_func(void *pconn) { if (reply == -1) { char errorstring[1024]; strerror_r(errno, (char *)errorstring, sizeof(errorstring)); - debug(1, "rtsp_read_request_response_bad_packet write response error %d: \"%s\".", errno, (char *)errorstring); + debug(1, "rtsp_read_request_response_bad_packet write response error %d: \"%s\".", errno, + (char *)errorstring); } else if (reply != (ssize_t)strlen(response_text)) { - debug(1, "rtsp_read_request_response_bad_packet write %d bytes requested but %d written.", strlen(response_text), - reply); + debug(1, "rtsp_read_request_response_bad_packet write %d bytes requested but %d written.", + strlen(response_text), reply); } } else { debug(1, "Connection %d: rtsp_read_request error %d, packet ignored.", @@ -2937,7 +2967,7 @@ void rtsp_listen_loop(void) { 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: @@ -2954,7 +2984,7 @@ void rtsp_listen_loop(void) { 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."); diff --git a/shairport.c b/shairport.c index 975b71fc..4ee45bc9 100644 --- a/shairport.c +++ b/shairport.c @@ -61,6 +61,7 @@ #endif #include "activity_monitor.h" +#include "audio.h" #include "common.h" #include "rtp.h" #include "rtsp.h" @@ -117,6 +118,8 @@ int daemonisewithout = 0; 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) { @@ -126,7 +129,6 @@ void print_version(void) { debug(1, "Can't print version string!"); } } - #ifdef CONFIG_SOXR pthread_t soxr_time_check_thread; void *soxr_time_check(__attribute__((unused)) void *arg) { @@ -419,15 +421,15 @@ int parse_options(int argc, char **argv) { // 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"; @@ -682,9 +684,9 @@ int parse_options(int argc, char **argv) { } 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. */ @@ -1313,113 +1315,114 @@ const char *pid_file_proc(void) { 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 @@ -1434,11 +1437,10 @@ void handle_sigchld(__attribute__((unused)) int sig) { } 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))) { @@ -1455,7 +1457,7 @@ int main(int argc, char **argv) { #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 @@ -1476,7 +1478,7 @@ int main(int argc, char **argv) { 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 @@ -1504,6 +1506,15 @@ int main(int argc, char **argv) { // 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 @@ -1598,13 +1609,16 @@ int main(int argc, char **argv) { 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 @@ -1651,14 +1665,19 @@ int main(int argc, char **argv) { 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); @@ -1709,8 +1728,8 @@ int main(int argc, char **argv) { #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