APE v1/2 tag reader plus typefind function.

Original commit message from CVS:
* configure.ac:
* gst/apetag/Makefile.am:
* gst/apetag/apedemux.c: (gst_ape_demux_get_type),
(gst_ape_demux_base_init), (gst_ape_demux_class_init),
(gst_ape_demux_init), (gst_ape_demux_get_src_formats),
(gst_ape_demux_get_src_query_types),
(gst_ape_demux_handle_src_query), (gst_ape_demux_get_event_mask),
(gst_ape_demux_handle_src_event), (gst_ape_demux_handle_event),
(gst_ape_demux_typefind_peek), (gst_ape_demux_typefind_get_length),
(gst_ape_demux_typefind_suggest), (gst_ape_demux_typefind),
(gst_ape_demux_parse_tags), (gst_ape_demux_stream_init),
(gst_ape_demux_stream_data), (gst_ape_demux_loop),
(gst_ape_demux_change_state):
* gst/apetag/apedemux.h:
* gst/apetag/apetag.c: (plugin_init):
* gst/typefind/gsttypefindfunctions.c: (apetag_type_find),
(plugin_init):
APE v1/2 tag reader plus typefind function.
This commit is contained in:
Ronald S. Bultje 2004-11-25 20:14:32 +00:00
parent 56c00b370a
commit a9434b9d75
6 changed files with 911 additions and 1 deletions

View file

@ -1,3 +1,24 @@
2004-11-25 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* configure.ac:
* gst/apetag/Makefile.am:
* gst/apetag/apedemux.c: (gst_ape_demux_get_type),
(gst_ape_demux_base_init), (gst_ape_demux_class_init),
(gst_ape_demux_init), (gst_ape_demux_get_src_formats),
(gst_ape_demux_get_src_query_types),
(gst_ape_demux_handle_src_query), (gst_ape_demux_get_event_mask),
(gst_ape_demux_handle_src_event), (gst_ape_demux_handle_event),
(gst_ape_demux_typefind_peek), (gst_ape_demux_typefind_get_length),
(gst_ape_demux_typefind_suggest), (gst_ape_demux_typefind),
(gst_ape_demux_parse_tags), (gst_ape_demux_stream_init),
(gst_ape_demux_stream_data), (gst_ape_demux_loop),
(gst_ape_demux_change_state):
* gst/apetag/apedemux.h:
* gst/apetag/apetag.c: (plugin_init):
* gst/typefind/gsttypefindfunctions.c: (apetag_type_find),
(plugin_init):
APE v1/2 tag reader plus typefind function.
2004-11-25 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* configure.ac:

View file

@ -329,6 +329,7 @@ GST_PLUGINS_ALL="\
ac3parse \
adder \
alpha \
apetag \
asfdemux \
audioconvert \
audioscale \
@ -1846,12 +1847,13 @@ gst/Makefile
gst/ac3parse/Makefile
gst/adder/Makefile
gst/alpha/Makefile
gst/apetag/Makefile
gst/asfdemux/Makefile
gst/audioconvert/Makefile
gst/audioscale/Makefile
gst/audiorate/Makefile
gst/auparse/Makefile
gst/avi/Makefile
gst/asfdemux/Makefile
gst/cdxaparse/Makefile
gst/chart/Makefile
gst/colorspace/Makefile

11
gst/apetag/Makefile.am Normal file
View file

@ -0,0 +1,11 @@
plugin_LTLIBRARIES = libgstapetag.la
libgstapetag_la_SOURCES = \
apedemux.c \
apetag.c
libgstapetag_la_CFLAGS = $(GST_CFLAGS)
libgstapetag_la_LIBADD =
libgstapetag_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
noinst_HEADERS = \
apedemux.h

772
gst/apetag/apedemux.c Normal file
View file

@ -0,0 +1,772 @@
/* GStreamer APEv1/2 tag reader
* Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <gst/gst.h>
#include <gst/bytestream/bytestream.h>
#include "apedemux.h"
GST_DEBUG_CATEGORY_STATIC (apedemux_debug);
#define GST_CAT_DEFAULT apedemux_debug
static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/x-apetag")
);
static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_SOMETIMES, /* spider/decodebin hack */
GST_STATIC_CAPS_ANY);
static void gst_ape_demux_base_init (GstApeDemuxClass * klass);
static void gst_ape_demux_class_init (GstApeDemuxClass * klass);
static void gst_ape_demux_init (GstApeDemux * ape);
static void gst_ape_demux_loop (GstElement * element);
static const GstEventMask *gst_ape_demux_get_event_mask (GstPad * pad);
static gboolean gst_ape_demux_handle_src_event (GstPad * pad, GstEvent * event);
static const GstFormat *gst_ape_demux_get_src_formats (GstPad * pad);
static const GstQueryType *gst_ape_demux_get_src_query_types (GstPad * pad);
static gboolean gst_ape_demux_handle_src_query (GstPad * pad,
GstQueryType type, GstFormat * format, gint64 * value);
static GstElementStateReturn gst_ape_demux_change_state (GstElement * element);
static GstElementClass *parent_class = NULL;
GType
gst_ape_demux_get_type (void)
{
static GType ape_demux_type = 0;
if (!ape_demux_type) {
static const GTypeInfo ape_demux_info = {
sizeof (GstApeDemuxClass),
(GBaseInitFunc) gst_ape_demux_base_init,
NULL,
(GClassInitFunc) gst_ape_demux_class_init,
NULL,
NULL,
sizeof (GstApeDemux),
0,
(GInstanceInitFunc) gst_ape_demux_init,
};
ape_demux_type =
g_type_register_static (GST_TYPE_ELEMENT,
"GstApeDemux", &ape_demux_info, 0);
}
return ape_demux_type;
}
static void
gst_ape_demux_base_init (GstApeDemuxClass * klass)
{
static GstElementDetails gst_ape_demux_details =
GST_ELEMENT_DETAILS ("Ape tag reader",
"Codec/Demuxer/Audio",
"Reads APEv1/2 tags",
"Ronald Bultje <rbultje@ronald.bitfreak.net>");
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_templ));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_templ));
gst_element_class_set_details (element_class, &gst_ape_demux_details);
}
static void
gst_ape_demux_class_init (GstApeDemuxClass * klass)
{
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
GST_DEBUG_CATEGORY_INIT (apedemux_debug, "apedemux",
0, "Demuxer for APE tag reader");
parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
gstelement_class->change_state = gst_ape_demux_change_state;
}
static void
gst_ape_demux_init (GstApeDemux * ape)
{
GST_FLAG_SET (ape, GST_ELEMENT_EVENT_AWARE);
ape->sinkpad =
gst_pad_new_from_template (gst_static_pad_template_get (&sink_templ),
"sink");
gst_element_add_pad (GST_ELEMENT (ape), ape->sinkpad);
#if 0
ape->srcpad =
gst_pad_new_from_template (gst_static_pad_template_get (&src_templ),
"src");
gst_pad_set_formats_function (ape->srcpad, gst_ape_demux_get_src_formats);
gst_pad_set_event_mask_function (ape->srcpad, gst_ape_demux_get_event_mask);
gst_pad_set_event_function (ape->srcpad, gst_ape_demux_handle_src_event);
gst_pad_set_query_type_function (ape->srcpad,
gst_ape_demux_get_src_query_types);
gst_pad_set_query_function (ape->srcpad, gst_ape_demux_handle_src_query);
gst_pad_use_explicit_caps (ape->srcpad);
gst_element_add_pad (GST_ELEMENT (ape), ape->srcpad);
#endif
ape->srcpad = NULL;
gst_element_set_loop_function (GST_ELEMENT (ape), gst_ape_demux_loop);
ape->state = GST_APE_DEMUX_TAGREAD;
ape->start_off = ape->end_off = 0;
}
static const GstFormat *
gst_ape_demux_get_src_formats (GstPad * pad)
{
static const GstFormat formats[] = {
GST_FORMAT_BYTES,
0
};
return formats;
}
static const GstQueryType *
gst_ape_demux_get_src_query_types (GstPad * pad)
{
static const GstQueryType types[] = {
GST_QUERY_TOTAL,
GST_QUERY_POSITION,
0
};
return types;
}
static gboolean
gst_ape_demux_handle_src_query (GstPad * pad,
GstQueryType type, GstFormat * format, gint64 * value)
{
GstApeDemux *ape = GST_APE_DEMUX (gst_pad_get_parent (pad));
gboolean res;
res = gst_pad_query (GST_PAD_PEER (ape->sinkpad), type, format, value);
if (!res)
return FALSE;
switch (type) {
case GST_QUERY_TOTAL:
switch (*format) {
case GST_FORMAT_BYTES:
*value -= (ape->start_off + ape->end_off);
break;
default:
break;
}
break;
case GST_QUERY_POSITION:
switch (*format) {
case GST_FORMAT_BYTES:
*value -= ape->start_off;
break;
default:
break;
}
break;
default:
break;
}
return res;
}
static const GstEventMask *
gst_ape_demux_get_event_mask (GstPad * pad)
{
static const GstEventMask masks[] = {
{GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
{0,}
};
return masks;
}
static gboolean
gst_ape_demux_handle_src_event (GstPad * pad, GstEvent * event)
{
GstApeDemux *ape = GST_APE_DEMUX (gst_pad_get_parent (pad));
if (ape->state != GST_APE_DEMUX_IDENTITY)
return FALSE;
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
switch (GST_EVENT_SEEK_FORMAT (event)) {
case GST_FORMAT_BYTES:{
GstEvent *new;
gint64 new_off;
new_off = GST_EVENT_SEEK_OFFSET (event);
new_off += ape->start_off;
new = gst_event_new_seek (GST_EVENT_SEEK_TYPE (event), new_off);
gst_event_unref (event);
event = new;
break;
}
default:
break;
}
break;
default:
break;
}
return gst_pad_send_event (GST_PAD_PEER (ape->sinkpad), event);
}
/*
* Handle an event during 'open' stage.
*/
static gboolean
gst_ape_demux_handle_event (GstApeDemux * ape, GstByteStream * bs)
{
GstEvent *event;
guint32 remaining;
gboolean res = FALSE;
gst_bytestream_get_status (bs, &remaining, &event);
if (!event) {
GST_ELEMENT_ERROR (ape, RESOURCE, READ, (NULL), (NULL));
return FALSE;
}
switch (GST_EVENT_TYPE (event)) {
/* this shouldn't happen. We definately can't deal with it. */
case GST_EVENT_EOS:
case GST_EVENT_INTERRUPT:
GST_ELEMENT_ERROR (ape, RESOURCE, READ, (NULL),
("Cannot deal with EOS/interrupt events during init stage"));
break;
case GST_EVENT_DISCONTINUOUS:
case GST_EVENT_FLUSH:
/* we disregard those during init stage */
res = TRUE;
break;
default:
gst_pad_event_default (ape->sinkpad, event);
return TRUE;
}
gst_event_unref (event);
return res;
}
/*
* Find media type. Simple for now.
*/
typedef struct _GstApeDemuxTypeFind
{
GstApeDemux *ape;
GstByteStream *bs;
gboolean seekable;
guint64 len;
GstCaps *caps;
guint probability;
gboolean stop;
} GstApeDemuxTypeFind;
static guint8 *
gst_ape_demux_typefind_peek (gpointer ptr, gint64 offset, guint size)
{
GstApeDemuxTypeFind *apetf = ptr;
guint8 *data;
/* non-seekable first - easy */
if (!apetf->seekable || offset == 0) {
/* don't seek outside reach */
if (offset != 0 || size > apetf->len)
return NULL;
/* try to get data, fatal event *is* fatal for typefinding */
while (gst_bytestream_peek_bytes (apetf->bs, &data, size) != size) {
if (!gst_ape_demux_handle_event (apetf->ape, apetf->bs)) {
apetf->stop = TRUE;
return NULL;
}
}
return data;
}
/* FIXME: theoretically we could embed mp3 and we'd like to seek
* beyond just the beginnings then. */
return NULL;
}
static guint64
gst_ape_demux_typefind_get_length (gpointer ptr)
{
GstApeDemuxTypeFind *apetf = ptr;
return apetf->len;
}
static void
gst_ape_demux_typefind_suggest (gpointer ptr,
guint probability, const GstCaps * caps)
{
GstApeDemuxTypeFind *apetf = ptr;
GST_LOG ("Found type of mime %s, probability %u",
gst_structure_get_name (gst_caps_get_structure (caps, 0)), probability);
if (probability > apetf->probability) {
if (apetf->caps)
gst_caps_free (apetf->caps);
apetf->caps = gst_caps_copy (caps);
apetf->probability = probability;
}
}
static gboolean
gst_ape_demux_typefind (GstApeDemux * ape,
GstByteStream * bs, gboolean seekable)
{
GstApeDemuxTypeFind apetf;
GstTypeFind tf;
GList *factories;
GST_LOG ("Doing typefinding now");
/* prepare */
memset (&apetf, 0, sizeof (apetf));
memset (&tf, 0, sizeof (tf));
tf.peek = gst_ape_demux_typefind_peek;
tf.suggest = gst_ape_demux_typefind_suggest;
tf.data = &apetf;
apetf.bs = bs;
apetf.ape = ape;
apetf.len = gst_bytestream_length (bs);
if (apetf.len != (guint64) - 1) {
apetf.len -= ape->start_off + ape->end_off;
tf.get_length = gst_ape_demux_typefind_get_length;
}
apetf.seekable = seekable;
/* run */
for (factories = gst_type_find_factory_get_list ();
factories != NULL && !apetf.stop &&
apetf.probability < GST_TYPE_FIND_MAXIMUM; factories = factories->next) {
gst_type_find_factory_call_function (factories->data, &tf);
}
/* fatal error */
if (apetf.stop)
return FALSE;
/* type found? */
if (!apetf.caps || apetf.probability < GST_TYPE_FIND_MINIMUM) {
GST_ELEMENT_ERROR (ape, STREAM, TYPE_NOT_FOUND, (NULL), (NULL));
return FALSE;
}
GST_LOG ("Done typefinding, found mime %s",
gst_structure_get_name (gst_caps_get_structure (apetf.caps, 0)));
ape->srcpad =
gst_pad_new_from_template (gst_static_pad_template_get (&src_templ),
"src");
gst_pad_set_formats_function (ape->srcpad, gst_ape_demux_get_src_formats);
gst_pad_set_event_mask_function (ape->srcpad, gst_ape_demux_get_event_mask);
gst_pad_set_event_function (ape->srcpad, gst_ape_demux_handle_src_event);
gst_pad_set_query_type_function (ape->srcpad,
gst_ape_demux_get_src_query_types);
gst_pad_set_query_function (ape->srcpad, gst_ape_demux_handle_src_query);
gst_pad_use_explicit_caps (ape->srcpad);
gst_pad_set_explicit_caps (ape->srcpad, apetf.caps);
gst_element_add_pad (GST_ELEMENT (ape), ape->srcpad);
return TRUE;
}
/*
* Parse tags from a buffer.
*/
static void
gst_ape_demux_parse_tags (GstApeDemux * ape, guint8 * data, gint size)
{
GstTagList *taglist = gst_tag_list_new ();
gboolean have_tag = FALSE;
GST_LOG ("Reading tags from chunk of size %u bytes", size);
/* get rid of header/footer */
if (!memcmp (data, "APETAGEX", 8)) {
data += 32;
size -= 32;
}
if (!memcmp (data + size - 32, "APETAGEX", 8)) {
size -= 32;
}
/* read actual tags - at least 10 bytes for tag header */
while (size >= 10) {
guint len, n = 8;
gchar *tag, *val;
const gchar *type = NULL;
gboolean i = FALSE;
/* find tag type and size */
len = GST_READ_UINT32_LE (data);
while (n < size && data[n] != 0x0)
n++;
if (n == size)
break;
g_assert (data[n] == 0x0);
n++;
if (size - n < len)
break;
/* read */
tag = g_strndup (&data[8], n - 9);
val = g_strndup (&data[n], len);
if (!strcasecmp (tag, "title")) {
type = GST_TAG_TITLE;
} else if (!strcasecmp (tag, "artist")) {
type = GST_TAG_ARTIST;
} else if (!strcasecmp (tag, "album")) {
type = GST_TAG_ALBUM;
} else if (!strcasecmp (tag, "comment")) {
type = GST_TAG_COMMENT;
} else if (!strcasecmp (tag, "copyright")) {
type = GST_TAG_COPYRIGHT;
} else if (!strcasecmp (tag, "isrc")) {
type = GST_TAG_ISRC;
} else if (!strcasecmp (tag, "track")) {
type = GST_TAG_TRACK_NUMBER;
i = TRUE;
}
if (type) {
GValue v = { 0 };
if (i) {
g_value_init (&v, G_TYPE_INT);
g_value_set_int (&v, atoi (val));
} else {
g_value_init (&v, G_TYPE_STRING);
g_value_set_string (&v, val);
}
gst_tag_list_add_values (taglist, GST_TAG_MERGE_APPEND, type, &v, NULL);
g_value_unset (&v);
have_tag = TRUE;
}
GST_DEBUG ("Read tag %s: %s", tag, val);
g_free (tag);
g_free (val);
/* move data pointer */
size -= len + n;
data += len + n;
}
/* let people know */
if (have_tag) {
gst_element_found_tags (GST_ELEMENT (ape), taglist);
/*gst_pad_push (ape->srcpad, GST_DATA (gst_event_new_tag (taglist))); */
} else {
gst_tag_list_free (taglist);
}
}
/*
* "Open" a APEv1/2 file.
*/
static gboolean
gst_ape_demux_stream_init (GstApeDemux * ape)
{
GstByteStream *bs;
gboolean seekable = TRUE, res = TRUE;
guint8 *data;
guint32 size = 0;
GST_LOG ("Initializing stream, stripping tags");
/* start off, we'll want byte-reading here */
bs = gst_bytestream_new (ape->sinkpad);
/* can we seek? */
if (!gst_bytestream_seek (bs, 0, GST_SEEK_METHOD_END)) {
seekable = FALSE;
} else {
if (!gst_bytestream_seek (bs, 0, GST_SEEK_METHOD_SET)) {
GST_ELEMENT_ERROR (ape, RESOURCE, SEEK, (NULL),
("Couldn't seek back to start - cannot handle that"));
res = FALSE;
goto the_city;
}
}
/* ape tags at start? */
while (gst_bytestream_peek_bytes (bs, &data, 32) != 32) {
if (!gst_ape_demux_handle_event (ape, bs)) {
res = FALSE;
goto the_city;
}
}
if (!memcmp (data, "APETAGEX", 8)) {
GST_LOG ("Found tags at start");
/* APEv2 at start of file - note that the flags are useless because
* I have yet to see the first writer that writes correct HAS_HEADER
* and HAS_FOOTER flags... So we detect it ourselves. */
size = GST_READ_UINT32_LE (data + 12);
/* Size is without the header and with the footer. So add 32 because
* we're still at position 0 here (peek != read). */
size += 32;
while (gst_bytestream_peek_bytes (bs, &data, size) != size) {
if (!gst_ape_demux_handle_event (ape, bs)) {
res = FALSE;
goto the_city;
}
}
gst_ape_demux_parse_tags (ape, data, size);
ape->start_off = size;
}
/* if we're not seekable, then this is it already. Flush the tags,
* and forward the rest of the data to the next element. */
if (!seekable) {
if (size != 0)
gst_bytestream_flush_fast (bs, size);
if (!gst_ape_demux_typefind (ape, bs, FALSE)) {
res = FALSE;
goto the_city;
}
gst_bytestream_get_status (bs, &size, NULL);
if (size) {
GstBuffer *buf = NULL;
gst_bytestream_read (bs, &buf, size);
g_assert (buf);
gst_pad_push (ape->srcpad, GST_DATA (buf));
}
goto the_city;
}
/* now look for tags at the end */
if (!gst_bytestream_seek (bs, -32, GST_SEEK_METHOD_END)) {
GST_ELEMENT_ERROR (ape, RESOURCE, SEEK, (NULL), (NULL));
res = FALSE;
goto the_city;
}
while (gst_bytestream_peek_bytes (bs, &data, 32) != 32) {
if (!gst_ape_demux_handle_event (ape, bs)) {
res = FALSE;
goto the_city;
}
}
if (!memcmp (data, "APETAGEX", 8)) {
GST_LOG ("Found tags at end");
/* APEv1/2 at start of file - note that the flags are useless because
* I have yet to see the first writer that writes correct HAS_HEADER
* and HAS_FOOTER flags... So we detect it ourselves. */
size = GST_READ_UINT32_LE (data + 12);
/* size is without header, so add 32 to detect that. */
size += 32;
if (!gst_bytestream_seek (bs, -(gint64) size, GST_SEEK_METHOD_END)) {
GST_ELEMENT_ERROR (ape, RESOURCE, SEEK, (NULL), (NULL));
res = FALSE;
goto the_city;
}
while (gst_bytestream_peek_bytes (bs, &data, size) != size) {
if (!gst_ape_demux_handle_event (ape, bs)) {
res = FALSE;
goto the_city;
}
}
if (memcmp (data, "APETAGEX", 8) != 0) {
data += 32;
size -= 32;
}
gst_ape_demux_parse_tags (ape, data, size);
ape->end_off = size;
}
/* seek back to beginning */
if (!gst_bytestream_seek (bs, ape->start_off, GST_SEEK_METHOD_SET)) {
GST_ELEMENT_ERROR (ape, RESOURCE, SEEK, (NULL), (NULL));
res = FALSE;
goto the_city;
}
/* get any events */
while (gst_bytestream_peek_bytes (bs, &data, 1) != 1) {
if (!gst_ape_demux_handle_event (ape, bs)) {
res = FALSE;
goto the_city;
}
}
/* typefind */
if (!gst_ape_demux_typefind (ape, bs, TRUE)) {
res = FALSE;
goto the_city;
}
/* push any leftover data */
gst_bytestream_get_status (bs, &size, NULL);
if (size) {
GstBuffer *buf = NULL;
gst_bytestream_read (bs, &buf, size);
g_assert (buf);
gst_pad_push (ape->srcpad, GST_DATA (buf));
}
the_city:
/* become rich & famous */
gst_bytestream_destroy (bs);
return res;
}
/*
* Forward one buffer (we're an identity here).
*/
static void
gst_ape_demux_stream_data (GstApeDemux * ape)
{
GstData *data;
data = gst_pad_pull (ape->sinkpad);
if (GST_IS_EVENT (data)) {
GstEvent *event = GST_EVENT (data);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_DISCONTINUOUS:{
GstEvent *new;
gint64 new_off = ape->start_off;
gst_event_discont_get_value (event, GST_FORMAT_BYTES, &new_off);
new_off -= ape->start_off;
new = gst_event_new_discontinuous (GST_EVENT_DISCONT_NEW_MEDIA (event),
GST_FORMAT_BYTES, new_off, GST_FORMAT_UNDEFINED);
gst_event_unref (event);
data = GST_DATA (new);
break;
}
default:
break;
}
} else {
GstBuffer *buf = GST_BUFFER (data), *kid;
gint64 pos, len;
GstFormat fmt = GST_FORMAT_BYTES;
kid = gst_buffer_create_sub (buf, 0, GST_BUFFER_SIZE (buf));
GST_BUFFER_OFFSET (kid) -= ape->start_off;
gst_buffer_unref (buf);
data = GST_DATA (kid);
/* if the plugin allows us to, see if we're close to eos */
if (gst_pad_query (GST_PAD_PEER (ape->sinkpad),
GST_QUERY_POSITION, &fmt, &pos) &&
gst_pad_query (GST_PAD_PEER (ape->sinkpad),
GST_QUERY_TOTAL, &fmt, &len)) {
if (pos > len - ape->end_off) {
if (pos - GST_BUFFER_SIZE (buf) >= len - ape->end_off) {
gst_buffer_unref (kid);
data = NULL;
} else {
GST_BUFFER_SIZE (kid) -= ape->end_off - (len - pos);
}
}
}
}
if (data)
gst_pad_push (ape->srcpad, data);
}
static void
gst_ape_demux_loop (GstElement * element)
{
GstApeDemux *ape = GST_APE_DEMUX (element);
switch (ape->state) {
case GST_APE_DEMUX_TAGREAD:
if (!gst_ape_demux_stream_init (ape))
return;
GST_LOG ("From now on, we're in identity mode");
ape->state = GST_APE_DEMUX_IDENTITY;
break;
case GST_APE_DEMUX_IDENTITY:
gst_ape_demux_stream_data (ape);
break;
default:
g_assert (0);
}
}
static GstElementStateReturn
gst_ape_demux_change_state (GstElement * element)
{
GstApeDemux *ape = GST_APE_DEMUX (element);
switch (GST_STATE_TRANSITION (element)) {
case GST_STATE_PAUSED_TO_READY:
if (ape->srcpad) {
gst_element_remove_pad (element, ape->srcpad);
ape->srcpad = NULL;
}
ape->state = GST_APE_DEMUX_TAGREAD;
ape->start_off = ape->end_off = 0;
break;
default:
break;
}
if (GST_ELEMENT_CLASS (parent_class)->change_state)
return GST_ELEMENT_CLASS (parent_class)->change_state (element);
return GST_STATE_SUCCESS;
}

64
gst/apetag/apedemux.h Normal file
View file

@ -0,0 +1,64 @@
/* GStreamer APEv1/2 tag reader
* Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_APE_DEMUX_H__
#define __GST_APE_DEMUX_H__
#include <gst/gst.h>
G_BEGIN_DECLS
#define GST_TYPE_APE_DEMUX \
(gst_ape_demux_get_type ())
#define GST_APE_DEMUX(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_APE_DEMUX, GstApeDemux))
#define GST_APE_DEMUX_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_APE_DEMUX, GstApeDemux))
#define GST_IS_APE_DEMUX(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_APE_DEMUX))
#define GST_IS_APE_DEMUX_CLASS(obj) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_APE_DEMUX))
typedef enum {
GST_APE_DEMUX_TAGREAD,
GST_APE_DEMUX_IDENTITY
} GstApeDemuxState;
typedef struct _GstApeDemux {
GstElement parent;
/* pads */
GstPad *srcpad, *sinkpad;
/* tag read state */
GstApeDemuxState state;
/* length of ape tag at start/end */
guint64 start_off, end_off;
} GstApeDemux;
typedef struct _GstApeDemuxClass {
GstElementClass parent_class;
} GstApeDemuxClass;
GType gst_ape_demux_get_type (void);
G_END_DECLS
#endif /* __GST_APE_DEMUX_H__ */

40
gst/apetag/apetag.c Normal file
View file

@ -0,0 +1,40 @@
/* GStreamer APEv1/2 tag reader
* Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/* Element-Checklist-Version: 5 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "apedemux.h"
static gboolean
plugin_init (GstPlugin * plugin)
{
if (!gst_library_load ("gstbytestream"))
return FALSE;
return (gst_element_register (plugin, "apedemux",
GST_RANK_PRIMARY, GST_TYPE_APE_DEMUX));
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"apetag",
"APEv1/2 tag reader", plugin_init, VERSION, "LGPL", GST_PACKAGE, GST_ORIGIN)