Bug 1444479 - P6. Make Opus and Vorbis decoder deal with more channels than 8. r=padenot
authorJean-Yves Avenard <jyavenard@mozilla.com>
Fri, 16 Mar 2018 16:54:12 +0100
changeset 463340 938c300a46ca9c337a541a047c04b4be92e65a89
parent 463339 3207ab7618d5be61f1f5634271724457a29878a9
child 463341 6531087bc2062b7dfde6d0352d99ce2ad278a950
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs1444479
milestone61.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 1444479 - P6. Make Opus and Vorbis decoder deal with more channels than 8. r=padenot Under 8 channels, the audio will be reordered so it can be playable on any platforms. Over 8 channels, the channels will be as output by the decoder. Playing of such stream will be platform dependent as neither Opus nor Vorbis define a channel layout with more than 8 channels. With WebAudio however, the result will be platform independent (as long as you don't attempt to play it) MozReview-Commit-ID: 93ATiKm9y20
dom/media/ogg/OpusParser.cpp
dom/media/platforms/agnostic/OpusDecoder.cpp
dom/media/platforms/agnostic/OpusDecoder.h
dom/media/platforms/agnostic/VorbisDecoder.cpp
--- a/dom/media/ogg/OpusParser.cpp
+++ b/dom/media/ogg/OpusParser.cpp
@@ -75,24 +75,25 @@ bool OpusParser::DecodeHeader(unsigned c
                            " mapping family 0.", mChannels));
         return false;
       }
       mStreams = 1;
       mCoupledStreams = mChannels - 1;
       mMappingTable[0] = 0;
       mMappingTable[1] = 1;
     } else if (mChannelMapping == 1 || mChannelMapping == 255) {
-      // Currently only up to 8 channels are defined for mapping family 1 and we
-      // only supports only up to 8 channels for mapping family 255.
-      if (mChannels>8) {
-        OPUS_LOG(LogLevel::Debug, ("Invalid Opus file: too many channels (%d) for"
-                           " mapping family 1.", mChannels));
+      // Currently only up to 8 channels are defined for mapping family 1
+      if (mChannelMapping == 1 && mChannels > 8) {
+        OPUS_LOG(LogLevel::Debug,
+                 ("Invalid Opus file: too many channels (%d) for"
+                  " mapping family 1.",
+                  mChannels));
         return false;
       }
-      if (aLength>static_cast<unsigned>(20+mChannels)) {
+      if (aLength > static_cast<unsigned>(20 + mChannels)) {
         mStreams = aData[19];
         mCoupledStreams = aData[20];
         int i;
         for (i=0; i<mChannels; i++)
           mMappingTable[i] = aData[21+i];
       } else {
         OPUS_LOG(LogLevel::Debug, ("Invalid Opus file: channel mapping %d,"
                            " but no channel mapping table", mChannelMapping));
--- a/dom/media/platforms/agnostic/OpusDecoder.cpp
+++ b/dom/media/platforms/agnostic/OpusDecoder.cpp
@@ -82,22 +82,23 @@ OpusDataDecoder::Init()
   if (NS_FAILED(DecodeHeader(p, length))) {
     OPUS_DEBUG("Error decoding header!");
     return InitPromise::CreateAndReject(
       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                   RESULT_DETAIL("Error decoding header!")),
       __func__);
   }
 
+  MOZ_ASSERT(mMappingTable.Length() >= uint32_t(mOpusParser->mChannels));
   int r;
   mOpusDecoder = opus_multistream_decoder_create(mOpusParser->mRate,
                                                  mOpusParser->mChannels,
                                                  mOpusParser->mStreams,
                                                  mOpusParser->mCoupledStreams,
-                                                 mMappingTable,
+                                                 mMappingTable.Elements(),
                                                  &r);
   mSkip = mOpusParser->mPreSkip;
   mPaddingDiscarded = false;
 
   if (codecDelay != FramesToUsecs(mOpusParser->mPreSkip,
                                   mOpusParser->mRate).value()) {
     NS_WARNING("Invalid Opus header: CodecDelay and pre-skip do not match!");
     return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
@@ -126,41 +127,42 @@ OpusDataDecoder::DecodeHeader(const unsi
   mDecodedHeader = true;
 
   mOpusParser = new OpusParser;
   if (!mOpusParser->DecodeHeader(const_cast<unsigned char*>(aData), aLength)) {
     return NS_ERROR_FAILURE;
   }
   int channels = mOpusParser->mChannels;
 
+  mMappingTable.SetLength(channels);
   AudioConfig::ChannelLayout vorbisLayout(
     channels, VorbisDataDecoder::VorbisLayout(channels));
-  if (!vorbisLayout.IsValid()) {
-    OPUS_DEBUG("Invalid channel mapping. Source is %d channels", channels);
-    return NS_ERROR_FAILURE;
-  }
-  mChannelMap = vorbisLayout.Map();
+  if (vorbisLayout.IsValid()) {
+    mChannelMap = vorbisLayout.Map();
 
-  AudioConfig::ChannelLayout smpteLayout(
-    AudioConfig::ChannelLayout::SMPTEDefault(vorbisLayout));
+    AudioConfig::ChannelLayout smpteLayout(
+      AudioConfig::ChannelLayout::SMPTEDefault(vorbisLayout));
 
-  static_assert(sizeof(mOpusParser->mMappingTable) /
-                sizeof(mOpusParser->mMappingTable[0]) >=
-                MAX_AUDIO_CHANNELS,
-                "Invalid size set");
-  uint8_t map[sizeof(mOpusParser->mMappingTable) /
-              sizeof(mOpusParser->mMappingTable[0])];
-  if (vorbisLayout.MappingTable(smpteLayout, map)) {
-    for (int i = 0; i < channels; i++) {
-      mMappingTable[i] = mOpusParser->mMappingTable[map[i]];
+    AutoTArray<uint8_t, 8> map;
+    map.SetLength(channels);
+    if (vorbisLayout.MappingTable(smpteLayout, &map)) {
+      for (int i = 0; i < channels; i++) {
+        mMappingTable[i] = mOpusParser->mMappingTable[map[i]];
+      }
+    } else {
+      // Should never get here as vorbis layout is always convertible to SMPTE
+      // default layout.
+      PodCopy(mMappingTable.Elements(), mOpusParser->mMappingTable, channels);
     }
   } else {
-    // Should never get here as vorbis layout is always convertible to SMPTE
-    // default layout.
-    PodCopy(mMappingTable, mOpusParser->mMappingTable, MAX_AUDIO_CHANNELS);
+    // Create a dummy mapping table so that channel ordering stay the same
+    // during decoding.
+    for (int i = 0; i < channels; i++) {
+      mMappingTable[i] = i;
+    }
   }
 
   return NS_OK;
 }
 
 RefPtr<MediaDataDecoder::DecodePromise>
 OpusDataDecoder::Decode(MediaRawData* aSample)
 {
--- a/dom/media/platforms/agnostic/OpusDecoder.h
+++ b/dom/media/platforms/agnostic/OpusDecoder.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #if !defined(OpusDecoder_h_)
 #define OpusDecoder_h_
 
 #include "PlatformDecoderModule.h"
 
 #include "mozilla/Maybe.h"
 #include "nsAutoPtr.h"
+#include "nsTArray.h"
 
 struct OpusMSDecoder;
 
 namespace mozilla {
 
 class OpusParser;
 
 DDLoggedTypeDeclNameAndBase(OpusDataDecoder, MediaDataDecoder);
@@ -63,14 +64,14 @@ private:
   bool mDecodedHeader;
 
   // Opus padding should only be discarded on the final packet.  Once this
   // is set to true, if the reader attempts to decode any further packets it
   // will raise an error so we can indicate that the file is invalid.
   bool mPaddingDiscarded;
   int64_t mFrames;
   Maybe<int64_t> mLastFrameTime;
-  uint8_t mMappingTable[MAX_AUDIO_CHANNELS]; // Channel mapping table.
+  AutoTArray<uint8_t, 8> mMappingTable;
   AudioConfig::ChannelLayout::ChannelMap mChannelMap;
 };
 
 } // namespace mozilla
 #endif
--- a/dom/media/platforms/agnostic/VorbisDecoder.cpp
+++ b/dom/media/platforms/agnostic/VorbisDecoder.cpp
@@ -230,24 +230,19 @@ VorbisDataDecoder::ProcessDecode(MediaRa
           NS_ERROR_DOM_MEDIA_OVERFLOW_ERR,
           RESULT_DETAIL("Overflow adding total_duration and aSample->mTime")),
         __func__);
     };
 
     if (!mAudioConverter) {
       const AudioConfig::ChannelLayout layout =
         AudioConfig::ChannelLayout(channels, VorbisLayout(channels));
-      AudioConfig in(layout, rate);
-      AudioConfig out(AudioConfig::ChannelLayout::SMPTEDefault(layout), rate);
-      if (!in.IsValid() || !out.IsValid()) {
-        return DecodePromise::CreateAndReject(
-          MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
-                      RESULT_DETAIL("Invalid channel layout:%u", channels)),
-          __func__);
-      }
+      AudioConfig in(layout, channels, rate);
+      AudioConfig out(
+        AudioConfig::ChannelLayout::SMPTEDefault(layout), channels, rate);
       mAudioConverter = MakeUnique<AudioConverter>(in, out);
     }
     MOZ_ASSERT(mAudioConverter->CanWorkInPlace());
     AudioSampleBuffer data(Move(buffer));
     data = mAudioConverter->Process(Move(data));
 
     results.AppendElement(
       new AudioData(aOffset,