mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-06-07 07:58:51 +00:00
Implement seeking on wav
Original commit message from CVS: Implement seeking on wav
This commit is contained in:
parent
9e147e261e
commit
ab71414650
1 changed files with 139 additions and 64 deletions
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue