]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Use CLOCK_MONOLITHIC_RAW to avoid NTP effects, report raw and corrected timing from...
authorMike Brady <4265913+mikebrady@users.noreply.github.com>
Sun, 28 Nov 2021 16:08:48 +0000 (16:08 +0000)
committerMike Brady <4265913+mikebrady@users.noreply.github.com>
Sun, 28 Nov 2021 16:08:48 +0000 (16:08 +0000)
activity_monitor.c
audio.h
audio_alsa.c
common.c
common.h
dacp.c
nqptp-shm-structures.h
player.c
player.h
rtp.c
rtsp.c

index 1d301c492c87ed2792cc91e078e3c00ff1c218aa..deeab2b6f9a864f93ed8d187847e272d0d546ed0 100644 (file)
@@ -187,7 +187,7 @@ void *activity_monitor_thread_code(void *arg) {
         uint64_t time_to_wait_for_wakeup_ns = (uint64_t)(config.active_state_timeout * 1000000000);
 
 #ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
-        uint64_t time_of_wakeup_ns = get_absolute_time_in_ns() + time_to_wait_for_wakeup_ns;
+        uint64_t time_of_wakeup_ns = get_realtime_in_ns() + time_to_wait_for_wakeup_ns;
         sec = time_of_wakeup_ns / 1000000000;
         nsec = time_of_wakeup_ns % 1000000000;
         time_for_wait.tv_sec = sec;
diff --git a/audio.h b/audio.h
index 25742d7b05803f15cac7a222550d260ab89d97e7..5ff1419566e9dbc3143d061ae2b2bf44c5c89953 100644 (file)
--- a/audio.h
+++ b/audio.h
@@ -39,7 +39,8 @@ typedef struct {
   // will change dynamically, so keep watching it. Implemented in ALSA only.
   // returns a negative error code if there's a problem
   int (*delay)(long *the_delay); // snd_pcm_sframes_t is a signed long
-  int (*stats)(uint64_t *measurement_time, uint64_t *delay,
+  int (*stats)(uint64_t *raw_measurement_time, uint64_t *corrected_measurement_time,
+               uint64_t *delay,
                uint64_t *frames_sent_to_dac); // use this to get the true rate of the DAC
 
   // may be NULL, in which case soft volume is applied
index f2e0b1311f03f20c2de39c3508d1a654b65458c2..a007c41995884378e827bc090690fc039f3a717b 100644 (file)
@@ -60,8 +60,9 @@ static int play(void *buf, int samples);
 static void stop(void);
 static void flush(void);
 int delay(long *the_delay);
-int stats(uint64_t *the_time, uint64_t *the_delay, uint64_t *frames_sent_to_dac);
-// int get_rate_information(uint64_t *elapsed_time, uint64_t *frames_played);
+int stats(uint64_t *raw_measurement_time, uint64_t *corrected_measurement_time, uint64_t *the_delay,
+          uint64_t *frames_sent_to_dac);
+
 void *alsa_buffer_monitor_thread_code(void *arg);
 
 static void volume(double vol);
@@ -1633,7 +1634,8 @@ int delay(long *the_delay) {
   return ret;
 }
 
-int stats(uint64_t *the_time, uint64_t *the_delay, uint64_t *frames_sent_to_dac) {
+int stats(uint64_t *raw_measurement_time, uint64_t *corrected_measurement_time, uint64_t *the_delay,
+          uint64_t *frames_sent_to_dac) {
   // returns 0 if the device is in a valid state -- SND_PCM_STATE_RUNNING or
   // SND_PCM_STATE_PREPARED
   // or SND_PCM_STATE_DRAINING.
@@ -1654,7 +1656,9 @@ int stats(uint64_t *the_time, uint64_t *the_delay, uint64_t *frames_sent_to_dac)
   if (alsa_handle == NULL) {
     ret = ENODEV;
   } else {
-    *the_time = get_absolute_time_in_ns();
+    *raw_measurement_time =
+        get_absolute_time_in_ns(); // this is not conditioned ("disciplined") by NTP
+    *corrected_measurement_time = get_monotonic_time_in_ns(); // this is ("disciplined") by NTP
     ret = delay_and_status(&state, &my_delay, NULL);
   }
   if (ret == 0)
index a947be416baad62ee442d016726ca56d70eb338c..bbe36780370ea78fb36e7799888c6037edb74fe3 100644 (file)
--- a/common.c
+++ b/common.c
@@ -1210,29 +1210,27 @@ double vol2attn(double vol, long max_db, long min_db) {
   return vol_setting;
 }
 
-uint64_t get_absolute_time_in_fp() {
-  uint64_t 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 tnfpsec = tn.tv_sec;
-  if (tnfpsec > 0x100000000)
-    warn("clock_gettime seconds overflow!");
-  uint64_t tnfpnsec = tn.tv_nsec;
-  if (tnfpnsec > 0x100000000)
-    warn("clock_gettime nanoseconds seconds overflow!");
-  tnfpsec = tnfpsec << 32;
-  tnfpnsec = tnfpnsec << 32;
-  tnfpnsec = tnfpnsec / 1000000000;
-
-  time_now_fp = tnfpsec + tnfpnsec; // types okay
+  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.
@@ -1245,31 +1243,27 @@ uint64_t get_absolute_time_in_fp() {
     (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.
-
   if (sTimebaseInfo.denom == 0)
-    die("could not initialise Mac timebase info in get_absolute_time_in_fp().")
-        // this gives us nanoseconds
-        uint64_t time_now_ns = time_now_mach * sTimebaseInfo.numer / sTimebaseInfo.denom;
+    die("could not initialise Mac timebase info in get_monotonic_time_in_ns().")
 
-  // take the units and shift them to the upper half of the fp, and take the nanoseconds, shift them
-  // to the upper half and then divide the result to 1000000000
-  time_now_fp =
-      ((time_now_ns / 1000000000) << 32) + (((time_now_ns % 1000000000) << 32) / 1000000000);
+        // 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_fp;
+
+  return time_now_ns;
 }
 
-uint64_t get_monotonic_time_in_ns() {
+// all these clock things are now in macOS now since 10.13 (September 2017). Must update...
+uint64_t get_monotonic_raw_time_in_ns() {
+  // CLOCK_MONOTONIC_RAW in FreeBSD etc, monotonic in MacOSX
   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);
+  clock_gettime(CLOCK_MONOTONIC_RAW, &tn);
   uint64_t tnnsec = tn.tv_sec;
   tnnsec = tnnsec * 1000000000;
   uint64_t tnjnsec = tn.tv_nsec;
@@ -1308,10 +1302,11 @@ uint64_t get_monotonic_time_in_ns() {
   return time_now_ns;
 }
 
-uint64_t get_absolute_time_in_ns() {
+#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
+// Not defined for macOS
+uint64_t get_realtime_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.
@@ -1320,6 +1315,21 @@ uint64_t get_absolute_time_in_ns() {
   tnnsec = tnnsec * 1000000000;
   uint64_t tnjnsec = tn.tv_nsec;
   time_now_ns = tnnsec + tnjnsec;
+  return time_now_ns;
+}
+#endif
+
+uint64_t get_absolute_time_in_ns() {
+  // CLOCK_MONOTONIC_RAW in FreeBSD etc, monotonic in MacOSX
+  uint64_t time_now_ns;
+
+#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
+  struct timespec tn;
+  clock_gettime(CLOCK_MONOTONIC_RAW, &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
@@ -1521,8 +1531,8 @@ int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time,
   pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
 
   struct timespec timeoutTime;
-  uint64_t wait_until_time = dally_time * 1000;    // to nanoseconds
-  uint64_t start_time = get_absolute_time_in_ns(); // this is from CLOCK_REALTIME
+  uint64_t wait_until_time = dally_time * 1000; // to nanoseconds
+  uint64_t start_time = get_realtime_in_ns();   // this is from CLOCK_REALTIME
   wait_until_time = wait_until_time + start_time;
   uint64_t wait_until_sec = wait_until_time / 1000000000;
   uint64_t wait_until_nsec = wait_until_time % 1000000000;
@@ -1530,7 +1540,7 @@ int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time,
   timeoutTime.tv_nsec = wait_until_nsec;
 
   int r = pthread_mutex_timedlock(mutex, &timeoutTime);
-  uint64_t et = get_absolute_time_in_ns() - start_time;
+  uint64_t et = get_realtime_in_ns() - start_time;
 
   if ((debuglevel != 0) && (r != 0) && (debugmessage != NULL)) {
     char errstr[1000];
index 199b3d416704d96bf7dbdb311977d56d1e407b92..d9be03272aacccf4e3f4817d49360e49c4004567 100644 (file)
--- a/common.h
+++ b/common.h
@@ -379,9 +379,12 @@ double flat_vol2attn(double vol, long max_db, long min_db);
 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
+#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
+// Not defined for macOS
+uint64_t get_realtime_in_ns(void);
+#endif
+uint64_t get_absolute_time_in_ns(void);  // monotonic_raw or monotonic
+uint64_t get_monotonic_time_in_ns(void); // NTP-disciplined
 
 // time at startup for debugging timing
 extern uint64_t ns_time_at_startup, ns_time_at_last_debug_message;
diff --git a/dacp.c b/dacp.c
index ef2297e28d25f8547bc81155d27f708f7c215a21..d696413393cc09fea5a9507063866228c8fb5f0b 100644 (file)
--- a/dacp.c
+++ b/dacp.c
@@ -529,7 +529,7 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
           (uint64_t)(1000000000 * config.missing_port_dacp_scan_interval_seconds);
 
 #ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
-      uint64_t time_of_wakeup_ns = get_absolute_time_in_ns() + time_to_wait_for_wakeup_ns;
+      uint64_t time_of_wakeup_ns = get_realtime_in_ns() + time_to_wait_for_wakeup_ns;
       uint64_t sec = time_of_wakeup_ns / 1000000000;
       uint64_t nsec = time_of_wakeup_ns % 1000000000;
 #endif
index 36c8a8131da355033f21c30a320fed7a0fd1fd16..f4db8c6e291d95aeeaa83fe0e4a469c79a07f658 100644 (file)
@@ -22,7 +22,7 @@
 
 #define STORAGE_ID "/nqptp"
 #define MAX_CLOCKS 32
-#define NQPTP_SHM_STRUCTURES_VERSION 5
+#define NQPTP_SHM_STRUCTURES_VERSION 6
 #define NQPTP_CONTROL_PORT 9000
 
 // the control port will accept a UDP packet with the first letter being:
index 5e6e4133dd6420a508b8074cf2e52d2f76c02732..9b0b86ca66ec8eb75b7d2ef7b3c75d124bcdb82f 100644 (file)
--- a/player.c
+++ b/player.c
@@ -1299,7 +1299,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
       time_to_wait_for_wakeup_ns /= 3;       // two thirds of a packet time
 
 #ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
-      uint64_t time_of_wakeup_ns = local_time_now + time_to_wait_for_wakeup_ns;
+      uint64_t time_of_wakeup_ns = get_realtime_in_ns() + time_to_wait_for_wakeup_ns;
       uint64_t sec = time_of_wakeup_ns / 1000000000;
       uint64_t nsec = time_of_wakeup_ns % 1000000000;
 
@@ -1552,30 +1552,31 @@ int was_a_previous_column;
 int *statistics_print_profile;
 
 // these arrays specify which of the statistics specified by the statistics_item calls will actually
-// be printed -- 1 means print, 0 means skip
+// be printed -- 2 means print, 1 means print only in a debug mode, 0 means skip
 
 // don't display output fps -- not sure how accurate it is (change item 14 and 17 to 1 to restore)
-int ap1_synced_statistics_print_profile[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
-int ap1_nosync_statistics_print_profile[] = {1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0};
-int ap1_nodelay_statistics_print_profile[] = {0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0};
+int ap1_synced_statistics_print_profile[] = {2, 2, 2, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 1};
+int ap1_nosync_statistics_print_profile[] = {2, 0, 0, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 0, 0, 1, 0};
+int ap1_nodelay_statistics_print_profile[] = {0, 0, 0, 1, 2, 1, 1, 2, 0, 1, 1, 1, 1, 0, 0, 1, 0};
 
-int ap2_realtime_synced_stream_statistics_print_profile[] = {1, 1, 1, 1, 1, 1, 1, 1, 1,
-                                                             1, 1, 0, 0, 1, 0, 0, 0};
-int ap2_realtime_nosync_stream_statistics_print_profile[] = {1, 0, 0, 1, 1, 1, 1, 1, 1,
-                                                             1, 1, 0, 0, 0, 0, 0, 0};
-int ap2_realtime_nodelay_stream_statistics_print_profile[] = {0, 0, 0, 1, 1, 1, 1, 1, 0,
-                                                              1, 1, 0, 0, 0, 0, 0, 0};
+int ap2_realtime_synced_stream_statistics_print_profile[] = {2, 2, 2, 1, 2, 1, 1, 2, 1,
+                                                             1, 1, 1, 1, 2, 2, 0, 0};
+int ap2_realtime_nosync_stream_statistics_print_profile[] = {2, 0, 0, 1, 2, 1, 1, 2, 1,
+                                                             1, 1, 1, 1, 0, 0, 0, 0};
+int ap2_realtime_nodelay_stream_statistics_print_profile[] = {0, 0, 0, 1, 2, 1, 1, 2, 0,
+                                                              1, 1, 1, 1, 0, 0, 0, 0};
 
 // don't display output fps -- not sure how accurate it is (change item 14 1 to restore)
-int ap2_buffered_synced_stream_statistics_print_profile[] = {1, 1, 1, 1, 0, 0, 0, 0, 1,
-                                                             1, 1, 0, 0, 1, 0, 0, 0};
-int ap2_buffered_nosync_stream_statistics_print_profile[] = {1, 0, 0, 1, 0, 0, 0, 0, 1,
+int ap2_buffered_synced_stream_statistics_print_profile[] = {2, 2, 2, 1, 0, 0, 0, 0, 1,
+                                                             1, 1, 0, 0, 2, 2, 0, 0};
+int ap2_buffered_nosync_stream_statistics_print_profile[] = {2, 0, 0, 1, 0, 0, 0, 0, 1,
                                                              1, 1, 0, 0, 0, 0, 0, 0};
 int ap2_buffered_nodelay_stream_statistics_print_profile[] = {0, 0, 0, 1, 0, 0, 0, 0, 0,
                                                               1, 1, 0, 0, 0, 0, 0, 0};
 
 void statistics_item(const char *heading, const char *format, ...) {
-  if (statistics_print_profile[statistics_column] == 1) { // include this column?
+  if (((statistics_print_profile[statistics_column] == 1) && (debuglev != 0)) ||
+      (statistics_print_profile[statistics_column] == 2)) { // include this column?
     if (was_a_previous_column != 0)
       strcat(line_of_stats, " ");
     if (statistics_row == 0) {
@@ -1624,10 +1625,10 @@ void player_thread_cleanup_handler(void *arg) {
     int elapsedSec = rawSeconds % 60;
     if (conn->frame_rate_valid)
       inform("Connection %d: Playback Stopped. Total playing time %02d:%02d:%02d. Input: %0.2f, "
-             "output: %0.2f "
+             " Output: %0.2f (raw), %0.2f (corrected) "
              "frames per second.",
              conn->connection_number, elapsedHours, elapsedMin, elapsedSec, conn->input_frame_rate,
-             conn->frame_rate);
+             conn->raw_frame_rate, conn->corrected_frame_rate);
     else
       inform("Connection %d: Playback Stopped. Total playing time %02d:%02d:%02d. Input: %0.2f "
              "frames per second.",
@@ -1727,7 +1728,8 @@ 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;
+  uint64_t previous_raw_measurement_time;
+  uint64_t previous_corrected_measurement_time;
   int previous_frames_played_valid = 0;
 
   // pthread_cleanup_push(player_thread_initial_cleanup_handler, arg);
@@ -1888,7 +1890,8 @@ void *player_thread_func(void *arg) {
 
   conn->playstart = time(NULL);
 
-  conn->frame_rate = 0.0;
+  conn->raw_frame_rate = 0.0;
+  conn->corrected_frame_rate = 0.0;
   conn->frame_rate_valid = 0;
 
   conn->input_frame_rate = 0.0;
@@ -2335,10 +2338,12 @@ void *player_thread_func(void *arg) {
             int stats_status = 0;
             if ((config.output->delay) && (config.no_sync == 0) && (config.output->stats)) {
               uint64_t frames_sent_for_play;
-              uint64_t measurement_time;
+              uint64_t raw_measurement_time;
+              uint64_t corrected_measurement_time;
               uint64_t actual_delay;
               stats_status =
-                  config.output->stats(&measurement_time, &actual_delay, &frames_sent_for_play);
+                  config.output->stats(&raw_measurement_time, &corrected_measurement_time,
+                                       &actual_delay, &frames_sent_for_play);
               // debug(1,"status: %d, actual_delay: %" PRIu64 ", frames_sent_for_play: %" PRIu64 ",
               // frames_played: %" PRIu64 ".", stats_status, actual_delay, frames_sent_for_play,
               // frames_sent_for_play - actual_delay);
@@ -2347,9 +2352,13 @@ void *player_thread_func(void *arg) {
               // last time the stats call was made. Thus, the frame rate should be valid.
               if ((stats_status == 0) && (previous_frames_played_valid != 0)) {
                 uint64_t frames_played_in_this_interval = frames_played - previous_frames_played;
-                int64_t interval = measurement_time - previous_frames_played_time;
-                if (interval != 0) {
-                  conn->frame_rate = (1e9 * frames_played_in_this_interval) / interval;
+                int64_t raw_interval = raw_measurement_time - previous_raw_measurement_time;
+                int64_t corrected_interval =
+                    corrected_measurement_time - previous_corrected_measurement_time;
+                if (raw_interval != 0) {
+                  conn->raw_frame_rate = (1e9 * frames_played_in_this_interval) / raw_interval;
+                  conn->corrected_frame_rate =
+                      (1e9 * frames_played_in_this_interval) / corrected_interval;
                   conn->frame_rate_valid = 1;
                   // debug(1,"frames_played_in_this_interval: %" PRIu64 ", interval: %" PRId64 ",
                   // rate: %f.",
@@ -2365,7 +2374,8 @@ void *player_thread_func(void *arg) {
                 if (stats_status != 0)
                   conn->frame_rate_valid = 0;
                 previous_frames_played = frames_played;
-                previous_frames_played_time = measurement_time;
+                previous_raw_measurement_time = raw_measurement_time;
+                previous_corrected_measurement_time = corrected_measurement_time;
                 previous_frames_played_valid = 1;
               }
             }
@@ -2411,15 +2421,19 @@ void *player_thread_func(void *arg) {
                   statistics_item("min buffers", "%*" PRIu32 "", 11, minimum_buffer_occupancy);
                   statistics_item("max buffers", "%*" PRIu32 "", 11, maximum_buffer_occupancy);
                   statistics_item("nominal fps", "%*.2f", 11, conn->remote_frame_rate);
-                  statistics_item(" actual fps", "%*.2f", 11, conn->input_frame_rate);
-                  if (conn->frame_rate_valid)
-                    statistics_item(" output fps", "%*.2f", 11, conn->frame_rate);
-                  else
-                    statistics_item(" output fps", "        N/A");
+                  statistics_item("received fps", "%*.2f", 12, conn->input_frame_rate);
+                  if (conn->frame_rate_valid) {
+                    statistics_item("output fps (r)", "%*.2f", 14, conn->raw_frame_rate);
+                    statistics_item("output fps (c)", "%*.2f", 14, conn->corrected_frame_rate);
+                  } else {
+                    statistics_item("output fps (r)", "           N/A");
+                    statistics_item("output fps (c)", "           N/A");
+                  }
                   statistics_item("source drift ppm", "%*.2f", 16,
                                   (conn->local_to_remote_time_gradient - 1.0) * 1000000);
                   statistics_item("drift samples", "%*d", 13,
                                   conn->local_to_remote_time_gradient_sample_count);
+                  /*
                   statistics_item("estimated (unused) correction ppm", "%*.2f",
                                   strlen("estimated (unused) correction ppm"),
                                   (conn->frame_rate_valid != 0)
@@ -2429,6 +2443,7 @@ void *player_thread_func(void *arg) {
                                          1000000) /
                                             conn->frame_rate
                                       : 0.0);
+                  */
                   statistics_row++;
                   inform(line_of_stats);
                 } while (statistics_row < 2);
index b08a379efc8631606c7e8922b0d8e92f7c0f85dd..d37f00c7604414b733c63eff2d33653eb5df7e0d 100644 (file)
--- a/player.h
+++ b/player.h
@@ -180,7 +180,8 @@ typedef struct {
   stats_t *statistics;
 
   // for holding the output rate information until printed out at the end of a session
-  double frame_rate;
+  double raw_frame_rate;
+  double corrected_frame_rate;
   int frame_rate_valid;
 
   // for holding input rate information until printed out at the end of a session
diff --git a/rtp.c b/rtp.c
index a79e0bbd922366f0fb57bc87fc238769a5abc8ac..5b79ff4b970da813385612643eff80cccb7f91ff 100644 (file)
--- a/rtp.c
+++ b/rtp.c
@@ -1762,7 +1762,7 @@ void rtp_realtime_audio_cleanup_handler(__attribute__((unused)) void *arg) {
   debug(2, "Realtime Audio Receiver Cleanup Start.");
   rtsp_conn_info *conn = (rtsp_conn_info *)arg;
   close(conn->realtime_audio_socket);
-  debug(1, "Connection %d: closing realtime audio port %u", conn->local_realtime_audio_port);
+  debug(2, "Connection %d: closing realtime audio port %u", conn->local_realtime_audio_port);
   conn->realtime_audio_socket = 0;
   debug(2, "Realtime Audio Receiver Cleanup Done.");
 }
diff --git a/rtsp.c b/rtsp.c
index 5f9fa9ab8ac138934e651f79ad7d81e9df765150..079ad14d998d330338eacef5782c331f92cd0b48 100644 (file)
--- a/rtsp.c
+++ b/rtsp.c
@@ -583,7 +583,11 @@ void *player_watchdog_thread_code(void *arg) {
   do {
     usleep(2000000); // check every two seconds
     // debug(3, "Connection %d: Check the thread is doing something...", conn->connection_number);
+#ifdef CONFIG_AIRPLAY_2
+    if ((config.dont_check_timeout == 0) && (config.timeout != 0) && (conn->airplay_type == ap_1)) {
+#else
     if ((config.dont_check_timeout == 0) && (config.timeout != 0)) {
+#endif
       debug_mutex_lock(&conn->watchdog_mutex, 1000, 0);
       uint64_t last_watchdog_bark_time = conn->watchdog_bark_time;
       debug_mutex_unlock(&conn->watchdog_mutex, 0);