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