Box.cpp (7630B)
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 "Box.h" 8 9 #include <algorithm> 10 11 #include "ByteStream.h" 12 #include "mozilla/EndianUtils.h" 13 14 namespace mozilla { 15 16 // Limit reads to 32MiB max. 17 // static 18 const uint64_t Box::kMAX_BOX_READ = 32 * 1024 * 1024; 19 20 // Returns the offset from the start of the body of a box of type |aType| 21 // to the start of its first child. 22 static uint32_t BoxOffset(AtomType aType) { 23 const uint32_t FULLBOX_OFFSET = 4; 24 25 if (aType == AtomType("mp4a") || aType == AtomType("enca")) { 26 // AudioSampleEntry; ISO 14496-12, section 8.16 27 return 28; 28 } else if (aType == AtomType("mp4v") || aType == AtomType("encv")) { 29 // VideoSampleEntry; ISO 14496-12, section 8.16 30 return 78; 31 } else if (aType == AtomType("stsd")) { 32 // SampleDescriptionBox; ISO 14496-12, section 8.16 33 // This is a FullBox, and contains a |count| member before its child 34 // boxes. 35 return FULLBOX_OFFSET + 4; 36 } 37 38 return 0; 39 } 40 41 Box::Box(BoxContext* aContext, uint64_t aOffset, const Box* aParent) 42 : mContext(aContext), mParent(aParent) { 43 // Default error code for early returns if ranges are not as expected 44 mInitStatus = NS_ERROR_DOM_MEDIA_RANGE_ERR; 45 // Set start offset for Offset() even when the box is unavailable. 46 // The empty range indicates !IsAvailable(), overwritten below on success. 47 mRange = MediaByteRange(aOffset, aOffset); 48 49 uint8_t header[8]; 50 if (aOffset > INT64_MAX - sizeof(header)) { 51 return; 52 } 53 54 MediaByteRange headerRange(aOffset, aOffset + sizeof(header)); 55 if (mParent && !mParent->mRange.Contains(headerRange)) { 56 return; 57 } 58 59 const MediaByteRange* byteRange; 60 for (int i = 0;; i++) { 61 if (i == mContext->mByteRanges.Length()) { 62 return; 63 } 64 65 byteRange = static_cast<const MediaByteRange*>(&mContext->mByteRanges[i]); 66 if (byteRange->Contains(headerRange)) { 67 break; 68 } 69 } 70 71 size_t bytes; 72 nsresult rv = 73 mContext->mSource->CachedReadAt(aOffset, header, sizeof(header), &bytes); 74 if (NS_FAILED(rv)) { 75 mInitStatus = rv; 76 return; 77 } 78 if (bytes != sizeof(header)) { 79 // CachedReadAt() would usually return an error if the read cannot 80 // complete, but BlockingStream can return fewer bytes on end of stream or 81 // after a network error has occurred. 82 return; 83 } 84 85 uint64_t size = BigEndian::readUint32(header); 86 if (size == 1) { 87 uint8_t bigLength[8]; 88 if (aOffset > INT64_MAX - sizeof(header) - sizeof(bigLength)) { 89 return; 90 } 91 MediaByteRange bigLengthRange(headerRange.mEnd, 92 headerRange.mEnd + sizeof(bigLength)); 93 if ((mParent && !mParent->mRange.Contains(bigLengthRange)) || 94 !byteRange->Contains(bigLengthRange)) { 95 return; 96 } 97 rv = mContext->mSource->CachedReadAt(aOffset + sizeof(header), bigLength, 98 sizeof(bigLength), &bytes); 99 if (NS_FAILED(rv)) { 100 mInitStatus = rv; 101 return; 102 } 103 if (bytes != sizeof(bigLength)) { 104 return; 105 } 106 size = BigEndian::readUint64(bigLength); 107 mBodyOffset = bigLengthRange.mEnd; 108 } else if (size == 0) { 109 // box extends to end of file. 110 size = mContext->mByteRanges.LastInterval().mEnd - aOffset; 111 mBodyOffset = headerRange.mEnd; 112 } else { 113 mBodyOffset = headerRange.mEnd; 114 } 115 116 if (size > INT64_MAX) { 117 return; 118 } 119 int64_t end = static_cast<int64_t>(aOffset) + static_cast<int64_t>(size); 120 if (end < static_cast<int64_t>(aOffset)) { 121 // Overflowed. 122 return; 123 } 124 125 mType = BigEndian::readUint32(&header[4]); 126 mChildOffset = mBodyOffset + BoxOffset(mType); 127 128 MediaByteRange boxRange(aOffset, end); 129 if (mChildOffset > boxRange.mEnd || 130 (mParent && !mParent->mRange.Contains(boxRange)) || 131 !byteRange->Contains(boxRange)) { 132 return; 133 } 134 135 mInitStatus = NS_OK; 136 mRange = boxRange; 137 } 138 139 Box::Box() 140 : mContext(nullptr), mBodyOffset(0), mChildOffset(0), mParent(nullptr) {} 141 142 Box Box::Next() const { 143 MOZ_ASSERT(IsAvailable()); 144 return Box(mContext, mRange.mEnd, mParent); 145 } 146 147 Box Box::FirstChild() const { 148 MOZ_ASSERT(IsAvailable()); 149 if (mChildOffset == mRange.mEnd) { 150 return Box(); 151 } 152 return Box(mContext, mChildOffset, this); 153 } 154 155 nsTArray<uint8_t> Box::ReadCompleteBox() const { 156 const size_t length = mRange.mEnd - mRange.mStart; 157 nsTArray<uint8_t> out(length); 158 out.SetLength(length); 159 size_t bytesRead = 0; 160 if (NS_FAILED(mContext->mSource->CachedReadAt(mRange.mStart, out.Elements(), 161 length, &bytesRead)) || 162 bytesRead != length) { 163 // Byte ranges are being reported incorrectly 164 NS_WARNING("Read failed in mozilla::Box::ReadCompleteBox()"); 165 return nsTArray<uint8_t>(0); 166 } 167 return out; 168 } 169 170 nsTArray<uint8_t> Box::Read() const { 171 nsTArray<uint8_t> out; 172 (void)Read(&out, mRange); 173 return out; 174 } 175 176 bool Box::Read(nsTArray<uint8_t>* aDest, const MediaByteRange& aRange) const { 177 int64_t length; 178 if (!mContext->mSource->Length(&length)) { 179 // The HTTP server didn't give us a length to work with. 180 // Limit the read to kMAX_BOX_READ max. 181 length = std::min(aRange.mEnd - mChildOffset, kMAX_BOX_READ); 182 } else { 183 length = aRange.mEnd - mChildOffset; 184 } 185 aDest->SetLength(length); 186 size_t bytes; 187 if (NS_FAILED(mContext->mSource->CachedReadAt(mChildOffset, aDest->Elements(), 188 aDest->Length(), &bytes)) || 189 bytes != aDest->Length()) { 190 // Byte ranges are being reported incorrectly 191 NS_WARNING("Read failed in mozilla::Box::Read()"); 192 aDest->Clear(); 193 return false; 194 } 195 return true; 196 } 197 198 ByteSlice Box::ReadAsSlice() const { 199 if (!mContext || mRange.IsEmpty()) { 200 return ByteSlice{nullptr, 0}; 201 } 202 203 int64_t length; 204 if (!mContext->mSource->Length(&length)) { 205 // The HTTP server didn't give us a length to work with. 206 // Limit the read to kMAX_BOX_READ max. 207 length = std::min(mRange.mEnd - mChildOffset, kMAX_BOX_READ); 208 } else { 209 length = mRange.mEnd - mChildOffset; 210 } 211 212 const uint8_t* data = 213 mContext->mSource->GetContiguousAccess(mChildOffset, length); 214 if (data) { 215 // We can direct access the underlying storage of the ByteStream. 216 return ByteSlice{data, size_t(length)}; 217 } 218 219 uint8_t* p = mContext->mAllocator.Allocate(size_t(length)); 220 size_t bytes; 221 if (NS_FAILED( 222 mContext->mSource->CachedReadAt(mChildOffset, p, length, &bytes)) || 223 bytes != length) { 224 // Byte ranges are being reported incorrectly 225 NS_WARNING("Read failed in mozilla::Box::ReadAsSlice()"); 226 return ByteSlice{nullptr, 0}; 227 } 228 return ByteSlice{p, size_t(length)}; 229 } 230 231 const size_t BLOCK_CAPACITY = 16 * 1024; 232 233 uint8_t* BumpAllocator::Allocate(size_t aNumBytes) { 234 if (aNumBytes > BLOCK_CAPACITY) { 235 mBuffers.AppendElement(nsTArray<uint8_t>(aNumBytes)); 236 mBuffers.LastElement().SetLength(aNumBytes); 237 return mBuffers.LastElement().Elements(); 238 } 239 for (nsTArray<uint8_t>& buffer : mBuffers) { 240 if (buffer.Length() + aNumBytes < BLOCK_CAPACITY) { 241 size_t offset = buffer.Length(); 242 buffer.SetLength(buffer.Length() + aNumBytes); 243 return buffer.Elements() + offset; 244 } 245 } 246 mBuffers.AppendElement(nsTArray<uint8_t>(BLOCK_CAPACITY)); 247 mBuffers.LastElement().SetLength(aNumBytes); 248 return mBuffers.LastElement().Elements(); 249 } 250 251 } // namespace mozilla