* Boston, MA 02111-1307, USA.
*
*/
-
+/**
+ * SECTION:element-wavenc
+ *
+ * Format a audio stream into the wav format.
+ *
+ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include "gstwavenc.h"
-#include "riff.h"
+
+#include <gst/audio/audio.h>
+#include <gst/riff/riff-media.h>
GST_DEBUG_CATEGORY_STATIC (wavenc_debug);
#define GST_CAT_DEFAULT wavenc_debug
-#define WAVE_FORMAT_PCM 0x0001
-
struct riff_struct
{
guint8 id[4]; /* RIFF */
struct chunk_struct data;
};
-static const GstElementDetails gst_wavenc_details =
-GST_ELEMENT_DETAILS ("WAV audio muxer",
- "Codec/Muxer/Audio",
- "Encode raw audio into WAV",
- "Iain Holmes <iain@prettypeople.org>");
-
/* FIXME: mono doesn't produce correct files it seems, at least mplayer xruns */
/* Max. of two channels, more channels need WAVFORMATEX with
* channel layout, which we do not support yet */
#define SINK_CAPS \
- "audio/x-raw-int, " \
+ "audio/x-raw, " \
"rate = (int) [ 1, MAX ], " \
"channels = (int) [ 1, 2 ], " \
- "endianness = (int) LITTLE_ENDIAN, " \
- "width = (int) 32, " \
- "depth = (int) [ 25, 32 ], " \
- "signed = (boolean) true" \
+ "format = (string) { S32LE, S24LE, S16LE, U8, F32LE, F64LE } " \
"; " \
- "audio/x-raw-int, " \
- "rate = (int) [ 1, MAX ], " \
- "channels = (int) [ 1, 2 ], " \
- "endianness = (int) LITTLE_ENDIAN, " \
- "width = (int) 24, " \
- "depth = (int) [ 17, 24 ], " \
- "signed = (boolean) true" \
- "; " \
- "audio/x-raw-int, " \
- "rate = (int) [ 1, MAX ], " \
+ "audio/x-alaw, " \
+ "rate = (int) [ 8000, 192000 ], " \
"channels = (int) [ 1, 2 ], " \
- "endianness = (int) LITTLE_ENDIAN, " \
- "width = (int) 16, " \
- "depth = (int) [ 9, 16 ], " \
- "signed = (boolean) true" \
- "; " \
- "audio/x-raw-int, " \
- "rate = (int) [ 1, MAX ], " \
+ "width = (int) 8, " \
+ "depth = (int) 8, " \
+ "signed = (boolean) false; " \
+ "audio/x-mulaw, " \
+ "rate = (int) [ 8000, 192000 ], " \
"channels = (int) [ 1, 2 ], " \
"width = (int) 8, " \
- "depth = (int) [ 1, 8 ], " \
+ "depth = (int) 8, " \
"signed = (boolean) false"
+
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-wav")
);
-GST_BOILERPLATE (GstWavEnc, gst_wavenc, GstElement, GST_TYPE_ELEMENT);
+#define gst_wavenc_parent_class parent_class
+G_DEFINE_TYPE (GstWavEnc, gst_wavenc, GST_TYPE_ELEMENT);
static GstFlowReturn gst_wavenc_chain (GstPad * pad, GstBuffer * buf);
static gboolean gst_wavenc_event (GstPad * pad, GstEvent * event);
static gboolean gst_wavenc_sink_setcaps (GstPad * pad, GstCaps * caps);
static void
-gst_wavenc_base_init (gpointer g_class)
+gst_wavenc_class_init (GstWavEncClass * klass)
{
- GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+ GstElementClass *element_class;
+
+ element_class = (GstElementClass *) klass;
- gst_element_class_set_details (element_class, &gst_wavenc_details);
+ element_class->change_state = GST_DEBUG_FUNCPTR (gst_wavenc_change_state);
+
+ gst_element_class_set_details_simple (element_class, "WAV audio muxer",
+ "Codec/Muxer/Audio",
+ "Encode raw audio into WAV", "Iain Holmes <iain@prettypeople.org>");
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_factory));
}
static void
-gst_wavenc_class_init (GstWavEncClass * klass)
-{
- GstElementClass *element_class;
-
- element_class = (GstElementClass *) klass;
-
- element_class->change_state = GST_DEBUG_FUNCPTR (gst_wavenc_change_state);
-}
-
-static void
-gst_wavenc_init (GstWavEnc * wavenc, GstWavEncClass * klass)
+gst_wavenc_init (GstWavEnc * wavenc)
{
wavenc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
gst_pad_set_chain_function (wavenc->sinkpad,
GST_DEBUG_FUNCPTR (gst_wavenc_chain));
gst_pad_set_event_function (wavenc->sinkpad,
GST_DEBUG_FUNCPTR (gst_wavenc_event));
- gst_pad_set_setcaps_function (wavenc->sinkpad,
- GST_DEBUG_FUNCPTR (gst_wavenc_sink_setcaps));
+ gst_pad_use_fixed_caps (wavenc->sinkpad);
gst_element_add_pad (GST_ELEMENT (wavenc), wavenc->sinkpad);
wavenc->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
guint8 *header;
buf = gst_buffer_new_and_alloc (WAV_HEADER_LEN);
- header = GST_BUFFER_DATA (buf);
+ header = gst_buffer_map (buf, NULL, NULL, GST_MAP_WRITE);
memset (header, 0, WAV_HEADER_LEN);
wave.common.wChannels = wavenc->channels;
- wave.common.wBitsPerSample = wavenc->depth;
+ wave.common.wBitsPerSample = wavenc->width;
wave.common.dwSamplesPerSec = wavenc->rate;
/* Fill out our wav-header with some information */
memcpy (wave.format.id, "fmt ", 4);
wave.format.len = 16;
- wave.common.wFormatTag = WAVE_FORMAT_PCM;
+ wave.common.wFormatTag = wavenc->format;
wave.common.wBlockAlign = (wavenc->width / 8) * wave.common.wChannels;
wave.common.dwAvgBytesPerSec =
wave.common.wBlockAlign * wave.common.dwSamplesPerSec;
memcpy (header + 36, (char *) wave.data.id, 4);
GST_WRITE_UINT32_LE (header + 40, wave.data.len);
- gst_buffer_set_caps (buf, GST_PAD_CAPS (wavenc->srcpad));
+ gst_buffer_unmap (buf, header, -1);
return buf;
}
{
GstFlowReturn ret;
GstBuffer *outbuf;
+ GstSegment segment;
/* seek to beginning of file */
- gst_pad_push_event (wavenc->srcpad,
- gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
- 0, GST_CLOCK_TIME_NONE, 0));
+ gst_segment_init (&segment, GST_FORMAT_BYTES);
+ gst_pad_push_event (wavenc->srcpad, gst_event_new_segment (&segment));
GST_DEBUG_OBJECT (wavenc, "writing header with datasize=%u", audio_data_size);
{
GstWavEnc *wavenc;
GstStructure *structure;
- gint chans, rate, width, depth;
+ const gchar *name;
+ gint chans, rate;
+ GstCaps *ccaps;
wavenc = GST_WAVENC (gst_pad_get_parent (pad));
- if (wavenc->sent_header) {
+ ccaps = gst_pad_get_current_caps (pad);
+ if (wavenc->sent_header && ccaps && !gst_caps_can_intersect (caps, ccaps)) {
+ gst_caps_unref (ccaps);
GST_WARNING_OBJECT (wavenc, "cannot change format in middle of stream");
goto fail;
}
+ if (ccaps)
+ gst_caps_unref (ccaps);
GST_DEBUG_OBJECT (wavenc, "got caps: %" GST_PTR_FORMAT, caps);
structure = gst_caps_get_structure (caps, 0);
+ name = gst_structure_get_name (structure);
+
if (!gst_structure_get_int (structure, "channels", &chans) ||
- !gst_structure_get_int (structure, "rate", &rate) ||
- !gst_structure_get_int (structure, "width", &width) ||
- !gst_structure_get_int (structure, "depth", &depth)) {
+ !gst_structure_get_int (structure, "rate", &rate)) {
GST_WARNING_OBJECT (wavenc, "caps incomplete");
goto fail;
}
+ if (strcmp (name, "audio/x-raw") == 0) {
+ GstAudioInfo info;
+
+ if (!gst_audio_info_from_caps (&info, caps))
+ goto fail;
+
+ if (GST_AUDIO_INFO_IS_INTEGER (&info))
+ wavenc->format = GST_RIFF_WAVE_FORMAT_PCM;
+ else if (GST_AUDIO_INFO_IS_FLOAT (&info))
+ wavenc->format = GST_RIFF_WAVE_FORMAT_IEEE_FLOAT;
+ else
+ goto fail;
+
+ wavenc->width = GST_AUDIO_INFO_WIDTH (&info);
+ } else if (strcmp (name, "audio/x-alaw") == 0) {
+ wavenc->format = GST_RIFF_WAVE_FORMAT_ALAW;
+ wavenc->width = 8;
+ } else if (strcmp (name, "audio/x-mulaw") == 0) {
+ wavenc->format = GST_RIFF_WAVE_FORMAT_MULAW;
+ wavenc->width = 8;
+ } else {
+ GST_WARNING_OBJECT (wavenc, "Unsupported format %s", name);
+ goto fail;
+ }
+
wavenc->channels = chans;
- wavenc->depth = depth;
- wavenc->width = width;
wavenc->rate = rate;
- GST_LOG_OBJECT (wavenc, "accepted caps: chans=%u width=%u depth=%u rate=%u",
- wavenc->channels, wavenc->width, wavenc->depth, wavenc->rate);
+ GST_LOG_OBJECT (wavenc,
+ "accepted caps: format=0x%04x chans=%u width=%u rate=%u",
+ wavenc->format, wavenc->channels, wavenc->width, wavenc->rate);
gst_object_unref (wavenc);
return TRUE;
wavenc = GST_WAVENC (gst_pad_get_parent (pad));
switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_CAPS:
+ {
+ GstCaps *caps;
+
+ gst_event_parse_caps (event, &caps);
+ gst_wavenc_sink_setcaps (pad, caps);
+
+ /* have our own src caps */
+ gst_event_unref (event);
+ break;
+ }
case GST_EVENT_EOS:{
GST_DEBUG_OBJECT (wavenc, "got EOS");
#if 0
/* write header with correct length values */
gst_wavenc_push_header (wavenc, wavenc->length);
+ /* we're done with this file */
+ wavenc->finished_properly = TRUE;
+
/* and forward the EOS event */
res = gst_pad_event_default (pad, event);
break;
}
- case GST_EVENT_NEWSEGMENT:
+ case GST_EVENT_SEGMENT:
/* Just drop it, it's probably in TIME format
* anyway. We'll send our own newsegment event */
gst_event_unref (event);
return res;
}
-/* Copied from gst-plugins-base/gst/audioconvert/audioconvert.c */
-#define READ24_FROM_LE(p) (p[0] | (p[1] << 8) | (p[2] << 16))
-#define WRITE24_TO_LE(p,v) p[0] = v & 0xff; p[1] = (v >> 8) & 0xff; p[2] = (v >> 16) & 0xff
-
-/* Correctly format samples with width!=depth for the wav format, i.e.
- * have the data in the highest depth bits and all others zero */
-static void
-gst_wavenc_format_samples (GstBuffer * buf, guint width, guint depth)
-{
- guint8 *data = GST_BUFFER_DATA (buf);
- guint nsamples = (GST_BUFFER_SIZE (buf) * 8) / width;
- guint32 tmp;
-
- for (; nsamples; nsamples--) {
- switch (width) {
-
- case 8:
- tmp = *data;
- *data = *data << (width - depth);
- data += 1;
- break;
- case 16:
- tmp = GST_READ_UINT16_LE (data);
- tmp = tmp << (width - depth);
- GST_WRITE_UINT16_LE (data, tmp);
- data += 2;
- break;
- case 24:
- tmp = READ24_FROM_LE (data);
- tmp = tmp << (width - depth);
- WRITE24_TO_LE (data, tmp);
- data += 3;
- break;
- case 32:
- tmp = GST_READ_UINT32_LE (data);
- tmp = tmp << (width - depth);
- GST_WRITE_UINT32_LE (data, tmp);
- data += 4;
- break;
- }
- }
-}
-
-#undef READ24_FROM_LE
-#undef WRITE24_TO_LE
-
static GstFlowReturn
gst_wavenc_chain (GstPad * pad, GstBuffer * buf)
{
* header when we get EOS and know the exact length */
flow = gst_wavenc_push_header (wavenc, 0x7FFF0000);
+ /* starting a file, means we have to finish it properly */
+ wavenc->finished_properly = FALSE;
+
if (flow != GST_FLOW_OK)
return flow;
wavenc->sent_header = TRUE;
}
- wavenc->length += GST_BUFFER_SIZE (buf);
-
GST_LOG_OBJECT (wavenc, "pushing %u bytes raw audio, ts=%" GST_TIME_FORMAT,
- GST_BUFFER_SIZE (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
+ gst_buffer_get_size (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
- if (wavenc->width != wavenc->depth) {
- buf = gst_buffer_make_writable (buf);
- gst_wavenc_format_samples (buf, wavenc->width, wavenc->depth);
- } else {
- buf = gst_buffer_make_metadata_writable (buf);
- }
+ buf = gst_buffer_make_writable (buf);
- gst_buffer_set_caps (buf, GST_PAD_CAPS (wavenc->srcpad));
GST_BUFFER_OFFSET (buf) = WAV_HEADER_LEN + wavenc->length;
GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_NONE;
+ wavenc->length += gst_buffer_get_size (buf);
+
flow = gst_pad_push (wavenc->srcpad, buf);
return flow;
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
+ wavenc->format = 0;
wavenc->channels = 0;
- wavenc->depth = 0;
wavenc->width = 0;
wavenc->rate = 0;
wavenc->length = 0;
wavenc->sent_header = FALSE;
+ /* its true because we haven't writen anything */
+ wavenc->finished_properly = TRUE;
break;
default:
break;
}
- ret = parent_class->change_state (element, transition);
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret != GST_STATE_CHANGE_SUCCESS)
return ret;
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ if (!wavenc->finished_properly) {
+ GST_ELEMENT_WARNING (wavenc, STREAM, MUX,
+ ("Wav stream not finished properly"),
+ ("Wav stream not finished properly, no EOS received "
+ "before shutdown"));
+ }
+ break;
+ default:
+ break;
+ }
+
return ret;
}
static gboolean
plugin_init (GstPlugin * plugin)
{
- return gst_element_register (plugin, "wavenc", GST_RANK_NONE,
+ return gst_element_register (plugin, "wavenc", GST_RANK_PRIMARY,
GST_TYPE_WAVENC);
}