美文网首页webrtc 音视频传输
webrtc中的clock时钟获取模块

webrtc中的clock时钟获取模块

作者: yyming | 来源:发表于2022-02-24 19:32 被阅读0次

    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
    

    相关文章

      网友评论

        本文标题:webrtc中的clock时钟获取模块

        本文链接:https://www.haomeiwen.com/subject/ygqtrrtx.html