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.
This commit is contained in:
Wim Taymans 2019-08-22 13:23:48 +02:00
parent 570575f052
commit 6ad4adc194
8 changed files with 80 additions and 15 deletions

@ -1 +1 @@
Subproject commit 33cac9932c0febb6c0219b30795b4c1bb01a4160
Subproject commit 84071d2cacc0773a21524211a20e6068989d3883

View file

@ -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);
};

View file

@ -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" */

View file

@ -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;

View file

@ -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,

View file

@ -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,

View file

@ -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

View file

@ -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__)