Skip to content

Commit

Permalink
Begin to use a unified and more accurate scheme for translating betwe…
Browse files Browse the repository at this point in the history
…en local time and rtp timestamp, taking clock drift and actualt source rate into account if available. Not dealing with highes output rates yet.
  • Loading branch information
mikebrady committed Aug 5, 2018
1 parent e90f172 commit 3870195
Show file tree
Hide file tree
Showing 4 changed files with 375 additions and 158 deletions.
101 changes: 81 additions & 20 deletions player.c
Original file line number Diff line number Diff line change
Expand Up @@ -494,8 +494,7 @@ void player_put_packet(seq_t seqno, uint32_t actual_timestamp, int64_t timestamp
debug(3,
"Dropping flushed packet in player_put_packet, seqno %u, timestamp %lld, flushing to "
"timestamp: %lld.",
seqno, ltimestamp, conn->flush_rtp_timestamp);

seqno, ltimestamp, conn->flush_rtp_timestamp);
} else {
if ((conn->flush_rtp_timestamp != 0x0) &&
(ltimestamp > conn->flush_rtp_timestamp)) // if we have gone past the flush boundary time
Expand Down Expand Up @@ -527,11 +526,11 @@ void player_put_packet(seq_t seqno, uint32_t actual_timestamp, int64_t timestamp
if (conn->ab_write == seqno) { // expected packet
uint64_t reception_time = get_absolute_time_in_fp();

if ((conn->packet_count_since_flush>=145) && (conn->packet_count_since_flush<=155)) {
if ((conn->packet_count_since_flush>=500) && (conn->packet_count_since_flush<=510)) {
conn->frames_inward_measurement_start_time = reception_time;
conn->frames_inward_frames_received_at_measurement_start_time = timestamp;
conn->input_frame_rate_status = 1; // valid now
debug(1,"frames_inward_measurement_start_time set");
// debug(1,"frames_inward_measurement_start_time set");
}

conn->frames_inward_measurement_time = reception_time;
Expand Down Expand Up @@ -911,7 +910,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
// debug(1,"First frame seen, time %u, with %d
// frames...",curframe->timestamp,seq_diff(ab_read, ab_write));

if (reference_timestamp) { // if we have a reference time
if (have_timestamp_timing_information(conn)) { // if we have a reference time
// debug(1,"First frame seen with timestamp...");
conn->first_packet_timestamp =
curframe->timestamp; // we will keep buffering until we are
Expand Down Expand Up @@ -951,6 +950,9 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
// -4410 frames.

// debug(1, "Output sample ratio is %d", conn->output_sample_ratio);


// what we are asking for here is "what is the local time at which time the calculated frame should be played"

int64_t delta = (conn->first_packet_timestamp - reference_timestamp) +
conn->latency * conn->output_sample_ratio +
Expand All @@ -964,9 +966,23 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
int64_t abs_delta = -delta;
int64_t delta_fp_sec =
(abs_delta << 32) / config.output_rate; // int64_t which is positive
conn->first_packet_time_to_play = reference_timestamp_time - delta_fp_sec;
conn->first_packet_time_to_play = reference_timestamp_time - delta_fp_sec;
}

uint64_t should_be_time;
frame_to_local_time(conn->first_packet_timestamp +conn->latency * conn->output_sample_ratio + (int64_t)(config.audio_backend_latency_offset * config.output_rate),&should_be_time,conn);

if(should_be_time>=conn->first_packet_time_to_play) {
if ((((should_be_time-conn->first_packet_time_to_play)*1000000)>>32)>10)
debug(1,"New time for first packet timestamp %" PRId64 " is later than calculated time by %" PRId64 " microseconds.",curframe->timestamp,((should_be_time-conn->first_packet_time_to_play)*1000000)>>32);
} else {
if ((((conn->first_packet_time_to_play-should_be_time)*1000000)>>32)>10)
debug(1,"New time for first packet timestamp %" PRId64 " is earlier than calculated time by %" PRId64 " microseconds.",curframe->timestamp,((conn->first_packet_time_to_play-should_be_time)*1000000)>>32);
}

// cut over to new calculation scheme
conn->first_packet_time_to_play = should_be_time;

if (local_time_now >= conn->first_packet_time_to_play) {
debug(
1,
Expand All @@ -993,6 +1009,19 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
(abs_delta << 32) / config.output_rate; // int64_t which is positive
conn->first_packet_time_to_play = reference_timestamp_time - delta_fp_sec;
}

uint64_t should_be_time;
frame_to_local_time(conn->first_packet_timestamp +conn->latency * conn->output_sample_ratio + (int64_t)(config.audio_backend_latency_offset * config.output_rate),&should_be_time,conn);

if(should_be_time>=conn->first_packet_time_to_play) {
if ((((should_be_time-conn->first_packet_time_to_play)*1000000)>>32)>50)
debug(1,"New time for recalculated first packet timestamp %" PRId64 " is later than calculated time by %" PRId64 " microseconds.",curframe->timestamp,((should_be_time-conn->first_packet_time_to_play)*1000000)>>32);
} else {
if ((((conn->first_packet_time_to_play-should_be_time)*1000000)>>32)>50)
debug(1,"New time for recalculated first packet timestamp %" PRId64 " is earlier than calculated time by %" PRId64 " microseconds.",curframe->timestamp,((conn->first_packet_time_to_play-should_be_time)*1000000)>>32);
}



// now, the size of the initial silence must be affected by the lead-in time.
// it must be somewhat less than the lead-in time so that dynamic adjustments can be
Expand Down Expand Up @@ -1140,12 +1169,14 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
}
}
if (conn->ab_buffering == 0) {
/*
// note the time of the playing of the first frame
uint64_t reference_timestamp_time; // don't need this...
get_reference_timestamp_stuff(&conn->play_segment_reference_frame,
&reference_timestamp_time,
&conn->play_segment_reference_frame_remote_time, conn);
conn->play_segment_reference_frame *= conn->output_sample_ratio;
*/
#ifdef CONFIG_METADATA
debug(2, "prsm");
send_ssnc_metadata('prsm', NULL, 0,
Expand All @@ -1172,12 +1203,14 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
if ((conn->ab_synced) && (curframe) && (curframe->ready) && (curframe->timestamp)) {
do_wait =
1; // if the current frame exists and is ready, then wait unless it's time to let it go...

// here, get the time to play the current frame.
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); // all types okay
reference_timestamp *= conn->output_sample_ratio;
if (reference_timestamp) { // if we have a reference time
if (have_timestamp_timing_information(conn)) { // if we have a reference time
int64_t packet_timestamp = curframe->timestamp; // types okay
int64_t delta = packet_timestamp - reference_timestamp;
int64_t offset =
Expand All @@ -1186,6 +1219,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
config.audio_backend_buffer_desired_length *
config.output_rate; // all arguments are int32_t, so expression promotion okay
int64_t net_offset = delta + offset; // okay

uint64_t time_to_play = reference_timestamp_time; // type okay
if (net_offset >= 0) {
uint64_t net_offset_fp_sec =
Expand All @@ -1199,7 +1233,20 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
time_to_play -= net_offset_fp_sec;
// debug(2,"Net Offset: %lld, adjusted: -%lld.",net_offset,net_offset_fp_sec);
}


uint64_t new_time_to_play = 0;
frame_to_local_time(packet_timestamp+offset, &new_time_to_play, conn);

if(new_time_to_play>=time_to_play) {
if ((((new_time_to_play-time_to_play)*1000000)>>32)>100)
debug(1,"New time for frame %" PRId64 " is later than calculated time by %" PRId64 " microseconds.",curframe->timestamp,((new_time_to_play-time_to_play)*1000000)>>32);
} else {
if ((((time_to_play-new_time_to_play)*1000000)>>32)>100)
debug(1,"New time for frame %" PRId64 " is earlier than calculated time by %" PRId64 " microseconds.",curframe->timestamp,((time_to_play-new_time_to_play)*1000000)>>32);
}
// cut over to the new calculation system
time_to_play = new_time_to_play;

if (local_time_now >= time_to_play) {
do_wait = 0;
}
Expand Down Expand Up @@ -1583,7 +1630,7 @@ void *player_thread_func(void *arg) {
debug(3, "Output frame bytes is %d.", conn->output_bytes_per_frame);

conn->session_corrections = 0;
conn->play_segment_reference_frame = 0; // zero signals that we are not in a play segment
// conn->play_segment_reference_frame = 0; // zero signals that we are not in a play segment

// check that there are enough buffers to accommodate the desired latency and the latency offset

Expand Down Expand Up @@ -1739,7 +1786,8 @@ void *player_thread_func(void *arg) {
"min buffer occupancy, "
"max buffer occupancy, "
"input frames per second, "
"output frames per second.");
"output frames per second, "
"drift client vs local clock in ppm");
} else {
inform("sync error in milliseconds, "
"total packets, "
Expand Down Expand Up @@ -1937,10 +1985,10 @@ void *player_thread_func(void *arg) {
// uint64_t
// local_time_now=((uint64_t)tn.tv_sec<<32)+((uint64_t)tn.tv_nsec<<32)/1000000000;

int64_t td = 0;
int64_t td_in_frames = 0;
int64_t td = 0; // td is the time difference between the reference timestamp time and the present time. Only used to calculate td_in_frames
int64_t td_in_frames = 0; // td_in_frames is the number of frames between between the reference timestamp time and the present time

if (local_time_now >= reference_timestamp_time) {
// debug(1,"td is %lld.",td);
td = local_time_now - reference_timestamp_time; // this is the positive value.
// Conversion is positive uint64_t to
// int64_t, thus okay
Expand Down Expand Up @@ -2014,9 +2062,20 @@ void *player_thread_func(void *arg) {

if (resp >= 0) {

int64_t should_be_frame;
local_time_to_frame(local_time_now,&should_be_frame,conn);

if (abs(td_in_frames + rt-should_be_frame)>10)
debug(1,"Difference between old and new frame number is %" PRId64 " frames.",td_in_frames + rt - should_be_frame);
// this is the actual delay, including the latency we actually want, which will
// fluctuate a good bit about a potentially rising or falling trend.
int64_t delay = td_in_frames + rt - (nt - current_delay); // all int64_t

// int64_t delay = td_in_frames + rt - (nt - current_delay); // all int64_t
// cut over to the new calculation method
int64_t delay = should_be_frame - (nt - current_delay); // all int64_t

// td_in_frames + rt is the frame number that should be output at local_time_now.


// This is the timing error for the next audio frame in the DAC.

Expand Down Expand Up @@ -2344,7 +2403,7 @@ void *player_thread_func(void *arg) {

if ((config.output->delay)) {
if (config.no_sync == 0) {
inform("%*.1f," /* Sync error in milliseconds */
inform("%*.2f," /* Sync error in milliseconds */
"%*.1f," /* net correction in ppm */
"%*.1f," /* corrections in ppm */
"%*d," /* total packets */
Expand All @@ -2356,7 +2415,8 @@ void *player_thread_func(void *arg) {
"%*d," /* min buffer occupancy */
"%*d," /* max buffer occupancy */
"%*.2f," /* input frame rate */
"%*.2f", /* output frame rate */
"%*.2f," /* output frame rate */
"%*.2f", /* client clock to local clock drift in ppm */
10,
1000 * moving_average_sync_error / config.output_rate, 10,
moving_average_correction * 1000000 / (352 * conn->output_sample_ratio),
Expand All @@ -2365,9 +2425,9 @@ void *player_thread_func(void *arg) {
12, play_number, 7, conn->missing_packets, 7, conn->late_packets, 7,
conn->too_late_packets, 7, conn->resend_requests, 7,
minimum_dac_queue_size, 5, minimum_buffer_occupancy, 5,
maximum_buffer_occupancy, 11, conn->input_frame_rate, 11, conn->frame_rate);
maximum_buffer_occupancy, 11, conn->input_frame_rate, 11, conn->frame_rate , 10, (1.0-conn->local_to_remote_time_gradient)*1000000);
} else {
inform("%*.1f," /* Sync error in milliseconds */
inform("%*.2f," /* Sync error in milliseconds */
"%*d," /* total packets */
"%*llu," /* missing packets */
"%*llu," /* late packets */
Expand All @@ -2384,7 +2444,7 @@ void *player_thread_func(void *arg) {
minimum_buffer_occupancy, 5, maximum_buffer_occupancy, 11, conn->input_frame_rate);
}
} else {
inform("%*.1f," /* Sync error in milliseconds */
inform("%*.2f," /* Sync error in milliseconds */
"%*d," /* total packets */
"%*llu," /* missing packets */
"%*llu," /* late packets */
Expand Down Expand Up @@ -2661,9 +2721,10 @@ void do_flush(int64_t timestamp, rtsp_conn_info *conn) {
conn->flush_requested = 1;
// if (timestamp!=0)
conn->flush_rtp_timestamp = timestamp; // flush all packets up to (and including?) this
conn->play_segment_reference_frame = 0;
// conn->play_segment_reference_frame = 0;
conn->play_number_after_flush = 0;
conn->packet_count_since_flush = 0;

debug_mutex_unlock(&conn->flush_mutex, 3);

#ifdef CONFIG_METADATA
Expand Down
23 changes: 17 additions & 6 deletions player.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@
#include "alac.h"
#include "audio.h"

#define time_ping_history 8
#define time_ping_history 32

typedef struct time_ping_record {
uint64_t local_to_remote_difference;
uint64_t dispersion;
uint64_t local_time;
uint64_t remote_time;
int sequence_number;
int chosen;
} time_ping_record;

typedef uint16_t seq_t;
Expand Down Expand Up @@ -182,27 +184,36 @@ typedef struct {
uint16_t local_timing_port;

int64_t latency_delayed_timestamp; // this is for debugging only...

// this is what connects an rtp timestamp to the remote time

int64_t reference_timestamp;
uint64_t reference_timestamp_time;
uint64_t remote_reference_timestamp_time;
uint64_t remote_reference_timestamp_time;


// the ratio of the following should give us the operating rate, nominally 44,100
int64_t reference_to_previous_frame_difference;
uint64_t reference_to_previous_time_difference;

// debug variables
int request_sent;

uint8_t time_ping_count;
int time_ping_count;
struct time_ping_record time_pings[time_ping_history];

uint64_t departure_time; // dangerous -- this assumes that there will never be two timing
// request in flight at the same time

pthread_mutex_t reference_time_mutex;

double local_to_remote_time_gradient; // if no drift, this would be exactly 1.0; likely it's slightly above or below.
uint64_t local_to_remote_time_difference; // used to switch between local and remote clocks
uint64_t local_to_remote_time_difference_measurement_time; // when the above was calculated

int last_stuff_request;

int64_t play_segment_reference_frame;
uint64_t play_segment_reference_frame_remote_time;
// int64_t play_segment_reference_frame;
// uint64_t play_segment_reference_frame_remote_time;

int32_t buffer_occupancy; // allow it to be negative because seq_diff may be negative
int64_t session_corrections;
Expand Down
Loading

0 comments on commit 3870195

Please sign in to comment.