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     NS_ASSERTION(!reserved, "reserved should be 0");
860 
861     mCreationTime = creationTime;
862     mModificationTime = modificationTime;
863     mDuration = duration;
864   } else if (version == 1) {
865     uint32_t reserved;
866     MOZ_TRY_VAR(mCreationTime, reader->ReadU64());
867     MOZ_TRY_VAR(mModificationTime, reader->ReadU64());
868     MOZ_TRY_VAR(mTrackId, reader->ReadU32());
869     MOZ_TRY_VAR(reserved, reader->ReadU32());
870     NS_ASSERTION(!reserved, "reserved should be 0");
871     MOZ_TRY_VAR(mDuration, reader->ReadU64());
872   }
873   return Ok();
874 }
875 
Mvhd(Box & aBox)876 Mvhd::Mvhd(Box& aBox)
877     : mCreationTime(0), mModificationTime(0), mTimescale(0), mDuration(0) {
878   mValid = Parse(aBox).isOk();
879   if (!mValid) {
880     LOG_WARN(Mvhd, "Parse failed");
881   }
882 }
883 
Parse(Box & aBox)884 Result<Ok, nsresult> Mvhd::Parse(Box& aBox) {
885   BoxReader reader(aBox);
886 
887   uint32_t flags;
888   MOZ_TRY_VAR(flags, reader->ReadU32());
889   uint8_t version = flags >> 24;
890 
891   if (version == 0) {
892     uint32_t creationTime, modificationTime, duration;
893     MOZ_TRY_VAR(creationTime, reader->ReadU32());
894     MOZ_TRY_VAR(modificationTime, reader->ReadU32());
895     MOZ_TRY_VAR(mTimescale, reader->ReadU32());
896     MOZ_TRY_VAR(duration, reader->ReadU32());
897     mCreationTime = creationTime;
898     mModificationTime = modificationTime;
899     mDuration = duration;
900   } else if (version == 1) {
901     MOZ_TRY_VAR(mCreationTime, reader->ReadU64());
902     MOZ_TRY_VAR(mModificationTime, reader->ReadU64());
903     MOZ_TRY_VAR(mTimescale, reader->ReadU32());
904     MOZ_TRY_VAR(mDuration, reader->ReadU64());
905   } else {
906     return Err(NS_ERROR_FAILURE);
907   }
908   return Ok();
909 }
910 
Mdhd(Box & aBox)911 Mdhd::Mdhd(Box& aBox) : Mvhd(aBox) {}
912 
Trex(Box & aBox)913 Trex::Trex(Box& aBox)
914     : mFlags(0),
915       mTrackId(0),
916       mDefaultSampleDescriptionIndex(0),
917       mDefaultSampleDuration(0),
918       mDefaultSampleSize(0),
919       mDefaultSampleFlags(0) {
920   mValid = Parse(aBox).isOk();
921   if (!mValid) {
922     LOG_WARN(Trex, "Parse failed");
923   }
924 }
925 
Parse(Box & aBox)926 Result<Ok, nsresult> Trex::Parse(Box& aBox) {
927   BoxReader reader(aBox);
928 
929   MOZ_TRY_VAR(mFlags, reader->ReadU32());
930   MOZ_TRY_VAR(mTrackId, reader->ReadU32());
931   MOZ_TRY_VAR(mDefaultSampleDescriptionIndex, reader->ReadU32());
932   MOZ_TRY_VAR(mDefaultSampleDuration, reader->ReadU32());
933   MOZ_TRY_VAR(mDefaultSampleSize, reader->ReadU32());
934   MOZ_TRY_VAR(mDefaultSampleFlags, reader->ReadU32());
935 
936   return Ok();
937 }
938 
Tfhd(Box & aBox,Trex & aTrex)939 Tfhd::Tfhd(Box& aBox, Trex& aTrex) : Trex(aTrex), mBaseDataOffset(0) {
940   mValid = Parse(aBox).isOk();
941   if (!mValid) {
942     LOG_WARN(Tfhd, "Parse failed");
943   }
944 }
945 
Parse(Box & aBox)946 Result<Ok, nsresult> Tfhd::Parse(Box& aBox) {
947   MOZ_ASSERT(aBox.IsType("tfhd"));
948   MOZ_ASSERT(aBox.Parent()->IsType("traf"));
949   MOZ_ASSERT(aBox.Parent()->Parent()->IsType("moof"));
950 
951   BoxReader reader(aBox);
952 
953   MOZ_TRY_VAR(mFlags, reader->ReadU32());
954   MOZ_TRY_VAR(mTrackId, reader->ReadU32());
955   mBaseDataOffset = aBox.Parent()->Parent()->Offset();
956   if (mFlags & 0x01) {
957     MOZ_TRY_VAR(mBaseDataOffset, reader->ReadU64());
958   }
959   if (mFlags & 0x02) {
960     MOZ_TRY_VAR(mDefaultSampleDescriptionIndex, reader->ReadU32());
961   }
962   if (mFlags & 0x08) {
963     MOZ_TRY_VAR(mDefaultSampleDuration, reader->ReadU32());
964   }
965   if (mFlags & 0x10) {
966     MOZ_TRY_VAR(mDefaultSampleSize, reader->ReadU32());
967   }
968   if (mFlags & 0x20) {
969     MOZ_TRY_VAR(mDefaultSampleFlags, reader->ReadU32());
970   }
971 
972   return Ok();
973 }
974 
Tfdt(Box & aBox)975 Tfdt::Tfdt(Box& aBox) : mBaseMediaDecodeTime(0) {
976   mValid = Parse(aBox).isOk();
977   if (!mValid) {
978     LOG_WARN(Tfdt, "Parse failed");
979   }
980 }
981 
Parse(Box & aBox)982 Result<Ok, nsresult> Tfdt::Parse(Box& aBox) {
983   BoxReader reader(aBox);
984 
985   uint32_t flags;
986   MOZ_TRY_VAR(flags, reader->ReadU32());
987   uint8_t version = flags >> 24;
988   if (version == 0) {
989     uint32_t tmp;
990     MOZ_TRY_VAR(tmp, reader->ReadU32());
991     mBaseMediaDecodeTime = tmp;
992   } else if (version == 1) {
993     MOZ_TRY_VAR(mBaseMediaDecodeTime, reader->ReadU64());
994   }
995   return Ok();
996 }
997 
Edts(Box & aBox)998 Edts::Edts(Box& aBox) : mMediaStart(0), mEmptyOffset(0) {
999   mValid = Parse(aBox).isOk();
1000   if (!mValid) {
1001     LOG_WARN(Edts, "Parse failed");
1002   }
1003 }
1004 
Parse(Box & aBox)1005 Result<Ok, nsresult> Edts::Parse(Box& aBox) {
1006   Box child = aBox.FirstChild();
1007   if (!child.IsType("elst")) {
1008     return Err(NS_ERROR_FAILURE);
1009   }
1010 
1011   BoxReader reader(child);
1012   uint32_t flags;
1013   MOZ_TRY_VAR(flags, reader->ReadU32());
1014   uint8_t version = flags >> 24;
1015   bool emptyEntry = false;
1016   uint32_t entryCount;
1017   MOZ_TRY_VAR(entryCount, reader->ReadU32());
1018   for (uint32_t i = 0; i < entryCount; i++) {
1019     uint64_t segment_duration;
1020     int64_t media_time;
1021     if (version == 1) {
1022       MOZ_TRY_VAR(segment_duration, reader->ReadU64());
1023       MOZ_TRY_VAR(media_time, reader->Read64());
1024     } else {
1025       uint32_t tmp;
1026       MOZ_TRY_VAR(tmp, reader->ReadU32());
1027       segment_duration = tmp;
1028       int32_t tmp2;
1029       MOZ_TRY_VAR(tmp2, reader->Read32());
1030       media_time = tmp2;
1031     }
1032     if (media_time == -1 && i) {
1033       LOG_WARN(Edts, "Multiple empty edit, not handled");
1034     } else if (media_time == -1) {
1035       mEmptyOffset = segment_duration;
1036       emptyEntry = true;
1037     } else if (i > 1 || (i > 0 && !emptyEntry)) {
1038       LOG_WARN(Edts,
1039                "More than one edit entry, not handled. A/V sync will be wrong");
1040       break;
1041     } else {
1042       mMediaStart = media_time;
1043     }
1044     MOZ_TRY(reader->ReadU32());  // media_rate_integer and media_rate_fraction
1045   }
1046 
1047   return Ok();
1048 }
1049 
Saiz(Box & aBox,AtomType aDefaultType)1050 Saiz::Saiz(Box& aBox, AtomType aDefaultType)
1051     : mAuxInfoType(aDefaultType), mAuxInfoTypeParameter(0) {
1052   mValid = Parse(aBox).isOk();
1053   if (!mValid) {
1054     LOG_WARN(Saiz, "Parse failed");
1055   }
1056 }
1057 
Parse(Box & aBox)1058 Result<Ok, nsresult> Saiz::Parse(Box& aBox) {
1059   BoxReader reader(aBox);
1060 
1061   uint32_t flags;
1062   MOZ_TRY_VAR(flags, reader->ReadU32());
1063   if (flags & 1) {
1064     MOZ_TRY_VAR(mAuxInfoType, reader->ReadU32());
1065     MOZ_TRY_VAR(mAuxInfoTypeParameter, reader->ReadU32());
1066   }
1067   uint8_t defaultSampleInfoSize;
1068   MOZ_TRY_VAR(defaultSampleInfoSize, reader->ReadU8());
1069   uint32_t count;
1070   MOZ_TRY_VAR(count, reader->ReadU32());
1071   if (defaultSampleInfoSize) {
1072     if (!mSampleInfoSize.SetLength(count, fallible)) {
1073       LOG_ERROR(Saiz, "OOM");
1074       return Err(NS_ERROR_FAILURE);
1075     }
1076     memset(mSampleInfoSize.Elements(), defaultSampleInfoSize,
1077            mSampleInfoSize.Length());
1078   } else {
1079     if (!reader->ReadArray(mSampleInfoSize, count)) {
1080       LOG_WARN(Saiz, "Incomplete Box (OOM or missing count:%u)", count);
1081       return Err(NS_ERROR_FAILURE);
1082     }
1083   }
1084   return Ok();
1085 }
1086 
Saio(Box & aBox,AtomType aDefaultType)1087 Saio::Saio(Box& aBox, AtomType aDefaultType)
1088     : mAuxInfoType(aDefaultType), mAuxInfoTypeParameter(0) {
1089   mValid = Parse(aBox).isOk();
1090   if (!mValid) {
1091     LOG_WARN(Saio, "Parse failed");
1092   }
1093 }
1094 
Parse(Box & aBox)1095 Result<Ok, nsresult> Saio::Parse(Box& aBox) {
1096   BoxReader reader(aBox);
1097 
1098   uint32_t flags;
1099   MOZ_TRY_VAR(flags, reader->ReadU32());
1100   uint8_t version = flags >> 24;
1101   if (flags & 1) {
1102     MOZ_TRY_VAR(mAuxInfoType, reader->ReadU32());
1103     MOZ_TRY_VAR(mAuxInfoTypeParameter, reader->ReadU32());
1104   }
1105 
1106   size_t count;
1107   MOZ_TRY_VAR(count, reader->ReadU32());
1108   if (!mOffsets.SetCapacity(count, fallible)) {
1109     LOG_ERROR(Saiz, "OOM");
1110     return Err(NS_ERROR_FAILURE);
1111   }
1112   if (version == 0) {
1113     uint32_t offset;
1114     for (size_t i = 0; i < count; i++) {
1115       MOZ_TRY_VAR(offset, reader->ReadU32());
1116       MOZ_ALWAYS_TRUE(mOffsets.AppendElement(offset, fallible));
1117     }
1118   } else {
1119     uint64_t offset;
1120     for (size_t i = 0; i < count; i++) {
1121       MOZ_TRY_VAR(offset, reader->ReadU64());
1122       MOZ_ALWAYS_TRUE(mOffsets.AppendElement(offset, fallible));
1123     }
1124   }
1125   return Ok();
1126 }
1127 
Sbgp(Box & aBox)1128 Sbgp::Sbgp(Box& aBox) : mGroupingTypeParam(0) {
1129   mValid = Parse(aBox).isOk();
1130   if (!mValid) {
1131     LOG_WARN(Sbgp, "Parse failed");
1132   }
1133 }
1134 
Parse(Box & aBox)1135 Result<Ok, nsresult> Sbgp::Parse(Box& aBox) {
1136   BoxReader reader(aBox);
1137 
1138   uint32_t flags;
1139   MOZ_TRY_VAR(flags, reader->ReadU32());
1140   const uint8_t version = flags >> 24;
1141 
1142   uint32_t type;
1143   MOZ_TRY_VAR(type, reader->ReadU32());
1144   mGroupingType = type;
1145 
1146   if (version == 1) {
1147     MOZ_TRY_VAR(mGroupingTypeParam, reader->ReadU32());
1148   }
1149 
1150   uint32_t count;
1151   MOZ_TRY_VAR(count, reader->ReadU32());
1152 
1153   for (uint32_t i = 0; i < count; i++) {
1154     uint32_t sampleCount;
1155     MOZ_TRY_VAR(sampleCount, reader->ReadU32());
1156     uint32_t groupDescriptionIndex;
1157     MOZ_TRY_VAR(groupDescriptionIndex, reader->ReadU32());
1158 
1159     SampleToGroupEntry entry(sampleCount, groupDescriptionIndex);
1160     if (!mEntries.AppendElement(entry, mozilla::fallible)) {
1161       LOG_ERROR(Sbgp, "OOM");
1162       return Err(NS_ERROR_FAILURE);
1163     }
1164   }
1165   return Ok();
1166 }
1167 
Sgpd(Box & aBox)1168 Sgpd::Sgpd(Box& aBox) {
1169   mValid = Parse(aBox).isOk();
1170   if (!mValid) {
1171     LOG_WARN(Sgpd, "Parse failed");
1172   }
1173 }
1174 
Parse(Box & aBox)1175 Result<Ok, nsresult> Sgpd::Parse(Box& aBox) {
1176   BoxReader reader(aBox);
1177 
1178   uint32_t flags;
1179   MOZ_TRY_VAR(flags, reader->ReadU32());
1180   const uint8_t version = flags >> 24;
1181 
1182   uint32_t type;
1183   MOZ_TRY_VAR(type, reader->ReadU32());
1184   mGroupingType = type;
1185 
1186   const uint32_t entrySize = sizeof(uint32_t) + kKeyIdSize;
1187   uint32_t defaultLength = 0;
1188 
1189   if (version == 1) {
1190     MOZ_TRY_VAR(defaultLength, reader->ReadU32());
1191     if (defaultLength < entrySize && defaultLength != 0) {
1192       return Err(NS_ERROR_FAILURE);
1193     }
1194   }
1195 
1196   uint32_t count;
1197   MOZ_TRY_VAR(count, reader->ReadU32());
1198 
1199   for (uint32_t i = 0; i < count; ++i) {
1200     if (version == 1 && defaultLength == 0) {
1201       uint32_t descriptionLength;
1202       MOZ_TRY_VAR(descriptionLength, reader->ReadU32());
1203       if (descriptionLength < entrySize) {
1204         return Err(NS_ERROR_FAILURE);
1205       }
1206     }
1207 
1208     CencSampleEncryptionInfoEntry entry;
1209     bool valid = entry.Init(reader).isOk();
1210     if (!valid) {
1211       return Err(NS_ERROR_FAILURE);
1212     }
1213     if (!mEntries.AppendElement(entry, mozilla::fallible)) {
1214       LOG_ERROR(Sgpd, "OOM");
1215       return Err(NS_ERROR_FAILURE);
1216     }
1217   }
1218   return Ok();
1219 }
1220 
Init(BoxReader & aReader)1221 Result<Ok, nsresult> CencSampleEncryptionInfoEntry::Init(BoxReader& aReader) {
1222   // Skip a reserved byte.
1223   MOZ_TRY(aReader->ReadU8());
1224 
1225   uint8_t pattern;
1226   MOZ_TRY_VAR(pattern, aReader->ReadU8());
1227   mCryptByteBlock = pattern >> 4;
1228   mSkipByteBlock = pattern & 0x0f;
1229 
1230   uint8_t isEncrypted;
1231   MOZ_TRY_VAR(isEncrypted, aReader->ReadU8());
1232   mIsEncrypted = isEncrypted != 0;
1233 
1234   MOZ_TRY_VAR(mIVSize, aReader->ReadU8());
1235 
1236   // Read the key id.
1237   if (!mKeyId.SetLength(kKeyIdSize, fallible)) {
1238     LOG_ERROR(CencSampleEncryptionInfoEntry, "OOM");
1239     return Err(NS_ERROR_FAILURE);
1240   }
1241   for (uint32_t i = 0; i < kKeyIdSize; ++i) {
1242     MOZ_TRY_VAR(mKeyId.ElementAt(i), aReader->ReadU8());
1243   }
1244 
1245   if (mIsEncrypted) {
1246     if (mIVSize != 8 && mIVSize != 16) {
1247       return Err(NS_ERROR_FAILURE);
1248     }
1249   } else if (mIVSize != 0) {
1250     // Protected content with 0 sized IV indicates a constant IV is present.
1251     // This is used for the cbcs scheme.
1252     uint8_t constantIVSize;
1253     MOZ_TRY_VAR(constantIVSize, aReader->ReadU8());
1254     if (constantIVSize != 8 && constantIVSize != 16) {
1255       LOG_WARN(CencSampleEncryptionInfoEntry,
1256                "Unexpected constantIVSize: %" PRIu8, constantIVSize);
1257       return Err(NS_ERROR_FAILURE);
1258     }
1259     if (!mConsantIV.SetLength(constantIVSize, mozilla::fallible)) {
1260       LOG_ERROR(CencSampleEncryptionInfoEntry, "OOM");
1261       return Err(NS_ERROR_FAILURE);
1262     }
1263     for (uint32_t i = 0; i < constantIVSize; ++i) {
1264       MOZ_TRY_VAR(mConsantIV.ElementAt(i), aReader->ReadU8());
1265     }
1266   }
1267 
1268   return Ok();
1269 }
1270 }  // namespace mozilla
1271 
1272 #undef LOG_DEBUG
1273 #undef LOG_WARN
1274 #undef LOG_ERROR
1275