mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-04-26 06:54:49 +00:00
gst/apetag/: Remove old files, apetag is in gst-plugins-good now.
Original commit message from CVS: * gst/apetag/Makefile.am: * gst/apetag/apedemux.c: * gst/apetag/apedemux.h: * gst/apetag/apetag.c: Remove old files, apetag is in gst-plugins-good now.
This commit is contained in:
parent
940f559c09
commit
e0a010fb6d
5 changed files with 8 additions and 928 deletions
|
@ -1,3 +1,11 @@
|
||||||
|
2006-02-06 Tim-Philipp Müller <tim at centricular dot net>
|
||||||
|
|
||||||
|
* gst/apetag/Makefile.am:
|
||||||
|
* gst/apetag/apedemux.c:
|
||||||
|
* gst/apetag/apedemux.h:
|
||||||
|
* gst/apetag/apetag.c:
|
||||||
|
Remove old files, apetag is in gst-plugins-good now.
|
||||||
|
|
||||||
2006-02-06 Andy Wingo <wingo@pobox.com>
|
2006-02-06 Andy Wingo <wingo@pobox.com>
|
||||||
|
|
||||||
* ext/bz2/gstbz2enc.c (gst_bz2enc_class_init):
|
* ext/bz2/gstbz2enc.c (gst_bz2enc_class_init):
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
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
|
|
|
@ -1,813 +0,0 @@
|
||||||
/* 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 GstStateChangeReturn gst_ape_demux_change_state (GstElement * element,
|
|
||||||
GstStateChange transition);
|
|
||||||
|
|
||||||
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_OBJECT_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 GstTagList *
|
|
||||||
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, "genre")) {
|
|
||||||
type = GST_TAG_GENRE;
|
|
||||||
} 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);
|
|
||||||
/* we'll push it over the srcpad later */
|
|
||||||
} else {
|
|
||||||
gst_tag_list_free (taglist);
|
|
||||||
taglist = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 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;
|
|
||||||
GstTagList *taglist1 = NULL, *taglist2 = NULL, *taglist = NULL;
|
|
||||||
|
|
||||||
GST_LOG ("Initializing stream, stripping tags");
|
|
||||||
|
|
||||||
/* start off, we'll want byte-reading here */
|
|
||||||
bs = gst_bytestream_new (ape->sinkpad);
|
|
||||||
|
|
||||||
/* peek one byte to not confuse the typefinder */
|
|
||||||
while (gst_bytestream_peek_bytes (bs, &data, 1) != 1) {
|
|
||||||
if (!gst_ape_demux_handle_event (ape, bs)) {
|
|
||||||
res = FALSE;
|
|
||||||
goto the_city;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
taglist1 = 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;
|
|
||||||
}
|
|
||||||
taglist2 = 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);
|
|
||||||
if (taglist1 || taglist2) {
|
|
||||||
if (res) {
|
|
||||||
/* merge */
|
|
||||||
if (taglist1 && taglist2) {
|
|
||||||
taglist = gst_tag_list_merge (taglist1, taglist2,
|
|
||||||
GST_TAG_MERGE_REPLACE);
|
|
||||||
gst_tag_list_free (taglist1);
|
|
||||||
gst_tag_list_free (taglist2);
|
|
||||||
} else {
|
|
||||||
taglist = taglist1 ? taglist1 : taglist2;
|
|
||||||
}
|
|
||||||
gst_pad_push (ape->srcpad, GST_DATA (gst_event_new_tag (taglist)));
|
|
||||||
} else {
|
|
||||||
if (taglist1)
|
|
||||||
gst_tag_list_free (taglist1);
|
|
||||||
if (taglist2)
|
|
||||||
gst_tag_list_free (taglist2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
event = new;
|
|
||||||
data = GST_DATA (new);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_pad_event_default (ape->sinkpad, event);
|
|
||||||
|
|
||||||
return;
|
|
||||||
} 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 GstStateChangeReturn
|
|
||||||
gst_ape_demux_change_state (GstElement * element, GstStateChange transition)
|
|
||||||
{
|
|
||||||
GstApeDemux *ape = GST_APE_DEMUX (element);
|
|
||||||
|
|
||||||
switch (transition) {
|
|
||||||
case GST_STATE_CHANGE_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, transition);
|
|
||||||
|
|
||||||
return GST_STATE_CHANGE_SUCCESS;
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
/* 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__ */
|
|
|
@ -1,40 +0,0 @@
|
||||||
/* 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