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