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