LCOV - code coverage report
Current view: top level - dom/media/webm - WebMBufferedParser.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 275 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 16 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "nsAlgorithm.h"
       8             : #include "WebMBufferedParser.h"
       9             : #include "nsThreadUtils.h"
      10             : #include <algorithm>
      11             : 
      12             : extern mozilla::LazyLogModule gMediaDemuxerLog;
      13             : #define WEBM_DEBUG(arg, ...) MOZ_LOG(gMediaDemuxerLog, mozilla::LogLevel::Debug, ("WebMBufferedParser(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
      14             : 
      15             : namespace mozilla {
      16             : 
      17             : static uint32_t
      18           0 : VIntLength(unsigned char aFirstByte, uint32_t* aMask)
      19             : {
      20           0 :   uint32_t count = 1;
      21           0 :   uint32_t mask = 1 << 7;
      22           0 :   while (count < 8) {
      23           0 :     if ((aFirstByte & mask) != 0) {
      24           0 :       break;
      25             :     }
      26           0 :     mask >>= 1;
      27           0 :     count += 1;
      28             :   }
      29           0 :   if (aMask) {
      30           0 :     *aMask = mask;
      31             :   }
      32           0 :   NS_ASSERTION(count >= 1 && count <= 8, "Insane VInt length.");
      33           0 :   return count;
      34             : }
      35             : 
      36           0 : bool WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
      37             :                                 nsTArray<WebMTimeDataOffset>& aMapping,
      38             :                                 ReentrantMonitor& aReentrantMonitor)
      39             : {
      40             :   static const uint32_t EBML_ID = 0x1a45dfa3;
      41             :   static const uint32_t SEGMENT_ID = 0x18538067;
      42             :   static const uint32_t SEGINFO_ID = 0x1549a966;
      43             :   static const uint32_t TRACKS_ID = 0x1654AE6B;
      44             :   static const uint32_t CLUSTER_ID = 0x1f43b675;
      45             :   static const uint32_t TIMECODESCALE_ID = 0x2ad7b1;
      46             :   static const unsigned char TIMECODE_ID = 0xe7;
      47             :   static const unsigned char BLOCKGROUP_ID = 0xa0;
      48             :   static const unsigned char BLOCK_ID = 0xa1;
      49             :   static const unsigned char SIMPLEBLOCK_ID = 0xa3;
      50             :   static const uint32_t BLOCK_TIMECODE_LENGTH = 2;
      51             : 
      52             :   static const unsigned char CLUSTER_SYNC_ID[] = { 0x1f, 0x43, 0xb6, 0x75 };
      53             : 
      54           0 :   const unsigned char* p = aBuffer;
      55             : 
      56             :   // Parse each byte in aBuffer one-by-one, producing timecodes and updating
      57             :   // aMapping as we go.  Parser pauses at end of stream (which may be at any
      58             :   // point within the parse) and resumes parsing the next time Append is
      59             :   // called with new data.
      60           0 :   while (p < aBuffer + aLength) {
      61           0 :     switch (mState) {
      62             :     case READ_ELEMENT_ID:
      63           0 :       mVIntRaw = true;
      64           0 :       mState = READ_VINT;
      65           0 :       mNextState = READ_ELEMENT_SIZE;
      66           0 :       break;
      67             :     case READ_ELEMENT_SIZE:
      68           0 :       mVIntRaw = false;
      69           0 :       mElement.mID = mVInt;
      70           0 :       mState = READ_VINT;
      71           0 :       mNextState = PARSE_ELEMENT;
      72           0 :       break;
      73             :     case FIND_CLUSTER_SYNC:
      74           0 :       if (*p++ == CLUSTER_SYNC_ID[mClusterSyncPos]) {
      75           0 :         mClusterSyncPos += 1;
      76             :       } else {
      77           0 :         mClusterSyncPos = 0;
      78             :       }
      79           0 :       if (mClusterSyncPos == sizeof(CLUSTER_SYNC_ID)) {
      80           0 :         mVInt.mValue = CLUSTER_ID;
      81           0 :         mVInt.mLength = sizeof(CLUSTER_SYNC_ID);
      82           0 :         mState = READ_ELEMENT_SIZE;
      83             :       }
      84           0 :       break;
      85             :     case PARSE_ELEMENT:
      86           0 :       mElement.mSize = mVInt;
      87           0 :       switch (mElement.mID.mValue) {
      88             :       case SEGMENT_ID:
      89           0 :         mState = READ_ELEMENT_ID;
      90           0 :         break;
      91             :       case SEGINFO_ID:
      92           0 :         mGotTimecodeScale = true;
      93           0 :         mState = READ_ELEMENT_ID;
      94           0 :         break;
      95             :       case TIMECODE_ID:
      96           0 :         mVInt = VInt();
      97           0 :         mVIntLeft = mElement.mSize.mValue;
      98           0 :         mState = READ_VINT_REST;
      99           0 :         mNextState = READ_CLUSTER_TIMECODE;
     100           0 :         break;
     101             :       case TIMECODESCALE_ID:
     102           0 :         mVInt = VInt();
     103           0 :         mVIntLeft = mElement.mSize.mValue;
     104           0 :         mState = READ_VINT_REST;
     105           0 :         mNextState = READ_TIMECODESCALE;
     106           0 :         break;
     107             :       case CLUSTER_ID:
     108           0 :         mClusterOffset = mCurrentOffset + (p - aBuffer) -
     109           0 :                         (mElement.mID.mLength + mElement.mSize.mLength);
     110             :         // Handle "unknown" length;
     111           0 :         if (mElement.mSize.mValue + 1 != uint64_t(1) << (mElement.mSize.mLength * 7)) {
     112           0 :           mClusterEndOffset = mClusterOffset + mElement.mID.mLength + mElement.mSize.mLength + mElement.mSize.mValue;
     113             :         } else {
     114           0 :           mClusterEndOffset = -1;
     115             :         }
     116           0 :         mState = READ_ELEMENT_ID;
     117           0 :         break;
     118             :       case BLOCKGROUP_ID:
     119           0 :         mState = READ_ELEMENT_ID;
     120           0 :         break;
     121             :       case SIMPLEBLOCK_ID:
     122             :         /* FALLTHROUGH */
     123             :       case BLOCK_ID:
     124           0 :         mBlockSize = mElement.mSize.mValue;
     125           0 :         mBlockTimecode = 0;
     126           0 :         mBlockTimecodeLength = BLOCK_TIMECODE_LENGTH;
     127           0 :         mBlockOffset = mCurrentOffset + (p - aBuffer) -
     128           0 :                        (mElement.mID.mLength + mElement.mSize.mLength);
     129           0 :         mState = READ_VINT;
     130           0 :         mNextState = READ_BLOCK_TIMECODE;
     131           0 :         break;
     132             :       case TRACKS_ID:
     133           0 :         mSkipBytes = mElement.mSize.mValue;
     134           0 :         mState = CHECK_INIT_FOUND;
     135           0 :         break;
     136             :       case EBML_ID:
     137           0 :         mLastInitStartOffset = mCurrentOffset + (p - aBuffer) -
     138           0 :                             (mElement.mID.mLength + mElement.mSize.mLength);
     139             :         MOZ_FALLTHROUGH;
     140             :       default:
     141           0 :         mSkipBytes = mElement.mSize.mValue;
     142           0 :         mState = SKIP_DATA;
     143           0 :         mNextState = READ_ELEMENT_ID;
     144           0 :         break;
     145             :       }
     146           0 :       break;
     147             :     case READ_VINT: {
     148           0 :       unsigned char c = *p++;
     149             :       uint32_t mask;
     150           0 :       mVInt.mLength = VIntLength(c, &mask);
     151           0 :       mVIntLeft = mVInt.mLength - 1;
     152           0 :       mVInt.mValue = mVIntRaw ? c : c & ~mask;
     153           0 :       mState = READ_VINT_REST;
     154           0 :       break;
     155             :     }
     156             :     case READ_VINT_REST:
     157           0 :       if (mVIntLeft) {
     158           0 :         mVInt.mValue <<= 8;
     159           0 :         mVInt.mValue |= *p++;
     160           0 :         mVIntLeft -= 1;
     161             :       } else {
     162           0 :         mState = mNextState;
     163             :       }
     164           0 :       break;
     165             :     case READ_TIMECODESCALE:
     166           0 :       if (!mGotTimecodeScale) {
     167           0 :         return false;
     168             :       }
     169           0 :       mTimecodeScale = mVInt.mValue;
     170           0 :       mState = READ_ELEMENT_ID;
     171           0 :       break;
     172             :     case READ_CLUSTER_TIMECODE:
     173           0 :       mClusterTimecode = mVInt.mValue;
     174           0 :       mState = READ_ELEMENT_ID;
     175           0 :       break;
     176             :     case READ_BLOCK_TIMECODE:
     177           0 :       if (mBlockTimecodeLength) {
     178           0 :         mBlockTimecode <<= 8;
     179           0 :         mBlockTimecode |= *p++;
     180           0 :         mBlockTimecodeLength -= 1;
     181             :       } else {
     182             :         // It's possible we've parsed this data before, so avoid inserting
     183             :         // duplicate WebMTimeDataOffset entries.
     184             :         {
     185           0 :           ReentrantMonitorAutoEnter mon(aReentrantMonitor);
     186           0 :           int64_t endOffset = mBlockOffset + mBlockSize +
     187           0 :                               mElement.mID.mLength + mElement.mSize.mLength;
     188           0 :           uint32_t idx = aMapping.IndexOfFirstElementGt(endOffset);
     189           0 :           if (idx == 0 || aMapping[idx - 1] != endOffset) {
     190             :             // Don't insert invalid negative timecodes.
     191           0 :             if (mBlockTimecode >= 0 || mClusterTimecode >= uint16_t(abs(mBlockTimecode))) {
     192           0 :               if (!mGotTimecodeScale) {
     193           0 :                 return false;
     194             :               }
     195           0 :               uint64_t absTimecode = mClusterTimecode + mBlockTimecode;
     196           0 :               absTimecode *= mTimecodeScale;
     197             :               // Avoid creating an entry if the timecode is out of order
     198             :               // (invalid according to the WebM specification) so that
     199             :               // ordering invariants of aMapping are not violated.
     200           0 :               if (idx == 0 ||
     201           0 :                   aMapping[idx - 1].mTimecode <= absTimecode ||
     202           0 :                   (idx + 1 < aMapping.Length() &&
     203           0 :                    aMapping[idx + 1].mTimecode >= absTimecode)) {
     204             :                 WebMTimeDataOffset entry(endOffset, absTimecode, mLastInitStartOffset,
     205           0 :                                          mClusterOffset, mClusterEndOffset);
     206           0 :                 aMapping.InsertElementAt(idx, entry);
     207             :               } else {
     208           0 :                 WEBM_DEBUG("Out of order timecode %" PRIu64 " in Cluster at %" PRId64 " ignored",
     209             :                            absTimecode, mClusterOffset);
     210             :               }
     211             :             }
     212             :           }
     213             :         }
     214             : 
     215             :         // Skip rest of block header and the block's payload.
     216           0 :         mBlockSize -= mVInt.mLength;
     217           0 :         mBlockSize -= BLOCK_TIMECODE_LENGTH;
     218           0 :         mSkipBytes = uint32_t(mBlockSize);
     219           0 :         mState = SKIP_DATA;
     220           0 :         mNextState = READ_ELEMENT_ID;
     221             :       }
     222           0 :       break;
     223             :     case SKIP_DATA:
     224           0 :       if (mSkipBytes) {
     225           0 :         uint32_t left = aLength - (p - aBuffer);
     226           0 :         left = std::min(left, mSkipBytes);
     227           0 :         p += left;
     228           0 :         mSkipBytes -= left;
     229             :       }
     230           0 :       if (!mSkipBytes) {
     231           0 :         mBlockEndOffset = mCurrentOffset + (p - aBuffer);
     232           0 :         mState = mNextState;
     233             :       }
     234           0 :       break;
     235             :     case CHECK_INIT_FOUND:
     236           0 :       if (mSkipBytes) {
     237           0 :         uint32_t left = aLength - (p - aBuffer);
     238           0 :         left = std::min(left, mSkipBytes);
     239           0 :         p += left;
     240           0 :         mSkipBytes -= left;
     241             :       }
     242           0 :       if (!mSkipBytes) {
     243           0 :         if (mInitEndOffset < 0) {
     244           0 :           mInitEndOffset = mCurrentOffset + (p - aBuffer);
     245           0 :           mBlockEndOffset = mCurrentOffset + (p - aBuffer);
     246             :         }
     247           0 :         mState = READ_ELEMENT_ID;
     248             :       }
     249           0 :       break;
     250             :     }
     251             :   }
     252             : 
     253           0 :   NS_ASSERTION(p == aBuffer + aLength, "Must have parsed to end of data.");
     254           0 :   mCurrentOffset += aLength;
     255             : 
     256           0 :   return true;
     257             : }
     258             : 
     259             : int64_t
     260           0 : WebMBufferedParser::EndSegmentOffset(int64_t aOffset)
     261             : {
     262           0 :   if (mLastInitStartOffset > aOffset || mClusterOffset > aOffset) {
     263           0 :     return std::min(mLastInitStartOffset >= 0 ? mLastInitStartOffset : INT64_MAX,
     264           0 :                     mClusterOffset >= 0 ? mClusterOffset : INT64_MAX);
     265             :   }
     266           0 :   return mBlockEndOffset;
     267             : }
     268             : 
     269             : // SyncOffsetComparator and TimeComparator are slightly confusing, in that
     270             : // the nsTArray they're used with (mTimeMapping) is sorted by mEndOffset and
     271             : // these comparators are used on the other fields of WebMTimeDataOffset.
     272             : // This is only valid because timecodes are required to be monotonically
     273             : // increasing within a file (thus establishing an ordering relationship with
     274             : // mTimecode), and mEndOffset is derived from mSyncOffset.
     275             : struct SyncOffsetComparator {
     276           0 :   bool Equals(const WebMTimeDataOffset& a, const int64_t& b) const {
     277           0 :     return a.mSyncOffset == b;
     278             :   }
     279             : 
     280           0 :   bool LessThan(const WebMTimeDataOffset& a, const int64_t& b) const {
     281           0 :     return a.mSyncOffset < b;
     282             :   }
     283             : };
     284             : 
     285             : struct TimeComparator {
     286           0 :   bool Equals(const WebMTimeDataOffset& a, const uint64_t& b) const {
     287           0 :     return a.mTimecode == b;
     288             :   }
     289             : 
     290           0 :   bool LessThan(const WebMTimeDataOffset& a, const uint64_t& b) const {
     291           0 :     return a.mTimecode < b;
     292             :   }
     293             : };
     294             : 
     295           0 : bool WebMBufferedState::CalculateBufferedForRange(int64_t aStartOffset, int64_t aEndOffset,
     296             :                                                   uint64_t* aStartTime, uint64_t* aEndTime)
     297             : {
     298           0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     299             : 
     300             :   // Find the first WebMTimeDataOffset at or after aStartOffset.
     301           0 :   uint32_t start = mTimeMapping.IndexOfFirstElementGt(aStartOffset - 1, SyncOffsetComparator());
     302           0 :   if (start == mTimeMapping.Length()) {
     303           0 :     return false;
     304             :   }
     305             : 
     306             :   // Find the first WebMTimeDataOffset at or before aEndOffset.
     307           0 :   uint32_t end = mTimeMapping.IndexOfFirstElementGt(aEndOffset);
     308           0 :   if (end > 0) {
     309           0 :     end -= 1;
     310             :   }
     311             : 
     312             :   // Range is empty.
     313           0 :   if (end <= start) {
     314           0 :     return false;
     315             :   }
     316             : 
     317           0 :   NS_ASSERTION(mTimeMapping[start].mSyncOffset >= aStartOffset &&
     318             :                mTimeMapping[end].mEndOffset <= aEndOffset,
     319             :                "Computed time range must lie within data range.");
     320           0 :   if (start > 0) {
     321           0 :     NS_ASSERTION(mTimeMapping[start - 1].mSyncOffset < aStartOffset,
     322             :                  "Must have found least WebMTimeDataOffset for start");
     323             :   }
     324           0 :   if (end < mTimeMapping.Length() - 1) {
     325           0 :     NS_ASSERTION(mTimeMapping[end + 1].mEndOffset > aEndOffset,
     326             :                  "Must have found greatest WebMTimeDataOffset for end");
     327             :   }
     328             : 
     329           0 :   MOZ_ASSERT(mTimeMapping[end].mTimecode >= mTimeMapping[end - 1].mTimecode);
     330           0 :   uint64_t frameDuration = mTimeMapping[end].mTimecode - mTimeMapping[end - 1].mTimecode;
     331           0 :   *aStartTime = mTimeMapping[start].mTimecode;
     332           0 :   *aEndTime = mTimeMapping[end].mTimecode + frameDuration;
     333           0 :   return true;
     334             : }
     335             : 
     336           0 : bool WebMBufferedState::GetOffsetForTime(uint64_t aTime, int64_t* aOffset)
     337             : {
     338           0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     339             : 
     340           0 :   if(mTimeMapping.IsEmpty()) {
     341           0 :     return false;
     342             :   }
     343             : 
     344           0 :   uint64_t time = aTime;
     345           0 :   if (time > 0) {
     346           0 :     time = time - 1;
     347             :   }
     348           0 :   uint32_t idx = mTimeMapping.IndexOfFirstElementGt(time, TimeComparator());
     349           0 :   if (idx == mTimeMapping.Length()) {
     350             :     // Clamp to end
     351           0 :     *aOffset = mTimeMapping[mTimeMapping.Length() - 1].mSyncOffset;
     352             :   } else {
     353             :     // Idx is within array or has been clamped to start
     354           0 :     *aOffset = mTimeMapping[idx].mSyncOffset;
     355             :   }
     356           0 :   return true;
     357             : }
     358             : 
     359           0 : void WebMBufferedState::NotifyDataArrived(const unsigned char* aBuffer, uint32_t aLength, int64_t aOffset)
     360             : {
     361           0 :   uint32_t idx = mRangeParsers.IndexOfFirstElementGt(aOffset - 1);
     362           0 :   if (idx == 0 || !(mRangeParsers[idx-1] == aOffset)) {
     363             :     // If the incoming data overlaps an already parsed range, adjust the
     364             :     // buffer so that we only reparse the new data.  It's also possible to
     365             :     // have an overlap where the end of the incoming data is within an
     366             :     // already parsed range, but we don't bother handling that other than by
     367             :     // avoiding storing duplicate timecodes when the parser runs.
     368           0 :     if (idx != mRangeParsers.Length() && mRangeParsers[idx].mStartOffset <= aOffset) {
     369             :       // Complete overlap, skip parsing.
     370           0 :       if (aOffset + aLength <= mRangeParsers[idx].mCurrentOffset) {
     371           0 :         return;
     372             :       }
     373             : 
     374             :       // Partial overlap, adjust the buffer to parse only the new data.
     375           0 :       int64_t adjust = mRangeParsers[idx].mCurrentOffset - aOffset;
     376           0 :       NS_ASSERTION(adjust >= 0, "Overlap detection bug.");
     377           0 :       aBuffer += adjust;
     378           0 :       aLength -= uint32_t(adjust);
     379             :     } else {
     380           0 :       mRangeParsers.InsertElementAt(idx, WebMBufferedParser(aOffset));
     381           0 :       if (idx != 0) {
     382           0 :         mRangeParsers[idx].SetTimecodeScale(mRangeParsers[0].GetTimecodeScale());
     383             :       }
     384             :     }
     385             :   }
     386             : 
     387           0 :   mRangeParsers[idx].Append(aBuffer,
     388             :                             aLength,
     389             :                             mTimeMapping,
     390           0 :                             mReentrantMonitor);
     391             : 
     392             :   // Merge parsers with overlapping regions and clean up the remnants.
     393           0 :   uint32_t i = 0;
     394           0 :   while (i + 1 < mRangeParsers.Length()) {
     395           0 :     if (mRangeParsers[i].mCurrentOffset >= mRangeParsers[i + 1].mStartOffset) {
     396           0 :       mRangeParsers[i + 1].mStartOffset = mRangeParsers[i].mStartOffset;
     397           0 :       mRangeParsers[i + 1].mInitEndOffset = mRangeParsers[i].mInitEndOffset;
     398           0 :       mRangeParsers.RemoveElementAt(i);
     399             :     } else {
     400           0 :       i += 1;
     401             :     }
     402             :   }
     403             : 
     404           0 :   if (mRangeParsers.IsEmpty()) {
     405           0 :     return;
     406             :   }
     407             : 
     408           0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     409           0 :   mLastBlockOffset = mRangeParsers.LastElement().mBlockEndOffset;
     410             : }
     411             : 
     412           0 : void WebMBufferedState::Reset() {
     413           0 :   mRangeParsers.Clear();
     414           0 :   mTimeMapping.Clear();
     415           0 : }
     416             : 
     417           0 : void WebMBufferedState::UpdateIndex(const MediaByteRangeSet& aRanges, MediaResource* aResource)
     418             : {
     419           0 :   for (uint32_t index = 0; index < aRanges.Length(); index++) {
     420           0 :     const MediaByteRange& range = aRanges[index];
     421           0 :     int64_t offset = range.mStart;
     422           0 :     uint32_t length = range.mEnd - range.mStart;
     423             : 
     424           0 :     uint32_t idx = mRangeParsers.IndexOfFirstElementGt(offset - 1);
     425           0 :     if (!idx || !(mRangeParsers[idx-1] == offset)) {
     426             :       // If the incoming data overlaps an already parsed range, adjust the
     427             :       // buffer so that we only reparse the new data.  It's also possible to
     428             :       // have an overlap where the end of the incoming data is within an
     429             :       // already parsed range, but we don't bother handling that other than by
     430             :       // avoiding storing duplicate timecodes when the parser runs.
     431           0 :       if (idx != mRangeParsers.Length() && mRangeParsers[idx].mStartOffset <= offset) {
     432             :         // Complete overlap, skip parsing.
     433           0 :         if (offset + length <= mRangeParsers[idx].mCurrentOffset) {
     434           0 :           continue;
     435             :         }
     436             : 
     437             :         // Partial overlap, adjust the buffer to parse only the new data.
     438           0 :         int64_t adjust = mRangeParsers[idx].mCurrentOffset - offset;
     439           0 :         NS_ASSERTION(adjust >= 0, "Overlap detection bug.");
     440           0 :         offset += adjust;
     441           0 :         length -= uint32_t(adjust);
     442             :       } else {
     443           0 :         mRangeParsers.InsertElementAt(idx, WebMBufferedParser(offset));
     444           0 :         if (idx) {
     445           0 :           mRangeParsers[idx].SetTimecodeScale(mRangeParsers[0].GetTimecodeScale());
     446             :         }
     447             :       }
     448             :     }
     449           0 :     while (length > 0) {
     450             :       static const uint32_t BLOCK_SIZE = 1048576;
     451           0 :       uint32_t block = std::min(length, BLOCK_SIZE);
     452           0 :       RefPtr<MediaByteBuffer> bytes = aResource->CachedReadAt(offset, block);
     453           0 :       if (!bytes) {
     454           0 :         break;
     455             :       }
     456           0 :       NotifyDataArrived(bytes->Elements(), bytes->Length(), offset);
     457           0 :       length -= bytes->Length();
     458           0 :       offset += bytes->Length();
     459             :     }
     460             :   }
     461           0 : }
     462             : 
     463           0 : int64_t WebMBufferedState::GetInitEndOffset()
     464             : {
     465           0 :   if (mRangeParsers.IsEmpty()) {
     466           0 :     return -1;
     467             :   }
     468           0 :   return mRangeParsers[0].mInitEndOffset;
     469             : }
     470             : 
     471           0 : int64_t WebMBufferedState::GetLastBlockOffset()
     472             : {
     473           0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     474             : 
     475           0 :   return mLastBlockOffset;
     476             : }
     477             : 
     478           0 : bool WebMBufferedState::GetStartTime(uint64_t *aTime)
     479             : {
     480           0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     481             : 
     482           0 :   if (mTimeMapping.IsEmpty()) {
     483           0 :     return false;
     484             :   }
     485             : 
     486           0 :   uint32_t idx = mTimeMapping.IndexOfFirstElementGt(0, SyncOffsetComparator());
     487           0 :   if (idx == mTimeMapping.Length()) {
     488           0 :     return false;
     489             :   }
     490             : 
     491           0 :   *aTime = mTimeMapping[idx].mTimecode;
     492           0 :   return true;
     493             : }
     494             : 
     495             : bool
     496           0 : WebMBufferedState::GetNextKeyframeTime(uint64_t aTime, uint64_t* aKeyframeTime)
     497             : {
     498           0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     499           0 :   int64_t offset = 0;
     500           0 :   bool rv = GetOffsetForTime(aTime, &offset);
     501           0 :   if (!rv) {
     502           0 :     return false;
     503             :   }
     504           0 :   uint32_t idx = mTimeMapping.IndexOfFirstElementGt(offset, SyncOffsetComparator());
     505           0 :   if (idx == mTimeMapping.Length()) {
     506           0 :     return false;
     507             :   }
     508           0 :   *aKeyframeTime = mTimeMapping[idx].mTimecode;
     509           0 :   return true;
     510             : }
     511             : } // namespace mozilla
     512             : 
     513             : #undef WEBM_DEBUG
     514             : 

Generated by: LCOV version 1.13