Bug 1264199: P6. Drain resampler when changing format or reaching the end. r=kinetik

MozReview-Commit-ID: KqcB0FYxNtC

--HG--
extra : rebase_source : 777c5b85e8999a54a8b0b5c9da999d41e746daa2
This commit is contained in:
Jean-Yves Avenard 2016-04-14 15:44:02 +10:00
Родитель 7e4703cb30
Коммит 730f304d31
4 изменённых файлов: 114 добавлений и 23 удалений

Просмотреть файл

@ -33,19 +33,7 @@ AudioConverter::AudioConverter(const AudioConfig& aIn, const AudioConfig& aOut)
MOZ_DIAGNOSTIC_ASSERT(aOut.Interleaved(), "planar audio format not supported"); MOZ_DIAGNOSTIC_ASSERT(aOut.Interleaved(), "planar audio format not supported");
mIn.Layout().MappingTable(mOut.Layout(), mChannelOrderMap); mIn.Layout().MappingTable(mOut.Layout(), mChannelOrderMap);
if (aIn.Rate() != aOut.Rate()) { if (aIn.Rate() != aOut.Rate()) {
int error; RecreateResampler();
mResampler = speex_resampler_init(aOut.Channels(),
aIn.Rate(),
aOut.Rate(),
SPEEX_RESAMPLER_QUALITY_DEFAULT,
&error);
if (error == RESAMPLER_ERR_SUCCESS) {
speex_resampler_skip_zeros(mResampler);
} else {
NS_WARNING("Failed to initialize resampler.");
mResampler = nullptr;
}
} }
} }
@ -282,6 +270,46 @@ AudioConverter::ResampleAudio(void* aOut, const void* aIn, size_t aFrames)
return outframes; return outframes;
} }
void
AudioConverter::RecreateResampler()
{
if (mResampler) {
speex_resampler_destroy(mResampler);
}
int error;
mResampler = speex_resampler_init(mOut.Channels(),
mIn.Rate(),
mOut.Rate(),
SPEEX_RESAMPLER_QUALITY_DEFAULT,
&error);
if (error == RESAMPLER_ERR_SUCCESS) {
speex_resampler_skip_zeros(mResampler);
} else {
NS_WARNING("Failed to initialize resampler.");
mResampler = nullptr;
}
}
size_t
AudioConverter::DrainResampler(void* aOut)
{
if (!mResampler) {
return 0;
}
int frames = speex_resampler_get_input_latency(mResampler);
AlignedByteBuffer buffer(FramesOutToSamples(frames) *
AudioConfig::SampleSize(mOut.Format()));
if (!buffer) {
// OOM
return 0;
}
frames = ResampleAudio(aOut, buffer.Data(), frames);
// Tore down the resampler as it's easier than handling follow-up.
RecreateResampler();
return frames;
}
size_t size_t
AudioConverter::UpmixAudio(void* aOut, const void* aIn, size_t aFrames) const AudioConverter::UpmixAudio(void* aOut, const void* aIn, size_t aFrames) const
{ {
@ -327,7 +355,13 @@ AudioConverter::UpmixAudio(void* aOut, const void* aIn, size_t aFrames) const
size_t size_t
AudioConverter::ResampleRecipientFrames(size_t aFrames) const AudioConverter::ResampleRecipientFrames(size_t aFrames) const
{ {
return (uint64_t)aFrames * mOut.Rate() / mIn.Rate() + 1; if (!aFrames && mIn.Rate() != mOut.Rate()) {
// The resampler will be drained, account for frames currently buffered
// in the resampler.
return speex_resampler_get_output_latency(mResampler);
} else {
return (uint64_t)aFrames * mOut.Rate() / mIn.Rate() + 1;
}
} }
size_t size_t

Просмотреть файл

@ -123,6 +123,8 @@ public:
// Convert the AudioDataBuffer. // Convert the AudioDataBuffer.
// Conversion will be done in place if possible. Otherwise a new buffer will // Conversion will be done in place if possible. Otherwise a new buffer will
// be returned. // be returned.
// Providing an empty buffer and resampling is expected, the resampler
// will be drained.
template <AudioConfig::SampleFormat Format, typename Value> template <AudioConfig::SampleFormat Format, typename Value>
AudioDataBuffer<Format, Value> Process(AudioDataBuffer<Format, Value>&& aBuffer) AudioDataBuffer<Format, Value> Process(AudioDataBuffer<Format, Value>&& aBuffer)
{ {
@ -152,7 +154,7 @@ public:
return AudioDataBuffer<Format, Value>(Move(temp1)); return AudioDataBuffer<Format, Value>(Move(temp1));
} }
frames = ProcessInternal(temp1.Data(), aBuffer.Data(), frames); frames = ProcessInternal(temp1.Data(), aBuffer.Data(), frames);
if (!frames || mIn.Rate() == mOut.Rate()) { if (mIn.Rate() == mOut.Rate()) {
temp1.SetLength(FramesOutToSamples(frames)); temp1.SetLength(FramesOutToSamples(frames));
return AudioDataBuffer<Format, Value>(Move(temp1)); return AudioDataBuffer<Format, Value>(Move(temp1));
} }
@ -161,13 +163,17 @@ public:
// If we are downsampling we can re-use it. // If we are downsampling we can re-use it.
AlignedBuffer<Value>* outputBuffer = &temp1; AlignedBuffer<Value>* outputBuffer = &temp1;
AlignedBuffer<Value> temp2; AlignedBuffer<Value> temp2;
if (mOut.Rate() > mIn.Rate()) { if (!frames || mOut.Rate() > mIn.Rate()) {
// We are upsampling, we can't work in place. Allocate another temporary // We are upsampling or about to drain, we can't work in place.
// buffer where the upsampling will occur. // Allocate another temporary buffer where the upsampling will occur.
temp2.SetLength(FramesOutToSamples(ResampleRecipientFrames(frames))); temp2.SetLength(FramesOutToSamples(ResampleRecipientFrames(frames)));
outputBuffer = &temp2; outputBuffer = &temp2;
} }
frames = ResampleAudio(outputBuffer->Data(), temp1.Data(), frames); if (!frames) {
frames = DrainResampler(outputBuffer->Data());
} else {
frames = ResampleAudio(outputBuffer->Data(), temp1.Data(), frames);
}
outputBuffer->SetLength(FramesOutToSamples(frames)); outputBuffer->SetLength(FramesOutToSamples(frames));
return AudioDataBuffer<Format, Value>(Move(*outputBuffer)); return AudioDataBuffer<Format, Value>(Move(*outputBuffer));
} }
@ -223,6 +229,8 @@ private:
SpeexResamplerState* mResampler; SpeexResamplerState* mResampler;
size_t ResampleAudio(void* aOut, const void* aIn, size_t aFrames); size_t ResampleAudio(void* aOut, const void* aIn, size_t aFrames);
size_t ResampleRecipientFrames(size_t aFrames) const; size_t ResampleRecipientFrames(size_t aFrames) const;
void RecreateResampler();
size_t DrainResampler(void* aOut);
}; };
} // namespace mozilla } // namespace mozilla

Просмотреть файл

@ -321,6 +321,12 @@ DecodedAudioDataSink::NotifyAudioNeeded()
return; return;
} }
if (AudioQueue().IsFinished() && !AudioQueue().GetSize()) {
// We have reached the end of the data, drain the resampler.
DrainConverter();
return;
}
// Always ensure we have two processed frames pending to allow for processing // Always ensure we have two processed frames pending to allow for processing
// latency. // latency.
while (AudioQueue().GetSize() && mProcessedQueue.GetSize() < 2) { while (AudioQueue().GetSize() && mProcessedQueue.GetSize() < 2) {
@ -339,6 +345,8 @@ DecodedAudioDataSink::NotifyAudioNeeded()
mConverter ? mConverter->InputConfig().Rate() : 0, mConverter ? mConverter->InputConfig().Rate() : 0,
data->mChannels, data->mRate); data->mChannels, data->mRate);
DrainConverter();
// mFramesParsed indicates the current playtime in frames at the current // mFramesParsed indicates the current playtime in frames at the current
// input sampling rate. Recalculate it per the new sampling rate. // input sampling rate. Recalculate it per the new sampling rate.
if (mFramesParsed) { if (mFramesParsed) {
@ -385,10 +393,15 @@ DecodedAudioDataSink::NotifyAudioNeeded()
// time. // time.
missingFrames = std::min<int64_t>(INT32_MAX, missingFrames.value()); missingFrames = std::min<int64_t>(INT32_MAX, missingFrames.value());
mFramesParsed += missingFrames.value(); mFramesParsed += missingFrames.value();
AlignedAudioBuffer silenceData(missingFrames.value() * mOutputChannels); // We need to insert silence, first use drained frames if any.
RefPtr<AudioData> silence = CreateAudioFromBuffer(Move(silenceData), data); missingFrames -= DrainConverter(missingFrames.value());
if (silence) { // Insert silence is still needed.
mProcessedQueue.Push(silence); if (missingFrames.value()) {
AlignedAudioBuffer silenceData(missingFrames.value() * mOutputChannels);
RefPtr<AudioData> silence = CreateAudioFromBuffer(Move(silenceData), data);
if (silence) {
mProcessedQueue.Push(silence);
}
} }
} }
@ -404,6 +417,7 @@ DecodedAudioDataSink::NotifyAudioNeeded()
} }
} }
mProcessedQueue.Push(data); mProcessedQueue.Push(data);
mLastProcessedPacket = Some(data);
} }
} }
@ -432,5 +446,36 @@ DecodedAudioDataSink::CreateAudioFromBuffer(AlignedAudioBuffer&& aBuffer,
return data.forget(); return data.forget();
} }
uint32_t
DecodedAudioDataSink::DrainConverter(uint32_t aMaxFrames)
{
MOZ_ASSERT(mProcessingThread->IsCurrentThreadIn());
if (!mConverter || !mLastProcessedPacket) {
// nothing to drain.
return 0;
}
// To drain we simply provide an empty packet to the audio converter.
AlignedAudioBuffer convertedData =
mConverter->Process(AudioSampleBuffer(AlignedAudioBuffer())).Forget();
uint32_t frames = convertedData.Length() / mOutputChannels;
convertedData.SetLength(std::min(frames, aMaxFrames) * mOutputChannels);
// We assume the start time of the drained data is just before the end of the
// previous packet. Ultimately, the start time doesn't really matter, however
// we do not want to trigger the gap detection in PopFrames.
RefPtr<AudioData> data = CreateAudioFromBuffer(Move(convertedData),
mLastProcessedPacket.ref());
mLastProcessedPacket.reset();
if (!data) {
return 0;
}
mProcessedQueue.Push(data);
return data->mFrames;
}
} // namespace media } // namespace media
} // namespace mozilla } // namespace mozilla

Просмотреть файл

@ -115,6 +115,9 @@ private:
void OnAudioPopped(const RefPtr<MediaData>& aSample); void OnAudioPopped(const RefPtr<MediaData>& aSample);
void OnAudioPushed(const RefPtr<MediaData>& aSample); void OnAudioPushed(const RefPtr<MediaData>& aSample);
void NotifyAudioNeeded(); void NotifyAudioNeeded();
// Drain the converter and add the output to the processed audio queue.
// A maximum of aMaxFrames will be added.
uint32_t DrainConverter(uint32_t aMaxFrames = UINT32_MAX);
already_AddRefed<AudioData> CreateAudioFromBuffer(AlignedAudioBuffer&& aBuffer, already_AddRefed<AudioData> CreateAudioFromBuffer(AlignedAudioBuffer&& aBuffer,
AudioData* aReference); AudioData* aReference);
UniquePtr<AudioConverter> mConverter; UniquePtr<AudioConverter> mConverter;
@ -125,6 +128,7 @@ private:
// the input stream. It indicates the time in frames since playback started // the input stream. It indicates the time in frames since playback started
// at the current input framerate. // at the current input framerate.
int64_t mFramesParsed; int64_t mFramesParsed;
Maybe<RefPtr<AudioData>> mLastProcessedPacket;
int64_t mLastEndTime; int64_t mLastEndTime;
// Never modifed after construction. // Never modifed after construction.
uint32_t mOutputRate; uint32_t mOutputRate;