Bug 1264199: P5. Perform all downmixing operations in DecodedAudioDataSink. r=kinetik draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Wed, 13 Apr 2016 20:25:50 +1000
changeset 356284 ce935ab7fa71a8c0df449d68137dfda58681eb8c
parent 356283 33e4f515f3888f221848da0eb2db213789e86a09
child 356285 ae063077e30d397739952d9d2c5e61ba640242bb
push id16486
push userbmo:jyavenard@mozilla.com
push dateTue, 26 Apr 2016 02:36:37 +0000
reviewerskinetik
bugs1264199
milestone49.0a1
Bug 1264199: P5. Perform all downmixing operations in DecodedAudioDataSink. r=kinetik Performing all audio processing operations in the same place, allows to simplify the code. Additionally, if accessibility.monoaudio.enable is not set, we always upmix mono to stereo so that if the first audio stream seen was mono, we aren't stuck playing all future streams in mono. MozReview-Commit-ID: 5yANN6PLFhX
dom/media/AudioStream.cpp
dom/media/AudioStream.h
dom/media/mediasink/DecodedAudioDataSink.cpp
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -124,17 +124,16 @@ AudioStream::AudioStream(DataSource& aSo
   , mInRate(0)
   , mOutRate(0)
   , mChannels(0)
   , mOutChannels(0)
   , mAudioClock(this)
   , mTimeStretcher(nullptr)
   , mDumpFile(nullptr)
   , mState(INITIALIZED)
-  , mIsMonoAudioEnabled(gfxPrefs::MonoAudio())
   , mDataSource(aSource)
 {
 }
 
 AudioStream::~AudioStream()
 {
   LOG("deleted, state %d", mState);
   MOZ_ASSERT(mState == SHUTDOWN && !mCubebStream,
@@ -326,17 +325,17 @@ AudioStream::Init(uint32_t aNumChannels,
   if (!CubebUtils::GetCubebContext()) {
     return NS_ERROR_FAILURE;
   }
 
   MOZ_LOG(gAudioStreamLog, LogLevel::Debug,
     ("%s  channels: %d, rate: %d for %p", __FUNCTION__, aNumChannels, aRate, this));
   mInRate = mOutRate = aRate;
   mChannels = aNumChannels;
-  mOutChannels = mIsMonoAudioEnabled ? 1 : aNumChannels;
+  mOutChannels = aNumChannels;
 
   mDumpFile = OpenDumpFile(this);
 
   cubeb_stream_params params;
   params.rate = aRate;
   params.channels = mOutChannels;
 #if defined(__ANDROID__)
 #if defined(MOZ_B2G)
@@ -348,21 +347,16 @@ AudioStream::Init(uint32_t aNumChannels,
   if (params.stream_type == CUBEB_STREAM_TYPE_MAX) {
     return NS_ERROR_INVALID_ARG;
   }
 #endif
 
   params.format = ToCubebFormat<AUDIO_OUTPUT_FORMAT>::value;
   mAudioClock.Init();
 
-  if (mIsMonoAudioEnabled) {
-    AudioConfig inConfig(mChannels, mInRate);
-    AudioConfig outConfig(mOutChannels, mOutRate);
-    mAudioConverter = MakeUnique<AudioConverter>(inConfig, outConfig);
-  }
   return OpenCubeb(params);
 }
 
 // This code used to live inside AudioStream::Init(), but on Mac (others?)
 // it has been known to take 300-800 (or even 8500) ms to execute(!)
 nsresult
 AudioStream::OpenCubeb(cubeb_stream_params &aParams)
 {
@@ -547,31 +541,27 @@ AudioStream::GetPositionInFramesUnlocked
 bool
 AudioStream::IsPaused()
 {
   MonitorAutoLock mon(mMonitor);
   return mState == STOPPED;
 }
 
 bool
-AudioStream::Downmix(Chunk* aChunk)
+AudioStream::IsValidAudioFormat(Chunk* aChunk)
 {
   if (aChunk->Rate() != mInRate) {
     LOGW("mismatched sample %u, mInRate=%u", aChunk->Rate(), mInRate);
     return false;
   }
 
   if (aChunk->Channels() > 8) {
     return false;
   }
 
-  if (mAudioConverter) {
-    mAudioConverter->Process(aChunk->GetWritable(), aChunk->Frames());
-  }
-
   return true;
 }
 
 void
 AudioStream::GetUnprocessed(AudioBufferWriter& aWriter)
 {
   mMonitor.AssertCurrentThreadOwns();
 
@@ -590,20 +580,20 @@ AudioStream::GetUnprocessed(AudioBufferW
   }
 
   while (aWriter.Available() > 0) {
     UniquePtr<Chunk> c = mDataSource.PopFrames(aWriter.Available());
     if (c->Frames() == 0) {
       break;
     }
     MOZ_ASSERT(c->Frames() <= aWriter.Available());
-    if (Downmix(c.get())) {
+    if (IsValidAudioFormat(c.get())) {
       aWriter.Write(c->Data(), c->Frames());
     } else {
-      // Write silence if downmixing fails.
+      // Write silence if invalid format.
       aWriter.WriteZeros(c->Frames());
     }
   }
 }
 
 void
 AudioStream::GetTimeStretched(AudioBufferWriter& aWriter)
 {
@@ -618,20 +608,20 @@ AudioStream::GetTimeStretched(AudioBuffe
   uint32_t toPopFrames = ceil(aWriter.Available() * playbackRate);
 
   while (mTimeStretcher->numSamples() < aWriter.Available()) {
     UniquePtr<Chunk> c = mDataSource.PopFrames(toPopFrames);
     if (c->Frames() == 0) {
       break;
     }
     MOZ_ASSERT(c->Frames() <= toPopFrames);
-    if (Downmix(c.get())) {
+    if (IsValidAudioFormat(c.get())) {
       mTimeStretcher->putSamples(c->Data(), c->Frames());
     } else {
-      // Write silence if downmixing fails.
+      // Write silence if invalid format.
       AutoTArray<AudioDataValue, 1000> buf;
       buf.SetLength(mOutChannels * c->Frames());
       memset(buf.Elements(), 0, buf.Length() * sizeof(AudioDataValue));
       mTimeStretcher->putSamples(buf.Elements(), c->Frames());
     }
   }
 
   auto timeStretcher = mTimeStretcher;
--- a/dom/media/AudioStream.h
+++ b/dom/media/AudioStream.h
@@ -328,18 +328,19 @@ private:
   }
 
 
   long DataCallback(void* aBuffer, long aFrames);
   void StateCallback(cubeb_state aState);
 
   nsresult EnsureTimeStretcherInitializedUnlocked();
 
-  // Return true if downmixing succeeds otherwise false.
-  bool Downmix(Chunk* aChunk);
+  // Return true if audio frames are valid (correct sampling rate and valid
+  // channel count) otherwise false.
+  bool IsValidAudioFormat(Chunk* aChunk);
 
   void GetUnprocessed(AudioBufferWriter& aWriter);
   void GetTimeStretched(AudioBufferWriter& aWriter);
 
   void StartUnlocked();
 
   // The monitor is held to protect all access to member variables.
   Monitor mMonitor;
@@ -369,19 +370,15 @@ private:
     STOPPED,     // Stopped by a call to Pause().
     DRAINED,     // StateCallback has indicated that the drain is complete.
     ERRORED,     // Stream disabled due to an internal error.
     SHUTDOWN     // Shutdown has been called
   };
 
   StreamState mState;
   bool mIsFirst;
-  // Get this value from the preference, if true, we would downmix the stereo.
-  bool mIsMonoAudioEnabled;
 
   DataSource& mDataSource;
-
-  UniquePtr<AudioConverter> mAudioConverter;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/mediasink/DecodedAudioDataSink.cpp
+++ b/dom/media/mediasink/DecodedAudioDataSink.cpp
@@ -62,18 +62,20 @@ DecodedAudioDataSink::DecodedAudioDataSi
     // 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.");
 
-  mOutputChannels = mInfo.mChannels > 2 && gfxPrefs::AudioSinkForceStereo()
-                      ? 2 : mInfo.mChannels;
+  bool monoAudioEnabled = gfxPrefs::MonoAudio();
+
+  mOutputChannels = monoAudioEnabled
+    ? 1 : (gfxPrefs::AudioSinkForceStereo() ? 2 : mInfo.mChannels);
 }
 
 DecodedAudioDataSink::~DecodedAudioDataSink()
 {
 }
 
 RefPtr<GenericPromise>
 DecodedAudioDataSink::Init(const PlaybackParams& aParams)