]> git.sesse.net Git - vlc/blob - modules/meta_engine/taglib.cpp
vout/opengl: fixed compilation for OS X
[vlc] / modules / meta_engine / taglib.cpp
1 /*****************************************************************************
2  * taglib.cpp: Taglib tag parser/writer
3  *****************************************************************************
4  * Copyright (C) 2003-2011 the VideoLAN team
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
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 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 General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, 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
36 #ifdef WIN32
37 # include <vlc_charset.h>
38 # include <io.h>
39 #else
40 # include <unistd.h>
41 #endif
42
43
44 // Taglib headers
45 #include <taglib.h>
46 #define VERSION_INT(a, b, c) ((a)<<16 | (b)<<8 | (c))
47 #define TAGLIB_VERSION VERSION_INT(TAGLIB_MAJOR_VERSION, \
48                                    TAGLIB_MINOR_VERSION, \
49                                    TAGLIB_PATCH_VERSION)
50
51 #include <fileref.h>
52 #include <tag.h>
53 #include <tbytevector.h>
54
55 #if TAGLIB_VERSION >= VERSION_INT(1,7,0)
56 # define TAGLIB_HAVE_APEFILE_H
57 # include <apefile.h>
58 # ifdef TAGLIB_WITH_ASF                     // ASF pictures comes with v1.7.0
59 #  define TAGLIB_HAVE_ASFPICTURE_H
60 #  include <asffile.h>
61 # endif
62 #endif
63
64 #include <apetag.h>
65 #include <flacfile.h>
66 #include <mpcfile.h>
67 #include <mpegfile.h>
68 #include <oggfile.h>
69 #include <oggflacfile.h>
70 #include "../demux/vorbis.h"
71
72 #if TAGLIB_VERSION >= VERSION_INT(1,6,0)
73 # define TAGLIB_HAVE_AIFF_WAV_H
74 # include <aifffile.h>
75 # include <wavfile.h>
76 #else
77 # include <id3v2tag.h>
78 #endif
79
80 #if TAGLIB_VERSION >= VERSION_INT(1,6,1) && defined(TAGLIB_WITH_MP4)
81 # define TAGLIB_HAVE_MP4COVERTART_H
82 # include <mp4file.h>
83 #endif
84
85 #include <speexfile.h>
86 #include <trueaudiofile.h>
87 #include <vorbisfile.h>
88 #include <wavpackfile.h>
89
90 #include <attachedpictureframe.h>
91 #include <textidentificationframe.h>
92 #include <uniquefileidentifierframe.h>
93
94 // taglib is not thread safe
95 static vlc_mutex_t taglib_lock = VLC_STATIC_MUTEX;
96
97 // Local functions
98 static int ReadMeta    ( vlc_object_t * );
99 static int WriteMeta   ( vlc_object_t * );
100
101 vlc_module_begin ()
102     set_capability( "meta reader", 1000 )
103     set_callbacks( ReadMeta, NULL )
104     add_submodule ()
105         set_capability( "meta writer", 50 )
106         set_callbacks( WriteMeta, NULL )
107 vlc_module_end ()
108
109 using namespace TagLib;
110
111 static void ExtractTrackNumberValues( vlc_meta_t* p_meta, const char *psz_value )
112 {
113     unsigned int i_trknum, i_trktot;
114     if( sscanf( psz_value, "%u/%u", &i_trknum, &i_trktot ) == 2 )
115     {
116         char psz_trck[11];
117         snprintf( psz_trck, sizeof( psz_trck ), "%u", i_trknum );
118         vlc_meta_SetTrackNum( p_meta, psz_trck );
119         snprintf( psz_trck, sizeof( psz_trck ), "%u", i_trktot );
120         vlc_meta_Set( p_meta, vlc_meta_TrackTotal, psz_trck );
121     }
122 }
123
124 /**
125  * Read meta information from APE tags
126  * @param tag: the APE tag
127  * @param p_demux_meta: the demuxer meta
128  * @param p_meta: the meta
129  */
130 static void ReadMetaFromAPE( APE::Tag* tag, demux_meta_t*, vlc_meta_t* p_meta )
131 {
132     APE::Item item;
133 #define SET( keyName, metaName ) \
134     item = tag->itemListMap()[keyName]; \
135     if( !item.isEmpty() ) vlc_meta_Set##metaName( p_meta, item.toString().toCString( true ) ); \
136
137     SET( "COPYRIGHT", Copyright );
138     SET( "LANGUAGE", Language );
139     SET( "PUBLISHER", Publisher );
140
141 #undef SET
142
143     /* */
144     item = tag->itemListMap()["TRACK"];
145     if( !item.isEmpty() )
146     {
147         ExtractTrackNumberValues( p_meta, item.toString().toCString( true ) );
148     }
149 }
150
151
152 #ifdef TAGLIB_HAVE_ASFPICTURE_H
153 /**
154  * Read meta information from APE tags
155  * @param tag: the APE tag
156  * @param p_demux_meta: the demuxer meta
157  * @param p_meta: the meta
158  */
159 static void ReadMetaFromASF( ASF::Tag* tag, demux_meta_t* p_demux_meta, vlc_meta_t* p_meta )
160 {
161     // List the pictures
162     ASF::AttributeList list = tag->attributeListMap()["WM/Picture"];
163     ASF::AttributeList::Iterator iter;
164     for( iter = list.begin(); iter != list.end(); iter++ )
165     {
166         const ASF::Picture asfPicture = (*iter).toPicture();
167         const ByteVector picture = asfPicture.picture();
168         const char *psz_mime = asfPicture.mimeType().toCString();
169         const char *p_data = picture.data();
170         const unsigned i_data = picture.size();
171         char *psz_name;
172         input_attachment_t *p_attachment;
173
174         if( asfPicture.description().size() > 0 )
175             psz_name = strdup( asfPicture.description().toCString( true ) );
176         else
177         {
178             if( asprintf( &psz_name, "%i", asfPicture.type() ) == -1 )
179                 continue;
180         }
181
182         msg_Dbg( p_demux_meta, "Found embedded art: %s (%s) is %u bytes",
183                  psz_name, psz_mime, i_data );
184
185         p_attachment = vlc_input_attachment_New( psz_name, psz_mime,
186                                 psz_name, p_data, i_data );
187         if( p_attachment )
188             TAB_APPEND_CAST( (input_attachment_t**),
189                              p_demux_meta->i_attachments, p_demux_meta->attachments,
190                              p_attachment );
191         free( psz_name );
192
193         char *psz_url;
194         if( asprintf( &psz_url, "attachment://%s",
195                       p_attachment->psz_name ) == -1 )
196             continue;
197         vlc_meta_SetArtURL( p_meta, psz_url );
198         free( psz_url );
199     }
200 }
201 #endif
202
203
204 /**
205  * Read meta information from id3v2 tags
206  * @param tag: the id3v2 tag
207  * @param p_demux_meta: the demuxer meta
208  * @param p_meta: the meta
209  */
210 static void ReadMetaFromId3v2( ID3v2::Tag* tag, demux_meta_t* p_demux_meta, vlc_meta_t* p_meta )
211 {
212     // Get the unique file identifier
213     ID3v2::FrameList list = tag->frameListMap()["UFID"];
214     ID3v2::FrameList::Iterator iter;
215     for( iter = list.begin(); iter != list.end(); iter++ )
216     {
217         ID3v2::UniqueFileIdentifierFrame* p_ufid =
218                 dynamic_cast<ID3v2::UniqueFileIdentifierFrame*>(*iter);
219         if( !p_ufid )
220             continue;
221         const char *owner = p_ufid->owner().toCString();
222         if (!strcmp( owner, "http://musicbrainz.org" ))
223         {
224             /* ID3v2 UFID contains up to 64 bytes binary data
225              * but in our case it will be a '\0'
226              * terminated string */
227             char psz_ufid[64];
228             int max_size = __MIN( p_ufid->identifier().size(), 63);
229             strncpy( psz_ufid, p_ufid->identifier().data(), max_size );
230             psz_ufid[max_size] = '\0';
231             vlc_meta_SetTrackID( p_meta, psz_ufid );
232         }
233     }
234
235     // Get the use text
236     list = tag->frameListMap()["TXXX"];
237     for( iter = list.begin(); iter != list.end(); iter++ )
238     {
239         ID3v2::UserTextIdentificationFrame* p_txxx =
240                 dynamic_cast<ID3v2::UserTextIdentificationFrame*>(*iter);
241         if( !p_txxx )
242             continue;
243         if( !strcmp( p_txxx->description().toCString( true ), "TRACKTOTAL" ) )
244         {
245             vlc_meta_Set( p_meta, vlc_meta_TrackTotal, p_txxx->fieldList().back().toCString( true ) );
246             continue;
247         }
248         vlc_meta_AddExtra( p_meta, p_txxx->description().toCString( true ),
249                            p_txxx->fieldList().back().toCString( true ) );
250     }
251
252     // Get some more information
253 #define SET( tagName, metaName )                                               \
254     list = tag->frameListMap()[tagName];                                       \
255     if( !list.isEmpty() )                                                      \
256         vlc_meta_Set##metaName( p_meta,                                        \
257                                 (*list.begin())->toString().toCString( true ) );
258
259     SET( "TCOP", Copyright );
260     SET( "TENC", EncodedBy );
261     SET( "TLAN", Language );
262     SET( "TPUB", Publisher );
263
264 #undef SET
265
266     /* */
267     list = tag->frameListMap()["TRCK"];
268     if( !list.isEmpty() )
269     {
270         ExtractTrackNumberValues( p_meta, (*list.begin())->toString().toCString( true ) );
271     }
272
273     /* Preferred type of image
274      * The 21 types are defined in id3v2 standard:
275      * http://www.id3.org/id3v2.4.0-frames */
276     static const int pi_cover_score[] = {
277         0,  /* Other */
278         5,  /* 32x32 PNG image that should be used as the file icon */
279         4,  /* File icon of a different size or format. */
280         20, /* Front cover image of the album. */
281         19, /* Back cover image of the album. */
282         13, /* Inside leaflet page of the album. */
283         18, /* Image from the album itself. */
284         17, /* Picture of the lead artist or soloist. */
285         16, /* Picture of the artist or performer. */
286         14, /* Picture of the conductor. */
287         15, /* Picture of the band or orchestra. */
288         9,  /* Picture of the composer. */
289         8,  /* Picture of the lyricist or text writer. */
290         7,  /* Picture of the recording location or studio. */
291         10, /* Picture of the artists during recording. */
292         11, /* Picture of the artists during performance. */
293         6,  /* Picture from a movie or video related to the track. */
294         1,  /* Picture of a large, coloured fish. */
295         12, /* Illustration related to the track. */
296         3,  /* Logo of the band or performer. */
297         2   /* Logo of the publisher (record company). */
298     };
299     #define PI_COVER_SCORE_SIZE (sizeof (pi_cover_score) / sizeof (pi_cover_score[0]))
300     int i_score = -1;
301
302     // Try now to get embedded art
303     list = tag->frameListMap()[ "APIC" ];
304     if( list.isEmpty() )
305         return;
306
307     TAB_INIT( p_demux_meta->i_attachments, p_demux_meta->attachments );
308     for( iter = list.begin(); iter != list.end(); iter++ )
309     {
310         ID3v2::AttachedPictureFrame* p_apic =
311             dynamic_cast<ID3v2::AttachedPictureFrame*>(*iter);
312         if( !p_apic )
313             continue;
314         input_attachment_t *p_attachment;
315
316         const char *psz_mime;
317         char *psz_name, *psz_description;
318
319         // Get the mime and description of the image.
320         // If the description is empty, take the type as a description
321         psz_mime = p_apic->mimeType().toCString( true );
322         if( p_apic->description().size() > 0 )
323             psz_description = strdup( p_apic->description().toCString( true ) );
324         else
325         {
326             if( asprintf( &psz_description, "%i", p_apic->type() ) == -1 )
327                 psz_description = NULL;
328         }
329
330         if( !psz_description )
331             continue;
332         psz_name = psz_description;
333
334         /* some old iTunes version not only sets incorrectly the mime type
335          * or the description of the image,
336          * but also embeds incorrectly the image.
337          * Recent versions seem to behave correctly */
338         if( !strncmp( psz_mime, "PNG", 3 ) ||
339             !strncmp( psz_name, "\xC2\x89PNG", 5 ) )
340         {
341             msg_Warn( p_demux_meta, "Invalid picture embedded by broken iTunes version" );
342             free( psz_description );
343             continue;
344         }
345
346         const ByteVector picture = p_apic->picture();
347         const char *p_data = picture.data();
348         const unsigned i_data = picture.size();
349
350         msg_Dbg( p_demux_meta, "Found embedded art: %s (%s) is %u bytes",
351                  psz_name, psz_mime, i_data );
352
353         p_attachment = vlc_input_attachment_New( psz_name, psz_mime,
354                                 psz_description, p_data, i_data );
355         if( p_attachment )
356             TAB_APPEND_CAST( (input_attachment_t**),
357                              p_demux_meta->i_attachments, p_demux_meta->attachments,
358                              p_attachment );
359         free( psz_description );
360
361         unsigned i_pic_type = p_apic->type();
362         if( i_pic_type >= PI_COVER_SCORE_SIZE )
363             i_pic_type = 0; // Defaults to "Other"
364
365         if( pi_cover_score[i_pic_type] > i_score )
366         {
367             i_score = pi_cover_score[i_pic_type];
368             char *psz_url;
369             if( asprintf( &psz_url, "attachment://%s",
370                           p_attachment->psz_name ) == -1 )
371                 continue;
372             vlc_meta_SetArtURL( p_meta, psz_url );
373             free( psz_url );
374         }
375     }
376 }
377
378
379 /**
380  * Read the meta information from XiphComments
381  * @param tag: the Xiph Comment
382  * @param p_demux_meta: the demuxer meta
383  * @param p_meta: the meta
384  */
385 static void ReadMetaFromXiph( Ogg::XiphComment* tag, demux_meta_t* p_demux_meta, vlc_meta_t* p_meta )
386 {
387     StringList list;
388 #define SET( keyName, metaName )                                               \
389     list = tag->fieldListMap()[keyName];                                       \
390     if( !list.isEmpty() )                                                      \
391         vlc_meta_Set##metaName( p_meta, (*list.begin()).toCString( true ) );
392
393     SET( "COPYRIGHT", Copyright );
394 #undef SET
395
396     // Try now to get embedded art
397     StringList mime_list = tag->fieldListMap()[ "COVERARTMIME" ];
398     StringList art_list = tag->fieldListMap()[ "COVERART" ];
399
400     input_attachment_t *p_attachment;
401
402     if( mime_list.size() != 0 && art_list.size() != 0 )
403     {
404         // We get only the first covert art
405         if( mime_list.size() > 1 || art_list.size() > 1 )
406             msg_Warn( p_demux_meta, "Found %i embedded arts, so using only the first one",
407                     art_list.size() );
408
409         const char* psz_name = "cover";
410         const char* psz_mime = mime_list[0].toCString(true);
411         const char* psz_description = "cover";
412
413         uint8_t *p_data;
414         int i_data = vlc_b64_decode_binary( &p_data, art_list[0].toCString(true) );
415
416         msg_Dbg( p_demux_meta, "Found embedded art: %s (%s) is %i bytes",
417                 psz_name, psz_mime, i_data );
418
419         p_attachment = vlc_input_attachment_New( psz_name, psz_mime,
420                 psz_description, p_data, i_data );
421         free( p_data );
422     }
423     else
424     {
425         art_list = tag->fieldListMap()[ "METADATA_BLOCK_PICTURE" ];
426         if( art_list.size() == 0 )
427             return;
428
429         uint8_t *p_data;
430         int type;
431         int i_data = vlc_b64_decode_binary( &p_data, art_list[0].toCString(true) );
432         p_attachment = ParseFlacPicture( p_data, i_data, 0, &type );
433     }
434
435     TAB_INIT( p_demux_meta->i_attachments, p_demux_meta->attachments );
436     TAB_APPEND_CAST( (input_attachment_t**),
437                      p_demux_meta->i_attachments, p_demux_meta->attachments,
438                      p_attachment );
439
440     char *psz_url;
441     if( asprintf( &psz_url, "attachment://%s", p_attachment->psz_name ) != -1 ) {
442         vlc_meta_SetArtURL( p_meta, psz_url );
443         free( psz_url );
444     }
445 }
446
447
448 #ifdef TAGLIB_HAVE_MP4COVERTART_H
449 /**
450  * Read the meta information from mp4 specific tags
451  * @param tag: the mp4 tag
452  * @param p_demux_meta: the demuxer meta
453  * @param p_meta: the meta
454  */
455 static void ReadMetaFromMP4( MP4::Tag* tag, demux_meta_t *p_demux_meta, vlc_meta_t* p_meta )
456 {
457     if( tag->itemListMap().contains("covr") )
458     {
459         MP4::CoverArtList list = tag->itemListMap()["covr"].toCoverArtList();
460         const char *psz_format = list[0].format() == MP4::CoverArt::PNG ? "image/png" : "image/jpeg";
461
462         msg_Dbg( p_demux_meta, "Found embedded art (%s) is %i bytes",
463                  psz_format, list[0].data().size() );
464
465         TAB_INIT( p_demux_meta->i_attachments, p_demux_meta->attachments );
466         input_attachment_t *p_attachment =
467                 vlc_input_attachment_New( "cover", psz_format, "cover",
468                                           list[0].data().data(), list[0].data().size() );
469         TAB_APPEND_CAST( (input_attachment_t**),
470                          p_demux_meta->i_attachments, p_demux_meta->attachments,
471                          p_attachment );
472         vlc_meta_SetArtURL( p_meta, "attachment://cover" );
473     }
474 }
475 #endif
476
477
478 /**
479  * Get the tags from the file using TagLib
480  * @param p_this: the demux object
481  * @return VLC_SUCCESS if the operation success
482  */
483 static int ReadMeta( vlc_object_t* p_this)
484 {
485     vlc_mutex_locker locker (&taglib_lock);
486     demux_meta_t*   p_demux_meta = (demux_meta_t *)p_this;
487     demux_t*        p_demux = p_demux_meta->p_demux;
488     vlc_meta_t*     p_meta;
489     FileRef f;
490
491     p_demux_meta->p_meta = NULL;
492     if( strcmp( p_demux->psz_access, "file" ) )
493         return VLC_EGENERIC;
494
495     char *psz_path = strdup( p_demux->psz_file );
496     if( !psz_path )
497         return VLC_ENOMEM;
498
499 #if defined(WIN32) || defined (UNDER_CE)
500     wchar_t *wpath = ToWide( psz_path );
501     if( wpath == NULL )
502     {
503         free( psz_path );
504         return VLC_EGENERIC;
505     }
506     f = FileRef( wpath );
507     free( wpath );
508 #else
509     f = FileRef( psz_path );
510 #endif
511     free( psz_path );
512
513     if( f.isNull() )
514         return VLC_EGENERIC;
515     if( !f.tag() || f.tag()->isEmpty() )
516         return VLC_EGENERIC;
517
518     p_demux_meta->p_meta = p_meta = vlc_meta_New();
519     if( !p_meta )
520         return VLC_ENOMEM;
521
522
523     // Read the tags from the file
524     Tag* p_tag = f.tag();
525
526 #define SET( tag, meta )                                                       \
527     if( !p_tag->tag().isNull() && !p_tag->tag().isEmpty() )                    \
528         vlc_meta_Set##meta( p_meta, p_tag->tag().toCString(true) )
529 #define SETINT( tag, meta )                                                    \
530     if( p_tag->tag() )                                                         \
531     {                                                                          \
532         char psz_tmp[10];                                                      \
533         snprintf( psz_tmp, 10, "%d", p_tag->tag() );                           \
534         vlc_meta_Set##meta( p_meta, psz_tmp );                                 \
535     }
536
537     SET( title, Title );
538     SET( artist, Artist );
539     SET( album, Album );
540     SET( comment, Description );
541     SET( genre, Genre );
542     SETINT( year, Date );
543     SETINT( track, TrackNum );
544
545 #undef SETINT
546 #undef SET
547
548
549     // Try now to read special tags
550 #ifdef TAGLIB_HAVE_APEFILE_H
551     if( APE::File* ape = dynamic_cast<APE::File*>(f.file()) )
552     {
553         if( ape->APETag() )
554             ReadMetaFromAPE( ape->APETag(), p_demux_meta, p_meta );
555     }
556     else
557 #endif
558 #ifdef TAGLIB_HAVE_ASFPICTURE_H
559     if( ASF::File* asf = dynamic_cast<ASF::File*>(f.file()) )
560     {
561         if( asf->tag() )
562             ReadMetaFromASF( asf->tag(), p_demux_meta, p_meta );
563     }
564     else
565 #endif
566     if( FLAC::File* flac = dynamic_cast<FLAC::File*>(f.file()) )
567     {
568         if( flac->ID3v2Tag() )
569             ReadMetaFromId3v2( flac->ID3v2Tag(), p_demux_meta, p_meta );
570         else if( flac->xiphComment() )
571             ReadMetaFromXiph( flac->xiphComment(), p_demux_meta, p_meta );
572     }
573 #ifdef TAGLIB_HAVE_MP4COVERTART_H
574     else if( MP4::File *mp4 = dynamic_cast<MP4::File*>(f.file()) )
575     {
576         if( mp4->tag() )
577             ReadMetaFromMP4( mp4->tag(), p_demux_meta, p_meta );
578     }
579 #endif
580     else if( MPC::File* mpc = dynamic_cast<MPC::File*>(f.file()) )
581     {
582         if( mpc->APETag() )
583             ReadMetaFromAPE( mpc->APETag(), p_demux_meta, p_meta );
584     }
585     else if( MPEG::File* mpeg = dynamic_cast<MPEG::File*>(f.file()) )
586     {
587         if( mpeg->ID3v2Tag() )
588             ReadMetaFromId3v2( mpeg->ID3v2Tag(), p_demux_meta, p_meta );
589         else if( mpeg->APETag() )
590             ReadMetaFromAPE( mpeg->APETag(), p_demux_meta, p_meta );
591     }
592     else if( dynamic_cast<Ogg::File*>(f.file()) )
593     {
594         if( Ogg::FLAC::File* ogg_flac = dynamic_cast<Ogg::FLAC::File*>(f.file()))
595             ReadMetaFromXiph( ogg_flac->tag(), p_demux_meta, p_meta );
596         else if( Ogg::Speex::File* ogg_speex = dynamic_cast<Ogg::Speex::File*>(f.file()) )
597             ReadMetaFromXiph( ogg_speex->tag(), p_demux_meta, p_meta );
598         else if( Ogg::Vorbis::File* ogg_vorbis = dynamic_cast<Ogg::Vorbis::File*>(f.file()) )
599             ReadMetaFromXiph( ogg_vorbis->tag(), p_demux_meta, p_meta );
600     }
601 #ifdef TAGLIB_HAVE_AIFF_WAV_H
602     else if( dynamic_cast<RIFF::File*>(f.file()) )
603     {
604         if( RIFF::AIFF::File* riff_aiff = dynamic_cast<RIFF::AIFF::File*>(f.file()) )
605             ReadMetaFromId3v2( riff_aiff->tag(), p_demux_meta, p_meta );
606         else if( RIFF::WAV::File* riff_wav = dynamic_cast<RIFF::WAV::File*>(f.file()) )
607             ReadMetaFromId3v2( riff_wav->tag(), p_demux_meta, p_meta );
608     }
609 #endif
610     else if( TrueAudio::File* trueaudio = dynamic_cast<TrueAudio::File*>(f.file()) )
611     {
612         if( trueaudio->ID3v2Tag() )
613             ReadMetaFromId3v2( trueaudio->ID3v2Tag(), p_demux_meta, p_meta );
614     }
615     else if( WavPack::File* wavpack = dynamic_cast<WavPack::File*>(f.file()) )
616     {
617         if( wavpack->APETag() )
618             ReadMetaFromAPE( wavpack->APETag(), p_demux_meta, p_meta );
619     }
620
621     return VLC_SUCCESS;
622 }
623
624
625 /**
626  * Write meta information to APE tags
627  * @param tag: the APE tag
628  * @param p_item: the input item
629  */
630 static void WriteMetaToAPE( APE::Tag* tag, input_item_t* p_item )
631 {
632     char* psz_meta;
633 #define WRITE( metaName, keyName )                      \
634     psz_meta = input_item_Get##metaName( p_item );      \
635     if( psz_meta )                                      \
636     {                                                   \
637         String key( keyName, String::UTF8 );            \
638         String value( psz_meta, String::UTF8 );         \
639         tag->addValue( key, value, true );              \
640     }                                                   \
641     free( psz_meta );
642
643     WRITE( Copyright, "COPYRIGHT" );
644     WRITE( Language, "LANGUAGE" );
645     WRITE( Publisher, "PUBLISHER" );
646
647 #undef WRITE
648 }
649
650
651 /**
652  * Write meta information to id3v2 tags
653  * @param tag: the id3v2 tag
654  * @param p_input: the input item
655  */
656 static void WriteMetaToId3v2( ID3v2::Tag* tag, input_item_t* p_item )
657 {
658     char* psz_meta;
659 #define WRITE( metaName, tagName )                                            \
660     psz_meta = input_item_Get##metaName( p_item );                            \
661     if( psz_meta )                                                            \
662     {                                                                         \
663         ByteVector p_byte( tagName, 4 );                                      \
664         tag->removeFrames( p_byte );                                         \
665         ID3v2::TextIdentificationFrame* p_frame =                             \
666             new ID3v2::TextIdentificationFrame( p_byte, String::UTF8 );       \
667         p_frame->setText( psz_meta );                                         \
668         tag->addFrame( p_frame );                                             \
669     }                                                                         \
670     free( psz_meta );
671
672     WRITE( Copyright, "TCOP" );
673     WRITE( EncodedBy, "TENC" );
674     WRITE( Language,  "TLAN" );
675     WRITE( Publisher, "TPUB" );
676
677 #undef WRITE
678 }
679
680
681 /**
682  * Write the meta information to XiphComments
683  * @param tag: the Xiph Comment
684  * @param p_input: the input item
685  */
686 static void WriteMetaToXiph( Ogg::XiphComment* tag, input_item_t* p_item )
687 {
688     char* psz_meta;
689 #define WRITE( metaName, keyName )                      \
690     psz_meta = input_item_Get##metaName( p_item );      \
691     if( psz_meta )                                      \
692     {                                                   \
693         String key( keyName, String::UTF8 );            \
694         String value( psz_meta, String::UTF8 );         \
695         tag->addField( key, value, true );              \
696     }                                                   \
697     free( psz_meta );
698
699     WRITE( Copyright, "COPYRIGHT" );
700
701 #undef WRITE
702 }
703
704
705 /**
706  * Set the tags to the file using TagLib
707  * @param p_this: the demux object
708  * @return VLC_SUCCESS if the operation success
709  */
710
711 static int WriteMeta( vlc_object_t *p_this )
712 {
713     vlc_mutex_locker locker (&taglib_lock);
714     meta_export_t *p_export = (meta_export_t *)p_this;
715     input_item_t *p_item = p_export->p_item;
716     FileRef f;
717
718     if( !p_item )
719     {
720         msg_Err( p_this, "Can't save meta data of an empty input" );
721         return VLC_EGENERIC;
722     }
723
724 #if defined(WIN32) || defined (UNDER_CE)
725     wchar_t *wpath = ToWide( p_export->psz_file );
726     if( wpath == NULL )
727         return VLC_EGENERIC;
728     f = FileRef( wpath );
729     free( wpath );
730 #else
731     f = FileRef( p_export->psz_file );
732 #endif
733
734     if( f.isNull() || !f.tag() || f.file()->readOnly() )
735     {
736         msg_Err( p_this, "File %s can't be opened for tag writing",
737                  p_export->psz_file );
738         return VLC_EGENERIC;
739     }
740
741     msg_Dbg( p_this, "Writing metadata for %s", p_export->psz_file );
742
743     Tag *p_tag = f.tag();
744
745     char *psz_meta;
746
747 #define SET( a, b )                                             \
748     psz_meta = input_item_Get ## a( p_item );                   \
749     if( psz_meta )                                              \
750     {                                                           \
751         String tmp( psz_meta, String::UTF8 );                   \
752         p_tag->set##b( tmp );                                   \
753     }                                                           \
754     free( psz_meta );
755
756     // Saving all common fields
757     // If the title is empty, use the name
758     SET( TitleFbName, Title );
759     SET( Artist, Artist );
760     SET( Album, Album );
761     SET( Description, Comment );
762     SET( Genre, Genre );
763
764 #undef SET
765
766     psz_meta = input_item_GetDate( p_item );
767     if( !EMPTY_STR(psz_meta) ) p_tag->setYear( atoi( psz_meta ) );
768     free( psz_meta );
769
770     psz_meta = input_item_GetTrackNum( p_item );
771     if( !EMPTY_STR(psz_meta) ) p_tag->setTrack( atoi( psz_meta ) );
772     free( psz_meta );
773
774
775     // Try now to write special tags
776 #ifdef TAGLIB_HAVE_APEFILE_H
777     if( APE::File* ape = dynamic_cast<APE::File*>(f.file()) )
778     {
779         if( ape->APETag() )
780             WriteMetaToAPE( ape->APETag(), p_item );
781     }
782     else
783 #endif
784     if( FLAC::File* flac = dynamic_cast<FLAC::File*>(f.file()) )
785     {
786         if( flac->ID3v2Tag() )
787             WriteMetaToId3v2( flac->ID3v2Tag(), p_item );
788         else if( flac->xiphComment() )
789             WriteMetaToXiph( flac->xiphComment(), p_item );
790     }
791     else if( MPC::File* mpc = dynamic_cast<MPC::File*>(f.file()) )
792     {
793         if( mpc->APETag() )
794             WriteMetaToAPE( mpc->APETag(), p_item );
795     }
796     else if( MPEG::File* mpeg = dynamic_cast<MPEG::File*>(f.file()) )
797     {
798         if( mpeg->ID3v2Tag() )
799             WriteMetaToId3v2( mpeg->ID3v2Tag(), p_item );
800         else if( mpeg->APETag() )
801             WriteMetaToAPE( mpeg->APETag(), p_item );
802     }
803     else if( dynamic_cast<Ogg::File*>(f.file()) )
804     {
805         if( Ogg::FLAC::File* ogg_flac = dynamic_cast<Ogg::FLAC::File*>(f.file()))
806             WriteMetaToXiph( ogg_flac->tag(), p_item );
807         else if( Ogg::Speex::File* ogg_speex = dynamic_cast<Ogg::Speex::File*>(f.file()) )
808             WriteMetaToXiph( ogg_speex->tag(), p_item );
809         else if( Ogg::Vorbis::File* ogg_vorbis = dynamic_cast<Ogg::Vorbis::File*>(f.file()) )
810             WriteMetaToXiph( ogg_vorbis->tag(), p_item );
811     }
812 #ifdef TAGLIB_HAVE_AIFF_WAV_H
813     else if( dynamic_cast<RIFF::File*>(f.file()) )
814     {
815         if( RIFF::AIFF::File* riff_aiff = dynamic_cast<RIFF::AIFF::File*>(f.file()) )
816             WriteMetaToId3v2( riff_aiff->tag(), p_item );
817         else if( RIFF::WAV::File* riff_wav = dynamic_cast<RIFF::WAV::File*>(f.file()) )
818             WriteMetaToId3v2( riff_wav->tag(), p_item );
819     }
820 #endif
821     else if( TrueAudio::File* trueaudio = dynamic_cast<TrueAudio::File*>(f.file()) )
822     {
823         if( trueaudio->ID3v2Tag() )
824             WriteMetaToId3v2( trueaudio->ID3v2Tag(), p_item );
825     }
826     else if( WavPack::File* wavpack = dynamic_cast<WavPack::File*>(f.file()) )
827     {
828         if( wavpack->APETag() )
829             WriteMetaToAPE( wavpack->APETag(), p_item );
830     }
831
832     // Save the meta data
833     f.save();
834
835     return VLC_SUCCESS;
836 }
837