summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Moutte <julien@moutte.net>2008-02-29 18:44:36 +0000
committerJulien Moutte <julien@moutte.net>2008-02-29 18:44:36 +0000
commitf0154849b0bfafa7bb8cd690389afc824be9e90e (patch)
tree9580887f35182107c3a40381d102bfebb21e9428
parentca1b8db04afaaa42eefe1b5e9560fa036fbb30f4 (diff)
downloadgstreamer-plugins-base-f0154849b0bfafa7bb8cd690389afc824be9e90e.tar.gz
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.
-rw-r--r--ChangeLog13
-rw-r--r--ext/alsa/gstalsa.c53
-rw-r--r--ext/alsa/gstalsa.h2
-rw-r--r--ext/alsa/gstalsasink.c101
-rw-r--r--ext/alsa/gstalsasink.h2
-rw-r--r--gst-libs/gst/audio/gstringbuffer.c45
-rw-r--r--gst-libs/gst/audio/gstringbuffer.h10
7 files changed, 204 insertions, 22 deletions
diff --git a/ChangeLog b/ChangeLog
index 3567a6725..4ba771a33 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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),
diff --git a/ext/alsa/gstalsa.c b/ext/alsa/gstalsa.c
index 194e64d46..9fb16a498 100644
--- a/ext/alsa/gstalsa.c
+++ b/ext/alsa/gstalsa.c
@@ -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;
diff --git a/ext/alsa/gstalsa.h b/ext/alsa/gstalsa.h
index b2e1e9862..e7af889f8 100644
--- a/ext/alsa/gstalsa.h
+++ b/ext/alsa/gstalsa.h
@@ -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);
diff --git a/ext/alsa/gstalsasink.c b/ext/alsa/gstalsasink.c
index b4e3413be..1e90108cc 100644
--- a/ext/alsa/gstalsasink.c
+++ b/ext/alsa/gstalsasink.c
@@ -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:
{
diff --git a/ext/alsa/gstalsasink.h b/ext/alsa/gstalsasink.h
index 4218386d4..902dbf77e 100644
--- a/ext/alsa/gstalsasink.h
+++ b/ext/alsa/gstalsasink.h
@@ -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;
diff --git a/gst-libs/gst/audio/gstringbuffer.c b/gst-libs/gst/audio/gstringbuffer.c
index 7effd7fb6..da0479b06 100644
--- a/gst-libs/gst/audio/gstringbuffer.c
+++ b/gst-libs/gst/audio/gstringbuffer.c
@@ -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;
}
diff --git a/gst-libs/gst/audio/gstringbuffer.h b/gst-libs/gst/audio/gstringbuffer.h
index 6729cdb78..a998de034 100644
--- a/gst-libs/gst/audio/gstringbuffer.h
+++ b/gst-libs/gst/audio/gstringbuffer.h
@@ -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;
/**