Check for valid time information more completely.
This commit is contained in:
+171
-172
@@ -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, ¤tAnchorRTP,
|
||||
¤tAnchorLocalTime) == 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, ¤tAnchorRTP,
|
||||
¤tAnchorLocalTime) == 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...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user