Bug 524109 - Added support for 24 bit wav files. r=cpearce
authorLouis Christie <lchristie@mozilla.com>
Mon, 14 Dec 2015 15:07:10 +1300
changeset 315805 14b8a98aff19b6771114ba87f88a99a05aec9a6b
parent 315804 ec15b39b21808ddf8f069ddc592fd473f4f57fe0
child 315806 27def71e2d0b2acccfdfb67b267c5f1028be038e
push id8468
push userhurley@todesschaf.org
push dateWed, 16 Dec 2015 19:25:06 +0000
reviewerscpearce
bugs524109
milestone46.0a1
Bug 524109 - Added support for 24 bit wav files. r=cpearce
dom/media/test/manifest.js
dom/media/test/mochitest.ini
dom/media/test/wavedata_s24.wav
dom/media/test/wavedata_s24.wav^headers^
dom/media/wave/WaveReader.cpp
dom/media/wave/WaveReader.h
--- a/dom/media/test/manifest.js
+++ b/dom/media/test/manifest.js
@@ -149,16 +149,18 @@ var gPlayTests = [
   // 8-bit samples
   { name:"r11025_u8_c1.wav", type:"audio/x-wav", duration:1.0 },
   // 8-bit samples, file is truncated
   { name:"r11025_u8_c1_trunc.wav", type:"audio/x-wav", duration:1.8 },
   // file has trailing non-PCM data
   { name:"r11025_s16_c1_trailing.wav", type:"audio/x-wav", duration:1.0 },
   // file with list chunk
   { name:"r16000_u8_c1_list.wav", type:"audio/x-wav", duration:4.2 },
+  // 24-bit samples
+  { name:"wavedata_s24.wav", type:"audio/x-wav", duration:1.0 },
 
   // Ogg stream without eof marker
   { name:"bug461281.ogg", type:"application/ogg", duration:2.208 },
 
   // oggz-chop stream
   { name:"bug482461.ogv", type:"video/ogg", duration:4.34 },
   // Theora only oggz-chop stream
   { name:"bug482461-theora.ogv", type:"video/ogg", duration:4.138 },
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -550,16 +550,18 @@ support-files =
   wave_metadata_bad_no_null.wav
   wave_metadata_bad_no_null.wav^headers^
   wave_metadata_bad_utf8.wav
   wave_metadata_bad_utf8.wav^headers^
   wave_metadata_unknown_tag.wav
   wave_metadata_unknown_tag.wav^headers^
   wave_metadata_utf8.wav
   wave_metadata_utf8.wav^headers^
+  wavedata_s24.wav
+  wavedata_s24.wav^headers^
   wavedata_s16.wav
   wavedata_s16.wav^headers^
   wavedata_u8.wav
   wavedata_u8.wav^headers^
 
 [test_access_control.html]
 skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
 [test_aspectratio_mp4.html]
new file mode 100644
index 0000000000000000000000000000000000000000..dbdb6aac1ec3edc52909e529e591f578b36df2ee
GIT binary patch
literal 33071
zc%1FXAr6C300Yo2T!1;`+ys&nOpIi9f@u<h!{NtH4xe-EwNEsCzxw|EOFoa`IWOz2
zPI4)|wn<n1Dov(w9jmglf0Hd|HUIzs00000000000000000000000000000000000
G;BOtnXNleb
new file mode 100644
--- /dev/null
+++ b/dom/media/test/wavedata_s24.wav^headers^
@@ -0,0 +1,1 @@
+Cache-Control: no-store
--- a/dom/media/wave/WaveReader.cpp
+++ b/dom/media/wave/WaveReader.cpp
@@ -75,16 +75,30 @@ namespace {
   uint32_t
   ReadUint32LE(const char** aBuffer)
   {
     uint32_t result = LittleEndian::readUint32(*aBuffer);
     *aBuffer += sizeof(uint32_t);
     return result;
   }
 
+  int32_t
+  ReadInt24LE(const char** aBuffer)
+  {
+    int32_t result = int32_t((uint8_t((*aBuffer)[2]) << 16) |
+                             (uint8_t((*aBuffer)[1]) << 8 ) |
+                             (uint8_t((*aBuffer)[0])));
+    if (((*aBuffer)[2] & 0x80) == 0x80) {
+      result = (result | 0xff000000);
+    }
+
+    *aBuffer += 3 * sizeof(char);
+    return result;
+  }
+
   uint16_t
   ReadUint16LE(const char** aBuffer)
   {
     uint16_t result = LittleEndian::readUint16(*aBuffer);
     *aBuffer += sizeof(uint16_t);
     return result;
   }
 
@@ -143,16 +157,17 @@ nsresult WaveReader::ReadMetadata(MediaI
   *aTags = tags.forget();
 
 
   return NS_OK;
 }
 
 template <typename T> T UnsignedByteToAudioSample(uint8_t aValue);
 template <typename T> T SignedShortToAudioSample(int16_t aValue);
+template <typename T> T Signed24bIntToAudioSample(int32_t aValue);
 
 template <> inline float
 UnsignedByteToAudioSample<float>(uint8_t aValue)
 {
   return aValue * (2.0f / UINT8_MAX) - 1.0f;
 }
 template <> inline int16_t
 UnsignedByteToAudioSample<int16_t>(uint8_t aValue)
@@ -166,29 +181,44 @@ SignedShortToAudioSample<float>(int16_t 
   return AudioSampleToFloat(aValue);
 }
 template <> inline int16_t
 SignedShortToAudioSample<int16_t>(int16_t aValue)
 {
   return aValue;
 }
 
+template <> inline float
+Signed24bIntToAudioSample<float>(int32_t aValue)
+{
+  return aValue / 8388608.0f;
+}
+
+template <> inline int16_t
+Signed24bIntToAudioSample<int16_t>(int32_t aValue)
+{
+  return aValue / 256;
+}
+
 bool WaveReader::DecodeAudioData()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   int64_t pos = GetPosition() - mWavePCMOffset;
   int64_t len = GetDataLength();
   int64_t remaining = len - pos;
   NS_ASSERTION(remaining >= 0, "Current wave position is greater than wave file length");
 
-  static const int64_t BLOCK_SIZE = 4096;
+  static const int64_t BLOCK_SIZE = 6144;
   int64_t readSize = std::min(BLOCK_SIZE, remaining);
   int64_t frames = readSize / mFrameSize;
 
+  MOZ_ASSERT(BLOCK_SIZE % 3 == 0);
+  MOZ_ASSERT(BLOCK_SIZE % 2 == 0);
+
   static_assert(uint64_t(BLOCK_SIZE) < UINT_MAX /
                 sizeof(AudioDataValue) / MAX_CHANNELS,
                 "bufferSize calculation could overflow.");
   const size_t bufferSize = static_cast<size_t>(frames * mChannels);
   auto sampleBuffer = MakeUnique<AudioDataValue[]>(bufferSize);
 
   static_assert(uint64_t(BLOCK_SIZE) < UINT_MAX / sizeof(char),
                 "BLOCK_SIZE too large for enumerator.");
@@ -204,16 +234,19 @@ bool WaveReader::DecodeAudioData()
   for (int i = 0; i < frames; ++i) {
     for (unsigned int j = 0; j < mChannels; ++j) {
       if (mSampleFormat == FORMAT_U8) {
         uint8_t v =  ReadUint8(&d);
         *s++ = UnsignedByteToAudioSample<AudioDataValue>(v);
       } else if (mSampleFormat == FORMAT_S16) {
         int16_t v =  ReadInt16LE(&d);
         *s++ = SignedShortToAudioSample<AudioDataValue>(v);
+      } else if (mSampleFormat == FORMAT_S24) {
+        int32_t v =  ReadInt24LE(&d);
+        *s++ = Signed24bIntToAudioSample<AudioDataValue>(v);
       }
     }
   }
 
   double posTime = BytesToTime(pos);
   double readSizeTime = BytesToTime(readSize);
   NS_ASSERTION(posTime <= INT64_MAX / USECS_PER_S, "posTime overflow");
   NS_ASSERTION(readSizeTime <= INT64_MAX / USECS_PER_S, "readSizeTime overflow");
@@ -417,32 +450,35 @@ WaveReader::LoadFormatChunk(uint32_t aCh
   // RIFF chunks are always word (two byte) aligned.
   MOZ_ASSERT(mResource.Tell() % 2 == 0,
              "LoadFormatChunk left resource unaligned");
 
   // Make sure metadata is fairly sane.  The rate check is fairly arbitrary,
   // but the channels check is intentionally limited to mono or stereo
   // when the media is intended for direct playback because that's what the
   // audio backend currently supports.
-  unsigned int actualFrameSize = (sampleFormat == 8 ? 1 : 2) * channels;
+  unsigned int actualFrameSize = sampleFormat * channels / 8;
   if (rate < 100 || rate > 96000 ||
       (((channels < 1 || channels > MAX_CHANNELS) ||
-       (frameSize != 1 && frameSize != 2 && frameSize != 4)) &&
+       (frameSize != 1 && frameSize != 2 && frameSize != 3 &&
+        frameSize != 4 && frameSize != 6)) &&
        !mIgnoreAudioOutputFormat) ||
-       (sampleFormat != 8 && sampleFormat != 16) ||
+       (sampleFormat != 8 && sampleFormat != 16 && sampleFormat != 24) ||
       frameSize != actualFrameSize) {
     NS_WARNING("Invalid WAVE metadata");
     return false;
   }
 
   mSampleRate = rate;
   mChannels = channels;
   mFrameSize = frameSize;
   if (sampleFormat == 8) {
     mSampleFormat = FORMAT_U8;
+  } else if (sampleFormat == 24) {
+    mSampleFormat = FORMAT_S24;
   } else {
     mSampleFormat = FORMAT_S16;
   }
   return true;
 }
 
 bool
 WaveReader::FindDataOffset(uint32_t aChunkSize)
--- a/dom/media/wave/WaveReader.h
+++ b/dom/media/wave/WaveReader.h
@@ -71,17 +71,18 @@ private:
   // Size of a single audio frame, which includes a sample for each channel
   // (interleaved).
   uint32_t mFrameSize;
 
   // The sample format of the PCM data. AudioStream::SampleFormat doesn't
   // support U8.
   enum {
     FORMAT_U8,
-    FORMAT_S16
+    FORMAT_S16,
+    FORMAT_S24
   } mSampleFormat;
 
   // Size of PCM data stored in the WAVE as reported by the data chunk in
   // the media.
   int64_t mWaveLength;
 
   // Start offset of the PCM data in the media stream.  Extends mWaveLength
   // bytes.