mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-11-08 13:30:03 -05:00
pcm: iec958: implement HDMI HBR audio formatting
High bitrate compressed audio data like DTS HD or MAT is usually packed into 8-channel data. The HDMI specs state this has to be formatted as a single IEC958 stream, compared to normal multi- channel PCM data which has to be formatted as parallel IEC958 streams. As this single-stream formatting mode may break existing setups that expect non-PCM multichannel data to be formatted as parallel IEC958 streams it needs to be explicitly selected by setting the hdmi_mode option to true. The single-stream formatting implementation is prepared to cope with arbitrary channel counts but only limited testing was done for channel counts other than 8. Signed-off-by: Matthias Reichl <hias@horus.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
d824b461ae
commit
8defc5c2a6
1 changed files with 33 additions and 4 deletions
|
|
@ -63,6 +63,7 @@ struct snd_pcm_iec958 {
|
||||||
unsigned int byteswap;
|
unsigned int byteswap;
|
||||||
unsigned char preamble[3]; /* B/M/W or Z/X/Y */
|
unsigned char preamble[3]; /* B/M/W or Z/X/Y */
|
||||||
snd_pcm_fast_ops_t fops;
|
snd_pcm_fast_ops_t fops;
|
||||||
|
int hdmi_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum { PREAMBLE_Z, PREAMBLE_X, PREAMBLE_Y };
|
enum { PREAMBLE_Z, PREAMBLE_X, PREAMBLE_Y };
|
||||||
|
|
@ -193,6 +194,10 @@ static void snd_pcm_iec958_encode(snd_pcm_iec958_t *iec,
|
||||||
unsigned int channel;
|
unsigned int channel;
|
||||||
int32_t sample = 0;
|
int32_t sample = 0;
|
||||||
int counter = iec->counter;
|
int counter = iec->counter;
|
||||||
|
int single_stream = iec->hdmi_mode &&
|
||||||
|
(iec->status[0] & IEC958_AES0_NONAUDIO) &&
|
||||||
|
(channels == 8);
|
||||||
|
int counter_step = single_stream ? ((channels + 1) >> 1) : 1;
|
||||||
for (channel = 0; channel < channels; ++channel) {
|
for (channel = 0; channel < channels; ++channel) {
|
||||||
const char *src;
|
const char *src;
|
||||||
uint32_t *dst;
|
uint32_t *dst;
|
||||||
|
|
@ -205,7 +210,12 @@ static void snd_pcm_iec958_encode(snd_pcm_iec958_t *iec,
|
||||||
src_step = snd_pcm_channel_area_step(src_area);
|
src_step = snd_pcm_channel_area_step(src_area);
|
||||||
dst_step = snd_pcm_channel_area_step(dst_area) / sizeof(uint32_t);
|
dst_step = snd_pcm_channel_area_step(dst_area) / sizeof(uint32_t);
|
||||||
frames1 = frames;
|
frames1 = frames;
|
||||||
iec->counter = counter;
|
|
||||||
|
if (single_stream)
|
||||||
|
iec->counter = (counter + (channel >> 1)) % 192;
|
||||||
|
else
|
||||||
|
iec->counter = counter;
|
||||||
|
|
||||||
while (frames1-- > 0) {
|
while (frames1-- > 0) {
|
||||||
goto *get;
|
goto *get;
|
||||||
#define GET32_END after
|
#define GET32_END after
|
||||||
|
|
@ -217,9 +227,11 @@ static void snd_pcm_iec958_encode(snd_pcm_iec958_t *iec,
|
||||||
*dst = sample;
|
*dst = sample;
|
||||||
src += src_step;
|
src += src_step;
|
||||||
dst += dst_step;
|
dst += dst_step;
|
||||||
iec->counter++;
|
iec->counter += counter_step;
|
||||||
iec->counter %= 192;
|
iec->counter %= 192;
|
||||||
}
|
}
|
||||||
|
if (single_stream) /* set counter to ch0 value for next iteration */
|
||||||
|
iec->counter = (counter + frames * counter_step) % 192;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* DOC_HIDDEN */
|
#endif /* DOC_HIDDEN */
|
||||||
|
|
@ -473,6 +485,7 @@ static const snd_pcm_ops_t snd_pcm_iec958_ops = {
|
||||||
* \param close_slave When set, the slave PCM handle is closed with copy PCM
|
* \param close_slave When set, the slave PCM handle is closed with copy PCM
|
||||||
* \param status_bits The IEC958 status bits
|
* \param status_bits The IEC958 status bits
|
||||||
* \param preamble_vals The preamble byte values
|
* \param preamble_vals The preamble byte values
|
||||||
|
* \param hdmi_mode When set, enable HDMI compliant formatting
|
||||||
* \retval zero on success otherwise a negative error code
|
* \retval zero on success otherwise a negative error code
|
||||||
* \warning Using of this function might be dangerous in the sense
|
* \warning Using of this function might be dangerous in the sense
|
||||||
* of compatibility reasons. The prototype might be freely
|
* of compatibility reasons. The prototype might be freely
|
||||||
|
|
@ -481,7 +494,8 @@ static const snd_pcm_ops_t snd_pcm_iec958_ops = {
|
||||||
int snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat,
|
int snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat,
|
||||||
snd_pcm_t *slave, int close_slave,
|
snd_pcm_t *slave, int close_slave,
|
||||||
const unsigned char *status_bits,
|
const unsigned char *status_bits,
|
||||||
const unsigned char *preamble_vals)
|
const unsigned char *preamble_vals,
|
||||||
|
int hdmi_mode)
|
||||||
{
|
{
|
||||||
snd_pcm_t *pcm;
|
snd_pcm_t *pcm;
|
||||||
snd_pcm_iec958_t *iec;
|
snd_pcm_iec958_t *iec;
|
||||||
|
|
@ -519,6 +533,8 @@ int snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sfo
|
||||||
|
|
||||||
memcpy(iec->preamble, preamble_vals, 3);
|
memcpy(iec->preamble, preamble_vals, 3);
|
||||||
|
|
||||||
|
iec->hdmi_mode = hdmi_mode;
|
||||||
|
|
||||||
err = snd_pcm_new(&pcm, SND_PCM_TYPE_IEC958, name, slave->stream, slave->mode);
|
err = snd_pcm_new(&pcm, SND_PCM_TYPE_IEC958, name, slave->stream, slave->mode);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
free(iec);
|
free(iec);
|
||||||
|
|
@ -566,9 +582,14 @@ pcm.name {
|
||||||
[preamble.z or preamble.b val]
|
[preamble.z or preamble.b val]
|
||||||
[preamble.x or preamble.m val]
|
[preamble.x or preamble.m val]
|
||||||
[preamble.y or preamble.w val]
|
[preamble.y or preamble.w val]
|
||||||
|
[hdmi_mode true]
|
||||||
}
|
}
|
||||||
\endcode
|
\endcode
|
||||||
|
|
||||||
|
When <code>hdmi_mode</code> is true, 8-channel compressed data is
|
||||||
|
formatted as 4 contiguous frames of a single IEC958 stream as required
|
||||||
|
by the HDMI HBR specification.
|
||||||
|
|
||||||
\subsection pcm_plugins_iec958_funcref Function reference
|
\subsection pcm_plugins_iec958_funcref Function reference
|
||||||
|
|
||||||
<UL>
|
<UL>
|
||||||
|
|
@ -605,6 +626,7 @@ int _snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name,
|
||||||
unsigned char preamble_vals[3] = {
|
unsigned char preamble_vals[3] = {
|
||||||
0x08, 0x02, 0x04 /* Z, X, Y */
|
0x08, 0x02, 0x04 /* Z, X, Y */
|
||||||
};
|
};
|
||||||
|
int hdmi_mode = 0;
|
||||||
|
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
snd_config_t *n = snd_config_iterator_entry(i);
|
||||||
|
|
@ -633,6 +655,13 @@ int _snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name,
|
||||||
preamble = n;
|
preamble = n;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (strcmp(id, "hdmi_mode") == 0) {
|
||||||
|
err = snd_config_get_bool(n);
|
||||||
|
if (err < 0)
|
||||||
|
continue;
|
||||||
|
hdmi_mode = err;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
SNDERR("Unknown field %s", id);
|
SNDERR("Unknown field %s", id);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
@ -707,7 +736,7 @@ int _snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name,
|
||||||
return err;
|
return err;
|
||||||
err = snd_pcm_iec958_open(pcmp, name, sformat, spcm, 1,
|
err = snd_pcm_iec958_open(pcmp, name, sformat, spcm, 1,
|
||||||
status ? status_bits : NULL,
|
status ? status_bits : NULL,
|
||||||
preamble_vals);
|
preamble_vals, hdmi_mode);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
snd_pcm_close(spcm);
|
snd_pcm_close(spcm);
|
||||||
return err;
|
return err;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue