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