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
|
|
@ -100,7 +100,7 @@ static int impl_node_enum_params(void *object, int seq,
|
||||||
struct state *this = object;
|
struct state *this = object;
|
||||||
struct spa_pod *param;
|
struct spa_pod *param;
|
||||||
struct spa_pod_builder b = { 0 };
|
struct spa_pod_builder b = { 0 };
|
||||||
uint8_t buffer[1024];
|
uint8_t buffer[2048];
|
||||||
struct spa_result_node_params result;
|
struct spa_result_node_params result;
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
|
|
||||||
|
|
@ -169,6 +169,17 @@ static int impl_node_enum_params(void *object, int seq,
|
||||||
SPA_PROP_INFO_name, SPA_POD_String("Latency offset (ns)"),
|
SPA_PROP_INFO_name, SPA_POD_String("Latency offset (ns)"),
|
||||||
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(0, 0, INT64_MAX));
|
SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(0, 0, INT64_MAX));
|
||||||
break;
|
break;
|
||||||
|
case 7:
|
||||||
|
if (this->is_iec958 || this->is_hdmi) {
|
||||||
|
param = spa_pod_builder_add_object(&b,
|
||||||
|
SPA_TYPE_OBJECT_PropInfo, id,
|
||||||
|
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_iec958Codecs),
|
||||||
|
SPA_PROP_INFO_name, SPA_POD_String("Enabled IEC958 (S/PDIF) codecs"),
|
||||||
|
SPA_PROP_INFO_type, SPA_POD_Id(SPA_AUDIO_IEC958_CODEC_UNKNOWN),
|
||||||
|
SPA_PROP_INFO_container, SPA_POD_Id(SPA_TYPE_Array));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SPA_FALLTHROUGH
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -177,18 +188,30 @@ static int impl_node_enum_params(void *object, int seq,
|
||||||
case SPA_PARAM_Props:
|
case SPA_PARAM_Props:
|
||||||
{
|
{
|
||||||
struct props *p = &this->props;
|
struct props *p = &this->props;
|
||||||
|
struct spa_pod_frame f;
|
||||||
|
uint32_t codecs[16], n_codecs;
|
||||||
|
|
||||||
switch (result.index) {
|
switch (result.index) {
|
||||||
case 0:
|
case 0:
|
||||||
param = spa_pod_builder_add_object(&b,
|
spa_pod_builder_push_object(&b, &f,
|
||||||
SPA_TYPE_OBJECT_Props, id,
|
SPA_TYPE_OBJECT_Props, id);
|
||||||
|
spa_pod_builder_add(&b,
|
||||||
SPA_PROP_device, SPA_POD_Stringn(p->device, sizeof(p->device)),
|
SPA_PROP_device, SPA_POD_Stringn(p->device, sizeof(p->device)),
|
||||||
SPA_PROP_deviceName, SPA_POD_Stringn(p->device_name, sizeof(p->device_name)),
|
SPA_PROP_deviceName, SPA_POD_Stringn(p->device_name, sizeof(p->device_name)),
|
||||||
SPA_PROP_cardName, SPA_POD_Stringn(p->card_name, sizeof(p->card_name)),
|
SPA_PROP_cardName, SPA_POD_Stringn(p->card_name, sizeof(p->card_name)),
|
||||||
SPA_PROP_minLatency, SPA_POD_Int(p->min_latency),
|
SPA_PROP_minLatency, SPA_POD_Int(p->min_latency),
|
||||||
SPA_PROP_maxLatency, SPA_POD_Int(p->max_latency),
|
SPA_PROP_maxLatency, SPA_POD_Int(p->max_latency),
|
||||||
SPA_PROP_START_CUSTOM, SPA_POD_Bool(p->use_chmap),
|
SPA_PROP_START_CUSTOM, SPA_POD_Bool(p->use_chmap),
|
||||||
SPA_PROP_latencyOffsetNsec, SPA_POD_Long(this->process_latency.ns));
|
SPA_PROP_latencyOffsetNsec, SPA_POD_Long(this->process_latency.ns),
|
||||||
|
0);
|
||||||
|
|
||||||
|
n_codecs = spa_alsa_get_iec958_codecs(this, codecs, SPA_N_ELEMENTS(codecs));
|
||||||
|
if (n_codecs > 0) {
|
||||||
|
spa_pod_builder_prop(&b, SPA_PROP_iec958Codecs, 0);
|
||||||
|
spa_pod_builder_array(&b, sizeof(uint32_t), SPA_TYPE_Id,
|
||||||
|
n_codecs, codecs);
|
||||||
|
}
|
||||||
|
param = spa_pod_builder_pop(&b, &f);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -296,6 +319,7 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
|
||||||
{
|
{
|
||||||
struct props *p = &this->props;
|
struct props *p = &this->props;
|
||||||
struct spa_process_latency_info info;
|
struct spa_process_latency_info info;
|
||||||
|
struct spa_pod *iec958_codecs = NULL;
|
||||||
|
|
||||||
if (param == NULL) {
|
if (param == NULL) {
|
||||||
reset_props(p);
|
reset_props(p);
|
||||||
|
|
@ -310,8 +334,17 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
|
||||||
SPA_PROP_minLatency, SPA_POD_OPT_Int(&p->min_latency),
|
SPA_PROP_minLatency, SPA_POD_OPT_Int(&p->min_latency),
|
||||||
SPA_PROP_maxLatency, SPA_POD_OPT_Int(&p->max_latency),
|
SPA_PROP_maxLatency, SPA_POD_OPT_Int(&p->max_latency),
|
||||||
SPA_PROP_latencyOffsetNsec, SPA_POD_OPT_Long(&info.ns),
|
SPA_PROP_latencyOffsetNsec, SPA_POD_OPT_Long(&info.ns),
|
||||||
SPA_PROP_START_CUSTOM, SPA_POD_OPT_Bool(&p->use_chmap));
|
SPA_PROP_START_CUSTOM, SPA_POD_OPT_Bool(&p->use_chmap),
|
||||||
|
SPA_PROP_iec958Codecs, SPA_POD_OPT_Pod(&iec958_codecs));
|
||||||
|
|
||||||
|
if (iec958_codecs != NULL) {
|
||||||
|
uint32_t i, codecs[16], n_codecs;
|
||||||
|
n_codecs = spa_pod_copy_array(iec958_codecs, SPA_TYPE_Id,
|
||||||
|
codecs, SPA_N_ELEMENTS(codecs));
|
||||||
|
this->iec958_codecs = 0;
|
||||||
|
for (i = 0; i < n_codecs; i++)
|
||||||
|
this->iec958_codecs |= 1ULL << codecs[i];
|
||||||
|
}
|
||||||
handle_process_latency(this, &info);
|
handle_process_latency(this, &info);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -916,6 +949,8 @@ impl_init(const struct spa_handle_factory *factory,
|
||||||
this->process_latency.rate = atoi(s);
|
this->process_latency.rate = atoi(s);
|
||||||
} else if (spa_streq(k, "latency.internal.ns")) {
|
} else if (spa_streq(k, "latency.internal.ns")) {
|
||||||
this->process_latency.ns = atoi(s);
|
this->process_latency.ns = atoi(s);
|
||||||
|
} else if (spa_streq(k, "iec958.codecs")) {
|
||||||
|
spa_alsa_parse_iec958_codecs(&this->iec958_codecs, s, strlen(s));
|
||||||
} else if (spa_streq(k, "api.alsa.period-size")) {
|
} else if (spa_streq(k, "api.alsa.period-size")) {
|
||||||
this->default_period_size = atoi(s);
|
this->default_period_size = atoi(s);
|
||||||
} else if (spa_streq(k, "api.alsa.headroom")) {
|
} else if (spa_streq(k, "api.alsa.headroom")) {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,12 @@ int spa_alsa_init(struct state *state)
|
||||||
|
|
||||||
snd_config_update_free_global();
|
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) {
|
if (state->open_ucm) {
|
||||||
char card_name[64];
|
char card_name[64];
|
||||||
const char *alibpref = NULL;
|
const char *alibpref = NULL;
|
||||||
|
|
@ -57,6 +63,7 @@ int spa_alsa_init(struct state *state)
|
||||||
free((void*)alibpref);
|
free((void*)alibpref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -313,40 +320,21 @@ static void sanitize_map(snd_pcm_chmap_t* map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int enum_pcm_formats(struct state *state, uint32_t index, uint32_t *next,
|
||||||
spa_alsa_enum_format(struct state *state, int seq, uint32_t start, uint32_t num,
|
struct spa_pod **result, struct spa_pod_builder *b)
|
||||||
const struct spa_pod *filter)
|
|
||||||
{
|
{
|
||||||
|
int err, dir;
|
||||||
|
size_t i, j;
|
||||||
snd_pcm_t *hndl;
|
snd_pcm_t *hndl;
|
||||||
snd_pcm_hw_params_t *params;
|
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_format_mask_t *fmask;
|
||||||
snd_pcm_access_mask_t *amask;
|
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 min, max;
|
||||||
unsigned int rrate, rchannels;
|
unsigned int rrate, rchannels;
|
||||||
uint8_t buffer[4096];
|
|
||||||
struct spa_pod_builder b = { 0 };
|
|
||||||
struct spa_pod_choice *choice;
|
struct spa_pod_choice *choice;
|
||||||
struct spa_pod *fmt;
|
uint32_t rate;
|
||||||
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));
|
|
||||||
|
|
||||||
hndl = state->hndl;
|
hndl = state->hndl;
|
||||||
snd_pcm_hw_params_alloca(¶ms);
|
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_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
|
||||||
spa_pod_builder_add(&b,
|
spa_pod_builder_add(b,
|
||||||
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),
|
||||||
0);
|
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_access_mask_alloca(&amask);
|
||||||
snd_pcm_hw_params_get_access_mask(params, 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);
|
spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0);
|
||||||
choice = (struct spa_pod_choice*)spa_pod_builder_frame(&b, &f[1]);
|
choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]);
|
||||||
|
|
||||||
for (i = 1, j = 0; i < SPA_N_ELEMENTS(format_info); i++) {
|
for (i = 1, j = 0; i < SPA_N_ELEMENTS(format_info); i++) {
|
||||||
const struct format_info *fi = &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 &&
|
fi->spa_pformat != SPA_AUDIO_FORMAT_UNKNOWN &&
|
||||||
(state->default_format == 0 || state->default_format == fi->spa_pformat)) {
|
(state->default_format == 0 || state->default_format == fi->spa_pformat)) {
|
||||||
if (j++ == 0)
|
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) ||
|
if ((snd_pcm_access_mask_test(amask, SND_PCM_ACCESS_MMAP_INTERLEAVED) ||
|
||||||
snd_pcm_access_mask_test(amask, SND_PCM_ACCESS_RW_INTERLEAVED)) &&
|
snd_pcm_access_mask_test(amask, SND_PCM_ACCESS_RW_INTERLEAVED)) &&
|
||||||
(state->default_format == 0 || state->default_format == fi->spa_format)) {
|
(state->default_format == 0 || state->default_format == fi->spa_format)) {
|
||||||
if (j++ == 0)
|
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)
|
if (j > 1)
|
||||||
choice->body.type = SPA_CHOICE_Enum;
|
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_min(params, &min, &dir), "get_rate_min");
|
||||||
CHECK(snd_pcm_hw_params_get_rate_max(params, &max, &dir), "get_rate_max");
|
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;
|
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);
|
spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0);
|
||||||
choice = (struct spa_pod_choice*)spa_pod_builder_frame(&b, &f[1]);
|
choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]);
|
||||||
|
|
||||||
rate = state->position ? state->position->clock.rate.denom : DEFAULT_RATE;
|
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) {
|
if (min != max) {
|
||||||
spa_pod_builder_int(&b, min);
|
spa_pod_builder_int(b, min);
|
||||||
spa_pod_builder_int(&b, max);
|
spa_pod_builder_int(b, max);
|
||||||
choice->body.type = SPA_CHOICE_Range;
|
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_min(params, &min), "get_channels_min");
|
||||||
CHECK(snd_pcm_hw_params_get_channels_max(params, &max), "get_channels_max");
|
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);
|
min = SPA_MIN(min, SPA_AUDIO_MAX_CHANNELS);
|
||||||
max = SPA_MIN(max, 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) {
|
if (state->props.use_chmap && (maps = snd_pcm_query_chmaps(hndl)) != NULL) {
|
||||||
uint32_t channel;
|
uint32_t channel;
|
||||||
snd_pcm_chmap_t* map;
|
snd_pcm_chmap_t* map;
|
||||||
|
|
||||||
skip_channels:
|
skip_channels:
|
||||||
if (maps[result.index] == NULL) {
|
if (maps[index] == NULL) {
|
||||||
snd_pcm_free_chmaps(maps);
|
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);
|
spa_log_debug(state->log, "map %d channels (%d %d)", map->channels, min, max);
|
||||||
|
|
||||||
if (map->channels < min || map->channels > max) {
|
if (map->channels < min || map->channels > max) {
|
||||||
result.index = result.next++;
|
index = (*next)++;
|
||||||
goto skip_channels;
|
goto skip_channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
sanitize_map(map);
|
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_prop(b, SPA_FORMAT_AUDIO_position, 0);
|
||||||
spa_pod_builder_push_array(&b, &f[1]);
|
spa_pod_builder_push_array(b, &f[1]);
|
||||||
for (j = 0; j < map->channels; j++) {
|
for (j = 0; j < map->channels; j++) {
|
||||||
spa_log_debug(state->log, NAME" %p: position %zd %d", state, j, map->pos[j]);
|
spa_log_debug(state->log, NAME" %p: position %zd %d", state, j, map->pos[j]);
|
||||||
channel = chmap_position_to_channel(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);
|
snd_pcm_free_chmaps(maps);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const struct channel_map *map = NULL;
|
const struct channel_map *map = NULL;
|
||||||
|
|
||||||
if (result.index > 0)
|
if (index > 0)
|
||||||
goto enum_end;
|
return 0;
|
||||||
|
|
||||||
spa_pod_builder_push_choice(&b, &f[1], SPA_CHOICE_None, 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]);
|
choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]);
|
||||||
spa_pod_builder_int(&b, max);
|
spa_pod_builder_int(b, max);
|
||||||
if (min != max) {
|
if (min != max) {
|
||||||
spa_pod_builder_int(&b, min);
|
spa_pod_builder_int(b, min);
|
||||||
spa_pod_builder_int(&b, max);
|
spa_pod_builder_int(b, max);
|
||||||
choice->body.type = SPA_CHOICE_Range;
|
choice->body.type = SPA_CHOICE_Range;
|
||||||
}
|
}
|
||||||
spa_pod_builder_pop(&b, &f[1]);
|
spa_pod_builder_pop(b, &f[1]);
|
||||||
|
|
||||||
if (min == max) {
|
if (min == max) {
|
||||||
if (state->default_pos.channels == min)
|
if (state->default_pos.channels == min)
|
||||||
|
|
@ -537,17 +525,147 @@ skip_channels:
|
||||||
map = &default_map[min];
|
map = &default_map[min];
|
||||||
}
|
}
|
||||||
if (map) {
|
if (map) {
|
||||||
spa_pod_builder_prop(&b, SPA_FORMAT_AUDIO_position, 0);
|
spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_position, 0);
|
||||||
spa_pod_builder_push_array(&b, &f[1]);
|
spa_pod_builder_push_array(b, &f[1]);
|
||||||
for (j = 0; j < map->channels; j++) {
|
for (j = 0; j < map->channels; j++) {
|
||||||
spa_log_debug(state->log, NAME" %p: position %zd %d", state, j, map->pos[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)
|
if (spa_pod_filter(&b, &result.param, fmt, filter) < 0)
|
||||||
goto next;
|
goto next;
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,10 @@ struct state {
|
||||||
unsigned int planar:1;
|
unsigned int planar:1;
|
||||||
unsigned int freewheel:1;
|
unsigned int freewheel:1;
|
||||||
unsigned int open_ucm:1;
|
unsigned int open_ucm:1;
|
||||||
|
unsigned int is_iec958:1;
|
||||||
|
unsigned int is_hdmi:1;
|
||||||
|
|
||||||
|
uint64_t iec958_codecs;
|
||||||
|
|
||||||
int64_t sample_count;
|
int64_t sample_count;
|
||||||
|
|
||||||
|
|
@ -262,6 +266,44 @@ static inline void spa_alsa_parse_position(struct channel_map *map, const char *
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline uint32_t spa_alsa_iec958_codec_from_name(const char *name)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; spa_type_audio_iec958_codec[i].name; i++) {
|
||||||
|
if (strcmp(name, spa_debug_type_short_name(spa_type_audio_iec958_codec[i].name)) == 0)
|
||||||
|
return spa_type_audio_iec958_codec[i].type;
|
||||||
|
}
|
||||||
|
return SPA_AUDIO_IEC958_CODEC_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void spa_alsa_parse_iec958_codecs(uint64_t *codecs, const char *val, size_t len)
|
||||||
|
{
|
||||||
|
struct spa_json it[2];
|
||||||
|
char v[256];
|
||||||
|
|
||||||
|
spa_json_init(&it[0], val, len);
|
||||||
|
if (spa_json_enter_array(&it[0], &it[1]) <= 0)
|
||||||
|
spa_json_init(&it[1], val, len);
|
||||||
|
|
||||||
|
*codecs = 0;
|
||||||
|
while (spa_json_get_string(&it[1], v, sizeof(v)) > 0)
|
||||||
|
*codecs |= 1ULL << spa_alsa_iec958_codec_from_name(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t spa_alsa_get_iec958_codecs(struct state *state, uint32_t *codecs,
|
||||||
|
uint32_t max_codecs)
|
||||||
|
{
|
||||||
|
uint64_t mask = state->iec958_codecs;
|
||||||
|
uint32_t i = 0, j = 0;
|
||||||
|
while (mask && i < max_codecs) {
|
||||||
|
if (mask & 1)
|
||||||
|
codecs[i++] = j;
|
||||||
|
mask >>= 1;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@ rules = [
|
||||||
#api.alsa.disable-mmap = false
|
#api.alsa.disable-mmap = false
|
||||||
#api.alsa.disable-batch = false
|
#api.alsa.disable-batch = false
|
||||||
#api.alsa.use-chmap = false
|
#api.alsa.use-chmap = false
|
||||||
|
#iec958.codecs = [ PCM DTS AC3 MPEG MPEG2-AAC EAC3 TrueHD DTS-HD ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue