From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 8 Nov 2021 10:14:42 +0000 (+0000) Subject: Try using the monotonic clock for checking output rate... X-Git-Tag: 4.1-rc1~24^2~378^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=04240708a948cfb8a8424bb7d8aceaef6359c2f8;p=thirdparty%2Fshairport-sync.git Try using the monotonic clock for checking output rate... --- diff --git a/common.c b/common.c index 866222c2..b1525a31 100644 --- a/common.c +++ b/common.c @@ -55,9 +55,9 @@ #endif #ifdef COMPILE_FOR_FREEBSD -#include -#include #include +#include +#include #endif #ifdef COMPILE_FOR_OSX @@ -1258,6 +1258,49 @@ uint64_t get_absolute_time_in_fp() { return time_now_fp; } +uint64_t get_monotonic_time_in_ns() { + uint64_t time_now_ns; + +#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD + struct timespec tn; + // can't use CLOCK_MONOTONIC_RAW as it's not implemented in OpenWrt + // CLOCK_REALTIME because PTP uses it. + clock_gettime(CLOCK_MONOTONIC, &tn); + uint64_t tnnsec = tn.tv_sec; + tnnsec = tnnsec * 1000000000; + uint64_t tnjnsec = tn.tv_nsec; + time_now_ns = tnnsec + tnjnsec; +#endif + +#ifdef COMPILE_FOR_OSX + uint64_t time_now_mach; + uint64_t elapsedNano; + static mach_timebase_info_data_t sTimebaseInfo = {0, 0}; + + // this actually give you a monotonic clock + // see https://news.ycombinator.com/item?id=6303755 + time_now_mach = mach_absolute_time(); + + // If this is the first time we've run, get the timebase. + // We can use denom == 0 to indicate that sTimebaseInfo is + // uninitialised because it makes no sense to have a zero + // denominator in a fraction. + + if (sTimebaseInfo.denom == 0) { + debug(1, "Mac initialise timebase info."); + (void)mach_timebase_info(&sTimebaseInfo); + } + + // Do the maths. We hope that the multiplication doesn't + // overflow; the price you pay for working in fixed point. + + // this gives us nanoseconds + time_now_ns = time_now_mach * sTimebaseInfo.numer / sTimebaseInfo.denom; +#endif + + return time_now_ns; +} + uint64_t get_absolute_time_in_ns() { uint64_t time_now_ns; @@ -1277,6 +1320,7 @@ uint64_t get_absolute_time_in_ns() { uint64_t elapsedNano; static mach_timebase_info_data_t sTimebaseInfo = {0, 0}; + // this actually give you a monotonic clock time_now_mach = mach_absolute_time(); // If this is the first time we've run, get the timebase. @@ -1963,7 +2007,7 @@ int get_device_id(uint8_t *id, int int_length) { t = id; int found = 0; for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { - #ifdef AF_PACKET +#ifdef AF_PACKET if ((ifa->ifa_addr) && (ifa->ifa_addr->sa_family == AF_PACKET)) { struct sockaddr_ll *s = (struct sockaddr_ll *)ifa->ifa_addr; if ((strcmp(ifa->ifa_name, "lo") != 0) && (found == 0)) { @@ -1973,25 +2017,23 @@ int get_device_id(uint8_t *id, int int_length) { found = 1; } } - #else - #ifdef AF_LINK - struct sockaddr_dl * sdl = (struct sockaddr_dl *) ifa->ifa_addr; - if ((sdl) && (sdl->sdl_family == AF_LINK)) { +#else +#ifdef AF_LINK + struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr; + if ((sdl) && (sdl->sdl_family == AF_LINK)) { if (sdl->sdl_type == IFT_ETHER) { char *s = LLADDR(sdl); for (i = 0; i < sdl->sdl_alen; i++) { - debug(1,"char %d: \"%c\".", i, *s); - *t++ = (uint8_t)*s++; - } + debug(1, "char %d: \"%c\".", i, *s); + *t++ = (uint8_t)*s++; + } found = 1; } } - #endif - #endif - +#endif +#endif } freeifaddrs(ifaddr); } return response; } - diff --git a/common.h b/common.h index 9c3496f6..199b3d41 100644 --- a/common.h +++ b/common.h @@ -381,6 +381,7 @@ double vol2attn(double vol, long max_db, long min_db); // return a time in nanoseconds // uint64_t get_absolute_time_in_fp(void); // obselete uint64_t get_absolute_time_in_ns(void); +uint64_t get_monotonic_time_in_ns(void); // to try and get precise FPS values // time at startup for debugging timing extern uint64_t ns_time_at_startup, ns_time_at_last_debug_message; diff --git a/player.c b/player.c index 74c6eac2..e19e6cab 100644 --- a/player.c +++ b/player.c @@ -1714,12 +1714,11 @@ void player_thread_cleanup_handler(void *arg) { void *player_thread_func(void *arg) { rtsp_conn_info *conn = (rtsp_conn_info *)arg; - + uint64_t previous_frames_played; uint64_t previous_frames_played_time; int previous_frames_played_valid = 0; - // pthread_cleanup_push(player_thread_initial_cleanup_handler, arg); conn->packet_count = 0; conn->packet_count_since_flush = 0; @@ -2787,6 +2786,7 @@ void *player_thread_func(void *arg) { uint64_t frames_sent_for_play; int status = -1; if ((config.output->delay) && (config.no_sync == 0) && (config.output->rate_info)) { + uint64_t monotonic_time_now = get_monotonic_time_in_ns(); uint64_t elapsed_play_time; // dummy status = config.output->rate_info(&elapsed_play_time, &frames_sent_for_play); uint64_t frames_played = frames_sent_for_play - play_samples - current_delay; @@ -2794,22 +2794,22 @@ void *player_thread_func(void *arg) { // last time the rate_info call was made. Thus, the frame rate should be valid. if ((status == 0) && (previous_frames_played_valid)) { uint64_t frames_played_in_this_interval = frames_played - previous_frames_played; - uint64_t interval = local_time_now - previous_frames_played_time; + uint64_t interval = monotonic_time_now - previous_frames_played_time; conn->frame_rate = (1e9 * frames_played_in_this_interval) / interval; conn->frame_rate_valid = 1; } - + // uncomment the if statement if your want to get as long a period for // calculating the frame rate // if ((status != 0) || (previous_frames_played_valid == 0)) { - // if we have just detected an outputting error, or if we have no - // starting information - previous_frames_played = frames_played; - previous_frames_played_time = local_time_now; - previous_frames_played_valid = 1; - // } + // if we have just detected an outputting error, or if we have no + // starting information + previous_frames_played = frames_played; + previous_frames_played_time = monotonic_time_now; + previous_frames_played_valid = 1; + //} } - + // we can now calculate running averages for sync error (frames), corrections (ppm), // insertions plus deletions (ppm), drift (ppm) double moving_average_sync_error = (1.0 * tsum_of_sync_errors) / number_of_statistics; @@ -2931,7 +2931,7 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c else sw_min_db = (sw_max_db - desired_sw_range); } else { - hw_min_db = hw_max_db - desired_range_db; + hw_min_db = hw_max_db - desired_range_db; } } } else { diff --git a/player.h b/player.h index d5719031..41cdc5bd 100644 --- a/player.h +++ b/player.h @@ -109,7 +109,6 @@ typedef enum { remote_control_stream } airplay_stream_c; // "c" for category - #ifdef CONFIG_AIRPLAY_2 typedef enum { ts_ntp, ts_ptp } timing_t; typedef enum { ap_1, ap_2 } airplay_t; @@ -296,7 +295,8 @@ typedef struct { clock_status_t clock_status; airplay_stream_c - airplay_stream_category; // is it a remote control stream or a normal "full service" stream? (will be unspecified if not build for AirPlay 2) + airplay_stream_category; // is it a remote control stream or a normal "full service" stream? + // (will be unspecified if not build for AirPlay 2) #ifdef CONFIG_AIRPLAY_2 char *airplay_gid; // UUID in the Bonjour advertisement -- if NULL, the group UUID is the same as