[prev in list] [next in list] [prev in thread] [next in thread] 

List:       helix-datatype-cvs
Subject:    [Datatype-cvs] mp4/fileformat qtatmmgs.cpp,1.38,1.39
From:       ehyche () helixcommunity ! org
Date:       2008-04-29 5:45:37
Message-ID: 200804290546.m3T5kWuv031531 () mailer ! progressive-comp ! com
[Download RAW message or body]

Update of /cvsroot/datatype/mp4/fileformat
In directory cvs01.internal.helixcommunity.org:/tmp/cvs-serv10362

Modified Files:
	qtatmmgs.cpp 
Log Message:
Description
-------------------------------------------------
In mp4 files, samples can be stored in chunks, which
are contiguous blocks on media data. In order to tell
which samples belong to which chunks, there's a 
sample-to-chunk table which tells how many samples
are in each chunk:

  Chunk     NumberOfSamples
    1             31
    2             31
    3             31
    4             32
    5             32
    6             31
   ...

Since lots of times consecutive chunks have the same
number of samples, then this table is stored in compact fashion,
and only the first chunk is stored in the table for 
a run of chunks with the same number of samples.
For the example above, the table above would be stored
like this:

FirstChunk     NumberOfSamples
    1             31
    4             32
    6             31
   ...

which means that chunks 1-3 have 31 samples each,
chunks 4-5 have 32 samples each, etc. Therefore, in almost
all files I have seen up until now, the FirstChunk field
in the table is STRICTLY INCREASING. However, the mp4 file
format spec does not specifically prohibit duplicated entries
in the table. I have attached an image of a "normal"
sample-to-chunk table (normal_stsc.png).

However, in hc.org bug 7975, Intel discovered some content
where there are duplicated entries in the sample-to-chunk
table (see attached image odd_stsc.png). This duplicated entry
uncovered a bug in the CQT_SampleToChunk_Manager class in
the mp4 file format plugin. These duplicated entries were causing
us to produce a packet of the correct size but from the wrong
offset in the file. This was resulting in garbage packets
being handed to the AAC decoder.

This changes enables the CQT_SampleToChunk_Manager class to
handle duplicated entries in the sample-to-chunk table. It
does this by not assuming that the next entry in the table
will be valid, but rather introduces two new functions to
search for valid entries: GetNextFirstChunk() and GetPrevFirstChunk().
It also cleans up some code which was duplicated in several
places by centralizing these code in three new functions:
GetFirstChunk(), GetSamplesPerChunk(), and GetSampleDescID().

Files Modified
-------------------------------------------------
datatype/mp4/fileformat/qtatmmgs.cpp
datatype/mp4/fileformat/pub/qtatmmgs.h

Branches
-------------------------------------------------
HEAD and hxclient_3_1_0_atlas

Testing
-------------------------------------------------
I tested playback and seeking on files with duplicated
sample-to-chunk entries (the repro content) as well as
files with no duplicated sample-to-chunk entries.



Index: qtatmmgs.cpp
===================================================================
RCS file: /cvsroot/datatype/mp4/fileformat/qtatmmgs.cpp,v
retrieving revision 1.38
retrieving revision 1.39
diff -u -d -r1.38 -r1.39
--- qtatmmgs.cpp	16 Apr 2008 13:36:44 -0000	1.38
+++ qtatmmgs.cpp	29 Apr 2008 05:45:34 -0000	1.39
@@ -214,6 +214,7 @@
     , m_ulCurrentChunk(0)
     , m_ulNextEntryChunk(QT_NULL_CHUNK_NUM)
     , m_ulCurrentEntryIdx(0)
+    , m_ulNextEntryIdx(0)
     , m_ulSamplesPerChunk(QT_MAX_SAMPLES_PER_CHUNK)
     , m_ulSampleNumber(0)
     , m_ulSampleDescIdx(QT_BAD_IDX)
@@ -329,33 +330,11 @@
 	    // Search Forward from the beginning
 	    m_ulSampleInChunkNum = ulSampleNum;
 	    m_ulCurrentEntryIdx = 0;
-	    m_ulCurrentChunk = m_pSampleToChunkAtom->Get_FirstChunk(0)
-#ifdef _STCO_ZERO_BASED_IQ
-			       + m_ulChunkNumOffset
-#endif	// _STCO_ZERO_BASED_IQ
-			       ;
-	    m_ulSamplesPerChunk = m_pSampleToChunkAtom->Get_SamplesPerChunk(0);
-	    m_ulSampleDescIdx = m_pSampleToChunkAtom->Get_SampleDescID(0) - 1
-#ifdef _STCO_ZERO_BASED_IQ
-			        + m_ulChunkNumOffset
-#endif	// _STCO_ZERO_BASED_IQ
-			       ;
-
-	    if (m_ulNumEntries > 1)
-	    {
-		m_ulNextEntryChunk = m_pSampleToChunkAtom->
-		    Get_FirstChunk(1)
-#ifdef _STCO_ZERO_BASED_IQ
-		    + m_ulChunkNumOffset
-#endif	// _STCO_ZERO_BASED_IQ
-		    ;
-	    }
-	    else
-	    {
-		// No more chunk entries
-		m_ulNextEntryChunk = QT_NULL_CHUNK_NUM;
-	    }
-
+            m_ulNextEntryIdx     = 0;
+            m_ulCurrentChunk     = GetFirstChunk(m_ulCurrentEntryIdx);
+            m_ulSamplesPerChunk  = GetSamplesPerChunk(m_ulCurrentEntryIdx);
+            m_ulSampleDescIdx    = GetSampleDescID(m_ulCurrentEntryIdx);
+            m_ulNextEntryChunk   = GetNextFirstChunk(m_ulCurrentEntryIdx, m_ulNextEntryIdx);
 	    m_ulSampleNumber = ulSampleNum;
 
 	    return SequenceToChunk();
@@ -416,22 +395,12 @@
 					 m_ulCurrentChunk) *
 					m_ulSamplesPerChunk;
 		m_ulCurrentChunk = m_ulNextEntryChunk;
-		m_ulCurrentEntryIdx++;
+                m_ulCurrentEntryIdx = m_ulNextEntryIdx;
 		bEntryIdxChanged = TRUE;
 
-		m_ulSamplesPerChunk = m_pSampleToChunkAtom->
-		    Get_SamplesPerChunk(m_ulCurrentEntryIdx);
+                m_ulSamplesPerChunk = GetSamplesPerChunk(m_ulCurrentEntryIdx);
 
-		if ((m_ulCurrentEntryIdx + 1) < m_ulNumEntries)
-		{
-		    m_ulNextEntryChunk = m_pSampleToChunkAtom->
-			Get_FirstChunk(m_ulCurrentEntryIdx + 1);
-		}
-		else
-		{
-		    // No more chunk entries
-		    m_ulNextEntryChunk = QT_NULL_CHUNK_NUM;
-		}
+                m_ulNextEntryChunk = GetNextFirstChunk(m_ulCurrentEntryIdx, m_ulNextEntryIdx);
 	    }
 	    else
 	    {
@@ -452,12 +421,7 @@
 
 	if (bEntryIdxChanged)
 	{
-	    m_ulSampleDescIdx = m_pSampleToChunkAtom->
-		Get_SampleDescID(m_ulCurrentEntryIdx) - 1
-#ifdef _STCO_ZERO_BASED_IQ
-		+ m_ulChunkNumOffset
-#endif	// _STCO_ZERO_BASED_IQ
-			       ;
+            m_ulSampleDescIdx = GetSampleDescID(m_ulCurrentEntryIdx);
 	}
     }
 
@@ -485,26 +449,14 @@
 
 	    if (m_ulCurrentChunk == m_ulNextEntryChunk)
 	    {
-		m_ulCurrentEntryIdx++;
 		bEntryIdxChanged = TRUE;
-
-		m_ulSamplesPerChunk = m_pSampleToChunkAtom->
-		    Get_SamplesPerChunk(m_ulCurrentEntryIdx);
-
-		if ((m_ulCurrentEntryIdx + 1) < m_ulNumEntries)
-		{
-		    m_ulNextEntryChunk = m_pSampleToChunkAtom->
-			Get_FirstChunk(m_ulCurrentEntryIdx + 1)
-#ifdef _STCO_ZERO_BASED_IQ
-			+ m_ulChunkNumOffset
-#endif	// _STCO_ZERO_BASED_IQ
-			;
-		}
-		else
-		{
-		    // No more chunk entries
-		    m_ulNextEntryChunk = QT_NULL_CHUNK_NUM;
-		}
+                // Assign the current index to the next index
+                m_ulCurrentEntryIdx = m_ulNextEntryIdx;
+                // Get the new samples per chunk for the current entry
+                m_ulSamplesPerChunk = GetSamplesPerChunk(m_ulCurrentEntryIdx);
+                // Search for the next valid entry. This will update
+                // m_ulNextEntryChunk and m_ulNextEntryIdx
+                m_ulNextEntryChunk = GetNextFirstChunk(m_ulCurrentEntryIdx, m_ulNextEntryIdx);
 	    }
 	    else if ((m_ulSamplesPerChunk == 0) &&
 		(m_ulNextEntryChunk == QT_NULL_CHUNK_NUM))
@@ -515,12 +467,7 @@
 
 	if (bEntryIdxChanged)
 	{
-	    m_ulSampleDescIdx = m_pSampleToChunkAtom->
-		Get_SampleDescID(m_ulCurrentEntryIdx) - 1
-#ifdef _STCO_ZERO_BASED_IQ
-		+ m_ulChunkNumOffset
-#endif	// _STCO_ZERO_BASED_IQ
-		;
+            m_ulSampleDescIdx = GetSampleDescID(m_ulCurrentEntryIdx);
 	}
     }
 
@@ -541,8 +488,7 @@
     {
 	bEntryIdxChanged = FALSE;
 
-	ulEntryFirstChunk = m_pSampleToChunkAtom->
-	    Get_FirstChunk(m_ulCurrentEntryIdx);
+        ulEntryFirstChunk = GetFirstChunk(m_ulCurrentEntryIdx);
 
 	do
 	{
@@ -556,45 +502,171 @@
 		    return FALSE;
 		}
 
-		m_ulCurrentEntryIdx--;
+                // Set the flag saying we need to reset our sample description
 		bEntryIdxChanged = TRUE;
-
-		m_ulSamplesPerChunk = m_pSampleToChunkAtom->
-		    Get_SamplesPerChunk(m_ulCurrentEntryIdx);
-		ulEntryFirstChunk = m_pSampleToChunkAtom->
-		    Get_FirstChunk(m_ulCurrentEntryIdx);
+                // Find the previous valid entry in the list
+                ULONG32 ulPrevEntryIdx = 0;
+                ulEntryFirstChunk = GetPrevFirstChunk(m_ulCurrentEntryIdx, ulPrevEntryIdx);
+                // Set the current entry index to the previous index
+                m_ulCurrentEntryIdx = ulPrevEntryIdx;
+                // Get the new samples per chunk
+                m_ulSamplesPerChunk = GetSamplesPerChunk(m_ulCurrentEntryIdx);
 	    }
 	} while (m_ulSampleInChunkNum > m_ulSamplesPerChunk);
 
 	if (bEntryIdxChanged)
 	{
-	    m_ulSampleDescIdx = m_pSampleToChunkAtom->
-		Get_SampleDescID(m_ulCurrentEntryIdx) - 1
+            m_ulSampleDescIdx = GetSampleDescID(m_ulCurrentEntryIdx);
+        }
+        // Reset the next entry FirstChunk and the next entry index
+        m_ulNextEntryChunk = GetNextFirstChunk(m_ulCurrentEntryIdx, m_ulNextEntryIdx);
+    }
+
+    return TRUE;
+}
+
+ULONG32 CQT_SampleToChunk_Manager::GetFirstChunk(ULONG32 i)
+{
+    ULONG32 ulRet = QT_NULL_CHUNK_NUM;
+
+    if (m_pSampleToChunkAtom && i < m_ulNumEntries)
+    {
+        ulRet = m_pSampleToChunkAtom->Get_FirstChunk(i)
 #ifdef _STCO_ZERO_BASED_IQ
 		+ m_ulChunkNumOffset
 #endif	// _STCO_ZERO_BASED_IQ
 		;
 	}
 
-	if ((m_ulCurrentEntryIdx + 1) < m_ulNumEntries)
+    return ulRet;
+}
+
+ULONG32 CQT_SampleToChunk_Manager::GetSamplesPerChunk(ULONG32 i)
+{
+    ULONG32 ulRet = 0;
+
+    if (m_pSampleToChunkAtom && i < m_ulNumEntries)
+    {
+        ulRet = m_pSampleToChunkAtom->Get_SamplesPerChunk(i);
+    }
+
+    return ulRet;
+}
+
+ULONG32 CQT_SampleToChunk_Manager::GetSampleDescID(ULONG32 i)
 	{
-	    m_ulNextEntryChunk = m_pSampleToChunkAtom->
-		Get_FirstChunk(m_ulCurrentEntryIdx + 1)
+    ULONG32 ulRet = QT_BAD_IDX;
+
+    if (m_pSampleToChunkAtom && i < m_ulNumEntries)
+    {
+        ULONG32 ulTmp = m_pSampleToChunkAtom->Get_SampleDescID(i);
+        if (ulTmp)
+        {
+            // Go to zero-based
+            ulTmp--;
 #ifdef _STCO_ZERO_BASED_IQ
-		+ m_ulChunkNumOffset
+            ulTmp += m_ulChunkNumOffset;
 #endif	// _STCO_ZERO_BASED_IQ
-		;
+            // Assign to out parameter
+            ulRet = ulTmp;
+        }
+    }
+
+    return ulRet;
+}
+
+ULONG32 CQT_SampleToChunk_Manager::GetNextFirstChunk(ULONG32 i, ULONG32& rulNextIdx)
+{
+    ULONG32 ulRet = QT_NULL_CHUNK_NUM;
+
+    if (m_pSampleToChunkAtom && i < m_ulNumEntries)
+    {
+        // Get the FirstChunk field of the current entry
+        ULONG32 ulCurFirstChunk = GetFirstChunk(i);
+        // Loop through the entries until we find an entry
+        // with a FirstChunk field that is greater than
+        // the current entry. Usually this is just the
+        // very next entry. However, in some .mp4 files,
+        // entries can be duplicated in the Sample-to-Chunk
+        // table. Although this seems strange, the MP4 file
+        // format spec does not specifically prohibit this.
+        ULONG32 j = 0;
+        for (j = i + 1; j < m_ulNumEntries; j++)
+        {
+            // Get the FirstChunk field for this entry
+            ULONG32 ulTmpFirstChunk = GetFirstChunk(j);
+            // Is this FirstChunk field greater than the current FirstChunk field
+            if (ulTmpFirstChunk > ulCurFirstChunk)
+            {
+                break;
+            }
+        }
+        // Did we successfully find a next FirstChunk field?
+        if (j < m_ulNumEntries)
+        {
+            // We did find a valid next FirstChunk field, so 
+            // set the return value to that FirstChunk field.
+            ulRet = GetFirstChunk(j);
+            // Set the out parameter to the index
+            rulNextIdx = j;
 	}
 	else
 	{
-	    // No more chunk entries
-	    m_ulNextEntryChunk = QT_NULL_CHUNK_NUM;
+            // We did NOT find a next FirstChunk, so by default
+            // the return value will still be QT_NULL_CHUNK_NUM. 
+            // We should set the out parameter to a default value.
+            rulNextIdx = m_ulNumEntries;
 	}
     }
 
-    return TRUE;
+    return ulRet;
+}
+
+ULONG32 CQT_SampleToChunk_Manager::GetPrevFirstChunk(ULONG32 i, ULONG32& rulPrevIdx)
+{
+    ULONG32 ulRet = QT_NULL_CHUNK_NUM;
+
+    if (m_pSampleToChunkAtom && i < m_ulNumEntries)
+    {
+        // Get the FirstChunk field of the current entry
+        ULONG32 ulCurFirstChunk = GetFirstChunk(i);
+        // Loop through the entries until we find an entry
+        // with a FirstChunk field that is less than
+        // the current entry. Usually this is just the
+        // previous entry. However, in some .mp4 files,
+        // entries can be duplicated in the Sample-to-Chunk
+        // table. Although this seems strange, the MP4 file
+        // format spec does not specifically prohibit this.
+        INT32 j = 0;
+        for (j = ((INT32) i) - 1; j >= 0; j--)
+        {
+            // Get the FirstChunk field for this entry
+            ULONG32 ulTmpFirstChunk = GetFirstChunk((ULONG32) j);
+            // Is this FirstChunk field less than the current FirstChunk field
+            if (ulTmpFirstChunk < ulCurFirstChunk)
+            {
+                break;
+            }
+        }
+        // Did we find an entry?
+        if (j >= 0)
+        {
+            // Set the return value to this FirstChunk value
+            ulRet = GetFirstChunk((ULONG32) j);
+            // Set the out parameter to this index value
+            rulPrevIdx = (ULONG32) j;
+        }
+        else
+        {
+            // We did not find an entry. The return value will
+            // remain as QT_NULL_CHUNK_NUM, but we should set
+            // the out parameter as well.
+            rulPrevIdx = 0;
+        }
 }
 
+    return ulRet;
+}
 
 #define INVALID_COMP_SAMPLE_NUM	    0xFFFFFFFF
 #define QT_INVALID_KEY_MEDIA_TIME   0xFFFFFFFF


_______________________________________________
Datatype-cvs mailing list
Datatype-cvs@helixcommunity.org
http://lists.helixcommunity.org/mailman/listinfo/datatype-cvs
[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic