spa: Implement latency reporting

Implement latency reporting in alsa
Implement latency reporting on audioconf by passing on the latency
from the follower to outside of the adapter.
This commit is contained in:
Wim Taymans 2021-05-21 10:01:37 +02:00
parent 1a8f274a80
commit 1cd6d7b01d
8 changed files with 212 additions and 4 deletions

View file

@ -456,6 +456,21 @@ impl_node_port_enum_params(void *object, int seq,
}
break;
case SPA_PARAM_Latency:
switch (result.index) {
case 0:
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_ParamLatency, id,
SPA_PARAM_LATENCY_direction, SPA_POD_Id(direction),
SPA_PARAM_LATENCY_quantum, SPA_POD_Float(this->latency_quantum),
SPA_PARAM_LATENCY_min, SPA_POD_Int(this->latency),
SPA_PARAM_LATENCY_max, SPA_POD_Int(this->latency));
break;
default:
return 0;
}
break;
default:
return -ENOENT;
}
@ -526,6 +541,7 @@ static int port_set_format(void *object,
if (this->have_format) {
this->port_params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE);
this->port_params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ);
this->port_params[IDX_Latency].flags ^= SPA_PARAM_INFO_SERIAL;
} else {
this->port_params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
this->port_params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
@ -759,6 +775,7 @@ impl_init(const struct spa_handle_factory *factory,
spa_hook_list_init(&this->hooks);
this->stream = SND_PCM_STREAM_PLAYBACK;
this->latency_quantum = 1.0f;
this->info_all = SPA_NODE_CHANGE_MASK_FLAGS |
SPA_NODE_CHANGE_MASK_PROPS |
@ -785,6 +802,7 @@ impl_init(const struct spa_handle_factory *factory,
this->port_params[IDX_IO] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
this->port_params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
this->port_params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
this->port_params[IDX_Latency] = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE);
this->port_info.params = this->port_params;
this->port_info.n_params = N_PORT_PARAMS;

View file

@ -454,6 +454,21 @@ impl_node_port_enum_params(void *object, int seq,
}
break;
case SPA_PARAM_Latency:
switch (result.index) {
case 0:
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_ParamLatency, id,
SPA_PARAM_LATENCY_direction, SPA_POD_Id(direction),
SPA_PARAM_LATENCY_quantum, SPA_POD_Float(this->latency_quantum),
SPA_PARAM_LATENCY_min, SPA_POD_Int(this->latency),
SPA_PARAM_LATENCY_max, SPA_POD_Int(this->latency));
break;
default:
return 0;
}
break;
default:
return -ENOENT;
}
@ -523,6 +538,7 @@ static int port_set_format(void *object,
if (this->have_format) {
this->port_params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE);
this->port_params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ);
this->port_params[IDX_Latency].flags ^= SPA_PARAM_INFO_SERIAL;
} else {
this->port_params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
this->port_params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
@ -779,6 +795,8 @@ impl_init(const struct spa_handle_factory *factory,
spa_hook_list_init(&this->hooks);
this->stream = SND_PCM_STREAM_CAPTURE;
this->latency_quantum = 1.0f;
this->info_all = SPA_NODE_CHANGE_MASK_FLAGS |
SPA_NODE_CHANGE_MASK_PROPS |
SPA_NODE_CHANGE_MASK_PARAMS;
@ -802,6 +820,7 @@ impl_init(const struct spa_handle_factory *factory,
this->port_params[IDX_IO] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
this->port_params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
this->port_params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
this->port_params[IDX_Latency] = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE);
this->port_info.params = this->port_params;
this->port_info.n_params = N_PORT_PARAMS;

View file

@ -630,6 +630,8 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_
state->headroom = SPA_MIN(state->headroom, state->buffer_frames);
state->start_delay = state->default_start_delay;
state->latency = state->headroom;
state->period_frames = period_size;
periods = state->buffer_frames / state->period_frames;

View file

@ -143,7 +143,8 @@ struct state {
#define IDX_IO 2
#define IDX_Format 3
#define IDX_Buffers 4
#define N_PORT_PARAMS 5
#define IDX_Latency 5
#define N_PORT_PARAMS 6
struct spa_param_info port_params[N_PORT_PARAMS];
struct spa_io_buffers *io;
struct spa_io_clock *clock;
@ -190,6 +191,9 @@ struct state {
struct spa_dll dll;
double max_error;
float latency_quantum;
uint32_t latency;
};
int

View file

@ -83,7 +83,8 @@ struct impl {
#define IDX_Format 3
#define IDX_EnumPortConfig 4
#define IDX_PortConfig 5
#define N_NODE_PARAMS 6
#define IDX_Latency 6
#define N_NODE_PARAMS 7
struct spa_param_info params[N_NODE_PARAMS];
uint32_t convert_params_flags[N_NODE_PARAMS];
uint32_t follower_params_flags[N_NODE_PARAMS];
@ -715,12 +716,36 @@ static void follower_info(void *data, const struct spa_node_info *info)
emit_node_info(this, false);
}
static int recalc_latency(struct impl *this, enum spa_direction direction, uint32_t port_id)
{
struct spa_pod_builder b = { 0 };
uint8_t buffer[1024];
struct spa_pod *param;
uint32_t index = 0;
int res;
spa_pod_builder_init(&b, buffer, sizeof(buffer));
if ((res = spa_node_port_enum_params_sync(this->follower,
direction, port_id, SPA_PARAM_Latency,
&index, NULL, &param, &b)) != 1)
return res;
if ((res = spa_node_port_set_param(this->target,
SPA_DIRECTION_REVERSE(direction), 0,
SPA_PARAM_Latency, 0, param)) < 0)
return res;
return 0;
}
static void follower_port_info(void *data,
enum spa_direction direction, uint32_t port_id,
const struct spa_port_info *info)
{
struct impl *this = data;
uint32_t i;
int res;
if (info->change_mask & SPA_PORT_CHANGE_MASK_PARAMS) {
for (i = 0; i < info->n_params; i++) {
@ -730,6 +755,9 @@ static void follower_port_info(void *data,
case SPA_PARAM_Format:
idx = IDX_Format;
break;
case SPA_PARAM_Latency:
idx = IDX_Latency;
break;
default:
continue;
}
@ -742,6 +770,13 @@ static void follower_port_info(void *data,
(this->params[idx].flags & SPA_PARAM_INFO_SERIAL) |
(info->params[i].flags & SPA_PARAM_INFO_READWRITE);
if (idx == IDX_Latency) {
res = recalc_latency(this, direction, port_id);
if (res != 0)
spa_log_error(this->log, "latency: %d", res);
continue;
}
this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
if (!this->add_listener)
this->params[idx].user++;

View file

@ -1042,6 +1042,9 @@ impl_node_port_set_param(void *object,
this, id, direction, port_id, param);
switch (id) {
case SPA_PARAM_Latency:
target = this->fmt[SPA_DIRECTION_REVERSE(direction)];
break;
default:
is_monitor = IS_MONITOR_PORT(this, direction, port_id);
if (is_monitor)

View file

@ -119,7 +119,8 @@ struct port {
#define IDX_IO 2
#define IDX_Format 3
#define IDX_Buffers 4
#define N_PORT_PARAMS 5
#define IDX_Latency 5
#define N_PORT_PARAMS 6
struct spa_param_info params[N_PORT_PARAMS];
char position[16];
@ -175,6 +176,10 @@ struct impl {
uint32_t src_remap[SPA_AUDIO_MAX_CHANNELS];
uint32_t dst_remap[SPA_AUDIO_MAX_CHANNELS];
float latency_quantum[2];
uint32_t latency_min[2];
uint32_t latency_max[2];
float empty[MAX_SAMPLES + MAX_ALIGN];
};
@ -249,6 +254,7 @@ static int init_port(struct impl *this, enum spa_direction direction, uint32_t p
port->params[IDX_IO] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
port->params[IDX_Latency] = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE);
port->info.params = port->params;
port->info.n_params = N_PORT_PARAMS;
@ -805,6 +811,20 @@ impl_node_port_enum_params(void *object, int seq,
return 0;
}
break;
case SPA_PARAM_Latency:
switch (result.index) {
case 0:
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_ParamLatency, id,
SPA_PARAM_LATENCY_direction, SPA_POD_Id(direction),
SPA_PARAM_LATENCY_quantum, SPA_POD_Float(this->latency_quantum[direction]),
SPA_PARAM_LATENCY_min, SPA_POD_Int(this->latency_min[direction]),
SPA_PARAM_LATENCY_max, SPA_POD_Int(this->latency_max[direction]));
break;
default:
return 0;
}
break;
default:
return -ENOENT;
}
@ -905,6 +925,46 @@ static int calc_width(struct spa_audio_info *info)
}
}
static int port_set_latency(void *object,
enum spa_direction direction,
uint32_t port_id,
uint32_t flags,
const struct spa_pod *latency)
{
struct impl *this = object;
struct port *port;
enum spa_direction other = SPA_DIRECTION_REVERSE(direction);
uint32_t i;
spa_log_debug(this->log, NAME " %p: set latency", this);
if (latency == NULL) {
this->latency_quantum[other] = 0;
this->latency_min[other] = 0;
this->latency_max[other] = 0;
} else {
if (spa_pod_parse_object(latency,
SPA_TYPE_OBJECT_ParamLatency, NULL,
SPA_PARAM_LATENCY_quantum,SPA_POD_Float(&this->latency_quantum[other]),
SPA_PARAM_LATENCY_min, SPA_POD_Int(&this->latency_min[other]),
SPA_PARAM_LATENCY_max, SPA_POD_Int(&this->latency_max[other])) < 0)
return -EINVAL;
}
if (direction == SPA_DIRECTION_OUTPUT) {
for (i = 0; i < this->port_count; i++) {
port = GET_IN_PORT(this, i);
port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
port->params[IDX_Latency].flags ^= SPA_PARAM_INFO_SERIAL;
emit_port_info(this, port, false);
}
} else {
port = GET_OUT_PORT(this, 0);
port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
port->params[IDX_Latency].flags ^= SPA_PARAM_INFO_SERIAL;
emit_port_info(this, port, false);
}
return 0;
}
static int port_set_format(void *object,
enum spa_direction direction,
uint32_t port_id,
@ -1017,6 +1077,8 @@ impl_node_port_set_param(void *object,
spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
switch (id) {
case SPA_PARAM_Latency:
return port_set_latency(this, direction, port_id, flags, param);
case SPA_PARAM_Format:
return port_set_format(this, direction, port_id, flags, param);
default:
@ -1431,6 +1493,7 @@ impl_init(const struct spa_handle_factory *factory,
port->params[IDX_IO] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
port->params[IDX_Latency] = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE);
port->info.params = port->params;
port->info.n_params = N_PORT_PARAMS;
spa_list_init(&port->queue);

View file

@ -78,7 +78,8 @@ struct port {
#define IDX_IO 2
#define IDX_Format 3
#define IDX_Buffers 4
#define N_PORT_PARAMS 5
#define IDX_Latency 5
#define N_PORT_PARAMS 6
struct spa_param_info params[N_PORT_PARAMS];
struct spa_dict info_props;
@ -125,6 +126,10 @@ struct impl {
unsigned int is_passthrough:1;
unsigned int started:1;
float latency_quantum[2];
uint32_t latency_min[2];
uint32_t latency_max[2];
uint32_t src_remap[SPA_AUDIO_MAX_CHANNELS];
uint32_t dst_remap[SPA_AUDIO_MAX_CHANNELS];
@ -193,6 +198,7 @@ static int init_port(struct impl *this, enum spa_direction direction,
port->params[IDX_IO] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
port->params[IDX_Latency] = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE);
port->info.params = port->params;
port->info.n_params = N_PORT_PARAMS;
@ -563,6 +569,20 @@ impl_node_port_enum_params(void *object, int seq,
return 0;
}
break;
case SPA_PARAM_Latency:
switch (result.index) {
case 0:
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_ParamLatency, id,
SPA_PARAM_LATENCY_direction, SPA_POD_Id(direction),
SPA_PARAM_LATENCY_quantum, SPA_POD_Float(this->latency_quantum[direction]),
SPA_PARAM_LATENCY_min, SPA_POD_Int(this->latency_min[direction]),
SPA_PARAM_LATENCY_max, SPA_POD_Int(this->latency_max[direction]));
break;
default:
return 0;
}
break;
default:
return -ENOENT;
}
@ -667,6 +687,46 @@ static int calc_width(struct spa_audio_info *info)
}
}
static int port_set_latency(void *object,
enum spa_direction direction,
uint32_t port_id,
uint32_t flags,
const struct spa_pod *latency)
{
struct impl *this = object;
struct port *port;
enum spa_direction other = SPA_DIRECTION_REVERSE(direction);
uint32_t i;
spa_log_debug(this->log, NAME " %p: set latency", this);
if (latency == NULL) {
this->latency_quantum[other] = 0;
this->latency_min[other] = 0;
this->latency_max[other] = 0;
} else {
if (spa_pod_parse_object(latency,
SPA_TYPE_OBJECT_ParamLatency, NULL,
SPA_PARAM_LATENCY_quantum,SPA_POD_Float(&this->latency_quantum[other]),
SPA_PARAM_LATENCY_min, SPA_POD_Int(&this->latency_min[other]),
SPA_PARAM_LATENCY_max, SPA_POD_Int(&this->latency_max[other])) < 0)
return -EINVAL;
}
if (direction == SPA_DIRECTION_INPUT) {
for (i = 0; i < this->port_count; i++) {
port = GET_OUT_PORT(this, i);
port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
port->params[IDX_Latency].flags ^= SPA_PARAM_INFO_SERIAL;
emit_port_info(this, port, false);
}
} else {
port = GET_IN_PORT(this, 0);
port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
port->params[IDX_Latency].flags ^= SPA_PARAM_INFO_SERIAL;
emit_port_info(this, port, false);
}
return 0;
}
static int port_set_format(void *object,
enum spa_direction direction,
uint32_t port_id,
@ -739,6 +799,7 @@ static int port_set_format(void *object,
if (port->have_format) {
port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE);
port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ);
port->params[IDX_Latency].flags ^= SPA_PARAM_INFO_SERIAL;
} else {
port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
@ -761,6 +822,8 @@ impl_node_port_set_param(void *object,
spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
switch (id) {
case SPA_PARAM_Latency:
return port_set_latency(this, direction, port_id, flags, param);
case SPA_PARAM_Format:
return port_set_format(this, direction, port_id, flags, param);
default:
@ -1102,6 +1165,7 @@ impl_init(const struct spa_handle_factory *factory,
port->params[IDX_IO] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
port->params[IDX_Latency] = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE);
port->info.params = port->params;
port->info.n_params = N_PORT_PARAMS;