Coverage Report

Created: 2018-09-25 14:53

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