summaryrefslogtreecommitdiff
path: root/subprojects/gst-plugins-good/gst/id3demux
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/gst-plugins-good/gst/id3demux')
-rw-r--r--subprojects/gst-plugins-good/gst/id3demux/gstid3demux.c292
-rw-r--r--subprojects/gst-plugins-good/gst/id3demux/gstid3demux.h61
-rw-r--r--subprojects/gst-plugins-good/gst/id3demux/meson.build10
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]