upload tizen1.0 source
[framework/multimedia/gst-plugins-good0.10.git] / gst / id3demux / id3tags.c
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)
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <string.h>
26 #include <gst/tag/tag.h>
27
28 #include "id3tags.h"
29
30 GST_DEBUG_CATEGORY_EXTERN (id3demux_debug);
31 #define GST_CAT_DEFAULT (id3demux_debug)
32
33 #define HANDLE_INVALID_SYNCSAFE
34 static ID3TagsResult
35 id3demux_id3v2_frames_to_tag_list (ID3TagsWorking * work, guint size);
36
37 guint
38 read_synch_uint (const guint8 * data, guint size)
39 {
40   gint i;
41   guint result = 0;
42   gint invalid = 0;
43
44   g_assert (size <= 4);
45
46   size--;
47   for (i = 0; i <= size; i++) {
48     invalid |= data[i] & 0x80;
49     result |= (data[i] & 0x7f) << ((size - i) * 7);
50   }
51
52 #ifdef HANDLE_INVALID_SYNCSAFE
53   if (invalid) {
54     GST_WARNING ("Invalid synch-safe integer in ID3v2 frame "
55         "- using the actual value instead");
56     result = 0;
57     for (i = 0; i <= size; i++) {
58       result |= data[i] << ((size - i) * 8);
59     }
60   }
61 #endif
62   return result;
63 }
64
65 guint
66 id3demux_calc_id3v2_tag_size (GstBuffer * buf)
67 {
68   guint8 *data, flags;
69   guint size;
70
71   g_assert (buf != NULL);
72   g_assert (GST_BUFFER_SIZE (buf) >= ID3V2_HDR_SIZE);
73
74   data = GST_BUFFER_DATA (buf);
75
76   /* Check for 'ID3' string at start of buffer */
77   if (data[0] != 'I' || data[1] != 'D' || data[2] != '3') {
78     GST_DEBUG ("No ID3v2 tag in data");
79     return 0;
80   }
81
82   /* Read the flags */
83   flags = data[5];
84
85   /* Read the size from the header */
86   size = read_synch_uint (data + 6, 4);
87   if (size == 0)
88     return ID3V2_HDR_SIZE;
89
90   size += ID3V2_HDR_SIZE;
91
92   /* Expand the read size to include a footer if there is one */
93   if ((flags & ID3V2_HDR_FLAG_FOOTER))
94     size += 10;
95
96   GST_DEBUG ("ID3v2 tag, size: %u bytes", size);
97   return size;
98 }
99
100 guint8 *
101 id3demux_ununsync_data (const guint8 * unsync_data, guint32 * size)
102 {
103   const guint8 *end;
104   guint8 *out, *uu;
105   guint out_size;
106
107   uu = out = g_malloc (*size);
108
109   for (end = unsync_data + *size; unsync_data < end - 1; ++unsync_data, ++uu) {
110     *uu = *unsync_data;
111     if (G_UNLIKELY (*unsync_data == 0xff && *(unsync_data + 1) == 0x00))
112       ++unsync_data;
113   }
114
115   /* take care of last byte (if last two bytes weren't 0xff 0x00) */
116   if (unsync_data < end) {
117     *uu = *unsync_data;
118     ++uu;
119   }
120
121   out_size = uu - out;
122   GST_DEBUG ("size after un-unsyncing: %u (before: %u)", out_size, *size);
123
124   *size = out_size;
125   return out;
126 }
127
128 /* caller must pass buffer with full ID3 tag */
129 ID3TagsResult
130 id3demux_read_id3v2_tag (GstBuffer * buffer, guint * id3v2_size,
131     GstTagList ** tags)
132 {
133   guint8 *data, *uu_data = NULL;
134   guint read_size;
135   ID3TagsWorking work;
136   guint8 flags;
137   ID3TagsResult result;
138   guint16 version;
139
140   read_size = id3demux_calc_id3v2_tag_size (buffer);
141
142   if (id3v2_size)
143     *id3v2_size = read_size;
144
145   /* Ignore tag if it has no frames attached, but skip the header then */
146   if (read_size <= ID3V2_HDR_SIZE)
147     return ID3TAGS_BROKEN_TAG;
148
149   data = GST_BUFFER_DATA (buffer);
150
151   /* Read the version */
152   version = GST_READ_UINT16_BE (data + 3);
153
154   /* Read the flags */
155   flags = data[5];
156
157   /* Validate the version. At the moment, we only support up to 2.4.0 */
158   if (ID3V2_VER_MAJOR (version) > 4 || ID3V2_VER_MINOR (version) > 0) {
159     GST_WARNING ("ID3v2 tag is from revision 2.%d.%d, "
160         "but decoder only supports 2.%d.%d. Ignoring as per spec.",
161         version >> 8, version & 0xff, ID3V2_VERSION >> 8, ID3V2_VERSION & 0xff);
162     return ID3TAGS_BROKEN_TAG;
163   }
164
165   GST_DEBUG ("ID3v2 header flags: %s %s %s %s",
166       (flags & ID3V2_HDR_FLAG_UNSYNC) ? "UNSYNC" : "",
167       (flags & ID3V2_HDR_FLAG_EXTHDR) ? "EXTENDED_HEADER" : "",
168       (flags & ID3V2_HDR_FLAG_EXPERIMENTAL) ? "EXPERIMENTAL" : "",
169       (flags & ID3V2_HDR_FLAG_FOOTER) ? "FOOTER" : "");
170
171   /* This shouldn't really happen! Caller should have checked first */
172   if (GST_BUFFER_SIZE (buffer) < read_size) {
173     GST_DEBUG
174         ("Found ID3v2 tag with revision 2.%d.%d - need %u more bytes to read",
175         version >> 8, version & 0xff,
176         (guint) (read_size - GST_BUFFER_SIZE (buffer)));
177     return ID3TAGS_MORE_DATA;   /* Need more data to decode with */
178   }
179
180   GST_DEBUG ("Reading ID3v2 tag with revision 2.%d.%d of size %u", version >> 8,
181       version & 0xff, read_size);
182
183   g_return_val_if_fail (tags != NULL, ID3TAGS_READ_TAG);
184
185   GST_MEMDUMP ("ID3v2 tag", GST_BUFFER_DATA (buffer), read_size);
186
187   memset (&work, 0, sizeof (ID3TagsWorking));
188   work.buffer = buffer;
189   work.hdr.version = version;
190   work.hdr.size = read_size;
191   work.hdr.flags = flags;
192   work.hdr.frame_data = GST_BUFFER_DATA (buffer) + ID3V2_HDR_SIZE;
193   if (flags & ID3V2_HDR_FLAG_FOOTER)
194     work.hdr.frame_data_size = read_size - ID3V2_HDR_SIZE - 10;
195   else
196     work.hdr.frame_data_size = read_size - ID3V2_HDR_SIZE;
197
198   /* in v2.3 the frame sizes are not syncsafe, so the entire tag had to be
199    * unsynced. In v2.4 the frame sizes are syncsafe so it's just the frame
200    * data that needs un-unsyncing, but not the frame headers. */
201   if ((flags & ID3V2_HDR_FLAG_UNSYNC) != 0 && ID3V2_VER_MAJOR (version) <= 3) {
202     GST_DEBUG ("Un-unsyncing entire tag");
203     uu_data = id3demux_ununsync_data (work.hdr.frame_data,
204         &work.hdr.frame_data_size);
205     work.hdr.frame_data = uu_data;
206     GST_MEMDUMP ("ID3v2 tag (un-unsyced)", uu_data, work.hdr.frame_data_size);
207   }
208
209   result = id3demux_id3v2_frames_to_tag_list (&work, work.hdr.frame_data_size);
210
211   *tags = work.tags;
212
213   g_free (uu_data);
214
215   return result;
216 }
217
218 static guint
219 id3demux_id3v2_frame_hdr_size (guint id3v2ver)
220 {
221   /* ID3v2 < 2.3.0 only had 6 byte header */
222   switch (ID3V2_VER_MAJOR (id3v2ver)) {
223     case 0:
224     case 1:
225     case 2:
226       return 6;
227     case 3:
228     case 4:
229     default:
230       return 10;
231   }
232 }
233
234 static const gchar *obsolete_frame_ids[] = {
235   "CRM", "EQU", "LNK", "RVA", "TIM", "TSI",     /* From 2.2 */
236   "EQUA", "RVAD", "TIME", "TRDA", "TSIZ",       /* From 2.3 */
237   NULL
238 };
239
240 const struct ID3v2FrameIDConvert
241 {
242   const gchar *orig;
243   const gchar *new;
244 } frame_id_conversions[] = {
245   /* 2.3.x frames */
246   {
247   "TORY", "TDOR"}, {
248   "TYER", "TDRC"},
249       /* 2.2.x frames */
250   {
251   "BUF", "RBUF"}, {
252   "CNT", "PCNT"}, {
253   "COM", "COMM"}, {
254   "CRA", "AENC"}, {
255   "ETC", "ETCO"}, {
256   "GEO", "GEOB"}, {
257   "IPL", "TIPL"}, {
258   "MCI", "MCDI"}, {
259   "MLL", "MLLT"}, {
260   "PIC", "APIC"}, {
261   "POP", "POPM"}, {
262   "REV", "RVRB"}, {
263   "SLT", "SYLT"}, {
264   "STC", "SYTC"}, {
265   "TAL", "TALB"}, {
266   "TBP", "TBPM"}, {
267   "TCM", "TCOM"}, {
268   "TCO", "TCON"}, {
269   "TCR", "TCOP"}, {
270   "TDA", "TDAT"}, {             /* obsolete, but we need to parse it anyway */
271   "TDY", "TDLY"}, {
272   "TEN", "TENC"}, {
273   "TFT", "TFLT"}, {
274   "TKE", "TKEY"}, {
275   "TLA", "TLAN"}, {
276   "TLE", "TLEN"}, {
277   "TMT", "TMED"}, {
278   "TOA", "TOAL"}, {
279   "TOF", "TOFN"}, {
280   "TOL", "TOLY"}, {
281   "TOR", "TDOR"}, {
282   "TOT", "TOAL"}, {
283   "TP1", "TPE1"}, {
284   "TP2", "TPE2"}, {
285   "TP3", "TPE3"}, {
286   "TP4", "TPE4"}, {
287   "TPA", "TPOS"}, {
288   "TPB", "TPUB"}, {
289   "TRC", "TSRC"}, {
290   "TRD", "TDRC"}, {
291   "TRK", "TRCK"}, {
292   "TSS", "TSSE"}, {
293   "TT1", "TIT1"}, {
294   "TT2", "TIT2"}, {
295   "TT3", "TIT3"}, {
296   "TXT", "TOLY"}, {
297   "TXX", "TXXX"}, {
298   "TYE", "TDRC"}, {
299   "UFI", "UFID"}, {
300   "ULT", "USLT"}, {
301   "WAF", "WOAF"}, {
302   "WAR", "WOAR"}, {
303   "WAS", "WOAS"}, {
304   "WCM", "WCOM"}, {
305   "WCP", "WCOP"}, {
306   "WPB", "WPUB"}, {
307   "WXX", "WXXX"}, {
308   NULL, NULL}
309 };
310
311 static gboolean
312 convert_fid_to_v240 (gchar * frame_id)
313 {
314   gint i = 0;
315
316   while (obsolete_frame_ids[i] != NULL) {
317     if (strncmp (frame_id, obsolete_frame_ids[i], 5) == 0)
318       return TRUE;
319     i++;
320   }
321
322   i = 0;
323   while (frame_id_conversions[i].orig != NULL) {
324     if (strncmp (frame_id, frame_id_conversions[i].orig, 5) == 0) {
325       strcpy (frame_id, frame_id_conversions[i].new);
326       return FALSE;
327     }
328     i++;
329   }
330   return FALSE;
331 }
332
333
334 /* add unknown or unhandled ID3v2 frames to the taglist as binary blobs */
335 static void
336 id3demux_add_id3v2_frame_blob_to_taglist (ID3TagsWorking * work, guint size)
337 {
338   GstBuffer *blob;
339   GstCaps *caps;
340   guint8 *frame_data;
341   gchar *media_type;
342   guint frame_size, header_size;
343   guint i;
344
345   switch (ID3V2_VER_MAJOR (work->hdr.version)) {
346     case 1:
347     case 2:
348       header_size = 3 + 3;
349       break;
350     case 3:
351     case 4:
352       header_size = 4 + 4 + 2;
353       break;
354     default:
355       g_return_if_reached ();
356   }
357
358   frame_data = work->hdr.frame_data - header_size;
359   frame_size = size + header_size;
360
361   blob = gst_buffer_new_and_alloc (frame_size);
362   memcpy (GST_BUFFER_DATA (blob), frame_data, frame_size);
363
364   /* Sanitize frame id */
365   for (i = 0; i < 4; i++) {
366     if (!g_ascii_isalnum (frame_data[i]))
367       frame_data[i] = '_';
368   }
369
370   media_type = g_strdup_printf ("application/x-gst-id3v2-%c%c%c%c-frame",
371       g_ascii_tolower (frame_data[0]), g_ascii_tolower (frame_data[1]),
372       g_ascii_tolower (frame_data[2]), g_ascii_tolower (frame_data[3]));
373   caps = gst_caps_new_simple (media_type, "version", G_TYPE_INT,
374       (gint) ID3V2_VER_MAJOR (work->hdr.version), NULL);
375   gst_buffer_set_caps (blob, caps);
376   gst_caps_unref (caps);
377   g_free (media_type);
378
379   /* gst_util_dump_mem (GST_BUFFER_DATA (blob), GST_BUFFER_SIZE (blob)); */
380
381   gst_tag_list_add (work->tags, GST_TAG_MERGE_APPEND,
382       GST_ID3_DEMUX_TAG_ID3V2_FRAME, blob, NULL);
383   gst_buffer_unref (blob);
384 }
385
386 static ID3TagsResult
387 id3demux_id3v2_frames_to_tag_list (ID3TagsWorking * work, guint size)
388 {
389   guint frame_hdr_size;
390   guint8 *start;
391
392   /* Extended header if present */
393   if (work->hdr.flags & ID3V2_HDR_FLAG_EXTHDR) {
394     work->hdr.ext_hdr_size = read_synch_uint (work->hdr.frame_data, 4);
395     if (work->hdr.ext_hdr_size < 6 ||
396         (work->hdr.ext_hdr_size) > work->hdr.frame_data_size) {
397       GST_DEBUG ("Invalid extended header. Broken tag");
398       return ID3TAGS_BROKEN_TAG;
399     }
400     work->hdr.ext_flag_bytes = work->hdr.frame_data[4];
401     if (5 + work->hdr.ext_flag_bytes > work->hdr.frame_data_size) {
402       GST_DEBUG
403           ("Tag claims extended header, but doesn't have enough bytes. Broken tag");
404       return ID3TAGS_BROKEN_TAG;
405     }
406
407     work->hdr.ext_flag_data = work->hdr.frame_data + 5;
408     work->hdr.frame_data += work->hdr.ext_hdr_size;
409     work->hdr.frame_data_size -= work->hdr.ext_hdr_size;
410   }
411
412   start = GST_BUFFER_DATA (work->buffer);
413   frame_hdr_size = id3demux_id3v2_frame_hdr_size (work->hdr.version);
414   if (work->hdr.frame_data_size <= frame_hdr_size) {
415     GST_DEBUG ("Tag has no data frames. Broken tag");
416     return ID3TAGS_BROKEN_TAG;  /* Must have at least one frame */
417   }
418
419   work->tags = gst_tag_list_new ();
420   g_return_val_if_fail (work->tags != NULL, ID3TAGS_READ_TAG);
421
422   while (work->hdr.frame_data_size > frame_hdr_size) {
423     guint frame_size = 0;
424     gchar frame_id[5] = "";
425     guint16 frame_flags = 0x0;
426     gboolean obsolete_id = FALSE;
427     gboolean read_synch_size = TRUE;
428     guint i;
429
430     /* Read the header */
431     switch (ID3V2_VER_MAJOR (work->hdr.version)) {
432       case 0:
433       case 1:
434       case 2:
435         frame_id[0] = work->hdr.frame_data[0];
436         frame_id[1] = work->hdr.frame_data[1];
437         frame_id[2] = work->hdr.frame_data[2];
438         frame_id[3] = 0;
439         frame_id[4] = 0;
440         obsolete_id = convert_fid_to_v240 (frame_id);
441
442         /* 3 byte non-synchsafe size */
443         frame_size = work->hdr.frame_data[3] << 16 |
444             work->hdr.frame_data[4] << 8 | work->hdr.frame_data[5];
445         frame_flags = 0;
446         break;
447       case 3:
448         read_synch_size = FALSE;        /* 2.3 frame size is not synch-safe */
449       case 4:
450       default:
451         frame_id[0] = work->hdr.frame_data[0];
452         frame_id[1] = work->hdr.frame_data[1];
453         frame_id[2] = work->hdr.frame_data[2];
454         frame_id[3] = work->hdr.frame_data[3];
455         frame_id[4] = 0;
456         if (read_synch_size)
457           frame_size = read_synch_uint (work->hdr.frame_data + 4, 4);
458         else
459           frame_size = GST_READ_UINT32_BE (work->hdr.frame_data + 4);
460
461         frame_flags = GST_READ_UINT16_BE (work->hdr.frame_data + 8);
462
463         if (ID3V2_VER_MAJOR (work->hdr.version) == 3) {
464           frame_flags &= ID3V2_3_FRAME_FLAGS_MASK;
465           obsolete_id = convert_fid_to_v240 (frame_id);
466           if (obsolete_id)
467             GST_DEBUG ("Ignoring v2.3 frame %s", frame_id);
468         }
469         break;
470     }
471
472     work->hdr.frame_data += frame_hdr_size;
473     work->hdr.frame_data_size -= frame_hdr_size;
474
475     if (frame_size > work->hdr.frame_data_size || strcmp (frame_id, "") == 0)
476       break;                    /* No more frames to read */
477
478     /* Sanitize frame id */
479     switch (ID3V2_VER_MAJOR (work->hdr.version)) {
480       case 0:
481       case 1:
482       case 2:
483         for (i = 0; i < 3; i++) {
484           if (!g_ascii_isalnum (frame_id[i]))
485             frame_id[i] = '_';
486         }
487         break;
488       default:
489         for (i = 0; i < 4; i++) {
490           if (!g_ascii_isalnum (frame_id[i]))
491             frame_id[i] = '_';
492         }
493     }
494 #if 1
495     GST_LOG
496         ("Frame @ %ld (0x%02lx) id %s size %u, next=%ld (0x%02lx) obsolete=%d",
497         (glong) (work->hdr.frame_data - start),
498         (glong) (work->hdr.frame_data - start), frame_id, frame_size,
499         (glong) (work->hdr.frame_data + frame_hdr_size + frame_size - start),
500         (glong) (work->hdr.frame_data + frame_hdr_size + frame_size - start),
501         obsolete_id);
502 #define flag_string(flag,str) \
503         ((frame_flags & (flag)) ? (str) : "")
504     GST_LOG ("Frame header flags: 0x%04x %s %s %s %s %s %s %s", frame_flags,
505         flag_string (ID3V2_FRAME_STATUS_FRAME_ALTER_PRESERVE, "ALTER_PRESERVE"),
506         flag_string (ID3V2_FRAME_STATUS_READONLY, "READONLY"),
507         flag_string (ID3V2_FRAME_FORMAT_GROUPING_ID, "GROUPING_ID"),
508         flag_string (ID3V2_FRAME_FORMAT_COMPRESSION, "COMPRESSION"),
509         flag_string (ID3V2_FRAME_FORMAT_ENCRYPTION, "ENCRYPTION"),
510         flag_string (ID3V2_FRAME_FORMAT_UNSYNCHRONISATION, "UNSYNC"),
511         flag_string (ID3V2_FRAME_FORMAT_DATA_LENGTH_INDICATOR, "LENGTH_IND"));
512 #undef flag_str
513 #endif
514
515     if (!obsolete_id) {
516       /* Now, read, decompress etc the contents of the frame
517        * into a TagList entry */
518       work->cur_frame_size = frame_size;
519       work->frame_id = frame_id;
520       work->frame_flags = frame_flags;
521
522       if (id3demux_id3v2_parse_frame (work)) {
523         GST_LOG ("Extracted frame with id %s", frame_id);
524       } else {
525         GST_LOG ("Failed to extract frame with id %s", frame_id);
526         id3demux_add_id3v2_frame_blob_to_taglist (work, frame_size);
527       }
528     }
529     work->hdr.frame_data += frame_size;
530     work->hdr.frame_data_size -= frame_size;
531   }
532
533   if (gst_structure_n_fields (GST_STRUCTURE (work->tags)) == 0) {
534     GST_DEBUG ("Could not extract any frames from tag. Broken or empty tag");
535     gst_tag_list_free (work->tags);
536     work->tags = NULL;
537     return ID3TAGS_BROKEN_TAG;
538   }
539
540   /* Set day/month now if they were in a separate (obsolete) TDAT frame */
541   if (work->pending_day != 0 && work->pending_month != 0) {
542     GDate *date = NULL;
543
544     if (gst_tag_list_get_date (work->tags, GST_TAG_DATE, &date)) {
545       g_date_set_day (date, work->pending_day);
546       g_date_set_month (date, work->pending_month);
547       gst_tag_list_add (work->tags, GST_TAG_MERGE_REPLACE, GST_TAG_DATE,
548           date, NULL);
549       g_date_free (date);
550     }
551   }
552
553   return ID3TAGS_READ_TAG;
554 }