id3v2.c 17.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
/* Copyright 2005 Jan Schmidt <thaytan@mad.scientist.com>
 * Copyright 2002,2003 Scott Wheeler <wheeler@kde.org> (portions from taglib)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
17
18
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
19
20
21
22
23
24
25
26
27
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>
#include <gst/tag/tag.h>

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#ifndef GST_DISABLE_GST_DEBUG

#define GST_CAT_DEFAULT id3v2_ensure_debug_category()

GstDebugCategory *
id3v2_ensure_debug_category (void)
{
  static gsize cat_gonce = 0;

  if (g_once_init_enter (&cat_gonce)) {
    gsize cat;

    cat = (gsize) _gst_debug_category_new ("id3v2", 0, "ID3v2 tag parsing");

    g_once_init_leave (&cat_gonce, cat);
  }

  return (GstDebugCategory *) cat_gonce;
}

#endif /* GST_DISABLE_GST_DEBUG */

56
/* Synch safe uints have 28 bits (256MB max) available. */
57
guint
58
id3v2_read_synch_uint (const guint8 * data, guint size)
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
{
  gint i;
  guint result = 0;
  gint invalid = 0;

  g_assert (size <= 4);

  size--;
  for (i = 0; i <= size; i++) {
    invalid |= data[i] & 0x80;
    result |= (data[i] & 0x7f) << ((size - i) * 7);
  }

#ifdef HANDLE_INVALID_SYNCSAFE
  if (invalid) {
    GST_WARNING ("Invalid synch-safe integer in ID3v2 frame "
        "- using the actual value instead");
    result = 0;
    for (i = 0; i <= size; i++) {
      result |= data[i] << ((size - i) * 8);
    }
  }
#endif
  return result;
}

85
86
87
88
89
90
91
92
93
/**
 * gst_tag_get_id3v2_tag_size:
 * @buffer: buffer holding ID3v2 tag (or at least the start of one)
 *
 * Determines size of an ID3v2 tag on buffer containing at least ID3v2 header,
 * i.e. at least #GST_TAG_ID3V2_HEADER_SIZE (10) bytes;
 *
 * Returns: Size of tag, or 0 if header is invalid or too small.
 */
94
guint
95
gst_tag_get_id3v2_tag_size (GstBuffer * buffer)
96
{
Wim Taymans's avatar
Wim Taymans committed
97
98
  GstMapInfo info;
  guint8 flags;
Wim Taymans's avatar
Wim Taymans committed
99
  guint result = 0;
100

101
  g_return_val_if_fail (buffer != NULL, 0);
102

Wim Taymans's avatar
Wim Taymans committed
103
  gst_buffer_map (buffer, &info, GST_MAP_READ);
104

Wim Taymans's avatar
Wim Taymans committed
105
  if (info.size < ID3V2_HDR_SIZE)
Wim Taymans's avatar
Wim Taymans committed
106
    goto too_small;
107

108
  /* Check for 'ID3' string at start of buffer */
Wim Taymans's avatar
Wim Taymans committed
109
  if (info.data[0] != 'I' || info.data[1] != 'D' || info.data[2] != '3')
Wim Taymans's avatar
Wim Taymans committed
110
    goto no_tag;
111

112
  /* Read the flags */
Wim Taymans's avatar
Wim Taymans committed
113
  flags = info.data[5];
114

115
  /* Read the size from the header */
Wim Taymans's avatar
Wim Taymans committed
116
  result = id3v2_read_synch_uint (info.data + 6, 4);
Wim Taymans's avatar
Wim Taymans committed
117
118
  if (result == 0)
    goto empty;
119

Wim Taymans's avatar
Wim Taymans committed
120
  result += ID3V2_HDR_SIZE;
121

122
123
  /* Expand the read size to include a footer if there is one */
  if ((flags & ID3V2_HDR_FLAG_FOOTER))
Wim Taymans's avatar
Wim Taymans committed
124
    result += 10;
125

Wim Taymans's avatar
Wim Taymans committed
126
127
128
  GST_DEBUG ("ID3v2 tag, size: %u bytes", result);

done:
Wim Taymans's avatar
Wim Taymans committed
129
  gst_buffer_unmap (buffer, &info);
Wim Taymans's avatar
Wim Taymans committed
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148

  return result;

too_small:
  {
    GST_DEBUG ("size too small");
    goto done;
  }
no_tag:
  {
    GST_DEBUG ("No ID3v2 tag in data");
    goto done;
  }
empty:
  {
    GST_DEBUG ("Empty tag size");
    result = ID3V2_HDR_SIZE;
    goto done;
  }
149
150
}

151
guint8 *
152
id3v2_ununsync_data (const guint8 * unsync_data, guint32 * size)
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
{
  const guint8 *end;
  guint8 *out, *uu;
  guint out_size;

  uu = out = g_malloc (*size);

  for (end = unsync_data + *size; unsync_data < end - 1; ++unsync_data, ++uu) {
    *uu = *unsync_data;
    if (G_UNLIKELY (*unsync_data == 0xff && *(unsync_data + 1) == 0x00))
      ++unsync_data;
  }

  /* take care of last byte (if last two bytes weren't 0xff 0x00) */
  if (unsync_data < end) {
    *uu = *unsync_data;
    ++uu;
  }

  out_size = uu - out;
  GST_DEBUG ("size after un-unsyncing: %u (before: %u)", out_size, *size);

  *size = out_size;
  return out;
}

179
180
181
182
183
184
185
186
187
188
189
190
/**
 * gst_tag_list_from_id3v2_tag:
 * @buffer: buffer to convert
 *
 * Creates a new tag list that contains the information parsed out of a
 * ID3 tag.
 *
 * Returns: A new #GstTagList with all tags that could be extracted from the
 *          given vorbiscomment buffer or NULL on error.
 */
GstTagList *
gst_tag_list_from_id3v2_tag (GstBuffer * buffer)
191
{
Wim Taymans's avatar
Wim Taymans committed
192
193
  GstMapInfo info;
  guint8 *uu_data = NULL;
194
195
196
197
198
  guint read_size;
  ID3TagsWorking work;
  guint8 flags;
  guint16 version;

199
200
  gst_tag_register_musicbrainz_tags ();

201
  read_size = gst_tag_get_id3v2_tag_size (buffer);
202

203
  /* Ignore tag if it has no frames attached, but skip the header then */
204
205
  if (read_size < ID3V2_HDR_SIZE)
    return NULL;
206

Wim Taymans's avatar
Wim Taymans committed
207
  gst_buffer_map (buffer, &info, GST_MAP_READ);
208
209

  /* Read the version */
Wim Taymans's avatar
Wim Taymans committed
210
  version = GST_READ_UINT16_BE (info.data + 3);
211
212

  /* Read the flags */
Wim Taymans's avatar
Wim Taymans committed
213
  flags = info.data[5];
214
215

  /* Validate the version. At the moment, we only support up to 2.4.0 */
Wim Taymans's avatar
Wim Taymans committed
216
217
  if (ID3V2_VER_MAJOR (version) > 4 || ID3V2_VER_MINOR (version) > 0)
    goto wrong_version;
218

219
220
221
222
223
224
  GST_DEBUG ("ID3v2 header flags: %s %s %s %s",
      (flags & ID3V2_HDR_FLAG_UNSYNC) ? "UNSYNC" : "",
      (flags & ID3V2_HDR_FLAG_EXTHDR) ? "EXTENDED_HEADER" : "",
      (flags & ID3V2_HDR_FLAG_EXPERIMENTAL) ? "EXPERIMENTAL" : "",
      (flags & ID3V2_HDR_FLAG_FOOTER) ? "FOOTER" : "");

225
  /* This shouldn't really happen! Caller should have checked first */
Wim Taymans's avatar
Wim Taymans committed
226
  if (info.size < read_size)
Wim Taymans's avatar
Wim Taymans committed
227
    goto not_enough_data;
228
229
230

  GST_DEBUG ("Reading ID3v2 tag with revision 2.%d.%d of size %u", version >> 8,
      version & 0xff, read_size);
231

Wim Taymans's avatar
Wim Taymans committed
232
  GST_MEMDUMP ("ID3v2 tag", info.data, read_size);
233

234
235
236
237
238
  memset (&work, 0, sizeof (ID3TagsWorking));
  work.buffer = buffer;
  work.hdr.version = version;
  work.hdr.size = read_size;
  work.hdr.flags = flags;
Wim Taymans's avatar
Wim Taymans committed
239
  work.hdr.frame_data = info.data + ID3V2_HDR_SIZE;
240
241
242
243

  if (flags & ID3V2_HDR_FLAG_FOOTER) {
    if (read_size < ID3V2_HDR_SIZE + 10)
      goto not_enough_data;     /* Invalid frame size */
244
    work.hdr.frame_data_size = read_size - ID3V2_HDR_SIZE - 10;
245
  } else {
246
    g_assert (read_size >= ID3V2_HDR_SIZE);     /* checked above */
247
    work.hdr.frame_data_size = read_size - ID3V2_HDR_SIZE;
248
  }
249

250
251
252
253
  /* in v2.3 the frame sizes are not syncsafe, so the entire tag had to be
   * unsynced. In v2.4 the frame sizes are syncsafe so it's just the frame
   * data that needs un-unsyncing, but not the frame headers. */
  if ((flags & ID3V2_HDR_FLAG_UNSYNC) != 0 && ID3V2_VER_MAJOR (version) <= 3) {
254
    GST_DEBUG ("Un-unsyncing entire tag");
255
    uu_data = id3v2_ununsync_data (work.hdr.frame_data,
256
257
258
259
260
        &work.hdr.frame_data_size);
    work.hdr.frame_data = uu_data;
    GST_MEMDUMP ("ID3v2 tag (un-unsyced)", uu_data, work.hdr.frame_data_size);
  }

261
  id3v2_frames_to_tag_list (&work, work.hdr.frame_data_size);
262

263
264
  g_free (uu_data);

Wim Taymans's avatar
Wim Taymans committed
265
  gst_buffer_unmap (buffer, &info);
Wim Taymans's avatar
Wim Taymans committed
266

267
  return work.tags;
Wim Taymans's avatar
Wim Taymans committed
268
269
270
271
272
273
274

  /* ERRORS */
wrong_version:
  {
    GST_WARNING ("ID3v2 tag is from revision 2.%d.%d, "
        "but decoder only supports 2.%d.%d. Ignoring as per spec.",
        version >> 8, version & 0xff, ID3V2_VERSION >> 8, ID3V2_VERSION & 0xff);
Wim Taymans's avatar
Wim Taymans committed
275
    gst_buffer_unmap (buffer, &info);
Wim Taymans's avatar
Wim Taymans committed
276
277
278
279
280
281
    return NULL;
  }
not_enough_data:
  {
    GST_DEBUG
        ("Found ID3v2 tag with revision 2.%d.%d - need %u more bytes to read",
Wim Taymans's avatar
Wim Taymans committed
282
283
        version >> 8, version & 0xff, (guint) (read_size - info.size));
    gst_buffer_unmap (buffer, &info);
Wim Taymans's avatar
Wim Taymans committed
284
285
    return NULL;
  }
286
287
288
}

static guint
289
id3v2_frame_hdr_size (guint id3v2ver)
290
291
292
293
294
295
296
297
298
299
300
301
302
303
{
  /* ID3v2 < 2.3.0 only had 6 byte header */
  switch (ID3V2_VER_MAJOR (id3v2ver)) {
    case 0:
    case 1:
    case 2:
      return 6;
    case 3:
    case 4:
    default:
      return 10;
  }
}

304
305
306
static const gchar obsolete_frame_ids[][5] = {
  {"CRM"}, {"EQU"}, {"LNK"}, {"RVA"}, {"TIM"}, {"TSI"}, /* From 2.2 */
  {"EQUA"}, {"RVAD"}, {"TIME"}, {"TRDA"}, {"TSIZ"}      /* From 2.3 */
307
308
};

309
static const struct ID3v2FrameIDConvert
310
{
311
312
  const gchar orig[5];
  const gchar new[5];
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
} frame_id_conversions[] = {
  /* 2.3.x frames */
  {
  "TORY", "TDOR"}, {
  "TYER", "TDRC"},
      /* 2.2.x frames */
  {
  "BUF", "RBUF"}, {
  "CNT", "PCNT"}, {
  "COM", "COMM"}, {
  "CRA", "AENC"}, {
  "ETC", "ETCO"}, {
  "GEO", "GEOB"}, {
  "IPL", "TIPL"}, {
  "MCI", "MCDI"}, {
  "MLL", "MLLT"}, {
  "PIC", "APIC"}, {
  "POP", "POPM"}, {
  "REV", "RVRB"}, {
  "SLT", "SYLT"}, {
  "STC", "SYTC"}, {
  "TAL", "TALB"}, {
  "TBP", "TBPM"}, {
  "TCM", "TCOM"}, {
337
  "TCO", "TCON"}, {
338
  "TCR", "TCOP"}, {
339
  "TDA", "TDAT"}, {             /* obsolete, but we need to parse it anyway */
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
  "TDY", "TDLY"}, {
  "TEN", "TENC"}, {
  "TFT", "TFLT"}, {
  "TKE", "TKEY"}, {
  "TLA", "TLAN"}, {
  "TLE", "TLEN"}, {
  "TMT", "TMED"}, {
  "TOA", "TOAL"}, {
  "TOF", "TOFN"}, {
  "TOL", "TOLY"}, {
  "TOR", "TDOR"}, {
  "TOT", "TOAL"}, {
  "TP1", "TPE1"}, {
  "TP2", "TPE2"}, {
  "TP3", "TPE3"}, {
  "TP4", "TPE4"}, {
  "TPA", "TPOS"}, {
  "TPB", "TPUB"}, {
  "TRC", "TSRC"}, {
  "TRD", "TDRC"}, {
  "TRK", "TRCK"}, {
  "TSS", "TSSE"}, {
  "TT1", "TIT1"}, {
  "TT2", "TIT2"}, {
  "TT3", "TIT3"}, {
  "TXT", "TOLY"}, {
  "TXX", "TXXX"}, {
  "TYE", "TDRC"}, {
  "UFI", "UFID"}, {
  "ULT", "USLT"}, {
  "WAF", "WOAF"}, {
  "WAR", "WOAR"}, {
  "WAS", "WOAS"}, {
  "WCM", "WCOM"}, {
  "WCP", "WCOP"}, {
  "WPB", "WPUB"}, {
376
  "WXX", "WXXX"}
377
378
379
380
381
};

static gboolean
convert_fid_to_v240 (gchar * frame_id)
{
382
  gint i;
383

384
  for (i = 0; i < G_N_ELEMENTS (obsolete_frame_ids); ++i) {
385
386
387
388
    if (strncmp (frame_id, obsolete_frame_ids[i], 5) == 0)
      return TRUE;
  }

389
  for (i = 0; i < G_N_ELEMENTS (frame_id_conversions); ++i) {
390
391
392
393
394
395
396
397
    if (strncmp (frame_id, frame_id_conversions[i].orig, 5) == 0) {
      strcpy (frame_id, frame_id_conversions[i].new);
      return FALSE;
    }
  }
  return FALSE;
}

398
399
400

/* add unknown or unhandled ID3v2 frames to the taglist as binary blobs */
static void
401
402
id3v2_add_id3v2_frame_blob_to_taglist (ID3TagsWorking * work,
    guint8 * frame_data, guint frame_size)
403
404
{
  GstBuffer *blob;
405
  GstSample *sample;
Wim Taymans's avatar
Wim Taymans committed
406
407
#if 0
  GstCaps *caps;
408
  gchar *media_type;
Wim Taymans's avatar
Wim Taymans committed
409
#endif
410
  guint i;
411
412

  blob = gst_buffer_new_and_alloc (frame_size);
Wim Taymans's avatar
Wim Taymans committed
413
  gst_buffer_fill (blob, 0, frame_data, frame_size);
414

415
416
417
  sample = gst_sample_new (blob, NULL, NULL, NULL);
  gst_buffer_unref (blob);

418
419
420
421
422
423
  /* Sanitize frame id */
  for (i = 0; i < 4; i++) {
    if (!g_ascii_isalnum (frame_data[i]))
      frame_data[i] = '_';
  }

Wim Taymans's avatar
Wim Taymans committed
424
#if 0
425
426
427
  media_type = g_strdup_printf ("application/x-gst-id3v2-%c%c%c%c-frame",
      g_ascii_tolower (frame_data[0]), g_ascii_tolower (frame_data[1]),
      g_ascii_tolower (frame_data[2]), g_ascii_tolower (frame_data[3]));
428
429
  caps = gst_caps_new_simple (media_type, "version", G_TYPE_INT,
      (gint) ID3V2_VER_MAJOR (work->hdr.version), NULL);
430
431
432
  gst_buffer_set_caps (blob, caps);
  gst_caps_unref (caps);
  g_free (media_type);
Wim Taymans's avatar
Wim Taymans committed
433
#endif
434

435
436
  /* gst_util_dump_mem (GST_BUFFER_DATA (blob), GST_BUFFER_SIZE (blob)); */

437
  gst_tag_list_add (work->tags, GST_TAG_MERGE_APPEND,
438
439
      GST_TAG_ID3V2_FRAME, sample, NULL);
  gst_sample_unref (sample);
440
441
}

442
443
static gboolean
id3v2_frames_to_tag_list (ID3TagsWorking * work, guint size)
444
445
446
447
448
{
  guint frame_hdr_size;

  /* Extended header if present */
  if (work->hdr.flags & ID3V2_HDR_FLAG_EXTHDR) {
449
    work->hdr.ext_hdr_size = id3v2_read_synch_uint (work->hdr.frame_data, 4);
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471

    /* In id3v2.4.x the header size is the size of the *whole*
     * extended header.
     * In id3v2.3.x the header size does *not* include itself.
     * In older versions it's undefined but let's assume it follow 2.3.x
     */
    switch (ID3V2_VER_MAJOR (work->hdr.version)) {
      case 0:
      case 1:
      case 2:
      case 3:
        work->hdr.ext_hdr_size += 4;
        break;
      case 4:
        break;
      default:
        GST_WARNING
            ("Don't know how to handled Extended Header for this id3 version");
        break;
    }
    GST_LOG ("extended header size %d", work->hdr.ext_hdr_size);

472
    if (work->hdr.ext_hdr_size < 6 ||
473
        work->hdr.ext_hdr_size > work->hdr.frame_data_size) {
474
      GST_DEBUG ("Invalid extended header. Broken tag");
475
      return FALSE;
476
477
478
479
480
    }
    work->hdr.ext_flag_bytes = work->hdr.frame_data[4];
    if (5 + work->hdr.ext_flag_bytes > work->hdr.frame_data_size) {
      GST_DEBUG
          ("Tag claims extended header, but doesn't have enough bytes. Broken tag");
481
      return FALSE;
482
483
    }
    work->hdr.ext_flag_data = work->hdr.frame_data + 5;
484
485
    work->hdr.frame_data += work->hdr.ext_hdr_size;
    work->hdr.frame_data_size -= work->hdr.ext_hdr_size;
486
487
  }

488
  frame_hdr_size = id3v2_frame_hdr_size (work->hdr.version);
489
490
  if (work->hdr.frame_data_size <= frame_hdr_size) {
    GST_DEBUG ("Tag has no data frames. Broken tag");
491
    return FALSE;               /* Must have at least one frame */
492
493
  }

494
  work->tags = gst_tag_list_new_empty ();
495
496
497
498
499
500

  while (work->hdr.frame_data_size > frame_hdr_size) {
    guint frame_size = 0;
    gchar frame_id[5] = "";
    guint16 frame_flags = 0x0;
    gboolean obsolete_id = FALSE;
501
    gboolean read_synch_size = TRUE;
502
    guint i;
503
504
505
506
507
508
509
510
511
512
513
514
515

    /* Read the header */
    switch (ID3V2_VER_MAJOR (work->hdr.version)) {
      case 0:
      case 1:
      case 2:
        frame_id[0] = work->hdr.frame_data[0];
        frame_id[1] = work->hdr.frame_data[1];
        frame_id[2] = work->hdr.frame_data[2];
        frame_id[3] = 0;
        frame_id[4] = 0;
        obsolete_id = convert_fid_to_v240 (frame_id);

516
517
518
        /* 3 byte non-synchsafe size */
        frame_size = work->hdr.frame_data[3] << 16 |
            work->hdr.frame_data[4] << 8 | work->hdr.frame_data[5];
519
520
521
        frame_flags = 0;
        break;
      case 3:
522
        read_synch_size = FALSE;        /* 2.3 frame size is not synch-safe */
523
524
525
526
527
528
529
      case 4:
      default:
        frame_id[0] = work->hdr.frame_data[0];
        frame_id[1] = work->hdr.frame_data[1];
        frame_id[2] = work->hdr.frame_data[2];
        frame_id[3] = work->hdr.frame_data[3];
        frame_id[4] = 0;
530
        if (read_synch_size)
531
          frame_size = id3v2_read_synch_uint (work->hdr.frame_data + 4, 4);
532
533
534
        else
          frame_size = GST_READ_UINT32_BE (work->hdr.frame_data + 4);

535
536
537
538
539
        frame_flags = GST_READ_UINT16_BE (work->hdr.frame_data + 8);

        if (ID3V2_VER_MAJOR (work->hdr.version) == 3) {
          frame_flags &= ID3V2_3_FRAME_FLAGS_MASK;
          obsolete_id = convert_fid_to_v240 (frame_id);
540
541
          if (obsolete_id)
            GST_DEBUG ("Ignoring v2.3 frame %s", frame_id);
542
543
544
545
546
547
548
        }
        break;
    }

    work->hdr.frame_data += frame_hdr_size;
    work->hdr.frame_data_size -= frame_hdr_size;

549
    if (frame_size > work->hdr.frame_data_size || strcmp (frame_id, "") == 0)
550
551
      break;                    /* No more frames to read */

552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
    /* Sanitize frame id */
    switch (ID3V2_VER_MAJOR (work->hdr.version)) {
      case 0:
      case 1:
      case 2:
        for (i = 0; i < 3; i++) {
          if (!g_ascii_isalnum (frame_id[i]))
            frame_id[i] = '_';
        }
        break;
      default:
        for (i = 0; i < 4; i++) {
          if (!g_ascii_isalnum (frame_id[i]))
            frame_id[i] = '_';
        }
    }
568
#if 1
Wim Taymans's avatar
Wim Taymans committed
569
#if 0
570
    GST_LOG
571
        ("Frame @ %ld (0x%02lx) id %s size %u, next=%ld (0x%02lx) obsolete=%d",
572
573
574
575
        (glong) (work->hdr.frame_data - start),
        (glong) (work->hdr.frame_data - start), frame_id, frame_size,
        (glong) (work->hdr.frame_data + frame_hdr_size + frame_size - start),
        (glong) (work->hdr.frame_data + frame_hdr_size + frame_size - start),
576
        obsolete_id);
Wim Taymans's avatar
Wim Taymans committed
577
#endif
578
579
580
581
582
583
584
585
586
#define flag_string(flag,str) \
        ((frame_flags & (flag)) ? (str) : "")
    GST_LOG ("Frame header flags: 0x%04x %s %s %s %s %s %s %s", frame_flags,
        flag_string (ID3V2_FRAME_STATUS_FRAME_ALTER_PRESERVE, "ALTER_PRESERVE"),
        flag_string (ID3V2_FRAME_STATUS_READONLY, "READONLY"),
        flag_string (ID3V2_FRAME_FORMAT_GROUPING_ID, "GROUPING_ID"),
        flag_string (ID3V2_FRAME_FORMAT_COMPRESSION, "COMPRESSION"),
        flag_string (ID3V2_FRAME_FORMAT_ENCRYPTION, "ENCRYPTION"),
        flag_string (ID3V2_FRAME_FORMAT_UNSYNCHRONISATION, "UNSYNC"),
587
        flag_string (ID3V2_FRAME_FORMAT_DATA_LENGTH_INDICATOR, "LENGTH_IND"));
588
#undef flag_str
589
590
591
592
593
594
595
596
597
#endif

    if (!obsolete_id) {
      /* Now, read, decompress etc the contents of the frame
       * into a TagList entry */
      work->cur_frame_size = frame_size;
      work->frame_id = frame_id;
      work->frame_flags = frame_flags;

598
      if (id3v2_parse_frame (work)) {
599
        GST_LOG ("Extracted frame with id %s", frame_id);
600
601
      } else {
        GST_LOG ("Failed to extract frame with id %s", frame_id);
602
603
604
        /* Rewind the frame data / size to pass the header too */
        id3v2_add_id3v2_frame_blob_to_taglist (work,
            work->hdr.frame_data - frame_hdr_size, frame_hdr_size + frame_size);
605
      }
606
      work->frame_id = NULL;    /* clear ref to loop-local storage */
607
    }
608

609
610
611
612
    work->hdr.frame_data += frame_size;
    work->hdr.frame_data_size -= frame_size;
  }

613
  if (gst_tag_list_n_tags (work->tags) == 0) {
614
    GST_DEBUG ("Could not extract any frames from tag. Broken or empty tag");
615
    gst_tag_list_unref (work->tags);
616
    work->tags = NULL;
617
    return FALSE;
618
619
  }

620
  /* Set day/month now if they were in a separate (obsolete) TDAT frame */
621
  /* FIXME: we could extract the time as well now */
622
  if (work->pending_day != 0 && work->pending_month != 0) {
623
624
625
626
627
628
629
    GstDateTime *dt = NULL;

    if (gst_tag_list_get_date_time (work->tags, GST_TAG_DATE_TIME, &dt)) {
      GstDateTime *dt2;

      /* GstDateTime is immutable, so create new one and replace old one */
      dt2 = gst_date_time_new_ymd (gst_date_time_get_year (dt),
630
          work->pending_month, work->pending_day);
631
632
633
634
      gst_tag_list_add (work->tags, GST_TAG_MERGE_REPLACE, GST_TAG_DATE_TIME,
          dt2, NULL);
      gst_date_time_unref (dt2);
      gst_date_time_unref (dt);
635
636
637
    }
  }

638
  return TRUE;
639
}