mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
alsa: add enumeration of iec958 format
For hdmi and iec958 devices, enumerate the iec958 formats and codecs. Initially only PCM is supported as a codec but with a property or an init option, the list of codecs can be dynamically configured.
This commit is contained in:
parent
8147772cf5
commit
9dfe35b17c
4 changed files with 267 additions and 71 deletions
|
|
@ -22,6 +22,12 @@ int spa_alsa_init(struct state *state)
|
|||
|
||||
snd_config_update_free_global();
|
||||
|
||||
if (state->stream == SND_PCM_STREAM_PLAYBACK) {
|
||||
state->is_iec958 = spa_strstartswith(state->props.device, "iec958");
|
||||
state->is_hdmi = spa_strstartswith(state->props.device, "hdmi");
|
||||
state->iec958_codecs |= 1ULL << SPA_AUDIO_IEC958_CODEC_PCM;
|
||||
}
|
||||
|
||||
if (state->open_ucm) {
|
||||
char card_name[64];
|
||||
const char *alibpref = NULL;
|
||||
|
|
@ -57,6 +63,7 @@ int spa_alsa_init(struct state *state)
|
|||
free((void*)alibpref);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -313,40 +320,21 @@ static void sanitize_map(snd_pcm_chmap_t* map)
|
|||
}
|
||||
}
|
||||
|
||||
int
|
||||
spa_alsa_enum_format(struct state *state, int seq, uint32_t start, uint32_t num,
|
||||
const struct spa_pod *filter)
|
||||
static int enum_pcm_formats(struct state *state, uint32_t index, uint32_t *next,
|
||||
struct spa_pod **result, struct spa_pod_builder *b)
|
||||
{
|
||||
int err, dir;
|
||||
size_t i, j;
|
||||
snd_pcm_t *hndl;
|
||||
snd_pcm_hw_params_t *params;
|
||||
snd_pcm_chmap_query_t **maps;
|
||||
struct spa_pod_frame f[2];
|
||||
snd_pcm_format_mask_t *fmask;
|
||||
snd_pcm_access_mask_t *amask;
|
||||
snd_pcm_chmap_query_t **maps;
|
||||
size_t i, j;
|
||||
int err, dir;
|
||||
unsigned int min, max;
|
||||
unsigned int rrate, rchannels;
|
||||
uint8_t buffer[4096];
|
||||
struct spa_pod_builder b = { 0 };
|
||||
struct spa_pod_choice *choice;
|
||||
struct spa_pod *fmt;
|
||||
int res;
|
||||
bool opened;
|
||||
struct spa_pod_frame f[2];
|
||||
struct spa_result_node_params result;
|
||||
uint32_t count = 0, rate;
|
||||
|
||||
opened = state->opened;
|
||||
if ((err = spa_alsa_open(state)) < 0)
|
||||
return err;
|
||||
|
||||
result.id = SPA_PARAM_EnumFormat;
|
||||
result.next = start;
|
||||
|
||||
next:
|
||||
result.index = result.next++;
|
||||
|
||||
spa_pod_builder_init(&b, buffer, sizeof(buffer));
|
||||
uint32_t rate;
|
||||
|
||||
hndl = state->hndl;
|
||||
snd_pcm_hw_params_alloca(¶ms);
|
||||
|
|
@ -371,8 +359,8 @@ spa_alsa_enum_format(struct state *state, int seq, uint32_t start, uint32_t num,
|
|||
}
|
||||
}
|
||||
|
||||
spa_pod_builder_push_object(&b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
|
||||
spa_pod_builder_add(&b,
|
||||
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
|
||||
spa_pod_builder_add(b,
|
||||
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
|
||||
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
|
||||
0);
|
||||
|
|
@ -383,10 +371,10 @@ spa_alsa_enum_format(struct state *state, int seq, uint32_t start, uint32_t num,
|
|||
snd_pcm_access_mask_alloca(&amask);
|
||||
snd_pcm_hw_params_get_access_mask(params, amask);
|
||||
|
||||
spa_pod_builder_prop(&b, SPA_FORMAT_AUDIO_format, 0);
|
||||
spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_format, 0);
|
||||
|
||||
spa_pod_builder_push_choice(&b, &f[1], SPA_CHOICE_None, 0);
|
||||
choice = (struct spa_pod_choice*)spa_pod_builder_frame(&b, &f[1]);
|
||||
spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0);
|
||||
choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]);
|
||||
|
||||
for (i = 1, j = 0; i < SPA_N_ELEMENTS(format_info); i++) {
|
||||
const struct format_info *fi = &format_info[i];
|
||||
|
|
@ -397,15 +385,15 @@ spa_alsa_enum_format(struct state *state, int seq, uint32_t start, uint32_t num,
|
|||
fi->spa_pformat != SPA_AUDIO_FORMAT_UNKNOWN &&
|
||||
(state->default_format == 0 || state->default_format == fi->spa_pformat)) {
|
||||
if (j++ == 0)
|
||||
spa_pod_builder_id(&b, fi->spa_pformat);
|
||||
spa_pod_builder_id(&b, fi->spa_pformat);
|
||||
spa_pod_builder_id(b, fi->spa_pformat);
|
||||
spa_pod_builder_id(b, fi->spa_pformat);
|
||||
}
|
||||
if ((snd_pcm_access_mask_test(amask, SND_PCM_ACCESS_MMAP_INTERLEAVED) ||
|
||||
snd_pcm_access_mask_test(amask, SND_PCM_ACCESS_RW_INTERLEAVED)) &&
|
||||
(state->default_format == 0 || state->default_format == fi->spa_format)) {
|
||||
if (j++ == 0)
|
||||
spa_pod_builder_id(&b, fi->spa_format);
|
||||
spa_pod_builder_id(&b, fi->spa_format);
|
||||
spa_pod_builder_id(b, fi->spa_format);
|
||||
spa_pod_builder_id(b, fi->spa_format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -440,7 +428,7 @@ spa_alsa_enum_format(struct state *state, int seq, uint32_t start, uint32_t num,
|
|||
}
|
||||
if (j > 1)
|
||||
choice->body.type = SPA_CHOICE_Enum;
|
||||
spa_pod_builder_pop(&b, &f[1]);
|
||||
spa_pod_builder_pop(b, &f[1]);
|
||||
|
||||
CHECK(snd_pcm_hw_params_get_rate_min(params, &min, &dir), "get_rate_min");
|
||||
CHECK(snd_pcm_hw_params_get_rate_max(params, &max, &dir), "get_rate_max");
|
||||
|
|
@ -452,20 +440,20 @@ spa_alsa_enum_format(struct state *state, int seq, uint32_t start, uint32_t num,
|
|||
max = state->default_rate;
|
||||
}
|
||||
|
||||
spa_pod_builder_prop(&b, SPA_FORMAT_AUDIO_rate, 0);
|
||||
spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_rate, 0);
|
||||
|
||||
spa_pod_builder_push_choice(&b, &f[1], SPA_CHOICE_None, 0);
|
||||
choice = (struct spa_pod_choice*)spa_pod_builder_frame(&b, &f[1]);
|
||||
spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0);
|
||||
choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]);
|
||||
|
||||
rate = state->position ? state->position->clock.rate.denom : DEFAULT_RATE;
|
||||
|
||||
spa_pod_builder_int(&b, SPA_CLAMP(rate, min, max));
|
||||
spa_pod_builder_int(b, SPA_CLAMP(rate, min, max));
|
||||
if (min != max) {
|
||||
spa_pod_builder_int(&b, min);
|
||||
spa_pod_builder_int(&b, max);
|
||||
spa_pod_builder_int(b, min);
|
||||
spa_pod_builder_int(b, max);
|
||||
choice->body.type = SPA_CHOICE_Range;
|
||||
}
|
||||
spa_pod_builder_pop(&b, &f[1]);
|
||||
spa_pod_builder_pop(b, &f[1]);
|
||||
|
||||
CHECK(snd_pcm_hw_params_get_channels_min(params, &min), "get_channels_min");
|
||||
CHECK(snd_pcm_hw_params_get_channels_max(params, &max), "get_channels_max");
|
||||
|
|
@ -480,55 +468,55 @@ spa_alsa_enum_format(struct state *state, int seq, uint32_t start, uint32_t num,
|
|||
min = SPA_MIN(min, SPA_AUDIO_MAX_CHANNELS);
|
||||
max = SPA_MIN(max, SPA_AUDIO_MAX_CHANNELS);
|
||||
|
||||
spa_pod_builder_prop(&b, SPA_FORMAT_AUDIO_channels, 0);
|
||||
spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_channels, 0);
|
||||
|
||||
if (state->props.use_chmap && (maps = snd_pcm_query_chmaps(hndl)) != NULL) {
|
||||
uint32_t channel;
|
||||
snd_pcm_chmap_t* map;
|
||||
|
||||
skip_channels:
|
||||
if (maps[result.index] == NULL) {
|
||||
if (maps[index] == NULL) {
|
||||
snd_pcm_free_chmaps(maps);
|
||||
goto enum_end;
|
||||
return 0;
|
||||
}
|
||||
map = &maps[result.index]->map;
|
||||
map = &maps[index]->map;
|
||||
|
||||
spa_log_debug(state->log, "map %d channels (%d %d)", map->channels, min, max);
|
||||
|
||||
if (map->channels < min || map->channels > max) {
|
||||
result.index = result.next++;
|
||||
index = (*next)++;
|
||||
goto skip_channels;
|
||||
}
|
||||
|
||||
sanitize_map(map);
|
||||
spa_pod_builder_int(&b, map->channels);
|
||||
spa_pod_builder_int(b, map->channels);
|
||||
|
||||
spa_pod_builder_prop(&b, SPA_FORMAT_AUDIO_position, 0);
|
||||
spa_pod_builder_push_array(&b, &f[1]);
|
||||
spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_position, 0);
|
||||
spa_pod_builder_push_array(b, &f[1]);
|
||||
for (j = 0; j < map->channels; j++) {
|
||||
spa_log_debug(state->log, NAME" %p: position %zd %d", state, j, map->pos[j]);
|
||||
channel = chmap_position_to_channel(map->pos[j]);
|
||||
spa_pod_builder_id(&b, channel);
|
||||
spa_pod_builder_id(b, channel);
|
||||
}
|
||||
spa_pod_builder_pop(&b, &f[1]);
|
||||
spa_pod_builder_pop(b, &f[1]);
|
||||
|
||||
snd_pcm_free_chmaps(maps);
|
||||
}
|
||||
else {
|
||||
const struct channel_map *map = NULL;
|
||||
|
||||
if (result.index > 0)
|
||||
goto enum_end;
|
||||
if (index > 0)
|
||||
return 0;
|
||||
|
||||
spa_pod_builder_push_choice(&b, &f[1], SPA_CHOICE_None, 0);
|
||||
choice = (struct spa_pod_choice*)spa_pod_builder_frame(&b, &f[1]);
|
||||
spa_pod_builder_int(&b, max);
|
||||
spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0);
|
||||
choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]);
|
||||
spa_pod_builder_int(b, max);
|
||||
if (min != max) {
|
||||
spa_pod_builder_int(&b, min);
|
||||
spa_pod_builder_int(&b, max);
|
||||
spa_pod_builder_int(b, min);
|
||||
spa_pod_builder_int(b, max);
|
||||
choice->body.type = SPA_CHOICE_Range;
|
||||
}
|
||||
spa_pod_builder_pop(&b, &f[1]);
|
||||
spa_pod_builder_pop(b, &f[1]);
|
||||
|
||||
if (min == max) {
|
||||
if (state->default_pos.channels == min)
|
||||
|
|
@ -537,17 +525,147 @@ skip_channels:
|
|||
map = &default_map[min];
|
||||
}
|
||||
if (map) {
|
||||
spa_pod_builder_prop(&b, SPA_FORMAT_AUDIO_position, 0);
|
||||
spa_pod_builder_push_array(&b, &f[1]);
|
||||
spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_position, 0);
|
||||
spa_pod_builder_push_array(b, &f[1]);
|
||||
for (j = 0; j < map->channels; j++) {
|
||||
spa_log_debug(state->log, NAME" %p: position %zd %d", state, j, map->pos[j]);
|
||||
spa_pod_builder_id(&b, map->pos[j]);
|
||||
spa_pod_builder_id(b, map->pos[j]);
|
||||
}
|
||||
spa_pod_builder_pop(&b, &f[1]);
|
||||
spa_pod_builder_pop(b, &f[1]);
|
||||
}
|
||||
}
|
||||
*result = spa_pod_builder_pop(b, &f[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fmt = spa_pod_builder_pop(&b, &f[0]);
|
||||
static int enum_iec958_formats(struct state *state, uint32_t index, uint32_t *next,
|
||||
struct spa_pod **result, struct spa_pod_builder *b)
|
||||
{
|
||||
int err, dir;
|
||||
snd_pcm_t *hndl;
|
||||
snd_pcm_hw_params_t *params;
|
||||
struct spa_pod_frame f[2];
|
||||
struct spa_pod_choice *choice;
|
||||
unsigned int rmin, rmax;
|
||||
unsigned int chmin, chmax;
|
||||
|
||||
if ((index & 0xffff) > 0)
|
||||
return 0;
|
||||
|
||||
if (!(state->is_iec958 || state->is_hdmi))
|
||||
return 0;
|
||||
if (state->iec958_codecs == 0)
|
||||
return 0;
|
||||
|
||||
hndl = state->hndl;
|
||||
snd_pcm_hw_params_alloca(¶ms);
|
||||
CHECK(snd_pcm_hw_params_any(hndl, params), "Broken configuration: no configurations available");
|
||||
|
||||
CHECK(snd_pcm_hw_params_set_rate_resample(hndl, params, 0), "set_rate_resample");
|
||||
|
||||
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
|
||||
spa_pod_builder_add(b,
|
||||
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
|
||||
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_iec958),
|
||||
0);
|
||||
|
||||
CHECK(snd_pcm_hw_params_get_channels_min(params, &chmin), "get_channels_min");
|
||||
CHECK(snd_pcm_hw_params_get_channels_max(params, &chmax), "get_channels_max");
|
||||
spa_log_debug(state->log, "channels (%d %d)", chmin, chmax);
|
||||
|
||||
CHECK(snd_pcm_hw_params_get_rate_min(params, &rmin, &dir), "get_rate_min");
|
||||
CHECK(snd_pcm_hw_params_get_rate_max(params, &rmax, &dir), "get_rate_max");
|
||||
spa_log_debug(state->log, "rate (%d %d)", rmin, rmax);
|
||||
|
||||
if (state->default_rate != 0) {
|
||||
if (rmin < state->default_rate)
|
||||
rmin = state->default_rate;
|
||||
if (rmax > state->default_rate)
|
||||
rmax = state->default_rate;
|
||||
}
|
||||
|
||||
spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_iec958Codec, 0);
|
||||
spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_Enum, 0);
|
||||
if (chmax >= 2) {
|
||||
spa_pod_builder_id(b, SPA_AUDIO_IEC958_CODEC_PCM);
|
||||
spa_pod_builder_id(b, SPA_AUDIO_IEC958_CODEC_PCM);
|
||||
|
||||
if (state->iec958_codecs & (1ULL << SPA_AUDIO_IEC958_CODEC_DTS))
|
||||
spa_pod_builder_id(b, SPA_AUDIO_IEC958_CODEC_DTS);
|
||||
if (state->iec958_codecs & (1ULL << SPA_AUDIO_IEC958_CODEC_AC3))
|
||||
spa_pod_builder_id(b, SPA_AUDIO_IEC958_CODEC_AC3);
|
||||
if (state->iec958_codecs & (1ULL << SPA_AUDIO_IEC958_CODEC_MPEG))
|
||||
spa_pod_builder_id(b, SPA_AUDIO_IEC958_CODEC_MPEG);
|
||||
if (state->iec958_codecs & (1ULL << SPA_AUDIO_IEC958_CODEC_MPEG2_AAC))
|
||||
spa_pod_builder_id(b, SPA_AUDIO_IEC958_CODEC_MPEG2_AAC);
|
||||
|
||||
if (rmax >= 48000 * 4 &&
|
||||
(state->iec958_codecs & (1ULL << SPA_AUDIO_IEC958_CODEC_EAC3)))
|
||||
spa_pod_builder_id(b, SPA_AUDIO_IEC958_CODEC_EAC3);
|
||||
}
|
||||
if (chmax >= 8) {
|
||||
if (state->iec958_codecs & (1ULL << SPA_AUDIO_IEC958_CODEC_TRUEHD))
|
||||
spa_pod_builder_id(b, SPA_AUDIO_IEC958_CODEC_TRUEHD);
|
||||
if (state->iec958_codecs & (1ULL << SPA_AUDIO_IEC958_CODEC_DTSHD))
|
||||
spa_pod_builder_id(b, SPA_AUDIO_IEC958_CODEC_DTSHD);
|
||||
}
|
||||
spa_pod_builder_pop(b, &f[1]);
|
||||
|
||||
spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_rate, 0);
|
||||
spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0);
|
||||
choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]);
|
||||
|
||||
spa_pod_builder_int(b, SPA_CLAMP(DEFAULT_RATE, rmin, rmax));
|
||||
if (rmin != rmax) {
|
||||
spa_pod_builder_int(b, rmin);
|
||||
spa_pod_builder_int(b, rmax);
|
||||
choice->body.type = SPA_CHOICE_Range;
|
||||
}
|
||||
spa_pod_builder_pop(b, &f[1]);
|
||||
|
||||
(*next)++;
|
||||
*result = spa_pod_builder_pop(b, &f[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
spa_alsa_enum_format(struct state *state, int seq, uint32_t start, uint32_t num,
|
||||
const struct spa_pod *filter)
|
||||
{
|
||||
uint8_t buffer[4096];
|
||||
struct spa_pod_builder b = { 0 };
|
||||
struct spa_pod *fmt;
|
||||
int err, res;
|
||||
bool opened;
|
||||
struct spa_result_node_params result;
|
||||
uint32_t count = 0;
|
||||
|
||||
opened = state->opened;
|
||||
if ((err = spa_alsa_open(state)) < 0)
|
||||
return err;
|
||||
|
||||
result.id = SPA_PARAM_EnumFormat;
|
||||
result.next = start;
|
||||
|
||||
next:
|
||||
result.index = result.next++;
|
||||
|
||||
spa_pod_builder_init(&b, buffer, sizeof(buffer));
|
||||
|
||||
if (result.index < 0x10000) {
|
||||
if ((res = enum_pcm_formats(state, result.index, &result.next, &fmt, &b)) != 1) {
|
||||
result.next = 0x10000;
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
else if (result.index < 0x20000) {
|
||||
if ((res = enum_iec958_formats(state, result.index, &result.next, &fmt, &b)) != 1) {
|
||||
result.next = 0x20000;
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
else
|
||||
goto enum_end;
|
||||
|
||||
if (spa_pod_filter(&b, &result.param, fmt, filter) < 0)
|
||||
goto next;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue