mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-26 17:18:15 +00:00
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:
parent
ca1b8db04a
commit
f0154849b0
7 changed files with 204 additions and 22 deletions
13
ChangeLog
13
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),
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 (¶ms);
|
||||
|
||||
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:
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue