Line data Source code
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/MoofParser.h"
6 : #include "mp4_demuxer/Box.h"
7 : #include "mp4_demuxer/SinfParser.h"
8 : #include <limits>
9 : #include "Intervals.h"
10 :
11 : #include "mozilla/CheckedInt.h"
12 : #include "mozilla/Logging.h"
13 : #include "mozilla/SizePrintfMacros.h"
14 :
15 : #if defined(MOZ_FMP4)
16 : extern mozilla::LogModule* GetDemuxerLog();
17 :
18 : #define STRINGIFY(x) #x
19 : #define TOSTRING(x) STRINGIFY(x)
20 : #define LOG(name, arg, ...) MOZ_LOG(GetDemuxerLog(), mozilla::LogLevel::Debug, (TOSTRING(name) "(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
21 : #else
22 : #define LOG(...)
23 : #endif
24 :
25 : namespace mp4_demuxer
26 : {
27 :
28 : using namespace stagefright;
29 : using namespace mozilla;
30 :
31 : const uint32_t kKeyIdSize = 16;
32 :
33 : bool
34 0 : MoofParser::RebuildFragmentedIndex(const MediaByteRangeSet& aByteRanges)
35 : {
36 0 : BoxContext context(mSource, aByteRanges);
37 0 : return RebuildFragmentedIndex(context);
38 : }
39 :
40 : bool
41 0 : MoofParser::RebuildFragmentedIndex(
42 : const MediaByteRangeSet& aByteRanges, bool* aCanEvict)
43 : {
44 0 : MOZ_ASSERT(aCanEvict);
45 0 : if (*aCanEvict && mMoofs.Length() > 1) {
46 0 : MOZ_ASSERT(mMoofs.Length() == mMediaRanges.Length());
47 0 : mMoofs.RemoveElementsAt(0, mMoofs.Length() - 1);
48 0 : mMediaRanges.RemoveElementsAt(0, mMediaRanges.Length() - 1);
49 0 : *aCanEvict = true;
50 : } else {
51 0 : *aCanEvict = false;
52 : }
53 0 : return RebuildFragmentedIndex(aByteRanges);
54 : }
55 :
56 : bool
57 0 : MoofParser::RebuildFragmentedIndex(BoxContext& aContext)
58 : {
59 0 : bool foundValidMoof = false;
60 0 : bool foundMdat = false;
61 :
62 0 : for (Box box(&aContext, mOffset); box.IsAvailable(); box = box.Next()) {
63 0 : if (box.IsType("moov") && mInitRange.IsEmpty()) {
64 0 : mInitRange = MediaByteRange(0, box.Range().mEnd);
65 0 : ParseMoov(box);
66 0 : } else if (box.IsType("moof")) {
67 0 : Moof moof(box, mTrex, mMvhd, mMdhd, mEdts, mSinf, &mLastDecodeTime, mIsAudio);
68 :
69 0 : if (!moof.IsValid() && !box.Next().IsAvailable()) {
70 : // Moof isn't valid abort search for now.
71 0 : break;
72 : }
73 :
74 0 : if (!mMoofs.IsEmpty()) {
75 : // Stitch time ranges together in the case of a (hopefully small) time
76 : // range gap between moofs.
77 0 : mMoofs.LastElement().FixRounding(moof);
78 : }
79 :
80 0 : mMoofs.AppendElement(moof);
81 0 : mMediaRanges.AppendElement(moof.mRange);
82 0 : foundValidMoof = true;
83 0 : } else if (box.IsType("mdat") && !Moofs().IsEmpty()) {
84 : // Check if we have all our data from last moof.
85 0 : Moof& moof = Moofs().LastElement();
86 0 : media::Interval<int64_t> datarange(moof.mMdatRange.mStart, moof.mMdatRange.mEnd, 0);
87 0 : media::Interval<int64_t> mdat(box.Range().mStart, box.Range().mEnd, 0);
88 0 : if (datarange.Intersects(mdat)) {
89 0 : mMediaRanges.LastElement() =
90 0 : mMediaRanges.LastElement().Span(box.Range());
91 : }
92 : }
93 0 : mOffset = box.NextOffset();
94 : }
95 0 : return foundValidMoof;
96 : }
97 :
98 : MediaByteRange
99 0 : MoofParser::FirstCompleteMediaHeader()
100 : {
101 0 : if (Moofs().IsEmpty()) {
102 0 : return MediaByteRange();
103 : }
104 0 : return Moofs()[0].mRange;
105 : }
106 :
107 : MediaByteRange
108 0 : MoofParser::FirstCompleteMediaSegment()
109 : {
110 0 : for (uint32_t i = 0 ; i < mMediaRanges.Length(); i++) {
111 0 : if (mMediaRanges[i].Contains(Moofs()[i].mMdatRange)) {
112 0 : return mMediaRanges[i];
113 : }
114 : }
115 0 : return MediaByteRange();
116 : }
117 :
118 0 : class BlockingStream : public Stream {
119 : public:
120 0 : explicit BlockingStream(Stream* aStream) : mStream(aStream)
121 : {
122 0 : }
123 :
124 0 : bool ReadAt(int64_t offset, void* data, size_t size, size_t* bytes_read)
125 : override
126 : {
127 0 : return mStream->ReadAt(offset, data, size, bytes_read);
128 : }
129 :
130 0 : bool CachedReadAt(int64_t offset, void* data, size_t size, size_t* bytes_read)
131 : override
132 : {
133 0 : return mStream->ReadAt(offset, data, size, bytes_read);
134 : }
135 :
136 0 : virtual bool Length(int64_t* size) override
137 : {
138 0 : return mStream->Length(size);
139 : }
140 :
141 : private:
142 : RefPtr<Stream> mStream;
143 : };
144 :
145 : bool
146 0 : MoofParser::BlockingReadNextMoof()
147 : {
148 0 : int64_t length = std::numeric_limits<int64_t>::max();
149 0 : mSource->Length(&length);
150 0 : MediaByteRangeSet byteRanges;
151 0 : byteRanges += MediaByteRange(0, length);
152 0 : RefPtr<mp4_demuxer::BlockingStream> stream = new BlockingStream(mSource);
153 :
154 0 : BoxContext context(stream, byteRanges);
155 0 : for (Box box(&context, mOffset); box.IsAvailable(); box = box.Next()) {
156 0 : if (box.IsType("moof")) {
157 0 : byteRanges.Clear();
158 0 : byteRanges += MediaByteRange(mOffset, box.Range().mEnd);
159 0 : return RebuildFragmentedIndex(context);
160 : }
161 : }
162 0 : return false;
163 : }
164 :
165 : void
166 0 : MoofParser::ScanForMetadata(mozilla::MediaByteRange& aFtyp,
167 : mozilla::MediaByteRange& aMoov)
168 : {
169 0 : int64_t length = std::numeric_limits<int64_t>::max();
170 0 : mSource->Length(&length);
171 0 : MediaByteRangeSet byteRanges;
172 0 : byteRanges += MediaByteRange(0, length);
173 0 : RefPtr<mp4_demuxer::BlockingStream> stream = new BlockingStream(mSource);
174 :
175 0 : BoxContext context(stream, byteRanges);
176 0 : for (Box box(&context, mOffset); box.IsAvailable(); box = box.Next()) {
177 0 : if (box.IsType("ftyp")) {
178 0 : aFtyp = box.Range();
179 0 : continue;
180 : }
181 0 : if (box.IsType("moov")) {
182 0 : aMoov = box.Range();
183 0 : break;
184 : }
185 : }
186 0 : mInitRange = aFtyp.Span(aMoov);
187 0 : }
188 :
189 : bool
190 0 : MoofParser::HasMetadata()
191 : {
192 0 : MediaByteRange ftyp;
193 0 : MediaByteRange moov;
194 0 : ScanForMetadata(ftyp, moov);
195 0 : return !!ftyp.Length() && !!moov.Length();
196 : }
197 :
198 : already_AddRefed<mozilla::MediaByteBuffer>
199 0 : MoofParser::Metadata()
200 : {
201 0 : MediaByteRange ftyp;
202 0 : MediaByteRange moov;
203 0 : ScanForMetadata(ftyp, moov);
204 0 : CheckedInt<MediaByteBuffer::size_type> ftypLength = ftyp.Length();
205 0 : CheckedInt<MediaByteBuffer::size_type> moovLength = moov.Length();
206 0 : if (!ftypLength.isValid() || !moovLength.isValid()
207 0 : || !ftypLength.value() || !moovLength.value()) {
208 : // No ftyp or moov, or they cannot be used as array size.
209 0 : return nullptr;
210 : }
211 0 : CheckedInt<MediaByteBuffer::size_type> totalLength = ftypLength + moovLength;
212 0 : if (!totalLength.isValid()) {
213 : // Addition overflow, or sum cannot be used as array size.
214 0 : return nullptr;
215 : }
216 0 : RefPtr<MediaByteBuffer> metadata = new MediaByteBuffer();
217 0 : if (!metadata->SetLength(totalLength.value(), fallible)) {
218 : // OOM
219 0 : return nullptr;
220 : }
221 :
222 0 : RefPtr<mp4_demuxer::BlockingStream> stream = new BlockingStream(mSource);
223 : size_t read;
224 : bool rv =
225 0 : stream->ReadAt(ftyp.mStart, metadata->Elements(), ftypLength.value(), &read);
226 0 : if (!rv || read != ftypLength.value()) {
227 0 : return nullptr;
228 : }
229 : rv =
230 0 : stream->ReadAt(moov.mStart, metadata->Elements() + ftypLength.value(), moovLength.value(), &read);
231 0 : if (!rv || read != moovLength.value()) {
232 0 : return nullptr;
233 : }
234 0 : return metadata.forget();
235 : }
236 :
237 : Interval<Microseconds>
238 0 : MoofParser::GetCompositionRange(const MediaByteRangeSet& aByteRanges)
239 : {
240 0 : Interval<Microseconds> compositionRange;
241 0 : BoxContext context(mSource, aByteRanges);
242 0 : for (size_t i = 0; i < mMoofs.Length(); i++) {
243 0 : Moof& moof = mMoofs[i];
244 0 : Box box(&context, moof.mRange.mStart);
245 0 : if (box.IsAvailable()) {
246 0 : compositionRange = compositionRange.Extents(moof.mTimeRange);
247 : }
248 : }
249 0 : return compositionRange;
250 : }
251 :
252 : bool
253 0 : MoofParser::ReachedEnd()
254 : {
255 : int64_t length;
256 0 : return mSource->Length(&length) && mOffset == length;
257 : }
258 :
259 : void
260 0 : MoofParser::ParseMoov(Box& aBox)
261 : {
262 0 : for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
263 0 : if (box.IsType("mvhd")) {
264 0 : mMvhd = Mvhd(box);
265 0 : } else if (box.IsType("trak")) {
266 0 : ParseTrak(box);
267 0 : } else if (box.IsType("mvex")) {
268 0 : ParseMvex(box);
269 : }
270 : }
271 0 : }
272 :
273 : void
274 0 : MoofParser::ParseTrak(Box& aBox)
275 : {
276 0 : Tkhd tkhd;
277 0 : for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
278 0 : if (box.IsType("tkhd")) {
279 0 : tkhd = Tkhd(box);
280 0 : } else if (box.IsType("mdia")) {
281 0 : if (!mTrex.mTrackId || tkhd.mTrackId == mTrex.mTrackId) {
282 0 : ParseMdia(box, tkhd);
283 : }
284 0 : } else if (box.IsType("edts") &&
285 0 : (!mTrex.mTrackId || tkhd.mTrackId == mTrex.mTrackId)) {
286 0 : mEdts = Edts(box);
287 : }
288 : }
289 0 : }
290 :
291 : void
292 0 : MoofParser::ParseMdia(Box& aBox, Tkhd& aTkhd)
293 : {
294 0 : for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
295 0 : if (box.IsType("mdhd")) {
296 0 : mMdhd = Mdhd(box);
297 0 : } else if (box.IsType("minf")) {
298 0 : ParseMinf(box);
299 : }
300 : }
301 0 : }
302 :
303 : void
304 0 : MoofParser::ParseMvex(Box& aBox)
305 : {
306 0 : for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
307 0 : if (box.IsType("trex")) {
308 0 : Trex trex = Trex(box);
309 0 : if (!mTrex.mTrackId || trex.mTrackId == mTrex.mTrackId) {
310 0 : auto trackId = mTrex.mTrackId;
311 0 : mTrex = trex;
312 : // Keep the original trackId, as should it be 0 we want to continue
313 : // parsing all tracks.
314 0 : mTrex.mTrackId = trackId;
315 : }
316 : }
317 : }
318 0 : }
319 :
320 : void
321 0 : MoofParser::ParseMinf(Box& aBox)
322 : {
323 0 : for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
324 0 : if (box.IsType("stbl")) {
325 0 : ParseStbl(box);
326 : }
327 : }
328 0 : }
329 :
330 : void
331 0 : MoofParser::ParseStbl(Box& aBox)
332 : {
333 0 : for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
334 0 : if (box.IsType("stsd")) {
335 0 : ParseStsd(box);
336 0 : } else if (box.IsType("sgpd")) {
337 0 : Sgpd sgpd(box);
338 0 : if (sgpd.IsValid() && sgpd.mGroupingType == "seig") {
339 0 : mTrackSampleEncryptionInfoEntries.Clear();
340 0 : mTrackSampleEncryptionInfoEntries.AppendElements(sgpd.mEntries);
341 : }
342 0 : } else if (box.IsType("sbgp")) {
343 0 : Sbgp sbgp(box);
344 0 : if (sbgp.IsValid() && sbgp.mGroupingType == "seig") {
345 0 : mTrackSampleToGroupEntries.Clear();
346 0 : mTrackSampleToGroupEntries.AppendElements(sbgp.mEntries);
347 : }
348 : }
349 : }
350 0 : }
351 :
352 : void
353 0 : MoofParser::ParseStsd(Box& aBox)
354 : {
355 0 : for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
356 0 : if (box.IsType("encv") || box.IsType("enca")) {
357 0 : ParseEncrypted(box);
358 : }
359 : }
360 0 : }
361 :
362 : void
363 0 : MoofParser::ParseEncrypted(Box& aBox)
364 : {
365 0 : for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
366 : // Some MP4 files have been found to have multiple sinf boxes in the same
367 : // enc* box. This does not match spec anyway, so just choose the first
368 : // one that parses properly.
369 0 : if (box.IsType("sinf")) {
370 0 : mSinf = Sinf(box);
371 :
372 0 : if (mSinf.IsValid()) {
373 0 : break;
374 : }
375 : }
376 : }
377 0 : }
378 :
379 : class CtsComparator
380 : {
381 : public:
382 0 : bool Equals(Sample* const aA, Sample* const aB) const
383 : {
384 0 : return aA->mCompositionRange.start == aB->mCompositionRange.start;
385 : }
386 : bool
387 0 : LessThan(Sample* const aA, Sample* const aB) const
388 : {
389 0 : return aA->mCompositionRange.start < aB->mCompositionRange.start;
390 : }
391 : };
392 :
393 0 : Moof::Moof(Box& aBox, Trex& aTrex, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf, uint64_t* aDecodeTime, bool aIsAudio)
394 0 : : mRange(aBox.Range())
395 0 : , mMaxRoundingError(35000)
396 : {
397 0 : for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
398 0 : if (box.IsType("traf")) {
399 0 : ParseTraf(box, aTrex, aMvhd, aMdhd, aEdts, aSinf, aDecodeTime, aIsAudio);
400 : }
401 : }
402 0 : if (IsValid()) {
403 0 : if (mIndex.Length()) {
404 : // Ensure the samples are contiguous with no gaps.
405 0 : nsTArray<Sample*> ctsOrder;
406 0 : for (auto& sample : mIndex) {
407 0 : ctsOrder.AppendElement(&sample);
408 : }
409 0 : ctsOrder.Sort(CtsComparator());
410 :
411 0 : for (size_t i = 1; i < ctsOrder.Length(); i++) {
412 0 : ctsOrder[i-1]->mCompositionRange.end = ctsOrder[i]->mCompositionRange.start;
413 : }
414 :
415 : // In MP4, the duration of a sample is defined as the delta between two decode
416 : // timestamps. The operation above has updated the duration of each sample
417 : // as a Sample's duration is mCompositionRange.end - mCompositionRange.start
418 : // MSE's TrackBuffersManager expects dts that increased by the sample's
419 : // duration, so we rewrite the dts accordingly.
420 : int64_t presentationDuration =
421 0 : ctsOrder.LastElement()->mCompositionRange.end
422 0 : - ctsOrder[0]->mCompositionRange.start;
423 : int64_t endDecodeTime =
424 0 : aMdhd.ToMicroseconds((int64_t)*aDecodeTime - aEdts.mMediaStart)
425 0 : + aMvhd.ToMicroseconds(aEdts.mEmptyOffset);
426 0 : int64_t decodeDuration = endDecodeTime - mIndex[0].mDecodeTime;
427 0 : double adjust = (double)decodeDuration / presentationDuration;
428 0 : int64_t dtsOffset = mIndex[0].mDecodeTime;
429 0 : int64_t compositionDuration = 0;
430 : // Adjust the dts, ensuring that the new adjusted dts will never be greater
431 : // than decodeTime (the next moof's decode start time).
432 0 : for (auto& sample : mIndex) {
433 0 : sample.mDecodeTime = dtsOffset + int64_t(compositionDuration * adjust);
434 0 : compositionDuration += sample.mCompositionRange.Length();
435 : }
436 0 : mTimeRange = Interval<Microseconds>(ctsOrder[0]->mCompositionRange.start,
437 0 : ctsOrder.LastElement()->mCompositionRange.end);
438 : }
439 0 : ProcessCenc();
440 : }
441 0 : }
442 :
443 : bool
444 0 : Moof::GetAuxInfo(AtomType aType, nsTArray<MediaByteRange>* aByteRanges)
445 : {
446 0 : aByteRanges->Clear();
447 :
448 0 : Saiz* saiz = nullptr;
449 0 : for (int i = 0; ; i++) {
450 0 : if (i == mSaizs.Length()) {
451 0 : return false;
452 : }
453 0 : if (mSaizs[i].mAuxInfoType == aType) {
454 0 : saiz = &mSaizs[i];
455 0 : break;
456 : }
457 : }
458 0 : Saio* saio = nullptr;
459 0 : for (int i = 0; ; i++) {
460 0 : if (i == mSaios.Length()) {
461 0 : return false;
462 : }
463 0 : if (mSaios[i].mAuxInfoType == aType) {
464 0 : saio = &mSaios[i];
465 0 : break;
466 : }
467 : }
468 :
469 0 : if (saio->mOffsets.Length() == 1) {
470 0 : aByteRanges->SetCapacity(saiz->mSampleInfoSize.Length());
471 0 : uint64_t offset = mRange.mStart + saio->mOffsets[0];
472 0 : for (size_t i = 0; i < saiz->mSampleInfoSize.Length(); i++) {
473 0 : aByteRanges->AppendElement(
474 0 : MediaByteRange(offset, offset + saiz->mSampleInfoSize[i]));
475 0 : offset += saiz->mSampleInfoSize[i];
476 : }
477 0 : return true;
478 : }
479 :
480 0 : if (saio->mOffsets.Length() == saiz->mSampleInfoSize.Length()) {
481 0 : aByteRanges->SetCapacity(saiz->mSampleInfoSize.Length());
482 0 : for (size_t i = 0; i < saio->mOffsets.Length(); i++) {
483 0 : uint64_t offset = mRange.mStart + saio->mOffsets[i];
484 0 : aByteRanges->AppendElement(
485 0 : MediaByteRange(offset, offset + saiz->mSampleInfoSize[i]));
486 : }
487 0 : return true;
488 : }
489 :
490 0 : return false;
491 : }
492 :
493 : bool
494 0 : Moof::ProcessCenc()
495 : {
496 0 : nsTArray<MediaByteRange> cencRanges;
497 0 : if (!GetAuxInfo(AtomType("cenc"), &cencRanges) ||
498 0 : cencRanges.Length() != mIndex.Length()) {
499 0 : return false;
500 : }
501 0 : for (int i = 0; i < cencRanges.Length(); i++) {
502 0 : mIndex[i].mCencRange = cencRanges[i];
503 : }
504 0 : return true;
505 : }
506 :
507 : void
508 0 : Moof::ParseTraf(Box& aBox, Trex& aTrex, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf, uint64_t* aDecodeTime, bool aIsAudio)
509 : {
510 0 : MOZ_ASSERT(aDecodeTime);
511 0 : Tfhd tfhd(aTrex);
512 0 : Tfdt tfdt;
513 :
514 0 : for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
515 0 : if (box.IsType("tfhd")) {
516 0 : tfhd = Tfhd(box, aTrex);
517 0 : } else if (!aTrex.mTrackId || tfhd.mTrackId == aTrex.mTrackId) {
518 0 : if (box.IsType("tfdt")) {
519 0 : tfdt = Tfdt(box);
520 0 : } else if (box.IsType("sgpd")) {
521 0 : Sgpd sgpd(box);
522 0 : if (sgpd.IsValid() && sgpd.mGroupingType == "seig") {
523 0 : mFragmentSampleEncryptionInfoEntries.Clear();
524 0 : mFragmentSampleEncryptionInfoEntries.AppendElements(sgpd.mEntries);
525 : }
526 0 : } else if (box.IsType("sbgp")) {
527 0 : Sbgp sbgp(box);
528 0 : if (sbgp.IsValid() && sbgp.mGroupingType == "seig") {
529 0 : mFragmentSampleToGroupEntries.Clear();
530 0 : mFragmentSampleToGroupEntries.AppendElements(sbgp.mEntries);
531 : }
532 0 : } else if (box.IsType("saiz")) {
533 0 : mSaizs.AppendElement(Saiz(box, aSinf.mDefaultEncryptionType));
534 0 : } else if (box.IsType("saio")) {
535 0 : mSaios.AppendElement(Saio(box, aSinf.mDefaultEncryptionType));
536 : }
537 : }
538 : }
539 0 : if (aTrex.mTrackId && tfhd.mTrackId != aTrex.mTrackId) {
540 0 : return;
541 : }
542 : // Now search for TRUN boxes.
543 : uint64_t decodeTime =
544 0 : tfdt.IsValid() ? tfdt.mBaseMediaDecodeTime : *aDecodeTime;
545 0 : for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
546 0 : if (box.IsType("trun")) {
547 0 : if (ParseTrun(box, tfhd, aMvhd, aMdhd, aEdts, &decodeTime, aIsAudio)) {
548 0 : mValid = true;
549 : } else {
550 0 : mValid = false;
551 0 : break;
552 : }
553 : }
554 : }
555 0 : *aDecodeTime = decodeTime;
556 : }
557 :
558 : void
559 0 : Moof::FixRounding(const Moof& aMoof) {
560 0 : Microseconds gap = aMoof.mTimeRange.start - mTimeRange.end;
561 0 : if (gap > 0 && gap <= mMaxRoundingError) {
562 0 : mTimeRange.end = aMoof.mTimeRange.start;
563 : }
564 0 : }
565 :
566 : bool
567 0 : Moof::ParseTrun(Box& aBox, Tfhd& aTfhd, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, uint64_t* aDecodeTime, bool aIsAudio)
568 : {
569 0 : if (!aTfhd.IsValid() || !aMvhd.IsValid() || !aMdhd.IsValid() ||
570 0 : !aEdts.IsValid()) {
571 0 : LOG(Moof, "Invalid dependencies: aTfhd(%d) aMvhd(%d) aMdhd(%d) aEdts(%d)",
572 : aTfhd.IsValid(), aMvhd.IsValid(), aMdhd.IsValid(), !aEdts.IsValid());
573 0 : return false;
574 : }
575 :
576 0 : BoxReader reader(aBox);
577 0 : if (!reader->CanReadType<uint32_t>()) {
578 0 : LOG(Moof, "Incomplete Box (missing flags)");
579 0 : return false;
580 : }
581 0 : uint32_t flags = reader->ReadU32();
582 0 : uint8_t version = flags >> 24;
583 :
584 0 : if (!reader->CanReadType<uint32_t>()) {
585 0 : LOG(Moof, "Incomplete Box (missing sampleCount)");
586 0 : return false;
587 : }
588 0 : uint32_t sampleCount = reader->ReadU32();
589 0 : if (sampleCount == 0) {
590 0 : return true;
591 : }
592 :
593 : size_t need =
594 0 : ((flags & 1) ? sizeof(uint32_t) : 0) +
595 0 : ((flags & 4) ? sizeof(uint32_t) : 0);
596 0 : uint16_t flag[] = { 0x100, 0x200, 0x400, 0x800, 0 };
597 0 : for (size_t i = 0; flag[i]; i++) {
598 0 : if (flags & flag[i]) {
599 0 : need += sizeof(uint32_t) * sampleCount;
600 : }
601 : }
602 0 : if (reader->Remaining() < need) {
603 0 : LOG(Moof, "Incomplete Box (have:%" PRIuSIZE " need:%" PRIuSIZE ")",
604 : reader->Remaining(), need);
605 0 : return false;
606 : }
607 :
608 0 : uint64_t offset = aTfhd.mBaseDataOffset + (flags & 1 ? reader->ReadU32() : 0);
609 : uint32_t firstSampleFlags =
610 0 : flags & 4 ? reader->ReadU32() : aTfhd.mDefaultSampleFlags;
611 0 : uint64_t decodeTime = *aDecodeTime;
612 0 : nsTArray<Interval<Microseconds>> timeRanges;
613 :
614 0 : if (!mIndex.SetCapacity(sampleCount, fallible)) {
615 0 : LOG(Moof, "Out of Memory");
616 0 : return false;
617 : }
618 :
619 0 : for (size_t i = 0; i < sampleCount; i++) {
620 : uint32_t sampleDuration =
621 0 : flags & 0x100 ? reader->ReadU32() : aTfhd.mDefaultSampleDuration;
622 : uint32_t sampleSize =
623 0 : flags & 0x200 ? reader->ReadU32() : aTfhd.mDefaultSampleSize;
624 : uint32_t sampleFlags =
625 0 : flags & 0x400 ? reader->ReadU32()
626 0 : : i ? aTfhd.mDefaultSampleFlags : firstSampleFlags;
627 0 : int32_t ctsOffset = 0;
628 0 : if (flags & 0x800) {
629 0 : ctsOffset = reader->Read32();
630 : }
631 :
632 0 : Sample sample;
633 0 : sample.mByteRange = MediaByteRange(offset, offset + sampleSize);
634 0 : offset += sampleSize;
635 :
636 0 : sample.mDecodeTime =
637 0 : aMdhd.ToMicroseconds((int64_t)decodeTime - aEdts.mMediaStart) + aMvhd.ToMicroseconds(aEdts.mEmptyOffset);
638 0 : sample.mCompositionRange = Interval<Microseconds>(
639 0 : aMdhd.ToMicroseconds((int64_t)decodeTime + ctsOffset - aEdts.mMediaStart) + aMvhd.ToMicroseconds(aEdts.mEmptyOffset),
640 0 : aMdhd.ToMicroseconds((int64_t)decodeTime + ctsOffset + sampleDuration - aEdts.mMediaStart) + aMvhd.ToMicroseconds(aEdts.mEmptyOffset));
641 0 : decodeTime += sampleDuration;
642 :
643 : // Sometimes audio streams don't properly mark their samples as keyframes,
644 : // because every audio sample is a keyframe.
645 0 : sample.mSync = !(sampleFlags & 0x1010000) || aIsAudio;
646 :
647 : // FIXME: Make this infallible after bug 968520 is done.
648 0 : MOZ_ALWAYS_TRUE(mIndex.AppendElement(sample, fallible));
649 :
650 0 : mMdatRange = mMdatRange.Span(sample.mByteRange);
651 : }
652 0 : mMaxRoundingError += aMdhd.ToMicroseconds(sampleCount);
653 :
654 0 : *aDecodeTime = decodeTime;
655 :
656 0 : return true;
657 : }
658 :
659 0 : Tkhd::Tkhd(Box& aBox)
660 : {
661 0 : BoxReader reader(aBox);
662 0 : if (!reader->CanReadType<uint32_t>()) {
663 0 : LOG(Tkhd, "Incomplete Box (missing flags)");
664 0 : return;
665 : }
666 0 : uint32_t flags = reader->ReadU32();
667 0 : uint8_t version = flags >> 24;
668 : size_t need =
669 0 : 3*(version ? sizeof(int64_t) : sizeof(int32_t)) + 2*sizeof(int32_t);
670 0 : if (reader->Remaining() < need) {
671 0 : LOG(Tkhd, "Incomplete Box (have:%" PRIu64 " need:%" PRIu64 ")",
672 : (uint64_t)reader->Remaining(), (uint64_t)need);
673 0 : return;
674 : }
675 0 : if (version == 0) {
676 0 : mCreationTime = reader->ReadU32();
677 0 : mModificationTime = reader->ReadU32();
678 0 : mTrackId = reader->ReadU32();
679 0 : uint32_t reserved = reader->ReadU32();
680 0 : NS_ASSERTION(!reserved, "reserved should be 0");
681 0 : mDuration = reader->ReadU32();
682 0 : } else if (version == 1) {
683 0 : mCreationTime = reader->ReadU64();
684 0 : mModificationTime = reader->ReadU64();
685 0 : mTrackId = reader->ReadU32();
686 0 : uint32_t reserved = reader->ReadU32();
687 0 : NS_ASSERTION(!reserved, "reserved should be 0");
688 0 : mDuration = reader->ReadU64();
689 : }
690 : // We don't care about whatever else may be in the box.
691 0 : mValid = true;
692 : }
693 :
694 0 : Mvhd::Mvhd(Box& aBox)
695 : {
696 0 : BoxReader reader(aBox);
697 0 : if (!reader->CanReadType<uint32_t>()) {
698 0 : LOG(Mdhd, "Incomplete Box (missing flags)");
699 0 : return;
700 : }
701 0 : uint32_t flags = reader->ReadU32();
702 0 : uint8_t version = flags >> 24;
703 : size_t need =
704 0 : 3*(version ? sizeof(int64_t) : sizeof(int32_t)) + sizeof(uint32_t);
705 0 : if (reader->Remaining() < need) {
706 0 : LOG(Mvhd, "Incomplete Box (have:%" PRIu64 " need:%" PRIu64 ")",
707 : (uint64_t)reader->Remaining(), (uint64_t)need);
708 0 : return;
709 : }
710 :
711 0 : if (version == 0) {
712 0 : mCreationTime = reader->ReadU32();
713 0 : mModificationTime = reader->ReadU32();
714 0 : mTimescale = reader->ReadU32();
715 0 : mDuration = reader->ReadU32();
716 0 : } else if (version == 1) {
717 0 : mCreationTime = reader->ReadU64();
718 0 : mModificationTime = reader->ReadU64();
719 0 : mTimescale = reader->ReadU32();
720 0 : mDuration = reader->ReadU64();
721 : } else {
722 0 : return;
723 : }
724 : // We don't care about whatever else may be in the box.
725 0 : if (mTimescale) {
726 0 : mValid = true;
727 : }
728 : }
729 :
730 0 : Mdhd::Mdhd(Box& aBox)
731 0 : : Mvhd(aBox)
732 : {
733 0 : }
734 :
735 0 : Trex::Trex(Box& aBox)
736 : {
737 0 : BoxReader reader(aBox);
738 0 : if (reader->Remaining() < 6*sizeof(uint32_t)) {
739 0 : LOG(Trex, "Incomplete Box (have:%" PRIu64 " need:%" PRIu64 ")",
740 : (uint64_t)reader->Remaining(), (uint64_t)6*sizeof(uint32_t));
741 0 : return;
742 : }
743 0 : mFlags = reader->ReadU32();
744 0 : mTrackId = reader->ReadU32();
745 0 : mDefaultSampleDescriptionIndex = reader->ReadU32();
746 0 : mDefaultSampleDuration = reader->ReadU32();
747 0 : mDefaultSampleSize = reader->ReadU32();
748 0 : mDefaultSampleFlags = reader->ReadU32();
749 0 : mValid = true;
750 : }
751 :
752 0 : Tfhd::Tfhd(Box& aBox, Trex& aTrex)
753 0 : : Trex(aTrex)
754 : {
755 0 : MOZ_ASSERT(aBox.IsType("tfhd"));
756 0 : MOZ_ASSERT(aBox.Parent()->IsType("traf"));
757 0 : MOZ_ASSERT(aBox.Parent()->Parent()->IsType("moof"));
758 :
759 0 : BoxReader reader(aBox);
760 0 : if (!reader->CanReadType<uint32_t>()) {
761 0 : LOG(Tfhd, "Incomplete Box (missing flags)");
762 0 : return;
763 : }
764 0 : mFlags = reader->ReadU32();
765 0 : size_t need = sizeof(uint32_t) /* trackid */;
766 0 : uint8_t flag[] = { 1, 2, 8, 0x10, 0x20, 0 };
767 0 : uint8_t flagSize[] = { sizeof(uint64_t), sizeof(uint32_t), sizeof(uint32_t), sizeof(uint32_t), sizeof(uint32_t) };
768 0 : for (size_t i = 0; flag[i]; i++) {
769 0 : if (mFlags & flag[i]) {
770 0 : need += flagSize[i];
771 : }
772 : }
773 0 : if (reader->Remaining() < need) {
774 0 : LOG(Tfhd, "Incomplete Box (have:%" PRIu64 " need:%" PRIu64 ")",
775 : (uint64_t)reader->Remaining(), (uint64_t)need);
776 0 : return;
777 : }
778 0 : mTrackId = reader->ReadU32();
779 0 : mBaseDataOffset =
780 0 : mFlags & 1 ? reader->ReadU64() : aBox.Parent()->Parent()->Offset();
781 0 : if (mFlags & 2) {
782 0 : mDefaultSampleDescriptionIndex = reader->ReadU32();
783 : }
784 0 : if (mFlags & 8) {
785 0 : mDefaultSampleDuration = reader->ReadU32();
786 : }
787 0 : if (mFlags & 0x10) {
788 0 : mDefaultSampleSize = reader->ReadU32();
789 : }
790 0 : if (mFlags & 0x20) {
791 0 : mDefaultSampleFlags = reader->ReadU32();
792 : }
793 0 : mValid = true;
794 : }
795 :
796 0 : Tfdt::Tfdt(Box& aBox)
797 : {
798 0 : BoxReader reader(aBox);
799 0 : if (!reader->CanReadType<uint32_t>()) {
800 0 : LOG(Tfdt, "Incomplete Box (missing flags)");
801 0 : return;
802 : }
803 0 : uint32_t flags = reader->ReadU32();
804 0 : uint8_t version = flags >> 24;
805 0 : size_t need = version ? sizeof(uint64_t) : sizeof(uint32_t) ;
806 0 : if (reader->Remaining() < need) {
807 0 : LOG(Tfdt, "Incomplete Box (have:%" PRIu64 " need:%" PRIu64 ")",
808 : (uint64_t)reader->Remaining(), (uint64_t)need);
809 0 : return;
810 : }
811 0 : if (version == 0) {
812 0 : mBaseMediaDecodeTime = reader->ReadU32();
813 0 : } else if (version == 1) {
814 0 : mBaseMediaDecodeTime = reader->ReadU64();
815 : }
816 0 : mValid = true;
817 : }
818 :
819 0 : Edts::Edts(Box& aBox)
820 : : mMediaStart(0)
821 0 : , mEmptyOffset(0)
822 : {
823 0 : Box child = aBox.FirstChild();
824 0 : if (!child.IsType("elst")) {
825 0 : return;
826 : }
827 :
828 0 : BoxReader reader(child);
829 0 : if (!reader->CanReadType<uint32_t>()) {
830 0 : LOG(Edts, "Incomplete Box (missing flags)");
831 0 : return;
832 : }
833 0 : uint32_t flags = reader->ReadU32();
834 0 : uint8_t version = flags >> 24;
835 : size_t need =
836 0 : sizeof(uint32_t) + 2*(version ? sizeof(int64_t) : sizeof(uint32_t));
837 0 : if (reader->Remaining() < need) {
838 0 : LOG(Edts, "Incomplete Box (have:%" PRIu64 " need:%" PRIu64 ")",
839 : (uint64_t)reader->Remaining(), (uint64_t)need);
840 0 : return;
841 : }
842 0 : bool emptyEntry = false;
843 0 : uint32_t entryCount = reader->ReadU32();
844 0 : for (uint32_t i = 0; i < entryCount; i++) {
845 : uint64_t segment_duration;
846 : int64_t media_time;
847 0 : if (version == 1) {
848 0 : segment_duration = reader->ReadU64();
849 0 : media_time = reader->Read64();
850 : } else {
851 0 : segment_duration = reader->ReadU32();
852 0 : media_time = reader->Read32();
853 : }
854 0 : if (media_time == -1 && i) {
855 0 : LOG(Edts, "Multiple empty edit, not handled");
856 0 : } else if (media_time == -1) {
857 0 : mEmptyOffset = segment_duration;
858 0 : emptyEntry = true;
859 0 : } else if (i > 1 || (i > 0 && !emptyEntry)) {
860 0 : LOG(Edts, "More than one edit entry, not handled. A/V sync will be wrong");
861 0 : break;
862 : } else {
863 0 : mMediaStart = media_time;
864 : }
865 0 : reader->ReadU32(); // media_rate_integer and media_rate_fraction
866 : }
867 : }
868 :
869 0 : Saiz::Saiz(Box& aBox, AtomType aDefaultType)
870 : : mAuxInfoType(aDefaultType)
871 0 : , mAuxInfoTypeParameter(0)
872 : {
873 0 : BoxReader reader(aBox);
874 0 : if (!reader->CanReadType<uint32_t>()) {
875 0 : LOG(Saiz, "Incomplete Box (missing flags)");
876 0 : return;
877 : }
878 0 : uint32_t flags = reader->ReadU32();
879 0 : uint8_t version = flags >> 24;
880 : size_t need =
881 0 : ((flags & 1) ? 2*sizeof(uint32_t) : 0) + sizeof(uint8_t) + sizeof(uint32_t);
882 0 : if (reader->Remaining() < need) {
883 0 : LOG(Saiz, "Incomplete Box (have:%" PRIu64 " need:%" PRIu64 ")",
884 : (uint64_t)reader->Remaining(), (uint64_t)need);
885 0 : return;
886 : }
887 0 : if (flags & 1) {
888 0 : mAuxInfoType = reader->ReadU32();
889 0 : mAuxInfoTypeParameter = reader->ReadU32();
890 : }
891 0 : uint8_t defaultSampleInfoSize = reader->ReadU8();
892 0 : uint32_t count = reader->ReadU32();
893 0 : if (defaultSampleInfoSize) {
894 0 : if (!mSampleInfoSize.SetLength(count, fallible)) {
895 0 : LOG(Saiz, "OOM");
896 0 : return;
897 : }
898 0 : memset(mSampleInfoSize.Elements(), defaultSampleInfoSize, mSampleInfoSize.Length());
899 : } else {
900 0 : if (!reader->ReadArray(mSampleInfoSize, count)) {
901 0 : LOG(Saiz, "Incomplete Box (OOM or missing count:%u)", count);
902 0 : return;
903 : }
904 : }
905 0 : mValid = true;
906 : }
907 :
908 0 : Saio::Saio(Box& aBox, AtomType aDefaultType)
909 : : mAuxInfoType(aDefaultType)
910 0 : , mAuxInfoTypeParameter(0)
911 : {
912 0 : BoxReader reader(aBox);
913 0 : if (!reader->CanReadType<uint32_t>()) {
914 0 : LOG(Saio, "Incomplete Box (missing flags)");
915 0 : return;
916 : }
917 0 : uint32_t flags = reader->ReadU32();
918 0 : uint8_t version = flags >> 24;
919 0 : size_t need = ((flags & 1) ? (2*sizeof(uint32_t)) : 0) + sizeof(uint32_t);
920 0 : if (reader->Remaining() < need) {
921 0 : LOG(Saio, "Incomplete Box (have:%" PRIu64 " need:%" PRIu64 ")",
922 : (uint64_t)reader->Remaining(), (uint64_t)need);
923 0 : return;
924 : }
925 0 : if (flags & 1) {
926 0 : mAuxInfoType = reader->ReadU32();
927 0 : mAuxInfoTypeParameter = reader->ReadU32();
928 : }
929 0 : size_t count = reader->ReadU32();
930 0 : need = (version ? sizeof(uint64_t) : sizeof(uint32_t)) * count;
931 0 : if (reader->Remaining() < need) {
932 0 : LOG(Saio, "Incomplete Box (have:%" PRIu64 " need:%" PRIu64 ")",
933 : (uint64_t)reader->Remaining(), (uint64_t)need);
934 0 : return;
935 : }
936 0 : if (!mOffsets.SetCapacity(count, fallible)) {
937 0 : LOG(Saiz, "OOM");
938 0 : return;
939 : }
940 0 : if (version == 0) {
941 0 : for (size_t i = 0; i < count; i++) {
942 0 : MOZ_ALWAYS_TRUE(mOffsets.AppendElement(reader->ReadU32(), fallible));
943 : }
944 : } else {
945 0 : for (size_t i = 0; i < count; i++) {
946 0 : MOZ_ALWAYS_TRUE(mOffsets.AppendElement(reader->ReadU64(), fallible));
947 : }
948 : }
949 0 : mValid = true;
950 : }
951 :
952 0 : Sbgp::Sbgp(Box& aBox)
953 : {
954 0 : BoxReader reader(aBox);
955 :
956 0 : if (!reader->CanReadType<uint32_t>()) {
957 0 : LOG(Sbgp, "Incomplete Box (missing flags)");
958 0 : return;
959 : }
960 :
961 0 : uint32_t flags = reader->ReadU32();
962 0 : const uint8_t version = flags >> 24;
963 0 : flags = flags & 0xffffff;
964 :
965 : // Make sure we have enough bytes to read as far as the count.
966 0 : uint32_t need = (version == 1 ? sizeof(uint32_t) : 0) + sizeof(uint32_t) * 2;
967 0 : if (reader->Remaining() < need) {
968 0 : LOG(Sbgp, "Incomplete Box (have:%" PRIu64 ", need:%" PRIu64 ")",
969 : (uint64_t)reader->Remaining(), (uint64_t)need);
970 0 : return;
971 : }
972 :
973 0 : mGroupingType = reader->ReadU32();
974 :
975 0 : if (version == 1) {
976 0 : mGroupingTypeParam = reader->Read32();
977 : }
978 :
979 0 : uint32_t count = reader->ReadU32();
980 :
981 : // Make sure we can read all the entries.
982 0 : need = sizeof(uint32_t) * 2 * count;
983 0 : if (reader->Remaining() < need) {
984 0 : LOG(Sbgp, "Incomplete Box (have:%" PRIu64 ", need:%" PRIu64 "). Failed to read entries",
985 : (uint64_t)reader->Remaining(), (uint64_t)need);
986 0 : return;
987 : }
988 :
989 0 : for (uint32_t i = 0; i < count; i++) {
990 0 : uint32_t sampleCount = reader->ReadU32();
991 0 : uint32_t groupDescriptionIndex = reader->ReadU32();
992 :
993 0 : SampleToGroupEntry entry(sampleCount, groupDescriptionIndex);
994 0 : mEntries.AppendElement(entry);
995 : }
996 :
997 0 : mValid = true;
998 : }
999 :
1000 0 : Sgpd::Sgpd(Box& aBox)
1001 : {
1002 0 : BoxReader reader(aBox);
1003 :
1004 0 : if (!reader->CanReadType<uint32_t>()) {
1005 0 : LOG(Sgpd, "Incomplete Box (missing flags)");
1006 0 : return;
1007 : }
1008 :
1009 0 : uint32_t flags = reader->ReadU32();
1010 0 : const uint8_t version = flags >> 24;
1011 0 : flags = flags & 0xffffff;
1012 :
1013 0 : uint32_t need = ((flags & 1) ? sizeof(uint32_t) : 0) + sizeof(uint32_t) * 2;
1014 0 : if (reader->Remaining() < need) {
1015 0 : LOG(Sgpd, "Incomplete Box (have:%" PRIu64 " need:%" PRIu64 ")",
1016 : (uint64_t)reader->Remaining(), (uint64_t)need);
1017 0 : return;
1018 : }
1019 :
1020 0 : mGroupingType = reader->ReadU32();
1021 :
1022 0 : const uint32_t entrySize = sizeof(uint32_t) + kKeyIdSize;
1023 0 : uint32_t defaultLength = 0;
1024 :
1025 0 : if (version == 1) {
1026 0 : defaultLength = reader->ReadU32();
1027 0 : if (defaultLength < entrySize && defaultLength != 0) {
1028 0 : return;
1029 : }
1030 : }
1031 :
1032 0 : uint32_t count = reader->ReadU32();
1033 :
1034 : // Make sure we have sufficient remaining bytes to read the entries.
1035 0 : need =
1036 0 : count * (sizeof(uint32_t) * (version == 1 && defaultLength == 0 ? 2 : 1) +
1037 0 : kKeyIdSize * sizeof(uint8_t));
1038 0 : if (reader->Remaining() < need) {
1039 0 : LOG(Sgpd, "Incomplete Box (have:%" PRIu64 " need:%" PRIu64 "). Failed to read entries",
1040 : (uint64_t)reader->Remaining(), (uint64_t)need);
1041 0 : return;
1042 : }
1043 0 : for (uint32_t i = 0; i < count; ++i) {
1044 0 : if (version == 1 && defaultLength == 0) {
1045 0 : uint32_t descriptionLength = reader->ReadU32();
1046 0 : if (descriptionLength < entrySize) {
1047 0 : return;
1048 : }
1049 : }
1050 :
1051 0 : CencSampleEncryptionInfoEntry entry;
1052 0 : bool valid = entry.Init(reader);
1053 0 : if (!valid) {
1054 0 : return;
1055 : }
1056 0 : mEntries.AppendElement(entry);
1057 : }
1058 :
1059 0 : mValid = true;
1060 : }
1061 :
1062 0 : bool CencSampleEncryptionInfoEntry::Init(BoxReader& aReader)
1063 : {
1064 : // Skip a reserved byte.
1065 0 : aReader->ReadU8();
1066 :
1067 0 : uint8_t possiblePatternInfo = aReader->ReadU8();
1068 0 : uint8_t flag = aReader->ReadU8();
1069 :
1070 0 : mIVSize = aReader->ReadU8();
1071 :
1072 : // Read the key id.
1073 0 : for (uint32_t i = 0; i < kKeyIdSize; ++i) {
1074 0 : mKeyId.AppendElement(aReader->ReadU8());
1075 : }
1076 :
1077 0 : mIsEncrypted = flag != 0;
1078 :
1079 0 : if (mIsEncrypted) {
1080 0 : if (mIVSize != 8 && mIVSize != 16) {
1081 0 : return false;
1082 : }
1083 0 : } else if (mIVSize != 0) {
1084 0 : return false;
1085 : }
1086 :
1087 0 : return true;
1088 : }
1089 :
1090 : #undef LOG
1091 : }
|