]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Move latency calculations to rtp.c. Remove checks for infeasible latencies from player.c.
authorMike Brady <4265913+mikebrady@users.noreply.github.com>
Wed, 28 Apr 2021 12:03:27 +0000 (13:03 +0100)
committerMike Brady <4265913+mikebrady@users.noreply.github.com>
Wed, 28 Apr 2021 12:03:27 +0000 (13:03 +0100)
player.c
rtp.c

index 78912e3be9c862d87ca3ad4d0add1ad326c452bd..7c0fd0077fe6cd477082a29eb552c77057e614dc 100644 (file)
--- a/player.c
+++ b/player.c
@@ -4,7 +4,7 @@
  * All rights reserved.
  *
  * Modifications for audio synchronisation
- * and related work, copyright (c) Mike Brady 2014 -- 2020
+ * and related work, copyright (c) Mike Brady 2014 -- 2021
  * All rights reserved.
  *
  * Permission is hereby granted, free of charge, to any person
@@ -641,31 +641,6 @@ int32_t rand_in_range(int32_t exclusive_range_limit) {
   return sp >> 32;
 }
 
-int get_and_check_effective_latency(rtsp_conn_info *conn, uint32_t *effective_latency,
-                                    double offset_time) {
-  // check that the overall effective latency remains positive and is not greater than the capacity
-  // of the buffers return 0 if okay, -1 if the latency would be negative, +1 if it would be too
-  // large
-
-  int result = 0;
-  if (offset_time >= 0.0) {
-    uint32_t latency_addition = (uint32_t)(offset_time * conn->input_rate);
-    // keep about one second of buffers back
-    if ((*effective_latency + latency_addition) <=
-        (conn->max_frames_per_packet * (BUFFER_FRAMES - config.minimum_free_buffer_headroom)))
-      *effective_latency += latency_addition;
-    else
-      result = 1;
-  } else {
-    uint32_t latency_reduction = (uint32_t)((-offset_time) * conn->input_rate);
-    if (latency_reduction <= *effective_latency)
-      *effective_latency -= latency_reduction;
-    else
-      result = -1;
-  }
-  return result;
-}
-
 static inline void process_sample(int32_t sample, char **outp, sps_format_t format, int volume,
                                   int dither, rtsp_conn_info *conn) {
   /*
@@ -1081,33 +1056,21 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
 
               // To calculate when the first packet will be played, we figure out the exact time the
               // packet should be played according to its timestamp and the reference time.
-              // We then need to add the desired latency, typically 88200 frames.
-
-              // Then we need to offset this by the backend latency offset. For example, if we knew
-              // that the audio back end has a latency of 100 ms, we would
-              // ask for the first packet to be emitted 100 ms earlier than it should, i.e. -4410
-              // frames, so that when it got through the audio back end,
+              // The desired latency, typically 88200 frames, will be calculated for in rtp.c,
+              // and any desired backend latency offset included in it there.
 
-              // if would be in sync. To do this, we would give it a latency offset of -0.1 sec,
-              // i.e. -4410 frames.
 
               uint64_t should_be_time;
-              uint32_t effective_latency = conn->latency;
 
-              get_and_check_effective_latency(conn, &effective_latency,
-                                              config.audio_backend_latency_offset);
-              // we are ignoring the returned status because it will be captured on subsequent
-              // frames, below.
-
-              // what we are asking for here is "what is the local time at which time the calculated
-              // frame should be played"
-
-              frame_to_local_time(conn->first_packet_timestamp +
-                                      effective_latency, // this will go modulo 2^32
+              frame_to_local_time(conn->first_packet_timestamp, // this will go modulo 2^32
                                   &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);
+
+              int64_t lt = conn->first_packet_time_to_play - local_time_now;
+
+              debug(1, "Connection %d: Lead time: %f seconds.", conn->connection_number,
+                    lt * 0.000000001);
 
               if (local_time_now > conn->first_packet_time_to_play) {
                 uint64_t lateness = local_time_now - conn->first_packet_time_to_play;
@@ -1130,39 +1093,9 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
               // 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
+              frame_to_local_time(conn->first_packet_timestamp, // this will go modulo 2^32
                                   &should_be_time, conn);
 
               int64_t change_in_should_be_time =
@@ -1315,50 +1248,13 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
       if (have_timestamp_timing_information(conn)) { // if we have a reference time
 
         uint64_t time_to_play;
-        uint32_t effective_latency = conn->latency;
-
-        switch (get_and_check_effective_latency(conn, &effective_latency,
-                                                config.audio_backend_latency_offset -
-                                                    config.audio_backend_buffer_desired_length)) {
-        case -1:
-          // this means that the latency is negative, i.e. the packet must be played before its
-          // time. This is clearly a mistake, and can arise if the combination of a large negative
-          // latency offset and the desired buffer length are greater than the latency chosen by the
-          // player. For example if the latency is 88200 frames (2 seconds), the
-          // audio_backend_latency_offset is -1.9 and the audio_backend_buffer_desired_length is 0.2
-          // the effective latency is -0.1, so the frame must be played 0.1 seconds before it is
-          // time-tagged for, and in reality before it arrives at the player.
-
-          // to deal with the problem, rather than block the packets, we'll just let 'em go...
-
-          //
-          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 an "
-                 "audio_backend_buffer_desired_length of %f seconds, would make the overall "
-                 "latency negative. No latency is used. Synchronisation may fail.",
-                 conn->latency, config.audio_backend_latency_offset,
-                 config.audio_backend_buffer_desired_length);
-            conn->unachievable_audio_backend_latency_offset_notified = 1;
-          };
-          time_to_play = local_time_now; // pretend the frame should be played now...
-          break;
-        case 1:
-          if (conn->unachievable_audio_backend_latency_offset_notified == 0) {
-            warn("Latency too long! An audio_backend_latency_offset of %f seconds combined with an "
-                 "audio_backend_buffer_desired_length of %f seconds  may exceed the frame "
-                 "buffering capacity.",
-                 config.audio_backend_latency_offset, config.audio_backend_buffer_desired_length);
-            conn->unachievable_audio_backend_latency_offset_notified = 1;
-          };
-          time_to_play = local_time_now; // pretend the frame should be played now...
-          break;
-        default:
-          frame_to_local_time(curframe->given_timestamp +
-                                  effective_latency, // this will go modulo 2^32
-                              &time_to_play, conn);
-          break;
-        }
+
+        // we must enable packets to be released early enough for the
+        // audio buffer to be filled to the desired length
+
+        uint32_t buffer_latency_offset = (uint32_t)(config.audio_backend_buffer_desired_length * conn->input_rate);
+        frame_to_local_time(curframe->given_timestamp - buffer_latency_offset, // this will go modulo 2^32
+                                &time_to_play, conn);
 
         if (local_time_now >= time_to_play) {
           do_wait = 0;
@@ -2134,7 +2030,7 @@ void *player_thread_func(void *arg) {
             die("Shairport Sync only supports 16 bit input");
           }
 
-          at_least_one_frame_seen = 1;
+          inbuflength *= conn->output_sample_ratio;
 
           // 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.
@@ -2146,22 +2042,7 @@ void *player_thread_func(void *arg) {
           // now, go back as far as the total latency less, say, 100 ms, and check the presence of
           // frames from then onwards
 
-          inbuflength *= conn->output_sample_ratio;
-          /*
-          uint32_t reference_timestamp;
-          uint64_t reference_timestamp_time, remote_reference_timestamp_time;
-          get_reference_timestamp_stuff(&reference_timestamp, &reference_timestamp_time,
-                                        &remote_reference_timestamp_time, conn); // types okay
-          */
-
-          // nt is the rtp timestamp of the first frame of the current packet of
-          // frames from the source.
-          // multiply it by the output frame ratio to get, effectively, the rtp timestamp
-          // of the first frame of the corresponding packet of output frames.
-
-          uint64_t nt;
-          nt = inframe->given_timestamp; // uint32_t to int64_t
-          nt = nt * conn->output_sample_ratio;
+          at_least_one_frame_seen = 1;
 
           uint64_t local_time_now = get_absolute_time_in_ns(); // types okay
 
@@ -2244,23 +2125,13 @@ void *player_thread_func(void *arg) {
 
             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;
+            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 = 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
+                should_be_frame - (inframe->given_timestamp * conn->output_sample_ratio -
+                                   current_delay); // all int64_t
 
-            sync_error =
-                delay - ((int64_t)conn->latency * conn->output_sample_ratio +
-                         (int64_t)(config.audio_backend_latency_offset *
-                                   config.output_rate)); // int64_t from int64_t - int32_t, so okay
+            sync_error = delay; // 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;
@@ -2361,7 +2232,8 @@ void *player_thread_func(void *arg) {
                 debug(2,
                       "Large negative sync error: %" PRId64 " with should_be_frame_32 of %" PRIu32
                       ", nt of %" PRId64 " and current_delay of %" PRId64 ".",
-                      sync_error, should_be_frame_32, nt, current_delay);
+                      sync_error, should_be_frame_32,
+                      inframe->given_timestamp * conn->output_sample_ratio, current_delay);
                 int64_t silence_length = -sync_error;
                 if (silence_length > (filler_length * 5))
                   silence_length = filler_length * 5;
@@ -2588,29 +2460,6 @@ void *player_thread_func(void *arg) {
             }
           } else {
 
-            // 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
-
-                        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 idelay = (int32_t)(should_be_frame_32 - inframe->given_timestamp);
-
-                        idelay = idelay - ilatency;
-
-                        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) {
diff --git a/rtp.c b/rtp.c
index c558645ee1df460ac60683000d6c88ddf0d892e8..87fd25ddf450ddee70d1d3ba6366b1333b43bea5 100644 (file)
--- a/rtp.c
+++ b/rtp.c
@@ -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 "
@@ -364,18 +364,30 @@ void *rtp_control_receiver(void *arg) {
                      la, max_frames, conn->latency);
               } else {
 
+                // here we have the latency but it does not yet account for the audio_backend_latency_offset
+                int32_t latency_offset = (uint32_t)(config.audio_backend_latency_offset * conn->input_rate);
+                int32_t adjusted_latency = latency_offset + (int32_t)la;
+                if ((adjusted_latency < 0) || (adjusted_latency > (int32_t)(conn->max_frames_per_packet * (BUFFER_FRAMES - config.minimum_free_buffer_headroom))))
+                  warn("audio_backend_latency_offset out of range -- ignored.");
+                else
+                  la = adjusted_latency;
+
                 if (la != conn->latency) {
                   conn->latency = la;
-                  debug(3,
-                        "New latency detected: %" PRIu32 ", sync latency: %" PRIu32
+                  debug(1,
+                        "New latency: %" PRIu32 ", sync latency: %" PRIu32
                         ", minimum latency: %" PRIu32 ", maximum "
-                        "latency: %" PRIu32 ", fixed offset: %" PRIu32 ".",
+                        "latency: %" PRIu32 ", fixed offset: %" PRIu32 ", audio_backend_latency_offset: %f.",
                         la, sync_rtp_timestamp - rtp_timestamp_less_latency, conn->minimum_latency,
-                        conn->maximum_latency, config.fixedLatencyOffset);
+                        conn->maximum_latency, config.fixedLatencyOffset, config.audio_backend_latency_offset);
                 }
               }
             }
 
+            // here, we apply the latency to the sync_rtp_timestamp
+
+            sync_rtp_timestamp = sync_rtp_timestamp - conn->latency;
+
             debug_mutex_lock(&conn->reference_time_mutex, 1000, 0);
 
             if (conn->initial_reference_time == 0) {