void WebMBufferedState::NotifyDataArrived(const unsigned char* aBuffer, uint32_t aLength, int64_t aOffset) { uint32_t idx = mRangeParsers.IndexOfFirstElementGt(aOffset - 1); if (idx == 0 || !(mRangeParsers[idx-1] == aOffset)) { // If the incoming data overlaps an already parsed range, adjust the // buffer so that we only reparse the new data. It's also possible to // have an overlap where the end of the incoming data is within an // already parsed range, but we don't bother handling that other than by // avoiding storing duplicate timecodes when the parser runs. if (idx != mRangeParsers.Length() && mRangeParsers[idx].mStartOffset <= aOffset) { // Complete overlap, skip parsing. if (aOffset + aLength <= mRangeParsers[idx].mCurrentOffset) { return; } // Partial overlap, adjust the buffer to parse only the new data. int64_t adjust = mRangeParsers[idx].mCurrentOffset - aOffset; NS_ASSERTION(adjust >= 0, "Overlap detection bug."); aBuffer += adjust; aLength -= uint32_t(adjust); } else { mRangeParsers.InsertElementAt(idx, WebMBufferedParser(aOffset)); if (idx != 0) { mRangeParsers[idx].SetTimecodeScale(mRangeParsers[0].GetTimecodeScale()); } } } mRangeParsers[idx].Append(aBuffer, aLength, mTimeMapping, mReentrantMonitor); // Merge parsers with overlapping regions and clean up the remnants. uint32_t i = 0; while (i + 1 < mRangeParsers.Length()) { if (mRangeParsers[i].mCurrentOffset >= mRangeParsers[i + 1].mStartOffset) { mRangeParsers[i + 1].mStartOffset = mRangeParsers[i].mStartOffset; mRangeParsers[i + 1].mInitEndOffset = mRangeParsers[i].mInitEndOffset; mRangeParsers.RemoveElementAt(i); } else { i += 1; } } if (mRangeParsers.IsEmpty()) { return; } ReentrantMonitorAutoEnter mon(mReentrantMonitor); mLastBlockOffset = mRangeParsers.LastElement().mBlockEndOffset; }
void WebMBufferedState::UpdateIndex(const MediaByteRangeSet& aRanges, MediaResource* aResource) { for (uint32_t index = 0; index < aRanges.Length(); index++) { const MediaByteRange& range = aRanges[index]; int64_t offset = range.mStart; uint32_t length = range.mEnd - range.mStart; uint32_t idx = mRangeParsers.IndexOfFirstElementGt(offset - 1); if (!idx || !(mRangeParsers[idx-1] == offset)) { // If the incoming data overlaps an already parsed range, adjust the // buffer so that we only reparse the new data. It's also possible to // have an overlap where the end of the incoming data is within an // already parsed range, but we don't bother handling that other than by // avoiding storing duplicate timecodes when the parser runs. if (idx != mRangeParsers.Length() && mRangeParsers[idx].mStartOffset <= offset) { // Complete overlap, skip parsing. if (offset + length <= mRangeParsers[idx].mCurrentOffset) { continue; } // Partial overlap, adjust the buffer to parse only the new data. int64_t adjust = mRangeParsers[idx].mCurrentOffset - offset; NS_ASSERTION(adjust >= 0, "Overlap detection bug."); offset += adjust; length -= uint32_t(adjust); } else { mRangeParsers.InsertElementAt(idx, WebMBufferedParser(offset)); if (idx) { mRangeParsers[idx].SetTimecodeScale(mRangeParsers[0].GetTimecodeScale()); } } } MediaResourceIndex res(aResource); while (length > 0) { static const uint32_t BLOCK_SIZE = 1048576; uint32_t block = std::min(length, BLOCK_SIZE); RefPtr<MediaByteBuffer> bytes = res.CachedMediaReadAt(offset, block); if (!bytes) { break; } NotifyDataArrived(bytes->Elements(), bytes->Length(), offset); length -= bytes->Length(); offset += bytes->Length(); } } }
bool ParseStartAndEndTimestamps(MediaLargeByteBuffer* aData, int64_t& aStart, int64_t& aEnd) { bool initSegment = IsInitSegmentPresent(aData); if (initSegment) { mOffset = 0; mParser = WebMBufferedParser(0); mOverlappedMapping.Clear(); mInitData = new MediaLargeByteBuffer(); mResource = new SourceBufferResource(NS_LITERAL_CSTRING("video/webm")); } // XXX if it only adds new mappings, overlapped but not available // (e.g. overlap < 0) frames are "lost" from the reported mappings here. nsTArray<WebMTimeDataOffset> mapping; mapping.AppendElements(mOverlappedMapping); mOverlappedMapping.Clear(); ReentrantMonitor dummy("dummy"); mParser.Append(aData->Elements(), aData->Length(), mapping, dummy); if (mResource) { mResource->AppendData(aData); } // XXX This is a bit of a hack. Assume if there are no timecodes // present and it's an init segment that it's _just_ an init segment. // We should be more precise. if (initSegment || !HasCompleteInitData()) { if (mParser.mInitEndOffset > 0) { MOZ_ASSERT(mParser.mInitEndOffset <= mResource->GetLength()); if (!mInitData->SetLength(mParser.mInitEndOffset)) { // Super unlikely OOM return false; } char* buffer = reinterpret_cast<char*>(mInitData->Elements()); mResource->ReadFromCache(buffer, 0, mParser.mInitEndOffset); MSE_DEBUG(WebMContainerParser, "Stashed init of %u bytes.", mParser.mInitEndOffset); mResource = nullptr; } else { MSE_DEBUG(WebMContainerParser, "Incomplete init found."); } mHasInitData = true; } mOffset += aData->Length(); if (mapping.IsEmpty()) { return false; } // Exclude frames that we don't enough data to cover the end of. uint32_t endIdx = mapping.Length() - 1; while (mOffset < mapping[endIdx].mEndOffset && endIdx > 0) { endIdx -= 1; } if (endIdx == 0) { return false; } uint64_t frameDuration = mapping[endIdx].mTimecode - mapping[endIdx - 1].mTimecode; aStart = mapping[0].mTimecode / NS_PER_USEC; aEnd = (mapping[endIdx].mTimecode + frameDuration) / NS_PER_USEC; MSE_DEBUG(WebMContainerParser, "[%lld, %lld] [fso=%lld, leo=%lld, l=%u endIdx=%u]", aStart, aEnd, mapping[0].mSyncOffset, mapping[endIdx].mEndOffset, mapping.Length(), endIdx); mapping.RemoveElementsAt(0, endIdx + 1); mOverlappedMapping.AppendElements(mapping); return true; }