blob: c74c7471d18007463ff1cd39bd937549f6c8f7a0 [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)
pbos@webrtc.orgacaf3a12013-05-27 15:07:45 +000014// Windows needs to be included before mmsystem.h
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +000015#include <Windows.h>
16#include <WinSock.h>
17#include <MMSystem.h>
18#elif ((defined WEBRTC_LINUX) || (defined WEBRTC_MAC))
19#include <sys/time.h>
20#include <time.h>
21#endif
22
23#include "webrtc/system_wrappers/interface/tick_util.h"
24
25namespace webrtc {
26
stefan@webrtc.orgb8e7f4c2013-04-12 11:56:23 +000027const double kNtpFracPerMs = 4.294967296E6;
28
29int64_t Clock::NtpToMs(uint32_t ntp_secs, uint32_t ntp_frac) {
30 const double ntp_frac_ms = static_cast<double>(ntp_frac) / kNtpFracPerMs;
31 return 1000 * static_cast<int64_t>(ntp_secs) +
32 static_cast<int64_t>(ntp_frac_ms + 0.5);
33}
34
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +000035#if defined(_WIN32)
36
37struct reference_point {
38 FILETIME file_time;
39 LARGE_INTEGER counterMS;
40};
41
42struct WindowsHelpTimer {
43 volatile LONG _timeInMs;
44 volatile LONG _numWrapTimeInMs;
45 reference_point _ref_point;
46
47 volatile LONG _sync_flag;
48};
49
50void Synchronize(WindowsHelpTimer* help_timer) {
51 const LONG start_value = 0;
52 const LONG new_value = 1;
53 const LONG synchronized_value = 2;
54
55 LONG compare_flag = new_value;
56 while (help_timer->_sync_flag == start_value) {
57 const LONG new_value = 1;
58 compare_flag = InterlockedCompareExchange(
59 &help_timer->_sync_flag, new_value, start_value);
60 }
61 if (compare_flag != start_value) {
62 // This thread was not the one that incremented the sync flag.
63 // Block until synchronization finishes.
64 while (compare_flag != synchronized_value) {
65 ::Sleep(0);
66 }
67 return;
68 }
69 // Only the synchronizing thread gets here so this part can be
70 // considered single threaded.
71
72 // set timer accuracy to 1 ms
73 timeBeginPeriod(1);
74 FILETIME ft0 = { 0, 0 },
75 ft1 = { 0, 0 };
76 //
77 // Spin waiting for a change in system time. Get the matching
78 // performance counter value for that time.
79 //
80 ::GetSystemTimeAsFileTime(&ft0);
81 do {
82 ::GetSystemTimeAsFileTime(&ft1);
83
84 help_timer->_ref_point.counterMS.QuadPart = ::timeGetTime();
85 ::Sleep(0);
86 } while ((ft0.dwHighDateTime == ft1.dwHighDateTime) &&
87 (ft0.dwLowDateTime == ft1.dwLowDateTime));
mflodman@webrtc.orgc7e935f2013-01-17 17:12:50 +000088 help_timer->_ref_point.file_time = ft1;
89 timeEndPeriod(1);
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +000090}
91
92void get_time(WindowsHelpTimer* help_timer, FILETIME& current_time) {
93 // we can't use query performance counter due to speed stepping
94 DWORD t = timeGetTime();
95 // NOTE: we have a missmatch in sign between _timeInMs(LONG) and
96 // (DWORD) however we only use it here without +- etc
97 volatile LONG* timeInMsPtr = &help_timer->_timeInMs;
98 // Make sure that we only inc wrapper once.
99 DWORD old = InterlockedExchange(timeInMsPtr, t);
100 if(old > t) {
101 // wrap
102 help_timer->_numWrapTimeInMs++;
103 }
104 LARGE_INTEGER elapsedMS;
105 elapsedMS.HighPart = help_timer->_numWrapTimeInMs;
106 elapsedMS.LowPart = t;
107
108 elapsedMS.QuadPart = elapsedMS.QuadPart -
109 help_timer->_ref_point.counterMS.QuadPart;
110
111 // Translate to 100-nanoseconds intervals (FILETIME resolution)
112 // and add to reference FILETIME to get current FILETIME.
113 ULARGE_INTEGER filetime_ref_as_ul;
114
115 filetime_ref_as_ul.HighPart =
116 help_timer->_ref_point.file_time.dwHighDateTime;
117 filetime_ref_as_ul.LowPart =
118 help_timer->_ref_point.file_time.dwLowDateTime;
119 filetime_ref_as_ul.QuadPart +=
120 (ULONGLONG)((elapsedMS.QuadPart)*1000*10);
121
122 // Copy to result
123 current_time.dwHighDateTime = filetime_ref_as_ul.HighPart;
124 current_time.dwLowDateTime = filetime_ref_as_ul.LowPart;
125}
126#endif
127
128class RealTimeClock : public Clock {
129 // Return a timestamp in milliseconds relative to some arbitrary source; the
130 // source is fixed for this clock.
131 virtual int64_t TimeInMilliseconds() {
132 return TickTime::MillisecondTimestamp();
133 }
134
135 // Return a timestamp in microseconds relative to some arbitrary source; the
136 // source is fixed for this clock.
137 virtual int64_t TimeInMicroseconds() {
138 return TickTime::MicrosecondTimestamp();
139 }
stefan@webrtc.orgb8e7f4c2013-04-12 11:56:23 +0000140
141 // Retrieve an NTP absolute timestamp in seconds and fractions of a second.
142 virtual void CurrentNtp(uint32_t& seconds, uint32_t& fractions) {
143 timeval tv = CurrentTimeVal();
144 double microseconds_in_seconds;
145 Adjust(tv, &seconds, &microseconds_in_seconds);
146 fractions = static_cast<uint32_t>(
147 microseconds_in_seconds * kMagicNtpFractionalUnit + 0.5);
148 }
149
150 // Retrieve an NTP absolute timestamp in milliseconds.
151 virtual int64_t CurrentNtpInMilliseconds() {
152 timeval tv = CurrentTimeVal();
153 uint32_t seconds;
154 double microseconds_in_seconds;
155 Adjust(tv, &seconds, &microseconds_in_seconds);
156 return 1000 * static_cast<int64_t>(seconds) +
157 static_cast<int64_t>(1000.0 * microseconds_in_seconds + 0.5);
158 }
159
160 protected:
161 virtual timeval CurrentTimeVal() const = 0;
162
163 static void Adjust(const timeval& tv, uint32_t* adjusted_s,
164 double* adjusted_us_in_s) {
165 *adjusted_s = tv.tv_sec + kNtpJan1970;
166 *adjusted_us_in_s = tv.tv_usec / 1e6;
167
168 if (*adjusted_us_in_s >= 1) {
169 *adjusted_us_in_s -= 1;
170 ++*adjusted_s;
171 } else if (*adjusted_us_in_s < -1) {
172 *adjusted_us_in_s += 1;
173 --*adjusted_s;
174 }
175 }
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000176};
177
178#if defined(_WIN32)
179class WindowsRealTimeClock : public RealTimeClock {
180 public:
181 WindowsRealTimeClock(WindowsHelpTimer* helpTimer)
182 : _helpTimer(helpTimer) {}
183
184 virtual ~WindowsRealTimeClock() {}
185
stefan@webrtc.orgb8e7f4c2013-04-12 11:56:23 +0000186 protected:
187 timeval CurrentTimeVal() const {
pbos@webrtc.org046deb92013-04-09 09:06:11 +0000188 const uint64_t FILETIME_1970 = 0x019db1ded53e8000;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000189
190 FILETIME StartTime;
pbos@webrtc.org046deb92013-04-09 09:06:11 +0000191 uint64_t Time;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000192 struct timeval tv;
193
194 // We can't use query performance counter since they can change depending on
stefan@webrtc.orgb8e7f4c2013-04-12 11:56:23 +0000195 // speed stepping.
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000196 get_time(_helpTimer, StartTime);
197
pbos@webrtc.org046deb92013-04-09 09:06:11 +0000198 Time = (((uint64_t) StartTime.dwHighDateTime) << 32) +
199 (uint64_t) StartTime.dwLowDateTime;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000200
stefan@webrtc.orgb8e7f4c2013-04-12 11:56:23 +0000201 // Convert the hecto-nano second time to tv format.
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000202 Time -= FILETIME_1970;
203
pbos@webrtc.org046deb92013-04-09 09:06:11 +0000204 tv.tv_sec = (uint32_t)(Time / (uint64_t)10000000);
205 tv.tv_usec = (uint32_t)((Time % (uint64_t)10000000) / 10);
stefan@webrtc.orgb8e7f4c2013-04-12 11:56:23 +0000206 return tv;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000207 }
208
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000209 WindowsHelpTimer* _helpTimer;
210};
211
212#elif ((defined WEBRTC_LINUX) || (defined WEBRTC_MAC))
213class UnixRealTimeClock : public RealTimeClock {
214 public:
215 UnixRealTimeClock() {}
216
217 virtual ~UnixRealTimeClock() {}
218
stefan@webrtc.orgb8e7f4c2013-04-12 11:56:23 +0000219 protected:
220 timeval CurrentTimeVal() const {
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000221 struct timeval tv;
222 struct timezone tz;
stefan@webrtc.orgb8e7f4c2013-04-12 11:56:23 +0000223 tz.tz_minuteswest = 0;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000224 tz.tz_dsttime = 0;
225 gettimeofday(&tv, &tz);
stefan@webrtc.orgb8e7f4c2013-04-12 11:56:23 +0000226 return tv;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000227 }
228};
229#endif
230
231
232#if defined(_WIN32)
233// Keeps the global state for the Windows implementation of RtpRtcpClock.
234// Note that this is a POD. Only PODs are allowed to have static storage
235// duration according to the Google Style guide.
236static WindowsHelpTimer global_help_timer = {0, 0, {{ 0, 0}, 0}, 0};
237#endif
238
239Clock* Clock::GetRealTimeClock() {
240#if defined(_WIN32)
stefan@webrtc.orga678a3b2013-01-21 07:42:11 +0000241 static WindowsRealTimeClock clock(&global_help_timer);
242 return &clock;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000243#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
stefan@webrtc.orga678a3b2013-01-21 07:42:11 +0000244 static UnixRealTimeClock clock;
245 return &clock;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000246#else
247 return NULL;
248#endif
249}
250
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000251SimulatedClock::SimulatedClock(int64_t initial_time_us)
252 : time_us_(initial_time_us) {}
253
254int64_t SimulatedClock::TimeInMilliseconds() {
255 return (time_us_ + 500) / 1000;
256}
257
258int64_t SimulatedClock::TimeInMicroseconds() {
259 return time_us_;
260}
261
262void SimulatedClock::CurrentNtp(uint32_t& seconds, uint32_t& fractions) {
263 seconds = (TimeInMilliseconds() / 1000) + kNtpJan1970;
264 fractions = (uint32_t)((TimeInMilliseconds() % 1000) *
265 kMagicNtpFractionalUnit / 1000);
266}
267
stefan@webrtc.orgb8e7f4c2013-04-12 11:56:23 +0000268int64_t SimulatedClock::CurrentNtpInMilliseconds() {
269 return TimeInMilliseconds() + 1000 * static_cast<int64_t>(kNtpJan1970);
270}
271
stefan@webrtc.orga678a3b2013-01-21 07:42:11 +0000272void SimulatedClock::AdvanceTimeMilliseconds(int64_t milliseconds) {
273 AdvanceTimeMicroseconds(1000 * milliseconds);
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000274}
275
stefan@webrtc.orga678a3b2013-01-21 07:42:11 +0000276void SimulatedClock::AdvanceTimeMicroseconds(int64_t microseconds) {
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000277 time_us_ += microseconds;
278}
279
280}; // namespace webrtc