1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 // The contents of this file are subject to the Mozilla Public License
4 // Version 1.1 (the "License"); you may not use this file except in
5 // compliance with the License. You may obtain a copy of the License at
6 // http://www.mozilla.org/MPL/
7 //
8 // Software distributed under the License is distributed on an "AS IS"
9 // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
10 // License for the specific language governing rights and limitations
11 // under the License.
12 //
13 // The Original Code is MP4v2.
14 //
15 // The Initial Developer of the Original Code is Kona Blend.
16 // Portions created by Kona Blend are Copyright (C) 2008.
17 // Portions created by David Byron are Copyright (C) 2011.
18 // All Rights Reserved.
19 //
20 // Contributors:
21 // Kona Blend, kona8lend@@gmail.com
22 // Rouven Wessling, mp4v2@rouvenwessling.de
23 // David Byron, dbyron@dbyron.com
24 //
25 ///////////////////////////////////////////////////////////////////////////////
26
27 #include "impl.h"
28
29 namespace mp4v2 { namespace impl { namespace itmf {
30
31 ///////////////////////////////////////////////////////////////////////////////
32
Tags()33 Tags::Tags()
34 : hasMetadata(false)
35 {
36 }
37
38 ///////////////////////////////////////////////////////////////////////////////
39
~Tags()40 Tags::~Tags()
41 {
42 }
43
44 ///////////////////////////////////////////////////////////////////////////////
45
46 void
c_addArtwork(MP4Tags * & tags,MP4TagArtwork & c_artwork)47 Tags::c_addArtwork( MP4Tags*& tags, MP4TagArtwork& c_artwork )
48 {
49 artwork.resize( artwork.size() + 1 );
50 c_setArtwork( tags, (uint32_t)artwork.size() - 1, c_artwork );
51 updateArtworkShadow( tags );
52 }
53
54 ///////////////////////////////////////////////////////////////////////////////
55
56 void
c_alloc(MP4Tags * & tags)57 Tags::c_alloc( MP4Tags*& tags )
58 {
59 tags = new MP4Tags();
60 memset( tags, 0, sizeof(MP4Tags) ); // safe: pure C-struct
61 tags->__handle = this;
62 }
63
64 ///////////////////////////////////////////////////////////////////////////////
65
66 void
c_fetch(MP4Tags * & tags,MP4FileHandle hFile)67 Tags::c_fetch( MP4Tags*& tags, MP4FileHandle hFile )
68 {
69 MP4Tags& c = *tags;
70 MP4File& file = *static_cast<MP4File*>(hFile);
71
72 MP4ItmfItemList* itemList = genericGetItems( file ); // alloc
73
74 hasMetadata = (itemList->size > 0);
75
76 /* create code -> item map.
77 * map will only be used for items which do not repeat; we do not care if
78 * cover-art is inserted multiple times.
79 */
80 CodeItemMap cim;
81 for( uint32_t i = 0; i < itemList->size; i++ ) {
82 MP4ItmfItem& item = itemList->elements[i];
83 cim.insert( CodeItemMap::value_type( item.code, &item ));
84 }
85
86 fetchString( cim, CODE_NAME, name, c.name );
87 fetchString( cim, CODE_ARTIST, artist, c.artist );
88 fetchString( cim, CODE_ALBUMARTIST, albumArtist, c.albumArtist );
89 fetchString( cim, CODE_ALBUM, album, c.album );
90 fetchString( cim, CODE_GROUPING, grouping, c.grouping );
91 fetchString( cim, CODE_COMPOSER, composer, c.composer );
92 fetchString( cim, CODE_COMMENTS, comments, c.comments );
93
94 fetchString( cim, CODE_GENRE, genre, c.genre );
95 fetchGenre( cim, genreType, c.genreType );
96
97 fetchString( cim, CODE_RELEASEDATE, releaseDate, c.releaseDate );
98 fetchTrack( cim, track, c.track );
99 fetchDisk( cim, disk, c.disk );
100 fetchInteger( cim, CODE_TEMPO, tempo, c.tempo );
101 fetchInteger( cim, CODE_COMPILATION, compilation, c.compilation );
102
103 fetchString( cim, CODE_TVSHOW, tvShow, c.tvShow );
104 fetchString( cim, CODE_TVNETWORK, tvNetwork, c.tvNetwork );
105 fetchString( cim, CODE_TVEPISODEID, tvEpisodeID, c.tvEpisodeID );
106 fetchInteger( cim, CODE_TVSEASON, tvSeason, c.tvSeason );
107 fetchInteger( cim, CODE_TVEPISODE, tvEpisode, c.tvEpisode );
108
109 fetchString( cim, CODE_SORTNAME, sortName, c.sortName );
110 fetchString( cim, CODE_SORTARTIST, sortArtist, c.sortArtist );
111 fetchString( cim, CODE_SORTALBUMARTIST, sortAlbumArtist, c.sortAlbumArtist );
112 fetchString( cim, CODE_SORTALBUM, sortAlbum, c.sortAlbum );
113 fetchString( cim, CODE_SORTCOMPOSER, sortComposer, c.sortComposer );
114 fetchString( cim, CODE_SORTTVSHOW, sortTVShow, c.sortTVShow );
115
116 fetchString( cim, CODE_DESCRIPTION, description, c.description );
117 fetchString( cim, CODE_LONGDESCRIPTION, longDescription, c.longDescription );
118 fetchString( cim, CODE_LYRICS, lyrics, c.lyrics );
119
120 fetchString( cim, CODE_COPYRIGHT, copyright, c.copyright );
121 fetchString( cim, CODE_ENCODINGTOOL, encodingTool, c.encodingTool );
122 fetchString( cim, CODE_ENCODEDBY, encodedBy, c.encodedBy );
123 fetchString( cim, CODE_PURCHASEDATE, purchaseDate, c.purchaseDate );
124
125 fetchInteger( cim, CODE_PODCAST, podcast, c.podcast );
126 fetchString( cim, CODE_KEYWORDS, keywords, c.keywords );
127 fetchString( cim, CODE_CATEGORY, category, c.category );
128
129 fetchInteger( cim, CODE_HDVIDEO, hdVideo, c.hdVideo );
130 fetchInteger( cim, CODE_MEDIATYPE, mediaType, c.mediaType );
131 fetchInteger( cim, CODE_CONTENTRATING, contentRating, c.contentRating );
132 fetchInteger( cim, CODE_GAPLESS, gapless, c.gapless );
133
134 fetchString( cim, CODE_ITUNESACCOUNT, iTunesAccount, c.iTunesAccount );
135 fetchInteger( cim, CODE_ITUNESACCOUNTTYPE, iTunesAccountType, c.iTunesAccountType );
136 fetchInteger( cim, CODE_ITUNESCOUNTRY, iTunesCountry, c.iTunesCountry );
137
138 fetchInteger( cim, CODE_CONTENTID, contentID, c.contentID );
139 fetchInteger( cim, CODE_ARTISTID, artistID, c.artistID );
140 fetchInteger( cim, CODE_PLAYLISTID, playlistID, c.playlistID );
141 fetchInteger( cim, CODE_GENREID, genreID, c.genreID );
142 fetchInteger( cim, CODE_COMPOSERID, composerID, c.composerID );
143 fetchString( cim, CODE_XID, xid, c.xid );
144
145 genericItemListFree( itemList ); // free
146
147 // fetch full list and overwrite our copy, otherwise clear
148 {
149 CoverArtBox::ItemList items;
150 if( CoverArtBox::list( hFile, items ))
151 artwork.clear();
152 else
153 artwork = items;
154
155 updateArtworkShadow( tags );
156 }
157 }
158
159 ///////////////////////////////////////////////////////////////////////////////
160
161 void
c_free(MP4Tags * & tags)162 Tags::c_free( MP4Tags*& tags )
163 {
164 MP4Tags* c = const_cast<MP4Tags*>(tags);
165
166 delete[] c->artwork;
167 delete c;
168
169 tags = NULL;
170 }
171
172 ///////////////////////////////////////////////////////////////////////////////
173
174 void
c_removeArtwork(MP4Tags * & tags,uint32_t index)175 Tags::c_removeArtwork( MP4Tags*& tags, uint32_t index )
176 {
177 if( !(index < artwork.size()) )
178 return;
179
180 artwork.erase( artwork.begin() + index );
181 updateArtworkShadow( tags );
182 }
183
184 ///////////////////////////////////////////////////////////////////////////////
185
186 void
c_setArtwork(MP4Tags * & tags,uint32_t index,MP4TagArtwork & c_artwork)187 Tags::c_setArtwork( MP4Tags*& tags, uint32_t index, MP4TagArtwork& c_artwork )
188 {
189 if( !(index < artwork.size()) )
190 return;
191
192 CoverArtBox::Item& item = artwork[index];
193
194 switch( c_artwork.type ) {
195 case MP4_ART_BMP:
196 item.type = BT_BMP;
197 break;
198
199 case MP4_ART_GIF:
200 item.type = BT_GIF;
201 break;
202
203 case MP4_ART_JPEG:
204 item.type = BT_JPEG;
205 break;
206
207 case MP4_ART_PNG:
208 item.type = BT_PNG;
209 break;
210
211 case MP4_ART_UNDEFINED:
212 default:
213 item.type = computeBasicType( c_artwork.data, c_artwork.size );
214 break;
215 }
216
217 item.buffer = (uint8_t*)malloc( c_artwork.size );
218 item.size = c_artwork.size;
219 item.autofree = true;
220
221 memcpy( item.buffer, c_artwork.data, c_artwork.size );
222 updateArtworkShadow( tags );
223 }
224
225 ///////////////////////////////////////////////////////////////////////////////
226
227 void
c_setInteger(const uint8_t * value,uint8_t & cpp,const uint8_t * & c)228 Tags::c_setInteger( const uint8_t* value, uint8_t& cpp, const uint8_t*& c )
229 {
230 if( !value ) {
231 cpp = 0;
232 c = NULL;
233 }
234 else {
235 cpp = *value;
236 c = &cpp;
237 }
238 }
239
240 ///////////////////////////////////////////////////////////////////////////////
241
242 void
c_setInteger(const uint16_t * value,uint16_t & cpp,const uint16_t * & c)243 Tags::c_setInteger( const uint16_t* value, uint16_t& cpp, const uint16_t*& c )
244 {
245 if( !value ) {
246 cpp = 0;
247 c = NULL;
248 }
249 else {
250 cpp = *value;
251 c = &cpp;
252 }
253 }
254
255 ///////////////////////////////////////////////////////////////////////////////
256
257 void
c_setInteger(const uint32_t * value,uint32_t & cpp,const uint32_t * & c)258 Tags::c_setInteger( const uint32_t* value, uint32_t& cpp, const uint32_t*& c )
259 {
260 if( !value ) {
261 cpp = 0;
262 c = NULL;
263 }
264 else {
265 cpp = *value;
266 c = &cpp;
267 }
268 }
269
270 ///////////////////////////////////////////////////////////////////////////////
271
272 void
c_setInteger(const uint64_t * value,uint64_t & cpp,const uint64_t * & c)273 Tags::c_setInteger( const uint64_t* value, uint64_t& cpp, const uint64_t*& c )
274 {
275 if( !value ) {
276 cpp = 0;
277 c = NULL;
278 }
279 else {
280 cpp = *value;
281 c = &cpp;
282 }
283 }
284
285 ///////////////////////////////////////////////////////////////////////////////
286
287 void
c_setString(const char * value,string & cpp,const char * & c)288 Tags::c_setString( const char* value, string& cpp, const char*& c )
289 {
290 if( !value ) {
291 cpp.clear();
292 c = NULL;
293 }
294 else {
295 cpp = value;
296 c = cpp.c_str();
297 }
298 }
299
300 ///////////////////////////////////////////////////////////////////////////////
301
302 void
c_setTrack(const MP4TagTrack * value,MP4TagTrack & cpp,const MP4TagTrack * & c)303 Tags::c_setTrack( const MP4TagTrack* value, MP4TagTrack& cpp, const MP4TagTrack*& c )
304 {
305 if( !value ) {
306 cpp.index = 0;
307 cpp.total = 0;
308 c = NULL;
309 }
310 else {
311 cpp.index = value->index;
312 cpp.total = value->total;
313 c = &cpp;
314 }
315 }
316
317 ///////////////////////////////////////////////////////////////////////////////
318
319 void
c_setDisk(const MP4TagDisk * value,MP4TagDisk & cpp,const MP4TagDisk * & c)320 Tags::c_setDisk( const MP4TagDisk* value, MP4TagDisk& cpp, const MP4TagDisk*& c )
321 {
322 if( !value ) {
323 cpp.index = 0;
324 cpp.total = 0;
325 c = NULL;
326 }
327 else {
328 cpp.index = value->index;
329 cpp.total = value->total;
330 c = &cpp;
331 }
332 }
333
334 ///////////////////////////////////////////////////////////////////////////////
335
336 void
c_store(MP4Tags * & tags,MP4FileHandle hFile)337 Tags::c_store( MP4Tags*& tags, MP4FileHandle hFile )
338 {
339 MP4Tags& c = *tags;
340 MP4File& file = *static_cast<MP4File*>(hFile);
341
342 storeString( file, CODE_NAME, name, c.name );
343 storeString( file, CODE_ARTIST, artist, c.artist );
344 storeString( file, CODE_ALBUMARTIST, albumArtist, c.albumArtist );
345 storeString( file, CODE_ALBUM, album, c.album );
346 storeString( file, CODE_GROUPING, grouping, c.grouping );
347 storeString( file, CODE_COMPOSER, composer, c.composer );
348 storeString( file, CODE_COMMENTS, comments, c.comments );
349
350 storeString( file, CODE_GENRE, genre, c.genre );
351 storeGenre( file, genreType, c.genreType );
352
353 storeString( file, CODE_RELEASEDATE, releaseDate, c.releaseDate );
354 storeTrack( file, track, c.track );
355 storeDisk( file, disk, c.disk );
356 storeInteger( file, CODE_TEMPO, tempo, c.tempo );
357 storeInteger( file, CODE_COMPILATION, compilation, c.compilation );
358
359 storeString( file, CODE_TVSHOW, tvShow, c.tvShow );
360 storeString( file, CODE_TVNETWORK, tvNetwork, c.tvNetwork );
361 storeString( file, CODE_TVEPISODEID, tvEpisodeID, c.tvEpisodeID );
362 storeInteger( file, CODE_TVSEASON, tvSeason, c.tvSeason );
363 storeInteger( file, CODE_TVEPISODE, tvEpisode, c.tvEpisode );
364
365 storeString( file, CODE_SORTNAME, sortName, c.sortName );
366 storeString( file, CODE_SORTARTIST, sortArtist, c.sortArtist );
367 storeString( file, CODE_SORTALBUMARTIST, sortAlbumArtist, c.sortAlbumArtist );
368 storeString( file, CODE_SORTALBUM, sortAlbum, c.sortAlbum );
369 storeString( file, CODE_SORTCOMPOSER, sortComposer, c.sortComposer );
370 storeString( file, CODE_SORTTVSHOW, sortTVShow, c.sortTVShow );
371
372 storeString( file, CODE_DESCRIPTION, description, c.description );
373 storeString( file, CODE_LONGDESCRIPTION, longDescription, c.longDescription );
374 storeString( file, CODE_LYRICS, lyrics, c.lyrics );
375
376 storeString( file, CODE_COPYRIGHT, copyright, c.copyright );
377 storeString( file, CODE_ENCODINGTOOL, encodingTool, c.encodingTool );
378 storeString( file, CODE_ENCODEDBY, encodedBy, c.encodedBy );
379 storeString( file, CODE_PURCHASEDATE, purchaseDate, c.purchaseDate );
380
381 storeInteger( file, CODE_PODCAST, podcast, c.podcast );
382 storeString( file, CODE_KEYWORDS, keywords, c.keywords );
383 storeString( file, CODE_CATEGORY, category, c.category );
384
385 storeInteger( file, CODE_HDVIDEO, hdVideo, c.hdVideo );
386 storeInteger( file, CODE_MEDIATYPE, mediaType, c.mediaType );
387 storeInteger( file, CODE_CONTENTRATING, contentRating, c.contentRating );
388 storeInteger( file, CODE_GAPLESS, gapless, c.gapless );
389
390 storeString( file, CODE_ITUNESACCOUNT, iTunesAccount, c.iTunesAccount );
391 storeInteger( file, CODE_ITUNESACCOUNTTYPE, iTunesAccountType, c.iTunesAccountType );
392 storeInteger( file, CODE_ITUNESCOUNTRY, iTunesCountry, c.iTunesCountry );
393
394 storeInteger( file, CODE_CONTENTID, contentID, c.contentID );
395 storeInteger( file, CODE_ARTISTID, artistID, c.artistID );
396 storeInteger( file, CODE_PLAYLISTID, playlistID, c.playlistID );
397 storeInteger( file, CODE_GENREID, genreID, c.genreID );
398 storeInteger( file, CODE_COMPOSERID, composerID, c.composerID );
399 storeString( file, CODE_XID, xid, c.xid );
400
401 // destroy all cover-art then add each
402 {
403 CoverArtBox::remove( hFile );
404 const CoverArtBox::ItemList::size_type max = artwork.size();
405 for( CoverArtBox::ItemList::size_type i = 0; i < max; i++ )
406 CoverArtBox::add( hFile, artwork[i] );
407 }
408 }
409
410 ///////////////////////////////////////////////////////////////////////////////
411
412 void
fetchGenre(const CodeItemMap & cim,uint16_t & cpp,const uint16_t * & c)413 Tags::fetchGenre( const CodeItemMap& cim, uint16_t& cpp, const uint16_t*& c )
414 {
415 cpp = 0;
416 c = NULL;
417
418 CodeItemMap::const_iterator f = cim.find( CODE_GENRETYPE );
419 if( f == cim.end() || 0 == f->second->dataList.size )
420 return;
421
422 MP4ItmfData& data = f->second->dataList.elements[0];
423 if( NULL == data.value )
424 return;
425
426 cpp = (uint16_t(data.value[0]) << 8)
427 | (uint16_t(data.value[1]) );
428
429 c = &cpp;
430 }
431
432 ///////////////////////////////////////////////////////////////////////////////
433
434 void
fetchDisk(const CodeItemMap & cim,MP4TagDisk & cpp,const MP4TagDisk * & c)435 Tags::fetchDisk( const CodeItemMap& cim, MP4TagDisk& cpp, const MP4TagDisk*& c )
436 {
437 cpp.index = 0;
438 cpp.total = 0;
439 c = NULL;
440
441 CodeItemMap::const_iterator f = cim.find( CODE_DISK );
442 if( f == cim.end() || 0 == f->second->dataList.size )
443 return;
444
445 MP4ItmfData& data = f->second->dataList.elements[0];
446
447 if( NULL == data.value )
448 return;
449
450 cpp.index = (uint16_t(data.value[2]) << 8)
451 | (uint16_t(data.value[3]) );
452
453 cpp.total = (uint16_t(data.value[4]) << 8)
454 | (uint16_t(data.value[5]) );
455
456 c = &cpp;
457 }
458
459 ///////////////////////////////////////////////////////////////////////////////
460
461 void
fetchTrack(const CodeItemMap & cim,MP4TagTrack & cpp,const MP4TagTrack * & c)462 Tags::fetchTrack( const CodeItemMap& cim, MP4TagTrack& cpp, const MP4TagTrack*& c )
463 {
464 cpp.index = 0;
465 cpp.total = 0;
466 c = NULL;
467
468 CodeItemMap::const_iterator f = cim.find( CODE_TRACK );
469 if( f == cim.end() || 0 == f->second->dataList.size )
470 return;
471
472 MP4ItmfData& data = f->second->dataList.elements[0];
473
474 if( NULL == data.value )
475 return;
476
477 cpp.index = (uint16_t(data.value[2]) << 8)
478 | (uint16_t(data.value[3]) );
479
480 cpp.total = (uint16_t(data.value[4]) << 8)
481 | (uint16_t(data.value[5]) );
482
483 c = &cpp;
484 }
485
486 ///////////////////////////////////////////////////////////////////////////////
487
488 void
fetchInteger(const CodeItemMap & cim,const string & code,uint8_t & cpp,const uint8_t * & c)489 Tags::fetchInteger( const CodeItemMap& cim, const string& code, uint8_t& cpp, const uint8_t*& c )
490 {
491 cpp = 0;
492 c = NULL;
493
494 CodeItemMap::const_iterator f = cim.find( code );
495 if( f == cim.end() || 0 == f->second->dataList.size )
496 return;
497
498 MP4ItmfData& data = f->second->dataList.elements[0];
499 if( NULL == data.value )
500 return;
501
502 cpp = data.value[0];
503 c = &cpp;
504 }
505
506 ///////////////////////////////////////////////////////////////////////////////
507
508 void
fetchInteger(const CodeItemMap & cim,const string & code,uint16_t & cpp,const uint16_t * & c)509 Tags::fetchInteger( const CodeItemMap& cim, const string& code, uint16_t& cpp, const uint16_t*& c )
510 {
511 cpp = 0;
512 c = NULL;
513
514 CodeItemMap::const_iterator f = cim.find( code );
515 if( f == cim.end() || 0 == f->second->dataList.size )
516 return;
517
518 MP4ItmfData& data = f->second->dataList.elements[0];
519
520 if( NULL == data.value )
521 return;
522
523 cpp = (uint16_t(data.value[0]) << 8)
524 | (uint16_t(data.value[1]) );
525
526 c = &cpp;
527 }
528
529 ///////////////////////////////////////////////////////////////////////////////
530
531 void
fetchInteger(const CodeItemMap & cim,const string & code,uint32_t & cpp,const uint32_t * & c)532 Tags::fetchInteger( const CodeItemMap& cim, const string& code, uint32_t& cpp, const uint32_t*& c )
533 {
534 cpp = 0;
535 c = NULL;
536
537 CodeItemMap::const_iterator f = cim.find( code );
538 if( f == cim.end() || 0 == f->second->dataList.size )
539 return;
540
541 MP4ItmfData& data = f->second->dataList.elements[0];
542
543 if( NULL == data.value )
544 return;
545
546 cpp = (uint32_t(data.value[0]) << 24)
547 | (uint32_t(data.value[1]) << 16)
548 | (uint32_t(data.value[2]) << 8)
549 | (uint32_t(data.value[3]) );
550
551 c = &cpp;
552 }
553
554 ///////////////////////////////////////////////////////////////////////////////
555
556 void
fetchInteger(const CodeItemMap & cim,const string & code,uint64_t & cpp,const uint64_t * & c)557 Tags::fetchInteger( const CodeItemMap& cim, const string& code, uint64_t& cpp, const uint64_t*& c )
558 {
559 cpp = 0;
560 c = NULL;
561
562 CodeItemMap::const_iterator f = cim.find( code );
563 if( f == cim.end() || 0 == f->second->dataList.size )
564 return;
565
566 MP4ItmfData& data = f->second->dataList.elements[0];
567
568 if( NULL == data.value )
569 return;
570
571 cpp = (uint64_t(data.value[0]) << 56)
572 | (uint64_t(data.value[1]) << 48)
573 | (uint64_t(data.value[2]) << 40)
574 | (uint64_t(data.value[3]) << 32)
575 | (uint64_t(data.value[4]) << 24)
576 | (uint64_t(data.value[5]) << 16)
577 | (uint64_t(data.value[6]) << 8)
578 | (uint64_t(data.value[7]) );
579
580 c = &cpp;
581 }
582
583 ///////////////////////////////////////////////////////////////////////////////
584
585 void
fetchString(const CodeItemMap & cim,const string & code,string & cpp,const char * & c)586 Tags::fetchString( const CodeItemMap& cim, const string& code, string& cpp, const char*& c )
587 {
588 cpp.clear();
589 c = NULL;
590
591 CodeItemMap::const_iterator f = cim.find( code );
592 if( f == cim.end() || 0 == f->second->dataList.size )
593 return;
594
595 MP4ItmfData& data = f->second->dataList.elements[0];
596
597 if( NULL == data.value )
598 return;
599
600 cpp.append( reinterpret_cast<char*>( data.value ), data.valueSize );
601 c = cpp.c_str();
602 }
603
604 ///////////////////////////////////////////////////////////////////////////////
605
606 void
remove(MP4File & file,const string & code)607 Tags::remove( MP4File& file, const string& code )
608 {
609 MP4ItmfItemList* itemList = genericGetItemsByCode( file, code ); // alloc
610
611 if( itemList->size )
612 genericRemoveItem( file, &itemList->elements[0] );
613
614 genericItemListFree( itemList ); // free
615 }
616
617 ///////////////////////////////////////////////////////////////////////////////
618
619 void
store(MP4File & file,const string & code,MP4ItmfBasicType basicType,const void * buffer,uint32_t size)620 Tags::store( MP4File& file, const string& code, MP4ItmfBasicType basicType, const void* buffer, uint32_t size )
621 {
622 // remove existing item
623 remove( file, code );
624
625 // add item
626 MP4ItmfItem& item = *genericItemAlloc( code, 1 ); // alloc
627 MP4ItmfData& data = item.dataList.elements[0];
628
629 data.typeCode = basicType;
630 data.valueSize = size;
631 data.value = (uint8_t*)malloc( data.valueSize );
632 memcpy( data.value, buffer, data.valueSize );
633
634 genericAddItem( file, &item );
635 genericItemFree( &item ); // free
636 }
637
638 ///////////////////////////////////////////////////////////////////////////////
639
640 void
storeGenre(MP4File & file,uint16_t cpp,const uint16_t * c)641 Tags::storeGenre( MP4File& file, uint16_t cpp, const uint16_t* c )
642 {
643 if( c ) {
644 uint8_t buf[2];
645
646 buf[0] = uint8_t((cpp & 0xff00) >> 8);
647 buf[1] = uint8_t((cpp & 0x00ff) );
648
649 // it's not clear if you must use implicit in these situations and iirc iTunes and other software are not consistent in this regard.
650 // many other tags must be integer type yet no issues there. Silly that iTunes insists it must be implict, which is then hardcoded
651 // to interpret as genres anyways.
652 store( file, CODE_GENRETYPE, MP4_ITMF_BT_IMPLICIT, buf, sizeof(buf) );
653 }
654 else {
655 remove( file, CODE_GENRETYPE );
656 }
657 }
658
659 ///////////////////////////////////////////////////////////////////////////////
660
661 void
storeDisk(MP4File & file,const MP4TagDisk & cpp,const MP4TagDisk * c)662 Tags::storeDisk( MP4File& file, const MP4TagDisk& cpp, const MP4TagDisk* c )
663 {
664 if( c ) {
665 uint8_t buf[6];
666 memset( buf, 0, sizeof(buf) );
667
668 buf[2] = uint8_t((cpp.index & 0xff00) >> 8);
669 buf[3] = uint8_t((cpp.index & 0x00ff) );
670 buf[4] = uint8_t((cpp.total & 0xff00) >> 8);
671 buf[5] = uint8_t((cpp.total & 0x00ff) );
672
673 store( file, CODE_DISK, MP4_ITMF_BT_IMPLICIT, buf, sizeof(buf) );
674 }
675 else {
676 remove( file, CODE_DISK );
677 }
678 }
679
680 ///////////////////////////////////////////////////////////////////////////////
681
682 void
storeTrack(MP4File & file,const MP4TagTrack & cpp,const MP4TagTrack * c)683 Tags::storeTrack( MP4File& file, const MP4TagTrack& cpp, const MP4TagTrack* c )
684 {
685 if( c ) {
686 uint8_t buf[8]; // iTMF spec says 7 but iTunes media is 8
687 memset( buf, 0, sizeof(buf) );
688
689 buf[2] = uint8_t((cpp.index & 0xff00) >> 8);
690 buf[3] = uint8_t((cpp.index & 0x00ff) );
691 buf[4] = uint8_t((cpp.total & 0xff00) >> 8);
692 buf[5] = uint8_t((cpp.total & 0x00ff) );
693
694 store( file, CODE_TRACK, MP4_ITMF_BT_IMPLICIT, buf, sizeof(buf) );
695 }
696 else {
697 remove( file, CODE_TRACK );
698 }
699 }
700
701 ///////////////////////////////////////////////////////////////////////////////
702
703 void
storeInteger(MP4File & file,const string & code,uint8_t cpp,const uint8_t * c)704 Tags::storeInteger( MP4File& file, const string& code, uint8_t cpp, const uint8_t* c )
705 {
706 if( c )
707 store( file, code, MP4_ITMF_BT_INTEGER, &cpp, sizeof(cpp) );
708 else
709 remove( file, code );
710 }
711
712 ///////////////////////////////////////////////////////////////////////////////
713
714 void
storeInteger(MP4File & file,const string & code,uint16_t cpp,const uint16_t * c)715 Tags::storeInteger( MP4File& file, const string& code, uint16_t cpp, const uint16_t* c )
716 {
717 if( c ) {
718 uint8_t buf[2];
719
720 buf[0] = uint8_t((cpp & 0xff00) >> 8);
721 buf[1] = uint8_t((cpp & 0x00ff) );
722
723 store( file, code, MP4_ITMF_BT_INTEGER, buf, sizeof(buf) );
724 }
725 else {
726 remove( file, code );
727 }
728 }
729
730
731 ///////////////////////////////////////////////////////////////////////////////
732
733 void
storeInteger(MP4File & file,const string & code,uint32_t cpp,const uint32_t * c)734 Tags::storeInteger( MP4File& file, const string& code, uint32_t cpp, const uint32_t* c )
735 {
736 if( c ) {
737 uint8_t buf[4];
738
739 buf[0] = uint8_t((cpp & 0xff000000) >> 24 );
740 buf[1] = uint8_t((cpp & 0x00ff0000) >> 16 );
741 buf[2] = uint8_t((cpp & 0x0000ff00) >> 8 );
742 buf[3] = uint8_t((cpp & 0x000000ff) );
743
744 store( file, code, MP4_ITMF_BT_INTEGER, buf, sizeof(buf) );
745 }
746 else {
747 remove( file, code );
748 }
749 }
750
751 ///////////////////////////////////////////////////////////////////////////////
752
753 void
storeInteger(MP4File & file,const string & code,uint64_t cpp,const uint64_t * c)754 Tags::storeInteger( MP4File& file, const string& code, uint64_t cpp, const uint64_t* c )
755 {
756 if( c ) {
757 uint8_t buf[8];
758
759 buf[0] = uint8_t((cpp & 0xff00000000000000LL) >> 56 );
760 buf[1] = uint8_t((cpp & 0x00ff000000000000LL) >> 48 );
761 buf[2] = uint8_t((cpp & 0x0000ff0000000000LL) >> 40 );
762 buf[3] = uint8_t((cpp & 0x000000ff00000000LL) >> 32 );
763 buf[4] = uint8_t((cpp & 0x00000000ff000000LL) >> 24 );
764 buf[5] = uint8_t((cpp & 0x0000000000ff0000LL) >> 16 );
765 buf[6] = uint8_t((cpp & 0x000000000000ff00LL) >> 8 );
766 buf[7] = uint8_t((cpp & 0x00000000000000ffLL) );
767
768 store( file, code, MP4_ITMF_BT_INTEGER, buf, sizeof(buf) );
769 }
770 else {
771 remove( file, code );
772 }
773 }
774
775 ///////////////////////////////////////////////////////////////////////////////
776
777 void
storeString(MP4File & file,const string & code,const string & cpp,const char * c)778 Tags::storeString( MP4File& file, const string& code, const string& cpp, const char* c )
779 {
780 if( c )
781 store( file, code, MP4_ITMF_BT_UTF8, cpp.c_str(), (uint32_t)cpp.size() );
782 else
783 remove( file, code );
784 }
785
786 ///////////////////////////////////////////////////////////////////////////////
787
788 void
updateArtworkShadow(MP4Tags * & tags)789 Tags::updateArtworkShadow( MP4Tags*& tags )
790 {
791 if( tags->artwork ) {
792 delete[] tags->artwork;
793 tags->artwork = NULL;
794 tags->artworkCount = 0;
795 }
796
797 if( artwork.empty() )
798 return;
799
800 MP4TagArtwork* const cartwork = new MP4TagArtwork[ artwork.size() ];
801 uint32_t max = (uint32_t)artwork.size();
802
803 for( uint32_t i = 0; i < max; i++ ) {
804 MP4TagArtwork& a = cartwork[i];
805 CoverArtBox::Item& item = artwork[i];
806
807 a.data = item.buffer;
808 a.size = item.size;
809
810 switch( item.type ) {
811 case BT_BMP:
812 a.type = MP4_ART_BMP;
813 break;
814
815 case BT_GIF:
816 a.type = MP4_ART_GIF;
817 break;
818
819 case BT_JPEG:
820 a.type = MP4_ART_JPEG;
821 break;
822
823 case BT_PNG:
824 a.type = MP4_ART_PNG;
825 break;
826
827 default:
828 a.type = MP4_ART_UNDEFINED;
829 break;
830 }
831 }
832
833 tags->artwork = cartwork;
834 tags->artworkCount = max;
835 }
836
837 ///////////////////////////////////////////////////////////////////////////////
838
839 const string Tags::CODE_NAME = "\xa9" "nam";
840 const string Tags::CODE_ARTIST = "\xa9" "ART";
841 const string Tags::CODE_ALBUMARTIST = "aART";
842 const string Tags::CODE_ALBUM = "\xa9" "alb";
843 const string Tags::CODE_GROUPING = "\xa9" "grp";
844 const string Tags::CODE_COMPOSER = "\xa9" "wrt";
845 const string Tags::CODE_COMMENTS = "\xa9" "cmt";
846 const string Tags::CODE_GENRE = "\xa9" "gen";
847 const string Tags::CODE_GENRETYPE = "gnre";
848 const string Tags::CODE_RELEASEDATE = "\xa9" "day";
849 const string Tags::CODE_TRACK = "trkn";
850 const string Tags::CODE_DISK = "disk";
851 const string Tags::CODE_TEMPO = "tmpo";
852 const string Tags::CODE_COMPILATION = "cpil";
853
854 const string Tags::CODE_TVSHOW = "tvsh";
855 const string Tags::CODE_TVNETWORK = "tvnn";
856 const string Tags::CODE_TVEPISODEID = "tven";
857 const string Tags::CODE_TVSEASON = "tvsn";
858 const string Tags::CODE_TVEPISODE = "tves";
859
860 const string Tags::CODE_DESCRIPTION = "desc";
861 const string Tags::CODE_LONGDESCRIPTION = "ldes";
862 const string Tags::CODE_LYRICS = "\xa9" "lyr";
863
864 const string Tags::CODE_SORTNAME = "sonm";
865 const string Tags::CODE_SORTARTIST = "soar";
866 const string Tags::CODE_SORTALBUMARTIST = "soaa";
867 const string Tags::CODE_SORTALBUM = "soal";
868 const string Tags::CODE_SORTCOMPOSER = "soco";
869 const string Tags::CODE_SORTTVSHOW = "sosn";
870
871 const string Tags::CODE_COPYRIGHT = "cprt";
872 const string Tags::CODE_ENCODINGTOOL = "\xa9" "too";
873 const string Tags::CODE_ENCODEDBY = "\xa9" "enc";
874 const string Tags::CODE_PURCHASEDATE = "purd";
875
876 const string Tags::CODE_PODCAST = "pcst";
877 const string Tags::CODE_KEYWORDS = "keyw";
878 const string Tags::CODE_CATEGORY = "catg";
879
880 const string Tags::CODE_HDVIDEO = "hdvd";
881 const string Tags::CODE_MEDIATYPE = "stik";
882 const string Tags::CODE_CONTENTRATING = "rtng";
883 const string Tags::CODE_GAPLESS = "pgap";
884
885 const string Tags::CODE_ITUNESACCOUNT = "apID";
886 const string Tags::CODE_ITUNESACCOUNTTYPE = "akID";
887 const string Tags::CODE_ITUNESCOUNTRY = "sfID";
888 const string Tags::CODE_CONTENTID = "cnID";
889 const string Tags::CODE_ARTISTID = "atID";
890 const string Tags::CODE_PLAYLISTID = "plID";
891 const string Tags::CODE_GENREID = "geID";
892 const string Tags::CODE_COMPOSERID = "cmID";
893 const string Tags::CODE_XID = "xid ";
894
895 ///////////////////////////////////////////////////////////////////////////////
896
897 }}} // namespace mp4v2::impl::itmf
898