1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
2 /* Copyright 2005 Jan Schmidt <thaytan@mad.scientist.com>
3 * Copyright 2002,2003 Scott Wheeler <wheeler@kde.org> (portions from taglib)
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
26 #include <gst/tag/tag.h>
30 GST_DEBUG_CATEGORY_EXTERN (id3demux_debug);
31 #define GST_CAT_DEFAULT (id3demux_debug)
33 #define HANDLE_INVALID_SYNCSAFE
35 id3demux_id3v2_frames_to_tag_list (ID3TagsWorking * work, guint size);
38 read_synch_uint (guint8 * data, guint size)
47 for (i = 0; i <= size; i++) {
48 invalid |= data[i] & 0x80;
49 result |= (data[i] & 0x7f) << ((size - i) * 7);
52 #ifdef HANDLE_INVALID_SYNCSAFE
54 GST_WARNING ("Invalid synch-safe integer in ID3v2 frame "
55 "- using the actual value instead");
57 for (i = 0; i <= size; i++) {
58 result |= data[i] << ((size - i) * 8);
66 id3demux_read_id3v1_tag (GstBuffer * buffer, guint * id3v1_size,
73 g_return_val_if_fail (buffer != NULL, ID3TAGS_V1_BAD_SIZE);
75 data = GST_BUFFER_DATA (buffer);
77 if (GST_BUFFER_SIZE (buffer) != ID3V1_TAG_SIZE)
78 return ID3TAGS_V1_BAD_SIZE;
80 /* Check that buffer starts with 'TAG' */
81 if (data[0] != 'T' || data[1] != 'A' || data[2] != 'G') {
84 GST_DEBUG ("No ID3v1 tag in data");
85 return ID3TAGS_READ_TAG;
88 g_return_val_if_fail (tags != NULL, ID3TAGS_READ_TAG);
90 new_tags = gst_tag_list_new_from_id3v1 (GST_BUFFER_DATA (buffer));
92 return ID3TAGS_BROKEN_TAG;
97 merged = gst_tag_list_merge (*tags, new_tags, GST_TAG_MERGE_REPLACE);
98 gst_tag_list_free (*tags);
99 gst_tag_list_free (new_tags);
104 return ID3TAGS_READ_TAG;
108 id3demux_read_id3v2_tag (GstBuffer * buffer, guint * id3v2_size,
115 ID3TagsResult result;
118 g_return_val_if_fail (buffer != NULL, ID3TAGS_MORE_DATA);
120 if (GST_BUFFER_SIZE (buffer) < ID3V2_MARK_SIZE)
121 return ID3TAGS_MORE_DATA; /* Need more data to decide with */
123 data = GST_BUFFER_DATA (buffer);
125 /* Check for 'ID3' string at start of buffer */
126 if (data[0] != 'I' || data[1] != 'D' || data[2] != '3') {
129 GST_DEBUG ("No ID3v2 tag in data");
130 return ID3TAGS_READ_TAG;
133 /* OK, get enough data to read the entire header */
134 if (GST_BUFFER_SIZE (buffer) < ID3V2_HDR_SIZE)
135 return ID3TAGS_MORE_DATA; /* Need more data to decide with */
137 /* Read the version */
138 version = GST_READ_UINT16_BE (data + 3);
143 /* Read the size from the header */
144 read_size = read_synch_uint (data + 6, 4);
145 if (read_size == 0) {
146 /* Tag has no frames attached. Ignore it, but skip the header */
148 *id3v2_size = ID3V2_HDR_SIZE;
149 return ID3TAGS_BROKEN_TAG;
151 read_size += ID3V2_HDR_SIZE;
153 /* Expand the read size to include a footer if there is one */
154 if (flags & ID3V2_HDR_FLAG_FOOTER) {
159 *id3v2_size = read_size;
161 /* Validate the version. At the moment, we only support up to 2.4.0 */
162 if (ID3V2_VER_MAJOR (version) > 4 || ID3V2_VER_MINOR (version) > 0) {
163 GST_WARNING ("ID3v2 tag is from revision 2.%d.%d, "
164 "but decoder only supports 2.%d.%d. Ignoring as per spec.",
165 version >> 8, version & 0xff, ID3V2_VERSION >> 8, ID3V2_VERSION & 0xff);
166 return ID3TAGS_READ_TAG;
168 GST_DEBUG ("ID3v2 tag with revision 2.%d.%d\n", version >> 8, version & 0xff);
170 if (GST_BUFFER_SIZE (buffer) < read_size)
171 return ID3TAGS_MORE_DATA; /* Need more data to decode with */
173 g_return_val_if_fail (tags != NULL, ID3TAGS_READ_TAG);
175 memset (&work, 0, sizeof (ID3TagsWorking));
176 work.buffer = buffer;
177 work.hdr.version = version;
178 work.hdr.size = read_size;
179 work.hdr.flags = flags;
180 work.hdr.frame_data = GST_BUFFER_DATA (buffer) + ID3V2_HDR_SIZE;
181 if (flags & ID3V2_HDR_FLAG_FOOTER)
182 work.hdr.frame_data_size = read_size - ID3V2_HDR_SIZE - 10;
184 work.hdr.frame_data_size = read_size - ID3V2_HDR_SIZE;
186 result = id3demux_id3v2_frames_to_tag_list (&work, read_size);
188 /* Actually read the tags */
189 if (work.tags != NULL) {
193 merged = gst_tag_list_merge (*tags, work.tags, GST_TAG_MERGE_REPLACE);
194 gst_tag_list_free (*tags);
195 gst_tag_list_free (work.tags);
202 g_free (work.prev_genre);
208 id3demux_id3v2_frame_hdr_size (guint id3v2ver)
210 /* ID3v2 < 2.3.0 only had 6 byte header */
211 switch (ID3V2_VER_MAJOR (id3v2ver)) {
223 static const gchar *obsolete_frame_ids[] = {
224 "CRM", "EQU", "LNK", "RVA", "TIM", "TSI", /* From 2.2 */
225 "EQUA", "RVAD", "TIME", "TRDA", "TSIZ", /* From 2.3 */
229 const struct ID3v2FrameIDConvert
233 } frame_id_conversions[] = {
301 convert_fid_to_v240 (gchar * frame_id)
305 while (obsolete_frame_ids[i] != NULL) {
306 if (strncmp (frame_id, obsolete_frame_ids[i], 5) == 0)
312 while (frame_id_conversions[i].orig != NULL) {
313 if (strncmp (frame_id, frame_id_conversions[i].orig, 5) == 0) {
314 strcpy (frame_id, frame_id_conversions[i].new);
323 id3demux_id3v2_frames_to_tag_list (ID3TagsWorking * work, guint size)
325 guint frame_hdr_size;
326 gboolean read_a_frame = FALSE;
329 /* Extended header if present */
330 if (work->hdr.flags & ID3V2_HDR_FLAG_EXTHDR) {
331 work->hdr.ext_hdr_size = read_synch_uint (work->hdr.frame_data, 4);
332 if (work->hdr.ext_hdr_size < 6 ||
333 (work->hdr.ext_hdr_size) > work->hdr.frame_data_size) {
334 return ID3TAGS_BROKEN_TAG;
336 work->hdr.ext_flag_bytes = work->hdr.frame_data[4];
337 if (5 + work->hdr.ext_flag_bytes > work->hdr.frame_data_size) {
339 ("Tag claims extended header, but doesn't have enough bytes. Broken tag");
340 return ID3TAGS_BROKEN_TAG;
343 work->hdr.ext_flag_data = work->hdr.frame_data + 5;
344 work->hdr.frame_data += work->hdr.ext_hdr_size;
345 work->hdr.frame_data_size -= work->hdr.ext_hdr_size;
348 start = GST_BUFFER_DATA (work->buffer);
349 frame_hdr_size = id3demux_id3v2_frame_hdr_size (work->hdr.version);
350 if (work->hdr.frame_data_size <= frame_hdr_size) {
351 GST_DEBUG ("Tag has no data frames. Broken tag");
352 return ID3TAGS_BROKEN_TAG; /* Must have at least one frame */
355 work->tags = gst_tag_list_new ();
356 g_return_val_if_fail (work->tags != NULL, ID3TAGS_READ_TAG);
358 while (work->hdr.frame_data_size > frame_hdr_size) {
359 guint frame_size = 0;
360 gchar frame_id[5] = "";
361 guint16 frame_flags = 0x0;
362 gboolean obsolete_id = FALSE;
364 /* Read the header */
365 switch (ID3V2_VER_MAJOR (work->hdr.version)) {
369 frame_id[0] = work->hdr.frame_data[0];
370 frame_id[1] = work->hdr.frame_data[1];
371 frame_id[2] = work->hdr.frame_data[2];
374 obsolete_id = convert_fid_to_v240 (frame_id);
376 frame_size = read_synch_uint (work->hdr.frame_data + 3, 3);
382 frame_id[0] = work->hdr.frame_data[0];
383 frame_id[1] = work->hdr.frame_data[1];
384 frame_id[2] = work->hdr.frame_data[2];
385 frame_id[3] = work->hdr.frame_data[3];
387 frame_size = read_synch_uint (work->hdr.frame_data + 4, 4);
388 frame_flags = GST_READ_UINT16_BE (work->hdr.frame_data + 8);
390 if (ID3V2_VER_MAJOR (work->hdr.version) == 3) {
391 frame_flags &= ID3V2_3_FRAME_FLAGS_MASK;
392 obsolete_id = convert_fid_to_v240 (frame_id);
397 work->hdr.frame_data += frame_hdr_size;
398 work->hdr.frame_data_size -= frame_hdr_size;
400 if (frame_size > work->hdr.frame_data_size ||
401 frame_size == 0 || strcmp (frame_id, "") == 0)
402 break; /* No more frames to read */
406 ("Frame @ %d (0x%02x) id %s size %d, next=%d (0x%02x) obsolete=%d\n",
407 work->hdr.frame_data - start, work->hdr.frame_data - start, frame_id,
408 frame_size, work->hdr.frame_data + frame_hdr_size + frame_size - start,
409 work->hdr.frame_data + frame_hdr_size + frame_size - start,
414 /* Now, read, decompress etc the contents of the frame
415 * into a TagList entry */
416 work->cur_frame_size = frame_size;
417 work->frame_id = frame_id;
418 work->frame_flags = frame_flags;
420 if (id3demux_id3v2_parse_frame (work)) {
422 GST_LOG ("Extracted frame with id %s", frame_id);
425 work->hdr.frame_data += frame_size;
426 work->hdr.frame_data_size -= frame_size;
430 GST_DEBUG ("Could not extract any frames from tag. Broken tag");
431 gst_tag_list_free (work->tags);
433 return ID3TAGS_BROKEN_TAG;
436 return ID3TAGS_READ_TAG;