]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Check for valid time information more completely.
authorMike Brady <4265913+mikebrady@users.noreply.github.com>
Sat, 30 May 2026 21:41:57 +0000 (22:41 +0100)
committerMike Brady <4265913+mikebrady@users.noreply.github.com>
Sat, 30 May 2026 21:41:57 +0000 (22:41 +0100)
ap2_buffered_audio_processor.c

index 66067dd424f4d4ea07d9e34706352b7328083e1a..e7fa237fc08e3764bd44b796d0846eecdddc1c8a 100644 (file)
@@ -495,196 +495,195 @@ void *rtp_buffered_audio_processor(void *arg) {
 
         // But it also means that Shairport Sync's buffers must be sufficient to hold all the
         // entire lead-time's amount of audio in case the device has a zero-sized buffer.
-
-        if ((play_enabled != 0) &&
-            (have_valid_time != 0)
-            //            (audio_decoded_buffer_below_desired_length != 0) &&
-            && (lead_time * 1E-9 < (config.audio_decoded_buffer_desired_length + 0.1))
-            //            && (audio_decoded_buffer_below_desired_length != 0)
-        ) {
-
-          very_early_packets_signalled = 0; // reset very early packet warning signaller
-
-          // try to identify blocks that are timed to before the last buffer, and drop 'em
-          int64_t time_from_last_buffer_time =
-              buffer_should_be_time - previous_buffer_should_be_time;
-
-          if ((packets_played_in_this_sequence == 0) || (time_from_last_buffer_time > 0)) {
-
-            payload_length = 0;
-            if (ssrc_is_recognised(payload_ssrc) != 0) {
-              // prepare_decoding_chain(conn, payload_ssrc);
-              unsigned long long new_payload_length = 0;
-              payload_pointer = m + leading_free_space_length;
-              if (lead_time >= 0) { // only decipher the packet if it's not too late
-                int response = -1;  // guess that there is a problem
-                if (conn->session_key != NULL) {
-                  unsigned char nonce[12];
-                  memset(nonce, 0, sizeof(nonce));
-                  memcpy(
-                      nonce + 4, packet + nread - 8,
-                      8); // front-pad the 8-byte nonce received to get the 12-byte nonce expected
-
-                  // https://libsodium.gitbook.io/doc/secret-key_cryptography/aead/chacha20-poly1305/ietf_chacha20-poly1305_construction
-                  // Note: the eight-byte nonce must be front-padded out to 12 bytes.
-
-                  // Leave leading_free_space_length bytes at the start for possible headers like an
-                  // ADTS header (7 bytes)
-                  memset(m, 0, leading_free_space_length);
-                  response = crypto_aead_chacha20poly1305_ietf_decrypt(
-                      payload_pointer,     // where the decrypted payload will start
-                      &new_payload_length, // mlen_p
-                      NULL,                // nsec,
-                      packet +
-                          12, // the ciphertext starts 12 bytes in and is followed by the MAC tag,
-                      nread - (8 + 12), // clen -- the last 8 bytes are the nonce
-                      packet + 4,       // authenticated additional data
-                      8,                // authenticated additional data length
-                      nonce,
-                      conn->session_key); // *k
-                  if (response != 0)
-                    debug(1, "Error decrypting audio packet %u -- packet length %zd.", seq_no,
-                          nread);
-                } else {
-                  debug(2, "No session key, so the audio packet can not be deciphered -- skipped.");
-                }
-
-                if ((response == 0) && (new_payload_length > 0)) {
-                  // now we have the deciphered block, so send it to the player if we can
-                  payload_length = new_payload_length;
-
-                  if (ssrc_is_aac(payload_ssrc)) {
-                    payload_pointer =
-                        payload_pointer - 7; // including the 7-byte leader for the ADTS
-                    payload_length = payload_length + 7;
-
-                    // now, fill in the 7-byte ADTS information, which seems to be needed by the
-                    // decoder we made room for it in the front of the buffer by filling from m + 7.
-                    int channelConfiguration = 2; // 2: 2 channels: front-left, front-right
-                    if (payload_ssrc == AAC_48000_F24_5P1)
-                      channelConfiguration = 6; // 6: 6 channels: front-center, front-left,
-                                                // front-right, back-left, back-right, LFE-channel
-                    else if (payload_ssrc == AAC_48000_F24_7P1)
-                      channelConfiguration =
-                          7; // 7: 8 channels: front-center, front-left, front-right,
-                             // side-left, side-right, back-left, back-right, LFE-channel
-                    addADTStoPacket(payload_pointer, payload_length, conn->input_rate,
-                                    channelConfiguration);
-                  }
-                  int mute =
-                      ((packets_played_in_this_sequence == 0) && (ssrc_is_aac(payload_ssrc)));
-                  if (mute) {
-                    debug(2, "Connection %d: muting first AAC block -- block %u -- timestamp %u.",
-                          conn->connection_number, seq_no, timestamp);
-                  }
-                  int32_t timestamp_difference = 0;
-                  if (packets_played_in_this_sequence == 0) {
-                    // first_block_in_this_sequence = seq_no;
-                    first_timestamp_in_this_sequence = timestamp;
-                    debug(2,
-                          "Connection %d: "
-                          "first block %u, first timestamp %u.",
-                          conn->connection_number, seq_no, timestamp);
+        
+        if (have_valid_time != 0) {
+          if ((play_enabled != 0) && (lead_time * 1E-9 < (config.audio_decoded_buffer_desired_length + 0.1))) {
+              //            && (audio_decoded_buffer_below_desired_length != 0)  
+            very_early_packets_signalled = 0; // reset very early packet warning signaller
+  
+            // try to identify blocks that are timed to before the last buffer, and drop 'em
+            int64_t time_from_last_buffer_time =
+                buffer_should_be_time - previous_buffer_should_be_time;
+  
+            if ((packets_played_in_this_sequence == 0) || (time_from_last_buffer_time > 0)) {
+  
+              payload_length = 0;
+              if (ssrc_is_recognised(payload_ssrc) != 0) {
+                // prepare_decoding_chain(conn, payload_ssrc);
+                unsigned long long new_payload_length = 0;
+                payload_pointer = m + leading_free_space_length;
+                if (lead_time >= 0) { // only decipher the packet if it's not too late
+                  int response = -1;  // guess that there is a problem
+                  if (conn->session_key != NULL) {
+                    unsigned char nonce[12];
+                    memset(nonce, 0, sizeof(nonce));
+                    memcpy(
+                        nonce + 4, packet + nread - 8,
+                        8); // front-pad the 8-byte nonce received to get the 12-byte nonce expected
+  
+                    // https://libsodium.gitbook.io/doc/secret-key_cryptography/aead/chacha20-poly1305/ietf_chacha20-poly1305_construction
+                    // Note: the eight-byte nonce must be front-padded out to 12 bytes.
+  
+                    // Leave leading_free_space_length bytes at the start for possible headers like an
+                    // ADTS header (7 bytes)
+                    memset(m, 0, leading_free_space_length);
+                    response = crypto_aead_chacha20poly1305_ietf_decrypt(
+                        payload_pointer,     // where the decrypted payload will start
+                        &new_payload_length, // mlen_p
+                        NULL,                // nsec,
+                        packet +
+                            12, // the ciphertext starts 12 bytes in and is followed by the MAC tag,
+                        nread - (8 + 12), // clen -- the last 8 bytes are the nonce
+                        packet + 4,       // authenticated additional data
+                        8,                // authenticated additional data length
+                        nonce,
+                        conn->session_key); // *k
+                    if (response != 0)
+                      debug(1, "Error decrypting audio packet %u -- packet length %zd.", seq_no,
+                            nread);
                   } else {
-                    timestamp_difference = timestamp - expected_timestamp;
-                    if (timestamp_difference != 0) {
+                    debug(2, "No session key, so the audio packet can not be deciphered -- skipped.");
+                  }
+  
+                  if ((response == 0) && (new_payload_length > 0)) {
+                    // now we have the deciphered block, so send it to the player if we can
+                    payload_length = new_payload_length;
+  
+                    if (ssrc_is_aac(payload_ssrc)) {
+                      payload_pointer =
+                          payload_pointer - 7; // including the 7-byte leader for the ADTS
+                      payload_length = payload_length + 7;
+  
+                      // now, fill in the 7-byte ADTS information, which seems to be needed by the
+                      // decoder we made room for it in the front of the buffer by filling from m + 7.
+                      int channelConfiguration = 2; // 2: 2 channels: front-left, front-right
+                      if (payload_ssrc == AAC_48000_F24_5P1)
+                        channelConfiguration = 6; // 6: 6 channels: front-center, front-left,
+                                                  // front-right, back-left, back-right, LFE-channel
+                      else if (payload_ssrc == AAC_48000_F24_7P1)
+                        channelConfiguration =
+                            7; // 7: 8 channels: front-center, front-left, front-right,
+                               // side-left, side-right, back-left, back-right, LFE-channel
+                      addADTStoPacket(payload_pointer, payload_length, conn->input_rate,
+                                      channelConfiguration);
+                    }
+                    int mute =
+                        ((packets_played_in_this_sequence == 0) && (ssrc_is_aac(payload_ssrc)));
+                    if (mute) {
+                      debug(2, "Connection %d: muting first AAC block -- block %u -- timestamp %u.",
+                            conn->connection_number, seq_no, timestamp);
+                    }
+                    int32_t timestamp_difference = 0;
+                    if (packets_played_in_this_sequence == 0) {
+                      // first_block_in_this_sequence = seq_no;
+                      first_timestamp_in_this_sequence = timestamp;
                       debug(2,
                             "Connection %d: "
-                            "unexpected timestamp in block %u. Actual: %u, expected: %u "
-                            "difference: %d, "
-                            "%f ms. "
-                            "Positive means later, i.e. a gap. First timestamp was %u, payload "
-                            "type: \"%s\".",
-                            conn->connection_number, seq_no, timestamp, expected_timestamp,
-                            timestamp_difference, 1000.0 * timestamp_difference / conn->input_rate,
-                            first_timestamp_in_this_sequence, get_ssrc_name(payload_ssrc));
-                      // mute the first packet after a discontinuity
-                      if (ssrc_is_aac(payload_ssrc)) {
+                            "first block %u, first timestamp %u.",
+                            conn->connection_number, seq_no, timestamp);
+                    } else {
+                      timestamp_difference = timestamp - expected_timestamp;
+                      if (timestamp_difference != 0) {
                         debug(2,
-                              "Connection %d: muting first AAC block -- block %u -- following a "
-                              "timestamp discontinuity, timestamp %u.",
-                              conn->connection_number, seq_no, timestamp);
-                        mute = 1;
+                              "Connection %d: "
+                              "unexpected timestamp in block %u. Actual: %u, expected: %u "
+                              "difference: %d, "
+                              "%f ms. "
+                              "Positive means later, i.e. a gap. First timestamp was %u, payload "
+                              "type: \"%s\".",
+                              conn->connection_number, seq_no, timestamp, expected_timestamp,
+                              timestamp_difference, 1000.0 * timestamp_difference / conn->input_rate,
+                              first_timestamp_in_this_sequence, get_ssrc_name(payload_ssrc));
+                        // mute the first packet after a discontinuity
+                        if (ssrc_is_aac(payload_ssrc)) {
+                          debug(2,
+                                "Connection %d: muting first AAC block -- block %u -- following a "
+                                "timestamp discontinuity, timestamp %u.",
+                                conn->connection_number, seq_no, timestamp);
+                          mute = 1;
+                        }
                       }
                     }
-                  }
-                  int skip_this_block = 0;
-                  if (timestamp_difference < 0) {
-
-                    // uncomment this to work back to replace buffers that have been already decoded
-                    // and placed in the player queue with the incoming new buffers this is a bit
-                    // trickier, but maybe the new buffers are better than the previous ones they
-                    // will replace (?)
-                    /*
-                    seq_t revised_seqno = get_revised_seqno(conn, timestamp);
-                    if (revised_seqno != sequence_number_for_player) {
-                      debug(1, "revised seqno calculated: conn->ab_read: %u, revised_seqno: %u,
-                    conn->ab_write: %u.", conn->ab_read, revised_seqno, conn->ab_write);
-                      clear_buffers_from(conn, revised_seqno);
-                      sequence_number_for_player = revised_seqno;
-                      timestamp_difference = 0;
+                    int skip_this_block = 0;
+                    if (timestamp_difference < 0) {
+  
+                      // uncomment this to work back to replace buffers that have been already decoded
+                      // and placed in the player queue with the incoming new buffers this is a bit
+                      // trickier, but maybe the new buffers are better than the previous ones they
+                      // will replace (?)
+                      /*
+                      seq_t revised_seqno = get_revised_seqno(conn, timestamp);
+                      if (revised_seqno != sequence_number_for_player) {
+                        debug(1, "revised seqno calculated: conn->ab_read: %u, revised_seqno: %u,
+                      conn->ab_write: %u.", conn->ab_read, revised_seqno, conn->ab_write);
+                        clear_buffers_from(conn, revised_seqno);
+                        sequence_number_for_player = revised_seqno;
+                        timestamp_difference = 0;
+                      }
+                      */
+  
+                      // uncomment this to drop incoming new buffers that are too old and for whose
+                      // timings buffers have already been decoded and placed in the player queue this
+                      // is easier, but maybe the new late buffers are better than the previous ones
+                      // (?)
+  
+                      int32_t abs_timestamp_difference = -timestamp_difference;
+                      if ((size_t)abs_timestamp_difference > get_ssrc_block_length(payload_ssrc)) {
+                        skip_this_block = 1;
+                        debug(2,
+                              "skipping block %u because it is too old. Timestamp "
+                              "difference: %d, length of block: %zu.",
+                              seq_no, timestamp_difference, get_ssrc_block_length(payload_ssrc));
+                      }
                     }
-                    */
-
-                    // uncomment this to drop incoming new buffers that are too old and for whose
-                    // timings buffers have already been decoded and placed in the player queue this
-                    // is easier, but maybe the new late buffers are better than the previous ones
-                    // (?)
-
-                    int32_t abs_timestamp_difference = -timestamp_difference;
-                    if ((size_t)abs_timestamp_difference > get_ssrc_block_length(payload_ssrc)) {
-                      skip_this_block = 1;
-                      debug(2,
-                            "skipping block %u because it is too old. Timestamp "
-                            "difference: %d, length of block: %zu.",
-                            seq_no, timestamp_difference, get_ssrc_block_length(payload_ssrc));
+                    if (skip_this_block == 0) {
+                      uint32_t packet_size = player_put_packet(
+                          payload_ssrc, sequence_number_for_player, timestamp, payload_pointer,
+                          payload_length, mute, timestamp_difference, conn);
+                      debug(4, "block %u, timestamp %u, length %u sent to the player.", seq_no,
+                            timestamp, packet_size);
+                      sequence_number_for_player++;                 // simply increment
+                      expected_timestamp = timestamp + packet_size; // for the next time
+                      packets_played_in_this_sequence++;
                     }
                   }
-                  if (skip_this_block == 0) {
-                    uint32_t packet_size = player_put_packet(
-                        payload_ssrc, sequence_number_for_player, timestamp, payload_pointer,
-                        payload_length, mute, timestamp_difference, conn);
-                    debug(4, "block %u, timestamp %u, length %u sent to the player.", seq_no,
-                          timestamp, packet_size);
-                    sequence_number_for_player++;                 // simply increment
-                    expected_timestamp = timestamp + packet_size; // for the next time
-                    packets_played_in_this_sequence++;
+                } else {
+                  debug(3,
+                        "skipped deciphering block %u with timestamp %u because its lead time is "
+                        "out of range at %f "
+                        "seconds.",
+                        seq_no, timestamp, lead_time * 1.0E-9);
+                  uint32_t currentAnchorRTP = 0;
+                  uint64_t currentAnchorLocalTime = 0;
+                  if (get_ptp_anchor_local_time_info(conn, &currentAnchorRTP,
+                                                     &currentAnchorLocalTime) == clock_ok) {
+                    debug(3, "anchorRTP: %u, anchorLocalTime: %" PRIu64 ".", currentAnchorRTP,
+                          currentAnchorLocalTime);
+                  } else {
+                    debug(3, "Clock not okay");
                   }
                 }
               } else {
-                debug(3,
-                      "skipped deciphering block %u with timestamp %u because its lead time is "
-                      "out of range at %f "
-                      "seconds.",
-                      seq_no, timestamp, lead_time * 1.0E-9);
-                uint32_t currentAnchorRTP = 0;
-                uint64_t currentAnchorLocalTime = 0;
-                if (get_ptp_anchor_local_time_info(conn, &currentAnchorRTP,
-                                                   &currentAnchorLocalTime) == clock_ok) {
-                  debug(3, "anchorRTP: %u, anchorLocalTime: %" PRIu64 ".", currentAnchorRTP,
-                        currentAnchorLocalTime);
-                } else {
-                  debug(3, "Clock not okay");
-                }
+                debug(2, "Unrecognised or invalid ssrc: %s.", get_ssrc_name(payload_ssrc));
               }
             } else {
-              debug(2, "Unrecognised or invalid ssrc: %s.", get_ssrc_name(payload_ssrc));
+              debug(1, "dropping buffer that should have played before the last actually played.");
             }
+            new_audio_block_needed = 1; // the block has been used up and is no longer current
           } else {
-            debug(1, "dropping buffer that should have played before the last actually played.");
+            if ((very_early_packets_signalled == 0) && (lead_time * 1E-9 > (config.audio_decoded_buffer_desired_length + 0.2))) {
+              debug(1,
+                    "incoming frame suddenly (?) has a lead time of %f seconds, with a desired "
+                    "decoded buffer length of %f.",
+                    1.0 * lead_time * 1E-9, config.audio_decoded_buffer_desired_length);
+              very_early_packets_signalled = 1;
+            }
+            usleep(((1000000 * conn->frames_per_packet) / conn->input_rate) *
+                   2); // wait for approximately the length of two packets
           }
-          new_audio_block_needed = 1; // the block has been used up and is no longer current
         } else {
-          if ((have_valid_time != 0) && (very_early_packets_signalled == 0) &&
-              (lead_time * 1E-9 > (config.audio_decoded_buffer_desired_length + 0.2))) {
-            debug(1,
-                  "incoming frame suddenly (?) has a lead time of %f seconds, with a desired "
-                  "decoded buffer length of %f.",
-                  1.0 * lead_time * 1E-9, config.audio_decoded_buffer_desired_length);
-            very_early_packets_signalled = 1;
-          }
-          usleep(((1000000 * conn->frames_per_packet) / conn->input_rate) *
-                 2); // wait for approximately the length of two packets
+          debug(3, "just you wait, Henry Higgins, without valid timing information...");
+          usleep(20000); // just you wait, Henry Higgins...        
         }
       }
     }