shithub: aacdec

ref: 0ab055fe640979fa9161ae5ddea6160e2340fd16
dir: /common/mp4v2/mp4meta.cpp/

View raw version
/*
 * 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)
 - �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)
 - 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)
 - 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

**/

#include "mp4common.h"

bool MP4File::GetMetadataByIndex(u_int32_t index,
                                 const char** ppName,
                                 u_int8_t** ppValue, u_int32_t* pValueSize)
{
    char s[256];

    sprintf(s, "moov.udta.meta.ilst.*[%u].data.metadata", index);
    GetBytesProperty(s, ppValue, pValueSize);

    sprintf(s, "moov.udta.meta.ilst.*[%u]", index);
    MP4Atom* pParent = m_pRootAtom->FindAtom(s);
    *ppName = pParent->GetType();

    /* check for free form tagfield */
    if (memcmp(*ppName, "----", 4) == 0)
    {
        u_int8_t* pV;
        u_int32_t VSize = 0;
        char *pN;

        sprintf(s, "moov.udta.meta.ilst.*[%u].name.metadata", index);
        GetBytesProperty(s, &pV, &VSize);

        pN = (char*)malloc((VSize+1)*sizeof(char));
        memset(pN, 0, (VSize+1)*sizeof(char));
        memcpy(pN, pV, VSize*sizeof(char));

        *ppName = pN;
    }

    return true;
}

bool MP4File::CreateMetadataAtom(const char* name)
{
    char s[256];
    char t[256];

    sprintf(t, "udta.meta.ilst.%s.data", name);
    sprintf(s, "moov.udta.meta.ilst.%s.data", name);
    AddDescendantAtoms("moov", t);
    MP4Atom *pMetaAtom = m_pRootAtom->FindAtom(s);

    if (!pMetaAtom)
        return false;

    /* some fields need special flags set */
    if (name[0] == '�')
    {
        pMetaAtom->SetFlags(0x1);
    } else if ((memcmp(name, "cpil", 4) == 0) || (memcmp(name, "tmpo", 4) == 0)) {
        pMetaAtom->SetFlags(0xF);
    }

    MP4Atom *pHdlrAtom = m_pRootAtom->FindAtom("moov.udta.meta.hdlr");
    MP4StringProperty *pStringProperty = NULL;
    MP4BytesProperty *pBytesProperty = NULL;
    ASSERT(pHdlrAtom);

    pHdlrAtom->FindProperty(
        "hdlr.handlerType", (MP4Property**)&pStringProperty);
    ASSERT(pStringProperty);
    pStringProperty->SetValue("mdir");

    u_int8_t val[12];
    memset(val, 0, 12*sizeof(u_int8_t));
    val[0] = 0x61;
    val[1] = 0x70;
    val[2] = 0x70;
    val[3] = 0x6c;
    pHdlrAtom->FindProperty(
        "hdlr.reserved2", (MP4Property**)&pBytesProperty);
    ASSERT(pBytesProperty);
    pBytesProperty->SetReadOnly(false);
    pBytesProperty->SetValue(val, 12);
    pBytesProperty->SetReadOnly(true);

    return true;
}

bool MP4File::SetMetadataName(const char* value)
{
    const char *s = "moov.udta.meta.ilst.�nam.data";
    MP4BytesProperty *pMetadataProperty = NULL;
    MP4Atom *pMetaAtom = NULL;
    
    pMetaAtom = m_pRootAtom->FindAtom(s);

    if (!pMetaAtom)
    {
        if (!CreateMetadataAtom("�nam"))
            return false;

        pMetaAtom = m_pRootAtom->FindAtom(s);
    }

    pMetaAtom->FindProperty("data.metadata", (MP4Property**)&pMetadataProperty);
    ASSERT(pMetadataProperty);

    pMetadataProperty->SetValue((u_int8_t*)value, strlen(value));

    return true;
}

bool MP4File::GetMetadataName(char** value)
{
    unsigned char *val = NULL;
    u_int32_t valSize = 0;
    const char *s = "moov.udta.meta.ilst.�nam.data.metadata";
    GetBytesProperty(s, (u_int8_t**)&val, &valSize);

    if (valSize > 0)
    {
        *value = (char*)malloc((valSize+1)*sizeof(unsigned char));
        memset(*value, 0, (valSize+1)*sizeof(unsigned char));
        memcpy(*value, val, valSize*sizeof(unsigned char));
        return true;
    } else {
        *value = NULL;
        return false;
    }
}

bool MP4File::SetMetadataWriter(const char* value)
{
    const char *s = "moov.udta.meta.ilst.�wrt.data";
    MP4BytesProperty *pMetadataProperty = NULL;
    MP4Atom *pMetaAtom = NULL;
    
    pMetaAtom = m_pRootAtom->FindAtom(s);

    if (!pMetaAtom)
    {
        if (!CreateMetadataAtom("�wrt"))
            return false;

        pMetaAtom = m_pRootAtom->FindAtom(s);
    }

    pMetaAtom->FindProperty("data.metadata", (MP4Property**)&pMetadataProperty);
    ASSERT(pMetadataProperty);

    pMetadataProperty->SetValue((u_int8_t*)value, strlen(value));

    return true;
}

bool MP4File::GetMetadataWriter(char** value)
{
    unsigned char *val = NULL;
    u_int32_t valSize = 0;
    const char *s = "moov.udta.meta.ilst.�wrt.data.metadata";
    GetBytesProperty(s, (u_int8_t**)&val, &valSize);

    if (valSize > 0)
    {
        *value = (char*)malloc((valSize+1)*sizeof(unsigned char));
        memset(*value, 0, (valSize+1)*sizeof(unsigned char));
        memcpy(*value, val, valSize*sizeof(unsigned char));
        return true;
    } else {
        *value = NULL;
        return false;
    }
}

bool MP4File::SetMetadataAlbum(const char* value)
{
    const char *s = "moov.udta.meta.ilst.�alb.data";
    MP4BytesProperty *pMetadataProperty = NULL;
    MP4Atom *pMetaAtom = NULL;
    
    pMetaAtom = m_pRootAtom->FindAtom(s);

    if (!pMetaAtom)
    {
        if (!CreateMetadataAtom("�alb"))
            return false;

        pMetaAtom = m_pRootAtom->FindAtom(s);
    }

    pMetaAtom->FindProperty("data.metadata", (MP4Property**)&pMetadataProperty);
    ASSERT(pMetadataProperty);

    pMetadataProperty->SetValue((u_int8_t*)value, strlen(value));

    return true;
}

bool MP4File::GetMetadataAlbum(char** value)
{
    unsigned char *val = NULL;
    u_int32_t valSize = 0;
    const char *s = "moov.udta.meta.ilst.�alb.data.metadata";
    GetBytesProperty(s, (u_int8_t**)&val, &valSize);

    if (valSize > 0)
    {
        *value = (char*)malloc((valSize+1)*sizeof(unsigned char));
        memset(*value, 0, (valSize+1)*sizeof(unsigned char));
        memcpy(*value, val, valSize*sizeof(unsigned char));
        return true;
    } else {
        *value = NULL;
        return false;
    }
}

bool MP4File::SetMetadataArtist(const char* value)
{
    const char *s = "moov.udta.meta.ilst.�ART.data";
    MP4BytesProperty *pMetadataProperty = NULL;
    MP4Atom *pMetaAtom = NULL;
    
    pMetaAtom = m_pRootAtom->FindAtom(s);

    if (!pMetaAtom)
    {
        if (!CreateMetadataAtom("�ART"))
            return false;

        pMetaAtom = m_pRootAtom->FindAtom(s);
    }

    pMetaAtom->FindProperty("data.metadata", (MP4Property**)&pMetadataProperty);
    ASSERT(pMetadataProperty);

    pMetadataProperty->SetValue((u_int8_t*)value, strlen(value));

    return true;
}

bool MP4File::GetMetadataArtist(char** value)
{
    unsigned char *val = NULL;
    u_int32_t valSize = 0;
    const char *s = "moov.udta.meta.ilst.�ART.data.metadata";
    GetBytesProperty(s, (u_int8_t**)&val, &valSize);

    if (valSize > 0)
    {
        *value = (char*)malloc((valSize+1)*sizeof(unsigned char));
        memset(*value, 0, (valSize+1)*sizeof(unsigned char));
        memcpy(*value, val, valSize*sizeof(unsigned char));
        return true;
    } else {
        *value = NULL;
        return false;
    }
}

bool MP4File::SetMetadataTool(const char* value)
{
    const char *s = "moov.udta.meta.ilst.�too.data";
    MP4BytesProperty *pMetadataProperty = NULL;
    MP4Atom *pMetaAtom = NULL;
    
    pMetaAtom = m_pRootAtom->FindAtom(s);

    if (!pMetaAtom)
    {
        if (!CreateMetadataAtom("�too"))
            return false;

        pMetaAtom = m_pRootAtom->FindAtom(s);
    }

    pMetaAtom->FindProperty("data.metadata", (MP4Property**)&pMetadataProperty);
    ASSERT(pMetadataProperty);

    pMetadataProperty->SetValue((u_int8_t*)value, strlen(value));

    return true;
}

bool MP4File::GetMetadataTool(char** value)
{
    unsigned char *val = NULL;
    u_int32_t valSize = 0;
    const char *s = "moov.udta.meta.ilst.�too.data.metadata";
    GetBytesProperty(s, (u_int8_t**)&val, &valSize);

    if (valSize > 0)
    {
        *value = (char*)malloc((valSize+1)*sizeof(unsigned char));
        memset(*value, 0, (valSize+1)*sizeof(unsigned char));
        memcpy(*value, val, valSize*sizeof(unsigned char));
        return true;
    } else {
        *value = NULL;
        return false;
    }
}

bool MP4File::SetMetadataComment(const char* value)
{
    const char *s = "moov.udta.meta.ilst.�cmt.data";
    MP4BytesProperty *pMetadataProperty = NULL;
    MP4Atom *pMetaAtom = NULL;
    
    pMetaAtom = m_pRootAtom->FindAtom(s);

    if (!pMetaAtom)
    {
        if (!CreateMetadataAtom("�cmt"))
            return false;

        pMetaAtom = m_pRootAtom->FindAtom(s);
    }

    pMetaAtom->FindProperty("data.metadata", (MP4Property**)&pMetadataProperty);
    ASSERT(pMetadataProperty);

    pMetadataProperty->SetValue((u_int8_t*)value, strlen(value));

    return true;
}

bool MP4File::GetMetadataComment(char** value)
{
    unsigned char *val = NULL;
    u_int32_t valSize = 0;
    const char *s = "moov.udta.meta.ilst.�cmt.data.metadata";
    GetBytesProperty(s, (u_int8_t**)&val, &valSize);

    if (valSize > 0)
    {
        *value = (char*)malloc((valSize+1)*sizeof(unsigned char));
        memset(*value, 0, (valSize+1)*sizeof(unsigned char));
        memcpy(*value, val, valSize*sizeof(unsigned char));
        return true;
    } else {
        *value = NULL;
        return false;
    }
}

bool MP4File::SetMetadataYear(const char* value)
{
    const char *s = "moov.udta.meta.ilst.�day.data";
    MP4BytesProperty *pMetadataProperty = NULL;
    MP4Atom *pMetaAtom = NULL;
    
    pMetaAtom = m_pRootAtom->FindAtom(s);

    if (!pMetaAtom)
    {
        if (!CreateMetadataAtom("�day"))
            return false;

        pMetaAtom = m_pRootAtom->FindAtom(s);
    }

    pMetaAtom->FindProperty("data.metadata", (MP4Property**)&pMetadataProperty);
    ASSERT(pMetadataProperty);

    if (strlen(value) < 4)
        return false;

    pMetadataProperty->SetValue((u_int8_t*)value, 4);

    return true;
}

bool MP4File::GetMetadataYear(char** value)
{
    unsigned char *val = NULL;
    u_int32_t valSize = 0;
    const char *s = "moov.udta.meta.ilst.�day.data.metadata";
    GetBytesProperty(s, (u_int8_t**)&val, &valSize);

    if (valSize > 0)
    {
        *value = (char*)malloc((valSize+1)*sizeof(unsigned char));
        memset(*value, 0, (valSize+1)*sizeof(unsigned char));
        memcpy(*value, val, valSize*sizeof(unsigned char));
        return true;
    } else {
        *value = NULL;
        return false;
    }
}

bool MP4File::SetMetadataTrack(u_int16_t track, u_int16_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"))
            return false;

        pMetaAtom = m_pRootAtom->FindAtom(s);
    }

    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;

    pMetaAtom->FindProperty("data.metadata", (MP4Property**)&pMetadataProperty);
    ASSERT(pMetadataProperty);

    pMetadataProperty->SetValue((u_int8_t*)t, 8);

    return true;
}

bool MP4File::GetMetadataTrack(u_int16_t* track, u_int16_t* totalTracks)
{
    unsigned char *val = NULL;
    u_int32_t valSize = 0;
    const char *s = "moov.udta.meta.ilst.trkn.data.metadata";
    GetBytesProperty(s, (u_int8_t**)&val, &valSize);

    *track = 0;
    *totalTracks = 0;

    if (valSize != 8)
        return false;

    *track = (u_int16_t)(val[3]);
    *track += (u_int16_t)(val[2]<<8);
    *totalTracks = (u_int16_t)(val[5]);
    *totalTracks += (u_int16_t)(val[4]<<8);

    return true;
}

bool MP4File::SetMetadataDisk(u_int16_t disk, u_int16_t totalDisks)
{
    unsigned char t[9];
    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"))
            return false;

        pMetaAtom = m_pRootAtom->FindAtom(s);
    }

    memset(t, 0, 9*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;

    pMetaAtom->FindProperty("data.metadata", (MP4Property**)&pMetadataProperty);
    ASSERT(pMetadataProperty);

    pMetadataProperty->SetValue((u_int8_t*)t, 8);

    return true;
}

bool MP4File::GetMetadataDisk(u_int16_t* disk, u_int16_t* totalDisks)
{
    unsigned char *val = NULL;
    u_int32_t valSize = 0;
    const char *s = "moov.udta.meta.ilst.disk.data.metadata";
    GetBytesProperty(s, (u_int8_t**)&val, &valSize);

    *disk = 0;
    *totalDisks = 0;

    if (valSize != 8)
        return false;

    *disk = (u_int16_t)(val[3]);
    *disk += (u_int16_t)(val[2]<<8);
    *totalDisks = (u_int16_t)(val[5]);
    *totalDisks += (u_int16_t)(val[4]<<8);

    return true;
}

bool MP4File::SetMetadataGenre(u_int16_t genreIndex)
{
    unsigned char t[3];
    const char *s = "moov.udta.meta.ilst.gnre.data";
    MP4BytesProperty *pMetadataProperty = NULL;
    MP4Atom *pMetaAtom = NULL;
    
    pMetaAtom = m_pRootAtom->FindAtom(s);

    if (!pMetaAtom)
    {
        if (!CreateMetadataAtom("gnre"))
            return false;

        pMetaAtom = m_pRootAtom->FindAtom(s);
    }

    memset(t, 0, 3*sizeof(unsigned char));
    t[0] = (unsigned char)(genreIndex>>8)&0xFF;
    t[1] = (unsigned char)(genreIndex)&0xFF;

    pMetaAtom->FindProperty("data.metadata", (MP4Property**)&pMetadataProperty);
    ASSERT(pMetadataProperty);

    pMetadataProperty->SetValue((u_int8_t*)t, 2);

    return true;
}

bool MP4File::GetMetadataGenre(u_int16_t* genreIndex)
{
    unsigned char *val = NULL;
    u_int32_t valSize = 0;
    const char *s = "moov.udta.meta.ilst.gnre.data.metadata";
    GetBytesProperty(s, (u_int8_t**)&val, &valSize);

    *genreIndex = 0;

    if (valSize != 2)
        return false;

    *genreIndex = (u_int16_t)(val[1]);
    *genreIndex += (u_int16_t)(val[0]<<8);

    return true;
}

bool MP4File::SetMetadataTempo(u_int16_t tempo)
{
    unsigned char t[3];
    const char *s = "moov.udta.meta.ilst.tmpo.data";
    MP4BytesProperty *pMetadataProperty = NULL;
    MP4Atom *pMetaAtom = NULL;
    
    pMetaAtom = m_pRootAtom->FindAtom(s);

    if (!pMetaAtom)
    {
        if (!CreateMetadataAtom("tmpo"))
            return false;

        pMetaAtom = m_pRootAtom->FindAtom(s);
    }

    memset(t, 0, 3*sizeof(unsigned char));
    t[0] = (unsigned char)(tempo>>8)&0xFF;
    t[1] = (unsigned char)(tempo)&0xFF;

    pMetaAtom->FindProperty("data.metadata", (MP4Property**)&pMetadataProperty);
    ASSERT(pMetadataProperty);

    pMetadataProperty->SetValue((u_int8_t*)t, 2);

    return true;
}

bool MP4File::GetMetadataTempo(u_int16_t* tempo)
{
    unsigned char *val = NULL;
    u_int32_t valSize = 0;
    const char *s = "moov.udta.meta.ilst.tmpo.data.metadata";
    GetBytesProperty(s, (u_int8_t**)&val, &valSize);

    *tempo = 0;

    if (valSize != 2)
        return false;

    *tempo = (u_int16_t)(val[1]);
    *tempo += (u_int16_t)(val[0]<<8);

    return true;
}

bool MP4File::SetMetadataCompilation(u_int8_t compilation)
{
    const char *s = "moov.udta.meta.ilst.cpil.data";
    MP4BytesProperty *pMetadataProperty = NULL;
    MP4Atom *pMetaAtom = NULL;
    
    pMetaAtom = m_pRootAtom->FindAtom(s);

    if (!pMetaAtom)
    {
        if (!CreateMetadataAtom("cpil"))
            return false;

        pMetaAtom = m_pRootAtom->FindAtom(s);
    }

    pMetaAtom->FindProperty("data.metadata", (MP4Property**)&pMetadataProperty);
    ASSERT(pMetadataProperty);

    compilation &= 0x1;
    pMetadataProperty->SetValue((u_int8_t*)&compilation, 1);

    return true;
}

bool MP4File::GetMetadataCompilation(u_int8_t* compilation)
{
    unsigned char *val = NULL;
    u_int32_t valSize = 0;
    const char *s = "moov.udta.meta.ilst.cpil.data.metadata";
    GetBytesProperty(s, (u_int8_t**)&val, &valSize);

    *compilation = 0;

    if (valSize != 1)
        return false;

    *compilation = (u_int16_t)(val[0]);

    return true;
}

bool MP4File::SetMetadataCoverArt(u_int8_t *coverArt, u_int32_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"))
            return false;

        pMetaAtom = m_pRootAtom->FindAtom(s);
    }

    pMetaAtom->FindProperty("data.metadata", (MP4Property**)&pMetadataProperty);
    ASSERT(pMetadataProperty);

    pMetadataProperty->SetValue(coverArt, size);

    return true;
}

bool MP4File::GetMetadataCoverArt(u_int8_t **coverArt, u_int32_t *size)
{
    const char *s = "moov.udta.meta.ilst.covr.data.metadata";
    GetBytesProperty(s, coverArt, size);

    if (size == 0)
        return false;

    return true;
}

bool MP4File::SetMetadataFreeForm(char *name, u_int8_t* pValue, u_int32_t valueSize)
{
    MP4Atom *pMetaAtom = NULL;
    MP4BytesProperty *pMetadataProperty = NULL;
    char s[256];
    int i = 0;
    bool nameExists = false;

    while (1)
    {
        MP4BytesProperty *pMetadataProperty;

        sprintf(s, "moov.udta.meta.ilst.----[%u].name", i);

        MP4Atom *pTagAtom = m_pRootAtom->FindAtom(s);

        if (!pTagAtom)
            break;

        pTagAtom->FindProperty("name.metadata", (MP4Property**)&pMetadataProperty);
        if (pMetadataProperty)
        {
            u_int8_t* pV;
            u_int32_t VSize = 0;

            pMetadataProperty->GetValue(&pV, &VSize);

            if (VSize != 0)
            {
                if (memcmp(pV, name, VSize) == 0)
                {
                    sprintf(s, "moov.udta.meta.ilst.----[%u].data.metadata", i);
                    SetBytesProperty(s, pValue, valueSize);

                    return true;
                }
            }
        }

        i++;
    }

    /* doesn't exist yet, create it */
    char t[256];

    sprintf(t, "udta.meta.ilst.----[%u]", i);
    sprintf(s, "moov.udta.meta.ilst.----[%u].data", i);
    AddDescendantAtoms("moov", t);

    pMetaAtom = m_pRootAtom->FindAtom(s);

    if (!pMetaAtom)
        return false;

    pMetaAtom->SetFlags(0x1);

    MP4Atom *pHdlrAtom = m_pRootAtom->FindAtom("moov.udta.meta.hdlr");
    MP4StringProperty *pStringProperty = NULL;
    MP4BytesProperty *pBytesProperty = NULL;
    ASSERT(pHdlrAtom);

    pHdlrAtom->FindProperty(
        "hdlr.handlerType", (MP4Property**)&pStringProperty);
    ASSERT(pStringProperty);
    pStringProperty->SetValue("mdir");

    u_int8_t val[12];
    memset(val, 0, 12*sizeof(u_int8_t));
    val[0] = 0x61;
    val[1] = 0x70;
    val[2] = 0x70;
    val[3] = 0x6c;
    pHdlrAtom->FindProperty(
        "hdlr.reserved2", (MP4Property**)&pBytesProperty);
    ASSERT(pBytesProperty);
    pBytesProperty->SetReadOnly(false);
    pBytesProperty->SetValue(val, 12);
    pBytesProperty->SetReadOnly(true);

    pMetaAtom = m_pRootAtom->FindAtom(s);
    pMetaAtom->FindProperty("data.metadata", (MP4Property**)&pMetadataProperty);
    ASSERT(pMetadataProperty);
    pMetadataProperty->SetValue(pValue, valueSize);

    sprintf(s, "moov.udta.meta.ilst.----[%u].name", i);
    pMetaAtom = m_pRootAtom->FindAtom(s);
    pMetaAtom->FindProperty("name.metadata", (MP4Property**)&pMetadataProperty);
    ASSERT(pMetadataProperty);
    pMetadataProperty->SetValue((u_int8_t*)name, strlen(name));

    sprintf(s, "moov.udta.meta.ilst.----[%u].mean", i);
    pMetaAtom = m_pRootAtom->FindAtom(s);
    pMetaAtom->FindProperty("mean.metadata", (MP4Property**)&pMetadataProperty);
    ASSERT(pMetadataProperty);
    pMetadataProperty->SetValue((u_int8_t*)"com.apple.iTunes", 16); /* ?? */

    return true;
}

bool MP4File::GetMetadataFreeForm(char *name, u_int8_t** ppValue, u_int32_t *pValueSize)
{
    char s[256];
    int i = 0;

    while (1)
    {
        MP4BytesProperty *pMetadataProperty;

        sprintf(s, "moov.udta.meta.ilst.----[%u].name", i);

        MP4Atom *pTagAtom = m_pRootAtom->FindAtom(s);

        if (!pTagAtom)
            return false;

        pTagAtom->FindProperty("name.metadata", (MP4Property**)&pMetadataProperty);
        if (pMetadataProperty)
        {
            u_int8_t* pV;
            u_int32_t VSize = 0;

            pMetadataProperty->GetValue(&pV, &VSize);

            if (VSize != 0)
            {
                if (memcmp(pV, name, VSize) == 0)
                {
                    sprintf(s, "moov.udta.meta.ilst.----[%u].data.metadata", i);
                    GetBytesProperty(s, ppValue, pValueSize);

                    return true;
                }
            }
        }

        i++;
    }
}

bool MP4File::MetadataDelete()
{
    MP4Atom *pMetaAtom = NULL;
    char s[256];

    sprintf(s, "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;
}