[Helix-client-dev] CR/CN: Metadata atom order modification: udta-box comes before ID3-tag related meta-box [GMPMetaEditor branch]

Petar Basic <[email protected]> Fri, 15 Jan 2010 17:43:11 +0100
Newsgroups gmane.comp.multimedia.helix.devel
Message-ID <[email protected]>
Modified by: pbasic at real.com
Date: 2010/01/15
Project: GMPMetaEditor (meta3gp.exe)

Synopsis:
Metadata atom order modification: udta-box comes before ID3-tag
related meta-box [GMPMetaEditor branch]

Details:
Recognition of file type and metadata display work incorrectly with
particular metadata atom order on some HTC handsets.

'meta' atom which contains ID3 tag comes before 'udta' atom which
contains 3GPP assets in files produced by meta3gp, and the opposite is
true for files produced by CodingTech.  HTC code obviously gets
confused while parsing 'meta' atom or ID3 tag within it.  With
CodingTech files, HTC first reads textual metadata from 'udta' atom
then proceeds parsing ID3 and fails.  With meta3gp files, HTC finds
ID3 first and fails before reading textual metadata in 'udta' atom.
This is why the files produced by CodingTech work better to some
extent if CoverArt is injected (i.e. ID3 tag stored in the file).

To improve compatibility with HTC handsets, MP4 filewriter has been
changed to produce metadata atom order in which 'udta' atom always
comes before ID3-tag related 'meta' atom.  This does not influence the
position of other atoms in the file.

Files Modified:
datatype/mp4/filewriter/mp4sm.cpp
datatype/mp4/filewriter/mp4sm.h

Platforms and Profiles Affected:
All

Image Size and Heap Use impact:
None

Platforms and Profiles Build Verified:
system id: win32-i386-vc7, sunos-5.10-sparc-studio11
profile: helix-client-all-defines

Platforms and Profiles Functionality Verified:
x86 Windows XP SP2
Sparc SunOS 5.10

Branch:
GMPMetaEditor

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
datatype_mp4_filewriter.diff (application/octet-stream, 10.2 KB)
Index: mp4sm.cpp
===================================================================
RCS file: /cvsroot/datatype/mp4/filewriter/mp4sm.cpp,v
retrieving revision 1.24.2.1
diff -d -H -w -U30 -r1.24.2.1 mp4sm.cpp
--- mp4sm.cpp	11 Jan 2010 17:43:16 -0000	1.24.2.1
+++ mp4sm.cpp	15 Jan 2010 16:10:14 -0000
@@ -1682,86 +1682,101 @@
                             retVal = HXR_OK;
 
                             // Call out to build the trak tree
                             BuildTrack( (UINT16)i, pTrak );
 
                             pMoov->AddChild( pTrak );
 
                             if (ulTrackType == MP4_BUILD_ATOMID('v','i','d','e'))
                             {
                                 bHasVideo = TRUE;
                             }
                         }
                     }
                 }
             }
 	    for (iStream = 0; iStream < m_ulStreamCount && SUCCEEDED(retVal); ++iStream)
 	    {
 		HintInfo *hi = m_pStreamInfo[iStream].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);
                 }
+
+                CMP4Atom_meta* pMeta = 0;
+
                 if (m_bRequestedOutputMetaFlavor3GPP || (m_eMetaFlavor == META_3GPP)
                     || m_bFileHeaderCarriesUITSData)
                 {
                     // Build 3GP meta-data
-                    Build3GPPMetaData(pUdta, pMoov);
+                    Build3GPPMetaData(pUdta, pMoov, &pMeta);
                 }
+
                 // 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);
+
+                // Add meta box as a child of the moov box.
+                // XXXPB: Add 'meta' after the 'udta' to improve
+                // compatibility of output file with some handsets (HTC).
+                if(pMeta)
+                {
+                    pMoov->AddChild(pMeta);
+                }
             }
         }
 
         // 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 );
             }
         }
     
     }
 
     if( SUCCEEDED( retVal ) )
     {
         // Set the duration in our new atoms to the reported one
         UpdateDuration( m_ulReportedDuration );
     
         // Set the base offsets for our atoms.
         // In the default case, where a duration is available, all the atom
         // data is written in a padding block preceding the actual content.
         // This lets us set a write offset for the root atom at 0 and be done with
@@ -4461,79 +4476,84 @@
             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 CMP4StreamMixer::Build3GPPMetaData( CMP4Atom* pUdta, CMP4Atom* pMoov, CMP4Atom_meta** ppMeta )
+{
+    if(!ppMeta || *ppMeta)
 {
+        return HXR_INVALID_PARAMETER;
+    }
+
     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]
     if(SUCCEEDED(retVal))
     {
-        CMP4Atom_meta* pMeta = NULL;
-        retVal = Build3GPPMetaDataID32(m_pFileHeader, &pMeta, TRUE);
+        retVal = Build3GPPMetaDataID32(m_pFileHeader, ppMeta, TRUE);
 
-        if(SUCCEEDED(retVal) && pMeta)
+        if(FAILED(retVal) && *ppMeta)
         {
-            retVal = pMoov->AddChild(pMeta);
+            delete *ppMeta;
+            *ppMeta = 0;
         }
     }
 
     return retVal;
 }
 
 HX_RESULT CMP4StreamMixer::Build3GPPMetaDataID32( IHXValues* pHeader, CMP4Atom_meta** ppMeta, HXBOOL bFileHeader )
 {
     if(!ppMeta || !pHeader)
     {
         return HXR_INVALID_PARAMETER;
     }
 
     *ppMeta = NULL;
     HX_RESULT retVal = HXR_OK;
 
     // check if we need to skip writing-out ID32
     if(!m_uID3TagVersionMinorOutput)
     {
         return retVal;
     }
 
     // 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());
         }
Index: mp4sm.h
===================================================================
RCS file: /cvsroot/datatype/mp4/filewriter/mp4sm.h,v
retrieving revision 1.16
diff -d -H -w -U30 -r1.16 mp4sm.h
--- mp4sm.h	6 Jan 2010 17:40:10 -0000	1.16
+++ mp4sm.h	15 Jan 2010 16:10:15 -0000
@@ -189,61 +189,61 @@
         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 Build3GPPMetaDataStandardSet( CMP4Atom* pUdta, CMP4Atom* pMoov );
-    HX_RESULT Build3GPPMetaData( CMP4Atom* pUdta, CMP4Atom* pMoov );
+    HX_RESULT Build3GPPMetaData( CMP4Atom* pUdta, CMP4Atom* pMoov, CMP4Atom_meta** ppMeta );
     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