blob: 9b017b8213131d5acb53d514be62735b7eb36d51 [file] [log] [blame]
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +00001/*
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/system_wrappers/interface/clock.h"
12
13#if defined(_WIN32)
14#include <Windows.h>
15#include <WinSock.h>
16#include <MMSystem.h>
17#elif ((defined WEBRTC_LINUX) || (defined WEBRTC_MAC))
18#include <sys/time.h>
19#include <time.h>
20#endif
21
22#include "webrtc/system_wrappers/interface/tick_util.h"
23
24namespace webrtc {
25
stefan@webrtc.orgafcc6102013-04-09 13:37:40 +000026const double kNtpFracPerMs = 4.294967296E6;
27
28int64_t Clock::NtpToMs(uint32_t ntp_secs, uint32_t ntp_frac) {
29 const double ntp_frac_ms = static_cast<double>(ntp_frac) / kNtpFracPerMs;
30 return 1000 * static_cast<int64_t>(ntp_secs) +
31 static_cast<int64_t>(ntp_frac_ms + 0.5);
32}
33
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +000034#if defined(_WIN32)
35
36struct reference_point {
37 FILETIME file_time;
38 LARGE_INTEGER counterMS;
39};
40
41struct WindowsHelpTimer {
42 volatile LONG _timeInMs;
43 volatile LONG _numWrapTimeInMs;
44 reference_point _ref_point;
45
46 volatile LONG _sync_flag;
47};
48
49void Synchronize(WindowsHelpTimer* help_timer) {
50 const LONG start_value = 0;
51 const LONG new_value = 1;
52 const LONG synchronized_value = 2;
53
54 LONG compare_flag = new_value;
55 while (help_timer->_sync_flag == start_value) {
56 const LONG new_value = 1;
57 compare_flag = InterlockedCompareExchange(
58 &help_timer->_sync_flag, new_value, start_value);
59 }
60 if (compare_flag != start_value) {
61 // This thread was not the one that incremented the sync flag.
62 // Block until synchronization finishes.
63 while (compare_flag != synchronized_value) {
64 ::Sleep(0);
65 }
66 return;
67 }
68 // Only the synchronizing thread gets here so this part can be
69 // considered single threaded.
70
71 // set timer accuracy to 1 ms
72 timeBeginPeriod(1);
73 FILETIME ft0 = { 0, 0 },
74 ft1 = { 0, 0 };
75 //
76 // Spin waiting for a change in system time. Get the matching
77 // performance counter value for that time.
78 //
79 ::GetSystemTimeAsFileTime(&ft0);
80 do {
81 ::GetSystemTimeAsFileTime(&ft1);
82
83 help_timer->_ref_point.counterMS.QuadPart = ::timeGetTime();
84 ::Sleep(0);
85 } while ((ft0.dwHighDateTime == ft1.dwHighDateTime) &&
86 (ft0.dwLowDateTime == ft1.dwLowDateTime));
mflodman@webrtc.orgc7e935f2013-01-17 17:12:50 +000087 help_timer->_ref_point.file_time = ft1;
88 timeEndPeriod(1);
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +000089}
90
91void get_time(WindowsHelpTimer* help_timer, FILETIME& current_time) {
92 // we can't use query performance counter due to speed stepping
93 DWORD t = timeGetTime();
94 // NOTE: we have a missmatch in sign between _timeInMs(LONG) and
95 // (DWORD) however we only use it here without +- etc
96 volatile LONG* timeInMsPtr = &help_timer->_timeInMs;
97 // Make sure that we only inc wrapper once.
98 DWORD old = InterlockedExchange(timeInMsPtr, t);
99 if(old > t) {
100 // wrap
101 help_timer->_numWrapTimeInMs++;
102 }
103 LARGE_INTEGER elapsedMS;
104 elapsedMS.HighPart = help_timer->_numWrapTimeInMs;
105 elapsedMS.LowPart = t;
106
107 elapsedMS.QuadPart = elapsedMS.QuadPart -
108 help_timer->_ref_point.counterMS.QuadPart;
109
110 // Translate to 100-nanoseconds intervals (FILETIME resolution)
111 // and add to reference FILETIME to get current FILETIME.
112 ULARGE_INTEGER filetime_ref_as_ul;
113
114 filetime_ref_as_ul.HighPart =
115 help_timer->_ref_point.file_time.dwHighDateTime;
116 filetime_ref_as_ul.LowPart =
117 help_timer->_ref_point.file_time.dwLowDateTime;
118 filetime_ref_as_ul.QuadPart +=
119 (ULONGLONG)((elapsedMS.QuadPart)*1000*10);
120
121 // Copy to result
122 current_time.dwHighDateTime = filetime_ref_as_ul.HighPart;
123 current_time.dwLowDateTime = filetime_ref_as_ul.LowPart;
124}
125#endif
126
127class RealTimeClock : public Clock {
128 // Return a timestamp in milliseconds relative to some arbitrary source; the
129 // source is fixed for this clock.
130 virtual int64_t TimeInMilliseconds() {
131 return TickTime::MillisecondTimestamp();
132 }
133
134 // Return a timestamp in microseconds relative to some arbitrary source; the
135 // source is fixed for this clock.
136 virtual int64_t TimeInMicroseconds() {
137 return TickTime::MicrosecondTimestamp();
138 }
stefan@webrtc.orgafcc6102013-04-09 13:37:40 +0000139
140 // Retrieve an NTP absolute timestamp in seconds and fractions of a second.
141 virtual void CurrentNtp(uint32_t& seconds, uint32_t& fractions) {
142 timeval tv = CurrentTimeVal();
143 double microseconds_in_seconds;
144 Adjust(tv, &seconds, &microseconds_in_seconds);
145 fractions = static_cast<uint32_t>(
146 microseconds_in_seconds * kMagicNtpFractionalUnit + 0.5);
147 }
148
149 // Retrieve an NTP absolute timestamp in milliseconds.
150 virtual int64_t CurrentNtpInMilliseconds() {
151 timeval tv = CurrentTimeVal();
152 uint32_t seconds;
153 double microseconds_in_seconds;
154 Adjust(tv, &seconds, &microseconds_in_seconds);
155 return 1000 * static_cast<int64_t>(seconds) +
156 static_cast<int64_t>(1000.0 * microseconds_in_seconds + 0.5);
157 }
158
159 protected:
160 virtual timeval CurrentTimeVal() const = 0;
161
162 static void Adjust(const timeval& tv, uint32_t* adjusted_s,
163 double* adjusted_us_in_s) {
164 *adjusted_s = tv.tv_sec + kNtpJan1970;
165 *adjusted_us_in_s = tv.tv_usec / 1e6;
166
167 if (*adjusted_us_in_s >= 1) {
168 *adjusted_us_in_s -= 1;
169 ++*adjusted_s;
170 } else if (*adjusted_us_in_s < -1) {
171 *adjusted_us_in_s += 1;
172 --*adjusted_s;
173 }
174 }
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000175};
176
177#if defined(_WIN32)
178class WindowsRealTimeClock : public RealTimeClock {
179 public:
180 WindowsRealTimeClock(WindowsHelpTimer* helpTimer)
181 : _helpTimer(helpTimer) {}
182
183 virtual ~WindowsRealTimeClock() {}
184
stefan@webrtc.orgafcc6102013-04-09 13:37:40 +0000185 protected:
186 timeval CurrentTimeVal() const {
pbos@webrtc.org046deb92013-04-09 09:06:11 +0000187 const uint64_t FILETIME_1970 = 0x019db1ded53e8000;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000188
189 FILETIME StartTime;
pbos@webrtc.org046deb92013-04-09 09:06:11 +0000190 uint64_t Time;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000191 struct timeval tv;
192
193 // We can't use query performance counter since they can change depending on
stefan@webrtc.orgafcc6102013-04-09 13:37:40 +0000194 // speed stepping.
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000195 get_time(_helpTimer, StartTime);
196
pbos@webrtc.org046deb92013-04-09 09:06:11 +0000197 Time = (((uint64_t) StartTime.dwHighDateTime) << 32) +
198 (uint64_t) StartTime.dwLowDateTime;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000199
stefan@webrtc.orgafcc6102013-04-09 13:37:40 +0000200 // Convert the hecto-nano second time to tv format.
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000201 Time -= FILETIME_1970;
202
pbos@webrtc.org046deb92013-04-09 09:06:11 +0000203 tv.tv_sec = (uint32_t)(Time / (uint64_t)10000000);
204 tv.tv_usec = (uint32_t)((Time % (uint64_t)10000000) / 10);
stefan@webrtc.orgafcc6102013-04-09 13:37:40 +0000205 return tv;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000206 }
207
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000208 WindowsHelpTimer* _helpTimer;
209};
210
211#elif ((defined WEBRTC_LINUX) || (defined WEBRTC_MAC))
212class UnixRealTimeClock : public RealTimeClock {
213 public:
214 UnixRealTimeClock() {}
215
216 virtual ~UnixRealTimeClock() {}
217
stefan@webrtc.orgafcc6102013-04-09 13:37:40 +0000218 protected:
219 timeval CurrentTimeVal() const {
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000220 struct timeval tv;
221 struct timezone tz;
stefan@webrtc.orgafcc6102013-04-09 13:37:40 +0000222 tz.tz_minuteswest = 0;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000223 tz.tz_dsttime = 0;
224 gettimeofday(&tv, &tz);
stefan@webrtc.orgafcc6102013-04-09 13:37:40 +0000225 return tv;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000226 }
227};
228#endif
229
230
231#if defined(_WIN32)
232// Keeps the global state for the Windows implementation of RtpRtcpClock.
233// Note that this is a POD. Only PODs are allowed to have static storage
234// duration according to the Google Style guide.
235static WindowsHelpTimer global_help_timer = {0, 0, {{ 0, 0}, 0}, 0};
236#endif
237
238Clock* Clock::GetRealTimeClock() {
239#if defined(_WIN32)
stefan@webrtc.orga678a3b2013-01-21 07:42:11 +0000240 static WindowsRealTimeClock clock(&global_help_timer);
241 return &clock;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000242#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
stefan@webrtc.orga678a3b2013-01-21 07:42:11 +0000243 static UnixRealTimeClock clock;
244 return &clock;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000245#else
246 return NULL;
247#endif
248}
249
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000250SimulatedClock::SimulatedClock(int64_t initial_time_us)
251 : time_us_(initial_time_us) {}
252
253int64_t SimulatedClock::TimeInMilliseconds() {
254 return (time_us_ + 500) / 1000;
255}
256
257int64_t SimulatedClock::TimeInMicroseconds() {
258 return time_us_;
259}
260
261void SimulatedClock::CurrentNtp(uint32_t& seconds, uint32_t& fractions) {
262 seconds = (TimeInMilliseconds() / 1000) + kNtpJan1970;
263 fractions = (uint32_t)((TimeInMilliseconds() % 1000) *
264 kMagicNtpFractionalUnit / 1000);
265}
266
stefan@webrtc.orgafcc6102013-04-09 13:37:40 +0000267int64_t SimulatedClock::CurrentNtpInMilliseconds() {
268 return TimeInMilliseconds() + 1000 * static_cast<int64_t>(kNtpJan1970);
269}
270
stefan@webrtc.orga678a3b2013-01-21 07:42:11 +0000271void SimulatedClock::AdvanceTimeMilliseconds(int64_t milliseconds) {
272 AdvanceTimeMicroseconds(1000 * milliseconds);
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000273}
274
stefan@webrtc.orga678a3b2013-01-21 07:42:11 +0000275void SimulatedClock::AdvanceTimeMicroseconds(int64_t microseconds) {
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000276 time_us_ += microseconds;
277}
278
279}; // namespace webrtc