Bug 1262753: P6. Perform downmixing in DecodeAudioDataSink. r=kinetik

Rather than performing the audio processing a few frames at a time, we perform the operation when popping the audio data block.
The only downmixing task left to AudioStream is to handle the force mono preference.

Resampling is currently disabled.

MozReview-Commit-ID: GNQA1h0xcp7

--HG--
extra : rebase_source : c58aa4514a0dc4de06be6dc1da6a598742caa262
This commit is contained in:
Jean-Yves Avenard 2016-04-11 21:16:17 +10:00
parent b382680a6a
commit 4132e3555b
3 changed files with 46 additions and 17 deletions

View File

@ -331,7 +331,7 @@ AudioStream::Init(uint32_t aNumChannels, uint32_t aRate,
("%s channels: %d, rate: %d for %p", __FUNCTION__, aNumChannels, aRate, this));
mInRate = mOutRate = aRate;
mChannels = aNumChannels;
mOutChannels = (aNumChannels > 2) ? 2 : aNumChannels;
mOutChannels = mIsMonoAudioEnabled ? 1 : aNumChannels;
mDumpFile = OpenDumpFile(this);
@ -353,9 +353,11 @@ AudioStream::Init(uint32_t aNumChannels, uint32_t aRate,
params.format = ToCubebFormat<AUDIO_OUTPUT_FORMAT>::value;
mAudioClock.Init();
AudioConfig inConfig(mChannels, mInRate);
AudioConfig outConfig(mOutChannels, mOutRate);
mAudioConverter = MakeUnique<AudioConverter>(inConfig, outConfig);
if (mIsMonoAudioEnabled) {
AudioConfig inConfig(mChannels, mInRate);
AudioConfig outConfig(mOutChannels, mOutRate);
mAudioConverter = MakeUnique<AudioConverter>(inConfig, outConfig);
}
return OpenCubeb(params);
}
@ -561,15 +563,10 @@ AudioStream::Downmix(Chunk* aChunk)
return false;
}
if (aChunk->Channels() > 2) {
MOZ_ASSERT(mAudioConverter);
if (mAudioConverter) {
mAudioConverter->Process(aChunk->GetWritable(), aChunk->Frames());
}
if (aChunk->Channels() >= 2 && mIsMonoAudioEnabled) {
DownmixStereoToMono(aChunk->GetWritable(), aChunk->Frames());
}
return true;
}

View File

@ -8,6 +8,7 @@
#include "MediaQueue.h"
#include "DecodedAudioDataSink.h"
#include "VideoUtils.h"
#include "AudioConverter.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/DebugOnly.h"
@ -39,6 +40,9 @@ DecodedAudioDataSink::DecodedAudioDataSink(MediaQueue<MediaData>& aAudioQueue,
, mChannel(aChannel)
, mPlaying(true)
, mPlaybackComplete(false)
, mConverter(MakeUnique<AudioConverter>(
AudioConfig(mInfo.mChannels, mInfo.mRate),
AudioConfig(mInfo.mChannels > 2 ? 2 : mInfo.mChannels, mInfo.mRate)))
{
}
@ -136,7 +140,9 @@ nsresult
DecodedAudioDataSink::InitializeAudioStream(const PlaybackParams& aParams)
{
mAudioStream = new AudioStream(*this);
nsresult rv = mAudioStream->Init(mInfo.mChannels, mInfo.mRate, mChannel);
nsresult rv = mAudioStream->Init(mConverter->OutputConfig().Channels(),
mConverter->OutputConfig().Rate(),
mChannel);
if (NS_FAILED(rv)) {
mAudioStream->Shutdown();
mAudioStream = nullptr;
@ -156,7 +162,8 @@ DecodedAudioDataSink::InitializeAudioStream(const PlaybackParams& aParams)
int64_t
DecodedAudioDataSink::GetEndTime() const
{
CheckedInt64 playedUsecs = FramesToUsecs(mWritten, mInfo.mRate) + mStartTime;
CheckedInt64 playedUsecs =
FramesToUsecs(mWritten, mConverter->OutputConfig().Rate()) + mStartTime;
if (!playedUsecs.isValid()) {
NS_WARNING("Int overflow calculating audio end time");
return -1;
@ -231,9 +238,11 @@ DecodedAudioDataSink::PopFrames(uint32_t aFrames)
// audio hardware, so we can play across the gap.
// Calculate the timestamp of the next chunk of audio in numbers of
// samples.
CheckedInt64 sampleTime = UsecsToFrames(AudioQueue().PeekFront()->mTime, mInfo.mRate);
CheckedInt64 sampleTime = UsecsToFrames(AudioQueue().PeekFront()->mTime,
mConverter->OutputConfig().Rate());
// Calculate the number of frames that have been pushed onto the audio hardware.
CheckedInt64 playedFrames = UsecsToFrames(mStartTime, mInfo.mRate) +
CheckedInt64 playedFrames = UsecsToFrames(mStartTime,
mConverter->OutputConfig().Rate()) +
static_cast<int64_t>(mWritten);
CheckedInt64 missingFrames = sampleTime - playedFrames;
@ -243,6 +252,9 @@ DecodedAudioDataSink::PopFrames(uint32_t aFrames)
return MakeUnique<Chunk>();
}
const uint32_t rate = mConverter->OutputConfig().Rate();
const uint32_t channels = mConverter->OutputConfig().Channels();
if (missingFrames.value() > AUDIO_FUZZ_FRAMES) {
// The next audio chunk begins some time after the end of the last chunk
// we pushed to the audio hardware. We must push silence into the audio
@ -251,10 +263,26 @@ DecodedAudioDataSink::PopFrames(uint32_t aFrames)
missingFrames = std::min<int64_t>(UINT32_MAX, missingFrames.value());
auto framesToPop = std::min<uint32_t>(missingFrames.value(), aFrames);
mWritten += framesToPop;
return MakeUnique<SilentChunk>(framesToPop, mInfo.mChannels, mInfo.mRate);
return MakeUnique<SilentChunk>(framesToPop, channels, rate);
}
RefPtr<AudioData> data =
dont_AddRef(AudioQueue().PopFront().take()->As<AudioData>());
if (mConverter->InputConfig() != mConverter->OutputConfig()) {
AlignedAudioBuffer convertedData =
mConverter->Process(AudioSampleBuffer(Move(data->mAudioData))).Forget();
mCurrentData =
new AudioData(data->mOffset,
data->mTime,
data->mDuration,
convertedData.Length() / channels,
Move(convertedData),
channels,
rate);
} else {
mCurrentData = Move(data);
}
mCurrentData = dont_AddRef(AudioQueue().PopFront().take()->As<AudioData>());
mCursor = MakeUnique<AudioBufferCursor>(mCurrentData->mAudioData.get(),
mCurrentData->mChannels,
mCurrentData->mFrames);

View File

@ -21,6 +21,8 @@
namespace mozilla {
class AudioConverter;
namespace media {
class DecodedAudioDataSink : public AudioSink,
@ -97,13 +99,15 @@ private:
*/
// The AudioData at which AudioStream::DataSource is reading.
RefPtr<AudioData> mCurrentData;
// Keep track of the read positoin of mCurrentData.
// Keep track of the read position of mCurrentData.
UniquePtr<AudioBufferCursor> mCursor;
// True if there is any error in processing audio data like overflow.
bool mErrored = false;
// Set on the callback thread of cubeb once the stream has drained.
Atomic<bool> mPlaybackComplete;
UniquePtr<AudioConverter> mConverter;
};
} // namespace media