blob: 03f1f7a5f0b9fa773a4a8267cdb2e049088a8225 [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));
79 help_timer->_ref_point.file_time = ft1;
80}
81
82void get_time(WindowsHelpTimer* help_timer, FILETIME& current_time) {
83 // we can't use query performance counter due to speed stepping
84 DWORD t = timeGetTime();
85 // NOTE: we have a missmatch in sign between _timeInMs(LONG) and
86 // (DWORD) however we only use it here without +- etc
87 volatile LONG* timeInMsPtr = &help_timer->_timeInMs;
88 // Make sure that we only inc wrapper once.
89 DWORD old = InterlockedExchange(timeInMsPtr, t);
90 if(old > t) {
91 // wrap
92 help_timer->_numWrapTimeInMs++;
93 }
94 LARGE_INTEGER elapsedMS;
95 elapsedMS.HighPart = help_timer->_numWrapTimeInMs;
96 elapsedMS.LowPart = t;
97
98 elapsedMS.QuadPart = elapsedMS.QuadPart -
99 help_timer->_ref_point.counterMS.QuadPart;
100
101 // Translate to 100-nanoseconds intervals (FILETIME resolution)
102 // and add to reference FILETIME to get current FILETIME.
103 ULARGE_INTEGER filetime_ref_as_ul;
104
105 filetime_ref_as_ul.HighPart =
106 help_timer->_ref_point.file_time.dwHighDateTime;
107 filetime_ref_as_ul.LowPart =
108 help_timer->_ref_point.file_time.dwLowDateTime;
109 filetime_ref_as_ul.QuadPart +=
110 (ULONGLONG)((elapsedMS.QuadPart)*1000*10);
111
112 // Copy to result
113 current_time.dwHighDateTime = filetime_ref_as_ul.HighPart;
114 current_time.dwLowDateTime = filetime_ref_as_ul.LowPart;
115}
116#endif
117
118class RealTimeClock : public Clock {
119 // Return a timestamp in milliseconds relative to some arbitrary source; the
120 // source is fixed for this clock.
121 virtual int64_t TimeInMilliseconds() {
122 return TickTime::MillisecondTimestamp();
123 }
124
125 // Return a timestamp in microseconds relative to some arbitrary source; the
126 // source is fixed for this clock.
127 virtual int64_t TimeInMicroseconds() {
128 return TickTime::MicrosecondTimestamp();
129 }
130};
131
132#if defined(_WIN32)
133class WindowsRealTimeClock : public RealTimeClock {
134 public:
135 WindowsRealTimeClock(WindowsHelpTimer* helpTimer)
136 : _helpTimer(helpTimer) {}
137
138 virtual ~WindowsRealTimeClock() {}
139
140 // Retrieve an NTP absolute timestamp.
141 virtual void CurrentNtp(uint32_t& seconds, uint32_t& fractions) {
142 const WebRtc_UWord64 FILETIME_1970 = 0x019db1ded53e8000;
143
144 FILETIME StartTime;
145 WebRtc_UWord64 Time;
146 struct timeval tv;
147
148 // We can't use query performance counter since they can change depending on
149 // speed steping
150 get_time(_helpTimer, StartTime);
151
152 Time = (((WebRtc_UWord64) StartTime.dwHighDateTime) << 32) +
153 (WebRtc_UWord64) StartTime.dwLowDateTime;
154
155 // Convert the hecto-nano second time to tv format
156 Time -= FILETIME_1970;
157
158 tv.tv_sec = (WebRtc_UWord32)(Time / (WebRtc_UWord64)10000000);
159 tv.tv_usec = (WebRtc_UWord32)((Time % (WebRtc_UWord64)10000000) / 10);
160
161 double dtemp;
162
163 seconds = tv.tv_sec + kNtpJan1970;
164 dtemp = tv.tv_usec / 1e6;
165
166 if (dtemp >= 1) {
167 dtemp -= 1;
168 seconds++;
169 } else if (dtemp < -1) {
170 dtemp += 1;
171 seconds--;
172 }
173 dtemp *= kMagicNtpFractionalUnit;
174 fractions = (WebRtc_UWord32)dtemp;
175 }
176
177 private:
178 WindowsHelpTimer* _helpTimer;
179};
180
181#elif ((defined WEBRTC_LINUX) || (defined WEBRTC_MAC))
182class UnixRealTimeClock : public RealTimeClock {
183 public:
184 UnixRealTimeClock() {}
185
186 virtual ~UnixRealTimeClock() {}
187
188 // Retrieve an NTP absolute timestamp.
189 virtual void CurrentNtp(uint32_t& seconds, uint32_t& fractions) {
190 double dtemp;
191 struct timeval tv;
192 struct timezone tz;
193 tz.tz_minuteswest = 0;
194 tz.tz_dsttime = 0;
195 gettimeofday(&tv, &tz);
196
197 seconds = tv.tv_sec + kNtpJan1970;
198 dtemp = tv.tv_usec / 1e6;
199 if (dtemp >= 1) {
200 dtemp -= 1;
201 seconds++;
202 } else if (dtemp < -1) {
203 dtemp += 1;
204 seconds--;
205 }
206 dtemp *= kMagicNtpFractionalUnit;
207 fractions = (WebRtc_UWord32)dtemp;
208 }
209};
210#endif
211
212
213#if defined(_WIN32)
214// Keeps the global state for the Windows implementation of RtpRtcpClock.
215// Note that this is a POD. Only PODs are allowed to have static storage
216// duration according to the Google Style guide.
217static WindowsHelpTimer global_help_timer = {0, 0, {{ 0, 0}, 0}, 0};
218#endif
219
220Clock* Clock::GetRealTimeClock() {
221#if defined(_WIN32)
222 return new WindowsRealTimeClock(&global_help_timer);
223#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
224 return new UnixRealTimeClock();
225#else
226 return NULL;
227#endif
228}
229
230SimulatedClock::SimulatedClock() : time_us_(0) {}
231
232SimulatedClock::SimulatedClock(int64_t initial_time_us)
233 : time_us_(initial_time_us) {}
234
235int64_t SimulatedClock::TimeInMilliseconds() {
236 return (time_us_ + 500) / 1000;
237}
238
239int64_t SimulatedClock::TimeInMicroseconds() {
240 return time_us_;
241}
242
243void SimulatedClock::CurrentNtp(uint32_t& seconds, uint32_t& fractions) {
244 seconds = (TimeInMilliseconds() / 1000) + kNtpJan1970;
245 fractions = (uint32_t)((TimeInMilliseconds() % 1000) *
246 kMagicNtpFractionalUnit / 1000);
247}
248
249void SimulatedClock::AdvanceTimeMs(int64_t milliseconds) {
250 AdvanceTimeUs(1000 * milliseconds);
251}
252
253void SimulatedClock::AdvanceTimeUs(int64_t microseconds) {
254 time_us_ += microseconds;
255}
256
257}; // namespace webrtc