packetizer: hevc: add poc debug
[vlc.git] / modules / meta_engine / taglib.cpp
blob123d09c94fdce884486d2a5aaeb31ea62da2b1d3
1 /*****************************************************************************
2 * taglib.cpp: Taglib tag parser/writer
3 *****************************************************************************
4 * Copyright (C) 2003-2016 VLC authors and VideoLAN
5 * $Id$
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 *****************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
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>
39 #include <sys/stat.h>
41 #ifdef _WIN32
42 # include <vlc_charset.h> /* ToWide */
43 # include <io.h>
44 #else
45 # include <unistd.h>
46 #endif
48 // Taglib headers
49 #ifdef _WIN32
50 # define TAGLIB_STATIC
51 #endif
52 #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)
59 #define TAGLIB_VERSION_1_11 VERSION_INT(1,11,0)
61 #include <fileref.h>
62 #include <tag.h>
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>
69 #endif
71 #include <apefile.h>
72 #include <asffile.h>
73 #include <apetag.h>
74 #include <flacfile.h>
75 #include <mpcfile.h>
76 #include <mpegfile.h>
77 #include <mp4file.h>
78 #include <oggfile.h>
79 #include <oggflacfile.h>
80 #include <opusfile.h>
81 #include "../demux/xiph_metadata.h"
83 #include <aifffile.h>
84 #include <wavfile.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;
98 #include <algorithm>
100 namespace VLCTagLib
102 template <class T>
103 class ExtResolver : public FileRef::FileTypeResolver
105 public:
106 ExtResolver(const std::string &);
107 ~ExtResolver() {}
108 virtual File *createFile(FileName, bool, AudioProperties::ReadStyle) const;
110 protected:
111 std::string ext;
115 template <class T>
116 VLCTagLib::ExtResolver<T>::ExtResolver(const std::string & ext) : FileTypeResolver()
118 this->ext = ext;
119 std::transform(this->ext.begin(), this->ext.end(), this->ext.begin(), ::toupper);
122 template <class T>
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);
132 if(fext == ext)
133 return new T(fileName, false, AudioProperties::Fast);
136 return 0;
139 #if TAGLIB_VERSION >= TAGLIB_VERSION_1_11
140 static VLCTagLib::ExtResolver<MPEG::File> aacresolver(".aac");
141 #endif
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;
148 // Local functions
149 static int ReadMeta ( vlc_object_t * );
150 static int WriteMeta ( vlc_object_t * );
152 vlc_module_begin ()
153 set_capability( "meta reader", 1000 )
154 set_callbacks( ReadMeta, NULL )
155 add_submodule ()
156 set_capability( "meta writer", 50 )
157 set_callbacks( WriteMeta, NULL )
158 vlc_module_end ()
160 #if TAGLIB_VERSION >= TAGLIB_VERSION_1_11
161 class VlcIostream : public IOStream
163 public:
164 VlcIostream(stream_t* p_stream)
165 : m_stream( p_stream )
166 , m_previousPos( 0 )
170 ~VlcIostream()
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);
184 if (i_read < 0)
185 return ByteVector::null;
186 else if ((size_t)i_read != length)
187 res.resize(i_read);
188 return res;
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
206 return true;
209 bool isOpen() const
211 return true;
214 void seek(long offset, Position p)
216 uint64_t pos = 0;
217 switch (p)
219 case Current:
220 pos = m_previousPos;
221 break;
222 case End:
223 pos = length();
224 break;
225 default:
226 break;
228 if (vlc_stream_Seek( m_stream, pos + offset ) == 0)
229 m_previousPos = pos + offset;
232 void clear()
234 return;
237 long tell() const
239 return m_previousPos;
242 long length()
244 uint64_t i_size;
245 if (vlc_stream_GetSize( m_stream, &i_size ) != VLC_SUCCESS)
246 return -1;
247 return i_size;
250 void truncate(long)
254 private:
255 stream_t* m_stream;
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 );
266 char psz_trck[11];
267 if( i_ret >= 1 )
269 snprintf( psz_trck, sizeof( psz_trck ), "%u", i_trknum );
270 vlc_meta_Set( p_meta, first, psz_trck );
272 if( i_ret == 2)
274 snprintf( psz_trck, sizeof( psz_trck ), "%u", i_trktot );
275 vlc_meta_Set( p_meta, second, psz_trck );
277 return i_ret;
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 );
316 if( p_attachment )
318 TAB_APPEND_CAST( (input_attachment_t**),
319 p_demux_meta->i_attachments, p_demux_meta->attachments,
320 p_attachment );
322 char *psz_url;
323 if( asprintf( &psz_url, "attachment://%s", p_attachment->psz_name ) != -1 )
325 vlc_meta_SetArtURL( p_meta, psz_url );
326 free( psz_url );
331 fields.erase(iter);
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 );
360 #undef SET
361 #undef SET_EXTRA
363 /* */
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 );
372 /* Remainings */
373 for( iter = fields.begin(); iter != fields.end(); ++iter )
375 if( iter->second.isEmpty() )
376 continue;
378 if( iter->second.type() != APE::Item::Text )
379 continue;
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 );
415 #undef SET
416 #undef SET_EXTRA
418 // List the pictures
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();
428 char *psz_name;
429 input_attachment_t *p_attachment;
431 if( asfPicture.description().size() > 0 )
432 psz_name = strdup( asfPicture.description().toCString( true ) );
433 else
435 if( asprintf( &psz_name, "%i", asfPicture.type() ) == -1 )
436 psz_name = NULL;
439 if( unlikely(psz_name == NULL) )
440 continue;
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 );
447 if( p_attachment )
448 TAB_APPEND_CAST( (input_attachment_t**),
449 p_demux_meta->i_attachments, p_demux_meta->attachments,
450 p_attachment );
451 char *psz_url;
452 if( asprintf( &psz_url, "attachment://%s", psz_name ) != -1 )
454 vlc_meta_SetArtURL( p_meta, psz_url );
455 free( psz_url );
457 free( psz_name );
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);
477 if( !p_ufid )
478 continue;
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 */
485 char psz_ufid[64];
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 );
493 // Get the use text
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);
499 if( !p_txxx )
500 continue;
501 if( !strcmp( p_txxx->description().toCString( true ), "TRACKTOTAL" ) )
503 vlc_meta_Set( p_meta, vlc_meta_TrackTotal, p_txxx->fieldList().back().toCString( true ) );
504 continue;
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 ) );
509 continue;
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" );
536 #undef SET_EXTRA
537 #undef SET
539 /* */
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 );
547 /* */
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[] = {
559 0, /* Other */
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]))
582 int i_score = -1;
584 // Try now to get embedded art
585 list = tag->frameListMap()[ "APIC" ];
586 if( list.isEmpty() )
587 return;
589 for( iter = list.begin(); iter != list.end(); iter++ )
591 ID3v2::AttachedPictureFrame* p_apic =
592 dynamic_cast<ID3v2::AttachedPictureFrame*>(*iter);
593 if( !p_apic )
594 continue;
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 ) );
605 else
607 if( asprintf( &psz_description, "%i", p_apic->type() ) == -1 )
608 psz_description = NULL;
611 if( !psz_description )
612 continue;
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 );
624 continue;
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 );
636 if( !p_attachment )
638 free( psz_description );
639 continue;
641 TAB_APPEND_CAST( (input_attachment_t**),
642 p_demux_meta->i_attachments, p_demux_meta->attachments,
643 p_attachment );
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];
653 char *psz_url;
654 if( asprintf( &psz_url, "attachment://%s",
655 p_attachment->psz_name ) == -1 )
656 continue;
657 vlc_meta_SetArtURL( p_meta, psz_url );
658 free( 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 )
672 StringList list;
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 );
686 SET( "DATE", Date );
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 );
695 #undef SET
696 #undef SET_EXTRA
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;
705 if( !hasTrackTotal )
707 list = tag->fieldListMap()["TRACKTOTAL"];
708 if( list.isEmpty() )
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",
725 art_list.size() );
727 const char* psz_name = "cover";
728 const char* psz_mime = mime_list[0].toCString(true);
729 const char* psz_description = "cover";
731 uint8_t *p_data;
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 );
739 free( p_data );
741 else
743 art_list = tag->fieldListMap()[ "METADATA_BLOCK_PICTURE" ];
744 if( art_list.size() == 0 )
745 return;
747 uint8_t *p_data;
748 int i_cover_score;
749 int i_cover_idx;
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 );
755 free( p_data );
758 if (p_attachment) {
759 TAB_APPEND_CAST( (input_attachment_t**),
760 p_demux_meta->i_attachments, p_demux_meta->attachments,
761 p_attachment );
763 char *psz_url;
764 if( asprintf( &psz_url, "attachment://%s", p_attachment->psz_name ) != -1 ) {
765 vlc_meta_SetArtURL( p_meta, psz_url );
766 free( 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 )
779 MP4::Item list;
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 );
796 #undef SET
797 #undef SET_EXTRA
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() );
810 if( p_attachment )
812 TAB_APPEND_CAST( (input_attachment_t**),
813 p_demux_meta->i_attachments, p_demux_meta->attachments,
814 p_attachment );
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;
829 vlc_meta_t* p_meta;
830 FileRef f;
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) )
836 return VLC_ENOMEM;
838 char *psz_path = vlc_uri2path( psz_uri );
839 #if VLC_WINSTORE_APP && TAGLIB_VERSION >= TAGLIB_VERSION_1_11
840 if( psz_path == NULL )
842 free( psz_uri );
843 return VLC_EGENERIC;
845 free( psz_path );
847 stream_t *p_stream = vlc_access_NewMRL( p_this, psz_uri );
848 free( psz_uri );
849 if( p_stream == NULL )
850 return VLC_EGENERIC;
852 VlcIostream s( p_stream );
853 f = FileRef( &s );
854 #else /* VLC_WINSTORE_APP */
855 free( psz_uri );
856 if( psz_path == NULL )
857 return VLC_EGENERIC;
859 if( !b_extensions_registered )
861 #if TAGLIB_VERSION >= TAGLIB_VERSION_1_11
862 FileRef::addFileTypeResolver( &aacresolver );
863 #endif
864 FileRef::addFileTypeResolver( &m4vresolver );
865 b_extensions_registered = true;
868 #if defined(_WIN32)
869 wchar_t *wpath = ToWide( psz_path );
870 if( wpath == NULL )
872 free( psz_path );
873 return VLC_EGENERIC;
875 f = FileRef( wpath );
876 free( wpath );
877 #else
878 f = FileRef( psz_path );
879 #endif
880 free( psz_path );
881 #endif /* VLC_WINSTORE_APP */
883 if( f.isNull() )
884 return VLC_EGENERIC;
885 if( !f.tag() || f.tag()->isEmpty() )
886 return VLC_EGENERIC;
888 p_demux_meta->p_meta = p_meta = vlc_meta_New();
889 if( !p_meta )
890 return VLC_ENOMEM;
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 ) \
900 if( p_tag->tag() ) \
902 char psz_tmp[10]; \
903 snprintf( psz_tmp, 10, "%d", p_tag->tag() ); \
904 vlc_meta_Set##meta( p_meta, psz_tmp ); \
907 SET( title, Title );
908 SET( artist, Artist );
909 SET( album, Album );
910 SET( comment, Description );
911 SET( genre, Genre );
912 SETINT( year, Date );
913 SETINT( track, TrackNum );
915 #undef SETINT
916 #undef SET
918 TAB_INIT( p_demux_meta->i_attachments, p_demux_meta->attachments );
920 if( APE::File* ape = dynamic_cast<APE::File*>(f.file()) )
922 if( ape->APETag() )
923 ReadMetaFromAPE( ape->APETag(), p_demux_meta, p_meta );
925 else
926 if( ASF::File* asf = dynamic_cast<ASF::File*>(f.file()) )
928 if( asf->tag() )
929 ReadMetaFromASF( asf->tag(), p_demux_meta, p_meta );
931 else
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()) )
941 if( mp4->tag() )
942 ReadMetaFromMP4( mp4->tag(), p_demux_meta, p_meta );
944 else if( MPC::File* mpc = dynamic_cast<MPC::File*>(f.file()) )
946 if( mpc->APETag() )
947 ReadMetaFromAPE( mpc->APETag(), p_demux_meta, p_meta );
949 else if( MPEG::File* mpeg = dynamic_cast<MPEG::File*>(f.file()) )
951 if( mpeg->APETag() )
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 );
967 #endif
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 );
987 return VLC_SUCCESS;
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 )
998 char* psz_meta;
999 #define WRITE( metaName, keyName ) \
1000 psz_meta = input_item_Get##metaName( p_item ); \
1001 if( psz_meta ) \
1003 String key( keyName, String::UTF8 ); \
1004 String value( psz_meta, String::UTF8 ); \
1005 tag->addValue( key, value, true ); \
1007 free( psz_meta );
1009 WRITE( Copyright, "COPYRIGHT" );
1010 WRITE( Language, "LANGUAGE" );
1011 WRITE( Publisher, "PUBLISHER" );
1012 WRITE( TrackID, "MUSICBRAINZ_TRACKID" );
1013 #undef WRITE
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 )
1024 char* psz_meta;
1025 #define WRITE( metaName, tagName ) \
1026 psz_meta = input_item_Get##metaName( p_item ); \
1027 if( psz_meta ) \
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 ); \
1036 free( psz_meta );
1038 WRITE( Copyright, "TCOP" );
1039 WRITE( EncodedBy, "TENC" );
1040 WRITE( Language, "TLAN" );
1041 WRITE( Publisher, "TPUB" );
1043 #undef WRITE
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 ); \
1049 if ( psz_meta ) \
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); \
1055 if( !p_txxx ) \
1056 continue; \
1057 if( !strcmp( p_txxx->description().toCString( true ), txxName ) ) \
1059 p_txxx->setText( psz_meta ); \
1060 FREENULL( psz_meta ); \
1061 break; \
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 ); \
1070 free( psz_meta ); \
1071 tag->addFrame( p_txxx ); \
1075 WRITETXXX( TrackTotal, "TRACKTOTAL" );
1077 #undef WRITETXXX
1079 /* Write album art */
1080 char *psz_url = input_item_GetArtworkURL( p_item );
1081 if( psz_url == NULL )
1082 return;
1084 char *psz_path = vlc_uri2path( psz_url );
1085 free( psz_url );
1086 if( psz_path == NULL )
1087 return;
1089 const char *psz_mime = vlc_mime_Ext2Mime( psz_path );
1091 FILE *p_file = vlc_fopen( psz_path, "rb" );
1092 if( p_file == NULL )
1094 free( psz_path );
1095 return;
1098 struct stat st;
1099 if( vlc_stat( psz_path, &st ) == -1 )
1101 free( psz_path );
1102 fclose( p_file );
1103 return;
1105 off_t file_size = st.st_size;
1107 free( psz_path );
1109 /* Limit picture size to 10MiB */
1110 if( file_size > 10485760 )
1112 fclose( p_file );
1113 return;
1116 char *p_buffer = new (std::nothrow) char[file_size];
1117 if( p_buffer == NULL )
1119 fclose( p_file );
1120 return;
1123 if( fread( p_buffer, 1, file_size, p_file ) != (unsigned)file_size )
1125 fclose( p_file );
1126 delete[] p_buffer;
1127 return;
1129 fclose( p_file );
1131 ByteVector data( p_buffer, file_size );
1132 delete[] p_buffer;
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 );
1141 else
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 )
1158 char* psz_meta;
1159 #define WRITE( metaName, keyName ) \
1160 psz_meta = input_item_Get##metaName( p_item ); \
1161 if( psz_meta ) \
1163 String key( keyName, String::UTF8 ); \
1164 String value( psz_meta, String::UTF8 ); \
1165 tag->addField( key, value, true ); \
1167 free( psz_meta );
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" );
1178 #undef WRITE
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;
1193 FileRef f;
1195 if( !p_item )
1197 msg_Err( p_this, "Can't save meta data of an empty input" );
1198 return VLC_EGENERIC;
1201 #if defined(_WIN32)
1202 wchar_t *wpath = ToWide( p_export->psz_file );
1203 if( wpath == NULL )
1204 return VLC_EGENERIC;
1205 f = FileRef( wpath, false );
1206 free( wpath );
1207 #else
1208 f = FileRef( p_export->psz_file, false );
1209 #endif
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();
1222 char *psz_meta;
1224 #define SET( a, b ) \
1225 psz_meta = input_item_Get ## a( p_item ); \
1226 if( psz_meta ) \
1228 String tmp( psz_meta, String::UTF8 ); \
1229 p_tag->set##b( tmp ); \
1231 free( psz_meta );
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 );
1241 #undef SET
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 );
1246 free( psz_meta );
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 );
1251 free( psz_meta );
1254 // Try now to write special tags
1255 if( APE::File* ape = dynamic_cast<APE::File*>(f.file()) )
1257 if( ape->APETag() )
1258 WriteMetaToAPE( ape->APETag(), p_item );
1260 else
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()) )
1270 if( mpc->APETag() )
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 );
1291 #endif
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
1312 f.save();
1314 return VLC_SUCCESS;