Bug 1321502 - part 2: Use preferred layout for initializing cubeb when audio queue is empty; r=jya
authorChun-Min Chang <chun.m.chang@gmail.com>
Sat, 04 Feb 2017 08:22:15 +0800
changeset 340877 0cdf5249e6d06897c0ffec56a6675dc971a16847
parent 340876 401683745308733d1ec980b6ba4cab53c7f38bd5
child 340878 c5621cb6f9075d731ccb28dcf20dd29deae2bddd
push id36940
push userihsiao@mozilla.com
push dateMon, 06 Feb 2017 07:55:46 +0000
treeherderautoland@0cdf5249e6d0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya
bugs1321502
milestone54.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 1321502 - part 2: Use preferred layout for initializing cubeb when audio queue is empty; r=jya MozReview-Commit-ID: BDEb8IxuJRn
dom/media/AudioStream.h
dom/media/CubebUtils.cpp
dom/media/CubebUtils.h
dom/media/mediasink/DecodedAudioDataSink.cpp
--- a/dom/media/AudioStream.h
+++ b/dom/media/AudioStream.h
@@ -220,16 +220,21 @@ public:
   // was opened, of the audio hardware.  Thread-safe.
   int64_t GetPositionInFrames();
 
   static uint32_t GetPreferredRate()
   {
     return CubebUtils::PreferredSampleRate();
   }
 
+  static uint32_t GetPreferredChannelMap(uint32_t aChannels)
+  {
+    return CubebUtils::PreferredChannelMap(aChannels);
+  }
+
   uint32_t GetOutChannels() { return mOutChannels; }
 
   // Set playback rate as a multiple of the intrinsic playback rate. This is to
   // be called only with aPlaybackRate > 0.0.
   nsresult SetPlaybackRate(double aPlaybackRate);
   // Switch between resampling (if false) and time stretching (if true, default).
   nsresult SetPreservesPitch(bool aPreservesPitch);
 
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -106,16 +106,21 @@ const int CUBEB_BACKEND_UNKNOWN = CUBEB_
 // thread before fetching, after which it is safe to fetch without holding the
 // mutex because it is only written once per process execution (by the first
 // initialization to complete).  Since the init must have been called on a
 // given thread before fetching the value, it's guaranteed (via the mutex) that
 // sufficient memory barriers have occurred to ensure the correct value is
 // visible on the querying thread/CPU.
 uint32_t sPreferredSampleRate;
 
+// We only support SMPTE layout in cubeb for now. If the value is
+// CUBEB_LAYOUT_UNDEFINED, then it implies that the preferred layout is
+// non-SMPTE format.
+cubeb_channel_layout sPreferredChannelLayout;
+
 } // namespace
 
 extern LazyLogModule gAudioStreamLog;
 
 static const uint32_t CUBEB_NORMAL_LATENCY_MS = 100;
 // Consevative default that can work on all platforms.
 static const uint32_t CUBEB_NORMAL_LATENCY_FRAMES = 1024;
 
@@ -211,16 +216,71 @@ uint32_t PreferredSampleRate()
 {
   if (!InitPreferredSampleRate()) {
     return 44100;
   }
   MOZ_ASSERT(sPreferredSampleRate);
   return sPreferredSampleRate;
 }
 
+bool InitPreferredChannelLayout()
+{
+  StaticMutexAutoLock lock(sMutex);
+  if (sPreferredChannelLayout != 0) {
+    return true;
+  }
+  cubeb* context = GetCubebContextUnlocked();
+  if (!context) {
+    return false;
+  }
+  return cubeb_get_preferred_channel_layout(context,
+                                            &sPreferredChannelLayout) == CUBEB_OK
+         ? true : false;
+}
+
+uint32_t PreferredChannelMap(uint32_t aChannels)
+{
+  // The first element of the following mapping table is channel counts,
+  // and the second one is its bit mask. It will be used in many times,
+  // so we shoule avoid to allocate it in stack, or it will be created
+  // and removed repeatedly. Use static to allocate this local variable
+  // in data space instead of stack.
+  static uint32_t layoutInfo[CUBEB_LAYOUT_MAX][2] = {
+    { 0, 0 },               // CUBEB_LAYOUT_UNDEFINED
+    { 2, MASK_STEREO },     // CUBEB_LAYOUT_DUAL_MONO
+    { 3, MASK_STEREO_LFE }, // CUBEB_LAYOUT_DUAL_MONO_LFE
+    { 1, MASK_MONO },       // CUBEB_LAYOUT_MONO
+    { 2, MASK_MONO_LFE },   // CUBEB_LAYOUT_MONO_LFE
+    { 2, MASK_STEREO },     // CUBEB_LAYOUT_STEREO
+    { 3, MASK_STEREO_LFE }, // CUBEB_LAYOUT_STEREO_LFE
+    { 3, MASK_3F },         // CUBEB_LAYOUT_3F
+    { 4, MASK_3F_LFE },     // CUBEB_LAYOUT_3F_LFE
+    { 3, MASK_2F1 },        // CUBEB_LAYOUT_2F1
+    { 4, MASK_2F1_LFE },    // CUBEB_LAYOUT_2F1_LFE
+    { 4, MASK_3F1 },        // CUBEB_LAYOUT_3F1
+    { 5, MASK_3F1_LFE },    // CUBEB_LAYOUT_3F1_LFE
+    { 4, MASK_2F2 },        // CUBEB_LAYOUT_2F2
+    { 5, MASK_2F2_LFE },    // CUBEB_LAYOUT_2F2_LFE
+    { 5, MASK_3F2 },        // CUBEB_LAYOUT_3F2
+    { 6, MASK_3F2_LFE },    // CUBEB_LAYOUT_3F2_LFE
+    { 7, MASK_3F3R_LFE },   // CUBEB_LAYOUT_3F3R_LFE
+    { 8, MASK_3F4_LFE },    // CUBEB_LAYOUT_3F4_LFE
+  };
+
+  // Use SMPTE default channel map if we can't get preferred layout
+  // or the channel counts of preferred layout is different from input's one
+  if (!InitPreferredChannelLayout()
+      || layoutInfo[sPreferredChannelLayout][0] != aChannels) {
+    AudioConfig::ChannelLayout smpteLayout(aChannels);
+    return smpteLayout.Map();
+  }
+
+  return layoutInfo[sPreferredChannelLayout][1];
+}
+
 void InitBrandName()
 {
   if (sBrandName) {
     return;
   }
   nsXPIDLString brandName;
   nsCOMPtr<nsIStringBundleService> stringBundleService =
     mozilla::services::GetStringBundleService();
--- a/dom/media/CubebUtils.h
+++ b/dom/media/CubebUtils.h
@@ -25,16 +25,19 @@ void InitLibrary();
 void ShutdownLibrary();
 
 // Returns the maximum number of channels supported by the audio hardware.
 uint32_t MaxNumberOfChannels();
 
 // Get the sample rate the hardware/mixer runs at. Thread safe.
 uint32_t PreferredSampleRate();
 
+// Get the bit mask of the connected audio device's preferred layout.
+uint32_t PreferredChannelMap(uint32_t aChannels);
+
 void PrefChanged(const char* aPref, void* aClosure);
 double GetVolumeScale();
 bool GetFirstStream();
 cubeb* GetCubebContext();
 cubeb* GetCubebContextUnlocked();
 void ReportCubebStreamInitFailure(bool aIsFirstStream);
 void ReportCubebBackendUsed();
 uint32_t GetCubebPlaybackLatencyInMilliseconds();
--- a/dom/media/mediasink/DecodedAudioDataSink.cpp
+++ b/dom/media/mediasink/DecodedAudioDataSink.cpp
@@ -192,23 +192,25 @@ DecodedAudioDataSink::SetPlaying(bool aP
   }
   mPlaying = aPlaying;
 }
 
 nsresult
 DecodedAudioDataSink::InitializeAudioStream(const PlaybackParams& aParams)
 {
   mAudioStream = new AudioStream(*this);
+  // When AudioQueue is empty, there is no way to know the channel layout of
+  // the coming audio data, so we use the predefined channel map instead.
+  uint32_t channelMap = mConverter
+                        ? mConverter->OutputConfig().Layout().Map()
+                        : AudioStream::GetPreferredChannelMap(mOutputChannels);
   // The layout map used here is already processed by mConverter with
   // mOutputChannels into SMPTE format, so there is no need to worry if
   // MediaPrefs::MonoAudio() or MediaPrefs::AudioSinkForceStereo() is applied.
-  nsresult rv = mAudioStream->Init(mOutputChannels,
-                                   mConverter->OutputConfig().Layout().Map(),
-                                   mOutputRate,
-                                   mChannel);
+  nsresult rv = mAudioStream->Init(mOutputChannels, channelMap, mOutputRate, mChannel);
   if (NS_FAILED(rv)) {
     mAudioStream->Shutdown();
     mAudioStream = nullptr;
     return rv;
   }
 
   // Set playback params before calling Start() so they can take effect
   // as soon as the 1st DataCallback of the AudioStream fires.