LCOV - app.info - content/media/webm/nsWebMBufferedParser.cpp

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-04-21 | 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