1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "cast/streaming/clock_drift_smoother.h"
6 
7 #include <cmath>
8 
9 #include "util/osp_logging.h"
10 #include "util/saturate_cast.h"
11 
12 namespace openscreen {
13 namespace cast {
14 
15 namespace {
16 constexpr Clock::time_point kNullTime = Clock::time_point::min();
17 }
18 
ClockDriftSmoother(Clock::duration time_constant)19 ClockDriftSmoother::ClockDriftSmoother(Clock::duration time_constant)
20     : time_constant_(time_constant),
21       last_update_time_(kNullTime),
22       estimated_tick_offset_(0.0) {
23   OSP_DCHECK(time_constant_ > decltype(time_constant_)::zero());
24 }
25 
26 ClockDriftSmoother::~ClockDriftSmoother() = default;
27 
Current() const28 Clock::duration ClockDriftSmoother::Current() const {
29   OSP_DCHECK(last_update_time_ != kNullTime);
30   return Clock::duration(
31       rounded_saturate_cast<Clock::duration::rep>(estimated_tick_offset_));
32 }
33 
Reset(Clock::time_point now,Clock::duration measured_offset)34 void ClockDriftSmoother::Reset(Clock::time_point now,
35                                Clock::duration measured_offset) {
36   OSP_DCHECK(now != kNullTime);
37   last_update_time_ = now;
38   estimated_tick_offset_ = static_cast<double>(measured_offset.count());
39 }
40 
Update(Clock::time_point now,Clock::duration measured_offset)41 void ClockDriftSmoother::Update(Clock::time_point now,
42                                 Clock::duration measured_offset) {
43   OSP_DCHECK(now != kNullTime);
44   if (last_update_time_ == kNullTime) {
45     Reset(now, measured_offset);
46   } else if (now < last_update_time_) {
47     // |now| is not monotonically non-decreasing.
48     OSP_NOTREACHED();
49   } else {
50     const double elapsed_ticks =
51         static_cast<double>((now - last_update_time_).count());
52     last_update_time_ = now;
53     // Compute a weighted-average between the last estimate and
54     // |measured_offset|. The more time that has elasped since the last call to
55     // Update(), the more-heavily |measured_offset| will be weighed.
56     const double weight =
57         elapsed_ticks / (elapsed_ticks + time_constant_.count());
58     estimated_tick_offset_ = weight * measured_offset.count() +
59                              (1.0 - weight) * estimated_tick_offset_;
60   }
61 }
62 
63 // static
64 constexpr std::chrono::seconds ClockDriftSmoother::kDefaultTimeConstant;
65 
66 }  // namespace cast
67 }  // namespace openscreen
68