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:
parent
520f74867f
commit
13ecca8090
13
ChangeLog
13
ChangeLog
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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 */
|
||||||
|
if (available < bpf + 4)
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
/* wait until we have the the entire current frame as well as the next frame header */
|
data2 = gst_adapter_peek (mp3parse->adapter, bpf + 4);
|
||||||
header2 = GST_READ_UINT32_BE (data + offset + bpf);
|
header2 = GST_READ_UINT32_BE (data2 + bpf);
|
||||||
GST_DEBUG ("mp3parse: header=%08X, header2=%08X, bpf=%d",
|
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);
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user