Implement seeking on wav

Original commit message from CVS:
Implement seeking on wav
This commit is contained in:
Wim Taymans 2003-03-01 18:34:38 +00:00
parent 9e147e261e
commit ab71414650

View file

@ -22,24 +22,28 @@
#include <gstwavparse.h> #include <gstwavparse.h>
static void gst_wavparse_class_init (GstWavParseClass *klass); static void gst_wavparse_class_init (GstWavParseClass *klass);
static void gst_wavparse_init (GstWavParse *wavparse); 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 GstFormat* gst_wavparse_get_formats (GstPad *pad);
static const GstQueryType * static const GstQueryType *
gst_wavparse_get_query_types (GstPad *pad); gst_wavparse_get_query_types (GstPad *pad);
static gboolean gst_wavparse_pad_query (GstPad *pad, static gboolean gst_wavparse_pad_query (GstPad *pad,
GstQueryType type, GstQueryType type,
GstFormat *format, GstFormat *format,
gint64 *value); gint64 *value);
static gboolean gst_wavparse_pad_convert (GstPad *pad, static gboolean gst_wavparse_pad_convert (GstPad *pad,
GstFormat src_format, GstFormat src_format,
gint64 src_value, gint64 src_value,
GstFormat *dest_format, GstFormat *dest_format,
gint64 *dest_value); gint64 *dest_value);
static void gst_wavparse_chain (GstPad *pad, GstBuffer *buf); 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 */ /* elementfactory information */
static GstElementDetails gst_wavparse_details = { static GstElementDetails gst_wavparse_details = {
@ -172,6 +176,8 @@ gst_wavparse_init (GstWavParse *wavparse)
gst_pad_set_query_type_function (wavparse->srcpad, gst_pad_set_query_type_function (wavparse->srcpad,
gst_wavparse_get_query_types); gst_wavparse_get_query_types);
gst_pad_set_query_function (wavparse->srcpad, gst_wavparse_pad_query); 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); gst_pad_set_chain_function (wavparse->sinkpad, gst_wavparse_chain);
@ -183,6 +189,8 @@ gst_wavparse_init (GstWavParse *wavparse)
wavparse->riff_nextlikely = 0; wavparse->riff_nextlikely = 0;
wavparse->size = 0; wavparse->size = 0;
wavparse->bps = 0; wavparse->bps = 0;
wavparse->offset = 0;
wavparse->need_discont = FALSE;
} }
static GstCaps* static GstCaps*
@ -196,37 +204,11 @@ wav_type_find (GstBuffer *buf, gpointer private)
return gst_caps_new ("wav_type_find", "audio/x-wav", NULL); 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 static void
gst_wavparse_chain (GstPad *pad, GstBuffer *buf) gst_wavparse_chain (GstPad *pad, GstBuffer *buf)
{ {
GstWavParse *wavparse; GstWavParse *wavparse;
gboolean buffer_riffed = FALSE; /* so we don't parse twice */ gboolean buffer_riffed = FALSE; /* so we don't parse twice */
gchar *data;
gulong size; gulong size;
g_return_if_fail (pad != NULL); g_return_if_fail (pad != NULL);
@ -237,29 +219,55 @@ gst_wavparse_chain (GstPad *pad, GstBuffer *buf)
wavparse = GST_WAVPARSE (gst_pad_get_parent (pad)); wavparse = GST_WAVPARSE (gst_pad_get_parent (pad));
GST_DEBUG (0, "gst_wavparse_chain: got buffer in '%s'", GST_DEBUG (0, "gst_wavparse_chain: got buffer in '%s'",
gst_object_get_name (GST_OBJECT (wavparse))); gst_object_get_name (GST_OBJECT (wavparse)));
data = (guchar *) GST_BUFFER_DATA (buf);
size = GST_BUFFER_SIZE (buf); size = GST_BUFFER_SIZE (buf);
/* walk through the states in priority order */ /* walk through the states in priority order */
/* we're in the data region */ /* we're in the data region */
if (wavparse->state == GST_WAVPARSE_DATA) { 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 we're expected to see a new chunk in this buffer */
if ((wavparse->riff_nextlikely - GST_BUFFER_OFFSET (buf)) if (maxsize < size) {
< GST_BUFFER_SIZE (buf)) { GstBuffer *newbuf;
GST_BUFFER_SIZE (buf) = wavparse->riff_nextlikely
- GST_BUFFER_OFFSET (buf); newbuf = gst_buffer_create_sub (buf, 0, maxsize);
gst_buffer_unref (buf);
buf = newbuf;
size = maxsize;
wavparse->state = GST_WAVPARSE_OTHER; wavparse->state = GST_WAVPARSE_OTHER;
/* I suppose we could signal an EOF at this point, but that may be /* 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. */ premature. We've stopped data flow, that's the main thing. */
} }
gst_wavparse_set_timestamp (wavparse, buf); 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 (GST_PAD_IS_USABLE (wavparse->srcpad)) 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); gst_pad_push (wavparse->srcpad, buf);
}
else else
gst_buffer_unref (buf); gst_buffer_unref (buf);
wavparse->offset += size;
return; return;
} }
@ -396,6 +404,8 @@ gst_wavparse_chain (GstPad *pad, GstBuffer *buf)
GST_DEBUG (0, "data begins at %ld", datachunk->offset); GST_DEBUG (0, "data begins at %ld", datachunk->offset);
wavparse->datastart = datachunk->offset;
/* at this point we can ACK that we have data */ /* at this point we can ACK that we have data */
wavparse->state = GST_WAVPARSE_DATA; wavparse->state = GST_WAVPARSE_DATA;
@ -403,22 +413,18 @@ gst_wavparse_chain (GstPad *pad, GstBuffer *buf)
subsize = size - datachunk->offset; subsize = size - datachunk->offset;
GST_DEBUG (0, "sending last %ld bytes along as audio", subsize); GST_DEBUG (0, "sending last %ld bytes along as audio", subsize);
newbuf = gst_buffer_new (); newbuf = gst_buffer_create_sub (buf, datachunk->offset, subsize);
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);
gst_buffer_unref (buf); gst_buffer_unref (buf);
GST_BUFFER_TIMESTAMP (newbuf) = 0;
if (GST_PAD_IS_USABLE (wavparse->srcpad)) if (GST_PAD_IS_USABLE (wavparse->srcpad))
gst_pad_push (wavparse->srcpad, newbuf); gst_pad_push (wavparse->srcpad, newbuf);
else else
gst_buffer_unref (newbuf); gst_buffer_unref (newbuf);
wavparse->offset = subsize;
/* now we're ready to go, the next buffer should start data */ /* now we're ready to go, the next buffer should start data */
wavparse->state = GST_WAVPARSE_DATA; wavparse->state = GST_WAVPARSE_DATA;
@ -456,22 +462,23 @@ gst_wavparse_pad_convert (GstPad *pad,
GstWavParse *wavparse; GstWavParse *wavparse;
wavparse = GST_WAVPARSE (gst_pad_get_parent (pad)); wavparse = GST_WAVPARSE (gst_pad_get_parent (pad));
/* FIXME default should be samples in this case IMO */
if (*dest_format == GST_FORMAT_DEFAULT) if (*dest_format == GST_FORMAT_DEFAULT)
*dest_format = GST_FORMAT_TIME; *dest_format = GST_FORMAT_TIME;
bytes_per_sample = wavparse->channels * wavparse->width / 8; bytes_per_sample = wavparse->channels * wavparse->width / 8;
if (bytes_per_sample == 0) { if (bytes_per_sample == 0) {
g_warning ("bytes_per_sample is 0, internal error\n"); g_warning ("bytes_per_sample is 0, internal error\n");
g_warning ("channels %d, width %d\n", g_warning ("channels %d, width %d\n",
wavparse->channels, wavparse->width); wavparse->channels, wavparse->width);
return FALSE; return FALSE;
} }
byterate = (glong) (bytes_per_sample * wavparse->rate); byterate = (glong) (bytes_per_sample * wavparse->rate);
if (byterate == 0) { if (byterate == 0) {
g_warning ("byterate is 0, internal error\n"); g_warning ("byterate is 0, internal error\n");
return FALSE; 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) { switch (src_format) {
case GST_FORMAT_BYTES: case GST_FORMAT_BYTES:
@ -494,9 +501,11 @@ gst_wavparse_pad_convert (GstPad *pad,
if (*dest_format == GST_FORMAT_BYTES) if (*dest_format == GST_FORMAT_BYTES)
*dest_value = src_value * byterate / GST_SECOND; *dest_value = src_value * byterate / GST_SECOND;
else if (*dest_format == GST_FORMAT_UNITS) else if (*dest_format == GST_FORMAT_UNITS)
*dest_value = src_value * wavparse->rate /GST_SECOND; *dest_value = src_value * wavparse->rate / GST_SECOND;
else else
return FALSE; return FALSE;
*dest_value = *dest_value & ~(bytes_per_sample - 1);
break; break;
default: default:
g_warning ("unhandled format for wavparse\n"); g_warning ("unhandled format for wavparse\n");
@ -539,10 +548,76 @@ gst_wavparse_pad_query (GstPad *pad, GstQueryType type,
g_warning ("Could not query sink pad's peer\n"); g_warning ("Could not query sink pad's peer\n");
return FALSE; 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; 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 static gboolean
plugin_init (GModule *module, GstPlugin *plugin) plugin_init (GModule *module, GstPlugin *plugin)