LCOV - code coverage report
Current view: directory - content/media/webm - nsWebMBufferedParser.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 133 0 0.0 %
Date: 2012-06-02 Functions: 4 0 0.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is the Mozilla Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2010
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *  Matthew Gregan <kinetik@flim.org>
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include "nsAlgorithm.h"
      40                 : #include "nsWebMBufferedParser.h"
      41                 : #include "nsTimeRanges.h"
      42                 : #include "nsThreadUtils.h"
      43                 : 
      44                 : using mozilla::ReentrantMonitorAutoEnter;
      45                 : 
      46                 : static const double NS_PER_S = 1e9;
      47                 : 
      48                 : static PRUint32
      49               0 : VIntLength(unsigned char aFirstByte, PRUint32* aMask)
      50                 : {
      51               0 :   PRUint32 count = 1;
      52               0 :   PRUint32 mask = 1 << 7;
      53               0 :   while (count < 8) {
      54               0 :     if ((aFirstByte & mask) != 0) {
      55               0 :       break;
      56                 :     }
      57               0 :     mask >>= 1;
      58               0 :     count += 1;
      59                 :   }
      60               0 :   if (aMask) {
      61               0 :     *aMask = mask;
      62                 :   }
      63               0 :   NS_ASSERTION(count >= 1 && count <= 8, "Insane VInt length.");
      64               0 :   return count;
      65                 : }
      66                 : 
      67               0 : void nsWebMBufferedParser::Append(const unsigned char* aBuffer, PRUint32 aLength,
      68                 :                                   nsTArray<nsWebMTimeDataOffset>& aMapping,
      69                 :                                   ReentrantMonitor& aReentrantMonitor)
      70                 : {
      71                 :   static const unsigned char CLUSTER_ID[] = { 0x1f, 0x43, 0xb6, 0x75 };
      72                 :   static const unsigned char TIMECODE_ID = 0xe7;
      73                 :   static const unsigned char BLOCKGROUP_ID = 0xa0;
      74                 :   static const unsigned char BLOCK_ID = 0xa1;
      75                 :   static const unsigned char SIMPLEBLOCK_ID = 0xa3;
      76                 : 
      77               0 :   const unsigned char* p = aBuffer;
      78                 : 
      79                 :   // Parse each byte in aBuffer one-by-one, producing timecodes and updating
      80                 :   // aMapping as we go.  Parser pauses at end of stream (which may be at any
      81                 :   // point within the parse) and resumes parsing the next time Append is
      82                 :   // called with new data.
      83               0 :   while (p < aBuffer + aLength) {
      84               0 :     switch (mState) {
      85                 :     case CLUSTER_SYNC:
      86               0 :       if (*p++ == CLUSTER_ID[mClusterIDPos]) {
      87               0 :         mClusterIDPos += 1;
      88                 :       } else {
      89               0 :         mClusterIDPos = 0;
      90                 :       }
      91                 :       // Cluster ID found, it's likely this is a valid sync point.  If this
      92                 :       // is a spurious match, the later parse steps will encounter an error
      93                 :       // and return to CLUSTER_SYNC.
      94               0 :       if (mClusterIDPos == sizeof(CLUSTER_ID)) {
      95               0 :         mClusterIDPos = 0;
      96               0 :         mState = READ_VINT;
      97               0 :         mNextState = TIMECODE_SYNC;
      98                 :       }
      99               0 :       break;
     100                 :     case READ_VINT: {
     101               0 :       unsigned char c = *p++;
     102                 :       PRUint32 mask;
     103               0 :       mVIntLength = VIntLength(c, &mask);
     104               0 :       mVIntLeft = mVIntLength - 1;
     105               0 :       mVInt = c & ~mask;
     106               0 :       mState = READ_VINT_REST;
     107               0 :       break;
     108                 :     }
     109                 :     case READ_VINT_REST:
     110               0 :       if (mVIntLeft) {
     111               0 :         mVInt <<= 8;
     112               0 :         mVInt |= *p++;
     113               0 :         mVIntLeft -= 1;
     114                 :       } else {
     115               0 :         mState = mNextState;
     116                 :       }
     117               0 :       break;
     118                 :     case TIMECODE_SYNC:
     119               0 :       if (*p++ != TIMECODE_ID) {
     120               0 :         p -= 1;
     121               0 :         mState = CLUSTER_SYNC;
     122               0 :         break;
     123                 :       }
     124               0 :       mClusterTimecode = 0;
     125               0 :       mState = READ_VINT;
     126               0 :       mNextState = READ_CLUSTER_TIMECODE;
     127               0 :       break;
     128                 :     case READ_CLUSTER_TIMECODE:
     129               0 :       if (mVInt) {
     130               0 :         mClusterTimecode <<= 8;
     131               0 :         mClusterTimecode |= *p++;
     132               0 :         mVInt -= 1;
     133                 :       } else {
     134               0 :         mState = ANY_BLOCK_SYNC;
     135                 :       }
     136               0 :       break;
     137                 :     case ANY_BLOCK_SYNC: {
     138               0 :       unsigned char c = *p++;
     139               0 :       if (c == BLOCKGROUP_ID) {
     140               0 :         mState = READ_VINT;
     141               0 :         mNextState = ANY_BLOCK_SYNC;
     142               0 :       } else if (c == SIMPLEBLOCK_ID || c == BLOCK_ID) {
     143               0 :         mBlockOffset = mCurrentOffset + (p - aBuffer) - 1;
     144               0 :         mState = READ_VINT;
     145               0 :         mNextState = READ_BLOCK;
     146                 :       } else {
     147               0 :         PRUint32 length = VIntLength(c, nsnull);
     148               0 :         if (length == 4) {
     149               0 :           p -= 1;
     150               0 :           mState = CLUSTER_SYNC;
     151                 :         } else {
     152               0 :           mState = READ_VINT;
     153               0 :           mNextState = SKIP_ELEMENT;
     154                 :         }
     155                 :       }
     156               0 :       break;
     157                 :     }
     158                 :     case READ_BLOCK:
     159               0 :       mBlockSize = mVInt;
     160               0 :       mBlockTimecode = 0;
     161               0 :       mBlockTimecodeLength = 2;
     162               0 :       mState = READ_VINT;
     163               0 :       mNextState = READ_BLOCK_TIMECODE;
     164               0 :       break;
     165                 :     case READ_BLOCK_TIMECODE:
     166               0 :       if (mBlockTimecodeLength) {
     167               0 :         mBlockTimecode <<= 8;
     168               0 :         mBlockTimecode |= *p++;
     169               0 :         mBlockTimecodeLength -= 1;
     170                 :       } else {
     171                 :         // It's possible we've parsed this data before, so avoid inserting
     172                 :         // duplicate nsWebMTimeDataOffset entries.
     173                 :         {
     174               0 :           ReentrantMonitorAutoEnter mon(aReentrantMonitor);
     175                 :           PRUint32 idx;
     176               0 :           if (!aMapping.GreatestIndexLtEq(mBlockOffset, idx)) {
     177               0 :             nsWebMTimeDataOffset entry(mBlockOffset, mClusterTimecode + mBlockTimecode);
     178               0 :             aMapping.InsertElementAt(idx, entry);
     179                 :           }
     180                 :         }
     181                 : 
     182                 :         // Skip rest of block header and the block's payload.
     183               0 :         mBlockSize -= mVIntLength;
     184               0 :         mBlockSize -= 2;
     185               0 :         mSkipBytes = PRUint32(mBlockSize);
     186               0 :         mState = SKIP_DATA;
     187               0 :         mNextState = ANY_BLOCK_SYNC;
     188                 :       }
     189               0 :       break;
     190                 :     case SKIP_DATA:
     191               0 :       if (mSkipBytes) {
     192               0 :         PRUint32 left = aLength - (p - aBuffer);
     193               0 :         left = NS_MIN(left, mSkipBytes);
     194               0 :         p += left;
     195               0 :         mSkipBytes -= left;
     196                 :       } else {
     197               0 :         mState = mNextState;
     198                 :       }
     199               0 :       break;
     200                 :     case SKIP_ELEMENT:
     201               0 :       mSkipBytes = PRUint32(mVInt);
     202               0 :       mState = SKIP_DATA;
     203               0 :       mNextState = ANY_BLOCK_SYNC;
     204               0 :       break;
     205                 :     }
     206                 :   }
     207                 : 
     208               0 :   NS_ASSERTION(p == aBuffer + aLength, "Must have parsed to end of data.");
     209               0 :   mCurrentOffset += aLength;
     210               0 : }
     211                 : 
     212               0 : void nsWebMBufferedState::CalculateBufferedForRange(nsTimeRanges* aBuffered,
     213                 :                                                     PRInt64 aStartOffset, PRInt64 aEndOffset,
     214                 :                                                     PRUint64 aTimecodeScale,
     215                 :                                                     PRInt64 aStartTimeOffsetNS)
     216                 : {
     217               0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     218                 : 
     219                 :   // Find the first nsWebMTimeDataOffset at or after aStartOffset.
     220                 :   PRUint32 start;
     221               0 :   mTimeMapping.GreatestIndexLtEq(aStartOffset, start);
     222               0 :   if (start == mTimeMapping.Length()) {
     223                 :     return;
     224                 :   }
     225                 : 
     226                 :   // Find the first nsWebMTimeDataOffset at or before aEndOffset.
     227                 :   PRUint32 end;
     228               0 :   if (!mTimeMapping.GreatestIndexLtEq(aEndOffset, end) && end > 0) {
     229                 :     // No exact match, so adjust end to be the first entry before
     230                 :     // aEndOffset.
     231               0 :     end -= 1;
     232                 :   }
     233                 : 
     234                 :   // Range is empty.
     235               0 :   if (end <= start) {
     236                 :     return;
     237                 :   }
     238                 : 
     239               0 :   NS_ASSERTION(mTimeMapping[start].mOffset >= aStartOffset &&
     240                 :                mTimeMapping[end].mOffset <= aEndOffset,
     241                 :                "Computed time range must lie within data range.");
     242               0 :   if (start > 0) {
     243               0 :     NS_ASSERTION(mTimeMapping[start - 1].mOffset <= aStartOffset,
     244                 :                  "Must have found least nsWebMTimeDataOffset for start");
     245                 :   }
     246               0 :   if (end < mTimeMapping.Length() - 1) {
     247               0 :     NS_ASSERTION(mTimeMapping[end + 1].mOffset >= aEndOffset,
     248                 :                  "Must have found greatest nsWebMTimeDataOffset for end");
     249                 :   }
     250                 : 
     251                 :   // The timestamp of the first media sample, in ns. We must subtract this
     252                 :   // from the ranges' start and end timestamps, so that those timestamps are
     253                 :   // normalized in the range [0,duration].
     254                 : 
     255               0 :   double startTime = (mTimeMapping[start].mTimecode * aTimecodeScale - aStartTimeOffsetNS) / NS_PER_S;
     256               0 :   double endTime = (mTimeMapping[end].mTimecode * aTimecodeScale - aStartTimeOffsetNS) / NS_PER_S;
     257               0 :   aBuffered->Add(startTime, endTime);
     258                 : }
     259                 : 
     260               0 : void nsWebMBufferedState::NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRInt64 aOffset)
     261                 : {
     262               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     263                 :   PRUint32 idx;
     264               0 :   if (!mRangeParsers.GreatestIndexLtEq(aOffset, idx)) {
     265                 :     // If the incoming data overlaps an already parsed range, adjust the
     266                 :     // buffer so that we only reparse the new data.  It's also possible to
     267                 :     // have an overlap where the end of the incoming data is within an
     268                 :     // already parsed range, but we don't bother handling that other than by
     269                 :     // avoiding storing duplicate timecodes when the parser runs.
     270               0 :     if (idx != mRangeParsers.Length() && mRangeParsers[idx].mStartOffset <= aOffset) {
     271                 :       // Complete overlap, skip parsing.
     272               0 :       if (aOffset + aLength <= mRangeParsers[idx].mCurrentOffset) {
     273               0 :         return;
     274                 :       }
     275                 : 
     276                 :       // Partial overlap, adjust the buffer to parse only the new data.
     277               0 :       PRInt64 adjust = mRangeParsers[idx].mCurrentOffset - aOffset;
     278               0 :       NS_ASSERTION(adjust >= 0, "Overlap detection bug.");
     279               0 :       aBuffer += adjust;
     280               0 :       aLength -= PRUint32(adjust);
     281                 :     } else {
     282               0 :       mRangeParsers.InsertElementAt(idx, nsWebMBufferedParser(aOffset));
     283                 :     }
     284                 :   }
     285                 : 
     286               0 :   mRangeParsers[idx].Append(reinterpret_cast<const unsigned char*>(aBuffer),
     287                 :                             aLength,
     288                 :                             mTimeMapping,
     289               0 :                             mReentrantMonitor);
     290                 : 
     291                 :   // Merge parsers with overlapping regions and clean up the remnants.
     292               0 :   PRUint32 i = 0;
     293               0 :   while (i + 1 < mRangeParsers.Length()) {
     294               0 :     if (mRangeParsers[i].mCurrentOffset >= mRangeParsers[i + 1].mStartOffset) {
     295               0 :       mRangeParsers[i + 1].mStartOffset = mRangeParsers[i].mStartOffset;
     296               0 :       mRangeParsers.RemoveElementAt(i);
     297                 :     } else {
     298               0 :       i += 1;
     299                 :     }
     300                 :   }
     301                 : }

Generated by: LCOV version 1.7