diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:28:17 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:28:17 +0000 |
commit | 7a46c07230b8d8108c0e8e80df4522d0ac116538 (patch) | |
tree | d483300dab478b994fe199a5d19d18d74153718a /doc/spa-pod.dox | |
parent | Initial commit. (diff) | |
download | pipewire-upstream/0.3.65.tar.xz pipewire-upstream/0.3.65.zip |
Adding upstream version 0.3.65.upstream/0.3.65upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'doc/spa-pod.dox')
-rw-r--r-- | doc/spa-pod.dox | 530 |
1 files changed, 530 insertions, 0 deletions
diff --git a/doc/spa-pod.dox b/doc/spa-pod.dox new file mode 100644 index 0000000..0c88bb7 --- /dev/null +++ b/doc/spa-pod.dox @@ -0,0 +1,530 @@ +/** \page page_spa_pod SPA POD + +\ref spa_pod (plain old data) is a sort of data container. It is comparable to +DBus Variant or LV2 Atom. + +A POD can express nested structures of objects (with properties), vectors, +arrays, sequences and various primitives types. All information in the POD +is laid out sequentially in memory and can be written directly to +storage or exchanged between processes or threads without additional +marshalling. + +Each POD is made of a 32 bits size followed by a 32 bits type field, +followed by the POD contents. This makes it possible to skip over unknown +POD types. The POD start is always aligned to 8 bytes. + +POD's can be efficiently constructed and parsed in real-time threads without +requiring memory allocations. + +POD's use the SPA type system for the basic types and containers. See +the SPA types for more info. + + +# Types + +POD's can contain a number of basic SPA types: + +- `SPA_TYPE_None`: No value or a NULL pointer. +- `SPA_TYPE_Bool`: A boolean value. +- `SPA_TYPE_Id`: An enumerated value. +- `SPA_TYPE_Int`, `SPA_TYPE_Long`, `SPA_TYPE_Float`, `SPA_TYPE_Double`: + various numeral types, 32 and 64 bits. +- `SPA_TYPE_String`: A string. +- `SPA_TYPE_Bytes`: A byte array. +- `SPA_TYPE_Rectangle`: A rectangle with width and height. +- `SPA_TYPE_Fraction`: A fraction with numerator and denominator. +- `SPA_TYPE_Bitmap`: An array of bits. + +POD's can be grouped together in these container types: + +- `SPA_TYPE_Array`: An array of equal sized objects. +- `SPA_TYPE_Struct`: A collection of types and objects. +- `SPA_TYPE_Object`: An object with properties. +- `SPA_TYPE_Sequence`: A timed sequence of POD's. + +POD's can also contain some extra types: + +- `SPA_TYPE_Pointer`: A typed pointer in memory. +- `SPA_TYPE_Fd`: A file descriptor. +- `SPA_TYPE_Choice`: A choice of values. +- `SPA_TYPE_Pod`: A generic type for the POD itself. + + +# Constructing A POD + +A POD is usually constructed with a `struct spa_pod_builder`. The builder +needs to be initialized with a memory region to write into. It is +also possible to dynamically grow the memory as needed. + +The most common way to construct a POD is on the stack. This does +not require any memory allocations. The size of the POD can be +estimated pretty easily and if the buffer is not large enough, an +appropriate error will be generated. + +The code fragment below initializes a POD builder to write into +the stack allocated buffer. + +\code{.c} +uint8_t buffer[4096]; +struct spa_pod_builder b; +spa_pod_builder_init(&b, buffer, sizeof(buffer)); +\endcode + +Next we need to write some object into the builder. Let's write +a simple struct with an Int and Float in it. Structs are comparable +to JSON arrays. + +\code{.c} +struct spa_pod_frame f; +spa_pod_builder_push_struct(&b, &f); +\endcode + +First we open the struct container, the `struct spa_pod_frame` keeps +track of the container context. Next we add some values to +the container like this: + +\code{.c} +spa_pod_builder_int(&b, 5); +spa_pod_builder_float(&b, 3.1415f); +\endcode + +Then we close the container by popping the frame again: + +\code{.c} +struct spa_pod *pod; +pod = spa_pod_builder_pop(&b, &f); +\endcode + +`spa_pod_builder_pop()` returns a reference to the object we completed +on the stack. + +## Using varargs Builder + +We can also use the following construct to make POD objects: + +\code{.c} +spa_pod_builder_push_struct(&b, &f); +spa_pod_builder_add(&b, + SPA_POD_Int(5), + SPA_POD_Float(3.1415f)); +pod = spa_pod_builder_pop(&b, &f); +\endcode + +Or even shorter: + +\code{.c} +pod = spa_pod_builder_add_struct(&b, + SPA_POD_Int(5), + SPA_POD_Float(3.1415f)); +\endcode + +It's not possible to use the varargs builder to make a sequence or +array, use the normal builder methods for that. + +## Making Objects + +POD objects are containers for properties and are comparable to JSON +objects. + +Start by pushing an object: + +\code{.c} +spa_pod_builder_push_object(&b, &f, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props); +\endcode + +An object requires an object type (`SPA_TYPE_OBJECT_Props`) and a context +ID (`SPA_PARAM_Props`). The object type defines the properties that can be +added to the object and their meaning. The SPA type system allows you to +make this connection (See the type system). + +Next we can push some properties in the object: + +\code{.c} +spa_pod_builder_prop(&b, SPA_PROP_device, 0); +spa_pod_builder_string(&b, "hw:0"); +spa_pod_builder_prop(&b, SPA_PROP_frequency, 0); +spa_pod_builder_float(&b, 440.0); +\endcode + +As can be seen, we always need to push a prop (with key and flags) +and then the associated value. For performance reasons it is a good +idea to always push (and parse) the object keys in ascending order. + +Don't forget to pop the result when the object is finished: + +\code{.c} +pod = spa_pod_builder_pop(&b, &f); +\endcode + +There is a shortcut for making objects: + +\code{.c} +pod = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_device, SPA_POD_String("hw:0"), + SPA_PROP_frequency, SPA_POD_Float(440.0f)); +\endcode + +## Choice Values + +It is possible to express ranges or enumerations of possible +values for properties (and to some extend structs). This is achieved +with choice values. + +Choice values are really just a choice type and an array of choice values +(of the same type). Depending on the choice type, the array values are +interpreted in different ways: + +- `SPA_CHOICE_None`: No choice, first value is current. +- `SPA_CHOICE_Range`: Range: default, min, max. +- `SPA_CHOICE_Step`: Range with step: default, min, max, step. +- `SPA_CHOICE_Enum`: Enum: default, alternative,... +- `SPA_CHOICE_Flags`: Bitmask of flags. + +Let's illustrate this with a props object that specifies a range of +possible values for the frequency: + +\code{.c} +struct spa_pod_frame f2; + +spa_pod_builder_push_object(&b, &f, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props); +spa_pod_builder_prop(&b, SPA_PROP_frequency, 0); +spa_pod_builder_push_choice(&b, &f2, SPA_CHOICE_Range, 0); +spa_pod_builder_float(&b, 440.0); // default +spa_pod_builder_float(&b, 110.0); // min +spa_pod_builder_float(&b, 880.0); // min +pod = spa_pod_builder_pop(&b, &f2); +pod = spa_pod_builder_pop(&b, &f); +\endcode + +As you can see, first push the choice as a range, then the values. A range +choice expects at least three values, the default value, minimum and maximum +values. There is a shortcut for this as well using varargs: + +\code{.c} +pod = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_frequency, SPA_POD_CHOICE_RANGE_Float(440.0f, 110.0f, 880.0f)); +\endcode + +## Choice Examples + +This is a description of a possible `SPA_TYPE_OBJECT_Format` as used when +enumerating allowed formats (`SPA_PARAM_EnumFormat`) in SPA objects: + +\code{.c} +pod = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, + // specify the media type and subtype + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + // audio/raw properties + SPA_FORMAT_AUDIO_format, SPA_POD_CHOICE_ENUM_Id( + SPA_AUDIO_FORMAT_S16, // default + SPA_AUDIO_FORMAT_S16, // alternative1 + SPA_AUDIO_FORMAT_S32, // alternative2 + SPA_AUDIO_FORMAT_f32 // alternative3 + ), + SPA_FORMAT_AUDIO_rate, SPA_POD_CHOICE_RANGE_Int( + 44100, // default + 8000, // min + 192000 // max + ), + SPA_FORMAT_AUDIO_channels, SPA_POD_Int(2)); +\endcode + +## Fixate + +We can remove all choice values from the object with the +`spa_pod_object_fixate()` method. This modifies the pod in-place and sets all +choice properties to `SPA_CHOICE_None`, forcing the default value as the +only available value in the choice. + +Running fixate on our previous example would result in an object equivalent +to: + +\code{.c} +pod = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, + // specify the media type and subtype + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + // audio/raw properties + SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_S16), + SPA_FORMAT_AUDIO_rate, SPA_POD_Int(44100), + SPA_FORMAT_AUDIO_channels, SPA_POD_Int(2)); +\endcode + + +# Parsing A POD + +Parsing a POD usually consists of: + +- Validating if raw bytes + size can contain a valid POD. +- Inspecting the type of a POD. +- Looping over the items in an object or struct. +- Getting data out of POD's. + +## Validating Bytes + +Use `spa_pod_from_data()` to check if maxsize of bytes in data contain +a POD at the size bytes starting at offset. This function checks that +the POD size will fit and not overflow. + +\code{.c} +struct spa_pod *pod; +pod = spa_pod_from_data(data, maxsize, offset, size); +\endcode + +## Checking The Type Of POD + +Use one of `spa_pod_is_bool()`, `spa_pod_is_int()`, etc to check +for the type of the pod. For simple (non-container) types, +`spa_pod_get_bool()`, `spa_pod_get_int()` etc can be used to +extract the value of the pod. + +`spa_pod_is_object_type()` can be used to check if the POD contains +an object of the expected type. + +## Struct Fields + +To iterate over the fields of a struct use: + +\code{.c} +struct spa_pod *pod, *obj; +SPA_POD_STRUCT_FOREACH(obj, pod) { + printf("field type:%d\n", pod->type); +} +\endcode + +For parsing structs it is usually much easier to use the parser +below. + +## Object Properties + +To iterate over the properties in an object you can do: + +\code{.c} +struct spa_pod_prop *prop; +struct spa_pod_object *obj = (struct spa_pod_object*)pod; +SPA_POD_OBJECT_FOREACH(pod, prop) { + printf("prop key:%d\n", prop->key); +} +\endcode + +There is a function to retrieve the property for a certain key +in the object. If the properties of the object are in ascending +order, you can start searching from the previous key. + +\code{.c} +struct spa_pod_prop *prop; +prop = spa_pod_find_prop(obj, NULL, SPA_FORMAT_AUDIO_format); + // .. use first prop +prop = spa_pod_find_prop(obj, prop, SPA_FORMAT_AUDIO_rate); + // .. use next prop +\endcode + +## Parser + +Similar to the builder, there is a parser object as well. + +If the fields in a struct are known, it is much easier to use the +parser. Similarly, if the object type (and thus its keys) are known, +the parser is easier. + +First initialize a `struct spa_pod_parser`: + +\code{.c} +struct spa_pod_parser p; +spa_pod_parser_pod(&p, obj); +\endcode + +You can then enter containers such as objects or structs with a push +operation: + +\code{.c} +struct spa_pod_frame f; +spa_pod_parser_push_struct(&p, &f); +\endcode + +You need to store the context in a `struct spa_pod_frame` to be able +to exit the container again later. + +You can then parse each field. The parser takes care of moving to the +next field. + +\code{.c} +uint32_t id, val; +spa_pod_parser_get_id(&p, &id); +spa_pod_parser_get_int(&p, &val); +... +\endcode + +And finally exit the container again: + +\code{.c} +spa_pod_parser_pop(&p, &f); +\endcode + +## Parser With Variable Arguments + +In most cases, parsing objects is easier with the variable argument +functions. The parse function look like the mirror image of the builder +functions. + +To parse a struct: + +\code{.c} +spa_pod_parser_get_struct(&p, + SPA_POD_Id(&id), + SPA_POD_Int(&val)); +\endcode + +To parse properties in an object: + +\code{.c} +uint32_t type, subtype, format, rate, channels; +spa_pod_parser_get_object(&p, + SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, + SPA_FORMAT_mediaType, SPA_POD_Id(&type), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(&subtype), + SPA_FORMAT_AUDIO_format, SPA_POD_Id(&format), + SPA_FORMAT_AUDIO_rate, SPA_POD_Int(&rate), + SPA_FORMAT_AUDIO_channels, SPA_POD_Int(&channels)); +\endcode + +When parsing objects it is possible to have optional fields. You can +make a field optional be parsing it with the `SPA_POD_OPT_` prefix +for the type. + +In the next example, the rate and channels fields are optional +and when they are not present, the variables will not be changed. + +\code{.c} +uint32_t type, subtype, format, rate = 0, channels = 0; +spa_pod_parser_get_object(&p, + SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, + SPA_FORMAT_mediaType, SPA_POD_Id(&type), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(&subtype), + SPA_FORMAT_AUDIO_format, SPA_POD_Id(&format), + SPA_FORMAT_AUDIO_rate, SPA_POD_OPT_Int(&rate), + SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&channels)); +\endcode + +It is not possible to parse a sequence or array with the parser. +Use the iterator for this. + +## Choice Values + +The parser will handle choice values as long as they are of type +`none`. It will then parse the single value from the choice. When +dealing with other choice values, it's possible to parse the +property values into a `struct spa_pod` and then inspect the choice +manually, if needed. + +Here is an example of parsing the format values as a POD: + +\code{.c} +uint32_t type, subtype; +struct spa_pod *format; +spa_pod_parser_get_object(&p, + SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, + SPA_FORMAT_mediaType, SPA_POD_Id(&type), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(&subtype), + SPA_FORMAT_AUDIO_format, SPA_POD_Pod(&format)); +\endcode + +`spa_pod_get_values()` is a useful function. It returns a +`struct spa_pod*` with and array of values. For normal POD's +and choice none values, it simply returns the POD and one value. +For other choice values it returns the choice type and an array +of values: + +\code{.c} +struct spa_pod *value; +uint32_t n_vals, choice; + +value = spa_pod_get_values(pod, &n_vals, &choice); + +switch (choice) { +case SPA_CHOICE_None: + // one single value + break; +case SPA_CHOICE_Range: + // array of values of type of pod, cast to right type + // to iterate. + uint32_t *v = SPA_POD_BODY(values); + if (n_vals < 3) + break; + printf("default value: %u\n", v[0]); + printf("min value: %u\n", v[1]); + printf("max value: %u\n", v[2]); + break; + + // ... +default: + break; +} +\endcode + + +# Filter + +Given two POD objects of the same type (object, struct, ..) one can +run a filter and generate a new POD that only contains values that +are compatible with both input POD's. + +This is, for example, used to find a compatible format between two ports. + +As an example we can run a filter on two simple POD's: + +\code{.c} +pod = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + SPA_FORMAT_AUDIO_format, SPA_POD_CHOICE_ENUM_Id( + SPA_AUDIO_FORMAT_S16, // default + SPA_AUDIO_FORMAT_S16, // alternative1 + SPA_AUDIO_FORMAT_S32, // alternative2 + SPA_AUDIO_FORMAT_f32 // alternative3 + )); + +filter = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + SPA_FORMAT_AUDIO_format, SPA_POD_CHOICE_ENUM_Id( + SPA_AUDIO_FORMAT_S16, // default + SPA_AUDIO_FORMAT_S16, // alternative1 + SPA_AUDIO_FORMAT_f64 // alternative2 + )); + +struct spa_pod *result; +if (spa_pod_filter(&b, &result, pod, filter) < 0) + goto exit_error; +\endcode + +Filter will contain a POD equivalent to: + +\code{.c} +result = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + SPA_FORMAT_AUDIO_format, SPA_AUDIO_FORMAT_S16); +\endcode + +# POD Layout + +Each POD has a 32 bits size field, followed by a 32 bits type field. The size +field specifies the size following the type field. + +Each POD is aligned to an 8 byte boundary. + + +\addtogroup spa_pod + +See: \ref page_spa_pod + +*/ |