tor-browser

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

AudioDriftCorrection.cpp (7727B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "AudioDriftCorrection.h"
      7 
      8 #include <cmath>
      9 
     10 #include "AudioResampler.h"
     11 #include "DriftController.h"
     12 
     13 namespace mozilla {
     14 
     15 extern LazyLogModule gMediaTrackGraphLog;
     16 
     17 #define LOG_CONTROLLER(level, controller, format, ...)             \
     18  MOZ_LOG(gMediaTrackGraphLog, level,                              \
     19          ("DriftController %p: (plot-id %u) " format, controller, \
     20           (controller)->mPlotId, ##__VA_ARGS__))
     21 
     22 static media::TimeUnit DesiredBuffering(media::TimeUnit aSourceLatency) {
     23  constexpr media::TimeUnit kMinBuffer(10, MSECS_PER_S);
     24  constexpr media::TimeUnit kMaxBuffer(2500, MSECS_PER_S);
     25 
     26  const auto clamped = std::clamp(aSourceLatency, kMinBuffer, kMaxBuffer);
     27 
     28  // Ensure the base is the source's sampling rate.
     29  return clamped.ToBase(aSourceLatency);
     30 }
     31 
     32 AudioDriftCorrection::AudioDriftCorrection(
     33    uint32_t aSourceRate, uint32_t aTargetRate,
     34    const PrincipalHandle& aPrincipalHandle)
     35    : mTargetRate(aTargetRate),
     36      mDriftController(MakeUnique<DriftController>(aSourceRate, aTargetRate,
     37                                                   mDesiredBuffering)),
     38      mResampler(MakeUnique<AudioResampler>(aSourceRate, aTargetRate, 0,
     39                                            aPrincipalHandle)) {}
     40 
     41 AudioDriftCorrection::~AudioDriftCorrection() = default;
     42 
     43 AudioSegment AudioDriftCorrection::RequestFrames(const AudioSegment& aInput,
     44                                                 uint32_t aOutputFrames) {
     45  const media::TimeUnit inputDuration(aInput.GetDuration(),
     46                                      mDriftController->mSourceRate);
     47  const media::TimeUnit outputDuration(aOutputFrames, mTargetRate);
     48 
     49  if (inputDuration.IsPositive()) {
     50    if (mDesiredBuffering.IsZero()) {
     51      // Start with the desired buffering at at least 50ms, since the drift is
     52      // still unknown. It may be adjust downward later on, when we have adapted
     53      // to the drift more.
     54      const media::TimeUnit desiredBuffering = DesiredBuffering(std::max(
     55          inputDuration * 11 / 10, media::TimeUnit::FromSeconds(0.05)));
     56      LOG_CONTROLLER(LogLevel::Info, mDriftController.get(),
     57                     "Initial desired buffering %.2fms",
     58                     desiredBuffering.ToSeconds() * 1000.0);
     59      SetDesiredBuffering(desiredBuffering);
     60    } else if (inputDuration > mDesiredBuffering) {
     61      // Input latency is higher than the desired buffering. Increase the
     62      // desired buffering to try to avoid underruns.
     63      if (inputDuration > mSourceLatency) {
     64        const media::TimeUnit desiredBuffering =
     65            DesiredBuffering(inputDuration * 11 / 10);
     66        LOG_CONTROLLER(
     67            LogLevel::Info, mDriftController.get(),
     68            "High observed input latency %.2fms (%" PRId64
     69            " frames). Increasing desired buffering %.2fms->%.2fms frames",
     70            inputDuration.ToSeconds() * 1000.0, aInput.GetDuration(),
     71            mDesiredBuffering.ToSeconds() * 1000.0,
     72            desiredBuffering.ToSeconds() * 1000.0);
     73        SetDesiredBuffering(desiredBuffering);
     74      } else {
     75        const media::TimeUnit desiredBuffering =
     76            DesiredBuffering(mSourceLatency * 11 / 10);
     77        LOG_CONTROLLER(LogLevel::Info, mDriftController.get(),
     78                       "Increasing desired buffering %.2fms->%.2fms, "
     79                       "based on reported input-latency %.2fms.",
     80                       mDesiredBuffering.ToSeconds() * 1000.0,
     81                       desiredBuffering.ToSeconds() * 1000.0,
     82                       mSourceLatency.ToSeconds() * 1000.0);
     83        SetDesiredBuffering(desiredBuffering);
     84      }
     85    }
     86 
     87    mIsHandlingUnderrun = false;
     88    // Very important to go first since DynamicResampler will get the sample
     89    // format from the chunk.
     90    mResampler->AppendInput(aInput);
     91  }
     92  bool hasUnderrun = false;
     93  AudioSegment output = mResampler->Resample(aOutputFrames, &hasUnderrun);
     94  mDriftController->UpdateClock(inputDuration, outputDuration,
     95                                CurrentBuffering(), BufferSize());
     96  // Update resampler's rate if there is a new correction.
     97  mResampler->UpdateInRate(mDriftController->GetCorrectedSourceRate());
     98  if (hasUnderrun) {
     99    if (!mIsHandlingUnderrun) {
    100      NS_WARNING("Drift-correction: Underrun");
    101      LOG_CONTROLLER(LogLevel::Info, mDriftController.get(),
    102                     "Underrun. Doubling the desired buffering %.2fms->%.2fms",
    103                     mDesiredBuffering.ToSeconds() * 1000.0,
    104                     (mDesiredBuffering * 2).ToSeconds() * 1000.0);
    105      mIsHandlingUnderrun = true;
    106      ++mNumUnderruns;
    107      SetDesiredBuffering(DesiredBuffering(mDesiredBuffering * 2));
    108      mDriftController->ResetAfterUnderrun();
    109    }
    110  }
    111 
    112  if (mDriftController->DurationNearDesired() > mLatencyReductionTimeLimit &&
    113      mDriftController->DurationSinceDesiredBufferingChange() >
    114          mLatencyReductionTimeLimit) {
    115    // We have been stable for a while.
    116    // Let's reduce the desired buffering if we can.
    117    const media::TimeUnit sourceLatency =
    118        mDriftController->MeasuredSourceLatency();
    119    // We target 30% over the measured source latency, a bit higher than how we
    120    // adapt to high source latency.
    121    const media::TimeUnit targetDesiredBuffering =
    122        DesiredBuffering(sourceLatency * 13 / 10);
    123    if (targetDesiredBuffering < mDesiredBuffering) {
    124      // The new target is lower than the current desired buffering. Proceed by
    125      // reducing the difference by 10%, but do it in 10ms-steps so there is a
    126      // chance of reaching the target (by truncation).
    127      const media::TimeUnit diff =
    128          (mDesiredBuffering - targetDesiredBuffering) / 10;
    129      // Apply the 10%-diff and 2ms-steps, but don't go lower than the
    130      // already-decided desired target.
    131      const media::TimeUnit target = std::max(
    132          targetDesiredBuffering, (mDesiredBuffering - diff).ToBase(500));
    133      if (target < mDesiredBuffering) {
    134        LOG_CONTROLLER(
    135            LogLevel::Info, mDriftController.get(),
    136            "Reducing desired buffering because the buffering level is stable. "
    137            "%.2fms->%.2fms. Measured source latency is %.2fms, ideal target "
    138            "is %.2fms.",
    139            mDesiredBuffering.ToSeconds() * 1000.0, target.ToSeconds() * 1000.0,
    140            sourceLatency.ToSeconds() * 1000.0,
    141            targetDesiredBuffering.ToSeconds() * 1000.0);
    142        SetDesiredBuffering(target);
    143      }
    144    }
    145  }
    146  return output;
    147 }
    148 
    149 uint32_t AudioDriftCorrection::CurrentBuffering() const {
    150  return mResampler->InputReadableFrames();
    151 }
    152 
    153 uint32_t AudioDriftCorrection::BufferSize() const {
    154  return mResampler->InputCapacityFrames();
    155 }
    156 
    157 uint32_t AudioDriftCorrection::NumCorrectionChanges() const {
    158  return mDriftController->NumCorrectionChanges();
    159 }
    160 
    161 void AudioDriftCorrection::SetSourceLatency(media::TimeUnit aSourceLatency) {
    162  LOG_CONTROLLER(
    163      LogLevel::Info, mDriftController.get(), "SetSourceLatency %.2fms->%.2fms",
    164      mSourceLatency.ToSeconds() * 1000.0, aSourceLatency.ToSeconds() * 1000.0);
    165 
    166  mSourceLatency = aSourceLatency;
    167 }
    168 
    169 void AudioDriftCorrection::SetDesiredBuffering(
    170    media::TimeUnit aDesiredBuffering) {
    171  mDesiredBuffering = aDesiredBuffering;
    172  mDriftController->SetDesiredBuffering(mDesiredBuffering);
    173  mResampler->SetInputPreBufferFrameCount(
    174      mDesiredBuffering.ToTicksAtRate(mDriftController->mSourceRate));
    175 }
    176 }  // namespace mozilla
    177 
    178 #undef LOG_CONTROLLER