diff --git a/spa/include/spa/param/io.h b/spa/include/spa/param/io.h index f33ecd6b9..f5e83360f 100644 --- a/spa/include/spa/param/io.h +++ b/spa/include/spa/param/io.h @@ -53,16 +53,11 @@ extern "C" { /** enumerate output property io areas */ #define SPA_TYPE_PARAM_ID_IO_PROPS__Out SPA_TYPE_PARAM_ID_IO_PROPS_BASE "Out" -/* an io area to exchange properties */ +/* an io area to exchange properties. Contents can include + * SPA_TYPE_PARAM__PropInfo */ #define SPA_TYPE_PARAM_IO__Prop SPA_TYPE_PARAM_IO_BASE "Prop" #define SPA_TYPE_PARAM_IO_PROP_BASE SPA_TYPE_PARAM_IO__Prop ":" -/** associated property if any */ -#define SPA_TYPE_PARAM_IO_PROP__id SPA_TYPE_PARAM_IO_PROP_BASE "id" -/** associated type of property if any */ -#define SPA_TYPE_PARAM_IO_PROP__type SPA_TYPE_PARAM_IO_PROP_BASE "type" - - struct spa_type_param_io { uint32_t id; /**< id to configure the io area */ uint32_t size; /**< size of io area */ @@ -74,8 +69,6 @@ struct spa_type_param_io { uint32_t idPropsIn; /**< id to enumerate input properties io */ uint32_t idPropsOut; /**< id to enumerate output properties io */ uint32_t Prop; /**< object type of property area */ - uint32_t propId; /**< property id */ - uint32_t propType; /**< property type */ }; static inline void @@ -93,8 +86,6 @@ spa_type_param_io_map(struct spa_type_map *map, type->idPropsIn = spa_type_map_get_id(map, SPA_TYPE_PARAM_ID_IO_PROPS__In); type->idPropsOut = spa_type_map_get_id(map, SPA_TYPE_PARAM_ID_IO_PROPS__Out); type->Prop = spa_type_map_get_id(map, SPA_TYPE_PARAM_IO__Prop); - type->propId = spa_type_map_get_id(map, SPA_TYPE_PARAM_IO_PROP__id); - type->propType = spa_type_map_get_id(map, SPA_TYPE_PARAM_IO_PROP__type); } } diff --git a/spa/include/spa/param/param.h b/spa/include/spa/param/param.h index 133775890..5a8cc689f 100644 --- a/spa/include/spa/param/param.h +++ b/spa/include/spa/param/param.h @@ -44,6 +44,23 @@ extern "C" { #define SPA_TYPE_PARAM_LIST_BASE SPA_TYPE_PARAM__List ":" #define SPA_TYPE_PARAM_LIST__id SPA_TYPE_PARAM_LIST_BASE "id" +/** Enum Property info */ +#define SPA_TYPE_PARAM_ID__PropInfo SPA_TYPE_PARAM_ID_BASE "PropInfo" + +#define SPA_TYPE_PARAM__PropInfo SPA_TYPE_PARAM_BASE "PropInfo" +#define SPA_TYPE_PARAM_PROP_INFO_BASE SPA_TYPE_PARAM__PropInfo ":" + +/** associated id of the property */ +#define SPA_TYPE_PARAM_PROP_INFO__id SPA_TYPE_PARAM_PROP_INFO_BASE "id" +/** name of property */ +#define SPA_TYPE_PARAM_PROP_INFO__name SPA_TYPE_PARAM_PROP_INFO_BASE "name" +/** associated type and range/enums of property */ +#define SPA_TYPE_PARAM_PROP_INFO__type SPA_TYPE_PARAM_PROP_INFO_BASE "type" +/** associated labels of property if any, this is a struct with pairs of values, + * the first one is of the type of the property, the second one is a string with + * a user readable label for the value. */ +#define SPA_TYPE_PARAM_PROP_INFO__labels SPA_TYPE_PARAM_PROP_INFO_BASE "labels" + /** Property parameter id, deals with SPA_TYPE__Props */ #define SPA_TYPE_PARAM_ID__Props SPA_TYPE_PARAM_ID_BASE "Props" @@ -73,6 +90,12 @@ struct spa_type_param { uint32_t idList; /**< id of the list param */ uint32_t List; /**< list object type */ uint32_t listId; /**< id in the list object */ + uint32_t idPropInfo; /**< id to enumerate property info */ + uint32_t PropInfo; /**< property info object */ + uint32_t propId; /**< property id */ + uint32_t propName; /**< property name */ + uint32_t propType; /**< property type */ + uint32_t propLabels; /**< property labels */ uint32_t idProps; /**< id to enumerate properties */ uint32_t idEnumFormat; /**< id to enumerate formats */ uint32_t idFormat; /**< id to get/set format parameter */ @@ -88,6 +111,12 @@ spa_type_param_map(struct spa_type_map *map, type->idList = spa_type_map_get_id(map, SPA_TYPE_PARAM_ID__List); type->List = spa_type_map_get_id(map, SPA_TYPE_PARAM__List); type->listId = spa_type_map_get_id(map, SPA_TYPE_PARAM_LIST__id); + type->idPropInfo = spa_type_map_get_id(map, SPA_TYPE_PARAM_ID__PropInfo); + type->PropInfo = spa_type_map_get_id(map, SPA_TYPE_PARAM__PropInfo); + type->propId = spa_type_map_get_id(map, SPA_TYPE_PARAM_PROP_INFO__id); + type->propName = spa_type_map_get_id(map, SPA_TYPE_PARAM_PROP_INFO__name); + type->propType = spa_type_map_get_id(map, SPA_TYPE_PARAM_PROP_INFO__type); + type->propLabels = spa_type_map_get_id(map, SPA_TYPE_PARAM_PROP_INFO__labels); type->idProps = spa_type_map_get_id(map, SPA_TYPE_PARAM_ID__Props); type->idEnumFormat = spa_type_map_get_id(map, SPA_TYPE_PARAM_ID__EnumFormat); type->idFormat = spa_type_map_get_id(map, SPA_TYPE_PARAM_ID__Format); diff --git a/spa/plugins/alsa/alsa-sink.c b/spa/plugins/alsa/alsa-sink.c index 44fb8a485..f04a57fad 100644 --- a/spa/plugins/alsa/alsa-sink.c +++ b/spa/plugins/alsa/alsa-sink.c @@ -66,28 +66,76 @@ static int impl_node_enum_params(struct spa_node *node, spa_pod_builder_init(&b, buffer, sizeof(buffer)); if (id == t->param.idList) { - if (*index > 0) - return 0; + uint32_t list[] = { t->param.idPropInfo, + t->param.idProps }; - param = spa_pod_builder_object(&b, - id, t->param.List, - ":", t->param.listId, "I", t->param.idProps); + if (*index < SPA_N_ELEMENTS(list)) + param = spa_pod_builder_object(&b, id, t->param.List, + ":", t->param.listId, "I", list[*index]); + else + return 0; + } + else if (id == t->param.idPropInfo) { + struct props *p = &this->props; + + switch (*index) { + case 0: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_device, + ":", t->param.propName, "s", "The ALSA device", + ":", t->param.propType, "S", p->device, sizeof(p->device)); + break; + case 1: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_device_name, + ":", t->param.propName, "s", "The ALSA device name", + ":", t->param.propType, "S-r", p->device_name, sizeof(p->device_name)); + break; + case 2: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_card_name, + ":", t->param.propName, "s", "The ALSA card name", + ":", t->param.propType, "S-r", p->card_name, sizeof(p->card_name)); + break; + case 3: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_min_latency, + ":", t->param.propName, "s", "The minimum latency", + ":", t->param.propType, "ir", p->min_latency, + 2, 1, INT32_MAX); + break; + case 4: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_max_latency, + ":", t->param.propName, "s", "The maximum latency", + ":", t->param.propType, "ir", p->max_latency, + 2, 1, INT32_MAX); + break; + default: + return 0; + } } else if (id == t->param.idProps) { struct props *p = &this->props; - if (*index > 0) + switch (*index) { + case 0: + param = spa_pod_builder_object(&b, + id, t->props, + ":", t->prop_device, "S", p->device, sizeof(p->device), + ":", t->prop_device_name, "S-r", p->device_name, sizeof(p->device_name), + ":", t->prop_card_name, "S-r", p->card_name, sizeof(p->card_name), + ":", t->prop_min_latency, "i", p->min_latency, + ":", t->prop_max_latency, "i", p->max_latency); + break; + default: return 0; - - param = spa_pod_builder_object(&b, - id, t->props, - ":", t->prop_device, "S", p->device, sizeof(p->device), - ":", t->prop_device_name, "S-r", p->device_name, sizeof(p->device_name), - ":", t->prop_card_name, "S-r", p->card_name, sizeof(p->card_name), - ":", t->prop_min_latency, "ir", p->min_latency, - 2, 1, INT32_MAX, - ":", t->prop_max_latency, "ir", p->max_latency, - 2, 1, INT32_MAX); + } } else return -ENOENT; @@ -120,7 +168,8 @@ static int impl_node_set_param(struct spa_node *node, uint32_t id, uint32_t flag } spa_pod_object_parse(param, ":", t->prop_device, "?S", p->device, sizeof(p->device), - ":", t->prop_min_latency, "?i", &p->min_latency, NULL); + ":", t->prop_min_latency, "?i", &p->min_latency, + ":", t->prop_max_latency, "?i", &p->max_latency, NULL); } else return -ENOENT; diff --git a/spa/plugins/alsa/alsa-source.c b/spa/plugins/alsa/alsa-source.c index a5395f021..4201c0ba6 100644 --- a/spa/plugins/alsa/alsa-source.c +++ b/spa/plugins/alsa/alsa-source.c @@ -53,6 +53,8 @@ static int impl_node_enum_params(struct spa_node *node, struct spa_pod *param; uint8_t buffer[1024]; struct spa_pod_builder b = { 0 }; + struct props *p; + spa_return_val_if_fail(node != NULL, -EINVAL); spa_return_val_if_fail(index != NULL, -EINVAL); @@ -60,31 +62,69 @@ static int impl_node_enum_params(struct spa_node *node, this = SPA_CONTAINER_OF(node, struct state, node); t = &this->type; + p = &this->props; next: spa_pod_builder_init(&b, buffer, sizeof(buffer)); if (id == t->param.idList) { - if (*index > 0) - return 0; + uint32_t list[] = { t->param.idPropInfo, + t->param.idProps }; - param = spa_pod_builder_object(&b, - id, t->param.List, - ":", t->param.listId, "I", t->param.idProps); + if (*index < SPA_N_ELEMENTS(list)) + param = spa_pod_builder_object(&b, id, t->param.List, + ":", t->param.listId, "I", list[*index]); + else + return 0; + } + else if (id == t->param.idPropInfo) { + switch (*index) { + case 0: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_device, + ":", t->param.propName, "s", "The ALSA device", + ":", t->param.propType, "S", p->device, sizeof(p->device)); + break; + case 1: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_device_name, + ":", t->param.propName, "s", "The ALSA device name", + ":", t->param.propType, "S-r", p->device_name, sizeof(p->device_name)); + break; + case 2: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_card_name, + ":", t->param.propName, "s", "The ALSA card name", + ":", t->param.propType, "S-r", p->card_name, sizeof(p->card_name)); + break; + case 3: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_min_latency, + ":", t->param.propName, "s", "The minimum latency", + ":", t->param.propType, "ir", p->min_latency, + 2, 1, INT32_MAX); + break; + default: + return 0; + } } else if (id == t->param.idProps) { - struct props *p = &this->props; - - if (*index > 0) + switch (*index) { + case 0: + param = spa_pod_builder_object(&b, + id, t->props, + ":", t->prop_device, "S", p->device, sizeof(p->device), + ":", t->prop_device_name, "S-r", p->device_name, sizeof(p->device_name), + ":", t->prop_card_name, "S-r", p->card_name, sizeof(p->card_name), + ":", t->prop_min_latency, "i", p->min_latency); + break; + default: return 0; - - param = spa_pod_builder_object(&b, - id, t->props, - ":", t->prop_device, "S", p->device, sizeof(p->device), - ":", t->prop_device_name, "S-r", p->device_name, sizeof(p->device_name), - ":", t->prop_card_name, "S-r", p->card_name, sizeof(p->card_name), - ":", t->prop_min_latency, "ir", p->min_latency, - 2, 1, INT32_MAX); + } } else return -ENOENT; diff --git a/spa/plugins/audiomixer/audiomixer.c b/spa/plugins/audiomixer/audiomixer.c index 1d1280867..ebaa78510 100644 --- a/spa/plugins/audiomixer/audiomixer.c +++ b/spa/plugins/audiomixer/audiomixer.c @@ -534,16 +534,16 @@ impl_node_port_enum_params(struct spa_node *node, id, t->param_io.Prop, ":", t->param_io.id, "I", t->io_prop_volume, ":", t->param_io.size, "i", sizeof(struct spa_pod_double), - ":", t->param_io.propId, "I", t->prop_volume, - ":", t->param_io.propType, "dru", p->volume, 2, 0.0, 10.0); + ":", t->param.propId, "I", t->prop_volume, + ":", t->param.propType, "dru", p->volume, 2, 0.0, 10.0); break; case 1: param = spa_pod_builder_object(&b, id, t->param_io.Prop, ":", t->param_io.id, "I", t->io_prop_mute, ":", t->param_io.size, "i", sizeof(struct spa_pod_bool), - ":", t->param_io.propId, "I", t->prop_mute, - ":", t->param_io.propType, "b", p->mute); + ":", t->param.propId, "I", t->prop_mute, + ":", t->param.propType, "b", p->mute); break; default: return 0; diff --git a/spa/plugins/audiotestsrc/audiotestsrc.c b/spa/plugins/audiotestsrc/audiotestsrc.c index 1016d8547..ee300b51f 100644 --- a/spa/plugins/audiotestsrc/audiotestsrc.c +++ b/spa/plugins/audiotestsrc/audiotestsrc.c @@ -56,8 +56,6 @@ struct type { uint32_t io_prop_wave; uint32_t io_prop_freq; uint32_t io_prop_volume; - uint32_t wave_sine; - uint32_t wave_square; struct spa_type_io io; struct spa_type_param param; struct spa_type_meta meta; @@ -86,8 +84,6 @@ static inline void init_type(struct type *type, struct spa_type_map *map) type->io_prop_wave = spa_type_map_get_id(map, SPA_TYPE_IO_PROP_BASE "waveType"); type->io_prop_freq = spa_type_map_get_id(map, SPA_TYPE_IO_PROP_BASE "frequency"); type->io_prop_volume = spa_type_map_get_id(map, SPA_TYPE_IO_PROP_BASE "volume"); - type->wave_sine = spa_type_map_get_id(map, SPA_TYPE_PROPS__waveType ":sine"); - type->wave_square = spa_type_map_get_id(map, SPA_TYPE_PROPS__waveType ":square"); spa_type_io_map(map, &type->io); spa_type_param_map(map, &type->param); spa_type_meta_map(map, &type->meta); @@ -103,6 +99,16 @@ static inline void init_type(struct type *type, struct spa_type_map *map) spa_type_param_io_map(map, &type->param_io); } +enum wave_type { + WAVE_SINE, + WAVE_SQUARE, +}; + +#define DEFAULT_LIVE false +#define DEFAULT_WAVE WAVE_SINE +#define DEFAULT_FREQ 440.0 +#define DEFAULT_VOLUME 1.0 + struct props { bool live; uint32_t wave; @@ -110,6 +116,14 @@ struct props { double volume; }; +static void reset_props(struct props *props) +{ + props->live = DEFAULT_LIVE; + props->wave = DEFAULT_WAVE; + props->freq = DEFAULT_FREQ; + props->volume = DEFAULT_VOLUME; +} + #define MAX_BUFFERS 16 #define MAX_PORTS 1 @@ -170,19 +184,6 @@ struct impl { #define CHECK_PORT(this,d,p) ((d) == SPA_DIRECTION_OUTPUT && (p) < MAX_PORTS) -#define DEFAULT_LIVE false -#define DEFAULT_WAVE wave_sine -#define DEFAULT_FREQ 440.0 -#define DEFAULT_VOLUME 1.0 - -static void reset_props(struct impl *this, struct props *props) -{ - props->live = DEFAULT_LIVE; - props->wave = this->type.DEFAULT_WAVE; - props->freq = DEFAULT_FREQ; - props->volume = DEFAULT_VOLUME; -} - static int impl_node_enum_params(struct spa_node *node, uint32_t id, uint32_t *index, const struct spa_pod *filter, @@ -206,29 +207,71 @@ static int impl_node_enum_params(struct spa_node *node, spa_pod_builder_init(&b, buffer, sizeof(buffer)); if (id == t->param.idList) { - if (*index > 0) - return 0; + uint32_t list[] = { t->param.idPropInfo, + t->param.idProps }; - param = spa_pod_builder_object(&b, - id, t->param.List, - ":", t->param.listId, "I", t->param.idProps); + if (*index < SPA_N_ELEMENTS(list)) + param = spa_pod_builder_object(&b, id, t->param.List, + ":", t->param.listId, "I", list[*index]); + else + return 0; + } + else if (id == t->param.idPropInfo) { + struct props *p = &this->props; + + switch (*index) { + case 0: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_live, + ":", t->param.propName, "s", "Configure live mode of the source", + ":", t->param.propType, "b", p->live); + break; + case 1: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_wave, + ":", t->param.propName, "s", "Select the waveform", + ":", t->param.propType, "i", p->wave, + ":", t->param.propLabels, "[-i", + "i", WAVE_SINE, "s", "Sine wave", + "i", WAVE_SQUARE, "s", "Square wave", "]"); + break; + case 2: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_freq, + ":", t->param.propName, "s", "Select the frequency", + ":", t->param.propType, "dr", p->freq, + 2, 0.0, 50000000.0); + break; + case 3: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_volume, + ":", t->param.propName, "s", "Select the volume", + ":", t->param.propType, "dr", p->volume, + 2, 0.0, 10.0); + break; + default: + return 0; + } } else if (id == t->param.idProps) { struct props *p = &this->props; - if (*index > 0) + switch (*index) { + case 0: + param = spa_pod_builder_object(&b, + id, t->props, + ":", t->prop_live, "b", p->live, + ":", t->prop_wave, "i", p->wave, + ":", t->prop_freq, "d", p->freq, + ":", t->prop_volume, "d", p->volume); + break; + default: return 0; - - param = spa_pod_builder_object(&b, - id, t->props, - ":", t->prop_live, "b", p->live, - ":", t->prop_wave, "Ie", p->wave, - 2, t->wave_sine, - t->wave_square, - ":", t->prop_freq, "dr", p->freq, - 2, 0.0, 50000000.0, - ":", t->prop_volume, "dr", p->volume, - 2, 0.0, 10.0); + } } else return -ENOENT; @@ -256,12 +299,12 @@ static int impl_node_set_param(struct spa_node *node, uint32_t id, uint32_t flag struct props *p = &this->props; if (param == NULL) { - reset_props(this, p); + reset_props(p); return 0; } spa_pod_object_parse(param, ":",t->prop_live, "?b", &p->live, - ":",t->prop_wave, "?I", &p->wave, + ":",t->prop_wave, "?i", &p->wave, ":",t->prop_freq, "?d", &p->freq, ":",t->prop_volume, "?d", &p->volume, NULL); @@ -695,26 +738,27 @@ impl_node_port_enum_params(struct spa_node *node, id, t->param_io.Prop, ":", t->param_io.id, "I", t->io_prop_wave, ":", t->param_io.size, "i", sizeof(struct spa_pod_id), - ":", t->param_io.propId, "I", t->prop_wave, - ":", t->param_io.propType, "Ie", p->wave, - 2, t->wave_sine, - t->wave_square); + ":", t->param.propId, "I", t->prop_wave, + ":", t->param.propType, "i", p->wave, + ":", t->param.propLabels, "[-i", + "i", WAVE_SINE, "s", "Sine wave", + "i", WAVE_SQUARE, "s", "Square wave", "]"); break; case 1: param = spa_pod_builder_object(&b, id, t->param_io.Prop, ":", t->param_io.id, "I", t->io_prop_freq, ":", t->param_io.size, "i", sizeof(struct spa_pod_double), - ":", t->param_io.propId, "I", t->prop_freq, - ":", t->param_io.propType, "dr", p->freq, 2, 0.0, 50000000.0); + ":", t->param.propId, "I", t->prop_freq, + ":", t->param.propType, "dr", p->freq, 2, 0.0, 50000000.0); break; case 2: param = spa_pod_builder_object(&b, id, t->param_io.Prop, ":", t->param_io.id, "I", t->io_prop_volume, ":", t->param_io.size, "i", sizeof(struct spa_pod_double), - ":", t->param_io.propId, "I", t->prop_volume, - ":", t->param_io.propType, "dr", p->volume, 2, 0.0, 10.0); + ":", t->param.propId, "I", t->prop_volume, + ":", t->param.propType, "dr", p->volume, 2, 0.0, 10.0); break; default: return 0; @@ -1141,7 +1185,7 @@ impl_init(const struct spa_handle_factory *factory, this->node = impl_node; this->clock = impl_clock; - reset_props(this, &this->props); + reset_props(&this->props); this->io_wave = &this->props.wave; this->io_freq = &this->props.freq; diff --git a/spa/plugins/v4l2/v4l2-source.c b/spa/plugins/v4l2/v4l2-source.c index 9087fc15c..29c06c796 100644 --- a/spa/plugins/v4l2/v4l2-source.c +++ b/spa/plugins/v4l2/v4l2-source.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -82,6 +83,7 @@ struct type { struct spa_type_command_node command_node; struct spa_type_param_buffers param_buffers; struct spa_type_param_meta param_meta; + struct spa_type_param_io param_io; struct spa_type_meta meta; struct spa_type_data data; }; @@ -95,8 +97,8 @@ static inline void init_type(struct type *type, struct spa_type_map *map) type->prop_device = spa_type_map_get_id(map, SPA_TYPE_PROPS__device); type->prop_device_name = spa_type_map_get_id(map, SPA_TYPE_PROPS__deviceName); type->prop_device_fd = spa_type_map_get_id(map, SPA_TYPE_PROPS__deviceFd); - spa_type_io_map(map, &type->io); - spa_type_param_map(map, &type->param); + spa_type_meta_map(map, &type->meta); + spa_type_data_map(map, &type->data); spa_type_media_type_map(map, &type->media_type); spa_type_media_subtype_map(map, &type->media_subtype); spa_type_media_subtype_video_map(map, &type->media_subtype_video); @@ -104,10 +106,11 @@ static inline void init_type(struct type *type, struct spa_type_map *map) spa_type_video_format_map(map, &type->video_format); spa_type_event_node_map(map, &type->event_node); spa_type_command_node_map(map, &type->command_node); + spa_type_param_map(map, &type->param); spa_type_param_buffers_map(map, &type->param_buffers); spa_type_param_meta_map(map, &type->param_meta); - spa_type_meta_map(map, &type->meta); - spa_type_data_map(map, &type->data); + spa_type_io_map(map, &type->io); + spa_type_param_io_map(map, &type->param_io); } struct port { @@ -129,6 +132,7 @@ struct port { int fd; bool opened; + bool have_query_ext_ctrl; struct v4l2_capability cap; struct v4l2_format fmt; enum v4l2_buf_type type; @@ -194,23 +198,58 @@ static int impl_node_enum_params(struct spa_node *node, spa_pod_builder_init(&b, buffer, sizeof(buffer)); if (id == t->param.idList) { - if (*index > 0) - return 0; + uint32_t list[] = { t->param.idPropInfo, + t->param.idProps }; - param = spa_pod_builder_object(&b, - id, t->param.List, - ":", t->param.listId, "I", t->param.idProps); + if (*index < SPA_N_ELEMENTS(list)) + param = spa_pod_builder_object(&b, id, t->param.List, + ":", t->param.listId, "I", list[*index]); + else + return 0; + } + else if (id == t->param.idPropInfo) { + struct props *p = &this->props; + + switch (*index) { + case 0: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_device, + ":", t->param.propName, "s", "The V4L2 device", + ":", t->param.propType, "S", p->device, sizeof(p->device)); + break; + case 1: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_device_name, + ":", t->param.propName, "s", "The V4L2 device name", + ":", t->param.propType, "S-r", p->device_name, sizeof(p->device_name)); + break; + case 2: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_device_fd, + ":", t->param.propName, "s", "The V4L2 fd", + ":", t->param.propType, "i-r", p->device_fd); + break; + default: + return 0; + } } else if (id == t->param.idProps) { struct props *p = &this->props; - if (*index > 0) + switch (*index) { + case 0: + param = spa_pod_builder_object(&b, + id, t->props, + ":", t->prop_device, "S", p->device, sizeof(p->device), + ":", t->prop_device_name, "S-r", p->device_name, sizeof(p->device_name), + ":", t->prop_device_fd, "i-r", p->device_fd); + break; + default: return 0; - - param = spa_pod_builder_object(&b, t->param.idProps, t->props, - ":", t->prop_device, "S", p->device, sizeof(p->device), - ":", t->prop_device_name, "S-r", p->device_name, sizeof(p->device_name), - ":", t->prop_device_fd, "i-r", p->device_fd); + } } else return -ENOENT; @@ -547,7 +586,8 @@ static int impl_node_port_enum_params(struct spa_node *node, uint32_t list[] = { t->param.idEnumFormat, t->param.idFormat, t->param.idBuffers, - t->param.idMeta }; + t->param.idMeta, + t->param_io.idPropsIn }; if (*index < SPA_N_ELEMENTS(list)) param = spa_pod_builder_object(&b, id, t->param.List, @@ -588,6 +628,9 @@ static int impl_node_port_enum_params(struct spa_node *node, return 0; } } + else if (id == t->param_io.idPropsIn) { + return spa_v4l2_enum_controls(this, index, filter, result, builder); + } else return -ENOENT; @@ -1010,6 +1053,7 @@ impl_init(const struct spa_handle_factory *factory, this->out_ports[0].info.flags = SPA_PORT_INFO_FLAG_LIVE; this->out_ports[0].export_buf = true; + this->out_ports[0].have_query_ext_ctrl = true; if (info && (str = spa_dict_lookup(info, "device.path"))) { strncpy(this->props.device, str, 63); diff --git a/spa/plugins/v4l2/v4l2-utils.c b/spa/plugins/v4l2/v4l2-utils.c index 198e52716..73b97f940 100644 --- a/spa/plugins/v4l2/v4l2-utils.c +++ b/spa/plugins/v4l2/v4l2-utils.c @@ -821,7 +821,7 @@ spa_v4l2_enum_format(struct impl *this, static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format, bool try_only) { struct port *port = &this->out_ports[0]; - int cmd; + int res, cmd; struct v4l2_format reqfmt, fmt; struct v4l2_streamparm streamparm; const struct format_info *info = NULL; @@ -858,7 +858,7 @@ static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format, if (info == NULL || size == NULL || framerate == NULL) { spa_log_error(port->log, "v4l2: unknown media type %d %d %d", format->media_type, format->media_subtype, video_format); - return -1; + return -EINVAL; } @@ -876,13 +876,14 @@ static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format, reqfmt = fmt; - if (spa_v4l2_open(this) < 0) - return -1; + if ((res = spa_v4l2_open(this)) < 0) + return res; cmd = try_only ? VIDIOC_TRY_FMT : VIDIOC_S_FMT; if (xioctl(port->fd, cmd, &fmt) < 0) { + res = -errno; perror("VIDIOC_S_FMT"); - return -1; + return res; } /* some cheap USB cam's won't accept any change */ @@ -897,7 +898,7 @@ static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format, if (reqfmt.fmt.pix.pixelformat != fmt.fmt.pix.pixelformat || reqfmt.fmt.pix.width != fmt.fmt.pix.width || reqfmt.fmt.pix.height != fmt.fmt.pix.height) - return -1; + return -EINVAL; if (try_only) return 0; @@ -915,6 +916,185 @@ static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format, return 0; } +static int query_ext_ctrl_ioctl(struct port *port, struct v4l2_query_ext_ctrl *qctrl) +{ + struct v4l2_queryctrl qc; + int res; + + if (port->have_query_ext_ctrl) { + res = ioctl(port->fd, VIDIOC_QUERY_EXT_CTRL, qctrl); + if (errno != ENOTTY) + return res; + port->have_query_ext_ctrl = false; + } + qc.id = qctrl->id; + res = ioctl(port->fd, VIDIOC_QUERYCTRL, &qc); + if (res == 0) { + qctrl->type = qc.type; + memcpy(qctrl->name, qc.name, sizeof(qctrl->name)); + qctrl->minimum = qc.minimum; + if (qc.type == V4L2_CTRL_TYPE_BITMASK) { + qctrl->maximum = (__u32)qc.maximum; + qctrl->default_value = (__u32)qc.default_value; + } else { + qctrl->maximum = qc.maximum; + qctrl->default_value = qc.default_value; + } + qctrl->step = qc.step; + qctrl->flags = qc.flags; + qctrl->elems = 1; + qctrl->nr_of_dims = 0; + memset(qctrl->dims, 0, sizeof(qctrl->dims)); + switch (qctrl->type) { + case V4L2_CTRL_TYPE_INTEGER64: + qctrl->elem_size = sizeof(__s64); + break; + case V4L2_CTRL_TYPE_STRING: + qctrl->elem_size = qc.maximum + 1; + break; + default: + qctrl->elem_size = sizeof(__s32); + break; + } + memset(qctrl->reserved, 0, sizeof(qctrl->reserved)); + } + qctrl->id = qc.id; + return res; +} + +static int +spa_v4l2_enum_controls(struct impl *this, + uint32_t *index, + const struct spa_pod *filter, + struct spa_pod **result, + struct spa_pod_builder *builder) +{ + struct port *port = &this->out_ports[0]; + struct type *t = &this->type; + struct v4l2_query_ext_ctrl queryctrl; + struct spa_pod *param; + struct spa_pod_builder b = { 0 }; + uint8_t buffer[1024]; + int res; + const unsigned next_fl = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND; + + if ((res = spa_v4l2_open(this)) < 0) + return res; + + next: + spa_zero(queryctrl); + + if (*index == 0) + *index |= next_fl; + + queryctrl.id = *index; + spa_log_debug(port->log, "test control %08x", queryctrl.id); + + if (query_ext_ctrl_ioctl(port, &queryctrl) != 0) { + if (errno == EINVAL) { + if (queryctrl.id != next_fl) + goto enum_end; + + if (*index & next_fl) + *index = V4L2_CID_USER_BASE; + else if (*index >= V4L2_CID_USER_BASE && *index < V4L2_CID_LASTP1) + (*index)++; + else if (*index >= V4L2_CID_LASTP1) + *index = V4L2_CID_PRIVATE_BASE; + else + goto enum_end; + goto next; + } + res = -errno; + perror("VIDIOC_QUERYCTRL"); + return res; + } + if (*index & next_fl) + (*index) = queryctrl.id | next_fl; + else + (*index)++; + + if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) + goto next; + + spa_log_debug(port->log, "Control %s", queryctrl.name); + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + + switch (queryctrl.type) { + case V4L2_CTRL_TYPE_INTEGER: + param = spa_pod_builder_object(&b, + t->param_io.idPropsIn, t->param_io.Prop, + ":", t->param_io.size, "i", sizeof(struct spa_pod_int), + ":", t->param.propType, "isu", queryctrl.default_value, + 3, queryctrl.minimum, + queryctrl.maximum, + queryctrl.step, + ":", t->param.propName, "s", queryctrl.name); + break; + case V4L2_CTRL_TYPE_BOOLEAN: + param = spa_pod_builder_object(&b, + t->param_io.idPropsIn, t->param_io.Prop, + ":", t->param_io.size, "i", sizeof(struct spa_pod_bool), + ":", t->param.propType, "b-u", queryctrl.default_value, + ":", t->param.propName, "s", queryctrl.name); + break; + case V4L2_CTRL_TYPE_MENU: + { + struct v4l2_querymenu querymenu; + + spa_pod_builder_push_object(&b, t->param_io.idPropsIn, t->param_io.Prop); + spa_pod_builder_add(&b, + ":", t->param_io.size, "i", sizeof(struct spa_pod_int), + ":", t->param.propName, "s", queryctrl.name, + NULL); + + spa_pod_builder_push_prop(&b, t->param.propType, SPA_POD_PROP_FLAG_UNSET); + spa_pod_builder_int(&b, queryctrl.default_value); + spa_pod_builder_pop(&b); + + spa_zero(querymenu); + querymenu.id = queryctrl.id; + + spa_pod_builder_push_prop(&b, t->param.propLabels, 0); + spa_pod_builder_push_struct(&b); + for (querymenu.index = queryctrl.minimum; + querymenu.index <= queryctrl.maximum; + querymenu.index++) { + if (ioctl(port->fd, VIDIOC_QUERYMENU, &querymenu) == 0) { + spa_pod_builder_int(&b, querymenu.index); + spa_pod_builder_string(&b, (const char *)querymenu.name); + } + } + spa_pod_builder_pop(&b); + spa_pod_builder_pop(&b); + param = spa_pod_builder_pop(&b); + break; + } + case V4L2_CTRL_TYPE_INTEGER_MENU: + case V4L2_CTRL_TYPE_BITMASK: + case V4L2_CTRL_TYPE_BUTTON: + case V4L2_CTRL_TYPE_INTEGER64: + case V4L2_CTRL_TYPE_STRING: + default: + goto next; + + } + if (spa_pod_filter(builder, result, param, filter) < 0) + goto next; + + res = 1; + + exit: + spa_v4l2_close(this); + + return res; + + enum_end: + res = 0; + goto exit; +} + static int mmap_read(struct impl *this) { struct port *port = &this->out_ports[0]; diff --git a/spa/plugins/videotestsrc/draw.c b/spa/plugins/videotestsrc/draw.c index 8117dd049..193ad19c8 100644 --- a/spa/plugins/videotestsrc/draw.c +++ b/spa/plugins/videotestsrc/draw.c @@ -262,20 +262,21 @@ static int draw(struct impl *this, char *data) { DrawingData dd; int res; - uint32_t pattern; init_colors(); if ((res = drawing_data_init(&dd, this, data)) < 0) return res; - pattern = this->props.pattern; - if (pattern == this->type.pattern_smpte_snow) + switch (this->props.pattern) { + case PATTERN_SMPTE_SNOW: draw_smpte_snow(&dd); - else if (pattern == this->type.pattern_snow) + break; + case PATTERN_SNOW: draw_snow(&dd); - else + break; + default: return -ENOTSUP; - + } return 0; } diff --git a/spa/plugins/videotestsrc/videotestsrc.c b/spa/plugins/videotestsrc/videotestsrc.c index 456c3bc36..f354523d4 100644 --- a/spa/plugins/videotestsrc/videotestsrc.c +++ b/spa/plugins/videotestsrc/videotestsrc.c @@ -50,8 +50,6 @@ struct type { uint32_t props; uint32_t prop_live; uint32_t prop_pattern; - uint32_t pattern_smpte_snow; - uint32_t pattern_snow; struct spa_type_io io; struct spa_type_param param; struct spa_type_meta meta; @@ -74,8 +72,6 @@ static inline void init_type(struct type *type, struct spa_type_map *map) type->props = spa_type_map_get_id(map, SPA_TYPE__Props); type->prop_live = spa_type_map_get_id(map, SPA_TYPE_PROPS__live); type->prop_pattern = spa_type_map_get_id(map, SPA_TYPE_PROPS__patternType); - type->pattern_smpte_snow = spa_type_map_get_id(map, SPA_TYPE_PROPS__patternType ":smpte-snow"); - type->pattern_snow = spa_type_map_get_id(map, SPA_TYPE_PROPS__patternType ":snow"); spa_type_io_map(map, &type->io); spa_type_param_map(map, &type->param); spa_type_meta_map(map, &type->meta); @@ -90,11 +86,25 @@ static inline void init_type(struct type *type, struct spa_type_map *map) spa_type_param_meta_map(map, &type->param_meta); } +enum pattern { + PATTERN_SMPTE_SNOW, + PATTERN_SNOW, +}; + +#define DEFAULT_LIVE false +#define DEFAULT_PATTERN PATTERN_SMPTE_SNOW + struct props { bool live; uint32_t pattern; }; +static void reset_props(struct props *props) +{ + props->live = DEFAULT_LIVE; + props->pattern = DEFAULT_PATTERN; +} + #define MAX_BUFFERS 16 #define MAX_PORTS 1 @@ -145,15 +155,6 @@ struct impl { #define CHECK_PORT(this,d,p) ((d) == SPA_DIRECTION_OUTPUT && (p) < MAX_PORTS) -#define DEFAULT_LIVE false -#define DEFAULT_PATTERN pattern_smpte_snow - -static void reset_props(struct impl *this, struct props *props) -{ - props->live = DEFAULT_LIVE; - props->pattern = this->type.DEFAULT_PATTERN; -} - static int impl_node_enum_params(struct spa_node *node, uint32_t id, uint32_t *index, const struct spa_pod *filter, @@ -176,25 +177,53 @@ static int impl_node_enum_params(struct spa_node *node, spa_pod_builder_init(&b, buffer, sizeof(buffer)); if (id == t->param.idList) { - if (*index > 0) - return 0; + uint32_t list[] = { t->param.idPropInfo, + t->param.idProps }; - param = spa_pod_builder_object(&b, - id, t->param.List, - ":", t->param.listId, "I", t->param.idProps); + if (*index < SPA_N_ELEMENTS(list)) + param = spa_pod_builder_object(&b, id, t->param.List, + ":", t->param.listId, "I", list[*index]); + else + return 0; + } + else if (id == t->param.idPropInfo) { + struct props *p = &this->props; + + switch (*index) { + case 0: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_live, + ":", t->param.propName, "s", "Configure live mode of the source", + ":", t->param.propType, "b", p->live); + break; + case 1: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_pattern, + ":", t->param.propName, "s", "The pattern", + ":", t->param.propType, "i", p->pattern, + ":", t->param.propLabels, "[-i", + "i", PATTERN_SMPTE_SNOW, "s", "SMPTE snow", + "i", PATTERN_SNOW, "s", "Snow", "]"); + break; + default: + return 0; + } } else if (id == t->param.idProps) { struct props *p = &this->props; - if (*index > 0) + switch (*index) { + case 0: + param = spa_pod_builder_object(&b, + id, t->props, + ":", t->prop_live, "b", p->live, + ":", t->prop_pattern, "i", p->pattern); + break; + default: return 0; - - param = spa_pod_builder_object(&b, - id, t->props, - ":", t->prop_live, "b", p->live, - ":", t->prop_pattern, "Ie", p->pattern, - 2, t->pattern_smpte_snow, - t->pattern_snow); + } } else return -ENOENT; @@ -221,9 +250,13 @@ static int impl_node_set_param(struct spa_node *node, uint32_t id, uint32_t flag if (id == t->param.idProps) { struct props *p = &this->props; + if (param == NULL) { + reset_props(p); + return 0; + } spa_pod_object_parse(param, ":", t->prop_live, "?b", &p->live, - ":", t->prop_pattern, "?I", &p->pattern, + ":", t->prop_pattern, "?i", &p->pattern, NULL); if (p->live) @@ -993,7 +1026,7 @@ impl_init(const struct spa_handle_factory *factory, this->node = impl_node; this->clock = impl_clock; - reset_props(this, &this->props); + reset_props(&this->props); spa_list_init(&this->empty); diff --git a/spa/plugins/volume/volume.c b/spa/plugins/volume/volume.c index 2196b6dff..4c44466c1 100644 --- a/spa/plugins/volume/volume.c +++ b/spa/plugins/volume/volume.c @@ -35,13 +35,22 @@ #define NAME "volume" -#define MAX_BUFFERS 16 +#define DEFAULT_VOLUME 1.0 +#define DEFAULT_MUTE false struct props { double volume; bool mute; }; +static void reset_props(struct props *props) +{ + props->volume = DEFAULT_VOLUME; + props->mute = DEFAULT_MUTE; +} + +#define MAX_BUFFERS 16 + struct buffer { struct spa_buffer *outbuf; bool outstanding; @@ -136,15 +145,6 @@ struct impl { #define GET_OUT_PORT(this,p) (&this->out_ports[p]) #define GET_PORT(this,d,p) (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(this,p) : GET_OUT_PORT(this,p)) -#define DEFAULT_VOLUME 1.0 -#define DEFAULT_MUTE false - -static void reset_props(struct props *props) -{ - props->volume = DEFAULT_VOLUME; - props->mute = DEFAULT_MUTE; -} - static int impl_node_enum_params(struct spa_node *node, uint32_t id, uint32_t *index, const struct spa_pod *filter, @@ -156,6 +156,7 @@ static int impl_node_enum_params(struct spa_node *node, struct spa_pod_builder b = { 0 }; uint8_t buffer[1024]; struct spa_pod *param; + struct props *p; spa_return_val_if_fail(node != NULL, -EINVAL); spa_return_val_if_fail(index != NULL, -EINVAL); @@ -163,28 +164,52 @@ static int impl_node_enum_params(struct spa_node *node, this = SPA_CONTAINER_OF(node, struct impl, node); t = &this->type; + p = &this->props; next: spa_pod_builder_init(&b, buffer, sizeof(buffer)); if (id == t->param.idList) { - if (*index > 0) - return 0; + uint32_t list[] = { t->param.idPropInfo, + t->param.idProps }; - param = spa_pod_builder_object(&b, - id, t->param.List, - ":", t->param.listId, "I", t->param.idProps); + if (*index < SPA_N_ELEMENTS(list)) + param = spa_pod_builder_object(&b, id, t->param.List, + ":", t->param.listId, "I", list[*index]); + else + return 0; + } + else if (id == t->param.idPropInfo) { + switch (*index) { + case 0: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_volume, + ":", t->param.propName, "s", "The volume", + ":", t->param.propType, "dr", p->volume, 2, 0.0, 10.0); + break; + case 1: + param = spa_pod_builder_object(&b, + id, t->param.PropInfo, + ":", t->param.propId, "I", t->prop_mute, + ":", t->param.propName, "s", "Mute", + ":", t->param.propType, "b", p->mute); + break; + default: + return 0; + } } else if (id == t->param.idProps) { - struct props *p = &this->props; - - if(*index > 0) + switch (*index) { + case 0: + param = spa_pod_builder_object(&b, + id, t->props, + ":", t->prop_volume, "d", p->volume, + ":", t->prop_mute, "b", p->mute); + break; + default: return 0; - - param = spa_pod_builder_object(&b, - id, t->props, - ":", t->prop_volume, "dr", p->volume, 2, 0.0, 10.0, - ":", t->prop_mute, "b", p->mute); + } } else return -ENOENT; diff --git a/spa/tests/test-control.c b/spa/tests/test-control.c index 9601ec7bb..9cda5e89d 100644 --- a/spa/tests/test-control.c +++ b/spa/tests/test-control.c @@ -365,7 +365,7 @@ static int make_nodes(struct data *data, const char *device) spa_pod_object_parse(param, ":", data->type.param_io.id, "I", &id, - ":", data->type.param_io.propId, "?I", &propId, + ":", data->type.param.propId, "?I", &propId, NULL); if (propId == data->type.props_freq) { diff --git a/src/examples/export-source.c b/src/examples/export-source.c index 8eebcbd2f..f973942b6 100644 --- a/src/examples/export-source.c +++ b/src/examples/export-source.c @@ -324,8 +324,8 @@ static int impl_port_enum_params(struct spa_node *node, id, t->param_io.Prop, ":", t->param_io.id, "I", d->type.io_prop_volume, ":", t->param_io.size, "i", sizeof(struct spa_pod_double), - ":", t->param_io.propId, "I", d->type.prop_volume, - ":", t->param_io.propType, "dru", p->volume, 2, 0.0, 10.0); + ":", t->param.propId, "I", d->type.prop_volume, + ":", t->param.propType, "dru", p->volume, 2, 0.0, 10.0); break; default: return 0;