Bug 1231793: Part 5 - Added Wave Data Demuxer and Decoder. r=jya
☠☠ backed out by 52d290be7c7d ☠ ☠
authorLouis Christie <lchristie@mozilla.com>
Mon, 15 Feb 2016 12:10:48 +1300
changeset 320565 75c7942267f809bf71ebff8e1061e8ddda79883d
parent 320564 684e62a4d9f50293bf4ae2b9006916ed269a2434
child 320566 d79218caeefc464cca4eb72a6db4146ab3d81c40
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya
bugs1231793
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1231793: Part 5 - Added Wave Data Demuxer and Decoder. r=jya
dom/media/platforms/agnostic/AgnosticDecoderModule.cpp
dom/media/platforms/agnostic/WAVDecoder.cpp
dom/media/platforms/agnostic/WAVDecoder.h
dom/media/platforms/moz.build
dom/media/wave/WaveDecoder.cpp
dom/media/wave/WaveDecoder.h
dom/media/wave/WaveDemuxer.cpp
dom/media/wave/WaveDemuxer.h
dom/media/wave/moz.build
modules/libpref/init/all.js
--- a/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp
@@ -3,25 +3,27 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AgnosticDecoderModule.h"
 #include "OpusDecoder.h"
 #include "VorbisDecoder.h"
 #include "VPXDecoder.h"
+#include "WAVDecoder.h"
 
 namespace mozilla {
 
 bool
 AgnosticDecoderModule::SupportsMimeType(const nsACString& aMimeType) const
 {
   return VPXDecoder::IsVPX(aMimeType) ||
     OpusDataDecoder::IsOpus(aMimeType) ||
-    VorbisDataDecoder::IsVorbis(aMimeType);
+    VorbisDataDecoder::IsVorbis(aMimeType) ||
+    WaveDataDecoder::IsWave(aMimeType);
 }
 
 already_AddRefed<MediaDataDecoder>
 AgnosticDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
                                           layers::LayersBackend aLayersBackend,
                                           layers::ImageContainer* aImageContainer,
                                           FlushableTaskQueue* aVideoTaskQueue,
                                           MediaDataDecoderCallback* aCallback)
@@ -48,14 +50,18 @@ AgnosticDecoderModule::CreateAudioDecode
   if (VorbisDataDecoder::IsVorbis(aConfig.mMimeType)) {
     m = new VorbisDataDecoder(*aConfig.GetAsAudioInfo(),
                               aAudioTaskQueue,
                               aCallback);
   } else if (OpusDataDecoder::IsOpus(aConfig.mMimeType)) {
     m = new OpusDataDecoder(*aConfig.GetAsAudioInfo(),
                             aAudioTaskQueue,
                             aCallback);
+  } else if (WaveDataDecoder::IsWave(aConfig.mMimeType)) {
+    m = new WaveDataDecoder(*aConfig.GetAsAudioInfo(),
+                            aAudioTaskQueue,
+                            aCallback);
   }
 
   return m.forget();
 }
 
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/agnostic/WAVDecoder.cpp
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WAVDecoder.h"
+#include "AudioSampleFormat.h"
+#include "nsAutoPtr.h"
+
+using mp4_demuxer::ByteReader;
+
+namespace mozilla {
+
+WaveDataDecoder::WaveDataDecoder(const AudioInfo& aConfig,
+                                 FlushableTaskQueue* aTaskQueue,
+                                 MediaDataDecoderCallback* aCallback)
+  : mInfo(aConfig)
+  , mTaskQueue(aTaskQueue)
+  , mCallback(aCallback)
+  , mFrames(0)
+{
+}
+
+nsresult
+WaveDataDecoder::Shutdown()
+{
+  return NS_OK;
+}
+
+RefPtr<MediaDataDecoder::InitPromise>
+WaveDataDecoder::Init()
+{
+  return InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__);
+}
+
+nsresult
+WaveDataDecoder::Input(MediaRawData* aSample)
+{
+  nsCOMPtr<nsIRunnable> runnable(
+    NS_NewRunnableMethodWithArg<RefPtr<MediaRawData>>(
+      this, &WaveDataDecoder::Decode,
+      RefPtr<MediaRawData>(aSample)));
+  mTaskQueue->Dispatch(runnable.forget());
+
+  return NS_OK;
+}
+
+void
+WaveDataDecoder::Decode(MediaRawData* aSample)
+{
+  if (!DoDecode(aSample)) {
+    mCallback->Error();
+  } else if (mTaskQueue->IsEmpty()) {
+    mCallback->InputExhausted();
+  }
+}
+
+bool
+WaveDataDecoder::DoDecode(MediaRawData* aSample)
+{
+  size_t aLength = aSample->Size();
+  ByteReader aReader = ByteReader(aSample->Data(), aLength);
+  int64_t aOffset = aSample->mOffset;
+  uint64_t aTstampUsecs = aSample->mTime;
+
+  int32_t frames = aLength * 8 / mInfo.mBitDepth / mInfo.mChannels;
+
+  auto buffer = MakeUnique<AudioDataValue[]>(frames * mInfo.mChannels);
+  for (int i = 0; i < frames; ++i) {
+    for (unsigned int j = 0; j < mInfo.mChannels; ++j) {
+      if (mInfo.mBitDepth == 8) {
+        uint8_t v = aReader.ReadU8();
+        buffer[i * mInfo.mChannels + j] =
+            UInt8bitToAudioSample<AudioDataValue>(v);
+      } else if (mInfo.mBitDepth == 16) {
+        int16_t v = aReader.ReadLE16();
+        buffer[i * mInfo.mChannels + j] =
+            IntegerToAudioSample<AudioDataValue>(v);
+      } else if (mInfo.mBitDepth == 24) {
+        int32_t v = aReader.ReadLE24();
+        buffer[i * mInfo.mChannels + j] =
+            Int24bitToAudioSample<AudioDataValue>(v);
+      }
+    }
+  }
+
+  aReader.DiscardRemaining();
+
+  int64_t duration = frames / mInfo.mRate;
+
+  mCallback->Output(new AudioData(aOffset,
+                                  aTstampUsecs,
+                                  duration,
+                                  frames,
+                                  Move(buffer),
+                                  mInfo.mChannels,
+                                  mInfo.mRate));
+  mFrames += frames;
+
+  return true;
+}
+
+void
+WaveDataDecoder::DoDrain()
+{
+  mCallback->DrainComplete();
+}
+
+nsresult
+WaveDataDecoder::Drain()
+{
+  nsCOMPtr<nsIRunnable> runnable(
+    NS_NewRunnableMethod(this, &WaveDataDecoder::DoDrain));
+  mTaskQueue->Dispatch(runnable.forget());
+  return NS_OK;
+}
+
+nsresult
+WaveDataDecoder::Flush()
+{
+  mTaskQueue->Flush();
+  mFrames = 0;
+  return NS_OK;
+}
+
+/* static */
+bool
+WaveDataDecoder::IsWave(const nsACString& aMimeType)
+{
+  // Some WebAudio uses "audio/x-wav",
+  // WAVdemuxer uses "audio/wave; codecs=aNum".
+  return aMimeType.EqualsLiteral("audio/x-wav") ||
+         aMimeType.EqualsLiteral("audio/wave; codecs=1") ||
+         aMimeType.EqualsLiteral("audio/wave; codecs=65534");
+}
+
+} // namespace mozilla
+#undef LOG
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/agnostic/WAVDecoder.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(WaveDecoder_h_)
+#define WaveDecoder_h_
+
+#include "PlatformDecoderModule.h"
+#include "mp4_demuxer/ByteReader.h"
+
+namespace mozilla {
+
+class WaveDataDecoder : public MediaDataDecoder
+{
+public:
+  WaveDataDecoder(const AudioInfo& aConfig,
+                  FlushableTaskQueue* aTaskQueue,
+                  MediaDataDecoderCallback* aCallback);
+
+  RefPtr<InitPromise> Init() override;
+  nsresult Input(MediaRawData* aSample) override;
+  nsresult Flush() override;
+  nsresult Drain() override;
+  nsresult Shutdown() override;
+  const char* GetDescriptionName() const override
+  {
+    return "wave audio decoder";
+  }
+
+  // Return true if mimetype is Wave
+  static bool IsWave(const nsACString& aMimeType);
+
+private:
+  void Decode (MediaRawData* aSample);
+  bool DoDecode (MediaRawData* aSample);
+  void DoDrain ();
+
+  const AudioInfo& mInfo;
+  RefPtr<FlushableTaskQueue> mTaskQueue;
+  MediaDataDecoderCallback* mCallback;
+
+  int64_t mFrames;
+};
+
+} // namespace mozilla
+#endif
\ No newline at end of file
--- a/dom/media/platforms/moz.build
+++ b/dom/media/platforms/moz.build
@@ -16,16 +16,17 @@ EXPORTS += [
 ]
 
 UNIFIED_SOURCES += [
     'agnostic/AgnosticDecoderModule.cpp',
     'agnostic/BlankDecoderModule.cpp',
     'agnostic/OpusDecoder.cpp',
     'agnostic/VorbisDecoder.cpp',
     'agnostic/VPXDecoder.cpp',
+    'agnostic/WAVDecoder.cpp',
     'PDMFactory.cpp',
     'PlatformDecoderModule.cpp',
     'wrappers/FuzzingWrapper.cpp',
     'wrappers/H264Converter.cpp'
 ]
 
 DIRS += [
     'agnostic/gmp',
--- a/dom/media/wave/WaveDecoder.cpp
+++ b/dom/media/wave/WaveDecoder.cpp
@@ -1,17 +1,60 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WaveDemuxer.h"
+#include "mozilla/Preferences.h"
 #include "MediaDecoderStateMachine.h"
 #include "WaveReader.h"
 #include "WaveDecoder.h"
+#include "MediaFormatReader.h"
+#include "PDMFactory.h"
 
 namespace mozilla {
 
-MediaDecoderStateMachine* WaveDecoder::CreateStateMachine()
+MediaDecoder*
+WaveDecoder::Clone(MediaDecoderOwner* aOwner)
+{
+  if (!IsEnabled())
+    return nullptr;
+
+  return new WaveDecoder(aOwner);
+}
+
+MediaDecoderStateMachine*
+WaveDecoder::CreateStateMachine()
 {
-  return new MediaDecoderStateMachine(this, new WaveReader(this));
+  if (Preferences::GetBool("media.wave.decoder.enabled")) {
+    RefPtr<MediaDecoderReader> reader =
+        new MediaFormatReader(this, new WAVDemuxer(GetResource()));
+    return new MediaDecoderStateMachine(this, reader);
+  } else {
+    return new MediaDecoderStateMachine(this, new WaveReader(this));
+  }
+}
+
+/* static */
+bool
+WaveDecoder::IsEnabled()
+{
+  PDMFactory::Init();
+  RefPtr<PDMFactory> platform = new PDMFactory();
+  return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/x-wav"));
+}
+
+/* static */
+bool
+WaveDecoder::CanHandleMediaType(const nsACString& aType,
+                               const nsAString& aCodecs)
+{
+  if (aType.EqualsASCII("audio/wave") || aType.EqualsASCII("audio/x-wav") ||
+      aType.EqualsASCII("audio/wav")  || aType.EqualsASCII("audio/x-pn-wav")) {
+    return IsEnabled() && (aCodecs.IsEmpty() || aCodecs.EqualsASCII("1"));
+  }
+
+  return false;
 }
 
 } // namespace mozilla
--- a/dom/media/wave/WaveDecoder.h
+++ b/dom/media/wave/WaveDecoder.h
@@ -3,37 +3,27 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #if !defined(WaveDecoder_h_)
 #define WaveDecoder_h_
 
 #include "MediaDecoder.h"
 
-/**
- * The decoder implementation is currently limited to Linear PCM encoded
- * audio data with one or two channels of 8- or 16-bit samples at sample
- * rates from 100 Hz to 96 kHz.  The number of channels is limited by what
- * the audio backend (via AudioStream) currently supports.  The supported
- * sample rate is artificially limited to arbitrarily selected sane values.
- * Support for additional channels (and other new features) would
- * require extending WaveDecoder to support parsing the newer
- * WAVE_FORMAT_EXTENSIBLE chunk format.
-**/
-
 namespace mozilla {
 
-class WaveDecoder : public MediaDecoder
-{
+class WaveDecoder : public MediaDecoder {
 public:
+  // MediaDecoder interface.
   explicit WaveDecoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {}
-  MediaDecoder* Clone(MediaDecoderOwner* aOwner) override {
-    if (!IsWaveEnabled()) {
-      return nullptr;
-    }
-    return new WaveDecoder(aOwner);
-  }
+  MediaDecoder* Clone(MediaDecoderOwner* aOwner) override;
   MediaDecoderStateMachine* CreateStateMachine() override;
+
+  // Returns true if the WAV backend is pref'ed on, and we're running on a
+  // platform that is likely to have decoders for the format.
+  static bool IsEnabled();
+  static bool CanHandleMediaType(const nsACString& aType,
+                                 const nsAString& aCodecs);
 };
 
 } // namespace mozilla
 
 #endif
new file mode 100644
--- /dev/null
+++ b/dom/media/wave/WaveDemuxer.cpp
@@ -0,0 +1,889 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WaveDemuxer.h"
+
+#include <inttypes.h>
+#include <algorithm>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Endian.h"
+#include "VideoUtils.h"
+#include "TimeUnits.h"
+#include "prenv.h"
+
+using mozilla::media::TimeUnit;
+using mozilla::media::TimeIntervals;
+using mp4_demuxer::ByteReader;
+
+namespace mozilla {
+
+// WAVDemuxer
+
+WAVDemuxer::WAVDemuxer(MediaResource* aSource)
+  : mSource(aSource)
+{
+}
+
+bool
+WAVDemuxer::InitInternal()
+{
+  if (!mTrackDemuxer) {
+    mTrackDemuxer = new WAVTrackDemuxer(mSource);
+  }
+  return mTrackDemuxer->Init();
+}
+
+RefPtr<WAVDemuxer::InitPromise>
+WAVDemuxer::Init()
+{
+  if (!InitInternal()) {
+    return InitPromise::CreateAndReject(
+      DemuxerFailureReason::DEMUXER_ERROR, __func__);
+  }
+  return InitPromise::CreateAndResolve(NS_OK, __func__);
+}
+
+bool
+WAVDemuxer::HasTrackType(TrackInfo::TrackType aType) const
+{
+  return aType == TrackInfo::kAudioTrack;
+}
+
+uint32_t
+WAVDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const
+{
+  return aType == TrackInfo::kAudioTrack ? 1u : 0u;
+}
+
+already_AddRefed<MediaTrackDemuxer>
+WAVDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
+{
+  if (!mTrackDemuxer) {
+    return nullptr;
+  }
+  return RefPtr<WAVTrackDemuxer>(mTrackDemuxer).forget();
+}
+
+bool
+WAVDemuxer::IsSeekable() const
+{
+  return true;
+}
+
+// WAVTrackDemuxer
+
+WAVTrackDemuxer::WAVTrackDemuxer(MediaResourceIndex aSource)
+  : mSource(aSource)
+  , mOffset(0)
+  , mFirstChunkOffset(0)
+  , mNumParsedChunks(0)
+  , mChunkIndex(0)
+  , mTotalChunkLen(0)
+  , mSamplesPerChunk(0)
+  , mSamplesPerSecond(0)
+  , mChannels(0)
+{
+  Reset();
+}
+
+bool
+WAVTrackDemuxer::Init()
+{
+  Reset();
+  FastSeek(TimeUnit());
+
+  if (!mInfo) {
+    mInfo = MakeUnique<AudioInfo>();
+  }
+
+  if (!RIFFParserInit()) {
+    return false;
+  }
+
+  while (true) {
+    if (!HeaderParserInit()) {
+      return false;
+    }
+
+    uint32_t aChunkName = mHeaderParser.GiveHeader().ChunkName();
+    uint32_t aChunkSize = mHeaderParser.GiveHeader().ChunkSize();
+
+    aChunkSize += aChunkSize % 2;
+
+    if (aChunkName == FRMT_CODE) {
+      if (!FmtChunkParserInit()) {
+        return false;
+      }
+    } else if (aChunkName == LIST_CODE) {
+      mHeaderParser.Reset();
+      uint32_t endOfListChunk = mOffset + aChunkSize;
+      if (!ListChunkParserInit(aChunkSize)) {
+        mOffset = endOfListChunk;
+      }
+    } else if (aChunkName == DATA_CODE) {
+      mDataLength = aChunkSize;
+      if (mFirstChunkOffset != mOffset) {
+        mFirstChunkOffset = mOffset;
+      }
+      break;
+    } else {
+      mOffset += aChunkSize; // Skip other irrelevant chunks.
+    }
+    mHeaderParser.Reset();
+  }
+
+  if (mDataLength > StreamLength() - mFirstChunkOffset) {
+    mDataLength = StreamLength() - mFirstChunkOffset;
+  }
+
+  mSamplesPerSecond = mFmtParser.FmtChunk().SampleRate();
+  mChannels = mFmtParser.FmtChunk().Channels();
+  mSampleFormat = mFmtParser.FmtChunk().SampleFormat();
+  mSamplesPerChunk = DATA_CHUNK_SIZE * 8 / mChannels / mSampleFormat;
+
+  mInfo->mRate = mSamplesPerSecond;
+  mInfo->mChannels = mChannels;
+  mInfo->mBitDepth = mSampleFormat;
+  mInfo->mProfile = mFmtParser.FmtChunk().WaveFormat() & 0x00FF;
+  mInfo->mExtendedProfile = (mFmtParser.FmtChunk().WaveFormat() & 0xFF00) >> 8;
+  mInfo->mMimeType = "audio/wave; codecs=";
+  mInfo->mMimeType.AppendInt(mFmtParser.FmtChunk().WaveFormat());
+  mInfo->mDuration = Duration().ToMicroseconds();
+
+  return !!(mInfo->mDuration);
+}
+
+bool
+WAVTrackDemuxer::RIFFParserInit()
+{
+  RefPtr<MediaRawData> riffHeader = GetFileHeader(FindRIFFHeader());
+  if (!riffHeader) {
+    return false;
+  }
+  ByteReader RIFFReader = ByteReader(riffHeader->Data(), 12);
+  mRIFFParser.Parse(RIFFReader);
+  return mRIFFParser.RiffHeader().IsValid(11);
+}
+
+bool
+WAVTrackDemuxer::HeaderParserInit()
+{
+  RefPtr<MediaRawData> header = GetFileHeader(FindChunkHeader());
+  if (!header) {
+    return false;
+  }
+  ByteReader HeaderReader = ByteReader(header->Data(), 8);
+  mHeaderParser.Parse(HeaderReader);
+  return true;
+}
+
+bool
+WAVTrackDemuxer::FmtChunkParserInit()
+{
+  RefPtr<MediaRawData> fmtChunk = GetFileHeader(FindFmtChunk());
+  if (!fmtChunk) {
+    return false;
+  }
+  ByteReader fmtReader = ByteReader(fmtChunk->Data(),
+                                    mHeaderParser.GiveHeader().ChunkSize());
+  mFmtParser.Parse(fmtReader);
+  return true;
+}
+
+bool
+WAVTrackDemuxer::ListChunkParserInit(uint32_t aChunkSize)
+{
+  uint32_t bytesRead = 0;
+
+  RefPtr<MediaRawData> infoTag = GetFileHeader(FindInfoTag());
+  if (!infoTag) {
+    return false;
+  }
+  ByteReader infoTagReader = ByteReader(infoTag->Data(), 4);
+  if (!infoTagReader.CanRead32() || infoTagReader.ReadU32() != INFO_CODE) {
+    return false;
+  }
+
+  bytesRead += 4;
+
+  while (bytesRead < aChunkSize) {
+    if (!HeaderParserInit()) {
+      return false;
+    }
+
+    bytesRead += 8;
+
+    uint32_t id = mHeaderParser.GiveHeader().ChunkName();
+    uint32_t length = mHeaderParser.GiveHeader().ChunkSize();
+
+    // SubChunk Length Cannot Exceed List Chunk length.
+    if (length > aChunkSize - bytesRead) {
+      length = aChunkSize - bytesRead;
+    }
+
+    MediaByteRange mRange = { mOffset, mOffset + length };
+    RefPtr<MediaRawData> mChunkData = GetFileHeader(mRange);
+    if (!mChunkData) {
+      return false;
+    }
+
+    const char* mRawData = reinterpret_cast<const char*>(mChunkData->Data());
+
+    nsCString val(mRawData, length);
+    if (length > 0 && val[length - 1] == '\0') {
+      val.SetLength(length - 1);
+    }
+
+    if (length % 2) {
+      mOffset += 1;
+      length += length % 2;
+    }
+
+    bytesRead += length;
+
+    if (!IsUTF8(val)) {
+      mHeaderParser.Reset();
+      continue;
+    }
+
+    switch (id) {
+      case 0x49415254:                // IART
+        mInfo->mTags.AppendElement(MetadataTag(NS_LITERAL_CSTRING("artist"), val));
+        break;
+      case 0x49434d54:                // ICMT
+        mInfo->mTags.AppendElement(MetadataTag(NS_LITERAL_CSTRING("comments"), val));
+        break;
+      case 0x49474e52:                // IGNR
+        mInfo->mTags.AppendElement(MetadataTag(NS_LITERAL_CSTRING("genre"), val));
+        break;
+      case 0x494e414d:                // INAM
+        mInfo->mTags.AppendElement(MetadataTag(NS_LITERAL_CSTRING("name"), val));
+        break;
+    }
+
+    mHeaderParser.Reset();
+  }
+  return true;
+}
+
+media::TimeUnit
+WAVTrackDemuxer::SeekPosition() const
+{
+  TimeUnit pos = Duration(mChunkIndex);
+  if (Duration() > TimeUnit()) {
+    pos = std::min(Duration(), pos);
+  }
+  return pos;
+}
+
+RefPtr<MediaRawData>
+WAVTrackDemuxer::DemuxSample()
+{
+  return GetNextChunk(FindNextChunk());
+}
+
+UniquePtr<TrackInfo>
+WAVTrackDemuxer::GetInfo() const
+{
+  return mInfo->Clone();
+}
+
+RefPtr<WAVTrackDemuxer::SeekPromise>
+WAVTrackDemuxer::Seek(TimeUnit aTime)
+{
+  FastSeek(aTime);
+  const TimeUnit seekTime = ScanUntil(aTime);
+  return SeekPromise::CreateAndResolve(seekTime, __func__);
+}
+
+TimeUnit
+WAVTrackDemuxer::FastSeek(const TimeUnit& aTime)
+{
+  if (aTime.ToMicroseconds()) {
+    mChunkIndex = ChunkIndexFromTime(aTime);
+  } else {
+    mChunkIndex = 0;
+  }
+
+  mOffset = OffsetFromChunkIndex(mChunkIndex);
+
+  if (mOffset > mFirstChunkOffset && StreamLength() > 0) {
+    mOffset = std::min(StreamLength() - 1, mOffset);
+  }
+
+  return Duration(mChunkIndex);
+}
+
+TimeUnit
+WAVTrackDemuxer::ScanUntil(const TimeUnit& aTime)
+{
+  if (!aTime.ToMicroseconds()) {
+    return FastSeek(aTime);
+  }
+
+  if (Duration(mChunkIndex) > aTime) {
+    FastSeek(aTime);
+  }
+
+  return SeekPosition();
+}
+
+RefPtr<WAVTrackDemuxer::SamplesPromise>
+WAVTrackDemuxer::GetSamples(int32_t aNumSamples)
+{
+  if (!aNumSamples) {
+    return SamplesPromise::CreateAndReject(
+        DemuxerFailureReason::DEMUXER_ERROR, __func__);
+  }
+
+  RefPtr<SamplesHolder> datachunks = new SamplesHolder();
+
+  while (aNumSamples--) {
+    RefPtr<MediaRawData> datachunk = GetNextChunk(FindNextChunk());
+    if (!datachunk) {
+      break;
+    }
+    datachunks->mSamples.AppendElement(datachunk);
+  }
+
+  if (datachunks->mSamples.IsEmpty()) {
+    return SamplesPromise::CreateAndReject(
+        DemuxerFailureReason::END_OF_STREAM, __func__);
+  }
+
+  return SamplesPromise::CreateAndResolve(datachunks, __func__);
+}
+
+void
+WAVTrackDemuxer::Reset()
+{
+  FastSeek(TimeUnit());
+  mParser.Reset();
+  mHeaderParser.Reset();
+  mRIFFParser.Reset();
+  mFmtParser.Reset();
+}
+
+RefPtr<WAVTrackDemuxer::SkipAccessPointPromise>
+WAVTrackDemuxer::SkipToNextRandomAccessPoint(TimeUnit aTimeThreshold)
+{
+  return SkipAccessPointPromise::CreateAndReject(
+    SkipFailureHolder(DemuxerFailureReason::DEMUXER_ERROR, 0), __func__);
+}
+
+int64_t
+WAVTrackDemuxer::GetResourceOffset() const
+{
+  return mOffset;
+}
+
+TimeIntervals
+WAVTrackDemuxer::GetBuffered()
+{
+  TimeUnit duration = Duration();
+
+  if (duration <= TimeUnit()) {
+    return TimeIntervals();
+  }
+
+  AutoPinned<MediaResource> stream(mSource.GetResource());
+  return GetEstimatedBufferedTimeRanges(stream, duration.ToMicroseconds());
+}
+
+int64_t
+WAVTrackDemuxer::StreamLength() const
+{
+  return mSource.GetLength();
+}
+
+TimeUnit
+WAVTrackDemuxer::Duration() const
+{
+  if (!mDataLength) {
+    return TimeUnit();
+  }
+
+  int64_t numSamples = mDataLength * 8 / mChannels / mSampleFormat;
+
+  int64_t numUSeconds = USECS_PER_S * numSamples / mSamplesPerSecond;
+
+  if (USECS_PER_S * numSamples % mSamplesPerSecond > mSamplesPerSecond / 2) {
+    numUSeconds++;
+  }
+
+  return TimeUnit::FromMicroseconds(numUSeconds);
+}
+
+TimeUnit
+WAVTrackDemuxer::Duration(int64_t aNumDataChunks) const
+{
+  if (!mSamplesPerSecond) {
+    return TimeUnit();
+  }
+  const double usPerDataChunk = USECS_PER_S * mSamplesPerChunk /
+                                mSamplesPerSecond;
+  return TimeUnit::FromMicroseconds(aNumDataChunks * usPerDataChunk);
+}
+
+TimeUnit
+WAVTrackDemuxer::DurationFromBytes(uint32_t aNumBytes) const
+{
+  if (!mSamplesPerSecond) {
+    return TimeUnit();
+  }
+
+  int64_t numSamples = aNumBytes * 8 / mChannels / mSampleFormat;
+
+  int64_t numUSeconds = USECS_PER_S * numSamples / mSamplesPerSecond;
+
+  if (USECS_PER_S * numSamples % mSamplesPerSecond > mSamplesPerSecond / 2) {
+    numUSeconds++;
+  }
+
+  return TimeUnit::FromMicroseconds(numUSeconds);
+}
+
+MediaByteRange
+WAVTrackDemuxer::FindNextChunk()
+{
+  if (mOffset + DATA_CHUNK_SIZE < mFirstChunkOffset + mDataLength) {
+    return { mOffset, mOffset + DATA_CHUNK_SIZE };
+  } else {
+    return { mOffset, mFirstChunkOffset + mDataLength };
+  }
+}
+
+MediaByteRange
+WAVTrackDemuxer::FindChunkHeader()
+{
+  return { mOffset, mOffset + CHUNK_HEAD_SIZE };
+}
+
+MediaByteRange
+WAVTrackDemuxer::FindRIFFHeader()
+{
+  return { mOffset, mOffset + RIFF_CHUNK_SIZE };
+}
+
+MediaByteRange
+WAVTrackDemuxer::FindFmtChunk()
+{
+  return { mOffset, mOffset + mHeaderParser.GiveHeader().ChunkSize() };
+}
+
+MediaByteRange
+WAVTrackDemuxer::FindListChunk()
+{
+  return { mOffset, mOffset + mHeaderParser.GiveHeader().ChunkSize() };
+}
+
+MediaByteRange
+WAVTrackDemuxer::FindInfoTag()
+{
+  return { mOffset, mOffset + 4 };
+}
+
+bool
+WAVTrackDemuxer::SkipNextChunk(const MediaByteRange& aRange)
+{
+  UpdateState(aRange);
+  return true;
+}
+
+already_AddRefed<MediaRawData>
+WAVTrackDemuxer::GetNextChunk(const MediaByteRange& aRange)
+{
+  if (!aRange.Length()) {
+    return nullptr;
+  }
+
+  RefPtr<MediaRawData> datachunk = new MediaRawData();
+  datachunk->mOffset = aRange.mStart;
+
+  nsAutoPtr<MediaRawDataWriter> chunkWriter(datachunk->CreateWriter());
+  if (!chunkWriter->SetSize(aRange.Length())) {
+    return nullptr;
+  }
+
+  const uint32_t read = Read(chunkWriter->Data(),
+                             datachunk->mOffset,
+                             datachunk->Size());
+
+  if (read != aRange.Length()) {
+    return nullptr;
+  }
+
+  UpdateState(aRange);
+  ++mNumParsedChunks;
+  ++mChunkIndex;
+
+  datachunk->mTime = Duration(mChunkIndex - 1).ToMicroseconds();
+
+  if (static_cast<uint32_t>(mChunkIndex) * DATA_CHUNK_SIZE < mDataLength) {
+    datachunk->mDuration = Duration(1).ToMicroseconds();
+  } else {
+    uint32_t mBytesRemaining =
+      mDataLength - mChunkIndex * DATA_CHUNK_SIZE;
+    datachunk->mDuration = DurationFromBytes(mBytesRemaining).ToMicroseconds();
+  }
+  datachunk->mTimecode = datachunk->mTime;
+  datachunk->mKeyframe = true;
+
+  MOZ_ASSERT(datachunk->mTime >= 0);
+  MOZ_ASSERT(datachunk->mDuration >= 0);
+
+  return datachunk.forget();
+}
+
+already_AddRefed<MediaRawData>
+WAVTrackDemuxer::GetFileHeader(const MediaByteRange& aRange)
+{
+  if (!aRange.Length()) {
+    return nullptr;
+  }
+
+  RefPtr<MediaRawData> fileHeader = new MediaRawData();
+  fileHeader->mOffset = aRange.mStart;
+
+  nsAutoPtr<MediaRawDataWriter> headerWriter(fileHeader->CreateWriter());
+  if (!headerWriter->SetSize(aRange.Length())) {
+    return nullptr;
+  }
+
+  const uint32_t read = Read(headerWriter->Data(),
+                             fileHeader->mOffset,
+                             fileHeader->Size());
+
+  if (read != aRange.Length()) {
+    return nullptr;
+  }
+
+  UpdateState(aRange);
+
+  return fileHeader.forget();
+}
+
+int64_t
+WAVTrackDemuxer::OffsetFromChunkIndex(int64_t aChunkIndex) const
+{
+  return mFirstChunkOffset + aChunkIndex * DATA_CHUNK_SIZE;
+}
+
+int64_t
+WAVTrackDemuxer::ChunkIndexFromOffset(int64_t aOffset) const
+{
+  int64_t chunkIndex = (aOffset - mFirstChunkOffset) / DATA_CHUNK_SIZE;
+  return std::max<int64_t>(0, chunkIndex);
+}
+
+int64_t
+WAVTrackDemuxer::ChunkIndexFromTime(const media::TimeUnit& aTime) const
+{
+  int64_t chunkIndex =
+    (aTime.ToSeconds() * mSamplesPerSecond / mSamplesPerChunk) - 1;
+  return chunkIndex;
+}
+
+void
+WAVTrackDemuxer::UpdateState(const MediaByteRange& aRange)
+{
+  // Full chunk parsed, move offset to its end.
+  mOffset = aRange.mEnd;
+  mTotalChunkLen += aRange.Length();
+}
+
+int32_t
+WAVTrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize)
+{
+  const int64_t streamLen = StreamLength();
+  if (mInfo && streamLen > 0) {
+    aSize = std::min<int64_t>(aSize, streamLen - aOffset);
+  }
+  uint32_t read = 0;
+  const nsresult rv = mSource.ReadAt(aOffset,
+                                     reinterpret_cast<char*>(aBuffer),
+                                     static_cast<uint32_t>(aSize),
+                                     &read);
+  NS_ENSURE_SUCCESS(rv, 0);
+  return static_cast<int32_t>(read);
+}
+
+// RIFFParser
+
+uint32_t
+RIFFParser::Parse(ByteReader& aReader)
+{
+  MOZ_ASSERT(&aReader);
+
+  while (aReader.CanRead8() && !mRiffHeader.ParseNext(aReader.ReadU8())) { }
+
+  if (mRiffHeader.IsValid()) {
+    return RIFF_CHUNK_SIZE;
+  }
+
+  return 0;
+}
+
+void
+RIFFParser::Reset()
+{
+  mRiffHeader.Reset();
+}
+
+const RIFFParser::RIFFHeader&
+RIFFParser::RiffHeader() const
+{
+  return mRiffHeader;
+}
+
+// RIFFParser::RIFFHeader
+
+RIFFParser::RIFFHeader::RIFFHeader()
+{
+  Reset();
+}
+
+void
+RIFFParser::RIFFHeader::Reset()
+{
+  mPos = 0;
+}
+
+bool
+RIFFParser::RIFFHeader::ParseNext(uint8_t c)
+{
+  if (!Update(c)) {
+    Reset();
+    if (!Update(c)) {
+      Reset();
+    }
+  }
+  return IsValid();
+}
+
+bool
+RIFFParser::RIFFHeader::IsValid(int aPos) const
+{
+  if (aPos > -1 && aPos < 4) {
+    return RIFF[aPos] == mRaw[aPos];
+  } else if (aPos > 7 && aPos < 12) {
+    return WAVE[aPos - 8] == mRaw[aPos];
+  } else {
+    return true;
+  }
+}
+
+bool
+RIFFParser::RIFFHeader::IsValid() const
+{
+  return mPos >= RIFF_CHUNK_SIZE;
+}
+
+bool
+RIFFParser::RIFFHeader::Update(uint8_t c)
+{
+  if (mPos < RIFF_CHUNK_SIZE) {
+    mRaw[mPos] = c;
+  }
+  return IsValid(mPos++);
+}
+
+// HeaderParser
+
+uint32_t
+HeaderParser::Parse(ByteReader& aReader)
+{
+  while (aReader.CanRead8() && !mHeader.ParseNext(aReader.ReadU8())) { }
+
+  if (mHeader.IsValid()) {
+    return CHUNK_HEAD_SIZE;
+  }
+
+  return 0;
+}
+
+void
+HeaderParser::Reset()
+{
+  mHeader.Reset();
+}
+
+const HeaderParser::ChunkHeader&
+HeaderParser::GiveHeader() const
+{
+  return mHeader;
+}
+
+// HeaderParser::ChunkHeader
+
+HeaderParser::ChunkHeader::ChunkHeader()
+{
+  Reset();
+}
+
+void
+HeaderParser::ChunkHeader::Reset()
+{
+  mPos = 0;
+}
+
+bool
+HeaderParser::ChunkHeader::ParseNext(uint8_t c)
+{
+  Update(c);
+  return IsValid();
+}
+
+bool
+HeaderParser::ChunkHeader::IsValid() const
+{
+  return mPos >= CHUNK_HEAD_SIZE;
+}
+
+uint32_t
+HeaderParser::ChunkHeader::ChunkName() const
+{
+  return ((mRaw[0] << 24) | (mRaw[1] << 16) |
+          (mRaw[2] << 8 ) | (mRaw[3]));
+}
+
+uint32_t
+HeaderParser::ChunkHeader::ChunkSize() const
+{
+  return ((mRaw[7] << 24) | (mRaw[6] << 16) |
+          (mRaw[5] << 8 ) | (mRaw[4]));
+}
+
+void
+HeaderParser::ChunkHeader::Update(uint8_t c)
+{
+  if (mPos < CHUNK_HEAD_SIZE) {
+    mRaw[mPos++] = c;
+  }
+}
+
+// FormatParser
+
+uint32_t
+FormatParser::Parse(ByteReader& aReader)
+{
+  MOZ_ASSERT(&aReader);
+
+  while (aReader.CanRead8() && !mFmtChunk.ParseNext(aReader.ReadU8())) { }
+
+  if (mFmtChunk.IsValid()) {
+    return FMT_CHUNK_MIN_SIZE;
+  }
+
+  return 0;
+}
+
+void
+FormatParser::Reset()
+{
+  mFmtChunk.Reset();
+}
+
+const FormatParser::FormatChunk&
+FormatParser::FmtChunk() const
+{
+  return mFmtChunk;
+}
+
+// FormatParser::FormatChunk
+
+FormatParser::FormatChunk::FormatChunk()
+{
+  Reset();
+}
+
+void
+FormatParser::FormatChunk::Reset()
+{
+  mPos = 0;
+}
+
+uint16_t
+FormatParser::FormatChunk::WaveFormat() const
+{
+  return (mRaw[1] << 8) | (mRaw[0]);
+}
+
+uint16_t
+FormatParser::FormatChunk::Channels() const
+{
+  return (mRaw[3] << 8) | (mRaw[2]);
+}
+
+uint32_t
+FormatParser::FormatChunk::SampleRate() const
+{
+  return (mRaw[7] << 24) | (mRaw[6] << 16) |
+         (mRaw[5] << 8 ) | (mRaw[4]);
+}
+
+uint16_t
+FormatParser::FormatChunk::FrameSize() const
+{
+  return (mRaw[13] << 8) | (mRaw[12]);
+}
+
+uint16_t
+FormatParser::FormatChunk::SampleFormat() const
+{
+  return (mRaw[15] << 8) | (mRaw[14]);
+}
+
+bool
+FormatParser::FormatChunk::ParseNext(uint8_t c)
+{
+  Update(c);
+  return IsValid();
+}
+
+bool
+FormatParser::FormatChunk::IsValid() const
+{
+  return (FrameSize() == SampleRate() * Channels() / 8) &&
+         (mPos >= FMT_CHUNK_MIN_SIZE);
+}
+
+void
+FormatParser::FormatChunk::Update(uint8_t c)
+{
+  if (mPos < FMT_CHUNK_MIN_SIZE) {
+    mRaw[mPos++] = c;
+  }
+}
+
+// DataParser
+
+DataParser::DataParser()
+{
+}
+
+void
+DataParser::Reset()
+{
+  mChunk.Reset();
+}
+
+const DataParser::DataChunk&
+DataParser::CurrentChunk() const
+{
+  return mChunk;
+}
+
+// DataParser::DataChunk
+
+void
+DataParser::DataChunk::Reset()
+{
+  mPos = 0;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/wave/WaveDemuxer.h
@@ -0,0 +1,262 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * Licence, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WAV_DEMUXER_H_
+#define WAV_DEMUXER_H_
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+#include "MediaDataDemuxer.h"
+#include "MediaResource.h"
+#include "mp4_demuxer/ByteReader.h"
+
+using mp4_demuxer::ByteReader;
+
+namespace mozilla {
+
+static const uint32_t FRMT_CODE = 0x666d7420;
+static const uint32_t DATA_CODE = 0x64617461;
+static const uint32_t LIST_CODE = 0x4c495354;
+static const uint32_t INFO_CODE = 0x494e464f;
+
+static const uint8_t RIFF[4] = {'R', 'I', 'F', 'F'};
+static const uint8_t WAVE[4] = {'W', 'A', 'V', 'E'};
+
+static const uint16_t RIFF_CHUNK_SIZE = 12;
+static const uint16_t CHUNK_HEAD_SIZE = 8;
+static const uint16_t FMT_CHUNK_MIN_SIZE = 16;
+static const uint16_t DATA_CHUNK_SIZE = 768;
+
+class WAVTrackDemuxer;
+
+class WAVDemuxer : public MediaDataDemuxer {
+public:
+  // MediaDataDemuxer interface.
+  explicit WAVDemuxer(MediaResource* aSource);
+  RefPtr<InitPromise> Init() override;
+  bool HasTrackType(TrackInfo::TrackType aType) const override;
+  uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
+  already_AddRefed<MediaTrackDemuxer> GetTrackDemuxer(
+      TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
+  bool IsSeekable() const override;
+
+private:
+  // Synchronous Initialization.
+  bool InitInternal();
+
+  MediaResourceIndex mSource;
+  RefPtr<WAVTrackDemuxer> mTrackDemuxer;
+};
+
+class RIFFParser {
+private:
+  class RIFFHeader;
+public:
+  const RIFFHeader& RiffHeader() const;
+
+  uint32_t Parse(ByteReader& aReader);
+
+  void Reset();
+
+private:
+  class RIFFHeader {
+  public:
+    RIFFHeader();
+    void Reset();
+
+    bool IsValid() const;
+    bool IsValid(int aPos) const;
+
+    bool ParseNext(uint8_t c);
+
+  private:
+    bool Update(uint8_t c);
+
+    uint8_t mRaw[RIFF_CHUNK_SIZE];
+
+    int mPos;
+  };
+
+  RIFFHeader mRiffHeader;
+};
+
+class HeaderParser {
+private:
+  class ChunkHeader;
+public:
+  const ChunkHeader& GiveHeader() const;
+
+  uint32_t Parse(ByteReader& aReader);
+
+  void Reset();
+
+private:
+  class ChunkHeader {
+  public:
+    ChunkHeader();
+    void Reset();
+
+    bool IsValid() const;
+
+    uint32_t ChunkName() const;
+    uint32_t ChunkSize() const;
+
+    bool ParseNext(uint8_t c);
+
+  private:
+    void Update(uint8_t c);
+
+    uint8_t mRaw[CHUNK_HEAD_SIZE];
+
+    int mPos;
+  };
+
+  ChunkHeader mHeader;
+};
+
+class FormatParser {
+private:
+  class FormatChunk;
+public:
+  const FormatChunk& FmtChunk() const;
+
+  uint32_t Parse(ByteReader& aReader);
+
+  void Reset();
+
+private:
+  class FormatChunk {
+  public:
+    FormatChunk();
+    void Reset();
+
+    uint16_t WaveFormat() const;
+    uint16_t Channels() const;
+    uint32_t SampleRate() const;
+    uint16_t FrameSize() const;
+    uint16_t SampleFormat() const;
+
+    bool IsValid() const;
+    bool ParseNext(uint8_t c);
+
+  private:
+    void Update(uint8_t c);
+
+    uint8_t mRaw[FMT_CHUNK_MIN_SIZE];
+
+    int mPos;
+  };
+
+  FormatChunk mFmtChunk;
+};
+
+class DataParser {
+private:
+  class DataChunk;
+public:
+  DataParser();
+
+  const DataChunk& CurrentChunk() const;
+
+  void Reset();
+
+private:
+  class DataChunk {
+  public:
+    void Reset();
+  private:
+    int mPos; // To Check Alignment
+  };
+
+  DataChunk mChunk;
+};
+
+class WAVTrackDemuxer : public MediaTrackDemuxer {
+public:
+  explicit WAVTrackDemuxer(MediaResourceIndex aSource);
+
+  bool Init();
+
+  int64_t StreamLength() const;
+
+  media::TimeUnit Duration() const;
+  media::TimeUnit Duration(int64_t aNumDataChunks) const;
+  media::TimeUnit DurationFromBytes(uint32_t aNumBytes) const;
+
+  media::TimeUnit SeekPosition() const;
+
+  RefPtr<MediaRawData> DemuxSample();
+
+  // MediaTrackDemuxer interface.
+  UniquePtr<TrackInfo> GetInfo() const override;
+  RefPtr<SeekPromise> Seek(media::TimeUnit aTime) override;
+  RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples) override;
+  void Reset() override;
+  RefPtr<SkipAccessPointPromise> SkipToNextRandomAccessPoint(
+    media::TimeUnit aTimeThreshold) override;
+  int64_t GetResourceOffset() const override;
+  media::TimeIntervals GetBuffered() override;
+
+private:
+  ~WAVTrackDemuxer() {}
+
+  media::TimeUnit FastSeek(const media::TimeUnit& aTime);
+  media::TimeUnit ScanUntil(const media::TimeUnit& aTime);
+
+  MediaByteRange FindNextChunk();
+
+  MediaByteRange FindChunkHeader();
+  MediaByteRange FindRIFFHeader();
+  MediaByteRange FindFmtChunk();
+  MediaByteRange FindListChunk();
+  MediaByteRange FindInfoTag();
+
+  bool RIFFParserInit();
+  bool HeaderParserInit();
+  bool FmtChunkParserInit();
+  bool ListChunkParserInit(uint32_t aChunkSize);
+
+  bool SkipNextChunk(const MediaByteRange& aRange);
+
+  already_AddRefed<MediaRawData> GetNextChunk(const MediaByteRange& aRange);
+  already_AddRefed<MediaRawData> GetFileHeader(const MediaByteRange& aRange);
+
+  void UpdateState(const MediaByteRange& aRange);
+
+  int64_t OffsetFromChunkIndex(int64_t aChunkIndex) const;
+  int64_t ChunkIndexFromOffset(int64_t aOffet) const;
+  int64_t ChunkIndexFromTime(const media::TimeUnit& aTime) const;
+
+  int32_t Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize);
+
+  MediaResourceIndex mSource;
+
+  DataParser mParser;
+  RIFFParser mRIFFParser;
+  HeaderParser mHeaderParser;
+
+  FormatParser mFmtParser;
+  // ListChunkParser mListChunkParser;
+
+  int64_t mOffset;
+  int64_t mFirstChunkOffset;
+
+  uint32_t mNumParsedChunks;
+  int32_t mChunkIndex;
+
+  uint32_t mDataLength;
+  uint64_t mTotalChunkLen;
+
+  int32_t mSamplesPerChunk;
+  int32_t mSamplesPerSecond;
+
+  int32_t mChannels;
+  int32_t mSampleFormat;
+
+  UniquePtr<AudioInfo> mInfo;
+};
+
+} // namespace mozilla
+
+#endif
--- a/dom/media/wave/moz.build
+++ b/dom/media/wave/moz.build
@@ -6,12 +6,13 @@
 
 EXPORTS += [
     'WaveDecoder.h',
     'WaveReader.h',
 ]
 
 UNIFIED_SOURCES += [
     'WaveDecoder.cpp',
+    'WaveDemuxer.cpp',
     'WaveReader.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -344,16 +344,17 @@ pref("media.gmp.decoder.enabled", false)
 pref("media.gmp.decoder.aac", 0);
 pref("media.gmp.decoder.h264", 0);
 #ifdef MOZ_RAW
 pref("media.raw.enabled", true);
 #endif
 pref("media.ogg.enabled", true);
 pref("media.opus.enabled", true);
 pref("media.wave.enabled", true);
+pref("media.wave.decoder.enabled", false);
 pref("media.webm.enabled", true);
 #if defined(MOZ_FMP4) && defined(MOZ_WMF)
 pref("media.webm.intel_decoder.enabled", false);
 #endif
 
 #ifdef MOZ_APPLEMEDIA
 #ifdef MOZ_WIDGET_UIKIT
 pref("media.mp3.enabled", true);