tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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