mirror of
https://gitlab.freedesktop.org/pipewire/pipewire
synced 2024-11-05 16:26:16 +00:00
doc: switch the spa documentation to doxygen
This commit is contained in:
parent
9ed9980fa2
commit
46a39e0ba7
7 changed files with 178 additions and 167 deletions
|
@ -55,10 +55,10 @@ extra_docs = [
|
|||
'tutorial4.dox',
|
||||
'tutorial5.dox',
|
||||
'tutorial6.dox',
|
||||
'spa-index.md',
|
||||
'spa-design.md',
|
||||
'spa-pod.md',
|
||||
'spa-buffer.md',
|
||||
'spa-index.dox',
|
||||
'spa-design.dox',
|
||||
'spa-pod.dox',
|
||||
'spa-buffer.dox',
|
||||
'pulseaudio.md',
|
||||
]
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# SPA Buffers
|
||||
/** \page page_spa_buffer SPA Buffers
|
||||
|
||||
> What is the array of `spa_data` in `spa_buffer`?
|
||||
|
||||
|
@ -61,3 +61,5 @@ The reason why is this set up like this is that the metadata memory, the data an
|
|||
* +==============================+
|
||||
|
||||
Taken from [here](https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/11f95fe11e07192cec19fddb4fafc708e023e49c/spa/include/spa/buffer/alloc.h).
|
||||
|
||||
*/
|
|
@ -1,4 +1,4 @@
|
|||
# SPA Design
|
||||
/** \page page_spa_design SPA Design
|
||||
|
||||
SPA (Simple Plugin API) is an extensible API to implement all kinds of plugins.
|
||||
It is inspired by many other plugin APIs, mostly LV2 and GStreamer.
|
||||
|
@ -8,20 +8,20 @@ can be introspected and used at runtime in any application.
|
|||
|
||||
SPA provides the following functionality:
|
||||
|
||||
* enumeration of object factories and the interfaces provided by the objects
|
||||
* creation of objects (AKA a handle)
|
||||
* retrieve interfaces to perform actions on the objects
|
||||
- enumeration of object factories and the interfaces provided by the objects
|
||||
- creation of objects (AKA a handle)
|
||||
- retrieve interfaces to perform actions on the objects
|
||||
|
||||
SPA was designed with the following goals in mind:
|
||||
|
||||
* No dependencies, SPA is shipped as a set of header files that have no dependencies
|
||||
- No dependencies, SPA is shipped as a set of header files that have no dependencies
|
||||
except for the standard c library.
|
||||
* Very efficient both in space and in time.
|
||||
* Very configurable and usable in many different environments. All aspects of
|
||||
- Very efficient both in space and in time.
|
||||
- Very configurable and usable in many different environments. All aspects of
|
||||
the plugin environment can be configured and changed, like logging, poll loops,
|
||||
system calls etc.
|
||||
* Consistent API
|
||||
* Extensible, new API can be added with minimal effort, existing API can be
|
||||
- Consistent API
|
||||
- Extensible, new API can be added with minimal effort, existing API can be
|
||||
updated and versioned.
|
||||
|
||||
The original user of SPA is PipeWire, which uses SPA to implement the low-level
|
||||
|
@ -38,8 +38,8 @@ the API and then talks about implementing new Plugins.
|
|||
|
||||
Types are generally divided into two categories:
|
||||
|
||||
* String types: They identify interfaces and highlevel object types.
|
||||
* integer types: These are enumerations used in the parts where high
|
||||
- String types: They identify interfaces and highlevel object types.
|
||||
- integer types: These are enumerations used in the parts where high
|
||||
performance/ease of use/low space overhead is needed.
|
||||
|
||||
The SPA type is system is statis and very simple but still allows you
|
||||
|
@ -85,10 +85,10 @@ later, instead of hardcoding the plugin name.
|
|||
|
||||
To dlopen a plugin we then need to prefix the plugin path like this:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
#define SPA_PLUGIN_PATH /usr/lib64/spa-0.2/"
|
||||
void *hnd = dlopen(SPA_PLUGIN_PATH"/support/libspa-support.so", RTLD_NOW);
|
||||
```
|
||||
\endcode
|
||||
|
||||
The environment variable `SPA_PLUGIN_PATH` is usually used to find the
|
||||
location of the plugins. You will have to do some more work to construct the
|
||||
|
@ -99,10 +99,10 @@ The plugin has (should have) exactly one public symbol, called
|
|||
`SPA_HANDLE_FACTORY_ENUM_FUNC_NAME` to get some compile time checks and avoid
|
||||
typos in the symbol name. We can get the symbol like so:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
spa_handle_factory_enum_func_t enum_func;
|
||||
enum_func = dlsym(hnd, SPA_HANDLE_FACTORY_ENUM_FUNC_NAME));
|
||||
```
|
||||
\endcode
|
||||
|
||||
If this symbol is not available, this is not a valid SPA plugin.
|
||||
|
||||
|
@ -110,16 +110,16 @@ If this symbol is not available, this is not a valid SPA plugin.
|
|||
|
||||
With the `enum_func` we can now enumerate all the factories in the plugin:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
uint32_t i;
|
||||
const struct spa_handle_factory *factory = NULL;
|
||||
for (i = 0;;) {
|
||||
if (enum_func(&factory, &i) <= 0)
|
||||
break;
|
||||
/* check name and version, introspect interfaces,
|
||||
* do something with the factory. */
|
||||
// check name and version, introspect interfaces,
|
||||
// do something with the factory.
|
||||
}
|
||||
```
|
||||
\endcode
|
||||
|
||||
A factory has a version, a name, some properties and a couple of functions
|
||||
that we can check and use. The main use of a factory is to create an
|
||||
|
@ -134,11 +134,11 @@ The name of the factory is a well-known name that describes the functionality
|
|||
of the objects created from the factory. `<spa/utils/names.h>` contains
|
||||
definitions for common functionality, for example:
|
||||
|
||||
```c
|
||||
#define SPA_NAME_SUPPORT_CPU "support.cpu" /**< A CPU interface */
|
||||
#define SPA_NAME_SUPPORT_LOG "support.log" /**< A Log interface */
|
||||
#define SPA_NAME_SUPPORT_DBUS "support.dbus" /**< A DBUS interface */
|
||||
```
|
||||
\code{.c}
|
||||
#define SPA_NAME_SUPPORT_CPU "support.cpu" // A CPU interface
|
||||
#define SPA_NAME_SUPPORT_LOG "support.log" // A Log interface
|
||||
#define SPA_NAME_SUPPORT_DBUS "support.dbus" // A DBUS interface
|
||||
\endcode
|
||||
|
||||
Usually the name will be mapped to a specific plugin. This way an
|
||||
alternative compatible implementation can be made in a different library.
|
||||
|
@ -151,21 +151,23 @@ the application and the stack for storage.
|
|||
|
||||
First get the size of the required memory:
|
||||
|
||||
```c
|
||||
size_t size = spa_handle_factory_get_size(factory, NULL /* extra params */);
|
||||
```
|
||||
\code{.c}
|
||||
struct spa_dict *extra_params = NULL;
|
||||
size_t size = spa_handle_factory_get_size(factory, extra_params);
|
||||
\endcode
|
||||
|
||||
Sometimes the memory can depend on the extra parameters given in
|
||||
`_get_size()`. Next we need to allocate the memory and initialize the object
|
||||
in it:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
handle = calloc(1, size);
|
||||
spa_handle_factory_init(factory, handle,
|
||||
NULL, /* info */
|
||||
NULL, /* support */
|
||||
0 /* n_support */);
|
||||
```
|
||||
NULL, // info
|
||||
NULL, // support
|
||||
0 // n_support
|
||||
);
|
||||
\endcode
|
||||
|
||||
The info parameter should contain the same extra properties given in
|
||||
`spa_handle_factory_get_size()`.
|
||||
|
@ -180,18 +182,18 @@ certain support libraries to function.
|
|||
When a SPA handle is made, you can retrieve any of the interfaces that
|
||||
it provides:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
void *iface;
|
||||
spa_handle_get_interface(handle, SPA_NAME_SUPPORT_LOG, &iface);
|
||||
```
|
||||
\endcode
|
||||
|
||||
If this method succeeds, you can cast the `iface` variable to
|
||||
`struct spa_log *` and start using the log interface methods.
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
struct spa_log *log = iface;
|
||||
spa_log_warn(log, "Hello World!\n");
|
||||
```
|
||||
\endcode
|
||||
|
||||
|
||||
## Clearing an object
|
||||
|
@ -230,7 +232,7 @@ listeners about this.
|
|||
For example, the `struct spa_node` interface has a method to register such
|
||||
an event handler like this:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
static void node_info(void *data, const struct spa_node_info *info)
|
||||
{
|
||||
printf("got node info!\n");
|
||||
|
@ -244,7 +246,7 @@ static struct spa_node_events node_events = {
|
|||
struct spa_hook listener;
|
||||
spa_zero(listener);
|
||||
spa_node_add_listener(node, &listener, &node_event, my_data);
|
||||
```
|
||||
\endcode
|
||||
|
||||
You make a structure with pointers to the events you are interested in
|
||||
and then use `spa_node_add_listener()` to register a listener. The
|
||||
|
@ -263,9 +265,9 @@ will be checked and the new signal will be ignored for older versions.
|
|||
|
||||
You can remove your listener with:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
spa_hook_remove(&listener);
|
||||
```
|
||||
\endcode
|
||||
|
||||
## API results
|
||||
|
||||
|
@ -284,7 +286,7 @@ Here is an example of enumerating parameters on a node interface.
|
|||
|
||||
First install a listener for the result:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
static void node_result(void *data, int seq, int res,
|
||||
uint32_t type, const void *result)
|
||||
{
|
||||
|
@ -301,20 +303,20 @@ static const struct spa_node_events node_events = {
|
|||
};
|
||||
|
||||
spa_node_add_listener(node, &listener, &node_events, node);
|
||||
```
|
||||
\endcode
|
||||
|
||||
Then perform the `enum_param` method:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
int res = spa_node_enum_params(node, 0, SPA_PARAM_EnumFormat, 0, MAXINT, NULL);
|
||||
```
|
||||
\endcode
|
||||
|
||||
This triggers the result event handler with a 0 sequence number for each
|
||||
supported format. After this completes, remove the listener again:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
spa_hook_remove(&listener);
|
||||
```
|
||||
\endcode
|
||||
|
||||
|
||||
### Asynchronous results
|
||||
|
@ -323,20 +325,24 @@ Asynchronous results are pushed to the application in the same way as
|
|||
synchronous results, they are just pushed later. You can check that
|
||||
a result is asynchronous by the return value of the enum function:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
int res = spa_node_enum_params(node, 0, SPA_PARAM_EnumFormat, 0, MAXINT, NULL);
|
||||
|
||||
if (SPA_RESULT_IS_ASYNC(res)) {
|
||||
/* result will be received later */
|
||||
// result will be received later
|
||||
...
|
||||
}
|
||||
```
|
||||
\endcode
|
||||
|
||||
In the case of async results, the result callback will be called with the
|
||||
sequence number of the async result code, which can be obtained with:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
expected_seq = SPA_RESULT_ASYNC_SEQ(res);
|
||||
```
|
||||
\endcode
|
||||
|
||||
# Implementing a new plugin
|
||||
|
||||
FIXME
|
||||
|
||||
*/
|
|
@ -1,9 +1,11 @@
|
|||
# SPA (Simple Plugin API)
|
||||
/** \page page_spa SPA (Simple Plugin API)
|
||||
|
||||
SPA (Simple Plugin API) is an extensible API to implement all kinds of
|
||||
plugins. It is inspired by many other plugin APIs, mostly LV2 and
|
||||
GStreamer.
|
||||
|
||||
* SPA [Design](spa-design.md)
|
||||
* [Data format](spa-pod.md)
|
||||
* SPA [Buffers](spa-buffer.md)
|
||||
- \subpage page_spa_design
|
||||
- \subpage page_spa_pod
|
||||
- \subpage page_spa_buffer
|
||||
|
||||
*/
|
|
@ -1,4 +1,4 @@
|
|||
# SPA POD
|
||||
/** \page page_spa_pod SPA POD
|
||||
|
||||
POD (plain old data) is a sort of data container. It is comparable to
|
||||
DBus Variant or LV2 Atom.
|
||||
|
@ -23,30 +23,30 @@ the SPA types for more info.
|
|||
|
||||
PODs 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
|
||||
- `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
|
||||
|
||||
PODs 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 PODs
|
||||
- `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 PODs
|
||||
|
||||
PODs 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
|
||||
- `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
|
||||
|
||||
|
@ -62,36 +62,36 @@ appropriate error will be generated.
|
|||
The code fragment below initializes a pod builder to write into
|
||||
the stack allocated buffer.
|
||||
|
||||
```c
|
||||
\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.
|
||||
|
||||
```c
|
||||
\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:
|
||||
|
||||
```c
|
||||
\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:
|
||||
|
||||
```c
|
||||
\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.
|
||||
|
@ -100,21 +100,21 @@ on the stack.
|
|||
|
||||
We can also use the following construct to make POD objects:
|
||||
|
||||
```c
|
||||
\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:
|
||||
|
||||
```c
|
||||
\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.
|
||||
|
@ -126,9 +126,9 @@ objects.
|
|||
|
||||
Start by pushing an object:
|
||||
|
||||
```c
|
||||
\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
|
||||
|
@ -137,12 +137,12 @@ make this connection (See the type system).
|
|||
|
||||
Next we can push some properties in the object:
|
||||
|
||||
```c
|
||||
\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
|
||||
|
@ -150,18 +150,18 @@ idea to always push (and parse) the object keys in ascending order.
|
|||
|
||||
Don't forget to pop the result when the object is finished:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
pod = spa_pod_builder_pop(&b, &f);
|
||||
```
|
||||
\endcode
|
||||
|
||||
There is a shortcut for making objects:
|
||||
|
||||
```c
|
||||
\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
|
||||
|
||||
|
@ -182,54 +182,54 @@ interpreted in different ways:
|
|||
Let's illustrate this with a Props object that specifies a range of
|
||||
possible values for the frequency:
|
||||
|
||||
```c
|
||||
\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 */
|
||||
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 3 values, the default value, minimum and maximum
|
||||
values. There is a shortcut for this as well using varargs:
|
||||
|
||||
```c
|
||||
\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:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
pod = spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
|
||||
/* specify the media type and subtype */
|
||||
// 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 */
|
||||
// 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_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 */
|
||||
44100, // default
|
||||
8000, // min
|
||||
192000 // max
|
||||
),
|
||||
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(2));
|
||||
```
|
||||
\endcode
|
||||
|
||||
## Fixate
|
||||
|
||||
|
@ -241,17 +241,17 @@ only available value in the choice.
|
|||
Running fixate on our previous example would result in an object equivalent
|
||||
to:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
pod = spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
|
||||
/* specify the media type and subtype */
|
||||
// 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 */
|
||||
// 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
|
||||
|
||||
|
@ -268,10 +268,10 @@ 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.
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
struct spa_pod *pod;
|
||||
pod = spa_pod_from_data(data, maxsize, offset, size);
|
||||
```
|
||||
\endcode
|
||||
|
||||
## Checking the type of POD
|
||||
|
||||
|
@ -287,12 +287,12 @@ an object of the expected type.
|
|||
|
||||
To iterate over the fields of a Struct use:
|
||||
|
||||
```c
|
||||
\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.
|
||||
|
@ -301,25 +301,25 @@ below.
|
|||
|
||||
To iterate over the properties in an object you can do:
|
||||
|
||||
```c
|
||||
\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.
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
struct spa_pod_prop *prop;
|
||||
prop = spa_pod_find_prop(obj, NULL, SPA_FORMAT_AUDIO_format);
|
||||
/* .. use first prop */
|
||||
// .. use first prop
|
||||
prop = spa_pod_find_prop(obj, prop, SPA_FORMAT_AUDIO_rate);
|
||||
/* .. use next prop */
|
||||
```
|
||||
// .. use next prop
|
||||
\endcode
|
||||
|
||||
## Parser
|
||||
|
||||
|
@ -331,18 +331,18 @@ the parser is easier.
|
|||
|
||||
First initialize a `struct spa_pod_parser`:
|
||||
|
||||
```c
|
||||
\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:
|
||||
|
||||
```c
|
||||
\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.
|
||||
|
@ -350,18 +350,18 @@ to exit the container again later.
|
|||
You can then parse each field. The parser takes care of moving to the
|
||||
next field.
|
||||
|
||||
```c
|
||||
\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:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
spa_pod_parser_pop(&p, &f);
|
||||
```
|
||||
\endcode
|
||||
|
||||
## Parser with variable arguments
|
||||
|
||||
|
@ -371,15 +371,15 @@ functions.
|
|||
|
||||
To parse a struct:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
spa_pod_parser_get_struct(&p,
|
||||
SPA_POD_Id(&id),
|
||||
SPA_POD_Int(&val));
|
||||
```
|
||||
\endcode
|
||||
|
||||
To parse properties in an object:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
uint32_t type, subtype, format, rate, channels;
|
||||
spa_pod_parser_get_object(&p,
|
||||
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
|
||||
|
@ -388,7 +388,7 @@ spa_pod_parser_get_object(&p,
|
|||
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
|
||||
|
@ -397,7 +397,7 @@ 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.
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
uint32_t type, subtype, format, rate = 0, channels = 0;
|
||||
spa_pod_parser_get_object(&p,
|
||||
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
|
||||
|
@ -406,7 +406,7 @@ spa_pod_parser_get_object(&p,
|
|||
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.
|
||||
|
@ -421,7 +421,7 @@ manually, if needed.
|
|||
|
||||
Here is an example of parsing the format values as a POD:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
uint32_t type, subtype;
|
||||
struct spa_pod *format;
|
||||
spa_pod_parser_get_object(&p,
|
||||
|
@ -429,7 +429,7 @@ spa_pod_parser_get_object(&p,
|
|||
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 PODs
|
||||
|
@ -437,7 +437,7 @@ and Choice None values, it simply returns the POD and 1 value.
|
|||
For other Choice values it returns the Choice type and an array
|
||||
of values:
|
||||
|
||||
```c
|
||||
\code{.c}
|
||||
struct spa_pod *value;
|
||||
uint32_t n_vals, choice;
|
||||
|
||||
|
@ -445,11 +445,11 @@ value = spa_pod_get_values(pod, &n_vals, &choice);
|
|||
|
||||
switch (choice) {
|
||||
case SPA_CHOICE_None:
|
||||
/* one single value */
|
||||
// one single value
|
||||
break;
|
||||
case SPA_CHOICE_Range:
|
||||
/* array of values of type of pod, cast to right type
|
||||
* to iterate. */
|
||||
// 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;
|
||||
|
@ -458,11 +458,11 @@ case SPA_CHOICE_Range:
|
|||
printf("max value: %u\n", v[2]);
|
||||
break;
|
||||
|
||||
/* ... */
|
||||
// ...
|
||||
default:
|
||||
break;
|
||||
}
|
||||
```
|
||||
\endcode
|
||||
|
||||
# Filter
|
||||
|
||||
|
@ -474,16 +474,16 @@ This is, for example, used to find a compatible format between two ports.
|
|||
|
||||
As an example we can run a filter on two simple PODs:
|
||||
|
||||
```c
|
||||
\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 */
|
||||
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,
|
||||
|
@ -491,25 +491,25 @@ filter = spa_pod_builder_add_object(&b,
|
|||
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 */
|
||||
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:
|
||||
|
||||
```c
|
||||
\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
|
||||
|
||||
|
@ -519,3 +519,4 @@ field specifies the size following the type field.
|
|||
Each POD is aligned to an 8 byte boundary.
|
||||
|
||||
|
||||
*/
|
|
@ -206,7 +206,7 @@ which means that it enumerates the possible formats for this stream. We have
|
|||
only one, a Signed 16 bit stereo format at 44.1KHz.
|
||||
|
||||
We use `spa_format_audio_raw_build()` which is a helper function to make the param
|
||||
with the builder. See [SPA POD](spa-pod.md) for more information about how to
|
||||
with the builder. See \ref page_spa_pod for more information about how to
|
||||
make these POD objects.
|
||||
|
||||
Now we're ready to connect the stream and run the main loop:
|
||||
|
@ -289,7 +289,7 @@ static void on_process(void *userdata)
|
|||
}
|
||||
\endcode
|
||||
|
||||
Check out the docs for [buffers](spa-buffer.md) for more information
|
||||
Check out the docs for \ref page_spa_buffer for more information
|
||||
about how to work with buffers.
|
||||
|
||||
Try to change the number of channels, samplerate or format; the stream
|
||||
|
|
|
@ -243,7 +243,7 @@ We have something similar for the framerate.
|
|||
Note that there are other video parameters that we don't specify here. This
|
||||
means that we don't have any restrictions for their values.
|
||||
|
||||
See [SPA POD](spa-pod.md) for more information about how to make these
|
||||
See \ref page_spa_pod for more information about how to make these
|
||||
POD objects.
|
||||
|
||||
Now we're ready to connect the stream and run the main loop:
|
||||
|
|
Loading…
Reference in a new issue