DriftCompensation.h (4731B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 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 https://mozilla.org/MPL/2.0/. */ 6 7 #ifndef DriftCompensation_h_ 8 #define DriftCompensation_h_ 9 10 #include "MediaSegment.h" 11 #include "VideoUtils.h" 12 #include "mozilla/Atomics.h" 13 14 namespace mozilla { 15 16 static LazyLogModule gDriftCompensatorLog("DriftCompensator"); 17 #define LOG(type, ...) MOZ_LOG(gDriftCompensatorLog, type, (__VA_ARGS__)) 18 19 /** 20 * DriftCompensator can be used to handle drift between audio and video tracks 21 * from the MediaTrackGraph. 22 * 23 * Drift can occur because audio is driven by a MediaTrackGraph running off an 24 * audio callback, thus it's progressed by the clock of one the audio output 25 * devices on the user's machine. Video on the other hand is always expressed in 26 * wall-clock TimeStamps, i.e., it's progressed by the system clock. These 27 * clocks will, over time, drift apart. 28 * 29 * Do not use the DriftCompensator across multiple audio tracks, as it will 30 * automatically record the start time of the first audio samples, and all 31 * samples for the same audio track on the same audio clock will have to be 32 * processed to retain accuracy. 33 * 34 * DriftCompensator is designed to be used from two threads: 35 * - The audio thread for notifications of audio samples. 36 * - The video thread for compensating drift of video frames to match the audio 37 * clock. 38 */ 39 class DriftCompensator { 40 const RefPtr<nsIEventTarget> mVideoThread; 41 const TrackRate mAudioRate; 42 43 // Number of audio samples produced. Any thread. 44 Atomic<TrackTime> mAudioSamples{0}; 45 46 // Time the first audio samples were added. mVideoThread only. 47 TimeStamp mAudioStartTime; 48 49 void SetAudioStartTime(TimeStamp aTime) { 50 MOZ_ASSERT(mVideoThread->IsOnCurrentThread()); 51 MOZ_ASSERT(mAudioStartTime.IsNull()); 52 mAudioStartTime = aTime; 53 } 54 55 protected: 56 virtual ~DriftCompensator() = default; 57 58 public: 59 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DriftCompensator) 60 61 DriftCompensator(RefPtr<nsIEventTarget> aVideoThread, TrackRate aAudioRate) 62 : mVideoThread(std::move(aVideoThread)), mAudioRate(aAudioRate) { 63 MOZ_ASSERT(mAudioRate > 0); 64 } 65 66 void NotifyAudioStart(TimeStamp aStart) { 67 MOZ_ASSERT(mAudioSamples == 0); 68 LOG(LogLevel::Info, "DriftCompensator %p at rate %d started", this, 69 mAudioRate); 70 nsresult rv = mVideoThread->Dispatch(NewRunnableMethod<TimeStamp>( 71 "DriftCompensator::SetAudioStartTime", this, 72 &DriftCompensator::SetAudioStartTime, aStart)); 73 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 74 (void)rv; 75 } 76 77 /** 78 * aSamples is the number of samples fed by an AudioStream. 79 */ 80 void NotifyAudio(TrackTime aSamples) { 81 MOZ_ASSERT(aSamples > 0); 82 mAudioSamples += aSamples; 83 84 LOG(LogLevel::Verbose, 85 "DriftCompensator %p Processed another %" PRId64 86 " samples; now %.3fs audio", 87 this, aSamples, static_cast<double>(mAudioSamples) / mAudioRate); 88 } 89 90 /** 91 * Drift compensates a video TimeStamp based on historical audio data. 92 */ 93 virtual TimeStamp GetVideoTime(TimeStamp aNow, TimeStamp aTime) { 94 MOZ_ASSERT(mVideoThread->IsOnCurrentThread()); 95 TrackTime samples = mAudioSamples; 96 97 if (samples / mAudioRate < 10) { 98 // We don't apply compensation for the first 10 seconds because of the 99 // higher inaccuracy during this time. 100 LOG(LogLevel::Debug, "DriftCompensator %p %" PRId64 "ms so far; ignoring", 101 this, samples * 1000 / mAudioRate); 102 return aTime; 103 } 104 105 if (aNow == mAudioStartTime) { 106 LOG(LogLevel::Warning, 107 "DriftCompensator %p video scale 0, assuming no drift", this); 108 return aTime; 109 } 110 111 double videoScaleUs = (aNow - mAudioStartTime).ToMicroseconds(); 112 double audioScaleUs = FramesToUsecs(samples, mAudioRate).value(); 113 double videoDurationUs = (aTime - mAudioStartTime).ToMicroseconds(); 114 115 TimeStamp reclocked = 116 mAudioStartTime + TimeDuration::FromMicroseconds( 117 videoDurationUs * audioScaleUs / videoScaleUs); 118 119 LOG(LogLevel::Debug, 120 "DriftCompensator %p GetVideoTime, v-now: %.3fs, a-now: %.3fs; %.3fs " 121 "-> %.3fs (d %.3fms)", 122 this, (aNow - mAudioStartTime).ToSeconds(), 123 TimeDuration::FromMicroseconds(audioScaleUs).ToSeconds(), 124 (aTime - mAudioStartTime).ToSeconds(), 125 (reclocked - mAudioStartTime).ToSeconds(), 126 (reclocked - aTime).ToMilliseconds()); 127 128 return reclocked; 129 } 130 }; 131 132 #undef LOG 133 134 } // namespace mozilla 135 136 #endif /* DriftCompensation_h_ */