gst/mpegaudioparse/: Bring mp3parse into the 21st century.

Original commit message from CVS:
* gst/mpegaudioparse/Makefile.am:
* gst/mpegaudioparse/gstmpegaudioparse.c:
(mp3_type_frame_length_from_header), (gst_mp3parse_class_init),
(gst_mp3parse_reset), (gst_mp3parse_init), (gst_mp3parse_dispose),
(gst_mp3parse_sink_event), (gst_mp3parse_chain), (head_check),
(gst_mp3parse_change_state), (plugin_init):
* gst/mpegaudioparse/gstmpegaudioparse.h:
Bring mp3parse into the 21st century.
Use its own debug category, use gstadapter, format nicely to 80
columns, and fix incorrect handling of 32 kHz and less files.
This commit is contained in:
Michael Smith 2006-11-13 16:23:22 +00:00
parent 520f74867f
commit 13ecca8090
4 changed files with 148 additions and 145 deletions

View File

@ -1,3 +1,16 @@
2006-11-13 Michael Smith <msmith@fluendo.com>
* gst/mpegaudioparse/Makefile.am:
* gst/mpegaudioparse/gstmpegaudioparse.c:
(mp3_type_frame_length_from_header), (gst_mp3parse_class_init),
(gst_mp3parse_reset), (gst_mp3parse_init), (gst_mp3parse_dispose),
(gst_mp3parse_sink_event), (gst_mp3parse_chain), (head_check),
(gst_mp3parse_change_state), (plugin_init):
* gst/mpegaudioparse/gstmpegaudioparse.h:
Bring mp3parse into the 21st century.
Use its own debug category, use gstadapter, format nicely to 80
columns, and fix incorrect handling of 32 kHz and less files.
2006-11-03 Wim Taymans <wim@fluendo.com> 2006-11-03 Wim Taymans <wim@fluendo.com>
Patch by: Sebastian Droege <slomo at ubuntu dot com> Patch by: Sebastian Droege <slomo at ubuntu dot com>

View File

@ -2,7 +2,7 @@ plugin_LTLIBRARIES = libgstmpegaudioparse.la
libgstmpegaudioparse_la_SOURCES = gstmpegaudioparse.c libgstmpegaudioparse_la_SOURCES = gstmpegaudioparse.c
libgstmpegaudioparse_la_CFLAGS = $(GST_CFLAGS) libgstmpegaudioparse_la_CFLAGS = $(GST_CFLAGS)
libgstmpegaudioparse_la_LIBADD = $(GST_LIBS) libgstmpegaudioparse_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS)
libgstmpegaudioparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstmpegaudioparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
noinst_HEADERS = gstmpegaudioparse.h noinst_HEADERS = gstmpegaudioparse.h

View File

@ -17,12 +17,13 @@
* Boston, MA 02111-1307, USA. * Boston, MA 02111-1307, USA.
*/ */
/*#define GST_DEBUG_ENABLED */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" #include "config.h"
#endif #endif
#include "gstmpegaudioparse.h" #include "gstmpegaudioparse.h"
GST_DEBUG_CATEGORY_STATIC (mp3parse_debug);
#define GST_CAT_DEFAULT mp3parse_debug
/* elementfactory information */ /* elementfactory information */
static GstElementDetails mp3parse_details = { static GstElementDetails mp3parse_details = {
@ -70,8 +71,9 @@ static void gst_mp3parse_init (GstMPEGAudioParse * mp3parse);
static gboolean gst_mp3parse_sink_event (GstPad * pad, GstEvent * event); static gboolean gst_mp3parse_sink_event (GstPad * pad, GstEvent * event);
static GstFlowReturn gst_mp3parse_chain (GstPad * pad, GstBuffer * buffer); static GstFlowReturn gst_mp3parse_chain (GstPad * pad, GstBuffer * buffer);
static int head_check (unsigned long head); static int head_check (GstMPEGAudioParse * mp3parse, unsigned long head);
static void gst_mp3parse_dispose (GObject * object);
static void gst_mp3parse_set_property (GObject * object, guint prop_id, static void gst_mp3parse_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec); const GValue * value, GParamSpec * pspec);
static void gst_mp3parse_get_property (GObject * object, guint prop_id, static void gst_mp3parse_get_property (GObject * object, guint prop_id,
@ -122,8 +124,9 @@ static guint mp3types_freqs[3][3] = { {44100, 48000, 32000},
}; };
static inline guint static inline guint
mp3_type_frame_length_from_header (guint32 header, guint * put_layer, mp3_type_frame_length_from_header (GstMPEGAudioParse * mp3parse, guint32 header,
guint * put_channels, guint * put_bitrate, guint * put_samplerate) guint * put_layer, guint * put_channels, guint * put_bitrate,
guint * put_samplerate)
{ {
guint length; guint length;
gulong mode, samplerate, bitrate, layer, channels, padding; gulong mode, samplerate, bitrate, layer, channels, padding;
@ -160,9 +163,10 @@ mp3_type_frame_length_from_header (guint32 header, guint * put_layer,
break; break;
} }
GST_DEBUG ("Calculated mp3 frame length of %u bytes", length); GST_DEBUG_OBJECT (mp3parse, "Calculated mp3 frame length of %u bytes",
GST_DEBUG ("samplerate = %lu, bitrate = %lu, layer = %lu, channels = %lu", length);
samplerate, bitrate, layer, channels); GST_DEBUG_OBJECT (mp3parse, "samplerate = %lu, bitrate = %lu, layer = %lu, "
"channels = %lu", samplerate, bitrate, layer, channels);
if (put_layer) if (put_layer)
*put_layer = layer; *put_layer = layer;
@ -176,32 +180,6 @@ mp3_type_frame_length_from_header (guint32 header, guint * put_layer,
return length; return length;
} }
/*
* The chance that random data is identified as a valid mp3 header is 63 / 2^18
* (0.024%) per try. This makes the function for calculating false positives
* 1 - (1 - ((63 / 2 ^18) ^ GST_MP3_TYPEFIND_MIN_HEADERS)) ^ buffersize)
* This has the following probabilities of false positives:
* bufsize MIN_HEADERS
* (bytes) 1 2 3 4
* 4096 62.6% 0.02% 0% 0%
* 16384 98% 0.09% 0% 0%
* 1 MiB 100% 5.88% 0% 0%
* 1 GiB 100% 100% 1.44% 0%
* 1 TiB 100% 100% 100% 0.35%
* This means that the current choice (3 headers by most of the time 4096 byte
* buffers is pretty safe for now.
*
* The max. size of each frame is 1440 bytes, which means that for N frames
* to be detected, we need 1440 * GST_MP3_TYPEFIND_MIN_HEADERS + 3 of data.
* Assuming we step into the stream right after the frame header, this
* means we need 1440 * (GST_MP3_TYPEFIND_MIN_HEADERS + 1) - 1 + 3 bytes
* of data (5762) to always detect any mp3.
*/
/* increase this value when this function finds too many false positives */
#define GST_MP3_TYPEFIND_MIN_HEADERS 3
#define GST_MP3_TYPEFIND_MIN_DATA (1440 * (GST_MP3_TYPEFIND_MIN_HEADERS + 1) - 1 + 3)
static GstCaps * static GstCaps *
mp3_caps_create (guint layer, guint channels, guint bitrate, guint samplerate) mp3_caps_create (guint layer, guint channels, guint bitrate, guint samplerate)
{ {
@ -245,6 +223,7 @@ gst_mp3parse_class_init (GstMPEGAudioParseClass * klass)
gobject_class->set_property = gst_mp3parse_set_property; gobject_class->set_property = gst_mp3parse_set_property;
gobject_class->get_property = gst_mp3parse_get_property; gobject_class->get_property = gst_mp3parse_get_property;
gobject_class->dispose = gst_mp3parse_dispose;
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SKIP, g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SKIP,
g_param_spec_int ("skip", "skip", "skip", g_param_spec_int ("skip", "skip", "skip",
@ -256,6 +235,17 @@ gst_mp3parse_class_init (GstMPEGAudioParseClass * klass)
gstelement_class->change_state = gst_mp3parse_change_state; gstelement_class->change_state = gst_mp3parse_change_state;
} }
static void
gst_mp3parse_reset (GstMPEGAudioParse * mp3parse)
{
mp3parse->skip = 0;
mp3parse->resyncing = TRUE;
gst_adapter_clear (mp3parse->adapter);
mp3parse->rate = mp3parse->channels = mp3parse->layer = -1;
}
static void static void
gst_mp3parse_init (GstMPEGAudioParse * mp3parse) gst_mp3parse_init (GstMPEGAudioParse * mp3parse)
{ {
@ -271,13 +261,23 @@ gst_mp3parse_init (GstMPEGAudioParse * mp3parse)
(&mp3_src_template), "src"); (&mp3_src_template), "src");
gst_pad_use_fixed_caps (mp3parse->srcpad); gst_pad_use_fixed_caps (mp3parse->srcpad);
gst_element_add_pad (GST_ELEMENT (mp3parse), mp3parse->srcpad); gst_element_add_pad (GST_ELEMENT (mp3parse), mp3parse->srcpad);
/*gst_pad_set_type_id(mp3parse->srcpad, mp3frametype); */
mp3parse->partialbuf = NULL; mp3parse->adapter = gst_adapter_new ();
mp3parse->skip = 0;
mp3parse->in_flush = FALSE;
mp3parse->rate = mp3parse->channels = mp3parse->layer = -1; gst_mp3parse_reset (mp3parse);
}
static void
gst_mp3parse_dispose (GObject * object)
{
GstMPEGAudioParse *mp3parse = GST_MP3PARSE (object);
if (mp3parse->adapter) {
g_object_unref (mp3parse->adapter);
mp3parse->adapter = NULL;
}
G_OBJECT_CLASS (parent_class)->dispose (object);
} }
static gboolean static gboolean
@ -297,10 +297,10 @@ gst_mp3parse_sink_event (GstPad * pad, GstEvent * event)
NULL); NULL);
if (format != GST_FORMAT_TIME) if (format != GST_FORMAT_TIME)
mp3parse->last_ts = 0; mp3parse->next_ts = 0;
else else
/* we will be receiving timestamps */ /* we will be receiving timestamps */
mp3parse->last_ts = -1; mp3parse->next_ts = -1;
break; break;
} }
default: default:
@ -313,109 +313,107 @@ gst_mp3parse_sink_event (GstPad * pad, GstEvent * event)
return res; return res;
} }
/* FIXME, use adapter */
static GstFlowReturn static GstFlowReturn
gst_mp3parse_chain (GstPad * pad, GstBuffer * buf) gst_mp3parse_chain (GstPad * pad, GstBuffer * buf)
{ {
GstMPEGAudioParse *mp3parse; GstMPEGAudioParse *mp3parse;
guchar *data; const guchar *data;
glong size, offset = 0;
guint32 header; guint32 header;
int bpf; int bpf;
GstBuffer *outbuf; GstBuffer *outbuf;
GstClockTime timestamp; GstClockTime timestamp;
guint available;
mp3parse = GST_MP3PARSE (gst_pad_get_parent (pad)); mp3parse = GST_MP3PARSE (gst_pad_get_parent (pad));
GST_DEBUG ("mp3parse: received buffer of %d bytes", GST_BUFFER_SIZE (buf)); GST_DEBUG_OBJECT (mp3parse, "received buffer of %d bytes",
GST_BUFFER_SIZE (buf));
timestamp = GST_BUFFER_TIMESTAMP (buf); timestamp = GST_BUFFER_TIMESTAMP (buf);
if (GST_CLOCK_TIME_IS_VALID (timestamp)) { if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
mp3parse->last_ts = timestamp; GST_DEBUG_OBJECT (mp3parse, "Using incoming timestamp of %" GST_TIME_FORMAT,
GST_TIME_ARGS (timestamp));
mp3parse->next_ts = timestamp;
} }
/* if we have something left from the previous frame */ gst_adapter_push (mp3parse->adapter, buf);
if (mp3parse->partialbuf) {
GstBuffer *newbuf;
newbuf = gst_buffer_merge (mp3parse->partialbuf, buf); /* while we still have at least 4 bytes (for the header) available */
/* and the one we received.. */ while (gst_adapter_available (mp3parse->adapter) >= 4) {
gst_buffer_unref (buf);
gst_buffer_unref (mp3parse->partialbuf);
mp3parse->partialbuf = newbuf;
} else {
mp3parse->partialbuf = buf;
}
size = GST_BUFFER_SIZE (mp3parse->partialbuf);
data = GST_BUFFER_DATA (mp3parse->partialbuf);
/* while we still have bytes left -4 for the header */
while (offset < size - 4) {
int skipped = 0;
GST_DEBUG ("mp3parse: offset %ld, size %ld ", offset, size);
/* search for a possible start byte */ /* search for a possible start byte */
for (; ((offset < size - 4) && (data[offset] != 0xff)); offset++) data = gst_adapter_peek (mp3parse->adapter, 4);
skipped++; if (*data != 0xff) {
if (skipped) { /* It'd be nice to make this efficient, but it's ok for now; this is only
GST_DEBUG ("mp3parse: **** now at %ld skipped %d bytes", offset, skipped); * when resyncing
*/
mp3parse->resyncing = TRUE;
gst_adapter_flush (mp3parse->adapter, 1);
continue;
} }
available = gst_adapter_available (mp3parse->adapter);
/* construct the header word */ /* construct the header word */
header = GST_READ_UINT32_BE (data + offset); header = GST_READ_UINT32_BE (data);
/* if it's a valid header, go ahead and send off the frame */ /* if it's a valid header, go ahead and send off the frame */
if (head_check (header)) { if (head_check (mp3parse, header)) {
guint bitrate = 0, layer = 0, rate = 0, channels = 0; guint bitrate = 0, layer = 0, rate = 0, channels = 0;
if (!(bpf = mp3_type_frame_length_from_header (header, &layer, if (!(bpf = mp3_type_frame_length_from_header (mp3parse, header, &layer,
&channels, &bitrate, &rate))) { &channels, &bitrate, &rate))) {
g_error ("Header failed internal error"); g_error ("Header failed internal error");
} }
/******************************************************************************** /*************************************************************************
* robust seek support * robust seek support
* - This performs additional frame validation if the in_flush flag is set * - This performs additional frame validation if the resyncing flag is set
* (indicating a discontinuous stream). * (indicating a discontinuous stream).
* - The current frame header is not accepted as valid unless the NEXT frame * - The current frame header is not accepted as valid unless the NEXT
* header has the same values for most fields. This significantly increases * frame header has the same values for most fields. This significantly
* the probability that we aren't processing random data. * increases the probability that we aren't processing random data.
* - It is not clear if this is sufficient for robust seeking of Layer III * - It is not clear if this is sufficient for robust seeking of Layer III
* streams which utilize the concept of a "bit reservoir" by borrow bitrate * streams which utilize the concept of a "bit reservoir" by borrowing
* from previous frames. In this case, seeking may be more complicated because * bitrate from previous frames. In this case, seeking may be more
* the frames are not independently coded. * complicated because the frames are not independently coded.
********************************************************************************/ *************************************************************************/
if (mp3parse->in_flush) { if (mp3parse->resyncing) {
guint32 header2; guint32 header2;
const guint8 *data2;
if ((size - offset) < (bpf + 4)) { /* wait until we have the the entire current frame as well as the next
if (mp3parse->in_flush) * frame header */
break; if (available < bpf + 4)
} break;
/* wait until we have the the entire current frame as well as the next frame header */
header2 = GST_READ_UINT32_BE (data + offset + bpf); data2 = gst_adapter_peek (mp3parse->adapter, bpf + 4);
GST_DEBUG ("mp3parse: header=%08X, header2=%08X, bpf=%d", header2 = GST_READ_UINT32_BE (data2 + bpf);
GST_DEBUG_OBJECT (mp3parse, "header=%08X, header2=%08X, bpf=%d",
(unsigned int) header, (unsigned int) header2, bpf); (unsigned int) header, (unsigned int) header2, bpf);
/* mask the bits which are allowed to differ between frames */ /* mask the bits which are allowed to differ between frames */
#define HDRMASK ~((0xF << 12) /* bitrate */ | \ #define HDRMASK ~((0xF << 12) /* bitrate */ | \
(0x1 << 9) /* padding */ | \ (0x1 << 9) /* padding */ | \
(0x3 << 4)) /*mode extension */ (0x3 << 4)) /* mode extension */
if ((header2 & HDRMASK) != (header & HDRMASK)) { /* require 2 matching headers in a row */ /* require 2 matching headers in a row */
GST_DEBUG if ((header2 & HDRMASK) != (header & HDRMASK)) {
("mp3parse: next header doesn't match (header=%08X, header2=%08X, bpf=%d)", GST_DEBUG_OBJECT (mp3parse, "next header doesn't match "
"(header=%08X, header2=%08X, bpf=%d)",
(unsigned int) header, (unsigned int) header2, bpf); (unsigned int) header, (unsigned int) header2, bpf);
offset++; /* This frame is invalid. Start looking for a valid frame at the next position in the stream */ /* This frame is invalid. Start looking for a valid frame at the
* next position in the stream */
mp3parse->resyncing = TRUE;
gst_adapter_flush (mp3parse->adapter, 1);
continue; continue;
} }
} }
/* if we don't have the whole frame... */ /* if we don't have the whole frame... */
if ((size - offset) < bpf) { if (available < bpf) {
GST_DEBUG ("mp3parse: partial buffer needed %ld < %d ", (size - offset), GST_DEBUG_OBJECT (mp3parse, "insufficient data available, need "
bpf); "%d bytes, have %d", bpf, available);
break; break;
} else { } else {
if (channels != mp3parse->channels || if (channels != mp3parse->channels ||
@ -433,16 +431,17 @@ gst_mp3parse_chain (GstPad * pad, GstBuffer * buf)
mp3parse->bit_rate = bitrate; mp3parse->bit_rate = bitrate;
} }
outbuf = gst_buffer_create_sub (mp3parse->partialbuf, offset, bpf); outbuf = gst_adapter_take_buffer (mp3parse->adapter, bpf);
offset += bpf; if (!mp3parse->skip) {
if (mp3parse->skip == 0) { gint spf; /* samples per frame */
gint spf; /* samples fer frame */
GST_DEBUG ("mp3parse: pushing buffer of %d bytes", mp3parse->resyncing = FALSE;
GST_DEBUG_OBJECT (mp3parse, "pushing buffer of %d bytes",
GST_BUFFER_SIZE (outbuf)); GST_BUFFER_SIZE (outbuf));
GST_BUFFER_TIMESTAMP (outbuf) = mp3parse->last_ts; GST_BUFFER_TIMESTAMP (outbuf) = mp3parse->next_ts;
/* see http://www.codeproject.com/audio/MPEGAudioInfo.asp */ /* see http://www.codeproject.com/audio/MPEGAudioInfo.asp */
if (mp3parse->layer == 1) if (mp3parse->layer == 1)
@ -450,46 +449,32 @@ gst_mp3parse_chain (GstPad * pad, GstBuffer * buf)
else if (mp3parse->layer == 2) else if (mp3parse->layer == 2)
spf = 1152; spf = 1152;
else { else {
if (mp3parse->rate < 32100) if (mp3parse->rate < 16000)
spf = 576; spf = 576;
else else
spf = 1152; spf = 1152;
} }
GST_BUFFER_DURATION (outbuf) = spf * GST_SECOND / mp3parse->rate; GST_BUFFER_DURATION (outbuf) = spf * GST_SECOND / mp3parse->rate;
mp3parse->last_ts += GST_BUFFER_DURATION (outbuf); mp3parse->next_ts += GST_BUFFER_DURATION (outbuf);
gst_buffer_set_caps (outbuf, GST_PAD_CAPS (mp3parse->srcpad)); gst_buffer_set_caps (outbuf, GST_PAD_CAPS (mp3parse->srcpad));
gst_pad_push (mp3parse->srcpad, outbuf); gst_pad_push (mp3parse->srcpad, outbuf);
} else { } else {
GST_DEBUG ("mp3parse: skipping buffer of %d bytes", GST_DEBUG_OBJECT (mp3parse, "skipping buffer of %d bytes",
GST_BUFFER_SIZE (outbuf)); GST_BUFFER_SIZE (outbuf));
gst_buffer_unref (outbuf); gst_buffer_unref (outbuf);
mp3parse->skip--; mp3parse->skip--;
} }
} }
} else { } else {
offset++; mp3parse->resyncing = TRUE;
GST_DEBUG ("mp3parse: *** wrong header, skipping byte (FIXME?)"); gst_adapter_flush (mp3parse->adapter, 1);
GST_DEBUG_OBJECT (mp3parse, "wrong header, skipping byte");
} }
} }
/* if we have processed this block and there are still */
/* bytes left not in a partial block, copy them over. */
if (size - offset > 0) {
glong remainder = (size - offset);
GST_DEBUG ("mp3parse: partial buffer needed %ld for trailing bytes",
remainder);
outbuf = gst_buffer_create_sub (mp3parse->partialbuf, offset, remainder);
gst_buffer_unref (mp3parse->partialbuf);
mp3parse->partialbuf = outbuf;
} else {
gst_buffer_unref (mp3parse->partialbuf);
mp3parse->partialbuf = NULL;
}
gst_object_unref (mp3parse); gst_object_unref (mp3parse);
@ -497,44 +482,44 @@ gst_mp3parse_chain (GstPad * pad, GstBuffer * buf)
} }
static gboolean static gboolean
head_check (unsigned long head) head_check (GstMPEGAudioParse * mp3parse, unsigned long head)
{ {
GST_DEBUG ("checking mp3 header 0x%08lx", head); GST_DEBUG_OBJECT (mp3parse, "checking mp3 header 0x%08lx", head);
/* if it's not a valid sync */ /* if it's not a valid sync */
if ((head & 0xffe00000) != 0xffe00000) { if ((head & 0xffe00000) != 0xffe00000) {
GST_DEBUG ("invalid sync"); GST_DEBUG_OBJECT (mp3parse, "invalid sync");
return FALSE; return FALSE;
} }
/* if it's an invalid MPEG version */ /* if it's an invalid MPEG version */
if (((head >> 19) & 3) == 0x1) { if (((head >> 19) & 3) == 0x1) {
GST_DEBUG ("invalid MPEG version"); GST_DEBUG_OBJECT (mp3parse, "invalid MPEG version");
return FALSE; return FALSE;
} }
/* if it's an invalid layer */ /* if it's an invalid layer */
if (!((head >> 17) & 3)) { if (!((head >> 17) & 3)) {
GST_DEBUG ("invalid layer"); GST_DEBUG_OBJECT (mp3parse, "invalid layer");
return FALSE; return FALSE;
} }
/* if it's an invalid bitrate */ /* if it's an invalid bitrate */
if (((head >> 12) & 0xf) == 0x0) { if (((head >> 12) & 0xf) == 0x0) {
GST_DEBUG ("invalid bitrate"); GST_DEBUG_OBJECT (mp3parse, "invalid bitrate");
return FALSE; return FALSE;
} }
if (((head >> 12) & 0xf) == 0xf) { if (((head >> 12) & 0xf) == 0xf) {
GST_DEBUG ("invalid bitrate"); GST_DEBUG_OBJECT (mp3parse, "invalid bitrate");
return FALSE; return FALSE;
} }
/* if it's an invalid samplerate */ /* if it's an invalid samplerate */
if (((head >> 10) & 0x3) == 0x3) { if (((head >> 10) & 0x3) == 0x3) {
GST_DEBUG ("invalid samplerate"); GST_DEBUG_OBJECT (mp3parse, "invalid samplerate");
return FALSE; return FALSE;
} }
if ((head & 0xffff0000) == 0xfffe0000) { if ((head & 0xffff0000) == 0xfffe0000) {
GST_DEBUG ("invalid sync"); GST_DEBUG_OBJECT (mp3parse, "invalid sync");
return FALSE; return FALSE;
} }
if (head & 0x00000002) { if (head & 0x00000002) {
GST_DEBUG ("invalid emphasis"); GST_DEBUG_OBJECT (mp3parse, "invalid emphasis");
return FALSE; return FALSE;
} }
@ -584,22 +569,21 @@ gst_mp3parse_get_property (GObject * object, guint prop_id, GValue * value,
static GstStateChangeReturn static GstStateChangeReturn
gst_mp3parse_change_state (GstElement * element, GstStateChange transition) gst_mp3parse_change_state (GstElement * element, GstStateChange transition)
{ {
GstMPEGAudioParse *src; GstMPEGAudioParse *mp3parse;
GstStateChangeReturn result; GstStateChangeReturn result;
src = GST_MP3PARSE (element); mp3parse = GST_MP3PARSE (element);
result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) { switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY: case GST_STATE_CHANGE_PAUSED_TO_READY:
src->channels = -1; gst_mp3parse_reset (mp3parse);
src->rate = -1;
src->layer = -1;
break; break;
default: default:
break; break;
} }
result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
return result; return result;
} }
@ -607,6 +591,8 @@ gst_mp3parse_change_state (GstElement * element, GstStateChange transition)
static gboolean static gboolean
plugin_init (GstPlugin * plugin) plugin_init (GstPlugin * plugin)
{ {
GST_DEBUG_CATEGORY_INIT (mp3parse_debug, "mp3parse", 0, "MP3 Parser");
return gst_element_register (plugin, "mp3parse", return gst_element_register (plugin, "mp3parse",
GST_RANK_NONE, GST_TYPE_MP3PARSE); GST_RANK_NONE, GST_TYPE_MP3PARSE);
} }

View File

@ -23,6 +23,7 @@
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/base/gstadapter.h>
G_BEGIN_DECLS G_BEGIN_DECLS
@ -45,13 +46,16 @@ struct _GstMPEGAudioParse {
GstPad *sinkpad,*srcpad; GstPad *sinkpad,*srcpad;
guint64 last_ts; GstClockTime next_ts;
GstAdapter *adapter;
GstBuffer *partialbuf; /* previous buffer (if carryover) */
guint skip; /* number of frames to skip */ guint skip; /* number of frames to skip */
guint bit_rate; guint bit_rate; /* in kbps */
gint channels, rate, layer; gint channels, rate, layer;
gboolean in_flush;
gboolean resyncing; /* True when attempting to resync (stricter checks are
performed) */
}; };
struct _GstMPEGAudioParseClass { struct _GstMPEGAudioParseClass {