diff --git a/webrtc/system_wrappers/source/clock.cc b/webrtc/system_wrappers/source/clock.cc index b341a5f06..aa80f8fb5 100644 --- a/webrtc/system_wrappers/source/clock.cc +++ b/webrtc/system_wrappers/source/clock.cc @@ -33,6 +33,99 @@ int64_t Clock::NtpToMs(uint32_t ntp_secs, uint32_t ntp_frac) { static_cast(ntp_frac_ms + 0.5); } +#if defined(_WIN32) + +struct reference_point { + FILETIME file_time; + LARGE_INTEGER counterMS; +}; + +struct WindowsHelpTimer { + volatile LONG _timeInMs; + volatile LONG _numWrapTimeInMs; + reference_point _ref_point; + + volatile LONG _sync_flag; +}; + +void Synchronize(WindowsHelpTimer* help_timer) { + const LONG start_value = 0; + const LONG new_value = 1; + const LONG synchronized_value = 2; + + LONG compare_flag = new_value; + while (help_timer->_sync_flag == start_value) { + const LONG new_value = 1; + compare_flag = InterlockedCompareExchange( + &help_timer->_sync_flag, new_value, start_value); + } + if (compare_flag != start_value) { + // This thread was not the one that incremented the sync flag. + // Block until synchronization finishes. + while (compare_flag != synchronized_value) { + ::Sleep(0); + } + return; + } + // Only the synchronizing thread gets here so this part can be + // considered single threaded. + + // set timer accuracy to 1 ms + timeBeginPeriod(1); + FILETIME ft0 = { 0, 0 }, + ft1 = { 0, 0 }; + // + // Spin waiting for a change in system time. Get the matching + // performance counter value for that time. + // + ::GetSystemTimeAsFileTime(&ft0); + do { + ::GetSystemTimeAsFileTime(&ft1); + + help_timer->_ref_point.counterMS.QuadPart = ::timeGetTime(); + ::Sleep(0); + } while ((ft0.dwHighDateTime == ft1.dwHighDateTime) && + (ft0.dwLowDateTime == ft1.dwLowDateTime)); + help_timer->_ref_point.file_time = ft1; + timeEndPeriod(1); +} + +void get_time(WindowsHelpTimer* help_timer, FILETIME& current_time) { + // we can't use query performance counter due to speed stepping + DWORD t = timeGetTime(); + // NOTE: we have a missmatch in sign between _timeInMs(LONG) and + // (DWORD) however we only use it here without +- etc + volatile LONG* timeInMsPtr = &help_timer->_timeInMs; + // Make sure that we only inc wrapper once. + DWORD old = InterlockedExchange(timeInMsPtr, t); + if(old > t) { + // wrap + help_timer->_numWrapTimeInMs++; + } + LARGE_INTEGER elapsedMS; + elapsedMS.HighPart = help_timer->_numWrapTimeInMs; + elapsedMS.LowPart = t; + + elapsedMS.QuadPart = elapsedMS.QuadPart - + help_timer->_ref_point.counterMS.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 = + help_timer->_ref_point.file_time.dwHighDateTime; + filetime_ref_as_ul.LowPart = + help_timer->_ref_point.file_time.dwLowDateTime; + filetime_ref_as_ul.QuadPart += + (ULONGLONG)((elapsedMS.QuadPart)*1000*10); + + // Copy to result + current_time.dwHighDateTime = filetime_ref_as_ul.HighPart; + current_time.dwLowDateTime = filetime_ref_as_ul.LowPart; +} +#endif + class RealTimeClock : public Clock { // Return a timestamp in milliseconds relative to some arbitrary source; the // source is fixed for this clock. @@ -86,7 +179,8 @@ class RealTimeClock : public Clock { #if defined(_WIN32) class WindowsRealTimeClock : public RealTimeClock { public: - WindowsRealTimeClock() {} + WindowsRealTimeClock(WindowsHelpTimer* helpTimer) + : _helpTimer(helpTimer) {} virtual ~WindowsRealTimeClock() {} @@ -98,7 +192,9 @@ class WindowsRealTimeClock : public RealTimeClock { uint64_t Time; struct timeval tv; - GetSystemTimeAsFileTime(&StartTime); + // We can't use query performance counter since they can change depending on + // speed stepping. + get_time(_helpTimer, StartTime); Time = (((uint64_t) StartTime.dwHighDateTime) << 32) + (uint64_t) StartTime.dwLowDateTime; @@ -110,6 +206,8 @@ class WindowsRealTimeClock : public RealTimeClock { tv.tv_usec = (uint32_t)((Time % (uint64_t)10000000) / 10); return tv; } + + WindowsHelpTimer* _helpTimer; }; #elif ((defined WEBRTC_LINUX) || (defined WEBRTC_MAC)) @@ -131,9 +229,31 @@ class UnixRealTimeClock : public RealTimeClock { }; #endif + +#if defined(_WIN32) +// Keeps the global state for the Windows implementation of RtpRtcpClock. +// Note that this is a POD. Only PODs are allowed to have static storage +// duration according to the Google Style guide. +// +// Note that on Windows, GetSystemTimeAsFileTime has poorer (up to 15 ms) +// resolution than the media timers, hence the WindowsHelpTimer context +// object and Synchronize API to sync the two. +// +// We only sync up once, which means that on Windows, our realtime clock +// wont respond to system time/date changes without a program restart. +// TODO(henrike): We should probably call sync more often to catch +// drift and time changes for parity with other platforms. + +static WindowsHelpTimer *SyncGlobalHelpTimer() { + static WindowsHelpTimer global_help_timer = {0, 0, {{ 0, 0}, 0}, 0}; + Synchronize(&global_help_timer); + return &global_help_timer; +} +#endif + Clock* Clock::GetRealTimeClock() { #if defined(_WIN32) - static WindowsRealTimeClock clock; + static WindowsRealTimeClock clock(SyncGlobalHelpTimer()); return &clock; #elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) static UnixRealTimeClock clock;