Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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 #include "nsError.h"
7 #include "AbstractMediaDecoder.h"
8 #include "MediaResource.h"
9 #include "WaveReader.h"
10 #include "mozilla/dom/TimeRanges.h"
11 #include "MediaDecoderStateMachine.h"
12 #include "VideoUtils.h"
13 #include "nsISeekableStream.h"
15 #include <stdint.h>
16 #include "mozilla/ArrayUtils.h"
17 #include "mozilla/CheckedInt.h"
18 #include "mozilla/Endian.h"
19 #include <algorithm>
21 namespace mozilla {
23 // Un-comment to enable logging of seek bisections.
24 //#define SEEK_LOGGING
26 #ifdef PR_LOGGING
27 extern PRLogModuleInfo* gMediaDecoderLog;
28 #define LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
29 #ifdef SEEK_LOGGING
30 #define SEEK_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
31 #else
32 #define SEEK_LOG(type, msg)
33 #endif
34 #else
35 #define LOG(type, msg)
36 #define SEEK_LOG(type, msg)
37 #endif
39 struct waveIdToName {
40 uint32_t id;
41 nsCString name;
42 };
45 // Magic values that identify RIFF chunks we're interested in.
46 static const uint32_t RIFF_CHUNK_MAGIC = 0x52494646;
47 static const uint32_t WAVE_CHUNK_MAGIC = 0x57415645;
48 static const uint32_t FRMT_CHUNK_MAGIC = 0x666d7420;
49 static const uint32_t DATA_CHUNK_MAGIC = 0x64617461;
50 static const uint32_t LIST_CHUNK_MAGIC = 0x4c495354;
52 // Size of chunk header. 4 byte chunk header type and 4 byte size field.
53 static const uint16_t CHUNK_HEADER_SIZE = 8;
55 // Size of RIFF header. RIFF chunk and 4 byte RIFF type.
56 static const uint16_t RIFF_INITIAL_SIZE = CHUNK_HEADER_SIZE + 4;
58 // Size of required part of format chunk. Actual format chunks may be
59 // extended (for non-PCM encodings), but we skip any extended data.
60 static const uint16_t WAVE_FORMAT_CHUNK_SIZE = 16;
62 // PCM encoding type from format chunk. Linear PCM is the only encoding
63 // supported by AudioStream.
64 static const uint16_t WAVE_FORMAT_ENCODING_PCM = 1;
66 // We reject files with more than this number of channels if we're decoding for
67 // playback.
68 static const uint8_t MAX_CHANNELS = 2;
70 namespace {
71 uint32_t
72 ReadUint32BE(const char** aBuffer)
73 {
74 uint32_t result = BigEndian::readUint32(*aBuffer);
75 *aBuffer += sizeof(uint32_t);
76 return result;
77 }
79 uint32_t
80 ReadUint32LE(const char** aBuffer)
81 {
82 uint32_t result = LittleEndian::readUint32(*aBuffer);
83 *aBuffer += sizeof(uint32_t);
84 return result;
85 }
87 uint16_t
88 ReadUint16LE(const char** aBuffer)
89 {
90 uint16_t result = LittleEndian::readUint16(*aBuffer);
91 *aBuffer += sizeof(uint16_t);
92 return result;
93 }
95 int16_t
96 ReadInt16LE(const char** aBuffer)
97 {
98 uint16_t result = LittleEndian::readInt16(*aBuffer);
99 *aBuffer += sizeof(int16_t);
100 return result;
101 }
103 uint8_t
104 ReadUint8(const char** aBuffer)
105 {
106 uint8_t result = uint8_t((*aBuffer)[0]);
107 *aBuffer += sizeof(uint8_t);
108 return result;
109 }
110 }
112 WaveReader::WaveReader(AbstractMediaDecoder* aDecoder)
113 : MediaDecoderReader(aDecoder)
114 {
115 MOZ_COUNT_CTOR(WaveReader);
116 }
118 WaveReader::~WaveReader()
119 {
120 MOZ_COUNT_DTOR(WaveReader);
121 }
123 nsresult WaveReader::Init(MediaDecoderReader* aCloneDonor)
124 {
125 return NS_OK;
126 }
128 nsresult WaveReader::ReadMetadata(MediaInfo* aInfo,
129 MetadataTags** aTags)
130 {
131 NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
133 bool loaded = LoadRIFFChunk();
134 if (!loaded) {
135 return NS_ERROR_FAILURE;
136 }
138 nsAutoPtr<dom::HTMLMediaElement::MetadataTags> tags;
140 bool loadAllChunks = LoadAllChunks(tags);
141 if (!loadAllChunks) {
142 return NS_ERROR_FAILURE;
143 }
145 mInfo.mAudio.mHasAudio = true;
146 mInfo.mAudio.mRate = mSampleRate;
147 mInfo.mAudio.mChannels = mChannels;
149 *aInfo = mInfo;
151 *aTags = tags.forget();
153 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
155 mDecoder->SetMediaDuration(
156 static_cast<int64_t>(BytesToTime(GetDataLength()) * USECS_PER_S));
158 return NS_OK;
159 }
161 template <typename T> T UnsignedByteToAudioSample(uint8_t aValue);
162 template <typename T> T SignedShortToAudioSample(int16_t aValue);
164 template <> inline float
165 UnsignedByteToAudioSample<float>(uint8_t aValue)
166 {
167 return aValue * (2.0f / UINT8_MAX) - 1.0f;
168 }
169 template <> inline int16_t
170 UnsignedByteToAudioSample<int16_t>(uint8_t aValue)
171 {
172 return int16_t(aValue * UINT16_MAX / UINT8_MAX + INT16_MIN);
173 }
175 template <> inline float
176 SignedShortToAudioSample<float>(int16_t aValue)
177 {
178 return AudioSampleToFloat(aValue);
179 }
180 template <> inline int16_t
181 SignedShortToAudioSample<int16_t>(int16_t aValue)
182 {
183 return aValue;
184 }
186 bool WaveReader::DecodeAudioData()
187 {
188 NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
190 int64_t pos = GetPosition() - mWavePCMOffset;
191 int64_t len = GetDataLength();
192 int64_t remaining = len - pos;
193 NS_ASSERTION(remaining >= 0, "Current wave position is greater than wave file length");
195 static const int64_t BLOCK_SIZE = 4096;
196 int64_t readSize = std::min(BLOCK_SIZE, remaining);
197 int64_t frames = readSize / mFrameSize;
199 static_assert(uint64_t(BLOCK_SIZE) < UINT_MAX /
200 sizeof(AudioDataValue) / MAX_CHANNELS,
201 "bufferSize calculation could overflow.");
202 const size_t bufferSize = static_cast<size_t>(frames * mChannels);
203 nsAutoArrayPtr<AudioDataValue> sampleBuffer(new AudioDataValue[bufferSize]);
205 static_assert(uint64_t(BLOCK_SIZE) < UINT_MAX / sizeof(char),
206 "BLOCK_SIZE too large for enumerator.");
207 nsAutoArrayPtr<char> dataBuffer(new char[static_cast<size_t>(readSize)]);
209 if (!ReadAll(dataBuffer, readSize)) {
210 return false;
211 }
213 // convert data to samples
214 const char* d = dataBuffer.get();
215 AudioDataValue* s = sampleBuffer.get();
216 for (int i = 0; i < frames; ++i) {
217 for (unsigned int j = 0; j < mChannels; ++j) {
218 if (mSampleFormat == FORMAT_U8) {
219 uint8_t v = ReadUint8(&d);
220 *s++ = UnsignedByteToAudioSample<AudioDataValue>(v);
221 } else if (mSampleFormat == FORMAT_S16) {
222 int16_t v = ReadInt16LE(&d);
223 *s++ = SignedShortToAudioSample<AudioDataValue>(v);
224 }
225 }
226 }
228 double posTime = BytesToTime(pos);
229 double readSizeTime = BytesToTime(readSize);
230 NS_ASSERTION(posTime <= INT64_MAX / USECS_PER_S, "posTime overflow");
231 NS_ASSERTION(readSizeTime <= INT64_MAX / USECS_PER_S, "readSizeTime overflow");
232 NS_ASSERTION(frames < INT32_MAX, "frames overflow");
234 mAudioQueue.Push(new AudioData(pos,
235 static_cast<int64_t>(posTime * USECS_PER_S),
236 static_cast<int64_t>(readSizeTime * USECS_PER_S),
237 static_cast<int32_t>(frames),
238 sampleBuffer.forget(),
239 mChannels));
241 return true;
242 }
244 bool WaveReader::DecodeVideoFrame(bool &aKeyframeSkip,
245 int64_t aTimeThreshold)
246 {
247 NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
249 return false;
250 }
252 nsresult WaveReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime)
253 {
254 NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
255 LOG(PR_LOG_DEBUG, ("%p About to seek to %lld", mDecoder, aTarget));
256 if (NS_FAILED(ResetDecode())) {
257 return NS_ERROR_FAILURE;
258 }
259 double d = BytesToTime(GetDataLength());
260 NS_ASSERTION(d < INT64_MAX / USECS_PER_S, "Duration overflow");
261 int64_t duration = static_cast<int64_t>(d * USECS_PER_S);
262 double seekTime = std::min(aTarget, duration) / static_cast<double>(USECS_PER_S);
263 int64_t position = RoundDownToFrame(static_cast<int64_t>(TimeToBytes(seekTime)));
264 NS_ASSERTION(INT64_MAX - mWavePCMOffset > position, "Integer overflow during wave seek");
265 position += mWavePCMOffset;
266 return mDecoder->GetResource()->Seek(nsISeekableStream::NS_SEEK_SET, position);
267 }
269 static double RoundToUsecs(double aSeconds) {
270 return floor(aSeconds * USECS_PER_S) / USECS_PER_S;
271 }
273 nsresult WaveReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
274 {
275 if (!mInfo.HasAudio()) {
276 return NS_OK;
277 }
278 int64_t startOffset = mDecoder->GetResource()->GetNextCachedData(mWavePCMOffset);
279 while (startOffset >= 0) {
280 int64_t endOffset = mDecoder->GetResource()->GetCachedDataEnd(startOffset);
281 // Bytes [startOffset..endOffset] are cached.
282 NS_ASSERTION(startOffset >= mWavePCMOffset, "Integer underflow in GetBuffered");
283 NS_ASSERTION(endOffset >= mWavePCMOffset, "Integer underflow in GetBuffered");
285 // We need to round the buffered ranges' times to microseconds so that they
286 // have the same precision as the currentTime and duration attribute on
287 // the media element.
288 aBuffered->Add(RoundToUsecs(BytesToTime(startOffset - mWavePCMOffset)),
289 RoundToUsecs(BytesToTime(endOffset - mWavePCMOffset)));
290 startOffset = mDecoder->GetResource()->GetNextCachedData(endOffset);
291 }
292 return NS_OK;
293 }
295 bool
296 WaveReader::ReadAll(char* aBuf, int64_t aSize, int64_t* aBytesRead)
297 {
298 uint32_t got = 0;
299 if (aBytesRead) {
300 *aBytesRead = 0;
301 }
302 do {
303 uint32_t read = 0;
304 if (NS_FAILED(mDecoder->GetResource()->Read(aBuf + got, uint32_t(aSize - got), &read))) {
305 NS_WARNING("Resource read failed");
306 return false;
307 }
308 if (read == 0) {
309 return false;
310 }
311 got += read;
312 if (aBytesRead) {
313 *aBytesRead = got;
314 }
315 } while (got != aSize);
316 return true;
317 }
319 bool
320 WaveReader::LoadRIFFChunk()
321 {
322 char riffHeader[RIFF_INITIAL_SIZE];
323 const char* p = riffHeader;
325 NS_ABORT_IF_FALSE(mDecoder->GetResource()->Tell() == 0,
326 "LoadRIFFChunk called when resource in invalid state");
328 if (!ReadAll(riffHeader, sizeof(riffHeader))) {
329 return false;
330 }
332 static_assert(sizeof(uint32_t) * 3 <= RIFF_INITIAL_SIZE,
333 "Reads would overflow riffHeader buffer.");
334 if (ReadUint32BE(&p) != RIFF_CHUNK_MAGIC) {
335 NS_WARNING("resource data not in RIFF format");
336 return false;
337 }
339 // Skip over RIFF size field.
340 p += sizeof(uint32_t);
342 if (ReadUint32BE(&p) != WAVE_CHUNK_MAGIC) {
343 NS_WARNING("Expected WAVE chunk");
344 return false;
345 }
347 return true;
348 }
350 bool
351 WaveReader::LoadFormatChunk(uint32_t aChunkSize)
352 {
353 uint32_t rate, channels, frameSize, sampleFormat;
354 char waveFormat[WAVE_FORMAT_CHUNK_SIZE];
355 const char* p = waveFormat;
357 // RIFF chunks are always word (two byte) aligned.
358 NS_ABORT_IF_FALSE(mDecoder->GetResource()->Tell() % 2 == 0,
359 "LoadFormatChunk called with unaligned resource");
361 if (!ReadAll(waveFormat, sizeof(waveFormat))) {
362 return false;
363 }
365 static_assert(sizeof(uint16_t) +
366 sizeof(uint16_t) +
367 sizeof(uint32_t) +
368 4 +
369 sizeof(uint16_t) +
370 sizeof(uint16_t) <= sizeof(waveFormat),
371 "Reads would overflow waveFormat buffer.");
372 if (ReadUint16LE(&p) != WAVE_FORMAT_ENCODING_PCM) {
373 NS_WARNING("WAVE is not uncompressed PCM, compressed encodings are not supported");
374 return false;
375 }
377 channels = ReadUint16LE(&p);
378 rate = ReadUint32LE(&p);
380 // Skip over average bytes per second field.
381 p += 4;
383 frameSize = ReadUint16LE(&p);
385 sampleFormat = ReadUint16LE(&p);
387 // PCM encoded WAVEs are not expected to have an extended "format" chunk,
388 // but I have found WAVEs that have a extended "format" chunk with an
389 // extension size of 0 bytes. Be polite and handle this rather than
390 // considering the file invalid. This code skips any extension of the
391 // "format" chunk.
392 if (aChunkSize > WAVE_FORMAT_CHUNK_SIZE) {
393 char extLength[2];
394 const char* p = extLength;
396 if (!ReadAll(extLength, sizeof(extLength))) {
397 return false;
398 }
400 static_assert(sizeof(uint16_t) <= sizeof(extLength),
401 "Reads would overflow extLength buffer.");
402 uint16_t extra = ReadUint16LE(&p);
403 if (aChunkSize - (WAVE_FORMAT_CHUNK_SIZE + 2) != extra) {
404 NS_WARNING("Invalid extended format chunk size");
405 return false;
406 }
407 extra += extra % 2;
409 if (extra > 0) {
410 static_assert(UINT16_MAX + (UINT16_MAX % 2) < UINT_MAX / sizeof(char),
411 "chunkExtension array too large for iterator.");
412 nsAutoArrayPtr<char> chunkExtension(new char[extra]);
413 if (!ReadAll(chunkExtension.get(), extra)) {
414 return false;
415 }
416 }
417 }
419 // RIFF chunks are always word (two byte) aligned.
420 NS_ABORT_IF_FALSE(mDecoder->GetResource()->Tell() % 2 == 0,
421 "LoadFormatChunk left resource unaligned");
423 // Make sure metadata is fairly sane. The rate check is fairly arbitrary,
424 // but the channels check is intentionally limited to mono or stereo
425 // when the media is intended for direct playback because that's what the
426 // audio backend currently supports.
427 unsigned int actualFrameSize = (sampleFormat == 8 ? 1 : 2) * channels;
428 if (rate < 100 || rate > 96000 ||
429 (((channels < 1 || channels > MAX_CHANNELS) ||
430 (frameSize != 1 && frameSize != 2 && frameSize != 4)) &&
431 !mIgnoreAudioOutputFormat) ||
432 (sampleFormat != 8 && sampleFormat != 16) ||
433 frameSize != actualFrameSize) {
434 NS_WARNING("Invalid WAVE metadata");
435 return false;
436 }
438 ReentrantMonitorAutoEnter monitor(mDecoder->GetReentrantMonitor());
439 mSampleRate = rate;
440 mChannels = channels;
441 mFrameSize = frameSize;
442 if (sampleFormat == 8) {
443 mSampleFormat = FORMAT_U8;
444 } else {
445 mSampleFormat = FORMAT_S16;
446 }
447 return true;
448 }
450 bool
451 WaveReader::FindDataOffset(uint32_t aChunkSize)
452 {
453 // RIFF chunks are always word (two byte) aligned.
454 NS_ABORT_IF_FALSE(mDecoder->GetResource()->Tell() % 2 == 0,
455 "FindDataOffset called with unaligned resource");
457 int64_t offset = mDecoder->GetResource()->Tell();
458 if (offset <= 0 || offset > UINT32_MAX) {
459 NS_WARNING("PCM data offset out of range");
460 return false;
461 }
463 ReentrantMonitorAutoEnter monitor(mDecoder->GetReentrantMonitor());
464 mWaveLength = aChunkSize;
465 mWavePCMOffset = uint32_t(offset);
466 return true;
467 }
469 double
470 WaveReader::BytesToTime(int64_t aBytes) const
471 {
472 NS_ABORT_IF_FALSE(aBytes >= 0, "Must be >= 0");
473 return float(aBytes) / mSampleRate / mFrameSize;
474 }
476 int64_t
477 WaveReader::TimeToBytes(double aTime) const
478 {
479 NS_ABORT_IF_FALSE(aTime >= 0.0f, "Must be >= 0");
480 return RoundDownToFrame(int64_t(aTime * mSampleRate * mFrameSize));
481 }
483 int64_t
484 WaveReader::RoundDownToFrame(int64_t aBytes) const
485 {
486 NS_ABORT_IF_FALSE(aBytes >= 0, "Must be >= 0");
487 return aBytes - (aBytes % mFrameSize);
488 }
490 int64_t
491 WaveReader::GetDataLength()
492 {
493 int64_t length = mWaveLength;
494 // If the decoder has a valid content length, and it's shorter than the
495 // expected length of the PCM data, calculate the playback duration from
496 // the content length rather than the expected PCM data length.
497 int64_t streamLength = mDecoder->GetResource()->GetLength();
498 if (streamLength >= 0) {
499 int64_t dataLength = std::max<int64_t>(0, streamLength - mWavePCMOffset);
500 length = std::min(dataLength, length);
501 }
502 return length;
503 }
505 int64_t
506 WaveReader::GetPosition()
507 {
508 return mDecoder->GetResource()->Tell();
509 }
511 bool
512 WaveReader::GetNextChunk(uint32_t* aChunk, uint32_t* aChunkSize)
513 {
514 NS_ABORT_IF_FALSE(aChunk, "Must have aChunk");
515 NS_ABORT_IF_FALSE(aChunkSize, "Must have aChunkSize");
516 NS_ABORT_IF_FALSE(mDecoder->GetResource()->Tell() % 2 == 0,
517 "GetNextChunk called with unaligned resource");
519 char chunkHeader[CHUNK_HEADER_SIZE];
520 const char* p = chunkHeader;
522 if (!ReadAll(chunkHeader, sizeof(chunkHeader))) {
523 return false;
524 }
526 static_assert(sizeof(uint32_t) * 2 <= CHUNK_HEADER_SIZE,
527 "Reads would overflow chunkHeader buffer.");
528 *aChunk = ReadUint32BE(&p);
529 *aChunkSize = ReadUint32LE(&p);
531 return true;
532 }
534 bool
535 WaveReader::LoadListChunk(uint32_t aChunkSize,
536 nsAutoPtr<dom::HTMLMediaElement::MetadataTags> &aTags)
537 {
538 // List chunks are always word (two byte) aligned.
539 NS_ABORT_IF_FALSE(mDecoder->GetResource()->Tell() % 2 == 0,
540 "LoadListChunk called with unaligned resource");
542 static const unsigned int MAX_CHUNK_SIZE = 1 << 16;
543 static_assert(uint64_t(MAX_CHUNK_SIZE) < UINT_MAX / sizeof(char),
544 "MAX_CHUNK_SIZE too large for enumerator.");
546 if (aChunkSize > MAX_CHUNK_SIZE) {
547 return false;
548 }
550 nsAutoArrayPtr<char> chunk(new char[aChunkSize]);
551 if (!ReadAll(chunk.get(), aChunkSize)) {
552 return false;
553 }
555 static const uint32_t INFO_LIST_MAGIC = 0x494e464f;
556 const char *p = chunk.get();
557 if (ReadUint32BE(&p) != INFO_LIST_MAGIC) {
558 return false;
559 }
561 const waveIdToName ID_TO_NAME[] = {
562 { 0x49415254, NS_LITERAL_CSTRING("artist") }, // IART
563 { 0x49434d54, NS_LITERAL_CSTRING("comments") }, // ICMT
564 { 0x49474e52, NS_LITERAL_CSTRING("genre") }, // IGNR
565 { 0x494e414d, NS_LITERAL_CSTRING("name") }, // INAM
566 };
568 const char* const end = chunk.get() + aChunkSize;
570 aTags = new dom::HTMLMediaElement::MetadataTags;
572 while (p + 8 < end) {
573 uint32_t id = ReadUint32BE(&p);
574 // Uppercase tag id, inspired by GStreamer's Wave parser.
575 id &= 0xDFDFDFDF;
577 uint32_t length = ReadUint32LE(&p);
579 // Subchunk shall not exceed parent chunk.
580 if (p + length > end) {
581 break;
582 }
584 // Wrap the string, adjusting length to account for optional
585 // null termination in the chunk.
586 nsCString val(p, length);
587 if (length > 0 && val[length - 1] == '\0') {
588 val.SetLength(length - 1);
589 }
591 // Chunks in List::INFO are always word (two byte) aligned. So round up if
592 // necessary.
593 length += length % 2;
594 p += length;
596 if (!IsUTF8(val)) {
597 continue;
598 }
600 for (size_t i = 0; i < mozilla::ArrayLength(ID_TO_NAME); ++i) {
601 if (id == ID_TO_NAME[i].id) {
602 aTags->Put(ID_TO_NAME[i].name, val);
603 break;
604 }
605 }
606 }
608 return true;
609 }
611 bool
612 WaveReader::LoadAllChunks(nsAutoPtr<dom::HTMLMediaElement::MetadataTags> &aTags)
613 {
614 // Chunks are always word (two byte) aligned.
615 NS_ABORT_IF_FALSE(mDecoder->GetResource()->Tell() % 2 == 0,
616 "LoadAllChunks called with unaligned resource");
618 bool loadFormatChunk = false;
619 bool findDataOffset = false;
621 for (;;) {
622 static const unsigned int CHUNK_HEADER_SIZE = 8;
623 char chunkHeader[CHUNK_HEADER_SIZE];
624 const char* p = chunkHeader;
626 if (!ReadAll(chunkHeader, sizeof(chunkHeader))) {
627 return false;
628 }
630 static_assert(sizeof(uint32_t) * 2 <= CHUNK_HEADER_SIZE,
631 "Reads would overflow chunkHeader buffer.");
633 uint32_t magic = ReadUint32BE(&p);
634 uint32_t chunkSize = ReadUint32LE(&p);
635 int64_t chunkStart = GetPosition();
637 switch (magic) {
638 case FRMT_CHUNK_MAGIC:
639 loadFormatChunk = LoadFormatChunk(chunkSize);
640 if (!loadFormatChunk) {
641 return false;
642 }
643 break;
645 case LIST_CHUNK_MAGIC:
646 if (!aTags) {
647 LoadListChunk(chunkSize, aTags);
648 }
649 break;
651 case DATA_CHUNK_MAGIC:
652 findDataOffset = FindDataOffset(chunkSize);
653 return loadFormatChunk && findDataOffset;
655 default:
656 break;
657 }
659 // RIFF chunks are two-byte aligned, so round up if necessary.
660 chunkSize += chunkSize % 2;
662 // Move forward to next chunk
663 CheckedInt64 forward = CheckedInt64(chunkStart) + chunkSize - GetPosition();
665 if (!forward.isValid() || forward.value() < 0) {
666 return false;
667 }
669 static const int64_t MAX_CHUNK_SIZE = 1 << 16;
670 static_assert(uint64_t(MAX_CHUNK_SIZE) < UINT_MAX / sizeof(char),
671 "MAX_CHUNK_SIZE too large for enumerator.");
672 nsAutoArrayPtr<char> chunk(new char[MAX_CHUNK_SIZE]);
673 while (forward.value() > 0) {
674 int64_t size = std::min(forward.value(), MAX_CHUNK_SIZE);
675 if (!ReadAll(chunk.get(), size)) {
676 return false;
677 }
678 forward -= size;
679 }
680 }
682 return false;
683 }
685 } // namespace mozilla