Try using the monotonic clock for checking output rate...
This commit is contained in:
@@ -55,9 +55,9 @@
|
||||
#endif
|
||||
|
||||
#ifdef COMPILE_FOR_FREEBSD
|
||||
#include <netinet/in.h>
|
||||
#include <net/if_types.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_types.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#ifdef COMPILE_FOR_OSX
|
||||
@@ -1258,6 +1258,49 @@ uint64_t get_absolute_time_in_fp() {
|
||||
return time_now_fp;
|
||||
}
|
||||
|
||||
uint64_t get_monotonic_time_in_ns() {
|
||||
uint64_t time_now_ns;
|
||||
|
||||
#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
|
||||
struct timespec tn;
|
||||
// can't use CLOCK_MONOTONIC_RAW as it's not implemented in OpenWrt
|
||||
// CLOCK_REALTIME because PTP uses it.
|
||||
clock_gettime(CLOCK_MONOTONIC, &tn);
|
||||
uint64_t tnnsec = tn.tv_sec;
|
||||
tnnsec = tnnsec * 1000000000;
|
||||
uint64_t tnjnsec = tn.tv_nsec;
|
||||
time_now_ns = tnnsec + tnjnsec;
|
||||
#endif
|
||||
|
||||
#ifdef COMPILE_FOR_OSX
|
||||
uint64_t time_now_mach;
|
||||
uint64_t elapsedNano;
|
||||
static mach_timebase_info_data_t sTimebaseInfo = {0, 0};
|
||||
|
||||
// this actually give you a monotonic clock
|
||||
// see https://news.ycombinator.com/item?id=6303755
|
||||
time_now_mach = mach_absolute_time();
|
||||
|
||||
// If this is the first time we've run, get the timebase.
|
||||
// We can use denom == 0 to indicate that sTimebaseInfo is
|
||||
// uninitialised because it makes no sense to have a zero
|
||||
// denominator in a fraction.
|
||||
|
||||
if (sTimebaseInfo.denom == 0) {
|
||||
debug(1, "Mac initialise timebase info.");
|
||||
(void)mach_timebase_info(&sTimebaseInfo);
|
||||
}
|
||||
|
||||
// Do the maths. We hope that the multiplication doesn't
|
||||
// overflow; the price you pay for working in fixed point.
|
||||
|
||||
// this gives us nanoseconds
|
||||
time_now_ns = time_now_mach * sTimebaseInfo.numer / sTimebaseInfo.denom;
|
||||
#endif
|
||||
|
||||
return time_now_ns;
|
||||
}
|
||||
|
||||
uint64_t get_absolute_time_in_ns() {
|
||||
uint64_t time_now_ns;
|
||||
|
||||
@@ -1277,6 +1320,7 @@ uint64_t get_absolute_time_in_ns() {
|
||||
uint64_t elapsedNano;
|
||||
static mach_timebase_info_data_t sTimebaseInfo = {0, 0};
|
||||
|
||||
// this actually give you a monotonic clock
|
||||
time_now_mach = mach_absolute_time();
|
||||
|
||||
// If this is the first time we've run, get the timebase.
|
||||
@@ -1963,7 +2007,7 @@ int get_device_id(uint8_t *id, int int_length) {
|
||||
t = id;
|
||||
int found = 0;
|
||||
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
|
||||
#ifdef AF_PACKET
|
||||
#ifdef AF_PACKET
|
||||
if ((ifa->ifa_addr) && (ifa->ifa_addr->sa_family == AF_PACKET)) {
|
||||
struct sockaddr_ll *s = (struct sockaddr_ll *)ifa->ifa_addr;
|
||||
if ((strcmp(ifa->ifa_name, "lo") != 0) && (found == 0)) {
|
||||
@@ -1973,25 +2017,23 @@ int get_device_id(uint8_t *id, int int_length) {
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
#ifdef AF_LINK
|
||||
struct sockaddr_dl * sdl = (struct sockaddr_dl *) ifa->ifa_addr;
|
||||
if ((sdl) && (sdl->sdl_family == AF_LINK)) {
|
||||
#else
|
||||
#ifdef AF_LINK
|
||||
struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr;
|
||||
if ((sdl) && (sdl->sdl_family == AF_LINK)) {
|
||||
if (sdl->sdl_type == IFT_ETHER) {
|
||||
char *s = LLADDR(sdl);
|
||||
for (i = 0; i < sdl->sdl_alen; i++) {
|
||||
debug(1,"char %d: \"%c\".", i, *s);
|
||||
*t++ = (uint8_t)*s++;
|
||||
}
|
||||
debug(1, "char %d: \"%c\".", i, *s);
|
||||
*t++ = (uint8_t)*s++;
|
||||
}
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
freeifaddrs(ifaddr);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
@@ -381,6 +381,7 @@ double vol2attn(double vol, long max_db, long min_db);
|
||||
// return a time in nanoseconds
|
||||
// uint64_t get_absolute_time_in_fp(void); // obselete
|
||||
uint64_t get_absolute_time_in_ns(void);
|
||||
uint64_t get_monotonic_time_in_ns(void); // to try and get precise FPS values
|
||||
|
||||
// time at startup for debugging timing
|
||||
extern uint64_t ns_time_at_startup, ns_time_at_last_debug_message;
|
||||
|
||||
@@ -1714,12 +1714,11 @@ void player_thread_cleanup_handler(void *arg) {
|
||||
|
||||
void *player_thread_func(void *arg) {
|
||||
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
|
||||
|
||||
|
||||
uint64_t previous_frames_played;
|
||||
uint64_t previous_frames_played_time;
|
||||
int previous_frames_played_valid = 0;
|
||||
|
||||
|
||||
// pthread_cleanup_push(player_thread_initial_cleanup_handler, arg);
|
||||
conn->packet_count = 0;
|
||||
conn->packet_count_since_flush = 0;
|
||||
@@ -2787,6 +2786,7 @@ void *player_thread_func(void *arg) {
|
||||
uint64_t frames_sent_for_play;
|
||||
int status = -1;
|
||||
if ((config.output->delay) && (config.no_sync == 0) && (config.output->rate_info)) {
|
||||
uint64_t monotonic_time_now = get_monotonic_time_in_ns();
|
||||
uint64_t elapsed_play_time; // dummy
|
||||
status = config.output->rate_info(&elapsed_play_time, &frames_sent_for_play);
|
||||
uint64_t frames_played = frames_sent_for_play - play_samples - current_delay;
|
||||
@@ -2794,22 +2794,22 @@ void *player_thread_func(void *arg) {
|
||||
// last time the rate_info call was made. Thus, the frame rate should be valid.
|
||||
if ((status == 0) && (previous_frames_played_valid)) {
|
||||
uint64_t frames_played_in_this_interval = frames_played - previous_frames_played;
|
||||
uint64_t interval = local_time_now - previous_frames_played_time;
|
||||
uint64_t interval = monotonic_time_now - previous_frames_played_time;
|
||||
conn->frame_rate = (1e9 * frames_played_in_this_interval) / interval;
|
||||
conn->frame_rate_valid = 1;
|
||||
}
|
||||
|
||||
|
||||
// uncomment the if statement if your want to get as long a period for
|
||||
// calculating the frame rate
|
||||
// if ((status != 0) || (previous_frames_played_valid == 0)) {
|
||||
// if we have just detected an outputting error, or if we have no
|
||||
// starting information
|
||||
previous_frames_played = frames_played;
|
||||
previous_frames_played_time = local_time_now;
|
||||
previous_frames_played_valid = 1;
|
||||
// }
|
||||
// if we have just detected an outputting error, or if we have no
|
||||
// starting information
|
||||
previous_frames_played = frames_played;
|
||||
previous_frames_played_time = monotonic_time_now;
|
||||
previous_frames_played_valid = 1;
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
// we can now calculate running averages for sync error (frames), corrections (ppm),
|
||||
// insertions plus deletions (ppm), drift (ppm)
|
||||
double moving_average_sync_error = (1.0 * tsum_of_sync_errors) / number_of_statistics;
|
||||
@@ -2931,7 +2931,7 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c
|
||||
else
|
||||
sw_min_db = (sw_max_db - desired_sw_range);
|
||||
} else {
|
||||
hw_min_db = hw_max_db - desired_range_db;
|
||||
hw_min_db = hw_max_db - desired_range_db;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -109,7 +109,6 @@ typedef enum {
|
||||
remote_control_stream
|
||||
} airplay_stream_c; // "c" for category
|
||||
|
||||
|
||||
#ifdef CONFIG_AIRPLAY_2
|
||||
typedef enum { ts_ntp, ts_ptp } timing_t;
|
||||
typedef enum { ap_1, ap_2 } airplay_t;
|
||||
@@ -296,7 +295,8 @@ typedef struct {
|
||||
clock_status_t clock_status;
|
||||
|
||||
airplay_stream_c
|
||||
airplay_stream_category; // is it a remote control stream or a normal "full service" stream? (will be unspecified if not build for AirPlay 2)
|
||||
airplay_stream_category; // is it a remote control stream or a normal "full service" stream?
|
||||
// (will be unspecified if not build for AirPlay 2)
|
||||
|
||||
#ifdef CONFIG_AIRPLAY_2
|
||||
char *airplay_gid; // UUID in the Bonjour advertisement -- if NULL, the group UUID is the same as
|
||||
|
||||
Reference in New Issue
Block a user