webrtc中的clock时钟获取模块

webrtc实现了一个在不同平台下都可以稳定获取时间的Clock模块, 单例模式, 有学习价值.

下面是Clock基类

class Clock {
 public:
  virtual ~Clock() {}
  virtual Timestamp CurrentTime() = 0;
  virtual NtpTime CurrentNtpTime();
  virtual NtpTime ConvertTimestampToNtpTime(Timestamp timestamp) = 0;
  
  int64_t TimeInMilliseconds();
  int64_t TimeInMicroseconds();
  int64_t CurrentNtpInMilliseconds();
  int64_t ConvertTimestampToNtpTimeInMilliseconds(int64_t timestamp_ms);
  static Clock* GetRealTimeClock();
};

在不同平台通过宏定义去绑定不同的Clock实现

Clock* Clock::GetRealTimeClock() {
#if defined(WINUWP)
  static Clock* const clock = new WinUwpRealTimeClock();
#elif defined(WEBRTC_WIN)
  static Clock* const clock = new WindowsRealTimeClock();
#elif defined(WEBRTC_POSIX)
  static Clock* const clock = new UnixRealTimeClock();
#else
  static Clock* const clock = nullptr;
#endif
  return clock;
}

那么实现以上这个static Clock, 想到了三种写法:

//static clock* clock = nullptr
Clock* Clock::GetTimeClock(const ClockType type)
{
    static Clock* clock = nullptr;

    switch (type) {
        case ClockType::WinClock:
            clock = new WinClock();
            return clock;
        case ClockType::LinuxClock:
            clock = new LinuxClock();
            return clock;
        default:
            break;
    }
    return nullptr;
}

// static Clocl clock = XXXClock
Clock* Clock::GetTimeClock(const ClockType type)
{
    if(type == ClockType::WinClock) {
        static Clock clock = WinClock();
        return &clock;
    }else if(type == ClockType::LinuxClock) {
        static Clock clock = LinuxClock();
        return &clock;
    }else {
        return nullptr;
    }
}

// static Clock* clock = new XXXClock
Clock* Clock::GetTimeClock(const ClockType type)
{
  if (type == ClockType::WinClock) {
    static Clock* clock = new WinClock();
    return clock;
  }
  else if (type == ClockType::LinuxClock) {
    static Clock* clock = new LinuxClock();
    return clock;
  }
  else {
    return nullptr;
  }
}

经测试,

第一种写法不能保证单例, 并没有把生成的对象放到堆上去.

第二种生成的对象被放到了全局数据区, 可以不用delete数据, 生命周期随程序走, 会自动释放内存, 没有办法提前释放内存占用,

第三种被放到了堆区, 需要调用delete, 但是如果单例生命周期也是跟着程序走的, 也不需要调delete. 一般谨慎调用delete.

一般使用的时候向下面这样调接口就可以

Clock* clock = Clock::GetRealTimeClock();

Clock* const clock_;

初始化列表: clock_(clock)

const auto now_ms =  clock_->TimeInMilliseconds();

源文件

clock.h

/*
 *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#ifndef SYSTEM_WRAPPERS_INCLUDE_CLOCK_H_
#define SYSTEM_WRAPPERS_INCLUDE_CLOCK_H_

#include <stdint.h>

#include <atomic>
#include <memory>

#include "api/units/timestamp.h"
#include "rtc_base/system/rtc_export.h"
#include "system_wrappers/include/ntp_time.h"

namespace webrtc {

// January 1970, in NTP seconds.
const uint32_t kNtpJan1970 = 2208988800UL;

// Magic NTP fractional unit.
const double kMagicNtpFractionalUnit = 4.294967296E+9;

// A clock interface that allows reading of absolute and relative timestamps.
class RTC_EXPORT Clock {
 public:
  virtual ~Clock() {}

  // Return a timestamp relative to an unspecified epoch.
  virtual Timestamp CurrentTime() = 0;
  int64_t TimeInMilliseconds() { return CurrentTime().ms(); }
  int64_t TimeInMicroseconds() { return CurrentTime().us(); }

  // Retrieve an NTP absolute timestamp (with an epoch of Jan 1, 1900).
  // TODO(bugs.webrtc.org/11327): Make this non-virtual once
  // "WebRTC-SystemIndependentNtpTimeKillSwitch" is removed.
  virtual NtpTime CurrentNtpTime() {
    return ConvertTimestampToNtpTime(CurrentTime());
  }
  int64_t CurrentNtpInMilliseconds() { return CurrentNtpTime().ToMs(); }

  // Converts between a relative timestamp returned by this clock, to NTP time.
  virtual NtpTime ConvertTimestampToNtpTime(Timestamp timestamp) = 0;
  int64_t ConvertTimestampToNtpTimeInMilliseconds(int64_t timestamp_ms) {
    return ConvertTimestampToNtpTime(Timestamp::Millis(timestamp_ms)).ToMs();
  }

  // Returns an instance of the real-time system clock implementation.
  static Clock* GetRealTimeClock();
};

class SimulatedClock : public Clock {
 public:
  // The constructors assume an epoch of Jan 1, 1970.
  explicit SimulatedClock(int64_t initial_time_us);
  explicit SimulatedClock(Timestamp initial_time);
  ~SimulatedClock() override;

  // Return a timestamp with an epoch of Jan 1, 1970.
  Timestamp CurrentTime() override;

  NtpTime ConvertTimestampToNtpTime(Timestamp timestamp) override;

  // Advance the simulated clock with a given number of milliseconds or
  // microseconds.
  void AdvanceTimeMilliseconds(int64_t milliseconds);
  void AdvanceTimeMicroseconds(int64_t microseconds);
  void AdvanceTime(TimeDelta delta);

 private:
  // The time is read and incremented with relaxed order. Each thread will see
  // monotonically increasing time, and when threads post tasks or messages to
  // one another, the synchronization done as part of the message passing should
  // ensure that any causual chain of events on multiple threads also
  // corresponds to monotonically increasing time.
  std::atomic<int64_t> time_us_;
};

}  // namespace webrtc

#endif  // SYSTEM_WRAPPERS_INCLUDE_CLOCK_H_

clock.cc

/*
 *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "system_wrappers/include/clock.h"

#include "system_wrappers/include/field_trial.h"

#if defined(WEBRTC_WIN)

// Windows needs to be included before mmsystem.h
#include "rtc_base/win32.h"

#include <mmsystem.h>


#elif defined(WEBRTC_POSIX)

#include <sys/time.h>
#include <time.h>

#endif  // defined(WEBRTC_POSIX)

#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/time_utils.h"

namespace webrtc {
namespace {

int64_t NtpOffsetUsCalledOnce() {
  constexpr int64_t kNtpJan1970Sec = 2208988800;
  int64_t clock_time = rtc::TimeMicros();
  int64_t utc_time = rtc::TimeUTCMicros();
  return utc_time - clock_time + kNtpJan1970Sec * rtc::kNumMicrosecsPerSec;
}

NtpTime TimeMicrosToNtp(int64_t time_us) {
  static int64_t ntp_offset_us = NtpOffsetUsCalledOnce();

  int64_t time_ntp_us = time_us + ntp_offset_us;
  RTC_DCHECK_GE(time_ntp_us, 0);  // Time before year 1900 is unsupported.

  // Convert seconds to uint32 through uint64 for a well-defined cast.
  // A wrap around, which will happen in 2036, is expected for NTP time.
  uint32_t ntp_seconds =
      static_cast<uint64_t>(time_ntp_us / rtc::kNumMicrosecsPerSec);

  // Scale fractions of the second to NTP resolution.
  constexpr int64_t kNtpFractionsInSecond = 1LL << 32;
  int64_t us_fractions = time_ntp_us % rtc::kNumMicrosecsPerSec;
  uint32_t ntp_fractions =
      us_fractions * kNtpFractionsInSecond / rtc::kNumMicrosecsPerSec;

  return NtpTime(ntp_seconds, ntp_fractions);
}

void GetSecondsAndFraction(const timeval& time,
                           uint32_t* seconds,
                           double* fraction) {
  *seconds = time.tv_sec + kNtpJan1970;
  *fraction = time.tv_usec / 1e6;

  while (*fraction >= 1) {
    --*fraction;
    ++*seconds;
  }
  while (*fraction < 0) {
    ++*fraction;
    --*seconds;
  }
}

}  // namespace

class RealTimeClock : public Clock {
 public:
  RealTimeClock()
      : use_system_independent_ntp_time_(!field_trial::IsEnabled(
            "WebRTC-SystemIndependentNtpTimeKillSwitch")) {}

  Timestamp CurrentTime() override {
    return Timestamp::Micros(rtc::TimeMicros());
  }

  NtpTime CurrentNtpTime() override {
    return use_system_independent_ntp_time_ ? TimeMicrosToNtp(rtc::TimeMicros())
                                            : SystemDependentNtpTime();
  }

  NtpTime ConvertTimestampToNtpTime(Timestamp timestamp) override {
    // This method does not check `use_system_independent_ntp_time_` because
    // all callers never used the old behavior of `CurrentNtpTime`.
    return TimeMicrosToNtp(timestamp.us());
  }

 protected:
  virtual timeval CurrentTimeVal() = 0;

 private:
  NtpTime SystemDependentNtpTime() {
    uint32_t seconds;
    double fraction;
    GetSecondsAndFraction(CurrentTimeVal(), &seconds, &fraction);

    return NtpTime(seconds, static_cast<uint32_t>(
                                fraction * kMagicNtpFractionalUnit + 0.5));
  }

  bool use_system_independent_ntp_time_;
};

#if defined(WINUWP)
class WinUwpRealTimeClock final : public RealTimeClock {
 public:
  WinUwpRealTimeClock() = default;
  ~WinUwpRealTimeClock() override {}

 protected:
  timeval CurrentTimeVal() override {
    // The rtc::WinUwpSystemTimeNanos() method is already time offset from a
    // base epoch value and might as be synchronized against an NTP time server
    // as an added bonus.
    auto nanos = rtc::WinUwpSystemTimeNanos();

    struct timeval tv;

    tv.tv_sec = rtc::dchecked_cast<long>(nanos / 1000000000);
    tv.tv_usec = rtc::dchecked_cast<long>(nanos / 1000);

    return tv;
  }
};

#elif defined(WEBRTC_WIN)
// TODO(pbos): Consider modifying the implementation to synchronize itself
// against system time (update ref_point_) periodically to
// prevent clock drift.
class WindowsRealTimeClock : public RealTimeClock {
 public:
  WindowsRealTimeClock()
      : last_time_ms_(0),
        num_timer_wraps_(0),
        ref_point_(GetSystemReferencePoint()) {}

  ~WindowsRealTimeClock() override {}

 protected:
  struct ReferencePoint {
    FILETIME file_time;
    LARGE_INTEGER counter_ms;
  };

  timeval CurrentTimeVal() override {
    const uint64_t FILETIME_1970 = 0x019db1ded53e8000;

    FILETIME StartTime;
    uint64_t Time;
    struct timeval tv;

    // We can't use query performance counter since they can change depending on
    // speed stepping.
    GetTime(&StartTime);

    Time = (((uint64_t)StartTime.dwHighDateTime) << 32) +
           (uint64_t)StartTime.dwLowDateTime;

    // Convert the hecto-nano second time to tv format.
    Time -= FILETIME_1970;

    tv.tv_sec = (uint32_t)(Time / (uint64_t)10000000);
    tv.tv_usec = (uint32_t)((Time % (uint64_t)10000000) / 10);
    return tv;
  }

  void GetTime(FILETIME* current_time) {
    DWORD t;
    LARGE_INTEGER elapsed_ms;
    {
      MutexLock lock(&mutex_);
      // time MUST be fetched inside the critical section to avoid non-monotonic
      // last_time_ms_ values that'll register as incorrect wraparounds due to
      // concurrent calls to GetTime.
      t = timeGetTime();
      if (t < last_time_ms_)
        num_timer_wraps_++;
      last_time_ms_ = t;
      elapsed_ms.HighPart = num_timer_wraps_;
    }
    elapsed_ms.LowPart = t;
    elapsed_ms.QuadPart = elapsed_ms.QuadPart - ref_point_.counter_ms.QuadPart;

    // Translate to 100-nanoseconds intervals (FILETIME resolution)
    // and add to reference FILETIME to get current FILETIME.
    ULARGE_INTEGER filetime_ref_as_ul;
    filetime_ref_as_ul.HighPart = ref_point_.file_time.dwHighDateTime;
    filetime_ref_as_ul.LowPart = ref_point_.file_time.dwLowDateTime;
    filetime_ref_as_ul.QuadPart +=
        static_cast<ULONGLONG>((elapsed_ms.QuadPart) * 1000 * 10);

    // Copy to result
    current_time->dwHighDateTime = filetime_ref_as_ul.HighPart;
    current_time->dwLowDateTime = filetime_ref_as_ul.LowPart;
  }

  static ReferencePoint GetSystemReferencePoint() {
    ReferencePoint ref = {};
    FILETIME ft0 = {};
    FILETIME ft1 = {};
    // Spin waiting for a change in system time. As soon as this change happens,
    // get the matching call for timeGetTime() as soon as possible. This is
    // assumed to be the most accurate offset that we can get between
    // timeGetTime() and system time.

    // Set timer accuracy to 1 ms.
    timeBeginPeriod(1);
    GetSystemTimeAsFileTime(&ft0);
    do {
      GetSystemTimeAsFileTime(&ft1);

      ref.counter_ms.QuadPart = timeGetTime();
      Sleep(0);
    } while ((ft0.dwHighDateTime == ft1.dwHighDateTime) &&
             (ft0.dwLowDateTime == ft1.dwLowDateTime));
    ref.file_time = ft1;
    timeEndPeriod(1);
    return ref;
  }

  Mutex mutex_;
  DWORD last_time_ms_;
  LONG num_timer_wraps_;
  const ReferencePoint ref_point_;
};

#elif defined(WEBRTC_POSIX)
class UnixRealTimeClock : public RealTimeClock {
 public:
  UnixRealTimeClock() {}

  ~UnixRealTimeClock() override {}

 protected:
  timeval CurrentTimeVal() override {
    struct timeval tv;
    gettimeofday(&tv, nullptr);
    return tv;
  }
};
#endif  // defined(WEBRTC_POSIX)

Clock* Clock::GetRealTimeClock() {
#if defined(WINUWP)
  static Clock* const clock = new WinUwpRealTimeClock();
#elif defined(WEBRTC_WIN)
  static Clock* const clock = new WindowsRealTimeClock();
#elif defined(WEBRTC_POSIX)
  static Clock* const clock = new UnixRealTimeClock();
#else
  static Clock* const clock = nullptr;
#endif
  return clock;
}

SimulatedClock::SimulatedClock(int64_t initial_time_us)
    : time_us_(initial_time_us) {}

SimulatedClock::SimulatedClock(Timestamp initial_time)
    : SimulatedClock(initial_time.us()) {}

SimulatedClock::~SimulatedClock() {}

Timestamp SimulatedClock::CurrentTime() {
  return Timestamp::Micros(time_us_.load(std::memory_order_relaxed));
}

NtpTime SimulatedClock::ConvertTimestampToNtpTime(Timestamp timestamp) {
  int64_t now_us = timestamp.us();
  uint32_t seconds = (now_us / 1'000'000) + kNtpJan1970;
  uint32_t fractions = static_cast<uint32_t>(
      (now_us % 1'000'000) * kMagicNtpFractionalUnit / 1'000'000);
  return NtpTime(seconds, fractions);
}

void SimulatedClock::AdvanceTimeMilliseconds(int64_t milliseconds) {
  AdvanceTime(TimeDelta::Millis(milliseconds));
}

void SimulatedClock::AdvanceTimeMicroseconds(int64_t microseconds) {
  AdvanceTime(TimeDelta::Micros(microseconds));
}

// TODO(bugs.webrtc.org(12102): It's desirable to let a single thread own
// advancement of the clock. We could then replace this read-modify-write
// operation with just a thread checker. But currently, that breaks a couple of
// tests, in particular, RepeatingTaskTest.ClockIntegration and
// CallStatsTest.LastProcessedRtt.
void SimulatedClock::AdvanceTime(TimeDelta delta) {
  time_us_.fetch_add(delta.us(), std::memory_order_relaxed);
}

}  // namespace webrtc
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,227评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,755评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,899评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,257评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,617评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,757评论 1 221
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,982评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,715评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,454评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,666评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,148评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,512评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,156评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,112评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,896评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,809评论 2 279
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,691评论 2 272