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