content/media/wave/WaveReader.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

     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

mercurial