Bug 1431810 - Disable Opus phase inversion on stereo to mono downmix. r?rillian draft
authorAlex Chronopoulos <achronop@gmail.com>
Thu, 15 Mar 2018 18:28:14 +0200
changeset 771713 91824dfedcd1723acdf847d119255f3e03ec54c5
parent 769888 827c686c570935483c3ad8022aa92b9e005574c3
child 771714 140701da8aecde5d165fb0f677dddfcef92f7694
child 773750 65e6eedc3b8b6fa038773802bfd8f571fed6e4e9
push id103756
push userachronop@gmail.com
push dateFri, 23 Mar 2018 18:06:54 +0000
reviewersrillian
bugs1431810
milestone61.0a1
Bug 1431810 - Disable Opus phase inversion on stereo to mono downmix. r?rillian MozReview-Commit-ID: 5eaSPQzUu9o
dom/media/VideoUtils.cpp
dom/media/VideoUtils.h
dom/media/mediasink/AudioSink.cpp
dom/media/platforms/agnostic/OpusDecoder.cpp
--- a/dom/media/VideoUtils.cpp
+++ b/dom/media/VideoUtils.cpp
@@ -157,16 +157,29 @@ void DownmixStereoToMono(mozilla::AudioD
     int sample = 0;
 #endif
     // The sample of the buffer would be interleaved.
     sample = (aBuffer[fIdx*channels] + aBuffer[fIdx*channels + 1]) * 0.5;
     aBuffer[fIdx*channels] = aBuffer[fIdx*channels + 1] = sample;
   }
 }
 
+uint32_t DecideAudioPlaybackChannels(const AudioInfo& info)
+{
+  if (MediaPrefs::MonoAudio()) {
+    return 1;
+  }
+
+  if (MediaPrefs::AudioSinkForceStereo()) {
+    return 2;
+  }
+
+  return info.mChannels;
+}
+
 bool
 IsVideoContentType(const nsCString& aContentType)
 {
   NS_NAMED_LITERAL_CSTRING(video, "video");
   if (FindInReadable(video, aContentType)) {
     return true;
   }
   return false;
--- a/dom/media/VideoUtils.h
+++ b/dom/media/VideoUtils.h
@@ -153,16 +153,20 @@ nsresult SecondsToUsecs(double aSeconds,
 void
 ScaleDisplayByAspectRatio(gfx::IntSize& aDisplay, float aAspectRatio);
 
 // Downmix Stereo audio samples to Mono.
 // Input are the buffer contains stereo data and the number of frames.
 void DownmixStereoToMono(mozilla::AudioDataValue* aBuffer,
                          uint32_t aFrames);
 
+// Decide the number of playback channels according to the
+// given AudioInfo and the prefs that are being set.
+uint32_t DecideAudioPlaybackChannels(const AudioInfo& info);
+
 bool IsVideoContentType(const nsCString& aContentType);
 
 // Returns true if it's safe to use aPicture as the picture to be
 // extracted inside a frame of size aFrame, and scaled up to and displayed
 // at a size of aDisplay. You should validate the frame, picture, and
 // display regions before using them to display video frames.
 bool
 IsValidVideoRegion(const gfx::IntSize& aFrame,
--- a/dom/media/mediasink/AudioSink.cpp
+++ b/dom/media/mediasink/AudioSink.cpp
@@ -59,22 +59,17 @@ AudioSink::AudioSink(AbstractThread* aTh
     // content provider want change from those rates mid-stream.
     mOutputRate = mInfo.mRate;
   } else {
     // We will resample all data to match cubeb's preferred sampling rate.
     mOutputRate = AudioStream::GetPreferredRate();
   }
   MOZ_DIAGNOSTIC_ASSERT(mOutputRate, "output rate can't be 0.");
 
-  bool monoAudioEnabled = MediaPrefs::MonoAudio();
-
-  mOutputChannels =
-    monoAudioEnabled
-    ? 1
-    : (MediaPrefs::AudioSinkForceStereo() ? 2 : mInfo.mChannels);
+  mOutputChannels = DecideAudioPlaybackChannels(mInfo);
 }
 
 AudioSink::~AudioSink()
 {
 }
 
 RefPtr<GenericPromise>
 AudioSink::Init(const PlaybackParams& aParams)
--- a/dom/media/platforms/agnostic/OpusDecoder.cpp
+++ b/dom/media/platforms/agnostic/OpusDecoder.cpp
@@ -88,16 +88,27 @@ OpusDataDecoder::Init()
 
   int r;
   mOpusDecoder = opus_multistream_decoder_create(mOpusParser->mRate,
                                                  mOpusParser->mChannels,
                                                  mOpusParser->mStreams,
                                                  mOpusParser->mCoupledStreams,
                                                  mMappingTable,
                                                  &r);
+
+  // Opus has a special feature for stereo coding where it represent wide
+  // stereo channels by 180-degree out of phase. This improves quality, but
+  // needs to be disabled when the output is downmixed to mono. Playback number
+  // of channels are set in AudioSink, using the same method
+  // `DecideAudioPlaybackChannels()`, and triggers downmix if needed.
+  if (mOpusDecoder && mOpusParser->mChannels == 2 &&
+      DecideAudioPlaybackChannels(mInfo) == 1) {
+    opus_multistream_decoder_ctl(mOpusDecoder, OPUS_SET_PHASE_INVERSION_DISABLED(1));
+  }
+
   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__);
   }