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