blob: 52d5909f378a1e09124a3bb7dd9c5613a3889f6c [file] [log] [blame]
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is MPEG4IP.
*
* The Initial Developer of the Original Code is Cisco Systems Inc.
* Portions created by Cisco Systems Inc. are
* Copyright (C) Cisco Systems Inc. 2001. All Rights Reserved.
*
* Contributor(s):
* M. Bakker mbakker at nero.com
*
* Apple iTunes Metadata handling
*/
/**
The iTunes tagging seems to support any tag field name
but there are some predefined fields, also known from the QuickTime format
predefined fields (the ones I know of until now):
- nam : Name of the song/movie (string)
- ART : Name of the artist/performer (string)
- ART : Album artist
- wrt : Name of the writer (string)
- alb : Name of the album (string)
- day : Year (4 bytes, e.g. "2003") (string)
- too : Tool(s) used to create the file (string)
- cmt : Comment (string)
- gen : Custom genre (string)
- grp : Grouping (string)
- trkn : Tracknumber (8 byte string)
16 bit: empty
16 bit: tracknumber
16 bit: total tracks on album
16 bit: empty
- disk : Disknumber (8 byte string)
16 bit: empty
16 bit: disknumber
16 bit: total number of disks
16 bit: empty
- gnre : Genre (16 bit genre) (ID3v1 index + 1)
- cpil : Part of a compilation (1 byte, 1 or 0)
- tmpo : Tempo in BPM (16 bit)
- covr : Cover art (xx bytes binary data)
- ---- : Free form metadata, can have any name and any data
- pgap : gapless - 8 bit boolean
- apID : purchaser name.
- cprt : copyright
- purd : purchase date.
**/
#include "src/impl.h"
namespace mp4v2 {
namespace impl {
///////////////////////////////////////////////////////////////////////////////
bool MP4File::GetMetadataByIndex(uint32_t index,
char** ppName,
uint8_t** ppValue, uint32_t* pValueSize)
{
char s[256];
snprintf(s, 256, "moov.udta.meta.ilst.*[%u].data.metadata", index);
GetBytesProperty(s, ppValue, pValueSize);
snprintf(s, 256, "moov.udta.meta.ilst.*[%u]", index);
MP4Atom* pParent = m_pRootAtom->FindAtom(s);
if (pParent == NULL) return false;
/* check for free form tagfield */
if (memcmp(*ppName, "----", 4) == 0)
{
uint8_t* pV;
uint32_t VSize = 0;
char *pN;
snprintf(s, 256, "moov.udta.meta.ilst.*[%u].name.metadata", index);
GetBytesProperty(s, &pV, &VSize);
pN = (char*)malloc((VSize+1)*sizeof(char));
if (pN != NULL) {
memset(pN, 0, (VSize+1)*sizeof(char));
memcpy(pN, pV, VSize*sizeof(char));
}
free(pV);
*ppName = pN;
} else {
*ppName = strdup(pParent->GetType());
}
return true;
}
bool MP4File::CreateMetadataAtom(const char* name, itmf::BasicType typeCode)
{
char s[256];
char t[256];
snprintf(t, 256, "udta.meta.ilst.%s.data", name);
snprintf(s, 256, "moov.udta.meta.ilst.%s.data", name);
(void)AddDescendantAtoms("moov", t);
MP4Atom *pMetaAtom = m_pRootAtom->FindAtom(s);
if (!pMetaAtom)
return false;
MP4DataAtom& data = *(MP4DataAtom*)pMetaAtom;
data.typeCode.SetValue( typeCode );
MP4Atom *pHdlrAtom = m_pRootAtom->FindAtom("moov.udta.meta.hdlr");
MP4StringProperty *pStringProperty = NULL;
MP4BytesProperty *pBytesProperty = NULL;
ASSERT(pHdlrAtom);
ASSERT(pHdlrAtom->FindProperty("hdlr.handlerType",
(MP4Property**)&pStringProperty));
ASSERT(pStringProperty);
pStringProperty->SetValue("mdir");
uint8_t val[12];
memset(val, 0, 12*sizeof(uint8_t));
val[0] = 0x61;
val[1] = 0x70;
val[2] = 0x70;
val[3] = 0x6c;
ASSERT(pHdlrAtom->FindProperty("hdlr.reserved2",
(MP4Property**)&pBytesProperty));
ASSERT(pBytesProperty);
pBytesProperty->SetReadOnly(false);
pBytesProperty->SetValue(val, 12);
pBytesProperty->SetReadOnly(true);
return true;
}
bool MP4File::DeleteMetadataAtom(const char* name, bool try_udta)
{
MP4Atom *pMetaAtom = NULL;
char s[256];
snprintf(s, 256, "moov.udta.meta.ilst.%s", name);
pMetaAtom = m_pRootAtom->FindAtom(s);
if (pMetaAtom == NULL && try_udta) {
snprintf(s, 256, "moov.udta.%s", name);
pMetaAtom = m_pRootAtom->FindAtom(s);
}
/* if it exists, delete it */
if (pMetaAtom)
{
MP4Atom *pParent = pMetaAtom->GetParentAtom();
pParent->DeleteChildAtom(pMetaAtom);
delete pMetaAtom;
return true;
}
return false;
}
bool MP4File::SetMetadataString (const char *atom, const char *value)
{
char atomstring[40];
MP4Atom *pMetaAtom;
MP4BytesProperty *pMetadataProperty = NULL;
snprintf(atomstring, 40, "moov.udta.meta.ilst.%s.data", atom);
pMetaAtom = m_pRootAtom->FindAtom(atomstring);
if (!pMetaAtom)
{
if (!CreateMetadataAtom(atom, itmf::BT_UTF8))
return false;
pMetaAtom = m_pRootAtom->FindAtom(atomstring);
if (pMetaAtom == NULL) return false;
}
ASSERT(pMetaAtom->FindProperty("data.metadata",
(MP4Property**)&pMetadataProperty));
ASSERT(pMetadataProperty);
pMetadataProperty->SetValue((uint8_t*)value, strlen(value));
return true;
}
bool MP4File::GetMetadataString (const char *atom, char **value, bool try_udta)
{
unsigned char *val = NULL;
uint32_t valSize = 0;
char atomstring[60];
snprintf(atomstring, 60, "moov.udta.meta.ilst.%s.data.metadata", atom);
*value = NULL;
if (try_udta == false) {
GetBytesProperty(atomstring, (uint8_t**)&val, &valSize);
} else {
bool got_it = false;
try {
GetBytesProperty(atomstring, (uint8_t**)&val, &valSize);
got_it = true;
}
catch (MP4Error* e) {
delete e;
}
if (got_it == false) {
snprintf(atomstring, 60, "moov.udta.%s.metadata", atom);
GetBytesProperty(atomstring, (uint8_t**)&val, &valSize);
}
}
if (valSize > 0)
{
*value = (char*)malloc((valSize+1)*sizeof(char));
if (*value == NULL) {
free(val);
return false;
}
memcpy(*value, val, valSize*sizeof(unsigned char));
free(val);
(*value)[valSize] = '\0';
return true;
}
return false;
}
bool MP4File::SetMetadataTrack(uint16_t track, uint16_t totalTracks)
{
unsigned char t[9];
const char *s = "moov.udta.meta.ilst.trkn.data";
MP4BytesProperty *pMetadataProperty = NULL;
MP4Atom *pMetaAtom = NULL;
pMetaAtom = m_pRootAtom->FindAtom(s);
if (!pMetaAtom)
{
if (!CreateMetadataAtom("trkn", itmf::BT_IMPLICIT))
return false;
pMetaAtom = m_pRootAtom->FindAtom(s);
if (pMetaAtom == NULL) return false;
}
memset(t, 0, 9*sizeof(unsigned char));
t[2] = (unsigned char)(track>>8)&0xFF;
t[3] = (unsigned char)(track)&0xFF;
t[4] = (unsigned char)(totalTracks>>8)&0xFF;
t[5] = (unsigned char)(totalTracks)&0xFF;
ASSERT(pMetaAtom->FindProperty("data.metadata",
(MP4Property**)&pMetadataProperty));
ASSERT(pMetadataProperty);
pMetadataProperty->SetValue((uint8_t*)t, 8);
return true;
}
bool MP4File::GetMetadataTrack(uint16_t* track, uint16_t* totalTracks)
{
unsigned char *val = NULL;
uint32_t valSize = 0;
const char *s = "moov.udta.meta.ilst.trkn.data.metadata";
*track = 0;
*totalTracks = 0;
GetBytesProperty(s, (uint8_t**)&val, &valSize);
if (valSize == 8) {
*track = (uint16_t)(val[3]);
*track += (uint16_t)(val[2]<<8);
*totalTracks = (uint16_t)(val[5]);
*totalTracks += (uint16_t)(val[4]<<8);
CHECK_AND_FREE(val);
return true;
}
CHECK_AND_FREE(val);
return false;
}
bool MP4File::SetMetadataDisk(uint16_t disk, uint16_t totalDisks)
{
unsigned char t[7];
const char *s = "moov.udta.meta.ilst.disk.data";
MP4BytesProperty *pMetadataProperty = NULL;
MP4Atom *pMetaAtom = NULL;
pMetaAtom = m_pRootAtom->FindAtom(s);
if (!pMetaAtom)
{
if (!CreateMetadataAtom("disk", itmf::BT_IMPLICIT))
return false;
pMetaAtom = m_pRootAtom->FindAtom(s);
if (pMetaAtom == NULL) return false;
}
memset(t, 0, 7*sizeof(unsigned char));
t[2] = (unsigned char)(disk>>8)&0xFF;
t[3] = (unsigned char)(disk)&0xFF;
t[4] = (unsigned char)(totalDisks>>8)&0xFF;
t[5] = (unsigned char)(totalDisks)&0xFF;
ASSERT(pMetaAtom->FindProperty("data.metadata",
(MP4Property**)&pMetadataProperty));
ASSERT(pMetadataProperty);
pMetadataProperty->SetValue((uint8_t*)t, 6);
return true;
}
bool MP4File::GetMetadataDisk(uint16_t* disk, uint16_t* totalDisks)
{
unsigned char *val = NULL;
uint32_t valSize = 0;
const char *s = "moov.udta.meta.ilst.disk.data.metadata";
*disk = 0;
*totalDisks = 0;
GetBytesProperty(s, (uint8_t**)&val, &valSize);
if (valSize == 6 || valSize == 8) {
*disk = (uint16_t)(val[3]);
*disk += (uint16_t)(val[2]<<8);
*totalDisks = (uint16_t)(val[5]);
*totalDisks += (uint16_t)(val[4]<<8);
free(val);
return true;
}
CHECK_AND_FREE(val);
return true;
}
static const char* ID3v1GenreList[] = {
"Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk",
"Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies",
"Other", "Pop", "R&B", "Rap", "Reggae", "Rock",
"Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks",
"Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk",
"Fusion", "Trance", "Classical", "Instrumental", "Acid", "House",
"Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass",
"Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock",
"Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk",
"Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta",
"Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
"New Wave", "Psychedelic", "Rave", "Showtunes", "Trailer", "Lo-Fi",
"Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical",
"Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk", "Swing",
"Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde",
"Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
"Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
"Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
"Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
"Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet",
"Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall",
"Goa", "Drum & Bass", "Club House", "Hardcore", "Terror",
"Indie", "BritPop", "NegerPunk", "Polsk Punk", "Beat",
"Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover", "Contemporary C",
"Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop",
"SynthPop",
};
void GenreToString(char** GenreStr, const int genre)
{
if (genre > 0 &&
genre <= (int)(sizeof(ID3v1GenreList)/sizeof(*ID3v1GenreList)))
{
uint32_t len = strlen(ID3v1GenreList[genre-1])+1;
*GenreStr = (char*)malloc(len);
if (*GenreStr == NULL) return;
// no need for strncpy; enough was malloced
strcpy(*GenreStr, ID3v1GenreList[genre-1]);
return;
}
*GenreStr = (char*)malloc(2*sizeof(char));
if (*GenreStr == NULL) return;
memset(*GenreStr, 0, 2*sizeof(char));
return;
}
bool MP4File::SetMetadataGenre(const char* value)
{
uint16_t genreIndex = 0;
unsigned char t[3];
MP4BytesProperty *pMetadataProperty = NULL;
MP4Atom *pMetaAtom = NULL;
genreIndex = static_cast<uint16_t>( itmf::enumGenreType.toType(value) );
const char *s = "moov.udta.meta.ilst.gnre.data";
const char *sroot = "moov.udta.meta.ilst.gnre";
const char *s2 = "moov.udta.meta.ilst.\251gen.data";
const char *s2root = "moov.udta.meta.ilst.\251gen";
if (genreIndex != 0)
{
pMetaAtom = m_pRootAtom->FindAtom(s);
if (!pMetaAtom)
{
if (!CreateMetadataAtom("gnre", itmf::BT_GENRES))
return false;
pMetaAtom = m_pRootAtom->FindAtom(s);
if (pMetaAtom == NULL) return false;
}
memset(t, 0, 3*sizeof(unsigned char));
t[0] = (unsigned char)(genreIndex>>8)&0xFF;
t[1] = (unsigned char)(genreIndex)&0xFF;
ASSERT(pMetaAtom->FindProperty("data.metadata",
(MP4Property**)&pMetadataProperty));
ASSERT(pMetadataProperty);
pMetadataProperty->SetValue((uint8_t*)t, 2);
// remove other style of genre atom, if this one is added
pMetaAtom = m_pRootAtom->FindAtom(s2root);
if (pMetaAtom != NULL) {
MP4Atom *pParent = pMetaAtom->GetParentAtom();
if (pParent != NULL) {
pParent->DeleteChildAtom(pMetaAtom);
delete pMetaAtom;
}
}
(void)DeleteMetadataAtom( "\251gen" );
return true;
} else {
pMetaAtom = m_pRootAtom->FindAtom(s2);
if (!pMetaAtom)
{
if (!CreateMetadataAtom("\251gen", itmf::BT_GENRES))
return false;
pMetaAtom = m_pRootAtom->FindAtom(s2);
}
ASSERT(pMetaAtom->FindProperty("data.metadata",
(MP4Property**)&pMetadataProperty));
ASSERT(pMetadataProperty);
pMetadataProperty->SetValue((uint8_t*)value, strlen(value));
// remove other gnre atom if this one is entered
pMetaAtom = m_pRootAtom->FindAtom(sroot);
if (pMetaAtom != NULL) {
MP4Atom *pParent = pMetaAtom->GetParentAtom();
pParent->DeleteChildAtom(pMetaAtom);
delete pMetaAtom;
}
return true;
}
return false;
}
bool MP4File::GetMetadataGenre(char** value)
{
uint16_t genreIndex = 0;
unsigned char *val = NULL;
uint32_t valSize = 0;
const char *t = "moov.udta.meta.ilst.gnre";
const char *t2 = "moov.udta.meta.ilst.\251gen";
*value = NULL;
MP4Atom *gnre = FindAtom(t);
MP4Atom *cgen = FindAtom(t2);
if (gnre)
{
const char *s = "moov.udta.meta.ilst.gnre.data.metadata";
GetBytesProperty(s, (uint8_t**)&val, &valSize);
if (valSize != 2) {
CHECK_AND_FREE(val);
return false;
}
genreIndex = (uint16_t)(val[1]);
genreIndex += (uint16_t)(val[0]<<8);
GenreToString(value, genreIndex);
(void)DeleteMetadataAtom( "gnre" );
free(val);
return true;
} else if (cgen) {
const char *s2 = "moov.udta.meta.ilst.\251gen.data.metadata";
val = NULL;
valSize = 0;
GetBytesProperty(s2, (uint8_t**)&val, &valSize);
if (valSize > 0)
{
*value = (char*)malloc((valSize+1)*sizeof(unsigned char));
if (*value != NULL) {
memset(*value, 0, (valSize+1)*sizeof(unsigned char));
memcpy(*value, val, valSize*sizeof(unsigned char));
}
free(val);
return true;
}
}
CHECK_AND_FREE(val);
return false;
}
bool MP4File::DeleteMetadataGenre()
{
bool val1 = DeleteMetadataAtom("\251gen");
bool val2 = DeleteMetadataAtom("gnre");
return val1 || val2;
}
bool MP4File::SetMetadataUint8 (const char *atom, uint8_t value)
{
char atompath[36];
MP4BytesProperty *pMetadataProperty = NULL;
MP4Atom *pMetaAtom = NULL;
snprintf(atompath, 36, "moov.udta.meta.ilst.%s.data", atom);
pMetaAtom = m_pRootAtom->FindAtom(atompath);
if (pMetaAtom == NULL) {
if (!CreateMetadataAtom(atom, itmf::BT_INTEGER))
return false;
pMetaAtom = m_pRootAtom->FindAtom(atompath);
if (pMetaAtom == NULL) return false;
}
ASSERT(pMetaAtom->FindProperty("data.metadata",
(MP4Property**)&pMetadataProperty));
ASSERT(pMetadataProperty);
pMetadataProperty->SetValue(&value, 1);
return true;
}
bool MP4File::GetMetadataUint8(const char *atom, uint8_t* retvalue)
{
unsigned char *val = NULL;
uint32_t valSize = 0;
char atompath[80];
snprintf(atompath, 80, "moov.udta.meta.ilst.%s.data.metadata", atom);
*retvalue = 0;
GetBytesProperty(atompath, (uint8_t**)&val, &valSize);
if (valSize != 1) {
CHECK_AND_FREE(val);
return false;
}
*retvalue = val[0];
free(val);
return true;
}
bool MP4File::SetMetadataUint16(const char *atom, uint16_t value)
{
char atompath[36];
unsigned char t[3];
MP4BytesProperty *pMetadataProperty = NULL;
MP4Atom *pMetaAtom = NULL;
snprintf(atompath, 36, "moov.udta.meta.ilst.%s.data", atom);
pMetaAtom = m_pRootAtom->FindAtom(atompath);
if (!pMetaAtom)
{
if (!CreateMetadataAtom(atom, itmf::BT_INTEGER))
return false;
pMetaAtom = m_pRootAtom->FindAtom(atompath);
if (pMetaAtom == NULL) return false;
}
memset(t, 0, 3*sizeof(unsigned char));
t[0] = (unsigned char)(value>>8)&0xFF;
t[1] = (unsigned char)(value)&0xFF;
ASSERT(pMetaAtom->FindProperty("data.metadata",
(MP4Property**)&pMetadataProperty));
ASSERT(pMetadataProperty);
pMetadataProperty->SetValue((uint8_t*)t, 2);
return true;
}
bool MP4File::GetMetadataUint16(const char *atom, uint16_t* retvalue)
{
unsigned char *val = NULL;
uint32_t valSize = 0;
char atompath[80];
snprintf(atompath, 80, "moov.udta.meta.ilst.%s.data.metadata", atom);
*retvalue = 0;
GetBytesProperty(atompath, (uint8_t**)&val, &valSize);
if (valSize != 2) {
CHECK_AND_FREE(val);
return false;
}
*retvalue = (uint16_t)(val[1]);
*retvalue += (uint16_t)(val[0]<<8);
free(val);
return true;
}
bool MP4File::SetMetadataUint32(const char *atom, uint32_t value)
{
char atompath[36];
unsigned char t[5];
MP4BytesProperty *pMetadataProperty = NULL;
MP4Atom *pMetaAtom = NULL;
snprintf(atompath, 36, "moov.udta.meta.ilst.%s.data", atom);
pMetaAtom = m_pRootAtom->FindAtom(atompath);
if (pMetaAtom == NULL) {
if (!CreateMetadataAtom(atom, itmf::BT_INTEGER))
return false;
pMetaAtom = m_pRootAtom->FindAtom(atompath);
if (pMetaAtom == NULL) return false;
}
memset(t, 0, 5*sizeof(unsigned char));
t[0] = (unsigned char)(value>>24)&0xFF;
t[1] = (unsigned char)(value>>16)&0xFF;
t[2] = (unsigned char)(value>>8)&0xFF;
t[3] = (unsigned char)(value)&0xFF;
ASSERT(pMetaAtom->FindProperty("data.metadata",
(MP4Property**)&pMetadataProperty));
ASSERT(pMetadataProperty);
pMetadataProperty->SetValue((uint8_t*)t, 4);
return true;
}
bool MP4File::GetMetadataUint32(const char *atom, uint32_t* retvalue)
{
unsigned char *val = NULL;
uint32_t valSize = 0;
char atompath[80];
snprintf(atompath, 80, "moov.udta.meta.ilst.%s.data.metadata", atom);
*retvalue = 0;
GetBytesProperty(atompath, (uint8_t**)&val, &valSize);
if (valSize != 4) {
CHECK_AND_FREE(val);
return false;
}
*retvalue = val[3];
*retvalue += (uint32_t)(val[2]<<8);
*retvalue += (uint32_t)(val[1]<<16);
*retvalue += (uint32_t)(val[0]<<24);
free(val);
return true;
}
bool MP4File::SetMetadataCoverArt(uint8_t *coverArt, uint32_t size)
{
const char *s = "moov.udta.meta.ilst.covr.data";
MP4BytesProperty *pMetadataProperty = NULL;
MP4Atom *pMetaAtom = NULL;
pMetaAtom = m_pRootAtom->FindAtom(s);
if (!pMetaAtom)
{
if (!CreateMetadataAtom("covr", itmf::BT_IMPLICIT))
return false;
pMetaAtom = m_pRootAtom->FindAtom(s);
if (pMetaAtom == NULL) return false;
}
ASSERT(pMetaAtom->FindProperty("data.metadata",
(MP4Property**)&pMetadataProperty));
ASSERT(pMetadataProperty);
pMetadataProperty->SetValue(coverArt, size);
return true;
}
bool MP4File::GetMetadataCoverArtPosition(uint32_t *start, uint32_t* size,
uint32_t index)
{
char buffer[256];
if (start == NULL || size == NULL) return false;
if (index > 0 && index > GetMetadataCoverArtCount()) return false;
snprintf(buffer, 256, "moov.udta.meta.ilst.covr.data[%d].metadata", index);
*start = 0;
*size = 0;
MP4Property* pProperty = NULL;
FindBytesProperty(buffer, &pProperty, size);
if (size == 0 || pProperty == NULL)
return false;
*start = ((MP4BytesProperty*)pProperty)->GetValueStart(index);
*size = ((MP4BytesProperty*)pProperty)->GetValueSize(0, index);
return true;
}
bool MP4File::GetMetadataCoverArt(uint8_t **coverArt, uint32_t *size,
uint32_t index)
{
char buffer[256];
if (size == NULL || coverArt == NULL) return false;
if (index > 0 && index > GetMetadataCoverArtCount()) return false;
snprintf(buffer, 256, "moov.udta.meta.ilst.covr.data[%d].metadata", index);
*coverArt = NULL;
*size = 0;
GetBytesProperty(buffer, coverArt, size);
if (size == 0)
return false;
return true;
}
uint32_t MP4File::GetMetadataCoverArtCount (void)
{
MP4Atom *pMetaAtom = m_pRootAtom->FindAtom("moov.udta.meta.ilst.covr");
if (!pMetaAtom)
return 0;
return pMetaAtom->GetNumberOfChildAtoms();
}
bool MP4File::SetMetadataFreeForm (const char *name,
const uint8_t* pValue,
uint32_t valueSize,
const char *owner)
{
MP4Atom *pMetaAtom = NULL;
MP4BytesProperty *pMetadataProperty = NULL;
char s[256];
int i = 0;
size_t nameLen = strlen(name);
size_t ownerLen = owner != NULL ? strlen(owner) : 0;
while (1)
{
snprintf(s, 256, "moov.udta.meta.ilst.----[%u].name", i);
MP4Atom *pTagAtom = m_pRootAtom->FindAtom(s);
if (!pTagAtom)
break;
snprintf(s, 256, "moov.udta.meta.ilst.----[%u].mean", i);
MP4Atom *pMeanAtom = m_pRootAtom->FindAtom(s);
if (pTagAtom->FindProperty("name.metadata",
(MP4Property**)&pMetadataProperty) &&
pMetadataProperty) {
uint8_t* pV;
uint32_t VSize = 0;
pMetadataProperty->GetValue(&pV, &VSize);
if (VSize == nameLen && memcmp(pV, name, VSize) == 0) {
uint8_t* pOwner=0;
uint32_t ownerSize = 0;
if (pMeanAtom &&
pMeanAtom->FindProperty("mean.metadata",
(MP4Property**)&pMetadataProperty) &&
pMetadataProperty) {
pMetadataProperty->GetValue(&pOwner, &ownerSize);
}
if (owner == NULL||
(pOwner &&
ownerLen == ownerSize &&
memcmp(owner, pOwner, ownerSize))) {
snprintf(s, 256, "moov.udta.meta.ilst.----[%u].data.metadata", i);
SetBytesProperty(s, pValue, valueSize);
CHECK_AND_FREE(pV);
CHECK_AND_FREE(pOwner);
return true;
}
CHECK_AND_FREE(pOwner);
}
CHECK_AND_FREE(pV);
}
i++;
}
/* doesn't exist yet, create it */
char t[256];
snprintf(t, 256, "udta.meta.ilst.----[%u]", i);
snprintf(s, 256, "moov.udta.meta.ilst.----[%u].data", i);
(void)AddDescendantAtoms("moov", t);
pMetaAtom = m_pRootAtom->FindAtom(s);
if (!pMetaAtom)
return false;
// all ilst data atoms are of MP4DataAtom type.
((MP4DataAtom*)pMetaAtom)->typeCode.SetValue( itmf::BT_UTF8 );
MP4Atom *pHdlrAtom = m_pRootAtom->FindAtom("moov.udta.meta.hdlr");
MP4StringProperty *pStringProperty = NULL;
MP4BytesProperty *pBytesProperty = NULL;
ASSERT(pHdlrAtom);
ASSERT(pHdlrAtom->FindProperty("hdlr.handlerType",
(MP4Property**)&pStringProperty));
ASSERT(pStringProperty);
pStringProperty->SetValue("mdir");
uint8_t val[12];
memset(val, 0, 12*sizeof(uint8_t));
val[0] = 0x61;
val[1] = 0x70;
val[2] = 0x70;
val[3] = 0x6c;
ASSERT(pHdlrAtom->FindProperty("hdlr.reserved2",
(MP4Property**)&pBytesProperty));
ASSERT(pBytesProperty);
pBytesProperty->SetReadOnly(false);
pBytesProperty->SetValue(val, 12);
pBytesProperty->SetReadOnly(true);
pMetaAtom = m_pRootAtom->FindAtom(s);
ASSERT(pMetaAtom);
ASSERT(pMetaAtom->FindProperty("data.metadata",
(MP4Property**)&pMetadataProperty));
ASSERT(pMetadataProperty);
pMetadataProperty->SetValue(pValue, valueSize);
snprintf(s, 256, "moov.udta.meta.ilst.----[%u].name", i);
pMetaAtom = m_pRootAtom->FindAtom(s);
ASSERT(pMetaAtom->FindProperty("name.metadata",
(MP4Property**)&pMetadataProperty));
ASSERT(pMetadataProperty);
pMetadataProperty->SetValue((const uint8_t*)name, strlen(name));
snprintf(s, 256, "moov.udta.meta.ilst.----[%u].mean", i);
pMetaAtom = m_pRootAtom->FindAtom(s);
ASSERT(pMetaAtom->FindProperty("mean.metadata",
(MP4Property**)&pMetadataProperty));
ASSERT(pMetadataProperty);
if (!owner || !*owner)
pMetadataProperty->SetValue((uint8_t*)"com.apple.iTunes", 16); /* com.apple.iTunes is the default*/
else
pMetadataProperty->SetValue((const uint8_t*)owner, strlen((const char *)owner));
return true;
}
bool MP4File::GetMetadataFreeForm(const char *name,
uint8_t** ppValue,
uint32_t *pValueSize,
const char *owner)
{
char s[256];
int i = 0;
*ppValue = NULL;
*pValueSize = 0;
size_t nameLen = strlen(name);
size_t ownerLen = owner?strlen(owner):0;
while (1)
{
MP4BytesProperty *pMetadataProperty;
snprintf(s, 256,"moov.udta.meta.ilst.----[%u].name", i);
MP4Atom *pTagAtom = m_pRootAtom->FindAtom(s);
snprintf(s, 256,"moov.udta.meta.ilst.----[%u].mean", i);
MP4Atom *pMeanAtom = m_pRootAtom->FindAtom(s);
if (!pTagAtom)
return false;
if (pTagAtom->FindProperty("name.metadata",
(MP4Property**)&pMetadataProperty) &&
pMetadataProperty) {
uint8_t* pV;
uint32_t VSize = 0;
pMetadataProperty->GetValue(&pV, &VSize);
if (VSize == nameLen && memcmp(pV, name, VSize) == 0) {
uint8_t* pOwner=0;
uint32_t ownerSize = 0;
if (pMeanAtom && pMeanAtom->FindProperty("mean.metadata",
(MP4Property**)&pMetadataProperty) &&
pMetadataProperty) {
pMetadataProperty->GetValue(&pOwner, &ownerSize);
}
if (!owner || (pOwner && ownerLen == ownerSize && memcmp(owner, pOwner, ownerSize))) {
snprintf(s, 256, "moov.udta.meta.ilst.----[%u].data.metadata", i);
GetBytesProperty(s, ppValue, pValueSize);
CHECK_AND_FREE(pV);
CHECK_AND_FREE(pOwner);
return true;
}
CHECK_AND_FREE(pOwner);
}
CHECK_AND_FREE(pV);
}
i++;
}
}
bool MP4File::DeleteMetadataFreeForm(const char *name, const char *owner)
{
char s[256];
int i = 0;
size_t nameLen = strlen(name);
size_t ownerLen = owner?strlen(owner):0;
while (1)
{
MP4BytesProperty *pMetadataProperty;
snprintf(s, 256, "moov.udta.meta.ilst.----[%u].name", i);
MP4Atom *pTagAtom = m_pRootAtom->FindAtom(s);
snprintf(s, 256,"moov.udta.meta.ilst.----[%u].mean", i);
MP4Atom *pMeanAtom = m_pRootAtom->FindAtom(s);
if (!pTagAtom)
return false;
if (pTagAtom->FindProperty("name.metadata",
(MP4Property**)&pMetadataProperty) &&
pMetadataProperty) {
uint8_t* pV;
uint32_t VSize = 0;
pMetadataProperty->GetValue(&pV, &VSize);
if (VSize != 0)
{
if (VSize == nameLen && memcmp(pV, name, VSize) == 0)
{
uint8_t* pOwner=0;
uint32_t ownerSize = 0;
if (pMeanAtom && pMeanAtom->FindProperty("mean.metadata",
(MP4Property**)&pMetadataProperty) &&
pMetadataProperty)
{
pMetadataProperty->GetValue(&pOwner, &ownerSize);
}
if (!owner || (pOwner && ownerLen == ownerSize && memcmp(owner, pOwner, ownerSize)))
{
snprintf(s, 256, "----[%u]", i);
CHECK_AND_FREE(pOwner);
CHECK_AND_FREE(pV);
return DeleteMetadataAtom(s);
}
CHECK_AND_FREE(pOwner);
}
}
CHECK_AND_FREE(pV);
}
i++;
}
}
bool MP4File::MetadataDelete()
{
MP4Atom *pMetaAtom = NULL;
char s[256];
snprintf(s, 256, "moov.udta.meta");
pMetaAtom = m_pRootAtom->FindAtom(s);
/* if it exists, delete it */
if (pMetaAtom)
{
MP4Atom *pParent = pMetaAtom->GetParentAtom();
pParent->DeleteChildAtom(pMetaAtom);
delete pMetaAtom;
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
}
} // namespace mp4v2::impl