diff options
Diffstat (limited to 'subprojects/gst-plugins-good/gst/id3demux')
3 files changed, 363 insertions, 0 deletions
diff --git a/subprojects/gst-plugins-good/gst/id3demux/gstid3demux.c b/subprojects/gst-plugins-good/gst/id3demux/gstid3demux.c new file mode 100644 index 0000000000..7c2b5e75e3 --- /dev/null +++ b/subprojects/gst-plugins-good/gst/id3demux/gstid3demux.c @@ -0,0 +1,292 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */ +/* GStreamer ID3 tag demuxer + * Copyright (C) 2005 Jan Schmidt <thaytan@mad.scientist.com> + * Copyright (C) 2003-2004 Benjamin Otte <otte@gnome.org> + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-id3demux + * @title: id3demux + * + * id3demux accepts data streams with either (or both) ID3v2 regions at the + * start, or ID3v1 at the end. The mime type of the data between the tag blocks + * is detected using typefind functions, and the appropriate output mime type + * set on outgoing buffers. + * + * The element is only able to read ID3v1 tags from a seekable stream, because + * they are at the end of the stream. That is, when get_range mode is supported + * by the upstream elements. If get_range operation is available, id3demux makes + * it available downstream. This means that elements which require get_range + * mode, such as wavparse, can operate on files containing ID3 tag information. + * + * This id3demux element replaced an older element with the same name which + * relied on libid3tag from the MAD project. + * + * ## Example launch line + * |[ + * gst-launch-1.0 filesrc location=file.mp3 ! id3demux ! fakesink -t + * ]| This pipeline should read any available ID3 tag information and output it. + * The contents of the file inside the ID3 tag regions should be detected, and + * the appropriate mime type set on buffers produced from id3demux. + * + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <gst/gst.h> +#include <gst/gst-i18n-plugin.h> +#include <gst/tag/tag.h> +#include <gst/pbutils/pbutils.h> +#include <string.h> + +#include "gstid3demux.h" + +enum +{ + PROP_0, + PROP_PREFER_V1 +}; + +#define DEFAULT_PREFER_V1 FALSE + +GST_DEBUG_CATEGORY (id3demux_debug); +#define GST_CAT_DEFAULT (id3demux_debug) + +#define ID3V1_TAG_SIZE 128 +#define ID3V2_HDR_SIZE GST_TAG_ID3V2_HEADER_SIZE + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-id3") + ); + +static gboolean gst_id3demux_identify_tag (GstTagDemux * demux, + GstBuffer * buffer, gboolean start_tag, guint * tag_size); +static GstTagDemuxResult gst_id3demux_parse_tag (GstTagDemux * demux, + GstBuffer * buffer, gboolean start_tag, guint * tag_size, + GstTagList ** tags); +static GstTagList *gst_id3demux_merge_tags (GstTagDemux * tagdemux, + const GstTagList * start_tags, const GstTagList * end_tags); + +static void gst_id3demux_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_id3demux_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +#define gst_id3demux_parent_class parent_class +G_DEFINE_TYPE (GstID3Demux, gst_id3demux, GST_TYPE_TAG_DEMUX); +#define _do_init \ + GST_DEBUG_CATEGORY_INIT (id3demux_debug, "id3demux", 0, \ + "GStreamer ID3 tag demuxer"); \ + gst_tag_register_musicbrainz_tags (); +GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (id3demux, "id3demux", + GST_RANK_PRIMARY, GST_TYPE_ID3DEMUX, _do_init); + +static void +gst_id3demux_class_init (GstID3DemuxClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; + GstTagDemuxClass *tagdemux_class = (GstTagDemuxClass *) klass; + + gobject_class->set_property = gst_id3demux_set_property; + gobject_class->get_property = gst_id3demux_get_property; + + g_object_class_install_property (gobject_class, PROP_PREFER_V1, + g_param_spec_boolean ("prefer-v1", "Prefer version 1 tag", + "Prefer tags from ID3v1 tag at end of file when both ID3v1 " + "and ID3v2 tags are present", DEFAULT_PREFER_V1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_static_pad_template (gstelement_class, &sink_factory); + + gst_element_class_set_static_metadata (gstelement_class, "ID3 tag demuxer", + "Codec/Demuxer/Metadata", + "Read and output ID3v1 and ID3v2 tags while demuxing the contents", + "Jan Schmidt <thaytan@mad.scientist.com>"); + + tagdemux_class->identify_tag = GST_DEBUG_FUNCPTR (gst_id3demux_identify_tag); + tagdemux_class->parse_tag = GST_DEBUG_FUNCPTR (gst_id3demux_parse_tag); + tagdemux_class->merge_tags = GST_DEBUG_FUNCPTR (gst_id3demux_merge_tags); + + tagdemux_class->min_start_size = ID3V2_HDR_SIZE; + tagdemux_class->min_end_size = ID3V1_TAG_SIZE; +} + +static void +gst_id3demux_init (GstID3Demux * id3demux) +{ + id3demux->prefer_v1 = DEFAULT_PREFER_V1; +} + +static gboolean +gst_id3demux_identify_tag (GstTagDemux * demux, GstBuffer * buf, + gboolean start_tag, guint * tag_size) +{ + guint8 data[3]; + + gst_buffer_extract (buf, 0, data, 3); + + if (start_tag) { + if (data[0] != 'I' || data[1] != 'D' || data[2] != '3') + goto no_marker; + + *tag_size = gst_tag_get_id3v2_tag_size (buf); + } else { + if (data[0] != 'T' || data[1] != 'A' || data[2] != 'G') + goto no_marker; + + *tag_size = ID3V1_TAG_SIZE; + } + + GST_INFO_OBJECT (demux, "Found ID3v%u marker, tag_size = %u", + (start_tag) ? 2 : 1, *tag_size); + + return TRUE; + +no_marker: + { + GST_DEBUG_OBJECT (demux, "No ID3v%u marker found", (start_tag) ? 2 : 1); + return FALSE; + } +} + +static void +gst_id3demux_add_container_format (GstTagList * tags) +{ + GstCaps *sink_caps; + + sink_caps = gst_static_pad_template_get_caps (&sink_factory); + gst_pb_utils_add_codec_description_to_tag_list (tags, + GST_TAG_CONTAINER_FORMAT, sink_caps); + gst_caps_unref (sink_caps); +} + +static GstTagDemuxResult +gst_id3demux_parse_tag (GstTagDemux * demux, GstBuffer * buffer, + gboolean start_tag, guint * tag_size, GstTagList ** tags) +{ + if (start_tag) { + *tag_size = gst_tag_get_id3v2_tag_size (buffer); + *tags = gst_tag_list_from_id3v2_tag (buffer); + + if (G_LIKELY (*tags != NULL)) { + gst_id3demux_add_container_format (*tags); + return GST_TAG_DEMUX_RESULT_OK; + } else { + return GST_TAG_DEMUX_RESULT_BROKEN_TAG; + } + } else { + GstMapInfo map; + + gst_buffer_map (buffer, &map, GST_MAP_READ); + *tags = gst_tag_list_new_from_id3v1 (map.data); + gst_buffer_unmap (buffer, &map); + + if (G_UNLIKELY (*tags == NULL)) + return GST_TAG_DEMUX_RESULT_BROKEN_TAG; + + gst_id3demux_add_container_format (*tags); + *tag_size = ID3V1_TAG_SIZE; + return GST_TAG_DEMUX_RESULT_OK; + } +} + +static GstTagList * +gst_id3demux_merge_tags (GstTagDemux * tagdemux, const GstTagList * start_tags, + const GstTagList * end_tags) +{ + GstID3Demux *id3demux; + GstTagList *merged; + gboolean prefer_v1; + + id3demux = GST_ID3DEMUX (tagdemux); + + GST_OBJECT_LOCK (id3demux); + prefer_v1 = id3demux->prefer_v1; + GST_OBJECT_UNLOCK (id3demux); + + /* we merge in REPLACE mode, so put the less important tags first */ + if (prefer_v1) + merged = gst_tag_list_merge (start_tags, end_tags, GST_TAG_MERGE_REPLACE); + else + merged = gst_tag_list_merge (end_tags, start_tags, GST_TAG_MERGE_REPLACE); + + GST_LOG_OBJECT (id3demux, "start tags: %" GST_PTR_FORMAT, start_tags); + GST_LOG_OBJECT (id3demux, "end tags: %" GST_PTR_FORMAT, end_tags); + GST_LOG_OBJECT (id3demux, "merged tags: %" GST_PTR_FORMAT, merged); + + return merged; +} + +static void +gst_id3demux_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstID3Demux *id3demux; + + id3demux = GST_ID3DEMUX (object); + + switch (prop_id) { + case PROP_PREFER_V1:{ + GST_OBJECT_LOCK (id3demux); + id3demux->prefer_v1 = g_value_get_boolean (value); + GST_OBJECT_UNLOCK (id3demux); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_id3demux_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstID3Demux *id3demux; + + id3demux = GST_ID3DEMUX (object); + + switch (prop_id) { + case PROP_PREFER_V1: + GST_OBJECT_LOCK (id3demux); + g_value_set_boolean (value, id3demux->prefer_v1); + GST_OBJECT_UNLOCK (id3demux); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + + return GST_ELEMENT_REGISTER (id3demux, plugin); + + +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + id3demux, + "Demux ID3v1 and ID3v2 tags from a file", + plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/subprojects/gst-plugins-good/gst/id3demux/gstid3demux.h b/subprojects/gst-plugins-good/gst/id3demux/gstid3demux.h new file mode 100644 index 0000000000..e8d74eb096 --- /dev/null +++ b/subprojects/gst-plugins-good/gst/id3demux/gstid3demux.h @@ -0,0 +1,61 @@ +/* GStreamer ID3 tag demuxer + * Copyright (C) 2005 Jan Schmidt <thaytan@mad.scientist.com> + * Copyright (C) 2003-2004 Benjamin Otte <otte@gnome.org> + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_ID3DEMUX_H__ +#define __GST_ID3DEMUX_H__ + +#include <gst/tag/gsttagdemux.h> + +G_BEGIN_DECLS + +#define GST_TYPE_ID3DEMUX \ + (gst_id3demux_get_type()) +#define GST_ID3DEMUX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ID3DEMUX,GstID3Demux)) +#define GST_ID3DEMUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ID3DEMUX,GstID3DemuxClass)) +#define GST_IS_ID3DEMUX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ID3DEMUX)) +#define GST_IS_ID3DEMUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ID3DEMUX)) + +typedef struct _GstID3Demux GstID3Demux; +typedef struct _GstID3DemuxClass GstID3DemuxClass; + +struct _GstID3Demux +{ + GstTagDemux tagdemux; + + gboolean prefer_v1; /* prefer ID3v1 tags over ID3v2 tags? */ +}; + +struct _GstID3DemuxClass +{ + GstTagDemuxClass parent_class; +}; + +GType gst_id3demux_get_type (void); + +GST_ELEMENT_REGISTER_DECLARE (id3demux); + +G_END_DECLS + +#endif /* __GST_ID3DEMUX_H__ */ + diff --git a/subprojects/gst-plugins-good/gst/id3demux/meson.build b/subprojects/gst-plugins-good/gst/id3demux/meson.build new file mode 100644 index 0000000000..e97c6381f6 --- /dev/null +++ b/subprojects/gst-plugins-good/gst/id3demux/meson.build @@ -0,0 +1,10 @@ +gstid3demux = library('gstid3demux', + 'gstid3demux.c', + c_args : gst_plugins_good_args, + include_directories : [configinc, libsinc], + dependencies : [gst_dep, gstbase_dep, gsttag_dep, gstpbutils_dep], + install : true, + install_dir : plugins_install_dir, +) +pkgconfig.generate(gstid3demux, install_dir : plugins_pkgconfig_install_dir) +plugins += [gstid3demux] |