#include <gstwavparse.h>
-static void gst_wavparse_class_init (GstWavParseClass *klass);
-static void gst_wavparse_init (GstWavParse *wavparse);
+static void gst_wavparse_class_init (GstWavParseClass *klass);
+static void gst_wavparse_init (GstWavParse *wavparse);
-static GstCaps* wav_type_find (GstBuffer *buf, gpointer private);
+static GstCaps* wav_type_find (GstBuffer *buf, gpointer private);
static const GstFormat* gst_wavparse_get_formats (GstPad *pad);
static const GstQueryType *
gst_wavparse_get_query_types (GstPad *pad);
-static gboolean gst_wavparse_pad_query (GstPad *pad,
- GstQueryType type,
- GstFormat *format,
- gint64 *value);
-static gboolean gst_wavparse_pad_convert (GstPad *pad,
- GstFormat src_format,
- gint64 src_value,
- GstFormat *dest_format,
- gint64 *dest_value);
-static void gst_wavparse_chain (GstPad *pad, GstBuffer *buf);
+static gboolean gst_wavparse_pad_query (GstPad *pad,
+ GstQueryType type,
+ GstFormat *format,
+ gint64 *value);
+static gboolean gst_wavparse_pad_convert (GstPad *pad,
+ GstFormat src_format,
+ gint64 src_value,
+ GstFormat *dest_format,
+ gint64 *dest_value);
+static void gst_wavparse_chain (GstPad *pad, GstBuffer *buf);
+
+static const GstEventMask*
+ gst_wavparse_get_event_masks (GstPad *pad);
+static gboolean gst_wavparse_srcpad_event (GstPad *pad, GstEvent *event);
/* elementfactory information */
static GstElementDetails gst_wavparse_details = {
gst_pad_set_query_type_function (wavparse->srcpad,
gst_wavparse_get_query_types);
gst_pad_set_query_function (wavparse->srcpad, gst_wavparse_pad_query);
+ gst_pad_set_event_function (wavparse->srcpad, gst_wavparse_srcpad_event);
+ gst_pad_set_event_mask_function (wavparse->srcpad, gst_wavparse_get_event_masks);
gst_pad_set_chain_function (wavparse->sinkpad, gst_wavparse_chain);
wavparse->riff_nextlikely = 0;
wavparse->size = 0;
wavparse->bps = 0;
+ wavparse->offset = 0;
+ wavparse->need_discont = FALSE;
}
static GstCaps*
return gst_caps_new ("wav_type_find", "audio/x-wav", NULL);
}
-
-/* set timestamp on outgoing buffer
- * returns TRUE if a timestamp was set
- */
-static gboolean
-gst_wavparse_set_timestamp (GstWavParse *wavparse, GstBuffer *buf)
-{
- gboolean retval = FALSE;
-
- /* only do timestamps on linear audio */
- switch (wavparse->format)
- {
- case GST_RIFF_WAVE_FORMAT_PCM:
- GST_BUFFER_TIMESTAMP (buf) = wavparse->offset * GST_SECOND
- / wavparse->rate;
- wavparse->offset += GST_BUFFER_SIZE (buf) * 8
- / (wavparse->width * wavparse->channels);
- retval = TRUE;
- break;
- default:
- break;
- }
- return retval;
-}
-
static void
gst_wavparse_chain (GstPad *pad, GstBuffer *buf)
{
GstWavParse *wavparse;
gboolean buffer_riffed = FALSE; /* so we don't parse twice */
- gchar *data;
gulong size;
g_return_if_fail (pad != NULL);
wavparse = GST_WAVPARSE (gst_pad_get_parent (pad));
GST_DEBUG (0, "gst_wavparse_chain: got buffer in '%s'",
gst_object_get_name (GST_OBJECT (wavparse)));
- data = (guchar *) GST_BUFFER_DATA (buf);
+
size = GST_BUFFER_SIZE (buf);
/* walk through the states in priority order */
/* we're in the data region */
if (wavparse->state == GST_WAVPARSE_DATA) {
+ GstFormat format;
+ guint64 maxsize;
+
+ /* we can't go beyond the max length */
+ maxsize = wavparse->riff_nextlikely - GST_BUFFER_OFFSET (buf);
+
/* if we're expected to see a new chunk in this buffer */
- if ((wavparse->riff_nextlikely - GST_BUFFER_OFFSET (buf))
- < GST_BUFFER_SIZE (buf)) {
- GST_BUFFER_SIZE (buf) = wavparse->riff_nextlikely
- - GST_BUFFER_OFFSET (buf);
+ if (maxsize < size) {
+ GstBuffer *newbuf;
+
+ newbuf = gst_buffer_create_sub (buf, 0, maxsize);
+ gst_buffer_unref (buf);
+ buf = newbuf;
+
+ size = maxsize;
wavparse->state = GST_WAVPARSE_OTHER;
/* I suppose we could signal an EOF at this point, but that may be
premature. We've stopped data flow, that's the main thing. */
}
- gst_wavparse_set_timestamp (wavparse, buf);
-
- if (GST_PAD_IS_USABLE (wavparse->srcpad))
+ if (GST_PAD_IS_USABLE (wavparse->srcpad)) {
+ format = GST_FORMAT_TIME;
+ gst_pad_convert (wavparse->srcpad,
+ GST_FORMAT_BYTES,
+ wavparse->offset,
+ &format,
+ &GST_BUFFER_TIMESTAMP (buf));
+
+ if (wavparse->need_discont) {
+ gst_pad_push (wavparse->srcpad,
+ GST_BUFFER (gst_event_new_discontinuous (FALSE,
+ GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buf),
+ NULL)));
+ wavparse->need_discont = FALSE;
+ }
gst_pad_push (wavparse->srcpad, buf);
+ }
else
gst_buffer_unref (buf);
+
+ wavparse->offset += size;
+
return;
}
GST_DEBUG (0, "data begins at %ld", datachunk->offset);
+ wavparse->datastart = datachunk->offset;
+
/* at this point we can ACK that we have data */
wavparse->state = GST_WAVPARSE_DATA;
subsize = size - datachunk->offset;
GST_DEBUG (0, "sending last %ld bytes along as audio", subsize);
- newbuf = gst_buffer_new ();
- GST_BUFFER_DATA (newbuf) = g_malloc (subsize);
- GST_BUFFER_SIZE (newbuf) = subsize;
-
- gst_wavparse_set_timestamp (wavparse, newbuf);
-
- memcpy (GST_BUFFER_DATA (newbuf),
- GST_BUFFER_DATA (buf) + datachunk->offset, subsize);
-
+ newbuf = gst_buffer_create_sub (buf, datachunk->offset, subsize);
gst_buffer_unref (buf);
+ GST_BUFFER_TIMESTAMP (newbuf) = 0;
+
if (GST_PAD_IS_USABLE (wavparse->srcpad))
gst_pad_push (wavparse->srcpad, newbuf);
else
gst_buffer_unref (newbuf);
+ wavparse->offset = subsize;
+
/* now we're ready to go, the next buffer should start data */
wavparse->state = GST_WAVPARSE_DATA;
GstWavParse *wavparse;
wavparse = GST_WAVPARSE (gst_pad_get_parent (pad));
+ /* FIXME default should be samples in this case IMO */
if (*dest_format == GST_FORMAT_DEFAULT)
*dest_format = GST_FORMAT_TIME;
bytes_per_sample = wavparse->channels * wavparse->width / 8;
if (bytes_per_sample == 0) {
- g_warning ("bytes_per_sample is 0, internal error\n");
- g_warning ("channels %d, width %d\n",
- wavparse->channels, wavparse->width);
- return FALSE;
+ g_warning ("bytes_per_sample is 0, internal error\n");
+ g_warning ("channels %d, width %d\n",
+ wavparse->channels, wavparse->width);
+ return FALSE;
}
byterate = (glong) (bytes_per_sample * wavparse->rate);
if (byterate == 0) {
- g_warning ("byterate is 0, internal error\n");
- return FALSE;
+ g_warning ("byterate is 0, internal error\n");
+ return FALSE;
}
- g_print ("DEBUG: bytes per sample: %d\n", bytes_per_sample);
+ GST_DEBUG (0, "bytes per sample: %d\n", bytes_per_sample);
switch (src_format) {
case GST_FORMAT_BYTES:
if (*dest_format == GST_FORMAT_BYTES)
*dest_value = src_value * byterate / GST_SECOND;
else if (*dest_format == GST_FORMAT_UNITS)
- *dest_value = src_value * wavparse->rate /GST_SECOND;
+ *dest_value = src_value * wavparse->rate / GST_SECOND;
else
return FALSE;
+
+ *dest_value = *dest_value & ~(bytes_per_sample - 1);
break;
default:
g_warning ("unhandled format for wavparse\n");
g_warning ("Could not query sink pad's peer\n");
return FALSE;
}
- g_print ("DEBUG: pad_query done, value %" G_GINT64_FORMAT "\n", *value);
+ GST_DEBUG (0, "pad_query done, value %" G_GINT64_FORMAT "\n", *value);
return TRUE;
}
+static const GstEventMask*
+gst_wavparse_get_event_masks (GstPad *pad)
+{
+ static const GstEventMask gst_wavparse_src_event_masks[] = {
+ { GST_EVENT_SEEK, GST_SEEK_METHOD_SET |
+ GST_SEEK_FLAG_FLUSH },
+ { 0, }
+ };
+ return gst_wavparse_src_event_masks;
+}
+
+static gboolean
+gst_wavparse_srcpad_event (GstPad *pad, GstEvent *event)
+{
+ GstWavParse *wavparse = GST_WAVPARSE (GST_PAD_PARENT (pad));
+ gboolean res = FALSE;
+
+ GST_DEBUG(0, "event %d", GST_EVENT_TYPE (event));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEEK:
+ {
+ gint64 byteoffset;
+ GstFormat format;
+
+ /* we can only seek when in the DATA state */
+ if (wavparse->state != GST_WAVPARSE_DATA) {
+ return FALSE;
+ }
+
+ format = GST_FORMAT_BYTES;
+
+ /* bring format to bytes for the peer element,
+ * FIXME be smarter here */
+ res = gst_pad_convert (pad,
+ GST_EVENT_SEEK_FORMAT (event),
+ GST_EVENT_SEEK_OFFSET (event),
+ &format,
+ &byteoffset);
+
+ if (res) {
+ GstEvent *seek;
+
+ /* seek to byteoffset + header length */
+ seek = gst_event_new_seek (
+ GST_FORMAT_BYTES |
+ (GST_EVENT_SEEK_TYPE (event) & ~GST_SEEK_FORMAT_MASK),
+ byteoffset + wavparse->datastart);
+
+ res = gst_pad_send_event (GST_PAD_PEER (wavparse->sinkpad), seek);
+
+ if (res) {
+ /* ok, seek worked, update our state */
+ wavparse->offset = byteoffset;
+ wavparse->need_discont = TRUE;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ gst_event_unref (event);
+ return res;
+}
static gboolean
plugin_init (GModule *module, GstPlugin *plugin)