1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include "Index.h"
6
7 #include <algorithm>
8 #include <limits>
9
10 #include "BufferReader.h"
11 #include "mozilla/RefPtr.h"
12 #include "MP4Interval.h"
13 #include "MP4Metadata.h"
14 #include "SinfParser.h"
15
16 using namespace mozilla::media;
17
18 namespace mozilla {
19
20 class MOZ_STACK_CLASS RangeFinder {
21 public:
22 // Given that we're processing this in order we don't use a binary search
23 // to find the apropriate time range. Instead we search linearly from the
24 // last used point.
RangeFinder(const MediaByteRangeSet & ranges)25 explicit RangeFinder(const MediaByteRangeSet& ranges)
26 : mRanges(ranges), mIndex(0) {
27 // Ranges must be normalised for this to work
28 }
29
30 bool Contains(MediaByteRange aByteRange);
31
32 private:
33 const MediaByteRangeSet& mRanges;
34 size_t mIndex;
35 };
36
Contains(MediaByteRange aByteRange)37 bool RangeFinder::Contains(MediaByteRange aByteRange) {
38 if (mRanges.IsEmpty()) {
39 return false;
40 }
41
42 if (mRanges[mIndex].ContainsStrict(aByteRange)) {
43 return true;
44 }
45
46 if (aByteRange.mStart < mRanges[mIndex].mStart) {
47 // Search backwards
48 do {
49 if (!mIndex) {
50 return false;
51 }
52 --mIndex;
53 if (mRanges[mIndex].ContainsStrict(aByteRange)) {
54 return true;
55 }
56 } while (aByteRange.mStart < mRanges[mIndex].mStart);
57
58 return false;
59 }
60
61 while (aByteRange.mEnd > mRanges[mIndex].mEnd) {
62 if (mIndex == mRanges.Length() - 1) {
63 return false;
64 }
65 ++mIndex;
66 if (mRanges[mIndex].ContainsStrict(aByteRange)) {
67 return true;
68 }
69 }
70
71 return false;
72 }
73
SampleIterator(Index * aIndex)74 SampleIterator::SampleIterator(Index* aIndex)
75 : mIndex(aIndex), mCurrentMoof(0), mCurrentSample(0) {
76 mIndex->RegisterIterator(this);
77 }
78
~SampleIterator()79 SampleIterator::~SampleIterator() { mIndex->UnregisterIterator(this); }
80
GetNext()81 already_AddRefed<MediaRawData> SampleIterator::GetNext() {
82 Sample* s(Get());
83 if (!s) {
84 return nullptr;
85 }
86
87 int64_t length = std::numeric_limits<int64_t>::max();
88 mIndex->mSource->Length(&length);
89 if (s->mByteRange.mEnd > length) {
90 // We don't have this complete sample.
91 return nullptr;
92 }
93
94 RefPtr<MediaRawData> sample = new MediaRawData();
95 sample->mTimecode = TimeUnit::FromMicroseconds(s->mDecodeTime);
96 sample->mTime = TimeUnit::FromMicroseconds(s->mCompositionRange.start);
97 sample->mDuration = TimeUnit::FromMicroseconds(s->mCompositionRange.Length());
98 sample->mOffset = s->mByteRange.mStart;
99 sample->mKeyframe = s->mSync;
100
101 UniquePtr<MediaRawDataWriter> writer(sample->CreateWriter());
102 // Do the blocking read
103 if (!writer->SetSize(s->mByteRange.Length())) {
104 return nullptr;
105 }
106
107 size_t bytesRead;
108 if (!mIndex->mSource->ReadAt(sample->mOffset, writer->Data(), sample->Size(),
109 &bytesRead) ||
110 bytesRead != sample->Size()) {
111 return nullptr;
112 }
113
114 MoofParser* moofParser = mIndex->mMoofParser.get();
115 if (!moofParser) {
116 // File is not fragmented, we can't have crypto, just early return.
117 Next();
118 return sample.forget();
119 }
120
121 // We need to check if this moof has init data the CDM expects us to surface.
122 // This should happen when handling the first sample, even if that sample
123 // isn't encrypted (samples later in the moof may be).
124 if (mCurrentSample == 0) {
125 const nsTArray<Moof>& moofs = moofParser->Moofs();
126 const Moof* currentMoof = &moofs[mCurrentMoof];
127 if (!currentMoof->mPsshes.IsEmpty()) {
128 // This Moof contained crypto init data. Report that. We only report
129 // the init data on the Moof's first sample, to avoid reporting it more
130 // than once per Moof.
131 writer->mCrypto.mInitDatas.AppendElements(currentMoof->mPsshes);
132 writer->mCrypto.mInitDataType = u"cenc"_ns;
133 }
134 }
135
136 auto cryptoSchemeResult = GetEncryptionScheme();
137 if (cryptoSchemeResult.isErr()) {
138 // Log the error here in future.
139 return nullptr;
140 }
141 CryptoScheme cryptoScheme = cryptoSchemeResult.unwrap();
142 if (cryptoScheme == CryptoScheme::None) {
143 // No crypto to handle, early return.
144 Next();
145 return sample.forget();
146 }
147
148 writer->mCrypto.mCryptoScheme = cryptoScheme;
149 MOZ_ASSERT(writer->mCrypto.mCryptoScheme != CryptoScheme::None,
150 "Should have early returned if we don't have a crypto scheme!");
151 MOZ_ASSERT(writer->mCrypto.mKeyId.IsEmpty(),
152 "Sample should not already have a key ID");
153 MOZ_ASSERT(writer->mCrypto.mConstantIV.IsEmpty(),
154 "Sample should not already have a constant IV");
155 CencSampleEncryptionInfoEntry* sampleInfo = GetSampleEncryptionEntry();
156 if (sampleInfo) {
157 // Use sample group information if present, this supersedes track level
158 // information.
159 writer->mCrypto.mKeyId.AppendElements(sampleInfo->mKeyId);
160 writer->mCrypto.mIVSize = sampleInfo->mIVSize;
161 writer->mCrypto.mCryptByteBlock = sampleInfo->mCryptByteBlock;
162 writer->mCrypto.mSkipByteBlock = sampleInfo->mSkipByteBlock;
163 writer->mCrypto.mConstantIV.AppendElements(sampleInfo->mConsantIV);
164 } else {
165 // Use the crypto info from track metadata
166 writer->mCrypto.mKeyId.AppendElements(moofParser->mSinf.mDefaultKeyID, 16);
167 writer->mCrypto.mIVSize = moofParser->mSinf.mDefaultIVSize;
168 writer->mCrypto.mCryptByteBlock = moofParser->mSinf.mDefaultCryptByteBlock;
169 writer->mCrypto.mSkipByteBlock = moofParser->mSinf.mDefaultSkipByteBlock;
170 writer->mCrypto.mConstantIV.AppendElements(
171 moofParser->mSinf.mDefaultConstantIV);
172 }
173
174 if ((writer->mCrypto.mIVSize == 0 && writer->mCrypto.mConstantIV.IsEmpty()) ||
175 (writer->mCrypto.mIVSize != 0 && s->mCencRange.IsEmpty())) {
176 // If mIVSize == 0, this indicates that a constant IV is in use, thus we
177 // should have a non empty constant IV. Alternatively if IV size is non
178 // zero, we should have an IV for this sample, which we need to look up
179 // in mCencRange (which must then be non empty). If neither of these are
180 // true we have bad crypto data, so bail.
181 return nullptr;
182 }
183 // Parse auxiliary information if present
184 if (!s->mCencRange.IsEmpty()) {
185 // The size comes from an 8 bit field
186 AutoTArray<uint8_t, 256> cencAuxInfo;
187 cencAuxInfo.SetLength(s->mCencRange.Length());
188 if (!mIndex->mSource->ReadAt(s->mCencRange.mStart, cencAuxInfo.Elements(),
189 cencAuxInfo.Length(), &bytesRead) ||
190 bytesRead != cencAuxInfo.Length()) {
191 return nullptr;
192 }
193 BufferReader reader(cencAuxInfo);
194 if (!reader.ReadArray(writer->mCrypto.mIV, writer->mCrypto.mIVSize)) {
195 return nullptr;
196 }
197
198 // Parse the auxiliary information for subsample information
199 auto res = reader.ReadU16();
200 if (res.isOk() && res.unwrap() > 0) {
201 uint16_t count = res.unwrap();
202
203 if (reader.Remaining() < count * 6) {
204 return nullptr;
205 }
206
207 for (size_t i = 0; i < count; i++) {
208 auto res_16 = reader.ReadU16();
209 auto res_32 = reader.ReadU32();
210 if (res_16.isErr() || res_32.isErr()) {
211 return nullptr;
212 }
213 writer->mCrypto.mPlainSizes.AppendElement(res_16.unwrap());
214 writer->mCrypto.mEncryptedSizes.AppendElement(res_32.unwrap());
215 }
216 } else {
217 // No subsample information means the entire sample is encrypted.
218 writer->mCrypto.mPlainSizes.AppendElement(0);
219 writer->mCrypto.mEncryptedSizes.AppendElement(sample->Size());
220 }
221 }
222
223 Next();
224
225 return sample.forget();
226 }
227
GetSampleDescriptionEntry()228 SampleDescriptionEntry* SampleIterator::GetSampleDescriptionEntry() {
229 nsTArray<Moof>& moofs = mIndex->mMoofParser->Moofs();
230 Moof& currentMoof = moofs[mCurrentMoof];
231 uint32_t sampleDescriptionIndex =
232 currentMoof.mTfhd.mDefaultSampleDescriptionIndex;
233 // Mp4 indices start at 1, shift down 1 so we index our array correctly.
234 sampleDescriptionIndex--;
235 FallibleTArray<SampleDescriptionEntry>& sampleDescriptions =
236 mIndex->mMoofParser->mSampleDescriptions;
237 if (sampleDescriptionIndex >= sampleDescriptions.Length()) {
238 // The sample description index is invalid, the mp4 is malformed. Bail out.
239 return nullptr;
240 }
241 return &sampleDescriptions[sampleDescriptionIndex];
242 }
243
GetSampleEncryptionEntry()244 CencSampleEncryptionInfoEntry* SampleIterator::GetSampleEncryptionEntry() {
245 nsTArray<Moof>& moofs = mIndex->mMoofParser->Moofs();
246 Moof* currentMoof = &moofs[mCurrentMoof];
247 SampleToGroupEntry* sampleToGroupEntry = nullptr;
248
249 // Default to using the sample to group entries for the fragment, otherwise
250 // fall back to the sample to group entries for the track.
251 FallibleTArray<SampleToGroupEntry>* sampleToGroupEntries =
252 currentMoof->mFragmentSampleToGroupEntries.Length() != 0
253 ? ¤tMoof->mFragmentSampleToGroupEntries
254 : &mIndex->mMoofParser->mTrackSampleToGroupEntries;
255
256 uint32_t seen = 0;
257
258 for (SampleToGroupEntry& entry : *sampleToGroupEntries) {
259 if (seen + entry.mSampleCount > mCurrentSample) {
260 sampleToGroupEntry = &entry;
261 break;
262 }
263 seen += entry.mSampleCount;
264 }
265
266 // ISO-14496-12 Section 8.9.2.3 and 8.9.4 : group description index
267 // (1) ranges from 1 to the number of sample group entries in the track
268 // level SampleGroupDescription Box, or (2) takes the value 0 to
269 // indicate that this sample is a member of no group, in this case, the
270 // sample is associated with the default values specified in
271 // TrackEncryption Box, or (3) starts at 0x10001, i.e. the index value
272 // 1, with the value 1 in the top 16 bits, to reference fragment-local
273 // SampleGroupDescription Box.
274
275 // According to the spec, ISO-14496-12, the sum of the sample counts in this
276 // box should be equal to the total number of samples, and, if less, the
277 // reader should behave as if an extra SampleToGroupEntry existed, with
278 // groupDescriptionIndex 0.
279
280 if (!sampleToGroupEntry || sampleToGroupEntry->mGroupDescriptionIndex == 0) {
281 return nullptr;
282 }
283
284 FallibleTArray<CencSampleEncryptionInfoEntry>* entries =
285 &mIndex->mMoofParser->mTrackSampleEncryptionInfoEntries;
286
287 uint32_t groupIndex = sampleToGroupEntry->mGroupDescriptionIndex;
288
289 // If the first bit is set to a one, then we should use the sample group
290 // descriptions from the fragment.
291 if (groupIndex > SampleToGroupEntry::kFragmentGroupDescriptionIndexBase) {
292 groupIndex -= SampleToGroupEntry::kFragmentGroupDescriptionIndexBase;
293 entries = ¤tMoof->mFragmentSampleEncryptionInfoEntries;
294 }
295
296 // The group_index is one based.
297 return groupIndex > entries->Length() ? nullptr
298 : &entries->ElementAt(groupIndex - 1);
299 }
300
GetEncryptionScheme()301 Result<CryptoScheme, nsCString> SampleIterator::GetEncryptionScheme() {
302 // See ISO/IEC 23001-7 for information on the metadata being checked.
303 MoofParser* moofParser = mIndex->mMoofParser.get();
304 if (!moofParser) {
305 // This mp4 isn't fragmented so it can't be encrypted.
306 return CryptoScheme::None;
307 }
308
309 SampleDescriptionEntry* sampleDescriptionEntry = GetSampleDescriptionEntry();
310 if (!sampleDescriptionEntry) {
311 // For the file to be valid the tfhd must reference a sample description
312 // entry.
313 // If we encounter this error often, we may consider using the first
314 // sample description entry if the index is out of bounds.
315 return mozilla::Err(nsLiteralCString(
316 "Could not determine encryption scheme due to bad index for sample "
317 "description entry."));
318 }
319
320 if (!sampleDescriptionEntry->mIsEncryptedEntry) {
321 return CryptoScheme::None;
322 }
323
324 if (!moofParser->mSinf.IsValid()) {
325 // The sample description entry says this sample is encrypted, but we
326 // don't have a valid sinf box. This shouldn't happen as the sinf box is
327 // part of the sample description entry. Suggests a malformed file, bail.
328 return mozilla::Err(nsLiteralCString(
329 "Could not determine encryption scheme. Sample description entry "
330 "indicates encryption, but could not find associated sinf box."));
331 }
332
333 CencSampleEncryptionInfoEntry* sampleInfo = GetSampleEncryptionEntry();
334 if (sampleInfo && !sampleInfo->mIsEncrypted) {
335 // May not have sample encryption info, but if we do, it should match other
336 // metadata.
337 return mozilla::Err(nsLiteralCString(
338 "Could not determine encryption scheme. Sample description entry "
339 "indicates encryption, but sample encryption entry indicates sample is "
340 "not encrypted. These should be consistent."));
341 }
342
343 if (moofParser->mSinf.mDefaultEncryptionType == AtomType("cenc")) {
344 return CryptoScheme::Cenc;
345 } else if (moofParser->mSinf.mDefaultEncryptionType == AtomType("cbcs")) {
346 return CryptoScheme::Cbcs;
347 }
348 return mozilla::Err(nsLiteralCString(
349 "Could not determine encryption scheme. Sample description entry "
350 "reports sample is encrypted, but no scheme, or an unsupported scheme "
351 "is in use."));
352 }
353
Get()354 Sample* SampleIterator::Get() {
355 if (!mIndex->mMoofParser) {
356 MOZ_ASSERT(!mCurrentMoof);
357 return mCurrentSample < mIndex->mIndex.Length()
358 ? &mIndex->mIndex[mCurrentSample]
359 : nullptr;
360 }
361
362 nsTArray<Moof>& moofs = mIndex->mMoofParser->Moofs();
363 while (true) {
364 if (mCurrentMoof == moofs.Length()) {
365 if (!mIndex->mMoofParser->BlockingReadNextMoof()) {
366 return nullptr;
367 }
368 MOZ_ASSERT(mCurrentMoof < moofs.Length());
369 }
370 if (mCurrentSample < moofs[mCurrentMoof].mIndex.Length()) {
371 break;
372 }
373 mCurrentSample = 0;
374 ++mCurrentMoof;
375 }
376 return &moofs[mCurrentMoof].mIndex[mCurrentSample];
377 }
378
Next()379 void SampleIterator::Next() { ++mCurrentSample; }
380
Seek(Microseconds aTime)381 void SampleIterator::Seek(Microseconds aTime) {
382 size_t syncMoof = 0;
383 size_t syncSample = 0;
384 mCurrentMoof = 0;
385 mCurrentSample = 0;
386 Sample* sample;
387 while (!!(sample = Get())) {
388 if (sample->mCompositionRange.start > aTime) {
389 break;
390 }
391 if (sample->mSync) {
392 syncMoof = mCurrentMoof;
393 syncSample = mCurrentSample;
394 }
395 if (sample->mCompositionRange.start == aTime) {
396 break;
397 }
398 Next();
399 }
400 mCurrentMoof = syncMoof;
401 mCurrentSample = syncSample;
402 }
403
GetNextKeyframeTime()404 Microseconds SampleIterator::GetNextKeyframeTime() {
405 SampleIterator itr(*this);
406 Sample* sample;
407 while (!!(sample = itr.Get())) {
408 if (sample->mSync) {
409 return sample->mCompositionRange.start;
410 }
411 itr.Next();
412 }
413 return -1;
414 }
415
Index(const IndiceWrapper & aIndices,ByteStream * aSource,uint32_t aTrackId,bool aIsAudio)416 Index::Index(const IndiceWrapper& aIndices, ByteStream* aSource,
417 uint32_t aTrackId, bool aIsAudio)
418 : mSource(aSource), mIsAudio(aIsAudio) {
419 if (!aIndices.Length()) {
420 mMoofParser =
421 MakeUnique<MoofParser>(aSource, AsVariant(aTrackId), aIsAudio);
422 } else {
423 if (!mIndex.SetCapacity(aIndices.Length(), fallible)) {
424 // OOM.
425 return;
426 }
427 media::IntervalSet<int64_t> intervalTime;
428 MediaByteRange intervalRange;
429 bool haveSync = false;
430 bool progressive = true;
431 int64_t lastOffset = 0;
432 for (size_t i = 0; i < aIndices.Length(); i++) {
433 Indice indice;
434 if (!aIndices.GetIndice(i, indice)) {
435 // Out of index?
436 return;
437 }
438 if (indice.sync || mIsAudio) {
439 haveSync = true;
440 }
441 if (!haveSync) {
442 continue;
443 }
444 Sample sample;
445 sample.mByteRange =
446 MediaByteRange(indice.start_offset, indice.end_offset);
447 sample.mCompositionRange = MP4Interval<Microseconds>(
448 indice.start_composition, indice.end_composition);
449 sample.mDecodeTime = indice.start_decode;
450 sample.mSync = indice.sync || mIsAudio;
451 // FIXME: Make this infallible after bug 968520 is done.
452 MOZ_ALWAYS_TRUE(mIndex.AppendElement(sample, fallible));
453 if (indice.start_offset < lastOffset) {
454 NS_WARNING("Chunks in MP4 out of order, expect slow down");
455 progressive = false;
456 }
457 lastOffset = indice.end_offset;
458
459 // Pack audio samples in group of 128.
460 if (sample.mSync && progressive && (!mIsAudio || !(i % 128))) {
461 if (mDataOffset.Length()) {
462 auto& last = mDataOffset.LastElement();
463 last.mEndOffset = intervalRange.mEnd;
464 NS_ASSERTION(intervalTime.Length() == 1,
465 "Discontinuous samples between keyframes");
466 last.mTime.start = intervalTime.GetStart();
467 last.mTime.end = intervalTime.GetEnd();
468 }
469 if (!mDataOffset.AppendElement(
470 MP4DataOffset(mIndex.Length() - 1, indice.start_offset),
471 fallible)) {
472 // OOM.
473 return;
474 }
475 intervalTime = media::IntervalSet<int64_t>();
476 intervalRange = MediaByteRange();
477 }
478 intervalTime += media::Interval<int64_t>(sample.mCompositionRange.start,
479 sample.mCompositionRange.end);
480 intervalRange = intervalRange.Span(sample.mByteRange);
481 }
482
483 if (mDataOffset.Length() && progressive) {
484 Indice indice;
485 if (!aIndices.GetIndice(aIndices.Length() - 1, indice)) {
486 return;
487 }
488 auto& last = mDataOffset.LastElement();
489 last.mEndOffset = indice.end_offset;
490 last.mTime =
491 MP4Interval<int64_t>(intervalTime.GetStart(), intervalTime.GetEnd());
492 } else {
493 mDataOffset.Clear();
494 }
495 }
496 }
497
498 Index::~Index() = default;
499
UpdateMoofIndex(const MediaByteRangeSet & aByteRanges)500 void Index::UpdateMoofIndex(const MediaByteRangeSet& aByteRanges) {
501 UpdateMoofIndex(aByteRanges, false);
502 }
503
UpdateMoofIndex(const MediaByteRangeSet & aByteRanges,bool aCanEvict)504 void Index::UpdateMoofIndex(const MediaByteRangeSet& aByteRanges,
505 bool aCanEvict) {
506 if (!mMoofParser) {
507 return;
508 }
509 size_t moofs = mMoofParser->Moofs().Length();
510 bool canEvict = aCanEvict && moofs > 1;
511 if (canEvict) {
512 // Check that we can trim the mMoofParser. We can only do so if all
513 // iterators have demuxed all possible samples.
514 for (const SampleIterator* iterator : mIterators) {
515 if ((iterator->mCurrentSample == 0 && iterator->mCurrentMoof == moofs) ||
516 iterator->mCurrentMoof == moofs - 1) {
517 continue;
518 }
519 canEvict = false;
520 break;
521 }
522 }
523 mMoofParser->RebuildFragmentedIndex(aByteRanges, &canEvict);
524 if (canEvict) {
525 // The moofparser got trimmed. Adjust all registered iterators.
526 for (SampleIterator* iterator : mIterators) {
527 iterator->mCurrentMoof -= moofs - 1;
528 }
529 }
530 }
531
GetEndCompositionIfBuffered(const MediaByteRangeSet & aByteRanges)532 Microseconds Index::GetEndCompositionIfBuffered(
533 const MediaByteRangeSet& aByteRanges) {
534 FallibleTArray<Sample>* index;
535 if (mMoofParser) {
536 if (!mMoofParser->ReachedEnd() || mMoofParser->Moofs().IsEmpty()) {
537 return 0;
538 }
539 index = &mMoofParser->Moofs().LastElement().mIndex;
540 } else {
541 index = &mIndex;
542 }
543
544 Microseconds lastComposition = 0;
545 RangeFinder rangeFinder(aByteRanges);
546 for (size_t i = index->Length(); i--;) {
547 const Sample& sample = (*index)[i];
548 if (!rangeFinder.Contains(sample.mByteRange)) {
549 return 0;
550 }
551 lastComposition = std::max(lastComposition, sample.mCompositionRange.end);
552 if (sample.mSync) {
553 return lastComposition;
554 }
555 }
556 return 0;
557 }
558
ConvertByteRangesToTimeRanges(const MediaByteRangeSet & aByteRanges)559 TimeIntervals Index::ConvertByteRangesToTimeRanges(
560 const MediaByteRangeSet& aByteRanges) {
561 if (aByteRanges == mLastCachedRanges) {
562 return mLastBufferedRanges;
563 }
564 mLastCachedRanges = aByteRanges;
565
566 if (mDataOffset.Length()) {
567 TimeIntervals timeRanges;
568 for (const auto& range : aByteRanges) {
569 uint32_t start = mDataOffset.IndexOfFirstElementGt(range.mStart - 1);
570 if (!mIsAudio && start == mDataOffset.Length()) {
571 continue;
572 }
573 uint32_t end = mDataOffset.IndexOfFirstElementGt(
574 range.mEnd, MP4DataOffset::EndOffsetComparator());
575 if (!mIsAudio && end < start) {
576 continue;
577 }
578 if (mIsAudio && start &&
579 range.Intersects(MediaByteRange(mDataOffset[start - 1].mStartOffset,
580 mDataOffset[start - 1].mEndOffset))) {
581 // Check if previous audio data block contains some available samples.
582 for (size_t i = mDataOffset[start - 1].mIndex; i < mIndex.Length();
583 i++) {
584 if (range.ContainsStrict(mIndex[i].mByteRange)) {
585 timeRanges += TimeInterval(
586 TimeUnit::FromMicroseconds(mIndex[i].mCompositionRange.start),
587 TimeUnit::FromMicroseconds(mIndex[i].mCompositionRange.end));
588 }
589 }
590 }
591 if (end > start) {
592 for (uint32_t i = start; i < end; i++) {
593 timeRanges += TimeInterval(
594 TimeUnit::FromMicroseconds(mDataOffset[i].mTime.start),
595 TimeUnit::FromMicroseconds(mDataOffset[i].mTime.end));
596 }
597 }
598 if (end < mDataOffset.Length()) {
599 // Find samples in partial block contained in the byte range.
600 for (size_t i = mDataOffset[end].mIndex;
601 i < mIndex.Length() && range.ContainsStrict(mIndex[i].mByteRange);
602 i++) {
603 timeRanges += TimeInterval(
604 TimeUnit::FromMicroseconds(mIndex[i].mCompositionRange.start),
605 TimeUnit::FromMicroseconds(mIndex[i].mCompositionRange.end));
606 }
607 }
608 }
609 mLastBufferedRanges = timeRanges;
610 return timeRanges;
611 }
612
613 RangeFinder rangeFinder(aByteRanges);
614 nsTArray<MP4Interval<Microseconds>> timeRanges;
615 nsTArray<FallibleTArray<Sample>*> indexes;
616 if (mMoofParser) {
617 // We take the index out of the moof parser and move it into a local
618 // variable so we don't get concurrency issues. It gets freed when we
619 // exit this function.
620 for (int i = 0; i < mMoofParser->Moofs().Length(); i++) {
621 Moof& moof = mMoofParser->Moofs()[i];
622
623 // We need the entire moof in order to play anything
624 if (rangeFinder.Contains(moof.mRange)) {
625 if (rangeFinder.Contains(moof.mMdatRange)) {
626 MP4Interval<Microseconds>::SemiNormalAppend(timeRanges,
627 moof.mTimeRange);
628 } else {
629 indexes.AppendElement(&moof.mIndex);
630 }
631 }
632 }
633 } else {
634 indexes.AppendElement(&mIndex);
635 }
636
637 bool hasSync = false;
638 for (size_t i = 0; i < indexes.Length(); i++) {
639 FallibleTArray<Sample>* index = indexes[i];
640 for (size_t j = 0; j < index->Length(); j++) {
641 const Sample& sample = (*index)[j];
642 if (!rangeFinder.Contains(sample.mByteRange)) {
643 // We process the index in decode order so we clear hasSync when we hit
644 // a range that isn't buffered.
645 hasSync = false;
646 continue;
647 }
648
649 hasSync |= sample.mSync;
650 if (!hasSync) {
651 continue;
652 }
653
654 MP4Interval<Microseconds>::SemiNormalAppend(timeRanges,
655 sample.mCompositionRange);
656 }
657 }
658
659 // This fixes up when the compositon order differs from the byte range order
660 nsTArray<MP4Interval<Microseconds>> timeRangesNormalized;
661 MP4Interval<Microseconds>::Normalize(timeRanges, &timeRangesNormalized);
662 // convert timeRanges.
663 media::TimeIntervals ranges;
664 for (size_t i = 0; i < timeRangesNormalized.Length(); i++) {
665 ranges += media::TimeInterval(
666 media::TimeUnit::FromMicroseconds(timeRangesNormalized[i].start),
667 media::TimeUnit::FromMicroseconds(timeRangesNormalized[i].end));
668 }
669 mLastBufferedRanges = ranges;
670 return ranges;
671 }
672
GetEvictionOffset(Microseconds aTime)673 uint64_t Index::GetEvictionOffset(Microseconds aTime) {
674 uint64_t offset = std::numeric_limits<uint64_t>::max();
675 if (mMoofParser) {
676 // We need to keep the whole moof if we're keeping any of it because the
677 // parser doesn't keep parsed moofs.
678 for (int i = 0; i < mMoofParser->Moofs().Length(); i++) {
679 Moof& moof = mMoofParser->Moofs()[i];
680
681 if (moof.mTimeRange.Length() && moof.mTimeRange.end > aTime) {
682 offset = std::min(offset, uint64_t(std::min(moof.mRange.mStart,
683 moof.mMdatRange.mStart)));
684 }
685 }
686 } else {
687 // We've already parsed and stored the moov so we don't need to keep it.
688 // All we need to keep is the sample data itself.
689 for (size_t i = 0; i < mIndex.Length(); i++) {
690 const Sample& sample = mIndex[i];
691 if (aTime >= sample.mCompositionRange.end) {
692 offset = std::min(offset, uint64_t(sample.mByteRange.mEnd));
693 }
694 }
695 }
696 return offset;
697 }
698
RegisterIterator(SampleIterator * aIterator)699 void Index::RegisterIterator(SampleIterator* aIterator) {
700 mIterators.AppendElement(aIterator);
701 }
702
UnregisterIterator(SampleIterator * aIterator)703 void Index::UnregisterIterator(SampleIterator* aIterator) {
704 mIterators.RemoveElement(aIterator);
705 }
706
707 } // namespace mozilla
708