mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
spa: add spa_ratelimit
This commit is contained in:
parent
17bc9d520e
commit
dc07c2321b
8 changed files with 76 additions and 69 deletions
42
spa/include/spa/utils/ratelimit.h
Normal file
42
spa/include/spa/utils/ratelimit.h
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
/* Ratelimit */
|
||||||
|
/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */
|
||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
#ifndef SPA_RATELIMIT_H
|
||||||
|
#define SPA_RATELIMIT_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
struct spa_ratelimit {
|
||||||
|
uint64_t interval;
|
||||||
|
uint64_t begin;
|
||||||
|
unsigned burst;
|
||||||
|
unsigned n_printed;
|
||||||
|
unsigned n_missed;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int spa_ratelimit_test(struct spa_ratelimit *r, uint64_t now)
|
||||||
|
{
|
||||||
|
unsigned missed = 0;
|
||||||
|
if (r->begin + r->interval < now) {
|
||||||
|
missed = r->n_missed;
|
||||||
|
r->begin = now;
|
||||||
|
r->n_printed = 0;
|
||||||
|
r->n_missed = 0;
|
||||||
|
} else if (r->n_printed >= r->burst) {
|
||||||
|
r->n_missed++;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
r->n_printed++;
|
||||||
|
return missed;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SPA_RATELIMIT_H */
|
||||||
|
|
@ -1980,7 +1980,7 @@ static int get_avail(struct state *state, uint64_t current_time, snd_pcm_uframes
|
||||||
if ((res = alsa_recover(state, avail)) < 0)
|
if ((res = alsa_recover(state, avail)) < 0)
|
||||||
return res;
|
return res;
|
||||||
if ((avail = snd_pcm_avail(state->hndl)) < 0) {
|
if ((avail = snd_pcm_avail(state->hndl)) < 0) {
|
||||||
if ((missed = ratelimit_test(&state->rate_limit, current_time)) >= 0) {
|
if ((missed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
|
||||||
spa_log_warn(state->log, "%s: (%d missed) snd_pcm_avail after recover: %s",
|
spa_log_warn(state->log, "%s: (%d missed) snd_pcm_avail after recover: %s",
|
||||||
state->props.device, missed, snd_strerror(avail));
|
state->props.device, missed, snd_strerror(avail));
|
||||||
}
|
}
|
||||||
|
|
@ -1997,7 +1997,7 @@ static int get_avail(struct state *state, uint64_t current_time, snd_pcm_uframes
|
||||||
uint64_t then;
|
uint64_t then;
|
||||||
|
|
||||||
if ((res = snd_pcm_htimestamp(state->hndl, &havail, &tstamp)) < 0) {
|
if ((res = snd_pcm_htimestamp(state->hndl, &havail, &tstamp)) < 0) {
|
||||||
if ((missed = ratelimit_test(&state->rate_limit, current_time)) >= 0) {
|
if ((missed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
|
||||||
spa_log_warn(state->log, "%s: (%d missed) snd_pcm_htimestamp error: %s",
|
spa_log_warn(state->log, "%s: (%d missed) snd_pcm_htimestamp error: %s",
|
||||||
state->props.device, missed, snd_strerror(res));
|
state->props.device, missed, snd_strerror(res));
|
||||||
}
|
}
|
||||||
|
|
@ -2025,7 +2025,7 @@ static int get_avail(struct state *state, uint64_t current_time, snd_pcm_uframes
|
||||||
state->htimestamp_error = 0;
|
state->htimestamp_error = 0;
|
||||||
state->htimestamp = false;
|
state->htimestamp = false;
|
||||||
}
|
}
|
||||||
else if ((missed = ratelimit_test(&state->rate_limit, current_time)) >= 0) {
|
else if ((missed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
|
||||||
spa_log_warn(state->log, "%s: (%d missed) impossible htimestamp diff:%"PRIi64,
|
spa_log_warn(state->log, "%s: (%d missed) impossible htimestamp diff:%"PRIi64,
|
||||||
state->props.device, missed, diff);
|
state->props.device, missed, diff);
|
||||||
}
|
}
|
||||||
|
|
@ -2253,7 +2253,7 @@ int spa_alsa_write(struct state *state)
|
||||||
else
|
else
|
||||||
lev = SPA_LOG_LEVEL_INFO;
|
lev = SPA_LOG_LEVEL_INFO;
|
||||||
|
|
||||||
if ((missed = ratelimit_test(&state->rate_limit, current_time)) >= 0) {
|
if ((missed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
|
||||||
spa_log_lev(state->log, lev, "%s: follower avail:%lu delay:%ld "
|
spa_log_lev(state->log, lev, "%s: follower avail:%lu delay:%ld "
|
||||||
"target:%ld thr:%u, resync (%d missed)",
|
"target:%ld thr:%u, resync (%d missed)",
|
||||||
state->props.device, avail, delay,
|
state->props.device, avail, delay,
|
||||||
|
|
@ -2490,7 +2490,7 @@ int spa_alsa_read(struct state *state)
|
||||||
else
|
else
|
||||||
lev = SPA_LOG_LEVEL_INFO;
|
lev = SPA_LOG_LEVEL_INFO;
|
||||||
|
|
||||||
if ((missed = ratelimit_test(&state->rate_limit, current_time)) >= 0) {
|
if ((missed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
|
||||||
spa_log_lev(state->log, lev, "%s: follower delay:%ld target:%ld thr:%u, "
|
spa_log_lev(state->log, lev, "%s: follower delay:%ld target:%ld thr:%u, "
|
||||||
"resync (%d missed)", state->props.device, delay,
|
"resync (%d missed)", state->props.device, delay,
|
||||||
target, state->threshold, missed);
|
target, state->threshold, missed);
|
||||||
|
|
@ -2743,7 +2743,7 @@ done:
|
||||||
if (!state->disable_tsched &&
|
if (!state->disable_tsched &&
|
||||||
(state->next_time > current_time + SPA_NSEC_PER_SEC ||
|
(state->next_time > current_time + SPA_NSEC_PER_SEC ||
|
||||||
current_time > state->next_time + SPA_NSEC_PER_SEC)) {
|
current_time > state->next_time + SPA_NSEC_PER_SEC)) {
|
||||||
if ((missed = ratelimit_test(&state->rate_limit, current_time)) >= 0) {
|
if ((missed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) {
|
||||||
spa_log_error(state->log, "%s: impossible timeout %lu %lu %lu %"
|
spa_log_error(state->log, "%s: impossible timeout %lu %lu %lu %"
|
||||||
PRIu64" %"PRIu64" %"PRIi64" %d %"PRIi64" (%d missed)",
|
PRIu64" %"PRIu64" %"PRIi64" %d %"PRIi64" (%d missed)",
|
||||||
state->props.device, avail, delay, target,
|
state->props.device, avail, delay, target,
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ extern "C" {
|
||||||
#include <spa/utils/list.h>
|
#include <spa/utils/list.h>
|
||||||
#include <spa/utils/json.h>
|
#include <spa/utils/json.h>
|
||||||
#include <spa/utils/dll.h>
|
#include <spa/utils/dll.h>
|
||||||
|
#include <spa/utils/ratelimit.h>
|
||||||
|
|
||||||
#include <spa/node/node.h>
|
#include <spa/node/node.h>
|
||||||
#include <spa/node/utils.h>
|
#include <spa/node/utils.h>
|
||||||
|
|
@ -81,13 +82,6 @@ struct card {
|
||||||
uint32_t rate;
|
uint32_t rate;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ratelimit {
|
|
||||||
uint64_t interval;
|
|
||||||
uint64_t begin;
|
|
||||||
unsigned burst;
|
|
||||||
unsigned n_printed, n_missed;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct state {
|
struct state {
|
||||||
struct spa_handle handle;
|
struct spa_handle handle;
|
||||||
struct spa_node node;
|
struct spa_node node;
|
||||||
|
|
@ -97,7 +91,7 @@ struct state {
|
||||||
struct spa_loop *data_loop;
|
struct spa_loop *data_loop;
|
||||||
|
|
||||||
FILE *log_file;
|
FILE *log_file;
|
||||||
struct ratelimit rate_limit;
|
struct spa_ratelimit rate_limit;
|
||||||
|
|
||||||
uint32_t card_index;
|
uint32_t card_index;
|
||||||
struct card *card;
|
struct card *card;
|
||||||
|
|
@ -348,22 +342,6 @@ static inline uint32_t spa_alsa_get_iec958_codecs(struct state *state, uint32_t
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int ratelimit_test(struct ratelimit *r, uint64_t now)
|
|
||||||
{
|
|
||||||
unsigned missed = 0;
|
|
||||||
if (r->begin + r->interval < now) {
|
|
||||||
missed = r->n_missed;
|
|
||||||
r->begin = now;
|
|
||||||
r->n_printed = 0;
|
|
||||||
r->n_missed = 0;
|
|
||||||
} else if (r->n_printed >= r->burst) {
|
|
||||||
r->n_missed++;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
r->n_printed++;
|
|
||||||
return missed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This function is also as snd_pcm_channel_area_addr() since 1.2.6 which is not yet
|
/* This function is also as snd_pcm_channel_area_addr() since 1.2.6 which is not yet
|
||||||
* in ubuntu and I can't figure out how to do the ALSA version check. */
|
* in ubuntu and I can't figure out how to do the ALSA version check. */
|
||||||
static inline void *channel_area_addr(const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset)
|
static inline void *channel_area_addr(const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset)
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ struct impl {
|
||||||
struct pw_properties *props;
|
struct pw_properties *props;
|
||||||
void *dbus_name;
|
void *dbus_name;
|
||||||
|
|
||||||
struct ratelimit rate_limit;
|
struct spa_ratelimit rate_limit;
|
||||||
|
|
||||||
struct spa_hook_list hooks;
|
struct spa_hook_list hooks;
|
||||||
struct spa_list servers;
|
struct spa_list servers;
|
||||||
|
|
|
||||||
|
|
@ -216,10 +216,11 @@ int stream_send_underflow(struct stream *stream, int64_t offset)
|
||||||
struct client *client = stream->client;
|
struct client *client = stream->client;
|
||||||
struct impl *impl = client->impl;
|
struct impl *impl = client->impl;
|
||||||
struct message *reply;
|
struct message *reply;
|
||||||
|
int missed;
|
||||||
|
|
||||||
if (ratelimit_test(&impl->rate_limit, stream->timestamp, SPA_LOG_LEVEL_INFO)) {
|
if ((missed = spa_ratelimit_test(&impl->rate_limit, stream->timestamp)) >= 0) {
|
||||||
pw_log_info("[%s]: UNDERFLOW channel:%u offset:%" PRIi64,
|
pw_log_info("[%s]: UNDERFLOW channel:%u offset:%" PRIi64" (%d missed)",
|
||||||
client->name, stream->channel, offset);
|
client->name, stream->channel, offset, missed);
|
||||||
}
|
}
|
||||||
|
|
||||||
reply = message_alloc(impl, -1, 0);
|
reply = message_alloc(impl, -1, 0);
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
#include <spa/utils/json.h>
|
#include <spa/utils/json.h>
|
||||||
#include <spa/utils/ringbuffer.h>
|
#include <spa/utils/ringbuffer.h>
|
||||||
#include <spa/utils/dll.h>
|
#include <spa/utils/dll.h>
|
||||||
|
#include <spa/utils/ratelimit.h>
|
||||||
#include <spa/debug/types.h>
|
#include <spa/debug/types.h>
|
||||||
#include <spa/pod/builder.h>
|
#include <spa/pod/builder.h>
|
||||||
#include <spa/param/audio/format-utils.h>
|
#include <spa/param/audio/format-utils.h>
|
||||||
|
|
@ -170,7 +171,7 @@ struct impl {
|
||||||
pa_stream *pa_stream;
|
pa_stream *pa_stream;
|
||||||
uint32_t pa_index;
|
uint32_t pa_index;
|
||||||
|
|
||||||
struct ratelimit rate_limit;
|
struct spa_ratelimit rate_limit;
|
||||||
|
|
||||||
uint32_t target_latency;
|
uint32_t target_latency;
|
||||||
uint32_t current_latency;
|
uint32_t current_latency;
|
||||||
|
|
@ -669,19 +670,22 @@ static void stream_underflow_cb(pa_stream *s, void *userdata)
|
||||||
{
|
{
|
||||||
struct impl *impl = userdata;
|
struct impl *impl = userdata;
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
|
int missed;
|
||||||
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
if (ratelimit_test(&impl->rate_limit, SPA_TIMESPEC_TO_NSEC(&ts), SPA_LOG_LEVEL_WARN))
|
if ((missed = spa_ratelimit_test(&impl->rate_limit, SPA_TIMESPEC_TO_NSEC(&ts))) >= 0)
|
||||||
pw_log_warn("underflow");
|
pw_log_warn("underflow (%d missed)", missed);
|
||||||
impl->resync = true;
|
impl->resync = true;
|
||||||
}
|
}
|
||||||
static void stream_overflow_cb(pa_stream *s, void *userdata)
|
static void stream_overflow_cb(pa_stream *s, void *userdata)
|
||||||
{
|
{
|
||||||
struct impl *impl = userdata;
|
struct impl *impl = userdata;
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
|
int missed;
|
||||||
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
if (ratelimit_test(&impl->rate_limit, SPA_TIMESPEC_TO_NSEC(&ts), SPA_LOG_LEVEL_WARN))
|
if ((missed = spa_ratelimit_test(&impl->rate_limit, SPA_TIMESPEC_TO_NSEC(&ts))) >= 0)
|
||||||
pw_log_warn("overflow");
|
pw_log_warn("overflow (%d missed)", missed);
|
||||||
impl->resync = true;
|
impl->resync = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1095,8 +1095,9 @@ static void check_states(struct pw_impl_node *driver, uint64_t nsec)
|
||||||
struct pw_node_activation *na = driver->rt.target.activation;
|
struct pw_node_activation *na = driver->rt.target.activation;
|
||||||
struct spa_io_clock *cl = &na->position.clock;
|
struct spa_io_clock *cl = &na->position.clock;
|
||||||
enum spa_log_level level = SPA_LOG_LEVEL_DEBUG;
|
enum spa_log_level level = SPA_LOG_LEVEL_DEBUG;
|
||||||
|
int missed;
|
||||||
|
|
||||||
if (ratelimit_test(&driver->rt.rate_limit, nsec, SPA_LOG_LEVEL_DEBUG))
|
if ((missed = spa_ratelimit_test(&driver->rt.rate_limit, nsec)) >= 0)
|
||||||
level = SPA_LOG_LEVEL_INFO;
|
level = SPA_LOG_LEVEL_INFO;
|
||||||
|
|
||||||
spa_list_for_each(t, &driver->rt.target_list, link) {
|
spa_list_for_each(t, &driver->rt.target_list, link) {
|
||||||
|
|
@ -1110,10 +1111,11 @@ static void check_states(struct pw_impl_node *driver, uint64_t nsec)
|
||||||
a->status == PW_NODE_ACTIVATION_AWAKE) {
|
a->status == PW_NODE_ACTIVATION_AWAKE) {
|
||||||
update_xrun_stats(a, nsec / 1000, 0);
|
update_xrun_stats(a, nsec / 1000, 0);
|
||||||
|
|
||||||
pw_log(level, "(%s-%u) client too slow! rate:%u/%u pos:%"PRIu64" status:%s",
|
pw_log(level, "(%s-%u) client too slow! rate:%u/%u pos:%"PRIu64" status:%s (%u missed)",
|
||||||
t->name, t->id,
|
t->name, t->id,
|
||||||
(uint32_t)(cl->rate.num * cl->duration), cl->rate.denom,
|
(uint32_t)(cl->rate.num * cl->duration), cl->rate.denom,
|
||||||
cl->position, str_status(a->status));
|
cl->position, str_status(a->status),
|
||||||
|
missed);
|
||||||
}
|
}
|
||||||
pw_log_debug("(%s-%u) state:%p pending:%d/%d s:%"PRIu64" a:%"PRIu64" f:%"PRIu64
|
pw_log_debug("(%s-%u) state:%p pending:%d/%d s:%"PRIu64" a:%"PRIu64" f:%"PRIu64
|
||||||
" waiting:%"PRIu64" process:%"PRIu64" status:%s sync:%d",
|
" waiting:%"PRIu64" process:%"PRIu64" status:%s sync:%d",
|
||||||
|
|
@ -1881,10 +1883,11 @@ static int node_xrun(void *data, uint64_t trigger, uint64_t delay, struct spa_po
|
||||||
struct pw_node_activation *da = this->rt.driver_target.activation;
|
struct pw_node_activation *da = this->rt.driver_target.activation;
|
||||||
struct spa_system *data_system = this->data_system;
|
struct spa_system *data_system = this->data_system;
|
||||||
uint64_t nsec = get_time_ns(data_system);
|
uint64_t nsec = get_time_ns(data_system);
|
||||||
|
int missed;
|
||||||
|
|
||||||
update_xrun_stats(a, trigger, delay);
|
update_xrun_stats(a, trigger, delay);
|
||||||
|
|
||||||
if (ratelimit_test(&this->rt.rate_limit, nsec, SPA_LOG_LEVEL_INFO)) {
|
if ((missed = spa_ratelimit_test(&this->rt.rate_limit, nsec)) >= 0) {
|
||||||
struct spa_fraction rate;
|
struct spa_fraction rate;
|
||||||
if (da) {
|
if (da) {
|
||||||
struct spa_io_clock *cl = &da->position.clock;
|
struct spa_io_clock *cl = &da->position.clock;
|
||||||
|
|
@ -1894,10 +1897,11 @@ static int node_xrun(void *data, uint64_t trigger, uint64_t delay, struct spa_po
|
||||||
rate = SPA_FRACTION(0,0);
|
rate = SPA_FRACTION(0,0);
|
||||||
}
|
}
|
||||||
pw_log_info("(%s-%d) XRun! rate:%u/%u count:%u time:%"PRIu64
|
pw_log_info("(%s-%d) XRun! rate:%u/%u count:%u time:%"PRIu64
|
||||||
" delay:%"PRIu64" max:%"PRIu64,
|
" delay:%"PRIu64" max:%"PRIu64" (%d missed)",
|
||||||
this->name, this->info.id,
|
this->name, this->info.id,
|
||||||
rate.num, rate.denom, a->xrun_count,
|
rate.num, rate.denom, a->xrun_count,
|
||||||
trigger, delay, a->max_delay);
|
trigger, delay, a->max_delay,
|
||||||
|
missed);
|
||||||
}
|
}
|
||||||
|
|
||||||
pw_context_driver_emit_xrun(this->context, this);
|
pw_context_driver_emit_xrun(this->context, this);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ extern "C" {
|
||||||
#include <spa/support/plugin.h>
|
#include <spa/support/plugin.h>
|
||||||
#include <spa/pod/builder.h>
|
#include <spa/pod/builder.h>
|
||||||
#include <spa/param/latency-utils.h>
|
#include <spa/param/latency-utils.h>
|
||||||
|
#include <spa/utils/ratelimit.h>
|
||||||
#include <spa/utils/result.h>
|
#include <spa/utils/result.h>
|
||||||
#include <spa/utils/type-info.h>
|
#include <spa/utils/type-info.h>
|
||||||
|
|
||||||
|
|
@ -53,29 +54,6 @@ struct settings {
|
||||||
uint32_t clock_force_quantum; /* force a quantum */
|
uint32_t clock_force_quantum; /* force a quantum */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ratelimit {
|
|
||||||
uint64_t interval;
|
|
||||||
uint64_t begin;
|
|
||||||
unsigned burst;
|
|
||||||
unsigned n_printed, n_missed;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline bool ratelimit_test(struct ratelimit *r, uint64_t now, enum spa_log_level level)
|
|
||||||
{
|
|
||||||
if (r->begin + r->interval < now) {
|
|
||||||
if (r->n_missed)
|
|
||||||
pw_log(level, "%u events suppressed", r->n_missed);
|
|
||||||
r->begin = now;
|
|
||||||
r->n_printed = 0;
|
|
||||||
r->n_missed = 0;
|
|
||||||
} else if (r->n_printed >= r->burst) {
|
|
||||||
r->n_missed++;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
r->n_printed++;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MAX_PARAMS 32
|
#define MAX_PARAMS 32
|
||||||
|
|
||||||
struct pw_param {
|
struct pw_param {
|
||||||
|
|
@ -795,7 +773,7 @@ struct pw_impl_node {
|
||||||
driver */
|
driver */
|
||||||
struct spa_list driver_link; /* our link in driver */
|
struct spa_list driver_link; /* our link in driver */
|
||||||
|
|
||||||
struct ratelimit rate_limit;
|
struct spa_ratelimit rate_limit;
|
||||||
} rt;
|
} rt;
|
||||||
struct spa_fraction target_rate;
|
struct spa_fraction target_rate;
|
||||||
uint64_t target_quantum;
|
uint64_t target_quantum;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue