upload tizen1.0 source
[framework/multimedia/gst-plugins-good0.10.git] / gst / id3demux / gstid3demux.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
2 /* GStreamer ID3 tag demuxer
3  * Copyright (C) 2005 Jan Schmidt <thaytan@mad.scientist.com>
4  * Copyright (C) 2003-2004 Benjamin Otte <otte@gnome.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /**
23  * SECTION:element-id3demux
24  *
25  * id3demux accepts data streams with either (or both) ID3v2 regions at the
26  * start, or ID3v1 at the end. The mime type of the data between the tag blocks
27  * is detected using typefind functions, and the appropriate output mime type
28  * set on outgoing buffers. 
29  *
30  * The element is only able to read ID3v1 tags from a seekable stream, because
31  * they are at the end of the stream. That is, when get_range mode is supported
32  * by the upstream elements. If get_range operation is available, id3demux makes
33  * it available downstream. This means that elements which require get_range
34  * mode, such as wavparse, can operate on files containing ID3 tag information.
35  *
36  * This id3demux element replaced an older element with the same name which
37  * relied on libid3tag from the MAD project.
38  *
39  * <refsect2>
40  * <title>Example launch line</title>
41  * |[
42  * gst-launch filesrc location=file.mp3 ! id3demux ! fakesink -t
43  * ]| This pipeline should read any available ID3 tag information and output it.
44  * The contents of the file inside the ID3 tag regions should be detected, and
45  * the appropriate mime type set on buffers produced from id3demux.
46  * </refsect2>
47  */
48 #ifdef HAVE_CONFIG_H
49 #include "config.h"
50 #endif
51 #include <gst/gst.h>
52 #include <gst/gst-i18n-plugin.h>
53 #include <gst/tag/tag.h>
54 #include <gst/pbutils/pbutils.h>
55 #include <string.h>
56
57 #include "gstid3demux.h"
58 #include "id3tags.h"
59
60 enum
61 {
62   ARG_0,
63   ARG_PREFER_V1
64 };
65
66 #define DEFAULT_PREFER_V1  FALSE
67
68 GST_DEBUG_CATEGORY (id3demux_debug);
69 #define GST_CAT_DEFAULT (id3demux_debug)
70
71 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
72     GST_PAD_SINK,
73     GST_PAD_ALWAYS,
74     GST_STATIC_CAPS ("application/x-id3")
75     );
76
77 static gboolean gst_id3demux_identify_tag (GstTagDemux * demux,
78     GstBuffer * buffer, gboolean start_tag, guint * tag_size);
79 static GstTagDemuxResult gst_id3demux_parse_tag (GstTagDemux * demux,
80     GstBuffer * buffer, gboolean start_tag, guint * tag_size,
81     GstTagList ** tags);
82 static GstTagList *gst_id3demux_merge_tags (GstTagDemux * tagdemux,
83     const GstTagList * start_tags, const GstTagList * end_tags);
84
85 static void gst_id3demux_set_property (GObject * object, guint prop_id,
86     const GValue * value, GParamSpec * pspec);
87 static void gst_id3demux_get_property (GObject * object, guint prop_id,
88     GValue * value, GParamSpec * pspec);
89
90 GST_BOILERPLATE (GstID3Demux, gst_id3demux, GstTagDemux, GST_TYPE_TAG_DEMUX);
91
92 static void
93 gst_id3demux_base_init (gpointer klass)
94 {
95   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
96
97   gst_element_class_add_pad_template (element_class,
98       gst_static_pad_template_get (&sink_factory));
99
100   gst_element_class_set_details_simple (element_class, "ID3 tag demuxer",
101       "Codec/Demuxer/Metadata",
102       "Read and output ID3v1 and ID3v2 tags while demuxing the contents",
103       "Jan Schmidt <thaytan@mad.scientist.com>");
104 }
105
106 static void
107 gst_id3demux_class_init (GstID3DemuxClass * klass)
108 {
109   GstTagDemuxClass *tagdemux_class = (GstTagDemuxClass *) klass;
110   GObjectClass *gobject_class = (GObjectClass *) klass;
111
112   gobject_class->set_property = gst_id3demux_set_property;
113   gobject_class->get_property = gst_id3demux_get_property;
114
115   g_object_class_install_property (gobject_class, ARG_PREFER_V1,
116       g_param_spec_boolean ("prefer-v1", "Prefer version 1 tag",
117           "Prefer tags from ID3v1 tag at end of file when both ID3v1 "
118           "and ID3v2 tags are present", DEFAULT_PREFER_V1,
119           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
120
121   tagdemux_class->identify_tag = GST_DEBUG_FUNCPTR (gst_id3demux_identify_tag);
122   tagdemux_class->parse_tag = GST_DEBUG_FUNCPTR (gst_id3demux_parse_tag);
123   tagdemux_class->merge_tags = GST_DEBUG_FUNCPTR (gst_id3demux_merge_tags);
124
125   tagdemux_class->min_start_size = ID3V2_HDR_SIZE;
126   tagdemux_class->min_end_size = ID3V1_TAG_SIZE;
127 }
128
129 static void
130 gst_id3demux_init (GstID3Demux * id3demux, GstID3DemuxClass * klass)
131 {
132   id3demux->prefer_v1 = DEFAULT_PREFER_V1;
133 }
134
135 static gboolean
136 gst_id3demux_identify_tag (GstTagDemux * demux, GstBuffer * buf,
137     gboolean start_tag, guint * tag_size)
138 {
139   const guint8 *data = GST_BUFFER_DATA (buf);
140
141   if (start_tag) {
142     if (data[0] != 'I' || data[1] != 'D' || data[2] != '3')
143       goto no_marker;
144
145     *tag_size = id3demux_calc_id3v2_tag_size (buf);
146   } else {
147     if (data[0] != 'T' || data[1] != 'A' || data[2] != 'G')
148       goto no_marker;
149
150     *tag_size = ID3V1_TAG_SIZE;
151   }
152
153   GST_INFO_OBJECT (demux, "Found ID3v%u marker, tag_size = %u",
154       (start_tag) ? 2 : 1, *tag_size);
155
156   return TRUE;
157
158 no_marker:
159   {
160     GST_DEBUG_OBJECT (demux, "No ID3v%u marker found", (start_tag) ? 2 : 1);
161     return FALSE;
162   }
163 }
164
165 static void
166 gst_id3demux_add_container_format (GstTagList * tags)
167 {
168   GstCaps *sink_caps;
169
170   sink_caps = gst_static_pad_template_get_caps (&sink_factory);
171   gst_pb_utils_add_codec_description_to_tag_list (tags,
172       GST_TAG_CONTAINER_FORMAT, sink_caps);
173   gst_caps_unref (sink_caps);
174 }
175
176 static GstTagDemuxResult
177 gst_id3demux_parse_tag (GstTagDemux * demux, GstBuffer * buffer,
178     gboolean start_tag, guint * tag_size, GstTagList ** tags)
179 {
180   if (start_tag) {
181     ID3TagsResult res;          /* FIXME: make id3tags.c return tagmuxresult values */
182
183     res = id3demux_read_id3v2_tag (buffer, tag_size, tags);
184
185     if (G_LIKELY (res == ID3TAGS_READ_TAG)) {
186       gst_id3demux_add_container_format (*tags);
187       return GST_TAG_DEMUX_RESULT_OK;
188     } else {
189       return GST_TAG_DEMUX_RESULT_BROKEN_TAG;
190     }
191   } else {
192     *tags = gst_tag_list_new_from_id3v1 (GST_BUFFER_DATA (buffer));
193
194     if (G_UNLIKELY (*tags == NULL))
195       return GST_TAG_DEMUX_RESULT_BROKEN_TAG;
196
197     gst_id3demux_add_container_format (*tags);
198     *tag_size = ID3V1_TAG_SIZE;
199     return GST_TAG_DEMUX_RESULT_OK;
200   }
201 }
202
203 static GstTagList *
204 gst_id3demux_merge_tags (GstTagDemux * tagdemux, const GstTagList * start_tags,
205     const GstTagList * end_tags)
206 {
207   GstID3Demux *id3demux;
208   GstTagList *merged;
209   gboolean prefer_v1;
210
211   id3demux = GST_ID3DEMUX (tagdemux);
212
213   GST_OBJECT_LOCK (id3demux);
214   prefer_v1 = id3demux->prefer_v1;
215   GST_OBJECT_UNLOCK (id3demux);
216
217   /* we merge in REPLACE mode, so put the less important tags first */
218   if (prefer_v1)
219     merged = gst_tag_list_merge (start_tags, end_tags, GST_TAG_MERGE_REPLACE);
220   else
221     merged = gst_tag_list_merge (end_tags, start_tags, GST_TAG_MERGE_REPLACE);
222
223   GST_LOG_OBJECT (id3demux, "start  tags: %" GST_PTR_FORMAT, start_tags);
224   GST_LOG_OBJECT (id3demux, "end    tags: %" GST_PTR_FORMAT, end_tags);
225   GST_LOG_OBJECT (id3demux, "merged tags: %" GST_PTR_FORMAT, merged);
226
227   return merged;
228 }
229
230 static void
231 gst_id3demux_set_property (GObject * object, guint prop_id,
232     const GValue * value, GParamSpec * pspec)
233 {
234   GstID3Demux *id3demux;
235
236   id3demux = GST_ID3DEMUX (object);
237
238   switch (prop_id) {
239     case ARG_PREFER_V1:{
240       GST_OBJECT_LOCK (id3demux);
241       id3demux->prefer_v1 = g_value_get_boolean (value);
242       GST_OBJECT_UNLOCK (id3demux);
243       break;
244     }
245     default:
246       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
247       break;
248   }
249 }
250
251 static void
252 gst_id3demux_get_property (GObject * object, guint prop_id,
253     GValue * value, GParamSpec * pspec)
254 {
255   GstID3Demux *id3demux;
256
257   id3demux = GST_ID3DEMUX (object);
258
259   switch (prop_id) {
260     case ARG_PREFER_V1:
261       GST_OBJECT_LOCK (id3demux);
262       g_value_set_boolean (value, id3demux->prefer_v1);
263       GST_OBJECT_UNLOCK (id3demux);
264       break;
265     default:
266       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
267       break;
268   }
269 }
270
271 static gboolean
272 plugin_init (GstPlugin * plugin)
273 {
274   GST_DEBUG_CATEGORY_INIT (id3demux_debug, "id3demux", 0,
275       "GStreamer ID3 tag demuxer");
276
277   gst_tag_register_musicbrainz_tags ();
278
279   /* ensure private tag is registered */
280   gst_tag_register (GST_ID3_DEMUX_TAG_ID3V2_FRAME, GST_TAG_FLAG_META,
281       GST_TYPE_BUFFER, "ID3v2 frame", "unparsed id3v2 tag frame",
282       gst_tag_merge_use_first);
283
284   return gst_element_register (plugin, "id3demux",
285       GST_RANK_PRIMARY, GST_TYPE_ID3DEMUX);
286 }
287
288 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
289     GST_VERSION_MINOR,
290     "id3demux",
291     "Demux ID3v1 and ID3v2 tags from a file",
292     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)