1 /*
2 * The contents of this file are subject to the Mozilla Public
3 * License Version 1.1 (the "License"); you may not use this file
4 * except in compliance with the License. You may obtain a copy of
5 * the License at http://www.mozilla.org/MPL/
6 *
7 * Software distributed under the License is distributed on an "AS
8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9 * implied. See the License for the specific language governing
10 * rights and limitations under the License.
11 *
12 * The Original Code is MPEG4IP.
13 *
14 * The Initial Developer of the Original Code is Cisco Systems Inc.
15 * Portions created by Cisco Systems Inc. are
16 * Copyright (C) Cisco Systems Inc. 2001. All Rights Reserved.
17 *
18 * Contributor(s):
19 * M. Bakker mbakker at nero.com
20 *
21 * Apple iTunes Metadata handling
22 */
23
24 /**
25
26 The iTunes tagging seems to support any tag field name
27 but there are some predefined fields, also known from the QuickTime format
28
29 predefined fields (the ones I know of until now):
30 - �nam : Name of the song/movie (string)
31 - �ART : Name of the artist/performer (string)
32 - aART : Album artist
33 - �wrt : Name of the writer (string)
34 - �alb : Name of the album (string)
35 - �day : Year (4 bytes, e.g. "2003") (string)
36 - �too : Tool(s) used to create the file (string)
37 - �cmt : Comment (string)
38 - �gen : Custom genre (string)
39 - �grp : Grouping (string)
40 - trkn : Tracknumber (8 byte string)
41 16 bit: empty
42 16 bit: tracknumber
43 16 bit: total tracks on album
44 16 bit: empty
45 - disk : Disknumber (8 byte string)
46 16 bit: empty
47 16 bit: disknumber
48 16 bit: total number of disks
49 16 bit: empty
50 - gnre : Genre (16 bit genre) (ID3v1 index + 1)
51 - cpil : Part of a compilation (1 byte, 1 or 0)
52 - tmpo : Tempo in BPM (16 bit)
53 - covr : Cover art (xx bytes binary data)
54 - ---- : Free form metadata, can have any name and any data
55 - pgap : gapless - 8 bit boolean
56
57 - apID : purchaser name.
58 - cprt : copyright
59 - purd : purchase date.
60
61 **/
62
63 #include "mp4common.h"
64
GetMetadataByIndex(u_int32_t index,char ** ppName,u_int8_t ** ppValue,u_int32_t * pValueSize)65 bool MP4File::GetMetadataByIndex(u_int32_t index,
66 char** ppName,
67 u_int8_t** ppValue, u_int32_t* pValueSize)
68 {
69 char s[256];
70
71 snprintf(s, 256, "moov.udta.meta.ilst.*[%u].data.metadata", index);
72 GetBytesProperty(s, ppValue, pValueSize);
73
74 snprintf(s, 256, "moov.udta.meta.ilst.*[%u]", index);
75 MP4Atom* pParent = m_pRootAtom->FindAtom(s);
76 if (pParent == NULL) return false;
77
78 /* check for free form tagfield */
79 if (memcmp(*ppName, "----", 4) == 0)
80 {
81 u_int8_t* pV;
82 u_int32_t VSize = 0;
83 char *pN;
84
85 snprintf(s, 256, "moov.udta.meta.ilst.*[%u].name.metadata", index);
86 GetBytesProperty(s, &pV, &VSize);
87
88 pN = (char*)malloc((VSize+1)*sizeof(char));
89 if (pN != NULL) {
90 memset(pN, 0, (VSize+1)*sizeof(char));
91 memcpy(pN, pV, VSize*sizeof(char));
92 }
93 free(pV);
94 *ppName = pN;
95 } else {
96 *ppName = strdup(pParent->GetType());
97 }
98
99 return true;
100 }
101
CreateMetadataAtom(const char * name)102 bool MP4File::CreateMetadataAtom(const char* name)
103 {
104 char s[256];
105 char t[256];
106
107 snprintf(t, 256, "udta.meta.ilst.%s.data", name);
108 snprintf(s, 256, "moov.udta.meta.ilst.%s.data", name);
109 (void)AddDescendantAtoms("moov", t);
110 MP4Atom *pMetaAtom = m_pRootAtom->FindAtom(s);
111
112 if (!pMetaAtom)
113 return false;
114
115 /* some fields need special flags set */
116 if ((uint8_t)name[0] == 0251 || ATOMID(name) == ATOMID("aART"))
117 {
118 pMetaAtom->SetFlags(0x1);
119 } else if ((memcmp(name, "cpil", 4) == 0) || (memcmp(name, "tmpo", 4) == 0)) {
120 pMetaAtom->SetFlags(0x15);
121 }
122
123 MP4Atom *pHdlrAtom = m_pRootAtom->FindAtom("moov.udta.meta.hdlr");
124 MP4StringProperty *pStringProperty = NULL;
125 MP4BytesProperty *pBytesProperty = NULL;
126 ASSERT(pHdlrAtom);
127
128 ASSERT(pHdlrAtom->FindProperty("hdlr.handlerType",
129 (MP4Property**)&pStringProperty));
130 ASSERT(pStringProperty);
131 pStringProperty->SetValue("mdir");
132
133 u_int8_t val[12];
134 memset(val, 0, 12*sizeof(u_int8_t));
135 val[0] = 0x61;
136 val[1] = 0x70;
137 val[2] = 0x70;
138 val[3] = 0x6c;
139 ASSERT(pHdlrAtom->FindProperty("hdlr.reserved2",
140 (MP4Property**)&pBytesProperty));
141 ASSERT(pBytesProperty);
142 pBytesProperty->SetReadOnly(false);
143 pBytesProperty->SetValue(val, 12);
144 pBytesProperty->SetReadOnly(true);
145
146 return true;
147 }
148
DeleteMetadataAtom(const char * name,bool try_udta)149 bool MP4File::DeleteMetadataAtom(const char* name, bool try_udta)
150 {
151 MP4Atom *pMetaAtom = NULL;
152 char s[256];
153
154 snprintf(s, 256, "moov.udta.meta.ilst.%s", name);
155 pMetaAtom = m_pRootAtom->FindAtom(s);
156
157 if (pMetaAtom == NULL && try_udta) {
158 snprintf(s, 256, "moov.udta.%s", name);
159 pMetaAtom = m_pRootAtom->FindAtom(s);
160 }
161 /* if it exists, delete it */
162 if (pMetaAtom)
163 {
164 MP4Atom *pParent = pMetaAtom->GetParentAtom();
165
166 pParent->DeleteChildAtom(pMetaAtom);
167
168 delete pMetaAtom;
169
170 return true;
171 }
172
173 return false;
174 }
175
SetMetadataString(const char * atom,const char * value)176 bool MP4File::SetMetadataString (const char *atom, const char *value)
177 {
178 char atomstring[40];
179 MP4Atom *pMetaAtom;
180 MP4BytesProperty *pMetadataProperty = NULL;
181 snprintf(atomstring, 40, "moov.udta.meta.ilst.%s.data", atom);
182
183 pMetaAtom = m_pRootAtom->FindAtom(atomstring);
184
185 if (!pMetaAtom)
186 {
187 if (!CreateMetadataAtom(atom))
188 return false;
189
190 pMetaAtom = m_pRootAtom->FindAtom(atomstring);
191 if (pMetaAtom == NULL) return false;
192 }
193
194 ASSERT(pMetaAtom->FindProperty("data.metadata",
195 (MP4Property**)&pMetadataProperty));
196 ASSERT(pMetadataProperty);
197
198 pMetadataProperty->SetValue((u_int8_t*)value, strlen(value));
199
200 return true;
201 }
202
GetMetadataString(const char * atom,char ** value,bool try_udta)203 bool MP4File::GetMetadataString (const char *atom, char **value, bool try_udta)
204 {
205 unsigned char *val = NULL;
206 u_int32_t valSize = 0;
207 char atomstring[60];
208 snprintf(atomstring, 60, "moov.udta.meta.ilst.%s.data.metadata", atom);
209
210 *value = NULL;
211 if (try_udta == false) {
212 GetBytesProperty(atomstring, (u_int8_t**)&val, &valSize);
213 } else {
214 bool got_it = false;
215 try {
216 GetBytesProperty(atomstring, (u_int8_t**)&val, &valSize);
217 got_it = true;
218 }
219 catch (MP4Error* e) {
220 delete e;
221 }
222 if (got_it == false) {
223 snprintf(atomstring, 60, "moov.udta.%s.metadata", atom);
224 GetBytesProperty(atomstring, (u_int8_t**)&val, &valSize);
225 }
226 }
227 if (valSize > 0)
228 {
229 *value = (char*)malloc((valSize+1)*sizeof(char));
230 if (*value == NULL) {
231 free(val);
232 return false;
233 }
234 memcpy(*value, val, valSize*sizeof(unsigned char));
235 free(val);
236 (*value)[valSize] = '\0';
237 return true;
238 }
239 return false;
240 }
241
SetMetadataTrack(u_int16_t track,u_int16_t totalTracks)242 bool MP4File::SetMetadataTrack(u_int16_t track, u_int16_t totalTracks)
243 {
244 unsigned char t[9];
245 const char *s = "moov.udta.meta.ilst.trkn.data";
246 MP4BytesProperty *pMetadataProperty = NULL;
247 MP4Atom *pMetaAtom = NULL;
248
249 pMetaAtom = m_pRootAtom->FindAtom(s);
250
251 if (!pMetaAtom)
252 {
253 if (!CreateMetadataAtom("trkn"))
254 return false;
255
256 pMetaAtom = m_pRootAtom->FindAtom(s);
257 if (pMetaAtom == NULL) return false;
258 }
259
260 memset(t, 0, 9*sizeof(unsigned char));
261 t[2] = (unsigned char)(track>>8)&0xFF;
262 t[3] = (unsigned char)(track)&0xFF;
263 t[4] = (unsigned char)(totalTracks>>8)&0xFF;
264 t[5] = (unsigned char)(totalTracks)&0xFF;
265
266 ASSERT(pMetaAtom->FindProperty("data.metadata",
267 (MP4Property**)&pMetadataProperty));
268 ASSERT(pMetadataProperty);
269
270 pMetadataProperty->SetValue((u_int8_t*)t, 8);
271
272 return true;
273 }
274
GetMetadataTrack(u_int16_t * track,u_int16_t * totalTracks)275 bool MP4File::GetMetadataTrack(u_int16_t* track, u_int16_t* totalTracks)
276 {
277 unsigned char *val = NULL;
278 u_int32_t valSize = 0;
279 const char *s = "moov.udta.meta.ilst.trkn.data.metadata";
280
281 *track = 0;
282 *totalTracks = 0;
283
284 GetBytesProperty(s, (u_int8_t**)&val, &valSize);
285
286 if (valSize == 8) {
287 *track = (u_int16_t)(val[3]);
288 *track += (u_int16_t)(val[2]<<8);
289 *totalTracks = (u_int16_t)(val[5]);
290 *totalTracks += (u_int16_t)(val[4]<<8);
291 CHECK_AND_FREE(val);
292 return true;
293 }
294 CHECK_AND_FREE(val);
295 return false;
296 }
297
SetMetadataDisk(u_int16_t disk,u_int16_t totalDisks)298 bool MP4File::SetMetadataDisk(u_int16_t disk, u_int16_t totalDisks)
299 {
300 unsigned char t[7];
301 const char *s = "moov.udta.meta.ilst.disk.data";
302 MP4BytesProperty *pMetadataProperty = NULL;
303 MP4Atom *pMetaAtom = NULL;
304
305 pMetaAtom = m_pRootAtom->FindAtom(s);
306
307 if (!pMetaAtom)
308 {
309 if (!CreateMetadataAtom("disk"))
310 return false;
311
312 pMetaAtom = m_pRootAtom->FindAtom(s);
313 if (pMetaAtom == NULL) return false;
314 }
315
316 memset(t, 0, 7*sizeof(unsigned char));
317 t[2] = (unsigned char)(disk>>8)&0xFF;
318 t[3] = (unsigned char)(disk)&0xFF;
319 t[4] = (unsigned char)(totalDisks>>8)&0xFF;
320 t[5] = (unsigned char)(totalDisks)&0xFF;
321
322 ASSERT(pMetaAtom->FindProperty("data.metadata",
323 (MP4Property**)&pMetadataProperty));
324 ASSERT(pMetadataProperty);
325
326 pMetadataProperty->SetValue((u_int8_t*)t, 6);
327
328 return true;
329 }
330
GetMetadataDisk(u_int16_t * disk,u_int16_t * totalDisks)331 bool MP4File::GetMetadataDisk(u_int16_t* disk, u_int16_t* totalDisks)
332 {
333 unsigned char *val = NULL;
334 u_int32_t valSize = 0;
335 const char *s = "moov.udta.meta.ilst.disk.data.metadata";
336
337 *disk = 0;
338 *totalDisks = 0;
339
340 GetBytesProperty(s, (u_int8_t**)&val, &valSize);
341
342 if (valSize == 6 || valSize == 8) {
343 *disk = (u_int16_t)(val[3]);
344 *disk += (u_int16_t)(val[2]<<8);
345 *totalDisks = (u_int16_t)(val[5]);
346 *totalDisks += (u_int16_t)(val[4]<<8);
347 free(val);
348 return true;
349 }
350 CHECK_AND_FREE(val);
351 return true;
352 }
353
354 static const char* ID3v1GenreList[] = {
355 "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk",
356 "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies",
357 "Other", "Pop", "R&B", "Rap", "Reggae", "Rock",
358 "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks",
359 "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk",
360 "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House",
361 "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass",
362 "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock",
363 "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk",
364 "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta",
365 "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
366 "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi",
367 "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical",
368 "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk", "Swing",
369 "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde",
370 "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
371 "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
372 "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
373 "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
374 "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet",
375 "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall",
376 "Goa", "Drum & Bass", "Club House", "Hardcore", "Terror",
377 "Indie", "BritPop", "NegerPunk", "Polsk Punk", "Beat",
378 "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover", "Contemporary C",
379 "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop",
380 "SynthPop",
381 };
382
GenreToString(char ** GenreStr,const int genre)383 void GenreToString(char** GenreStr, const int genre)
384 {
385 if (genre > 0 &&
386 genre <= (int)(sizeof(ID3v1GenreList)/sizeof(*ID3v1GenreList)))
387 {
388 uint len = strlen(ID3v1GenreList[genre-1])+1;
389 *GenreStr = (char*)malloc(len);
390 if (*GenreStr == NULL) return;
391 // no need for strncpy; enough was malloced
392 strcpy(*GenreStr, ID3v1GenreList[genre-1]);
393 return;
394 }
395 *GenreStr = (char*)malloc(2*sizeof(char));
396 if (*GenreStr == NULL) return;
397 memset(*GenreStr, 0, 2*sizeof(char));
398 return;
399 }
400
StringToGenre(const char * GenreStr)401 int StringToGenre(const char* GenreStr)
402 {
403 unsigned int i;
404
405 for (i = 0; i < sizeof(ID3v1GenreList)/sizeof(*ID3v1GenreList); i++)
406 {
407 if (strcasecmp(GenreStr, ID3v1GenreList[i]) == 0)
408 return i+1;
409 }
410 return 0;
411 }
412
SetMetadataGenre(const char * value)413 bool MP4File::SetMetadataGenre(const char* value)
414 {
415 u_int16_t genreIndex = 0;
416 unsigned char t[3];
417 MP4BytesProperty *pMetadataProperty = NULL;
418 MP4Atom *pMetaAtom = NULL;
419
420 genreIndex = StringToGenre(value);
421
422 const char *s = "moov.udta.meta.ilst.gnre.data";
423 const char *sroot = "moov.udta.meta.ilst.gnre";
424 const char *s2 = "moov.udta.meta.ilst.\251gen.data";
425 const char *s2root = "moov.udta.meta.ilst.\251gen";
426 if (genreIndex != 0)
427 {
428 pMetaAtom = m_pRootAtom->FindAtom(s);
429 if (!pMetaAtom)
430 {
431 if (!CreateMetadataAtom("gnre"))
432 return false;
433
434 pMetaAtom = m_pRootAtom->FindAtom(s);
435 if (pMetaAtom == NULL) return false;
436 }
437
438 memset(t, 0, 3*sizeof(unsigned char));
439 t[0] = (unsigned char)(genreIndex>>8)&0xFF;
440 t[1] = (unsigned char)(genreIndex)&0xFF;
441
442 ASSERT(pMetaAtom->FindProperty("data.metadata",
443 (MP4Property**)&pMetadataProperty));
444 ASSERT(pMetadataProperty);
445
446 pMetadataProperty->SetValue((u_int8_t*)t, 2);
447
448 // remove other style of genre atom, if this one is added
449 pMetaAtom = m_pRootAtom->FindAtom(s2root);
450 if (pMetaAtom != NULL) {
451 MP4Atom *pParent = pMetaAtom->GetParentAtom();
452 if (pParent != NULL) {
453 pParent->DeleteChildAtom(pMetaAtom);
454 delete pMetaAtom;
455 }
456 }
457
458
459 (void)DeleteMetadataAtom( "\251gen" );
460
461 return true;
462 } else {
463 pMetaAtom = m_pRootAtom->FindAtom(s2);
464
465 if (!pMetaAtom)
466 {
467 if (!CreateMetadataAtom("\251gen"))
468 return false;
469
470 pMetaAtom = m_pRootAtom->FindAtom(s2);
471 }
472
473 ASSERT(pMetaAtom->FindProperty("data.metadata",
474 (MP4Property**)&pMetadataProperty));
475 ASSERT(pMetadataProperty);
476
477 pMetadataProperty->SetValue((u_int8_t*)value, strlen(value));
478
479 // remove other gnre atom if this one is entered
480 pMetaAtom = m_pRootAtom->FindAtom(sroot);
481 if (pMetaAtom != NULL) {
482 MP4Atom *pParent = pMetaAtom->GetParentAtom();
483 pParent->DeleteChildAtom(pMetaAtom);
484 delete pMetaAtom;
485 }
486 return true;
487 }
488
489 return false;
490 }
491
GetMetadataGenre(char ** value)492 bool MP4File::GetMetadataGenre(char** value)
493 {
494 u_int16_t genreIndex = 0;
495 unsigned char *val = NULL;
496 u_int32_t valSize = 0;
497 const char *t = "moov.udta.meta.ilst.gnre";
498 const char *s = "moov.udta.meta.ilst.gnre.data.metadata";
499
500 *value = NULL;
501
502 MP4Atom *gnre = FindAtom(t);
503
504 if (gnre)
505 {
506 GetBytesProperty(s, (u_int8_t**)&val, &valSize);
507
508 if (valSize != 2) {
509 CHECK_AND_FREE(val);
510 return false;
511 }
512
513 genreIndex = (u_int16_t)(val[1]);
514 genreIndex += (u_int16_t)(val[0]<<8);
515
516 GenreToString(value, genreIndex);
517
518 (void)DeleteMetadataAtom( "gnre" );
519 free(val);
520 return true;
521 } else {
522 const char *s2 = "moov.udta.meta.ilst.\251gen.data.metadata";
523
524 val = NULL;
525 valSize = 0;
526
527 GetBytesProperty(s2, (u_int8_t**)&val, &valSize);
528
529 if (valSize > 0)
530 {
531 *value = (char*)malloc((valSize+1)*sizeof(unsigned char));
532 if (*value != NULL) {
533 memset(*value, 0, (valSize+1)*sizeof(unsigned char));
534 memcpy(*value, val, valSize*sizeof(unsigned char));
535 }
536 free(val);
537 return true;
538 } else {
539 CHECK_AND_FREE(val);
540 }
541 }
542
543 return false;
544 }
545
DeleteMetadataGenre()546 bool MP4File::DeleteMetadataGenre()
547 {
548 bool val1 = DeleteMetadataAtom("\251gen");
549 bool val2 = DeleteMetadataAtom("gnre");
550 return val1 || val2;
551 }
552
SetMetadataTempo(u_int16_t tempo)553 bool MP4File::SetMetadataTempo(u_int16_t tempo)
554 {
555 unsigned char t[3];
556 const char *s = "moov.udta.meta.ilst.tmpo.data";
557 MP4BytesProperty *pMetadataProperty = NULL;
558 MP4Atom *pMetaAtom = NULL;
559
560 pMetaAtom = m_pRootAtom->FindAtom(s);
561
562 if (!pMetaAtom)
563 {
564 if (!CreateMetadataAtom("tmpo"))
565 return false;
566
567 pMetaAtom = m_pRootAtom->FindAtom(s);
568 if (pMetaAtom == NULL) return false;
569 }
570
571 memset(t, 0, 3*sizeof(unsigned char));
572 t[0] = (unsigned char)(tempo>>8)&0xFF;
573 t[1] = (unsigned char)(tempo)&0xFF;
574
575 ASSERT(pMetaAtom->FindProperty("data.metadata",
576 (MP4Property**)&pMetadataProperty));
577 ASSERT(pMetadataProperty);
578
579 pMetadataProperty->SetValue((u_int8_t*)t, 2);
580
581 return true;
582 }
583
GetMetadataTempo(u_int16_t * tempo)584 bool MP4File::GetMetadataTempo(u_int16_t* tempo)
585 {
586 unsigned char *val = NULL;
587 u_int32_t valSize = 0;
588 const char *s = "moov.udta.meta.ilst.tmpo.data.metadata";
589
590 *tempo = 0;
591
592 GetBytesProperty(s, (u_int8_t**)&val, &valSize);
593
594 if (valSize != 2) {
595 CHECK_AND_FREE(val);
596 return false;
597 }
598
599 *tempo = (u_int16_t)(val[1]);
600 *tempo += (u_int16_t)(val[0]<<8);
601 free(val);
602 return true;
603 }
SetMetadataUint8(const char * atom,uint8_t value)604 bool MP4File::SetMetadataUint8 (const char *atom, uint8_t value)
605 {
606 char atompath[36];
607 MP4BytesProperty *pMetadataProperty = NULL;
608 MP4Atom *pMetaAtom = NULL;
609
610 snprintf(atompath, 36, "moov.udta.meta.ilst.%s.data", atom);
611
612 pMetaAtom = m_pRootAtom->FindAtom(atompath);
613
614 if (pMetaAtom == NULL) {
615 if (!CreateMetadataAtom(atom))
616 return false;
617
618 pMetaAtom = m_pRootAtom->FindAtom(atompath);
619 if (pMetaAtom == NULL) return false;
620 }
621
622 ASSERT(pMetaAtom->FindProperty("data.metadata",
623 (MP4Property**)&pMetadataProperty));
624 ASSERT(pMetadataProperty);
625
626 pMetadataProperty->SetValue(&value, 1);
627
628 return true;
629 }
630
631
GetMetadataUint8(const char * atom,u_int8_t * retvalue)632 bool MP4File::GetMetadataUint8(const char *atom, u_int8_t* retvalue)
633 {
634 unsigned char *val = NULL;
635 u_int32_t valSize = 0;
636 char atompath[80];
637 snprintf(atompath, 80, "moov.udta.meta.ilst.%s.data.metadata", atom);
638
639 *retvalue = 0;
640
641 GetBytesProperty(atompath, (u_int8_t**)&val, &valSize);
642
643 if (valSize != 1) {
644 CHECK_AND_FREE(val);
645 return false;
646 }
647
648 *retvalue = val[0];
649 free(val);
650 return true;
651 }
652
SetMetadataCoverArt(u_int8_t * coverArt,u_int32_t size)653 bool MP4File::SetMetadataCoverArt(u_int8_t *coverArt, u_int32_t size)
654 {
655 const char *s = "moov.udta.meta.ilst.covr.data";
656 MP4BytesProperty *pMetadataProperty = NULL;
657 MP4Atom *pMetaAtom = NULL;
658
659 pMetaAtom = m_pRootAtom->FindAtom(s);
660
661 if (!pMetaAtom)
662 {
663 if (!CreateMetadataAtom("covr"))
664 return false;
665
666 pMetaAtom = m_pRootAtom->FindAtom(s);
667 if (pMetaAtom == NULL) return false;
668 }
669
670 ASSERT(pMetaAtom->FindProperty("data.metadata",
671 (MP4Property**)&pMetadataProperty));
672 ASSERT(pMetadataProperty);
673
674 pMetadataProperty->SetValue(coverArt, size);
675
676 return true;
677 }
678
GetMetadataCoverArt(u_int8_t ** coverArt,u_int32_t * size,uint32_t index)679 bool MP4File::GetMetadataCoverArt(u_int8_t **coverArt, u_int32_t *size,
680 uint32_t index)
681 {
682 char buffer[256];
683 if (size == NULL || coverArt == NULL) return false;
684
685 if (index > 0 && index > GetMetadataCoverArtCount()) return false;
686
687 snprintf(buffer, 256, "moov.udta.meta.ilst.covr.data[%d].metadata", index);
688
689 *coverArt = NULL;
690 *size = 0;
691
692 GetBytesProperty(buffer, coverArt, size);
693
694 if (size == 0)
695 return false;
696
697 return true;
698 }
699
GetMetadataCoverArtCount(void)700 u_int32_t MP4File::GetMetadataCoverArtCount (void)
701 {
702 MP4Atom *pMetaAtom = m_pRootAtom->FindAtom("moov.udta.meta.ilst.covr");
703 if (!pMetaAtom)
704 return 0;
705
706 return pMetaAtom->GetNumberOfChildAtoms();
707 }
708
SetMetadataFreeForm(const char * name,const u_int8_t * pValue,u_int32_t valueSize,const char * owner)709 bool MP4File::SetMetadataFreeForm (const char *name,
710 const u_int8_t* pValue,
711 u_int32_t valueSize,
712 const char *owner)
713 {
714 MP4Atom *pMetaAtom = NULL;
715 MP4BytesProperty *pMetadataProperty = NULL;
716 char s[256];
717 int i = 0;
718 size_t nameLen = strlen(name);
719 size_t ownerLen = owner != NULL ? strlen(owner) : 0;
720
721 while (1)
722 {
723 snprintf(s, 256, "moov.udta.meta.ilst.----[%u].name", i);
724
725 MP4Atom *pTagAtom = m_pRootAtom->FindAtom(s);
726
727 if (!pTagAtom)
728 break;
729
730 snprintf(s, 256, "moov.udta.meta.ilst.----[%u].mean", i);
731
732 MP4Atom *pMeanAtom = m_pRootAtom->FindAtom(s);
733
734 if (pTagAtom->FindProperty("name.metadata",
735 (MP4Property**)&pMetadataProperty) &&
736 pMetadataProperty) {
737 u_int8_t* pV;
738 u_int32_t VSize = 0;
739
740 pMetadataProperty->GetValue(&pV, &VSize);
741
742 if (VSize == nameLen && memcmp(pV, name, VSize) == 0) {
743 u_int8_t* pOwner=0;
744 u_int32_t ownerSize = 0;
745
746 if (pMeanAtom &&
747 pMeanAtom->FindProperty("mean.metadata",
748 (MP4Property**)&pMetadataProperty) &&
749 pMetadataProperty) {
750 pMetadataProperty->GetValue(&pOwner, &ownerSize);
751 }
752
753 if (owner == NULL||
754 (pOwner &&
755 ownerLen == ownerSize &&
756 memcmp(owner, pOwner, ownerSize))) {
757 snprintf(s, 256, "moov.udta.meta.ilst.----[%u].data.metadata", i);
758 SetBytesProperty(s, pValue, valueSize);
759 CHECK_AND_FREE(pV);
760 CHECK_AND_FREE(pOwner);
761
762 return true;
763 }
764 CHECK_AND_FREE(pOwner);
765 }
766 CHECK_AND_FREE(pV);
767 }
768
769 i++;
770 }
771
772 /* doesn't exist yet, create it */
773 char t[256];
774
775 snprintf(t, 256, "udta.meta.ilst.----[%u]", i);
776 snprintf(s, 256, "moov.udta.meta.ilst.----[%u].data", i);
777 (void)AddDescendantAtoms("moov", t);
778
779 pMetaAtom = m_pRootAtom->FindAtom(s);
780
781 if (!pMetaAtom)
782 return false;
783
784 pMetaAtom->SetFlags(0x1);
785
786 MP4Atom *pHdlrAtom = m_pRootAtom->FindAtom("moov.udta.meta.hdlr");
787 MP4StringProperty *pStringProperty = NULL;
788 MP4BytesProperty *pBytesProperty = NULL;
789 ASSERT(pHdlrAtom);
790
791 ASSERT(pHdlrAtom->FindProperty("hdlr.handlerType",
792 (MP4Property**)&pStringProperty));
793 ASSERT(pStringProperty);
794 pStringProperty->SetValue("mdir");
795
796 u_int8_t val[12];
797 memset(val, 0, 12*sizeof(u_int8_t));
798 val[0] = 0x61;
799 val[1] = 0x70;
800 val[2] = 0x70;
801 val[3] = 0x6c;
802 ASSERT(pHdlrAtom->FindProperty("hdlr.reserved2",
803 (MP4Property**)&pBytesProperty));
804 ASSERT(pBytesProperty);
805 pBytesProperty->SetReadOnly(false);
806 pBytesProperty->SetValue(val, 12);
807 pBytesProperty->SetReadOnly(true);
808
809 pMetaAtom = m_pRootAtom->FindAtom(s);
810 ASSERT(pMetaAtom);
811 ASSERT(pMetaAtom->FindProperty("data.metadata",
812 (MP4Property**)&pMetadataProperty));
813 ASSERT(pMetadataProperty);
814 pMetadataProperty->SetValue(pValue, valueSize);
815
816 snprintf(s, 256, "moov.udta.meta.ilst.----[%u].name", i);
817 pMetaAtom = m_pRootAtom->FindAtom(s);
818 ASSERT(pMetaAtom->FindProperty("name.metadata",
819 (MP4Property**)&pMetadataProperty));
820 ASSERT(pMetadataProperty);
821 pMetadataProperty->SetValue((const u_int8_t*)name, strlen(name));
822
823 snprintf(s, 256, "moov.udta.meta.ilst.----[%u].mean", i);
824 pMetaAtom = m_pRootAtom->FindAtom(s);
825 ASSERT(pMetaAtom->FindProperty("mean.metadata",
826 (MP4Property**)&pMetadataProperty));
827 ASSERT(pMetadataProperty);
828 if (!owner || !*owner)
829 pMetadataProperty->SetValue((u_int8_t*)"com.apple.iTunes", 16); /* com.apple.iTunes is the default*/
830 else
831 pMetadataProperty->SetValue((const u_int8_t*)owner, strlen((const char *)owner));
832
833 return true;
834 }
835
GetMetadataFreeForm(const char * name,u_int8_t ** ppValue,u_int32_t * pValueSize,const char * owner)836 bool MP4File::GetMetadataFreeForm(const char *name,
837 u_int8_t** ppValue,
838 u_int32_t *pValueSize,
839 const char *owner)
840 {
841 char s[256];
842 int i = 0;
843
844 *ppValue = NULL;
845 *pValueSize = 0;
846
847 size_t nameLen = strlen(name);
848 size_t ownerLen = owner?strlen(owner):0;
849
850 while (1)
851 {
852 MP4BytesProperty *pMetadataProperty;
853
854 snprintf(s, 256,"moov.udta.meta.ilst.----[%u].name", i);
855 MP4Atom *pTagAtom = m_pRootAtom->FindAtom(s);
856
857 snprintf(s, 256,"moov.udta.meta.ilst.----[%u].mean", i);
858 MP4Atom *pMeanAtom = m_pRootAtom->FindAtom(s);
859
860 if (!pTagAtom)
861 return false;
862
863 if (pTagAtom->FindProperty("name.metadata",
864 (MP4Property**)&pMetadataProperty) &&
865 pMetadataProperty) {
866 u_int8_t* pV;
867 u_int32_t VSize = 0;
868
869 pMetadataProperty->GetValue(&pV, &VSize);
870
871 if (VSize == nameLen && memcmp(pV, name, VSize) == 0) {
872 u_int8_t* pOwner=0;
873 u_int32_t ownerSize = 0;
874
875 if (pMeanAtom && pMeanAtom->FindProperty("mean.metadata",
876 (MP4Property**)&pMetadataProperty) &&
877 pMetadataProperty) {
878 pMetadataProperty->GetValue(&pOwner, &ownerSize);
879 }
880
881 if (!owner || (pOwner && ownerLen == ownerSize && memcmp(owner, pOwner, ownerSize))) {
882 snprintf(s, 256, "moov.udta.meta.ilst.----[%u].data.metadata", i);
883 GetBytesProperty(s, ppValue, pValueSize);
884 CHECK_AND_FREE(pV);
885 CHECK_AND_FREE(pOwner);
886 return true;
887 }
888 CHECK_AND_FREE(pOwner);
889 }
890 CHECK_AND_FREE(pV);
891 }
892
893 i++;
894 }
895 }
896
DeleteMetadataFreeForm(const char * name,const char * owner)897 bool MP4File::DeleteMetadataFreeForm(const char *name, const char *owner)
898 {
899 char s[256];
900 int i = 0;
901
902 size_t nameLen = strlen(name);
903 size_t ownerLen = owner?strlen(owner):0;
904
905 while (1)
906 {
907 MP4BytesProperty *pMetadataProperty;
908
909 snprintf(s, 256, "moov.udta.meta.ilst.----[%u].name", i);
910 MP4Atom *pTagAtom = m_pRootAtom->FindAtom(s);
911
912 snprintf(s, 256,"moov.udta.meta.ilst.----[%u].mean", i);
913 MP4Atom *pMeanAtom = m_pRootAtom->FindAtom(s);
914
915
916 if (!pTagAtom)
917 return false;
918
919 if (pTagAtom->FindProperty("name.metadata",
920 (MP4Property**)&pMetadataProperty) &&
921 pMetadataProperty) {
922 u_int8_t* pV;
923 u_int32_t VSize = 0;
924
925 pMetadataProperty->GetValue(&pV, &VSize);
926
927 if (VSize != 0)
928 {
929 if (VSize == nameLen && memcmp(pV, name, VSize) == 0)
930 {
931 u_int8_t* pOwner=0;
932 u_int32_t ownerSize = 0;
933
934 if (pMeanAtom && pMeanAtom->FindProperty("mean.metadata",
935 (MP4Property**)&pMetadataProperty) &&
936 pMetadataProperty)
937 {
938 pMetadataProperty->GetValue(&pOwner, &ownerSize);
939 }
940
941 if (!owner || (pOwner && ownerLen == ownerSize && memcmp(owner, pOwner, ownerSize)))
942 {
943 snprintf(s, 256, "----[%u]", i);
944 CHECK_AND_FREE(pOwner);
945 CHECK_AND_FREE(pV);
946 return DeleteMetadataAtom(s);
947 }
948 CHECK_AND_FREE(pOwner);
949
950 }
951 }
952 CHECK_AND_FREE(pV);
953 }
954
955 i++;
956 }
957 }
958
MetadataDelete()959 bool MP4File::MetadataDelete()
960 {
961 MP4Atom *pMetaAtom = NULL;
962 char s[256];
963
964 snprintf(s, 256, "moov.udta.meta");
965 pMetaAtom = m_pRootAtom->FindAtom(s);
966
967 /* if it exists, delete it */
968 if (pMetaAtom)
969 {
970 MP4Atom *pParent = pMetaAtom->GetParentAtom();
971
972 pParent->DeleteChildAtom(pMetaAtom);
973
974 delete pMetaAtom;
975
976 return true;
977 }
978
979 return false;
980 }
981