convert all times from micro to nano secs

This commit is contained in:
F. Duncanh
2023-02-05 01:59:25 -05:00
parent ba1dd3ccbd
commit cf6452b713
7 changed files with 51 additions and 42 deletions

View File

@@ -12,6 +12,8 @@
* Lesser General Public License for more details.
*/
#define SECOND_IN_NSECS 1000000000UL
#include <time.h>
#ifdef _WIN32
# include <winsock2.h>
@@ -96,23 +98,23 @@ void byteutils_put_int(unsigned char* b, int offset, uint32_t value) {
}
/**
* Reads an ntp timestamp and returns it as micro seconds since the Unix epoch
* Reads an ntp timestamp and returns it as nano seconds since the Unix epoch
*/
uint64_t byteutils_get_ntp_timestamp(unsigned char *b, int offset) {
uint64_t seconds = ntohl(((unsigned int) byteutils_get_int(b, offset))) - SECONDS_FROM_1900_TO_1970;
uint64_t fraction = ntohl((unsigned int) byteutils_get_int(b, offset + 4));
return (seconds * 1000000L) + ((fraction * 1000000L) >> 32);
return (seconds * SECOND_IN_NSECS) + ((fraction * SECOND_IN_NSECS) >> 32);
}
/**
* Writes a time given as micro seconds since the Unix time epoch as an ntp timestamp
* Writes a time given as nano seconds since the Unix time epoch as an ntp timestamp
* into the buffer at position offset
*/
void byteutils_put_ntp_timestamp(unsigned char *b, int offset, uint64_t us_since_1970) {
uint64_t seconds = us_since_1970 / 1000000L;
uint64_t microseconds = us_since_1970 % 1000000L;
void byteutils_put_ntp_timestamp(unsigned char *b, int offset, uint64_t ns_since_1970) {
uint64_t seconds = ns_since_1970 / SECOND_IN_NSECS;
uint64_t nanoseconds = ns_since_1970 % SECOND_IN_NSECS;
seconds += SECONDS_FROM_1900_TO_1970;
uint64_t fraction = (microseconds << 32) / 1000000L;
uint64_t fraction = (nanoseconds << 32) / SECOND_IN_NSECS;
// Write in big endian!
byteutils_put_int(b, offset, htonl(seconds));

View File

@@ -33,6 +33,7 @@
#include "byteutils.h"
#include "utils.h"
#define SECOND_IN_NSECS 1000000000UL
#define RAOP_NTP_DATA_COUNT 8
#define RAOP_NTP_PHI_PPM 15ull // PPM
#define RAOP_NTP_R_RHO ((1ull << 32) / 1000u) // packet precision
@@ -284,7 +285,8 @@ raop_ntp_thread(void *arg)
byteutils_put_ntp_timestamp(request, 24, send_time);
int send_len = sendto(raop_ntp->tsock, (char *)request, sizeof(request), 0,
(struct sockaddr *) &raop_ntp->remote_saddr, raop_ntp->remote_saddr_len);
logger_log(raop_ntp->logger, LOGGER_DEBUG, "\nraop_ntp send_len = %d, now = %llu", send_len, send_time);
logger_log(raop_ntp->logger, LOGGER_DEBUG, "\nraop_ntp send_len = %d, now = %8.6f", send_len,
(double) send_time / SECOND_IN_NSECS);
if (send_len < 0) {
logger_log(raop_ntp->logger, LOGGER_ERR, "raop_ntp error sending request");
} else {
@@ -305,7 +307,6 @@ raop_ntp_thread(void *arg)
} else {
//local time of the server when the NTP response packet returns
int64_t t3 = (int64_t) raop_ntp_get_local_time(raop_ntp);
timeout_counter = 0;
char *str = utils_data_to_string(response, response_len, 16);
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp receive time type_t=%d packetlen = %d\n%s",
@@ -321,15 +322,15 @@ raop_ntp_thread(void *arg)
// Local time of the client when the response message leaves the client
int64_t t2 = (int64_t) byteutils_get_ntp_timestamp(response, 24);
// The iOS client device sends its time in micro seconds relative to an arbitrary Epoch (the last boot).
// For a little bonus confusion, they add SECONDS_FROM_1900_TO_1970 * 1000000 us.
// The iOS client device sends its time in seconds relative to an arbitrary Epoch (the last boot).
// For a little bonus confusion, they add SECONDS_FROM_1900_TO_1970.
// This means we have to expect some rather huge offset, but its growth or shrink over time should be small.
raop_ntp->data_index = (raop_ntp->data_index + 1) % RAOP_NTP_DATA_COUNT;
raop_ntp->data[raop_ntp->data_index].time = t3;
raop_ntp->data[raop_ntp->data_index].offset = ((t1 - t0) + (t2 - t3)) / 2;
raop_ntp->data[raop_ntp->data_index].delay = ((t3 - t0) - (t2 - t1));
raop_ntp->data[raop_ntp->data_index].dispersion = RAOP_NTP_R_RHO + RAOP_NTP_S_RHO + (t3 - t0) * RAOP_NTP_PHI_PPM / 1000000u;
raop_ntp->data[raop_ntp->data_index].dispersion = RAOP_NTP_R_RHO + RAOP_NTP_S_RHO + (t3 - t0) * RAOP_NTP_PHI_PPM / SECOND_IN_NSECS;
// Sort by delay
memcpy(data_sorted, raop_ntp->data, sizeof(data_sorted));
@@ -341,7 +342,7 @@ raop_ntp_thread(void *arg)
// Calculate dispersion
for(int i = 0; i < RAOP_NTP_DATA_COUNT; ++i) {
unsigned long long disp = raop_ntp->data[i].dispersion + (t3 - raop_ntp->data[i].time) * RAOP_NTP_PHI_PPM / 1000000u;
unsigned long long disp = raop_ntp->data[i].dispersion + (t3 - raop_ntp->data[i].time) * RAOP_NTP_PHI_PPM / SECOND_IN_NSECS;
dispersion += disp / two_pow_n[i];
}
@@ -454,54 +455,54 @@ raop_ntp_stop(raop_ntp_t *raop_ntp)
}
/**
* Converts from a little endian ntp timestamp to micro seconds since the Unix epoch.
* Converts from a little endian ntp timestamp to nano seconds since the Unix epoch.
* Does the same thing as byteutils_get_ntp_timestamp, except its input is an uint64_t
* and expected to already be in little endian.
* Please note this just converts to a different representation, the clock remains the
* same.
*/
uint64_t raop_ntp_timestamp_to_micro_seconds(uint64_t ntp_timestamp, bool account_for_epoch_diff) {
uint64_t raop_ntp_timestamp_to_nano_seconds(uint64_t ntp_timestamp, bool account_for_epoch_diff) {
uint64_t seconds = ((ntp_timestamp >> 32) & 0xffffffff) - (account_for_epoch_diff ? SECONDS_FROM_1900_TO_1970 : 0);
uint64_t fraction = (ntp_timestamp & 0xffffffff);
return (seconds * 1000000) + ((fraction * 1000000) >> 32);
return (seconds * SECOND_IN_NSECS) + ((fraction * SECOND_IN_NSECS) >> 32);
}
/**
* Returns the current time in micro seconds according to the local wall clock.
* Returns the current time in nano seconds according to the local wall clock.
* The system Unix time is used as the local wall clock.
*/
uint64_t raop_ntp_get_local_time(raop_ntp_t *raop_ntp) {
struct timespec time;
clock_gettime(CLOCK_REALTIME, &time);
return (uint64_t)time.tv_sec * 1000000L + (uint64_t)(time.tv_nsec / 1000);
return ((uint64_t)time.tv_sec) * SECOND_IN_NSECS + (uint64_t)(time.tv_nsec);
}
/**
* Returns the current time in micro seconds according to the remote wall clock.
* Returns the current time in nano seconds according to the remote wall clock.
*/
uint64_t raop_ntp_get_remote_time(raop_ntp_t *raop_ntp) {
MUTEX_LOCK(raop_ntp->sync_params_mutex);
int64_t offset = raop_ntp->sync_offset;
MUTEX_UNLOCK(raop_ntp->sync_params_mutex);
return (uint64_t) ((int64_t) raop_ntp_get_local_time(raop_ntp)) + ((int64_t) offset);
return (uint64_t) (((int64_t) raop_ntp_get_local_time(raop_ntp)) + offset);
}
/**
* Returns the local wall clock time in micro seconds for the given point in remote clock time
* Returns the local wall clock time in nano seconds for the given point in remote clock time
*/
uint64_t raop_ntp_convert_remote_time(raop_ntp_t *raop_ntp, uint64_t remote_time) {
MUTEX_LOCK(raop_ntp->sync_params_mutex);
uint64_t offset = raop_ntp->sync_offset;
int64_t offset = raop_ntp->sync_offset;
MUTEX_UNLOCK(raop_ntp->sync_params_mutex);
return (uint64_t) ((int64_t) remote_time) - ((int64_t) offset);
return (uint64_t) (((int64_t) remote_time) - offset);
}
/**
* Returns the remote wall clock time in micro seconds for the given point in local clock time
* Returns the remote wall clock time in nano seconds for the given point in local clock time
*/
uint64_t raop_ntp_convert_local_time(raop_ntp_t *raop_ntp, uint64_t local_time) {
MUTEX_LOCK(raop_ntp->sync_params_mutex);
uint64_t offset = raop_ntp->sync_offset;
int64_t offset = raop_ntp->sync_offset;
MUTEX_UNLOCK(raop_ntp->sync_params_mutex);
return (uint64_t) ((int64_t) local_time) + ((int64_t) offset);
return (uint64_t) (((int64_t) local_time) + offset);
}

View File

@@ -31,7 +31,7 @@ unsigned short raop_ntp_get_port(raop_ntp_t *raop_ntp);
void raop_ntp_destroy(raop_ntp_t *raop_rtp);
uint64_t raop_ntp_timestamp_to_micro_seconds(uint64_t ntp_timestamp, bool account_for_epoch_diff);
uint64_t raop_ntp_timestamp_to_nano_seconds(uint64_t ntp_timestamp, bool account_for_epoch_diff);
uint64_t raop_ntp_get_local_time(raop_ntp_t *raop_ntp);
uint64_t raop_ntp_get_remote_time(raop_ntp_t *raop_ntp);

View File

@@ -32,12 +32,13 @@
#define NO_FLUSH (-42)
#define RAOP_RTP_SAMPLE_RATE (44100.0 / 1000000.0)
#define SECOND_IN_NSECS 1000000000UL
#define RAOP_RTP_SAMPLE_RATE (44100.0 / SECOND_IN_NSECS)
#define RAOP_RTP_SYNC_DATA_COUNT 8
#define SEC 1000000
#define SEC SECOND_IN_NSECS
#define DELAY_AAC 500000 //empirical, matches audio latency of about -0.5 sec after first clock sync event
#define DELAY_ALAC 2000000 //empirical, matches audio latency of about -2.0 sec after first clock sync event
#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
/* 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!) */
@@ -550,7 +551,7 @@ raop_rtp_thread_udp(void *arg)
have_synced = true;
}
uint64_t sync_ntp_raw = byteutils_get_long_be(packet, 8);
uint64_t sync_ntp_remote = raop_ntp_timestamp_to_micro_seconds(sync_ntp_raw, true);
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;
switch (raop_rtp->ct) {
@@ -631,12 +632,12 @@ raop_rtp_thread_udp(void *arg)
offset_estimate_initialized = true;
switch (raop_rtp->ct) {
case 0x02:
delay = DELAY_ALAC; /* DELAY = 2000000 (2.0 sec) is empirical choice for ALAC */
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 = 500000 (0.5 sec) is empirical choice for AAC-ELD */
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;
@@ -682,7 +683,7 @@ raop_rtp_thread_udp(void *arg)
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);
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp audio: now = %8.6f, npt = %8.6f, latency = %8.6f, rtp_time=%u seqnum = %u",
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,
seqnum);
}

View File

@@ -48,7 +48,8 @@
#define CAST
#endif
#define SEC 1000000
#define SECOND_IN_NSECS 1000000000UL
#define SEC SECOND_IN_NSECS
/* for MacOS, where SOL_TCP and TCP_KEEPIDLE are not defined */
#if !defined(SOL_TCP) && defined(IPPROTO_TCP)
@@ -359,9 +360,9 @@ raop_rtp_mirror_thread(void *arg)
// Conveniently, the video data is already stamped with the remote wall clock time,
// so no additional clock syncing needed. The only thing odd here is that the video
// ntp time stamps don't include the SECONDS_FROM_1900_TO_1970, so it's really just
// counting micro seconds since last boot.
// counting nano seconds since last boot.
ntp_timestamp_raw = byteutils_get_long(packet, 8);
uint64_t ntp_timestamp_remote = raop_ntp_timestamp_to_micro_seconds(ntp_timestamp_raw, false);
uint64_t ntp_timestamp_remote = raop_ntp_timestamp_to_nano_seconds(ntp_timestamp_raw, false);
uint64_t ntp_timestamp = raop_ntp_convert_remote_time(raop_rtp_mirror->ntp, ntp_timestamp_remote);
uint64_t ntp_now = raop_ntp_get_local_time(raop_rtp_mirror->ntp);

View File

@@ -21,6 +21,7 @@
#include <gst/gst.h>
#include <gst/app/gstappsrc.h>
#include "audio_renderer.h"
#define SECOND_IN_NSECS 1000000000UL
/* GStreamer Caps strings for Airplay-defined audio compression types (ct) */
@@ -201,11 +202,12 @@ void audio_renderer_start(unsigned char *ct) {
void audio_renderer_render_buffer(unsigned char* data, int *data_len, unsigned short *seqnum, uint64_t *ntp_time) {
GstBuffer *buffer;
bool valid;
GstClockTime pts = (GstClockTime) (*ntp_time * 1000); /* convert from usec to nsec */
GstClockTime pts = (GstClockTime) *ntp_time ; /* now in nsecs */
if (pts >= gst_audio_pipeline_base_time) {
pts -= gst_audio_pipeline_base_time;
} else {
logger_log(logger, LOGGER_ERR, "*** invalid *pts_raw < gst_audio_pipeline_base_time");
logger_log(logger, LOGGER_ERR, "*** invalid ntp_time < gst_audio_pipeline_base_time\n%8.6f ntp_time\n%8.6f base_time",
((double) *ntp_time) / SECOND_IN_NSECS, ((double) gst_audio_pipeline_base_time) / SECOND_IN_NSECS);
return;
}
if (data_len == 0 || renderer == NULL) return;

View File

@@ -21,6 +21,7 @@
#include <gst/gst.h>
#include <gst/app/gstappsrc.h>
#define SECOND_IN_NSECS 1000000000UL
#ifdef X_DISPLAY_FIX
#include <gst/video/navigation.h>
#include "x_display_fix.h"
@@ -220,11 +221,12 @@ void video_renderer_start() {
void video_renderer_render_buffer(unsigned char* data, int *data_len, int *nal_count, uint64_t *ntp_time) {
GstBuffer *buffer;
GstClockTime pts = (GstClockTime) (*ntp_time * 1000); /*convert from usec to nsec */
GstClockTime pts = (GstClockTime) *ntp_time; /*now in nsecs */
if (pts >= gst_video_pipeline_base_time) {
pts -= gst_video_pipeline_base_time;
} else {
logger_log(logger, LOGGER_ERR, "*** invalid *pts_raw < gst_video_pipeline_base_time") ;
logger_log(logger, LOGGER_ERR, "*** invalid ntp_time < gst_video_pipeline_base_time\n%8.6f ntp_time\n%8.6f base_time",
((double) *ntp_time) / SECOND_IN_NSECS, ((double) gst_video_pipeline_base_time) / SECOND_IN_NSECS);
return;
}
g_assert(data_len != 0);