diff --git a/spa/include/spa/param/dict-types.h b/spa/include/spa/param/dict-types.h new file mode 100644 index 000000000..c938686fa --- /dev/null +++ b/spa/include/spa/param/dict-types.h @@ -0,0 +1,38 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_DICT_TYPES_H +#define SPA_PARAM_DICT_TYPES_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#define SPA_TYPE_INFO_PARAM_Dict SPA_TYPE_INFO_PARAM_BASE "Dict" +#define SPA_TYPE_INFO_PARAM_DICT_BASE SPA_TYPE_INFO_PARAM_Dict ":" + +static const struct spa_type_info spa_type_param_dict[] = { + { SPA_PARAM_DICT_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_DICT_BASE, spa_type_param, }, + { SPA_PARAM_DICT_info, SPA_TYPE_Struct, SPA_TYPE_INFO_PARAM_DICT_BASE "info", NULL, }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_DICT_TYPES_H */ diff --git a/spa/include/spa/param/dict-utils.h b/spa/include/spa/param/dict-utils.h new file mode 100644 index 000000000..7dec70837 --- /dev/null +++ b/spa/include/spa/param/dict-utils.h @@ -0,0 +1,125 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_DICT_UTILS_H +#define SPA_PARAM_DICT_UTILS_H + +#include + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#ifndef SPA_API_DICT_UTILS + #ifdef SPA_API_IMPL + #define SPA_API_DICT_UTILS SPA_API_IMPL + #else + #define SPA_API_DICT_UTILS static inline + #endif +#endif + +SPA_API_DICT_UTILS int +spa_param_dict_compare(const struct spa_pod *a, const struct spa_pod *b) +{ + return spa_pod_memcmp(a, b); +} + +SPA_API_DICT_UTILS struct spa_pod * +spa_param_dict_build_dict(struct spa_pod_builder *builder, uint32_t id, struct spa_dict *dict) +{ + struct spa_pod_frame f[2]; + uint32_t i, n_items; + + spa_pod_builder_push_object(builder, &f[0], SPA_TYPE_OBJECT_ParamDict, id); + + n_items = dict ? dict->n_items : 0; + + spa_pod_builder_prop(builder, SPA_PARAM_DICT_info, SPA_POD_PROP_FLAG_HINT_DICT); + spa_pod_builder_push_struct(builder, &f[1]); + spa_pod_builder_int(builder, n_items); + for (i = 0; i < n_items; i++) { + spa_pod_builder_string(builder, dict->items[i].key); + spa_pod_builder_string(builder, dict->items[i].value); + } + spa_pod_builder_pop(builder, &f[1]); + + return (struct spa_pod*)spa_pod_builder_pop(builder, &f[0]); +} + +SPA_API_DICT_UTILS struct spa_pod * +spa_param_dict_build_info(struct spa_pod_builder *builder, uint32_t id, struct spa_param_dict_info *info) +{ + struct spa_pod_frame f; + spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_ParamDict, id); + spa_pod_builder_add(builder, SPA_PARAM_DICT_info, SPA_POD_PROP_FLAG_HINT_DICT); + spa_pod_builder_primitive(builder, info->info); + return (struct spa_pod*)spa_pod_builder_pop(builder, &f); +} + +SPA_API_DICT_UTILS int +spa_param_dict_parse(const struct spa_pod *dict, struct spa_param_dict_info *info, size_t size) +{ + memset(info, 0, size); + return spa_pod_parse_object(dict, + SPA_TYPE_OBJECT_ParamDict, NULL, + SPA_PARAM_DICT_info, SPA_POD_PodStruct(&info->info)); +} + +SPA_API_DICT_UTILS int +spa_param_dict_info_parse(const struct spa_param_dict_info *info, size_t size, + struct spa_dict *dict, struct spa_dict_item *items) +{ + struct spa_pod_parser prs; + uint32_t n, n_items; + const char *key, *value; + struct spa_pod_frame f[1]; + + spa_pod_parser_pod(&prs, info->info); + if (spa_pod_parser_push_struct(&prs, &f[0]) < 0 || + spa_pod_parser_get_int(&prs, (int32_t*)&n_items) < 0) + return -EINVAL; + + if (items == NULL) { + dict->n_items = n_items; + return 0; + } + n_items = SPA_MIN(dict->n_items, n_items); + + for (n = 0; n < n_items; n++) { + if (spa_pod_parser_get(&prs, + SPA_POD_String(&key), + SPA_POD_String(&value), + NULL) < 0) + break; + if (key == NULL || value == NULL) + return -EINVAL; + items[n].key = key; + items[n].value = value; + } + dict->items = items; + spa_pod_parser_pop(&prs, &f[0]); + return 0; +} + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_DICT_UTILS_H */ diff --git a/spa/include/spa/param/dict.h b/spa/include/spa/param/dict.h new file mode 100644 index 000000000..551567209 --- /dev/null +++ b/spa/include/spa/param/dict.h @@ -0,0 +1,42 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2025 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_DICT_H +#define SPA_PARAM_DICT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +/** properties for SPA_TYPE_OBJECT_ParamDict */ +enum spa_param_dict { + SPA_PARAM_DICT_START, + SPA_PARAM_DICT_info, /**< Struct( + * Int: n_items + * (String: key + * String: value)* + * ) */ +}; + +/** helper structure for managing info objects */ +struct spa_param_dict_info { + const struct spa_pod *info; +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_DICT_H */ diff --git a/spa/include/spa/param/param-types.h b/spa/include/spa/param/param-types.h index cb4bcd666..f84f19dce 100644 --- a/spa/include/spa/param/param-types.h +++ b/spa/include/spa/param/param-types.h @@ -41,7 +41,9 @@ static const struct spa_type_info spa_type_param[] = { { SPA_PARAM_Latency, SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_INFO_PARAM_ID_BASE "Latency", NULL }, { SPA_PARAM_ProcessLatency, SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_INFO_PARAM_ID_BASE "ProcessLatency", NULL }, { SPA_PARAM_Tag, SPA_TYPE_OBJECT_ParamTag, SPA_TYPE_INFO_PARAM_ID_BASE "Tag", NULL }, - { SPA_PARAM_PeerFormats, SPA_TYPE_Struct, SPA_TYPE_INFO_PARAM_ID_BASE "PeerFormats", NULL }, + { SPA_PARAM_PeerEnumFormat, SPA_TYPE_OBJECT_PeerParam, SPA_TYPE_INFO_PARAM_ID_BASE "PeerEnumFormat", NULL }, + { SPA_PARAM_Capability, SPA_TYPE_OBJECT_ParamDict, SPA_TYPE_INFO_PARAM_ID_BASE "Capability", NULL }, + { SPA_PARAM_PeerCapability, SPA_TYPE_OBJECT_PeerParam, SPA_TYPE_INFO_PARAM_ID_BASE "PeerCapability", NULL }, { 0, 0, NULL, NULL }, }; diff --git a/spa/include/spa/param/param.h b/spa/include/spa/param/param.h index cf72ac43e..f2078a5a1 100644 --- a/spa/include/spa/param/param.h +++ b/spa/include/spa/param/param.h @@ -40,7 +40,11 @@ enum spa_param_type { SPA_PARAM_Latency, /**< latency reporting, a SPA_TYPE_OBJECT_ParamLatency */ SPA_PARAM_ProcessLatency, /**< processing latency, a SPA_TYPE_OBJECT_ParamProcessLatency */ SPA_PARAM_Tag, /**< tag reporting, a SPA_TYPE_OBJECT_ParamTag. Since 0.3.79 */ - SPA_PARAM_PeerFormats, /**< peer formats, a SPA_TYPE_Struct of SPA_TYPE_OBJECT_Format. Since 1.5.0 */ + SPA_PARAM_PeerEnumFormat, /**< peer formats, a SPA_TYPE_OBJECT_PeerParam with + * SPA_TYPE_OBJECT_Format. Since 1.5.0 */ + SPA_PARAM_Capability, /**< capability info, a SPA_TYPE_OBJECT_ParamDict, Since 1.5.84 */ + SPA_PARAM_PeerCapability, /**< peer capabilities, a SPA_TYPE_OBJECT_PeerParam with + * SPA_TYPE_OBJECT_ParamDict, since 1.5.84 */ }; /** information about a parameter */ diff --git a/spa/include/spa/param/peer-types.h b/spa/include/spa/param/peer-types.h new file mode 100644 index 000000000..809f01446 --- /dev/null +++ b/spa/include/spa/param/peer-types.h @@ -0,0 +1,38 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_PEER_TYPES_H +#define SPA_PARAM_PEER_TYPES_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#define SPA_TYPE_INFO_PeerParam SPA_TYPE_INFO_OBJECT_BASE "PeerParam" +#define SPA_TYPE_INFO_PEER_PARAM_BASE SPA_TYPE_INFO_PeerParam ":" + +static const struct spa_type_info spa_type_peer_param[] = { + { SPA_PEER_PARAM_START, SPA_TYPE_Id, SPA_TYPE_INFO_PEER_PARAM_BASE, spa_type_param, }, + { SPA_ID_INVALID, SPA_TYPE_Id, SPA_TYPE_INFO_PEER_PARAM_BASE, NULL, }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_PEER_TYPES_H */ diff --git a/spa/include/spa/param/peer-utils.h b/spa/include/spa/param/peer-utils.h new file mode 100644 index 000000000..eb79448b7 --- /dev/null +++ b/spa/include/spa/param/peer-utils.h @@ -0,0 +1,92 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_PEER_PARAM_UTILS_H +#define SPA_PARAM_PEER_PARAM_UTILS_H + +#include + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#ifndef SPA_API_PEER_PARAM_UTILS + #ifdef SPA_API_IMPL + #define SPA_API_PEER_PARAM_UTILS SPA_API_IMPL + #else + #define SPA_API_PEER_PARAM_UTILS static inline + #endif +#endif + +SPA_API_PEER_PARAM_UTILS int +spa_peer_param_parse(const struct spa_pod *param, struct spa_peer_param_info *info, + size_t size, void **state) +{ + int res; + const struct spa_pod_object *obj = (const struct spa_pod_object*)param; + const struct spa_pod_prop *first, *start, *cur; + + if ((res = spa_pod_parse_object(param, + SPA_TYPE_OBJECT_PeerParam, NULL)) < 0) + return res; + + first = spa_pod_prop_first(&obj->body); + start = *state ? spa_pod_prop_next((struct spa_pod_prop*)*state) : first; + + res = 0; + for (cur = start; spa_pod_prop_is_inside(&obj->body, obj->pod.size, cur); + cur = spa_pod_prop_next(cur)) { + info->peer_id = cur->key; + info->param = &cur->value; + *state = (void*)cur; + return 1; + } + return 0; +} + + +SPA_API_PEER_PARAM_UTILS void +spa_peer_param_build_start(struct spa_pod_builder *builder, struct spa_pod_frame *f, uint32_t id) +{ + spa_pod_builder_push_object(builder, f, SPA_TYPE_OBJECT_PeerParam, id); +} + +SPA_API_PEER_PARAM_UTILS void +spa_peer_param_build_add_param(struct spa_pod_builder *builder, uint32_t peer_id, + const struct spa_pod *param) +{ + spa_pod_builder_prop(builder, peer_id, 0); + if (param) + spa_pod_builder_primitive(builder, param); + else + spa_pod_builder_none(builder); +} + +SPA_API_PEER_PARAM_UTILS struct spa_pod * +spa_peer_param_build_end(struct spa_pod_builder *builder, struct spa_pod_frame *f) +{ + return (struct spa_pod*)spa_pod_builder_pop(builder, f); +} + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_PEER_PARAM_UTILS_H */ diff --git a/spa/include/spa/param/peer.h b/spa/include/spa/param/peer.h new file mode 100644 index 000000000..40efe3e0a --- /dev/null +++ b/spa/include/spa/param/peer.h @@ -0,0 +1,37 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2025 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_PEER_PARAM_H +#define SPA_PARAM_PEER_PARAM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +/** properties for SPA_TYPE_OBJECT_PeerParam */ +enum spa_peer_param { + SPA_PEER_PARAM_START, /**< id of peer as key, SPA_TYPE_Pod as value */ + SPA_PEER_PARAM_END = 0xfffffffe, +}; + +struct spa_peer_param_info { + uint32_t peer_id; + const struct spa_pod *param; +}; +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_PEER_PARAM_H */ diff --git a/spa/include/spa/param/type-info.h b/spa/include/spa/param/type-info.h index fee2d030a..ba39294a9 100644 --- a/spa/include/spa/param/type-info.h +++ b/spa/include/spa/param/type-info.h @@ -15,5 +15,7 @@ #include #include #include +#include +#include #endif /* SPA_PARAM_TYPE_INFO_H */ diff --git a/spa/include/spa/pod/dynamic.h b/spa/include/spa/pod/dynamic.h index ac69381a9..6acbdf20f 100644 --- a/spa/include/spa/pod/dynamic.h +++ b/spa/include/spa/pod/dynamic.h @@ -70,8 +70,10 @@ SPA_API_POD_DYNAMIC void spa_pod_dynamic_builder_continue(struct spa_pod_dynamic SPA_API_POD_DYNAMIC void spa_pod_dynamic_builder_clean(struct spa_pod_dynamic_builder *builder) { - if (builder->data != builder->b.data) + if (builder->data != builder->b.data) { free(builder->b.data); + builder->b.data = NULL; + } } SPA_DEFINE_AUTO_CLEANUP(spa_pod_dynamic_builder, struct spa_pod_dynamic_builder, { diff --git a/spa/include/spa/utils/type-info.h b/spa/include/spa/utils/type-info.h index a099b9f0d..5eece4db2 100644 --- a/spa/include/spa/utils/type-info.h +++ b/spa/include/spa/utils/type-info.h @@ -5,6 +5,7 @@ #ifndef SPA_TYPE_INFO_H #define SPA_TYPE_INFO_H + #include #include #include @@ -78,6 +79,8 @@ static const struct spa_type_info spa_types[] = { { SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Latency, spa_type_param_latency }, { SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_ProcessLatency, spa_type_param_process_latency }, { SPA_TYPE_OBJECT_ParamTag, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Tag, spa_type_param_tag }, + { SPA_TYPE_OBJECT_PeerParam, SPA_TYPE_Object, SPA_TYPE_INFO_PeerParam, spa_type_peer_param }, + { SPA_TYPE_OBJECT_ParamDict, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Dict, spa_type_param_dict }, { 0, 0, NULL, NULL } }; diff --git a/spa/include/spa/utils/type.h b/spa/include/spa/utils/type.h index 758a9bd58..435129393 100644 --- a/spa/include/spa/utils/type.h +++ b/spa/include/spa/utils/type.h @@ -88,6 +88,8 @@ enum { SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_OBJECT_ParamTag, + SPA_TYPE_OBJECT_PeerParam, + SPA_TYPE_OBJECT_ParamDict, _SPA_TYPE_OBJECT_LAST, /**< not part of ABI */ /* vendor extensions */ diff --git a/spa/plugins/videoconvert/videoadapter.c b/spa/plugins/videoconvert/videoadapter.c index 23b55d768..e324b49de 100644 --- a/spa/plugins/videoconvert/videoadapter.c +++ b/spa/plugins/videoconvert/videoadapter.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -919,7 +920,7 @@ static int update_param_peer_formats(struct impl *impl) uint8_t buffer[4096]; spa_auto(spa_pod_dynamic_builder) b = { 0 }; uint32_t state = 0; - struct spa_pod *param; + struct spa_pod *param, *p, *str; struct spa_pod_frame f; int res; @@ -933,7 +934,6 @@ static int update_param_peer_formats(struct impl *impl) spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096); spa_pod_builder_push_struct(&b.b, &f); - while (true) { res = node_port_enum_params_sync(impl, impl->follower, impl->direction, 0, @@ -950,10 +950,24 @@ static int update_param_peer_formats(struct impl *impl) spa_pod_simplify(&b.b, ¶m, param); spa_debug_log_pod(impl->log, SPA_LOG_LEVEL_DEBUG, 0, NULL, param); + str = spa_pod_copy(param); + spa_pod_dynamic_builder_clean(&b); + + spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096); + spa_peer_param_build_start(&b.b, &f, SPA_PARAM_PeerEnumFormat); + SPA_POD_STRUCT_FOREACH(str, p) { + spa_debug_log_pod(impl->log, SPA_LOG_LEVEL_DEBUG, 0, NULL, p); + spa_peer_param_build_add_param(&b.b, 1, p); + } + param = spa_peer_param_build_end(&b.b, &f); + + spa_debug_log_pod(impl->log, SPA_LOG_LEVEL_DEBUG, 0, NULL, param); + res = spa_node_port_set_param(impl->target, SPA_DIRECTION_REVERSE(impl->direction), 0, - SPA_PARAM_PeerFormats, 0, param); + SPA_PARAM_PeerEnumFormat, 0, param); + free(str); impl->recheck_format = false; spa_log_debug(impl->log, "done updating peer formats: %d", res); diff --git a/spa/plugins/videoconvert/videoconvert-ffmpeg.c b/spa/plugins/videoconvert/videoconvert-ffmpeg.c index 525b2c933..3a556eede 100644 --- a/spa/plugins/videoconvert/videoconvert-ffmpeg.c +++ b/spa/plugins/videoconvert/videoconvert-ffmpeg.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -77,15 +78,16 @@ struct port { uint64_t info_all; struct spa_port_info info; -#define IDX_EnumFormat 0 -#define IDX_Meta 1 -#define IDX_IO 2 -#define IDX_Format 3 -#define IDX_Buffers 4 -#define IDX_Latency 5 -#define IDX_Tag 6 -#define IDX_PeerFormats 7 -#define N_PORT_PARAMS 8 +#define IDX_EnumFormat 0 +#define IDX_Meta 1 +#define IDX_IO 2 +#define IDX_Format 3 +#define IDX_Buffers 4 +#define IDX_Latency 5 +#define IDX_Tag 6 +#define IDX_PeerEnumFormat 7 +#define IDX_PeerCapability 8 +#define N_PORT_PARAMS 9 struct spa_param_info params[N_PORT_PARAMS]; struct spa_pod *peer_format_pod; @@ -485,7 +487,8 @@ static int init_port(struct impl *this, enum spa_direction direction, uint32_t p 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->params[IDX_Tag] = SPA_PARAM_INFO(SPA_PARAM_Tag, SPA_PARAM_INFO_READWRITE); - port->params[IDX_PeerFormats] = SPA_PARAM_INFO(SPA_PARAM_PeerFormats, SPA_PARAM_INFO_WRITE); + port->params[IDX_PeerEnumFormat] = SPA_PARAM_INFO(SPA_PARAM_PeerEnumFormat, SPA_PARAM_INFO_WRITE); + port->params[IDX_PeerCapability] = SPA_PARAM_INFO(SPA_PARAM_PeerCapability, SPA_PARAM_INFO_WRITE); port->info.params = port->params; port->info.n_params = N_PORT_PARAMS; @@ -1599,7 +1602,7 @@ static int port_param_tag(struct impl *this, struct port *port, uint32_t id, return 0; } -static int port_param_peer_formats(struct impl *this, struct port *port, uint32_t index, +static int port_param_peer_enum_format(struct impl *this, struct port *port, uint32_t index, const struct spa_pod **param, struct spa_pod_builder *b) { if (index >= port->n_peer_formats) @@ -1664,8 +1667,8 @@ impl_node_port_enum_params(void *object, int seq, case SPA_PARAM_Tag: res = port_param_tag(this, port, id, result.index, ¶m, &b); break; - case SPA_PARAM_PeerFormats: - res = port_param_peer_formats(this, port, result.index, ¶m, &b); + case SPA_PARAM_PeerEnumFormat: + res = port_param_peer_enum_format(this, port, result.index, ¶m, &b); break; default: return -ENOENT; @@ -1980,7 +1983,7 @@ static int port_set_format(void *object, } -static int port_set_peer_formats(void *object, +static int port_set_peer_enum_format(void *object, enum spa_direction direction, uint32_t port_id, uint32_t flags, @@ -1990,14 +1993,15 @@ static int port_set_peer_formats(void *object, struct port *port, *oport; int res = 0; uint32_t i; - const struct spa_pod *format; enum spa_direction other = SPA_DIRECTION_REVERSE(direction); static uint32_t subtypes[] = { SPA_MEDIA_SUBTYPE_raw, SPA_MEDIA_SUBTYPE_mjpg, SPA_MEDIA_SUBTYPE_h264 }; + struct spa_peer_param_info info; + void *state = NULL; - spa_return_val_if_fail(spa_pod_is_struct(formats), -EINVAL); + spa_return_val_if_fail(spa_pod_is_object(formats), -EINVAL); port = GET_PORT(this, direction, port_id); oport = GET_PORT(this, other, port_id); @@ -2013,9 +2017,10 @@ static int port_set_peer_formats(void *object, port->peer_format_pod = spa_pod_copy(formats); for (i = 0; i < SPA_N_ELEMENTS(subtypes); i++) { - SPA_POD_STRUCT_FOREACH(port->peer_format_pod, format) { + state = NULL; + while (spa_peer_param_parse(formats, &info, sizeof(info), &state) > 0) { uint32_t media_type, media_subtype; - if (!spa_format_parse(format, &media_type, &media_subtype) || + if (!spa_format_parse(info.param, &media_type, &media_subtype) || media_type != SPA_MEDIA_TYPE_video || media_subtype != subtypes[i]) continue; @@ -2024,13 +2029,14 @@ static int port_set_peer_formats(void *object, } port->peer_formats = calloc(count, sizeof(struct spa_pod *)); for (i = 0; i < SPA_N_ELEMENTS(subtypes); i++) { - SPA_POD_STRUCT_FOREACH(port->peer_format_pod, format) { + state = NULL; + while (spa_peer_param_parse(port->peer_format_pod, &info, sizeof(info), &state) > 0) { uint32_t media_type, media_subtype; - if (!spa_format_parse(format, &media_type, &media_subtype) || + if (!spa_format_parse(info.param, &media_type, &media_subtype) || media_type != SPA_MEDIA_TYPE_video || media_subtype != subtypes[i]) continue; - port->peer_formats[port->n_peer_formats++] = format; + port->peer_formats[port->n_peer_formats++] = info.param; } } } @@ -2038,7 +2044,7 @@ static int port_set_peer_formats(void *object, oport->params[IDX_EnumFormat].user++; port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; port->params[IDX_EnumFormat].user++; - port->params[IDX_PeerFormats].user++; + port->params[IDX_PeerEnumFormat].user++; return res; } @@ -2068,8 +2074,9 @@ impl_node_port_set_param(void *object, case SPA_PARAM_Format: res = port_set_format(this, direction, port_id, flags, param); break; - case SPA_PARAM_PeerFormats: - res = port_set_peer_formats(this, direction, port_id, flags, param); + case SPA_PARAM_PeerEnumFormat: + res = port_set_peer_enum_format(this, direction, port_id, flags, param); + break; break; default: return -ENOENT; diff --git a/src/examples/video-play.c b/src/examples/video-play.c index f4f65bb8f..64c68c874 100644 --- a/src/examples/video-play.c +++ b/src/examples/video-play.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include #include @@ -270,6 +272,33 @@ on_stream_io_changed(void *_data, uint32_t id, void *area, uint32_t size) } } +static void parse_peer_capability(struct data *data, const struct spa_pod *param) +{ + struct spa_peer_param_info info; + void *state = NULL; + + while (spa_peer_param_parse(param, &info, sizeof(info), &state) == 1) { + struct spa_param_dict_info di; + + if (spa_param_dict_parse(info.param, &di, sizeof(di)) > 0) { + struct spa_dict dict; + struct spa_dict_item *items; + const struct spa_dict_item *it; + + if (spa_param_dict_info_parse(&di, sizeof(di), &dict, NULL) < 0) + return; + + items = alloca(sizeof(struct spa_dict_item) * dict.n_items); + if (spa_param_dict_info_parse(&di, sizeof(di), &dict, items) < 0) + return; + + spa_dict_for_each(it, &dict) + fprintf(stderr, "peer:%u %s: %s\n", info.peer_id, it->key, it->value); + + } + } +} + /* Be notified when the stream param changes. We're only looking at the * format changes. * @@ -294,8 +323,11 @@ on_stream_param_changed(void *_data, uint32_t id, const struct spa_pod *param) void *d; int32_t mult, size, blocks; - if (param != NULL && id == SPA_PARAM_Tag) { - spa_debug_pod(0, NULL, param); + if (param != NULL && (id == SPA_PARAM_Tag || id == SPA_PARAM_PeerCapability)) { + if (id == SPA_PARAM_PeerCapability) + parse_peer_capability(data, param); + else + spa_debug_pod(0, NULL, param); return; } if (param != NULL && id == SPA_PARAM_Latency) { diff --git a/src/examples/video-src.c b/src/examples/video-src.c index 452924e59..b88f6d5e2 100644 --- a/src/examples/video-src.c +++ b/src/examples/video-src.c @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -219,7 +220,7 @@ on_stream_param_changed(void *_data, uint32_t id, const struct spa_pod *param) const struct spa_pod *params[5]; uint32_t n_params = 0; - if (param != NULL && id == SPA_PARAM_Tag) { + if (param != NULL && (id == SPA_PARAM_Tag || id == SPA_PARAM_PeerCapability)) { spa_debug_pod(0, NULL, param); return; } @@ -290,7 +291,8 @@ static void do_quit(void *userdata, int signal_number) int main(int argc, char *argv[]) { struct data data = { 0, }; - const struct spa_pod *params[2]; + const struct spa_pod *params[3]; + uint32_t n_params = 0; uint8_t buffer[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); @@ -318,7 +320,7 @@ int main(int argc, char *argv[]) PW_KEY_NODE_SUPPORTS_REQUEST, "1", NULL)); - params[0] = spa_pod_builder_add_object(&b, + params[n_params++] = spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), @@ -336,7 +338,13 @@ int main(int argc, char *argv[]) spa_tag_build_add_dict(&b, &SPA_DICT_ITEMS( SPA_DICT_ITEM("my-tag-key", "my-special-tag-value"))); - params[1] = spa_tag_build_end(&b, &f); + params[n_params++] = spa_tag_build_end(&b, &f); + } + { + params[n_params++] = spa_param_dict_build_dict(&b, SPA_PARAM_Capability, + &SPA_DICT_ITEMS( + SPA_DICT_ITEM("my-capability-key", "my-capability-value"))); + } pw_stream_add_listener(data.stream, @@ -349,7 +357,7 @@ int main(int argc, char *argv[]) PW_ID_ANY, PW_STREAM_FLAG_DRIVER | PW_STREAM_FLAG_MAP_BUFFERS, - params, 2); + params, n_params); pw_main_loop_run(data.loop); diff --git a/src/pipewire/impl-link.c b/src/pipewire/impl-link.c index 3760cbe04..a77dcf35f 100644 --- a/src/pipewire/impl-link.c +++ b/src/pipewire/impl-link.c @@ -924,6 +924,7 @@ static void input_remove(struct pw_impl_link *this) spa_list_remove(&this->input_link); pw_impl_port_emit_link_removed(port, this); + pw_impl_port_recalc_capability(port); pw_impl_port_recalc_latency(port); pw_impl_port_recalc_tag(port); @@ -956,6 +957,7 @@ static void output_remove(struct pw_impl_link *this) spa_list_remove(&this->output_link); pw_impl_port_emit_link_removed(port, this); + pw_impl_port_recalc_capability(port); pw_impl_port_recalc_latency(port); pw_impl_port_recalc_tag(port); @@ -1138,6 +1140,14 @@ static void input_port_tag_changed(void *data) pw_impl_port_recalc_tag(this->output); } +static void input_port_capability_changed(void *data) +{ + struct impl *impl = data; + struct pw_impl_link *this = &impl->this; + if (!this->feedback) + pw_impl_port_recalc_capability(this->output); +} + static void output_port_latency_changed(void *data) { struct impl *impl = data; @@ -1154,12 +1164,21 @@ static void output_port_tag_changed(void *data) pw_impl_port_recalc_tag(this->input); } +static void output_port_capability_changed(void *data) +{ + struct impl *impl = data; + struct pw_impl_link *this = &impl->this; + if (!this->feedback) + pw_impl_port_recalc_capability(this->input); +} + static const struct pw_impl_port_events input_port_events = { PW_VERSION_IMPL_PORT_EVENTS, .param_changed = input_port_param_changed, .state_changed = input_port_state_changed, .latency_changed = input_port_latency_changed, .tag_changed = input_port_tag_changed, + .capability_changed = input_port_capability_changed, }; static const struct pw_impl_port_events output_port_events = { @@ -1168,6 +1187,7 @@ static const struct pw_impl_port_events output_port_events = { .state_changed = output_port_state_changed, .latency_changed = output_port_latency_changed, .tag_changed = output_port_tag_changed, + .capability_changed = output_port_capability_changed, }; static void node_result(struct impl *impl, void *obj, @@ -1576,6 +1596,8 @@ struct pw_impl_link *pw_context_create_link(struct pw_context *context, try_link_controls(impl, output, input); + pw_impl_port_recalc_capability(output); + pw_impl_port_recalc_capability(input); pw_impl_port_recalc_latency(output); pw_impl_port_recalc_latency(input); pw_impl_port_recalc_tag(output); diff --git a/src/pipewire/impl-port.c b/src/pipewire/impl-port.c index 33c495de9..9d49a8097 100644 --- a/src/pipewire/impl-port.c +++ b/src/pipewire/impl-port.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include #include @@ -762,6 +764,35 @@ static int check_param_io(void *data, int seq, uint32_t id, return 0; } +static int process_capability_param(void *data, int seq, + uint32_t id, uint32_t index, uint32_t next, struct spa_pod *param) +{ + struct pw_impl_port *this = data; + struct spa_param_dict_info info; + struct spa_pod *old; + + if (id != SPA_PARAM_Capability || param == NULL) + return -EINVAL; + if (spa_param_dict_parse(param, &info, sizeof(info)) < 0) + return 0; + + old = this->cap[this->direction]; + if (spa_param_dict_compare(old, param) == 0) + return 0; + + pw_log_debug("port %p: got %s capabilty %p", this, + pw_direction_as_string(this->direction), param); + if (param) + pw_log_pod(SPA_LOG_LEVEL_DEBUG, param); + + free(old); + this->cap[this->direction] = spa_pod_copy(param); + + pw_impl_port_emit_capability_changed(this); + + return 0; +} + static int process_latency_param(void *data, int seq, uint32_t id, uint32_t index, uint32_t next, struct spa_pod *param) { @@ -831,6 +862,7 @@ static void check_params(struct pw_impl_port *port) PW_IMPL_PORT_FLAG_BUFFERS); pw_impl_port_for_each_param(port, 0, SPA_PARAM_IO, 0, 0, NULL, check_param_io, port); + pw_impl_port_for_each_param(port, 0, SPA_PARAM_Capability, 0, 0, NULL, process_capability_param, port); pw_impl_port_for_each_param(port, 0, SPA_PARAM_Latency, 0, 0, NULL, process_latency_param, port); pw_impl_port_for_each_param(port, 0, SPA_PARAM_Tag, 0, 0, NULL, process_tag_param, port); } @@ -877,6 +909,15 @@ static void update_info(struct pw_impl_port *port, const struct spa_port_info *i changed_ids[n_changed_ids++] = id; switch (id) { + case SPA_PARAM_PeerCapability: + port->have_peer_capability_param = + SPA_FLAG_IS_SET(info->params[i].flags, SPA_PARAM_INFO_WRITE); + break; + case SPA_PARAM_Capability: + if (port->node != NULL) + pw_impl_port_for_each_param(port, 0, id, 0, UINT32_MAX, + NULL, process_capability_param, port); + break; case SPA_PARAM_Latency: port->have_latency_param = SPA_FLAG_IS_SET(info->params[i].flags, SPA_PARAM_INFO_WRITE); @@ -1918,6 +1959,66 @@ int pw_impl_port_recalc_tag(struct pw_impl_port *port) return pw_impl_port_set_param(port, SPA_PARAM_Tag, 0, port->tag[direction]); } +int pw_impl_port_recalc_capability(struct pw_impl_port *port) +{ + struct pw_impl_link *l; + struct pw_impl_port *other; + struct spa_pod *param, *old; + struct spa_pod_dynamic_builder b = { 0 }; + struct spa_pod_frame f; + enum spa_direction direction; + uint8_t buffer[1024]; + int count = 0; + bool changed; + + if (port->destroying) + return 0; + + direction = SPA_DIRECTION_REVERSE(port->direction); + + spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096); + spa_peer_param_build_start(&b.b, &f, SPA_PARAM_PeerCapability); + + if (port->direction == PW_DIRECTION_OUTPUT) { + spa_list_for_each(l, &port->links, output_link) { + other = l->input; + spa_peer_param_build_add_param(&b.b, other->info.id, other->cap[other->direction]); + count++; + } + } else { + spa_list_for_each(l, &port->links, input_link) { + other = l->output; + spa_peer_param_build_add_param(&b.b, other->info.id, other->cap[other->direction]); + count++; + } + } + param = count == 0 ? NULL : spa_peer_param_build_end(&b.b, &f); + + old = port->cap[direction]; + + changed = spa_pod_memcmp(old, param); + + pw_log_info("port %d: %p %s %s cap %p %d", + port->info.id, port, changed ? "set" : "keep", + pw_direction_as_string(direction), param, port->have_peer_capability_param); + + if (changed) { + free(old); + port->cap[direction] = param ? spa_pod_copy(param) : NULL; + if (param) + pw_log_pod(SPA_LOG_LEVEL_INFO, param); + } + spa_pod_dynamic_builder_clean(&b); + + if (!changed) + return 0; + + if (!port->have_peer_capability_param) + return 0; + + return pw_impl_port_set_param(port, SPA_PARAM_PeerCapability, 0, port->cap[direction]); +} + SPA_EXPORT int pw_impl_port_is_linked(struct pw_impl_port *port) { diff --git a/src/pipewire/impl-port.h b/src/pipewire/impl-port.h index f68e850b8..9cd43661b 100644 --- a/src/pipewire/impl-port.h +++ b/src/pipewire/impl-port.h @@ -36,7 +36,7 @@ enum pw_impl_port_state { /** Port events, use \ref pw_impl_port_add_listener */ struct pw_impl_port_events { -#define PW_VERSION_IMPL_PORT_EVENTS 3 +#define PW_VERSION_IMPL_PORT_EVENTS 4 uint32_t version; /** The port is destroyed */ @@ -74,6 +74,8 @@ struct pw_impl_port_events { void (*latency_changed) (void *data); /** tag changed. Since version 3 */ void (*tag_changed) (void *data); + /** capability changed. Since version 4 */ + void (*capability_changed) (void *data); }; /** Create a new port diff --git a/src/pipewire/private.h b/src/pipewire/private.h index 81fe7dff5..5582709ad 100644 --- a/src/pipewire/private.h +++ b/src/pipewire/private.h @@ -902,6 +902,7 @@ struct pw_impl_port_implementation { #define pw_impl_port_emit_param_changed(p,i) pw_impl_port_emit(p, param_changed, 1, i) #define pw_impl_port_emit_latency_changed(p) pw_impl_port_emit(p, latency_changed, 2) #define pw_impl_port_emit_tag_changed(p) pw_impl_port_emit(p, tag_changed, 3) +#define pw_impl_port_emit_capability_changed(p) pw_impl_port_emit(p, capability_changed, 4) #define PW_IMPL_PORT_IS_CONTROL(port) SPA_FLAG_MASK((port)->flags, \ PW_IMPL_PORT_FLAG_BUFFERS|PW_IMPL_PORT_FLAG_CONTROL,\ @@ -977,6 +978,9 @@ struct pw_impl_port { unsigned int have_tag_param:1; struct spa_pod *tag[2]; /**< tags */ + unsigned int have_peer_capability_param:1; + struct spa_pod *cap[2]; /**< capabilities */ + void *owner_data; /**< extra owner data */ void *user_data; /**< extra user data */ }; @@ -1332,6 +1336,7 @@ int pw_impl_port_set_param(struct pw_impl_port *port, int pw_impl_port_use_buffers(struct pw_impl_port *port, struct pw_impl_port_mix *mix, uint32_t flags, struct spa_buffer **buffers, uint32_t n_buffers); +int pw_impl_port_recalc_capability(struct pw_impl_port *port); int pw_impl_port_recalc_latency(struct pw_impl_port *port); int pw_impl_port_recalc_tag(struct pw_impl_port *port); diff --git a/src/pipewire/stream.c b/src/pipewire/stream.c index 9387ee58a..dca27278e 100644 --- a/src/pipewire/stream.c +++ b/src/pipewire/stream.c @@ -103,14 +103,16 @@ struct stream { uint64_t port_change_mask_all; struct spa_port_info port_info; struct pw_properties *port_props; -#define PORT_EnumFormat 0 -#define PORT_Meta 1 -#define PORT_IO 2 -#define PORT_Format 3 -#define PORT_Buffers 4 -#define PORT_Latency 5 -#define PORT_Tag 6 -#define N_PORT_PARAMS 7 +#define PORT_EnumFormat 0 +#define PORT_Meta 1 +#define PORT_IO 2 +#define PORT_Format 3 +#define PORT_Buffers 4 +#define PORT_Latency 5 +#define PORT_Tag 6 +#define PORT_Capability 7 +#define PORT_PeerCapability 8 +#define N_PORT_PARAMS 9 struct spa_param_info port_params[N_PORT_PARAMS]; struct spa_list param_list; @@ -196,6 +198,10 @@ static int get_port_param_index(uint32_t id) return PORT_Latency; case SPA_PARAM_Tag: return PORT_Tag; + case SPA_PARAM_Capability: + return PORT_Capability; + case SPA_PARAM_PeerCapability: + return PORT_PeerCapability; default: return -1; } @@ -2015,6 +2021,8 @@ pw_stream_connect(struct pw_stream *stream, impl->port_params[PORT_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); impl->port_params[PORT_Latency] = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_WRITE); impl->port_params[PORT_Tag] = SPA_PARAM_INFO(SPA_PARAM_Tag, SPA_PARAM_INFO_WRITE); + impl->port_params[PORT_Capability] = SPA_PARAM_INFO(SPA_PARAM_Capability, 0); + impl->port_params[PORT_PeerCapability] = SPA_PARAM_INFO(SPA_PARAM_PeerCapability, SPA_PARAM_INFO_WRITE); impl->port_info.props = &impl->port_props->dict; impl->port_info.params = impl->port_params; impl->port_info.n_params = N_PORT_PARAMS; diff --git a/test/test-spa-utils.c b/test/test-spa-utils.c index 43c2ffa2c..dd663d3e0 100644 --- a/test/test-spa-utils.c +++ b/test/test-spa-utils.c @@ -127,7 +127,9 @@ PWTEST(utils_abi) pwtest_int_eq(SPA_TYPE_OBJECT_ParamLatency, 0x4000b); pwtest_int_eq(SPA_TYPE_OBJECT_ParamProcessLatency, 0x4000c); pwtest_int_eq(SPA_TYPE_OBJECT_ParamTag, 0x4000d); - pwtest_int_eq(_SPA_TYPE_OBJECT_LAST, 0x4000e); + pwtest_int_eq(SPA_TYPE_OBJECT_PeerParam, 0x4000e); + pwtest_int_eq(SPA_TYPE_OBJECT_ParamDict, 0x4000f); + pwtest_int_eq(_SPA_TYPE_OBJECT_LAST, 0x40010); pwtest_int_eq(SPA_TYPE_VENDOR_PipeWire, 0x02000000); pwtest_int_eq(SPA_TYPE_VENDOR_Other, 0x7f000000);