mirror of
https://github.com/morgan9e/UxPlay
synced 2026-04-14 00:04:13 +09:00
cleanups of initial latency code. ntp and rtp times
This commit is contained in:
@@ -38,7 +38,8 @@ typedef struct {
|
||||
|
||||
/* RTP header */
|
||||
unsigned short seqnum;
|
||||
uint64_t timestamp;
|
||||
uint64_t rtp_timestamp;
|
||||
uint64_t ntp_timestamp;
|
||||
|
||||
/* Payload data */
|
||||
unsigned int payload_size;
|
||||
@@ -206,7 +207,7 @@ raop_buffer_decrypt(raop_buffer_t *raop_buffer, unsigned char *data, unsigned ch
|
||||
}
|
||||
|
||||
int
|
||||
raop_buffer_enqueue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned short datalen, uint64_t timestamp, int use_seqnum) {
|
||||
raop_buffer_enqueue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned short datalen, uint64_t *ntp_timestamp, uint64_t *rtp_timestamp, int use_seqnum) {
|
||||
unsigned char empty_packet_marker[] = { 0x00, 0x68, 0x34, 0x00 };
|
||||
assert(raop_buffer);
|
||||
|
||||
@@ -247,7 +248,8 @@ raop_buffer_enqueue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned sh
|
||||
|
||||
/* Update the raop_buffer entry header */
|
||||
entry->seqnum = seqnum;
|
||||
entry->timestamp = timestamp;
|
||||
entry->rtp_timestamp = *rtp_timestamp;
|
||||
entry->ntp_timestamp = *ntp_timestamp;
|
||||
entry->filled = 1;
|
||||
|
||||
entry->payload_data = malloc(payload_size);
|
||||
@@ -268,7 +270,7 @@ raop_buffer_enqueue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned sh
|
||||
}
|
||||
|
||||
void *
|
||||
raop_buffer_dequeue(raop_buffer_t *raop_buffer, unsigned int *length, uint64_t *timestamp, unsigned short *seqnum, int no_resend) {
|
||||
raop_buffer_dequeue(raop_buffer_t *raop_buffer, unsigned int *length, uint64_t *ntp_timestamp, uint64_t *rtp_timestamp, unsigned short *seqnum, int no_resend) {
|
||||
assert(raop_buffer);
|
||||
|
||||
/* Calculate number of entries in the current buffer */
|
||||
@@ -300,7 +302,8 @@ raop_buffer_dequeue(raop_buffer_t *raop_buffer, unsigned int *length, uint64_t *
|
||||
entry->filled = 0;
|
||||
|
||||
/* Return entry payload buffer */
|
||||
*timestamp = entry->timestamp;
|
||||
*rtp_timestamp = entry->rtp_timestamp;
|
||||
*ntp_timestamp = entry->ntp_timestamp;
|
||||
*seqnum = entry->seqnum;
|
||||
*length = entry->payload_size;
|
||||
entry->payload_size = 0;
|
||||
|
||||
@@ -25,8 +25,8 @@ typedef int (*raop_resend_cb_t)(void *opaque, unsigned short seqno, unsigned sho
|
||||
raop_buffer_t *raop_buffer_init(logger_t *logger,
|
||||
const unsigned char *aeskey,
|
||||
const unsigned char *aesiv);
|
||||
int raop_buffer_enqueue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned short datalen, uint64_t timestamp, int use_seqnum);
|
||||
void *raop_buffer_dequeue(raop_buffer_t *raop_buffer, unsigned int *length, uint64_t *timestamp, unsigned short *seqnum, int no_resend);
|
||||
int raop_buffer_enqueue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned short datalen, uint64_t *ntp_timestamp, uint64_t *rtp_timestamp, int use_seqnum);
|
||||
void *raop_buffer_dequeue(raop_buffer_t *raop_buffer, unsigned int *length, uint64_t *ntp_timestamp, uint64_t *rtp_timestamp, unsigned short *seqnum, int no_resend);
|
||||
void raop_buffer_handle_resends(raop_buffer_t *raop_buffer, raop_resend_cb_t resend_cb, void *opaque);
|
||||
void raop_buffer_flush(raop_buffer_t *raop_buffer, int next_seq);
|
||||
|
||||
|
||||
@@ -503,7 +503,7 @@ raop_handler_setup(raop_conn_t *conn,
|
||||
unsigned short remote_cport = 0;
|
||||
unsigned char ct;
|
||||
unsigned int sr = AUDIO_SAMPLE_RATE; /* all AirPlay audio formats supported so far have sample rate 44.1kHz */
|
||||
unsigned int ad = (unsigned int) (((uint64_t) conn->raop->audio_delay_micros) * AUDIO_SAMPLE_RATE / SECOND_IN_USECS);
|
||||
unsigned int ad = (unsigned int) conn->raop->audio_delay_micros;
|
||||
|
||||
uint64_t uint_val = 0;
|
||||
plist_t req_stream_control_port_node = plist_dict_get_item(req_stream_node, "controlPort");
|
||||
|
||||
156
lib/raop_rtp.c
156
lib/raop_rtp.c
@@ -36,8 +36,8 @@
|
||||
#define RAOP_RTP_SYNC_DATA_COUNT 8
|
||||
#define SEC SECOND_IN_NSECS
|
||||
|
||||
#define DELAY_AAC 500000000 //empirical, matches audio latency of about -0.5 sec after first clock sync event
|
||||
#define DELAY_ALAC 200000000 //empirical, matches audio latency of about -2.0 sec after first clock sync event
|
||||
#define DELAY_AAC 0.25 //empirical, matches audio latency of about -0.25 sec after first clock sync event
|
||||
#define DELAY_ALAC 2.0 //empirical, matches audio latency of about -2.0 sec after first clock sync event
|
||||
|
||||
/* note: it is unclear what will happen in the unlikely event that this code is running at the time of the unix-time
|
||||
* epoch event on 2038-01-19 at 3:14:08 UTC ! (but Apple will surely have removed AirPlay "legacy pairing" by then!) */
|
||||
@@ -54,7 +54,7 @@ struct raop_rtp_s {
|
||||
// Time and sync
|
||||
raop_ntp_t *ntp;
|
||||
double rtp_clock_rate;
|
||||
unsigned int audio_delay_rtp;
|
||||
unsigned int audio_delay_micros;
|
||||
int64_t rtp_sync_offset;
|
||||
raop_rtp_sync_data_t sync_data[RAOP_RTP_SYNC_DATA_COUNT];
|
||||
int sync_data_index;
|
||||
@@ -388,34 +388,35 @@ raop_rtp_process_events(raop_rtp_t *raop_rtp, void *cb_data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void raop_rtp_sync_clock(raop_rtp_t *raop_rtp, uint64_t ntp_time, uint64_t rtp_time, int shift) {
|
||||
int latest;
|
||||
uint32_t valid_data_count = 0;
|
||||
valid_data_count = 0;
|
||||
double total_offsets = 0;
|
||||
int64_t rtp_offset, avg_offset, correction;
|
||||
void raop_rtp_sync_clock(raop_rtp_t *raop_rtp, uint64_t *ntp_time, uint64_t *rtp_time) {
|
||||
/* ntp_time = (uint64_t)(((int64_t)(raop_rtp->rtp_clock_rate * rtp_time)) + raop_rtp->rtp_sync_offset) */
|
||||
int latest, valid_data_count = 0;
|
||||
uint64_t ntp_sum = 0, rtp_sum = 0;
|
||||
double offset = ((double) *ntp_time) - raop_rtp->rtp_clock_rate * *rtp_time;
|
||||
int64_t correction = 0;
|
||||
|
||||
raop_rtp->sync_data_index = (raop_rtp->sync_data_index + 1) % RAOP_RTP_SYNC_DATA_COUNT;
|
||||
latest = raop_rtp->sync_data_index;
|
||||
raop_rtp->sync_data[latest].rtp_time = rtp_time;
|
||||
raop_rtp->sync_data[latest].ntp_time = ntp_time;
|
||||
raop_rtp->sync_data[latest].rtp_time = *rtp_time;
|
||||
raop_rtp->sync_data[latest].ntp_time = *ntp_time;
|
||||
|
||||
for (int i = 0; i < RAOP_RTP_SYNC_DATA_COUNT; i++) {
|
||||
if (raop_rtp->sync_data[i].ntp_time == 0) continue;
|
||||
rtp_offset = ((int64_t) raop_rtp->sync_data[i].rtp_time) - ((int64_t) raop_rtp->sync_data[latest].rtp_time);
|
||||
total_offsets += ((double) rtp_offset) * raop_rtp-> rtp_clock_rate;
|
||||
total_offsets -= (double) (((int64_t) raop_rtp->sync_data[i].ntp_time) - ((int64_t) raop_rtp->sync_data[latest].ntp_time));
|
||||
valid_data_count++;
|
||||
if (i == latest) continue;
|
||||
ntp_sum += *ntp_time - raop_rtp->sync_data[i].ntp_time;
|
||||
rtp_sum += *rtp_time - raop_rtp->sync_data[i].rtp_time;
|
||||
}
|
||||
total_offsets = (total_offsets / valid_data_count);
|
||||
rtp_offset = ((int64_t) raop_rtp->sync_data[latest].rtp_time) - ((int64_t) raop_rtp->rtp_start_time) + ((int64_t) shift);
|
||||
total_offsets += ((double) rtp_offset) * raop_rtp->rtp_clock_rate;
|
||||
avg_offset = (int64_t) total_offsets;
|
||||
avg_offset -= ((int64_t) raop_rtp->sync_data[latest].ntp_time) - ((int64_t) raop_rtp->ntp_start_time);
|
||||
correction = avg_offset - raop_rtp->rtp_sync_offset;
|
||||
raop_rtp->rtp_sync_offset = avg_offset;
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp sync correction=%lld, rtp_sync_offset = %8.6f ",
|
||||
correction, ((double) raop_rtp->rtp_sync_offset) / SEC);
|
||||
|
||||
if (valid_data_count > 1) {
|
||||
correction -= raop_rtp->rtp_sync_offset;
|
||||
offset += (((double) ntp_sum) - raop_rtp->rtp_clock_rate * rtp_sum) / valid_data_count;
|
||||
}
|
||||
raop_rtp->rtp_sync_offset = (int64_t) offset;
|
||||
correction += raop_rtp->rtp_sync_offset;
|
||||
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "dataset %d raop_rtp sync correction=%lld, rtp_sync_offset = %8.6f ",
|
||||
valid_data_count, correction, offset);
|
||||
}
|
||||
|
||||
uint64_t rtp64_time (raop_rtp_t *raop_rtp, const uint32_t *rtp32) {
|
||||
@@ -451,14 +452,15 @@ raop_rtp_thread_udp(void *arg)
|
||||
struct sockaddr_storage saddr;
|
||||
socklen_t saddrlen;
|
||||
|
||||
double latency = 0;
|
||||
|
||||
/* for initial rtp to ntp conversions */
|
||||
bool have_synced = false;
|
||||
int rtp_count = 0;
|
||||
int64_t initial_offset = 0;
|
||||
double sync_adjustment = 0;
|
||||
int64_t delay = 0;
|
||||
uint64_t delay = 0;
|
||||
unsigned short seqnum1 = 0, seqnum2 = 0;
|
||||
bool offset_estimate_initialized = false;
|
||||
|
||||
|
||||
assert(raop_rtp);
|
||||
raop_rtp->ntp_start_time = raop_ntp_get_local_time(raop_rtp->ntp);
|
||||
@@ -469,7 +471,22 @@ raop_rtp_thread_udp(void *arg)
|
||||
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp start_time = %8.6f (raop_rtp audio)",
|
||||
((double) raop_rtp->ntp_start_time) / SEC);
|
||||
|
||||
|
||||
switch (raop_rtp->ct) {
|
||||
case 0x02:
|
||||
latency = DELAY_ALAC;
|
||||
delay = (uint64_t)(latency * SECOND_IN_NSECS); /* DELAY = 2.0 sec is empirical choice for ALAC */
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "Audio is ALAC: using initial latency estimate -%8.6f sec", latency);
|
||||
break;
|
||||
case 0x08:
|
||||
latency = DELAY_AAC;
|
||||
delay = (uint64_t)(latency * SECOND_IN_NSECS); /* DELAY = 0.25 sec is empirical choice for AAC-ELD */
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "Audio is AAC: using initial latency estimate -%8.6f sec", latency);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
while(1) {
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
@@ -520,8 +537,13 @@ raop_rtp_thread_udp(void *arg)
|
||||
if (resent_packetlen >= 12) {
|
||||
uint32_t timestamp = byteutils_get_int_be(resent_packet, 4);
|
||||
uint64_t rtp_time = rtp64_time(raop_rtp, ×tamp);
|
||||
uint64_t ntp_time = 0;
|
||||
if (have_synced) {
|
||||
ntp_time = (uint64_t) (raop_rtp->rtp_sync_offset + (int64_t) (raop_rtp->rtp_clock_rate * rtp_time));
|
||||
ntp_time = raop_ntp_convert_remote_time(raop_rtp->ntp, ntp_time);
|
||||
}
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp resent audio packet: seqnum=%u", seqnum);
|
||||
int result = raop_buffer_enqueue(raop_rtp->buffer, resent_packet, resent_packetlen, rtp_time, 1);
|
||||
int result = raop_buffer_enqueue(raop_rtp->buffer, resent_packet, resent_packetlen, &ntp_time, &rtp_time, 1);
|
||||
assert(result >= 0);
|
||||
} else {
|
||||
/* type_c = 0x56 packets with length 8 have been reported */
|
||||
@@ -543,8 +565,6 @@ raop_rtp_thread_udp(void *arg)
|
||||
// The unit for the rtp clock is 1 / sample rate = 1 / 44100
|
||||
uint32_t sync_rtp = byteutils_get_int_be(packet, 4);
|
||||
uint64_t sync_rtp64 = rtp64_time(raop_rtp, &sync_rtp);
|
||||
|
||||
|
||||
if (have_synced == false) {
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "first audio rtp sync");
|
||||
have_synced = true;
|
||||
@@ -552,21 +572,13 @@ raop_rtp_thread_udp(void *arg)
|
||||
uint64_t sync_ntp_raw = byteutils_get_long_be(packet, 8);
|
||||
uint64_t sync_ntp_remote = raop_ntp_timestamp_to_nano_seconds(sync_ntp_raw, true);
|
||||
uint64_t sync_ntp_local = raop_ntp_convert_remote_time(raop_rtp->ntp, sync_ntp_remote);
|
||||
int64_t shift = 0;
|
||||
switch (raop_rtp->ct) {
|
||||
case 0x02: /* ALAC audio-only mode */
|
||||
break;
|
||||
default:
|
||||
shift -= (int64_t) raop_rtp->audio_delay_rtp; /* remove delay in Mirror mode */
|
||||
break;
|
||||
}
|
||||
char *str = utils_data_to_string(packet, packetlen, 20);
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG,
|
||||
"raop_rtp sync: client ntp=%8.6f, ntp = %8.6f, ntp_start_time %8.6f, sync_rtp=%u\n%s",
|
||||
((double) sync_ntp_remote) / SEC, ((double)sync_ntp_local) / SEC,
|
||||
((double) raop_rtp->ntp_start_time) / SEC, sync_rtp, str);
|
||||
free(str);
|
||||
raop_rtp_sync_clock(raop_rtp, sync_ntp_local, sync_rtp64, shift);
|
||||
raop_rtp_sync_clock(raop_rtp, &sync_ntp_remote, &sync_rtp64);
|
||||
} else {
|
||||
char *str = utils_data_to_string(packet, packetlen, 16);
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp unknown udp control packet\n%s", str);
|
||||
@@ -619,69 +631,59 @@ raop_rtp_thread_udp(void *arg)
|
||||
int no_resend = (raop_rtp->control_rport == 0); /* true when control_rport is not set */
|
||||
uint32_t rtp_timestamp = byteutils_get_int_be(packet, 4);
|
||||
uint64_t rtp_time = rtp64_time(raop_rtp, &rtp_timestamp);
|
||||
uint64_t ntp_time = 0;
|
||||
if (have_synced == false) {
|
||||
/* until the first rtp sync occurs, we don't know the exact client ntp timestamp that matches the client rtp timestamp */
|
||||
int64_t sync_ntp = ((int64_t) raop_ntp_get_local_time(raop_rtp->ntp)) - ((int64_t) raop_rtp->ntp_start_time) ;
|
||||
int64_t sync_rtp = ((int64_t) rtp_time) - ((int64_t) raop_rtp->rtp_start_time);
|
||||
int64_t offset;
|
||||
unsigned short seqnum = byteutils_get_short_be(packet,2);
|
||||
if (!offset_estimate_initialized) {
|
||||
offset_estimate_initialized = true;
|
||||
switch (raop_rtp->ct) {
|
||||
case 0x02:
|
||||
delay = DELAY_ALAC; /* DELAY = 2000000000 (2.0 sec) is empirical choice for ALAC */
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "Audio is ALAC: using initial latency estimate -%8.6f sec",
|
||||
((double) delay) / SEC);
|
||||
break;
|
||||
case 0x08:
|
||||
delay = DELAY_AAC; /* DELAY = 500000000 (0.5 sec) is empirical choice for AAC-ELD */
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "Audio is AAC: using initial latency estimate -%8.6f sec",
|
||||
((double) delay ) / SEC);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
initial_offset = -(sync_ntp + delay);
|
||||
raop_rtp->rtp_sync_offset = initial_offset;
|
||||
sync_adjustment = 0;
|
||||
if (rtp_count == 0) {
|
||||
sync_adjustment = ((double) sync_ntp);
|
||||
rtp_count = 1;
|
||||
seqnum1 = seqnum;
|
||||
seqnum2 = seqnum;
|
||||
}
|
||||
sync_ntp += delay;
|
||||
offset = -sync_ntp;
|
||||
if (seqnum2 != seqnum) { /* for AAC-ELD only use copy 3 of the 3 copies of each frame */
|
||||
if (seqnum2 != seqnum) { /* for AAC-ELD only use copy 1 of the 3 copies of each frame */
|
||||
rtp_count++;
|
||||
offset -= initial_offset;
|
||||
sync_adjustment += ((double) offset) + (((double) sync_rtp) * raop_rtp->rtp_clock_rate);
|
||||
raop_rtp->rtp_sync_offset = initial_offset + (int64_t) (sync_adjustment / rtp_count);
|
||||
//logger_log(raop_rtp->logger, LOGGER_DEBUG, "initial estimate of rtp_sync_offset %d secnum = %u: %8.6f",
|
||||
// rtp_count, seqnum, ((double) raop_rtp->rtp_sync_offset) / SEC);
|
||||
sync_adjustment += (((double) sync_ntp) - raop_rtp->rtp_clock_rate * sync_rtp - sync_adjustment) / rtp_count;
|
||||
}
|
||||
seqnum2 = seqnum1;
|
||||
seqnum1 = seqnum;
|
||||
} else {
|
||||
ntp_time = (uint64_t) (raop_rtp->rtp_sync_offset + (int64_t) (raop_rtp->rtp_clock_rate * rtp_time));
|
||||
ntp_time = raop_ntp_convert_remote_time(raop_rtp->ntp, ntp_time);
|
||||
}
|
||||
int result = raop_buffer_enqueue(raop_rtp->buffer, packet, packetlen, rtp_time, 1);
|
||||
int result = raop_buffer_enqueue(raop_rtp->buffer, packet, packetlen, &ntp_time, &rtp_time, 1);
|
||||
assert(result >= 0);
|
||||
// Render continuous buffer entries
|
||||
void *payload = NULL;
|
||||
unsigned int payload_size;
|
||||
unsigned short seqnum;
|
||||
uint64_t rtp64_timestamp;
|
||||
while ((payload = raop_buffer_dequeue(raop_rtp->buffer, &payload_size, &rtp64_timestamp, &seqnum, no_resend))) {
|
||||
double elapsed_time = (((double) (rtp64_timestamp - (uint64_t) raop_rtp->rtp_start_time)) * raop_rtp->rtp_clock_rate);
|
||||
uint64_t ntp_timestamp;
|
||||
while ((payload = raop_buffer_dequeue(raop_rtp->buffer, &payload_size, &ntp_timestamp, &rtp64_timestamp, &seqnum, no_resend))) {
|
||||
audio_decode_struct audio_data;
|
||||
audio_data.data_len = payload_size;
|
||||
audio_data.data = payload;
|
||||
audio_data.ntp_time = raop_rtp->ntp_start_time + (uint64_t) elapsed_time;
|
||||
audio_data.ntp_time -= raop_rtp->rtp_sync_offset;
|
||||
audio_data.rtp_time = rtp64_timestamp;
|
||||
audio_data.seqnum = seqnum;
|
||||
audio_data.data_len = payload_size;
|
||||
audio_data.data = payload;
|
||||
if (ntp_timestamp) {
|
||||
audio_data.ntp_time = ntp_timestamp;
|
||||
} else {
|
||||
if (have_synced) {
|
||||
uint64_t ntp_remote = (uint64_t) (raop_rtp->rtp_sync_offset + (int64_t) (raop_rtp->rtp_clock_rate * rtp64_timestamp));
|
||||
audio_data.ntp_time = raop_ntp_convert_remote_time(raop_rtp->ntp, ntp_remote);
|
||||
} else {
|
||||
audio_data.ntp_time = raop_rtp->ntp_start_time + delay +
|
||||
(uint64_t) (sync_adjustment + raop_rtp->rtp_clock_rate * (rtp64_timestamp - raop_rtp->rtp_start_time));
|
||||
}
|
||||
}
|
||||
raop_rtp->callbacks.audio_process(raop_rtp->callbacks.cls, raop_rtp->ntp, &audio_data);
|
||||
free(payload);
|
||||
uint64_t ntp_now = raop_ntp_get_local_time(raop_rtp->ntp);
|
||||
int64_t latency = ((int64_t) ntp_now) - ((int64_t) audio_data.ntp_time);
|
||||
int64_t latency = ((int64_t) ntp_now) - ((int64_t) audio_data.ntp_time);
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp audio: now = %8.6f, ntp = %8.6f, latency = %8.6f, rtp_time=%u seqnum = %u",
|
||||
((double) ntp_now ) / SEC, ((double) audio_data.ntp_time) / SEC, ((double) latency) / SEC, (uint32_t) rtp64_timestamp,
|
||||
((double) ntp_now ) / SEC, ((double) audio_data.ntp_time) / SEC, ((double) latency) / SEC, (uint32_t) rtp64_timestamp,
|
||||
seqnum);
|
||||
}
|
||||
|
||||
@@ -725,7 +727,7 @@ raop_rtp_start_audio(raop_rtp_t *raop_rtp, int use_udp, unsigned short *control_
|
||||
|
||||
raop_rtp->ct = *ct;
|
||||
raop_rtp->rtp_clock_rate = SECOND_IN_NSECS / *sr;
|
||||
raop_rtp->audio_delay_rtp = *ad;
|
||||
raop_rtp->audio_delay_micros = *ad;
|
||||
|
||||
/* Initialize ports and sockets */
|
||||
raop_rtp->control_lport = *control_lport;
|
||||
|
||||
Reference in New Issue
Block a user