[Helix-client-dev] CR: Implemented APICData and UITSData metadata support in datatype projects

Petar Basic <[email protected]> Sat, 7 Nov 2009 00:47:01 +0100
Newsgroups gmane.comp.multimedia.helix.devel
Message-ID <[email protected]>
Modified by: pbasic at real.com
Date: 2009/11/06
Project: GMP MetaEditor (meta3gp.exe)

Synopsis:
Implemented APICData and UITSData metadata support in datatype projects

Overview:
This CR deals with:
A.) Implementation of proposal outlined in the document
"SOD-CoverArt-UITS-Properties-1.0.txt", also submitted to HC under
topic "Statement of Design: Transfer of Cover Art and UITS properties
through Helix DNA".
B.) Implementation of iTunes covr (Cover Art) atom support.

Details:
1.) Bugfixes and additions to common tools:
--Added more buffer copying utilities to PropTools.
--Added UTF8ContainsNewline utility to EncStrUtils.
--Improved input parameter checking in metaeditor DLL's CEditor::Drive.

2.) MetaInfo and ID3Tools updates:
--Added APICData and UITSData header property key definitions.
--Added custom ID3v2 tag parsing and generation tools.  Currently
implemented are reading of APIC and PRIV frames (v2.3.0, v2.4.0),
writing of APIC and PRIV frames (v2.4.0), reading of ID3v2 tag
(v2.3.0, v2.4.0), writing of ID3v2 tag (v2.4.0).
--Added support for APICData and UITSData metadata properties to MetaInfo.

3.) FFDriver updates:
--Introduced FFDriver "MetaDataIgnore" option which is primarily
intended as a hint to file-writers.  The value can carry multiple
flags each of which can specify a subset of metadata that should be
ignored during writing.  Currently this is used to suppress precedence
of legacy ID32BLOB property over newer properties in MP4 file-writer.

4.) MP4 file-format changes:
--Added reading and processing of iTunes covr atom to MP4 file-format.
--Added parsing of ID3v2 tag and generation of APICData and UITSData
properties via new MetaInfo utilities.
--Merged ID32BLOB insertion into the stream header missed by previous
GMPMetaEditor CR.

5.) MP4 file-writer changes:
--Fixed some constant-definition bugs.
--Fixed bug in calculation of free size of 'free' atom inside the 'meta' atom.
--Added writing of iTunes covr atom to MP4 file-writer.
--Added ID3v2 tag generation from APICData and UITSData properties and
writing of the constructed tag to ID32 atom.  ID32 atom is outputted
by default for M4A brand if UITSData is present in the header.
--Writer now has to decide whether to write out legacy ID32BLOB
property or construct ID3v2 tag from newly defined APICData and
UITSData properties.  By default, if legacy ID32BLOB is found in the
header, it has precedence over other metadata.  However, MP4
file-writer now also observes "MetaDataIgnore" hint which can override
the default behaviour.

6.) Metaeditor executable changes:
--Added CoverArt injection/extraction options.
--Added UITS injection/extraction options.
--Completely refactored option processing.

Testing:
Verified that meta-info injected by GMP MetaEditor can be read with
other software (iTunes 9, AtomicParsley 0.9, CT metadata extractor
1.3).  Verified that meta-info injected by other software can be read
with GMP MetaEditor.

Files Modified:
common/util/encstr.cpp
common/util/pub/encstr.h
common/util/proptools.cpp
common/util/pub/proptools.h
datatype/include/metainfokeys.h
datatype/common/util/metautil.cpp
datatype/common/util/pub/metautil.h
datatype/tools/metaeditor/editor.cpp
datatype/tools/dtdriver/engine/pub/ffdriver.h
datatype/tools/dtdriver/engine/ffdriver.cpp
datatype/tools/dtdriver/apps/meta3gp/main.cpp
datatype/mp4/fileformat/qtatmmgs.cpp
datatype/mp4/fileformat/pub/qtatmmgs.h
datatype/mp4/fileformat/qtatoms.cpp
datatype/mp4/fileformat/pub/qtatoms.h
datatype/mp4/fileformat/qtffplin.cpp
datatype/mp4/fileformat/qttrack.cpp
datatype/mp4/filewriter/3gpmeta.cpp
datatype/mp4/filewriter/3gpmeta.h
datatype/mp4/filewriter/m4amdatoms.h
datatype/mp4/filewriter/mp4sm.cpp
datatype/mp4/filewriter/mp4sm.h

Platforms and Profiles Affected:
All

Image Size and Heap Use impact:
Small increase

Platforms and Profiles Build Verified:
system id: win32-i386-vc7
profile: helix-client-all-defines

Platforms and Profiles Functionality Verified:
x86 Windows XP SP2

Branch:
HEAD

Copyright assignment:
I am a RealNetworks employee or contractor.

_______________________________________________
Helix-client-dev mailing list
[email protected]
http://lists.helixcommunity.org/mailman/listinfo/helix-client-dev
common_util.diff (text/x-patch, 14.4 KB)
Index: encstr.cpp
===================================================================
RCS file: /cvsroot/common/util/encstr.cpp,v
retrieving revision 1.6
diff -d -H -w -U30 -r1.6 encstr.cpp
--- encstr.cpp	23 Oct 2009 09:36:18 -0000	1.6
+++ encstr.cpp	6 Nov 2009 21:04:31 -0000
@@ -208,60 +208,111 @@
             return EncStrUtils::PrintTextUTF8(psz);
 
         default:
             break;
     }
     return FALSE;
 }
 
 void EncStrUtils::Dump(const char* psz, HX_TEXT_ENCODING_TYPE enc)
 {
     if(!psz)
     {
         printf("(null)");
         return;
     }
     // encoding type
     EncStrUtils::PrintEncodingType(enc);
 
     // total byte count
     printf(" | %d byte(s) | ", int(EncStrUtils::ByteLength(psz, enc, TRUE)));
 
     // hex digits
     EncStrUtils::HexDump(psz, enc);
     printf(" | ");
 
     // print characters
     EncStrUtils::PrintText(psz, enc);
 }
 #endif // End of #ifndef HELIX_CONFIG_DISABLE_CONSOLE_PRINT
 
+UINT32 EncStrUtils::UTF8ContainsNewline(const char* psz)
+{
+    if(!psz)
+    {
+        return 0;
+    }
+
+    char ch = *psz;
+
+    while(ch != 0)
+    {
+        if(ch <= 0x7F)
+        {
+            // 1-byte sequence
+            if(ch == '\n' || ch == '\r')
+            {
+                return 1;
+            }
+            psz++;
+        }
+        else
+        {
+            // determine number of bytes in the sequence
+            if((ch & 0xE0) == 0xC0)
+            {
+                // skip 2 bytes
+                psz += 2;
+            }
+            else if((ch & 0xF0) == 0xE0)
+            {
+                // skip 3 bytes
+                psz += 3;
+            }
+            else if((ch & 0xF8) == 0xF0)
+            {
+                // skip 4 bytes
+                psz += 4;
+            }
+            else
+            {
+                // invalid sequence, bail out
+                return -1;
+            }
+        }
+
+        ch = *psz;
+    }
+
+    return 0;
+}
+
 HXBOOL EncStrUtils::IsAsciiOnly(const char* psz, HX_TEXT_ENCODING_TYPE enc)
 {
     if(!psz)
     {
         return TRUE;
     }
     if(enc == HX_TEXT_ENCODING_TYPE_UTF16)
     {
         // assume BE by default
         enc = HX_TEXT_ENCODING_TYPE_UTF16BE;
         if(EncStrUtils::IsUTF16LE(psz))
         {
             enc = HX_TEXT_ENCODING_TYPE_UTF16LE;
         }
     }
     switch(enc)
     {
         case HX_TEXT_ENCODING_TYPE_UTF16BE:
         {
             // skip byte order mark
             if(EncStrUtils::IsUTF16BE(psz))
             {
                 psz += 2;
             }
             UINT32 count = 0;
             return IsUTF16StringAsciiOnly((const UINT16*)psz, MAX_UINT32, TRUE, count);
         }
         case HX_TEXT_ENCODING_TYPE_UTF16LE:
         {
             // skip byte order mark
Index: proptools.cpp
===================================================================
RCS file: /cvsroot/common/util/proptools.cpp,v
retrieving revision 1.2
diff -d -H -w -U30 -r1.2 proptools.cpp
--- proptools.cpp	28 Nov 2007 18:59:47 -0000	1.2
+++ proptools.cpp	6 Nov 2009 21:04:31 -0000
@@ -335,111 +335,168 @@
     {
         CopyPropertyCString(retVal, dst, src, pszPropName, FALSE);
         if(SUCCEEDED(retVal))
         {
             iterResult = src->GetNextPropertyCString(pszPropName, pBuffer);
             HX_RELEASE(pBuffer);
         }
     }
 }
 
 HXBOOL PropTools::CopyPropertyBuffer(EncodedString& out, IHXValues* src, const char* propertyName)
 {
     out.Clear();
     if(!propertyName)
     {
         return FALSE;
     }
 
     HXBOOL result = FALSE;
     IHXBuffer* pBufValue = 0;
     if(SUCCEEDED(src->GetPropertyBuffer(propertyName, pBufValue)))
     {
         out.Set((const char*)pBufValue->GetBuffer(),
                 (HX_TEXT_ENCODING_TYPE)GetPropertyBufferEncodingType(src, propertyName));
         result = TRUE;
     }
     HX_RELEASE(pBufValue);
     return result;
 }
 
+UINT32 PropTools::CopyPropertyBuffer(UINT8* pDstBuffer, UINT32 dstBufferSize, IHXValues* src, const char* propertyName)
+{
+    UINT32 numBytes = 0;
+
+    if(!src || !propertyName)
+    {
+        return numBytes;
+    }
+
+    IHXBuffer* pBufValue = 0;
+    if(SUCCEEDED(src->GetPropertyBuffer(propertyName, pBufValue)))
+    {
+        if(pDstBuffer && (dstBufferSize >= pBufValue->GetSize()))
+        {
+            memcpy(pDstBuffer, pBufValue->GetBuffer(), pBufValue->GetSize());
+        }
+        numBytes = pBufValue->GetSize();
+    }
+
+    HX_RELEASE(pBufValue);
+
+    return numBytes;
+}
+
 HXBOOL PropTools::CopyPropertyULONG32(ULONG32& out, IHXValues* src, const char* propertyName)
 {
     out = 0;
     if(!propertyName)
     {
         return FALSE;
     }
     ULONG32 uValue = 0;
     if(SUCCEEDED(src->GetPropertyULONG32(propertyName, uValue)))
     {
         out = uValue;
         return TRUE;
     }
     return FALSE;
 }
 
 void PropTools::CopyPropertyBuffer(IHXValues* dst, const EncodedString& val, const char* propertyName, HXBOOL bBlast)
 {
     if(!propertyName)
     {
         return;
     }
     if(val.IsEmpty())
     {
         if(bBlast)
         {
             RemovePropertyBuffer(dst, propertyName);
         }
         return;
     }
 
     IHXBuffer* pBuffer = new CHXBuffer();
     if(!pBuffer) return;
     pBuffer->AddRef();
 
     HX_RESULT retVal = pBuffer->Set((const UCHAR*)val.GetData(), (UINT32)val.GetByteLength(TRUE));
     if(SUCCEEDED(retVal))
     {
         retVal = dst->SetPropertyBuffer(propertyName, pBuffer);
     }
     HX_RELEASE(pBuffer);
 
     if(SUCCEEDED(retVal))
     {
         // copy encoding type
         CHXString sEncName(propertyName);
         sEncName += "_@@_TextEncodingType";
         dst->SetPropertyULONG32((const char*)sEncName, val.GetEncoding());
     }
 }
 
+HX_RESULT PropTools::CopyPropertyBuffer(IHXValues* dst, const UINT8* pData, UINT32 dataByteSize, const char* propertyName, HXBOOL bBlast)
+{
+    if(!dst || !propertyName || !propertyName[0])
+    {
+        return HXR_INVALID_PARAMETER;
+    }
+
+    if(!pData || !dataByteSize)
+    {
+        if(bBlast)
+        {
+            RemovePropertyBuffer(dst, propertyName);
+        }
+        return HXR_OK;
+    }
+
+    IHXBuffer* pBuffer = new CHXBuffer();
+    if(!pBuffer)
+    {
+        return HXR_OUTOFMEMORY;
+    }
+    pBuffer->AddRef();
+
+    HX_RESULT retVal = pBuffer->Set((const UCHAR*)pData, dataByteSize);
+    if(SUCCEEDED(retVal))
+    {
+        retVal = dst->SetPropertyBuffer(propertyName, pBuffer);
+    }
+    HX_RELEASE(pBuffer);
+
+    return retVal;
+}
+
 void PropTools::CopyPropertyULONG32(IHXValues* dst, HXBOOL bValid, ULONG32 val, const char* propertyName, HXBOOL bBlast)
 {
     if(propertyName)
     {
         if(bValid)
         {
             dst->SetPropertyULONG32(propertyName, val);
         }
         else if(bBlast)
         {
             RemovePropertyULONG32(dst, propertyName);
         }
     }
 }
 
 HXBOOL PropTools::IsFixedPointNumber(EncodedString& val, HXBOOL bNormalize)
 {
     if(val.IsEmpty() || !val.IsAsciiOnly())
     {
         return FALSE;
     }
     EncodedString valConv;
     val.ConvertToUTF8(valConv);
 
     // check for valid characters
     const char* input = valConv.GetData();
     if(!input)
     {
         return FALSE;
     }
Index: pub/encstr.h
===================================================================
RCS file: /cvsroot/common/util/pub/encstr.h,v
retrieving revision 1.3
diff -d -H -w -U30 -r1.3 encstr.h
--- pub/encstr.h	13 Dec 2007 17:56:52 -0000	1.3
+++ pub/encstr.h	6 Nov 2009 21:04:32 -0000
@@ -55,60 +55,65 @@
 #include "hxmemprb.h"
 #include "hxsrcin.h"
 #include "hxformt.h"
 #include "hxvalues.h"
 #include "pckunpck.h"
 
 
 class EncStrUtils
 {
 public:
 
 #ifndef HELIX_CONFIG_DISABLE_CONSOLE_PRINT
     
     // Prints string in hexadecimal format.
     static void HexDump(const char* psz, HX_TEXT_ENCODING_TYPE enc);
 
     // Prints encoding-type string, returns FALSE if encoding was of unknown type.
     static HXBOOL PrintEncodingType(HX_TEXT_ENCODING_TYPE enc);
 
     // Prints UTF8 string, returns FALSE if the sequence was invalid.
     static HXBOOL PrintTextUTF8(const char* psz);
 
     // Prints string, returns FALSE if the sequence was invalid.
     static HXBOOL PrintText(const char* psz, HX_TEXT_ENCODING_TYPE enc);
 
     // Prints string contents (encoding-type, byte-length, hex-dump, characters).
     static void Dump(const char* psz, HX_TEXT_ENCODING_TYPE enc);
 
 #endif // End of #ifndef HELIX_CONFIG_DISABLE_CONSOLE_PRINT
 
+    // Returns 1 if the terminated UTF8 string contains a newline, 0 otherwise.
+    // Newline means CR or LF.
+    // Returns -1 if an invalid sequence of bytes is encountered.
+    static UINT32 UTF8ContainsNewline(const char* psz);
+
     // Returns TRUE if the terminated string contains only ASCII characters.
     static HXBOOL IsAsciiOnly(const char* psz, HX_TEXT_ENCODING_TYPE enc);
 
     // Returns TRUE if the terminated string is empty (contains only terminator and possibly BOM).
     static HXBOOL IsEmpty(const char* psz, HX_TEXT_ENCODING_TYPE enc);
 
     // Returns TRUE if the string starts with "FEFF" big-endian byte-order-mark, FALSE otherwise.
     static HXBOOL IsUTF16BE(const char* psz);
 
     // Returns TRUE if the string starts with "FFFE" little-endian byte-order-mark, FALSE otherwise.
     static HXBOOL IsUTF16LE(const char* psz);
 
     // Return codes: 0 if the string does not start with byte-order-mark.
     //               1 if the string starts with "FFFE" little-endian byte-order-mark.
     //               2 if the string starts with "FEFF" big-endian byte-order-mark.
     static INT32 IsUTF16(const char* psz);
 
     // Scan flags.
     typedef enum {
         SCAN_FLAG_UTF16BE = 0x00000001,
         SCAN_FLAG_UTF16LE = 0x00000002,
 
         SCAN_FLAGS_UTF16 = SCAN_FLAG_UTF16BE | SCAN_FLAG_UTF16LE,
         SCAN_FLAGS_UTF = SCAN_FLAGS_UTF16,
         SCAN_FLAGS_ALL = SCAN_FLAGS_UTF
     } SCAN_FLAG;
 
     // Scans the string for byte-order-mark and tries to determine it's encoding.
     static HX_TEXT_ENCODING_TYPE ScanEncoding(const char* psz, UINT32 scanFlags = SCAN_FLAGS_ALL);
 
Index: pub/proptools.h
===================================================================
RCS file: /cvsroot/common/util/pub/proptools.h,v
retrieving revision 1.2
diff -d -H -w -U30 -r1.2 proptools.h
--- pub/proptools.h	28 Nov 2007 18:59:48 -0000	1.2
+++ pub/proptools.h	6 Nov 2009 21:04:32 -0000
@@ -70,54 +70,60 @@
     // Removes <propertyName> from <pValues>.
     static void RemovePropertyCString(HX_RESULT& retVal, IHXValues* pValues, const char* propertyName);
     static HX_RESULT RemovePropertyCString(IHXValues* pValues, const char* propertyName);
 
     static void RemovePropertyULONG32(HX_RESULT& retVal, IHXValues* pValues, const char* propertyName);
     static HX_RESULT RemovePropertyULONG32(IHXValues* pValues, const char* propertyName);
 
     // Removes all properties from <pValues>.
     static void RemoveAllProperties(HX_RESULT& retVal, IHXValues* pValues);
     static HX_RESULT RemoveAllProperties(IHXValues* pValues);
 
     // If <propertyName>'s encoding is not found, <notFoundResult> is returned.
     static ULONG32 GetPropertyBufferEncodingType(IHXValues* src, const char* propertyName,
                                     ULONG32 notFoundResult = HX_TEXT_ENCODING_TYPE_UTF8);
 
     // If <bBlast> is TRUE, properties not found in <src> are removed from <dst>.
     static void CopyPropertyBuffer(HX_RESULT& retVal, IHXValues* dst, IHXValues* src,
                                     const char* propertyName, HXBOOL bBlast);
 
     static void CopyPropertyCString(HX_RESULT& retVal, IHXValues* dst, IHXValues* src,
                                     const char* propertyName, HXBOOL bBlast);
 
     static void CopyPropertyULONG32(HX_RESULT& retVal, IHXValues* dst, IHXValues* src,
                                     const char* propertyName, HXBOOL bBlast);
 
     static void CopyAllProperties(HX_RESULT& retVal, IHXValues* dst, IHXValues* src);
 
     // Returns TRUE if <propertyName> is found in <src>. <out> is set to empty string if <propertyName> is not found.
     static HXBOOL CopyPropertyBuffer(EncodedString& out, IHXValues* src, const char* propertyName);
 
+    // Returns the number of bytes copied to buffer. If <pDstBuffer> is 0, it returns the number of bytes needed.
+    static UINT32 CopyPropertyBuffer(UINT8* pDstBuffer, UINT32 dstBufferSize, IHXValues* src, const char* propertyName);
+
     // Returns TRUE if <propertyName> is found in <src>.  <out> is set to 0 if <propertyName> is not found.
     static HXBOOL CopyPropertyULONG32(ULONG32& out, IHXValues* src, const char* propertyName);
 
     // If <bBlast> is TRUE, property is removed if <val> is empty.
     static void CopyPropertyBuffer(IHXValues* dst, const EncodedString& val, const char* propertyName, HXBOOL bBlast);
 
+    // If <bBlast> is TRUE, property is removed if <pData> is NULL or <dataByteSize> is 0.
+    static HX_RESULT CopyPropertyBuffer(IHXValues* dst, const UINT8* pData, UINT32 dataByteSize, const char* propertyName, HXBOOL bBlast);
+
     // If <bBlast> is TRUE, property is removed if <bValid> is FALSE.
     static void CopyPropertyULONG32(IHXValues* dst, HXBOOL bValid, ULONG32 val, const char* propertyName, HXBOOL bBlast);
 
     // Returns TRUE if <val> can be converted to fixed-point number.
     // If <bNormalize> is TRUE and <val> can be converted, <val> is updated to normalized format, e.g. "-0" -> "0".
     static HXBOOL IsFixedPointNumber(EncodedString& val, HXBOOL bNormalize);
 
     // Returns TRUE if <val> can be converted to number which falls inside specified range.
     static HXBOOL ExtractUInt32(UINT32& out, const EncodedString& val, UINT32 minVal = 0, UINT32 maxVal = 0xFFFFFFFF);
 
     // Returns TRUE if <val> can be converted to number which falls inside specified range.
     static HXBOOL ExtractUInt16(UINT16& out, const EncodedString& val, UINT16 minVal = 0, UINT16 maxVal = 0xFFFF);
 
     // Returns TRUE if <val> can be converted to number which falls inside specified range.
     static HXBOOL ExtractUInt8(UINT8& out, const EncodedString& val, UINT8 minVal = 0, UINT8 maxVal = 0xFF);
 };
 
 #endif /* _PROPTOOLS_H_ */
datatype_common_util.diff (text/x-patch, 139.7 KB) - not displayed
datatype_include.diff (text/x-patch, 7 KB)
Index: metainfokeys.h
===================================================================
RCS file: /cvsroot/datatype/include/metainfokeys.h,v
retrieving revision 1.5
diff -d -H -w -U30 -r1.5 metainfokeys.h
--- metainfokeys.h	23 Oct 2009 11:39:29 -0000	1.5
+++ metainfokeys.h	6 Nov 2009 21:14:41 -0000
@@ -20,99 +20,110 @@
  * developer of the Original Code and owns the copyrights in the portions 
  * it created. 
  *  
  * This file, and the files included with this file, is distributed and made 
  * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS 
  * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  * 
  * Technology Compatibility Kit Test Suite(s) Location: 
  *    http://www.helixcommunity.org/content/tck 
  * 
  * Contributor(s): 
  *  
  * ***** END LICENSE BLOCK ***** */ 
 
 #ifndef _METAINFOKEYS_H_
 #define _METAINFOKEYS_H_ 
 
 
 typedef enum 
 {
     METAINFO_SET_GENERIC          = 0x00000001,
     METAINFO_SET_3GPP_SPECIFIC    = 0x00000010,
     METAINFO_SET_3GPP_ALL         = METAINFO_SET_GENERIC | METAINFO_SET_3GPP_SPECIFIC,
     METAINFO_SET_ITUNES_SPECIFIC  = 0x00000100,
     METAINFO_SET_ITUNES_ALL       = METAINFO_SET_GENERIC | METAINFO_SET_ITUNES_SPECIFIC,
     METAINFO_SET_ALL              = METAINFO_SET_GENERIC | METAINFO_SET_3GPP_SPECIFIC | METAINFO_SET_ITUNES_SPECIFIC
 } METAINFO_SET;
 
+#define _META_INFO_BUFFER_PROPERTY_ID3_TAG_VERSION_MINOR   4
+#define _META_INFO_UITS_OWNER_IDENTIFIER                   "http://www.uits-data.org/"
+
 // Generic set
 #define _GENERIC_META_INFO_TITLE_KEY                    "Title"
 #define _GENERIC_META_INFO_COMPOSER_KEY                 "Author"
 #define _GENERIC_META_INFO_COPYRIGHT_KEY                "Copyright"
 #define _GENERIC_META_INFO_DESCRIPTION_KEY              "Description"
 #define _GENERIC_META_INFO_ARTIST_KEY                   "Performer"
 #define _GENERIC_META_INFO_GENRE_KEY                    "Genre"
 #define _GENERIC_META_INFO_ALBUM_KEY                    "AlbumTitle"
 #define _GENERIC_META_INFO_TRACK_NUMBER_KEY             "TrackNumber"
 #define _GENERIC_META_INFO_RECORDING_YEAR_KEY           "RecordingYear"
+#define _GENERIC_META_INFO_PICTURE_COUNT_KEY            "APICDataCount"
+#define _GENERIC_META_INFO_PICTURE_KEY                  "APICData%d"
+#define _GENERIC_META_INFO_UITS_KEY                     "UITSData"
 
 // iTunes aliases for Generic set, for use in file-format/file-writer
 #define _ITUNES_META_INFO_TITLE_KEY                     _GENERIC_META_INFO_TITLE_KEY
 #define _ITUNES_META_INFO_COMPOSER_KEY                  _GENERIC_META_INFO_COMPOSER_KEY
 #define _ITUNES_META_INFO_COPYRIGHT_KEY                 _GENERIC_META_INFO_COPYRIGHT_KEY
 #define _ITUNES_META_INFO_COMMENT_KEY                   _GENERIC_META_INFO_DESCRIPTION_KEY
 #define _ITUNES_META_INFO_ARTIST_KEY                    _GENERIC_META_INFO_ARTIST_KEY
 #define _ITUNES_META_INFO_GENRE_KEY                     _GENERIC_META_INFO_GENRE_KEY
 #define _ITUNES_META_INFO_ALBUM_KEY                     _GENERIC_META_INFO_ALBUM_KEY
 #define _ITUNES_META_INFO_TRACK_NUMBER_KEY              _GENERIC_META_INFO_TRACK_NUMBER_KEY
 #define _ITUNES_META_INFO_RELEASE_DATE_KEY              _GENERIC_META_INFO_RECORDING_YEAR_KEY
+#define _ITUNES_META_INFO_PICTURE_COUNT_KEY             _GENERIC_META_INFO_PICTURE_COUNT_KEY
+#define _ITUNES_META_INFO_PICTURE_KEY                   _GENERIC_META_INFO_PICTURE_KEY
 
 // iTunes specific set
 #define _ITUNES_META_INFO_TRACK_SUBTITLE_KEY            "TrackSubtitle"
 #define _ITUNES_META_INFO_GENERATOR_TOOL_KEY            "GeneratorTool"
 #define _ITUNES_META_INFO_ENCODED_BY_KEY                "EncodedBy"
 #define _ITUNES_META_INFO_GROUPING_KEY                  "Grouping"
 #define _ITUNES_META_INFO_ALBUM_ARTIST_KEY              "AlbumArtist"
 #define _ITUNES_META_INFO_LYRICS_KEY                    "Lyrics"
 #define _ITUNES_META_INFO_BEATS_PER_MINUTE_KEY          "BeatsPerMinute"
 #define _ITUNES_META_INFO_PART_OF_COMPILATION_KEY       "PartOfCompilation"
 #define _ITUNES_META_INFO_EXPLICIT_CONTENT_KEY          "ExplicitContent"
 #define _ITUNES_META_INFO_TRACK_COUNT_KEY               "TrackCount"
 #define _ITUNES_META_INFO_DISC_NUMBER_KEY               "DiscNumber"
 #define _ITUNES_META_INFO_DISC_COUNT_KEY                "DiscCount"
 
 // 3GPP aliases for Generic set, for use in file-format/file-writer
 #define _3GPP_META_INFO_TITLE_KEY                       _GENERIC_META_INFO_TITLE_KEY
 #define _3GPP_META_INFO_AUTHOR_KEY                      _GENERIC_META_INFO_COMPOSER_KEY
 #define _3GPP_META_INFO_COPYRIGHT_KEY                   _GENERIC_META_INFO_COPYRIGHT_KEY
 #define _3GPP_META_INFO_DESCRIPTION_KEY                 _GENERIC_META_INFO_DESCRIPTION_KEY
 #define _3GPP_META_INFO_PERFORMER_KEY                   _GENERIC_META_INFO_ARTIST_KEY
 #define _3GPP_META_INFO_GENRE_KEY                       _GENERIC_META_INFO_GENRE_KEY
 #define _3GPP_META_INFO_ALBUM_TITLE_KEY                 _GENERIC_META_INFO_ALBUM_KEY
 #define _3GPP_META_INFO_TRACK_NUMBER_KEY                _GENERIC_META_INFO_TRACK_NUMBER_KEY
 #define _3GPP_META_INFO_RECORDING_YEAR_KEY              _GENERIC_META_INFO_RECORDING_YEAR_KEY
+#define _3GPP_META_INFO_PICTURE_COUNT_KEY               _GENERIC_META_INFO_PICTURE_COUNT_KEY
+#define _3GPP_META_INFO_PICTURE_KEY                     _GENERIC_META_INFO_PICTURE_KEY
+#define _3GPP_META_INFO_UITS_KEY                        _GENERIC_META_INFO_UITS_KEY
 
 // 3GPP specific set
 #define _3GPP_META_INFO_GLOBAL_LANGUAGE_ENCODING_KEY    "GlobalLanguageEncoding"
 #define _3GPP_META_INFO_RATING_ENTITY_KEY               "RatingEntity"
 #define _3GPP_META_INFO_RATING_CRITERIA_KEY             "RatingCriteria"
 #define _3GPP_META_INFO_RATING_INFO_KEY                 "RatingInfo"
 #define _3GPP_META_INFO_CLASSIFICATION_ENTITY_KEY       "ClassificationEntity"
 #define _3GPP_META_INFO_CLASSIFICATION_TABLE_KEY        "ClassificationTable"
 #define _3GPP_META_INFO_CLASSIFICATION_INFO_KEY         "ClassificationInfo"
 #define _3GPP_META_INFO_LOCATION_NAME_KEY               "LocationName"
 #define _3GPP_META_INFO_LOCATION_ASTRONOMICAL_BODY_KEY  "LocationAstronomicalBody"
 #define _3GPP_META_INFO_LOCATION_ADDITIONAL_NOTES_KEY   "LocationAdditionalNotes"
 #define _3GPP_META_INFO_LOCATION_ROLE_KEY               "LocationRole"
 #define _3GPP_META_INFO_LOCATION_LONGITUDE_KEY          "LocationLongitude"
 #define _3GPP_META_INFO_LOCATION_LATITUDE_KEY           "LocationLatitude"
 #define _3GPP_META_INFO_LOCATION_ALTITUDE_KEY           "LocationAltitude"
 #define _3GPP_META_INFO_KEYWORD_COUNT_KEY               "KeywordCount"
 #define _3GPP_META_INFO_KEYWORD_KEY                     "Keyword%d"
-#define _3GPP_META_INFO_ID32_BLOB_KEY                   "ID32BLOB"
+#define _3GPP_META_INFO_ID32_BLOB_KEY                   "ID32BLOB"   //obsolete
 
 
 #endif // _METAINFOKEYS_H_
datatype_mp4_fileformat.diff (text/x-patch, 40.7 KB)
Index: qtatmmgs.cpp
===================================================================
RCS file: /cvsroot/datatype/mp4/fileformat/qtatmmgs.cpp,v
retrieving revision 1.61
diff -d -H -w -U30 -r1.61 qtatmmgs.cpp
--- qtatmmgs.cpp	4 Nov 2009 15:27:11 -0000	1.61
+++ qtatmmgs.cpp	6 Nov 2009 21:30:01 -0000
@@ -84,69 +84,75 @@
             {
                 // There may be multiple 'ID32' atoms with different language codes.
                 // For now, return the first one.
                 return (CQT_ID32_Atom*)pMetaAtom->FindPresentChild(QT_ID32);
             }
         }
     }
     return NULL;
 }
 #endif // QTCONFIG_3GPP_METAINFO
 
 #ifdef QTCONFIG_ITUNES_METAINFO
 CQT_iTunes_ilst_Atom* CQT_ManagerTools::FindiTunesIlstAtom(CQT_meta_Atom* pMetaAtom)
 {
     if(pMetaAtom)
     {
         // HandlerBox is required to indicate the nature of the contents.
         CQT_hdlr_Atom* pHandlerAtom = (CQT_hdlr_Atom*)pMetaAtom->FindPresentChild(QT_hdlr);
         if(pHandlerAtom)
         {
             if(pHandlerAtom->Get_CompSubtype() == QT_mdir)
             {
                 // Search for ilst child.
                 return (CQT_iTunes_ilst_Atom*)pMetaAtom->FindPresentChild(QT_ilst);
             }
         }
     }
     return NULL;
 }
 
-CQT_iTunes_data_Atom* CQT_ManagerTools::FindiTunesDataAtom(CQT_iTunes_ilst_Atom* pIlstAtom, QTAtomType MetaInfoAtomType)
+CQTAtom* CQT_ManagerTools::FindiTunesItemAtom(CQT_iTunes_ilst_Atom* pIlstAtom, QTAtomType ItemAtomType)
 {
     if(pIlstAtom)
     {
-        CQTAtom* piTunesMetaInfoAtom = pIlstAtom->FindPresentChild(MetaInfoAtomType);
-        if(piTunesMetaInfoAtom)
-        {
-            return (CQT_iTunes_data_Atom*)piTunesMetaInfoAtom->FindPresentChild(QT_data);
+        return pIlstAtom->FindPresentChild(ItemAtomType);
+    }
+    return NULL;
         }
+
+CQT_iTunes_data_Atom* CQT_ManagerTools::FindiTunesDataAtom(CQT_iTunes_ilst_Atom* pIlstAtom, QTAtomType ItemAtomType)
+{
+    CQTAtom* piTunesItemAtom = FindiTunesItemAtom(pIlstAtom, ItemAtomType);
+    if(piTunesItemAtom)
+    {
+        return (CQT_iTunes_data_Atom*)piTunesItemAtom->FindPresentChild(QT_data);
     }
     return NULL;
 }
 #endif // QTCONFIG_ITUNES_METAINFO
 
 /****************************************************************************
  *  Track Edit Manager
  */
 #define QT_MAX_MOVIE_DURATION	0xFFFFFFFF
 
 /****************************************************************************
  *  Constructor/Destructor
  */
 CQT_TrackEdit_Manager::CQT_TrackEdit_Manager(void)
     : m_pEditListAtom(NULL)
     , m_ulMovieTimeScale(1)
     , m_ulMediaTimeScale(1)
     , m_ulNumEdits(0)
     , m_ulCurrentEditIdx(0)
     , m_ulCurrentEditTime(0)
     , m_ulCurrentInEditTime(0)
     , m_ulCurrentEditDuration(QT_MAX_MOVIE_DURATION)
     , m_ulCurrentMediaStartTime(0)
 {
     ;
 }
 
 CQT_TrackEdit_Manager::~CQT_TrackEdit_Manager(void)
 {
     HX_RELEASE(m_pEditListAtom);
@@ -2740,175 +2746,178 @@
     , m_pClsfAtom(NULL)
     , m_pKywdAtom(NULL)
     , m_pLociAtom(NULL)
     , m_pAlbmAtom(NULL)
     , m_pYrrcAtom(NULL)
     , m_pID32Atom(NULL)
 #endif // QTCONFIG_3GPP_METAINFO
 
 #ifdef QTCONFIG_ITUNES_METAINFO
     , m_piTunesDataAtomA9nam(NULL)
     , m_piTunesDataAtomA9ART(NULL)
     , m_piTunesDataAtomA9alb(NULL)
     , m_piTunesDataAtomA9gen(NULL)
     , m_piTunesDataAtomA9day(NULL)
     , m_piTunesDataAtomA9cmt(NULL)
     , m_piTunesDataAtomA9wrt(NULL)
     , m_piTunesDataAtomA9too(NULL)
     , m_piTunesDataAtomA9enc(NULL)
     , m_piTunesDataAtomA9grp(NULL)
     , m_piTunesDataAtomgrup(NULL)
     , m_piTunesDataAtomaART(NULL)
     , m_piTunesDataAtomcprt(NULL)
     , m_piTunesDataAtomA9st3(NULL)
     , m_piTunesDataAtomA9lyr(NULL)
     , m_piTunesDataAtomtmpo(NULL)
     , m_piTunesDataAtomcpil(NULL)
     , m_piTunesDataAtomrtng(NULL)
     , m_piTunesDataAtomtrkn(NULL)
     , m_piTunesDataAtomdisk(NULL)
     , m_piTunesDataAtomgnre(NULL)
+    , m_piTunesItemAtomcovr(NULL)
 #endif //QTCONFIG_ITUNES_METAINFO
 
     , m_pRTPSDPAtom(NULL)
     , m_pRefURL(NULL)
 {
     ;
 }
 
 CQT_MovieInfo_Manager::~CQT_MovieInfo_Manager()
 {
     Clear();
 }
 
 void CQT_MovieInfo_Manager::Clear()
 {
     HX_RELEASE(m_pNameAtom);
 
 #ifdef QTCONFIG_3GPP_TAC
     HX_RELEASE(m_pTitlAtom);
     HX_RELEASE(m_pAuthAtom);
     HX_RELEASE(m_pCprtAtom);
 #endif // QTCONFIG_3GPP_TAC
 #ifdef QTCONFIG_3GPP_METAINFO
     HX_RELEASE(m_pDscpAtom);
     HX_RELEASE(m_pPerfAtom);
     HX_RELEASE(m_pGnreAtom);
     HX_RELEASE(m_pRtngAtom);
     HX_RELEASE(m_pClsfAtom);
     HX_RELEASE(m_pKywdAtom);
     HX_RELEASE(m_pLociAtom);
     HX_RELEASE(m_pAlbmAtom);
     HX_RELEASE(m_pYrrcAtom);
     HX_RELEASE(m_pID32Atom);
 #endif // QTCONFIG_3GPP_METAINFO
 
 #ifdef QTCONFIG_ITUNES_METAINFO
     HX_RELEASE(m_piTunesDataAtomA9nam);
     HX_RELEASE(m_piTunesDataAtomA9ART);
     HX_RELEASE(m_piTunesDataAtomA9alb);
     HX_RELEASE(m_piTunesDataAtomA9gen);
     HX_RELEASE(m_piTunesDataAtomA9day);
     HX_RELEASE(m_piTunesDataAtomA9cmt);
     HX_RELEASE(m_piTunesDataAtomA9wrt);
     HX_RELEASE(m_piTunesDataAtomA9too);
     HX_RELEASE(m_piTunesDataAtomA9enc);
     HX_RELEASE(m_piTunesDataAtomA9grp);
     HX_RELEASE(m_piTunesDataAtomgrup);
     HX_RELEASE(m_piTunesDataAtomaART);
     HX_RELEASE(m_piTunesDataAtomcprt);
     HX_RELEASE(m_piTunesDataAtomA9st3);
     HX_RELEASE(m_piTunesDataAtomA9lyr);
     HX_RELEASE(m_piTunesDataAtomtmpo);
     HX_RELEASE(m_piTunesDataAtomcpil);
     HX_RELEASE(m_piTunesDataAtomrtng);
     HX_RELEASE(m_piTunesDataAtomtrkn);
     HX_RELEASE(m_piTunesDataAtomdisk);
     HX_RELEASE(m_piTunesDataAtomgnre);
+    HX_RELEASE(m_piTunesItemAtomcovr);
 #endif //QTCONFIG_ITUNES_METAINFO
 
     HX_VECTOR_DELETE(m_pRefURL);
     HX_RELEASE(m_pRTPSDPAtom);
 }
 
 /****************************************************************************
  *  Main Interface
  */
 HX_RESULT CQT_MovieInfo_Manager::Init(CQTAtom* pAtom,
 				      CQTTrackManager* pTrackManager)
 {
     HX_RESULT retVal;
 
     m_ulMovieTimeScale = 0;
     m_ulMovieDuration = 0;
     HX_RELEASE(m_pNameAtom);
 
 #ifdef QTCONFIG_3GPP_TAC
     HX_RELEASE(m_pTitlAtom);
     HX_RELEASE(m_pAuthAtom);
     HX_RELEASE(m_pCprtAtom);
 #endif // QTCONFIG_3GPP_TAC
 #ifdef QTCONFIG_3GPP_METAINFO
     HX_RELEASE(m_pDscpAtom);
     HX_RELEASE(m_pPerfAtom);
     HX_RELEASE(m_pGnreAtom);
     HX_RELEASE(m_pRtngAtom);
     HX_RELEASE(m_pClsfAtom);
     HX_RELEASE(m_pKywdAtom);
     HX_RELEASE(m_pLociAtom);
     HX_RELEASE(m_pAlbmAtom);
     HX_RELEASE(m_pYrrcAtom);
     HX_RELEASE(m_pID32Atom);
 #endif // QTCONFIG_3GPP_METAINFO
 
 #ifdef QTCONFIG_ITUNES_METAINFO
     HX_RELEASE(m_piTunesDataAtomA9nam);
     HX_RELEASE(m_piTunesDataAtomA9ART);
     HX_RELEASE(m_piTunesDataAtomA9alb);
     HX_RELEASE(m_piTunesDataAtomA9gen);
     HX_RELEASE(m_piTunesDataAtomA9day);
     HX_RELEASE(m_piTunesDataAtomA9cmt);
     HX_RELEASE(m_piTunesDataAtomA9wrt);
     HX_RELEASE(m_piTunesDataAtomA9too);
     HX_RELEASE(m_piTunesDataAtomA9enc);
     HX_RELEASE(m_piTunesDataAtomA9grp);
     HX_RELEASE(m_piTunesDataAtomgrup);
     HX_RELEASE(m_piTunesDataAtomaART);
     HX_RELEASE(m_piTunesDataAtomcprt);
     HX_RELEASE(m_piTunesDataAtomA9st3);
     HX_RELEASE(m_piTunesDataAtomA9lyr);
     HX_RELEASE(m_piTunesDataAtomtmpo);
     HX_RELEASE(m_piTunesDataAtomcpil);
     HX_RELEASE(m_piTunesDataAtomrtng);
     HX_RELEASE(m_piTunesDataAtomtrkn);
     HX_RELEASE(m_piTunesDataAtomdisk);
     HX_RELEASE(m_piTunesDataAtomgnre);
+    HX_RELEASE(m_piTunesItemAtomcovr);
 #endif //QTCONFIG_ITUNES_METAINFO
 
     HX_RELEASE(m_pRTPSDPAtom);
 
     if (pAtom && (pAtom->GetType() != QT_moov))
     {
 	pAtom = NULL;
     }
 
 #ifdef QTCONFIG_3GPP_METAINFO
     m_pID32Atom = FindID32Atom((CQT_moov_Atom*)pAtom);
     if(m_pID32Atom)
     {
         m_pID32Atom->AddRef();
     }
 #endif // QTCONFIG_3GPP_METAINFO
 
     if (pAtom)
     {
 	CQT_mvhd_Atom *pMovieHeaderAtom = NULL;
 
 	pMovieHeaderAtom = (CQT_mvhd_Atom*)
 			   pAtom->FindPresentChild(QT_mvhd);
 
 	CQT_rmra_Atom *pRmraHeaderAtom = NULL;
 	pRmraHeaderAtom = (CQT_rmra_Atom*)
 			   pAtom->FindPresentChild(QT_rmra);
 	if (pMovieHeaderAtom)
 	{
 	    m_ulMovieTimeScale = pMovieHeaderAtom->Get_TimeScale();
@@ -3089,60 +3098,63 @@
 
                     m_piTunesDataAtomaART = CQT_ManagerTools::FindiTunesDataAtom(pIlstAtom, QT_aART);
                     HX_ADDREF(m_piTunesDataAtomaART);
 
                     m_piTunesDataAtomcprt = CQT_ManagerTools::FindiTunesDataAtom(pIlstAtom, QT_cprt);
                     HX_ADDREF(m_piTunesDataAtomcprt);
 
                     m_piTunesDataAtomA9st3 = CQT_ManagerTools::FindiTunesDataAtom(pIlstAtom, QT_A9st3);
                     HX_ADDREF(m_piTunesDataAtomA9st3);
 
                     m_piTunesDataAtomA9lyr = CQT_ManagerTools::FindiTunesDataAtom(pIlstAtom, QT_A9lyr);
                     HX_ADDREF(m_piTunesDataAtomA9lyr);
 
                     m_piTunesDataAtomtmpo = CQT_ManagerTools::FindiTunesDataAtom(pIlstAtom, QT_tmpo);
                     HX_ADDREF(m_piTunesDataAtomtmpo);
 
                     m_piTunesDataAtomcpil = CQT_ManagerTools::FindiTunesDataAtom(pIlstAtom, QT_cpil);
                     HX_ADDREF(m_piTunesDataAtomcpil);
 
                     m_piTunesDataAtomrtng = CQT_ManagerTools::FindiTunesDataAtom(pIlstAtom, QT_rtng);
                     HX_ADDREF(m_piTunesDataAtomrtng);
 
                     m_piTunesDataAtomtrkn = CQT_ManagerTools::FindiTunesDataAtom(pIlstAtom, QT_trkn);
                     HX_ADDREF(m_piTunesDataAtomtrkn);
 
                     m_piTunesDataAtomdisk = CQT_ManagerTools::FindiTunesDataAtom(pIlstAtom, QT_disk);
                     HX_ADDREF(m_piTunesDataAtomdisk);
 
                     m_piTunesDataAtomgnre = CQT_ManagerTools::FindiTunesDataAtom(pIlstAtom, QT_gnre);
                     HX_ADDREF(m_piTunesDataAtomgnre);
+
+                    m_piTunesItemAtomcovr = (CQT_iTunes_covr_Atom*)CQT_ManagerTools::FindiTunesItemAtom(pIlstAtom, QT_covr);
+                    HX_ADDREF(m_piTunesItemAtomcovr);
                 }
             }
         }
 #endif //QTCONFIG_ITUNES_METAINFO
 
 	pAtom = pAtom->FindPresentChild(QT_hnti);
     }
 
     ParseMovieHintInfo(pAtom);
 
     retVal = m_ulMovieTimeScale ? HXR_OK : HXR_FAIL;
 
     return retVal;
 }
 
 #ifdef QTCONFIG_3GPP_METAINFO
 static void MergeLanguageEncoding(HXBOOL& bFound, HXBOOL& bAmbiguous, char lang[4], char out[3])
 {
     if(!bAmbiguous)
     {
         CHXISO639LangCodeRegistry lcreg;
         if(bFound)
         {
             if((lang[0] != out[0]) || (lang[1] != out[1]) || (lang[2] != out[2]))
             {
                 bAmbiguous = TRUE;
             }
         }
         else if(lcreg.IsValidLangCode(lang))
         {
Index: qtatoms.cpp
===================================================================
RCS file: /cvsroot/datatype/mp4/fileformat/qtatoms.cpp,v
retrieving revision 1.15
diff -d -H -w -U30 -r1.15 qtatoms.cpp
--- qtatoms.cpp	23 Oct 2009 09:53:02 -0000	1.15
+++ qtatoms.cpp	6 Nov 2009 21:30:01 -0000
@@ -175,60 +175,62 @@
     case QT_A9gen:
         return new CQT_iTunes_A9gen_Atom(ulOffset, ulSize, pParent);
     case QT_A9day:
         return new CQT_iTunes_A9day_Atom(ulOffset, ulSize, pParent);
     case QT_A9cmt:
         return new CQT_iTunes_A9cmt_Atom(ulOffset, ulSize, pParent);
     case QT_A9wrt:
         return new CQT_iTunes_A9wrt_Atom(ulOffset, ulSize, pParent);
     case QT_A9too:
         return new CQT_iTunes_A9too_Atom(ulOffset, ulSize, pParent);
     case QT_A9enc:
         return new CQT_iTunes_A9enc_Atom(ulOffset, ulSize, pParent);
     case QT_A9grp:
         return new CQT_iTunes_A9grp_Atom(ulOffset, ulSize, pParent);
     case QT_grup:
         return new CQT_iTunes_grup_Atom(ulOffset, ulSize, pParent);
     case QT_aART:
         return new CQT_iTunes_aART_Atom(ulOffset, ulSize, pParent);
     case QT_A9st3:
         return new CQT_iTunes_A9st3_Atom(ulOffset, ulSize, pParent);
     case QT_A9lyr:
         return new CQT_iTunes_A9lyr_Atom(ulOffset, ulSize, pParent);
     case QT_tmpo:
         return new CQT_iTunes_tmpo_Atom(ulOffset, ulSize, pParent);
     case QT_cpil:
         return new CQT_iTunes_cpil_Atom(ulOffset, ulSize, pParent);
     case QT_trkn:
         return new CQT_iTunes_trkn_Atom(ulOffset, ulSize, pParent);
     case QT_disk:
         return new CQT_iTunes_disk_Atom(ulOffset, ulSize, pParent);
+    case QT_covr:
+        return new CQT_iTunes_covr_Atom(ulOffset, ulSize, pParent);
 #endif //QTCONFIG_ITUNES_METAINFO
 
     case QT_meta:
         return new CQT_meta_Atom(ulOffset, ulSize, pParent);
     case QT_minf:
 	return new CQT_minf_Atom(ulOffset, ulSize, pParent);
     case QT_dinf:
 	return new CQT_dinf_Atom(ulOffset, ulSize, pParent);
     case QT_dref:
 	return new CQT_dref_Atom(ulOffset, ulSize, pParent);
     case QT_stbl:
 	return new CQT_stbl_Atom(ulOffset, ulSize, pParent);
     case QT_stts:
 	return new CQT_stts_Atom(ulOffset, ulSize, pParent);
     case QT_ctts:
 	return new CQT_ctts_Atom(ulOffset, ulSize, pParent);
     case QT_stss:
 	return new CQT_stss_Atom(ulOffset, ulSize, pParent);
     case QT_stsd:
 	return new CQT_stsd_Atom(ulOffset, ulSize, pParent);
     case QT_stsz:
 	return new CQT_stsz_Atom(ulOffset, ulSize, pParent);
     case QT_stz2:
 	return new CQT_stz2_Atom(ulOffset, ulSize, pParent);
     case QT_stsc:
 	return new CQT_stsc_Atom(ulOffset, ulSize, pParent);
     case QT_stco:
 	return new CQT_stco_Atom(ulOffset, ulSize, pParent);
     case QT_co64:
 	return new CQT_co64_Atom(ulOffset, ulSize, pParent);
Index: qtffplin.cpp
===================================================================
RCS file: /cvsroot/datatype/mp4/fileformat/qtffplin.cpp,v
retrieving revision 1.80
diff -d -H -w -U30 -r1.80 qtffplin.cpp
--- qtffplin.cpp	26 Oct 2009 21:15:02 -0000	1.80
+++ qtffplin.cpp	6 Nov 2009 21:30:05 -0000
@@ -750,62 +750,70 @@
             if(SUCCEEDED(status))
             {
                 status = UpdateHeaderProperty(status, pHeader,
                             _3GPP_META_INFO_LOCATION_ALTITUDE_KEY, strPos, strlen(strPos));
             }
         }
     }
 
     // 3GP Asset Info: Album
     if(SUCCEEDED(status) && m_MovieInfo.GetAlbumTitleLength())
     {
         status = UpdateHeaderProperty(status, pHeader, _3GPP_META_INFO_ALBUM_TITLE_KEY,
                        (const char*)m_MovieInfo.GetAlbumTitle(), m_MovieInfo.GetAlbumTitleLength());
     }
 
     // track number is optional
     if(SUCCEEDED(status) && m_MovieInfo.HasTrackNumber())
     {
         pHeader->SetPropertyULONG32(_3GPP_META_INFO_TRACK_NUMBER_KEY, (ULONG32) m_MovieInfo.GetTrackNumber());
     }
 
     // 3GP Asset Info: Recording Year
     if(SUCCEEDED(status) && m_MovieInfo.HasRecordingYear())
     {
         status = pHeader->SetPropertyULONG32(_3GPP_META_INFO_RECORDING_YEAR_KEY, (ULONG32) m_MovieInfo.GetRecordingYear());
     }
 
     // 3GP Asset Info: ID32 BLOB
     if(SUCCEEDED(status) && m_MovieInfo.GetID32BlobSize())
     {
+        // _3GPP_META_INFO_ID32_BLOB_KEY is a legacy way of injecting ID32 data into the header
         status = SetBufferPropertyCCF(pHeader, _3GPP_META_INFO_ID32_BLOB_KEY,
                         m_MovieInfo.GetID32Blob(), m_MovieInfo.GetID32BlobSize(), m_pContext);
+
+        // 3GP ID32 box may be contained by moov box, in this case
+        // insert results of ID32 blob processing into the file header
+        if(SUCCEEDED(status))
+        {
+            status = ID3Tools::TransformID32BlobToProperties(pHeader, m_MovieInfo.GetID32Blob(), m_MovieInfo.GetID32BlobSize());
+        }
     }
 
     // 3GP language encoding, extract as global property for all atoms
     if(SUCCEEDED(status))
     {
         char globalLangEnc[4] = { 0, 0, 0, 0 };
         if(m_MovieInfo.GetGlobalLanguageEncoding(globalLangEnc))
         {
             status = SetCStringPropertyWithNullTermEx(pHeader, _3GPP_META_INFO_GLOBAL_LANGUAGE_ENCODING_KEY,
                       (BYTE*)globalLangEnc, 3, m_pContext, HX_TEXT_ENCODING_TYPE_UTF8, SET_AS_BUFFER_PROPERTY);
         }
     }
 #endif // QTCONFIG_3GPP_METAINFO
 
     return status;
 }
 #endif // QTCONFIG_3GPP_TAC || QTCONFIG_3GPP_METAINFO
 
 #ifdef QTCONFIG_ITUNES_METAINFO
 HX_RESULT CQTFileFormat::ExtractiTunesAssetInfoToHeader(HX_RESULT status, IHXValues* pHeader)
 {
     // iTunes Asset Info: Song Name
     if(SUCCEEDED(status) && m_MovieInfo.GetiTunesSongNameByteLength())
     {
         status = UpdateHeaderProperty(status, pHeader, _ITUNES_META_INFO_TITLE_KEY,
                         (const char*)m_MovieInfo.GetiTunesSongName(), m_MovieInfo.GetiTunesSongNameByteLength(),
                         m_MovieInfo.GetiTunesSongNameEncoding());
     }
 
     // iTunes Asset Info: Artist
@@ -958,60 +966,112 @@
     // iTunes Asset Info: Content Rating
     if(SUCCEEDED(status) && m_MovieInfo.HasiTunesContentRating())
     {
         status = pHeader->SetPropertyULONG32(_ITUNES_META_INFO_EXPLICIT_CONTENT_KEY, (ULONG32) m_MovieInfo.GetiTunesContentRating());
     }
 
     // iTunes Asset Info: Track Number
     if(SUCCEEDED(status) && m_MovieInfo.HasiTunesTrackNumber())
     {
         status = pHeader->SetPropertyULONG32(_ITUNES_META_INFO_TRACK_NUMBER_KEY, (ULONG32) m_MovieInfo.GetiTunesTrackNumber());
     }
 
     // iTunes Asset Info: Track Count
     if(SUCCEEDED(status) && m_MovieInfo.HasiTunesTrackCount())
     {
         status = pHeader->SetPropertyULONG32(_ITUNES_META_INFO_TRACK_COUNT_KEY, (ULONG32) m_MovieInfo.GetiTunesTrackCount());
     }
 
     // iTunes Asset Info: Disc Number
     if(SUCCEEDED(status) && m_MovieInfo.HasiTunesDiscNumber())
     {
         status = pHeader->SetPropertyULONG32(_ITUNES_META_INFO_DISC_NUMBER_KEY, (ULONG32) m_MovieInfo.GetiTunesDiscNumber());
     }
 
     // iTunes Asset Info: Disc Count
     if(SUCCEEDED(status) && m_MovieInfo.HasiTunesDiscCount())
     {
         status = pHeader->SetPropertyULONG32(_ITUNES_META_INFO_DISC_COUNT_KEY, (ULONG32) m_MovieInfo.GetiTunesDiscCount());
     }
 
+    // iTunes Asset Info: Cover Art
+    if(SUCCEEDED(status) && m_MovieInfo.GetiTunesImageCount())
+    {
+        int iAPICDataIndex = 0;
+
+        // go through available iTunes images
+        for(UINT16 index = 0; SUCCEEDED(status) && (index < m_MovieInfo.GetiTunesImageCount()); index++)
+        {
+            const char* szImageMimeType = m_MovieInfo.GetiTunesImageValueMimeType(index);
+            UINT8* pImageData = m_MovieInfo.GetiTunesImageValue(index);
+            UINT32 uImageDataByteLen = m_MovieInfo.GetiTunesImageValueByteLength(index);
+
+            if(szImageMimeType && pImageData && uImageDataByteLen)
+            {
+                status = HXR_OUTOFMEMORY;
+
+                // construct APICFrame
+                ID3Tools::APICFrame* pFrame = new ID3Tools::APICFrame;
+                if(pFrame)
+                {
+                    // ID3v2 picture type has no equivalent field in iTunes meta-info structures,
+                    // so just use unique value for each injected frame.
+                    pFrame->SetPictureType((UINT8)index);
+                    pFrame->SetMimeType(szImageMimeType);
+
+                    status = pFrame->SetPictureData(pImageData, uImageDataByteLen);
+                }
+
+                // inject frame into the header
+                if(SUCCEEDED(status) && pFrame)
+                {
+                    char key[128];
+                    sprintf(key, _ITUNES_META_INFO_PICTURE_KEY, int(iAPICDataIndex));
+
+                    status = pFrame->SerializeToBufferProperty(pHeader, key);
+                }
+
+                // update image count property
+                if(SUCCEEDED(status))
+                {
+                    iAPICDataIndex++;
+
+                    PropTools::CopyPropertyULONG32(pHeader, TRUE, iAPICDataIndex,
+                                        _GENERIC_META_INFO_PICTURE_COUNT_KEY, TRUE);
+                }
+
+                // cleanup
+                delete pFrame;
+            }
+        }
+    }
+
     return status;
 }
 #endif //QTCONFIG_ITUNES_METAINFO
 
 HX_RESULT CQTFileFormat::MakeFileHeader(HX_RESULT status)
 {
     HX_RESULT retVal;
     IHXValues* pHeader = NULL;
 
     // Check the license
 #ifdef QTCONFIG_SERVER
     if (SUCCEEDED(status) && (m_TrackManager.GetEType() == QT_ETYPE_SERVER))
     {
         switch (m_TrackManager.GetFType())
         {
             case QT_FTYPE_QT:
             case QT_FTYPE_UNKNOWN:
             {
                 if(!m_bQTLicensed)
                 {
                     ReportError(IDS_ERR_QT_NOTLICENSED, HXR_NOT_LICENSED);
                     return m_pFFResponse->FileHeaderReady(
                         HXR_NOT_LICENSED, NULL);
                 }
                 break;
             }
             case QT_FTYPE_MP4:
             {
 		UINT32 ulBrand = 0;
 		m_TrackManager.GetMajorBrand(&ulBrand);
Index: qttrack.cpp
===================================================================
RCS file: /cvsroot/datatype/mp4/fileformat/qttrack.cpp,v
retrieving revision 1.39
diff -d -H -w -U30 -r1.39 qttrack.cpp
--- qttrack.cpp	5 Oct 2009 20:50:08 -0000	1.39
+++ qttrack.cpp	6 Nov 2009 21:30:06 -0000
@@ -37,60 +37,61 @@
  *  Defines
  */
 // #define _LOG_DATA_ACCESS
 #define QTTRACKCACHE_PAGE_SIZE	0x0000FFFF
 
 #ifdef HELIX_FEATURE_MIN_HEAP
 #define QT_MAX_SEEK_SKIPBACK_TIME_CLIENT	20000   // milliseconds
 #else
 #define QT_MAX_SEEK_SKIPBACK_TIME_CLIENT	300000  // milliseconds
 #endif
 #define QT_MAX_SEEK_SKIPBACKAHEAD_RATIO_CLIENT	20
 #define QT_MAX_SEEK_SKIPBACK_TIME_SERVER	10000	// milliseconds
 #define QT_MAX_SEEK_SKIPBACKAHEAD_RATIO_SERVER	5	
 #define QT_NO_KEY_FRAME_SKIPBACK_TIME		5000
 
 
 /****************************************************************************
  *  Includes
  */
 #include "qtpktasm.h"
 
 #include "qtffplin.h"
 #include "qtpacketizerfct.h"
 #include "qttrack.h"
 #include "qtffrefcounter.h"
 
 #include "hxstrutl.h"
 
 #include "sdpchunk.h"
 #include "sdppyldinfo.h"
+#include "metautil.h"
 
 
 /****************************************************************************
  *  Class CQTTrack
  */
 /****************************************************************************
  *  Constructor/Destructor
  */
 CQTTrack::CQTTrack(CQTAtom* pTrackAtom)
     : m_pTrackAtom((CQT_trak_Atom*) pTrackAtom)
     , m_pResponse(NULL)
     , m_pFileFormat(NULL)
     , m_pPacketAssembler(NULL)
     , m_pClassFactory(NULL)
     , m_ulTrackID(0)
     , m_ulReadSize(0)
     , m_ulReadPageSize(0)
     , m_ulReadPageOffset(0)
     , m_pReadFileNameBuffer(NULL)
     , m_pReadFileName(NULL)
     , m_PendingState(QTT_Offline)
     , m_bTrackDone(FALSE)
     , m_uBytesPerCBlock(0)
     , m_uSamplesPerCBlock(0)
     , m_uStreamNumber(0)
     , m_uBaseRuleNumber(0)
     , m_pPacketizer(NULL)
     , m_pFileSwitcher(NULL)
     , m_bIsSubscribed(FALSE)
     , m_lRefCount(0)
@@ -526,60 +527,73 @@
         if (ulWidth != 0)
 	{
 	    pHeader->SetPropertyULONG32(QT_WIDTH_METANAME, ulWidth);
 	}
 
 	if (ulHeight != 0)
 	{
 	    pHeader->SetPropertyULONG32(QT_HEIGHT_METANAME, ulHeight);
 	}
 
 	if (ulFrameWidth != 0)
 	{
 	    pHeader->SetPropertyULONG32(QT_FRAMEWIDTH_METANAME, ulFrameWidth);
 	}
 
 	if (ulFrameHeight != 0)
 	{
 	    pHeader->SetPropertyULONG32(QT_FRAMEHEIGHT_METANAME, ulFrameHeight);
 	}
 
     if (m_TrackInfo.GetTrackType() == QT_vide)
     {
         UINT32 framesPerMSecond;
         if ( (framesPerMSecond=GetFramesPerMSecond(pMovieInfo)) )
         {
 	            pHeader->SetPropertyULONG32("FramesPerMSecond", framesPerMSecond);
         }
     }
     }
 
+#ifdef QTCONFIG_3GPP_METAINFO
+    if (SUCCEEDED(retVal) && m_TrackInfo.GetID32BlobSize())
+    {
+        // 3GP ID32 box may be contained by trak box, in this case
+        // insert results of ID32 blob processing into the stream header
+        ID3Tools::TransformID32BlobToProperties(pHeader, m_TrackInfo.GetID32Blob(), m_TrackInfo.GetID32BlobSize());
+
+        // _3GPP_META_INFO_ID32_BLOB_KEY is a legacy way of injecting ID32 data into the header
+        SetBufferPropertyCCF(pHeader, _3GPP_META_INFO_ID32_BLOB_KEY,
+                m_TrackInfo.GetID32Blob(), m_TrackInfo.GetID32BlobSize(), m_pFileFormat->GetContext());
+    }
+#endif // QTCONFIG_3GPP_METAINFO
+
     if (SUCCEEDED(retVal))
     {
 	// .mp4 and .mov files may produce streams with out-of-order time stamps
 	// Client core does not normally expect this in RDT protocol so set this
 	// property to alert client core of this fact.
 	pHeader->SetPropertyULONG32("HasOutOfOrderTS", 1);
     }
 
 #ifdef QTCONFIG_ALTERNATE_STREAMS
     // If switchable, add the base rule for this stream
     if (SUCCEEDED(retVal) && m_TrackInfo.GetSwitchGroupId() != 0)
     {
         pHeader->SetPropertyULONG32("BaseRule", m_uBaseRuleNumber);
     }
 #endif
 
     // Set ASM Rule Book
     if (SUCCEEDED(retVal))
     {
 	char pRuleBook[256]; /* Flawfinder: ignore */
 
 #ifdef QTCONFIG_SERVER
 	pHeader->GetPropertyULONG32("RTPPayloadType", ulPayloadType);
 
 	if (pTrackManager->GetEType() != QT_ETYPE_CLIENT)
 	{
 	    // Server Side
 	    retVal = HXR_FAIL;
 	    if (ulPayloadType != QT_BAD_PAYLOAD)
 	    {
Index: pub/qtatmmgs.h
===================================================================
RCS file: /cvsroot/datatype/mp4/fileformat/pub/qtatmmgs.h,v
retrieving revision 1.29
diff -d -H -w -U30 -r1.29 qtatmmgs.h
--- pub/qtatmmgs.h	23 Oct 2009 09:53:02 -0000	1.29
+++ pub/qtatmmgs.h	6 Nov 2009 21:30:08 -0000
@@ -61,61 +61,62 @@
 #define QT_TSEL_BANDWIDTH	0x02
 #define QT_TSEL_CODEC		0x04
 #define QT_TSEL_SCREEN_SIZE	0x08
 #define QT_TSEL_MAX_PACKET_SIZE	0x10
 #define QT_TSEL_MEDIA_TYPE	0x20
 
 
 /****************************************************************************
  *  Includes
  */
 #include "qtatoms.h"
 #include "hxcomm.h"
 
 class CQTTrackManager;
 class CQT_MovieInfo_Manager;
 
 /****************************************************************************
  *  Manager Tools
  */
 class CQT_ManagerTools
 {
 public:
     static CQT_meta_Atom* FindMetaAtom(CQTAtom* pAtom);
 
 #ifdef QTCONFIG_3GPP_METAINFO
     static CQT_ID32_Atom* FindID32Atom(CQT_meta_Atom* pMetaAtom);
 #endif // QTCONFIG_3GPP_METAINFO
 
 #ifdef QTCONFIG_ITUNES_METAINFO
     static CQT_iTunes_ilst_Atom* FindiTunesIlstAtom(CQT_meta_Atom* pMetaAtom);
-    static CQT_iTunes_data_Atom* FindiTunesDataAtom(CQT_iTunes_ilst_Atom* pIlstAtom, QTAtomType MetaInfoAtomType);
+    static CQTAtom* FindiTunesItemAtom(CQT_iTunes_ilst_Atom* pIlstAtom, QTAtomType ItemAtomType);
+    static CQT_iTunes_data_Atom* FindiTunesDataAtom(CQT_iTunes_ilst_Atom* pIlstAtom, QTAtomType ItemAtomType);
 #endif // QTCONFIG_ITUNES_METAINFO
 };
 
 /****************************************************************************
  *  Track Edit Manager
  *  Note: All locally stored times are in media units
  */
 class CQT_TrackEdit_Manager
 {
 public:
     /*
      *	Constructor/Destructor
      */
     CQT_TrackEdit_Manager(void);
     ~CQT_TrackEdit_Manager();
 
     /*
      *	Main Interface
      */
     HX_RESULT Init( CQTAtom* pAtom, 
 		    ULONG32 ulMovieTimeScale,
 		    ULONG32 ulMediaTimeScale);
 
     HXBOOL EstablishByMediaTime(ULONG32 ulTime);
 
     HXBOOL EstablishByTime(ULONG32 ulTime) // Given in miliseconds
     {
 	return EstablishByMediaTime(
 	    (ULONG32) (((double) ulTime) / 1000.0 * m_ulMediaTimeScale + 0.5));
     }
@@ -1369,60 +1370,85 @@
         return m_piTunesDataAtomtrkn ? m_piTunesDataAtomtrkn->IsAtomWellFormed() : FALSE;
     }
 
     UINT32 GetiTunesTrackCount(void)
     {
         return m_piTunesDataAtomtrkn ? m_piTunesDataAtomtrkn->GetSpecialValue_TrackCount() : 0;
     }
 
     //*** iTunes disk atom / Disc Number & Count *************************************
 
     HXBOOL HasiTunesDiscNumber(void)
     {
         return m_piTunesDataAtomdisk ? m_piTunesDataAtomdisk->IsAtomWellFormed() : FALSE;
     }
 
     UINT32 GetiTunesDiscNumber(void)
     {
         return m_piTunesDataAtomdisk ? m_piTunesDataAtomdisk->GetSpecialValue_DiskNumber() : 0;
     }
 
     HXBOOL HasiTunesDiscCount(void)
     {
         return m_piTunesDataAtomdisk ? m_piTunesDataAtomdisk->IsAtomWellFormed() : FALSE;
     }
 
     UINT32 GetiTunesDiscCount(void)
     {
         return m_piTunesDataAtomdisk ? m_piTunesDataAtomdisk->GetSpecialValue_DiskCount() : 0;
     }
 
+    //*** iTunes covr atom / Cover Art *************************************
+
+    UINT16 GetiTunesImageCount(void)
+    {
+        return m_piTunesItemAtomcovr ? m_piTunesItemAtomcovr->GetImageAtomCount() : 0;
+    }
+
+    const char* GetiTunesImageValueMimeType(UINT16 imageIndex)
+    {
+        CQT_iTunes_data_Atom* pDataAtom = m_piTunesItemAtomcovr ? m_piTunesItemAtomcovr->GetImageAtom(imageIndex) : 0;
+        return pDataAtom ? pDataAtom->GetImageValueMimeType() : 0;
+    }
+    
+    UINT32 GetiTunesImageValueByteLength(UINT16 imageIndex)
+    {
+        CQT_iTunes_data_Atom* pDataAtom = m_piTunesItemAtomcovr ? m_piTunesItemAtomcovr->GetImageAtom(imageIndex) : 0;
+        return pDataAtom ? pDataAtom->GetImageValueByteLength() : 0;
+    }
+
+    UINT8* GetiTunesImageValue(UINT16 imageIndex)
+    {
+        CQT_iTunes_data_Atom* pDataAtom = m_piTunesItemAtomcovr ? m_piTunesItemAtomcovr->GetImageAtom(imageIndex) : 0;
+        return pDataAtom ? pDataAtom->GetImageValue() : 0;
+    }
+
 #endif //QTCONFIG_ITUNES_METAINFO
 
     char* GetRefURL(void)
     {
         return m_pRefURL;
     }
 
     ULONG32 GetSDPLength(void);
     UINT8* GetSDP(void);
 
 private:
     HX_RESULT ParseMovieHintInfo(CQTAtom* pAtom);
 	
 #ifdef QTCONFIG_3GPP_METAINFO
     static CQT_ID32_Atom* FindID32Atom(CQT_moov_Atom* pMoovAtom);
 #endif // QTCONFIG_3GPP_METAINFO
 
     char *m_pRefURL;
     ULONG32 m_ulMovieTimeScale;
     ULONG32 m_ulMovieDuration;
 
     CQT_name_Atom*    m_pNameAtom;
 
 #ifdef QTCONFIG_3GPP_TAC
     CQT_titl_Atom*    m_pTitlAtom;
     CQT_auth_Atom*    m_pAuthAtom;
     CQT_cprt_Atom*    m_pCprtAtom;
 #endif // QTCONFIG_3GPP_TAC
 #ifdef QTCONFIG_3GPP_METAINFO
     CQT_dscp_Atom*    m_pDscpAtom;
@@ -1432,40 +1458,41 @@
     CQT_clsf_Atom*    m_pClsfAtom;
     CQT_kywd_Atom*    m_pKywdAtom;
     CQT_loci_Atom*    m_pLociAtom;
     CQT_albm_Atom*    m_pAlbmAtom;
     CQT_yrrc_Atom*    m_pYrrcAtom;
     CQT_ID32_Atom*    m_pID32Atom;
 #endif // QTCONFIG_3GPP_METAINFO
 
 #ifdef QTCONFIG_ITUNES_METAINFO
     CQT_iTunes_data_Atom*  m_piTunesDataAtomA9nam;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomA9ART;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomA9alb;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomA9gen;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomA9day;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomA9cmt;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomA9wrt;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomA9too;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomA9enc;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomA9grp;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomgrup;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomaART;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomcprt;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomA9st3;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomA9lyr;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomtmpo;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomcpil;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomrtng;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomtrkn;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomdisk;
     CQT_iTunes_data_Atom*  m_piTunesDataAtomgnre;
+    CQT_iTunes_covr_Atom*  m_piTunesItemAtomcovr;
 #endif //QTCONFIG_ITUNES_METAINFO
 
     CQT_rtp_Atom*     m_pRTPSDPAtom;
 };
 
 #ifdef QTCONFIG_SPEED_OVER_SIZE
 #include "qtatmmgs_inline.h"
 #endif	// QTCONFIG_SPEED_OVER_SIZE
 
 #endif  // _QTATMMGS_H_
Index: pub/qtatoms.h
===================================================================
RCS file: /cvsroot/datatype/mp4/fileformat/pub/qtatoms.h,v
retrieving revision 1.36
diff -d -H -w -U30 -r1.36 qtatoms.h
--- pub/qtatoms.h	23 Oct 2009 09:53:02 -0000	1.36
+++ pub/qtatoms.h	6 Nov 2009 21:30:12 -0000
@@ -1587,82 +1587,112 @@
             case QT_A9too:
             case QT_A9enc:
             case QT_A9grp:
             case QT_grup:
             case QT_aART:
             case QT_cprt:
             case QT_A9st3:
             case QT_A9lyr:
                 return ((tc == TypeIndicatorTypeCode_Implicit || tc == TypeIndicatorTypeCode_UTF8) && (valueSize >= 1))
                     || ((tc == TypeIndicatorTypeCode_UTF16BE) && (valueSize >= 2) && (valueSize % 2 == 0));
 
             case QT_tmpo: //4 bytes per iTMF spec, most tools expect 2 bytes
             case QT_cpil: //1 byte per iTMF spec
             case QT_rtng: //1 byte per iTMF spec
                 // tolerate integer sizes 1,2,4 for any pure integer field
                 return (tc == TypeIndicatorTypeCode_Implicit || tc == TypeIndicatorTypeCode_Integer)
                         && ((valueSize == 1) || (valueSize == 2) || (valueSize == 4));
 
             case QT_trkn:
                 // 7 bytes per iTMF spec, most tools expect and write out 8 bytes, we need only 6
                 return (tc == TypeIndicatorTypeCode_Implicit) && (valueSize >= 6);
 
             case QT_disk:
                 // 6 bytes per iTMF spec, but we can tolerate anything larger
                 return (tc == TypeIndicatorTypeCode_Implicit) && (valueSize >= 6);
 
             case QT_gnre:
                 // list of 2-byte entries
                 return (tc == TypeIndicatorTypeCode_Implicit || tc == TypeIndicatorTypeCode_Genres)
                         && (valueSize >= 2) && (valueSize % 2 == 0);
+
+            case QT_covr:
+                // currently we support JPEG and PNG
+                return (tc == TypeIndicatorTypeCode_JPEG || tc == TypeIndicatorTypeCode_PNG);
         }
         return FALSE;
     }
 
     HX_TEXT_ENCODING_TYPE GetStringValueEncoding()
     {
         return IsAtomWellFormed()
             ? ((GetTypeCode() == TypeIndicatorTypeCode_UTF16BE) ? HX_TEXT_ENCODING_TYPE_UTF16BE : HX_TEXT_ENCODING_TYPE_UTF8)
             : HX_TEXT_ENCODING_TYPE_UNKNOWN;
     }
 
-    ULONG32 GetStringValueByteLength()
+    UINT32 GetStringValueByteLength()
     {
         return IsAtomWellFormed() ? GetValueSize() : 0;
     }
 
     UINT8* GetStringValue()
     {
         return IsAtomWellFormed() ? ((Data*) m_pData)->pValue : 0;
     }
 
-    ULONG32 GetIntegerValue()
+    const char* GetImageValueMimeType()
+    {
+        if(IsAtomWellFormed())
+        {
+            switch(GetTypeCode())
+            {
+                case TypeIndicatorTypeCode_JPEG:
+                    return "image/jpeg";
+
+                case TypeIndicatorTypeCode_PNG:
+                    return "image/png";
+            }
+        }
+        return 0;
+    }
+
+    UINT32 GetImageValueByteLength()
+    {
+        return IsAtomWellFormed() ? GetValueSize() : 0;
+    }
+
+    UINT8* GetImageValue()
+    {
+        return IsAtomWellFormed() ? ((Data*) m_pData)->pValue : 0;
+    }
+
+    UINT32 GetIntegerValue()
     {
         UINT32 val = 0;
 
         if(IsAtomWellFormed())
         {
             switch(GetValueSize())
             {
                 case 1:
                     val = UINT32(*(((Data*)m_pData)->pValue));
                     break;
 
                 case 2:
                     val = UINT32(GetUI16(((Data*)m_pData)->pValue));
                     break;
 
                 case 4:
                     val = UINT32(GetUL32(((Data*)m_pData)->pValue));
                     break;
             }
         }
         return FilterIntegerValueByAtomType(val);
     }
 
     UINT32 GetSpecialValue_PredefinedGenreCount()
     {
         // gnre: 2 bytes for each predefined entry
         return IsAtomWellFormed() ? (GetValueSize() / 2) : 0;
     }
 
     UINT16 GetSpecialValue_PredefinedGenreEntry(UINT32 ulListIndex)
@@ -1690,60 +1720,62 @@
         return IsAtomWellFormed() ? UINT32(GetUI16(((Data*)m_pData)->pValue + 2)) : 0;
     }
 
     UINT32 GetSpecialValue_DiskCount()
     {
         // disk: 2 bytes reserved, 2 bytes disc number, 2 bytes disc count
         return IsAtomWellFormed() ? UINT32(GetUI16(((Data*)m_pData)->pValue + 4)) : 0;
     }
 
 protected:
     UINT32 GetValueSize()
     {
         // (value size) = (atom size) - (8 bytes atom size/type fields)
         //              - (4 bytes type-indicator field) - (4 bytes locale)
         const UINT32 headerSize = 16;
         UINT32 atomSize = GetSize();
         return (atomSize > headerSize) ? (atomSize - headerSize) : 0;
     }
 
     QTAtomType GetParentAtomType()
     {
         CQTAtom* pParent = GetParent();
         return pParent ? pParent->GetType() : 0;
     }
 
     enum TypeIndicatorTypeCode
     {
         TypeIndicatorTypeCode_Implicit = 0, //no type needs to be indicated because only one type is allowed
         TypeIndicatorTypeCode_UTF8 = 1, //without count or null terminator
         TypeIndicatorTypeCode_UTF16BE = 2, //without count or null terminator
+        TypeIndicatorTypeCode_JPEG = 13, //in JFIF wrapper
+        TypeIndicatorTypeCode_PNG = 14, //in PNG wrapper
         TypeIndicatorTypeCode_Genres = 18, //special enumeration
         TypeIndicatorTypeCode_Integer = 21 //signed big-endian integer in 1,2,3,4,8 bytes
     };
 
     TypeIndicatorTypeCode GetTypeCode()
     {
         return (TypeIndicatorTypeCode)((Data*)m_pData)->pTypeIndicatorTypeCode[0];
     }
 
     UINT32 GetLocale()
     {
         return UINT32(GetUL32(((Data*)m_pData)->pLocale));
     }
 
     UINT32 FilterIntegerValueByAtomType(UINT32 value)
     {
         switch(GetParentAtomType())
         {
             case QT_cpil: //1 byte boolean per iTMF spec
             case QT_rtng: //1 byte boolean per iTMF spec
                 return value ? 1 : 0;
         }
         return value;
     }
 };
 
 /****************************************************************************
  *  iTunes A9nam Atom Class
  */
 class CQT_iTunes_A9nam_Atom : public CQTAtom
@@ -2178,60 +2210,98 @@
 
     /*
      *	Required Virtual Methods
      */
     virtual HXBOOL	IsLeafType(void)  { return FALSE; }
     virtual QTAtomType	GetType(void)	{ return QT_trkn; }
 };
 
 /****************************************************************************
  *  iTunes disk Atom Class
  */
 class CQT_iTunes_disk_Atom : public CQTAtom
 {
 public:
     /*
      *	Constructor/Destructor
      */
     CQT_iTunes_disk_Atom(ULONG32 ulOffset,
                         ULONG32 ulSize,
                         CQTAtom *pParent) : CQTAtom(ulOffset,
                                                 ulSize,
                                                 pParent) {;}
 
     /*
      *	Required Virtual Methods
      */
     virtual HXBOOL	IsLeafType(void)  { return FALSE; }
     virtual QTAtomType	GetType(void)	{ return QT_disk; }
 };
 
+/****************************************************************************
+ *  iTunes covr Atom Class
+ */
+class CQT_iTunes_covr_Atom : public CQTAtom
+{
+public:
+    /*
+     *	Constructor/Destructor
+     */
+    CQT_iTunes_covr_Atom(ULONG32 ulOffset,
+                        ULONG32 ulSize,
+                        CQTAtom *pParent) : CQTAtom(ulOffset,
+                                                ulSize,
+                                                pParent) {;}
+
+    /*
+     *	Required Virtual Methods
+     */
+    virtual HXBOOL	IsLeafType(void)  { return FALSE; }
+    virtual QTAtomType	GetType(void)	{ return QT_covr; }
+
+    /*
+     *	Data Access Methods
+     */
+    UINT16 GetImageAtomCount()
+    {
+        return (UINT16)GetPresentChildCount();
+    }
+    CQT_iTunes_data_Atom* GetImageAtom(UINT16 index)
+    {
+        if(index < GetPresentChildCount())
+        {
+            return (CQT_iTunes_data_Atom*)GetPresentChild(index);
+        }
+        return 0;
+    }
+};
+
 #endif //QTCONFIG_ITUNES_METAINFO
 
 /****************************************************************************
  *  name Atom Class
  */
 class CQT_name_Atom : public CQTAtom
 {
 public:
     /*
      *	Leaf Data Format
      */
     struct Data
     {
 	UINT8 pName[1];
     } PACKING;
 
     /*
      *	Constructor/Destructor
      */
     CQT_name_Atom(ULONG32 ulOffset,
 		  ULONG32 ulSize, 
 		  CQTAtom *pParent) : CQTAtom(ulOffset,
 					      ulSize,
 					      pParent) {;}
 
     /*
      *	Required Virtual Methods
      */
     virtual HXBOOL	IsLeafType(void)    { return TRUE; }
     virtual QTAtomType	GetType(void)	    { return QT_name; }
datatype_mp4_filewriter.diff (text/x-patch, 52.6 KB)
Index: 3gpmeta.cpp
===================================================================
RCS file: /cvsroot/datatype/mp4/filewriter/3gpmeta.cpp,v
retrieving revision 1.3
diff -d -H -w -U30 -r1.3 3gpmeta.cpp
--- 3gpmeta.cpp	23 Oct 2009 09:47:54 -0000	1.3
+++ 3gpmeta.cpp	6 Nov 2009 21:45:31 -0000
@@ -502,58 +502,65 @@
 
 STDMETHODIMP_(UINT32) C3GPAtom_yrrc::GetCurrentSize(THIS_ HXBOOL bIncludeChildren)
 {
     return CMP4VersionedAtom::GetCurrentSize( bIncludeChildren ) +
             sizeof(UINT16);
 }
 
 STDMETHODIMP C3GPAtom_yrrc::WriteToBuffer(THIS_ UCHAR*& pBuffer, HXBOOL bIncludeChildren)
 {
     HX_RESULT retVal = HXR_OK;
     if( pBuffer )
     {
         CMP4VersionedAtom::WriteAtomAndVersionInfo( pBuffer );
         CMP4Atom::WriteToBufferAndInc( pBuffer, m_year );
     }
     return retVal;
 }
 
 C3GPAtom_ID32::C3GPAtom_ID32()
     : C3GPAtom_LangBase(MP4_BUILD_ATOMID('I','D','3','2'))
     , m_pBlob(0)
     , m_uBlobSize(0)
 {
 }
 
 C3GPAtom_ID32::~C3GPAtom_ID32()
 {
     HX_VECTOR_DELETE(m_pBlob);
 }
 
-void C3GPAtom_ID32::SetBlob(UCHAR* pData, UINT32 size)
+HX_RESULT C3GPAtom_ID32::SetBlob(UCHAR* pData, UINT32 size)
 {
     HX_VECTOR_DELETE(m_pBlob);
-    m_uBlobSize = size;
+    m_uBlobSize = 0;
+
     m_pBlob = new UCHAR[size];
+    if(m_pBlob)
+    {
+        m_uBlobSize = size;
     memcpy(m_pBlob, pData, size);
+        return HXR_OK;
+    }
+    return HXR_OUTOFMEMORY;
 }
 
 STDMETHODIMP_(UINT32) C3GPAtom_ID32::GetCurrentSize(THIS_ HXBOOL bIncludeChildren)
 {
     return C3GPAtom_LangBase::GetCurrentSize( bIncludeChildren ) +
                 m_uBlobSize;
 }
 
 STDMETHODIMP C3GPAtom_ID32::WriteToBuffer(THIS_ UCHAR*& pBuffer, HXBOOL bIncludeChildren)
 {
     HX_RESULT retVal = HXR_OK;
     if(pBuffer)
     {
         CMP4VersionedAtom::WriteAtomAndVersionInfo( pBuffer );
         C3GPAtom_LangBase::WriteLanguageToBuffer( pBuffer );
         if(m_uBlobSize)
         {
             CMP4Atom::WriteToBufferAndInc( pBuffer, (UCHAR*)m_pBlob, m_uBlobSize );
         }
     }
     return retVal;
 }
Index: 3gpmeta.h
===================================================================
RCS file: /cvsroot/datatype/mp4/filewriter/3gpmeta.h,v
retrieving revision 1.5
diff -d -H -w -U30 -r1.5 3gpmeta.h
--- 3gpmeta.h	23 Oct 2009 09:47:54 -0000	1.5
+++ 3gpmeta.h	6 Nov 2009 21:45:33 -0000
@@ -271,41 +271,41 @@
     STDMETHOD(WriteToBuffer)(THIS_ UCHAR*& pBuffer, HXBOOL bIncludeChildren = FALSE);
 
 private:
     char* m_pszAlbumTitle;
     HXBOOL m_include_tracknum;
     UINT8 m_tracknum;
 };
 
 // Recording Year
 class C3GPAtom_yrrc : public CMP4VersionedAtom
 {
 public:
     C3GPAtom_yrrc();
 
     void SetRecordingYear(UINT16 year);
 
     STDMETHOD_(UINT32, GetCurrentSize)(THIS_ HXBOOL bIncludeChildren = FALSE);
     STDMETHOD(WriteToBuffer)(THIS_ UCHAR*& pBuffer, HXBOOL bIncludeChildren = FALSE);
 
 private:
     UINT16 m_year;
 };
 
 // ID32
 class C3GPAtom_ID32 : public C3GPAtom_LangBase
 {
 public:
     C3GPAtom_ID32();
     virtual ~C3GPAtom_ID32();
 
-    void SetBlob(UCHAR* pData, UINT32 size);
+    HX_RESULT SetBlob(UCHAR* pData, UINT32 size);
 
     STDMETHOD_(UINT32, GetCurrentSize)(THIS_ HXBOOL bIncludeChildren = FALSE);
     STDMETHOD(WriteToBuffer)(THIS_ UCHAR*& pBuffer, HXBOOL bIncludeChildren = FALSE);
 
 private:
     UCHAR* m_pBlob;
     UINT32 m_uBlobSize;
 };
 
 #endif  /* _3GPMETA_H_ */
Index: m4amdatoms.h
===================================================================
RCS file: /cvsroot/datatype/mp4/filewriter/m4amdatoms.h,v
retrieving revision 1.7
diff -d -H -w -U30 -r1.7 m4amdatoms.h
--- m4amdatoms.h	23 Oct 2009 09:47:54 -0000	1.7
+++ m4amdatoms.h	6 Nov 2009 21:45:33 -0000
@@ -11,60 +11,61 @@
  * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl,
  * in which case the RCSL will apply. You may also obtain the license terms
  * directly from RealNetworks.  You may not use this file except in
  * compliance with the RPSL or, if you have a valid RCSL with RealNetworks
  * applicable to this file, the RCSL.  Please see the applicable RPSL or
  * RCSL for the rights, obligations and limitations governing use of the
  * contents of the file.
  *
  * This file is part of the Helix DNA Technology. RealNetworks is the
  * developer of the Original Code and owns the copyrights in the portions
  * it created.
  *
  * This file, and the files included with this file, is distributed and made
  * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
  * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  *
  * Technology Compatibility Kit Test Suite(s) Location:
  *    http://www.helixcommunity.org/content/tck
  *
  * Contributor(s):
  *
  * ***** END LICENSE BLOCK ***** */
 // m4amdatoms.h: atoms specific to m4a metadata
 
 #ifndef _M4AMDATOMS_H_
 #define _M4AMDATOMS_H_
 
 #include "mp4atoms.h"
+#include "metautil.h"
 
 //iTunes stype meta info
 
 // meta; this is the top level atom inside the udta, and all
 //       metadata atoms live inside it
 class CiTunesAtom_meta : public CMP4VersionedAtom
 {
 public:
     CiTunesAtom_meta() : CMP4VersionedAtom(MP4_BUILD_ATOMID('m','e','t','a'))
 	{}
     STDMETHOD( WriteToBuffer )    (THIS_ UCHAR*& pBuffer, HXBOOL bIncludeChildren = FALSE )
 	{
 	    HX_RESULT retVal = HXR_OK;
 	    
 	    if( pBuffer )
 	    {
 		CMP4VersionedAtom::WriteAtomAndVersionInfo( pBuffer );
 
 		if( bIncludeChildren )
 		{
 		    retVal = ChildrenWriteToBuffer( pBuffer );
 		}
 	    }
 	    return retVal;
 	}
 };
 
 class CiTunesAtom_hdlr : public CMP4VersionedAtom
 {
 public:
@@ -285,108 +286,158 @@
 	{
             // 2 bytes reserved + 2 bytes disc number + 2 bytes disc count
             const UINT32 dataLength = 6;
             HX_RESULT retVal = AllocBuffer(dataLength);
             if( SUCCEEDED(retVal) )
             {
                 memset(m_ucByteBuf, 0, dataLength);
 
                 UCHAR* pTmp = m_ucByteBuf + 2;
                 WriteToBufferAndInc(pTmp, static_cast<UINT16>(discNumber & 0x0000FFFF));
                 WriteToBufferAndInc(pTmp, static_cast<UINT16>(discCount & 0x0000FFFF));
 
                 SetTypeIndicatorTypeCode(TypeIndicatorTypeCode_Implicit);
             }
             return retVal;
         }
     STDMETHOD( SetSpecial_PredefinedGenreEntries ) (THIS_ CHXPtrArray& genreEntries )
 	{
             if(!genreEntries.GetSize())
             {
                 return HXR_FAILED;
             }
 
             // 2*count bytes
             const UINT32 dataLength = genreEntries.GetSize() * 2;
             HX_RESULT retVal = AllocBuffer(dataLength);
             if( SUCCEEDED(retVal) )
             {
                 UCHAR* pTmp = m_ucByteBuf;
 
-                for(UINT32 i = 0; i < genreEntries.GetSize(); i++)
+                for(int i = 0; i < genreEntries.GetSize(); i++)
                 {
                     UINT16 entry = (UINT16)(UINT32)genreEntries.GetAt(i);
                     WriteToBufferAndInc(pTmp, entry);
                 }
 
                 // The TypeIndicatorTypeCode_Genres makes most tools (including iTunes9)
                 // ignore this atom, so use implicit.
                 SetTypeIndicatorTypeCode(TypeIndicatorTypeCode_Implicit);
                 //SetTypeIndicatorTypeCode(TypeIndicatorTypeCode_Genres);
             }
             return retVal;
         }
+    STDMETHOD( SetPicture ) (THIS_ const ID3Tools::APICFrame* pFrame )
+	{
+            HX_RESULT retVal = HXR_FAILED;
+
+            if(pFrame && pFrame->GetPictureDataSize())
+            {
+                TypeIndicatorTypeCode tc = TypeIndicatorTypeCode_Implicit;
+
+                if(pFrame->GetMimeType() == "image/jpeg")
+                {
+                    tc = TypeIndicatorTypeCode_JPEG;
+                    retVal = HXR_OK;
+                }
+                else if(pFrame->GetMimeType() == "image/png")
+                {
+                    tc =  TypeIndicatorTypeCode_PNG;
+                    retVal = HXR_OK;
+                }
+                
+                if(SUCCEEDED(retVal))
+                {
+                    retVal = AllocBuffer(pFrame->GetPictureDataSize());
+                }
+
+                if(SUCCEEDED(retVal) && m_ucByteBuf && m_uiByteBufLength)
+                {
+                    memcpy(m_ucByteBuf, pFrame->GetPictureData(), m_uiByteBufLength);
+                }
+
+                if(SUCCEEDED(retVal))
+                {
+                    SetTypeIndicatorTypeCode(tc);
+                }
+            }
+            return retVal;
+        }
+    static HXBOOL IsSupportedPictureType( const ID3Tools::APICFrame* pFrame )
+        {
+            if(pFrame && pFrame->GetPictureDataSize())
+            {
+                if((pFrame->GetMimeType() == "image/jpeg") ||
+                   (pFrame->GetMimeType() == "image/png"))
+                {
+                    return TRUE;
+                }
+            }
+            return FALSE;
+        }
 
 protected:
     void ClearData()
     {
         m_uiByteBufLength = 0;
         HX_VECTOR_DELETE( m_ucByteBuf );
 
         SetTypeIndicatorTypeCode(TypeIndicatorTypeCode_Implicit);
     }
 
     HX_RESULT AllocBuffer(ULONG32 len)
     {
         ClearData();
 
         HX_RESULT retVal = HXR_INVALID_PARAMETER;
         if(len)
         {
             retVal = HXR_OUTOFMEMORY;
             m_ucByteBuf = new UCHAR[len];
 
             if(m_ucByteBuf)
             {
                 m_uiByteBufLength = len;
 		memset(m_ucByteBuf, 0, len);
                 retVal = HXR_OK;
             }
         }
         return retVal;
     }
 
     enum TypeIndicatorTypeCode
     {
         TypeIndicatorTypeCode_Implicit = 0, //no type needs to be indicated because only one type is allowed
         TypeIndicatorTypeCode_UTF8 = 1, //without count or null terminator
         TypeIndicatorTypeCode_UTF16BE = 2, //without count or null terminator
+        TypeIndicatorTypeCode_JPEG = 13, //in JFIF wrapper
+        TypeIndicatorTypeCode_PNG = 14, //in PNG wrapper
         TypeIndicatorTypeCode_Genres = 18, //special enumeration
         TypeIndicatorTypeCode_Integer = 21 //signed big-endian integer in 1,2,3,4,8 bytes
     };
 
     void SetTypeIndicatorTypeCode(TypeIndicatorTypeCode tc)
     {
         m_typeIndicatorTypeCode = (UCHAR)tc;
     }
 
 private:
     UINT16 m_typeIndicatorReserved; //reserved to be 0
     UCHAR m_typeIndicatorTypeSetIdentifier; //only one type set defined: 0
     UCHAR m_typeIndicatorTypeCode;
     UINT32 m_localeIndicator; //reserved to be 0
     UINT32 m_uiByteBufLength;
     UCHAR* m_ucByteBuf;
 };
 
 // track name
 // string
 class CiTunesAtom_A9nam : public CMP4ContainerAtom
 {
 public:
     CiTunesAtom_A9nam() : CMP4ContainerAtom(MP4_BUILD_ATOMID(0xA9,'n','a','m'))
 	{}
 };
 
 // track subtitle
 // string
 class CiTunesAtom_A9st3 : public CMP4ContainerAtom
@@ -540,31 +591,40 @@
 	{}
 };
 
 // beats per minute
 // signed int32
 class CiTunesAtom_tmpo : public CMP4ContainerAtom
 {
 public:
     CiTunesAtom_tmpo() : CMP4ContainerAtom(MP4_BUILD_ATOMID('t','m','p','o'))
 	{}
 };
 
 // compilation
 // signed int8
 class CiTunesAtom_cpil : public CMP4ContainerAtom
 {
 public:
     CiTunesAtom_cpil() : CMP4ContainerAtom(MP4_BUILD_ATOMID('c','p','i','l'))
 	{}
 };
 
 // content rating
 // signed int8
 class CiTunesAtom_rtng : public CMP4ContainerAtom
 {
 public:
     CiTunesAtom_rtng() : CMP4ContainerAtom(MP4_BUILD_ATOMID('r','t','n','g'))
 	{}
 };
 
+// cover art
+// byte array
+class CiTunesAtom_covr : public CMP4ContainerAtom
+{
+public:
+    CiTunesAtom_covr() : CMP4ContainerAtom(MP4_BUILD_ATOMID('c','o','v','r'))
+	{}
+};
+
 #endif  /* _M4AMDATOMS_H_ */
Index: mp4sm.cpp
===================================================================
RCS file: /cvsroot/datatype/mp4/filewriter/mp4sm.cpp,v
retrieving revision 1.20
diff -d -H -w -U30 -r1.20 mp4sm.cpp
--- mp4sm.cpp	23 Oct 2009 09:47:54 -0000	1.20
+++ mp4sm.cpp	6 Nov 2009 21:45:39 -0000
@@ -629,124 +629,134 @@
 CMP4StreamMixer::HintInfo::HintInfo(CHXHintContext &hctx)
     : hinting(hctx), trak(NULL), tkhd(NULL), mdhd(NULL), sdp(NULL), stblmgr(NULL), trackid(NULL)
 {
 }
 
 CMP4StreamMixer::HintInfo::~HintInfo()
 {
 }
 
 CMP4StreamMixer::CMP4StreamMixer(IUnknown* pContext, CMP4Archiver* pArchiver)
     : m_lRefCount(0)
     , m_ulLastTimestamp(0)
     , m_ulActualDuration(0)
     , m_ulReportedDuration(0)
     , m_uiByteCount(0)
     , m_uiReservedBlockCount(0)
     , m_ulFirstTimestamp(0)
     , m_eIsRtp(UNCHECKED)
     , m_bMoovAtEnd(FALSE)
     , m_bForceMoovFirst(FALSE)
     , m_pMvhd(NULL)
     , m_pFileHeader(NULL)
     , m_pStreamInfo(NULL)
     , m_ulStreamCount(0)
     , m_ulStreamGroupCount(0)
     , m_ulStreamHeaderReceived(0)
     , m_ulStreamDoneReceived(0)
     , m_eMetaFlavor(META_ITUNES)
     , m_bRequestedOutputMetaFlavor3GPP(FALSE)
     , m_bRequestedOutputMetaFlavorITUNES(FALSE)
+    , m_bIgnoreID32Blob(FALSE)
+    , m_bFileHeaderCarriesUITSData(FALSE)
     , m_bUTF16Output(FALSE)
     , m_e3gpMode(M3GPP_NONE)
     , m_bTSInterleave(FALSE)
     , m_bGenerateHintTracks(FALSE)
     , m_uiBaseWriteOffset(0)
     , m_uiCurrentOffset(0)
     , m_uiGlobalFlush(0)
 {
     m_pContext = pContext;
     HX_ADDREF(m_pContext);
 
     HX_ASSERT( pArchiver );
     m_pArchiver = pArchiver;
     m_pArchiver->AddRef();
 
     m_pRootAtom = new CMP4Atom_file();
     HX_ASSERT( m_pRootAtom );
     m_pRootAtom->AddRef();
 }
 
 
 CMP4StreamMixer::~CMP4StreamMixer()
 {
     HX_RELEASE( m_pArchiver );
     HX_RELEASE( m_pRootAtom );
     HX_RELEASE( m_pFileHeader );
     HX_VECTOR_DELETE(m_pStreamInfo);
     HX_RELEASE( m_pContext );
 }
 
 STDMETHODIMP CMP4StreamMixer::QueryInterface( THIS_ REFIID riid, void** ppvObj )
 {
     // dont support, for now
     return HXR_NOINTERFACE;
 }
 
 STDMETHODIMP_(ULONG32) CMP4StreamMixer::AddRef()
 {
     return InterlockedIncrement( &m_lRefCount );
 }
 STDMETHODIMP_(ULONG32) CMP4StreamMixer::Release()
 {
     if( InterlockedDecrement( &m_lRefCount ) > 0 )
     {
         return m_lRefCount;
     }
     delete this;
     return 0;
 }
 
 STDMETHODIMP CMP4StreamMixer::SetProperties( THIS_ IHXValues* pProperties )
 {
     UINT32 ulVal = 0;
 
     // Meta-data flavor options
     PropTools::CopyPropertyULONG32(ulVal, pProperties, "MetaDataFlavor");
 
-    const UINT32 METADATA_FLAVOR_3GPP = 0x00000001; //from ihxmetaeditor.h
-    m_bRequestedOutputMetaFlavor3GPP = (ulVal & METADATA_FLAVOR_3GPP) != 0;
+    const UINT32 METADATA_FLAVOR_3GPP = 0x00000010; //from ihxmetaeditor.h
+    m_bRequestedOutputMetaFlavor3GPP = (ulVal & METADATA_FLAVOR_3GPP) ? TRUE : FALSE;
 
-    const UINT32 METADATA_FLAVOR_ITUNES = 0x00000010; //from ihxmetaeditor.h
-    m_bRequestedOutputMetaFlavorITUNES = (ulVal & METADATA_FLAVOR_ITUNES) != 0;
+    const UINT32 METADATA_FLAVOR_ITUNES = 0x00000100; //from ihxmetaeditor.h
+    m_bRequestedOutputMetaFlavorITUNES = (ulVal & METADATA_FLAVOR_ITUNES) ? TRUE : FALSE;
+
+    // Meta-data ignore options
+    ulVal = 0;
+    PropTools::CopyPropertyULONG32(ulVal, pProperties, "MetaDataIgnore");
+
+    const UINT32 METADATAIGNORE_ID32BLOB = 0x00000001; //from ffdriver.h
+    m_bIgnoreID32Blob = (ulVal & METADATAIGNORE_ID32BLOB) ? TRUE : FALSE;
 
     // UTF16 output option
+    ulVal = 0;
     PropTools::CopyPropertyULONG32(ulVal, pProperties, "UTF16Output");
     m_bUTF16Output = ulVal ? TRUE : FALSE;
 
     // Compatibility option
     PropTools::CopyPropertyBuffer(m_spCompatibility, pProperties, "Compatibility");
 
     // 3GPP Mode option
     IHXBuffer *buf;
 
     if (SUCCEEDED(pProperties->GetPropertyCString("3GPPMode", buf)))
     {
 	UINT32 n = buf->GetSize();
 	const char *p3gpmode = (const char *)buf->GetBuffer();
 
 	if (n >= 4 && strncasecmp(p3gpmode, "none", 4) == 0)
 	{
 	    m_e3gpMode = M3GPP_NONE;
 	}
 	else if (n >= 7 && strncasecmp(p3gpmode, "general", 7) == 0)
 	{
 	    m_e3gpMode = M3GPP_GENERAL;
 	}
 	else if (n >= 5 && strncasecmp(p3gpmode, "basic", 5) == 0)
 	{
 	    m_e3gpMode = M3GPP_BASIC;
 	}
 	else if (n >= 9 && strncasecmp(p3gpmode, "streaming", 9) == 0)
 	{
 	    m_e3gpMode = M3GPP_STREAMING;
 	}
@@ -796,60 +806,70 @@
     if (ulVal != 0)
     {
 	m_bForceMoovFirst = TRUE;
     }
 
     return HXR_OK;
 }
 
 STDMETHODIMP CMP4StreamMixer::SetFileHeader( THIS_ IHXValues* pFileHeader )
 {
     HX_RESULT retVal = HXR_OK;
 
     pFileHeader->AddRef();
     m_pFileHeader = pFileHeader;
 
     //get stream count; this may be bigger than the actual number of tracks in
     //the output file, depending on how many NUL handlers are there
     m_pFileHeader->GetPropertyULONG32("StreamCount", m_ulStreamCount);
     m_pFileHeader->GetPropertyULONG32("StreamGroupCount", m_ulStreamGroupCount);
 
     if (m_ulStreamCount)
     {
         m_pStreamInfo = new StreamInfo[m_ulStreamCount];
     }
 
     HX_ASSERT(m_pStreamInfo);
 
     m_ulStreamHeaderReceived = 0;
     m_ulStreamDoneReceived = 0;
 
+    // check if the header carries UITS data
+    m_bFileHeaderCarriesUITSData = FALSE;
+
+    IHXBuffer* pUITSDataBuffer = 0;
+    if(SUCCEEDED(m_pFileHeader->GetPropertyBuffer(_3GPP_META_INFO_UITS_KEY, pUITSDataBuffer)) && pUITSDataBuffer)
+    {
+        m_bFileHeaderCarriesUITSData = TRUE;
+    }
+    HX_RELEASE(pUITSDataBuffer);
+
     return retVal;
 }
 
 STDMETHODIMP CMP4StreamMixer::SetStreamHeader( THIS_ IHXValues* pStreamHeader )
 {
     HX_RESULT retVal = HXR_OK;
 
     //save the stream header
     UINT32 ulStreamNumber = m_ulStreamCount;
     pStreamHeader->GetPropertyULONG32("StreamNumber",ulStreamNumber);
 
     if (ulStreamNumber >= m_ulStreamCount)
     {
         return HXR_INVALID_PARAMETER;
     }
 
     // Save the stream group number
     UINT32 ulStreamGroupNumber = HX_INVALID_STREAM_GROUP_NUMBER;
     pStreamHeader->GetPropertyULONG32("StreamGroupNumber", ulStreamGroupNumber);
 
     // If we have a "StreamGroupCount" in the file header, then we
     // should have a stream group number in each stream header
     if (m_ulStreamGroupCount && ulStreamGroupNumber >= m_ulStreamGroupCount)
     {
         return HXR_INVALID_PARAMETER;
     }
 
     // Save the stream group number
     m_pStreamInfo[ulStreamNumber].m_ulStreamGroupNumber = ulStreamGroupNumber;
 
@@ -1604,64 +1624,65 @@
                     }
                 }
             }
 	    for (UINT32 i = 0; i < m_ulStreamCount && SUCCEEDED(retVal); ++i)
 	    {
 		HintInfo *hi = m_pStreamInfo[i].m_pHintInfo;
 		
 		if (hi != NULL && hi->trak != NULL)
 		    pMoov->AddChild(hi->trak);
 	    }
         }
 
         // Do we need to generate a udta box that is a child
         // of the moov box?
         if (SUCCEEDED(retVal) && NeedToAddUdtaChildToMoovBox())
         {
             // Set the return value
             retVal = HXR_OUTOFMEMORY;
             // Create the udta box
             CMP4Atom_udta* pUdta = new CMP4Atom_udta();
             if (pUdta)
             {
                 // Clear the return value
                 retVal = HXR_OK;
                 // Check the meta-data format we are supposed to generate
                 if (m_bRequestedOutputMetaFlavorITUNES || (m_eMetaFlavor == META_ITUNES))
                 {
                     // Build iTunes meta-data
                     BuildiTunesMetaData(pUdta);
                 }
-                if (m_bRequestedOutputMetaFlavor3GPP || (m_eMetaFlavor == META_3GPP))
+                if (m_bRequestedOutputMetaFlavor3GPP || (m_eMetaFlavor == META_3GPP)
+                    || m_bFileHeaderCarriesUITSData)
                 {
                     // Build 3GP meta-data
-                    Build3GPMetaData(pUdta, pMoov);
+                    Build3GPPMetaData(pUdta, pMoov);
                 }
                 // Are we generating a hinted file?
                 if (m_bGenerateHintTracks)
                 {
                     // Add the hnti box as a child of the udta box
                     AddMovieHintInformation(pUdta);
                 }
                 // Add this udta box as a child of the moov box
                 pMoov->AddChild(pUdta);
             }
         }
 
         // uuid/USMT atom for PSP style meta info
         if( SUCCEEDED( retVal ) && m_eMetaFlavor == META_PSP )
         {
             retVal = HXR_OUTOFMEMORY;
             CPSPAtom_USMT* pUsmt = new CPSPAtom_USMT();
             if( pUsmt )
             {
                 retVal = HXR_OK;
 
                 // Call out to build the metadata tree
                 BuildPSPMetaData( pUsmt );
 
                 pMoov->AddChild( pUsmt );
             }
         }
     
     }
 
@@ -1804,61 +1825,61 @@
         retVal = BuildMdia(usStreamNum, pTrak, NULL);
         if (SUCCEEDED(retVal))
         {
             // Do we need to add a udta box child to this trak box?
             if (NeedToAddUdtaChildToTrakBox((CMP4Atom_trak*) pTrak, (UINT32) usStreamNum))
             {
                 // Set the return value
                 retVal = HXR_OUTOFMEMORY;
                 // Create a udta box
                 CMP4Atom_udta* pUdtaBox = new CMP4Atom_udta();
                 if (pUdtaBox)
                 {
                     // Clear the return value
                     retVal = HXR_OK;
                     // Add this child to the trak box
                     pTrak->AddChild(pUdtaBox);
                     // Handle switch groups here. This will check to see if 
                     // there is a "SwitchGroupID" property in the stream header,
                     // and if so, add a tsel child box to the udta box.
                     SetSwitchGroup(pUdtaBox, (UINT32) usStreamNum);
                 }
             }
         }
 
         // build meta atom at stream level (3GPP flavor only)
         if( SUCCEEDED( retVal ) &&
             (m_eMetaFlavor == META_3GPP || m_bRequestedOutputMetaFlavor3GPP))
         {
             // ID32, setup box hierarchy: trak/meta/[hdlr, ID32]
             CMP4Atom_meta* pMeta = NULL;
-            retVal = Build3GPMetaDataID32(m_pStreamInfo[usStreamNum].m_pStreamHeader, &pMeta);
+            retVal = Build3GPPMetaDataID32(m_pStreamInfo[usStreamNum].m_pStreamHeader, &pMeta, FALSE);
 
             if( SUCCEEDED( retVal ) && pMeta )
             {
                 retVal = pTrak->AddChild( pMeta );
             }
         }
     }
 
     if (SUCCEEDED(retVal) && m_bGenerateHintTracks)
     {
         HintInfo *hi = new HintInfo(m_HintContext);
 
         if (hi == NULL)
         {
             return HXR_OUTOFMEMORY;
         }
         retVal = hi->hinting.SetStreamHeader(m_pStreamInfo[usStreamNum].m_pStreamHeader);
         if (SUCCEEDED(retVal))
         {
             m_pStreamInfo[usStreamNum].m_pHintInfo = hi;
 
             // Assign the trackID we chose in ChooseTrackIDs()
             hi->trackid = m_pStreamInfo[usStreamNum].m_ulHintTrackID;
             // Set the hint track ID into the stream header
             m_pStreamInfo[usStreamNum].m_pStreamHeader->SetPropertyULONG32("TrackID", hi->trackid);
 
             hi->trak = new CMP4Atom_trak;
             if (hi->trak == NULL)
             {
                 return HXR_OUTOFMEMORY;
@@ -2005,64 +2026,66 @@
     {
         // The first question is: does the trak box already HAVE a udta box child? If
         // so then we don't need to add another one.
         CMP4Atom_udta* pUdtaBox = (CMP4Atom_udta*) pTrakBox->FindChild(MP4_BUILD_ATOMID('u','d','t','a'));
         if (!pUdtaBox)
         {
             // The trak box does not already have a udta child. The next question is: is this
             // stream involved in a switch group? If so, then we will need a udta box child
             // of the trak box in order to put our tsel (track selection) box in.
             if (IsStreamInSwitchGroup(ulStreamNumber))
             {
                 // For now, these are the only criteria. So if we made it here, we need
                 // to add a udta child to this trak box.
                 // XXXMEH - if we are hinting, we always add a udta box to the trak
                 // box for the hint track. However, if we are doing hinting of
                 // single-rate streams, then it's unclear if the trak box for the
                 // media tracks need udta children or not. For now, I'll assume that
                 // we only need udta children for media trak boxes if that track
                 // is in a switch group.
                 bRet = TRUE;
             }
         }
     }
 
     return bRet;
 }
 
 HXBOOL CMP4StreamMixer::NeedToAddUdtaChildToMoovBox()
 {
     // We need to generate a udta box as a child of the moov box if
-    // we are either generating iTunes or 3GPP meta-data OR we 
-    // are generating hint tracks.
+    // we are either generating iTunes or 3GPP meta-data OR
+    // we need to write out UITS data OR
+    // we are generating hint tracks.
     return (m_bRequestedOutputMetaFlavorITUNES || m_eMetaFlavor == META_ITUNES
             || m_bRequestedOutputMetaFlavor3GPP || m_eMetaFlavor == META_3GPP
+            || m_bFileHeaderCarriesUITSData
             || m_bGenerateHintTracks);
 }
 
 HX_RESULT
 CMP4StreamMixer::AddMovieHintInformation(CMP4Atom_udta* pUdta)
 {
     HX_RESULT retVal = HXR_INVALID_PARAMETER;
 
     if (pUdta)
     {
         // Clear the return value
         retVal = HXR_OK;
         // Does this udta box already have a hnti child box?
         CMP4Atom_hnti* pHnti = (CMP4Atom_hnti*) pUdta->FindChild(MP4_BUILD_ATOMID('h','n','t','i'));
         if (!pHnti)
         {
             // Set the return value
             retVal = HXR_OUTOFMEMORY;
             // This udta box does NOT already have a hnti child box, so create one
             pHnti = new CMP4Atom_hnti();
             if (pHnti)
             {
                 // Create a CMP4Atom_RTPMovieHintInformation child box
                 CMP4Atom_RTPMovieHintInformation* pRTP = new CMP4Atom_RTPMovieHintInformation();
                 if (pRTP)
                 {
                     // Generate the movie-level SDP
                     CHXString strMovieLevelSDP;
                     retVal = GenerateMovieLevelSDP(&strMovieLevelSDP);
                     if (SUCCEEDED(retVal))
@@ -3651,132 +3674,112 @@
     }
 
     CMP4Atom_free* pFree = NULL;
     CiTunesAtom_ilst* pIlst = NULL;
     if( SUCCEEDED( retVal ) )
     {
         retVal = HXR_OUTOFMEMORY;
 
         // hdlr
         CiTunesAtom_hdlr* pHdlr = new CiTunesAtom_hdlr();
         if( pHdlr )
         {
             retVal = HXR_OK;
             
             pMeta->AddChild( pHdlr );
         }
         
         // ilst
         if( SUCCEEDED( retVal ) )
         {
             retVal = HXR_OUTOFMEMORY;
             pIlst = new CiTunesAtom_ilst();
 
             if( pIlst )
             {
                 retVal = HXR_OK;
                 
                 pMeta->AddChild( pIlst );
             }
         }
-
-        // free. only create this if we are putting moov in the middle
-        if( !m_bMoovAtEnd && !m_bForceMoovFirst)
-        {
-            if( SUCCEEDED( retVal ) )
-            {
-                retVal = HXR_OUTOFMEMORY;
-
-                pFree = new CMP4Atom_free();
-
-                if( pFree )
-                {
-                    retVal = HXR_OK;
-
-                    // reserve space for metadata
-                    pFree->SetFreeSize(MP4_DEFAULT_METADATA_RESERVE_SIZE);
-                    pMeta->AddChild( pFree );
-                }
-            }
-        }
     }
 
     // Now build out the ilst
     if( SUCCEEDED( retVal ) )
     {
         // string properties
         // Although not mentioned by iTMF spec, iTunes have 255 character limit on string properties.
         const UINT32 ulStringLengthLimit = 255;
 
         iTunesStringTransferPort stringTranferPort;
         stringTranferPort.SetLengthLimit(ulStringLengthLimit);
         stringTranferPort.SetUTF16Output(m_bUTF16Output);
 
         MetaiTunes_AddItemAtom(retVal, _ITUNES_META_INFO_TITLE_KEY, stringTranferPort, pIlst, new CiTunesAtom_A9nam());
         MetaiTunes_AddItemAtom(retVal, _ITUNES_META_INFO_COMPOSER_KEY, stringTranferPort, pIlst, new CiTunesAtom_A9wrt());
         MetaiTunes_AddItemAtom(retVal, _ITUNES_META_INFO_COPYRIGHT_KEY, stringTranferPort, pIlst, new CiTunesAtom_cprt());
         MetaiTunes_AddItemAtom(retVal, _ITUNES_META_INFO_COMMENT_KEY, stringTranferPort, pIlst, new CiTunesAtom_A9cmt());
         MetaiTunes_AddItemAtom(retVal, _ITUNES_META_INFO_ARTIST_KEY, stringTranferPort, pIlst, new CiTunesAtom_A9ART());
         MetaiTunes_AddItemAtom(retVal, _ITUNES_META_INFO_GENRE_KEY, stringTranferPort, pIlst, new CiTunesAtom_A9gen());
         MetaiTunes_AddItemAtom(retVal, _ITUNES_META_INFO_ALBUM_KEY, stringTranferPort, pIlst, new CiTunesAtom_A9alb());
         MetaiTunes_AddItemAtom(retVal, _ITUNES_META_INFO_TRACK_SUBTITLE_KEY, stringTranferPort, pIlst, new CiTunesAtom_A9st3());
         MetaiTunes_AddItemAtom(retVal, _ITUNES_META_INFO_GENERATOR_TOOL_KEY, stringTranferPort, pIlst, new CiTunesAtom_A9too());
         MetaiTunes_AddItemAtom(retVal, _ITUNES_META_INFO_ENCODED_BY_KEY, stringTranferPort, pIlst, new CiTunesAtom_A9enc());
         MetaiTunes_AddItemAtom(retVal, _ITUNES_META_INFO_ALBUM_ARTIST_KEY, stringTranferPort, pIlst, new CiTunesAtom_aART());
 
         // Grouping is represented by two iTunes atoms: older and newer version. Output both.
         MetaiTunes_AddItemAtom(retVal, _ITUNES_META_INFO_GROUPING_KEY, stringTranferPort, pIlst, new CiTunesAtom_grup());
         MetaiTunes_AddItemAtom(retVal, _ITUNES_META_INFO_GROUPING_KEY, stringTranferPort, pIlst, new CiTunesAtom_A9grp());
 
         // Lyrics atom (which is not described by iTMF spec) does not have 255 character limit.
         // However it must be in UTF8 format.
         stringTranferPort.SetLengthLimit(0);
         stringTranferPort.SetUTF16Output(FALSE);
         MetaiTunes_AddItemAtom(retVal, _ITUNES_META_INFO_LYRICS_KEY, stringTranferPort, pIlst, new CiTunesAtom_A9lyr());
 
         // predefined genre
         if(SUCCEEDED(retVal))
         {
             // try converting user defined genres to the list of ID3 predefined genres
             EncodedString strGenres;
             if(PropTools::CopyPropertyBuffer(strGenres, m_pFileHeader, _ITUNES_META_INFO_GENRE_KEY))
             {
                 // need UTF8 to compare with ID3 names
                 EncodedString strGenresUTF8;
                 if(strGenres.ConvertToUTF8(strGenresUTF8))
                 {
                     // iTunes user defined genre can be a comma separated list
                     CHXPtrArray genreEntries;
                     if(ID3Tools::ParseGenreNameList(genreEntries, strGenresUTF8.GetData(), FALSE, ',', TRUE))
                     {
                         // iTunes stores genre ordinals with ID3 value +1
-                        for(UINT32 i = 0; i < genreEntries.GetSize(); i++)
+                        for(int i = 0; i < genreEntries.GetSize(); i++)
                         {
                             UINT8 uGenreOrdinal = (UINT8)(ULONG32)genreEntries.GetAt(i) + 1;
                             genreEntries.SetAt(i, (void*)(ULONG32)uGenreOrdinal);
                         }
 
                         retVal = HXR_OUTOFMEMORY;
 
                         CiTunesAtom_gnre* pItem = new CiTunesAtom_gnre();
                         CiTunesAtom_data* pData = new CiTunesAtom_data();
 
                         if(pItem && pData)
                         {
                             retVal = pData->SetSpecial_PredefinedGenreEntries(genreEntries);
                         }
 
                         MetaiTunes_LinkAtoms(retVal, pIlst, pItem, pData);
                     }
                 }
             }
         }
 
         // recording year (type conversion), always outputed as UTF8
         iTunesRecordingYearTransferPort recYearTranferPort;
         MetaiTunes_AddItemAtom(retVal, _ITUNES_META_INFO_RELEASE_DATE_KEY, recYearTranferPort, pIlst, new CiTunesAtom_A9day());
 
         // BPM should be 32 bits according to iTMF spec, but most implementations expect 16 bits
         iTunesUInteger16TransferPort uint16TranferPort;
         MetaiTunes_AddItemAtom(retVal, _ITUNES_META_INFO_BEATS_PER_MINUTE_KEY, uint16TranferPort, pIlst, new CiTunesAtom_tmpo());
 
         // uint8 properties
@@ -3806,69 +3809,156 @@
                 }
 
                 MetaiTunes_LinkAtoms(retVal, pIlst, pItem, pData);
             }
         }
 
         // disc number/count
         if(SUCCEEDED(retVal))
         {
             UINT32 ulDiscNumber = 0;
             HXBOOL bHaveDN = PropTools::CopyPropertyULONG32(ulDiscNumber, m_pFileHeader, _ITUNES_META_INFO_DISC_NUMBER_KEY);
 
             UINT32 ulDiscCount = 0;
             HXBOOL bHaveDC = PropTools::CopyPropertyULONG32(ulDiscCount, m_pFileHeader, _ITUNES_META_INFO_DISC_COUNT_KEY);
 
             if(bHaveDN || bHaveDC)
             {
                 retVal = HXR_OUTOFMEMORY;
 
                 CiTunesAtom_disk* pItem = new CiTunesAtom_disk();
                 CiTunesAtom_data* pData = new CiTunesAtom_data();
 
                 if(pItem && pData)
                 {
                     retVal = pData->SetSpecial_DiscNumberCount(ulDiscNumber, ulDiscCount);
                 }
 
                 MetaiTunes_LinkAtoms(retVal, pIlst, pItem, pData);
             }
         }
+
+        // pictures
+        if(SUCCEEDED(retVal))
+        {
+            // extract APIC frames from buffer properties
+            UINT32 uAPICDataCount = 0;
+            PropTools::CopyPropertyULONG32(uAPICDataCount, m_pFileHeader, _ITUNES_META_INFO_PICTURE_COUNT_KEY);
+
+            if(uAPICDataCount)
+            {
+                // keep count of successfully transfered pictures
+                UINT32 uPictureCount = 0;
+
+                // create covr atom
+                CiTunesAtom_covr* pItem = new CiTunesAtom_covr();
+                if(!pItem)
+                {
+                    retVal = HXR_OUTOFMEMORY;
     }
 
-    // Resize our free atom (if present) given our new metadata
-    if( SUCCEEDED( retVal ) && pFree )
+                // add data atoms to covr atom
+                for(UINT32 uAPICData = 0; SUCCEEDED(retVal) && (uAPICData < uAPICDataCount); uAPICData++)
+                {
+                    retVal = HXR_OUTOFMEMORY;
+
+                    // construct APIC frame
+                    ID3Tools::APICFrame* pFrame = new ID3Tools::APICFrame;
+                    if(pFrame)
+                    {
+                        char key[128];
+                        sprintf(key, _ITUNES_META_INFO_PICTURE_KEY, int(uAPICData));
+
+                        retVal = pFrame->DeserializeFromBufferProperty(m_pFileHeader, key);
+
+                        // skip unsupported picture types
+                        if(SUCCEEDED(retVal) && CiTunesAtom_data::IsSupportedPictureType(pFrame))
+                        {
+                            retVal = HXR_OUTOFMEMORY;
+
+                            // transfer picture to data atom
+                            CiTunesAtom_data* pData = new CiTunesAtom_data();
+                            if(pData)
+                            {
+                                retVal = pData->SetPicture(pFrame);
+                            }
+
+                            // link data atom to item atom
+                            if(SUCCEEDED(retVal) && pData)
+                            {
+                                uPictureCount++;
+                                pItem->AddChild(pData);
+                            }
+                            else
+                            {
+                                delete pData;
+                            }
+                        }
+                    }
+
+                    // cleanup frame
+                    delete pFrame;
+                }
+
+                // link item atom to ilst atom
+                if(SUCCEEDED(retVal) && uPictureCount)
+                {
+                    pIlst->AddChild(pItem);
+                }
+                else
+                {
+                    delete pItem;
+                }
+            }
+        }
+    }
+
+    // free. only create this if we are putting moov in the middle
+    if( SUCCEEDED( retVal ) )
     {
         UINT32 size = pUdta->GetCurrentSize( TRUE );
-        size -= MP4_DEFAULT_METADATA_RESERVE_SIZE;
         
+        if( !m_bMoovAtEnd && !m_bForceMoovFirst && (MP4_DEFAULT_METADATA_RESERVE_SIZE > size))
+        {
+            retVal = HXR_OUTOFMEMORY;
+
+            pFree = new CMP4Atom_free();
+
+            if( pFree )
+            {
+                retVal = HXR_OK;
+
+                // reserve space for metadata
         pFree->SetFreeSize( MP4_DEFAULT_METADATA_RESERVE_SIZE - size );
+                pMeta->AddChild( pFree );
+            }
+        }
     }
     
     return retVal;
 }
 
 void CMP4StreamMixer::MetaiTunes_LinkAtoms(HX_RESULT& retVal, CiTunesAtom_ilst* pIlst, CMP4ContainerAtom* pItem, CiTunesAtom_data* pData)
 {
     if(SUCCEEDED(retVal) && pIlst && pItem && pData)
     {
         pItem->AddChild(pData);
         pIlst->AddChild(pItem);
     }
 
     if(FAILED(retVal) || !pItem || !pData)
     {
         delete pItem;
         delete pData;
     }
 }
 
 HX_RESULT CMP4StreamMixer::BuildPSPMetaData( CMP4Atom* pUsmt )
 {
     HX_RESULT retVal = HXR_OUTOFMEMORY;
 
     CPSPAtom_MTDT* pMtdt = new CPSPAtom_MTDT();
 
     if( pMtdt )
     {
         retVal = HXR_OK;
 
@@ -4023,61 +4113,61 @@
 
 HXBOOL CMP4StreamMixer::Meta3GP_ExtractFixedPoint(const EncodedString& s, INT32& outWhole, UINT32& outFrac, UINT32& outFixed)
 {
     if(s.IsEmpty() || !s.IsAsciiOnly())
     {
         return FALSE;
     }
 
     EncodedString val;
     s.ConvertToUTF8(val);
 
     if(!val.GetData())
     {
         return FALSE;
     }
     // ignore trailing junk; if there's any, it won't be written to the file anyway
     if(sscanf(val.GetData(), "%ld.%lu", &outWhole, &outFrac) == 2)
     {
         outFixed = (outWhole << 16) | (outFrac << 0);
         return TRUE;
     }
     if(sscanf(val.GetData(), "%ld", &outWhole) == 1)
     {
         outFrac = 0;
         outFixed = (outWhole << 16);
         return TRUE;
     }
     return FALSE;
 }
 
-HX_RESULT CMP4StreamMixer::Build3GPMetaData(  CMP4Atom* pUdta, CMP4Atom* pMoov  )
+HX_RESULT CMP4StreamMixer::Build3GPPMetaDataStandardSet( CMP4Atom* pUdta, CMP4Atom* pMoov )
 {
     HX_RESULT retVal = HXR_OK;
 
     // configure UTF16 output
     C3GPAtom_LangBase::ConfigureStringOutput(m_bUTF16Output);
 
     // Title
     Meta3GP_AddAtom_StringBase(retVal, pUdta, new C3GPAtom_titl(), _3GPP_META_INFO_TITLE_KEY);
 
     // Description
     Meta3GP_AddAtom_StringBase(retVal, pUdta, new C3GPAtom_dscp(), _3GPP_META_INFO_DESCRIPTION_KEY);
 
     // Copyright
     Meta3GP_AddAtom_StringBase(retVal, pUdta, new C3GPAtom_cprt(), _3GPP_META_INFO_COPYRIGHT_KEY);
 
     // Performer
     Meta3GP_AddAtom_StringBase(retVal, pUdta, new C3GPAtom_perf(), _3GPP_META_INFO_PERFORMER_KEY);
 
     // Author
     Meta3GP_AddAtom_StringBase(retVal, pUdta, new C3GPAtom_auth(), _3GPP_META_INFO_AUTHOR_KEY);
 
     // Genre
     Meta3GP_AddAtom_StringBase(retVal, pUdta, new C3GPAtom_gnre(), _3GPP_META_INFO_GENRE_KEY);
 
     // Rating
     C3GPAtom_rtng* pRtng = new C3GPAtom_rtng();
     if( Meta3GP_NewAtomGuard(retVal, pRtng) )
     {
         retVal = HXR_OK;
 
@@ -4298,124 +4388,227 @@
             {
                 pAlbm->SetTrackNumber(TRUE, UINT8(uTrackNum));
             }
             Meta3GP_SetLanguageEncoding(pAlbm);
             pUdta->AddChild(pAlbm);
         }
         else
         {
             HX_DELETE(pAlbm);
         }
     }
 
     // Recording Year
     C3GPAtom_yrrc* pYrrc = new C3GPAtom_yrrc();
     if( Meta3GP_NewAtomGuard(retVal, pYrrc) )
     {
         retVal = HXR_OK;
 
         ULONG32 uRecYear = 0;
         if(PropTools::CopyPropertyULONG32(uRecYear, m_pFileHeader, _3GPP_META_INFO_RECORDING_YEAR_KEY))
         {
             pYrrc->SetRecordingYear(UINT16(uRecYear));
             pUdta->AddChild(pYrrc);
         }
         else
         {
             HX_DELETE(pYrrc);
         }
     }
 
+    return retVal;
+}
+
+HX_RESULT CMP4StreamMixer::Build3GPPMetaData(  CMP4Atom* pUdta, CMP4Atom* pMoov  )
+{
+    HX_RESULT retVal = HXR_OK;
+
+    // standard 3GPP meta-data set
+    if(m_bRequestedOutputMetaFlavor3GPP || (m_eMetaFlavor == META_3GPP))
+    {
+        retVal = Build3GPPMetaDataStandardSet(pUdta, pMoov);
+    }
+
     // ID32, setup box hierarchy: file/moov/meta/[hdlr, ID32]
-    CMP4Atom_meta* pMeta = NULL;
     if(SUCCEEDED(retVal))
     {
-        retVal = Build3GPMetaDataID32(m_pFileHeader, &pMeta);
+        CMP4Atom_meta* pMeta = NULL;
+        retVal = Build3GPPMetaDataID32(m_pFileHeader, &pMeta, TRUE);
+
         if(SUCCEEDED(retVal) && pMeta)
         {
             retVal = pMoov->AddChild(pMeta);
         }
     }
 
     return retVal;
 }
 
-HX_RESULT CMP4StreamMixer::Build3GPMetaDataID32( IHXValues* pHeader, CMP4Atom_meta** ppMeta )
-{
-    if(!ppMeta)
+HX_RESULT CMP4StreamMixer::Build3GPPMetaDataID32( IHXValues* pHeader, CMP4Atom_meta** ppMeta, HXBOOL bFileHeader )
     {
-        return HXR_POINTER;
-    }
-    if(!pHeader)
+    if(!ppMeta || !pHeader)
     {
         return HXR_INVALID_PARAMETER;
     }
 
     *ppMeta = NULL;
-
-    IHXBuffer* pBlobID32 = NULL;
     HX_RESULT retVal = HXR_OK;
 
-    if(SUCCEEDED(pHeader->GetPropertyBuffer(_3GPP_META_INFO_ID32_BLOB_KEY, pBlobID32)))
+    // If there's a complete ID32 blob present, use it as ID32 atom contents
+    if(!m_bIgnoreID32Blob)
+    {
+        IHXBuffer* pID32BlobBuffer = NULL;
+        if(SUCCEEDED(pHeader->GetPropertyBuffer(_3GPP_META_INFO_ID32_BLOB_KEY, pID32BlobBuffer)) && pID32BlobBuffer)
+        {
+            retVal = Build3GPPMetaDataID32(pHeader, ppMeta, pID32BlobBuffer->GetBuffer(), pID32BlobBuffer->GetSize());
+        }
+
+        if(pID32BlobBuffer)
+        {
+            HX_RELEASE(pID32BlobBuffer);
+            return retVal;
+        }
+    }
+
+    // Construct ID3v2 tag from header properties
+    ID3Tools::ID3v2Tag tag;
+
+    // Extract APIC frames from buffer properties.
+    if(m_bRequestedOutputMetaFlavor3GPP || (m_eMetaFlavor == META_3GPP))
+    {
+        UINT32 uAPICDataCount = 0;
+        PropTools::CopyPropertyULONG32(uAPICDataCount, pHeader, _3GPP_META_INFO_PICTURE_COUNT_KEY);
+
+        if(uAPICDataCount)
+        {
+            for(UINT32 uAPICData = 0; SUCCEEDED(retVal) && (uAPICData < uAPICDataCount); uAPICData++)
+            {
+                retVal = HXR_OUTOFMEMORY;
+                ID3Tools::APICFrame* pFrame = new ID3Tools::APICFrame;
+                if(pFrame)
+                {
+                    char key[128];
+                    sprintf(key, _3GPP_META_INFO_PICTURE_KEY, int(uAPICData));
+
+                    retVal = pFrame->DeserializeFromBufferProperty(pHeader, key);
+
+                    if(SUCCEEDED(retVal))
+                    {
+                        retVal = tag.AddFrame(pFrame);
+                    }
+                }
+            }
+        }
+    }
+
+    // Extract UITS data from buffer property. Write UITS to file header only.
+    if(bFileHeader && m_bFileHeaderCarriesUITSData)
+    {
+        EncodedString sUITSData;
+        if(PropTools::CopyPropertyBuffer(sUITSData, m_pFileHeader, _3GPP_META_INFO_UITS_KEY))
+        {
+            retVal = HXR_OUTOFMEMORY;
+            ID3Tools::PRIVFrame* pFrame = new ID3Tools::PRIVFrame;
+            if(pFrame)
+            {
+                pFrame->SetOwnerIdentifier(_META_INFO_UITS_OWNER_IDENTIFIER);
+                retVal = pFrame->SetPrivateData((const UINT8*)sUITSData.GetData(), sUITSData.GetByteLength(FALSE));
+
+                if(SUCCEEDED(retVal))
+                {
+                    retVal = tag.AddFrame(pFrame);
+                }
+            }
+        }
+    }
+
+    // write out the tag into the ID32 box
+    if(SUCCEEDED(retVal) && tag.GetNumFrames())
     {
         retVal = HXR_OUTOFMEMORY;
 
+        // construct ID32 payload buffer
+        UINT32 ulID32BlobSize = tag.CalcSerializationBufferSize();
+        UINT8* pID32Blob = new UINT8[ulID32BlobSize];
+        if(pID32Blob)
+        {
+            retVal = tag.SerializeToBuffer(pID32Blob, ulID32BlobSize);
+        }
+
+        if(SUCCEEDED(retVal))
+        {
+            retVal = Build3GPPMetaDataID32(pHeader, ppMeta, pID32Blob, ulID32BlobSize);
+        }
+
+        // cleanup
+        HX_VECTOR_DELETE(pID32Blob);
+    }
+
+    return retVal;
+}
+
+HX_RESULT CMP4StreamMixer::Build3GPPMetaDataID32( IHXValues* pHeader, CMP4Atom_meta** ppMeta, const UINT8* pID32Blob, UINT32 ulID32BlobSize )
+{
+    if(!ppMeta || !pHeader || !pID32Blob || !ulID32BlobSize)
+    {
+        return HXR_INVALID_PARAMETER;
+    }
+
+    *ppMeta = NULL;
+    HX_RESULT retVal = HXR_OK;
+
         // setup box hierarchy: meta/[hdlr, ID32]
         CMP4Atom_meta* pMeta = new CMP4Atom_meta();
         CMP4Atom_hdlr* pHdlr = new CMP4Atom_hdlr();
         C3GPAtom_ID32* pID32 = new C3GPAtom_ID32();
 
         if(pMeta && pHdlr && pID32)
         {
             // 'hdlr' must be the first child of 'meta'
             pHdlr->SetHandlerType("ID32");
             pMeta->AddChild(pHdlr);
 
             Meta3GP_SetLanguageEncoding(pID32);
-            pID32->SetBlob((UCHAR*)pBlobID32->GetBuffer(), pBlobID32->GetSize());
+        retVal = pID32->SetBlob((UCHAR*)pID32Blob, ulID32BlobSize);
             pMeta->AddChild(pID32);
-
-            retVal = HXR_OK;
         }
 
         if(SUCCEEDED(retVal))
         {
             *ppMeta = pMeta;
         }
         else
         {
             HX_DELETE(pMeta);
             HX_DELETE(pHdlr);
             HX_DELETE(pID32);
         }
-    }
-    HX_RELEASE(pBlobID32);
 
     return retVal;
 }
 
 HX_RESULT CMP4StreamMixer::StartBuildStsdEntry( CMP4Atom_stsd* pStsd, UINT16 usStreamNum )
 {
     return BuildStsdEntry(pStsd, usStreamNum);
 }
 
 // Build out the stsd entry for a specific stream; we only have one stream in this case, but
 // extending this to handle multiple streams should be easy
 HX_RESULT CMP4StreamMixer::BuildStsdEntry( CMP4Atom_stsd* pStsd, UINT16 usStreamNum )
 {
     // stream handler should have set SampleFormat already
     UINT32 ulSampleFormat  = 0;
     m_pStreamInfo[usStreamNum].m_pStreamHeader->GetPropertyULONG32("SampleFormat", ulSampleFormat);
 
     switch (ulSampleFormat)
     {
         case MP4_BUILD_ATOMID('m', 'p', '4', 'a'):
                 return BuildMP4AStsdEntry(pStsd, usStreamNum);
                 break;
         case MP4_BUILD_ATOMID('m', 'p', '4', 'v'):
                 return BuildMP4VStsdEntry(pStsd, usStreamNum);
                 break;
         case MP4_BUILD_ATOMID('a', 'v', 'c', '1'):
                 return BuildAVC1StsdEntry(pStsd, usStreamNum);
                 break;
         case MP4_BUILD_ATOMID('h', '2', '6', '3'):
                 return BuildH263StsdEntry(pStsd, usStreamNum);
Index: mp4sm.h
===================================================================
RCS file: /cvsroot/datatype/mp4/filewriter/mp4sm.h,v
retrieving revision 1.13
diff -d -H -w -U30 -r1.13 mp4sm.h
--- mp4sm.h	23 Oct 2009 09:47:54 -0000	1.13
+++ mp4sm.h	6 Nov 2009 21:45:39 -0000
@@ -187,62 +187,64 @@
         IHXValues*     m_pStreamHeader;
         CStblManager*  m_pStblManager;
         HXBOOL         m_bHaveSeenFirstPacket;
         HXBOOL         m_bIsAudio;
         HXBOOL         m_bIsVideo;
         UINT32         m_ulFirstTimestamp;
         UINT32         m_ulLastTimestamp;
         UINT32         m_ulLastTimestampDelta;
         UINT32         m_ulFirstRTPTimestamp;
         UINT32         m_ulStreamGroupNumber;
         UINT32         m_ulSwitchGroupID;
         UINT32         m_ulAvgBitRate;
         UINT32         m_ulASMAverageBandwidth;
         UINT32         m_ulTrackID;
         UINT32         m_ulHintTrackID;
         UINT32         m_ulBaseRule;
         UINT32         m_ulNumASMRules;
         HXASMRuleInfo* m_pASMRuleInfo;
         CHXSimpleList* m_pPacketList;
         CMP4Atom_tkhd* m_pTkhd;
         CMP4Atom_mdhd* m_pMdhd;
         HintInfo*      m_pHintInfo;
     };
 
     // Build the entire atom tree
     HX_RESULT BuildMP4Atoms();
     
     // Build the metadata subtree
     HX_RESULT BuildiTunesMetaData( CMP4Atom* pUdta );
     HX_RESULT BuildPSPMetaData( CMP4Atom* pUsmt );
-    HX_RESULT Build3GPMetaData( CMP4Atom* pUdta, CMP4Atom* pMoov );
-    HX_RESULT Build3GPMetaDataID32( IHXValues* pHeader, CMP4Atom_meta** ppMeta );
+    HX_RESULT Build3GPPMetaDataStandardSet( CMP4Atom* pUdta, CMP4Atom* pMoov );
+    HX_RESULT Build3GPPMetaData( CMP4Atom* pUdta, CMP4Atom* pMoov );
+    HX_RESULT Build3GPPMetaDataID32( IHXValues* pHeader, CMP4Atom_meta** ppMeta, HXBOOL bFileHeader );
+    HX_RESULT Build3GPPMetaDataID32( IHXValues* pHeader, CMP4Atom_meta** ppMeta, const UINT8* pID32Blob, UINT32 ulID32BlobSize);
 
     // Build the trak subtree
     HX_RESULT BuildTrack( UINT16 usStreamNum, CMP4Atom* pTrak );
     HXBOOL    IsStreamInSwitchGroup(UINT32 ulStreamNumber, UINT32* pulSwitchGroupID = NULL);
     void      SetSwitchGroup(CMP4Atom_udta* pUdtaAtom, UINT32 ulStreamNumber);
     HXBOOL    NeedToAddUdtaChildToTrakBox(CMP4Atom_trak* pTrakBox, UINT32 ulStreamNumber);
     HXBOOL    NeedToAddUdtaChildToMoovBox();
     HX_RESULT AddMovieHintInformation(CMP4Atom_udta* pUdta);
     HX_RESULT GenerateMovieLevelSDP(CHXString* pSDPStr);
     HX_RESULT GenerateAltGroupSDP(CHXString* pAltGroupSDPStr);
     HX_RESULT FindStreamNumInAlternateGroupWithAvgBandwidth(UINT32 ulStreamGroupNumber, UINT32 ulStreamBandwidth, UINT32* pulStreamNum);
     HX_RESULT GetTrackIDForAssociatedHintTrack(UINT32 ulStreamNumber, UINT32* pulHintTrackID);
     HX_RESULT ParseASMRulePropertyUINT32(IHXValues* pProps, const char* pszName, UINT32 ulFlag, UINT32* pulAllFlags, UINT32* pulValue);
     HX_RESULT ParseASMRulePropertyPresence(IHXValues* pProps, const char* pszName, UINT32 ulFlag, UINT32* pulAllFlags, HXBOOL* pbValue);
     HX_RESULT ParseASMRuleProperties(IHXValues* pProps, HXASMRuleInfo* pInfo);
     HX_RESULT ParseStreamASMRuleBook(StreamInfo* pInfo);
     HX_RESULT FindStreamInSwitchGroup(UINT32 ulSwitchGroupID, UINT32 ulASMRule, UINT32* pulStreamNumber);
     HX_RESULT ChangePacketStreamNumber(IHXPacket* pPacket, UINT32 ulNewStreamNumber, REF(IHXPacket*) rpPacket);
     void      CheckForRTPPackets(IHXPacket* pPacket);
     void      ChooseTrackIDs();
     HX_RESULT DetermineStreamType(IHXValues* pStreamHeader, HXBOOL* pbIsAudio, HXBOOL* pbIsVideo);
     HX_RESULT GetStreamBandwidth(UINT32 ulStreamNum, UINT32* pulBandwidth);
     void      AddHintingStatisticsBoxes(CMP4Atom_udta* pUdta);
 
     // Method used to sort stream headers by "AvgBitRate" property
     static int StreamHeaderAvgBitRateSort(void* pHdr1, void* pHdr2);
 
     // Build the mdia subtree
     HX_RESULT BuildMdia(UINT16 usStreamNum, CMP4Atom *pTrak, HintInfo *pHinfo);
 
@@ -306,58 +308,59 @@
     UINT32     m_ulStreamDoneReceived;
 
     CHXHintContext m_HintContext;
 
     StreamInfo* m_pStreamInfo;
     
     CMP4Archiver* m_pArchiver;
 
     enum RTPPacketStream
     {
 	UNCHECKED = 0,
 	IS_RTP    = 1,
 	NOT_RTP   = 2,
     };
 
     RTPPacketStream m_eIsRtp;
     
     UINT32 m_uiByteCount;
     UINT32 m_uiReservedBlockCount;
 
     enum MetaFlavor
     {
         META_ITUNES,
         META_3GPP,
         META_PSP
     };
 
     MetaFlavor m_eMetaFlavor;
     HXBOOL     m_bRequestedOutputMetaFlavor3GPP;
     HXBOOL     m_bRequestedOutputMetaFlavorITUNES;
-
+    HXBOOL        m_bIgnoreID32Blob;
+    HXBOOL        m_bFileHeaderCarriesUITSData;
     HXBOOL        m_bUTF16Output;
     EncodedString m_spCompatibility;
 
 protected:
     enum ThreeGPPMode
     {
 	M3GPP_NONE,
 	M3GPP_GENERAL,
 	M3GPP_BASIC,
 	M3GPP_STREAMING,
 	M3GPP_PROGDL,
 	M3GPP_STREAMDL,
 	M3GPP_REL5
     };
     ThreeGPPMode m_e3gpMode;
     HXBOOL m_bTSInterleave;
     HXBOOL m_bGenerateHintTracks;
 
     friend class CStblManager;
 
     UINT32 m_uiBaseWriteOffset;
     UINT32 m_uiCurrentOffset;
     UINT32 m_uiGlobalFlush;
 };
 
 
 #endif // _MP4SM_H_
datatype_tools_dtdriver_apps_meta3gp.diff (text/x-patch, 107.6 KB) - not displayed
datatype_tools_dtdriver_engine.diff (text/x-patch, 14.4 KB)
Index: ffdriver.cpp
===================================================================
RCS file: /cvsroot/datatype/tools/dtdriver/engine/ffdriver.cpp,v
retrieving revision 1.61
diff -d -H -w -U30 -r1.61 ffdriver.cpp
--- ffdriver.cpp	23 Oct 2009 09:46:03 -0000	1.61
+++ ffdriver.cpp	6 Nov 2009 21:22:14 -0000
@@ -255,60 +255,61 @@
     , m_ulNumBwThresholds(0)
     , m_lBwThresholdIdx(0)
     , m_ulCurrentBw(0)
     , m_ulLastThreasholdEventTime(0)
     , m_state(State_Initializing)
     , m_status(HXR_OK)
     , m_pInputFileName(NULL)
     , m_pInputURL(NULL)
     , m_pWriteFileName(NULL)
     , m_pTempDirName(NULL)
     , m_pInputFileProtocol(NULL)
     , m_pInputFileSysShortName(NULL)
     , m_uOptionFlags(DFLT_OPTIONS)
     , m_bUseNuggetFormat(FALSE)
     , m_uBandwidth(0)
     , m_ulRateMode(RATE_MODE_CONSTANT)
     , m_ulRateModeInterval(0)
     , m_ulMinBwThreshold(0)
     , m_ulMaxBwThreshold(0)
     , m_ulStartTime(0)
     , m_ulProcessUntil(0)
     , m_ulProcessTimeUnits(0)
     , m_bBandwidthExplicitlySet(FALSE)
     , m_bStartTimeExplicitlySet(FALSE)
     , m_bParseSDP(FALSE)
     , m_uServerMode(0)
     , m_ulContextEvents(0)
     , m_lTimerValue(-1)
     , m_ulRotationSize(0)
     , m_ulMetaDataFlavor(0)
+    , m_ulMetaDataIgnore(0)
     , m_bStop(FALSE)
     , m_bSoftStop(FALSE)
     , m_bPacketReady(FALSE)
     , m_pMemProbeArray(NULL)
     , m_ulGetPacketEvents(0)
     , m_bPadToSize(FALSE)
     , m_ulTargetSize(0)
     , m_ulMediaSize(0)
     , m_pLastWrittenPacket(NULL)
     , m_pLastWrittenFileHeader(NULL)
     , m_pDupPacketTrackerList(NULL)
 #if defined (HELIX_CONFIG_USE_EXTERNAL_CONTEXT)
     , m_bPersistentContext(TRUE)
 #else
     , m_bPersistentContext(FALSE)
 #endif
     , m_ulStartTS(DTDR_INVALID_TS)
     , m_ulStartTick(0)
     , m_ulProcessingRate(0) // 0 = as fast as possible
 #ifdef HELIX_FEATURE_DTDR_USE_RECOGNIZER
     , m_pFileRecognizer(NULL)
 #endif
     , m_bUseFileRecognition(FALSE)
     , m_bLiveSource(FALSE)
     , m_bTerminated(FALSE)
 {
 #ifdef _WRITER_WRITE_SIZE_TEST
     m_bPadToSize = TRUE;
     m_ulTargetSize = WRITER_WRITE_SIZE_TEST_GOAL;
 #endif  // _WRITER_WRITE_SIZE_TEST
@@ -655,60 +656,64 @@
                     m_ulStartTime = uOptValue;
 		    m_bStartTimeExplicitlySet = TRUE;
                 }
                 else if (strcmp(pOptName, PROCESSUNTIL_OPTION_NAME) == 0)
                 {
                     m_ulProcessUntil = uOptValue;
                 }
                 else if (strcmp(pOptName, PROCESSTIMEUNITS_OPTION_NAME) == 0)
                 {
                     m_ulProcessTimeUnits = uOptValue;
                 }
                 else if (strcmp(pOptName, ROTATIONSIZE_OPTION_NAME) == 0)
                 {
                     m_ulRotationSize = uOptValue;
                 }
                 else if (strcmp(pOptName, PROCESSHEADERS_OPTION_NAME) == 0)
                 {
                     if (m_lTimerValue < 0)
                     {
                         m_lTimerValue = 0;
                     }
                 }
                 else if (strcmp(pOptName, FILERECOGNITION_OPTION_NAME) == 0 && uOptValue)
                 {
                     m_bUseFileRecognition = TRUE;
                 }
                 else if (strcmp(pOptName, METADATAFLAVOR_OPTION_NAME) == 0)
                 {
                     m_ulMetaDataFlavor = uOptValue;
                 }
+                else if (strcmp(pOptName, METADATAIGNORE_OPTION_NAME) == 0)
+                {
+                    m_ulMetaDataIgnore = uOptValue;
+                }
             }
             Status = pOptions->GetNextPropertyULONG32(pOptName, uOptValue);
         }
     }
 
     // If we are to process packets for 0 amount of time and
     // PROCESSHEADERS_OPTION_NAME is not explicitly set, assume
     // PROCESSHEADERS_OPTION_NAME is needed.
     if ((m_lTimerValue == 0) && m_pOptions)
     {
         ULONG32 ulVal = 0;
         if (FAILED(m_pOptions->GetPropertyULONG32(
             PROCESSHEADERS_OPTION_NAME,
             ulVal)))
         {
             m_pOptions->SetPropertyULONG32(
             PROCESSHEADERS_OPTION_NAME,
             1);
         }
     }
 
     if (MEMORY_TRACKING_NEEDED)
     {
         m_pMemProbeArray = new CHXMemProbe [MEMPRB_MAX];
     }
 
     if (SUCCEEDED(RetVal) &&
     (m_ulRateMode != RATE_MODE_CONSTANT))
     {
         m_ulBwThresholdsSize = 0;
@@ -2034,67 +2039,76 @@
     }
 
     if (SUCCEEDED(retVal))
     {
         if (m_uOptionFlags & PREROLL_OPTION)
         {
             retVal = pNewProps->SetPropertyULONG32("RecomputePreroll", 1);
         }
     }
 
     if (SUCCEEDED(retVal))
     {
         if (m_uOptionFlags & BLASTFILE_OPTION)
         {
             retVal = pNewProps->SetPropertyULONG32("BlastFiles", 1);
         }
     }
 
     if (SUCCEEDED(retVal))
     {
         if (m_uOptionFlags & UTF16OUTPUT_OPTION)
         {
             retVal = pNewProps->SetPropertyULONG32("UTF16Output", 1);
         }
     }
 
     if (SUCCEEDED(retVal))
     {
         if (m_ulMetaDataFlavor != 0)
         {
-            retVal = pNewProps->SetPropertyULONG32("MetaDataFlavor",
+            retVal = pNewProps->SetPropertyULONG32(METADATAFLAVOR_OPTION_NAME,
                                 m_ulMetaDataFlavor);
         }
     }
 
     if (SUCCEEDED(retVal))
     {
+        if (m_ulMetaDataIgnore != 0)
+        {
+            retVal = pNewProps->SetPropertyULONG32(METADATAIGNORE_OPTION_NAME,
+                                m_ulMetaDataIgnore);
+        }
+    }
+
+    if (SUCCEEDED(retVal))
+    {
         if (m_uOptionFlags & RELATIVETS_OPTION)
         {
             retVal = pNewProps->SetPropertyULONG32("UseRelativeTS", 1);
         }
     }
 
     if (SUCCEEDED(retVal))
     {
         if (m_ulRotationSize != 0)
         {
             retVal = pNewProps->SetPropertyULONG32("RotationSize", 
                                m_ulRotationSize);
         }
     }
 
     if (SUCCEEDED(retVal))
     {
         if (m_bBandwidthExplicitlySet || 
             (m_uBandwidth != 0) ||
             (m_ulRateMode != RATE_MODE_CONSTANT))
         {
             retVal = pNewProps->SetPropertyULONG32("ForceIntoSingleRate", 1);
         }
     }
 
     if (SUCCEEDED(retVal))
     {
 	ULONG32 ulUseTempFiles = 0;
 	if (SUCCEEDED(m_pOptions->GetPropertyULONG32(USETEMPFILES_OPTION_NAME, ulUseTempFiles)))
 	{
Index: pub/ffdriver.h
===================================================================
RCS file: /cvsroot/datatype/tools/dtdriver/engine/pub/ffdriver.h,v
retrieving revision 1.45
diff -d -H -w -U30 -r1.45 ffdriver.h
--- pub/ffdriver.h	23 Oct 2009 09:46:04 -0000	1.45
+++ pub/ffdriver.h	6 Nov 2009 21:22:14 -0000
@@ -51,60 +51,61 @@
 #define RATE_OPTION_NAME	    "Rate"
 #define RATEMODE_OPTION_NAME	    "RateMode"
 #define RATEMAX_OPTION_NAME	    "RateMax"
 #define RATEMIN_OPTION_NAME	    "RateMin"
 #define PACE_OPTION_NAME	    "Pace"
 #define RATEMODEINTERVAL_OPTION_NAME "RateModeInterval"
 #define TIMER_OPTION_NAME	    "Timer"
 #define PASSSWOFFPKTS_OPTION_NAME   "PassSwitchOffPackets"
 #define USETEMPFILES_OPTION_NAME    "UseTempFiles"
 #define USENUGGETFORMAT_OPTION_NAME	    "UseNuggetFormat"
 #define NUGGETEXPIRATION_OPTION_NAME	    "NuggetExpiration"
 #define NUGGETCONNECTTIME_OPTION_NAME	    "NuggetConnectTime"
 #define NUGGETREMOTESOURCEURL_OPTION_NAME   "NuggetRemoteSourceURL"
 #define QUIET_OPTION_NAME           "Quiet"
 #define JUMPSTART_OPTION_NAME	    "StartTime"
 #define PROCESSUNTIL_OPTION_NAME    "ProcessUntil"
 #define PROCESSTIMEUNITS_OPTION_NAME    "ProcessTimeUnits"
 #define PROCESSDECODEDTIMEUNITS_OPTION_NAME    "ProcessDecodedTimeUnits"
 #define TIMING_OPTION_NAME	    "Timing"
 #define EVENT_OPTION_NAME	    "Event"
 #define DURATION_OPTION_NAME	    "Duration"
 #define TITLE_OPTION_NAME   	    "Title"
 #define AUTHOR_OPTION_NAME	    "Author"
 #define COPYRIGHT_OPTION_NAME	    "Copyright"
 #define ABSTRACT_OPTION_NAME	    "Abstract"
 #define KEYWORDS_OPTION_NAME	    "Keywords"
 #define PREROLL_OPTION_NAME	    "RecomputePreroll"
 #define BLASTFILE_OPTION_NAME	    "BlastFiles"
 #define UTF16OUTPUT_OPTION_NAME     "UTF16Output"
 #define METADATAFLAVOR_OPTION_NAME  "MetaDataFlavor"
+#define METADATAIGNORE_OPTION_NAME  "MetaDataIgnore"
 #define SYNCHRONOUS_OPTION_NAME     "Synchronous"
 #define RECORDASLIVE_OPTION_NAME    "RecordAsLive"
 #define ROTATIONSIZE_OPTION_NAME    "RotationSize"
 #define VIDEOOFFSET_OPTION_NAME	    "VideoTimeOffset"
 #define SOURCEOFFSET_OPTION_NAME    "SourceTimeOffset"
 #define VIDEOTIMESCALE_OPTION_NAME  "VideoTimeScale"
 #define KEYFRAMESONLY_OPTION_NAME   "KeyFramesOnly"
 #define MAXFRAMERATE_OPTION_NAME    "MaxFrameRate"
 #define MINFRAMERATE_OPTION_NAME    "MinFrameRate"
 #define LOOKAHEAD_OPTION_NAME       "LookAhead"
 #define RELATIVETS_OPTION_NAME	    "RelativeTS"
 #define DECRYPT_OPTION_NAME	    "DecryptSource"
 #define DECODE_OPTION_NAME	    "DecodeSource"
 #define DECODEAUDIO_OPTION_NAME	    "DecodeAudio"
 #define DECODEVIDEO_OPTION_NAME	    "DecodeVideo"
 #define DECODENONAV_OPTION_NAME	    "DecodeNonAV"
 #define DECODESTREAM_OPTION_NAME    "DecodeStream"
 #define DECODESTREAMMODE_OPTION_NAME	"DecodeStreamMode"
 #define OUTPUTSAMPLESPERSECOND_OPTION_NAME  "OutputSamplesPerSecond"
 #define OUTPUTCHANNELS_OPTION_NAME	    "OutputChannels"
 #define OUTPUTBITSPERSAMPLE_OPTION_NAME	    "OutputBitsPerSample"	
 #define MINOUTPUTBITSPERSAMPLE_OPTION_NAME  "MinOutputBitsPerSample"	
 #define MAXOUTPUTBITSPERSAMPLE_OPTION_NAME  "MaxOutputBitsPerSample"	
 #define ENCODE_OPTION_NAME	    "EncodeSource"
 #define ENCODEAUDIO_OPTION_NAME	    "EncodeAudioFormat"
 #define ENCODEVIDEO_OPTION_NAME	    "EncodeVideoFormat"
 #define ENCODEAUDIORATE_OPTION_NAME "EncodeAudioRate"
 #define ENCODEAUDIORATETOL_OPTION_NAME "EncodeAudioRateTol"
 #define ENCODEAUDIORATEMAX_OPTION_NAME "EncodeAudioRateMax"
 #define ENCODEAUDIORATECOMPFMTS_OPTION_NAME "EncodeAudioCompFormats"
@@ -130,60 +131,63 @@
 #define PROCESSHEADERS_OPTION_NAME  "ProcessHeadersOnly"
 #define SETSRCPROP_OPTION_NAME      "SetSrcProperties"
 #define OPTIMIZEDTRANSCODE_OPTION_NAME      "OptimizedTranscode"
 #define FILERECOGNITION_OPTION_NAME  "UseFileRecognition"
 #define COLORCONVERT_OPTION_NAME	"ColorConvert"
 #define MAXNUMIMAGEOUTPUTFILES_OPTION_NAME	"MaximumNumImageOutputFiles"
 #define DECODED_VIDEO_RESIZE_WIDTH_OPTION_NAME	"DecodedVideoResizeWidth"
 #define DECODED_VIDEO_RESIZE_HEIGHT_OPTION_NAME	"DecodedVideoResizeHeight"
 #define DECODED_VIDEO_RESIZE_PRESERVE_ASPECT_OPTION_NAME "DecodedVideoResizePreserveAspect"
 #define COMPATIBILITY_OPTION_NAME   "Compatibility"
 
 // Merge sorter related options
 #define MERGESORTFF_OPTION_NAME       "MergeSortFileFormat"
 #define MERGESORTDEC_OPTION_NAME      "MergeSortDecode"
 #define MERGESORTENC_OPTION_NAME      "MergeSortEncode"
 #define MERGESORTFFDEPTH_OPTION_NAME  "MergeSortMaxQueueDepthPostFileFormat"
 #define MERGESORTFFTIME_OPTION_NAME   "MergeSortMaxQueueTimespanFileFormat"
 #define MERGESORTFFBYTES_OPTION_NAME  "MergeSortMaxQueueBytesFileFormat"
 #define MERGESORTDECDEPTH_OPTION_NAME "MergeSortMaxQueueDepthDecode"
 #define MERGESORTDECTIME_OPTION_NAME  "MergeSortMaxQueueTimespanDecode"
 #define MERGESORTDECBYTES_OPTION_NAME "MergeSortMaxQueueBytesDecode"
 #define MERGESORTENCDEPTH_OPTION_NAME "MergeSortMaxQueueDepthEncode"
 #define MERGESORTENCTIME_OPTION_NAME  "MergeSortMaxQueueTimespanEncode"
 #define MERGESORTENCBYTES_OPTION_NAME "MergeSortMaxQueueBytesEncode"
 
 #define RATE_MODE_CONSTANT  0
 #define RATE_MODE_UPDOWN    1
 #define RATE_MODE_UP        2
 #define RATE_MODE_DOWN      3
 
+// MetaDataIgnore values
+#define METADATAIGNORE_ID32BLOB  0x00000001
+
 
 /****************************************************************************
  *  Includes
  */
 #include "hlxclib/stdio.h"
 #include "hlxclib/stdarg.h"
 
 #include "minicntx.h"
 #include "hxmemprb.h"
 
 #include "hxsrcin.h"
 #include "hxformt.h"
 #include "hxfiles.h"
 #include "hxfwrtr.h"
 #include "hxengin.h"
 
 #include "hxslist.h"
 
 
 #ifdef HELIX_FEATURE_DTDR_USE_RECOGNIZER
 #include "recognizer.h"
 #endif
 
 
 
 
 /****************************************************************************
  * 
  *  Class:
  *
@@ -624,60 +628,61 @@
     INT32		    m_lNextBwThresholdIdx;
     UINT32		    m_ulCurrentBw;
     UINT32		    m_ulLastThreasholdEventTime;
     ProcState               m_state;
     HX_RESULT               m_status;
     char*		    m_pInputFileName;
     char*		    m_pInputURL;
     char*		    m_pWriteFileName;
     char*		    m_pTempDirName;
     char*		    m_pInputFileProtocol;
     char*		    m_pInputFileSysShortName;
     ULONG32		    m_uOptionFlags;
     ULONG32		    m_uBandwidth;
     ULONG32		    m_ulRateMode;
     ULONG32		    m_ulRateModeInterval;
     ULONG32		    m_ulMinBwThreshold;
     ULONG32		    m_ulMaxBwThreshold;
     ULONG32		    m_ulStartTime;
     ULONG32		    m_ulProcessUntil;
     ULONG32                 m_ulProcessTimeUnits;
     HXBOOL		    m_bUseNuggetFormat;
     HXBOOL		    m_bBandwidthExplicitlySet;
     HXBOOL		    m_bStartTimeExplicitlySet;
     HXBOOL		    m_bParseSDP;
     ULONG32		    m_uServerMode;
     ULONG32		    m_ulContextEvents;
     ULONG32		    m_ulGetPacketEvents;
     LONG32		    m_lTimerValue;
     ULONG32		    m_ulRotationSize;
     ULONG32                 m_ulMetaDataFlavor;
+    ULONG32                 m_ulMetaDataIgnore;
     HXBOOL		    m_bStop;
     HXBOOL		    m_bSoftStop;
     HXBOOL		    m_bAwaitingFirstNonLostPacket;
     HXBOOL		    m_bPacketReady;
     HXBOOL		    m_bPadToSize;
     ULONG32		    m_ulTargetSize;
     ULONG32		    m_ulMediaSize;
     IHXPacket*		    m_pLastWrittenPacket;
     IHXValues*		    m_pLastWrittenFileHeader;
     IHXPacket**		    m_pDupPacketTrackerList;
     HXBOOL		    m_bPersistentContext;
     UINT32		    m_ulStartTS;
     UINT32		    m_ulStartTick;
     UINT32		    m_ulProcessingRate;
     HXBOOL		    m_bLiveSource;
     HXBOOL		    m_bTerminated;
 
     CHXMemProbe		    *m_pMemProbeArray;
 
 
 #ifdef HELIX_FEATURE_DTDR_USE_RECOGNIZER
 public:
     STDMETHOD(GetMimeTypeDone)(THIS_ HX_RESULT status, IHXBuffer* pMimeType);
 private:
     IHXFileRecognizer*      m_pFileRecognizer;
     CHXString               m_strMimeType;
 #endif
     HXBOOL                  m_bUseFileRecognition;
datatype_tools_metaeditor.diff (text/x-patch, 2.4 KB)
Index: editor.cpp
===================================================================
RCS file: /cvsroot/datatype/tools/metaeditor/editor.cpp,v
retrieving revision 1.1
diff -d -H -w -U30 -r1.1 editor.cpp
--- editor.cpp	5 Nov 2007 22:29:46 -0000	1.1
+++ editor.cpp	6 Nov 2009 21:18:57 -0000
@@ -229,72 +229,80 @@
 {
     if(m_bDrivingFile)
     {
         return HXR_UNEXPECTED;
     }
     HX_RELEASE(m_pContext);
     HX_RESULT retVal = Stop();
     if(FAILED(retVal))
     {
         return retVal;
     }
     if(!pContext)
     {
         return HXR_INVALID_PARAMETER;
     }
     m_pContext = pContext;
     HX_ADDREF(m_pContext);
 
     return HXR_OK;
 }
 
 STDMETHODIMP
 CEditor::Drive(const char* pszInputFilename, const char* pszOutputFilename,
                IHXMetaDataProcessor* pProcessor, IHXValues* pOptions,
                IHXMetaDataEditorResponse* pResponse)
 {
     if(!m_pContext)
     {
         return HXR_FAILED;
     }
+
     // cannot start while previous file operation is in progress
     if(m_bDrivingFile)
     {
         return HXR_UNEXPECTED;
     }
 
     // check arguments
-    if(!pProcessor || !pOptions || !pszInputFilename)
+    if(!pProcessor || !pOptions ||
+       !pszInputFilename || (pszInputFilename && !strlen(pszInputFilename)))
     {
         return HXR_INVALID_PARAMETER;
     }
 
+    // adjust arguments for ffdriver
+    if(pszOutputFilename && !strlen(pszOutputFilename))
+    {
+        pszOutputFilename = 0;
+    }
+
     // check for input file existence
     if(!CHXFileSpecUtils::FileExists(CHXFileSpecifier(pszInputFilename)))
     {
         return HXR_FAILED;
     }
 
     BOOL bWritingFile = (pszOutputFilename != 0);
     if(bWritingFile)
     {
         // writing to input file is not allowed
         if(!strcmp(pszInputFilename, pszOutputFilename))
         {
             return HXR_INVALID_PARAMETER;
         }
         ULONG32 ulOverwriteFile = 0;
         pOptions->GetPropertyULONG32(BLASTFILE_OPTION_NAME, ulOverwriteFile);
 
         // check for output file existence
         if(CHXFileSpecUtils::FileExists(CHXFileSpecifier(pszOutputFilename)) && !ulOverwriteFile)
         {
             return HXR_FAILED;
         }
     }
 
     HX_RESULT retVal = HXR_OK;
 
     // make a private copy of options
     IHXValues* pPrivateOptions = 0;
     if(SUCCEEDED(retVal))
     {