Bug 1175447 - mono audio support. r=padenot, r=sotaro
authorHayden Huang <hahuang@mozilla.com>
Thu, 06 Aug 2015 14:30:22 +0800
changeset 264415 6cf4450ccb569c942fcbc862f7055246a19db780
parent 264414 e0e53c6083c5b02f6e5e25d4d791ca7dc03ae7eb
child 264416 dcff442c846a24ed447f3e8fb7e0c27fdc075c00
push id15375
push userkwierso@gmail.com
push dateSat, 26 Sep 2015 01:22:48 +0000
treeherderfx-team@d24fd81248c8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot, sotaro
bugs1175447
milestone44.0a1
Bug 1175447 - mono audio support. r=padenot, r=sotaro This patch is to enable mono audio option for those who has full hearing loss in one ear. With this feature, they can get the complete audio with using one ear.
b2g/chrome/content/settings.js
dom/media/AudioStream.cpp
dom/media/AudioStream.h
dom/media/VideoUtils.cpp
dom/media/VideoUtils.h
dom/media/omx/MediaOmxCommonReader.cpp
dom/media/omx/MediaOmxCommonReader.h
dom/system/gonk/AudioManager.cpp
gfx/thebes/gfxPrefs.h
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -73,16 +73,22 @@ var SettingsListener = {
     }));
 
     this._callbacks[name] = callback;
   }
 };
 
 SettingsListener.init();
 
+// =================== Mono Audio ======================
+
+SettingsListener.observe('accessibility.monoaudio.enable', false, function(value) {
+  Services.prefs.setBoolPref('accessibility.monoaudio.enable', value);
+});
+
 // =================== Console ======================
 
 SettingsListener.observe('debug.console.enabled', true, function(value) {
   Services.prefs.setBoolPref('consoleservice.enabled', value);
   Services.prefs.setBoolPref('layout.css.report_errors', value);
 });
 
 SettingsListener.observe('homescreen.manifestURL', 'Sentinel Value' , function(value) {
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -12,16 +12,17 @@
 #include "VideoUtils.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Snprintf.h"
 #include <algorithm>
 #include "mozilla/Telemetry.h"
 #include "CubebUtils.h"
 #include "nsPrintfCString.h"
+#include "gfxPrefs.h"
 
 namespace mozilla {
 
 #ifdef LOG
 #undef LOG
 #endif
 
 PRLogModuleInfo* gAudioStreamLog = nullptr;
@@ -125,16 +126,17 @@ AudioStream::AudioStream()
   , mOutChannels(0)
   , mWritten(0)
   , mAudioClock(this)
   , mTimeStretcher(nullptr)
   , mDumpFile(nullptr)
   , mBytesPerFrame(0)
   , mState(INITIALIZED)
   , mLastGoodPosition(0)
+  , mIsMonoAudioEnabled(gfxPrefs::MonoAudio())
 {
 }
 
 AudioStream::~AudioStream()
 {
   LOG(("AudioStream: delete %p, state %d", this, mState));
   MOZ_ASSERT(mState == SHUTDOWN && !mCubebStream,
              "Should've called Shutdown() before deleting an AudioStream");
@@ -416,19 +418,22 @@ AudioStream::Write(const AudioDataValue*
     return NS_ERROR_FAILURE;
   }
   NS_ASSERTION(mState == INITIALIZED || mState == STARTED || mState == RUNNING,
     "Stream write in unexpected state.");
 
   // Downmix to Stereo.
   if (mChannels > 2 && mChannels <= 8) {
     DownmixAudioToStereo(const_cast<AudioDataValue*> (aBuf), mChannels, aFrames);
+  } else if (mChannels > 8) {
+    return NS_ERROR_FAILURE;
   }
-  else if (mChannels > 8) {
-    return NS_ERROR_FAILURE;
+
+  if (mChannels >= 2 && mIsMonoAudioEnabled) {
+    DownmixStereoToMono(const_cast<AudioDataValue*> (aBuf), aFrames);
   }
 
   const uint8_t* src = reinterpret_cast<const uint8_t*>(aBuf);
   uint32_t bytesToCopy = FramesToBytes(aFrames);
 
   while (bytesToCopy > 0) {
     uint32_t available = std::min(bytesToCopy, mBuffer.Available());
     MOZ_ASSERT(available % mBytesPerFrame == 0,
--- a/dom/media/AudioStream.h
+++ b/dom/media/AudioStream.h
@@ -320,13 +320,15 @@ private:
     SHUTDOWN     // Shutdown has been called
   };
 
   StreamState mState;
   bool mIsFirst;
   // The last good position returned by cubeb_stream_get_position(). Used to
   // check if the cubeb position is going backward.
   uint64_t mLastGoodPosition;
+  // Get this value from the preferece, if true, we would downmix the stereo.
+  bool mIsMonoAudioEnabled;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/VideoUtils.cpp
+++ b/dom/media/VideoUtils.cpp
@@ -183,16 +183,33 @@ int DownmixAudioToStereo(mozilla::AudioD
     buffer[i*outChannels] = static_cast<mozilla::AudioDataValue>(MOZ_CLIP_TO_15(sampL));
     sampR = (sampR + 8192)>>14;
     buffer[i*outChannels+1] = static_cast<mozilla::AudioDataValue>(MOZ_CLIP_TO_15(sampR));
   }
 #endif
   return outChannels;
 }
 
+void DownmixStereoToMono(mozilla::AudioDataValue* aBuffer,
+                         uint32_t aFrames)
+{
+  MOZ_ASSERT(aBuffer);
+  const int channels = 2;
+  for (uint32_t fIdx = 0; fIdx < aFrames; ++fIdx) {
+#ifdef MOZ_SAMPLE_TYPE_FLOAT32
+    float sample = 0.0;
+#else
+    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;
+  }
+}
+
 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
@@ -163,16 +163,21 @@ void ScaleDisplayByAspectRatio(nsIntSize
 
 // Downmix multichannel Audio samples to Stereo.
 // Input are the buffer contains multichannel data,
 // the number of channels and the number of frames.
 int DownmixAudioToStereo(mozilla::AudioDataValue* buffer,
                          int channels,
                          uint32_t frames);
 
+// 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);
+
 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 nsIntSize& aFrame, const nsIntRect& aPicture,
                         const nsIntSize& aDisplay);
--- a/dom/media/omx/MediaOmxCommonReader.cpp
+++ b/dom/media/omx/MediaOmxCommonReader.cpp
@@ -6,16 +6,17 @@
 
 #include "MediaOmxCommonReader.h"
 
 #include <stagefright/MediaSource.h>
 
 #include "AbstractMediaDecoder.h"
 #include "AudioChannelService.h"
 #include "MediaStreamSource.h"
+#include "gfxPrefs.h"
 
 #ifdef MOZ_AUDIO_OFFLOAD
 #include <stagefright/Utils.h>
 #include <cutils/properties.h>
 #include <stagefright/MetaData.h>
 #endif
 
 using namespace android;
@@ -31,16 +32,21 @@ MediaOmxCommonReader::MediaOmxCommonRead
 {
   if (!gMediaDecoderLog) {
     gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
   }
 
   mAudioChannel = dom::AudioChannelService::GetDefaultAudioChannel();
 }
 
+bool MediaOmxCommonReader::IsMonoAudioEnabled()
+{
+  return gfxPrefs::MonoAudio();
+}
+
 #ifdef MOZ_AUDIO_OFFLOAD
 void MediaOmxCommonReader::CheckAudioOffload()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   char offloadProp[128];
   property_get("audio.offload.disable", offloadProp, "0");
   bool offloadDisable =  atoi(offloadProp) != 0;
@@ -61,16 +67,17 @@ void MediaOmxCommonReader::CheckAudioOff
   // aren't supported and also duration would be less than a minute
   bool isTypeMusic = mAudioChannel == dom::AudioChannel::Content;
 
   DECODER_LOG(LogLevel::Debug, ("%s meta %p, no video %d, no streaming %d,"
       " channel type %d", __FUNCTION__, meta.get(), hasNoVideo,
       isNotStreaming, mAudioChannel));
 
   if ((meta.get()) && hasNoVideo && isNotStreaming && isTypeMusic &&
-      canOffloadStream(meta, false, false, AUDIO_STREAM_MUSIC)) {
+      canOffloadStream(meta, false, false, AUDIO_STREAM_MUSIC) &&
+      !IsMonoAudioEnabled()) {
     DECODER_LOG(LogLevel::Debug, ("Can offload this audio stream"));
     mDecoder->SetPlatformCanOffloadAudio(true);
   }
 }
 #endif
 
 } // namespace mozilla
--- a/dom/media/omx/MediaOmxCommonReader.h
+++ b/dom/media/omx/MediaOmxCommonReader.h
@@ -42,13 +42,15 @@ public:
   void CheckAudioOffload();
 #endif
 
 protected:
   dom::AudioChannel mAudioChannel;
   // Weak reference to the MediaStreamSource that will be created by either
   // MediaOmxReader or MediaCodecReader.
   android::MediaStreamSource* mStreamSource;
+  // Get value from the preferece, if true, we stop the audio offload.
+  bool IsMonoAudioEnabled();
 };
 
 } // namespace mozilla
 
 #endif // MEDIA_OMX_COMMON_READER_H
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -1097,17 +1097,16 @@ AudioManager::AppendProfileToVolumeSetti
       topic.Append(".");
       topic.Append(kAudioOutputProfilesTable[idx].tag);
       break;
     }
   }
   return topic;
 }
 
-
 void
 AudioManager::InitVolumeFromDatabase()
 {
   nsresult rv;
   nsCOMPtr<nsISettingsService> service = do_GetService(SETTINGS_SERVICE, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -128,16 +128,19 @@ private:
     }
     T mValue;
   };
 
   // This is where DECL_GFX_PREF for each of the preferences should go.
   // We will keep these in an alphabetical order to make it easier to see if
   // a method accessing a pref already exists. Just add yours in the list.
 
+  // It's a short time fix, and will be removed after landing bug 1206637.
+  DECL_GFX_PREF(Live, "accessibility.monoaudio.enable",        MonoAudio, bool, false);
+
   // The apz prefs are explained in AsyncPanZoomController.cpp
   DECL_GFX_PREF(Live, "apz.allow_checkerboarding",             APZAllowCheckerboarding, bool, true);
   DECL_GFX_PREF(Live, "apz.allow_zooming",                     APZAllowZooming, bool, false);
   DECL_GFX_PREF(Live, "apz.asyncscroll.throttle",              APZAsyncScrollThrottleTime, int32_t, 100);
   DECL_GFX_PREF(Live, "apz.asyncscroll.timeout",               APZAsyncScrollTimeout, int32_t, 300);
   DECL_GFX_PREF(Live, "apz.axis_lock.breakout_angle",          APZAxisBreakoutAngle, float, float(M_PI / 8.0) /* 22.5 degrees */);
   DECL_GFX_PREF(Live, "apz.axis_lock.breakout_threshold",      APZAxisBreakoutThreshold, float, 1.0f / 32.0f);
   DECL_GFX_PREF(Live, "apz.axis_lock.direct_pan_angle",        APZAllowedDirectPanAngle, float, float(M_PI / 3.0) /* 60 degrees */);