[datatype-dev] CR: Malformed FLV file can return invalid length

[datatype-dev] CR: Malformed FLV file can return invalid length

Mac McLain mmclain at real.com
Wed Jun 24 10:39:45 PDT 2009


Modified by: mmclain at real.com

Date: 06:24:09

Project: RealPlayer 12

 

Synopsis: The code to determine the duration of an FLV file can produce
inaccurate results when presented with certain malformed, but playable FLV
files.  In the case where walking backwards from the last block in an FLV
file to find the last valid duration fails due to invalid offsets, continue
walking forward from the last successfully read block until the last block
is read.

 

Files Added: none

 

Files Modified:

datatype/flash/flv/fileformat/flv_file_format.cpp

datatype/flash/flv/fileformat/pub/flv_file_format.h

 

Image Size and Heap Use impact (Client-Only): none

 

Platforms and Profiles Affected:

Platform: win32-i386-vc7

 

Platforms and Profiles Build Verified:

Platform: win32-i386-vc7

 

Profile: helix-client-all-defines

 

target(s): playinst

 

Branch: hxclient_3_1_0_atlas, HEAD

 

Index: pub/flv_file_format.h

===================================================================

RCS file: /cvsroot/datatype/flash/flv/fileformat/pub/flv_file_format.h,v

retrieving revision 1.7.2.9

diff -u -1 -0 -r1.7.2.9 flv_file_format.h

--- pub/flv_file_format.h     20 May 2009 13:46:04 -0000    1.7.2.9

+++ pub/flv_file_format.h     24 Jun 2009 16:18:29 -0000

@@ -199,20 +199,22 @@

         kStateGFHScanDurationReadDonePending,

         kStateFileHeaderSent,

         kStateStreamHeadersSent,

         kStateSendingPackets,

         kStateGPReadDonePending,

         kStateGPTagReadDonePending,

         kStateGPSeekDonePending,

         kStateSeekFinalSeekDonePending,

         kStateSeekSearchSeekDonePending,

         kStateSeekSearchReadDonePending,

+        kStateForwardDurationSeekDonePending,

+        kStateForwardDurationReadDonePending,

         kNumStates

     };

 

     INT32                m_lRefCount;

     IUnknown*            m_pContext;

     IHXRequest*          m_pRequest;

     IHXFormatResponse*   m_pFormatResponse;

     IHXFileObject*       m_pFileObject;

     IHXFileStat*         m_pFileStat;

     IHXValues*           m_pRequestedMetaInfo;

@@ -221,20 +223,21 @@

     CStreamMergeSorter*  m_pMergeSorter;

     CHXSimpleList*       m_pAVCRTPTimeCorrectionQueue;

     IHXBuffer*           m_pFLVHeader;

     IHXBuffer*           m_pFLVTagHeader;

     IHXBuffer*           m_pAudioSpecificConfig;

     IHXBuffer*           m_pAVCDecoderConfigurationRecord;

     UINT32               m_ulState;

     UINT32               m_ulSeekOffsetRequested;

     UINT32               m_ulReadBytesRequested;

     UINT32               m_ulFileOffset;

+    UINT32               m_ulLastForwardReadFileOffset;

     UINT32               m_ulFileSize;

     UINT32               m_ulStreamCount;

     UINT32               m_ulNumStreamHeadersSent;

     UINT32               m_ulSeekTime;

     UINT32               m_ulWidth;

     UINT32               m_ulHeight;

     UINT32               m_ulDuration;

     UINT32               m_ulProcessTimeUnits;

     UINT32               m_ulFirstAudioTimeStamp;

     UINT32               m_ulLastAudioTimeStamp;

Index: flv_file_format.cpp

===================================================================

RCS file: /cvsroot/datatype/flash/flv/fileformat/flv_file_format.cpp,v

retrieving revision 1.9.2.21

diff -u -1 -0 -r1.9.2.21 flv_file_format.cpp

--- flv_file_format.cpp 26 May 2009 01:39:58 -0000    1.9.2.21

+++ flv_file_format.cpp 24 Jun 2009 16:18:29 -0000

@@ -144,20 +144,21 @@

     , m_ulFirstVideoTimeStamp(0)

     , m_ulNumVideoFrames(0)

     , m_ulMergeSorterMaxTimespanPref(0)

     , m_ulMaxTimeBetweenKeyFramesFromMetaData(0)

     , m_ulNumBytesInNALUnitLength(0)

     , m_dVideoFrameRate(0.0)

     , m_ulState(kStateConstructed)

     , m_ulSeekOffsetRequested(0)

     , m_ulReadBytesRequested(0)

     , m_ulFileOffset(0)

+    , m_ulLastForwardReadFileOffset(0)

     , m_ulFileSize(0)

     , m_usAudioStreamNum(INVALID_STREAM_NUMBER)

     , m_usVideoStreamNum(INVALID_STREAM_NUMBER)

     , m_pLastVideoPacket(NULL)

     , m_bReadMetaPacket(FALSE)

     , m_bAudioOnlyFile(FALSE)

     , m_bVideoOnlyFile(FALSE)

     , m_bHaveFileSize(FALSE)

     , m_bHaveWidth(FALSE)

     , m_bHaveHeight(FALSE)

@@ -1190,20 +1191,22 @@

                         // and video timestamps.

                         if (!bLinearFileSystem && m_bHaveFileSize)

                         {

                             // The filesystem is capable of random access

                             // and we don't want to trust the meta-data
duration.

                             //

                             // The last 4 bytes of the FLV file should have
a

                             // previous tag size in it. That will allow us
to

                             // move backward through the file.

                             //

+                     // Save off our current file offset in case we need to
revisit it to read forward

+                     m_ulLastForwardReadFileOffset = m_ulFileOffset;

                             // Set the state

                             m_ulState =
kStateGFHScanDurationFirstSeekDonePending;

                             // Compute the file offset to seek to

                             m_ulSeekOffsetRequested = m_ulFileSize -
HX_FLV_PREV_TAG_SIZE;

                             // Seek to 4 bytes from the end of the file

                             m_pFileObject->Seek(m_ulSeekOffsetRequested,
FALSE);

                         }

                         else

                         {

                             // Set the state

@@ -2178,20 +2181,29 @@

                 }

                 // Do we need to continue the reverse scan?

                 if (bContinueReverseScan)

                 {

                     // We need to continue reverse scanning the tags

                     //

                     // Set the state

                     m_ulState = kStateGFHScanDurationSeekDonePending;

                     // Set the requested seek offset

                     m_ulSeekOffsetRequested = m_ulFileOffset -
m_cTagHeader.GetPreviousTagSize() - 19;

+

+               if (m_ulSeekOffsetRequested >= m_ulFileSize ||
m_ulSeekOffsetRequested > m_ulFileOffset)

+               {

+                 //

+                 // The FLV must be corrupted, so read forward from where
we last read to see if we can't build it that way

+                 //

+                 m_ulSeekOffsetRequested = m_ulLastForwardReadFileOffset;

+                 m_ulState = kStateForwardDurationSeekDonePending;

+               }

                     // Seek to the previous tag

                     m_pFileObject->Seek(m_ulSeekOffsetRequested, FALSE);

                 }

                 else

                 {

                     // We're done scanning, so we can seek back to the
first tag

                     //

                     // Set the state

                     m_ulState = kStateGFHFinalSeekDonePending;

                     // Set the requested seek offset

@@ -2208,22 +2220,75 @@

             //

             // Set the state

             m_ulState = kStateGFHFinalSeekDonePending;

             // Set the requested seek offset

             m_ulSeekOffsetRequested = m_cHeader.GetDataOffset();

             // Seek to the data offset

             m_pFileObject->Seek(m_ulSeekOffsetRequested, FALSE);

         }

         // Clear the return value

         retVal = HXR_OK;

+    } 

+    else if (m_ulState == kStateForwardDurationReadDonePending)

+    {

+        if (SUCCEEDED(status))

+        {

+            if (pBuffer->GetSize() >= HX_FLV_TAG_HEADER_SIZE)

+            {

+                // Parse the tag header

+                BOOL   bContinueSeek = true;

+                BYTE*  pBuf  = pBuffer->GetBuffer();

+                UINT32 ulLen = pBuffer->GetSize();

+                retVal = m_cTagHeader.Parse(&pBuf, &ulLen);

+                if (SUCCEEDED(retVal))

+                {

+                 switch (m_cTagHeader.GetTagType())

+                 {

+                     case HX_FLV_TAG_TYPE_AUDIO:

+                            // Save the last audio timestamp

+                            m_ulLastAudioTimeStamp =
m_cTagHeader.GetTimeStamp();

+                     m_bHaveLastAudioTimeStamp = TRUE;

+                     break;

+

+                 case HX_FLV_TAG_TYPE_VIDEO:

+                     break;

+

+                 default:

+                     bContinueSeek = false;

+                     break;

+               };

+

+               if (bContinueSeek)

+               {

+                 m_ulSeekOffsetRequested = m_ulFileOffset +
m_cTagHeader.GetDataSize() - 1;

+                 m_ulState = kStateForwardDurationSeekDonePending;

+                 // Seek to the next tag

+                 m_pFileObject->Seek(m_ulSeekOffsetRequested, FALSE);

+               }

+               else

+               {

+                   // Set the state

+                   m_ulState = kStateGFHFinalSeekDonePending;

+                     // Set the requested seek offset

+                     m_ulSeekOffsetRequested = m_cHeader.GetDataOffset();

+                        // Seek to the data offset

+                 m_pFileObject->Seek(m_ulSeekOffsetRequested, FALSE);

+               }

+

+           }

+         }

+     }

+        // Clear the return value

+        retVal = HXR_OK;

     }

 

+

     return retVal;

 }

 

 STDMETHODIMP CHXFLVFileFormat::WriteDone(HX_RESULT status)

 {

     return HXR_NOTIMPL;

 }

 

 STDMETHODIMP CHXFLVFileFormat::SeekDone(HX_RESULT status)

 {

@@ -2434,20 +2499,32 @@

             // Set the state

             m_ulState = kStateGFHFinalSeekDonePending;

             // Set the requested seek offset

             m_ulSeekOffsetRequested = m_cHeader.GetDataOffset();

             // Seek to the data offset

             m_pFileObject->Seek(m_ulSeekOffsetRequested, FALSE);

         }

         // Clear the return value

         retVal = HXR_OK;

     }

+    else if (m_ulState == kStateForwardDurationSeekDonePending)

+    {

+        if (SUCCEEDED(status))

+        {

+            // Set the state

+            m_ulState = kStateForwardDurationReadDonePending;

+            // Save the number of read bytes requested

+            m_ulReadBytesRequested = HX_FLV_TAG_HEADER_SIZE + 1;

+            // Read HX_FLV_TAG_HEADER_SIZE plus one bytes

+            m_pFileObject->Read(HX_FLV_TAG_HEADER_SIZE + 1);

+        }

+    }

 

     return retVal;

 }

 

 STDMETHODIMP CHXFLVFileFormat::StatDone(HX_RESULT status, UINT32 ulSize,
UINT32 ulCreationTime,

                                         UINT32 ulAccessTime, UINT32
ulModificationTime, UINT32 ulMode)

 {

     HX_RESULT retVal = HXR_UNEXPECTED;

 

     if (m_ulState == kStateIFFStatDonePending)

@@ -2513,20 +2590,31 @@

     {

         // Set the height property

         rpHdr->SetPropertyULONG32("Height", m_ulHeight);

         rpHdr->SetPropertyULONG32("FrameHeight", m_ulHeight);

     }

 

     // Set the duration property. If we have a video-specific duration,

     // then use that. If not, then if we have a file-level duration, then

     // use that. If we don't have either a video-specific duration or a

     // file-level duration, then set some default duration and assert.

+    if (!m_bHaveVideoDuration && !m_bHaveDuration)

+    {

+     //

+     // Use the last audio duration if we have that.

+     //

+     if (m_bHaveLastAudioTimeStamp)

+     {

+         m_ulDuration = m_ulLastAudioTimeStamp;

+         m_bHaveDuration = true;

+     }

+    }

     HX_ASSERT(m_bHaveVideoDuration || m_bHaveDuration);

     UINT32 ulDuration = (m_bHaveVideoDuration ? m_ulVideoDuration :

                             (m_bHaveDuration ? m_ulDuration : 10000));

     retVal = rpHdr->SetPropertyULONG32("Duration", ulDuration);

 

     // Do we have the video bitrate?

     if (!m_bHaveVideoBitrate)

     {

         // We don't know the video bitrate, but we may be

         // able to estimate it. If: a) we know the duration

 

 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.helixcommunity.org/pipermail/datatype-dev/attachments/20090624/00b170ec/attachment-0001.html


More information about the Datatype-dev mailing list
 

Site Map   |   Terms of Use   |   Privacy Policy   |   Contact Us

Copyright © 1995-2007 RealNetworks, Inc. All rights reserved. RealNetworks and Helix are trademarks of RealNetworks.
All other trademarks or registered trademarks are the property of their respective holders.