From 6ad4adc194b7b5aa42c5b04b935f64f5584bca83 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 22 Aug 2019 13:23:48 +0200 Subject: [PATCH] node: add xrun callback Let alsa emit xrun callbacks. Write the xrun stats to the activation area of the node so all clients can read it. --- pipewire-jack | 2 +- spa/include/spa/node/node.h | 16 ++++++++++++- spa/include/spa/node/utils.h | 13 +++++----- spa/plugins/alsa/alsa-utils.c | 11 +++++---- spa/plugins/audioconvert/audioadapter.c | 7 ++++++ src/modules/module-client-node/remote-node.c | 25 +++++++++++++++++--- src/pipewire/node.c | 17 +++++++++++++ src/pipewire/private.h | 4 ++++ 8 files changed, 80 insertions(+), 15 deletions(-) diff --git a/pipewire-jack b/pipewire-jack index 33cac9932..84071d2ca 160000 --- a/pipewire-jack +++ b/pipewire-jack @@ -1 +1 @@ -Subproject commit 33cac9932c0febb6c0219b30795b4c1bb01a4160 +Subproject commit 84071d2cacc0773a21524211a20e6068989d3883 diff --git a/spa/include/spa/node/node.h b/spa/include/spa/node/node.h index 4d8cd74ec..70a0b8b2b 100644 --- a/spa/include/spa/node/node.h +++ b/spa/include/spa/node/node.h @@ -186,7 +186,8 @@ struct spa_node_events { #define SPA_NODE_CALLBACK_READY 0 #define SPA_NODE_CALLBACK_REUSE_BUFFER 1 -#define SPA_NODE_CALLBACK_NUM 2 +#define SPA_NODE_CALLBACK_XRUN 2 +#define SPA_NODE_CALLBACK_NUM 3 /** Node callbacks * @@ -219,6 +220,19 @@ struct spa_node_callbacks { int (*reuse_buffer) (void *data, uint32_t port_id, uint32_t buffer_id); + + /** + * \param data user data + * \param trigger the timestamp in microseconds when the xrun happened + * \param delay the amount of microseconds of xrun. + * \param info an object with extra info (NULL for now) + * + * The node has encountered an over or underrun + * + * The info contains an object with more information + */ + int (*xrun) (void *data, uint64_t trigger, uint64_t delay, + struct spa_pod *info); }; diff --git a/spa/include/spa/node/utils.h b/spa/include/spa/node/utils.h index 3ccdbf851..9506cc3ed 100644 --- a/spa/include/spa/node/utils.h +++ b/spa/include/spa/node/utils.h @@ -117,10 +117,10 @@ static inline int spa_node_port_enum_params_sync(struct spa_node *node, spa_hook_list_call_simple(hooks, struct spa_node_events, \ method, version, ##__VA_ARGS__) -#define spa_node_emit_info(hooks,i) spa_node_emit(hooks,info, 0, i) -#define spa_node_emit_port_info(hooks,d,p,i) spa_node_emit(hooks,port_info, 0, d, p, i) -#define spa_node_emit_result(hooks,s,r,t,res) spa_node_emit(hooks,result, 0, s, r, t, res) -#define spa_node_emit_event(hooks,e) spa_node_emit(hooks,event, 0, e) +#define spa_node_emit_info(hooks,...) spa_node_emit(hooks,info, 0, __VA_ARGS__) +#define spa_node_emit_port_info(hooks,...) spa_node_emit(hooks,port_info, 0, __VA_ARGS__) +#define spa_node_emit_result(hooks,...) spa_node_emit(hooks,result, 0, __VA_ARGS__) +#define spa_node_emit_event(hooks,...) spa_node_emit(hooks,event, 0, __VA_ARGS__) #define spa_node_call(callbacks,method,version,...) \ @@ -131,8 +131,9 @@ static inline int spa_node_port_enum_params_sync(struct spa_node *node, _res; \ }) -#define spa_node_call_ready(hook,s) spa_node_call(hook, ready, 0, s) -#define spa_node_call_reuse_buffer(hook,p,b) spa_node_call(hook, reuse_buffer, 0, p, b) +#define spa_node_call_ready(hook,...) spa_node_call(hook, ready, 0, __VA_ARGS__) +#define spa_node_call_reuse_buffer(hook,...) spa_node_call(hook, reuse_buffer, 0, __VA_ARGS__) +#define spa_node_call_xrun(hook,...) spa_node_call(hook, xrun, 0, __VA_ARGS__) #ifdef __cplusplus } /* extern "C" */ diff --git a/spa/plugins/alsa/alsa-utils.c b/spa/plugins/alsa/alsa-utils.c index b2b24c001..a5bb7557c 100644 --- a/spa/plugins/alsa/alsa-utils.c +++ b/spa/plugins/alsa/alsa-utils.c @@ -542,17 +542,20 @@ static int alsa_recover(struct state *state, int err) case SND_PCM_STATE_XRUN: { struct timeval now, trigger, diff; - uint64_t xrun, missing; + uint64_t delay, missing; snd_pcm_status_get_tstamp (status, &now); snd_pcm_status_get_trigger_tstamp (status, &trigger); timersub(&now, &trigger, &diff); - xrun = SPA_TIMEVAL_TO_USEC(&diff); - missing = xrun * state->rate / SPA_USEC_PER_SEC; + delay = SPA_TIMEVAL_TO_USEC(&diff); + missing = delay * state->rate / SPA_USEC_PER_SEC; spa_log_error(state->log, "%p: xrun of %"PRIu64" usec %"PRIu64" %f", - state, xrun, missing, state->safety); + state, delay, missing, state->safety); + + spa_node_call_xrun(&state->callbacks, + SPA_TIMEVAL_TO_USEC(&trigger), delay, NULL); state->sample_count += missing ? missing : state->threshold; break; diff --git a/spa/plugins/audioconvert/audioadapter.c b/spa/plugins/audioconvert/audioadapter.c index 06a7c0ef3..4202997dd 100644 --- a/spa/plugins/audioconvert/audioadapter.c +++ b/spa/plugins/audioconvert/audioadapter.c @@ -430,10 +430,17 @@ static int slave_reuse_buffer(void *data, uint32_t port_id, uint32_t buffer_id) return res; } +static int slave_xrun(void *data, uint64_t trigger, uint64_t delay, struct spa_pod *info) +{ + struct impl *this = data; + return spa_node_call_xrun(&this->callbacks, trigger, delay, info); +} + static const struct spa_node_callbacks slave_node_callbacks = { SPA_VERSION_NODE_CALLBACKS, .ready = slave_ready, .reuse_buffer = slave_reuse_buffer, + .xrun = slave_xrun, }; static int impl_node_add_listener(void *object, diff --git a/src/modules/module-client-node/remote-node.c b/src/modules/module-client-node/remote-node.c index 9f54074fc..45e388359 100644 --- a/src/modules/module-client-node/remote-node.c +++ b/src/modules/module-client-node/remote-node.c @@ -1036,6 +1036,7 @@ static int node_ready(void *d, int status) { struct node_data *data = d; struct pw_node *node = data->node; + struct pw_node_activation *a = node->rt.activation; struct timespec ts; struct pw_port *p; uint64_t cmd = 1; @@ -1049,8 +1050,8 @@ static int node_ready(void *d, int status) } clock_gettime(CLOCK_MONOTONIC, &ts); - node->rt.activation->status = TRIGGERED; - node->rt.activation->signal_time = SPA_TIMESPEC_TO_NSEC(&ts); + a->status = TRIGGERED; + a->signal_time = SPA_TIMESPEC_TO_NSEC(&ts); if (write(data->rtwritefd, &cmd, sizeof(cmd)) != sizeof(cmd)) pw_log_warn("node %p: write failed %m", node); @@ -1063,10 +1064,28 @@ static int node_reuse_buffer(void *data, uint32_t port_id, uint32_t buffer_id) return 0; } +static int node_xrun(void *d, uint64_t trigger, uint64_t delay, struct spa_pod *info) +{ + struct node_data *data = d; + struct pw_node *node = data->node; + struct pw_node_activation *a = node->rt.activation; + + a->xrun_count++; + a->xrun_time = trigger; + a->xrun_delay = delay; + a->max_delay = SPA_MAX(a->max_delay, delay); + + pw_log_debug("node %p: XRun! count:%u time:%"PRIu64" delay:%"PRIu64" max:%"PRIu64, + node, a->xrun_count, trigger, delay, a->max_delay); + + return 0; +} + static const struct spa_node_callbacks node_callbacks = { SPA_VERSION_NODE_CALLBACKS, .ready = node_ready, - .reuse_buffer = node_reuse_buffer + .reuse_buffer = node_reuse_buffer, + .xrun = node_xrun }; static struct pw_proxy *node_export(struct pw_remote *remote, void *object, bool do_free, diff --git a/src/pipewire/node.c b/src/pipewire/node.c index 13765b81e..86a7a1288 100644 --- a/src/pipewire/node.c +++ b/src/pipewire/node.c @@ -1202,10 +1202,27 @@ static int node_reuse_buffer(void *data, uint32_t port_id, uint32_t buffer_id) return 0; } +static int node_xrun(void *data, uint64_t trigger, uint64_t delay, struct spa_pod *info) +{ + struct pw_node *this = data; + struct pw_node_activation *a = this->rt.activation; + + a->xrun_count++; + a->xrun_time = trigger; + a->xrun_delay = delay; + a->max_delay = SPA_MAX(a->max_delay, delay); + + pw_log_debug(NAME" %p: XRun! count:%u time:%"PRIu64" delay:%"PRIu64" max:%"PRIu64, + this, a->xrun_count, trigger, delay, a->max_delay); + + return 0; +} + static const struct spa_node_callbacks node_callbacks = { SPA_VERSION_NODE_CALLBACKS, .ready = node_ready, .reuse_buffer = node_reuse_buffer, + .xrun = node_xrun, }; SPA_EXPORT diff --git a/src/pipewire/private.h b/src/pipewire/private.h index 4370a9829..761f8b5f3 100644 --- a/src/pipewire/private.h +++ b/src/pipewire/private.h @@ -364,6 +364,10 @@ struct pw_node_activation { struct pw_node_activation_state state[2]; /* one current state and one next state, * as low bit of version in position */ float cpu_load[3]; /* averaged over short, medium, long time */ + uint32_t xrun_count; /* number of xruns */ + uint64_t xrun_time; /* time of last xrun in microseconds */ + uint64_t xrun_delay; /* delay of last xrun in microseconds */ + uint64_t max_delay; /* max of all xruns in microseconds */ }; #define pw_node_emit(o,m,v,...) spa_hook_list_call(&o->listener_list, struct pw_node_events, m, v, ##__VA_ARGS__)