1 /*****************************************************************************
2 * taglib.cpp: Taglib tag parser/writer
3 *****************************************************************************
4 * Copyright (C) 2003-2016 VLC authors and VideoLAN
7 * Authors: Clément Stenac <zorglub@videolan.org>
8 * Rafaël Carré <funman@videolanorg>
9 * Rémi Duraffort <ivoire@videolan.org>
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.
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.
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 *****************************************************************************/
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 */
42 # include <vlc_charset.h> /* ToWide */
50 # define TAGLIB_STATIC
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, \
59 #define TAGLIB_VERSION_1_11 VERSION_INT(1,11,0)
63 #include <tbytevector.h>
65 /* Support for stream-based metadata */
66 #if TAGLIB_VERSION >= TAGLIB_VERSION_1_11
67 # include <vlc_access.h>
68 # include <tiostream.h>
79 #include <oggflacfile.h>
81 #include "../demux/xiph_metadata.h"
86 #include <speexfile.h>
87 #include <trueaudiofile.h>
88 #include <vorbisfile.h>
89 #include <wavpackfile.h>
91 #include <attachedpictureframe.h>
92 #include <textidentificationframe.h>
93 #include <uniquefileidentifierframe.h>
95 using namespace TagLib
;
103 class ExtResolver
: public FileRef::FileTypeResolver
106 ExtResolver(const std::string
&);
108 virtual File
*createFile(FileName
, bool, AudioProperties::ReadStyle
) const;
116 VLCTagLib::ExtResolver
<T
>::ExtResolver(const std::string
& ext
) : FileTypeResolver()
119 std::transform(this->ext
.begin(), this->ext
.end(), this->ext
.begin(), ::toupper
);
123 File
*VLCTagLib::ExtResolver
<T
>::createFile(FileName fileName
, bool, AudioProperties::ReadStyle
) const
125 std::string filename
= std::string(fileName
);
126 std::size_t namesize
= filename
.size();
128 if (namesize
> ext
.length())
130 std::string fext
= filename
.substr(namesize
- ext
.length(), ext
.length());
131 std::transform(fext
.begin(), fext
.end(), fext
.begin(), ::toupper
);
133 return new T(fileName
, false, AudioProperties::Fast
);
139 #if TAGLIB_VERSION >= TAGLIB_VERSION_1_11
140 static VLCTagLib::ExtResolver
<MPEG::File
> aacresolver(".aac");
142 static VLCTagLib::ExtResolver
<MP4::File
> m4vresolver(".m4v");
143 static bool b_extensions_registered
= false;
145 // taglib is not thread safe
146 static vlc_mutex_t taglib_lock
= VLC_STATIC_MUTEX
;
149 static int ReadMeta ( vlc_object_t
* );
150 static int WriteMeta ( vlc_object_t
* );
153 set_capability( "meta reader", 1000 )
154 set_callbacks( ReadMeta
, NULL
)
156 set_capability( "meta writer", 50 )
157 set_callbacks( WriteMeta
, NULL
)
160 #if TAGLIB_VERSION >= TAGLIB_VERSION_1_11
161 class VlcIostream
: public IOStream
164 VlcIostream(stream_t
* p_stream
)
165 : m_stream( p_stream
)
172 vlc_stream_Delete( m_stream
);
175 FileName
name() const
177 return m_stream
->psz_location
;
180 ByteVector
readBlock(ulong length
)
182 ByteVector
res(length
, 0);
183 ssize_t i_read
= vlc_stream_Read( m_stream
, res
.data(), length
);
185 return ByteVector::null
;
186 else if ((size_t)i_read
!= length
)
191 void writeBlock(const ByteVector
&)
193 // Let's stay Read-Only for now
196 void insert(const ByteVector
&, ulong
, ulong
)
200 void removeBlock(ulong
, ulong
)
204 bool readOnly() const
214 void seek(long offset
, Position p
)
228 if (vlc_stream_Seek( m_stream
, pos
+ offset
) == 0)
229 m_previousPos
= pos
+ offset
;
239 return m_previousPos
;
245 if (vlc_stream_GetSize( m_stream
, &i_size
) != VLC_SUCCESS
)
256 int64_t m_previousPos
;
258 #endif /* TAGLIB_VERSION_1_11 */
260 static int ExtractCoupleNumberValues( vlc_meta_t
* p_meta
, const char *psz_value
,
261 vlc_meta_type_t first
, vlc_meta_type_t second
)
263 unsigned int i_trknum
, i_trktot
;
265 int i_ret
= sscanf( psz_value
, "%u/%u", &i_trknum
, &i_trktot
);
269 snprintf( psz_trck
, sizeof( psz_trck
), "%u", i_trknum
);
270 vlc_meta_Set( p_meta
, first
, psz_trck
);
274 snprintf( psz_trck
, sizeof( psz_trck
), "%u", i_trktot
);
275 vlc_meta_Set( p_meta
, second
, psz_trck
);
281 * Read meta information from APE tags
282 * @param tag: the APE tag
283 * @param p_demux_meta: the demuxer meta
284 * @param p_meta: the meta
286 static void ReadMetaFromAPE( APE::Tag
* tag
, demux_meta_t
* p_demux_meta
, vlc_meta_t
* p_meta
)
288 APE::ItemListMap
fields ( tag
->itemListMap() );
289 APE::ItemListMap::Iterator iter
;
291 iter
= fields
.find("COVER ART (FRONT)");
292 if( iter
!= fields
.end()
293 && !iter
->second
.isEmpty()
294 && iter
->second
.type() == APE::Item::Binary
)
296 input_attachment_t
*p_attachment
;
298 const ByteVector picture
= iter
->second
.binaryData();
299 const char *p_data
= picture
.data();
300 unsigned i_data
= picture
.size();
302 /* Null terminated filename followed by the image data */
303 size_t desc_len
= strnlen(p_data
, i_data
);
304 if( desc_len
< i_data
&& IsUTF8( p_data
) )
306 const char *psz_name
= p_data
;
307 const char *psz_mime
= vlc_mime_Ext2Mime( psz_name
);
308 p_data
+= desc_len
+ 1; /* '\0' */
309 i_data
-= desc_len
+ 1;
311 msg_Dbg( p_demux_meta
, "Found embedded art: %s (%s) is %u bytes",
312 psz_name
, psz_mime
, i_data
);
314 p_attachment
= vlc_input_attachment_New( psz_name
, psz_mime
,
315 psz_name
, p_data
, i_data
);
318 TAB_APPEND_CAST( (input_attachment_t
**),
319 p_demux_meta
->i_attachments
, p_demux_meta
->attachments
,
323 if( asprintf( &psz_url
, "attachment://%s", p_attachment
->psz_name
) != -1 )
325 vlc_meta_SetArtURL( p_meta
, psz_url
);
334 #define SET( keyName, metaName ) \
335 iter = fields.find(keyName); \
336 if( iter != fields.end() && !iter->second.isEmpty() ) { \
337 vlc_meta_Set##metaName( p_meta, iter->second.toString().toCString( true ) ); \
338 fields.erase(iter); \
341 #define SET_EXTRA( keyName, metaName ) \
342 iter = fields.find( keyName ); \
343 if( iter != fields.end() && !iter->second.isEmpty() ) { \
344 vlc_meta_AddExtra( p_meta, metaName, iter->second.toString().toCString( true ) ); \
345 fields.erase(iter); \
348 SET( "ALBUM", Album
);
349 SET( "ARTIST", Artist
);
350 SET( "COMMENT", Description
);
351 SET( "GENRE", Genre
);
352 SET( "TITLE", Title
);
353 SET( "COPYRIGHT", Copyright
);
354 SET( "LANGUAGE", Language
);
355 SET( "PUBLISHER", Publisher
);
356 SET( "MUSICBRAINZ_TRACKID", TrackID
);
358 SET_EXTRA( "MUSICBRAINZ_ALBUMID", VLC_META_EXTRA_MB_ALBUMID
);
364 iter
= fields
.find( "TRACK" );
365 if( iter
!= fields
.end() && !iter
->second
.isEmpty() )
367 ExtractCoupleNumberValues( p_meta
, iter
->second
.toString().toCString( true ),
368 vlc_meta_TrackNumber
, vlc_meta_TrackTotal
);
369 fields
.erase( iter
);
373 for( iter
= fields
.begin(); iter
!= fields
.end(); ++iter
)
375 if( iter
->second
.isEmpty() )
378 if( iter
->second
.type() != APE::Item::Text
)
381 vlc_meta_AddExtra( p_meta
,
382 iter
->first
.toCString( true ),
383 iter
->second
.toString().toCString( true ) );
389 * Read meta information from ASF tags
390 * @param tag: the ASF tag
391 * @param p_demux_meta: the demuxer meta
392 * @param p_meta: the meta
394 static void ReadMetaFromASF( ASF::Tag
* tag
, demux_meta_t
* p_demux_meta
, vlc_meta_t
* p_meta
)
397 ASF::AttributeList list
;
398 #define SET( keyName, metaName ) \
399 if( tag->attributeListMap().contains(keyName) ) \
401 list = tag->attributeListMap()[keyName]; \
402 vlc_meta_Set##metaName( p_meta, list.front().toString().toCString( true ) ); \
405 #define SET_EXTRA( keyName, metaName ) \
406 if( tag->attributeListMap().contains(keyName) ) \
408 list = tag->attributeListMap()[keyName]; \
409 vlc_meta_AddExtra( p_meta, metaName, list.front().toString().toCString( true ) ); \
412 SET("MusicBrainz/Track Id", TrackID
);
413 SET_EXTRA("MusicBrainz/Album Id", VLC_META_EXTRA_MB_ALBUMID
);
419 list
= tag
->attributeListMap()["WM/Picture"];
420 ASF::AttributeList::Iterator iter
;
421 for( iter
= list
.begin(); iter
!= list
.end(); iter
++ )
423 const ASF::Picture asfPicture
= (*iter
).toPicture();
424 const ByteVector picture
= asfPicture
.picture();
425 const char *psz_mime
= asfPicture
.mimeType().toCString();
426 const char *p_data
= picture
.data();
427 const unsigned i_data
= picture
.size();
429 input_attachment_t
*p_attachment
;
431 if( asfPicture
.description().size() > 0 )
432 psz_name
= strdup( asfPicture
.description().toCString( true ) );
435 if( asprintf( &psz_name
, "%i", asfPicture
.type() ) == -1 )
439 if( unlikely(psz_name
== NULL
) )
442 msg_Dbg( p_demux_meta
, "Found embedded art: %s (%s) is %u bytes",
443 psz_name
, psz_mime
, i_data
);
445 p_attachment
= vlc_input_attachment_New( psz_name
, psz_mime
,
446 psz_name
, p_data
, i_data
);
448 TAB_APPEND_CAST( (input_attachment_t
**),
449 p_demux_meta
->i_attachments
, p_demux_meta
->attachments
,
452 if( asprintf( &psz_url
, "attachment://%s", psz_name
) != -1 )
454 vlc_meta_SetArtURL( p_meta
, psz_url
);
463 * Read meta information from id3v2 tags
464 * @param tag: the id3v2 tag
465 * @param p_demux_meta: the demuxer meta
466 * @param p_meta: the meta
468 static void ReadMetaFromId3v2( ID3v2::Tag
* tag
, demux_meta_t
* p_demux_meta
, vlc_meta_t
* p_meta
)
470 // Get the unique file identifier
471 ID3v2::FrameList list
= tag
->frameListMap()["UFID"];
472 ID3v2::FrameList::Iterator iter
;
473 for( iter
= list
.begin(); iter
!= list
.end(); iter
++ )
475 ID3v2::UniqueFileIdentifierFrame
* p_ufid
=
476 dynamic_cast<ID3v2::UniqueFileIdentifierFrame
*>(*iter
);
479 const char *owner
= p_ufid
->owner().toCString();
480 if (!strcmp( owner
, "http://musicbrainz.org" ))
482 /* ID3v2 UFID contains up to 64 bytes binary data
483 * but in our case it will be a '\0'
484 * terminated string */
486 int max_size
= __MIN( p_ufid
->identifier().size(), 63);
487 strncpy( psz_ufid
, p_ufid
->identifier().data(), max_size
);
488 psz_ufid
[max_size
] = '\0';
489 vlc_meta_SetTrackID( p_meta
, psz_ufid
);
494 list
= tag
->frameListMap()["TXXX"];
495 for( iter
= list
.begin(); iter
!= list
.end(); iter
++ )
497 ID3v2::UserTextIdentificationFrame
* p_txxx
=
498 dynamic_cast<ID3v2::UserTextIdentificationFrame
*>(*iter
);
501 if( !strcmp( p_txxx
->description().toCString( true ), "TRACKTOTAL" ) )
503 vlc_meta_Set( p_meta
, vlc_meta_TrackTotal
, p_txxx
->fieldList().back().toCString( true ) );
506 if( !strcmp( p_txxx
->description().toCString( true ), "MusicBrainz Album Id" ) )
508 vlc_meta_AddExtra( p_meta
, VLC_META_EXTRA_MB_ALBUMID
, p_txxx
->fieldList().back().toCString( true ) );
511 vlc_meta_AddExtra( p_meta
, p_txxx
->description().toCString( true ),
512 p_txxx
->fieldList().back().toCString( true ) );
515 // Get some more information
516 #define SET( tagName, metaName ) \
517 list = tag->frameListMap()[tagName]; \
518 if( !list.isEmpty() ) \
519 vlc_meta_Set##metaName( p_meta, \
520 (*list.begin())->toString().toCString( true ) );
522 #define SET_EXTRA( tagName, metaName )\
523 list = tag->frameListMap()[tagName];\
524 if( !list.isEmpty() )\
525 vlc_meta_AddExtra( p_meta, metaName,\
526 (*list.begin())->toString().toCString( true ) );
529 SET( "TCOP", Copyright
);
530 SET( "TENC", EncodedBy
);
531 SET( "TLAN", Language
);
532 SET( "TPUB", Publisher
);
533 SET( "TPE2", AlbumArtist
);
534 SET_EXTRA( "USLT", "Lyrics" );
540 list
= tag
->frameListMap()["TRCK"];
541 if( !list
.isEmpty() )
543 ExtractCoupleNumberValues( p_meta
, (*list
.begin())->toString().toCString( true ),
544 vlc_meta_TrackNumber
, vlc_meta_TrackTotal
);
548 list
= tag
->frameListMap()["TPOS"];
549 if( !list
.isEmpty() )
551 ExtractCoupleNumberValues( p_meta
, (*list
.begin())->toString().toCString( true ),
552 vlc_meta_DiscNumber
, vlc_meta_DiscTotal
);
555 /* Preferred type of image
556 * The 21 types are defined in id3v2 standard:
557 * http://www.id3.org/id3v2.4.0-frames */
558 static const int pi_cover_score
[] = {
560 5, /* 32x32 PNG image that should be used as the file icon */
561 4, /* File icon of a different size or format. */
562 20, /* Front cover image of the album. */
563 19, /* Back cover image of the album. */
564 13, /* Inside leaflet page of the album. */
565 18, /* Image from the album itself. */
566 17, /* Picture of the lead artist or soloist. */
567 16, /* Picture of the artist or performer. */
568 14, /* Picture of the conductor. */
569 15, /* Picture of the band or orchestra. */
570 9, /* Picture of the composer. */
571 8, /* Picture of the lyricist or text writer. */
572 7, /* Picture of the recording location or studio. */
573 10, /* Picture of the artists during recording. */
574 11, /* Picture of the artists during performance. */
575 6, /* Picture from a movie or video related to the track. */
576 1, /* Picture of a large, coloured fish. */
577 12, /* Illustration related to the track. */
578 3, /* Logo of the band or performer. */
579 2 /* Logo of the publisher (record company). */
581 #define PI_COVER_SCORE_SIZE (sizeof (pi_cover_score) / sizeof (pi_cover_score[0]))
584 // Try now to get embedded art
585 list
= tag
->frameListMap()[ "APIC" ];
589 for( iter
= list
.begin(); iter
!= list
.end(); iter
++ )
591 ID3v2::AttachedPictureFrame
* p_apic
=
592 dynamic_cast<ID3v2::AttachedPictureFrame
*>(*iter
);
595 input_attachment_t
*p_attachment
;
597 const char *psz_mime
;
598 char *psz_name
, *psz_description
;
600 // Get the mime and description of the image.
601 // If the description is empty, take the type as a description
602 psz_mime
= p_apic
->mimeType().toCString( true );
603 if( p_apic
->description().size() > 0 )
604 psz_description
= strdup( p_apic
->description().toCString( true ) );
607 if( asprintf( &psz_description
, "%i", p_apic
->type() ) == -1 )
608 psz_description
= NULL
;
611 if( !psz_description
)
613 psz_name
= psz_description
;
615 /* some old iTunes version not only sets incorrectly the mime type
616 * or the description of the image,
617 * but also embeds incorrectly the image.
618 * Recent versions seem to behave correctly */
619 if( !strncmp( psz_mime
, "PNG", 3 ) ||
620 !strncmp( psz_name
, "\xC2\x89PNG", 5 ) )
622 msg_Warn( p_demux_meta
, "Invalid picture embedded by broken iTunes version" );
623 free( psz_description
);
627 const ByteVector picture
= p_apic
->picture();
628 const char *p_data
= picture
.data();
629 const unsigned i_data
= picture
.size();
631 msg_Dbg( p_demux_meta
, "Found embedded art: %s (%s) is %u bytes",
632 psz_name
, psz_mime
, i_data
);
634 p_attachment
= vlc_input_attachment_New( psz_name
, psz_mime
,
635 psz_description
, p_data
, i_data
);
638 free( psz_description
);
641 TAB_APPEND_CAST( (input_attachment_t
**),
642 p_demux_meta
->i_attachments
, p_demux_meta
->attachments
,
644 free( psz_description
);
646 unsigned i_pic_type
= p_apic
->type();
647 if( i_pic_type
>= PI_COVER_SCORE_SIZE
)
648 i_pic_type
= 0; // Defaults to "Other"
650 if( pi_cover_score
[i_pic_type
] > i_score
)
652 i_score
= pi_cover_score
[i_pic_type
];
654 if( asprintf( &psz_url
, "attachment://%s",
655 p_attachment
->psz_name
) == -1 )
657 vlc_meta_SetArtURL( p_meta
, psz_url
);
665 * Read the meta information from XiphComments
666 * @param tag: the Xiph Comment
667 * @param p_demux_meta: the demuxer meta
668 * @param p_meta: the meta
670 static void ReadMetaFromXiph( Ogg::XiphComment
* tag
, demux_meta_t
* p_demux_meta
, vlc_meta_t
* p_meta
)
673 bool hasTrackTotal
= false;
674 #define SET( keyName, metaName ) \
675 list = tag->fieldListMap()[keyName]; \
676 if( !list.isEmpty() ) \
677 vlc_meta_Set##metaName( p_meta, (*list.begin()).toCString( true ) );
679 #define SET_EXTRA( keyName, metaName ) \
680 list = tag->fieldListMap()[keyName]; \
681 if( !list.isEmpty() ) \
682 vlc_meta_AddExtra( p_meta, keyName, (*list.begin()).toCString( true ) );
684 SET( "COPYRIGHT", Copyright
);
685 SET( "ORGANIZATION", Publisher
);
687 SET( "ENCODER", EncodedBy
);
688 SET( "RATING", Rating
);
689 SET( "LANGUAGE", Language
);
690 SET( "MUSICBRAINZ_TRACKID", TrackID
);
691 SET( "ALBUMARTIST", AlbumArtist
);
692 SET( "DISCNUMBER", DiscNumber
);
694 SET_EXTRA( "MUSICBRAINZ_ALBUMID", VLC_META_EXTRA_MB_ALBUMID
);
698 list
= tag
->fieldListMap()["TRACKNUMBER"];
699 if( !list
.isEmpty() )
701 int i_values
= ExtractCoupleNumberValues( p_meta
, (*list
.begin()).toCString( true ),
702 vlc_meta_TrackNumber
, vlc_meta_TrackTotal
);
703 hasTrackTotal
= i_values
== 2;
707 list
= tag
->fieldListMap()["TRACKTOTAL"];
709 list
= tag
->fieldListMap()["TOTALTRACKS"];
710 if( !list
.isEmpty() )
711 vlc_meta_SetTrackTotal( p_meta
, (*list
.begin()).toCString( true ) );
714 // Try now to get embedded art
715 StringList mime_list
= tag
->fieldListMap()[ "COVERARTMIME" ];
716 StringList art_list
= tag
->fieldListMap()[ "COVERART" ];
718 input_attachment_t
*p_attachment
;
720 if( mime_list
.size() != 0 && art_list
.size() != 0 )
722 // We get only the first covert art
723 if( mime_list
.size() > 1 || art_list
.size() > 1 )
724 msg_Warn( p_demux_meta
, "Found %i embedded arts, so using only the first one",
727 const char* psz_name
= "cover";
728 const char* psz_mime
= mime_list
[0].toCString(true);
729 const char* psz_description
= "cover";
732 int i_data
= vlc_b64_decode_binary( &p_data
, art_list
[0].toCString(true) );
734 msg_Dbg( p_demux_meta
, "Found embedded art: %s (%s) is %i bytes",
735 psz_name
, psz_mime
, i_data
);
737 p_attachment
= vlc_input_attachment_New( psz_name
, psz_mime
,
738 psz_description
, p_data
, i_data
);
743 art_list
= tag
->fieldListMap()[ "METADATA_BLOCK_PICTURE" ];
744 if( art_list
.size() == 0 )
750 int i_data
= vlc_b64_decode_binary( &p_data
, art_list
[0].toCString(true) );
751 i_cover_score
= i_cover_idx
= 0;
752 /* TODO: Use i_cover_score / i_cover_idx to select the picture. */
753 p_attachment
= ParseFlacPicture( p_data
, i_data
, 0,
754 &i_cover_score
, &i_cover_idx
);
759 TAB_APPEND_CAST( (input_attachment_t
**),
760 p_demux_meta
->i_attachments
, p_demux_meta
->attachments
,
764 if( asprintf( &psz_url
, "attachment://%s", p_attachment
->psz_name
) != -1 ) {
765 vlc_meta_SetArtURL( p_meta
, psz_url
);
772 * Read the meta information from mp4 specific tags
773 * @param tag: the mp4 tag
774 * @param p_demux_meta: the demuxer meta
775 * @param p_meta: the meta
777 static void ReadMetaFromMP4( MP4::Tag
* tag
, demux_meta_t
*p_demux_meta
, vlc_meta_t
* p_meta
)
780 #define SET( keyName, metaName ) \
781 if( tag->itemListMap().contains(keyName) ) \
783 list = tag->itemListMap()[keyName]; \
784 vlc_meta_Set##metaName( p_meta, list.toStringList().front().toCString( true ) ); \
786 #define SET_EXTRA( keyName, metaName ) \
787 if( tag->itemListMap().contains(keyName) ) \
789 list = tag->itemListMap()[keyName]; \
790 vlc_meta_AddExtra( p_meta, metaName, list.toStringList().front().toCString( true ) ); \
793 SET("----:com.apple.iTunes:MusicBrainz Track Id", TrackID
);
794 SET_EXTRA("----:com.apple.iTunes:MusicBrainz Album Id", VLC_META_EXTRA_MB_ALBUMID
);
799 if( tag
->itemListMap().contains("covr") )
801 MP4::CoverArtList list
= tag
->itemListMap()["covr"].toCoverArtList();
802 const char *psz_format
= list
[0].format() == MP4::CoverArt::PNG
? "image/png" : "image/jpeg";
804 msg_Dbg( p_demux_meta
, "Found embedded art (%s) is %i bytes",
805 psz_format
, list
[0].data().size() );
807 input_attachment_t
*p_attachment
=
808 vlc_input_attachment_New( "cover", psz_format
, "cover",
809 list
[0].data().data(), list
[0].data().size() );
812 TAB_APPEND_CAST( (input_attachment_t
**),
813 p_demux_meta
->i_attachments
, p_demux_meta
->attachments
,
815 vlc_meta_SetArtURL( p_meta
, "attachment://cover" );
821 * Get the tags from the file using TagLib
822 * @param p_this: the demux object
823 * @return VLC_SUCCESS if the operation success
825 static int ReadMeta( vlc_object_t
* p_this
)
827 vlc_mutex_locker
locker (&taglib_lock
);
828 demux_meta_t
* p_demux_meta
= (demux_meta_t
*)p_this
;
832 p_demux_meta
->p_meta
= NULL
;
834 char *psz_uri
= input_item_GetURI( p_demux_meta
->p_item
);
835 if( unlikely(psz_uri
== NULL
) )
838 char *psz_path
= vlc_uri2path( psz_uri
);
839 #if VLC_WINSTORE_APP && TAGLIB_VERSION >= TAGLIB_VERSION_1_11
840 if( psz_path
== NULL
)
847 stream_t
*p_stream
= vlc_access_NewMRL( p_this
, psz_uri
);
849 if( p_stream
== NULL
)
852 VlcIostream
s( p_stream
);
854 #else /* VLC_WINSTORE_APP */
856 if( psz_path
== NULL
)
859 if( !b_extensions_registered
)
861 #if TAGLIB_VERSION >= TAGLIB_VERSION_1_11
862 FileRef::addFileTypeResolver( &aacresolver
);
864 FileRef::addFileTypeResolver( &m4vresolver
);
865 b_extensions_registered
= true;
869 wchar_t *wpath
= ToWide( psz_path
);
875 f
= FileRef( wpath
);
878 f
= FileRef( psz_path
);
881 #endif /* VLC_WINSTORE_APP */
885 if( !f
.tag() || f
.tag()->isEmpty() )
888 p_demux_meta
->p_meta
= p_meta
= vlc_meta_New();
893 // Read the tags from the file
894 Tag
* p_tag
= f
.tag();
896 #define SET( tag, meta ) \
897 if( !p_tag->tag().isNull() && !p_tag->tag().isEmpty() ) \
898 vlc_meta_Set##meta( p_meta, p_tag->tag().toCString(true) )
899 #define SETINT( tag, meta ) \
903 snprintf( psz_tmp, 10, "%d", p_tag->tag() ); \
904 vlc_meta_Set##meta( p_meta, psz_tmp ); \
908 SET( artist
, Artist
);
910 SET( comment
, Description
);
912 SETINT( year
, Date
);
913 SETINT( track
, TrackNum
);
918 TAB_INIT( p_demux_meta
->i_attachments
, p_demux_meta
->attachments
);
920 if( APE::File
* ape
= dynamic_cast<APE::File
*>(f
.file()) )
923 ReadMetaFromAPE( ape
->APETag(), p_demux_meta
, p_meta
);
926 if( ASF::File
* asf
= dynamic_cast<ASF::File
*>(f
.file()) )
929 ReadMetaFromASF( asf
->tag(), p_demux_meta
, p_meta
);
932 if( FLAC::File
* flac
= dynamic_cast<FLAC::File
*>(f
.file()) )
934 if( flac
->ID3v2Tag() )
935 ReadMetaFromId3v2( flac
->ID3v2Tag(), p_demux_meta
, p_meta
);
936 else if( flac
->xiphComment() )
937 ReadMetaFromXiph( flac
->xiphComment(), p_demux_meta
, p_meta
);
939 else if( MP4::File
*mp4
= dynamic_cast<MP4::File
*>(f
.file()) )
942 ReadMetaFromMP4( mp4
->tag(), p_demux_meta
, p_meta
);
944 else if( MPC::File
* mpc
= dynamic_cast<MPC::File
*>(f
.file()) )
947 ReadMetaFromAPE( mpc
->APETag(), p_demux_meta
, p_meta
);
949 else if( MPEG::File
* mpeg
= dynamic_cast<MPEG::File
*>(f
.file()) )
952 ReadMetaFromAPE( mpeg
->APETag(), p_demux_meta
, p_meta
);
953 if( mpeg
->ID3v2Tag() )
954 ReadMetaFromId3v2( mpeg
->ID3v2Tag(), p_demux_meta
, p_meta
);
956 else if( dynamic_cast<Ogg::File
*>(f
.file()) )
958 if( Ogg::FLAC::File
* ogg_flac
= dynamic_cast<Ogg::FLAC::File
*>(f
.file()))
959 ReadMetaFromXiph( ogg_flac
->tag(), p_demux_meta
, p_meta
);
960 else if( Ogg::Speex::File
* ogg_speex
= dynamic_cast<Ogg::Speex::File
*>(f
.file()) )
961 ReadMetaFromXiph( ogg_speex
->tag(), p_demux_meta
, p_meta
);
962 else if( Ogg::Vorbis::File
* ogg_vorbis
= dynamic_cast<Ogg::Vorbis::File
*>(f
.file()) )
963 ReadMetaFromXiph( ogg_vorbis
->tag(), p_demux_meta
, p_meta
);
964 #if defined(TAGLIB_OPUSFILE_H)
965 else if( Ogg::Opus::File
* ogg_opus
= dynamic_cast<Ogg::Opus::File
*>(f
.file()) )
966 ReadMetaFromXiph( ogg_opus
->tag(), p_demux_meta
, p_meta
);
969 else if( dynamic_cast<RIFF::File
*>(f
.file()) )
971 if( RIFF::AIFF::File
* riff_aiff
= dynamic_cast<RIFF::AIFF::File
*>(f
.file()) )
972 ReadMetaFromId3v2( riff_aiff
->tag(), p_demux_meta
, p_meta
);
973 else if( RIFF::WAV::File
* riff_wav
= dynamic_cast<RIFF::WAV::File
*>(f
.file()) )
974 ReadMetaFromId3v2( riff_wav
->tag(), p_demux_meta
, p_meta
);
976 else if( TrueAudio::File
* trueaudio
= dynamic_cast<TrueAudio::File
*>(f
.file()) )
978 if( trueaudio
->ID3v2Tag() )
979 ReadMetaFromId3v2( trueaudio
->ID3v2Tag(), p_demux_meta
, p_meta
);
981 else if( WavPack::File
* wavpack
= dynamic_cast<WavPack::File
*>(f
.file()) )
983 if( wavpack
->APETag() )
984 ReadMetaFromAPE( wavpack
->APETag(), p_demux_meta
, p_meta
);
992 * Write meta information to APE tags
993 * @param tag: the APE tag
994 * @param p_item: the input item
996 static void WriteMetaToAPE( APE::Tag
* tag
, input_item_t
* p_item
)
999 #define WRITE( metaName, keyName ) \
1000 psz_meta = input_item_Get##metaName( p_item ); \
1003 String key( keyName, String::UTF8 ); \
1004 String value( psz_meta, String::UTF8 ); \
1005 tag->addValue( key, value, true ); \
1009 WRITE( Copyright
, "COPYRIGHT" );
1010 WRITE( Language
, "LANGUAGE" );
1011 WRITE( Publisher
, "PUBLISHER" );
1012 WRITE( TrackID
, "MUSICBRAINZ_TRACKID" );
1018 * Write meta information to id3v2 tags
1019 * @param tag: the id3v2 tag
1020 * @param p_input: the input item
1022 static void WriteMetaToId3v2( ID3v2::Tag
* tag
, input_item_t
* p_item
)
1025 #define WRITE( metaName, tagName ) \
1026 psz_meta = input_item_Get##metaName( p_item ); \
1029 ByteVector p_byte( tagName, 4 ); \
1030 tag->removeFrames( p_byte ); \
1031 ID3v2::TextIdentificationFrame* p_frame = \
1032 new ID3v2::TextIdentificationFrame( p_byte, String::UTF8 ); \
1033 p_frame->setText( psz_meta ); \
1034 tag->addFrame( p_frame ); \
1038 WRITE( Copyright
, "TCOP" );
1039 WRITE( EncodedBy
, "TENC" );
1040 WRITE( Language
, "TLAN" );
1041 WRITE( Publisher
, "TPUB" );
1044 /* Known TXXX frames */
1045 ID3v2::FrameList list
= tag
->frameListMap()["TXXX"];
1047 #define WRITETXXX( metaName, txxName )\
1048 psz_meta = input_item_Get##metaName( p_item ); \
1051 ID3v2::UserTextIdentificationFrame *p_txxx; \
1052 for( ID3v2::FrameList::Iterator iter = list.begin(); iter != list.end(); iter++ )\
1054 p_txxx = dynamic_cast<ID3v2::UserTextIdentificationFrame*>(*iter); \
1057 if( !strcmp( p_txxx->description().toCString( true ), txxName ) ) \
1059 p_txxx->setText( psz_meta ); \
1060 FREENULL( psz_meta ); \
1064 if( psz_meta ) /* not found in existing custom fields */ \
1066 ByteVector p_byte( "TXXX", 4 ); \
1067 p_txxx = new ID3v2::UserTextIdentificationFrame( p_byte ); \
1068 p_txxx->setDescription( txxName ); \
1069 p_txxx->setText( psz_meta ); \
1071 tag->addFrame( p_txxx ); \
1075 WRITETXXX( TrackTotal
, "TRACKTOTAL" );
1079 /* Write album art */
1080 char *psz_url
= input_item_GetArtworkURL( p_item
);
1081 if( psz_url
== NULL
)
1084 char *psz_path
= vlc_uri2path( psz_url
);
1086 if( psz_path
== NULL
)
1089 const char *psz_mime
= vlc_mime_Ext2Mime( psz_path
);
1091 FILE *p_file
= vlc_fopen( psz_path
, "rb" );
1092 if( p_file
== NULL
)
1099 if( vlc_stat( psz_path
, &st
) == -1 )
1105 off_t file_size
= st
.st_size
;
1109 /* Limit picture size to 10MiB */
1110 if( file_size
> 10485760 )
1116 char *p_buffer
= new (std::nothrow
) char[file_size
];
1117 if( p_buffer
== NULL
)
1123 if( fread( p_buffer
, 1, file_size
, p_file
) != (unsigned)file_size
)
1131 ByteVector
data( p_buffer
, file_size
);
1134 ID3v2::FrameList frames
= tag
->frameList( "APIC" );
1135 ID3v2::AttachedPictureFrame
*frame
= NULL
;
1136 if( frames
.isEmpty() )
1138 frame
= new TagLib::ID3v2::AttachedPictureFrame
;
1139 tag
->addFrame( frame
);
1143 frame
= static_cast<ID3v2::AttachedPictureFrame
*>( frames
.back() );
1146 frame
->setPicture( data
);
1147 frame
->setMimeType( psz_mime
);
1152 * Write the meta information to XiphComments
1153 * @param tag: the Xiph Comment
1154 * @param p_input: the input item
1156 static void WriteMetaToXiph( Ogg::XiphComment
* tag
, input_item_t
* p_item
)
1159 #define WRITE( metaName, keyName ) \
1160 psz_meta = input_item_Get##metaName( p_item ); \
1163 String key( keyName, String::UTF8 ); \
1164 String value( psz_meta, String::UTF8 ); \
1165 tag->addField( key, value, true ); \
1169 WRITE( TrackNum
, "TRACKNUMBER" );
1170 WRITE( TrackTotal
, "TRACKTOTAL" );
1171 WRITE( Copyright
, "COPYRIGHT" );
1172 WRITE( Publisher
, "ORGANIZATION" );
1173 WRITE( Date
, "DATE" );
1174 WRITE( EncodedBy
, "ENCODER" );
1175 WRITE( Rating
, "RATING" );
1176 WRITE( Language
, "LANGUAGE" );
1177 WRITE( TrackID
, "MUSICBRAINZ_TRACKID" );
1183 * Set the tags to the file using TagLib
1184 * @param p_this: the demux object
1185 * @return VLC_SUCCESS if the operation success
1188 static int WriteMeta( vlc_object_t
*p_this
)
1190 vlc_mutex_locker
locker (&taglib_lock
);
1191 meta_export_t
*p_export
= (meta_export_t
*)p_this
;
1192 input_item_t
*p_item
= p_export
->p_item
;
1197 msg_Err( p_this
, "Can't save meta data of an empty input" );
1198 return VLC_EGENERIC
;
1202 wchar_t *wpath
= ToWide( p_export
->psz_file
);
1204 return VLC_EGENERIC
;
1205 f
= FileRef( wpath
, false );
1208 f
= FileRef( p_export
->psz_file
, false );
1211 if( f
.isNull() || !f
.tag() || f
.file()->readOnly() )
1213 msg_Err( p_this
, "File %s can't be opened for tag writing",
1214 p_export
->psz_file
);
1215 return VLC_EGENERIC
;
1218 msg_Dbg( p_this
, "Writing metadata for %s", p_export
->psz_file
);
1220 Tag
*p_tag
= f
.tag();
1224 #define SET( a, b ) \
1225 psz_meta = input_item_Get ## a( p_item ); \
1228 String tmp( psz_meta, String::UTF8 ); \
1229 p_tag->set##b( tmp ); \
1233 // Saving all common fields
1234 // If the title is empty, use the name
1235 SET( TitleFbName
, Title
);
1236 SET( Artist
, Artist
);
1237 SET( Album
, Album
);
1238 SET( Description
, Comment
);
1239 SET( Genre
, Genre
);
1243 psz_meta
= input_item_GetDate( p_item
);
1244 if( !EMPTY_STR(psz_meta
) ) p_tag
->setYear( atoi( psz_meta
) );
1245 else p_tag
->setYear( 0 );
1248 psz_meta
= input_item_GetTrackNum( p_item
);
1249 if( !EMPTY_STR(psz_meta
) ) p_tag
->setTrack( atoi( psz_meta
) );
1250 else p_tag
->setTrack( 0 );
1254 // Try now to write special tags
1255 if( APE::File
* ape
= dynamic_cast<APE::File
*>(f
.file()) )
1258 WriteMetaToAPE( ape
->APETag(), p_item
);
1261 if( FLAC::File
* flac
= dynamic_cast<FLAC::File
*>(f
.file()) )
1263 if( flac
->ID3v2Tag() )
1264 WriteMetaToId3v2( flac
->ID3v2Tag(), p_item
);
1265 else if( flac
->xiphComment() )
1266 WriteMetaToXiph( flac
->xiphComment(), p_item
);
1268 else if( MPC::File
* mpc
= dynamic_cast<MPC::File
*>(f
.file()) )
1271 WriteMetaToAPE( mpc
->APETag(), p_item
);
1273 else if( MPEG::File
* mpeg
= dynamic_cast<MPEG::File
*>(f
.file()) )
1275 if( mpeg
->ID3v2Tag() )
1276 WriteMetaToId3v2( mpeg
->ID3v2Tag(), p_item
);
1277 else if( mpeg
->APETag() )
1278 WriteMetaToAPE( mpeg
->APETag(), p_item
);
1280 else if( dynamic_cast<Ogg::File
*>(f
.file()) )
1282 if( Ogg::FLAC::File
* ogg_flac
= dynamic_cast<Ogg::FLAC::File
*>(f
.file()))
1283 WriteMetaToXiph( ogg_flac
->tag(), p_item
);
1284 else if( Ogg::Speex::File
* ogg_speex
= dynamic_cast<Ogg::Speex::File
*>(f
.file()) )
1285 WriteMetaToXiph( ogg_speex
->tag(), p_item
);
1286 else if( Ogg::Vorbis::File
* ogg_vorbis
= dynamic_cast<Ogg::Vorbis::File
*>(f
.file()) )
1287 WriteMetaToXiph( ogg_vorbis
->tag(), p_item
);
1288 #if defined(TAGLIB_OPUSFILE_H)
1289 else if( Ogg::Opus::File
* ogg_opus
= dynamic_cast<Ogg::Opus::File
*>(f
.file()) )
1290 WriteMetaToXiph( ogg_opus
->tag(), p_item
);
1293 else if( dynamic_cast<RIFF::File
*>(f
.file()) )
1295 if( RIFF::AIFF::File
* riff_aiff
= dynamic_cast<RIFF::AIFF::File
*>(f
.file()) )
1296 WriteMetaToId3v2( riff_aiff
->tag(), p_item
);
1297 else if( RIFF::WAV::File
* riff_wav
= dynamic_cast<RIFF::WAV::File
*>(f
.file()) )
1298 WriteMetaToId3v2( riff_wav
->tag(), p_item
);
1300 else if( TrueAudio::File
* trueaudio
= dynamic_cast<TrueAudio::File
*>(f
.file()) )
1302 if( trueaudio
->ID3v2Tag() )
1303 WriteMetaToId3v2( trueaudio
->ID3v2Tag(), p_item
);
1305 else if( WavPack::File
* wavpack
= dynamic_cast<WavPack::File
*>(f
.file()) )
1307 if( wavpack
->APETag() )
1308 WriteMetaToAPE( wavpack
->APETag(), p_item
);
1311 // Save the meta data