Bug 1500596 - pt3 - Add remote audio decoding for Vorbis. r=jya
authorMichael Froman <mfroman@mozilla.com>
Thu, 14 Feb 2019 19:08:21 +0000
changeset 459568 3699bea870c48d4d18bddb5ff40f2b192f83c479
parent 459567 c1648a2a4d081c351cbf05af0876ffdb22dc6acf
child 459569 382c65f3c2434dd096b9d917907fe6c83c5156fb
push id35563
push userccoroiu@mozilla.com
push dateSat, 16 Feb 2019 09:36:04 +0000
treeherdermozilla-central@1cfd69d05aa1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya
bugs1500596
milestone67.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 1500596 - pt3 - Add remote audio decoding for Vorbis. r=jya - Use a single remote decoder IPDL spec and make a remote decoding base class. Renames PRemoteVideoDecoder.ipdl to PRemoteDecoder.ipdl Renames RemoteVideoDecoder{Child|Parent}.{cpp|h} to RemoteDecoder{Child|Parent}.{cpp|h} - Move remote video decoding to new subclasses. Creates RemoteVideoDecoder.{cpp|h} that contains both the parent and child sides of the RemoteVideoDecoder{Child|Parent} classes. - Create new remote audio decoder Creates RemoteAudioDecoder.{cpp|h} that contains both the parent and child sides of the RemoteAudioDecoder{Child|Parent} classes. - Connect all the plumbing to use the new remote audio decoder to decode Vorbis in RDD including a new pref to control whether Vorbis is decoding in the AgnosticDecoderModule or the RemoteDecoderModule/RDD (media.rdd-vorbis.enabled). Depends on D18640 Differential Revision: https://phabricator.services.mozilla.com/D18641
dom/media/ipc/MediaIPCUtils.h
dom/media/ipc/PRemoteDecoder.ipdl
dom/media/ipc/PRemoteDecoderManager.ipdl
dom/media/ipc/PRemoteVideoDecoder.ipdl
dom/media/ipc/RemoteAudioDecoder.cpp
dom/media/ipc/RemoteAudioDecoder.h
dom/media/ipc/RemoteDecoderChild.cpp
dom/media/ipc/RemoteDecoderChild.h
dom/media/ipc/RemoteDecoderManagerChild.cpp
dom/media/ipc/RemoteDecoderManagerChild.h
dom/media/ipc/RemoteDecoderManagerParent.cpp
dom/media/ipc/RemoteDecoderManagerParent.h
dom/media/ipc/RemoteDecoderModule.cpp
dom/media/ipc/RemoteDecoderModule.h
dom/media/ipc/RemoteDecoderParent.cpp
dom/media/ipc/RemoteDecoderParent.h
dom/media/ipc/RemoteVideoDecoder.cpp
dom/media/ipc/RemoteVideoDecoder.h
dom/media/ipc/RemoteVideoDecoderChild.cpp
dom/media/ipc/RemoteVideoDecoderChild.h
dom/media/ipc/RemoteVideoDecoderParent.cpp
dom/media/ipc/RemoteVideoDecoderParent.h
dom/media/ipc/moz.build
dom/media/platforms/agnostic/AgnosticDecoderModule.cpp
modules/libpref/init/StaticPrefList.h
--- a/dom/media/ipc/MediaIPCUtils.h
+++ b/dom/media/ipc/MediaIPCUtils.h
@@ -39,41 +39,56 @@ struct ParamTraits<mozilla::VideoInfo> {
       aResult->SetImageRect(imageRect);
       return true;
     }
     return false;
   }
 };
 
 template <>
+struct ParamTraits<mozilla::TrackInfo::TrackType>
+    : public ContiguousEnumSerializerInclusive<
+          mozilla::TrackInfo::TrackType,
+          mozilla::TrackInfo::TrackType::kUndefinedTrack,
+          mozilla::TrackInfo::TrackType::kTextTrack> {};
+
+template <>
+struct ParamTraits<mozilla::MediaByteBuffer>
+    : public ParamTraits<nsTArray<uint8_t>> {
+  typedef mozilla::MediaByteBuffer paramType;
+};
+
+template <>
 struct ParamTraits<mozilla::AudioInfo> {
   typedef mozilla::AudioInfo paramType;
 
   static void Write(Message* aMsg, const paramType& aParam) {
     // TrackInfo
     WriteParam(aMsg, aParam.mMimeType);
 
     // AudioInfo
     WriteParam(aMsg, aParam.mRate);
     WriteParam(aMsg, aParam.mChannels);
     WriteParam(aMsg, aParam.mChannelMap);
     WriteParam(aMsg, aParam.mBitDepth);
     WriteParam(aMsg, aParam.mProfile);
     WriteParam(aMsg, aParam.mExtendedProfile);
+    WriteParam(aMsg, *aParam.mCodecSpecificConfig);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter,
                    paramType* aResult) {
     if (ReadParam(aMsg, aIter, &aResult->mMimeType) &&
         ReadParam(aMsg, aIter, &aResult->mRate) &&
         ReadParam(aMsg, aIter, &aResult->mChannels) &&
         ReadParam(aMsg, aIter, &aResult->mChannelMap) &&
         ReadParam(aMsg, aIter, &aResult->mBitDepth) &&
         ReadParam(aMsg, aIter, &aResult->mProfile) &&
-        ReadParam(aMsg, aIter, &aResult->mExtendedProfile)) {
+        ReadParam(aMsg, aIter, &aResult->mExtendedProfile) &&
+        ReadParam(aMsg, aIter, aResult->mCodecSpecificConfig.get())) {
       return true;
     }
     return false;
   }
 };
 
 template <>
 struct ParamTraits<mozilla::MediaDataDecoder::ConversionRequired>
rename from dom/media/ipc/PRemoteVideoDecoder.ipdl
rename to dom/media/ipc/PRemoteDecoder.ipdl
--- a/dom/media/ipc/PRemoteVideoDecoder.ipdl
+++ b/dom/media/ipc/PRemoteDecoder.ipdl
@@ -4,57 +4,72 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include "mozilla/dom/MediaIPCUtils.h";
 
 include protocol PRemoteDecoderManager;
 include PMediaDecoderParams;
 include LayersSurfaces;
 using mozilla::MediaDataDecoder::ConversionRequired from "PlatformDecoderModule.h";
+using mozilla::TrackInfo::TrackType from "MediaInfo.h";
 
 namespace mozilla {
 
 struct RemoteVideoDataIPDL
 {
   MediaDataIPDL base;
   IntSize display;
   IntSize frameSize;
   SurfaceDescriptorBuffer sdBuffer;
   int32_t frameID;
 };
 
+struct RemoteAudioDataIPDL
+{
+  MediaDataIPDL base;
+  uint32_t channels;
+  uint32_t rate;
+  uint32_t channelMap;
+  Shmem buffer;
+};
+
+union DecodedOutputIPDL
+{
+  RemoteAudioDataIPDL;
+  RemoteVideoDataIPDL;
+};
+
 // This protocol provides a way to use MediaDataDecoder across processes.
 // The parent side currently is only implemented to work with
 // RemoteDecoderModule.
 // The child side runs in the content process, and the parent side runs
 // in the RDD process. We run a separate IPDL thread for both sides.
-async protocol PRemoteVideoDecoder
+async protocol PRemoteDecoder
 {
   manager PRemoteDecoderManager;
 parent:
   async Init();
 
   async Input(MediaRawDataIPDL data);
 
   async Flush();
   async Drain();
   async Shutdown();
   // To clear the threshold, call with INT64_MIN.
   async SetSeekThreshold(int64_t time);
 
   async __delete__();
 
 child:
-  async InitComplete(nsCString decoderDescription,
+  async InitComplete(TrackType trackType,
+                     nsCString decoderDescription,
                      ConversionRequired conversion);
   async InitFailed(nsresult reason);
 
   async FlushComplete();
 
-  // Each output includes a SurfaceDescriptorBuffer that represents the decoded
-  // frame.
-  async VideoOutput(RemoteVideoDataIPDL data);
+  async Output(DecodedOutputIPDL data);
   async InputExhausted();
   async DrainComplete();
   async Error(nsresult error);
 };
 
 } // namespace mozilla
--- a/dom/media/ipc/PRemoteDecoderManager.ipdl
+++ b/dom/media/ipc/PRemoteDecoderManager.ipdl
@@ -1,27 +1,38 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 protocol PRemoteVideoDecoder;
+include protocol PRemoteDecoder;
 include "mozilla/dom/MediaIPCUtils.h";
 
 using VideoInfo from "MediaInfo.h";
 using AudioInfo from "MediaInfo.h";
 using mozilla::CreateDecoderParams::OptionSet from "PlatformDecoderModule.h";
 
 namespace mozilla {
 
+struct VideoDecoderInfoIPDL
+{
+  VideoInfo videoInfo;
+  float framerate;
+};
+
+union RemoteDecoderInfoIPDL
+{
+  AudioInfo;
+  VideoDecoderInfoIPDL;
+};
+
 sync protocol PRemoteDecoderManager
 {
-  manages PRemoteVideoDecoder;
+  manages PRemoteDecoder;
 
 parent:
-  sync PRemoteVideoDecoder(VideoInfo info,
-                           float framerate,
-                           OptionSet options)
+  sync PRemoteDecoder(RemoteDecoderInfoIPDL info,
+                      OptionSet options)
          returns (bool success,
                   nsCString aErrorDescription);
 };
 
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/ipc/RemoteAudioDecoder.cpp
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "RemoteAudioDecoder.h"
+
+#include "RemoteDecoderManagerChild.h"
+#include "VorbisDecoder.h"
+
+namespace mozilla {
+
+RemoteAudioDecoderChild::RemoteAudioDecoderChild() : RemoteDecoderChild() {}
+
+mozilla::ipc::IPCResult RemoteAudioDecoderChild::RecvOutput(
+    const DecodedOutputIPDL& aDecodedData) {
+  AssertOnManagerThread();
+  MOZ_ASSERT(aDecodedData.type() == DecodedOutputIPDL::TRemoteAudioDataIPDL);
+  const RemoteAudioDataIPDL& aData = aDecodedData.get_RemoteAudioDataIPDL();
+
+  AlignedAudioBuffer alignedAudioBuffer;
+  alignedAudioBuffer.SetLength(aData.buffer().Size<uint8_t>() /
+                               sizeof(*alignedAudioBuffer.Data()));
+  memcpy(alignedAudioBuffer.Data(), aData.buffer().get<uint8_t>(),
+         aData.buffer().Size<uint8_t>());
+
+  DeallocShmem(aData.buffer());
+
+  RefPtr<AudioData> audio =
+      new AudioData(aData.base().offset(),
+                    media::TimeUnit::FromMicroseconds(aData.base().time()),
+                    media::TimeUnit::FromMicroseconds(aData.base().duration()),
+                    aData.base().frames(),
+                    std::move(alignedAudioBuffer),
+                    aData.channels(),
+                    aData.rate(),
+                    aData.channelMap());
+
+  mDecodedData.AppendElement(std::move(audio));
+  return IPC_OK();
+}
+
+MediaResult RemoteAudioDecoderChild::InitIPDL(
+    const AudioInfo& aAudioInfo,
+    const CreateDecoderParams::OptionSet& aOptions) {
+  RefPtr<RemoteDecoderManagerChild> manager =
+      RemoteDecoderManagerChild::GetSingleton();
+
+  // The manager isn't available because RemoteDecoderManagerChild has been
+  // initialized with null end points and we don't want to decode video on RDD
+  // process anymore. Return false here so that we can fallback to other PDMs.
+  if (!manager) {
+    return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+                       RESULT_DETAIL("RemoteDecoderManager is not available."));
+  }
+
+  if (!manager->CanSend()) {
+    return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+                       RESULT_DETAIL("RemoteDecoderManager unable to send."));
+  }
+
+  mIPDLSelfRef = this;
+  bool success = false;
+  nsCString errorDescription;
+  if (manager->SendPRemoteDecoderConstructor(this,
+                                             aAudioInfo,
+                                             aOptions,
+                                             &success,
+                                             &errorDescription)) {
+    mCanSend = true;
+  }
+
+  return success ? MediaResult(NS_OK)
+                 : MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, errorDescription);
+}
+
+RemoteAudioDecoderParent::RemoteAudioDecoderParent(
+    RemoteDecoderManagerParent* aParent,
+    const AudioInfo& aAudioInfo,
+    const CreateDecoderParams::OptionSet& aOptions,
+    TaskQueue* aManagerTaskQueue,
+    TaskQueue* aDecodeTaskQueue,
+    bool* aSuccess,
+    nsCString* aErrorDescription)
+    : RemoteDecoderParent(aParent, aManagerTaskQueue, aDecodeTaskQueue),
+      mAudioInfo(aAudioInfo) {
+  CreateDecoderParams params(mAudioInfo);
+  params.mTaskQueue = mDecodeTaskQueue;
+  params.mOptions = aOptions;
+  MediaResult error(NS_OK);
+  params.mError = &error;
+
+  if (VorbisDataDecoder::IsVorbis(params.mConfig.mMimeType)) {
+    mDecoder = new VorbisDataDecoder(params);
+  }
+
+  if (NS_FAILED(error)) {
+    MOZ_ASSERT(aErrorDescription);
+    *aErrorDescription = error.Description();
+  }
+
+  *aSuccess = !!mDecoder;
+}
+
+void RemoteAudioDecoderParent::ProcessDecodedData(
+    const MediaDataDecoder::DecodedData& aData) {
+  MOZ_ASSERT(OnManagerThread());
+
+  for (const auto& data : aData) {
+    MOZ_ASSERT(data->mType == MediaData::AUDIO_DATA,
+               "Can only decode audio using RemoteAudioDecoderParent!");
+    AudioData* audio = static_cast<AudioData*>(data.get());
+
+    MOZ_ASSERT(audio->mAudioData,
+               "Decoded audio must output an AlignedAudioBuffer "
+               "to be used with RemoteAudioDecoderParent");
+
+    Shmem buffer;
+    if (AllocShmem(audio->mAudioData.Size(), Shmem::SharedMemory::TYPE_BASIC,
+                   &buffer) &&
+        audio->mAudioData.Size() == buffer.Size<uint8_t>()) {
+      memcpy(buffer.get<uint8_t>(), audio->mAudioData.Data(),
+             audio->mAudioData.Size());
+    }
+
+    RemoteAudioDataIPDL output(
+        MediaDataIPDL(data->mOffset,
+                      data->mTime.ToMicroseconds(),
+                      data->mTimecode.ToMicroseconds(),
+                      data->mDuration.ToMicroseconds(),
+                      data->mFrames,
+                      data->mKeyframe),
+        audio->mChannels,
+        audio->mRate,
+        audio->mChannelMap,
+        buffer);
+
+    Unused << SendOutput(output);
+  }
+}
+
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/ipc/RemoteAudioDecoder.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+#ifndef include_dom_media_ipc_RemoteAudioDecoderChild_h
+#define include_dom_media_ipc_RemoteAudioDecoderChild_h
+#include "RemoteDecoderChild.h"
+#include "RemoteDecoderParent.h"
+
+namespace mozilla {
+
+using mozilla::ipc::IPCResult;
+
+class RemoteAudioDecoderChild final : public RemoteDecoderChild {
+ public:
+  explicit RemoteAudioDecoderChild();
+
+  MOZ_IS_CLASS_INIT
+  MediaResult InitIPDL(const AudioInfo& aAudioInfo,
+                       const CreateDecoderParams::OptionSet& aOptions);
+
+  IPCResult RecvOutput(const DecodedOutputIPDL& aDecodedData) override;
+};
+
+class RemoteAudioDecoderParent final : public RemoteDecoderParent {
+ public:
+  RemoteAudioDecoderParent(RemoteDecoderManagerParent* aParent,
+                           const AudioInfo& aAudioInfo,
+                           const CreateDecoderParams::OptionSet& aOptions,
+                           TaskQueue* aManagerTaskQueue,
+                           TaskQueue* aDecodeTaskQueue,
+                           bool* aSuccess,
+                           nsCString* aErrorDescription);
+
+ protected:
+  void ProcessDecodedData(const MediaDataDecoder::DecodedData& aData) override;
+
+ private:
+  // Can only be accessed from the manager thread
+  // Note: we can't keep a reference to the original AudioInfo here
+  // because unlike in typical MediaDataDecoder situations, we're being
+  // passed a deserialized AudioInfo from RecvPRemoteDecoderConstructor
+  // which is destroyed when RecvPRemoteDecoderConstructor returns.
+  const AudioInfo mAudioInfo;
+};
+
+}  // namespace mozilla
+
+#endif  // include_dom_media_ipc_RemoteAudioDecoderChild_h
rename from dom/media/ipc/RemoteVideoDecoderChild.cpp
rename to dom/media/ipc/RemoteDecoderChild.cpp
--- a/dom/media/ipc/RemoteVideoDecoderChild.cpp
+++ b/dom/media/ipc/RemoteDecoderChild.cpp
@@ -1,224 +1,103 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "RemoteVideoDecoderChild.h"
+#include "RemoteDecoderChild.h"
 
-#include "base/thread.h"
-#include "mozilla/layers/ImageDataSerializer.h"
-
-#include "ImageContainer.h"  // for PlanarYCbCrData and BufferRecycleBin
 #include "RemoteDecoderManagerChild.h"
 
 namespace mozilla {
 
-using base::Thread;
-using namespace layers;  // for PlanarYCbCrData and BufferRecycleBin
+RemoteDecoderChild::RemoteDecoderChild()
+    : mThread(RemoteDecoderManagerChild::GetManagerThread()) {}
 
-RemoteVideoDecoderChild::RemoteVideoDecoderChild()
-    : mThread(RemoteDecoderManagerChild::GetManagerThread()),
-      mCanSend(false),
-      mInitialized(false),
-      mIsHardwareAccelerated(false),
-      mConversion(MediaDataDecoder::ConversionRequired::kNeedNone),
-      mBufferRecycleBin(new BufferRecycleBin) {}
-
-RemoteVideoDecoderChild::~RemoteVideoDecoderChild() {
+RemoteDecoderChild::~RemoteDecoderChild() {
   AssertOnManagerThread();
   mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
 }
 
-RefPtr<mozilla::layers::Image> RemoteVideoDecoderChild::DeserializeImage(
-    const SurfaceDescriptorBuffer& aSdBuffer, const IntSize& aPicSize) {
-  MOZ_ASSERT(aSdBuffer.desc().type() == BufferDescriptor::TYCbCrDescriptor);
-  if (aSdBuffer.desc().type() != BufferDescriptor::TYCbCrDescriptor) {
-    return nullptr;
-  }
-  const YCbCrDescriptor& descriptor = aSdBuffer.desc().get_YCbCrDescriptor();
-
-  uint8_t* buffer = nullptr;
-  const MemoryOrShmem& memOrShmem = aSdBuffer.data();
-  switch (memOrShmem.type()) {
-    case MemoryOrShmem::Tuintptr_t:
-      buffer = reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t());
-      break;
-    case MemoryOrShmem::TShmem:
-      buffer = memOrShmem.get_Shmem().get<uint8_t>();
-      break;
-    default:
-      MOZ_ASSERT(false, "Unknown MemoryOrShmem type");
-  }
-  if (!buffer) {
-    return nullptr;
-  }
-
-  PlanarYCbCrData pData;
-  pData.mYSize = descriptor.ySize();
-  pData.mYStride = descriptor.yStride();
-  pData.mCbCrSize = descriptor.cbCrSize();
-  pData.mCbCrStride = descriptor.cbCrStride();
-  // default mYSkip, mCbSkip, mCrSkip because not held in YCbCrDescriptor
-  pData.mYSkip = pData.mCbSkip = pData.mCrSkip = 0;
-  // default mPicX, mPicY because not held in YCbCrDescriptor
-  pData.mPicX = pData.mPicY = 0;
-  pData.mPicSize = aPicSize;
-  pData.mStereoMode = descriptor.stereoMode();
-  pData.mColorDepth = descriptor.colorDepth();
-  pData.mYUVColorSpace = descriptor.yUVColorSpace();
-  pData.mYChannel = ImageDataSerializer::GetYChannel(buffer, descriptor);
-  pData.mCbChannel = ImageDataSerializer::GetCbChannel(buffer, descriptor);
-  pData.mCrChannel = ImageDataSerializer::GetCrChannel(buffer, descriptor);
-
-  // images coming from AOMDecoder are RecyclingPlanarYCbCrImages.
-  RefPtr<RecyclingPlanarYCbCrImage> image =
-      new RecyclingPlanarYCbCrImage(mBufferRecycleBin);
-  image->CopyData(pData);
-
-  switch (memOrShmem.type()) {
-    case MemoryOrShmem::Tuintptr_t:
-      delete[] reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t());
-      break;
-    case MemoryOrShmem::TShmem:
-      DeallocShmem(memOrShmem.get_Shmem());
-      break;
-    default:
-      MOZ_ASSERT(false, "Unknown MemoryOrShmem type");
-  }
-
-  return image;
-}
-
-mozilla::ipc::IPCResult RemoteVideoDecoderChild::RecvVideoOutput(
-    const RemoteVideoDataIPDL& aData) {
-  AssertOnManagerThread();
-
-  RefPtr<Image> image = DeserializeImage(aData.sdBuffer(), aData.frameSize());
-
-  RefPtr<VideoData> video = VideoData::CreateFromImage(
-      aData.display(), aData.base().offset(),
-      media::TimeUnit::FromMicroseconds(aData.base().time()),
-      media::TimeUnit::FromMicroseconds(aData.base().duration()), image,
-      aData.base().keyframe(),
-      media::TimeUnit::FromMicroseconds(aData.base().timecode()));
-
-  mDecodedData.AppendElement(std::move(video));
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult RemoteVideoDecoderChild::RecvInputExhausted() {
+mozilla::ipc::IPCResult RemoteDecoderChild::RecvInputExhausted() {
   AssertOnManagerThread();
   mDecodePromise.ResolveIfExists(std::move(mDecodedData), __func__);
   mDecodedData = MediaDataDecoder::DecodedData();
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult RemoteVideoDecoderChild::RecvDrainComplete() {
+mozilla::ipc::IPCResult RemoteDecoderChild::RecvDrainComplete() {
   AssertOnManagerThread();
   mDrainPromise.ResolveIfExists(std::move(mDecodedData), __func__);
   mDecodedData = MediaDataDecoder::DecodedData();
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult RemoteVideoDecoderChild::RecvError(
-    const nsresult& aError) {
+mozilla::ipc::IPCResult RemoteDecoderChild::RecvError(const nsresult& aError) {
   AssertOnManagerThread();
   mDecodedData = MediaDataDecoder::DecodedData();
   mDecodePromise.RejectIfExists(aError, __func__);
   mDrainPromise.RejectIfExists(aError, __func__);
   mFlushPromise.RejectIfExists(aError, __func__);
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult RemoteVideoDecoderChild::RecvInitComplete(
+mozilla::ipc::IPCResult RemoteDecoderChild::RecvInitComplete(
+    const TrackInfo::TrackType& trackType,
     const nsCString& aDecoderDescription,
     const ConversionRequired& aConversion) {
   AssertOnManagerThread();
-  mInitPromise.ResolveIfExists(TrackInfo::kVideoTrack, __func__);
+  mInitPromise.ResolveIfExists(trackType, __func__);
   mInitialized = true;
   mDescription = aDecoderDescription;
   mConversion = aConversion;
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult RemoteVideoDecoderChild::RecvInitFailed(
+mozilla::ipc::IPCResult RemoteDecoderChild::RecvInitFailed(
     const nsresult& aReason) {
   AssertOnManagerThread();
   mInitPromise.RejectIfExists(aReason, __func__);
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult RemoteVideoDecoderChild::RecvFlushComplete() {
+mozilla::ipc::IPCResult RemoteDecoderChild::RecvFlushComplete() {
   AssertOnManagerThread();
   mFlushPromise.ResolveIfExists(true, __func__);
   return IPC_OK();
 }
 
-void RemoteVideoDecoderChild::ActorDestroy(ActorDestroyReason aWhy) {
+void RemoteDecoderChild::ActorDestroy(ActorDestroyReason aWhy) {
   mCanSend = false;
 }
 
-MediaResult RemoteVideoDecoderChild::InitIPDL(
-    const VideoInfo& aVideoInfo, float aFramerate,
-    const CreateDecoderParams::OptionSet& aOptions) {
-  RefPtr<RemoteDecoderManagerChild> manager =
-      RemoteDecoderManagerChild::GetSingleton();
-
-  // The manager isn't available because RemoteDecoderManagerChild has been
-  // initialized with null end points and we don't want to decode video on RDD
-  // process anymore. Return false here so that we can fallback to other PDMs.
-  if (!manager) {
-    return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
-                       RESULT_DETAIL("RemoteDecoderManager is not available."));
-  }
-
-  if (!manager->CanSend()) {
-    return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
-                       RESULT_DETAIL("RemoteDecoderManager unable to send."));
-  }
-
-  mIPDLSelfRef = this;
-  bool success = false;
-  nsCString errorDescription;
-  if (manager->SendPRemoteVideoDecoderConstructor(this, aVideoInfo, aFramerate,
-                                                  aOptions, &success,
-                                                  &errorDescription)) {
-    mCanSend = true;
-  }
-
-  return success ? MediaResult(NS_OK)
-                 : MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, errorDescription);
-}
-
-void RemoteVideoDecoderChild::DestroyIPDL() {
+void RemoteDecoderChild::DestroyIPDL() {
   if (mCanSend) {
-    PRemoteVideoDecoderChild::Send__delete__(this);
+    PRemoteDecoderChild::Send__delete__(this);
   }
 }
 
-void RemoteVideoDecoderChild::IPDLActorDestroyed() { mIPDLSelfRef = nullptr; }
+void RemoteDecoderChild::IPDLActorDestroyed() { mIPDLSelfRef = nullptr; }
 
 // MediaDataDecoder methods
 
-RefPtr<MediaDataDecoder::InitPromise> RemoteVideoDecoderChild::Init() {
+RefPtr<MediaDataDecoder::InitPromise> RemoteDecoderChild::Init() {
   AssertOnManagerThread();
 
   if (!mIPDLSelfRef || !mCanSend) {
     return MediaDataDecoder::InitPromise::CreateAndReject(
         NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
   }
 
   SendInit();
 
   return mInitPromise.Ensure(__func__);
 }
 
-RefPtr<MediaDataDecoder::DecodePromise> RemoteVideoDecoderChild::Decode(
+RefPtr<MediaDataDecoder::DecodePromise> RemoteDecoderChild::Decode(
     MediaRawData* aSample) {
   AssertOnManagerThread();
 
   if (!mCanSend) {
     return MediaDataDecoder::DecodePromise::CreateAndReject(
         NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
   }
 
@@ -229,85 +108,88 @@ RefPtr<MediaDataDecoder::DecodePromise> 
   if (!AllocShmem(aSample->Size(), Shmem::SharedMemory::TYPE_BASIC, &buffer)) {
     return MediaDataDecoder::DecodePromise::CreateAndReject(
         NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
   }
 
   memcpy(buffer.get<uint8_t>(), aSample->Data(), aSample->Size());
 
   MediaRawDataIPDL sample(
-      MediaDataIPDL(aSample->mOffset, aSample->mTime.ToMicroseconds(),
+      MediaDataIPDL(aSample->mOffset,
+                    aSample->mTime.ToMicroseconds(),
                     aSample->mTimecode.ToMicroseconds(),
-                    aSample->mDuration.ToMicroseconds(), aSample->mFrames,
+                    aSample->mDuration.ToMicroseconds(),
+                    aSample->mFrames,
                     aSample->mKeyframe),
       buffer);
   SendInput(sample);
+
   return mDecodePromise.Ensure(__func__);
 }
 
-RefPtr<MediaDataDecoder::FlushPromise> RemoteVideoDecoderChild::Flush() {
+RefPtr<MediaDataDecoder::FlushPromise> RemoteDecoderChild::Flush() {
   AssertOnManagerThread();
   mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
   mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
   if (!mCanSend) {
     return MediaDataDecoder::FlushPromise::CreateAndReject(
         NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
   }
   SendFlush();
   return mFlushPromise.Ensure(__func__);
 }
 
-RefPtr<MediaDataDecoder::DecodePromise> RemoteVideoDecoderChild::Drain() {
+RefPtr<MediaDataDecoder::DecodePromise> RemoteDecoderChild::Drain() {
   AssertOnManagerThread();
   if (!mCanSend) {
     return MediaDataDecoder::DecodePromise::CreateAndReject(
         NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
   }
   SendDrain();
   return mDrainPromise.Ensure(__func__);
 }
 
-void RemoteVideoDecoderChild::Shutdown() {
+void RemoteDecoderChild::Shutdown() {
   AssertOnManagerThread();
   mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
   if (mCanSend) {
     SendShutdown();
   }
   mInitialized = false;
 }
 
-bool RemoteVideoDecoderChild::IsHardwareAccelerated(
+bool RemoteDecoderChild::IsHardwareAccelerated(
     nsACString& aFailureReason) const {
   AssertOnManagerThread();
   aFailureReason = mHardwareAcceleratedReason;
   return mIsHardwareAccelerated;
 }
 
-nsCString RemoteVideoDecoderChild::GetDescriptionName() const {
+nsCString RemoteDecoderChild::GetDescriptionName() const {
   AssertOnManagerThread();
   return mDescription;
 }
 
-void RemoteVideoDecoderChild::SetSeekThreshold(const media::TimeUnit& aTime) {
+void RemoteDecoderChild::SetSeekThreshold(const media::TimeUnit& aTime) {
   AssertOnManagerThread();
   if (mCanSend) {
     SendSetSeekThreshold(aTime.IsValid() ? aTime.ToMicroseconds() : INT64_MIN);
   }
 }
 
-MediaDataDecoder::ConversionRequired RemoteVideoDecoderChild::NeedsConversion()
+MediaDataDecoder::ConversionRequired RemoteDecoderChild::NeedsConversion()
     const {
   AssertOnManagerThread();
   return mConversion;
 }
 
-void RemoteVideoDecoderChild::AssertOnManagerThread() const {
+void RemoteDecoderChild::AssertOnManagerThread() const {
   MOZ_ASSERT(NS_GetCurrentThread() == mThread);
 }
 
-RemoteDecoderManagerChild* RemoteVideoDecoderChild::GetManager() {
+RemoteDecoderManagerChild* RemoteDecoderChild::GetManager() {
   if (!mCanSend) {
     return nullptr;
   }
   return static_cast<RemoteDecoderManagerChild*>(Manager());
 }
 
 }  // namespace mozilla
rename from dom/media/ipc/RemoteVideoDecoderChild.h
rename to dom/media/ipc/RemoteDecoderChild.h
--- a/dom/media/ipc/RemoteVideoDecoderChild.h
+++ b/dom/media/ipc/RemoteDecoderChild.h
@@ -1,45 +1,38 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
-#ifndef include_dom_media_ipc_RemoteVideoDecoderChild_h
-#define include_dom_media_ipc_RemoteVideoDecoderChild_h
-#include "mozilla/PRemoteVideoDecoderChild.h"
+#ifndef include_dom_media_ipc_RemoteDecoderChild_h
+#define include_dom_media_ipc_RemoteDecoderChild_h
+#include "mozilla/PRemoteDecoderChild.h"
 #include "IRemoteDecoderChild.h"
 
-#include "MediaResult.h"
-
-namespace mozilla {
-namespace layers {
-class BufferRecycleBin;
-}
-}  // namespace mozilla
-
 namespace mozilla {
 
 class RemoteDecoderManagerChild;
 using mozilla::MediaDataDecoder;
 using mozilla::ipc::IPCResult;
 
-class RemoteVideoDecoderChild final : public PRemoteVideoDecoderChild,
-                                      public IRemoteDecoderChild {
-  friend class PRemoteVideoDecoderChild;
+class RemoteDecoderChild : public PRemoteDecoderChild,
+                           public IRemoteDecoderChild {
+  friend class PRemoteDecoderChild;
 
  public:
-  explicit RemoteVideoDecoderChild();
+  explicit RemoteDecoderChild();
 
-  // PRemoteVideoDecoderChild
-  IPCResult RecvVideoOutput(const RemoteVideoDataIPDL& aData);
+  // PRemoteDecoderChild
+  virtual IPCResult RecvOutput(const DecodedOutputIPDL& aDecodedData) = 0;
   IPCResult RecvInputExhausted();
   IPCResult RecvDrainComplete();
   IPCResult RecvError(const nsresult& aError);
-  IPCResult RecvInitComplete(const nsCString& aDecoderDescription,
+  IPCResult RecvInitComplete(const TrackInfo::TrackType& trackType,
+                             const nsCString& aDecoderDescription,
                              const ConversionRequired& aConversion);
   IPCResult RecvInitFailed(const nsresult& aReason);
   IPCResult RecvFlushComplete();
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
   // IRemoteDecoderChild
   RefPtr<MediaDataDecoder::InitPromise> Init() override;
@@ -49,45 +42,40 @@ class RemoteVideoDecoderChild final : pu
   RefPtr<MediaDataDecoder::FlushPromise> Flush() override;
   void Shutdown() override;
   bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
   nsCString GetDescriptionName() const override;
   void SetSeekThreshold(const media::TimeUnit& aTime) override;
   MediaDataDecoder::ConversionRequired NeedsConversion() const override;
   void DestroyIPDL() override;
 
-  MOZ_IS_CLASS_INIT
-  MediaResult InitIPDL(const VideoInfo& aVideoInfo, float aFramerate,
-                       const CreateDecoderParams::OptionSet& aOptions);
-
   // Called from IPDL when our actor has been destroyed
   void IPDLActorDestroyed();
 
   RemoteDecoderManagerChild* GetManager();
 
- private:
-  ~RemoteVideoDecoderChild();
-
+ protected:
+  virtual ~RemoteDecoderChild();
   void AssertOnManagerThread() const;
-  RefPtr<mozilla::layers::Image> DeserializeImage(
-      const SurfaceDescriptorBuffer& sdBuffer, const IntSize& aPicSize);
 
-  RefPtr<RemoteVideoDecoderChild> mIPDLSelfRef;
+  RefPtr<RemoteDecoderChild> mIPDLSelfRef;
+  bool mCanSend = false;
+  MediaDataDecoder::DecodedData mDecodedData;
+
+ private:
   RefPtr<nsIThread> mThread;
 
   MozPromiseHolder<MediaDataDecoder::InitPromise> mInitPromise;
   MozPromiseHolder<MediaDataDecoder::DecodePromise> mDecodePromise;
   MozPromiseHolder<MediaDataDecoder::DecodePromise> mDrainPromise;
   MozPromiseHolder<MediaDataDecoder::FlushPromise> mFlushPromise;
 
   nsCString mHardwareAcceleratedReason;
   nsCString mDescription;
-  bool mCanSend;
-  bool mInitialized;
-  bool mIsHardwareAccelerated;
-  MediaDataDecoder::ConversionRequired mConversion;
-  MediaDataDecoder::DecodedData mDecodedData;
-  RefPtr<mozilla::layers::BufferRecycleBin> mBufferRecycleBin;
+  bool mInitialized = false;
+  bool mIsHardwareAccelerated = false;
+  MediaDataDecoder::ConversionRequired mConversion =
+      MediaDataDecoder::ConversionRequired::kNeedNone;
 };
 
 }  // namespace mozilla
 
-#endif  // include_dom_media_ipc_RemoteVideoDecoderChild_h
+#endif  // include_dom_media_ipc_RemoteDecoderChild_h
--- a/dom/media/ipc/RemoteDecoderManagerChild.cpp
+++ b/dom/media/ipc/RemoteDecoderManagerChild.cpp
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "RemoteDecoderManagerChild.h"
 
 #include "base/task.h"
 
-#include "RemoteVideoDecoderChild.h"
+#include "RemoteDecoderChild.h"
 
 namespace mozilla {
 
 // Only modified on the main-thread
 StaticRefPtr<nsIThread> sRemoteDecoderManagerChildThread;
 StaticRefPtr<AbstractThread> sRemoteDecoderManagerChildAbstractThread;
 
 // Only accessed from sRemoteDecoderManagerChildThread
@@ -72,27 +72,31 @@ RemoteDecoderManagerChild::GetSingleton(
   return sRemoteDecoderManagerChildThread;
 }
 
 /* static */ AbstractThread*
 RemoteDecoderManagerChild::GetManagerAbstractThread() {
   return sRemoteDecoderManagerChildAbstractThread;
 }
 
-PRemoteVideoDecoderChild*
-RemoteDecoderManagerChild::AllocPRemoteVideoDecoderChild(
-    const VideoInfo& /* not used */, const float& /* not used */,
+PRemoteDecoderChild*
+RemoteDecoderManagerChild::AllocPRemoteDecoderChild(
+    const RemoteDecoderInfoIPDL& /* not used */,
     const CreateDecoderParams::OptionSet& /* not used */, bool* /* not used */,
     nsCString* /* not used */) {
-  return new RemoteVideoDecoderChild();
+  // RemoteDecoderModule is responsible for creating RemoteDecoderChild
+  // classes.
+  MOZ_ASSERT(false, "RemoteDecoderManagerChild cannot create "
+                    "RemoteDecoderChild classes");
+  return nullptr;
 }
 
-bool RemoteDecoderManagerChild::DeallocPRemoteVideoDecoderChild(
-    PRemoteVideoDecoderChild* actor) {
-  RemoteVideoDecoderChild* child = static_cast<RemoteVideoDecoderChild*>(actor);
+bool RemoteDecoderManagerChild::DeallocPRemoteDecoderChild(
+    PRemoteDecoderChild* actor) {
+  RemoteDecoderChild* child = static_cast<RemoteDecoderChild*>(actor);
   child->IPDLActorDestroyed();
   return true;
 }
 
 void RemoteDecoderManagerChild::Open(
     Endpoint<PRemoteDecoderManagerChild>&& aEndpoint) {
   MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
   // Only create RemoteDecoderManagerChild, bind new endpoint and init
--- a/dom/media/ipc/RemoteDecoderManagerChild.h
+++ b/dom/media/ipc/RemoteDecoderManagerChild.h
@@ -30,21 +30,22 @@ class RemoteDecoderManagerChild final : 
   bool CanSend();
 
  protected:
   void InitIPDL();
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void DeallocPRemoteDecoderManagerChild() override;
 
-  PRemoteVideoDecoderChild* AllocPRemoteVideoDecoderChild(
-      const VideoInfo& aVideoInfo, const float& aFramerate,
-      const CreateDecoderParams::OptionSet& aOptions, bool* aSuccess,
+  PRemoteDecoderChild* AllocPRemoteDecoderChild(
+      const RemoteDecoderInfoIPDL& aRemoteDecoderInfo,
+      const CreateDecoderParams::OptionSet& aOptions,
+      bool* aSuccess,
       nsCString* aErrorDescription);
-  bool DeallocPRemoteVideoDecoderChild(PRemoteVideoDecoderChild* actor);
+  bool DeallocPRemoteDecoderChild(PRemoteDecoderChild* actor);
 
  private:
   // Main thread only
   static void InitializeThread();
 
   RemoteDecoderManagerChild() = default;
   ~RemoteDecoderManagerChild() = default;
 
--- a/dom/media/ipc/RemoteDecoderManagerParent.cpp
+++ b/dom/media/ipc/RemoteDecoderManagerParent.cpp
@@ -4,17 +4,18 @@
  * 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 "RemoteDecoderManagerParent.h"
 
 #if XP_WIN
 #  include <objbase.h>
 #endif
 
-#include "RemoteVideoDecoderParent.h"
+#include "RemoteAudioDecoder.h"
+#include "RemoteVideoDecoder.h"
 #include "VideoUtils.h"  // for MediaThreadType
 
 namespace mozilla {
 
 StaticRefPtr<nsIThread> sRemoteDecoderManagerParentThread;
 StaticRefPtr<TaskQueue> sRemoteDecoderManagerTaskQueue;
 
 class RemoteDecoderManagerThreadHolder {
@@ -141,36 +142,56 @@ RemoteDecoderManagerParent::~RemoteDecod
   MOZ_COUNT_DTOR(RemoteDecoderManagerParent);
 }
 
 void RemoteDecoderManagerParent::ActorDestroy(
     mozilla::ipc::IProtocol::ActorDestroyReason) {
   mThreadHolder = nullptr;
 }
 
-PRemoteVideoDecoderParent*
-RemoteDecoderManagerParent::AllocPRemoteVideoDecoderParent(
-    const VideoInfo& aVideoInfo, const float& aFramerate,
-    const CreateDecoderParams::OptionSet& aOptions, bool* aSuccess,
+PRemoteDecoderParent*
+RemoteDecoderManagerParent::AllocPRemoteDecoderParent(
+    const RemoteDecoderInfoIPDL& aRemoteDecoderInfo,
+    const CreateDecoderParams::OptionSet& aOptions,
+    bool* aSuccess,
     nsCString* aErrorDescription) {
   RefPtr<TaskQueue> decodeTaskQueue =
       new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
                     "RemoteVideoDecoderParent::mDecodeTaskQueue");
 
-  auto* parent = new RemoteVideoDecoderParent(
-      this, aVideoInfo, aFramerate, aOptions, sRemoteDecoderManagerTaskQueue,
-      decodeTaskQueue, aSuccess, aErrorDescription);
+  if (aRemoteDecoderInfo.type() ==
+      RemoteDecoderInfoIPDL::TVideoDecoderInfoIPDL) {
+    const VideoDecoderInfoIPDL& decoderInfo =
+        aRemoteDecoderInfo.get_VideoDecoderInfoIPDL();
+    return new RemoteVideoDecoderParent(this,
+                                        decoderInfo.videoInfo(),
+                                        decoderInfo.framerate(),
+                                        aOptions,
+                                        sRemoteDecoderManagerTaskQueue,
+                                        decodeTaskQueue,
+                                        aSuccess,
+                                        aErrorDescription);
+  } else if (aRemoteDecoderInfo.type() == RemoteDecoderInfoIPDL::TAudioInfo) {
+    return new RemoteAudioDecoderParent(this,
+                                        aRemoteDecoderInfo.get_AudioInfo(),
+                                        aOptions,
+                                        sRemoteDecoderManagerTaskQueue,
+                                        decodeTaskQueue,
+                                        aSuccess,
+                                        aErrorDescription);
+  }
 
-  return parent;
+  MOZ_CRASH("unrecognized type of RemoteDecoderInfoIPDL union");
+  return nullptr;
 }
 
-bool RemoteDecoderManagerParent::DeallocPRemoteVideoDecoderParent(
-    PRemoteVideoDecoderParent* actor) {
-  RemoteVideoDecoderParent* parent =
-      static_cast<RemoteVideoDecoderParent*>(actor);
+bool RemoteDecoderManagerParent::DeallocPRemoteDecoderParent(
+    PRemoteDecoderParent* actor) {
+  RemoteDecoderParent* parent =
+      static_cast<RemoteDecoderParent*>(actor);
   parent->Destroy();
   return true;
 }
 
 void RemoteDecoderManagerParent::Open(
     Endpoint<PRemoteDecoderManagerParent>&& aEndpoint) {
   if (!aEndpoint.Bind(this)) {
     // We can't recover from this.
--- a/dom/media/ipc/RemoteDecoderManagerParent.h
+++ b/dom/media/ipc/RemoteDecoderManagerParent.h
@@ -21,21 +21,22 @@ class RemoteDecoderManagerParent final :
       Endpoint<PRemoteDecoderManagerParent>&& aEndpoint);
 
   static bool StartupThreads();
   static void ShutdownThreads();
 
   bool OnManagerThread();
 
  protected:
-  PRemoteVideoDecoderParent* AllocPRemoteVideoDecoderParent(
-      const VideoInfo& aVideoInfo, const float& aFramerate,
-      const CreateDecoderParams::OptionSet& aOptions, bool* aSuccess,
+  PRemoteDecoderParent* AllocPRemoteDecoderParent(
+      const RemoteDecoderInfoIPDL& aRemoteDecoderInfo,
+      const CreateDecoderParams::OptionSet& aOptions,
+      bool* aSuccess,
       nsCString* aErrorDescription);
-  bool DeallocPRemoteVideoDecoderParent(PRemoteVideoDecoderParent* actor);
+  bool DeallocPRemoteDecoderParent(PRemoteDecoderParent* actor);
 
   void ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyReason) override;
 
   void DeallocPRemoteDecoderManagerParent() override;
 
  private:
   explicit RemoteDecoderManagerParent(
       RemoteDecoderManagerThreadHolder* aThreadHolder);
--- a/dom/media/ipc/RemoteDecoderModule.cpp
+++ b/dom/media/ipc/RemoteDecoderModule.cpp
@@ -8,19 +8,21 @@
 #include "base/thread.h"
 #include "mozilla/dom/ContentChild.h"  // for launching RDD w/ ContentChild
 #include "mozilla/layers/SynchronousTask.h"
 #include "mozilla/StaticPrefs.h"
 
 #ifdef MOZ_AV1
 #  include "AOMDecoder.h"
 #endif
+#include "RemoteAudioDecoder.h"
 #include "RemoteDecoderManagerChild.h"
 #include "RemoteMediaDataDecoder.h"
-#include "RemoteVideoDecoderChild.h"
+#include "RemoteVideoDecoder.h"
+#include "VorbisDecoder.h"
 
 namespace mozilla {
 
 using base::Thread;
 using dom::ContentChild;
 using namespace ipc;
 using namespace layers;
 
@@ -28,22 +30,66 @@ bool RemoteDecoderModule::SupportsMimeTy
     const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const {
   bool supports = false;
 
 #ifdef MOZ_AV1
   if (StaticPrefs::MediaAv1Enabled()) {
     supports |= AOMDecoder::IsAV1(aMimeType);
   }
 #endif
+  if (StaticPrefs::MediaRddVorbisEnabled()) {
+    supports |= VorbisDataDecoder::IsVorbis(aMimeType);
+  }
+
   MOZ_LOG(
       sPDMLog, LogLevel::Debug,
       ("Sandbox decoder %s requested type", supports ? "supports" : "rejects"));
   return supports;
 }
 
+already_AddRefed<MediaDataDecoder>
+RemoteDecoderModule::CreateAudioDecoder(const CreateDecoderParams& aParams) {
+  if (XRE_IsContentProcess()) {
+    ContentChild* contentChild = ContentChild::GetSingleton();
+    contentChild->LaunchRDDProcess();
+  }
+
+  if (!RemoteDecoderManagerChild::GetManagerThread()) {
+    return nullptr;
+  }
+
+  RemoteAudioDecoderChild* child = new RemoteAudioDecoderChild();
+  RefPtr<RemoteMediaDataDecoder> object = new RemoteMediaDataDecoder(
+      child, RemoteDecoderManagerChild::GetManagerThread(),
+      RemoteDecoderManagerChild::GetManagerAbstractThread());
+
+  // (per Matt Woodrow) We can't use NS_DISPATCH_SYNC here since that
+  // can spin the event loop while it waits.
+  SynchronousTask task("InitIPDL");
+  MediaResult result(NS_OK);
+  RemoteDecoderManagerChild::GetManagerThread()->Dispatch(
+      NS_NewRunnableFunction("dom::RemoteDecoderModule::CreateAudioDecoder",
+                             [&, child]() {
+                               AutoCompleteTask complete(&task);
+                               result = child->InitIPDL(aParams.AudioConfig(),
+                                                        aParams.mOptions);
+                             }),
+      NS_DISPATCH_NORMAL);
+  task.Wait();
+
+  if (NS_FAILED(result)) {
+    if (aParams.mError) {
+      *aParams.mError = result;
+    }
+    return nullptr;
+  }
+
+  return object.forget();
+}
+
 already_AddRefed<MediaDataDecoder> RemoteDecoderModule::CreateVideoDecoder(
     const CreateDecoderParams& aParams) {
   if (XRE_IsContentProcess()) {
     ContentChild* contentChild = ContentChild::GetSingleton();
     contentChild->LaunchRDDProcess();
   }
 
   if (!RemoteDecoderManagerChild::GetManagerThread()) {
--- a/dom/media/ipc/RemoteDecoderModule.h
+++ b/dom/media/ipc/RemoteDecoderModule.h
@@ -19,16 +19,14 @@ class RemoteDecoderModule : public Platf
 
   bool SupportsMimeType(const nsACString& aMimeType,
                         DecoderDoctorDiagnostics* aDiagnostics) const override;
 
   already_AddRefed<MediaDataDecoder> CreateVideoDecoder(
       const CreateDecoderParams& aParams) override;
 
   already_AddRefed<MediaDataDecoder> CreateAudioDecoder(
-      const CreateDecoderParams& aParams) override {
-    return nullptr;
-  }
+      const CreateDecoderParams& aParams) override;
 };
 
 }  // namespace mozilla
 
 #endif  // include_dom_media_ipc_RemoteDecoderModule_h
rename from dom/media/ipc/RemoteVideoDecoderParent.cpp
rename to dom/media/ipc/RemoteDecoderParent.cpp
--- a/dom/media/ipc/RemoteVideoDecoderParent.cpp
+++ b/dom/media/ipc/RemoteDecoderParent.cpp
@@ -1,100 +1,71 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "RemoteVideoDecoderParent.h"
+#include "RemoteDecoderParent.h"
 
 #include "mozilla/Unused.h"
 
-#ifdef MOZ_AV1
-#  include "AOMDecoder.h"
-#endif
-#include "ImageContainer.h"
 #include "RemoteDecoderManagerParent.h"
-#include "RemoteDecoderModule.h"
 
 namespace mozilla {
 
 using media::TimeUnit;
-using namespace layers;  // for PlanarYCbCrImage and BufferRecycleBin
 
-RemoteVideoDecoderParent::RemoteVideoDecoderParent(
-    RemoteDecoderManagerParent* aParent, const VideoInfo& aVideoInfo,
-    float aFramerate, const CreateDecoderParams::OptionSet& aOptions,
-    TaskQueue* aManagerTaskQueue, TaskQueue* aDecodeTaskQueue, bool* aSuccess,
-    nsCString* aErrorDescription)
+RemoteDecoderParent::RemoteDecoderParent(RemoteDecoderManagerParent* aParent,
+                                         TaskQueue* aManagerTaskQueue,
+                                         TaskQueue* aDecodeTaskQueue)
     : mParent(aParent),
       mManagerTaskQueue(aManagerTaskQueue),
-      mDecodeTaskQueue(aDecodeTaskQueue),
-      mDestroyed(false),
-      mVideoInfo(aVideoInfo) {
-  MOZ_COUNT_CTOR(RemoteVideoDecoderParent);
+      mDecodeTaskQueue(aDecodeTaskQueue) {
+  MOZ_COUNT_CTOR(RemoteDecoderParent);
   MOZ_ASSERT(OnManagerThread());
   // We hold a reference to ourselves to keep us alive until IPDL
   // explictly destroys us. There may still be refs held by
   // tasks, but no new ones should be added after we're
   // destroyed.
   mIPDLSelfRef = this;
-
-  CreateDecoderParams params(mVideoInfo);
-  params.mTaskQueue = mDecodeTaskQueue;
-  params.mImageContainer = new layers::ImageContainer();
-  params.mRate = CreateDecoderParams::VideoFrameRate(aFramerate);
-  params.mOptions = aOptions;
-  MediaResult error(NS_OK);
-  params.mError = &error;
-
-#ifdef MOZ_AV1
-  if (AOMDecoder::IsAV1(params.mConfig.mMimeType)) {
-    mDecoder = new AOMDecoder(params);
-  }
-#endif
-
-  if (NS_FAILED(error)) {
-    MOZ_ASSERT(aErrorDescription);
-    *aErrorDescription = error.Description();
-  }
-
-  *aSuccess = !!mDecoder;
 }
 
-RemoteVideoDecoderParent::~RemoteVideoDecoderParent() {
-  MOZ_COUNT_DTOR(RemoteVideoDecoderParent);
+RemoteDecoderParent::~RemoteDecoderParent() {
+  MOZ_COUNT_DTOR(RemoteDecoderParent);
 }
 
-void RemoteVideoDecoderParent::Destroy() {
+void RemoteDecoderParent::Destroy() {
   MOZ_ASSERT(OnManagerThread());
   mDestroyed = true;
   mIPDLSelfRef = nullptr;
 }
 
-mozilla::ipc::IPCResult RemoteVideoDecoderParent::RecvInit() {
+mozilla::ipc::IPCResult RemoteDecoderParent::RecvInit() {
   MOZ_ASSERT(OnManagerThread());
-  RefPtr<RemoteVideoDecoderParent> self = this;
+  RefPtr<RemoteDecoderParent> self = this;
   mDecoder->Init()->Then(mManagerTaskQueue, __func__,
                          [self](TrackInfo::TrackType aTrack) {
-                           MOZ_ASSERT(aTrack == TrackInfo::kVideoTrack);
+                           MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack ||
+                                      aTrack == TrackInfo::kVideoTrack);
                            if (self->mDecoder) {
                              Unused << self->SendInitComplete(
+                                 aTrack,
                                  self->mDecoder->GetDescriptionName(),
                                  self->mDecoder->NeedsConversion());
                            }
                          },
                          [self](MediaResult aReason) {
                            if (!self->mDestroyed) {
                              Unused << self->SendInitFailed(aReason);
                            }
                          });
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult RemoteVideoDecoderParent::RecvInput(
+mozilla::ipc::IPCResult RemoteDecoderParent::RecvInput(
     const MediaRawDataIPDL& aData) {
   MOZ_ASSERT(OnManagerThread());
   // XXX: This copies the data into a buffer owned by the MediaRawData. Ideally
   // we'd just take ownership of the shmem.
   RefPtr<MediaRawData> data = new MediaRawData(aData.buffer().get<uint8_t>(),
                                                aData.buffer().Size<uint8_t>());
   if (aData.buffer().Size<uint8_t>() && !data->Data()) {
     // OOM
@@ -104,133 +75,98 @@ mozilla::ipc::IPCResult RemoteVideoDecod
   data->mOffset = aData.base().offset();
   data->mTime = TimeUnit::FromMicroseconds(aData.base().time());
   data->mTimecode = TimeUnit::FromMicroseconds(aData.base().timecode());
   data->mDuration = TimeUnit::FromMicroseconds(aData.base().duration());
   data->mKeyframe = aData.base().keyframe();
 
   DeallocShmem(aData.buffer());
 
-  RefPtr<RemoteVideoDecoderParent> self = this;
+  RefPtr<RemoteDecoderParent> self = this;
   mDecoder->Decode(data)->Then(
       mManagerTaskQueue, __func__,
       [self, this](const MediaDataDecoder::DecodedData& aResults) {
         if (mDestroyed) {
           return;
         }
         ProcessDecodedData(aResults);
         Unused << SendInputExhausted();
       },
       [self](const MediaResult& aError) { self->Error(aError); });
   return IPC_OK();
 }
 
-void RemoteVideoDecoderParent::ProcessDecodedData(
-    const MediaDataDecoder::DecodedData& aData) {
-  MOZ_ASSERT(OnManagerThread());
-
-  for (const auto& data : aData) {
-    MOZ_ASSERT(data->mType == MediaData::VIDEO_DATA,
-               "Can only decode videos using RemoteVideoDecoderParent!");
-    VideoData* video = static_cast<VideoData*>(data.get());
-
-    MOZ_ASSERT(video->mImage,
-               "Decoded video must output a layer::Image to "
-               "be used with RemoteVideoDecoderParent");
-
-    PlanarYCbCrImage* image =
-        static_cast<PlanarYCbCrImage*>(video->mImage.get());
-
-    SurfaceDescriptorBuffer sdBuffer;
-    Shmem buffer;
-    if (AllocShmem(image->GetDataSize(), Shmem::SharedMemory::TYPE_BASIC,
-                   &buffer) &&
-        image->GetDataSize() == buffer.Size<uint8_t>()) {
-      sdBuffer.data() = buffer;
-      image->BuildSurfaceDescriptorBuffer(sdBuffer);
-    }
-
-    RemoteVideoDataIPDL output(
-        MediaDataIPDL(data->mOffset, data->mTime.ToMicroseconds(),
-                      data->mTimecode.ToMicroseconds(),
-                      data->mDuration.ToMicroseconds(), data->mFrames,
-                      data->mKeyframe),
-        video->mDisplay, image->GetSize(), sdBuffer, video->mFrameID);
-    Unused << SendVideoOutput(output);
-  }
-}
-
-mozilla::ipc::IPCResult RemoteVideoDecoderParent::RecvFlush() {
+mozilla::ipc::IPCResult RemoteDecoderParent::RecvFlush() {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(OnManagerThread());
-  RefPtr<RemoteVideoDecoderParent> self = this;
+  RefPtr<RemoteDecoderParent> self = this;
   mDecoder->Flush()->Then(
       mManagerTaskQueue, __func__,
       [self]() {
         if (!self->mDestroyed) {
           Unused << self->SendFlushComplete();
         }
       },
       [self](const MediaResult& aError) { self->Error(aError); });
 
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult RemoteVideoDecoderParent::RecvDrain() {
+mozilla::ipc::IPCResult RemoteDecoderParent::RecvDrain() {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(OnManagerThread());
-  RefPtr<RemoteVideoDecoderParent> self = this;
+  RefPtr<RemoteDecoderParent> self = this;
   mDecoder->Drain()->Then(
       mManagerTaskQueue, __func__,
       [self, this](const MediaDataDecoder::DecodedData& aResults) {
         if (!mDestroyed) {
           ProcessDecodedData(aResults);
           Unused << SendDrainComplete();
         }
       },
       [self](const MediaResult& aError) { self->Error(aError); });
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult RemoteVideoDecoderParent::RecvShutdown() {
+mozilla::ipc::IPCResult RemoteDecoderParent::RecvShutdown() {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(OnManagerThread());
   if (mDecoder) {
     mDecoder->Shutdown();
   }
   mDecoder = nullptr;
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult RemoteVideoDecoderParent::RecvSetSeekThreshold(
+mozilla::ipc::IPCResult RemoteDecoderParent::RecvSetSeekThreshold(
     const int64_t& aTime) {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(OnManagerThread());
   mDecoder->SetSeekThreshold(aTime == INT64_MIN
                                  ? TimeUnit::Invalid()
                                  : TimeUnit::FromMicroseconds(aTime));
   return IPC_OK();
 }
 
-void RemoteVideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy) {
+void RemoteDecoderParent::ActorDestroy(ActorDestroyReason aWhy) {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(OnManagerThread());
   if (mDecoder) {
     mDecoder->Shutdown();
     mDecoder = nullptr;
   }
   if (mDecodeTaskQueue) {
     mDecodeTaskQueue->BeginShutdown();
   }
 }
 
-void RemoteVideoDecoderParent::Error(const MediaResult& aError) {
+void RemoteDecoderParent::Error(const MediaResult& aError) {
   MOZ_ASSERT(OnManagerThread());
   if (!mDestroyed) {
     Unused << SendError(aError);
   }
 }
 
-bool RemoteVideoDecoderParent::OnManagerThread() {
+bool RemoteDecoderParent::OnManagerThread() {
   return mParent->OnManagerThread();
 }
 
 }  // namespace mozilla
rename from dom/media/ipc/RemoteVideoDecoderParent.h
rename to dom/media/ipc/RemoteDecoderParent.h
--- a/dom/media/ipc/RemoteVideoDecoderParent.h
+++ b/dom/media/ipc/RemoteDecoderParent.h
@@ -1,62 +1,60 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
-#ifndef include_dom_media_ipc_RemoteVideoDecoderParent_h
-#define include_dom_media_ipc_RemoteVideoDecoderParent_h
-#include "mozilla/PRemoteVideoDecoderParent.h"
+#ifndef include_dom_media_ipc_RemoteDecoderParent_h
+#define include_dom_media_ipc_RemoteDecoderParent_h
+#include "mozilla/PRemoteDecoderParent.h"
 
 namespace mozilla {
 
 class RemoteDecoderManagerParent;
 using mozilla::ipc::IPCResult;
 
-class RemoteVideoDecoderParent final : public PRemoteVideoDecoderParent {
-  friend class PRemoteVideoDecoderParent;
+class RemoteDecoderParent : public PRemoteDecoderParent {
+  friend class PRemoteDecoderParent;
 
  public:
   // We refcount this class since the task queue can have runnables
   // that reference us.
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteVideoDecoderParent)
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteDecoderParent)
 
-  RemoteVideoDecoderParent(RemoteDecoderManagerParent* aParent,
-                           const VideoInfo& aVideoInfo, float aFramerate,
-                           const CreateDecoderParams::OptionSet& aOptions,
-                           TaskQueue* aManagerTaskQueue,
-                           TaskQueue* aDecodeTaskQueue, bool* aSuccess,
-                           nsCString* aErrorDescription);
+  RemoteDecoderParent(RemoteDecoderManagerParent* aParent,
+                      TaskQueue* aManagerTaskQueue,
+                      TaskQueue* aDecodeTaskQueue);
 
   void Destroy();
 
-  // PRemoteVideoDecoderParent
+  // PRemoteDecoderParent
   IPCResult RecvInit();
   IPCResult RecvInput(const MediaRawDataIPDL& aData);
   IPCResult RecvFlush();
   IPCResult RecvDrain();
   IPCResult RecvShutdown();
   IPCResult RecvSetSeekThreshold(const int64_t& aTime);
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
- private:
+ protected:
+  virtual ~RemoteDecoderParent();
+
   bool OnManagerThread();
   void Error(const MediaResult& aError);
 
-  ~RemoteVideoDecoderParent();
-  void ProcessDecodedData(const MediaDataDecoder::DecodedData& aData);
+  virtual void ProcessDecodedData(
+      const MediaDataDecoder::DecodedData& aData) = 0;
 
   RefPtr<RemoteDecoderManagerParent> mParent;
-  RefPtr<RemoteVideoDecoderParent> mIPDLSelfRef;
+  RefPtr<RemoteDecoderParent> mIPDLSelfRef;
   RefPtr<TaskQueue> mManagerTaskQueue;
   RefPtr<TaskQueue> mDecodeTaskQueue;
   RefPtr<MediaDataDecoder> mDecoder;
 
   // Can only be accessed from the manager thread
-  bool mDestroyed;
-  VideoInfo mVideoInfo;
+  bool mDestroyed = false;
 };
 
 }  // namespace mozilla
 
-#endif  // include_dom_media_ipc_RemoteVideoDecoderParent_h
+#endif  // include_dom_media_ipc_RemoteDecoderParent_h
new file mode 100644
--- /dev/null
+++ b/dom/media/ipc/RemoteVideoDecoder.cpp
@@ -0,0 +1,209 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "RemoteVideoDecoder.h"
+
+#include "mozilla/layers/ImageDataSerializer.h"
+
+#ifdef MOZ_AV1
+#  include "AOMDecoder.h"
+#endif
+#include "ImageContainer.h"  // for PlanarYCbCrData and BufferRecycleBin
+#include "RemoteDecoderManagerChild.h"
+
+namespace mozilla {
+
+using namespace layers;  // for PlanarYCbCrData and BufferRecycleBin
+
+RemoteVideoDecoderChild::RemoteVideoDecoderChild()
+    : RemoteDecoderChild(),
+      mBufferRecycleBin(new BufferRecycleBin) {}
+
+RefPtr<mozilla::layers::Image> RemoteVideoDecoderChild::DeserializeImage(
+    const SurfaceDescriptorBuffer& aSdBuffer, const IntSize& aPicSize) {
+  MOZ_ASSERT(aSdBuffer.desc().type() == BufferDescriptor::TYCbCrDescriptor);
+  if (aSdBuffer.desc().type() != BufferDescriptor::TYCbCrDescriptor) {
+    return nullptr;
+  }
+  const YCbCrDescriptor& descriptor = aSdBuffer.desc().get_YCbCrDescriptor();
+
+  uint8_t* buffer = nullptr;
+  const MemoryOrShmem& memOrShmem = aSdBuffer.data();
+  switch (memOrShmem.type()) {
+    case MemoryOrShmem::Tuintptr_t:
+      buffer = reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t());
+      break;
+    case MemoryOrShmem::TShmem:
+      buffer = memOrShmem.get_Shmem().get<uint8_t>();
+      break;
+    default:
+      MOZ_ASSERT(false, "Unknown MemoryOrShmem type");
+  }
+  if (!buffer) {
+    return nullptr;
+  }
+
+  PlanarYCbCrData pData;
+  pData.mYSize = descriptor.ySize();
+  pData.mYStride = descriptor.yStride();
+  pData.mCbCrSize = descriptor.cbCrSize();
+  pData.mCbCrStride = descriptor.cbCrStride();
+  // default mYSkip, mCbSkip, mCrSkip because not held in YCbCrDescriptor
+  pData.mYSkip = pData.mCbSkip = pData.mCrSkip = 0;
+  // default mPicX, mPicY because not held in YCbCrDescriptor
+  pData.mPicX = pData.mPicY = 0;
+  pData.mPicSize = aPicSize;
+  pData.mStereoMode = descriptor.stereoMode();
+  pData.mColorDepth = descriptor.colorDepth();
+  pData.mYUVColorSpace = descriptor.yUVColorSpace();
+  pData.mYChannel = ImageDataSerializer::GetYChannel(buffer, descriptor);
+  pData.mCbChannel = ImageDataSerializer::GetCbChannel(buffer, descriptor);
+  pData.mCrChannel = ImageDataSerializer::GetCrChannel(buffer, descriptor);
+
+  // images coming from AOMDecoder are RecyclingPlanarYCbCrImages.
+  RefPtr<RecyclingPlanarYCbCrImage> image =
+      new RecyclingPlanarYCbCrImage(mBufferRecycleBin);
+  image->CopyData(pData);
+
+  switch (memOrShmem.type()) {
+    case MemoryOrShmem::Tuintptr_t:
+      delete[] reinterpret_cast<uint8_t*>(memOrShmem.get_uintptr_t());
+      break;
+    case MemoryOrShmem::TShmem:
+      DeallocShmem(memOrShmem.get_Shmem());
+      break;
+    default:
+      MOZ_ASSERT(false, "Unknown MemoryOrShmem type");
+  }
+
+  return image;
+}
+
+mozilla::ipc::IPCResult RemoteVideoDecoderChild::RecvOutput(
+    const DecodedOutputIPDL& aDecodedData) {
+  AssertOnManagerThread();
+  MOZ_ASSERT(aDecodedData.type() == DecodedOutputIPDL::TRemoteVideoDataIPDL);
+  const RemoteVideoDataIPDL& aData = aDecodedData.get_RemoteVideoDataIPDL();
+
+  RefPtr<Image> image = DeserializeImage(aData.sdBuffer(), aData.frameSize());
+
+  RefPtr<VideoData> video = VideoData::CreateFromImage(
+      aData.display(), aData.base().offset(),
+      media::TimeUnit::FromMicroseconds(aData.base().time()),
+      media::TimeUnit::FromMicroseconds(aData.base().duration()), image,
+      aData.base().keyframe(),
+      media::TimeUnit::FromMicroseconds(aData.base().timecode()));
+
+  mDecodedData.AppendElement(std::move(video));
+  return IPC_OK();
+}
+
+MediaResult RemoteVideoDecoderChild::InitIPDL(
+    const VideoInfo& aVideoInfo,
+    float aFramerate,
+    const CreateDecoderParams::OptionSet& aOptions) {
+  RefPtr<RemoteDecoderManagerChild> manager =
+      RemoteDecoderManagerChild::GetSingleton();
+
+  // The manager isn't available because RemoteDecoderManagerChild has been
+  // initialized with null end points and we don't want to decode video on RDD
+  // process anymore. Return false here so that we can fallback to other PDMs.
+  if (!manager) {
+    return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+                       RESULT_DETAIL("RemoteDecoderManager is not available."));
+  }
+
+  if (!manager->CanSend()) {
+    return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+                       RESULT_DETAIL("RemoteDecoderManager unable to send."));
+  }
+
+  mIPDLSelfRef = this;
+  bool success = false;
+  nsCString errorDescription;
+  VideoDecoderInfoIPDL decoderInfo(aVideoInfo, aFramerate);
+  if (manager->SendPRemoteDecoderConstructor(this,
+                                             decoderInfo,
+                                             aOptions,
+                                             &success,
+                                             &errorDescription)) {
+    mCanSend = true;
+  }
+
+  return success ? MediaResult(NS_OK)
+                 : MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, errorDescription);
+}
+
+RemoteVideoDecoderParent::RemoteVideoDecoderParent(
+    RemoteDecoderManagerParent* aParent,
+    const VideoInfo& aVideoInfo,
+    float aFramerate,
+    const CreateDecoderParams::OptionSet& aOptions,
+    TaskQueue* aManagerTaskQueue,
+    TaskQueue* aDecodeTaskQueue,
+    bool* aSuccess,
+    nsCString* aErrorDescription)
+    : RemoteDecoderParent(aParent,
+                          aManagerTaskQueue,
+                          aDecodeTaskQueue),
+      mVideoInfo(aVideoInfo) {
+  CreateDecoderParams params(mVideoInfo);
+  params.mTaskQueue = mDecodeTaskQueue;
+  params.mImageContainer = new layers::ImageContainer();
+  params.mRate = CreateDecoderParams::VideoFrameRate(aFramerate);
+  params.mOptions = aOptions;
+  MediaResult error(NS_OK);
+  params.mError = &error;
+
+#ifdef MOZ_AV1
+  if (AOMDecoder::IsAV1(params.mConfig.mMimeType)) {
+    mDecoder = new AOMDecoder(params);
+  }
+#endif
+
+  if (NS_FAILED(error)) {
+    MOZ_ASSERT(aErrorDescription);
+    *aErrorDescription = error.Description();
+  }
+
+  *aSuccess = !!mDecoder;
+}
+
+void RemoteVideoDecoderParent::ProcessDecodedData(
+    const MediaDataDecoder::DecodedData& aData) {
+  MOZ_ASSERT(OnManagerThread());
+
+  for (const auto& data : aData) {
+    MOZ_ASSERT(data->mType == MediaData::VIDEO_DATA,
+               "Can only decode videos using RemoteDecoderParent!");
+    VideoData* video = static_cast<VideoData*>(data.get());
+
+    MOZ_ASSERT(video->mImage,
+               "Decoded video must output a layer::Image to "
+               "be used with RemoteDecoderParent");
+
+    PlanarYCbCrImage* image =
+        static_cast<PlanarYCbCrImage*>(video->mImage.get());
+
+    SurfaceDescriptorBuffer sdBuffer;
+    Shmem buffer;
+    if (AllocShmem(image->GetDataSize(), Shmem::SharedMemory::TYPE_BASIC,
+                   &buffer) &&
+        image->GetDataSize() == buffer.Size<uint8_t>()) {
+      sdBuffer.data() = buffer;
+      image->BuildSurfaceDescriptorBuffer(sdBuffer);
+    }
+
+    RemoteVideoDataIPDL output(
+        MediaDataIPDL(data->mOffset, data->mTime.ToMicroseconds(),
+                      data->mTimecode.ToMicroseconds(),
+                      data->mDuration.ToMicroseconds(), data->mFrames,
+                      data->mKeyframe),
+        video->mDisplay, image->GetSize(), sdBuffer, video->mFrameID);
+    Unused << SendOutput(output);
+  }
+}
+
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/ipc/RemoteVideoDecoder.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+#ifndef include_dom_media_ipc_RemoteVideoDecoderChild_h
+#define include_dom_media_ipc_RemoteVideoDecoderChild_h
+#include "RemoteDecoderChild.h"
+#include "RemoteDecoderParent.h"
+
+namespace mozilla {
+namespace layers {
+class BufferRecycleBin;
+}  // namespace layers
+}  // namespace mozilla
+
+namespace mozilla {
+
+using mozilla::ipc::IPCResult;
+
+class RemoteVideoDecoderChild final : public RemoteDecoderChild {
+ public:
+  explicit RemoteVideoDecoderChild();
+
+  MOZ_IS_CLASS_INIT
+  MediaResult InitIPDL(const VideoInfo& aVideoInfo,
+                       float aFramerate,
+                       const CreateDecoderParams::OptionSet& aOptions);
+
+  IPCResult RecvOutput(const DecodedOutputIPDL& aDecodedData) override;
+
+ private:
+  RefPtr<mozilla::layers::Image> DeserializeImage(
+      const SurfaceDescriptorBuffer& sdBuffer, const IntSize& aPicSize);
+
+  RefPtr<mozilla::layers::BufferRecycleBin> mBufferRecycleBin;
+};
+
+class RemoteVideoDecoderParent final : public RemoteDecoderParent {
+ public:
+  RemoteVideoDecoderParent(RemoteDecoderManagerParent* aParent,
+                           const VideoInfo& aVideoInfo,
+                           float aFramerate,
+                           const CreateDecoderParams::OptionSet& aOptions,
+                           TaskQueue* aManagerTaskQueue,
+                           TaskQueue* aDecodeTaskQueue,
+                           bool* aSuccess,
+                           nsCString* aErrorDescription);
+
+ protected:
+  void ProcessDecodedData(const MediaDataDecoder::DecodedData& aData) override;
+
+ private:
+  // Can only be accessed from the manager thread
+  // Note: we can't keep a reference to the original VideoInfo here
+  // because unlike in typical MediaDataDecoder situations, we're being
+  // passed a deserialized VideoInfo from RecvPRemoteDecoderConstructor
+  // which is destroyed when RecvPRemoteDecoderConstructor returns.
+  const VideoInfo mVideoInfo;
+};
+
+}  // namespace mozilla
+
+#endif  // include_dom_media_ipc_RemoteVideoDecoderChild_h
--- a/dom/media/ipc/moz.build
+++ b/dom/media/ipc/moz.build
@@ -3,36 +3,36 @@
 # 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/.
 
 
 IPDL_SOURCES += [
     'PMediaDecoderParams.ipdlh',
     'PRDD.ipdl',
+    'PRemoteDecoder.ipdl',
     'PRemoteDecoderManager.ipdl',
-    'PRemoteVideoDecoder.ipdl',
     'PVideoDecoder.ipdl',
     'PVideoDecoderManager.ipdl',
 ]
 
 EXPORTS.mozilla += [
     'GpuDecoderModule.h',
     'IRemoteDecoderChild.h',
     'RDDChild.h',
     'RDDParent.h',
     'RDDProcessHost.h',
     'RDDProcessImpl.h',
     'RDDProcessManager.h',
+    'RemoteDecoderChild.h',
     'RemoteDecoderManagerChild.h',
     'RemoteDecoderManagerParent.h',
     'RemoteDecoderModule.h',
+    'RemoteDecoderParent.h',
     'RemoteMediaDataDecoder.h',
-    'RemoteVideoDecoderChild.h',
-    'RemoteVideoDecoderParent.h',
     'VideoDecoderChild.h',
     'VideoDecoderManagerChild.h',
     'VideoDecoderManagerParent.h',
     'VideoDecoderParent.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'MediaIPCUtils.h',
@@ -40,22 +40,24 @@ EXPORTS.mozilla.dom += [
 
 SOURCES += [
     'GpuDecoderModule.cpp',
     'RDDChild.cpp',
     'RDDParent.cpp',
     'RDDProcessHost.cpp',
     'RDDProcessImpl.cpp',
     'RDDProcessManager.cpp',
+    'RemoteAudioDecoder.cpp',
+    'RemoteDecoderChild.cpp',
     'RemoteDecoderManagerChild.cpp',
     'RemoteDecoderManagerParent.cpp',
     'RemoteDecoderModule.cpp',
+    'RemoteDecoderParent.cpp',
     'RemoteMediaDataDecoder.cpp',
-    'RemoteVideoDecoderChild.cpp',
-    'RemoteVideoDecoderParent.cpp',
+    'RemoteVideoDecoder.cpp',
     'VideoDecoderChild.cpp',
     'VideoDecoderManagerChild.cpp',
     'VideoDecoderManagerParent.cpp',
     'VideoDecoderParent.cpp',
 ]
 
 # so we can include nsMacUtilsImpl.h in RDDParent.cpp for sandboxing
 LOCAL_INCLUDES += [
--- a/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp
@@ -19,18 +19,20 @@
 #endif
 
 namespace mozilla {
 
 bool AgnosticDecoderModule::SupportsMimeType(
     const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const {
   bool supports =
       VPXDecoder::IsVPX(aMimeType) || OpusDataDecoder::IsOpus(aMimeType) ||
-      VorbisDataDecoder::IsVorbis(aMimeType) ||
       WaveDataDecoder::IsWave(aMimeType) || TheoraDecoder::IsTheora(aMimeType);
+  if (!StaticPrefs::MediaRddVorbisEnabled() || !StaticPrefs::MediaRddProcessEnabled()) {
+    supports |= VorbisDataDecoder::IsVorbis(aMimeType);
+  }
 #ifdef MOZ_AV1
   if (StaticPrefs::MediaAv1Enabled()) {
     supports |= AOMDecoder::IsAV1(aMimeType);
   }
 #endif
   MOZ_LOG(sPDMLog, LogLevel::Debug,
           ("Agnostic decoder %s requested type",
            supports ? "supports" : "rejects"));
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -1200,16 +1200,22 @@ VARCACHE_PREF(
 #undef PREF_VALUE
 
 VARCACHE_PREF(
   "media.rdd-process.startup_timeout_ms",
    MediaRddProcessStartupTimeoutMs,
   RelaxedAtomicInt32, 5000
 )
 
+VARCACHE_PREF(
+  "media.rdd-vorbis.enabled",
+   MediaRddVorbisEnabled,
+  RelaxedAtomicBool, false
+)
+
 #ifdef ANDROID
 
 // Enable the MediaCodec PlatformDecoderModule by default.
 VARCACHE_PREF(
   "media.android-media-codec.enabled",
    MediaAndroidMediaCodecEnabled,
   RelaxedAtomicBool, true
 )