AudioSink.cpp (24417B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "AudioSink.h" 8 9 #include "AudioConverter.h" 10 #include "AudioDeviceInfo.h" 11 #include "MediaQueue.h" 12 #include "Tracing.h" 13 #include "VideoUtils.h" 14 #include "mozilla/CheckedInt.h" 15 #include "mozilla/DebugOnly.h" 16 #include "mozilla/IntegerPrintfMacros.h" 17 #include "mozilla/ProfilerMarkerTypes.h" 18 #include "mozilla/StaticPrefs_dom.h" 19 #include "mozilla/StaticPrefs_media.h" 20 #include "nsPrintfCString.h" 21 22 namespace mozilla { 23 24 mozilla::LazyLogModule gAudioSinkLog("AudioSink"); 25 #define SINK_LOG(msg, ...) \ 26 MOZ_LOG(gAudioSinkLog, LogLevel::Debug, \ 27 ("AudioSink=%p " msg, this, ##__VA_ARGS__)) 28 #define SINK_LOG_V(msg, ...) \ 29 MOZ_LOG(gAudioSinkLog, LogLevel::Verbose, \ 30 ("AudioSink=%p " msg, this, ##__VA_ARGS__)) 31 32 // The amount of audio frames that is used to fuzz rounding errors. 33 static const int64_t AUDIO_FUZZ_FRAMES = 1; 34 35 using media::TimeUnit; 36 37 AudioSink::AudioSink(AbstractThread* aThread, 38 MediaQueue<AudioData>& aAudioQueue, const AudioInfo& aInfo, 39 bool aShouldResistFingerprinting) 40 : mPlaying(true), 41 mWritten(0), 42 mErrored(false), 43 mOwnerThread(aThread), 44 mFramesParsed(0), 45 mOutputRate( 46 DecideAudioPlaybackSampleRate(aInfo, aShouldResistFingerprinting)), 47 mOutputChannels(DecideAudioPlaybackChannels(aInfo)), 48 mAudibilityMonitor( 49 mOutputRate, 50 StaticPrefs::dom_media_silence_duration_for_audibility()), 51 mIsAudioDataAudible(false), 52 mProcessedQueueFinished(false), 53 mAudioQueue(aAudioQueue), 54 mProcessedQueueThresholdMS( 55 StaticPrefs::media_audio_audiosink_threshold_ms()) { 56 // Not much to initialize here if there's no audio. 57 if (!aInfo.IsValid()) { 58 mProcessedSPSCQueue = MakeUnique<SPSCQueue<AudioDataValue>>(0); 59 return; 60 } 61 // Twice the limit that trigger a refill. 62 double capacitySeconds = mProcessedQueueThresholdMS / 1000.f * 2; 63 // Clamp to correct boundaries, and align on the channel count 64 int elementCount = static_cast<int>( 65 std::clamp(capacitySeconds * mOutputChannels * mOutputRate, 0., 66 std::numeric_limits<int>::max() - 1.)); 67 elementCount -= elementCount % mOutputChannels; 68 mProcessedSPSCQueue = MakeUnique<SPSCQueue<AudioDataValue>>(elementCount); 69 SINK_LOG("Ringbuffer has space for %u elements (%lf seconds)", 70 mProcessedSPSCQueue->Capacity(), 71 static_cast<float>(elementCount) / mOutputChannels / mOutputRate); 72 // Determine if the data is likely to be audible when the stream will be 73 // ready, if possible. 74 RefPtr<AudioData> frontPacket = mAudioQueue.PeekFront(); 75 if (frontPacket) { 76 mAudibilityMonitor.ProcessInterleaved(frontPacket->Data(), 77 frontPacket->mChannels); 78 mIsAudioDataAudible = mAudibilityMonitor.RecentlyAudible(); 79 SINK_LOG("New AudioSink -- audio is likely to be %s", 80 mIsAudioDataAudible ? "audible" : "inaudible"); 81 } else { 82 // If no packets are available, consider the audio audible. 83 mIsAudioDataAudible = true; 84 SINK_LOG( 85 "New AudioSink -- no audio packet avaialble, considering the stream " 86 "audible"); 87 } 88 } 89 90 AudioSink::~AudioSink() { 91 // Generally instances of AudioSink should be properly shut down manually. 92 // The only way deleting an AudioSink without shutdown can happen is if the 93 // dispatch back to the MDSM thread after initializing it asynchronously 94 // fails. When that's the case, the stream has been initialized but not 95 // started. Manually shutdown the AudioStream in this case. 96 if (mAudioStream) { 97 mAudioStream->ShutDown(); 98 } 99 } 100 101 nsresult AudioSink::InitializeAudioStream( 102 const RefPtr<AudioDeviceInfo>& aAudioDevice, 103 AudioSink::InitializationType aInitializationType) { 104 if (aInitializationType == AudioSink::InitializationType::UNMUTING) { 105 // Consider the stream to be audible immediately, before initialization 106 // finishes when unmuting, in case initialization takes some time and it 107 // looked audible when the AudioSink was created. 108 mAudibleEvent.Notify(mIsAudioDataAudible); 109 SINK_LOG("InitializeAudioStream (Unmuting) notifying that audio is %s", 110 mIsAudioDataAudible ? "audible" : "inaudible"); 111 } else { 112 // If not unmuting, the audibility event will be dispatched as usual, 113 // inspecting the audio content as it's being played and signaling the 114 // audibility event when a different in state is detected. 115 SINK_LOG("InitializeAudioStream (initial)"); 116 mIsAudioDataAudible = false; 117 } 118 119 // When AudioQueue is empty, there is no way to know the channel layout of 120 // the coming audio data, so we use the predefined channel map instead. 121 AudioConfig::ChannelLayout::ChannelMap channelMap = 122 AudioConfig::ChannelLayout(mOutputChannels).Map(); 123 // The layout map used here is already processed by mConverter with 124 // mOutputChannels into SMPTE format, so there is no need to worry if 125 // StaticPrefs::accessibility_monoaudio_enable() or 126 // StaticPrefs::media_forcestereo_enabled() is applied. 127 MOZ_ASSERT(!mAudioStream); 128 mAudioStream = 129 new AudioStream(*this, mOutputRate, mOutputChannels, channelMap); 130 nsresult rv = mAudioStream->Init(aAudioDevice); 131 if (NS_FAILED(rv)) { 132 mAudioStream->ShutDown(); 133 mAudioStream = nullptr; 134 return rv; 135 } 136 137 return NS_OK; 138 } 139 140 RefPtr<MediaSink::EndedPromise> AudioSink::Start( 141 const PlaybackParams& aParams, const media::TimeUnit& aStartTime) { 142 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); 143 144 // Set playback params before calling Start() so they can take effect 145 // as soon as the 1st DataCallback of the AudioStream fires. 146 mAudioStream->SetVolume(aParams.mVolume); 147 mAudioStream->SetPlaybackRate(aParams.mPlaybackRate); 148 mAudioStream->SetPreservesPitch(aParams.mPreservesPitch); 149 150 mAudioQueueListener = mAudioQueue.PushEvent().Connect( 151 mOwnerThread, this, &AudioSink::OnAudioPushed); 152 mAudioQueueFinishListener = mAudioQueue.FinishEvent().Connect( 153 mOwnerThread, this, &AudioSink::NotifyAudioNeeded); 154 mProcessedQueueListener = 155 mAudioPopped.Connect(mOwnerThread, this, &AudioSink::OnAudioPopped); 156 157 mStartTime = aStartTime; 158 159 // To ensure at least one audio packet will be popped from AudioQueue and 160 // ready to be played. 161 NotifyAudioNeeded(); 162 163 return mAudioStream->Start(); 164 } 165 166 TimeUnit AudioSink::GetPosition() { 167 int64_t tmp; 168 if (mAudioStream && (tmp = mAudioStream->GetPosition()) >= 0) { 169 TimeUnit pos = TimeUnit::FromMicroseconds(tmp); 170 NS_ASSERTION(pos >= mLastGoodPosition, 171 "AudioStream position shouldn't go backward"); 172 TimeUnit tmp = mStartTime + pos; 173 if (!tmp.IsValid()) { 174 mErrored = true; 175 return mStartTime + mLastGoodPosition; 176 } 177 // Update the last good position when we got a good one. 178 if (pos >= mLastGoodPosition) { 179 mLastGoodPosition = pos; 180 } 181 } 182 183 return mStartTime + mLastGoodPosition; 184 } 185 186 bool AudioSink::HasUnplayedFrames() { 187 // Experimentation suggests that GetPositionInFrames() is zero-indexed, 188 // so we need to add 1 here before comparing it to mWritten. 189 return mProcessedSPSCQueue->AvailableRead() || 190 (mAudioStream && mAudioStream->GetPositionInFrames() + 1 < mWritten); 191 } 192 193 TimeUnit AudioSink::UnplayedDuration() const { 194 return TimeUnit::FromMicroseconds(AudioQueuedInRingBufferMS()); 195 } 196 197 void AudioSink::ReenqueueUnplayedAudioDataIfNeeded() { 198 // This is OK: the AudioStream has been shut down. ShutDown guarantees that 199 // the audio callback thread won't call back again. 200 mProcessedSPSCQueue->ResetConsumerThreadId(); 201 202 // construct an AudioData 203 int sampleInRingbuffer = mProcessedSPSCQueue->AvailableRead(); 204 205 if (!sampleInRingbuffer) { 206 return; 207 } 208 209 uint32_t channelCount; 210 uint32_t rate; 211 if (mConverter) { 212 channelCount = mConverter->OutputConfig().Channels(); 213 rate = mConverter->OutputConfig().Rate(); 214 } else { 215 channelCount = mOutputChannels; 216 rate = mOutputRate; 217 } 218 219 uint32_t framesRemaining = sampleInRingbuffer / channelCount; 220 221 nsTArray<AlignedAudioBuffer> packetsToReenqueue; 222 RefPtr<AudioData> frontPacket = mAudioQueue.PeekFront(); 223 uint32_t offset; 224 TimeUnit time; 225 uint32_t typicalPacketFrameCount; 226 // Extrapolate mOffset, mTime from the front of the queue 227 // We can't really find a good value for `mOffset`, so we take what we have 228 // at the front of the queue. 229 // For `mTime`, assume there hasn't been a discontinuity recently. 230 if (!frontPacket) { 231 // We do our best here, but it's not going to be perfect. 232 typicalPacketFrameCount = 1024; // typical for e.g. AAC 233 offset = 0; 234 time = GetPosition(); 235 } else { 236 typicalPacketFrameCount = frontPacket->Frames(); 237 offset = frontPacket->mOffset; 238 time = frontPacket->mTime; 239 } 240 241 // Extract all audio data from the ring buffer, we can only read the data from 242 // the most recent, so we reenqueue the data, packetized, in a temporary 243 // array. 244 while (framesRemaining) { 245 uint32_t packetFrameCount = 246 std::min(framesRemaining, typicalPacketFrameCount); 247 framesRemaining -= packetFrameCount; 248 249 int packetSampleCount = packetFrameCount * channelCount; 250 AlignedAudioBuffer packetData(packetSampleCount); 251 DebugOnly<int> samplesRead = 252 mProcessedSPSCQueue->Dequeue(packetData.Data(), packetSampleCount); 253 MOZ_ASSERT(samplesRead == packetSampleCount); 254 255 packetsToReenqueue.AppendElement(packetData); 256 } 257 // Reenqueue in the audio queue in correct order in the audio queue, starting 258 // with the end of the temporary array. 259 while (!packetsToReenqueue.IsEmpty()) { 260 auto packetData = packetsToReenqueue.PopLastElement(); 261 uint32_t packetFrameCount = packetData.Length() / channelCount; 262 auto duration = TimeUnit(packetFrameCount, rate); 263 if (!duration.IsValid()) { 264 NS_WARNING("Int overflow in AudioSink"); 265 mErrored = true; 266 return; 267 } 268 time -= duration; 269 RefPtr<AudioData> packet = 270 new AudioData(offset, time, std::move(packetData), channelCount, rate); 271 MOZ_DIAGNOSTIC_ASSERT(duration == packet->mDuration, "must be equal"); 272 273 SINK_LOG( 274 "Muting: Pushing back %u frames (%lfms) from the ring buffer back into " 275 "the audio queue at pts %lf", 276 packetFrameCount, 1000 * static_cast<float>(packetFrameCount) / rate, 277 time.ToSeconds()); 278 // The audio data's timestamp would be adjusted already if we're in looping, 279 // so we don't want to adjust them again. 280 mAudioQueue.PushFront(packet, 281 MediaQueue<AudioData>::TimestampAdjustment::Disable); 282 } 283 } 284 285 void AudioSink::ShutDown() { 286 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); 287 288 mAudioQueueListener.DisconnectIfExists(); 289 mAudioQueueFinishListener.DisconnectIfExists(); 290 mProcessedQueueListener.DisconnectIfExists(); 291 292 if (mAudioStream) { 293 mAudioStream->ShutDown(); 294 mAudioStream = nullptr; 295 ReenqueueUnplayedAudioDataIfNeeded(); 296 } 297 mProcessedQueueFinished = true; 298 } 299 300 void AudioSink::SetVolume(double aVolume) { 301 if (mAudioStream) { 302 mAudioStream->SetVolume(aVolume); 303 } 304 } 305 306 void AudioSink::SetStreamName(const nsAString& aStreamName) { 307 if (mAudioStream) { 308 mAudioStream->SetStreamName(aStreamName); 309 } 310 } 311 312 void AudioSink::SetPlaybackRate(double aPlaybackRate) { 313 MOZ_ASSERT(aPlaybackRate != 0, 314 "Don't set the playbackRate to 0 on AudioStream"); 315 if (mAudioStream) { 316 mAudioStream->SetPlaybackRate(aPlaybackRate); 317 } 318 } 319 320 void AudioSink::SetPreservesPitch(bool aPreservesPitch) { 321 if (mAudioStream) { 322 mAudioStream->SetPreservesPitch(aPreservesPitch); 323 } 324 } 325 326 void AudioSink::SetPlaying(bool aPlaying) { 327 if (!mAudioStream || mAudioStream->IsPlaybackCompleted() || 328 mPlaying == aPlaying) { 329 return; 330 } 331 // pause/resume AudioStream as necessary. 332 if (!aPlaying) { 333 mAudioStream->Pause(); 334 } else if (aPlaying) { 335 mAudioStream->Resume(); 336 } 337 mPlaying = aPlaying; 338 } 339 340 TimeUnit AudioSink::GetEndTime() const { 341 uint64_t written = mWritten; 342 TimeUnit played = media::TimeUnit(written, mOutputRate) + mStartTime; 343 if (!played.IsValid()) { 344 NS_WARNING("Int overflow calculating audio end time"); 345 return TimeUnit::Zero(); 346 } 347 // As we may be resampling, rounding errors may occur. Ensure we never get 348 // past the original end time. 349 return std::min(mLastEndTime, played); 350 } 351 352 uint32_t AudioSink::PopFrames(AudioDataValue* aBuffer, uint32_t aFrames, 353 bool aAudioThreadChanged) { 354 // This is safe, because we have the guarantee, by the OS, that audio 355 // callbacks are never called concurrently. Audio thread changes can only 356 // happen when not using cubeb remoting, and often when changing audio device 357 // at the system level. 358 if (aAudioThreadChanged) { 359 mProcessedSPSCQueue->ResetConsumerThreadId(); 360 } 361 362 TRACE_COMMENT("AudioSink::PopFrames", "%u frames (ringbuffer: %u/%u)", 363 aFrames, SampleToFrame(mProcessedSPSCQueue->AvailableRead()), 364 SampleToFrame(mProcessedSPSCQueue->Capacity())); 365 366 const int samplesToPop = static_cast<int>(aFrames * mOutputChannels); 367 const int samplesRead = mProcessedSPSCQueue->Dequeue(aBuffer, samplesToPop); 368 MOZ_ASSERT(samplesRead % mOutputChannels == 0); 369 mWritten += SampleToFrame(samplesRead); 370 if (samplesRead != samplesToPop) { 371 if (Ended()) { 372 SINK_LOG("Last PopFrames -- Source ended."); 373 } else { 374 NS_WARNING("Underrun when popping samples from audiosink ring buffer."); 375 TRACE_COMMENT("AudioSink::PopFrames", "Underrun %u frames missing", 376 SampleToFrame(samplesToPop - samplesRead)); 377 } 378 // silence the rest 379 PodZero(aBuffer + samplesRead, samplesToPop - samplesRead); 380 } 381 382 mAudioPopped.Notify(); 383 384 SINK_LOG_V("Popping %u frames. Remaining in ringbuffer %u / %u\n", aFrames, 385 SampleToFrame(mProcessedSPSCQueue->AvailableRead()), 386 SampleToFrame(mProcessedSPSCQueue->Capacity())); 387 CheckIsAudible(Span(aBuffer, samplesRead), mOutputChannels); 388 389 return SampleToFrame(samplesRead); 390 } 391 392 bool AudioSink::Ended() const { 393 // Return true when error encountered so AudioStream can start draining. 394 // Both atomic so we don't need locking 395 return mProcessedQueueFinished || mErrored; 396 } 397 398 void AudioSink::CheckIsAudible(const Span<AudioDataValue>& aInterleaved, 399 size_t aChannel) { 400 mAudibilityMonitor.ProcessInterleaved(aInterleaved, aChannel); 401 bool isAudible = mAudibilityMonitor.RecentlyAudible(); 402 403 if (isAudible != mIsAudioDataAudible) { 404 mIsAudioDataAudible = isAudible; 405 SINK_LOG("Notifying that audio is now %s", 406 mIsAudioDataAudible ? "audible" : "inaudible"); 407 mAudibleEvent.Notify(mIsAudioDataAudible); 408 } 409 } 410 411 void AudioSink::OnAudioPopped() { 412 SINK_LOG_V("AudioStream has used an audio packet."); 413 NotifyAudioNeeded(); 414 } 415 416 void AudioSink::OnAudioPushed(const RefPtr<AudioData>& aSample) { 417 SINK_LOG_V("One new audio packet available."); 418 NotifyAudioNeeded(); 419 } 420 421 uint32_t AudioSink::AudioQueuedInRingBufferMS() const { 422 return static_cast<uint32_t>( 423 1000 * SampleToFrame(mProcessedSPSCQueue->AvailableRead()) / mOutputRate); 424 } 425 426 uint32_t AudioSink::SampleToFrame(uint32_t aSamples) const { 427 return aSamples / mOutputChannels; 428 } 429 430 void AudioSink::NotifyAudioNeeded() { 431 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn(), 432 "Not called from the owner's thread"); 433 434 while (mAudioQueue.GetSize() && 435 AudioQueuedInRingBufferMS() < 436 static_cast<uint32_t>(mProcessedQueueThresholdMS)) { 437 // Check if there's room in our ring buffer. 438 if (mAudioQueue.PeekFront()->Frames() > 439 SampleToFrame(mProcessedSPSCQueue->AvailableWrite())) { 440 SINK_LOG_V("Can't push %u frames. In ringbuffer %u / %u\n", 441 mAudioQueue.PeekFront()->Frames(), 442 SampleToFrame(mProcessedSPSCQueue->AvailableRead()), 443 SampleToFrame(mProcessedSPSCQueue->Capacity())); 444 return; 445 } 446 SINK_LOG_V("Pushing %u frames. In ringbuffer %u / %u\n", 447 mAudioQueue.PeekFront()->Frames(), 448 SampleToFrame(mProcessedSPSCQueue->AvailableRead()), 449 SampleToFrame(mProcessedSPSCQueue->Capacity())); 450 RefPtr<AudioData> data = mAudioQueue.PopFront(); 451 452 // Ignore the element with 0 frames and try next. 453 if (!data->Frames()) { 454 continue; 455 } 456 457 if (!mConverter || 458 (data->mRate != mConverter->InputConfig().Rate() || 459 data->mChannels != mConverter->InputConfig().Channels())) { 460 SINK_LOG_V("Audio format changed from %u@%uHz to %u@%uHz", 461 mConverter ? mConverter->InputConfig().Channels() : 0, 462 mConverter ? mConverter->InputConfig().Rate() : 0, 463 data->mChannels, data->mRate); 464 465 DrainConverter(SampleToFrame(mProcessedSPSCQueue->AvailableWrite())); 466 467 // mFramesParsed indicates the current playtime in frames at the current 468 // input sampling rate. Recalculate it per the new sampling rate. 469 if (mFramesParsed) { 470 // We minimize overflow. 471 uint32_t oldRate = mConverter->InputConfig().Rate(); 472 uint32_t newRate = data->mRate; 473 CheckedInt64 result = SaferMultDiv(mFramesParsed, newRate, oldRate); 474 if (!result.isValid()) { 475 NS_WARNING("Int overflow in AudioSink"); 476 mErrored = true; 477 return; 478 } 479 mFramesParsed = result.value(); 480 } 481 482 const AudioConfig::ChannelLayout inputLayout = 483 data->mChannelMap 484 ? AudioConfig::ChannelLayout::SMPTEDefault(data->mChannelMap) 485 : AudioConfig::ChannelLayout(data->mChannels); 486 const AudioConfig::ChannelLayout outputLayout = 487 mOutputChannels == data->mChannels 488 ? inputLayout 489 : AudioConfig::ChannelLayout(mOutputChannels); 490 AudioConfig inConfig = 491 AudioConfig(inputLayout, data->mChannels, data->mRate); 492 AudioConfig outConfig = 493 AudioConfig(outputLayout, mOutputChannels, mOutputRate); 494 if (!AudioConverter::CanConvert(inConfig, outConfig)) { 495 mErrored = true; 496 return; 497 } 498 mConverter = MakeUnique<AudioConverter>(inConfig, outConfig); 499 } 500 501 // See if there's a gap in the audio. If there is, push silence into the 502 // audio hardware, so we can play across the gap. 503 // Calculate the timestamp of the next chunk of audio in numbers of 504 // samples. 505 CheckedInt64 sampleTime = 506 TimeUnitToFrames(data->mTime - mStartTime, data->mRate); 507 // Calculate the number of frames that have been pushed onto the audio 508 // hardware. 509 CheckedInt64 missingFrames = sampleTime - mFramesParsed; 510 511 if (!missingFrames.isValid() || !sampleTime.isValid()) { 512 NS_WARNING("Int overflow in AudioSink"); 513 mErrored = true; 514 return; 515 } 516 517 if (missingFrames.value() > AUDIO_FUZZ_FRAMES) { 518 // The next audio packet begins some time after the end of the last packet 519 // we pushed to the audio hardware. We must push silence into the audio 520 // hardware so that the next audio packet begins playback at the correct 521 // time. But don't push more than the ring buffer can receive. 522 SINK_LOG("Sample time %" PRId64 " > frames parsed %" PRId64, 523 sampleTime.value(), mFramesParsed); 524 525 missingFrames = std::min<int64_t>( 526 std::min<int64_t>(INT32_MAX, missingFrames.value()), 527 SampleToFrame(mProcessedSPSCQueue->AvailableWrite())); 528 mFramesParsed += missingFrames.value(); 529 530 SINK_LOG("Gap in the audio input, push %" PRId64 " frames of silence", 531 missingFrames.value()); 532 533 RefPtr<AudioData> silenceData; 534 AlignedAudioBuffer silenceBuffer(missingFrames.value() * data->mChannels); 535 if (!silenceBuffer) { 536 NS_WARNING("OOM in AudioSink"); 537 mErrored = true; 538 return; 539 } 540 if (mConverter->InputConfig() != mConverter->OutputConfig()) { 541 AlignedAudioBuffer convertedData = 542 mConverter->Process(AudioSampleBuffer(std::move(silenceBuffer))) 543 .Forget(); 544 silenceData = CreateAudioFromBuffer(std::move(convertedData), data); 545 } else { 546 silenceData = CreateAudioFromBuffer(std::move(silenceBuffer), data); 547 } 548 TRACE("Pushing silence"); 549 PushProcessedAudio(silenceData); 550 } 551 552 mLastEndTime = data->GetEndTime(); 553 mFramesParsed += data->Frames(); 554 555 if (mConverter->InputConfig() != mConverter->OutputConfig()) { 556 AlignedAudioBuffer buffer(data->MoveableData()); 557 AlignedAudioBuffer convertedData = 558 mConverter->Process(AudioSampleBuffer(std::move(buffer))).Forget(); 559 data = CreateAudioFromBuffer(std::move(convertedData), data); 560 } 561 if (PushProcessedAudio(data)) { 562 mLastProcessedPacket = Some(data); 563 } 564 } 565 566 if (mAudioQueue.IsFinished() && mAudioQueue.GetSize() == 0) { 567 // We have reached the end of the data, drain the resampler. 568 DrainConverter(SampleToFrame(mProcessedSPSCQueue->AvailableWrite())); 569 mProcessedQueueFinished = true; 570 } 571 } 572 573 uint32_t AudioSink::PushProcessedAudio(AudioData* aData) { 574 if (!aData || !aData->Frames()) { 575 return 0; 576 } 577 int framesToEnqueue = static_cast<int>(aData->Frames() * aData->mChannels); 578 TRACE_COMMENT("AudioSink::PushProcessedAudio", "%u frames (%u/%u)", 579 framesToEnqueue, 580 SampleToFrame(mProcessedSPSCQueue->AvailableWrite()), 581 SampleToFrame(mProcessedSPSCQueue->Capacity())); 582 DebugOnly<int> rv = 583 mProcessedSPSCQueue->Enqueue(aData->Data().Elements(), framesToEnqueue); 584 NS_WARNING_ASSERTION( 585 rv == static_cast<int>(aData->Frames() * aData->mChannels), 586 "AudioSink ring buffer over-run, can't push new data"); 587 return aData->Frames(); 588 } 589 590 already_AddRefed<AudioData> AudioSink::CreateAudioFromBuffer( 591 AlignedAudioBuffer&& aBuffer, AudioData* aReference) { 592 uint32_t frames = SampleToFrame(aBuffer.Length()); 593 if (!frames) { 594 return nullptr; 595 } 596 auto duration = media::TimeUnit(frames, mOutputRate); 597 if (!duration.IsValid()) { 598 NS_WARNING("Int overflow in AudioSink"); 599 mErrored = true; 600 return nullptr; 601 } 602 RefPtr<AudioData> data = 603 new AudioData(aReference->mOffset, aReference->mTime, std::move(aBuffer), 604 mOutputChannels, mOutputRate); 605 MOZ_DIAGNOSTIC_ASSERT(duration == data->mDuration, "must be equal"); 606 return data.forget(); 607 } 608 609 uint32_t AudioSink::DrainConverter(uint32_t aMaxFrames) { 610 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); 611 612 if (!mConverter || !mLastProcessedPacket || !aMaxFrames) { 613 // nothing to drain. 614 return 0; 615 } 616 617 RefPtr<AudioData> lastPacket = mLastProcessedPacket.ref(); 618 mLastProcessedPacket.reset(); 619 620 // To drain we simply provide an empty packet to the audio converter. 621 AlignedAudioBuffer convertedData = 622 mConverter->Process(AudioSampleBuffer(AlignedAudioBuffer())).Forget(); 623 624 uint32_t frames = SampleToFrame(convertedData.Length()); 625 if (!convertedData.SetLength(std::min(frames, aMaxFrames) * 626 mOutputChannels)) { 627 // This can never happen as we were reducing the length of convertData. 628 mErrored = true; 629 return 0; 630 } 631 632 RefPtr<AudioData> data = 633 CreateAudioFromBuffer(std::move(convertedData), lastPacket); 634 return PushProcessedAudio(data); 635 } 636 637 void AudioSink::GetDebugInfo(dom::MediaSinkDebugInfo& aInfo) { 638 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); 639 aInfo.mAudioSinkWrapper.mAudioSink.mStartTime = mStartTime.ToMicroseconds(); 640 aInfo.mAudioSinkWrapper.mAudioSink.mLastGoodPosition = 641 mLastGoodPosition.ToMicroseconds(); 642 aInfo.mAudioSinkWrapper.mAudioSink.mIsPlaying = mPlaying; 643 aInfo.mAudioSinkWrapper.mAudioSink.mOutputRate = mOutputRate; 644 aInfo.mAudioSinkWrapper.mAudioSink.mWritten = mWritten; 645 aInfo.mAudioSinkWrapper.mAudioSink.mHasErrored = bool(mErrored); 646 aInfo.mAudioSinkWrapper.mAudioSink.mPlaybackComplete = 647 mAudioStream ? mAudioStream->IsPlaybackCompleted() : false; 648 } 649 650 } // namespace mozilla