mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-31 22:25:38 -04:00
bluez5: use kernel-provided RX timestamps
Use kernel-provided packet reception timestamps to get less jitter in packet timings. Mostly matters for ISO/SCO which have regular schedule. A2DP (L2CAP) doesn't currently do RX timestamps in kernel, but we can as well use the same mechanism for it.
This commit is contained in:
parent
081116906d
commit
0d61cc1b1d
8 changed files with 165 additions and 31 deletions
|
|
@ -31,6 +31,11 @@
|
|||
#define SPA_BLUEZ5_DECODE_BUFFER_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/net_tstamp.h>
|
||||
#include <linux/errqueue.h>
|
||||
|
||||
#include <spa/utils/defs.h>
|
||||
#include <spa/support/log.h>
|
||||
|
||||
|
|
@ -308,4 +313,121 @@ static inline void spa_bt_decode_buffer_process(struct spa_bt_decode_buffer *thi
|
|||
}
|
||||
}
|
||||
|
||||
struct spa_bt_recvmsg_data {
|
||||
struct spa_log *log;
|
||||
struct spa_system *data_system;
|
||||
int fd;
|
||||
int64_t offset;
|
||||
int64_t err;
|
||||
};
|
||||
|
||||
static inline void spa_bt_recvmsg_update_clock(struct spa_bt_recvmsg_data *data, uint64_t *now)
|
||||
{
|
||||
const int64_t max_resync = (50 * SPA_NSEC_PER_USEC);
|
||||
const int64_t n_avg = 10;
|
||||
struct timespec ts1, ts2, ts3;
|
||||
int64_t t1, t2, t3, offset, err;
|
||||
|
||||
spa_system_clock_gettime(data->data_system, CLOCK_MONOTONIC, &ts1);
|
||||
spa_system_clock_gettime(data->data_system, CLOCK_REALTIME, &ts2);
|
||||
spa_system_clock_gettime(data->data_system, CLOCK_MONOTONIC, &ts3);
|
||||
|
||||
t1 = SPA_TIMESPEC_TO_NSEC(&ts1);
|
||||
t2 = SPA_TIMESPEC_TO_NSEC(&ts2);
|
||||
t3 = SPA_TIMESPEC_TO_NSEC(&ts3);
|
||||
|
||||
if (now)
|
||||
*now = t1;
|
||||
|
||||
offset = t1 + (t3 - t1) / 2 - t2;
|
||||
|
||||
/* Moving average smoothing, discarding outliers */
|
||||
err = offset - data->offset;
|
||||
|
||||
if (SPA_ABS(err) > max_resync) {
|
||||
/* Clock jump */
|
||||
spa_log_debug(data->log, "%p: nsec err %"PRIi64" > max_resync %"PRIi64", resetting",
|
||||
data, err, max_resync);
|
||||
data->offset = offset;
|
||||
data->err = 0;
|
||||
err = 0;
|
||||
} else if (SPA_ABS(err) / 2 <= data->err) {
|
||||
data->offset += err / n_avg;
|
||||
}
|
||||
|
||||
data->err += (SPA_ABS(err) - data->err) / n_avg;
|
||||
}
|
||||
|
||||
static inline ssize_t spa_bt_recvmsg(struct spa_bt_recvmsg_data *r, void *buf, size_t max_size, uint64_t *rx_time)
|
||||
{
|
||||
char control[1024];
|
||||
struct iovec data = {
|
||||
.iov_base = buf,
|
||||
.iov_len = max_size
|
||||
};
|
||||
struct msghdr msg = {
|
||||
.msg_iov = &data,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = &control,
|
||||
.msg_controllen = sizeof(control),
|
||||
};
|
||||
struct cmsghdr *cmsg;
|
||||
uint64_t t = 0, now;
|
||||
ssize_t res;
|
||||
|
||||
res = recvmsg(r->fd, &msg, MSG_DONTWAIT);
|
||||
if (res < 0 || !rx_time)
|
||||
return res;
|
||||
|
||||
spa_bt_recvmsg_update_clock(r, &now);
|
||||
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
struct scm_timestamping *tss;
|
||||
|
||||
if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_TIMESTAMPING)
|
||||
continue;
|
||||
|
||||
tss = (struct scm_timestamping *)CMSG_DATA(cmsg);
|
||||
t = SPA_TIMESPEC_TO_NSEC(&tss->ts[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!t) {
|
||||
*rx_time = now;
|
||||
return res;
|
||||
}
|
||||
|
||||
*rx_time = t + r->offset;
|
||||
|
||||
/* CLOCK_REALTIME may jump, so sanity check */
|
||||
if (*rx_time > now || *rx_time + 20 * SPA_NSEC_PER_MSEC < now)
|
||||
*rx_time = now;
|
||||
|
||||
spa_log_trace(r->log, "%p: rx:%" PRIu64 " now:%" PRIu64 " d:%"PRIu64" off:%"PRIi64,
|
||||
r, *rx_time, now, now - *rx_time, r->offset);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static inline void spa_bt_recvmsg_init(struct spa_bt_recvmsg_data *data, int fd,
|
||||
struct spa_system *data_system, struct spa_log *log)
|
||||
{
|
||||
int flags = 0;
|
||||
socklen_t len = sizeof(flags);
|
||||
|
||||
data->log = log;
|
||||
data->data_system = data_system;
|
||||
data->fd = fd;
|
||||
data->offset = 0;
|
||||
data->err = 0;
|
||||
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &flags, &len) < 0)
|
||||
spa_log_info(log, "failed to get SO_TIMESTAMPING");
|
||||
|
||||
flags |= SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof(flags)) < 0)
|
||||
spa_log_info(log, "failed to set SO_TIMESTAMPING");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue