Begin to use a unified and more accurate scheme for translating between local time and rtp timestamp, taking clock drift and actualt source rate into account if available. Not dealing with highes output rates yet.

This commit is contained in:
Mike Brady
2018-08-05 17:23:07 +01:00
parent e90f172254
commit 3870195cf9
4 changed files with 376 additions and 159 deletions
+268 -132
View File
@@ -45,8 +45,8 @@
#include <time.h>
#include <unistd.h>
uint64_t local_to_remote_time_jitters;
uint64_t local_to_remote_time_jitters_count;
uint64_t local_to_remote_time_jitter;
uint64_t local_to_remote_time_jitter_count;
void rtp_initialise(rtsp_conn_info *conn) {
conn->rtp_time_of_last_resend_request_error_fp = 0;
@@ -65,6 +65,27 @@ void rtp_terminate(rtsp_conn_info *conn) {
debug(1, "Error destroying reference_time_mutex variable.");
}
uint64_t local_to_remote_time_difference_now(rtsp_conn_info *conn) {
// this is an attempt to compensate for clock drift since the last time ping that was used
// so, if we have a non-zero clock drift, we will calculate the drift there would
// be from the time of the last time ping
uint64_t local_time_now_fp = get_absolute_time_in_fp();
uint64_t time_since_last_local_to_remote_time_difference_measurement = local_time_now_fp - conn->local_to_remote_time_difference_measurement_time;
uint64_t remote_time_since_last_local_to_remote_time_difference_measurement = (uint64_t)(conn->local_to_remote_time_gradient*time_since_last_local_to_remote_time_difference_measurement);
double drift;
if (remote_time_since_last_local_to_remote_time_difference_measurement >= time_since_last_local_to_remote_time_difference_measurement)
drift = (1.0*(remote_time_since_last_local_to_remote_time_difference_measurement - time_since_last_local_to_remote_time_difference_measurement))/(uint64_t)0x100000000;
else
drift = -((1.0*(time_since_last_local_to_remote_time_difference_measurement - remote_time_since_last_local_to_remote_time_difference_measurement))/(uint64_t)0x100000000);
double interval_ms = 1.0*(((time_since_last_local_to_remote_time_difference_measurement)*1000)>>32);
// debug(1,"Measurement drift is %.2f microseconds (0x%" PRIx64 " in 64-bit fp) over %.2f milliseconds with drift of %.2f ppm.",drift*1000000,(uint64_t)(drift*(uint64_t)0x100000000),interval_ms,(1.0-conn->local_to_remote_time_gradient)*1000000);
// return conn->local_to_remote_time_difference + (uint64_t)(drift*(uint64_t 0x100000000));
return conn->local_to_remote_time_difference + (uint64_t)(drift*(uint64_t)0x100000000);
}
void rtp_audio_receiver_cleanup_handler(void *arg) {
debug(3, "Audio Receiver Cleanup.");
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
@@ -246,7 +267,7 @@ void *rtp_control_receiver(void *arg) {
// 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 packe is 7
// pair of bytes in the packet is 7
// then an extra time lag is expected to be added, presumably by
// the AirPort Express.
@@ -337,28 +358,30 @@ void *rtp_control_receiver(void *arg) {
debug_mutex_lock(&conn->reference_time_mutex, 1000, 1);
// this is for debugging
// uint64_t old_remote_reference_time = conn->remote_reference_timestamp_time;
// int64_t old_reference_timestamp = conn->reference_timestamp;
// int64_t old_latency_delayed_timestamp = conn->latency_delayed_timestamp;
uint64_t old_remote_reference_time = conn->remote_reference_timestamp_time;
int64_t old_reference_timestamp = conn->reference_timestamp;
int64_t old_latency_delayed_timestamp = conn->latency_delayed_timestamp;
conn->remote_reference_timestamp_time = remote_time_of_sync;
conn->reference_timestamp_time =
remote_time_of_sync - conn->local_to_remote_time_difference;
//conn->reference_timestamp_time =
// remote_time_of_sync - local_to_remote_time_difference_now(conn);
conn->reference_timestamp = sync_rtp_timestamp;
conn->latency_delayed_timestamp = rtp_timestamp_less_latency;
debug_mutex_unlock(&conn->reference_time_mutex, 3);
conn->reference_to_previous_time_difference = remote_time_of_sync - old_remote_reference_time;
if (old_reference_timestamp==0)
conn->reference_to_previous_frame_difference = 0;
else
conn->reference_to_previous_frame_difference = sync_rtp_timestamp - old_reference_timestamp;
// this is for debugging
int64_t delayed_frame_difference = rtp_timestamp_less_latency - old_latency_delayed_timestamp;
/*
uint64_t time_difference = remote_time_of_sync - old_remote_reference_time;
int64_t reference_frame_difference = sync_rtp_timestamp - old_reference_timestamp;
int64_t delayed_frame_difference = rtp_timestamp_less_latency -
old_latency_delayed_timestamp;
if (old_remote_reference_time)
debug(1,"Time difference: %" PRIu64 " reference and delayed frame differences: %"
PRId64 "
and %" PRId64 ", giving rates of %f and %f respectively.",
(time_difference*1000000)>>32,reference_frame_difference,delayed_frame_difference,(1.0*(reference_frame_difference*10000000))/((time_difference*10000000)>>32),(1.0*(delayed_frame_difference*10000000))/((time_difference*10000000)>>32));
debug(1,"Time difference: %" PRIu64 " reference and delayed frame differences: %" PRId64 " and %" PRId64 ", giving rates _at source!!_ of %f and %f respectively.",
(conn->reference_to_previous_time_difference*1000000)>>32,conn->reference_to_previous_frame_difference,delayed_frame_difference,
(1.0*(conn->reference_to_previous_frame_difference*10000000))/((conn->reference_to_previous_time_difference*10000000)>>32),(1.0*(delayed_frame_difference*10000000))/((conn->reference_to_previous_time_difference*10000000)>>32));
else
debug(1,"First sync received");
*/
@@ -493,14 +516,21 @@ void *rtp_timing_receiver(void *arg) {
pthread_create(&conn->timer_requester, NULL, &rtp_timing_sender, arg);
// struct timespec att;
uint64_t distant_receive_time, distant_transmit_time, arrival_time, return_time;
local_to_remote_time_jitters = 0;
local_to_remote_time_jitters_count = 0;
local_to_remote_time_jitter = 0;
local_to_remote_time_jitter_count = 0;
// uint64_t first_remote_time = 0;
uint64_t first_local_time = 0;
// uint64_t first_local_time = 0;
uint64_t first_local_to_remote_time_difference = 0;
// uint64_t first_local_to_remote_time_difference_time;
// uint64_t l2rtd = 0;
int sequence_number = 0;
// for getting mean and sd of return times
int32_t stat_n = 0;
double stat_mean = 0.0;
double stat_M2 = 0.0;
while (1) {
nread = recv(conn->timing_socket, packet, sizeof(packet), 0);
@@ -548,151 +578,175 @@ void *rtp_timing_receiver(void *arg) {
distant_transmit_time = (uint64_t)nctohl(&packet[24]) << 32;
distant_transmit_time += nctohl(&packet[28]);
// processing_time = distant_transmit_time - distant_receive_time;
// debug(1,"Return trip time: %lluuS, remote processing time:
// %lluuS.",(return_time*1000000)>>32,(processing_time*1000000)>>32);
uint64_t remote_processing_time = distant_transmit_time - distant_receive_time;
//debug(1,"Return trip time: %" PRIu64 " uS, remote processing time: %" PRIu64 " uS.",(return_time*1000000)>>32,(remote_processing_time*1000000)>>32);
uint64_t local_time_by_remote_clock = distant_transmit_time + return_time / 2;
// remove the remote processing time from the record of the return time, as long at the processing time looks sensible.
if ((remote_processing_time > 0) && (remote_processing_time < return_time))
return_time -= remote_processing_time;
else
debug(1,"Non-sensical remote processing time -- ignored.");
unsigned int cc;
int cc;
for (cc = time_ping_history - 1; cc > 0; cc--) {
conn->time_pings[cc] = conn->time_pings[cc - 1];
//if ((conn->time_ping_count) && (conn->time_ping_count < 10))
// conn->time_pings[cc].dispersion =
// conn->time_pings[cc].dispersion * pow(2.14, 1.0/conn->time_ping_count);
conn->time_pings[cc].dispersion =
(conn->time_pings[cc].dispersion * 110) /
100; // make the dispersions 'age' by this rational factor
(conn->time_pings[cc].dispersion * 110) / 100; // make the dispersions 'age' by this rational factor
}
// these are for diagnostics only -- not used
// these are used for doing a least squares calculation to get the drift
conn->time_pings[0].local_time = arrival_time;
conn->time_pings[0].remote_time = distant_transmit_time;
conn->time_pings[0].remote_time = distant_transmit_time;
conn->time_pings[0].sequence_number = sequence_number++;
conn->time_pings[0].chosen = 0;
conn->time_pings[0].local_to_remote_difference =
local_time_by_remote_clock - arrival_time;
conn->time_pings[0].dispersion = return_time;
if (conn->time_ping_count < time_ping_history)
conn->time_ping_count++;
uint64_t local_time_chosen = arrival_time;
// here, calculate the mean and standard deviation of the return times
// mean and variance calculations from "online_variance" algorithm at
// https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Online_algorithm
double rtfus = 1.0*((return_time * 1000000) >> 32);
stat_n += 1;
double stat_delta = rtfus - stat_mean;
stat_mean += stat_delta / stat_n;
stat_M2 += stat_delta * (rtfus - stat_mean);
//debug(1, "Timing packet return time stats: current, mean and standard deviation over %d packets: %.1f, %.1f, %.1f (microseconds).",
// stat_n,rtfus,stat_mean, sqrtf(stat_M2 / (stat_n - 1)));
// here, pick the record with the least dispersion, and record that it's been chosen
// uint64_t local_time_chosen = arrival_time;
// uint64_t remote_time_chosen = distant_transmit_time;
// now pick the timestamp with the lowest dispersion
uint64_t l2rtd = conn->time_pings[0].local_to_remote_difference;
uint64_t lt = conn->time_pings[0].local_time;
uint64_t tld = conn->time_pings[0].dispersion;
// chosen = 0;
int chosen = 0;
for (cc = 1; cc < conn->time_ping_count; cc++)
if (conn->time_pings[cc].dispersion < tld) {
chosen = cc;
l2rtd = conn->time_pings[cc].local_to_remote_difference;
// chosen = cc;
lt = conn->time_pings[cc].local_time;
tld = conn->time_pings[cc].dispersion;
local_time_chosen = conn->time_pings[cc].local_time;
// local_time_chosen = conn->time_pings[cc].local_time;
// remote_time_chosen = conn->time_pings[cc].remote_time;
}
// int64_t ji;
// debug(1,"Record %d has the lowest dispersion with %0.2f us dispersion.",chosen,1.0*((tld * 1000000) >> 32));
conn->time_pings[chosen].chosen = 1; // record the fact that it has been used for timing
// calculate the jitter -- the absolute time between the current local_to_remote_time_difference and the new one and add it to the total jitter count
int64_t ji;
int64_t ltd =0; // local time difference for the jitter
if (conn->time_ping_count > 1) {
if (l2rtd > conn->local_to_remote_time_difference) {
local_to_remote_time_jitters =
local_to_remote_time_jitters + l2rtd - conn->local_to_remote_time_difference;
// ji = l2rtd - conn->local_to_remote_time_difference;
local_to_remote_time_jitter =
local_to_remote_time_jitter + l2rtd - conn->local_to_remote_time_difference;
ji = l2rtd - conn->local_to_remote_time_difference; // this is the difference between the present local-to-remote-time-difference and the new one, i.e. the jitter step
} else {
local_to_remote_time_jitters =
local_to_remote_time_jitters + conn->local_to_remote_time_difference - l2rtd;
// ji = -(conn->local_to_remote_time_difference - l2rtd);
local_to_remote_time_jitter =
local_to_remote_time_jitter + conn->local_to_remote_time_difference - l2rtd;
ji = -(conn->local_to_remote_time_difference - l2rtd);
}
local_to_remote_time_jitters_count += 1;
local_to_remote_time_jitter_count += 1;
}
// uncomment below to print jitter between client's clock and oour clock
// int64_t rtus = (tld*1000000)>>32; ji = (ji*1000000)>>32; debug(1,"Choosing time
// difference
// with dispersion of %lld us with delta of %lld us",rtus,ji);
conn->local_to_remote_time_difference = l2rtd;
if (conn->local_to_remote_time_difference_measurement_time < lt)
ltd = lt-conn->local_to_remote_time_difference_measurement_time;
else
ltd = -(conn->local_to_remote_time_difference_measurement_time-lt);
/*
if (ltd) {
debug(1,"Jitter: %" PRId64 " microseconds in %" PRId64 " microseconds.", (ji * (int64_t)1000000)>>32, (ltd * (int64_t)1000000)>>32);
debug(1,"Source clock to local clock drift: %.2f ppm.",((1.0*ji)/ltd)*1000000.0);
}
// uncomment below to print jitter between client's clock and our clock
*/
/*
if (ji) {
int64_t rtus = (tld*1000000)>>32;
debug(1,"Choosing time difference[%d] with dispersion of %" PRId64 " us with an adjustment of %" PRId64 " us",chosen, rtus, (ji*1000000)>>32);
}
*/
conn->local_to_remote_time_difference = l2rtd; // make this the new local-to-remote-time-difference
conn->local_to_remote_time_difference_measurement_time = lt; // done at this time.
if (first_local_to_remote_time_difference == 0) {
first_local_to_remote_time_difference = conn->local_to_remote_time_difference;
// first_local_to_remote_time_difference_time = get_absolute_time_in_fp();
}
// int64_t clock_drift;
// int64_t clock_drift_in_usec;
// double clock_drift_ppm = 0.0;
if (first_local_time == 0) {
first_local_time = local_time_chosen;
// first_remote_time = remote_time_chosen;
// clock_drift = 0;
// here, let's try to use the timing pings that were selected because of their short return times to
// estimate a figure for drift between the local clock (x) and the remote clock (y)
// if we plug in a local interval, we will get back what that is in remote time
// calculate the line of best fit for relating the local time and the remote time
// we will calculate the slope, which is the drift
// see https://www.varsitytutors.com/hotmath/hotmath_help/topics/line-of-best-fit
double m,drift;
drift = 0.0;
uint64_t y_bar = 0; // remote timestamp average
uint64_t x_bar = 0; // local timestamp average
int sample_count = 0;
for (cc = 0; cc < conn->time_ping_count; cc++)
if ((conn->time_pings[cc].chosen) && (conn->time_pings[cc].sequence_number>9)) { // wait for a minute or so....
y_bar += (conn->time_pings[cc].remote_time>>12); //precision is down to 1/4th of a microsecond
x_bar += (conn->time_pings[cc].local_time>>12);
sample_count++;
}
y_bar = y_bar/sample_count;
x_bar = x_bar/sample_count;
int64_t xid, yid;
int64_t mtl, mbl;
mtl=0;mbl=0;
for (cc = 0; cc < conn->time_ping_count; cc++)
if ((conn->time_pings[cc].chosen) && (conn->time_pings[cc].sequence_number>9)) {
uint64_t slt = conn->time_pings[cc].local_time>>12;
if (slt > x_bar)
xid = slt - x_bar;
else
xid = -(x_bar - slt);
uint64_t srt = conn->time_pings[cc].remote_time>>12;
if (srt > y_bar)
yid = srt - y_bar;
else
yid = -(y_bar - srt);
mtl = mtl + xid*yid;
mbl = mbl + xid*xid;
}
if (sample_count > 2) {
conn->local_to_remote_time_gradient = (1.0*mtl)/mbl;
// debug(1,"Drift is %12.2f ppm, based on %d samples.",(1.0-conn->local_to_remote_time_gradient)*1000000,sample_count);
} else {
// uint64_t local_time_change = local_time_chosen - first_local_time;
// uint64_t remote_time_change = remote_time_chosen - first_remote_time;
/*
if (remote_time_change >= local_time_change)
clock_drift = remote_time_change - local_time_change;
else
clock_drift = -(local_time_change - remote_time_change);
*/
/*
if (clock_drift >= 0)
clock_drift_in_usec = (clock_drift * 1000000) >> 32;
else
clock_drift_in_usec = -(((-clock_drift) * 1000000) >> 32);
*/
// clock_drift_ppm = (1.0 * clock_drift_in_usec) / (local_time_change >> 32);
conn->local_to_remote_time_gradient = 1.0;
}
int64_t source_drift_usec;
if (conn->play_segment_reference_frame != 0) {
int64_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);
uint64_t frame_difference = 0;
if (reference_timestamp >= conn->play_segment_reference_frame)
frame_difference =
(uint64_t)reference_timestamp - (uint64_t)conn->play_segment_reference_frame;
else // rollover
frame_difference = (uint64_t)reference_timestamp + 0x100000000 -
(uint64_t)conn->play_segment_reference_frame;
uint64_t frame_time_difference_calculated =
(((uint64_t)frame_difference << 32) / 44100);
uint64_t frame_time_difference_actual =
remote_reference_timestamp_time -
conn->play_segment_reference_frame_remote_time; // this is all done by reference
// to
// the
// sources' system clock
// debug(1,"%llu frames since play started, %llu usec calculated, %llu usec
// actual",frame_difference, (frame_time_difference_calculated*1000000)>>32,
// (frame_time_difference_actual*1000000)>>32);
if (frame_time_difference_calculated >=
frame_time_difference_actual) // i.e. if the time it should have taken to send the
// packets is greater than the actual time difference
// measured on the source clock
// then the source DAC's clock is running fast relative to the source system clock
source_drift_usec = frame_time_difference_calculated - frame_time_difference_actual;
else
// otherwise the source DAC's clock is running slow relative to the source system
// clock
source_drift_usec =
-(frame_time_difference_actual - frame_time_difference_calculated);
} else
source_drift_usec = 0;
source_drift_usec = (source_drift_usec * 1000000) >> 32; // turn it to microseconds
// long current_delay = 0;
// if (config.output->delay) {
// config.output->delay(&current_delay);
//}
// Useful for troubleshooting:
// debug(1, "clock_drift_ppm %f\tchosen %5d\tsource_drift_usec
// %10.1lld\treturn_time_in_usec
// %10.1llu",
// clock_drift_ppm,
// chosen,
//(session_corrections*1000000)/44100,
// current_delay,
// source_drift_usec,
// buffer_occupancy,
//(return_time*1000000)>>32);
} else {
debug(2, "Time ping turnaround time: %lld us -- it looks like a timing ping was lost.",
(return_time * 1000000) >> 32);
@@ -902,21 +956,103 @@ void get_reference_timestamp_stuff(int64_t *timestamp, uint64_t *timestamp_time,
// types okay
debug_mutex_lock(&conn->reference_time_mutex, 1000, 1);
*timestamp = conn->reference_timestamp;
*timestamp_time = conn->reference_timestamp_time;
*remote_timestamp_time = conn->remote_reference_timestamp_time;
*timestamp_time = conn->remote_reference_timestamp_time - local_to_remote_time_difference_now(conn);
// if ((*timestamp == 0) && (*timestamp_time == 0)) {
// debug(1,"Reference timestamp is invalid.");
//}
*remote_timestamp_time = conn->remote_reference_timestamp_time;
debug_mutex_unlock(&conn->reference_time_mutex, 3);
}
void clear_reference_timestamp(rtsp_conn_info *conn) {
debug_mutex_lock(&conn->reference_time_mutex, 1000, 1);
conn->reference_timestamp = 0;
conn->reference_timestamp_time = 0;
conn->remote_reference_timestamp_time = 0;
debug_mutex_unlock(&conn->reference_time_mutex, 3);
}
int have_timestamp_timing_information(rtsp_conn_info *conn) {
if (conn->reference_timestamp==0)
return 0;
else
return 1;
}
int sanitised_source_rate_information(int64_t *frames, uint64_t *time, rtsp_conn_info *conn) {
int result = 0;
*frames = conn->input_rate;
*time = (uint64_t)(0x100000000); // one second in fp form
int64_t local_frames = conn->reference_to_previous_frame_difference;
uint64_t local_time = conn->reference_to_previous_time_difference;
if ((local_frames == 0) || (local_time == 0)) {
result = 1;
} else {
double calculated_frame_rate = ((1.0*local_frames)/local_time)*(uint64_t)0x100000000;
if (((calculated_frame_rate/conn->input_rate) > 1.001) || ((calculated_frame_rate/conn->input_rate) < 0.999)) {
result = 1;
} else {
*frames = local_frames;
*time = local_time;
result = 0;
}
}
return result;
}
int frame_to_local_time(int64_t timestamp, uint64_t *time, rtsp_conn_info *conn) {
debug_mutex_lock(&conn->reference_time_mutex, 1000, 1);
int result = 0;
uint64_t time_difference;
int64_t frame_difference;
result = sanitised_source_rate_information(&frame_difference,&time_difference,conn);
int64_t timestamp_interval = timestamp - conn->reference_timestamp; // we could be dealing with multiples of 44100 (nominally)
// debug(1, "Timestamp interval: %" PRId64 " frames with reference timestamp %" PRId64 ".",timestamp_interval,conn->reference_timestamp);
uint64_t timestamp_interval_time;
uint64_t remote_time_of_timestamp;
if (timestamp_interval>=0) {
timestamp_interval_time = (timestamp_interval * time_difference)/frame_difference; // this is the nominal time, based on the fps specified between current and previous sync frame.
remote_time_of_timestamp = conn->remote_reference_timestamp_time + timestamp_interval_time; // based on the reference timestamp time plus the time interval calculated based on the specified fps.
} else {
timestamp_interval_time = ((-timestamp_interval) * time_difference)/frame_difference; // this is the nominal time, based on the fps specified between current and previous sync frame.
remote_time_of_timestamp = conn->remote_reference_timestamp_time - timestamp_interval_time; // based on the reference timestamp time plus the time interval calculated based on the specified fps.
}
*time = remote_time_of_timestamp - local_to_remote_time_difference_now(conn);
debug_mutex_unlock(&conn->reference_time_mutex, 3);
return result;
}
int local_time_to_frame(uint64_t time, int64_t *frame, rtsp_conn_info *conn) {
debug_mutex_lock(&conn->reference_time_mutex, 1000, 1);
int result = 0;
uint64_t time_difference;
int64_t frame_difference;
result = sanitised_source_rate_information(&frame_difference,&time_difference,conn);
// first, get from [local] time to remote time.
uint64_t remote_time = time + local_to_remote_time_difference_now(conn);
// next, get the remote time interval from the remote_time to the reference time
uint64_t time_interval;
if (remote_time >= conn->remote_reference_timestamp_time)
time_interval = remote_time - conn->remote_reference_timestamp_time;
else
time_interval = conn->remote_reference_timestamp_time - remote_time;
// now, convert the remote time interval into frames
int64_t frame_interval = (time_interval * frame_difference)/time_difference;
if (remote_time >= conn->remote_reference_timestamp_time) {
// debug(1,"Frame interval is %" PRId64 " frames.",frame_interval);
*frame = conn->reference_timestamp+frame_interval;
} else {
// debug(1,"Frame interval is %" PRId64 " frames.",-frame_interval);
*frame = conn->reference_timestamp-frame_interval;
}
debug_mutex_unlock(&conn->reference_time_mutex, 3);
return result;
}
void rtp_request_resend(seq_t first, uint32_t count, rtsp_conn_info *conn) {
if (conn->rtp_running) {
// if (!request_sent) {