/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 | | } |