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
--- 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)