mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 12:11:13 +00:00
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:
parent
56c00b370a
commit
a9434b9d75
6 changed files with 911 additions and 1 deletions
21
ChangeLog
21
ChangeLog
|
@ -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:
|
||||
|
|
|
@ -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
11
gst/apetag/Makefile.am
Normal 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
772
gst/apetag/apedemux.c
Normal 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
64
gst/apetag/apedemux.h
Normal 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
40
gst/apetag/apetag.c
Normal 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)
|
Loading…
Reference in a new issue