videoadapter: sync with audioadapter

This commit is contained in:
Wim Taymans 2025-03-24 11:45:11 +01:00
parent 7e67daa292
commit 824354f38e

View file

@ -15,6 +15,7 @@
#include <spa/utils/names.h> #include <spa/utils/names.h>
#include <spa/utils/result.h> #include <spa/utils/result.h>
#include <spa/utils/string.h> #include <spa/utils/string.h>
#include <spa/utils/json.h>
#include <spa/buffer/alloc.h> #include <spa/buffer/alloc.h>
#include <spa/pod/parser.h> #include <spa/pod/parser.h>
#include <spa/pod/filter.h> #include <spa/pod/filter.h>
@ -97,16 +98,42 @@ struct impl {
unsigned int started:1; unsigned int started:1;
unsigned int ready:1; unsigned int ready:1;
unsigned int async:1; unsigned int async:1;
unsigned int passthrough:1; enum spa_param_port_config_mode mode;
unsigned int follower_removing:1; unsigned int follower_removing:1;
unsigned int in_recalc; unsigned int in_recalc;
unsigned int warned:1; unsigned int warned:1;
unsigned int driver:1; unsigned int driver:1;
int in_enum_sync;
}; };
/** \endcond */ /** \endcond */
static int node_enum_params_sync(struct impl *impl, struct spa_node *node,
uint32_t id, uint32_t *index, const struct spa_pod *filter,
struct spa_pod **param, struct spa_pod_builder *builder)
{
int res;
impl->in_enum_sync++;
res = spa_node_enum_params_sync(node, id, index, filter, param, builder);
impl->in_enum_sync--;
return res;
}
static int node_port_enum_params_sync(struct impl *impl, struct spa_node *node,
enum spa_direction direction, uint32_t port_id,
uint32_t id, uint32_t *index, const struct spa_pod *filter,
struct spa_pod **param, struct spa_pod_builder *builder)
{
int res;
impl->in_enum_sync++;
res = spa_node_port_enum_params_sync(node, direction, port_id, id, index,
filter, param, builder);
impl->in_enum_sync--;
return res;
}
static int follower_enum_params(struct impl *this, static int follower_enum_params(struct impl *this,
uint32_t id, uint32_t id,
uint32_t idx, uint32_t idx,
@ -117,14 +144,14 @@ static int follower_enum_params(struct impl *this,
int res; int res;
if (result->next < 0x100000 && if (result->next < 0x100000 &&
this->follower != this->target) { this->follower != this->target) {
if ((res = spa_node_enum_params_sync(this->target, if ((res = node_enum_params_sync(this, this->target,
id, &result->next, filter, &result->param, builder)) == 1) id, &result->next, filter, &result->param, builder)) == 1)
return res; return res;
result->next = 0x100000; result->next = 0x100000;
} }
if (result->next < 0x200000 && this->follower_params_flags[idx] & SPA_PARAM_INFO_READ) { if (result->next < 0x200000 && this->follower_params_flags[idx] & SPA_PARAM_INFO_READ) {
result->next &= 0xfffff; result->next &= 0xfffff;
if ((res = spa_node_enum_params_sync(this->follower, if ((res = node_enum_params_sync(this, this->follower,
id, &result->next, filter, &result->param, builder)) == 1) { id, &result->next, filter, &result->param, builder)) == 1) {
result->next |= 0x100000; result->next |= 0x100000;
return res; return res;
@ -188,7 +215,7 @@ next:
switch (id) { switch (id) {
case SPA_PARAM_EnumPortConfig: case SPA_PARAM_EnumPortConfig:
case SPA_PARAM_PortConfig: case SPA_PARAM_PortConfig:
if (this->passthrough) { if (this->mode == SPA_PARAM_PORT_CONFIG_MODE_passthrough) {
switch (result.index) { switch (result.index) {
case 0: case 0:
result.param = spa_pod_builder_add_object(&b.b, result.param = spa_pod_builder_add_object(&b.b,
@ -222,7 +249,7 @@ next:
case SPA_PARAM_Format: case SPA_PARAM_Format:
case SPA_PARAM_Latency: case SPA_PARAM_Latency:
case SPA_PARAM_Tag: case SPA_PARAM_Tag:
res = spa_node_port_enum_params_sync(this->follower, res = node_port_enum_params_sync(this, this->follower,
this->direction, 0, this->direction, 0,
id, &result.next, filter, &result.param, &b.b); id, &result.next, filter, &result.param, &b.b);
break; break;
@ -373,7 +400,7 @@ static int debug_params(struct impl *this, struct spa_node *node,
state = 0; state = 0;
while (true) { while (true) {
spa_pod_builder_init(&b, buffer, sizeof(buffer)); spa_pod_builder_init(&b, buffer, sizeof(buffer));
res = spa_node_port_enum_params_sync(node, res = node_port_enum_params_sync(this, node,
direction, port_id, direction, port_id,
id, &state, id, &state,
NULL, &param, &b); NULL, &param, &b);
@ -415,7 +442,7 @@ static int negotiate_buffers(struct impl *this)
state = 0; state = 0;
param = NULL; param = NULL;
if ((res = spa_node_port_enum_params_sync(this->follower, if ((res = node_port_enum_params_sync(this, this->follower,
this->direction, 0, this->direction, 0,
SPA_PARAM_Buffers, &state, SPA_PARAM_Buffers, &state,
param, &param, &b)) < 0) { param, &param, &b)) < 0) {
@ -429,7 +456,7 @@ static int negotiate_buffers(struct impl *this)
} }
state = 0; state = 0;
if ((res = spa_node_port_enum_params_sync(this->target, if ((res = node_port_enum_params_sync(this, this->target,
SPA_DIRECTION_REVERSE(this->direction), 0, SPA_DIRECTION_REVERSE(this->direction), 0,
SPA_PARAM_Buffers, &state, SPA_PARAM_Buffers, &state,
param, &param, &b)) != 1) { param, &param, &b)) != 1) {
@ -544,7 +571,7 @@ static int configure_format(struct impl *this, uint32_t flags, const struct spa_
/* format was changed to nearest compatible format */ /* format was changed to nearest compatible format */
if ((res = spa_node_port_enum_params_sync(this->follower, if ((res = node_port_enum_params_sync(this, this->follower,
this->direction, 0, this->direction, 0,
SPA_PARAM_Format, &state, SPA_PARAM_Format, &state,
NULL, &fmt, &b)) != 1) NULL, &fmt, &b)) != 1)
@ -610,7 +637,7 @@ static int recalc_latency(struct impl *this, struct spa_node *src, enum spa_dire
while (true) { while (true) {
spa_pod_builder_init(&b, buffer, sizeof(buffer)); spa_pod_builder_init(&b, buffer, sizeof(buffer));
if ((res = spa_node_port_enum_params_sync(src, if ((res = node_port_enum_params_sync(this, src,
direction, port_id, SPA_PARAM_Latency, direction, port_id, SPA_PARAM_Latency,
&index, NULL, &param, &b)) != 1) { &index, NULL, &param, &b)) != 1) {
param = NULL; param = NULL;
@ -651,7 +678,7 @@ static int recalc_tag(struct impl *this, struct spa_node *src, enum spa_directio
while (true) { while (true) {
void *tag_state = NULL; void *tag_state = NULL;
spa_pod_builder_reset(&b.b, &state); spa_pod_builder_reset(&b.b, &state);
if ((res = spa_node_port_enum_params_sync(src, if ((res = node_port_enum_params_sync(this, src,
direction, port_id, SPA_PARAM_Tag, direction, port_id, SPA_PARAM_Tag,
&index, NULL, &param, &b.b)) != 1) { &index, NULL, &param, &b.b)) != 1) {
param = NULL; param = NULL;
@ -673,13 +700,14 @@ static int reconfigure_mode(struct impl *this, enum spa_param_port_config_mode m
int res = 0; int res = 0;
struct spa_hook l; struct spa_hook l;
bool passthrough = mode == SPA_PARAM_PORT_CONFIG_MODE_passthrough; bool passthrough = mode == SPA_PARAM_PORT_CONFIG_MODE_passthrough;
bool old_passthrough = this->mode == SPA_PARAM_PORT_CONFIG_MODE_passthrough;
spa_log_debug(this->log, "%p: passthrough mode %d", this, passthrough); spa_log_debug(this->log, "%p: passthrough mode %d", this, passthrough);
if (!passthrough && this->convert == NULL) if (!passthrough && this->convert == NULL)
return -ENOTSUP; return -ENOTSUP;
if (this->passthrough != passthrough) { if (old_passthrough != passthrough) {
if (passthrough) { if (passthrough) {
/* remove converter split/merge ports */ /* remove converter split/merge ports */
configure_convert(this, SPA_PARAM_PORT_CONFIG_MODE_none); configure_convert(this, SPA_PARAM_PORT_CONFIG_MODE_none);
@ -699,8 +727,9 @@ static int reconfigure_mode(struct impl *this, enum spa_param_port_config_mode m
if ((res = configure_format(this, SPA_NODE_PARAM_FLAG_NEAREST, format)) < 0) if ((res = configure_format(this, SPA_NODE_PARAM_FLAG_NEAREST, format)) < 0)
return res; return res;
if (this->passthrough != passthrough) { this->mode = mode;
this->passthrough = passthrough;
if (old_passthrough != passthrough) {
if (passthrough) { if (passthrough) {
/* add follower ports */ /* add follower ports */
spa_zero(l); spa_zero(l);
@ -888,6 +917,7 @@ static int negotiate_format(struct impl *this)
uint8_t buffer[4096]; uint8_t buffer[4096];
struct spa_pod_builder b = { 0 }; struct spa_pod_builder b = { 0 };
int res, fres; int res, fres;
struct spa_node *src, *dst;
spa_log_debug(this->log, "%p: have_format:%d recheck:%d", this, this->have_format, spa_log_debug(this->log, "%p: have_format:%d recheck:%d", this, this->have_format,
this->recheck_format); this->recheck_format);
@ -905,15 +935,24 @@ static int negotiate_format(struct impl *this)
spa_node_send_command(this->follower, spa_node_send_command(this->follower,
&SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_ParamBegin)); &SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_ParamBegin));
if (this->direction == SPA_DIRECTION_INPUT) {
src = this->target;
dst = this->follower;
} else {
src = this->follower;
dst = this->target;
}
/* first try the ideal converter format, which is likely passthrough */ /* first try the ideal converter format, which is likely passthrough */
tstate = 0; tstate = 0;
fres = spa_node_port_enum_params_sync(this->target, fres = node_port_enum_params_sync(this, this->target,
SPA_DIRECTION_REVERSE(this->direction), 0, SPA_DIRECTION_REVERSE(this->direction), 0,
SPA_PARAM_EnumFormat, &tstate, SPA_PARAM_EnumFormat, &tstate,
NULL, &format, &b); NULL, &format, &b);
if (fres == 1) { if (fres == 1) {
fstate = 0; fstate = 0;
res = spa_node_port_enum_params_sync(this->follower, res = node_port_enum_params_sync(this, this->follower,
this->direction, 0, this->direction, 0,
SPA_PARAM_EnumFormat, &fstate, SPA_PARAM_EnumFormat, &fstate,
format, &format, &b); format, &format, &b);
@ -924,8 +963,8 @@ static int negotiate_format(struct impl *this)
/* then try something the follower can accept */ /* then try something the follower can accept */
for (fstate = 0;;) { for (fstate = 0;;) {
format = NULL; format = NULL;
res = spa_node_port_enum_params_sync(this->follower, res = node_port_enum_params_sync(this, src,
this->direction, 0, SPA_DIRECTION_OUTPUT, 0,
SPA_PARAM_EnumFormat, &fstate, SPA_PARAM_EnumFormat, &fstate,
NULL, &format, &b); NULL, &format, &b);
@ -935,8 +974,8 @@ static int negotiate_format(struct impl *this)
break; break;
tstate = 0; tstate = 0;
fres = spa_node_port_enum_params_sync(this->target, fres = node_port_enum_params_sync(this, dst,
SPA_DIRECTION_REVERSE(this->direction), 0, SPA_DIRECTION_INPUT, 0,
SPA_PARAM_EnumFormat, &tstate, SPA_PARAM_EnumFormat, &tstate,
format, &format, &b); format, &format, &b);
if (fres == 0 && res == 1) if (fres == 0 && res == 1)
@ -1194,7 +1233,7 @@ static void convert_result(void *data, int seq, int res, uint32_t type, const vo
{ {
struct impl *this = data; struct impl *this = data;
if (this->target == this->follower) if (this->target == this->follower || this->in_enum_sync)
return; return;
spa_log_trace(this->log, "%p: result %d %d", this, seq, res); spa_log_trace(this->log, "%p: result %d %d", this, seq, res);
@ -1223,7 +1262,7 @@ static void follower_info(void *data, const struct spa_node_info *info)
if (info->max_input_ports > 0) if (info->max_input_ports > 0)
this->direction = SPA_DIRECTION_INPUT; this->direction = SPA_DIRECTION_INPUT;
else else
this->direction = SPA_DIRECTION_OUTPUT; this->direction = SPA_DIRECTION_OUTPUT;
if (this->direction == SPA_DIRECTION_INPUT) { if (this->direction == SPA_DIRECTION_INPUT) {
@ -1371,7 +1410,7 @@ static void follower_result(void *data, int seq, int res, uint32_t type, const v
{ {
struct impl *this = data; struct impl *this = data;
if (this->target != this->follower) if (this->target != this->follower || this->in_enum_sync)
return; return;
spa_log_trace(this->log, "%p: result %d %d", this, seq, res); spa_log_trace(this->log, "%p: result %d %d", this, seq, res);
@ -1404,6 +1443,20 @@ static const struct spa_node_events follower_node_events = {
.event = follower_event, .event = follower_event,
}; };
static void follower_probe_info(void *data, const struct spa_node_info *info)
{
struct impl *this = data;
if (info->max_input_ports > 0)
this->direction = SPA_DIRECTION_INPUT;
else
this->direction = SPA_DIRECTION_OUTPUT;
}
static const struct spa_node_events follower_probe_events = {
SPA_VERSION_NODE_EVENTS,
.info = follower_probe_info,
};
static int follower_ready(void *data, int status) static int follower_ready(void *data, int status)
{ {
struct impl *this = data; struct impl *this = data;
@ -1551,30 +1604,52 @@ impl_node_remove_port(void *object, enum spa_direction direction, uint32_t port_
return spa_node_remove_port(this->target, direction, port_id); return spa_node_remove_port(this->target, direction, port_id);
} }
static int follower_port_enum_params(struct impl *this, static int
enum spa_direction direction, uint32_t port_id, port_enum_formats_for_convert(struct impl *this, int seq, enum spa_direction direction,
uint32_t id, uint32_t idx, uint32_t port_id, uint32_t id, uint32_t start, uint32_t num,
struct spa_result_node_params *result, const struct spa_pod *filter)
const struct spa_pod *filter,
struct spa_pod_builder *builder)
{ {
uint8_t buffer[4096];
struct spa_pod_builder b = { 0 };
int res; int res;
if (result->next < 0x100000 && this->follower_params_flags[idx] & SPA_PARAM_INFO_READ) { uint32_t count = 0;
if ((res = spa_node_port_enum_params_sync(this->follower, direction, port_id, struct spa_result_node_params result;
id, &result->next, filter, &result->param, builder)) == 1)
return res; spa_pod_builder_init(&b, buffer, sizeof(buffer));
result->next = 0x100000;
} result.id = id;
if (result->next < 0x200000 && result.next = start;
this->follower != this->target) { next:
result->next &= 0xfffff; result.index = result.next;
if ((res = spa_node_port_enum_params_sync(this->target, direction, port_id,
id, &result->next, filter, &result->param, builder)) == 1) { if (result.next < 0x100000) {
result->next |= 0x100000; /* Enumerate follower formats first, until we have enough or we run out */
return res; if ((res = node_port_enum_params_sync(this, this->follower, direction, port_id, id,
&result.next, filter, &result.param, &b)) != 1) {
if (res == 0 || res == -ENOENT) {
result.next = 0x100000;
goto next;
} else {
spa_log_error(this->log, "could not enum follower format: %s", spa_strerror(res));
return res;
}
}
} else if (result.next < 0x200000) {
/* Then enumerate converter formats */
result.next &= 0xfffff;
if ((res = node_port_enum_params_sync(this, this->convert, direction, port_id, id,
&result.next, filter, &result.param, &b)) != 1) {
return res;
} else {
result.next |= 0x100000;
} }
result->next = 0x200000;
} }
spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
if (++count < num)
goto next;
return 0; return 0;
} }
@ -1585,12 +1660,6 @@ impl_node_port_enum_params(void *object, int seq,
const struct spa_pod *filter) const struct spa_pod *filter)
{ {
struct impl *this = object; struct impl *this = object;
uint8_t buffer[4096];
spa_auto(spa_pod_dynamic_builder) b = { 0 };
struct spa_pod_builder_state state;
struct spa_result_node_params result;
uint32_t count = 0;
int res;
spa_return_val_if_fail(this != NULL, -EINVAL); spa_return_val_if_fail(this != NULL, -EINVAL);
spa_return_val_if_fail(num != 0, -EINVAL); spa_return_val_if_fail(num != 0, -EINVAL);
@ -1598,37 +1667,15 @@ impl_node_port_enum_params(void *object, int seq,
if (direction != this->direction) if (direction != this->direction)
port_id++; port_id++;
spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096); spa_log_debug(this->log, "%p: %d %u %u %u", this, seq, id, start, num);
spa_pod_builder_get_state(&b.b, &state);
result.id = id; /* We only need special handling for EnumFormat in convert mode */
result.next = start; if (id == SPA_PARAM_EnumFormat && this->mode == SPA_PARAM_PORT_CONFIG_MODE_convert)
next: return port_enum_formats_for_convert(this, seq, direction, port_id, id,
result.index = result.next; start, num, filter);
else
spa_log_debug(this->log, "%p: %d id:%u", this, seq, id);
spa_pod_builder_reset(&b.b, &state);
switch (id) {
case SPA_PARAM_EnumFormat:
res = follower_port_enum_params(this, direction, port_id,
id, IDX_EnumFormat, &result, filter, &b.b);
break;
default:
return spa_node_port_enum_params(this->target, seq, direction, port_id, id, return spa_node_port_enum_params(this->target, seq, direction, port_id, id,
start, num, filter); start, num, filter);
}
if (res != 1)
return res;
spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
count++;
if (count != num)
goto next;
return 0;
} }
static int static int
@ -1819,14 +1866,38 @@ static const struct spa_node_methods impl_node = {
.process = impl_node_process, .process = impl_node_process,
}; };
static int load_plugin_from(struct impl *this, const struct spa_dict *info, static int load_converter(struct impl *this, const struct spa_dict *info,
const char *convertname, struct spa_handle **handle, struct spa_node **iface) const struct spa_support *support, uint32_t n_support)
{ {
const char* factory_name = NULL;
struct spa_handle *hnd_convert = NULL; struct spa_handle *hnd_convert = NULL;
void *iface_conv = NULL; void *iface_conv = NULL;
hnd_convert = spa_plugin_loader_load(this->ploader, convertname, info); struct spa_dict_item *items;
if (!hnd_convert) struct spa_dict cinfo;
return -EINVAL; char direction[16];
uint32_t i;
items = alloca((info->n_items + 1) * sizeof(struct spa_dict_item));
cinfo = SPA_DICT(items, 0);
for (i = 0; i < info->n_items; i++)
items[cinfo.n_items++] = info->items[i];
snprintf(direction, sizeof(direction), "%s",
SPA_DIRECTION_REVERSE(this->direction) == SPA_DIRECTION_INPUT ?
"input" : "output");
items[cinfo.n_items++] = SPA_DICT_ITEM("convert.direction", direction);
factory_name = spa_dict_lookup(&cinfo, "video.adapt.converter");
if (factory_name == NULL)
factory_name = SPA_NAME_VIDEO_CONVERT".ffmpeg";
if (this->ploader) {
hnd_convert = spa_plugin_loader_load(this->ploader, factory_name, &cinfo);
if (!hnd_convert)
return -EINVAL;
} else {
return -ENOTSUP;
}
spa_handle_get_interface(hnd_convert, SPA_TYPE_INTERFACE_Node, &iface_conv); spa_handle_get_interface(hnd_convert, SPA_TYPE_INTERFACE_Node, &iface_conv);
if (iface_conv == NULL) { if (iface_conv == NULL) {
@ -1834,30 +1905,13 @@ static int load_plugin_from(struct impl *this, const struct spa_dict *info,
return -EINVAL; return -EINVAL;
} }
*handle = hnd_convert; this->hnd_convert = hnd_convert;
*iface = iface_conv; this->convert = iface_conv;
this->convertname = strdup(factory_name);
return 0; return 0;
} }
static int load_converter(struct impl *this, const struct spa_dict *info)
{
int ret;
if (!this->ploader || !info)
return -EINVAL;
const char* factory_name = spa_dict_lookup(info, "video.adapt.converter");
if (factory_name) {
ret = load_plugin_from(this, info, factory_name, &this->hnd_convert, &this->convert);
if (ret >= 0) {
this->convertname = strdup(factory_name);
return ret;
}
}
return 0;
}
static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
{ {
struct impl *this; struct impl *this;
@ -1915,6 +1969,7 @@ impl_init(const struct spa_handle_factory *factory,
struct impl *this; struct impl *this;
const char *str; const char *str;
int ret; int ret;
struct spa_hook probe_listener;
spa_return_val_if_fail(factory != NULL, -EINVAL); spa_return_val_if_fail(factory != NULL, -EINVAL);
spa_return_val_if_fail(handle != NULL, -EINVAL); spa_return_val_if_fail(handle != NULL, -EINVAL);
@ -1949,7 +2004,12 @@ impl_init(const struct spa_handle_factory *factory,
SPA_VERSION_NODE, SPA_VERSION_NODE,
&impl_node, this); &impl_node, this);
ret = load_converter(this, info); /* just probe the ports to get the direction */
spa_zero(probe_listener);
spa_node_add_listener(this->follower, &probe_listener, &follower_probe_events, this);
spa_hook_remove(&probe_listener);
ret = load_converter(this, info, support, n_support);
spa_log_info(this->log, "%p: loaded converter %s, hnd %p, convert %p", this, spa_log_info(this->log, "%p: loaded converter %s, hnd %p, convert %p", this,
this->convertname, this->hnd_convert, this->convert); this->convertname, this->hnd_convert, this->convert);
if (ret < 0) if (ret < 0)
@ -1957,10 +2017,11 @@ impl_init(const struct spa_handle_factory *factory,
if (this->convert == NULL) { if (this->convert == NULL) {
this->target = this->follower; this->target = this->follower;
this->passthrough = true; this->mode = SPA_PARAM_PORT_CONFIG_MODE_passthrough;
} else { } else {
this->target = this->convert; this->target = this->convert;
this->passthrough = false; /* the actual mode is selected below */
this->mode = SPA_PARAM_PORT_CONFIG_MODE_none;
} }
this->info_all = SPA_NODE_CHANGE_MASK_FLAGS | this->info_all = SPA_NODE_CHANGE_MASK_FLAGS |