Coverage Report

Created: 2018-09-25 14:53

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