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