void AudioSink::NotifyAudioNeeded() { MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn(), "Not called from the owner's thread"); // Always ensure we have two processed frames pending to allow for processing // latency. while (mAudioQueue.GetSize() && (mAudioQueue.IsFinished() || mProcessedQueueLength < LOW_AUDIO_USECS || mProcessedQueue.GetSize() < 2)) { RefPtr<AudioData> data = mAudioQueue.PopFront(); // Ignore the element with 0 frames and try next. if (!data->mFrames) { continue; } if (!mConverter || (data->mRate != mConverter->InputConfig().Rate() || data->mChannels != mConverter->InputConfig().Channels())) { SINK_LOG_V("Audio format changed from %u@%uHz to %u@%uHz", mConverter? mConverter->InputConfig().Channels() : 0, mConverter ? mConverter->InputConfig().Rate() : 0, data->mChannels, data->mRate); DrainConverter(); // mFramesParsed indicates the current playtime in frames at the current // input sampling rate. Recalculate it per the new sampling rate. if (mFramesParsed) { // We minimize overflow. uint32_t oldRate = mConverter->InputConfig().Rate(); uint32_t newRate = data->mRate; CheckedInt64 result = SaferMultDiv(mFramesParsed, newRate, oldRate); if (!result.isValid()) { NS_WARNING("Int overflow in AudioSink"); mErrored = true; return; } mFramesParsed = result.value(); } mConverter = MakeUnique<AudioConverter>( AudioConfig(data->mChannels, data->mRate), AudioConfig(mOutputChannels, mOutputRate)); } // See if there's a gap in the audio. If there is, push silence into the // audio hardware, so we can play across the gap. // Calculate the timestamp of the next chunk of audio in numbers of // samples. CheckedInt64 sampleTime = TimeUnitToFrames(data->mTime - mStartTime, data->mRate); // Calculate the number of frames that have been pushed onto the audio hardware. CheckedInt64 missingFrames = sampleTime - mFramesParsed; if (!missingFrames.isValid()) { NS_WARNING("Int overflow in AudioSink"); mErrored = true; return; } if (missingFrames.value() > AUDIO_FUZZ_FRAMES) { // The next audio packet begins some time after the end of the last packet // we pushed to the audio hardware. We must push silence into the audio // hardware so that the next audio packet begins playback at the correct // time. missingFrames = std::min<int64_t>(INT32_MAX, missingFrames.value()); mFramesParsed += missingFrames.value(); RefPtr<AudioData> silenceData; AlignedAudioBuffer silenceBuffer(missingFrames.value() * data->mChannels); if (!silenceBuffer) { NS_WARNING("OOM in AudioSink"); mErrored = true; return; } if (mConverter->InputConfig() != mConverter->OutputConfig()) { AlignedAudioBuffer convertedData = mConverter->Process(AudioSampleBuffer(Move(silenceBuffer))).Forget(); silenceData = CreateAudioFromBuffer(Move(convertedData), data); } else { silenceData = CreateAudioFromBuffer(Move(silenceBuffer), data); } PushProcessedAudio(silenceData); } mLastEndTime = data->GetEndTime(); mFramesParsed += data->mFrames; if (mConverter->InputConfig() != mConverter->OutputConfig()) { // We must ensure that the size in the buffer contains exactly the number // of frames, in case one of the audio producer over allocated the buffer. AlignedAudioBuffer buffer(Move(data->mAudioData)); buffer.SetLength(size_t(data->mFrames) * data->mChannels); AlignedAudioBuffer convertedData = mConverter->Process(AudioSampleBuffer(Move(buffer))).Forget(); data = CreateAudioFromBuffer(Move(convertedData), data); } if (PushProcessedAudio(data)) { mLastProcessedPacket = Some(data); } } if (mAudioQueue.IsFinished()) { // We have reached the end of the data, drain the resampler. DrainConverter(); mProcessedQueue.Finish(); } }
// Converts from microseconds to number of audio frames, given the specified // audio rate. CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate) { return SaferMultDiv(aUsecs, aRate, USECS_PER_S); }
// Converts from number of audio frames to microseconds, given the specified // audio rate. CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate) { return SaferMultDiv(aFrames, USECS_PER_S, aRate); }