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