mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
spa: make it possible to extend the spa_audio_info struct
Add functions that take the size of the spa_audio_info struct in various functions. We can use this to determine how many channels and channel positions we can store. Error out if we try to use more channels than we can fit positions. This is probably the safest thing to do because most code will blindly try to get the positions without checking the channel count. Make sure we also propagate errors to the callers.
This commit is contained in:
parent
c5533b3c32
commit
b8eeb2db45
4 changed files with 124 additions and 46 deletions
|
|
@ -47,7 +47,7 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SPA_API_AUDIO_FORMAT_UTILS int
|
SPA_API_AUDIO_FORMAT_UTILS int
|
||||||
spa_format_audio_parse(const struct spa_pod *format, struct spa_audio_info *info)
|
spa_format_audio_ext_parse(const struct spa_pod *format, struct spa_audio_info *info, size_t size)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
|
|
@ -59,7 +59,7 @@ spa_format_audio_parse(const struct spa_pod *format, struct spa_audio_info *info
|
||||||
|
|
||||||
switch (info->media_subtype) {
|
switch (info->media_subtype) {
|
||||||
case SPA_MEDIA_SUBTYPE_raw:
|
case SPA_MEDIA_SUBTYPE_raw:
|
||||||
return spa_format_audio_raw_parse(format, &info->info.raw);
|
return spa_format_audio_raw_ext_parse(format, &info->info.raw, size);
|
||||||
case SPA_MEDIA_SUBTYPE_dsp:
|
case SPA_MEDIA_SUBTYPE_dsp:
|
||||||
return spa_format_audio_dsp_parse(format, &info->info.dsp);
|
return spa_format_audio_dsp_parse(format, &info->info.dsp);
|
||||||
case SPA_MEDIA_SUBTYPE_iec958:
|
case SPA_MEDIA_SUBTYPE_iec958:
|
||||||
|
|
@ -98,13 +98,19 @@ spa_format_audio_parse(const struct spa_pod *format, struct spa_audio_info *info
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SPA_API_AUDIO_FORMAT_UTILS int
|
||||||
|
spa_format_audio_parse(const struct spa_pod *format, struct spa_audio_info *info)
|
||||||
|
{
|
||||||
|
return spa_format_audio_ext_parse(format, info, sizeof(*info));
|
||||||
|
}
|
||||||
|
|
||||||
SPA_API_AUDIO_FORMAT_UTILS struct spa_pod *
|
SPA_API_AUDIO_FORMAT_UTILS struct spa_pod *
|
||||||
spa_format_audio_build(struct spa_pod_builder *builder, uint32_t id,
|
spa_format_audio_ext_build(struct spa_pod_builder *builder, uint32_t id,
|
||||||
const struct spa_audio_info *info)
|
const struct spa_audio_info *info, size_t size)
|
||||||
{
|
{
|
||||||
switch (info->media_subtype) {
|
switch (info->media_subtype) {
|
||||||
case SPA_MEDIA_SUBTYPE_raw:
|
case SPA_MEDIA_SUBTYPE_raw:
|
||||||
return spa_format_audio_raw_build(builder, id, &info->info.raw);
|
return spa_format_audio_raw_ext_build(builder, id, &info->info.raw, size);
|
||||||
case SPA_MEDIA_SUBTYPE_dsp:
|
case SPA_MEDIA_SUBTYPE_dsp:
|
||||||
return spa_format_audio_dsp_build(builder, id, &info->info.dsp);
|
return spa_format_audio_dsp_build(builder, id, &info->info.dsp);
|
||||||
case SPA_MEDIA_SUBTYPE_iec958:
|
case SPA_MEDIA_SUBTYPE_iec958:
|
||||||
|
|
@ -143,6 +149,13 @@ spa_format_audio_build(struct spa_pod_builder *builder, uint32_t id,
|
||||||
errno = ENOTSUP;
|
errno = ENOTSUP;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SPA_API_AUDIO_FORMAT_UTILS struct spa_pod *
|
||||||
|
spa_format_audio_build(struct spa_pod_builder *builder, uint32_t id,
|
||||||
|
const struct spa_audio_info *info)
|
||||||
|
{
|
||||||
|
return spa_format_audio_ext_build(builder, id, info, sizeof(*info));
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* \}
|
* \}
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ spa_audio_parse_position_n(const char *str, size_t len,
|
||||||
*n_channels = channels;
|
*n_channels = channels;
|
||||||
return channels;
|
return channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
SPA_API_AUDIO_RAW_JSON int
|
SPA_API_AUDIO_RAW_JSON int
|
||||||
spa_audio_parse_position(const char *str, size_t len,
|
spa_audio_parse_position(const char *str, size_t len,
|
||||||
uint32_t *position, uint32_t *n_channels)
|
uint32_t *position, uint32_t *n_channels)
|
||||||
|
|
@ -54,9 +55,11 @@ spa_audio_parse_position(const char *str, size_t len,
|
||||||
}
|
}
|
||||||
|
|
||||||
SPA_API_AUDIO_RAW_JSON int
|
SPA_API_AUDIO_RAW_JSON int
|
||||||
spa_audio_info_raw_update(struct spa_audio_info_raw *info, const char *key, const char *val, bool force)
|
spa_audio_info_raw_ext_update(struct spa_audio_info_raw *info, size_t size,
|
||||||
|
const char *key, const char *val, bool force)
|
||||||
{
|
{
|
||||||
uint32_t v;
|
uint32_t v;
|
||||||
|
uint32_t max_position = SPA_AUDIO_INFO_RAW_MAX_POSITION(size);
|
||||||
if (spa_streq(key, SPA_KEY_AUDIO_FORMAT)) {
|
if (spa_streq(key, SPA_KEY_AUDIO_FORMAT)) {
|
||||||
if (force || info->format == 0)
|
if (force || info->format == 0)
|
||||||
info->format = (enum spa_audio_format)spa_type_audio_format_from_short_name(val);
|
info->format = (enum spa_audio_format)spa_type_audio_format_from_short_name(val);
|
||||||
|
|
@ -64,42 +67,84 @@ spa_audio_info_raw_update(struct spa_audio_info_raw *info, const char *key, cons
|
||||||
if (spa_atou32(val, &v, 0) && (force || info->rate == 0))
|
if (spa_atou32(val, &v, 0) && (force || info->rate == 0))
|
||||||
info->rate = v;
|
info->rate = v;
|
||||||
} else if (spa_streq(key, SPA_KEY_AUDIO_CHANNELS)) {
|
} else if (spa_streq(key, SPA_KEY_AUDIO_CHANNELS)) {
|
||||||
if (spa_atou32(val, &v, 0) && (force || info->channels == 0))
|
if (spa_atou32(val, &v, 0) && (force || info->channels == 0)) {
|
||||||
|
if (v > max_position)
|
||||||
|
return -ECHRNG;
|
||||||
info->channels = v;
|
info->channels = v;
|
||||||
|
}
|
||||||
} else if (spa_streq(key, SPA_KEY_AUDIO_POSITION)) {
|
} else if (spa_streq(key, SPA_KEY_AUDIO_POSITION)) {
|
||||||
if (force || info->channels == 0) {
|
if (force || info->channels == 0) {
|
||||||
if (spa_audio_parse_position_n(val, strlen(val), info->position,
|
if (spa_audio_parse_position_n(val, strlen(val), info->position,
|
||||||
SPA_N_ELEMENTS(info->position), &info->channels) > 0)
|
max_position, &v) > 0) {
|
||||||
|
if (v > max_position)
|
||||||
|
return -ECHRNG;
|
||||||
SPA_FLAG_CLEAR(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
|
SPA_FLAG_CLEAR(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SPA_API_AUDIO_RAW_JSON int
|
||||||
|
spa_audio_info_raw_update(struct spa_audio_info_raw *info,
|
||||||
|
const char *key, const char *val, bool force)
|
||||||
|
{
|
||||||
|
return spa_audio_info_raw_ext_update(info, sizeof(*info), key, val, force);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPA_API_AUDIO_RAW_JSON int
|
||||||
|
spa_audio_info_raw_ext_init_dict_keys_va(struct spa_audio_info_raw *info, size_t size,
|
||||||
|
const struct spa_dict *defaults,
|
||||||
|
const struct spa_dict *dict, va_list args)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
|
memset(info, 0, size);
|
||||||
|
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
|
||||||
|
if (dict) {
|
||||||
|
const char *val, *key;
|
||||||
|
while ((key = va_arg(args, const char *))) {
|
||||||
|
if ((val = spa_dict_lookup(dict, key)) == NULL)
|
||||||
|
continue;
|
||||||
|
if ((res = spa_audio_info_raw_ext_update(info, size,
|
||||||
|
key, val, true)) < 0)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (defaults) {
|
||||||
|
const struct spa_dict_item *it;
|
||||||
|
spa_dict_for_each(it, defaults)
|
||||||
|
if ((res = spa_audio_info_raw_ext_update(info, size,
|
||||||
|
it->key, it->value, false)) < 0)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPA_API_AUDIO_RAW_JSON int SPA_SENTINEL
|
||||||
|
spa_audio_info_raw_ext_init_dict_keys(struct spa_audio_info_raw *info, size_t size,
|
||||||
|
const struct spa_dict *defaults,
|
||||||
|
const struct spa_dict *dict, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
int res;
|
||||||
|
va_start(args, dict);
|
||||||
|
res = spa_audio_info_raw_ext_init_dict_keys_va(info, size, defaults, dict, args);
|
||||||
|
va_end(args);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
SPA_API_AUDIO_RAW_JSON int SPA_SENTINEL
|
SPA_API_AUDIO_RAW_JSON int SPA_SENTINEL
|
||||||
spa_audio_info_raw_init_dict_keys(struct spa_audio_info_raw *info,
|
spa_audio_info_raw_init_dict_keys(struct spa_audio_info_raw *info,
|
||||||
const struct spa_dict *defaults,
|
const struct spa_dict *defaults,
|
||||||
const struct spa_dict *dict, ...)
|
const struct spa_dict *dict, ...)
|
||||||
{
|
{
|
||||||
spa_zero(*info);
|
va_list args;
|
||||||
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
|
int res;
|
||||||
if (dict) {
|
va_start(args, dict);
|
||||||
const char *val, *key;
|
res = spa_audio_info_raw_ext_init_dict_keys_va(info, sizeof(*info), defaults, dict, args);
|
||||||
va_list args;
|
va_end(args);
|
||||||
va_start(args, dict);
|
return res;
|
||||||
while ((key = va_arg(args, const char *))) {
|
|
||||||
if ((val = spa_dict_lookup(dict, key)) == NULL)
|
|
||||||
continue;
|
|
||||||
spa_audio_info_raw_update(info, key, val, true);
|
|
||||||
}
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
if (defaults) {
|
|
||||||
const struct spa_dict_item *it;
|
|
||||||
spa_dict_for_each(it, defaults)
|
|
||||||
spa_audio_info_raw_update(info, it->key, it->value, false);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -58,10 +58,12 @@ spa_format_audio_raw_copy_positions(const struct spa_audio_info_raw *info, uint3
|
||||||
}
|
}
|
||||||
|
|
||||||
SPA_API_AUDIO_RAW_UTILS int
|
SPA_API_AUDIO_RAW_UTILS int
|
||||||
spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_raw *info)
|
spa_format_audio_raw_ext_parse(const struct spa_pod *format, struct spa_audio_info_raw *info, size_t size)
|
||||||
{
|
{
|
||||||
struct spa_pod *position = NULL;
|
struct spa_pod *position = NULL;
|
||||||
int res;
|
int res;
|
||||||
|
uint32_t max_position = SPA_AUDIO_INFO_RAW_MAX_POSITION(size);
|
||||||
|
|
||||||
info->flags = 0;
|
info->flags = 0;
|
||||||
res = spa_pod_parse_object(format,
|
res = spa_pod_parse_object(format,
|
||||||
SPA_TYPE_OBJECT_Format, NULL,
|
SPA_TYPE_OBJECT_Format, NULL,
|
||||||
|
|
@ -69,19 +71,29 @@ spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_r
|
||||||
SPA_FORMAT_AUDIO_rate, SPA_POD_OPT_Int(&info->rate),
|
SPA_FORMAT_AUDIO_rate, SPA_POD_OPT_Int(&info->rate),
|
||||||
SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels),
|
SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels),
|
||||||
SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position));
|
SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position));
|
||||||
|
if (info->channels > max_position)
|
||||||
|
return -ENOTSUP;
|
||||||
if (position == NULL ||
|
if (position == NULL ||
|
||||||
!spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_N_ELEMENTS(info->position)))
|
spa_pod_copy_array(position, SPA_TYPE_Id, info->position, max_position) != info->channels)
|
||||||
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
|
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
SPA_API_AUDIO_RAW_UTILS struct spa_pod *
|
SPA_API_AUDIO_RAW_UTILS int
|
||||||
spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id,
|
spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_raw *info)
|
||||||
const struct spa_audio_info_raw *info)
|
|
||||||
{
|
{
|
||||||
struct spa_pod_frame f[2];
|
return spa_format_audio_raw_ext_parse(format, info, sizeof(*info));
|
||||||
spa_pod_builder_push_object(builder, &f[0], SPA_TYPE_OBJECT_Format, id);
|
}
|
||||||
|
|
||||||
|
SPA_API_AUDIO_RAW_UTILS struct spa_pod *
|
||||||
|
spa_format_audio_raw_ext_build(struct spa_pod_builder *builder, uint32_t id,
|
||||||
|
const struct spa_audio_info_raw *info, size_t size)
|
||||||
|
{
|
||||||
|
struct spa_pod_frame f;
|
||||||
|
uint32_t max_position = SPA_AUDIO_INFO_RAW_MAX_POSITION(size);
|
||||||
|
|
||||||
|
spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id);
|
||||||
spa_pod_builder_add(builder,
|
spa_pod_builder_add(builder,
|
||||||
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
|
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
|
||||||
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
|
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
|
||||||
|
|
@ -95,17 +107,21 @@ spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id,
|
||||||
if (info->channels != 0) {
|
if (info->channels != 0) {
|
||||||
spa_pod_builder_add(builder,
|
spa_pod_builder_add(builder,
|
||||||
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(info->channels), 0);
|
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(info->channels), 0);
|
||||||
if (!SPA_FLAG_IS_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED)) {
|
if (!SPA_FLAG_IS_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED) &&
|
||||||
uint32_t i;
|
max_position > info->channels) {
|
||||||
spa_pod_builder_prop(builder, SPA_FORMAT_AUDIO_position, 0);
|
spa_pod_builder_add(builder, SPA_FORMAT_AUDIO_position,
|
||||||
spa_pod_builder_push_array(builder, &f[1]);
|
SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id,
|
||||||
for (i = 0; i < info->channels; i++)
|
info->channels, info->position), 0);
|
||||||
spa_pod_builder_id(builder,
|
|
||||||
spa_format_audio_raw_get_position(info, i));
|
|
||||||
spa_pod_builder_pop(builder, &f[1]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (struct spa_pod*)spa_pod_builder_pop(builder, &f[0]);
|
return (struct spa_pod*)spa_pod_builder_pop(builder, &f);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPA_API_AUDIO_RAW_UTILS struct spa_pod *
|
||||||
|
spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id,
|
||||||
|
const struct spa_audio_info_raw *info)
|
||||||
|
{
|
||||||
|
return spa_format_audio_raw_ext_build(builder, id, info, sizeof(*info));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -284,14 +284,18 @@ struct spa_audio_info_raw {
|
||||||
enum spa_audio_format format; /*< format, one of enum spa_audio_format */
|
enum spa_audio_format format; /*< format, one of enum spa_audio_format */
|
||||||
uint32_t flags; /*< extra flags */
|
uint32_t flags; /*< extra flags */
|
||||||
uint32_t rate; /*< sample rate */
|
uint32_t rate; /*< sample rate */
|
||||||
uint32_t channels; /*< number of channels. This can be larger than
|
uint32_t channels; /*< number of channels. This can be more than SPA_AUDIO_MAX_POSITION
|
||||||
* SPA_AUDIO_MAX_POSITION, the position is taken
|
* and you may assume there is enough padding for the extra
|
||||||
* (index % SPA_AUDIO_MAX_POSITION) */
|
* channel positions. */
|
||||||
uint32_t position[SPA_AUDIO_MAX_POSITION]; /*< channel position from enum spa_audio_channel */
|
uint32_t position[SPA_AUDIO_MAX_POSITION]; /*< channel position from enum spa_audio_channel */
|
||||||
|
/* more channels can be added here */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SPA_AUDIO_INFO_RAW_INIT(...) ((struct spa_audio_info_raw) { __VA_ARGS__ })
|
#define SPA_AUDIO_INFO_RAW_INIT(...) ((struct spa_audio_info_raw) { __VA_ARGS__ })
|
||||||
|
|
||||||
|
#define SPA_AUDIO_INFO_RAW_MAX_POSITION(size) (((size)-offsetof(struct spa_audio_info_raw,position))/sizeof(uint32_t))
|
||||||
|
|
||||||
|
|
||||||
#define SPA_KEY_AUDIO_FORMAT "audio.format" /**< an audio format as string,
|
#define SPA_KEY_AUDIO_FORMAT "audio.format" /**< an audio format as string,
|
||||||
* Ex. "S16LE" */
|
* Ex. "S16LE" */
|
||||||
#define SPA_KEY_AUDIO_CHANNEL "audio.channel" /**< an audio channel as string,
|
#define SPA_KEY_AUDIO_CHANNEL "audio.channel" /**< an audio channel as string,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue