Bug 1321502 - part 2: Use preferred layout for initializing cubeb when audio queue is empty; r=jya
MozReview-Commit-ID: BDEb8IxuJRn
--- 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
@@ -190,23 +190,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.