2020-10-08 11:57:35 +02:00
|
|
|
/* PipeWire
|
|
|
|
|
*
|
|
|
|
|
* Copyright © 2020 Wim Taymans
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
|
|
|
* to deal in the Software without restriction, including without limitation
|
|
|
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
|
|
|
*
|
|
|
|
|
* The above copyright notice and this permission notice (including the next
|
|
|
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
|
|
|
* Software.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
|
|
* DEALINGS IN THE SOFTWARE.
|
|
|
|
|
*/
|
|
|
|
|
|
2020-10-20 16:25:47 +02:00
|
|
|
#define RATE_MAX (48000u*8u)
|
|
|
|
|
#define CHANNELS_MAX (64u)
|
|
|
|
|
|
2020-10-08 11:57:35 +02:00
|
|
|
enum sample_format {
|
|
|
|
|
SAMPLE_U8,
|
|
|
|
|
SAMPLE_ALAW,
|
|
|
|
|
SAMPLE_ULAW,
|
|
|
|
|
SAMPLE_S16LE,
|
|
|
|
|
SAMPLE_S16BE,
|
|
|
|
|
SAMPLE_FLOAT32LE,
|
|
|
|
|
SAMPLE_FLOAT32BE,
|
|
|
|
|
SAMPLE_S32LE,
|
|
|
|
|
SAMPLE_S32BE,
|
|
|
|
|
SAMPLE_S24LE,
|
|
|
|
|
SAMPLE_S24BE,
|
|
|
|
|
SAMPLE_S24_32LE,
|
|
|
|
|
SAMPLE_S24_32BE,
|
|
|
|
|
SAMPLE_MAX,
|
|
|
|
|
SAMPLE_INVALID = -1
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct format {
|
|
|
|
|
uint32_t format;
|
|
|
|
|
const char *name;
|
|
|
|
|
uint32_t size;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct format audio_formats[] = {
|
|
|
|
|
[SAMPLE_U8] = { SPA_AUDIO_FORMAT_U8, "u8", 1 },
|
2020-10-20 16:39:31 +02:00
|
|
|
[SAMPLE_ALAW] = { SPA_AUDIO_FORMAT_UNKNOWN, "aLaw", 1 },
|
|
|
|
|
[SAMPLE_ULAW] = { SPA_AUDIO_FORMAT_UNKNOWN, "uLaw", 1 },
|
2020-10-08 11:57:35 +02:00
|
|
|
[SAMPLE_S16LE] = { SPA_AUDIO_FORMAT_S16_LE, "s16le", 2 },
|
|
|
|
|
[SAMPLE_S16BE] = { SPA_AUDIO_FORMAT_S16_BE, "s16be", 2 },
|
2020-10-20 16:39:31 +02:00
|
|
|
[SAMPLE_FLOAT32LE] = { SPA_AUDIO_FORMAT_F32_LE, "float32le", 4 },
|
|
|
|
|
[SAMPLE_FLOAT32BE] = { SPA_AUDIO_FORMAT_F32_BE, "float32be", 4 },
|
2020-10-08 11:57:35 +02:00
|
|
|
[SAMPLE_S32LE] = { SPA_AUDIO_FORMAT_S32_LE, "s32le", 4 },
|
|
|
|
|
[SAMPLE_S32BE] = { SPA_AUDIO_FORMAT_S32_BE, "s32be", 4 },
|
|
|
|
|
[SAMPLE_S24LE] = { SPA_AUDIO_FORMAT_S24_LE, "s24le", 3 },
|
|
|
|
|
[SAMPLE_S24BE] = { SPA_AUDIO_FORMAT_S24_BE, "s24be", 3 },
|
2020-10-20 16:39:31 +02:00
|
|
|
[SAMPLE_S24_32LE] = { SPA_AUDIO_FORMAT_S24_32_LE, "s24-32le", 4 },
|
|
|
|
|
[SAMPLE_S24_32BE] = { SPA_AUDIO_FORMAT_S24_32_BE, "s24-32be", 4 },
|
2020-10-08 11:57:35 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static inline uint32_t format_pa2id(enum sample_format format)
|
|
|
|
|
{
|
|
|
|
|
if (format < 0 || (size_t)format >= SPA_N_ELEMENTS(audio_formats))
|
|
|
|
|
return SPA_AUDIO_FORMAT_UNKNOWN;
|
|
|
|
|
return audio_formats[format].format;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-20 16:25:47 +02:00
|
|
|
static inline enum sample_format format_name2pa(const char *name, size_t size)
|
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
for (i = 0; i < SPA_N_ELEMENTS(audio_formats); i++) {
|
|
|
|
|
if (strncmp(name, audio_formats[i].name, size) == 0)
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
return SAMPLE_INVALID;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-08 11:57:35 +02:00
|
|
|
static inline enum sample_format format_id2pa(uint32_t id)
|
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
for (i = 0; i < SPA_N_ELEMENTS(audio_formats); i++) {
|
|
|
|
|
if (id == audio_formats[i].format)
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
return SAMPLE_INVALID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct sample_spec {
|
|
|
|
|
enum sample_format format;
|
|
|
|
|
uint32_t rate;
|
|
|
|
|
uint8_t channels;
|
|
|
|
|
};
|
2020-10-27 10:12:27 +01:00
|
|
|
#define SAMPLE_SPEC_INIT (struct sample_spec) { \
|
|
|
|
|
.format = SAMPLE_FLOAT32LE, \
|
|
|
|
|
.rate = 44100, \
|
|
|
|
|
.channels = 2, \
|
2020-10-28 16:48:46 +01:00
|
|
|
}
|
2020-10-08 11:57:35 +02:00
|
|
|
|
|
|
|
|
static inline uint32_t sample_spec_frame_size(const struct sample_spec *ss)
|
|
|
|
|
{
|
|
|
|
|
if (ss->format < 0 || (size_t)ss->format >= SPA_N_ELEMENTS(audio_formats))
|
2020-10-20 09:43:43 +02:00
|
|
|
return 0;
|
2020-10-08 11:57:35 +02:00
|
|
|
return audio_formats[ss->format].size * ss->channels;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-20 16:25:47 +02:00
|
|
|
static inline bool sample_spec_valid(const struct sample_spec *ss)
|
|
|
|
|
{
|
|
|
|
|
return (ss->format < SAMPLE_MAX &&
|
|
|
|
|
ss->rate > 0 && ss->rate <= RATE_MAX &&
|
|
|
|
|
ss->channels > 0 && ss->channels <= CHANNELS_MAX);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum channel_position {
|
|
|
|
|
CHANNEL_POSITION_INVALID = -1,
|
|
|
|
|
CHANNEL_POSITION_MONO = 0,
|
|
|
|
|
CHANNEL_POSITION_FRONT_LEFT,
|
|
|
|
|
CHANNEL_POSITION_FRONT_RIGHT,
|
|
|
|
|
CHANNEL_POSITION_FRONT_CENTER,
|
|
|
|
|
|
|
|
|
|
CHANNEL_POSITION_REAR_CENTER,
|
|
|
|
|
CHANNEL_POSITION_REAR_LEFT,
|
|
|
|
|
CHANNEL_POSITION_REAR_RIGHT,
|
|
|
|
|
|
|
|
|
|
CHANNEL_POSITION_LFE,
|
|
|
|
|
CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
|
|
|
|
|
CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
|
|
|
|
|
|
|
|
|
|
CHANNEL_POSITION_SIDE_LEFT,
|
|
|
|
|
CHANNEL_POSITION_SIDE_RIGHT,
|
|
|
|
|
CHANNEL_POSITION_AUX0,
|
|
|
|
|
CHANNEL_POSITION_AUX1,
|
|
|
|
|
CHANNEL_POSITION_AUX2,
|
|
|
|
|
CHANNEL_POSITION_AUX3,
|
|
|
|
|
CHANNEL_POSITION_AUX4,
|
|
|
|
|
CHANNEL_POSITION_AUX5,
|
|
|
|
|
CHANNEL_POSITION_AUX6,
|
|
|
|
|
CHANNEL_POSITION_AUX7,
|
|
|
|
|
CHANNEL_POSITION_AUX8,
|
|
|
|
|
CHANNEL_POSITION_AUX9,
|
|
|
|
|
CHANNEL_POSITION_AUX10,
|
|
|
|
|
CHANNEL_POSITION_AUX11,
|
|
|
|
|
CHANNEL_POSITION_AUX12,
|
|
|
|
|
CHANNEL_POSITION_AUX13,
|
|
|
|
|
CHANNEL_POSITION_AUX14,
|
|
|
|
|
CHANNEL_POSITION_AUX15,
|
|
|
|
|
CHANNEL_POSITION_AUX16,
|
|
|
|
|
CHANNEL_POSITION_AUX17,
|
|
|
|
|
CHANNEL_POSITION_AUX18,
|
|
|
|
|
CHANNEL_POSITION_AUX19,
|
|
|
|
|
CHANNEL_POSITION_AUX20,
|
|
|
|
|
CHANNEL_POSITION_AUX21,
|
|
|
|
|
CHANNEL_POSITION_AUX22,
|
|
|
|
|
CHANNEL_POSITION_AUX23,
|
|
|
|
|
CHANNEL_POSITION_AUX24,
|
|
|
|
|
CHANNEL_POSITION_AUX25,
|
|
|
|
|
CHANNEL_POSITION_AUX26,
|
|
|
|
|
CHANNEL_POSITION_AUX27,
|
|
|
|
|
CHANNEL_POSITION_AUX28,
|
|
|
|
|
CHANNEL_POSITION_AUX29,
|
|
|
|
|
CHANNEL_POSITION_AUX30,
|
|
|
|
|
CHANNEL_POSITION_AUX31,
|
|
|
|
|
|
|
|
|
|
CHANNEL_POSITION_TOP_CENTER,
|
|
|
|
|
|
|
|
|
|
CHANNEL_POSITION_TOP_FRONT_LEFT,
|
|
|
|
|
CHANNEL_POSITION_TOP_FRONT_RIGHT,
|
|
|
|
|
CHANNEL_POSITION_TOP_FRONT_CENTER,
|
|
|
|
|
|
|
|
|
|
CHANNEL_POSITION_TOP_REAR_LEFT,
|
|
|
|
|
CHANNEL_POSITION_TOP_REAR_RIGHT,
|
|
|
|
|
CHANNEL_POSITION_TOP_REAR_CENTER,
|
|
|
|
|
|
|
|
|
|
CHANNEL_POSITION_MAX
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct channel {
|
|
|
|
|
uint32_t channel;
|
|
|
|
|
const char *name;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct channel audio_channels[] = {
|
|
|
|
|
[CHANNEL_POSITION_MONO] = { SPA_AUDIO_CHANNEL_MONO, "mono", },
|
|
|
|
|
|
|
|
|
|
[CHANNEL_POSITION_FRONT_LEFT] = { SPA_AUDIO_CHANNEL_FL, "front-left", },
|
|
|
|
|
[CHANNEL_POSITION_FRONT_RIGHT] = { SPA_AUDIO_CHANNEL_FR, "front-right", },
|
|
|
|
|
[CHANNEL_POSITION_FRONT_CENTER] = { SPA_AUDIO_CHANNEL_FC, "front-center", },
|
|
|
|
|
|
|
|
|
|
[CHANNEL_POSITION_REAR_CENTER] = { SPA_AUDIO_CHANNEL_RC, "rear-center", },
|
|
|
|
|
[CHANNEL_POSITION_REAR_LEFT] = { SPA_AUDIO_CHANNEL_RL, "rear-left", },
|
|
|
|
|
[CHANNEL_POSITION_REAR_RIGHT] = { SPA_AUDIO_CHANNEL_RR, "rear-right", },
|
|
|
|
|
|
|
|
|
|
[CHANNEL_POSITION_LFE] = { SPA_AUDIO_CHANNEL_LFE, "lfe", },
|
|
|
|
|
[CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = { SPA_AUDIO_CHANNEL_FLC, "front-left-of-center", },
|
|
|
|
|
[CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = { SPA_AUDIO_CHANNEL_FRC, "front-right-of-center", },
|
|
|
|
|
|
|
|
|
|
[CHANNEL_POSITION_SIDE_LEFT] = { SPA_AUDIO_CHANNEL_SL, "side-left", },
|
|
|
|
|
[CHANNEL_POSITION_SIDE_RIGHT] = { SPA_AUDIO_CHANNEL_SR, "side-right", },
|
|
|
|
|
|
|
|
|
|
[CHANNEL_POSITION_AUX0] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 1, "aux0", },
|
|
|
|
|
[CHANNEL_POSITION_AUX1] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 2, "aux1", },
|
|
|
|
|
[CHANNEL_POSITION_AUX2] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 3, "aux2", },
|
|
|
|
|
[CHANNEL_POSITION_AUX3] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 4, "aux3", },
|
|
|
|
|
[CHANNEL_POSITION_AUX4] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 5, "aux4", },
|
|
|
|
|
[CHANNEL_POSITION_AUX5] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 6, "aux5", },
|
|
|
|
|
[CHANNEL_POSITION_AUX6] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 7, "aux6", },
|
|
|
|
|
[CHANNEL_POSITION_AUX7] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 8, "aux7", },
|
|
|
|
|
[CHANNEL_POSITION_AUX8] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 9, "aux8", },
|
|
|
|
|
[CHANNEL_POSITION_AUX9] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 10, "aux9", },
|
|
|
|
|
[CHANNEL_POSITION_AUX10] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 11, "aux10", },
|
|
|
|
|
[CHANNEL_POSITION_AUX11] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 12, "aux11", },
|
|
|
|
|
[CHANNEL_POSITION_AUX12] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 13, "aux12", },
|
|
|
|
|
[CHANNEL_POSITION_AUX13] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 14, "aux13", },
|
|
|
|
|
[CHANNEL_POSITION_AUX14] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 15, "aux14", },
|
|
|
|
|
[CHANNEL_POSITION_AUX15] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 16, "aux15", },
|
|
|
|
|
[CHANNEL_POSITION_AUX16] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 17, "aux16", },
|
|
|
|
|
[CHANNEL_POSITION_AUX17] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 18, "aux17", },
|
|
|
|
|
[CHANNEL_POSITION_AUX18] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 19, "aux18", },
|
|
|
|
|
[CHANNEL_POSITION_AUX19] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 20, "aux19", },
|
|
|
|
|
[CHANNEL_POSITION_AUX20] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 21, "aux20", },
|
|
|
|
|
[CHANNEL_POSITION_AUX21] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 22, "aux21", },
|
|
|
|
|
[CHANNEL_POSITION_AUX22] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 23, "aux22", },
|
|
|
|
|
[CHANNEL_POSITION_AUX23] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 24, "aux23", },
|
|
|
|
|
[CHANNEL_POSITION_AUX24] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 25, "aux24", },
|
|
|
|
|
[CHANNEL_POSITION_AUX25] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 26, "aux25", },
|
|
|
|
|
[CHANNEL_POSITION_AUX26] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 27, "aux26", },
|
|
|
|
|
[CHANNEL_POSITION_AUX27] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 28, "aux27", },
|
|
|
|
|
[CHANNEL_POSITION_AUX28] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 29, "aux28", },
|
|
|
|
|
[CHANNEL_POSITION_AUX29] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 30, "aux29", },
|
|
|
|
|
[CHANNEL_POSITION_AUX30] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 31, "aux30", },
|
|
|
|
|
[CHANNEL_POSITION_AUX31] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 32, "aux31", },
|
|
|
|
|
|
|
|
|
|
[CHANNEL_POSITION_TOP_CENTER] = { SPA_AUDIO_CHANNEL_TC, "top-center", },
|
|
|
|
|
|
|
|
|
|
[CHANNEL_POSITION_TOP_FRONT_LEFT] = { SPA_AUDIO_CHANNEL_TFL, "top-front-left", },
|
|
|
|
|
[CHANNEL_POSITION_TOP_FRONT_RIGHT] = { SPA_AUDIO_CHANNEL_TFR, "top-front-right", },
|
|
|
|
|
[CHANNEL_POSITION_TOP_FRONT_CENTER] = { SPA_AUDIO_CHANNEL_TFC, "top-front-center", },
|
|
|
|
|
|
|
|
|
|
[CHANNEL_POSITION_TOP_REAR_LEFT] = { SPA_AUDIO_CHANNEL_TRL, "top-rear-left", },
|
|
|
|
|
[CHANNEL_POSITION_TOP_REAR_RIGHT] = { SPA_AUDIO_CHANNEL_TRR, "top-rear-right", },
|
|
|
|
|
[CHANNEL_POSITION_TOP_REAR_CENTER] = { SPA_AUDIO_CHANNEL_TRC, "top-rear-center", },
|
|
|
|
|
};
|
2020-10-08 11:57:35 +02:00
|
|
|
|
|
|
|
|
struct channel_map {
|
|
|
|
|
uint8_t channels;
|
2020-10-20 16:25:47 +02:00
|
|
|
enum channel_position map[CHANNELS_MAX];
|
2020-10-08 11:57:35 +02:00
|
|
|
};
|
|
|
|
|
|
2020-10-27 10:12:27 +01:00
|
|
|
#define CHANNEL_MAP_INIT (struct channel_map) { \
|
|
|
|
|
.channels = 2, \
|
|
|
|
|
.map[0] = CHANNEL_POSITION_FRONT_LEFT, \
|
|
|
|
|
.map[1] = CHANNEL_POSITION_FRONT_RIGHT, \
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-20 16:25:47 +02:00
|
|
|
static inline uint32_t channel_pa2id(enum channel_position channel)
|
|
|
|
|
{
|
|
|
|
|
if (channel < 0 || (size_t)channel >= SPA_N_ELEMENTS(audio_channels))
|
|
|
|
|
return SPA_AUDIO_CHANNEL_UNKNOWN;
|
|
|
|
|
return audio_channels[channel].channel;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline enum channel_position channel_id2pa(uint32_t id, uint32_t *aux)
|
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
for (i = 0; i < SPA_N_ELEMENTS(audio_channels); i++) {
|
|
|
|
|
if (id == audio_channels[i].channel)
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
return CHANNEL_POSITION_AUX0 + (*aux)++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline enum channel_position channel_name2pa(const char *name, size_t size)
|
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
for (i = 0; i < SPA_N_ELEMENTS(audio_channels); i++) {
|
|
|
|
|
if (strncmp(name, audio_channels[i].name, size) == 0)
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
return CHANNEL_POSITION_INVALID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void channel_map_to_positions(const struct channel_map *map, uint32_t *pos)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
for (i = 0; i < map->channels; i++)
|
|
|
|
|
pos[i] = channel_pa2id(map->map[i]);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-08 11:57:35 +02:00
|
|
|
enum encoding {
|
|
|
|
|
ENCODING_ANY,
|
|
|
|
|
ENCODING_PCM,
|
|
|
|
|
ENCODING_AC3_IEC61937,
|
|
|
|
|
ENCODING_EAC3_IEC61937,
|
|
|
|
|
ENCODING_MPEG_IEC61937,
|
|
|
|
|
ENCODING_DTS_IEC61937,
|
|
|
|
|
ENCODING_MPEG2_AAC_IEC61937,
|
|
|
|
|
ENCODING_TRUEHD_IEC61937,
|
|
|
|
|
ENCODING_DTSHD_IEC61937,
|
|
|
|
|
ENCODING_MAX,
|
|
|
|
|
NCODING_INVALID = -1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct format_info {
|
|
|
|
|
enum encoding encoding;
|
|
|
|
|
struct pw_properties *props;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int format_parse_param(const struct spa_pod *param, struct sample_spec *ss, struct channel_map *map)
|
|
|
|
|
{
|
|
|
|
|
struct spa_audio_info info = { 0 };
|
2020-10-20 16:25:47 +02:00
|
|
|
uint32_t i, aux = 0;
|
2020-10-08 11:57:35 +02:00
|
|
|
|
|
|
|
|
spa_format_parse(param, &info.media_type, &info.media_subtype);
|
|
|
|
|
|
|
|
|
|
if (info.media_type != SPA_MEDIA_TYPE_audio ||
|
|
|
|
|
info.media_subtype != SPA_MEDIA_SUBTYPE_raw ||
|
|
|
|
|
spa_format_audio_raw_parse(param, &info.info.raw) < 0 ||
|
|
|
|
|
!SPA_AUDIO_FORMAT_IS_INTERLEAVED(info.info.raw.format)) {
|
|
|
|
|
return -ENOTSUP;
|
|
|
|
|
}
|
2020-10-27 15:42:48 +01:00
|
|
|
if (ss) {
|
|
|
|
|
ss->format = format_id2pa(info.info.raw.format);
|
|
|
|
|
if (ss->format == SAMPLE_INVALID)
|
|
|
|
|
return -ENOTSUP;
|
2020-10-08 11:57:35 +02:00
|
|
|
|
2020-10-27 15:42:48 +01:00
|
|
|
ss->rate = info.info.raw.rate;
|
|
|
|
|
ss->channels = info.info.raw.channels;
|
|
|
|
|
}
|
|
|
|
|
if (map) {
|
|
|
|
|
map->channels = info.info.raw.channels;
|
|
|
|
|
for (i = 0; i < map->channels; i++)
|
|
|
|
|
map->map[i] = channel_id2pa(info.info.raw.position[i], &aux);
|
|
|
|
|
}
|
2020-10-08 11:57:35 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
2020-10-20 16:25:47 +02:00
|
|
|
|
|
|
|
|
static const struct spa_pod *format_build_param(struct spa_pod_builder *b,
|
|
|
|
|
uint32_t id, struct sample_spec *spec, struct channel_map *map)
|
|
|
|
|
{
|
|
|
|
|
struct spa_audio_info_raw info;
|
|
|
|
|
|
|
|
|
|
info = SPA_AUDIO_INFO_RAW_INIT(
|
|
|
|
|
.format = format_pa2id(spec->format),
|
|
|
|
|
.channels = spec->channels,
|
|
|
|
|
.rate = spec->rate);
|
|
|
|
|
if (map)
|
|
|
|
|
channel_map_to_positions(map, info.position);
|
|
|
|
|
|
|
|
|
|
return spa_format_audio_raw_build(b, id, &info);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct spa_pod *format_info_build_param(struct spa_pod_builder *b,
|
|
|
|
|
uint32_t id, struct format_info *info)
|
|
|
|
|
{
|
|
|
|
|
const char *str;
|
|
|
|
|
struct sample_spec ss;
|
|
|
|
|
struct channel_map map, *pmap = NULL;
|
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
|
|
spa_zero(ss);
|
|
|
|
|
spa_zero(map);
|
|
|
|
|
|
|
|
|
|
if ((str = pw_properties_get(info->props, "format.sample_format")) == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
if (str[0] != '\"')
|
|
|
|
|
return NULL;
|
2020-10-20 16:39:31 +02:00
|
|
|
size = strcspn(++str, "\"");
|
2020-10-20 16:25:47 +02:00
|
|
|
ss.format = format_name2pa(str, size);
|
|
|
|
|
if (ss.format == SAMPLE_INVALID)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
if ((str = pw_properties_get(info->props, "format.rate")) == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
ss.rate = atoi(str);
|
|
|
|
|
|
|
|
|
|
if ((str = pw_properties_get(info->props, "format.channels")) == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
ss.channels = atoi(str);
|
|
|
|
|
|
|
|
|
|
if ((str = pw_properties_get(info->props, "format.channel_map")) != NULL) {
|
|
|
|
|
while ((*str == '\"' || *str == ',') &&
|
|
|
|
|
(size = strcspn(++str, "\",")) > 0) {
|
|
|
|
|
map.map[map.channels++] = channel_name2pa(str, size);
|
|
|
|
|
str += size;
|
|
|
|
|
}
|
|
|
|
|
if (map.channels == ss.channels)
|
|
|
|
|
pmap = ↦
|
|
|
|
|
}
|
|
|
|
|
return format_build_param(b, id, &ss, pmap);
|
|
|
|
|
}
|