Move latency calculations to rtp.c. Remove checks for infeasible latencies from player.c.

This commit is contained in:
Mike Brady
2021-04-28 13:03:27 +01:00
parent 0af7f4fbde
commit a8caa0f913
2 changed files with 46 additions and 185 deletions
+23 -174
View File
@@ -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.
// The desired latency, typically 88200 frames, will be calculated for in rtp.c,
// and any desired backend latency offset included in it there.
// 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,
// 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.
// we must enable packets to be released early enough for the
// audio buffer to be filled to the desired length
// 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;
}
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);
should_be_frame - (inframe->given_timestamp * conn->output_sample_ratio -
current_delay); // all int64_t
// 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
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) {
+23 -11
View File
@@ -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) {