mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-07 13:30:09 -05:00
impl-link: refactor format negotiation
Make a new port_info structure that holds the link port information for input and output. We can use this in some places and remove some redundant code. We can also pass a reference to this port_info as the work-queue object, which makes it more natural to find the associated port info in the various work queue callbacks. Move the private pw_context_find_format to the link implementation, where is it actually used. Rework the format negotiation code to use an array of 2 port_info structures with the two ports to negotiate. The negiotiation will always use the first port_info as higher priority. Make sure a driver port has a lower priority than the other port. We want to negotiate the source/sink to something close that what is provided/requested by the client. The client can always adapt to the driver port format fields by giving a "don't care" value for the format property (either unspecified or with an out of range default value).
This commit is contained in:
parent
abfad78fb3
commit
7d6e2a6417
3 changed files with 365 additions and 421 deletions
|
|
@ -886,194 +886,6 @@ SPA_PRINTF_FUNC(7, 8) int pw_context_debug_port_params(struct pw_context *this,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Find a common format between two ports
|
|
||||||
*
|
|
||||||
* \param context a context object
|
|
||||||
* \param output an output port
|
|
||||||
* \param input an input port
|
|
||||||
* \param props extra properties
|
|
||||||
* \param n_format_filters number of format filters
|
|
||||||
* \param format_filters array of format filters
|
|
||||||
* \param[out] format the common format between the ports
|
|
||||||
* \param builder builder to use for processing
|
|
||||||
* \param[out] error an error when something is wrong
|
|
||||||
* \return a common format of NULL on error
|
|
||||||
*
|
|
||||||
* Find a common format between the given ports. The format will
|
|
||||||
* be restricted to a subset given with the format filters.
|
|
||||||
*/
|
|
||||||
int pw_context_find_format(struct pw_context *context,
|
|
||||||
struct pw_impl_port *output,
|
|
||||||
uint32_t output_mix,
|
|
||||||
struct pw_impl_port *input,
|
|
||||||
uint32_t input_mix,
|
|
||||||
struct pw_properties *props,
|
|
||||||
uint32_t n_format_filters,
|
|
||||||
struct spa_pod **format_filters,
|
|
||||||
struct spa_pod **format,
|
|
||||||
struct spa_pod_builder *builder,
|
|
||||||
char **error)
|
|
||||||
{
|
|
||||||
uint32_t out_state, in_state;
|
|
||||||
int res;
|
|
||||||
uint32_t iidx = 0, oidx = 0;
|
|
||||||
struct spa_pod_builder fb = { 0 };
|
|
||||||
uint8_t fbuf[4096];
|
|
||||||
struct spa_pod *filter;
|
|
||||||
struct spa_node *in_node, *out_node;
|
|
||||||
uint32_t in_port, out_port;
|
|
||||||
|
|
||||||
out_state = output->state;
|
|
||||||
in_state = input->state;
|
|
||||||
|
|
||||||
if (output_mix == SPA_ID_INVALID) {
|
|
||||||
out_node = output->node->node;
|
|
||||||
out_port = output->port_id;
|
|
||||||
} else {
|
|
||||||
out_node = output->mix;
|
|
||||||
out_port = output_mix;
|
|
||||||
}
|
|
||||||
if (input_mix == SPA_ID_INVALID) {
|
|
||||||
in_node = input->node->node;
|
|
||||||
in_port = input->port_id;
|
|
||||||
} else {
|
|
||||||
in_node = input->mix;
|
|
||||||
in_port = input_mix;
|
|
||||||
}
|
|
||||||
|
|
||||||
pw_log_debug("%p: finding best format %d %d", context, out_state, in_state);
|
|
||||||
|
|
||||||
/* when a port is configured but the node is idle, we can reconfigure with a different format */
|
|
||||||
if (out_state > PW_IMPL_PORT_STATE_CONFIGURE && output->node->info.state == PW_NODE_STATE_IDLE)
|
|
||||||
out_state = PW_IMPL_PORT_STATE_CONFIGURE;
|
|
||||||
if (in_state > PW_IMPL_PORT_STATE_CONFIGURE && input->node->info.state == PW_NODE_STATE_IDLE)
|
|
||||||
in_state = PW_IMPL_PORT_STATE_CONFIGURE;
|
|
||||||
|
|
||||||
pw_log_debug("%p: states %d %d", context, out_state, in_state);
|
|
||||||
|
|
||||||
if (in_state == PW_IMPL_PORT_STATE_CONFIGURE && out_state > PW_IMPL_PORT_STATE_CONFIGURE) {
|
|
||||||
/* only input needs format */
|
|
||||||
spa_pod_builder_init(&fb, fbuf, sizeof(fbuf));
|
|
||||||
if ((res = spa_node_port_enum_params_sync(out_node,
|
|
||||||
output->direction, out_port,
|
|
||||||
SPA_PARAM_Format, &oidx,
|
|
||||||
NULL, &filter, &fb)) != 1) {
|
|
||||||
if (res < 0)
|
|
||||||
*error = spa_aprintf("error get output format: %s", spa_strerror(res));
|
|
||||||
else
|
|
||||||
*error = spa_aprintf("no output formats");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
pw_log_debug("%p: Got output format:", context);
|
|
||||||
pw_log_format(SPA_LOG_LEVEL_DEBUG, filter);
|
|
||||||
|
|
||||||
if ((res = spa_node_port_enum_params_sync(in_node,
|
|
||||||
input->direction, in_port,
|
|
||||||
SPA_PARAM_EnumFormat, &iidx,
|
|
||||||
filter, format, builder)) <= 0) {
|
|
||||||
if (res == -ENOENT || res == 0) {
|
|
||||||
pw_log_debug("%p: no input format filter, using output format: %s",
|
|
||||||
context, spa_strerror(res));
|
|
||||||
|
|
||||||
uint32_t offset = builder->state.offset;
|
|
||||||
res = spa_pod_builder_raw_padded(builder, filter, SPA_POD_SIZE(filter));
|
|
||||||
if (res < 0) {
|
|
||||||
*error = spa_aprintf("failed to add pod");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
*format = spa_pod_builder_deref(builder, offset);
|
|
||||||
} else {
|
|
||||||
*error = spa_aprintf("error input enum formats: %s", spa_strerror(res));
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (out_state >= PW_IMPL_PORT_STATE_CONFIGURE && in_state > PW_IMPL_PORT_STATE_CONFIGURE) {
|
|
||||||
/* only output needs format */
|
|
||||||
spa_pod_builder_init(&fb, fbuf, sizeof(fbuf));
|
|
||||||
if ((res = spa_node_port_enum_params_sync(in_node,
|
|
||||||
input->direction, in_port,
|
|
||||||
SPA_PARAM_Format, &iidx,
|
|
||||||
NULL, &filter, &fb)) != 1) {
|
|
||||||
if (res < 0)
|
|
||||||
*error = spa_aprintf("error get input format: %s", spa_strerror(res));
|
|
||||||
else
|
|
||||||
*error = spa_aprintf("no input format");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
pw_log_debug("%p: Got input format:", context);
|
|
||||||
pw_log_format(SPA_LOG_LEVEL_DEBUG, filter);
|
|
||||||
|
|
||||||
if ((res = spa_node_port_enum_params_sync(out_node,
|
|
||||||
output->direction, out_port,
|
|
||||||
SPA_PARAM_EnumFormat, &oidx,
|
|
||||||
filter, format, builder)) <= 0) {
|
|
||||||
if (res == -ENOENT || res == 0) {
|
|
||||||
pw_log_debug("%p: no output format filter, using input format: %s",
|
|
||||||
context, spa_strerror(res));
|
|
||||||
|
|
||||||
uint32_t offset = builder->state.offset;
|
|
||||||
res = spa_pod_builder_raw_padded(builder, filter, SPA_POD_SIZE(filter));
|
|
||||||
if (res < 0) {
|
|
||||||
*error = spa_aprintf("failed to add pod");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
*format = spa_pod_builder_deref(builder, offset);
|
|
||||||
} else {
|
|
||||||
*error = spa_aprintf("error output enum formats: %s", spa_strerror(res));
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (in_state == PW_IMPL_PORT_STATE_CONFIGURE && out_state == PW_IMPL_PORT_STATE_CONFIGURE) {
|
|
||||||
again:
|
|
||||||
/* both ports need a format */
|
|
||||||
pw_log_debug("%p: do enum input %d", context, iidx);
|
|
||||||
spa_pod_builder_init(&fb, fbuf, sizeof(fbuf));
|
|
||||||
if ((res = spa_node_port_enum_params_sync(in_node,
|
|
||||||
input->direction, in_port,
|
|
||||||
SPA_PARAM_EnumFormat, &iidx,
|
|
||||||
NULL, &filter, &fb)) != 1) {
|
|
||||||
if (res == -ENOENT) {
|
|
||||||
pw_log_debug("%p: no input filter", context);
|
|
||||||
filter = NULL;
|
|
||||||
} else {
|
|
||||||
if (res < 0)
|
|
||||||
*error = spa_aprintf("error input enum formats: %s", spa_strerror(res));
|
|
||||||
else
|
|
||||||
*error = spa_aprintf("no more input formats");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pw_log_debug("%p: enum output %d with filter: %p", context, oidx, filter);
|
|
||||||
pw_log_format(SPA_LOG_LEVEL_DEBUG, filter);
|
|
||||||
|
|
||||||
if ((res = spa_node_port_enum_params_sync(out_node,
|
|
||||||
output->direction, out_port,
|
|
||||||
SPA_PARAM_EnumFormat, &oidx,
|
|
||||||
filter, format, builder)) != 1) {
|
|
||||||
if (res == 0 && filter != NULL) {
|
|
||||||
oidx = 0;
|
|
||||||
goto again;
|
|
||||||
}
|
|
||||||
*error = spa_aprintf("error output enum formats: %s", spa_strerror(res));
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
pw_log_debug("%p: Got filtered:", context);
|
|
||||||
pw_log_format(SPA_LOG_LEVEL_DEBUG, *format);
|
|
||||||
} else {
|
|
||||||
res = -EBADF;
|
|
||||||
*error = spa_aprintf("error bad node state");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
error:
|
|
||||||
if (res == 0)
|
|
||||||
res = -EINVAL;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ensure_state(struct pw_impl_node *node, bool running)
|
static int ensure_state(struct pw_impl_node *node, bool running)
|
||||||
{
|
{
|
||||||
enum pw_node_state state = node->info.state;
|
enum pw_node_state state = node->info.state;
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,18 @@ PW_LOG_TOPIC_EXTERN(log_link);
|
||||||
|
|
||||||
#define pw_link_resource_info(r,...) pw_resource_call(r,struct pw_link_events,info,0,__VA_ARGS__)
|
#define pw_link_resource_info(r,...) pw_resource_call(r,struct pw_link_events,info,0,__VA_ARGS__)
|
||||||
|
|
||||||
|
struct port_info {
|
||||||
|
uint32_t busy_id;
|
||||||
|
int pending_seq;
|
||||||
|
int result;
|
||||||
|
struct spa_hook port_listener;
|
||||||
|
struct spa_hook node_listener;
|
||||||
|
struct spa_hook global_listener;
|
||||||
|
struct pw_impl_port *port;
|
||||||
|
struct pw_impl_node *node;
|
||||||
|
struct pw_impl_port_mix *mix;
|
||||||
|
};
|
||||||
|
|
||||||
/** \cond */
|
/** \cond */
|
||||||
struct impl {
|
struct impl {
|
||||||
struct pw_impl_link this;
|
struct pw_impl_link this;
|
||||||
|
|
@ -32,26 +44,14 @@ struct impl {
|
||||||
|
|
||||||
struct pw_work_queue *work;
|
struct pw_work_queue *work;
|
||||||
|
|
||||||
uint32_t output_busy_id;
|
struct port_info input;
|
||||||
uint32_t input_busy_id;
|
struct port_info output;
|
||||||
int output_pending_seq;
|
|
||||||
int input_pending_seq;
|
|
||||||
int output_result;
|
|
||||||
int input_result;
|
|
||||||
|
|
||||||
struct spa_pod *format_filter;
|
struct spa_pod *format_filter;
|
||||||
struct pw_properties *properties;
|
struct pw_properties *properties;
|
||||||
|
|
||||||
struct spa_hook input_port_listener;
|
|
||||||
struct spa_hook input_node_listener;
|
|
||||||
struct spa_hook input_global_listener;
|
|
||||||
struct spa_hook output_port_listener;
|
|
||||||
struct spa_hook output_node_listener;
|
|
||||||
struct spa_hook output_global_listener;
|
|
||||||
|
|
||||||
struct spa_io_buffers io[2];
|
struct spa_io_buffers io[2];
|
||||||
|
|
||||||
struct pw_impl_node *inode, *onode;
|
|
||||||
bool async;
|
bool async;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -73,35 +73,18 @@ static void info_changed(struct pw_impl_link *link)
|
||||||
link->info.change_mask = 0;
|
link->info.change_mask = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int input_set_busy_id(struct pw_impl_link *link, uint32_t id, int pending_seq)
|
static inline int port_set_busy_id(struct pw_impl_link *link, struct port_info *info, uint32_t id, int pending_seq)
|
||||||
{
|
{
|
||||||
struct impl *impl = SPA_CONTAINER_OF(link, struct impl, this);
|
int res = info->result;
|
||||||
int res = impl->input_result;
|
if (info->busy_id != SPA_ID_INVALID)
|
||||||
if (impl->input_busy_id != SPA_ID_INVALID)
|
info->port->busy_count--;
|
||||||
link->input->busy_count--;
|
|
||||||
if (id != SPA_ID_INVALID)
|
if (id != SPA_ID_INVALID)
|
||||||
link->input->busy_count++;
|
info->port->busy_count++;
|
||||||
impl->input_busy_id = id;
|
info->busy_id = id;
|
||||||
impl->input_pending_seq = SPA_RESULT_ASYNC_SEQ(pending_seq);
|
info->pending_seq = SPA_RESULT_ASYNC_SEQ(pending_seq);
|
||||||
impl->input_result = 0;
|
info->result = 0;
|
||||||
if (link->input->busy_count < 0)
|
if (info->port->busy_count < 0)
|
||||||
pw_log_error("%s: invalid busy count:%d", link->name, link->input->busy_count);
|
pw_log_error("%s: invalid busy count:%d", link->name, info->port->busy_count);
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int output_set_busy_id(struct pw_impl_link *link, uint32_t id, int pending_seq)
|
|
||||||
{
|
|
||||||
struct impl *impl = SPA_CONTAINER_OF(link, struct impl, this);
|
|
||||||
int res = impl->output_result;
|
|
||||||
if (impl->output_busy_id != SPA_ID_INVALID)
|
|
||||||
link->output->busy_count--;
|
|
||||||
if (id != SPA_ID_INVALID)
|
|
||||||
link->output->busy_count++;
|
|
||||||
impl->output_busy_id = id;
|
|
||||||
impl->output_pending_seq = SPA_RESULT_ASYNC_SEQ(pending_seq);
|
|
||||||
impl->output_result = 0;
|
|
||||||
if (link->output->busy_count < 0)
|
|
||||||
pw_log_error("%s: invalid busy count:%d", link->name, link->output->busy_count);
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,30 +143,23 @@ static void link_update_state(struct pw_impl_link *link, enum pw_link_state stat
|
||||||
link->prepared = false;
|
link->prepared = false;
|
||||||
link->preparing = false;
|
link->preparing = false;
|
||||||
|
|
||||||
output_set_busy_id(link, SPA_ID_INVALID, SPA_ID_INVALID);
|
port_set_busy_id(link, &impl->output, SPA_ID_INVALID, SPA_ID_INVALID);
|
||||||
pw_work_queue_cancel(impl->work, &link->output_link, SPA_ID_INVALID);
|
pw_work_queue_cancel(impl->work, &impl->output, SPA_ID_INVALID);
|
||||||
|
|
||||||
input_set_busy_id(link, SPA_ID_INVALID, SPA_ID_INVALID);
|
port_set_busy_id(link, &impl->input, SPA_ID_INVALID, SPA_ID_INVALID);
|
||||||
pw_work_queue_cancel(impl->work, &link->input_link, SPA_ID_INVALID);
|
pw_work_queue_cancel(impl->work, &impl->input, SPA_ID_INVALID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void complete_ready(void *obj, void *data, int res, uint32_t id)
|
static void complete_ready(void *obj, void *data, int res, uint32_t id)
|
||||||
{
|
{
|
||||||
struct pw_impl_port *port;
|
struct port_info *info = obj;
|
||||||
|
struct pw_impl_port *port = info->port;
|
||||||
struct pw_impl_link *this = data;
|
struct pw_impl_link *this = data;
|
||||||
struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
|
|
||||||
|
|
||||||
if (obj == &this->input_link)
|
|
||||||
port = this->input;
|
|
||||||
else
|
|
||||||
port = this->output;
|
|
||||||
|
|
||||||
if (id != SPA_ID_INVALID) {
|
if (id != SPA_ID_INVALID) {
|
||||||
if (id == impl->input_busy_id)
|
if (id == info->busy_id)
|
||||||
res = input_set_busy_id(this, SPA_ID_INVALID, SPA_ID_INVALID);
|
res = port_set_busy_id(this, info, SPA_ID_INVALID, SPA_ID_INVALID);
|
||||||
else if (id == impl->output_busy_id)
|
|
||||||
res = output_set_busy_id(this, SPA_ID_INVALID, SPA_ID_INVALID);
|
|
||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -205,24 +181,14 @@ static void complete_ready(void *obj, void *data, int res, uint32_t id)
|
||||||
|
|
||||||
static void complete_paused(void *obj, void *data, int res, uint32_t id)
|
static void complete_paused(void *obj, void *data, int res, uint32_t id)
|
||||||
{
|
{
|
||||||
struct pw_impl_port *port;
|
struct port_info *info = obj;
|
||||||
|
struct pw_impl_port *port = info->port;
|
||||||
|
struct pw_impl_port_mix *mix = info->mix;
|
||||||
struct pw_impl_link *this = data;
|
struct pw_impl_link *this = data;
|
||||||
struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
|
|
||||||
struct pw_impl_port_mix *mix;
|
|
||||||
|
|
||||||
if (obj == &this->input_link) {
|
|
||||||
port = this->input;
|
|
||||||
mix = &this->rt.in_mix;
|
|
||||||
} else {
|
|
||||||
port = this->output;
|
|
||||||
mix = &this->rt.out_mix;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id != SPA_ID_INVALID) {
|
if (id != SPA_ID_INVALID) {
|
||||||
if (id == impl->input_busy_id)
|
if (id == info->busy_id)
|
||||||
res = input_set_busy_id(this, SPA_ID_INVALID, SPA_ID_INVALID);
|
res = port_set_busy_id(this, info, SPA_ID_INVALID, SPA_ID_INVALID);
|
||||||
else if (id == impl->output_busy_id)
|
|
||||||
res = output_set_busy_id(this, SPA_ID_INVALID, SPA_ID_INVALID);
|
|
||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -246,18 +212,181 @@ static void complete_paused(void *obj, void *data, int res, uint32_t id)
|
||||||
|
|
||||||
static void complete_sync(void *obj, void *data, int res, uint32_t id)
|
static void complete_sync(void *obj, void *data, int res, uint32_t id)
|
||||||
{
|
{
|
||||||
struct pw_impl_port *port;
|
struct port_info *info = obj;
|
||||||
|
struct pw_impl_port *port = info->port;
|
||||||
struct pw_impl_link *this = data;
|
struct pw_impl_link *this = data;
|
||||||
|
|
||||||
if (obj == &this->input_link)
|
|
||||||
port = this->input;
|
|
||||||
else
|
|
||||||
port = this->output;
|
|
||||||
|
|
||||||
pw_log_debug("%p: obj:%p port %p complete state:%d: %s", this, obj, port,
|
pw_log_debug("%p: obj:%p port %p complete state:%d: %s", this, obj, port,
|
||||||
port->state, spa_strerror(res));
|
port->state, spa_strerror(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* find a common format. info[0] has the higher priority.
|
||||||
|
* Either the format contains a valid common format or error is set. */
|
||||||
|
static int link_find_format(struct pw_impl_link *this,
|
||||||
|
struct port_info *info[2],
|
||||||
|
uint32_t port_id[2],
|
||||||
|
struct spa_pod **format,
|
||||||
|
struct spa_pod_builder *builder,
|
||||||
|
char **error)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
uint32_t state[2];
|
||||||
|
uint32_t idx[2] = { 0, 0 };
|
||||||
|
struct spa_pod_builder fb = { 0 };
|
||||||
|
uint8_t fbuf[4096];
|
||||||
|
struct spa_pod *filter;
|
||||||
|
struct spa_node *node[2];
|
||||||
|
const char *dir[2];
|
||||||
|
|
||||||
|
state[0] = info[0]->port->state;
|
||||||
|
state[1] = info[1]->port->state;
|
||||||
|
node[0] = info[0]->node->node;
|
||||||
|
node[1] = info[1]->node->node;
|
||||||
|
port_id[0] = info[0]->port->port_id;
|
||||||
|
port_id[1] = info[1]->port->port_id;
|
||||||
|
dir[0] = pw_direction_as_string(info[0]->port->direction);
|
||||||
|
dir[1] = pw_direction_as_string(info[1]->port->direction);
|
||||||
|
|
||||||
|
pw_log_debug("%p: finding best format %d %d", this, state[0], state[1]);
|
||||||
|
|
||||||
|
/* when a port is configured but the node is idle, we can reconfigure with a different format */
|
||||||
|
if (state[1] > PW_IMPL_PORT_STATE_CONFIGURE && info[1]->node->info.state == PW_NODE_STATE_IDLE)
|
||||||
|
state[1] = PW_IMPL_PORT_STATE_CONFIGURE;
|
||||||
|
if (state[0] > PW_IMPL_PORT_STATE_CONFIGURE && info[0]->node->info.state == PW_NODE_STATE_IDLE)
|
||||||
|
state[0] = PW_IMPL_PORT_STATE_CONFIGURE;
|
||||||
|
|
||||||
|
pw_log_debug("%p: states %d %d", this, state[0], state[1]);
|
||||||
|
|
||||||
|
if (state[0] == PW_IMPL_PORT_STATE_CONFIGURE && state[1] > PW_IMPL_PORT_STATE_CONFIGURE) {
|
||||||
|
/* only port 0 needs format, take format from port 1 and filter */
|
||||||
|
spa_pod_builder_init(&fb, fbuf, sizeof(fbuf));
|
||||||
|
if ((res = spa_node_port_enum_params_sync(node[1],
|
||||||
|
info[1]->port->direction, port_id[1],
|
||||||
|
SPA_PARAM_Format, &idx[1],
|
||||||
|
NULL, &filter, &fb)) != 1) {
|
||||||
|
if (res < 0)
|
||||||
|
*error = spa_aprintf("error get %s format: %s", dir[1],
|
||||||
|
spa_strerror(res));
|
||||||
|
else
|
||||||
|
*error = spa_aprintf("no %s formats", dir[1]);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
pw_log_debug("%p: Got %s format:", this, dir[1]);
|
||||||
|
pw_log_pod(SPA_LOG_LEVEL_DEBUG, filter);
|
||||||
|
|
||||||
|
if ((res = spa_node_port_enum_params_sync(node[0],
|
||||||
|
info[0]->port->direction, port_id[0],
|
||||||
|
SPA_PARAM_EnumFormat, &idx[0],
|
||||||
|
filter, format, builder)) <= 0) {
|
||||||
|
if (res == -ENOENT || res == 0) {
|
||||||
|
pw_log_debug("%p: no %s format filter, using %s format: %s",
|
||||||
|
this, dir[0], dir[1], spa_strerror(res));
|
||||||
|
|
||||||
|
uint32_t offset = builder->state.offset;
|
||||||
|
res = spa_pod_builder_raw_padded(builder, filter, SPA_POD_SIZE(filter));
|
||||||
|
if (res < 0) {
|
||||||
|
*error = spa_aprintf("failed to add pod");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
*format = spa_pod_builder_deref(builder, offset);
|
||||||
|
} else {
|
||||||
|
*error = spa_aprintf("error %s enum formats: %s", dir[0],
|
||||||
|
spa_strerror(res));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (state[1] >= PW_IMPL_PORT_STATE_CONFIGURE && state[0] > PW_IMPL_PORT_STATE_CONFIGURE) {
|
||||||
|
/* only port 1 needs format, take and filter format from port 0 */
|
||||||
|
spa_pod_builder_init(&fb, fbuf, sizeof(fbuf));
|
||||||
|
if ((res = spa_node_port_enum_params_sync(node[0],
|
||||||
|
info[0]->port->direction, port_id[0],
|
||||||
|
SPA_PARAM_Format, &idx[0],
|
||||||
|
NULL, &filter, &fb)) != 1) {
|
||||||
|
if (res < 0)
|
||||||
|
*error = spa_aprintf("error get %s format: %s", dir[0],
|
||||||
|
spa_strerror(res));
|
||||||
|
else
|
||||||
|
*error = spa_aprintf("no %s format", dir[0]);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
pw_log_debug("%p: Got %s format:", this, dir[0]);
|
||||||
|
pw_log_pod(SPA_LOG_LEVEL_DEBUG, filter);
|
||||||
|
|
||||||
|
if ((res = spa_node_port_enum_params_sync(node[1],
|
||||||
|
info[1]->port->direction, port_id[1],
|
||||||
|
SPA_PARAM_EnumFormat, &idx[1],
|
||||||
|
filter, format, builder)) <= 0) {
|
||||||
|
if (res == -ENOENT || res == 0) {
|
||||||
|
pw_log_debug("%p: no %s format filter, using %s format: %s",
|
||||||
|
this, dir[1], dir[0], spa_strerror(res));
|
||||||
|
|
||||||
|
uint32_t offset = builder->state.offset;
|
||||||
|
res = spa_pod_builder_raw_padded(builder, filter, SPA_POD_SIZE(filter));
|
||||||
|
if (res < 0) {
|
||||||
|
*error = spa_aprintf("failed to add pod");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
*format = spa_pod_builder_deref(builder, offset);
|
||||||
|
} else {
|
||||||
|
*error = spa_aprintf("error %s enum formats: %s", dir[1],
|
||||||
|
spa_strerror(res));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (state[0] == PW_IMPL_PORT_STATE_CONFIGURE && state[1] == PW_IMPL_PORT_STATE_CONFIGURE) {
|
||||||
|
again:
|
||||||
|
/* both ports need a format, we start with a format from port 0 and use that
|
||||||
|
* as a filter for port 1. Because the filter has higher priority, its
|
||||||
|
* defaults will be prefered. */
|
||||||
|
pw_log_debug("%p: do enum %s %d", this, dir[0], idx[0]);
|
||||||
|
spa_pod_builder_init(&fb, fbuf, sizeof(fbuf));
|
||||||
|
if ((res = spa_node_port_enum_params_sync(node[0],
|
||||||
|
info[0]->port->direction, port_id[0],
|
||||||
|
SPA_PARAM_EnumFormat, &idx[0],
|
||||||
|
NULL, &filter, &fb)) != 1) {
|
||||||
|
if (res == -ENOENT) {
|
||||||
|
pw_log_debug("%p: no %s filter", this, dir[0]);
|
||||||
|
filter = NULL;
|
||||||
|
} else {
|
||||||
|
if (res < 0)
|
||||||
|
*error = spa_aprintf("error %s enum formats: %s", dir[0],
|
||||||
|
spa_strerror(res));
|
||||||
|
else
|
||||||
|
*error = spa_aprintf("no more %s formats", dir[0]);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pw_log_debug("%p: enum %s %d with filter: %p", this, dir[1], idx[1], filter);
|
||||||
|
pw_log_pod(SPA_LOG_LEVEL_DEBUG, filter);
|
||||||
|
|
||||||
|
if ((res = spa_node_port_enum_params_sync(node[1],
|
||||||
|
info[1]->port->direction, port_id[1],
|
||||||
|
SPA_PARAM_EnumFormat, &idx[1],
|
||||||
|
filter, format, builder)) != 1) {
|
||||||
|
if (res == 0 && filter != NULL) {
|
||||||
|
idx[1] = 0;
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
*error = spa_aprintf("error %s enum formats: %s", dir[1], spa_strerror(res));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
pw_log_debug("%p: Got filtered:", this);
|
||||||
|
pw_log_pod(SPA_LOG_LEVEL_DEBUG, *format);
|
||||||
|
} else {
|
||||||
|
res = -EBADF;
|
||||||
|
*error = spa_aprintf("error bad node state");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
error:
|
||||||
|
if (res == 0)
|
||||||
|
res = -EINVAL;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static int do_negotiate(struct pw_impl_link *this)
|
static int do_negotiate(struct pw_impl_link *this)
|
||||||
{
|
{
|
||||||
struct pw_context *context = this->context;
|
struct pw_context *context = this->context;
|
||||||
|
|
@ -265,65 +394,71 @@ static int do_negotiate(struct pw_impl_link *this)
|
||||||
int res = -EIO, res2;
|
int res = -EIO, res2;
|
||||||
struct spa_pod *format = NULL, *current;
|
struct spa_pod *format = NULL, *current;
|
||||||
char *error = NULL;
|
char *error = NULL;
|
||||||
bool changed = true;
|
bool changed = false;
|
||||||
struct pw_impl_port *input, *output;
|
struct port_info *info[2];
|
||||||
uint8_t buffer[4096];
|
uint8_t buffer[4096];
|
||||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||||
uint32_t index, busy_id;
|
uint32_t index, busy_id;
|
||||||
uint32_t in_state, out_state;
|
uint32_t state[2];
|
||||||
struct spa_node *in_node, *out_node;
|
struct spa_node *node[2];
|
||||||
uint32_t in_port, out_port;
|
uint32_t port_id[2];
|
||||||
|
const char *dir[2];
|
||||||
|
|
||||||
if (this->info.state >= PW_LINK_STATE_NEGOTIATING)
|
if (this->info.state >= PW_LINK_STATE_NEGOTIATING)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
input = this->input;
|
/* driver nodes have lower priority for selecting the format.
|
||||||
output = this->output;
|
* Higher priority nodes go into info[0] */
|
||||||
|
if (this->output->node->driver) {
|
||||||
|
info[0] = &impl->input;
|
||||||
|
info[1] = &impl->output;
|
||||||
|
} else {
|
||||||
|
info[0] = &impl->output;
|
||||||
|
info[1] = &impl->input;
|
||||||
|
}
|
||||||
|
state[0] = info[0]->port->state;
|
||||||
|
state[1] = info[1]->port->state;
|
||||||
|
|
||||||
in_state = input->state;
|
dir[0] = pw_direction_as_string(info[0]->port->direction),
|
||||||
out_state = output->state;
|
dir[1] = pw_direction_as_string(info[1]->port->direction),
|
||||||
|
|
||||||
pw_log_debug("%p: in_state:%d out_state:%d", this, in_state, out_state);
|
pw_log_info("%p: %s:%d -> %s:%d", this, dir[0], state[0], dir[1], state[1]);
|
||||||
|
|
||||||
if (in_state != PW_IMPL_PORT_STATE_CONFIGURE && out_state != PW_IMPL_PORT_STATE_CONFIGURE)
|
if (state[0] != PW_IMPL_PORT_STATE_CONFIGURE && state[1] != PW_IMPL_PORT_STATE_CONFIGURE)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
link_update_state(this, PW_LINK_STATE_NEGOTIATING, 0, NULL);
|
link_update_state(this, PW_LINK_STATE_NEGOTIATING, 0, NULL);
|
||||||
|
|
||||||
input = this->input;
|
|
||||||
output = this->output;
|
|
||||||
#if 0
|
#if 0
|
||||||
in_node = input->mix;
|
node[0] = info[0]->port->mix;
|
||||||
in_port = this->rt.in_mix.port.port_id;
|
port_id[0] = this->rt.in_mix.port.port_id;
|
||||||
out_node = output->mix;
|
node[1] = info[1]->port->mix;
|
||||||
out_port = this->rt.out_mix.port.port_id;
|
port_id[1] = this->rt.out_mix.port.port_id;
|
||||||
#else
|
#else
|
||||||
in_node = input->node->node;
|
node[0] = info[0]->node->node;
|
||||||
in_port = input->port_id;
|
port_id[0] = info[0]->port->port_id;
|
||||||
out_node = output->node->node;
|
node[1] = info[1]->node->node;
|
||||||
out_port = output->port_id;
|
port_id[1] = info[1]->port->port_id;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* find a common format for the ports */
|
/* find a common format for the ports */
|
||||||
if ((res = pw_context_find_format(context,
|
if ((res = link_find_format(this, info, port_id, &format, &b, &error)) < 0) {
|
||||||
output, SPA_ID_INVALID,
|
|
||||||
input, SPA_ID_INVALID,
|
|
||||||
NULL, 0, NULL,
|
|
||||||
&format, &b, &error)) < 0) {
|
|
||||||
format = NULL;
|
format = NULL;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
format = spa_pod_copy(format);
|
format = spa_pod_copy(format);
|
||||||
|
pw_log_pod(SPA_LOG_LEVEL_DEBUG, format);
|
||||||
spa_pod_fixate(format);
|
spa_pod_fixate(format);
|
||||||
|
pw_log_pod(SPA_LOG_LEVEL_DEBUG, format);
|
||||||
|
|
||||||
spa_pod_builder_init(&b, buffer, sizeof(buffer));
|
spa_pod_builder_init(&b, buffer, sizeof(buffer));
|
||||||
|
|
||||||
/* if output port had format and is idle, check if it changed. If so, renegotiate */
|
/* if port 1 had format and is idle, check if it changed. If so, renegotiate */
|
||||||
if (out_state > PW_IMPL_PORT_STATE_CONFIGURE && output->node->info.state == PW_NODE_STATE_IDLE) {
|
if (state[1] > PW_IMPL_PORT_STATE_CONFIGURE && info[1]->node->info.state == PW_NODE_STATE_IDLE) {
|
||||||
index = 0;
|
index = 0;
|
||||||
res = spa_node_port_enum_params_sync(out_node,
|
res = spa_node_port_enum_params_sync(node[1],
|
||||||
output->direction, out_port,
|
info[1]->port->direction, port_id[1],
|
||||||
SPA_PARAM_Format, &index,
|
SPA_PARAM_Format, &index,
|
||||||
NULL, ¤t, &b);
|
NULL, ¤t, &b);
|
||||||
switch (res) {
|
switch (res) {
|
||||||
|
|
@ -337,27 +472,29 @@ static int do_negotiate(struct pw_impl_link *this)
|
||||||
res = -EBADF;
|
res = -EBADF;
|
||||||
SPA_FALLTHROUGH
|
SPA_FALLTHROUGH
|
||||||
default:
|
default:
|
||||||
error = spa_aprintf("error get output format: %s", spa_strerror(res));
|
error = spa_aprintf("error get %s format: %s",
|
||||||
|
pw_direction_as_string(info[1]->port->direction),
|
||||||
|
spa_strerror(res));
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (current == NULL || spa_pod_compare(current, format) != 0) {
|
if (current == NULL || spa_pod_compare(current, format) != 0) {
|
||||||
pw_log_debug("%p: output format change, renegotiate", this);
|
pw_log_debug("%p: %s format change, renegotiate", this,
|
||||||
|
pw_direction_as_string(info[1]->port->direction));
|
||||||
if (current)
|
if (current)
|
||||||
pw_log_pod(SPA_LOG_LEVEL_DEBUG, current);
|
pw_log_pod(SPA_LOG_LEVEL_DEBUG, current);
|
||||||
pw_log_pod(SPA_LOG_LEVEL_DEBUG, format);
|
pw_log_pod(SPA_LOG_LEVEL_DEBUG, format);
|
||||||
pw_impl_node_set_state(output->node, PW_NODE_STATE_SUSPENDED);
|
pw_impl_node_set_state(info[1]->node, PW_NODE_STATE_SUSPENDED);
|
||||||
out_state = PW_IMPL_PORT_STATE_CONFIGURE;
|
state[1] = PW_IMPL_PORT_STATE_CONFIGURE;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pw_log_debug("%p: format was already set", this);
|
pw_log_debug("%p: format was already set", this);
|
||||||
changed = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* if input port had format and is idle, check if it changed. If so, renegotiate */
|
/* if port 0 had format and is idle, check if it changed. If so, renegotiate */
|
||||||
if (in_state > PW_IMPL_PORT_STATE_CONFIGURE && input->node->info.state == PW_NODE_STATE_IDLE) {
|
if (state[0] > PW_IMPL_PORT_STATE_CONFIGURE && info[0]->node->info.state == PW_NODE_STATE_IDLE) {
|
||||||
index = 0;
|
index = 0;
|
||||||
res = spa_node_port_enum_params_sync(in_node,
|
res = spa_node_port_enum_params_sync(node[0],
|
||||||
input->direction, in_port,
|
info[0]->port->direction, port_id[0],
|
||||||
SPA_PARAM_Format, &index,
|
SPA_PARAM_Format, &index,
|
||||||
NULL, ¤t, &b);
|
NULL, ¤t, &b);
|
||||||
switch (res) {
|
switch (res) {
|
||||||
|
|
@ -371,20 +508,22 @@ static int do_negotiate(struct pw_impl_link *this)
|
||||||
res = -EBADF;
|
res = -EBADF;
|
||||||
SPA_FALLTHROUGH
|
SPA_FALLTHROUGH
|
||||||
default:
|
default:
|
||||||
error = spa_aprintf("error get input format: %s", spa_strerror(res));
|
error = spa_aprintf("error get %s format: %s",
|
||||||
|
pw_direction_as_string(info[0]->port->direction),
|
||||||
|
spa_strerror(res));
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (current == NULL || spa_pod_compare(current, format) != 0) {
|
if (current == NULL || spa_pod_compare(current, format) != 0) {
|
||||||
pw_log_debug("%p: input format change, renegotiate", this);
|
pw_log_debug("%p: %s format change, renegotiate", this,
|
||||||
|
pw_direction_as_string(info[0]->port->direction));
|
||||||
if (current)
|
if (current)
|
||||||
pw_log_pod(SPA_LOG_LEVEL_DEBUG, current);
|
pw_log_pod(SPA_LOG_LEVEL_DEBUG, current);
|
||||||
pw_log_pod(SPA_LOG_LEVEL_DEBUG, format);
|
pw_log_pod(SPA_LOG_LEVEL_DEBUG, format);
|
||||||
pw_impl_node_set_state(input->node, PW_NODE_STATE_SUSPENDED);
|
pw_impl_node_set_state(info[0]->node, PW_NODE_STATE_SUSPENDED);
|
||||||
in_state = PW_IMPL_PORT_STATE_CONFIGURE;
|
state[0] = PW_IMPL_PORT_STATE_CONFIGURE;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pw_log_debug("%p: format was already set", this);
|
pw_log_debug("%p: format was already set", this);
|
||||||
changed = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -393,47 +532,51 @@ static int do_negotiate(struct pw_impl_link *this)
|
||||||
format, spa_pod_is_fixated(format));
|
format, spa_pod_is_fixated(format));
|
||||||
pw_log_pod(SPA_LOG_LEVEL_INFO, format);
|
pw_log_pod(SPA_LOG_LEVEL_INFO, format);
|
||||||
|
|
||||||
if (out_state == PW_IMPL_PORT_STATE_CONFIGURE) {
|
if (state[1] == PW_IMPL_PORT_STATE_CONFIGURE) {
|
||||||
pw_log_debug("%p: doing set format on output", this);
|
pw_log_debug("%p: doing set format on %s", this, dir[1]);
|
||||||
if ((res = pw_impl_port_set_param(output,
|
if ((res = pw_impl_port_set_param(info[1]->port,
|
||||||
SPA_PARAM_Format, 0,
|
SPA_PARAM_Format, 0,
|
||||||
format)) < 0) {
|
format)) < 0) {
|
||||||
error = spa_aprintf("error set output format: %d (%s)", res, spa_strerror(res));
|
error = spa_aprintf("error set %s format: %d (%s)", dir[1],
|
||||||
pw_log_error("tried to set output format:");
|
res, spa_strerror(res));
|
||||||
|
pw_log_error("tried to set %s format:", dir[1]);
|
||||||
pw_log_pod(SPA_LOG_LEVEL_ERROR, format);
|
pw_log_pod(SPA_LOG_LEVEL_ERROR, format);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
pw_log_debug("%s set format: %d", dir[1], res);
|
||||||
if (SPA_RESULT_IS_ASYNC(res)) {
|
if (SPA_RESULT_IS_ASYNC(res)) {
|
||||||
pw_log_debug("output set format %d", res);
|
busy_id = pw_work_queue_add(impl->work, info[1],
|
||||||
busy_id = pw_work_queue_add(impl->work, &this->output_link,
|
spa_node_sync(info[1]->node->node, res),
|
||||||
spa_node_sync(output->node->node, res),
|
|
||||||
complete_ready, this);
|
complete_ready, this);
|
||||||
output_set_busy_id(this, busy_id, res);
|
port_set_busy_id(this, info[1], busy_id, res);
|
||||||
} else {
|
} else {
|
||||||
complete_ready(&this->output_link, this, res, SPA_ID_INVALID);
|
complete_ready(info[1], this, res, SPA_ID_INVALID);
|
||||||
}
|
}
|
||||||
|
changed = true;
|
||||||
}
|
}
|
||||||
if (in_state == PW_IMPL_PORT_STATE_CONFIGURE) {
|
if (state[0] == PW_IMPL_PORT_STATE_CONFIGURE) {
|
||||||
pw_log_debug("%p: doing set format on input", this);
|
pw_log_debug("%p: doing set format on %s", this, dir[0]);
|
||||||
if ((res2 = pw_impl_port_set_param(input,
|
if ((res2 = pw_impl_port_set_param(info[0]->port,
|
||||||
SPA_PARAM_Format, 0,
|
SPA_PARAM_Format, 0,
|
||||||
format)) < 0) {
|
format)) < 0) {
|
||||||
error = spa_aprintf("error set input format: %d (%s)", res2, spa_strerror(res2));
|
error = spa_aprintf("error set %s format: %d (%s)", dir[0],
|
||||||
pw_log_error("tried to set input format:");
|
res2, spa_strerror(res2));
|
||||||
|
pw_log_error("tried to set %s format:", dir[0]);
|
||||||
pw_log_pod(SPA_LOG_LEVEL_ERROR, format);
|
pw_log_pod(SPA_LOG_LEVEL_ERROR, format);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
pw_log_debug("%s set format: %d", dir[0], res2);
|
||||||
if (SPA_RESULT_IS_ASYNC(res2)) {
|
if (SPA_RESULT_IS_ASYNC(res2)) {
|
||||||
pw_log_debug("input set format %d", res2);
|
busy_id = pw_work_queue_add(impl->work, info[0],
|
||||||
busy_id = pw_work_queue_add(impl->work, &this->input_link,
|
spa_node_sync(info[0]->node->node, res2),
|
||||||
spa_node_sync(input->node->node, res2),
|
|
||||||
complete_ready, this);
|
complete_ready, this);
|
||||||
input_set_busy_id(this, busy_id, res2);
|
port_set_busy_id(this, info[0], busy_id, res2);
|
||||||
if (res == 0)
|
if (res == 0)
|
||||||
res = res2;
|
res = res2;
|
||||||
} else {
|
} else {
|
||||||
complete_ready(&this->input_link, this, res2, SPA_ID_INVALID);
|
complete_ready(info[0], this, res2, SPA_ID_INVALID);
|
||||||
}
|
}
|
||||||
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(this->info.format);
|
free(this->info.format);
|
||||||
|
|
@ -446,21 +589,23 @@ static int do_negotiate(struct pw_impl_link *this)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
pw_context_debug_port_params(context, in_node,
|
pw_context_debug_port_params(context, node[0],
|
||||||
input->direction, in_port,
|
info[0]->port->direction, port_id[0],
|
||||||
SPA_PARAM_EnumFormat, res, "input format (%s)", error);
|
SPA_PARAM_EnumFormat, res, "input format (%s)", error);
|
||||||
pw_context_debug_port_params(context, out_node,
|
pw_context_debug_port_params(context, node[1],
|
||||||
output->direction, out_port,
|
info[1]->port->direction, port_id[1],
|
||||||
SPA_PARAM_EnumFormat, res, "output format (%s)", error);
|
SPA_PARAM_EnumFormat, res, "output format (%s)", error);
|
||||||
link_update_state(this, PW_LINK_STATE_ERROR, res, error);
|
link_update_state(this, PW_LINK_STATE_ERROR, res, error);
|
||||||
free(format);
|
free(format);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int port_set_io(struct pw_impl_link *this, struct pw_impl_port *port, uint32_t id,
|
static int port_set_io(struct pw_impl_link *this, struct port_info *info, uint32_t id,
|
||||||
void *data, size_t size, struct pw_impl_port_mix *mix)
|
void *data, size_t size)
|
||||||
{
|
{
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
struct pw_impl_port *port = info->port;
|
||||||
|
struct pw_impl_port_mix *mix = info->mix;
|
||||||
|
|
||||||
pw_log_debug("%p: %s port %p %d.%d set io: %d %p %zd", this,
|
pw_log_debug("%p: %s port %p %d.%d set io: %d %p %zd", this,
|
||||||
pw_direction_as_string(port->direction),
|
pw_direction_as_string(port->direction),
|
||||||
|
|
@ -590,14 +735,14 @@ static int do_allocation(struct pw_impl_link *this)
|
||||||
goto error_clear;
|
goto error_clear;
|
||||||
}
|
}
|
||||||
if (SPA_RESULT_IS_ASYNC(res)) {
|
if (SPA_RESULT_IS_ASYNC(res)) {
|
||||||
busy_id = pw_work_queue_add(impl->work, &this->output_link,
|
busy_id = pw_work_queue_add(impl->work, &impl->output,
|
||||||
spa_node_sync(output->node->node, res),
|
spa_node_sync(output->node->node, res),
|
||||||
complete_paused, this);
|
complete_paused, this);
|
||||||
output_set_busy_id(this, busy_id, res);
|
port_set_busy_id(this, &impl->output, busy_id, res);
|
||||||
if (flags & SPA_NODE_BUFFERS_FLAG_ALLOC)
|
if (flags & SPA_NODE_BUFFERS_FLAG_ALLOC)
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
complete_paused(&this->output_link, this, res, SPA_ID_INVALID);
|
complete_paused(&impl->output, this, res, SPA_ID_INVALID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -613,12 +758,12 @@ static int do_allocation(struct pw_impl_link *this)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SPA_RESULT_IS_ASYNC(res)) {
|
if (SPA_RESULT_IS_ASYNC(res)) {
|
||||||
busy_id = pw_work_queue_add(impl->work, &this->input_link,
|
busy_id = pw_work_queue_add(impl->work, &impl->input,
|
||||||
spa_node_sync(input->node->node, res),
|
spa_node_sync(input->node->node, res),
|
||||||
complete_paused, this);
|
complete_paused, this);
|
||||||
input_set_busy_id(this, busy_id, res);
|
port_set_busy_id(this, &impl->input, busy_id, res);
|
||||||
} else {
|
} else {
|
||||||
complete_paused(&this->input_link, this, res, SPA_ID_INVALID);
|
complete_paused(&impl->input, this, res, SPA_ID_INVALID);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -639,7 +784,7 @@ int pw_impl_link_activate(struct pw_impl_link *this)
|
||||||
pw_link_state_as_string(this->info.state));
|
pw_link_state_as_string(this->info.state));
|
||||||
|
|
||||||
if (this->destroyed || impl->activated || !this->prepared ||
|
if (this->destroyed || impl->activated || !this->prepared ||
|
||||||
!impl->inode->runnable || !impl->onode->runnable)
|
!impl->input.node->runnable || !impl->output.node->runnable)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (impl->async) {
|
if (impl->async) {
|
||||||
|
|
@ -650,11 +795,9 @@ int pw_impl_link_activate(struct pw_impl_link *this)
|
||||||
io_size = sizeof(struct spa_io_buffers);
|
io_size = sizeof(struct spa_io_buffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((res = port_set_io(this, this->input, io_type, this->io,
|
if ((res = port_set_io(this, &impl->input, io_type, this->io, io_size)) < 0)
|
||||||
io_size, &this->rt.in_mix)) < 0)
|
|
||||||
goto error;
|
goto error;
|
||||||
if ((res = port_set_io(this, this->output, io_type, this->io,
|
if ((res = port_set_io(this, &impl->output, io_type, this->io, io_size)) < 0)
|
||||||
io_size, &this->rt.out_mix)) < 0)
|
|
||||||
goto error_clean;
|
goto error_clean;
|
||||||
|
|
||||||
impl->activated = true;
|
impl->activated = true;
|
||||||
|
|
@ -664,7 +807,7 @@ int pw_impl_link_activate(struct pw_impl_link *this)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error_clean:
|
error_clean:
|
||||||
port_set_io(this, this->input, io_type, NULL, 0, &this->rt.in_mix);
|
port_set_io(this, &impl->input, io_type, NULL, 0);
|
||||||
error:
|
error:
|
||||||
pw_log_error("%p: can't activate link: %s", this, spa_strerror(res));
|
pw_log_error("%p: can't activate link: %s", this, spa_strerror(res));
|
||||||
return res;
|
return res;
|
||||||
|
|
@ -719,13 +862,13 @@ static void check_states(void *obj, void *user_data, int res, uint32_t id)
|
||||||
if (output->busy_count > 0) {
|
if (output->busy_count > 0) {
|
||||||
pw_log_debug("%p: output port %p was busy %d", this, output, output->busy_count);
|
pw_log_debug("%p: output port %p was busy %d", this, output, output->busy_count);
|
||||||
res = spa_node_sync(output->node->node, 0);
|
res = spa_node_sync(output->node->node, 0);
|
||||||
pw_work_queue_add(impl->work, &this->output_link, res, complete_sync, this);
|
pw_work_queue_add(impl->work, &impl->output, res, complete_sync, this);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
else if (input->busy_count > 0) {
|
else if (input->busy_count > 0) {
|
||||||
pw_log_debug("%p: input port %p was busy %d", this, input, input->busy_count);
|
pw_log_debug("%p: input port %p was busy %d", this, input, input->busy_count);
|
||||||
res = spa_node_sync(input->node->node, 0);
|
res = spa_node_sync(input->node->node, 0);
|
||||||
pw_work_queue_add(impl->work, &this->input_link, res, complete_sync, this);
|
pw_work_queue_add(impl->work, &impl->input, res, complete_sync, this);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -745,61 +888,65 @@ exit:
|
||||||
this, -EBUSY, (pw_work_func_t) check_states, this);
|
this, -EBUSY, (pw_work_func_t) check_states, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void input_remove(struct pw_impl_link *this, struct pw_impl_port *port)
|
static void input_remove(struct pw_impl_link *this)
|
||||||
{
|
{
|
||||||
struct impl *impl = (struct impl *) this;
|
struct impl *impl = (struct impl *) this;
|
||||||
struct pw_impl_port_mix *mix = &this->rt.in_mix;
|
struct port_info *info = &impl->input;
|
||||||
|
struct pw_impl_port_mix *mix = info->mix;
|
||||||
|
struct pw_impl_port *port = info->port;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
pw_log_debug("%p: remove input port %p", this, port);
|
pw_log_debug("%p: remove input port %p", this, port);
|
||||||
|
|
||||||
input_set_busy_id(this, SPA_ID_INVALID, SPA_ID_INVALID);
|
port_set_busy_id(this, info, SPA_ID_INVALID, SPA_ID_INVALID);
|
||||||
|
|
||||||
spa_hook_remove(&impl->input_port_listener);
|
spa_hook_remove(&info->port_listener);
|
||||||
spa_hook_remove(&impl->input_node_listener);
|
spa_hook_remove(&info->node_listener);
|
||||||
spa_hook_remove(&impl->input_global_listener);
|
spa_hook_remove(&info->global_listener);
|
||||||
|
|
||||||
spa_list_remove(&this->input_link);
|
spa_list_remove(&this->input_link);
|
||||||
pw_impl_port_emit_link_removed(this->input, this);
|
pw_impl_port_emit_link_removed(port, this);
|
||||||
|
|
||||||
pw_impl_port_recalc_latency(this->input);
|
pw_impl_port_recalc_latency(port);
|
||||||
pw_impl_port_recalc_tag(this->input);
|
pw_impl_port_recalc_tag(port);
|
||||||
|
|
||||||
if ((res = port_set_io(this, this->input, SPA_IO_Buffers, NULL, 0, mix)) < 0)
|
if ((res = port_set_io(this, info, SPA_IO_Buffers, NULL, 0)) < 0)
|
||||||
pw_log_warn("%p: port %p set_io error %s", this, port, spa_strerror(res));
|
pw_log_warn("%p: port %p set_io error %s", this, port, spa_strerror(res));
|
||||||
if ((res = pw_impl_port_use_buffers(port, mix, 0, NULL, 0)) < 0)
|
if ((res = pw_impl_port_use_buffers(port, mix, 0, NULL, 0)) < 0)
|
||||||
pw_log_warn("%p: port %p clear error %s", this, port, spa_strerror(res));
|
pw_log_warn("%p: port %p clear error %s", this, port, spa_strerror(res));
|
||||||
|
|
||||||
pw_impl_port_release_mix(port, mix);
|
pw_impl_port_release_mix(port, mix);
|
||||||
|
|
||||||
pw_work_queue_cancel(impl->work, &this->input_link, SPA_ID_INVALID);
|
pw_work_queue_cancel(impl->work, info, SPA_ID_INVALID);
|
||||||
this->input = NULL;
|
this->input = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_remove(struct pw_impl_link *this, struct pw_impl_port *port)
|
static void output_remove(struct pw_impl_link *this)
|
||||||
{
|
{
|
||||||
struct impl *impl = (struct impl *) this;
|
struct impl *impl = (struct impl *) this;
|
||||||
struct pw_impl_port_mix *mix = &this->rt.out_mix;
|
struct port_info *info = &impl->output;
|
||||||
|
struct pw_impl_port_mix *mix = info->mix;
|
||||||
|
struct pw_impl_port *port = info->port;
|
||||||
|
|
||||||
pw_log_debug("%p: remove output port %p", this, port);
|
pw_log_debug("%p: remove output port %p", this, port);
|
||||||
|
|
||||||
output_set_busy_id(this, SPA_ID_INVALID, SPA_ID_INVALID);
|
port_set_busy_id(this, info, SPA_ID_INVALID, SPA_ID_INVALID);
|
||||||
|
|
||||||
spa_hook_remove(&impl->output_port_listener);
|
spa_hook_remove(&info->port_listener);
|
||||||
spa_hook_remove(&impl->output_node_listener);
|
spa_hook_remove(&info->node_listener);
|
||||||
spa_hook_remove(&impl->output_global_listener);
|
spa_hook_remove(&info->global_listener);
|
||||||
|
|
||||||
spa_list_remove(&this->output_link);
|
spa_list_remove(&this->output_link);
|
||||||
pw_impl_port_emit_link_removed(this->output, this);
|
pw_impl_port_emit_link_removed(port, this);
|
||||||
|
|
||||||
pw_impl_port_recalc_latency(this->output);
|
pw_impl_port_recalc_latency(port);
|
||||||
pw_impl_port_recalc_tag(this->output);
|
pw_impl_port_recalc_tag(port);
|
||||||
|
|
||||||
/* we don't clear output buffers when the link goes away. They will get
|
/* we don't clear output buffers when the link goes away. They will get
|
||||||
* cleared when the node goes to suspend */
|
* cleared when the node goes to suspend */
|
||||||
pw_impl_port_release_mix(port, mix);
|
pw_impl_port_release_mix(port, mix);
|
||||||
|
|
||||||
pw_work_queue_cancel(impl->work, &this->output_link, SPA_ID_INVALID);
|
pw_work_queue_cancel(impl->work, info, SPA_ID_INVALID);
|
||||||
this->output = NULL;
|
this->output = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -809,9 +956,9 @@ int pw_impl_link_prepare(struct pw_impl_link *this)
|
||||||
|
|
||||||
pw_log_debug("%p: prepared:%d preparing:%d in_active:%d out_active:%d passive:%u",
|
pw_log_debug("%p: prepared:%d preparing:%d in_active:%d out_active:%d passive:%u",
|
||||||
this, this->prepared, this->preparing,
|
this, this->prepared, this->preparing,
|
||||||
impl->inode->active, impl->onode->active, this->passive);
|
impl->input.node->active, impl->output.node->active, this->passive);
|
||||||
|
|
||||||
if (!impl->inode->active || !impl->onode->active)
|
if (!impl->input.node->active || !impl->output.node->active)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (this->destroyed || this->preparing || this->prepared)
|
if (this->destroyed || this->preparing || this->prepared)
|
||||||
|
|
@ -834,10 +981,8 @@ int pw_impl_link_deactivate(struct pw_impl_link *this)
|
||||||
if (!impl->activated)
|
if (!impl->activated)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
port_set_io(this, this->output, SPA_IO_Buffers, NULL, 0,
|
port_set_io(this, &impl->output, SPA_IO_Buffers, NULL, 0);
|
||||||
&this->rt.out_mix);
|
port_set_io(this, &impl->input, SPA_IO_Buffers, NULL, 0);
|
||||||
port_set_io(this, this->input, SPA_IO_Buffers, NULL, 0,
|
|
||||||
&this->rt.in_mix);
|
|
||||||
|
|
||||||
impl->activated = false;
|
impl->activated = false;
|
||||||
pw_log_info("(%s) deactivated", this->name);
|
pw_log_info("(%s) deactivated", this->name);
|
||||||
|
|
@ -1010,6 +1155,16 @@ static const struct pw_impl_port_events output_port_events = {
|
||||||
static void node_result(struct impl *impl, void *obj,
|
static void node_result(struct impl *impl, void *obj,
|
||||||
int seq, int res, uint32_t type, const void *result)
|
int seq, int res, uint32_t type, const void *result)
|
||||||
{
|
{
|
||||||
|
struct port_info *info = obj;
|
||||||
|
struct pw_impl_port *port = info->port;
|
||||||
|
|
||||||
|
pw_log_trace("%p: %s port %p result seq:%d %d res:%d type:%u",
|
||||||
|
impl, pw_direction_as_string(port->direction),
|
||||||
|
port, seq, SPA_RESULT_ASYNC_SEQ(seq), res, type);
|
||||||
|
|
||||||
|
if (type == SPA_RESULT_TYPE_NODE_ERROR && info->pending_seq == seq)
|
||||||
|
info->result = res;
|
||||||
|
|
||||||
if (SPA_RESULT_IS_ASYNC(seq))
|
if (SPA_RESULT_IS_ASYNC(seq))
|
||||||
pw_work_queue_complete(impl->work, obj, SPA_RESULT_ASYNC_SEQ(seq), res);
|
pw_work_queue_complete(impl->work, obj, SPA_RESULT_ASYNC_SEQ(seq), res);
|
||||||
}
|
}
|
||||||
|
|
@ -1017,27 +1172,13 @@ static void node_result(struct impl *impl, void *obj,
|
||||||
static void input_node_result(void *data, int seq, int res, uint32_t type, const void *result)
|
static void input_node_result(void *data, int seq, int res, uint32_t type, const void *result)
|
||||||
{
|
{
|
||||||
struct impl *impl = data;
|
struct impl *impl = data;
|
||||||
struct pw_impl_port *port = impl->this.input;
|
node_result(impl, &impl->input, seq, res, type, result);
|
||||||
pw_log_trace("%p: input port %p result seq:%d %d res:%d type:%u",
|
|
||||||
impl, port, seq, SPA_RESULT_ASYNC_SEQ(seq), res, type);
|
|
||||||
|
|
||||||
if (type == SPA_RESULT_TYPE_NODE_ERROR && impl->input_pending_seq == seq)
|
|
||||||
impl->input_result = res;
|
|
||||||
|
|
||||||
node_result(impl, &impl->this.input_link, seq, res, type, result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_node_result(void *data, int seq, int res, uint32_t type, const void *result)
|
static void output_node_result(void *data, int seq, int res, uint32_t type, const void *result)
|
||||||
{
|
{
|
||||||
struct impl *impl = data;
|
struct impl *impl = data;
|
||||||
struct pw_impl_port *port = impl->this.output;
|
node_result(impl, &impl->output, seq, res, type, result);
|
||||||
pw_log_trace("%p: output port %p result seq:%d %d res:%d type:%u",
|
|
||||||
impl, port, seq, SPA_RESULT_ASYNC_SEQ(seq), res, type);
|
|
||||||
|
|
||||||
if (type == SPA_RESULT_TYPE_NODE_ERROR && impl->output_pending_seq == seq)
|
|
||||||
impl->output_result = res;
|
|
||||||
|
|
||||||
node_result(impl, &impl->this.output_link, seq, res, type, result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void node_active_changed(void *data, bool active)
|
static void node_active_changed(void *data, bool active)
|
||||||
|
|
@ -1312,8 +1453,8 @@ struct pw_impl_link *pw_context_create_link(struct pw_context *context,
|
||||||
if (impl == NULL)
|
if (impl == NULL)
|
||||||
goto error_no_mem;
|
goto error_no_mem;
|
||||||
|
|
||||||
impl->input_busy_id = SPA_ID_INVALID;
|
impl->input.busy_id = SPA_ID_INVALID;
|
||||||
impl->output_busy_id = SPA_ID_INVALID;
|
impl->output.busy_id = SPA_ID_INVALID;
|
||||||
|
|
||||||
this = &impl->this;
|
this = &impl->this;
|
||||||
this->feedback = pw_impl_node_can_reach(input_node, output_node, 0);
|
this->feedback = pw_impl_node_can_reach(input_node, output_node, 0);
|
||||||
|
|
@ -1363,12 +1504,12 @@ struct pw_impl_link *pw_context_create_link(struct pw_context *context,
|
||||||
if ((res = pw_impl_port_init_mix(input, &this->rt.in_mix)) < 0)
|
if ((res = pw_impl_port_init_mix(input, &this->rt.in_mix)) < 0)
|
||||||
goto error_input_mix;
|
goto error_input_mix;
|
||||||
|
|
||||||
pw_impl_port_add_listener(input, &impl->input_port_listener, &input_port_events, impl);
|
pw_impl_port_add_listener(input, &impl->input.port_listener, &input_port_events, impl);
|
||||||
pw_impl_node_add_listener(input_node, &impl->input_node_listener, &input_node_events, impl);
|
pw_impl_node_add_listener(input_node, &impl->input.node_listener, &input_node_events, impl);
|
||||||
pw_global_add_listener(input->global, &impl->input_global_listener, &input_global_events, impl);
|
pw_global_add_listener(input->global, &impl->input.global_listener, &input_global_events, impl);
|
||||||
pw_impl_port_add_listener(output, &impl->output_port_listener, &output_port_events, impl);
|
pw_impl_port_add_listener(output, &impl->output.port_listener, &output_port_events, impl);
|
||||||
pw_impl_node_add_listener(output_node, &impl->output_node_listener, &output_node_events, impl);
|
pw_impl_node_add_listener(output_node, &impl->output.node_listener, &output_node_events, impl);
|
||||||
pw_global_add_listener(output->global, &impl->output_global_listener, &output_global_events, impl);
|
pw_global_add_listener(output->global, &impl->output.global_listener, &output_global_events, impl);
|
||||||
|
|
||||||
input_node->live = output_node->live;
|
input_node->live = output_node->live;
|
||||||
|
|
||||||
|
|
@ -1380,14 +1521,18 @@ struct pw_impl_link *pw_context_create_link(struct pw_context *context,
|
||||||
|
|
||||||
select_io(this);
|
select_io(this);
|
||||||
|
|
||||||
|
impl->input.port = input;
|
||||||
|
impl->output.port = output;
|
||||||
if (this->feedback) {
|
if (this->feedback) {
|
||||||
impl->inode = output_node;
|
impl->input.node = output_node;
|
||||||
impl->onode = input_node;
|
impl->output.node = input_node;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
impl->onode = output_node;
|
impl->output.node = output_node;
|
||||||
impl->inode = input_node;
|
impl->input.node = input_node;
|
||||||
}
|
}
|
||||||
|
impl->input.mix = &this->rt.in_mix;
|
||||||
|
impl->output.mix = &this->rt.out_mix;
|
||||||
|
|
||||||
pw_log_debug("%p: constructed out:%p:%d.%d -> in:%p:%d.%d", impl,
|
pw_log_debug("%p: constructed out:%p:%d.%d -> in:%p:%d.%d", impl,
|
||||||
output_node, output->port_id, this->rt.out_mix.port.port_id,
|
output_node, output->port_id, this->rt.out_mix.port.port_id,
|
||||||
|
|
@ -1411,8 +1556,8 @@ struct pw_impl_link *pw_context_create_link(struct pw_context *context,
|
||||||
pw_impl_port_recalc_tag(output);
|
pw_impl_port_recalc_tag(output);
|
||||||
pw_impl_port_recalc_tag(input);
|
pw_impl_port_recalc_tag(input);
|
||||||
|
|
||||||
if (impl->onode != impl->inode)
|
if (impl->output.node != impl->input.node)
|
||||||
this->peer = pw_node_peer_ref(impl->onode, impl->inode);
|
this->peer = pw_node_peer_ref(impl->output.node, impl->input.node);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
|
||||||
|
|
@ -1555,8 +1700,8 @@ void pw_impl_link_destroy(struct pw_impl_link *link)
|
||||||
|
|
||||||
try_unlink_controls(impl, link->output, link->input);
|
try_unlink_controls(impl, link->output, link->input);
|
||||||
|
|
||||||
output_remove(link, link->output);
|
output_remove(link);
|
||||||
input_remove(link, link->input);
|
input_remove(link);
|
||||||
|
|
||||||
if (link->global) {
|
if (link->global) {
|
||||||
spa_hook_remove(&link->global_listener);
|
spa_hook_remove(&link->global_listener);
|
||||||
|
|
|
||||||
|
|
@ -1265,19 +1265,6 @@ struct pw_control {
|
||||||
void *user_data;
|
void *user_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Find a good format between 2 ports */
|
|
||||||
int pw_context_find_format(struct pw_context *context,
|
|
||||||
struct pw_impl_port *output,
|
|
||||||
uint32_t output_mix,
|
|
||||||
struct pw_impl_port *input,
|
|
||||||
uint32_t input_mix,
|
|
||||||
struct pw_properties *props,
|
|
||||||
uint32_t n_format_filters,
|
|
||||||
struct spa_pod **format_filters,
|
|
||||||
struct spa_pod **format,
|
|
||||||
struct spa_pod_builder *builder,
|
|
||||||
char **error);
|
|
||||||
|
|
||||||
int pw_context_debug_port_params(struct pw_context *context,
|
int pw_context_debug_port_params(struct pw_context *context,
|
||||||
struct spa_node *node, enum spa_direction direction,
|
struct spa_node *node, enum spa_direction direction,
|
||||||
uint32_t port_id, uint32_t id, int err, const char *debug, ...);
|
uint32_t port_id, uint32_t id, int err, const char *debug, ...);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue