Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mp4_demuxer/Box.h"
8 : #include "mp4_demuxer/Stream.h"
9 : #include "mozilla/EndianUtils.h"
10 : #include "mozilla/Unused.h"
11 : #include <algorithm>
12 :
13 : using namespace mozilla;
14 :
15 : namespace mp4_demuxer {
16 :
17 : // Limit reads to 32MiB max.
18 : // static
19 : const uint64_t Box::kMAX_BOX_READ = 32 * 1024 * 1024;
20 :
21 : // Returns the offset from the start of the body of a box of type |aType|
22 : // to the start of its first child.
23 : static uint32_t
24 0 : BoxOffset(AtomType aType)
25 : {
26 0 : const uint32_t FULLBOX_OFFSET = 4;
27 :
28 0 : if (aType == AtomType("mp4a") || aType == AtomType("enca")) {
29 : // AudioSampleEntry; ISO 14496-12, section 8.16
30 0 : return 28;
31 0 : } else if (aType == AtomType("mp4v") || aType == AtomType("encv")) {
32 : // VideoSampleEntry; ISO 14496-12, section 8.16
33 0 : return 78;
34 0 : } else if (aType == AtomType("stsd")) {
35 : // SampleDescriptionBox; ISO 14496-12, section 8.16
36 : // This is a FullBox, and contains a |count| member before its child
37 : // boxes.
38 0 : return FULLBOX_OFFSET + 4;
39 : }
40 :
41 0 : return 0;
42 : }
43 :
44 0 : Box::Box(BoxContext* aContext, uint64_t aOffset, const Box* aParent)
45 0 : : mContext(aContext), mParent(aParent)
46 : {
47 : uint8_t header[8];
48 :
49 0 : if (aOffset > INT64_MAX - sizeof(header)) {
50 0 : return;
51 : }
52 :
53 0 : MediaByteRange headerRange(aOffset, aOffset + sizeof(header));
54 0 : if (mParent && !mParent->mRange.Contains(headerRange)) {
55 0 : return;
56 : }
57 :
58 : const MediaByteRange* byteRange;
59 0 : for (int i = 0; ; i++) {
60 0 : if (i == mContext->mByteRanges.Length()) {
61 0 : return;
62 : }
63 :
64 0 : byteRange = static_cast<const MediaByteRange*>(&mContext->mByteRanges[i]);
65 0 : if (byteRange->Contains(headerRange)) {
66 0 : break;
67 : }
68 : }
69 :
70 : size_t bytes;
71 0 : if (!mContext->mSource->CachedReadAt(aOffset, header, sizeof(header),
72 0 : &bytes) ||
73 0 : bytes != sizeof(header)) {
74 0 : return;
75 : }
76 :
77 0 : uint64_t size = BigEndian::readUint32(header);
78 0 : if (size == 1) {
79 : uint8_t bigLength[8];
80 0 : if (aOffset > INT64_MAX - sizeof(header) - sizeof(bigLength)) {
81 0 : return;
82 : }
83 : MediaByteRange bigLengthRange(headerRange.mEnd,
84 0 : headerRange.mEnd + sizeof(bigLength));
85 0 : if ((mParent && !mParent->mRange.Contains(bigLengthRange)) ||
86 0 : !byteRange->Contains(bigLengthRange) ||
87 0 : !mContext->mSource->CachedReadAt(aOffset + sizeof(header), bigLength,
88 0 : sizeof(bigLength), &bytes) ||
89 0 : bytes != sizeof(bigLength)) {
90 0 : return;
91 : }
92 0 : size = BigEndian::readUint64(bigLength);
93 0 : mBodyOffset = bigLengthRange.mEnd;
94 0 : } else if (size == 0) {
95 : // box extends to end of file.
96 0 : size = mContext->mByteRanges.LastInterval().mEnd - aOffset;
97 0 : mBodyOffset = headerRange.mEnd;
98 : } else {
99 0 : mBodyOffset = headerRange.mEnd;
100 : }
101 :
102 0 : if (size > INT64_MAX) {
103 0 : return;
104 : }
105 0 : int64_t end = static_cast<int64_t>(aOffset) + static_cast<int64_t>(size);
106 0 : if (end < static_cast<int64_t>(aOffset)) {
107 : // Overflowed.
108 0 : return;
109 : }
110 :
111 0 : mType = BigEndian::readUint32(&header[4]);
112 0 : mChildOffset = mBodyOffset + BoxOffset(mType);
113 :
114 0 : MediaByteRange boxRange(aOffset, end);
115 0 : if (mChildOffset > boxRange.mEnd ||
116 0 : (mParent && !mParent->mRange.Contains(boxRange)) ||
117 0 : !byteRange->Contains(boxRange)) {
118 0 : return;
119 : }
120 :
121 0 : mRange = boxRange;
122 : }
123 :
124 0 : Box::Box()
125 0 : : mContext(nullptr)
126 0 : {}
127 :
128 : Box
129 0 : Box::Next() const
130 : {
131 0 : MOZ_ASSERT(IsAvailable());
132 0 : return Box(mContext, mRange.mEnd, mParent);
133 : }
134 :
135 : Box
136 0 : Box::FirstChild() const
137 : {
138 0 : MOZ_ASSERT(IsAvailable());
139 0 : if (mChildOffset == mRange.mEnd) {
140 0 : return Box();
141 : }
142 0 : return Box(mContext, mChildOffset, this);
143 : }
144 :
145 : nsTArray<uint8_t>
146 0 : Box::Read()
147 : {
148 0 : nsTArray<uint8_t> out;
149 0 : Unused << Read(&out, mRange);
150 0 : return out;
151 : }
152 :
153 : bool
154 0 : Box::Read(nsTArray<uint8_t>* aDest, const MediaByteRange& aRange)
155 : {
156 : int64_t length;
157 0 : if (!mContext->mSource->Length(&length)) {
158 : // The HTTP server didn't give us a length to work with.
159 : // Limit the read to kMAX_BOX_READ max.
160 0 : length = std::min(aRange.mEnd - mChildOffset, kMAX_BOX_READ);
161 : } else {
162 0 : length = aRange.mEnd - mChildOffset;
163 : }
164 0 : aDest->SetLength(length);
165 : size_t bytes;
166 0 : if (!mContext->mSource->CachedReadAt(mChildOffset, aDest->Elements(),
167 0 : aDest->Length(), &bytes) ||
168 0 : bytes != aDest->Length()) {
169 : // Byte ranges are being reported incorrectly
170 0 : NS_WARNING("Read failed in mp4_demuxer::Box::Read()");
171 0 : aDest->Clear();
172 0 : return false;
173 : }
174 0 : return true;
175 : }
176 : }
|