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           ? &currentMoof->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 = &currentMoof->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