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 "MoofParser.h"
6 #include "Box.h"
7 #include "SinfParser.h"
8 #include <limits>
9 #include "MP4Interval.h"
10 
11 #include "mozilla/CheckedInt.h"
12 #include "mozilla/HelperMacros.h"
13 #include "mozilla/Logging.h"
14 
15 #if defined(MOZ_FMP4)
16 extern mozilla::LogModule* GetDemuxerLog();
17 
18 #  define LOG_ERROR(name, arg, ...)                \
19     MOZ_LOG(                                       \
20         GetDemuxerLog(), mozilla::LogLevel::Error, \
21         (MOZ_STRINGIFY(name) "(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
22 #  define LOG_WARN(name, arg, ...)                   \
23     MOZ_LOG(                                         \
24         GetDemuxerLog(), mozilla::LogLevel::Warning, \
25         (MOZ_STRINGIFY(name) "(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
26 #  define LOG_DEBUG(name, arg, ...)                \
27     MOZ_LOG(                                       \
28         GetDemuxerLog(), mozilla::LogLevel::Debug, \
29         (MOZ_STRINGIFY(name) "(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
30 
31 #else
32 #  define LOG_ERROR(...)
33 #  define LOG_WARN(...)
34 #  define LOG_DEBUG(...)
35 #endif
36 
37 namespace mozilla {
38 
39 const uint32_t kKeyIdSize = 16;
40 
41 // We ensure there are no gaps in samples' CTS between the last sample in a
42 // Moof, and the first sample in the next Moof, if they're within these many
43 // Microseconds of each other.
44 const Microseconds CROSS_MOOF_CTS_MERGE_THRESHOLD = 1;
45 
RebuildFragmentedIndex(const MediaByteRangeSet & aByteRanges)46 bool MoofParser::RebuildFragmentedIndex(const MediaByteRangeSet& aByteRanges) {
47   BoxContext context(mSource, aByteRanges);
48   return RebuildFragmentedIndex(context);
49 }
50 
RebuildFragmentedIndex(const MediaByteRangeSet & aByteRanges,bool * aCanEvict)51 bool MoofParser::RebuildFragmentedIndex(const MediaByteRangeSet& aByteRanges,
52                                         bool* aCanEvict) {
53   MOZ_ASSERT(aCanEvict);
54   if (*aCanEvict && mMoofs.Length() > 1) {
55     MOZ_ASSERT(mMoofs.Length() == mMediaRanges.Length());
56     mMoofs.RemoveElementsAt(0, mMoofs.Length() - 1);
57     mMediaRanges.RemoveElementsAt(0, mMediaRanges.Length() - 1);
58     *aCanEvict = true;
59   } else {
60     *aCanEvict = false;
61   }
62   return RebuildFragmentedIndex(aByteRanges);
63 }
64 
RebuildFragmentedIndex(BoxContext & aContext)65 bool MoofParser::RebuildFragmentedIndex(BoxContext& aContext) {
66   LOG_DEBUG(
67       Moof,
68       "Starting, mTrackParseMode=%s, track#=%" PRIu32
69       " (ignore if multitrack).",
70       mTrackParseMode.is<ParseAllTracks>() ? "multitrack" : "single track",
71       mTrackParseMode.is<ParseAllTracks>() ? 0
72                                            : mTrackParseMode.as<uint32_t>());
73   bool foundValidMoof = false;
74 
75   for (Box box(&aContext, mOffset); box.IsAvailable(); box = box.Next()) {
76     if (box.IsType("moov") && mInitRange.IsEmpty()) {
77       mInitRange = MediaByteRange(0, box.Range().mEnd);
78       ParseMoov(box);
79     } else if (box.IsType("moof")) {
80       Moof moof(box, mTrackParseMode, mTrex, mMvhd, mMdhd, mEdts, mSinf,
81                 &mLastDecodeTime, mIsAudio, mTracksEndCts);
82 
83       if (!moof.IsValid() && !box.Next().IsAvailable()) {
84         // Moof isn't valid abort search for now.
85         LOG_WARN(Moof,
86                  "Could not find valid moof, moof may not be complete yet.");
87         break;
88       }
89 
90       if (!mMoofs.IsEmpty()) {
91         // Stitch time ranges together in the case of a (hopefully small) time
92         // range gap between moofs.
93         mMoofs.LastElement().FixRounding(moof);
94       }
95 
96       mMediaRanges.AppendElement(moof.mRange);
97       mMoofs.AppendElement(std::move(moof));
98       foundValidMoof = true;
99     } else if (box.IsType("mdat") && !Moofs().IsEmpty()) {
100       // Check if we have all our data from last moof.
101       Moof& moof = Moofs().LastElement();
102       media::Interval<int64_t> datarange(moof.mMdatRange.mStart,
103                                          moof.mMdatRange.mEnd, 0);
104       media::Interval<int64_t> mdat(box.Range().mStart, box.Range().mEnd, 0);
105       if (datarange.Intersects(mdat)) {
106         mMediaRanges.LastElement() =
107             mMediaRanges.LastElement().Span(box.Range());
108       }
109     }
110     mOffset = box.NextOffset();
111   }
112   MOZ_ASSERT(mTrackParseMode.is<ParseAllTracks>() ||
113                  mTrex.mTrackId == mTrackParseMode.as<uint32_t>(),
114              "If not parsing all tracks, mTrex should have the same track id "
115              "as the track being parsed.");
116   LOG_DEBUG(Moof, "Done, foundValidMoof=%s.",
117             foundValidMoof ? "true" : "false");
118   return foundValidMoof;
119 }
120 
FirstCompleteMediaHeader()121 MediaByteRange MoofParser::FirstCompleteMediaHeader() {
122   if (Moofs().IsEmpty()) {
123     return MediaByteRange();
124   }
125   return Moofs()[0].mRange;
126 }
127 
FirstCompleteMediaSegment()128 MediaByteRange MoofParser::FirstCompleteMediaSegment() {
129   for (uint32_t i = 0; i < mMediaRanges.Length(); i++) {
130     if (mMediaRanges[i].Contains(Moofs()[i].mMdatRange)) {
131       return mMediaRanges[i];
132     }
133   }
134   return MediaByteRange();
135 }
136 
137 DDLoggedTypeDeclNameAndBase(BlockingStream, ByteStream);
138 
139 class BlockingStream : public ByteStream,
140                        public DecoderDoctorLifeLogger<BlockingStream> {
141  public:
BlockingStream(ByteStream * aStream)142   explicit BlockingStream(ByteStream* aStream) : mStream(aStream) {
143     DDLINKCHILD("stream", aStream);
144   }
145 
ReadAt(int64_t offset,void * data,size_t size,size_t * bytes_read)146   bool ReadAt(int64_t offset, void* data, size_t size,
147               size_t* bytes_read) override {
148     return mStream->ReadAt(offset, data, size, bytes_read);
149   }
150 
CachedReadAt(int64_t offset,void * data,size_t size,size_t * bytes_read)151   bool CachedReadAt(int64_t offset, void* data, size_t size,
152                     size_t* bytes_read) override {
153     return mStream->ReadAt(offset, data, size, bytes_read);
154   }
155 
Length(int64_t * size)156   virtual bool Length(int64_t* size) override { return mStream->Length(size); }
157 
158  private:
159   RefPtr<ByteStream> mStream;
160 };
161 
BlockingReadNextMoof()162 bool MoofParser::BlockingReadNextMoof() {
163   LOG_DEBUG(Moof, "Starting.");
164   int64_t length = std::numeric_limits<int64_t>::max();
165   mSource->Length(&length);
166   RefPtr<BlockingStream> stream = new BlockingStream(mSource);
167   MediaByteRangeSet byteRanges(MediaByteRange(0, length));
168 
169   BoxContext context(stream, byteRanges);
170   for (Box box(&context, mOffset); box.IsAvailable(); box = box.Next()) {
171     if (box.IsType("moof")) {
172       MediaByteRangeSet parseByteRanges(
173           MediaByteRange(mOffset, box.Range().mEnd));
174       BoxContext parseContext(stream, parseByteRanges);
175       if (RebuildFragmentedIndex(parseContext)) {
176         LOG_DEBUG(Moof, "Succeeded on RebuildFragmentedIndex, returning true.");
177         return true;
178       }
179     }
180   }
181   LOG_DEBUG(Moof, "Couldn't read next moof, returning false.");
182   return false;
183 }
184 
ScanForMetadata(mozilla::MediaByteRange & aMoov)185 void MoofParser::ScanForMetadata(mozilla::MediaByteRange& aMoov) {
186   LOG_DEBUG(Moof, "Starting.");
187   int64_t length = std::numeric_limits<int64_t>::max();
188   mSource->Length(&length);
189   MediaByteRangeSet byteRanges;
190   byteRanges += MediaByteRange(0, length);
191   RefPtr<BlockingStream> stream = new BlockingStream(mSource);
192 
193   BoxContext context(stream, byteRanges);
194   for (Box box(&context, mOffset); box.IsAvailable(); box = box.Next()) {
195     if (box.IsType("moov")) {
196       aMoov = box.Range();
197       break;
198     }
199   }
200   mInitRange = aMoov;
201   LOG_DEBUG(Moof,
202             "Done, mInitRange.mStart=%" PRIi64 ", mInitRange.mEnd=%" PRIi64,
203             mInitRange.mStart, mInitRange.mEnd);
204 }
205 
Metadata()206 already_AddRefed<mozilla::MediaByteBuffer> MoofParser::Metadata() {
207   LOG_DEBUG(Moof, "Starting.");
208   MediaByteRange moov;
209   ScanForMetadata(moov);
210   CheckedInt<MediaByteBuffer::size_type> moovLength = moov.Length();
211   if (!moovLength.isValid() || !moovLength.value()) {
212     // No moov, or cannot be used as array size.
213     LOG_WARN(Moof,
214              "Did not get usable moov length while trying to parse Metadata.");
215     return nullptr;
216   }
217 
218   RefPtr<MediaByteBuffer> metadata = new MediaByteBuffer();
219   if (!metadata->SetLength(moovLength.value(), fallible)) {
220     LOG_ERROR(Moof, "OOM");
221     return nullptr;
222   }
223 
224   RefPtr<BlockingStream> stream = new BlockingStream(mSource);
225   size_t read;
226   bool rv = stream->ReadAt(moov.mStart, metadata->Elements(),
227                            moovLength.value(), &read);
228   if (!rv || read != moovLength.value()) {
229     LOG_WARN(Moof, "Failed to read moov while trying to parse Metadata.");
230     return nullptr;
231   }
232   LOG_DEBUG(Moof, "Done, found metadata.");
233   return metadata.forget();
234 }
235 
GetCompositionRange(const MediaByteRangeSet & aByteRanges)236 MP4Interval<Microseconds> MoofParser::GetCompositionRange(
237     const MediaByteRangeSet& aByteRanges) {
238   LOG_DEBUG(Moof, "Starting.");
239   MP4Interval<Microseconds> compositionRange;
240   BoxContext context(mSource, aByteRanges);
241   for (size_t i = 0; i < mMoofs.Length(); i++) {
242     Moof& moof = mMoofs[i];
243     Box box(&context, moof.mRange.mStart);
244     if (box.IsAvailable()) {
245       compositionRange = compositionRange.Extents(moof.mTimeRange);
246     }
247   }
248   LOG_DEBUG(Moof,
249             "Done, compositionRange.start=%" PRIi64
250             ", compositionRange.end=%" PRIi64 ".",
251             compositionRange.start, compositionRange.end);
252   return compositionRange;
253 }
254 
ReachedEnd()255 bool MoofParser::ReachedEnd() {
256   int64_t length;
257   return mSource->Length(&length) && mOffset == length;
258 }
259 
ParseMoov(Box & aBox)260 void MoofParser::ParseMoov(Box& aBox) {
261   LOG_DEBUG(Moof, "Starting.");
262   for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
263     if (box.IsType("mvhd")) {
264       mMvhd = Mvhd(box);
265     } else if (box.IsType("trak")) {
266       ParseTrak(box);
267     } else if (box.IsType("mvex")) {
268       ParseMvex(box);
269     }
270   }
271   LOG_DEBUG(Moof, "Done.");
272 }
273 
ParseTrak(Box & aBox)274 void MoofParser::ParseTrak(Box& aBox) {
275   LOG_DEBUG(Trak, "Starting.");
276   Tkhd tkhd;
277   for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
278     if (box.IsType("tkhd")) {
279       tkhd = Tkhd(box);
280     } else if (box.IsType("mdia")) {
281       if (mTrackParseMode.is<ParseAllTracks>() ||
282           tkhd.mTrackId == mTrackParseMode.as<uint32_t>()) {
283         ParseMdia(box);
284       }
285     } else if (box.IsType("edts") &&
286                (mTrackParseMode.is<ParseAllTracks>() ||
287                 tkhd.mTrackId == mTrackParseMode.as<uint32_t>())) {
288       mEdts = Edts(box);
289     }
290   }
291   LOG_DEBUG(Trak, "Done.");
292 }
293 
ParseMdia(Box & aBox)294 void MoofParser::ParseMdia(Box& aBox) {
295   LOG_DEBUG(Mdia, "Starting.");
296   for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
297     if (box.IsType("mdhd")) {
298       mMdhd = Mdhd(box);
299     } else if (box.IsType("minf")) {
300       ParseMinf(box);
301     }
302   }
303   LOG_DEBUG(Mdia, "Done.");
304 }
305 
ParseMvex(Box & aBox)306 void MoofParser::ParseMvex(Box& aBox) {
307   LOG_DEBUG(Mvex, "Starting.");
308   for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
309     if (box.IsType("trex")) {
310       Trex trex = Trex(box);
311       if (mTrackParseMode.is<ParseAllTracks>() ||
312           trex.mTrackId == mTrackParseMode.as<uint32_t>()) {
313         mTrex = trex;
314       }
315     }
316   }
317   LOG_DEBUG(Mvex, "Done.");
318 }
319 
ParseMinf(Box & aBox)320 void MoofParser::ParseMinf(Box& aBox) {
321   LOG_DEBUG(Minf, "Starting.");
322   for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
323     if (box.IsType("stbl")) {
324       ParseStbl(box);
325     }
326   }
327   LOG_DEBUG(Minf, "Done.");
328 }
329 
ParseStbl(Box & aBox)330 void MoofParser::ParseStbl(Box& aBox) {
331   LOG_DEBUG(Stbl, "Starting.");
332   for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
333     if (box.IsType("stsd")) {
334       ParseStsd(box);
335     } else if (box.IsType("sgpd")) {
336       Sgpd sgpd(box);
337       if (sgpd.IsValid() && sgpd.mGroupingType == "seig") {
338         mTrackSampleEncryptionInfoEntries.Clear();
339         if (!mTrackSampleEncryptionInfoEntries.AppendElements(
340                 sgpd.mEntries, mozilla::fallible)) {
341           LOG_ERROR(Stbl, "OOM");
342           return;
343         }
344       }
345     } else if (box.IsType("sbgp")) {
346       Sbgp sbgp(box);
347       if (sbgp.IsValid() && sbgp.mGroupingType == "seig") {
348         mTrackSampleToGroupEntries.Clear();
349         if (!mTrackSampleToGroupEntries.AppendElements(sbgp.mEntries,
350                                                        mozilla::fallible)) {
351           LOG_ERROR(Stbl, "OOM");
352           return;
353         }
354       }
355     }
356   }
357   LOG_DEBUG(Stbl, "Done.");
358 }
359 
ParseStsd(Box & aBox)360 void MoofParser::ParseStsd(Box& aBox) {
361   LOG_DEBUG(Stsd, "Starting.");
362   if (mTrackParseMode.is<ParseAllTracks>()) {
363     // It is not a sane operation to try and map sample description boxes from
364     // multiple tracks onto the parser, which is modeled around storing metadata
365     // for a single track.
366     LOG_DEBUG(Stsd, "Early return due to multitrack parser.");
367     return;
368   }
369   MOZ_ASSERT(
370       mSampleDescriptions.IsEmpty(),
371       "Shouldn't have any sample descriptions yet when starting to parse stsd");
372   uint32_t numberEncryptedEntries = 0;
373   for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
374     SampleDescriptionEntry sampleDescriptionEntry{false};
375     if (box.IsType("encv") || box.IsType("enca")) {
376       ParseEncrypted(box);
377       sampleDescriptionEntry.mIsEncryptedEntry = true;
378       numberEncryptedEntries++;
379     }
380     if (!mSampleDescriptions.AppendElement(sampleDescriptionEntry,
381                                            mozilla::fallible)) {
382       LOG_ERROR(Stsd, "OOM");
383       return;
384     }
385   }
386   if (mSampleDescriptions.IsEmpty()) {
387     LOG_WARN(Stsd,
388              "No sample description entries found while parsing Stsd! This "
389              "shouldn't happen, as the spec requires one for each track!");
390   }
391   if (numberEncryptedEntries > 1) {
392     LOG_WARN(Stsd,
393              "More than one encrypted sample description entry found while "
394              "parsing track! We don't expect this, and it will likely break "
395              "during fragment look up!");
396   }
397   LOG_DEBUG(Stsd,
398             "Done, numberEncryptedEntries=%" PRIu32
399             ", mSampleDescriptions.Length=%zu",
400             numberEncryptedEntries, mSampleDescriptions.Length());
401 }
402 
ParseEncrypted(Box & aBox)403 void MoofParser::ParseEncrypted(Box& aBox) {
404   LOG_DEBUG(Moof, "Starting.");
405   for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
406     // Some MP4 files have been found to have multiple sinf boxes in the same
407     // enc* box. This does not match spec anyway, so just choose the first
408     // one that parses properly.
409     if (box.IsType("sinf")) {
410       mSinf = Sinf(box);
411 
412       if (mSinf.IsValid()) {
413         break;
414       }
415     }
416   }
417   LOG_DEBUG(Moof, "Done.");
418 }
419 
420 class CtsComparator {
421  public:
Equals(Sample * const aA,Sample * const aB) const422   bool Equals(Sample* const aA, Sample* const aB) const {
423     return aA->mCompositionRange.start == aB->mCompositionRange.start;
424   }
LessThan(Sample * const aA,Sample * const aB) const425   bool LessThan(Sample* const aA, Sample* const aB) const {
426     return aA->mCompositionRange.start < aB->mCompositionRange.start;
427   }
428 };
429 
Moof(Box & aBox,const TrackParseMode & aTrackParseMode,Trex & aTrex,Mvhd & aMvhd,Mdhd & aMdhd,Edts & aEdts,Sinf & aSinf,uint64_t * aDecodeTime,bool aIsAudio,nsTArray<TrackEndCts> & aTracksEndCts)430 Moof::Moof(Box& aBox, const TrackParseMode& aTrackParseMode, Trex& aTrex,
431            Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf,
432            uint64_t* aDecodeTime, bool aIsAudio,
433            nsTArray<TrackEndCts>& aTracksEndCts)
434     : mRange(aBox.Range()), mTfhd(aTrex), mMaxRoundingError(35000) {
435   LOG_DEBUG(
436       Moof,
437       "Starting, aTrackParseMode=%s, track#=%" PRIu32
438       " (ignore if multitrack).",
439       aTrackParseMode.is<ParseAllTracks>() ? "multitrack" : "single track",
440       aTrackParseMode.is<ParseAllTracks>() ? 0
441                                            : aTrackParseMode.as<uint32_t>());
442   MOZ_ASSERT(aTrackParseMode.is<ParseAllTracks>() ||
443                  aTrex.mTrackId == aTrackParseMode.as<uint32_t>(),
444              "If not parsing all tracks, aTrex should have the same track id "
445              "as the track being parsed.");
446   nsTArray<Box> psshBoxes;
447   for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
448     if (box.IsType("traf")) {
449       ParseTraf(box, aTrackParseMode, aTrex, aMvhd, aMdhd, aEdts, aSinf,
450                 aDecodeTime, aIsAudio);
451     }
452     if (box.IsType("pssh")) {
453       psshBoxes.AppendElement(box);
454     }
455   }
456 
457   // The EME spec requires that PSSH boxes which are contiguous in the
458   // file are dispatched to the media element in a single "encrypted" event.
459   // So append contiguous boxes here.
460   for (size_t i = 0; i < psshBoxes.Length(); ++i) {
461     Box box = psshBoxes[i];
462     if (i == 0 || box.Offset() != psshBoxes[i - 1].NextOffset()) {
463       mPsshes.AppendElement();
464     }
465     nsTArray<uint8_t>& pssh = mPsshes.LastElement();
466     pssh.AppendElements(std::move(box.ReadCompleteBox()));
467   }
468 
469   if (IsValid()) {
470     if (mIndex.Length()) {
471       // Ensure the samples are contiguous with no gaps.
472       nsTArray<Sample*> ctsOrder;
473       for (auto& sample : mIndex) {
474         ctsOrder.AppendElement(&sample);
475       }
476       ctsOrder.Sort(CtsComparator());
477 
478       for (size_t i = 1; i < ctsOrder.Length(); i++) {
479         ctsOrder[i - 1]->mCompositionRange.end =
480             ctsOrder[i]->mCompositionRange.start;
481       }
482 
483       // Ensure that there are no gaps between the first sample in this
484       // Moof and the preceeding Moof.
485       if (!ctsOrder.IsEmpty()) {
486         bool found = false;
487         // Track ID of the track we're parsing.
488         const uint32_t trackId = aTrex.mTrackId;
489         // Find the previous CTS end time of Moof preceeding the Moofs we just
490         // parsed, for the track we're parsing.
491         for (auto& prevCts : aTracksEndCts) {
492           if (prevCts.mTrackId == trackId) {
493             // We have previously parsed a Moof for this track. Smooth the gap
494             // between samples for this track across the Moof bounary.
495             if (ctsOrder[0]->mCompositionRange.start > prevCts.mCtsEndTime &&
496                 ctsOrder[0]->mCompositionRange.start - prevCts.mCtsEndTime <=
497                     CROSS_MOOF_CTS_MERGE_THRESHOLD) {
498               ctsOrder[0]->mCompositionRange.start = prevCts.mCtsEndTime;
499             }
500             prevCts.mCtsEndTime = ctsOrder.LastElement()->mCompositionRange.end;
501             found = true;
502             break;
503           }
504         }
505         if (!found) {
506           // We've not parsed a Moof for this track yet. Save its CTS end
507           // time for the next Moof we parse.
508           aTracksEndCts.AppendElement(TrackEndCts(
509               trackId, ctsOrder.LastElement()->mCompositionRange.end));
510         }
511       }
512 
513       // In MP4, the duration of a sample is defined as the delta between two
514       // decode timestamps. The operation above has updated the duration of each
515       // sample as a Sample's duration is mCompositionRange.end -
516       // mCompositionRange.start MSE's TrackBuffersManager expects dts that
517       // increased by the sample's duration, so we rewrite the dts accordingly.
518       int64_t presentationDuration =
519           ctsOrder.LastElement()->mCompositionRange.end -
520           ctsOrder[0]->mCompositionRange.start;
521       auto decodeOffset =
522           aMdhd.ToMicroseconds((int64_t)*aDecodeTime - aEdts.mMediaStart);
523       auto offsetOffset = aMvhd.ToMicroseconds(aEdts.mEmptyOffset);
524       int64_t endDecodeTime =
525           (decodeOffset.isOk() && offsetOffset.isOk())
526               ? decodeOffset.unwrap() + offsetOffset.unwrap()
527               : 0;
528       int64_t decodeDuration = endDecodeTime - mIndex[0].mDecodeTime;
529       double adjust = !!presentationDuration
530                           ? (double)decodeDuration / presentationDuration
531                           : 0;
532       int64_t dtsOffset = mIndex[0].mDecodeTime;
533       int64_t compositionDuration = 0;
534       // Adjust the dts, ensuring that the new adjusted dts will never be
535       // greater than decodeTime (the next moof's decode start time).
536       for (auto& sample : mIndex) {
537         sample.mDecodeTime = dtsOffset + int64_t(compositionDuration * adjust);
538         compositionDuration += sample.mCompositionRange.Length();
539       }
540       mTimeRange = MP4Interval<Microseconds>(
541           ctsOrder[0]->mCompositionRange.start,
542           ctsOrder.LastElement()->mCompositionRange.end);
543     }
544     ProcessCencAuxInfo(aSinf.mDefaultEncryptionType);
545   }
546   LOG_DEBUG(Moof, "Done.");
547 }
548 
GetAuxInfo(AtomType aType,FallibleTArray<MediaByteRange> * aByteRanges)549 bool Moof::GetAuxInfo(AtomType aType,
550                       FallibleTArray<MediaByteRange>* aByteRanges) {
551   LOG_DEBUG(Moof, "Starting.");
552   aByteRanges->Clear();
553 
554   Saiz* saiz = nullptr;
555   for (int i = 0;; i++) {
556     if (i == mSaizs.Length()) {
557       LOG_DEBUG(Moof, "Could not find saiz matching aType. Returning false.");
558       return false;
559     }
560     if (mSaizs[i].mAuxInfoType == aType) {
561       saiz = &mSaizs[i];
562       break;
563     }
564   }
565   Saio* saio = nullptr;
566   for (int i = 0;; i++) {
567     if (i == mSaios.Length()) {
568       LOG_DEBUG(Moof, "Could not find saio matching aType. Returning false.");
569       return false;
570     }
571     if (mSaios[i].mAuxInfoType == aType) {
572       saio = &mSaios[i];
573       break;
574     }
575   }
576 
577   if (saio->mOffsets.Length() == 1) {
578     if (!aByteRanges->SetCapacity(saiz->mSampleInfoSize.Length(),
579                                   mozilla::fallible)) {
580       LOG_ERROR(Moof, "OOM");
581       return false;
582     }
583     uint64_t offset = mRange.mStart + saio->mOffsets[0];
584     for (size_t i = 0; i < saiz->mSampleInfoSize.Length(); i++) {
585       if (!aByteRanges->AppendElement(
586               MediaByteRange(offset, offset + saiz->mSampleInfoSize[i]),
587               mozilla::fallible)) {
588         LOG_ERROR(Moof, "OOM");
589         return false;
590       }
591       offset += saiz->mSampleInfoSize[i];
592     }
593     LOG_DEBUG(
594         Moof,
595         "Saio has 1 entry. aByteRanges populated accordingly. Returning true.");
596     return true;
597   }
598 
599   if (saio->mOffsets.Length() == saiz->mSampleInfoSize.Length()) {
600     if (!aByteRanges->SetCapacity(saiz->mSampleInfoSize.Length(),
601                                   mozilla::fallible)) {
602       LOG_ERROR(Moof, "OOM");
603       return false;
604     }
605     for (size_t i = 0; i < saio->mOffsets.Length(); i++) {
606       uint64_t offset = mRange.mStart + saio->mOffsets[i];
607       if (!aByteRanges->AppendElement(
608               MediaByteRange(offset, offset + saiz->mSampleInfoSize[i]),
609               mozilla::fallible)) {
610         LOG_ERROR(Moof, "OOM");
611         return false;
612       }
613     }
614     LOG_DEBUG(
615         Moof,
616         "Saio and saiz have same number of entries. aByteRanges populated "
617         "accordingly. Returning true.");
618     return true;
619   }
620 
621   LOG_DEBUG(Moof,
622             "Moof::GetAuxInfo could not find any Aux info, returning false.");
623   return false;
624 }
625 
ProcessCencAuxInfo(AtomType aScheme)626 bool Moof::ProcessCencAuxInfo(AtomType aScheme) {
627   LOG_DEBUG(Moof, "Starting.");
628   FallibleTArray<MediaByteRange> cencRanges;
629   if (!GetAuxInfo(aScheme, &cencRanges) ||
630       cencRanges.Length() != mIndex.Length()) {
631     LOG_DEBUG(Moof, "Couldn't find cenc aux info.");
632     return false;
633   }
634   for (int i = 0; i < cencRanges.Length(); i++) {
635     mIndex[i].mCencRange = cencRanges[i];
636   }
637   LOG_DEBUG(Moof, "Found cenc aux info and stored on index.");
638   return true;
639 }
640 
ParseTraf(Box & aBox,const TrackParseMode & aTrackParseMode,Trex & aTrex,Mvhd & aMvhd,Mdhd & aMdhd,Edts & aEdts,Sinf & aSinf,uint64_t * aDecodeTime,bool aIsAudio)641 void Moof::ParseTraf(Box& aBox, const TrackParseMode& aTrackParseMode,
642                      Trex& aTrex, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts,
643                      Sinf& aSinf, uint64_t* aDecodeTime, bool aIsAudio) {
644   LOG_DEBUG(
645       Traf,
646       "Starting, aTrackParseMode=%s, track#=%" PRIu32
647       " (ignore if multitrack).",
648       aTrackParseMode.is<ParseAllTracks>() ? "multitrack" : "single track",
649       aTrackParseMode.is<ParseAllTracks>() ? 0
650                                            : aTrackParseMode.as<uint32_t>());
651   MOZ_ASSERT(aDecodeTime);
652   MOZ_ASSERT(aTrackParseMode.is<ParseAllTracks>() ||
653                  aTrex.mTrackId == aTrackParseMode.as<uint32_t>(),
654              "If not parsing all tracks, aTrex should have the same track id "
655              "as the track being parsed.");
656   Tfdt tfdt;
657 
658   for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
659     if (box.IsType("tfhd")) {
660       mTfhd = Tfhd(box, aTrex);
661     } else if (aTrackParseMode.is<ParseAllTracks>() ||
662                mTfhd.mTrackId == aTrackParseMode.as<uint32_t>()) {
663       if (box.IsType("tfdt")) {
664         tfdt = Tfdt(box);
665       } else if (box.IsType("sgpd")) {
666         Sgpd sgpd(box);
667         if (sgpd.IsValid() && sgpd.mGroupingType == "seig") {
668           mFragmentSampleEncryptionInfoEntries.Clear();
669           if (!mFragmentSampleEncryptionInfoEntries.AppendElements(
670                   sgpd.mEntries, mozilla::fallible)) {
671             LOG_ERROR(Moof, "OOM");
672             return;
673           }
674         }
675       } else if (box.IsType("sbgp")) {
676         Sbgp sbgp(box);
677         if (sbgp.IsValid() && sbgp.mGroupingType == "seig") {
678           mFragmentSampleToGroupEntries.Clear();
679           if (!mFragmentSampleToGroupEntries.AppendElements(
680                   sbgp.mEntries, mozilla::fallible)) {
681             LOG_ERROR(Moof, "OOM");
682             return;
683           }
684         }
685       } else if (box.IsType("saiz")) {
686         if (!mSaizs.AppendElement(Saiz(box, aSinf.mDefaultEncryptionType),
687                                   mozilla::fallible)) {
688           LOG_ERROR(Moof, "OOM");
689           return;
690         }
691       } else if (box.IsType("saio")) {
692         if (!mSaios.AppendElement(Saio(box, aSinf.mDefaultEncryptionType),
693                                   mozilla::fallible)) {
694           LOG_ERROR(Moof, "OOM");
695           return;
696         }
697       }
698     }
699   }
700   if (aTrackParseMode.is<uint32_t>() &&
701       mTfhd.mTrackId != aTrackParseMode.as<uint32_t>()) {
702     LOG_DEBUG(Traf,
703               "Early return as not multitrack parser and track id didn't match "
704               "mTfhd.mTrackId=%" PRIu32,
705               mTfhd.mTrackId);
706     return;
707   }
708   // Now search for TRUN boxes.
709   uint64_t decodeTime =
710       tfdt.IsValid() ? tfdt.mBaseMediaDecodeTime : *aDecodeTime;
711   for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
712     if (box.IsType("trun")) {
713       if (ParseTrun(box, aMvhd, aMdhd, aEdts, &decodeTime, aIsAudio).isOk()) {
714         mValid = true;
715       } else {
716         LOG_WARN(Moof, "ParseTrun failed");
717         mValid = false;
718         break;
719       }
720     }
721   }
722   *aDecodeTime = decodeTime;
723   LOG_DEBUG(Traf, "Done, setting aDecodeTime=%." PRIu64 ".", decodeTime);
724 }
725 
FixRounding(const Moof & aMoof)726 void Moof::FixRounding(const Moof& aMoof) {
727   Microseconds gap = aMoof.mTimeRange.start - mTimeRange.end;
728   if (gap > 0 && gap <= mMaxRoundingError) {
729     mTimeRange.end = aMoof.mTimeRange.start;
730   }
731 }
732 
ParseTrun(Box & aBox,Mvhd & aMvhd,Mdhd & aMdhd,Edts & aEdts,uint64_t * aDecodeTime,bool aIsAudio)733 Result<Ok, nsresult> Moof::ParseTrun(Box& aBox, Mvhd& aMvhd, Mdhd& aMdhd,
734                                      Edts& aEdts, uint64_t* aDecodeTime,
735                                      bool aIsAudio) {
736   LOG_DEBUG(Trun, "Starting.");
737   if (!mTfhd.IsValid() || !aMvhd.IsValid() || !aMdhd.IsValid() ||
738       !aEdts.IsValid()) {
739     LOG_WARN(
740         Moof, "Invalid dependencies: mTfhd(%d) aMvhd(%d) aMdhd(%d) aEdts(%d)",
741         mTfhd.IsValid(), aMvhd.IsValid(), aMdhd.IsValid(), !aEdts.IsValid());
742     return Err(NS_ERROR_FAILURE);
743   }
744 
745   BoxReader reader(aBox);
746   if (!reader->CanReadType<uint32_t>()) {
747     LOG_WARN(Moof, "Incomplete Box (missing flags)");
748     return Err(NS_ERROR_FAILURE);
749   }
750   uint32_t flags;
751   MOZ_TRY_VAR(flags, reader->ReadU32());
752 
753   if (!reader->CanReadType<uint32_t>()) {
754     LOG_WARN(Moof, "Incomplete Box (missing sampleCount)");
755     return Err(NS_ERROR_FAILURE);
756   }
757   uint32_t sampleCount;
758   MOZ_TRY_VAR(sampleCount, reader->ReadU32());
759   if (sampleCount == 0) {
760     LOG_DEBUG(Trun, "Trun with no samples, returning.");
761     return Ok();
762   }
763 
764   uint64_t offset = mTfhd.mBaseDataOffset;
765   if (flags & 0x01) {
766     uint32_t tmp;
767     MOZ_TRY_VAR(tmp, reader->ReadU32());
768     offset += tmp;
769   }
770   uint32_t firstSampleFlags = mTfhd.mDefaultSampleFlags;
771   if (flags & 0x04) {
772     MOZ_TRY_VAR(firstSampleFlags, reader->ReadU32());
773   }
774   uint64_t decodeTime = *aDecodeTime;
775   nsTArray<MP4Interval<Microseconds>> timeRanges;
776 
777   if (!mIndex.SetCapacity(sampleCount, fallible)) {
778     LOG_ERROR(Moof, "Out of Memory");
779     return Err(NS_ERROR_FAILURE);
780   }
781 
782   for (size_t i = 0; i < sampleCount; i++) {
783     uint32_t sampleDuration = mTfhd.mDefaultSampleDuration;
784     if (flags & 0x100) {
785       MOZ_TRY_VAR(sampleDuration, reader->ReadU32());
786     }
787     uint32_t sampleSize = mTfhd.mDefaultSampleSize;
788     if (flags & 0x200) {
789       MOZ_TRY_VAR(sampleSize, reader->ReadU32());
790     }
791     uint32_t sampleFlags = i ? mTfhd.mDefaultSampleFlags : firstSampleFlags;
792     if (flags & 0x400) {
793       MOZ_TRY_VAR(sampleFlags, reader->ReadU32());
794     }
795     int32_t ctsOffset = 0;
796     if (flags & 0x800) {
797       MOZ_TRY_VAR(ctsOffset, reader->Read32());
798     }
799 
800     if (sampleSize) {
801       Sample sample;
802       sample.mByteRange = MediaByteRange(offset, offset + sampleSize);
803       offset += sampleSize;
804 
805       Microseconds decodeOffset, emptyOffset, startCts, endCts;
806       MOZ_TRY_VAR(decodeOffset, aMdhd.ToMicroseconds((int64_t)decodeTime -
807                                                      aEdts.mMediaStart));
808       MOZ_TRY_VAR(emptyOffset, aMvhd.ToMicroseconds(aEdts.mEmptyOffset));
809       sample.mDecodeTime = decodeOffset + emptyOffset;
810       MOZ_TRY_VAR(startCts,
811                   aMdhd.ToMicroseconds((int64_t)decodeTime + ctsOffset -
812                                        aEdts.mMediaStart));
813       MOZ_TRY_VAR(endCts,
814                   aMdhd.ToMicroseconds((int64_t)decodeTime + ctsOffset +
815                                        sampleDuration - aEdts.mMediaStart));
816       sample.mCompositionRange = MP4Interval<Microseconds>(
817           startCts + emptyOffset, endCts + emptyOffset);
818       // Sometimes audio streams don't properly mark their samples as keyframes,
819       // because every audio sample is a keyframe.
820       sample.mSync = !(sampleFlags & 0x1010000) || aIsAudio;
821 
822       // FIXME: Make this infallible after bug 968520 is done.
823       MOZ_ALWAYS_TRUE(mIndex.AppendElement(sample, fallible));
824 
825       mMdatRange = mMdatRange.Span(sample.mByteRange);
826     }
827     decodeTime += sampleDuration;
828   }
829   Microseconds roundTime;
830   MOZ_TRY_VAR(roundTime, aMdhd.ToMicroseconds(sampleCount));
831   mMaxRoundingError += roundTime;
832 
833   *aDecodeTime = decodeTime;
834 
835   LOG_DEBUG(Trun, "Done.");
836   return Ok();
837 }
838 
Tkhd(Box & aBox)839 Tkhd::Tkhd(Box& aBox) : mTrackId(0) {
840   mValid = Parse(aBox).isOk();
841   if (!mValid) {
842     LOG_WARN(Tkhd, "Parse failed");
843   }
844 }
845 
Parse(Box & aBox)846 Result<Ok, nsresult> Tkhd::Parse(Box& aBox) {
847   BoxReader reader(aBox);
848   uint32_t flags;
849   MOZ_TRY_VAR(flags, reader->ReadU32());
850   uint8_t version = flags >> 24;
851   if (version == 0) {
852     uint32_t creationTime, modificationTime, reserved, duration;
853     MOZ_TRY_VAR(creationTime, reader->ReadU32());
854     MOZ_TRY_VAR(modificationTime, reader->ReadU32());
855     MOZ_TRY_VAR(mTrackId, reader->ReadU32());
856     MOZ_TRY_VAR(reserved, reader->ReadU32());
857     MOZ_TRY_VAR(duration, reader->ReadU32());
858 
859     (void)reserved;
860     NS_ASSERTION(!reserved, "reserved should be 0");
861 
862     mCreationTime = creationTime;
863     mModificationTime = modificationTime;
864     mDuration = duration;
865   } else if (version == 1) {
866     uint32_t reserved;
867     MOZ_TRY_VAR(mCreationTime, reader->ReadU64());
868     MOZ_TRY_VAR(mModificationTime, reader->ReadU64());
869     MOZ_TRY_VAR(mTrackId, reader->ReadU32());
870     MOZ_TRY_VAR(reserved, reader->ReadU32());
871     (void)reserved;
872     NS_ASSERTION(!reserved, "reserved should be 0");
873     MOZ_TRY_VAR(mDuration, reader->ReadU64());
874   }
875   return Ok();
876 }
877 
Mvhd(Box & aBox)878 Mvhd::Mvhd(Box& aBox)
879     : mCreationTime(0), mModificationTime(0), mTimescale(0), mDuration(0) {
880   mValid = Parse(aBox).isOk();
881   if (!mValid) {
882     LOG_WARN(Mvhd, "Parse failed");
883   }
884 }
885 
Parse(Box & aBox)886 Result<Ok, nsresult> Mvhd::Parse(Box& aBox) {
887   BoxReader reader(aBox);
888 
889   uint32_t flags;
890   MOZ_TRY_VAR(flags, reader->ReadU32());
891   uint8_t version = flags >> 24;
892 
893   if (version == 0) {
894     uint32_t creationTime, modificationTime, duration;
895     MOZ_TRY_VAR(creationTime, reader->ReadU32());
896     MOZ_TRY_VAR(modificationTime, reader->ReadU32());
897     MOZ_TRY_VAR(mTimescale, reader->ReadU32());
898     MOZ_TRY_VAR(duration, reader->ReadU32());
899     mCreationTime = creationTime;
900     mModificationTime = modificationTime;
901     mDuration = duration;
902   } else if (version == 1) {
903     MOZ_TRY_VAR(mCreationTime, reader->ReadU64());
904     MOZ_TRY_VAR(mModificationTime, reader->ReadU64());
905     MOZ_TRY_VAR(mTimescale, reader->ReadU32());
906     MOZ_TRY_VAR(mDuration, reader->ReadU64());
907   } else {
908     return Err(NS_ERROR_FAILURE);
909   }
910   return Ok();
911 }
912 
Mdhd(Box & aBox)913 Mdhd::Mdhd(Box& aBox) : Mvhd(aBox) {}
914 
Trex(Box & aBox)915 Trex::Trex(Box& aBox)
916     : mFlags(0),
917       mTrackId(0),
918       mDefaultSampleDescriptionIndex(0),
919       mDefaultSampleDuration(0),
920       mDefaultSampleSize(0),
921       mDefaultSampleFlags(0) {
922   mValid = Parse(aBox).isOk();
923   if (!mValid) {
924     LOG_WARN(Trex, "Parse failed");
925   }
926 }
927 
Parse(Box & aBox)928 Result<Ok, nsresult> Trex::Parse(Box& aBox) {
929   BoxReader reader(aBox);
930 
931   MOZ_TRY_VAR(mFlags, reader->ReadU32());
932   MOZ_TRY_VAR(mTrackId, reader->ReadU32());
933   MOZ_TRY_VAR(mDefaultSampleDescriptionIndex, reader->ReadU32());
934   MOZ_TRY_VAR(mDefaultSampleDuration, reader->ReadU32());
935   MOZ_TRY_VAR(mDefaultSampleSize, reader->ReadU32());
936   MOZ_TRY_VAR(mDefaultSampleFlags, reader->ReadU32());
937 
938   return Ok();
939 }
940 
Tfhd(Box & aBox,Trex & aTrex)941 Tfhd::Tfhd(Box& aBox, Trex& aTrex) : Trex(aTrex), mBaseDataOffset(0) {
942   mValid = Parse(aBox).isOk();
943   if (!mValid) {
944     LOG_WARN(Tfhd, "Parse failed");
945   }
946 }
947 
Parse(Box & aBox)948 Result<Ok, nsresult> Tfhd::Parse(Box& aBox) {
949   MOZ_ASSERT(aBox.IsType("tfhd"));
950   MOZ_ASSERT(aBox.Parent()->IsType("traf"));
951   MOZ_ASSERT(aBox.Parent()->Parent()->IsType("moof"));
952 
953   BoxReader reader(aBox);
954 
955   MOZ_TRY_VAR(mFlags, reader->ReadU32());
956   MOZ_TRY_VAR(mTrackId, reader->ReadU32());
957   mBaseDataOffset = aBox.Parent()->Parent()->Offset();
958   if (mFlags & 0x01) {
959     MOZ_TRY_VAR(mBaseDataOffset, reader->ReadU64());
960   }
961   if (mFlags & 0x02) {
962     MOZ_TRY_VAR(mDefaultSampleDescriptionIndex, reader->ReadU32());
963   }
964   if (mFlags & 0x08) {
965     MOZ_TRY_VAR(mDefaultSampleDuration, reader->ReadU32());
966   }
967   if (mFlags & 0x10) {
968     MOZ_TRY_VAR(mDefaultSampleSize, reader->ReadU32());
969   }
970   if (mFlags & 0x20) {
971     MOZ_TRY_VAR(mDefaultSampleFlags, reader->ReadU32());
972   }
973 
974   return Ok();
975 }
976 
Tfdt(Box & aBox)977 Tfdt::Tfdt(Box& aBox) : mBaseMediaDecodeTime(0) {
978   mValid = Parse(aBox).isOk();
979   if (!mValid) {
980     LOG_WARN(Tfdt, "Parse failed");
981   }
982 }
983 
Parse(Box & aBox)984 Result<Ok, nsresult> Tfdt::Parse(Box& aBox) {
985   BoxReader reader(aBox);
986 
987   uint32_t flags;
988   MOZ_TRY_VAR(flags, reader->ReadU32());
989   uint8_t version = flags >> 24;
990   if (version == 0) {
991     uint32_t tmp;
992     MOZ_TRY_VAR(tmp, reader->ReadU32());
993     mBaseMediaDecodeTime = tmp;
994   } else if (version == 1) {
995     MOZ_TRY_VAR(mBaseMediaDecodeTime, reader->ReadU64());
996   }
997   return Ok();
998 }
999 
Edts(Box & aBox)1000 Edts::Edts(Box& aBox) : mMediaStart(0), mEmptyOffset(0) {
1001   mValid = Parse(aBox).isOk();
1002   if (!mValid) {
1003     LOG_WARN(Edts, "Parse failed");
1004   }
1005 }
1006 
Parse(Box & aBox)1007 Result<Ok, nsresult> Edts::Parse(Box& aBox) {
1008   Box child = aBox.FirstChild();
1009   if (!child.IsType("elst")) {
1010     return Err(NS_ERROR_FAILURE);
1011   }
1012 
1013   BoxReader reader(child);
1014   uint32_t flags;
1015   MOZ_TRY_VAR(flags, reader->ReadU32());
1016   uint8_t version = flags >> 24;
1017   bool emptyEntry = false;
1018   uint32_t entryCount;
1019   MOZ_TRY_VAR(entryCount, reader->ReadU32());
1020   for (uint32_t i = 0; i < entryCount; i++) {
1021     uint64_t segment_duration;
1022     int64_t media_time;
1023     if (version == 1) {
1024       MOZ_TRY_VAR(segment_duration, reader->ReadU64());
1025       MOZ_TRY_VAR(media_time, reader->Read64());
1026     } else {
1027       uint32_t tmp;
1028       MOZ_TRY_VAR(tmp, reader->ReadU32());
1029       segment_duration = tmp;
1030       int32_t tmp2;
1031       MOZ_TRY_VAR(tmp2, reader->Read32());
1032       media_time = tmp2;
1033     }
1034     if (media_time == -1 && i) {
1035       LOG_WARN(Edts, "Multiple empty edit, not handled");
1036     } else if (media_time == -1) {
1037       mEmptyOffset = segment_duration;
1038       emptyEntry = true;
1039     } else if (i > 1 || (i > 0 && !emptyEntry)) {
1040       LOG_WARN(Edts,
1041                "More than one edit entry, not handled. A/V sync will be wrong");
1042       break;
1043     } else {
1044       mMediaStart = media_time;
1045     }
1046     MOZ_TRY(reader->ReadU32());  // media_rate_integer and media_rate_fraction
1047   }
1048 
1049   return Ok();
1050 }
1051 
Saiz(Box & aBox,AtomType aDefaultType)1052 Saiz::Saiz(Box& aBox, AtomType aDefaultType)
1053     : mAuxInfoType(aDefaultType), mAuxInfoTypeParameter(0) {
1054   mValid = Parse(aBox).isOk();
1055   if (!mValid) {
1056     LOG_WARN(Saiz, "Parse failed");
1057   }
1058 }
1059 
Parse(Box & aBox)1060 Result<Ok, nsresult> Saiz::Parse(Box& aBox) {
1061   BoxReader reader(aBox);
1062 
1063   uint32_t flags;
1064   MOZ_TRY_VAR(flags, reader->ReadU32());
1065   if (flags & 1) {
1066     MOZ_TRY_VAR(mAuxInfoType, reader->ReadU32());
1067     MOZ_TRY_VAR(mAuxInfoTypeParameter, reader->ReadU32());
1068   }
1069   uint8_t defaultSampleInfoSize;
1070   MOZ_TRY_VAR(defaultSampleInfoSize, reader->ReadU8());
1071   uint32_t count;
1072   MOZ_TRY_VAR(count, reader->ReadU32());
1073   if (defaultSampleInfoSize) {
1074     if (!mSampleInfoSize.SetLength(count, fallible)) {
1075       LOG_ERROR(Saiz, "OOM");
1076       return Err(NS_ERROR_FAILURE);
1077     }
1078     memset(mSampleInfoSize.Elements(), defaultSampleInfoSize,
1079            mSampleInfoSize.Length());
1080   } else {
1081     if (!reader->ReadArray(mSampleInfoSize, count)) {
1082       LOG_WARN(Saiz, "Incomplete Box (OOM or missing count:%u)", count);
1083       return Err(NS_ERROR_FAILURE);
1084     }
1085   }
1086   return Ok();
1087 }
1088 
Saio(Box & aBox,AtomType aDefaultType)1089 Saio::Saio(Box& aBox, AtomType aDefaultType)
1090     : mAuxInfoType(aDefaultType), mAuxInfoTypeParameter(0) {
1091   mValid = Parse(aBox).isOk();
1092   if (!mValid) {
1093     LOG_WARN(Saio, "Parse failed");
1094   }
1095 }
1096 
Parse(Box & aBox)1097 Result<Ok, nsresult> Saio::Parse(Box& aBox) {
1098   BoxReader reader(aBox);
1099 
1100   uint32_t flags;
1101   MOZ_TRY_VAR(flags, reader->ReadU32());
1102   uint8_t version = flags >> 24;
1103   if (flags & 1) {
1104     MOZ_TRY_VAR(mAuxInfoType, reader->ReadU32());
1105     MOZ_TRY_VAR(mAuxInfoTypeParameter, reader->ReadU32());
1106   }
1107 
1108   size_t count;
1109   MOZ_TRY_VAR(count, reader->ReadU32());
1110   if (!mOffsets.SetCapacity(count, fallible)) {
1111     LOG_ERROR(Saiz, "OOM");
1112     return Err(NS_ERROR_FAILURE);
1113   }
1114   if (version == 0) {
1115     uint32_t offset;
1116     for (size_t i = 0; i < count; i++) {
1117       MOZ_TRY_VAR(offset, reader->ReadU32());
1118       MOZ_ALWAYS_TRUE(mOffsets.AppendElement(offset, fallible));
1119     }
1120   } else {
1121     uint64_t offset;
1122     for (size_t i = 0; i < count; i++) {
1123       MOZ_TRY_VAR(offset, reader->ReadU64());
1124       MOZ_ALWAYS_TRUE(mOffsets.AppendElement(offset, fallible));
1125     }
1126   }
1127   return Ok();
1128 }
1129 
Sbgp(Box & aBox)1130 Sbgp::Sbgp(Box& aBox) : mGroupingTypeParam(0) {
1131   mValid = Parse(aBox).isOk();
1132   if (!mValid) {
1133     LOG_WARN(Sbgp, "Parse failed");
1134   }
1135 }
1136 
Parse(Box & aBox)1137 Result<Ok, nsresult> Sbgp::Parse(Box& aBox) {
1138   BoxReader reader(aBox);
1139 
1140   uint32_t flags;
1141   MOZ_TRY_VAR(flags, reader->ReadU32());
1142   const uint8_t version = flags >> 24;
1143 
1144   uint32_t type;
1145   MOZ_TRY_VAR(type, reader->ReadU32());
1146   mGroupingType = type;
1147 
1148   if (version == 1) {
1149     MOZ_TRY_VAR(mGroupingTypeParam, reader->ReadU32());
1150   }
1151 
1152   uint32_t count;
1153   MOZ_TRY_VAR(count, reader->ReadU32());
1154 
1155   for (uint32_t i = 0; i < count; i++) {
1156     uint32_t sampleCount;
1157     MOZ_TRY_VAR(sampleCount, reader->ReadU32());
1158     uint32_t groupDescriptionIndex;
1159     MOZ_TRY_VAR(groupDescriptionIndex, reader->ReadU32());
1160 
1161     SampleToGroupEntry entry(sampleCount, groupDescriptionIndex);
1162     if (!mEntries.AppendElement(entry, mozilla::fallible)) {
1163       LOG_ERROR(Sbgp, "OOM");
1164       return Err(NS_ERROR_FAILURE);
1165     }
1166   }
1167   return Ok();
1168 }
1169 
Sgpd(Box & aBox)1170 Sgpd::Sgpd(Box& aBox) {
1171   mValid = Parse(aBox).isOk();
1172   if (!mValid) {
1173     LOG_WARN(Sgpd, "Parse failed");
1174   }
1175 }
1176 
Parse(Box & aBox)1177 Result<Ok, nsresult> Sgpd::Parse(Box& aBox) {
1178   BoxReader reader(aBox);
1179 
1180   uint32_t flags;
1181   MOZ_TRY_VAR(flags, reader->ReadU32());
1182   const uint8_t version = flags >> 24;
1183 
1184   uint32_t type;
1185   MOZ_TRY_VAR(type, reader->ReadU32());
1186   mGroupingType = type;
1187 
1188   const uint32_t entrySize = sizeof(uint32_t) + kKeyIdSize;
1189   uint32_t defaultLength = 0;
1190 
1191   if (version == 1) {
1192     MOZ_TRY_VAR(defaultLength, reader->ReadU32());
1193     if (defaultLength < entrySize && defaultLength != 0) {
1194       return Err(NS_ERROR_FAILURE);
1195     }
1196   }
1197 
1198   uint32_t count;
1199   MOZ_TRY_VAR(count, reader->ReadU32());
1200 
1201   for (uint32_t i = 0; i < count; ++i) {
1202     if (version == 1 && defaultLength == 0) {
1203       uint32_t descriptionLength;
1204       MOZ_TRY_VAR(descriptionLength, reader->ReadU32());
1205       if (descriptionLength < entrySize) {
1206         return Err(NS_ERROR_FAILURE);
1207       }
1208     }
1209 
1210     CencSampleEncryptionInfoEntry entry;
1211     bool valid = entry.Init(reader).isOk();
1212     if (!valid) {
1213       return Err(NS_ERROR_FAILURE);
1214     }
1215     if (!mEntries.AppendElement(entry, mozilla::fallible)) {
1216       LOG_ERROR(Sgpd, "OOM");
1217       return Err(NS_ERROR_FAILURE);
1218     }
1219   }
1220   return Ok();
1221 }
1222 
Init(BoxReader & aReader)1223 Result<Ok, nsresult> CencSampleEncryptionInfoEntry::Init(BoxReader& aReader) {
1224   // Skip a reserved byte.
1225   MOZ_TRY(aReader->ReadU8());
1226 
1227   uint8_t pattern;
1228   MOZ_TRY_VAR(pattern, aReader->ReadU8());
1229   mCryptByteBlock = pattern >> 4;
1230   mSkipByteBlock = pattern & 0x0f;
1231 
1232   uint8_t isEncrypted;
1233   MOZ_TRY_VAR(isEncrypted, aReader->ReadU8());
1234   mIsEncrypted = isEncrypted != 0;
1235 
1236   MOZ_TRY_VAR(mIVSize, aReader->ReadU8());
1237 
1238   // Read the key id.
1239   if (!mKeyId.SetLength(kKeyIdSize, fallible)) {
1240     LOG_ERROR(CencSampleEncryptionInfoEntry, "OOM");
1241     return Err(NS_ERROR_FAILURE);
1242   }
1243   for (uint32_t i = 0; i < kKeyIdSize; ++i) {
1244     MOZ_TRY_VAR(mKeyId.ElementAt(i), aReader->ReadU8());
1245   }
1246 
1247   if (mIsEncrypted) {
1248     if (mIVSize != 8 && mIVSize != 16) {
1249       return Err(NS_ERROR_FAILURE);
1250     }
1251   } else if (mIVSize != 0) {
1252     // Protected content with 0 sized IV indicates a constant IV is present.
1253     // This is used for the cbcs scheme.
1254     uint8_t constantIVSize;
1255     MOZ_TRY_VAR(constantIVSize, aReader->ReadU8());
1256     if (constantIVSize != 8 && constantIVSize != 16) {
1257       LOG_WARN(CencSampleEncryptionInfoEntry,
1258                "Unexpected constantIVSize: %" PRIu8, constantIVSize);
1259       return Err(NS_ERROR_FAILURE);
1260     }
1261     if (!mConsantIV.SetLength(constantIVSize, mozilla::fallible)) {
1262       LOG_ERROR(CencSampleEncryptionInfoEntry, "OOM");
1263       return Err(NS_ERROR_FAILURE);
1264     }
1265     for (uint32_t i = 0; i < constantIVSize; ++i) {
1266       MOZ_TRY_VAR(mConsantIV.ElementAt(i), aReader->ReadU8());
1267     }
1268   }
1269 
1270   return Ok();
1271 }
1272 }  // namespace mozilla
1273 
1274 #undef LOG_DEBUG
1275 #undef LOG_WARN
1276 #undef LOG_ERROR
1277