DCP: fixed include-guards
[vlc.git] / modules / meta_engine / taglib.cpp
1 /*****************************************************************************
2  * taglib.cpp: Taglib tag parser/writer
3  *****************************************************************************
4  * Copyright (C) 2003-2011 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Clément Stenac <zorglub@videolan.org>
8  *          Rafaël Carré <funman@videolanorg>
9  *          Rémi Duraffort <ivoire@videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 2.1 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_demux.h>              /* demux_meta_t */
33 #include <vlc_strings.h>            /* vlc_b64_decode_binary */
34 #include <vlc_input.h>              /* for attachment_new */
35 #include <vlc_url.h>                /* vlc_uri2path */
36 #include <vlc_mime.h>               /* mime type */
37 #include <vlc_fs.h>
38
39 #include <sys/stat.h>
40
41 #ifdef _WIN32
42 # include <vlc_charset.h>
43 # include <io.h>
44 #else
45 # include <unistd.h>
46 #endif
47
48
49 // Taglib headers
50 #ifdef _WIN32
51 # define TAGLIB_STATIC
52 #endif
53 #include <taglib.h>
54 #define VERSION_INT(a, b, c) ((a)<<16 | (b)<<8 | (c))
55 #define TAGLIB_VERSION VERSION_INT(TAGLIB_MAJOR_VERSION, \
56                                    TAGLIB_MINOR_VERSION, \
57                                    TAGLIB_PATCH_VERSION)
58
59 #include <fileref.h>
60 #include <tag.h>
61 #include <tbytevector.h>
62
63 #include <apefile.h>
64 #include <asffile.h>
65 #include <apetag.h>
66 #include <flacfile.h>
67 #include <mpcfile.h>
68 #include <mpegfile.h>
69 #include <mp4file.h>
70 #include <oggfile.h>
71 #include <oggflacfile.h>
72 #include <opusfile.h>
73 #include "../demux/xiph_metadata.h"
74
75 #include <aifffile.h>
76 #include <wavfile.h>
77
78 #include <speexfile.h>
79 #include <trueaudiofile.h>
80 #include <vorbisfile.h>
81 #include <wavpackfile.h>
82
83 #include <attachedpictureframe.h>
84 #include <textidentificationframe.h>
85 #include <uniquefileidentifierframe.h>
86
87 using namespace TagLib;
88
89 #define TAGLIB_SYNCDECODE_FIXED_VERSION VERSION_INT(1,11,0)
90
91 #include <algorithm>
92
93 namespace VLCTagLib
94 {
95     template <class T>
96     class ExtResolver : public FileRef::FileTypeResolver
97     {
98         public:
99             ExtResolver(const std::string &);
100             ~ExtResolver() {}
101             virtual File *createFile(FileName, bool, AudioProperties::ReadStyle) const;
102
103         protected:
104             std::string ext;
105     };
106 }
107
108 template <class T>
109 VLCTagLib::ExtResolver<T>::ExtResolver(const std::string & ext) : FileTypeResolver()
110 {
111     this->ext = ext;
112     std::transform(this->ext.begin(), this->ext.end(), this->ext.begin(), ::toupper);
113 }
114
115 template <class T>
116 File *VLCTagLib::ExtResolver<T>::createFile(FileName fileName, bool, AudioProperties::ReadStyle) const
117 {
118     std::string filename = std::string(fileName);
119     std::size_t namesize = filename.size();
120
121     if (namesize > ext.length())
122     {
123         std::string fext = filename.substr(namesize - ext.length(), ext.length());
124         std::transform(fext.begin(), fext.end(), fext.begin(), ::toupper);
125         if(fext == ext)
126             return new T(fileName, false, AudioProperties::ReadStyle::Fast);
127     }
128
129     return 0;
130 }
131
132 #if TAGLIB_VERSION >= TAGLIB_SYNCDECODE_FIXED_VERSION
133 static VLCTagLib::ExtResolver<MPEG::File> aacresolver(".aac");
134 #endif
135 static VLCTagLib::ExtResolver<MP4::File> m4vresolver(".m4v");
136 static bool b_extensions_registered = false;
137
138 // taglib is not thread safe
139 static vlc_mutex_t taglib_lock = VLC_STATIC_MUTEX;
140
141 // Local functions
142 static int ReadMeta    ( vlc_object_t * );
143 static int WriteMeta   ( vlc_object_t * );
144
145 vlc_module_begin ()
146     set_capability( "meta reader", 1000 )
147     set_callbacks( ReadMeta, NULL )
148     add_submodule ()
149         set_capability( "meta writer", 50 )
150         set_callbacks( WriteMeta, NULL )
151 vlc_module_end ()
152
153 static int ExtractCoupleNumberValues( vlc_meta_t* p_meta, const char *psz_value,
154         vlc_meta_type_t first, vlc_meta_type_t second)
155 {
156     unsigned int i_trknum, i_trktot;
157
158     int i_ret = sscanf( psz_value, "%u/%u", &i_trknum, &i_trktot );
159     char psz_trck[11];
160     if( i_ret >= 1 )
161     {
162         snprintf( psz_trck, sizeof( psz_trck ), "%u", i_trknum );
163         vlc_meta_Set( p_meta, first, psz_trck );
164     }
165     if( i_ret == 2)
166     {
167         snprintf( psz_trck, sizeof( psz_trck ), "%u", i_trktot );
168         vlc_meta_Set( p_meta, second, psz_trck );
169     }
170     return i_ret;
171 }
172
173 /**
174  * Read meta information from APE tags
175  * @param tag: the APE tag
176  * @param p_demux_meta: the demuxer meta
177  * @param p_meta: the meta
178  */
179 static void ReadMetaFromAPE( APE::Tag* tag, demux_meta_t* p_demux_meta, vlc_meta_t* p_meta )
180 {
181     APE::ItemListMap fields ( tag->itemListMap() );
182     APE::ItemListMap::Iterator iter;
183
184     iter = fields.find("COVER ART (FRONT)");
185     if( iter != fields.end()
186         && !iter->second.isEmpty()
187         && iter->second.type() == APE::Item::Binary)
188     {
189         input_attachment_t *p_attachment;
190
191         const ByteVector picture = iter->second.binaryData();
192         const char *p_data = picture.data();
193         unsigned i_data = picture.size();
194
195         /* Null terminated filename followed by the image data */
196         size_t desc_len = strnlen(p_data, i_data);
197         if( desc_len < i_data && IsUTF8( p_data ) )
198         {
199             const char *psz_name = p_data;
200             const char *psz_mime = vlc_mime_Ext2Mime( psz_name );
201             p_data += desc_len + 1; /* '\0' */
202             i_data -= desc_len + 1;
203
204             msg_Dbg( p_demux_meta, "Found embedded art: %s (%s) is %u bytes",
205                      psz_name, psz_mime, i_data );
206
207             p_attachment = vlc_input_attachment_New( psz_name, psz_mime,
208                                     psz_name, p_data, i_data );
209             if( p_attachment )
210             {
211                 TAB_APPEND_CAST( (input_attachment_t**),
212                                  p_demux_meta->i_attachments, p_demux_meta->attachments,
213                                  p_attachment );
214
215                 char *psz_url;
216                 if( asprintf( &psz_url, "attachment://%s", p_attachment->psz_name ) != -1 )
217                 {
218                     vlc_meta_SetArtURL( p_meta, psz_url );
219                     free( psz_url );
220                 }
221             }
222         }
223
224         fields.erase(iter);
225     }
226
227 #define SET( keyName, metaName ) \
228     iter = fields.find(keyName); \
229     if( iter != fields.end() && !iter->second.isEmpty() ) { \
230         vlc_meta_Set##metaName( p_meta, iter->second.toString().toCString( true ) ); \
231         fields.erase(iter); \
232     }
233
234 #define SET_EXTRA( keyName, metaName ) \
235     iter = fields.find( keyName ); \
236     if( iter != fields.end() && !iter->second.isEmpty() ) { \
237         vlc_meta_AddExtra( p_meta, metaName, iter->second.toString().toCString( true ) ); \
238         fields.erase(iter); \
239     }
240
241     SET( "ALBUM", Album );
242     SET( "ARTIST", Artist );
243     SET( "COMMENT", Description );
244     SET( "GENRE", Genre );
245     SET( "TITLE", Title );
246     SET( "COPYRIGHT", Copyright );
247     SET( "LANGUAGE", Language );
248     SET( "PUBLISHER", Publisher );
249     SET( "MUSICBRAINZ_TRACKID", TrackID );
250
251     SET_EXTRA( "MUSICBRAINZ_ALBUMID", VLC_META_EXTRA_MB_ALBUMID );
252
253 #undef SET
254 #undef SET_EXTRA
255
256     /* */
257     iter = fields.find( "TRACK" );
258     if( iter != fields.end() && !iter->second.isEmpty() )
259     {
260         ExtractCoupleNumberValues( p_meta, iter->second.toString().toCString( true ),
261                 vlc_meta_TrackNumber, vlc_meta_TrackTotal );
262         fields.erase( iter );
263     }
264
265     /* Remainings */
266     for( iter = fields.begin(); iter != fields.end(); ++iter )
267     {
268         if( iter->second.isEmpty() )
269             continue;
270
271         if( iter->second.type() != APE::Item::Text )
272             continue;
273
274         vlc_meta_AddExtra( p_meta,
275                            iter->first.toCString( true ),
276                            iter->second.toString().toCString( true ) );
277     }
278 }
279
280
281 /**
282  * Read meta information from ASF tags
283  * @param tag: the ASF tag
284  * @param p_demux_meta: the demuxer meta
285  * @param p_meta: the meta
286  */
287 static void ReadMetaFromASF( ASF::Tag* tag, demux_meta_t* p_demux_meta, vlc_meta_t* p_meta )
288 {
289
290     ASF::AttributeList list;
291 #define SET( keyName, metaName )                                                     \
292     if( tag->attributeListMap().contains(keyName) )                                  \
293     {                                                                                \
294         list = tag->attributeListMap()[keyName];                                     \
295         vlc_meta_Set##metaName( p_meta, list.front().toString().toCString( true ) ); \
296     }
297
298 #define SET_EXTRA( keyName, metaName )                                                     \
299     if( tag->attributeListMap().contains(keyName) )                                  \
300     {                                                                                \
301         list = tag->attributeListMap()[keyName];                                     \
302         vlc_meta_AddExtra( p_meta, metaName, list.front().toString().toCString( true ) ); \
303     }
304
305     SET("MusicBrainz/Track Id", TrackID );
306     SET_EXTRA("MusicBrainz/Album Id", VLC_META_EXTRA_MB_ALBUMID );
307
308 #undef SET
309 #undef SET_EXTRA
310
311     // List the pictures
312     list = tag->attributeListMap()["WM/Picture"];
313     ASF::AttributeList::Iterator iter;
314     for( iter = list.begin(); iter != list.end(); iter++ )
315     {
316         const ASF::Picture asfPicture = (*iter).toPicture();
317         const ByteVector picture = asfPicture.picture();
318         const char *psz_mime = asfPicture.mimeType().toCString();
319         const char *p_data = picture.data();
320         const unsigned i_data = picture.size();
321         char *psz_name;
322         input_attachment_t *p_attachment;
323
324         if( asfPicture.description().size() > 0 )
325             psz_name = strdup( asfPicture.description().toCString( true ) );
326         else
327         {
328             if( asprintf( &psz_name, "%i", asfPicture.type() ) == -1 )
329                 psz_name = NULL;
330         }
331
332         if( unlikely(psz_name == NULL) )
333             continue;
334
335         msg_Dbg( p_demux_meta, "Found embedded art: %s (%s) is %u bytes",
336                  psz_name, psz_mime, i_data );
337
338         p_attachment = vlc_input_attachment_New( psz_name, psz_mime,
339                                 psz_name, p_data, i_data );
340         if( p_attachment )
341             TAB_APPEND_CAST( (input_attachment_t**),
342                              p_demux_meta->i_attachments, p_demux_meta->attachments,
343                              p_attachment );
344         char *psz_url;
345         if( asprintf( &psz_url, "attachment://%s", psz_name ) != -1 )
346         {
347             vlc_meta_SetArtURL( p_meta, psz_url );
348             free( psz_url );
349         }
350         free( psz_name );
351     }
352 }
353
354
355 /**
356  * Read meta information from id3v2 tags
357  * @param tag: the id3v2 tag
358  * @param p_demux_meta: the demuxer meta
359  * @param p_meta: the meta
360  */
361 static void ReadMetaFromId3v2( ID3v2::Tag* tag, demux_meta_t* p_demux_meta, vlc_meta_t* p_meta )
362 {
363     // Get the unique file identifier
364     ID3v2::FrameList list = tag->frameListMap()["UFID"];
365     ID3v2::FrameList::Iterator iter;
366     for( iter = list.begin(); iter != list.end(); iter++ )
367     {
368         ID3v2::UniqueFileIdentifierFrame* p_ufid =
369                 dynamic_cast<ID3v2::UniqueFileIdentifierFrame*>(*iter);
370         if( !p_ufid )
371             continue;
372         const char *owner = p_ufid->owner().toCString();
373         if (!strcmp( owner, "http://musicbrainz.org" ))
374         {
375             /* ID3v2 UFID contains up to 64 bytes binary data
376              * but in our case it will be a '\0'
377              * terminated string */
378             char psz_ufid[64];
379             int max_size = __MIN( p_ufid->identifier().size(), 63);
380             strncpy( psz_ufid, p_ufid->identifier().data(), max_size );
381             psz_ufid[max_size] = '\0';
382             vlc_meta_SetTrackID( p_meta, psz_ufid );
383         }
384     }
385
386     // Get the use text
387     list = tag->frameListMap()["TXXX"];
388     for( iter = list.begin(); iter != list.end(); iter++ )
389     {
390         ID3v2::UserTextIdentificationFrame* p_txxx =
391                 dynamic_cast<ID3v2::UserTextIdentificationFrame*>(*iter);
392         if( !p_txxx )
393             continue;
394         if( !strcmp( p_txxx->description().toCString( true ), "TRACKTOTAL" ) )
395         {
396             vlc_meta_Set( p_meta, vlc_meta_TrackTotal, p_txxx->fieldList().back().toCString( true ) );
397             continue;
398         }
399         if( !strcmp( p_txxx->description().toCString( true ), "MusicBrainz Album Id" ) )
400         {
401             vlc_meta_AddExtra( p_meta, VLC_META_EXTRA_MB_ALBUMID, p_txxx->fieldList().back().toCString( true ) );
402             continue;
403         }
404         vlc_meta_AddExtra( p_meta, p_txxx->description().toCString( true ),
405                            p_txxx->fieldList().back().toCString( true ) );
406     }
407
408     // Get some more information
409 #define SET( tagName, metaName )                                               \
410     list = tag->frameListMap()[tagName];                                       \
411     if( !list.isEmpty() )                                                      \
412         vlc_meta_Set##metaName( p_meta,                                        \
413                                 (*list.begin())->toString().toCString( true ) );
414
415     SET( "TCOP", Copyright );
416     SET( "TENC", EncodedBy );
417     SET( "TLAN", Language );
418     SET( "TPUB", Publisher );
419     SET( "TPE2", AlbumArtist );
420
421 #undef SET
422
423     /* */
424     list = tag->frameListMap()["TRCK"];
425     if( !list.isEmpty() )
426     {
427         ExtractCoupleNumberValues( p_meta, (*list.begin())->toString().toCString( true ),
428                 vlc_meta_TrackNumber, vlc_meta_TrackTotal );
429     }
430
431     /* */
432     list = tag->frameListMap()["TPOS"];
433     if( !list.isEmpty() )
434     {
435         ExtractCoupleNumberValues( p_meta, (*list.begin())->toString().toCString( true ),
436                 vlc_meta_DiscNumber, vlc_meta_DiscTotal );
437     }
438
439     /* Preferred type of image
440      * The 21 types are defined in id3v2 standard:
441      * http://www.id3.org/id3v2.4.0-frames */
442     static const int pi_cover_score[] = {
443         0,  /* Other */
444         5,  /* 32x32 PNG image that should be used as the file icon */
445         4,  /* File icon of a different size or format. */
446         20, /* Front cover image of the album. */
447         19, /* Back cover image of the album. */
448         13, /* Inside leaflet page of the album. */
449         18, /* Image from the album itself. */
450         17, /* Picture of the lead artist or soloist. */
451         16, /* Picture of the artist or performer. */
452         14, /* Picture of the conductor. */
453         15, /* Picture of the band or orchestra. */
454         9,  /* Picture of the composer. */
455         8,  /* Picture of the lyricist or text writer. */
456         7,  /* Picture of the recording location or studio. */
457         10, /* Picture of the artists during recording. */
458         11, /* Picture of the artists during performance. */
459         6,  /* Picture from a movie or video related to the track. */
460         1,  /* Picture of a large, coloured fish. */
461         12, /* Illustration related to the track. */
462         3,  /* Logo of the band or performer. */
463         2   /* Logo of the publisher (record company). */
464     };
465     #define PI_COVER_SCORE_SIZE (sizeof (pi_cover_score) / sizeof (pi_cover_score[0]))
466     int i_score = -1;
467
468     // Try now to get embedded art
469     list = tag->frameListMap()[ "APIC" ];
470     if( list.isEmpty() )
471         return;
472
473     for( iter = list.begin(); iter != list.end(); iter++ )
474     {
475         ID3v2::AttachedPictureFrame* p_apic =
476             dynamic_cast<ID3v2::AttachedPictureFrame*>(*iter);
477         if( !p_apic )
478             continue;
479         input_attachment_t *p_attachment;
480
481         const char *psz_mime;
482         char *psz_name, *psz_description;
483
484         // Get the mime and description of the image.
485         // If the description is empty, take the type as a description
486         psz_mime = p_apic->mimeType().toCString( true );
487         if( p_apic->description().size() > 0 )
488             psz_description = strdup( p_apic->description().toCString( true ) );
489         else
490         {
491             if( asprintf( &psz_description, "%i", p_apic->type() ) == -1 )
492                 psz_description = NULL;
493         }
494
495         if( !psz_description )
496             continue;
497         psz_name = psz_description;
498
499         /* some old iTunes version not only sets incorrectly the mime type
500          * or the description of the image,
501          * but also embeds incorrectly the image.
502          * Recent versions seem to behave correctly */
503         if( !strncmp( psz_mime, "PNG", 3 ) ||
504             !strncmp( psz_name, "\xC2\x89PNG", 5 ) )
505         {
506             msg_Warn( p_demux_meta, "Invalid picture embedded by broken iTunes version" );
507             free( psz_description );
508             continue;
509         }
510
511         const ByteVector picture = p_apic->picture();
512         const char *p_data = picture.data();
513         const unsigned i_data = picture.size();
514
515         msg_Dbg( p_demux_meta, "Found embedded art: %s (%s) is %u bytes",
516                  psz_name, psz_mime, i_data );
517
518         p_attachment = vlc_input_attachment_New( psz_name, psz_mime,
519                                 psz_description, p_data, i_data );
520         if( !p_attachment )
521         {
522             free( psz_description );
523             continue;
524         }
525         TAB_APPEND_CAST( (input_attachment_t**),
526                          p_demux_meta->i_attachments, p_demux_meta->attachments,
527                          p_attachment );
528         free( psz_description );
529
530         unsigned i_pic_type = p_apic->type();
531         if( i_pic_type >= PI_COVER_SCORE_SIZE )
532             i_pic_type = 0; // Defaults to "Other"
533
534         if( pi_cover_score[i_pic_type] > i_score )
535         {
536             i_score = pi_cover_score[i_pic_type];
537             char *psz_url;
538             if( asprintf( &psz_url, "attachment://%s",
539                           p_attachment->psz_name ) == -1 )
540                 continue;
541             vlc_meta_SetArtURL( p_meta, psz_url );
542             free( psz_url );
543         }
544     }
545 }
546
547
548 /**
549  * Read the meta information from XiphComments
550  * @param tag: the Xiph Comment
551  * @param p_demux_meta: the demuxer meta
552  * @param p_meta: the meta
553  */
554 static void ReadMetaFromXiph( Ogg::XiphComment* tag, demux_meta_t* p_demux_meta, vlc_meta_t* p_meta )
555 {
556     StringList list;
557     bool hasTrackTotal = false;
558 #define SET( keyName, metaName )                                               \
559     list = tag->fieldListMap()[keyName];                                       \
560     if( !list.isEmpty() )                                                      \
561         vlc_meta_Set##metaName( p_meta, (*list.begin()).toCString( true ) );
562
563 #define SET_EXTRA( keyName, metaName ) \
564     list = tag->fieldListMap()[keyName]; \
565     if( !list.isEmpty() ) \
566         vlc_meta_AddExtra( p_meta, keyName, (*list.begin()).toCString( true ) );
567
568     SET( "COPYRIGHT", Copyright );
569     SET( "ORGANIZATION", Publisher );
570     SET( "DATE", Date );
571     SET( "ENCODER", EncodedBy );
572     SET( "RATING", Rating );
573     SET( "LANGUAGE", Language );
574     SET( "MUSICBRAINZ_TRACKID", TrackID );
575     SET( "ALBUMARTIST", AlbumArtist );
576     SET( "DISCNUMBER", DiscNumber );
577
578     SET_EXTRA( "MUSICBRAINZ_ALBUMID", VLC_META_EXTRA_MB_ALBUMID );
579 #undef SET
580 #undef SET_EXTRA
581
582     list = tag->fieldListMap()["TRACKNUMBER"];
583     if( !list.isEmpty() )
584     {
585         int i_values = ExtractCoupleNumberValues( p_meta, (*list.begin()).toCString( true ),
586                  vlc_meta_TrackNumber, vlc_meta_TrackTotal );
587         hasTrackTotal = i_values == 2;
588     }
589     if( !hasTrackTotal )
590     {
591         list = tag->fieldListMap()["TRACKTOTAL"];
592         if( list.isEmpty() )
593             list = tag->fieldListMap()["TOTALTRACKS"];
594         if( !list.isEmpty() )
595             vlc_meta_SetTrackTotal( p_meta, (*list.begin()).toCString( true ) );
596     }
597
598     // Try now to get embedded art
599     StringList mime_list = tag->fieldListMap()[ "COVERARTMIME" ];
600     StringList art_list = tag->fieldListMap()[ "COVERART" ];
601
602     input_attachment_t *p_attachment;
603
604     if( mime_list.size() != 0 && art_list.size() != 0 )
605     {
606         // We get only the first covert art
607         if( mime_list.size() > 1 || art_list.size() > 1 )
608             msg_Warn( p_demux_meta, "Found %i embedded arts, so using only the first one",
609                     art_list.size() );
610
611         const char* psz_name = "cover";
612         const char* psz_mime = mime_list[0].toCString(true);
613         const char* psz_description = "cover";
614
615         uint8_t *p_data;
616         int i_data = vlc_b64_decode_binary( &p_data, art_list[0].toCString(true) );
617
618         msg_Dbg( p_demux_meta, "Found embedded art: %s (%s) is %i bytes",
619                 psz_name, psz_mime, i_data );
620
621         p_attachment = vlc_input_attachment_New( psz_name, psz_mime,
622                 psz_description, p_data, i_data );
623         free( p_data );
624     }
625     else
626     {
627         art_list = tag->fieldListMap()[ "METADATA_BLOCK_PICTURE" ];
628         if( art_list.size() == 0 )
629             return;
630
631         uint8_t *p_data;
632         int i_cover_score;
633         int i_cover_idx;
634         int i_data = vlc_b64_decode_binary( &p_data, art_list[0].toCString(true) );
635         i_cover_score = i_cover_idx = 0;
636         /* TODO: Use i_cover_score / i_cover_idx to select the picture. */
637         p_attachment = ParseFlacPicture( p_data, i_data, 0,
638             &i_cover_score, &i_cover_idx );
639         free( p_data );
640     }
641
642     if (p_attachment) {
643         TAB_APPEND_CAST( (input_attachment_t**),
644                 p_demux_meta->i_attachments, p_demux_meta->attachments,
645                 p_attachment );
646
647         char *psz_url;
648         if( asprintf( &psz_url, "attachment://%s", p_attachment->psz_name ) != -1 ) {
649             vlc_meta_SetArtURL( p_meta, psz_url );
650             free( psz_url );
651         }
652     }
653 }
654
655 /**
656  * Read the meta information from mp4 specific tags
657  * @param tag: the mp4 tag
658  * @param p_demux_meta: the demuxer meta
659  * @param p_meta: the meta
660  */
661 static void ReadMetaFromMP4( MP4::Tag* tag, demux_meta_t *p_demux_meta, vlc_meta_t* p_meta )
662 {
663     MP4::Item list;
664 #define SET( keyName, metaName )                                                             \
665     if( tag->itemListMap().contains(keyName) )                                               \
666     {                                                                                        \
667         list = tag->itemListMap()[keyName];                                                  \
668         vlc_meta_Set##metaName( p_meta, list.toStringList().front().toCString( true ) );     \
669     }
670 #define SET_EXTRA( keyName, metaName )                                                   \
671     if( tag->itemListMap().contains(keyName) )                                  \
672     {                                                                                \
673         list = tag->itemListMap()[keyName];                                     \
674         vlc_meta_AddExtra( p_meta, metaName, list.toStringList().front().toCString( true ) ); \
675     }
676
677     SET("----:com.apple.iTunes:MusicBrainz Track Id", TrackID );
678     SET_EXTRA("----:com.apple.iTunes:MusicBrainz Album Id", VLC_META_EXTRA_MB_ALBUMID );
679
680 #undef SET
681 #undef SET_EXTRA
682
683     if( tag->itemListMap().contains("covr") )
684     {
685         MP4::CoverArtList list = tag->itemListMap()["covr"].toCoverArtList();
686         const char *psz_format = list[0].format() == MP4::CoverArt::PNG ? "image/png" : "image/jpeg";
687
688         msg_Dbg( p_demux_meta, "Found embedded art (%s) is %i bytes",
689                  psz_format, list[0].data().size() );
690
691         input_attachment_t *p_attachment =
692                 vlc_input_attachment_New( "cover", psz_format, "cover",
693                                           list[0].data().data(), list[0].data().size() );
694         if( p_attachment )
695         {
696             TAB_APPEND_CAST( (input_attachment_t**),
697                              p_demux_meta->i_attachments, p_demux_meta->attachments,
698                              p_attachment );
699             vlc_meta_SetArtURL( p_meta, "attachment://cover" );
700         }
701     }
702 }
703
704 /**
705  * Get the tags from the file using TagLib
706  * @param p_this: the demux object
707  * @return VLC_SUCCESS if the operation success
708  */
709 static int ReadMeta( vlc_object_t* p_this)
710 {
711     vlc_mutex_locker locker (&taglib_lock);
712     demux_meta_t*   p_demux_meta = (demux_meta_t *)p_this;
713     vlc_meta_t*     p_meta;
714     FileRef f;
715
716     p_demux_meta->p_meta = NULL;
717
718     char *psz_uri = input_item_GetURI( p_demux_meta->p_item );
719     if( unlikely(psz_uri == NULL) )
720         return VLC_ENOMEM;
721
722     char *psz_path = vlc_uri2path( psz_uri );
723     free( psz_uri );
724     if( psz_path == NULL )
725         return VLC_EGENERIC;
726
727     if( !b_extensions_registered )
728     {
729 #if TAGLIB_VERSION >= TAGLIB_SYNCDECODE_FIXED_VERSION
730         FileRef::addFileTypeResolver( &aacresolver );
731 #endif
732         FileRef::addFileTypeResolver( &m4vresolver );
733         b_extensions_registered = true;
734     }
735
736 #if defined(_WIN32)
737     wchar_t *wpath = ToWide( psz_path );
738     if( wpath == NULL )
739     {
740         free( psz_path );
741         return VLC_EGENERIC;
742     }
743     f = FileRef( wpath );
744     free( wpath );
745 #else
746     f = FileRef( psz_path );
747 #endif
748     free( psz_path );
749
750     if( f.isNull() )
751         return VLC_EGENERIC;
752     if( !f.tag() || f.tag()->isEmpty() )
753         return VLC_EGENERIC;
754
755     p_demux_meta->p_meta = p_meta = vlc_meta_New();
756     if( !p_meta )
757         return VLC_ENOMEM;
758
759
760     // Read the tags from the file
761     Tag* p_tag = f.tag();
762
763 #define SET( tag, meta )                                                       \
764     if( !p_tag->tag().isNull() && !p_tag->tag().isEmpty() )                    \
765         vlc_meta_Set##meta( p_meta, p_tag->tag().toCString(true) )
766 #define SETINT( tag, meta )                                                    \
767     if( p_tag->tag() )                                                         \
768     {                                                                          \
769         char psz_tmp[10];                                                      \
770         snprintf( psz_tmp, 10, "%d", p_tag->tag() );                           \
771         vlc_meta_Set##meta( p_meta, psz_tmp );                                 \
772     }
773
774     SET( title, Title );
775     SET( artist, Artist );
776     SET( album, Album );
777     SET( comment, Description );
778     SET( genre, Genre );
779     SETINT( year, Date );
780     SETINT( track, TrackNum );
781
782 #undef SETINT
783 #undef SET
784
785     TAB_INIT( p_demux_meta->i_attachments, p_demux_meta->attachments );
786
787     if( APE::File* ape = dynamic_cast<APE::File*>(f.file()) )
788     {
789         if( ape->APETag() )
790             ReadMetaFromAPE( ape->APETag(), p_demux_meta, p_meta );
791     }
792     else
793     if( ASF::File* asf = dynamic_cast<ASF::File*>(f.file()) )
794     {
795         if( asf->tag() )
796             ReadMetaFromASF( asf->tag(), p_demux_meta, p_meta );
797     }
798     else
799     if( FLAC::File* flac = dynamic_cast<FLAC::File*>(f.file()) )
800     {
801         if( flac->ID3v2Tag() )
802             ReadMetaFromId3v2( flac->ID3v2Tag(), p_demux_meta, p_meta );
803         else if( flac->xiphComment() )
804             ReadMetaFromXiph( flac->xiphComment(), p_demux_meta, p_meta );
805     }
806     else if( MP4::File *mp4 = dynamic_cast<MP4::File*>(f.file()) )
807     {
808         if( mp4->tag() )
809             ReadMetaFromMP4( mp4->tag(), p_demux_meta, p_meta );
810     }
811     else if( MPC::File* mpc = dynamic_cast<MPC::File*>(f.file()) )
812     {
813         if( mpc->APETag() )
814             ReadMetaFromAPE( mpc->APETag(), p_demux_meta, p_meta );
815     }
816     else if( MPEG::File* mpeg = dynamic_cast<MPEG::File*>(f.file()) )
817     {
818         if( mpeg->APETag() )
819             ReadMetaFromAPE( mpeg->APETag(), p_demux_meta, p_meta );
820         if( mpeg->ID3v2Tag() )
821             ReadMetaFromId3v2( mpeg->ID3v2Tag(), p_demux_meta, p_meta );
822     }
823     else if( dynamic_cast<Ogg::File*>(f.file()) )
824     {
825         if( Ogg::FLAC::File* ogg_flac = dynamic_cast<Ogg::FLAC::File*>(f.file()))
826             ReadMetaFromXiph( ogg_flac->tag(), p_demux_meta, p_meta );
827         else if( Ogg::Speex::File* ogg_speex = dynamic_cast<Ogg::Speex::File*>(f.file()) )
828             ReadMetaFromXiph( ogg_speex->tag(), p_demux_meta, p_meta );
829         else if( Ogg::Vorbis::File* ogg_vorbis = dynamic_cast<Ogg::Vorbis::File*>(f.file()) )
830             ReadMetaFromXiph( ogg_vorbis->tag(), p_demux_meta, p_meta );
831 #if defined(TAGLIB_OPUSFILE_H)
832         else if( Ogg::Opus::File* ogg_opus = dynamic_cast<Ogg::Opus::File*>(f.file()) )
833             ReadMetaFromXiph( ogg_opus->tag(), p_demux_meta, p_meta );
834 #endif
835     }
836     else if( dynamic_cast<RIFF::File*>(f.file()) )
837     {
838         if( RIFF::AIFF::File* riff_aiff = dynamic_cast<RIFF::AIFF::File*>(f.file()) )
839             ReadMetaFromId3v2( riff_aiff->tag(), p_demux_meta, p_meta );
840         else if( RIFF::WAV::File* riff_wav = dynamic_cast<RIFF::WAV::File*>(f.file()) )
841             ReadMetaFromId3v2( riff_wav->tag(), p_demux_meta, p_meta );
842     }
843     else if( TrueAudio::File* trueaudio = dynamic_cast<TrueAudio::File*>(f.file()) )
844     {
845         if( trueaudio->ID3v2Tag() )
846             ReadMetaFromId3v2( trueaudio->ID3v2Tag(), p_demux_meta, p_meta );
847     }
848     else if( WavPack::File* wavpack = dynamic_cast<WavPack::File*>(f.file()) )
849     {
850         if( wavpack->APETag() )
851             ReadMetaFromAPE( wavpack->APETag(), p_demux_meta, p_meta );
852     }
853
854     return VLC_SUCCESS;
855 }
856
857
858 /**
859  * Write meta information to APE tags
860  * @param tag: the APE tag
861  * @param p_item: the input item
862  */
863 static void WriteMetaToAPE( APE::Tag* tag, input_item_t* p_item )
864 {
865     char* psz_meta;
866 #define WRITE( metaName, keyName )                      \
867     psz_meta = input_item_Get##metaName( p_item );      \
868     if( psz_meta )                                      \
869     {                                                   \
870         String key( keyName, String::UTF8 );            \
871         String value( psz_meta, String::UTF8 );         \
872         tag->addValue( key, value, true );              \
873     }                                                   \
874     free( psz_meta );
875
876     WRITE( Copyright, "COPYRIGHT" );
877     WRITE( Language, "LANGUAGE" );
878     WRITE( Publisher, "PUBLISHER" );
879     WRITE( TrackID, "MUSICBRAINZ_TRACKID" );
880 #undef WRITE
881 }
882
883
884 /**
885  * Write meta information to id3v2 tags
886  * @param tag: the id3v2 tag
887  * @param p_input: the input item
888  */
889 static void WriteMetaToId3v2( ID3v2::Tag* tag, input_item_t* p_item )
890 {
891     char* psz_meta;
892 #define WRITE( metaName, tagName )                                            \
893     psz_meta = input_item_Get##metaName( p_item );                            \
894     if( psz_meta )                                                            \
895     {                                                                         \
896         ByteVector p_byte( tagName, 4 );                                      \
897         tag->removeFrames( p_byte );                                         \
898         ID3v2::TextIdentificationFrame* p_frame =                             \
899             new ID3v2::TextIdentificationFrame( p_byte, String::UTF8 );       \
900         p_frame->setText( psz_meta );                                         \
901         tag->addFrame( p_frame );                                             \
902     }                                                                         \
903     free( psz_meta );
904
905     WRITE( Copyright, "TCOP" );
906     WRITE( EncodedBy, "TENC" );
907     WRITE( Language,  "TLAN" );
908     WRITE( Publisher, "TPUB" );
909
910 #undef WRITE
911     /* Known TXXX frames */
912     ID3v2::FrameList list = tag->frameListMap()["TXXX"];
913
914 #define WRITETXXX( metaName, txxName )\
915     psz_meta = input_item_Get##metaName( p_item );                                       \
916     if ( psz_meta )                                                                      \
917     {                                                                                    \
918         ID3v2::UserTextIdentificationFrame *p_txxx;                                      \
919         for( ID3v2::FrameList::Iterator iter = list.begin(); iter != list.end(); iter++ )\
920         {                                                                                \
921             p_txxx = dynamic_cast<ID3v2::UserTextIdentificationFrame*>(*iter);           \
922             if( !p_txxx )                                                                \
923                 continue;                                                                \
924             if( !strcmp( p_txxx->description().toCString( true ), txxName ) )            \
925             {                                                                            \
926                 p_txxx->setText( psz_meta );                                             \
927                 FREENULL( psz_meta );                                                    \
928                 break;                                                                   \
929             }                                                                            \
930         }                                                                                \
931         if( psz_meta ) /* not found in existing custom fields */                         \
932         {                                                                                \
933             ByteVector p_byte( "TXXX", 4 );                                              \
934             p_txxx = new ID3v2::UserTextIdentificationFrame( p_byte );                   \
935             p_txxx->setDescription( txxName );                                           \
936             p_txxx->setText( psz_meta );                                                 \
937             free( psz_meta );                                                            \
938             tag->addFrame( p_txxx );                                                     \
939         }                                                                                \
940     }
941
942     WRITETXXX( TrackTotal, "TRACKTOTAL" );
943
944 #undef WRITETXXX
945
946     /* Write album art */
947     char *psz_url = input_item_GetArtworkURL( p_item );
948     if( psz_url == NULL )
949         return;
950
951     char *psz_path = vlc_uri2path( psz_url );
952     free( psz_url );
953     if( psz_path == NULL )
954         return;
955
956     const char *psz_mime = vlc_mime_Ext2Mime( psz_path );
957
958     FILE *p_file = vlc_fopen( psz_path, "rb" );
959     if( p_file == NULL )
960     {
961         free( psz_path );
962         return;
963     }
964
965     struct stat st;
966     if( vlc_stat( psz_path, &st ) == -1 )
967     {
968         free( psz_path );
969         fclose( p_file );
970         return;
971     }
972     off_t file_size = st.st_size;
973
974     free( psz_path );
975
976     /* Limit picture size to 10MiB */
977     if( file_size > 10485760 )
978     {
979       fclose( p_file );
980       return;
981     }
982
983     char *p_buffer = new (std::nothrow) char[file_size];
984     if( p_buffer == NULL )
985     {
986         fclose( p_file );
987         return;
988     }
989
990     if( fread( p_buffer, 1, file_size, p_file ) != (unsigned)file_size )
991     {
992         fclose( p_file );
993         delete[] p_buffer;
994         return;
995     }
996     fclose( p_file );
997
998     ByteVector data( p_buffer, file_size );
999     delete[] p_buffer;
1000
1001     ID3v2::FrameList frames = tag->frameList( "APIC" );
1002     ID3v2::AttachedPictureFrame *frame = NULL;
1003     if( frames.isEmpty() )
1004     {
1005         frame = new TagLib::ID3v2::AttachedPictureFrame;
1006         tag->addFrame( frame );
1007     }
1008     else
1009     {
1010         frame = static_cast<ID3v2::AttachedPictureFrame *>( frames.back() );
1011     }
1012
1013     frame->setPicture( data );
1014     frame->setMimeType( psz_mime );
1015 }
1016
1017
1018 /**
1019  * Write the meta information to XiphComments
1020  * @param tag: the Xiph Comment
1021  * @param p_input: the input item
1022  */
1023 static void WriteMetaToXiph( Ogg::XiphComment* tag, input_item_t* p_item )
1024 {
1025     char* psz_meta;
1026 #define WRITE( metaName, keyName )                      \
1027     psz_meta = input_item_Get##metaName( p_item );      \
1028     if( psz_meta )                                      \
1029     {                                                   \
1030         String key( keyName, String::UTF8 );            \
1031         String value( psz_meta, String::UTF8 );         \
1032         tag->addField( key, value, true );              \
1033     }                                                   \
1034     free( psz_meta );
1035
1036     WRITE( TrackNum, "TRACKNUMBER" );
1037     WRITE( TrackTotal, "TRACKTOTAL" );
1038     WRITE( Copyright, "COPYRIGHT" );
1039     WRITE( Publisher, "ORGANIZATION" );
1040     WRITE( Date, "DATE" );
1041     WRITE( EncodedBy, "ENCODER" );
1042     WRITE( Rating, "RATING" );
1043     WRITE( Language, "LANGUAGE" );
1044     WRITE( TrackID, "MUSICBRAINZ_TRACKID" );
1045 #undef WRITE
1046 }
1047
1048
1049 /**
1050  * Set the tags to the file using TagLib
1051  * @param p_this: the demux object
1052  * @return VLC_SUCCESS if the operation success
1053  */
1054
1055 static int WriteMeta( vlc_object_t *p_this )
1056 {
1057     vlc_mutex_locker locker (&taglib_lock);
1058     meta_export_t *p_export = (meta_export_t *)p_this;
1059     input_item_t *p_item = p_export->p_item;
1060     FileRef f;
1061
1062     if( !p_item )
1063     {
1064         msg_Err( p_this, "Can't save meta data of an empty input" );
1065         return VLC_EGENERIC;
1066     }
1067
1068 #if defined(_WIN32)
1069     wchar_t *wpath = ToWide( p_export->psz_file );
1070     if( wpath == NULL )
1071         return VLC_EGENERIC;
1072     f = FileRef( wpath, false );
1073     free( wpath );
1074 #else
1075     f = FileRef( p_export->psz_file, false );
1076 #endif
1077
1078     if( f.isNull() || !f.tag() || f.file()->readOnly() )
1079     {
1080         msg_Err( p_this, "File %s can't be opened for tag writing",
1081                  p_export->psz_file );
1082         return VLC_EGENERIC;
1083     }
1084
1085     msg_Dbg( p_this, "Writing metadata for %s", p_export->psz_file );
1086
1087     Tag *p_tag = f.tag();
1088
1089     char *psz_meta;
1090
1091 #define SET( a, b )                                             \
1092     psz_meta = input_item_Get ## a( p_item );                   \
1093     if( psz_meta )                                              \
1094     {                                                           \
1095         String tmp( psz_meta, String::UTF8 );                   \
1096         p_tag->set##b( tmp );                                   \
1097     }                                                           \
1098     free( psz_meta );
1099
1100     // Saving all common fields
1101     // If the title is empty, use the name
1102     SET( TitleFbName, Title );
1103     SET( Artist, Artist );
1104     SET( Album, Album );
1105     SET( Description, Comment );
1106     SET( Genre, Genre );
1107
1108 #undef SET
1109
1110     psz_meta = input_item_GetDate( p_item );
1111     if( !EMPTY_STR(psz_meta) ) p_tag->setYear( atoi( psz_meta ) );
1112     else p_tag->setYear( 0 );
1113     free( psz_meta );
1114
1115     psz_meta = input_item_GetTrackNum( p_item );
1116     if( !EMPTY_STR(psz_meta) ) p_tag->setTrack( atoi( psz_meta ) );
1117     else p_tag->setTrack( 0 );
1118     free( psz_meta );
1119
1120
1121     // Try now to write special tags
1122     if( APE::File* ape = dynamic_cast<APE::File*>(f.file()) )
1123     {
1124         if( ape->APETag() )
1125             WriteMetaToAPE( ape->APETag(), p_item );
1126     }
1127     else
1128     if( FLAC::File* flac = dynamic_cast<FLAC::File*>(f.file()) )
1129     {
1130         if( flac->ID3v2Tag() )
1131             WriteMetaToId3v2( flac->ID3v2Tag(), p_item );
1132         else if( flac->xiphComment() )
1133             WriteMetaToXiph( flac->xiphComment(), p_item );
1134     }
1135     else if( MPC::File* mpc = dynamic_cast<MPC::File*>(f.file()) )
1136     {
1137         if( mpc->APETag() )
1138             WriteMetaToAPE( mpc->APETag(), p_item );
1139     }
1140     else if( MPEG::File* mpeg = dynamic_cast<MPEG::File*>(f.file()) )
1141     {
1142         if( mpeg->ID3v2Tag() )
1143             WriteMetaToId3v2( mpeg->ID3v2Tag(), p_item );
1144         else if( mpeg->APETag() )
1145             WriteMetaToAPE( mpeg->APETag(), p_item );
1146     }
1147     else if( dynamic_cast<Ogg::File*>(f.file()) )
1148     {
1149         if( Ogg::FLAC::File* ogg_flac = dynamic_cast<Ogg::FLAC::File*>(f.file()))
1150             WriteMetaToXiph( ogg_flac->tag(), p_item );
1151         else if( Ogg::Speex::File* ogg_speex = dynamic_cast<Ogg::Speex::File*>(f.file()) )
1152             WriteMetaToXiph( ogg_speex->tag(), p_item );
1153         else if( Ogg::Vorbis::File* ogg_vorbis = dynamic_cast<Ogg::Vorbis::File*>(f.file()) )
1154             WriteMetaToXiph( ogg_vorbis->tag(), p_item );
1155 #if defined(TAGLIB_OPUSFILE_H)
1156         else if( Ogg::Opus::File* ogg_opus = dynamic_cast<Ogg::Opus::File*>(f.file()) )
1157             WriteMetaToXiph( ogg_opus->tag(), p_item );
1158 #endif
1159     }
1160     else if( dynamic_cast<RIFF::File*>(f.file()) )
1161     {
1162         if( RIFF::AIFF::File* riff_aiff = dynamic_cast<RIFF::AIFF::File*>(f.file()) )
1163             WriteMetaToId3v2( riff_aiff->tag(), p_item );
1164         else if( RIFF::WAV::File* riff_wav = dynamic_cast<RIFF::WAV::File*>(f.file()) )
1165             WriteMetaToId3v2( riff_wav->tag(), p_item );
1166     }
1167     else if( TrueAudio::File* trueaudio = dynamic_cast<TrueAudio::File*>(f.file()) )
1168     {
1169         if( trueaudio->ID3v2Tag() )
1170             WriteMetaToId3v2( trueaudio->ID3v2Tag(), p_item );
1171     }
1172     else if( WavPack::File* wavpack = dynamic_cast<WavPack::File*>(f.file()) )
1173     {
1174         if( wavpack->APETag() )
1175             WriteMetaToAPE( wavpack->APETag(), p_item );
1176     }
1177
1178     // Save the meta data
1179     f.save();
1180
1181     return VLC_SUCCESS;
1182 }