tor-browser

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

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_ */