blob: 8085e56d429a4cb439f9376aeabf3217ec7cc6c6 [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
26#if defined(_WIN32)
27
28struct reference_point {
29 FILETIME file_time;
30 LARGE_INTEGER counterMS;
31};
32
33struct WindowsHelpTimer {
34 volatile LONG _timeInMs;
35 volatile LONG _numWrapTimeInMs;
36 reference_point _ref_point;
37
38 volatile LONG _sync_flag;
39};
40
41void Synchronize(WindowsHelpTimer* help_timer) {
42 const LONG start_value = 0;
43 const LONG new_value = 1;
44 const LONG synchronized_value = 2;
45
46 LONG compare_flag = new_value;
47 while (help_timer->_sync_flag == start_value) {
48 const LONG new_value = 1;
49 compare_flag = InterlockedCompareExchange(
50 &help_timer->_sync_flag, new_value, start_value);
51 }
52 if (compare_flag != start_value) {
53 // This thread was not the one that incremented the sync flag.
54 // Block until synchronization finishes.
55 while (compare_flag != synchronized_value) {
56 ::Sleep(0);
57 }
58 return;
59 }
60 // Only the synchronizing thread gets here so this part can be
61 // considered single threaded.
62
63 // set timer accuracy to 1 ms
64 timeBeginPeriod(1);
65 FILETIME ft0 = { 0, 0 },
66 ft1 = { 0, 0 };
67 //
68 // Spin waiting for a change in system time. Get the matching
69 // performance counter value for that time.
70 //
71 ::GetSystemTimeAsFileTime(&ft0);
72 do {
73 ::GetSystemTimeAsFileTime(&ft1);
74
75 help_timer->_ref_point.counterMS.QuadPart = ::timeGetTime();
76 ::Sleep(0);
77 } while ((ft0.dwHighDateTime == ft1.dwHighDateTime) &&
78 (ft0.dwLowDateTime == ft1.dwLowDateTime));
mflodman@webrtc.orgc7e935f2013-01-17 17:12:50 +000079 help_timer->_ref_point.file_time = ft1;
80 timeEndPeriod(1);
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +000081}
82
83void get_time(WindowsHelpTimer* help_timer, FILETIME& current_time) {
84 // we can't use query performance counter due to speed stepping
85 DWORD t = timeGetTime();
86 // NOTE: we have a missmatch in sign between _timeInMs(LONG) and
87 // (DWORD) however we only use it here without +- etc
88 volatile LONG* timeInMsPtr = &help_timer->_timeInMs;
89 // Make sure that we only inc wrapper once.
90 DWORD old = InterlockedExchange(timeInMsPtr, t);
91 if(old > t) {
92 // wrap
93 help_timer->_numWrapTimeInMs++;
94 }
95 LARGE_INTEGER elapsedMS;
96 elapsedMS.HighPart = help_timer->_numWrapTimeInMs;
97 elapsedMS.LowPart = t;
98
99 elapsedMS.QuadPart = elapsedMS.QuadPart -
100 help_timer->_ref_point.counterMS.QuadPart;
101
102 // Translate to 100-nanoseconds intervals (FILETIME resolution)
103 // and add to reference FILETIME to get current FILETIME.
104 ULARGE_INTEGER filetime_ref_as_ul;
105
106 filetime_ref_as_ul.HighPart =
107 help_timer->_ref_point.file_time.dwHighDateTime;
108 filetime_ref_as_ul.LowPart =
109 help_timer->_ref_point.file_time.dwLowDateTime;
110 filetime_ref_as_ul.QuadPart +=
111 (ULONGLONG)((elapsedMS.QuadPart)*1000*10);
112
113 // Copy to result
114 current_time.dwHighDateTime = filetime_ref_as_ul.HighPart;
115 current_time.dwLowDateTime = filetime_ref_as_ul.LowPart;
116}
117#endif
118
119class RealTimeClock : public Clock {
120 // Return a timestamp in milliseconds relative to some arbitrary source; the
121 // source is fixed for this clock.
122 virtual int64_t TimeInMilliseconds() {
123 return TickTime::MillisecondTimestamp();
124 }
125
126 // Return a timestamp in microseconds relative to some arbitrary source; the
127 // source is fixed for this clock.
128 virtual int64_t TimeInMicroseconds() {
129 return TickTime::MicrosecondTimestamp();
130 }
131};
132
133#if defined(_WIN32)
134class WindowsRealTimeClock : public RealTimeClock {
135 public:
136 WindowsRealTimeClock(WindowsHelpTimer* helpTimer)
137 : _helpTimer(helpTimer) {}
138
139 virtual ~WindowsRealTimeClock() {}
140
stefan@webrtc.org7da34592013-04-09 14:56:29 +0000141 // Retrieve an NTP absolute timestamp.
142 virtual void CurrentNtp(uint32_t& seconds, uint32_t& fractions) {
pbos@webrtc.org046deb92013-04-09 09:06:11 +0000143 const uint64_t FILETIME_1970 = 0x019db1ded53e8000;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000144
145 FILETIME StartTime;
pbos@webrtc.org046deb92013-04-09 09:06:11 +0000146 uint64_t Time;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000147 struct timeval tv;
148
149 // We can't use query performance counter since they can change depending on
stefan@webrtc.org7da34592013-04-09 14:56:29 +0000150 // speed steping
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000151 get_time(_helpTimer, StartTime);
152
pbos@webrtc.org046deb92013-04-09 09:06:11 +0000153 Time = (((uint64_t) StartTime.dwHighDateTime) << 32) +
154 (uint64_t) StartTime.dwLowDateTime;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000155
stefan@webrtc.org7da34592013-04-09 14:56:29 +0000156 // Convert the hecto-nano second time to tv format
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000157 Time -= FILETIME_1970;
158
pbos@webrtc.org046deb92013-04-09 09:06:11 +0000159 tv.tv_sec = (uint32_t)(Time / (uint64_t)10000000);
160 tv.tv_usec = (uint32_t)((Time % (uint64_t)10000000) / 10);
stefan@webrtc.org7da34592013-04-09 14:56:29 +0000161
162 double dtemp;
163
164 seconds = tv.tv_sec + kNtpJan1970;
165 dtemp = tv.tv_usec / 1e6;
166
167 if (dtemp >= 1) {
168 dtemp -= 1;
169 seconds++;
170 } else if (dtemp < -1) {
171 dtemp += 1;
172 seconds--;
173 }
174 dtemp *= kMagicNtpFractionalUnit;
175 fractions = (uint32_t)dtemp;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000176 }
177
stefan@webrtc.org7da34592013-04-09 14:56:29 +0000178 private:
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000179 WindowsHelpTimer* _helpTimer;
180};
181
182#elif ((defined WEBRTC_LINUX) || (defined WEBRTC_MAC))
183class UnixRealTimeClock : public RealTimeClock {
184 public:
185 UnixRealTimeClock() {}
186
187 virtual ~UnixRealTimeClock() {}
188
stefan@webrtc.org7da34592013-04-09 14:56:29 +0000189 // Retrieve an NTP absolute timestamp.
190 virtual void CurrentNtp(uint32_t& seconds, uint32_t& fractions) {
191 double dtemp;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000192 struct timeval tv;
193 struct timezone tz;
stefan@webrtc.org7da34592013-04-09 14:56:29 +0000194 tz.tz_minuteswest = 0;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000195 tz.tz_dsttime = 0;
196 gettimeofday(&tv, &tz);
stefan@webrtc.org7da34592013-04-09 14:56:29 +0000197
198 seconds = tv.tv_sec + kNtpJan1970;
199 dtemp = tv.tv_usec / 1e6;
200 if (dtemp >= 1) {
201 dtemp -= 1;
202 seconds++;
203 } else if (dtemp < -1) {
204 dtemp += 1;
205 seconds--;
206 }
207 dtemp *= kMagicNtpFractionalUnit;
208 fractions = (uint32_t)dtemp;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000209 }
210};
211#endif
212
213
214#if defined(_WIN32)
215// Keeps the global state for the Windows implementation of RtpRtcpClock.
216// Note that this is a POD. Only PODs are allowed to have static storage
217// duration according to the Google Style guide.
218static WindowsHelpTimer global_help_timer = {0, 0, {{ 0, 0}, 0}, 0};
219#endif
220
221Clock* Clock::GetRealTimeClock() {
222#if defined(_WIN32)
stefan@webrtc.orga678a3b2013-01-21 07:42:11 +0000223 static WindowsRealTimeClock clock(&global_help_timer);
224 return &clock;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000225#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
stefan@webrtc.orga678a3b2013-01-21 07:42:11 +0000226 static UnixRealTimeClock clock;
227 return &clock;
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000228#else
229 return NULL;
230#endif
231}
232
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000233SimulatedClock::SimulatedClock(int64_t initial_time_us)
234 : time_us_(initial_time_us) {}
235
236int64_t SimulatedClock::TimeInMilliseconds() {
237 return (time_us_ + 500) / 1000;
238}
239
240int64_t SimulatedClock::TimeInMicroseconds() {
241 return time_us_;
242}
243
244void SimulatedClock::CurrentNtp(uint32_t& seconds, uint32_t& fractions) {
245 seconds = (TimeInMilliseconds() / 1000) + kNtpJan1970;
246 fractions = (uint32_t)((TimeInMilliseconds() % 1000) *
247 kMagicNtpFractionalUnit / 1000);
248}
249
stefan@webrtc.orga678a3b2013-01-21 07:42:11 +0000250void SimulatedClock::AdvanceTimeMilliseconds(int64_t milliseconds) {
251 AdvanceTimeMicroseconds(1000 * milliseconds);
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000252}
253
stefan@webrtc.orga678a3b2013-01-21 07:42:11 +0000254void SimulatedClock::AdvanceTimeMicroseconds(int64_t microseconds) {
stefan@webrtc.org20ed36d2013-01-17 14:01:20 +0000255 time_us_ += microseconds;
256}
257
258}; // namespace webrtc