ext/alsa/gstalsa.c: Probe for IEC958 pcm to detect if we can do SPDIF output.

Original commit message from CVS:
2008-02-29  Julien Moutte  <julien@fluendo.com>

* ext/alsa/gstalsa.c: (gst_alsa_open_iec958_pcm),
(gst_alsa_probe_supported_formats): Probe for IEC958 pcm to
detect
if we can do SPDIF output.
* ext/alsa/gstalsa.h:
* ext/alsa/gstalsasink.c: (set_hwparams), (alsasink_parse_spec),
(gst_alsasink_prepare), (gst_alsasink_close),
(gst_alsasink_write):
* ext/alsa/gstalsasink.h: Initial support for SPDIF.
* gst-libs/gst/audio/gstringbuffer.c:
(gst_ring_buffer_parse_caps):
* gst-libs/gst/audio/gstringbuffer.h: Add non linear buffer
types
to support AC3, EC3 and IEC958 buffers.
This commit is contained in:
Julien Moutte 2008-02-29 18:44:36 +00:00
parent ca1b8db04a
commit f0154849b0
7 changed files with 204 additions and 22 deletions

View file

@ -1,3 +1,16 @@
2008-02-29 Julien Moutte <julien@fluendo.com>
* ext/alsa/gstalsa.c: (gst_alsa_open_iec958_pcm),
(gst_alsa_probe_supported_formats): Probe for IEC958 pcm to detect
if we can do SPDIF output.
* ext/alsa/gstalsa.h:
* ext/alsa/gstalsasink.c: (set_hwparams), (alsasink_parse_spec),
(gst_alsasink_prepare), (gst_alsasink_close), (gst_alsasink_write):
* ext/alsa/gstalsasink.h: Initial support for SPDIF.
* gst-libs/gst/audio/gstringbuffer.c: (gst_ring_buffer_parse_caps):
* gst-libs/gst/audio/gstringbuffer.h: Add non linear buffer types
to support AC3, EC3 and IEC958 buffers.
2008-02-29 Tim-Philipp Müller <tim at centricular dot net>
* gst-libs/gst/interfaces/mixer.c: (GST_MIXER_MESSAGE_HAS_TYPE),

View file

@ -360,6 +360,45 @@ max_chan_error:
}
}
snd_pcm_t *
gst_alsa_open_iec958_pcm (GstObject * obj)
{
char *iec958_pcm_name = NULL;
snd_pcm_t *pcm = NULL;
int res;
char devstr[256]; /* Storage for local 'default' device string */
/*
* Try and open our default iec958 device. Fall back to searching on card x
* if this fails, which should only happen on older alsa setups
*/
/* The string will be one of these:
* SPDIF_CON: Non-audio flag not set:
* spdif:{AES0 0x0 AES1 0x82 AES2 0x0 AES3 0x2}
* SPDIF_CON: Non-audio flag set:
* spdif:{AES0 0x2 AES1 0x82 AES2 0x0 AES3 0x2}
*/
sprintf (devstr,
"iec958:{AES0 0x%02x AES1 0x%02x AES2 0x%02x AES3 0x%02x}",
IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO,
IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
0, IEC958_AES3_CON_FS_48000);
GST_DEBUG_OBJECT (obj, "Generated device string \"%s\"", devstr);
iec958_pcm_name = devstr;
res = snd_pcm_open (&pcm, iec958_pcm_name, SND_PCM_STREAM_PLAYBACK, 0);
if (G_UNLIKELY (res < 0)) {
GST_DEBUG_OBJECT (obj, "failed opening IEC958 device: %s",
snd_strerror (res));
pcm = NULL;
}
return pcm;
}
/*
* gst_alsa_probe_supported_formats:
*
@ -373,6 +412,7 @@ gst_alsa_probe_supported_formats (GstObject * obj, snd_pcm_t * handle,
const GstCaps * template_caps)
{
snd_pcm_hw_params_t *hw_params;
snd_pcm_stream_t stream_type;
GstCaps *caps;
gint err;
@ -380,6 +420,8 @@ gst_alsa_probe_supported_formats (GstObject * obj, snd_pcm_t * handle,
if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0)
goto error;
stream_type = snd_pcm_stream (handle);
caps = gst_caps_copy (template_caps);
if (!(caps = gst_alsa_detect_formats (obj, hw_params, caps)))
@ -391,6 +433,17 @@ gst_alsa_probe_supported_formats (GstObject * obj, snd_pcm_t * handle,
if (!(caps = gst_alsa_detect_channels (obj, hw_params, caps)))
goto subroutine_error;
/* Try opening IEC958 device to see if we can support that format (playback
* only for now but we could add SPDIF capture later) */
if (stream_type == SND_PCM_STREAM_PLAYBACK) {
snd_pcm_t *pcm = gst_alsa_open_iec958_pcm (obj);
if (G_LIKELY (pcm)) {
gst_caps_append (caps, gst_caps_new_simple ("audio/x-iec958", NULL));
snd_pcm_close (pcm);
}
}
snd_pcm_hw_params_free (hw_params);
return caps;

View file

@ -41,6 +41,8 @@
GST_DEBUG_CATEGORY_EXTERN (alsa_debug);
#define GST_CAT_DEFAULT alsa_debug
snd_pcm_t * gst_alsa_open_iec958_pcm (GstObject * obj);
GstCaps * gst_alsa_probe_supported_formats (GstObject * obj,
snd_pcm_t * handle,
const GstCaps * template_caps);

View file

@ -67,6 +67,8 @@ GST_ELEMENT_DETAILS ("Audio sink (ALSA)",
#define DEFAULT_DEVICE "default"
#define DEFAULT_DEVICE_NAME ""
#define SPDIF_PERIOD_SIZE 1536
#define SPDIF_BUFFER_SIZE 15360
enum
{
@ -141,7 +143,8 @@ static GstStaticPadTemplate alsasink_sink_factory =
"signed = (boolean) { TRUE, FALSE }, "
"width = (int) 8, "
"depth = (int) 8, "
"rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]")
"rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ];"
"audio/x-iec958")
);
static void
@ -335,8 +338,9 @@ set_hwparams (GstAlsaSink * alsa)
snd_pcm_hw_params_malloc (&params);
GST_DEBUG_OBJECT (alsa, "Negotiating to %d channels @ %d Hz (format = %s)",
alsa->channels, alsa->rate, snd_pcm_format_name (alsa->format));
GST_DEBUG_OBJECT (alsa, "Negotiating to %d channels @ %d Hz (format = %s) "
"SPDIF (%d)", alsa->channels, alsa->rate,
snd_pcm_format_name (alsa->format), alsa->iec958);
/* start with requested values, if we cannot configure alsa for those values,
* we set these values to -1, which will leave the default alsa values */
@ -350,6 +354,16 @@ retry:
CHECK (snd_pcm_hw_params_set_access (alsa->handle, params, alsa->access),
wrong_access);
/* set the sample format */
if (alsa->iec958) {
/* Try to use big endian first else fallback to le and swap bytes */
if (snd_pcm_hw_params_set_format (alsa->handle, params, alsa->format) < 0) {
alsa->format = SND_PCM_FORMAT_S16_LE;
alsa->need_swap = TRUE;
GST_DEBUG_OBJECT (alsa, "falling back to little endian with swapping");
} else {
alsa->need_swap = FALSE;
}
}
CHECK (snd_pcm_hw_params_set_format (alsa->handle, params, alsa->format),
no_sample_format);
/* set the count of channels */
@ -386,7 +400,7 @@ retry:
/* now try to configure the buffer time and period time, if one
* of those fail, we fall back to the defaults and emit a warning. */
if (buffer_time != -1) {
if (buffer_time != -1 && !alsa->iec958) {
/* set the buffer time */
if ((err = snd_pcm_hw_params_set_buffer_time_near (alsa->handle, params,
&buffer_time, &dir)) < 0) {
@ -399,7 +413,7 @@ retry:
}
GST_DEBUG_OBJECT (alsa, "buffer time %u", buffer_time);
}
if (period_time != -1) {
if (period_time != -1 && !alsa->iec958) {
/* set the period time */
if ((err = snd_pcm_hw_params_set_period_time_near (alsa->handle, params,
&period_time, &dir)) < 0) {
@ -413,6 +427,17 @@ retry:
GST_DEBUG_OBJECT (alsa, "period time %u", period_time);
}
/* Set buffer size and period size manually for SPDIF */
if (G_UNLIKELY (alsa->iec958)) {
snd_pcm_uframes_t buffer_size = SPDIF_BUFFER_SIZE;
snd_pcm_uframes_t period_size = SPDIF_PERIOD_SIZE;
CHECK (snd_pcm_hw_params_set_buffer_size_near (alsa->handle, params,
&buffer_size), buffer_size);
CHECK (snd_pcm_hw_params_set_period_size_near (alsa->handle, params,
&period_size, NULL), period_size);
}
/* write the parameters to device */
CHECK (snd_pcm_hw_params (alsa->handle, params), set_hw_params);
@ -584,6 +609,9 @@ set_sw_params:
static gboolean
alsasink_parse_spec (GstAlsaSink * alsa, GstRingBufferSpec * spec)
{
/* Initialize our boolean */
alsa->iec958 = FALSE;
switch (spec->type) {
case GST_BUFTYPE_LINEAR:
GST_DEBUG_OBJECT (alsa,
@ -617,6 +645,13 @@ alsasink_parse_spec (GstAlsaSink * alsa, GstRingBufferSpec * spec)
case GST_BUFTYPE_MU_LAW:
alsa->format = SND_PCM_FORMAT_MU_LAW;
break;
case GST_BUFTYPE_NONLINEAR:
alsa->format = SND_PCM_FORMAT_S16_BE;
if (G_LIKELY (spec->format == GST_IEC958))
alsa->iec958 = TRUE;
else
goto error;
break;
default:
goto error;
@ -676,6 +711,14 @@ gst_alsasink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
alsa = GST_ALSA_SINK (asink);
if (spec->format == GST_IEC958) {
snd_pcm_close (alsa->handle);
alsa->handle = gst_alsa_open_iec958_pcm (GST_OBJECT (alsa));
if (G_UNLIKELY (!alsa->handle)) {
goto no_iec958;
}
}
if (!alsasink_parse_spec (alsa, spec))
goto spec_parse;
@ -688,9 +731,31 @@ gst_alsasink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
spec->segsize = alsa->period_size * spec->bytes_per_sample;
spec->segtotal = alsa->buffer_size / alsa->period_size;
{
snd_output_t *out_buf = NULL;
char *msg = NULL;
snd_output_buffer_open (&out_buf);
snd_pcm_dump_hw_setup (alsa->handle, out_buf);
snd_output_buffer_string (out_buf, &msg);
GST_DEBUG_OBJECT (alsa, "Hardware setup: \n%s", msg);
snd_output_close (out_buf);
snd_output_buffer_open (&out_buf);
snd_pcm_dump_sw_setup (alsa->handle, out_buf);
snd_output_buffer_string (out_buf, &msg);
GST_DEBUG_OBJECT (alsa, "Software setup: \n%s", msg);
snd_output_close (out_buf);
}
return TRUE;
/* ERRORS */
no_iec958:
{
GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_WRITE, (NULL),
("Could not open IEC958 (SPDIF) device for playback"));
return FALSE;
}
spec_parse:
{
GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
@ -760,8 +825,10 @@ gst_alsasink_close (GstAudioSink * asink)
GstAlsaSink *alsa = GST_ALSA_SINK (asink);
gint err;
CHECK (snd_pcm_close (alsa->handle), close_error);
alsa->handle = NULL;
if (alsa->handle) {
CHECK (snd_pcm_close (alsa->handle), close_error);
alsa->handle = NULL;
}
gst_caps_replace (&alsa->cached_caps, NULL);
return TRUE;
@ -813,18 +880,28 @@ gst_alsasink_write (GstAudioSink * asink, gpointer data, guint length)
GstAlsaSink *alsa;
gint err;
gint cptr;
gint16 *ptr;
gint16 *ptr = data;
alsa = GST_ALSA_SINK (asink);
if (alsa->iec958 && alsa->need_swap) {
guint i;
GST_DEBUG_OBJECT (asink, "swapping bytes");
for (i = 0; i < length; i += 2) {
ptr[i / 2] = GUINT16_SWAP_LE_BE (ptr[i / 2]);
}
}
GST_LOG_OBJECT (asink, "received audio samples buffer of %u bytes", length);
cptr = length / alsa->bytes_per_sample;
ptr = data;
GST_ALSA_SINK_LOCK (asink);
while (cptr > 0) {
err = snd_pcm_writei (alsa->handle, ptr, cptr);
GST_DEBUG_OBJECT (asink, "written %d result %d", cptr, err);
GST_DEBUG_OBJECT (asink, "written %d frames out of %d", err, cptr);
if (err < 0) {
GST_DEBUG_OBJECT (asink, "Write error: %s", snd_strerror (err));
if (err == -EAGAIN) {
@ -835,12 +912,12 @@ gst_alsasink_write (GstAudioSink * asink, gpointer data, guint length)
continue;
}
ptr += err * alsa->channels;
ptr += snd_pcm_frames_to_bytes (alsa->handle, err);
cptr -= err;
}
GST_ALSA_SINK_UNLOCK (asink);
return length - cptr;
return length - (cptr * alsa->bytes_per_sample);
write_error:
{

View file

@ -62,6 +62,8 @@ struct _GstAlsaSink {
guint rate;
guint channels;
gint bytes_per_sample;
gboolean iec958;
gboolean need_swap;
guint buffer_time;
guint period_time;

View file

@ -286,11 +286,6 @@ gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
/* we have to differentiate between int and float formats */
mimetype = gst_structure_get_name (structure);
/* get rate and channels */
if (!(gst_structure_get_int (structure, "rate", &spec->rate) &&
gst_structure_get_int (structure, "channels", &spec->channels)))
goto parse_error;
if (!strncmp (mimetype, "audio/x-raw-int", 15)) {
gint endianness;
const FormatDef *def;
@ -299,7 +294,9 @@ gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
spec->type = GST_BUFTYPE_LINEAR;
/* extract the needed information from the cap */
if (!(gst_structure_get_int (structure, "width", &spec->width) &&
if (!(gst_structure_get_int (structure, "rate", &spec->rate) &&
gst_structure_get_int (structure, "channels", &spec->channels) &&
gst_structure_get_int (structure, "width", &spec->width) &&
gst_structure_get_int (structure, "depth", &spec->depth) &&
gst_structure_get_boolean (structure, "signed", &spec->sign)))
goto parse_error;
@ -333,8 +330,10 @@ gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
spec->type = GST_BUFTYPE_FLOAT;
/* get layout */
if (!gst_structure_get_int (structure, "width", &spec->width))
/* extract the needed information from the cap */
if (!(gst_structure_get_int (structure, "rate", &spec->rate) &&
gst_structure_get_int (structure, "channels", &spec->channels) &&
gst_structure_get_int (structure, "width", &spec->width)))
goto parse_error;
/* match layout to format wrt to endianness */
@ -353,6 +352,11 @@ gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
/* float silence is all zeros.. */
memset (spec->silence_sample, 0, 32);
} else if (!strncmp (mimetype, "audio/x-alaw", 12)) {
/* extract the needed information from the cap */
if (!(gst_structure_get_int (structure, "rate", &spec->rate) &&
gst_structure_get_int (structure, "channels", &spec->channels)))
goto parse_error;
spec->type = GST_BUFTYPE_A_LAW;
spec->format = GST_A_LAW;
spec->width = 8;
@ -360,12 +364,37 @@ gst_ring_buffer_parse_caps (GstRingBufferSpec * spec, GstCaps * caps)
for (i = 0; i < spec->channels; i++)
spec->silence_sample[i] = 0xd5;
} else if (!strncmp (mimetype, "audio/x-mulaw", 13)) {
/* extract the needed information from the cap */
if (!(gst_structure_get_int (structure, "rate", &spec->rate) &&
gst_structure_get_int (structure, "channels", &spec->channels)))
goto parse_error;
spec->type = GST_BUFTYPE_MU_LAW;
spec->format = GST_MU_LAW;
spec->width = 8;
spec->depth = 8;
for (i = 0; i < spec->channels; i++)
spec->silence_sample[i] = 0xff;
} else if (!strncmp (mimetype, "audio/x-iec958", 14)) {
/* extract the needed information from the cap */
if (!(gst_structure_get_int (structure, "rate", &spec->rate)))
goto parse_error;
spec->type = GST_BUFTYPE_NONLINEAR;
spec->format = GST_IEC958;
spec->width = 16;
spec->depth = 16;
spec->channels = 2;
} else if (!strncmp (mimetype, "audio/x-ac3", 11)) {
/* extract the needed information from the cap */
if (!(gst_structure_get_int (structure, "rate", &spec->rate)))
goto parse_error;
spec->type = GST_BUFTYPE_NONLINEAR;
spec->format = GST_AC3;
spec->width = 16;
spec->depth = 16;
spec->channels = 2;
} else {
goto parse_error;
}

View file

@ -90,6 +90,7 @@ typedef enum {
* @GST_BUFTYPE_IMA_ADPCM: samples in ima adpcm
* @GST_BUFTYPE_MPEG: samples in mpeg audio format
* @GST_BUFTYPE_GSM: samples in gsm format
* @GST_BUFTYPE_NONLINEAR: samples in non linear format (ac3, ec3, dts, ...)
*
* The format of the samples in the ringbuffer.
*/
@ -101,7 +102,8 @@ typedef enum
GST_BUFTYPE_A_LAW,
GST_BUFTYPE_IMA_ADPCM,
GST_BUFTYPE_MPEG,
GST_BUFTYPE_GSM
GST_BUFTYPE_GSM,
GST_BUFTYPE_NONLINEAR
} GstBufferFormatType;
typedef enum
@ -149,7 +151,11 @@ typedef enum
GST_A_LAW,
GST_IMA_ADPCM,
GST_MPEG,
GST_GSM
GST_GSM,
GST_IEC958,
GST_AC3,
GST_EC3,
GST_DTS
} GstBufferFormat;
/**