1010
1111namespace mozilla {
1212
13- extern LazyLogModule gMediaTrackGraphLog ;
14-
1513/* *
1614 * ClockDrift calculates the diverge of the source clock from the nominal
1715 * (provided) rate compared to the target clock, which is considered the master
@@ -30,19 +28,23 @@ extern LazyLogModule gMediaTrackGraphLog;
3028 * performance impact.
3129 *
3230 * The pref `media.clock drift.buffering` can be used to configure the desired
33- * internal buffering. Right now it is at 50ms . But it can be increased if there
31+ * internal buffering. Right now it is at 5ms . But it can be increased if there
3432 * are audio quality problems.
3533 */
3634class ClockDrift final {
3735 public:
3836 /* *
3937 * Provide the nominal source and the target sample rate.
4038 */
41- ClockDrift (uint32_t aSourceRate, uint32_t aTargetRate,
42- uint32_t aDesiredBuffering)
39+ ClockDrift (int32_t aSourceRate, int32_t aTargetRate)
4340 : mSourceRate (aSourceRate),
4441 mTargetRate (aTargetRate),
45- mDesiredBuffering(aDesiredBuffering) {}
42+ mDesiredBuffering(5 * mSourceRate / 100 ) {
43+ if (Preferences::HasUserValue (" media.clockdrift.buffering" )) {
44+ int msecs = Preferences::GetInt (" media.clockdrift.buffering" );
45+ mDesiredBuffering = msecs * mSourceRate / 100 ;
46+ }
47+ }
4648
4749 /* *
4850 * The correction in the form of a ratio. A correction of 0.98 means that the
@@ -53,80 +55,81 @@ class ClockDrift final {
5355
5456 /* *
5557 * Update the available source frames, target frames, and the current
56- * buffer , in every iteration. If the conditions are met a new correction is
58+ * buffering , in every iteration. If the condition are met a new correction is
5759 * calculated. A new correction is calculated in the following cases:
58- * 1. Every mAdjustmentIntervalMs milliseconds (1000ms) .
59- * 2. Every time we run low on buffered frames (less than 20ms ).
60+ * 1. Every 100 iterations which mean every 100 calls of this method .
61+ * 2. Every time we run out of buffered frames (less than 2ms ).
6062 * In addition to that, the correction is clamped to 10% to avoid sound
6163 * distortion so the result will be in [0.9, 1.1].
6264 */
63- void UpdateClock (uint32_t aSourceFrames, uint32_t aTargetFrames,
64- uint32_t aBufferedFrames, uint32_t aRemainingFrames) {
65- if (mSourceClock >= mSourceRate / 10 || mTargetClock >= mTargetRate / 10 ) {
66- // Only update the correction if 100ms has passed since last update.
67- if (aBufferedFrames < mDesiredBuffering * 4 / 10 /* 40%*/ ||
68- aRemainingFrames < mDesiredBuffering * 4 / 10 /* 40%*/ ) {
69- // We are getting close to the lower or upper bound of the internal
70- // buffer. Steer clear.
71- CalculateCorrection (0.9 , aBufferedFrames, aRemainingFrames);
72- } else if ((mTargetClock * 1000 / mTargetRate ) >= mAdjustmentIntervalMs ||
73- (mSourceClock * 1000 / mSourceRate ) >= mAdjustmentIntervalMs ) {
74- // The adjustment interval has passed on one side. Recalculate.
75- CalculateCorrection (0.6 , aBufferedFrames, aRemainingFrames);
76- }
65+ void UpdateClock (int aSourceClock, int aTargetClock, int aBufferedFrames) {
66+ if (mIterations == mAdjustementWindow ) {
67+ CalculateCorrection (aBufferedFrames);
68+ } else if (aBufferedFrames < 2 * mSourceRate / 100 /* 20ms*/ ) {
69+ BufferedFramesCorrection (aBufferedFrames);
7770 }
78- mTargetClock += aTargetFrames;
79- mSourceClock += aSourceFrames;
71+ mTargetClock += aTargetClock;
72+ mSourceClock += aSourceClock;
73+ ++mIterations ;
8074 }
8175
8276 private:
83- /* *
84- * aCalculationWeight is a percentage [0, 1] with which the calculated
85- * correction will be weighted. The existing correction will be weighted with
86- * 1 - aCalculationWeight. This gives some inertia to the speed at which the
87- * correction changes, for smoother changes.
88- */
89- void CalculateCorrection (float aCalculationWeight, uint32_t aBufferedFrames,
90- uint32_t aRemainingFrames) {
91- // We want to maintain the desired buffer
92- uint32_t bufferedFramesDiff = aBufferedFrames - mDesiredBuffering ;
93- uint32_t resampledSourceClock =
94- std::max (1u , mSourceClock + bufferedFramesDiff);
77+ void CalculateCorrection (int aBufferedFrames) {
78+ // We want to maintain 4 ms buffered
79+ int32_t bufferedFramesDiff = aBufferedFrames - mDesiredBuffering ;
80+ int32_t resampledSourceClock = mSourceClock + bufferedFramesDiff;
9581 if (mTargetRate != mSourceRate ) {
96- resampledSourceClock *= static_cast <float >(mTargetRate ) / mSourceRate ;
82+ resampledSourceClock =
83+ resampledSourceClock *
84+ (static_cast <float >(mTargetRate ) / static_cast <float >(mSourceRate ));
9785 }
86+ mCorrection = (float )mTargetClock / resampledSourceClock;
9887
99- MOZ_LOG (gMediaTrackGraphLog , LogLevel::Verbose,
100- (" ClockDrift %p Calculated correction %.3f (with weight: %.1f -> "
101- " %.3f) (buffer: %u, desired: %u, remaining: %u)" ,
102- this , static_cast <float >(mTargetClock ) / resampledSourceClock,
103- aCalculationWeight,
104- (1 - aCalculationWeight) * mCorrection +
105- aCalculationWeight * mTargetClock / resampledSourceClock,
106- aBufferedFrames, mDesiredBuffering , aRemainingFrames));
107-
108- mCorrection = (1 - aCalculationWeight) * mCorrection +
109- aCalculationWeight * mTargetClock / resampledSourceClock;
110-
111- // Clamp to range [0.9, 1.1] to avoid distortion
88+ // Clamp to ragnge [0.9, 1.1] to avoid distortion
11289 mCorrection = std::min (std::max (mCorrection , 0 .9f ), 1 .1f );
11390
114- // Reset the counters to prepare for the next period.
91+ // If previous correction slightly smaller (-1%) ignore it to avoid
92+ // recalculations. Don't do it when is greater (+1%) to avoid risking
93+ // running out of frames.
94+ if (mPreviousCorrection - mCorrection <= 0.01 &&
95+ mPreviousCorrection - mCorrection > 0 ) {
96+ mCorrection = mPreviousCorrection ;
97+ }
98+ mPreviousCorrection = mCorrection ;
99+
100+ // Reset the counters to preper for the new period.
101+ mIterations = 0 ;
115102 mTargetClock = 0 ;
116103 mSourceClock = 0 ;
117104 }
118105
119- public:
120- const uint32_t mSourceRate ;
121- const uint32_t mTargetRate ;
122- const uint32_t mAdjustmentIntervalMs = 1000 ;
123- const uint32_t mDesiredBuffering ;
106+ void BufferedFramesCorrection (int aBufferedFrames) {
107+ int32_t bufferedFramesDiff = aBufferedFrames - mDesiredBuffering ;
108+ int32_t resampledSourceClock = mSourceRate + bufferedFramesDiff;
109+ if (mTargetRate != mSourceRate ) {
110+ resampledSourceClock = resampledSourceClock *
111+ (static_cast <float >(mTargetRate ) / mSourceRate );
112+ }
113+ MOZ_ASSERT (mTargetRate > resampledSourceClock);
114+ mPreviousCorrection = mCorrection ;
115+ mCorrection +=
116+ static_cast <float >(mTargetRate ) / resampledSourceClock - 1 .0f ;
117+ // Clamp to range [0.9, 1.1] to avoid distortion
118+ mCorrection = std::min (std::max (mCorrection , 0 .9f ), 1 .1f );
119+ }
124120
125121 private:
122+ const int32_t mSourceRate ;
123+ const int32_t mTargetRate ;
124+
126125 float mCorrection = 1.0 ;
126+ float mPreviousCorrection = 1.0 ;
127+ const int32_t mAdjustementWindow = 100 ;
128+ int32_t mDesiredBuffering ; // defult: 5ms
127129
128- uint32_t mSourceClock = 0 ;
129- uint32_t mTargetClock = 0 ;
130+ int32_t mSourceClock = 0 ;
131+ int32_t mTargetClock = 0 ;
132+ int32_t mIterations = 0 ;
130133};
131134
132135/* *
@@ -150,13 +153,10 @@ class ClockDrift final {
150153 */
151154class AudioDriftCorrection final {
152155 public:
153- AudioDriftCorrection (uint32_t aSourceRate, uint32_t aTargetRate)
154- : mDesiredBuffering (
155- std::max (5 , Preferences::GetInt(" media.clockdrift.buffering" , 50 )) *
156- aSourceRate / 1000),
157- mTargetRate(aTargetRate),
158- mClockDrift(aSourceRate, aTargetRate, mDesiredBuffering ),
159- mResampler(aSourceRate, aTargetRate, mDesiredBuffering ) {}
156+ AudioDriftCorrection (int32_t aSourceRate, int32_t aTargetRate)
157+ : mClockDrift (aSourceRate, aTargetRate),
158+ mResampler (aSourceRate, aTargetRate, aTargetRate / 20 /* 50ms*/ ),
159+ mTargetRate(aTargetRate) {}
160160
161161 /* *
162162 * The source audio frames and request the number of target audio frames must
@@ -168,37 +168,30 @@ class AudioDriftCorrection final {
168168 * AudioSegment will be returned. Not thread-safe.
169169 */
170170 AudioSegment RequestFrames (const AudioSegment& aInput,
171- uint32_t aOutputFrames) {
171+ int32_t aOutputFrames) {
172172 // Very important to go first since the Dynamic will get the sample format
173173 // from the chunk.
174174 if (aInput.GetDuration ()) {
175175 // Always go through the resampler because the clock might shift later.
176176 mResampler .AppendInput (aInput);
177177 }
178178 mClockDrift .UpdateClock (aInput.GetDuration (), aOutputFrames,
179- mResampler .InputDuration (),
180- mResampler .InputRemainingDuration ());
179+ mResampler .InputDuration ());
181180 TrackRate receivingRate = mTargetRate * mClockDrift .GetCorrection ();
182181 // Update resampler's rate if there is a new correction.
183182 mResampler .UpdateOutRate (receivingRate);
184183 // If it does not have enough frames the result will be an empty segment.
185184 AudioSegment output = mResampler .Resample (aOutputFrames);
186185 if (output.IsEmpty ()) {
187- NS_WARNING (" Got nothing from the resampler" );
188186 output.AppendNullData (aOutputFrames);
189187 }
190188 return output;
191189 }
192190
193- // Only accessible from the same thread that is driving RequestFrames().
194- uint32_t CurrentBuffering () const { return mResampler .InputDuration (); }
195-
196- const uint32_t mDesiredBuffering ;
197- const uint32_t mTargetRate ;
198-
199191 private:
200192 ClockDrift mClockDrift ;
201193 AudioResampler mResampler ;
194+ const int32_t mTargetRate ;
202195};
203196
204197}; // namespace mozilla
0 commit comments