diff options
Diffstat (limited to 'third_party/libwebrtc/webrtc/system_wrappers/source')
33 files changed, 3841 insertions, 0 deletions
diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/aligned_array_unittest.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/aligned_array_unittest.cc new file mode 100644 index 0000000000..e5a3c18fcc --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/aligned_array_unittest.cc @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/aligned_array.h" + +#include <stdint.h> + +#include "test/gtest.h" + +namespace { + +bool IsAligned(const void* ptr, size_t alignment) { + return reinterpret_cast<uintptr_t>(ptr) % alignment == 0; +} + +} // namespace + +namespace webrtc { + +TEST(AlignedArrayTest, CheckAlignment) { + AlignedArray<bool> arr(10, 7, 128); + ASSERT_TRUE(IsAligned(arr.Array(), 128)); + for (size_t i = 0; i < 10; ++i) { + ASSERT_TRUE(IsAligned(arr.Row(i), 128)); + ASSERT_EQ(arr.Row(i), arr.Array()[i]); + } +} + +TEST(AlignedArrayTest, CheckOverlap) { + AlignedArray<size_t> arr(10, 7, 128); + + for (size_t i = 0; i < 10; ++i) { + for (size_t j = 0; j < 7; ++j) { + arr.At(i, j) = 20 * i + j; + } + } + + for (size_t i = 0; i < 10; ++i) { + for (size_t j = 0; j < 7; ++j) { + ASSERT_EQ(arr.At(i, j), 20 * i + j); + ASSERT_EQ(arr.Row(i)[j], 20 * i + j); + ASSERT_EQ(arr.Array()[i][j], 20 * i + j); + } + } +} + +TEST(AlignedArrayTest, CheckRowsCols) { + AlignedArray<bool> arr(10, 7, 128); + ASSERT_EQ(arr.rows(), 10u); + ASSERT_EQ(arr.cols(), 7u); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/aligned_malloc.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/aligned_malloc.cc new file mode 100644 index 0000000000..43ece9e681 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/aligned_malloc.cc @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/aligned_malloc.h" + +#include <memory.h> +#include <stdlib.h> + +#ifdef _WIN32 +#include <windows.h> +#else +#include <stdint.h> +#endif + +#include "typedefs.h" // NOLINT(build/include) + +// Reference on memory alignment: +// http://stackoverflow.com/questions/227897/solve-the-memory-alignment-in-c-interview-question-that-stumped-me +namespace webrtc { + +uintptr_t GetRightAlign(uintptr_t start_pos, size_t alignment) { + // The pointer should be aligned with |alignment| bytes. The - 1 guarantees + // that it is aligned towards the closest higher (right) address. + return (start_pos + alignment - 1) & ~(alignment - 1); +} + +// Alignment must be an integer power of two. +bool ValidAlignment(size_t alignment) { + if (!alignment) { + return false; + } + return (alignment & (alignment - 1)) == 0; +} + +void* GetRightAlign(const void* pointer, size_t alignment) { + if (!pointer) { + return NULL; + } + if (!ValidAlignment(alignment)) { + return NULL; + } + uintptr_t start_pos = reinterpret_cast<uintptr_t>(pointer); + return reinterpret_cast<void*>(GetRightAlign(start_pos, alignment)); +} + +void* AlignedMalloc(size_t size, size_t alignment) { + if (size == 0) { + return NULL; + } + if (!ValidAlignment(alignment)) { + return NULL; + } + + // The memory is aligned towards the lowest address that so only + // alignment - 1 bytes needs to be allocated. + // A pointer to the start of the memory must be stored so that it can be + // retreived for deletion, ergo the sizeof(uintptr_t). + void* memory_pointer = malloc(size + sizeof(uintptr_t) + alignment - 1); + if (memory_pointer == NULL) { + return NULL; + } + + // Aligning after the sizeof(uintptr_t) bytes will leave room for the header + // in the same memory block. + uintptr_t align_start_pos = reinterpret_cast<uintptr_t>(memory_pointer); + align_start_pos += sizeof(uintptr_t); + uintptr_t aligned_pos = GetRightAlign(align_start_pos, alignment); + void* aligned_pointer = reinterpret_cast<void*>(aligned_pos); + + // Store the address to the beginning of the memory just before the aligned + // memory. + uintptr_t header_pos = aligned_pos - sizeof(uintptr_t); + void* header_pointer = reinterpret_cast<void*>(header_pos); + uintptr_t memory_start = reinterpret_cast<uintptr_t>(memory_pointer); + memcpy(header_pointer, &memory_start, sizeof(uintptr_t)); + + return aligned_pointer; +} + +void AlignedFree(void* mem_block) { + if (mem_block == NULL) { + return; + } + uintptr_t aligned_pos = reinterpret_cast<uintptr_t>(mem_block); + uintptr_t header_pos = aligned_pos - sizeof(uintptr_t); + + // Read out the address of the AlignedMemory struct from the header. + uintptr_t memory_start_pos = *reinterpret_cast<uintptr_t*>(header_pos); + void* memory_start = reinterpret_cast<void*>(memory_start_pos); + free(memory_start); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/aligned_malloc_unittest.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/aligned_malloc_unittest.cc new file mode 100644 index 0000000000..7afbf6d1bb --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/aligned_malloc_unittest.cc @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/aligned_malloc.h" + +#include <memory> + +#ifdef _WIN32 +#include <windows.h> +#else +#include <stdint.h> +#endif + +#include "test/gtest.h" +#include "typedefs.h" // NOLINT(build/include) + +namespace webrtc { + +// Returns true if |size| and |alignment| are valid combinations. +bool CorrectUsage(size_t size, size_t alignment) { + std::unique_ptr<char, AlignedFreeDeleter> scoped( + static_cast<char*>(AlignedMalloc(size, alignment))); + if (scoped.get() == NULL) { + return false; + } + const uintptr_t scoped_address = reinterpret_cast<uintptr_t>(scoped.get()); + return 0u == scoped_address % alignment; +} + +TEST(AlignedMalloc, GetRightAlign) { + const size_t size = 100; + const size_t alignment = 32; + const size_t left_misalignment = 1; + std::unique_ptr<char, AlignedFreeDeleter> scoped( + static_cast<char*>(AlignedMalloc(size, alignment))); + EXPECT_TRUE(scoped.get() != NULL); + const uintptr_t aligned_address = reinterpret_cast<uintptr_t>(scoped.get()); + const uintptr_t misaligned_address = aligned_address - left_misalignment; + const char* misaligned_ptr = + reinterpret_cast<const char*>(misaligned_address); + const char* realigned_ptr = GetRightAlign(misaligned_ptr, alignment); + EXPECT_EQ(scoped.get(), realigned_ptr); +} + +TEST(AlignedMalloc, IncorrectSize) { + const size_t incorrect_size = 0; + const size_t alignment = 64; + EXPECT_FALSE(CorrectUsage(incorrect_size, alignment)); +} + +TEST(AlignedMalloc, IncorrectAlignment) { + const size_t size = 100; + const size_t incorrect_alignment = 63; + EXPECT_FALSE(CorrectUsage(size, incorrect_alignment)); +} + +TEST(AlignedMalloc, AlignTo2Bytes) { + size_t size = 100; + size_t alignment = 2; + EXPECT_TRUE(CorrectUsage(size, alignment)); +} + +TEST(AlignedMalloc, AlignTo32Bytes) { + size_t size = 100; + size_t alignment = 32; + EXPECT_TRUE(CorrectUsage(size, alignment)); +} + +TEST(AlignedMalloc, AlignTo128Bytes) { + size_t size = 100; + size_t alignment = 128; + EXPECT_TRUE(CorrectUsage(size, alignment)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/atomic32.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/atomic32.cc new file mode 100644 index 0000000000..581c13e0c3 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/atomic32.cc @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/atomic32.h" + +#include <assert.h> + +#include "common_types.h" // NOLINT(build/include) + +namespace webrtc { + +Atomic32::Atomic32(int32_t initial_value) : value_(initial_value) {} + +Atomic32::~Atomic32() {} + +int32_t Atomic32::operator++() { + return ++value_; +} + +int32_t Atomic32::operator--() { + return --value_; +} + +int32_t Atomic32::operator+=(int32_t value) { + return value_ += value; +} + +int32_t Atomic32::operator-=(int32_t value) { + return value_ -= value; +} + +bool Atomic32::CompareExchange(int32_t new_value, int32_t compare_value) { + return value_.compare_exchange_strong(compare_value, new_value); +} + +int32_t Atomic32::Value() const { + return value_.load(); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/clock.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/clock.cc new file mode 100644 index 0000000000..61fc4a49a6 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/clock.cc @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/clock.h" + +#if defined(_WIN32) + +// Windows needs to be included before mmsystem.h +#include "rtc_base/win32.h" + +#include <MMSystem.h> + +#elif ((defined WEBRTC_LINUX) || (defined WEBRTC_MAC) || (defined WEBRTC_BSD)) + +#include <sys/time.h> +#include <time.h> + +#endif + +#include "rtc_base/criticalsection.h" +#include "rtc_base/timeutils.h" +#include "system_wrappers/include/rw_lock_wrapper.h" + +namespace webrtc { + +class RealTimeClock : public Clock { + // Return a timestamp in milliseconds relative to some arbitrary source; the + // source is fixed for this clock. + int64_t TimeInMilliseconds() const override { return rtc::TimeMillis(); } + + // Return a timestamp in microseconds relative to some arbitrary source; the + // source is fixed for this clock. + int64_t TimeInMicroseconds() const override { return rtc::TimeMicros(); } + + // Retrieve an NTP absolute timestamp. + NtpTime CurrentNtpTime() const override { + timeval tv = CurrentTimeVal(); + double microseconds_in_seconds; + uint32_t seconds; + Adjust(tv, &seconds, µseconds_in_seconds); + uint32_t fractions = static_cast<uint32_t>( + microseconds_in_seconds * kMagicNtpFractionalUnit + 0.5); + return NtpTime(seconds, fractions); + } + + // Retrieve an NTP absolute timestamp in milliseconds. + int64_t CurrentNtpInMilliseconds() const override { + timeval tv = CurrentTimeVal(); + uint32_t seconds; + double microseconds_in_seconds; + Adjust(tv, &seconds, µseconds_in_seconds); + return 1000 * static_cast<int64_t>(seconds) + + static_cast<int64_t>(1000.0 * microseconds_in_seconds + 0.5); + } + + protected: + virtual timeval CurrentTimeVal() const = 0; + + static void Adjust(const timeval& tv, + uint32_t* adjusted_s, + double* adjusted_us_in_s) { + *adjusted_s = tv.tv_sec + kNtpJan1970; + *adjusted_us_in_s = tv.tv_usec / 1e6; + + if (*adjusted_us_in_s >= 1) { + *adjusted_us_in_s -= 1; + ++*adjusted_s; + } else if (*adjusted_us_in_s < -1) { + *adjusted_us_in_s += 1; + --*adjusted_s; + } + } +}; + +#if defined(_WIN32) +// TODO(pbos): Consider modifying the implementation to synchronize itself +// against system time (update ref_point_, make it non-const) periodically to +// prevent clock drift. +class WindowsRealTimeClock : public RealTimeClock { + public: + WindowsRealTimeClock() + : last_time_ms_(0), + num_timer_wraps_(0), + ref_point_(GetSystemReferencePoint()) {} + + virtual ~WindowsRealTimeClock() {} + + protected: + struct ReferencePoint { + FILETIME file_time; + LARGE_INTEGER counter_ms; + }; + + timeval CurrentTimeVal() const override { + const uint64_t FILETIME_1970 = 0x019db1ded53e8000; + + FILETIME StartTime; + uint64_t Time; + struct timeval tv; + + // We can't use query performance counter since they can change depending on + // speed stepping. + GetTime(&StartTime); + + Time = (((uint64_t)StartTime.dwHighDateTime) << 32) + + (uint64_t)StartTime.dwLowDateTime; + + // Convert the hecto-nano second time to tv format. + Time -= FILETIME_1970; + + tv.tv_sec = (uint32_t)(Time / (uint64_t)10000000); + tv.tv_usec = (uint32_t)((Time % (uint64_t)10000000) / 10); + return tv; + } + + void GetTime(FILETIME* current_time) const { + DWORD t; + LARGE_INTEGER elapsed_ms; + { + rtc::CritScope lock(&crit_); + // time MUST be fetched inside the critical section to avoid non-monotonic + // last_time_ms_ values that'll register as incorrect wraparounds due to + // concurrent calls to GetTime. + t = timeGetTime(); + if (t < last_time_ms_) + num_timer_wraps_++; + last_time_ms_ = t; + elapsed_ms.HighPart = num_timer_wraps_; + } + elapsed_ms.LowPart = t; + elapsed_ms.QuadPart = elapsed_ms.QuadPart - ref_point_.counter_ms.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 = ref_point_.file_time.dwHighDateTime; + filetime_ref_as_ul.LowPart = ref_point_.file_time.dwLowDateTime; + filetime_ref_as_ul.QuadPart += + static_cast<ULONGLONG>((elapsed_ms.QuadPart) * 1000 * 10); + + // Copy to result + current_time->dwHighDateTime = filetime_ref_as_ul.HighPart; + current_time->dwLowDateTime = filetime_ref_as_ul.LowPart; + } + + static ReferencePoint GetSystemReferencePoint() { + ReferencePoint ref = {}; + FILETIME ft0 = {}; + FILETIME ft1 = {}; + // Spin waiting for a change in system time. As soon as this change happens, + // get the matching call for timeGetTime() as soon as possible. This is + // assumed to be the most accurate offset that we can get between + // timeGetTime() and system time. + + // Set timer accuracy to 1 ms. + timeBeginPeriod(1); + GetSystemTimeAsFileTime(&ft0); + do { + GetSystemTimeAsFileTime(&ft1); + + ref.counter_ms.QuadPart = timeGetTime(); + Sleep(0); + } while ((ft0.dwHighDateTime == ft1.dwHighDateTime) && + (ft0.dwLowDateTime == ft1.dwLowDateTime)); + ref.file_time = ft1; + timeEndPeriod(1); + return ref; + } + + // mutable as time-accessing functions are const. + rtc::CriticalSection crit_; + mutable DWORD last_time_ms_; + mutable LONG num_timer_wraps_; + const ReferencePoint ref_point_; +}; + +#elif ((defined WEBRTC_LINUX) || (defined WEBRTC_MAC)) || (defined WEBRTC_BSD) +class UnixRealTimeClock : public RealTimeClock { + public: + UnixRealTimeClock() {} + + ~UnixRealTimeClock() override {} + + protected: + timeval CurrentTimeVal() const override { + struct timeval tv; + struct timezone tz; + tz.tz_minuteswest = 0; + tz.tz_dsttime = 0; + gettimeofday(&tv, &tz); + return tv; + } +}; +#endif + +#if defined(_WIN32) +static WindowsRealTimeClock* volatile g_shared_clock = nullptr; +#endif +Clock* Clock::GetRealTimeClock() { +#if defined(_WIN32) + // This read relies on volatile read being atomic-load-acquire. This is + // true in MSVC since at least 2005: + // "A read of a volatile object (volatile read) has Acquire semantics" + if (g_shared_clock != nullptr) + return g_shared_clock; + WindowsRealTimeClock* clock = new WindowsRealTimeClock; + if (InterlockedCompareExchangePointer( + reinterpret_cast<void* volatile*>(&g_shared_clock), clock, nullptr) != + nullptr) { + // g_shared_clock was assigned while we constructed/tried to assign our + // instance, delete our instance and use the existing one. + delete clock; + } + return g_shared_clock; +#elif ((defined WEBRTC_LINUX) || (defined WEBRTC_BSD) || (defined WEBRTC_MAC)) + static UnixRealTimeClock clock; + return &clock; +#else + return NULL; +#endif +} + +SimulatedClock::SimulatedClock(int64_t initial_time_us) + : time_us_(initial_time_us), lock_(RWLockWrapper::CreateRWLock()) {} + +SimulatedClock::~SimulatedClock() {} + +int64_t SimulatedClock::TimeInMilliseconds() const { + ReadLockScoped synchronize(*lock_); + return (time_us_ + 500) / 1000; +} + +int64_t SimulatedClock::TimeInMicroseconds() const { + ReadLockScoped synchronize(*lock_); + return time_us_; +} + +NtpTime SimulatedClock::CurrentNtpTime() const { + int64_t now_ms = TimeInMilliseconds(); + uint32_t seconds = (now_ms / 1000) + kNtpJan1970; + uint32_t fractions = + static_cast<uint32_t>((now_ms % 1000) * kMagicNtpFractionalUnit / 1000); + return NtpTime(seconds, fractions); +} + +int64_t SimulatedClock::CurrentNtpInMilliseconds() const { + return TimeInMilliseconds() + 1000 * static_cast<int64_t>(kNtpJan1970); +} + +void SimulatedClock::AdvanceTimeMilliseconds(int64_t milliseconds) { + AdvanceTimeMicroseconds(1000 * milliseconds); +} + +void SimulatedClock::AdvanceTimeMicroseconds(int64_t microseconds) { + WriteLockScoped synchronize(*lock_); + time_us_ += microseconds; +} + +}; // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/clock_unittest.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/clock_unittest.cc new file mode 100644 index 0000000000..f7b0ed7a47 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/clock_unittest.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/clock.h" + +#include "test/gtest.h" + +namespace webrtc { + +TEST(ClockTest, NtpTime) { + Clock* clock = Clock::GetRealTimeClock(); + + // To ensure the test runs correctly even on a heavily loaded system, do not + // compare the seconds/fractions and millisecond values directly. Instead, + // we check that the NTP time is between the "milliseconds" values returned + // right before and right after the call. + // The comparison includes 1 ms of margin to account for the rounding error in + // the conversion. + int64_t milliseconds_lower_bound = clock->CurrentNtpInMilliseconds(); + NtpTime ntp_time = clock->CurrentNtpTime(); + int64_t milliseconds_upper_bound = clock->CurrentNtpInMilliseconds(); + EXPECT_GT(milliseconds_lower_bound / 1000, kNtpJan1970); + EXPECT_LE(milliseconds_lower_bound - 1, ntp_time.ToMs()); + EXPECT_GE(milliseconds_upper_bound + 1, ntp_time.ToMs()); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/cpu_features.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/cpu_features.cc new file mode 100644 index 0000000000..061e763559 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/cpu_features.cc @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Parts of this file derived from Chromium's base/cpu.cc. + +#include "system_wrappers/include/cpu_features_wrapper.h" + +#if defined(WEBRTC_ARCH_X86_FAMILY) && defined(_MSC_VER) +#include <intrin.h> +#endif + +#include "typedefs.h" // NOLINT(build/include) + +// No CPU feature is available => straight C path. +int GetCPUInfoNoASM(CPUFeature feature) { + (void)feature; + return 0; +} + +#if defined(WEBRTC_ARCH_X86_FAMILY) +#ifndef _MSC_VER +// Intrinsic for "cpuid". +#if defined(__pic__) && defined(__i386__) +static inline void __cpuid(int cpu_info[4], int info_type) { + __asm__ volatile( + "mov %%ebx, %%edi\n" + "cpuid\n" + "xchg %%edi, %%ebx\n" + : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), + "=d"(cpu_info[3]) + : "a"(info_type)); +} +#else +static inline void __cpuid(int cpu_info[4], int info_type) { + __asm__ volatile("cpuid\n" + : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), + "=d"(cpu_info[3]) + : "a"(info_type)); +} +#endif +#endif // _MSC_VER +#endif // WEBRTC_ARCH_X86_FAMILY + +#if defined(WEBRTC_ARCH_X86_FAMILY) +// Actual feature detection for x86. +static int GetCPUInfo(CPUFeature feature) { + int cpu_info[4]; + __cpuid(cpu_info, 1); + if (feature == kSSE2) { + return 0 != (cpu_info[3] & 0x04000000); + } + if (feature == kSSE3) { + return 0 != (cpu_info[2] & 0x00000001); + } + return 0; +} +#else +// Default to straight C for other platforms. +static int GetCPUInfo(CPUFeature feature) { + (void)feature; + return 0; +} + +#endif + +WebRtc_CPUInfo WebRtc_GetCPUInfo = GetCPUInfo; +WebRtc_CPUInfo WebRtc_GetCPUInfoNoASM = GetCPUInfoNoASM; diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/cpu_features_android.c b/third_party/libwebrtc/webrtc/system_wrappers/source/cpu_features_android.c new file mode 100644 index 0000000000..0cb3a6c5ee --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/cpu_features_android.c @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <cpu-features.h> + +uint64_t WebRtc_GetCPUFeaturesARM(void) { + return android_getCpuFeatures(); +} diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/cpu_features_linux.c b/third_party/libwebrtc/webrtc/system_wrappers/source/cpu_features_linux.c new file mode 100644 index 0000000000..9c5645068a --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/cpu_features_linux.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <stdlib.h> +#include <string.h> +#include <features.h> +#ifndef __GLIBC_PREREQ +#define __GLIBC_PREREQ(a, b) 0 +#endif +#if __GLIBC_PREREQ(2, 16) +#include <sys/auxv.h> +#else +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <link.h> +#endif +#include "system_wrappers/include/cpu_features_wrapper.h" + +#if defined(WEBRTC_ARCH_ARM_FAMILY) +#include <asm/hwcap.h> + +uint64_t WebRtc_GetCPUFeaturesARM(void) { + uint64_t result = 0; + int architecture = 0; + unsigned long hwcap = 0; + const char* platform = NULL; +#if __GLIBC_PREREQ(2, 16) + hwcap = getauxval(AT_HWCAP); + platform = (const char*)getauxval(AT_PLATFORM); +#else + ElfW(auxv_t) auxv; + int fd = open("/proc/self/auxv", O_RDONLY); + if (fd >= 0) { + while (hwcap == 0 || platform == NULL) { + if (read(fd, &auxv, sizeof(auxv)) < (ssize_t)sizeof(auxv)) { + if (errno == EINTR) + continue; + break; + } + switch (auxv.a_type) { + case AT_HWCAP: + hwcap = auxv.a_un.a_val; + break; + case AT_PLATFORM: + platform = (const char*)auxv.a_un.a_val; + break; + } + } + close(fd); + } +#endif // __GLIBC_PREREQ(2,16) +#if defined(__aarch64__) + architecture = 8; + if ((hwcap & HWCAP_FP) != 0) + result |= kCPUFeatureVFPv3; + if ((hwcap & HWCAP_ASIMD) != 0) + result |= kCPUFeatureNEON; +#else + if (platform != NULL) { + /* expect a string in the form "v6l" or "v7l", etc. + */ + if (platform[0] == 'v' && '0' <= platform[1] && platform[1] <= '9' && + (platform[2] == 'l' || platform[2] == 'b')) { + architecture = platform[1] - '0'; + } + } + if ((hwcap & HWCAP_VFPv3) != 0) + result |= kCPUFeatureVFPv3; + if ((hwcap & HWCAP_NEON) != 0) + result |= kCPUFeatureNEON; +#endif + if (architecture >= 7) + result |= kCPUFeatureARMv7; + if (architecture >= 6) + result |= kCPUFeatureLDREXSTREX; + return result; +} +#endif // WEBRTC_ARCH_ARM_FAMILY diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/cpu_info.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/cpu_info.cc new file mode 100644 index 0000000000..f631be1db0 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/cpu_info.cc @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/cpu_info.h" + +#if defined(WEBRTC_WIN) +#include <windows.h> +#include <winsock2.h> +#ifndef EXCLUDE_D3D9 +#include <d3d9.h> +#endif +#elif defined(WEBRTC_LINUX) || defined(WEBRTC_BSD) +#include <unistd.h> +#endif +#if defined(WEBRTC_MAC) +#include <sys/sysctl.h> +#endif + +#include "rtc_base/logging.h" + +namespace internal { +static int DetectNumberOfCores() { + // We fall back on assuming a single core in case of errors. + int number_of_cores = 1; + +#if defined(WEBRTC_WIN) + SYSTEM_INFO si; + GetNativeSystemInfo(&si); + number_of_cores = static_cast<int>(si.dwNumberOfProcessors); +#elif defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID) || defined(WEBRTC_BSD) + number_of_cores = static_cast<int>(sysconf(_SC_NPROCESSORS_ONLN)); +#elif defined(WEBRTC_MAC) + int name[] = {CTL_HW, HW_AVAILCPU}; + size_t size = sizeof(number_of_cores); + if (0 != sysctl(name, 2, &number_of_cores, &size, NULL, 0)) { + RTC_LOG(LS_ERROR) << "Failed to get number of cores"; + number_of_cores = 1; + } +#else + RTC_LOG(LS_ERROR) << "No function to get number of cores"; +#endif + + RTC_LOG(LS_INFO) << "Available number of cores: " << number_of_cores; + + return number_of_cores; +} +} // namespace internal + +namespace webrtc { + +uint32_t CpuInfo::DetectNumberOfCores() { + // Statically cache the number of system cores available since if the process + // is running in a sandbox, we may only be able to read the value once (before + // the sandbox is initialized) and not thereafter. + // For more information see crbug.com/176522. + static uint32_t logical_cpus = 0; + if (!logical_cpus) + logical_cpus = static_cast<uint32_t>(internal::DetectNumberOfCores()); + return logical_cpus; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/droid-cpu-features.c b/third_party/libwebrtc/webrtc/system_wrappers/source/droid-cpu-features.c new file mode 100644 index 0000000000..cc24b30056 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/droid-cpu-features.c @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <sys/system_properties.h> +#ifdef __arm__ +#include <machine/cpu-features.h> +#endif +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +#include "droid-cpu-features.h" + +static pthread_once_t g_once; +static AndroidCpuFamily g_cpuFamily; +static uint64_t g_cpuFeatures; +static int g_cpuCount; + +static const int android_cpufeatures_debug = 0; + +#ifdef __arm__ +# define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_ARM +#elif defined __i386__ +# define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_X86 +#else +# define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_UNKNOWN +#endif + +#define D(...) \ + do { \ + if (android_cpufeatures_debug) { \ + printf(__VA_ARGS__); fflush(stdout); \ + } \ + } while (0) + +#ifdef __i386__ +static __inline__ void x86_cpuid(int func, int values[4]) +{ + int a, b, c, d; + /* We need to preserve ebx since we're compiling PIC code */ + /* this means we can't use "=b" for the second output register */ + __asm__ __volatile__ ( \ + "push %%ebx\n" + "cpuid\n" \ + "mov %1, %%ebx\n" + "pop %%ebx\n" + : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ + : "a" (func) \ + ); + values[0] = a; + values[1] = b; + values[2] = c; + values[3] = d; +} +#endif + +/* Read the content of /proc/cpuinfo into a user-provided buffer. + * Return the length of the data, or -1 on error. Does *not* + * zero-terminate the content. Will not read more + * than 'buffsize' bytes. + */ +static int +read_file(const char* pathname, char* buffer, size_t buffsize) +{ + int fd, len; + + fd = open(pathname, O_RDONLY); + if (fd < 0) + return -1; + + do { + len = read(fd, buffer, buffsize); + } while (len < 0 && errno == EINTR); + + close(fd); + + return len; +} + +/* Extract the content of a the first occurence of a given field in + * the content of /proc/cpuinfo and return it as a heap-allocated + * string that must be freed by the caller. + * + * Return NULL if not found + */ +static char* +extract_cpuinfo_field(char* buffer, int buflen, const char* field) +{ + int fieldlen = strlen(field); + char* bufend = buffer + buflen; + char* result = NULL; + int len, ignore; + const char *p, *q; + + /* Look for first field occurence, and ensures it starts the line. + */ + p = buffer; + bufend = buffer + buflen; + for (;;) { + p = memmem(p, bufend-p, field, fieldlen); + if (p == NULL) + goto EXIT; + + if (p == buffer || p[-1] == '\n') + break; + + p += fieldlen; + } + + /* Skip to the first column followed by a space */ + p += fieldlen; + p = memchr(p, ':', bufend-p); + if (p == NULL || p[1] != ' ') + goto EXIT; + + /* Find the end of the line */ + p += 2; + q = memchr(p, '\n', bufend-p); + if (q == NULL) + q = bufend; + + /* Copy the line into a heap-allocated buffer */ + len = q-p; + result = malloc(len+1); + if (result == NULL) + goto EXIT; + + memcpy(result, p, len); + result[len] = '\0'; + +EXIT: + return result; +} + +/* Count the number of occurences of a given field prefix in /proc/cpuinfo. + */ +static int +count_cpuinfo_field(char* buffer, int buflen, const char* field) +{ + int fieldlen = strlen(field); + const char* p = buffer; + const char* bufend = buffer + buflen; + const char* q; + int count = 0; + + for (;;) { + const char* q; + + p = memmem(p, bufend-p, field, fieldlen); + if (p == NULL) + break; + + /* Ensure that the field is at the start of a line */ + if (p > buffer && p[-1] != '\n') { + p += fieldlen; + continue; + } + + + /* skip any whitespace */ + q = p + fieldlen; + while (q < bufend && (*q == ' ' || *q == '\t')) + q++; + + /* we must have a colon now */ + if (q < bufend && *q == ':') { + count += 1; + q ++; + } + p = q; + } + + return count; +} + +/* Like strlen(), but for constant string literals */ +#define STRLEN_CONST(x) ((sizeof(x)-1) + + +/* Checks that a space-separated list of items contains one given 'item'. + * Returns 1 if found, 0 otherwise. + */ +static int +has_list_item(const char* list, const char* item) +{ + const char* p = list; + int itemlen = strlen(item); + + if (list == NULL) + return 0; + + while (*p) { + const char* q; + + /* skip spaces */ + while (*p == ' ' || *p == '\t') + p++; + + /* find end of current list item */ + q = p; + while (*q && *q != ' ' && *q != '\t') + q++; + + if (itemlen == q-p && !memcmp(p, item, itemlen)) + return 1; + + /* skip to next item */ + p = q; + } + return 0; +} + + +static void +android_cpuInit(void) +{ + char cpuinfo[4096]; + int cpuinfo_len; + + g_cpuFamily = DEFAULT_CPU_FAMILY; + g_cpuFeatures = 0; + g_cpuCount = 1; + + cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, sizeof cpuinfo); + D("cpuinfo_len is (%d):\n%.*s\n", cpuinfo_len, + cpuinfo_len >= 0 ? cpuinfo_len : 0, cpuinfo); + + if (cpuinfo_len < 0) /* should not happen */ { + return; + } + + /* Count the CPU cores, the value may be 0 for single-core CPUs */ + g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "processor"); + if (g_cpuCount == 0) { + g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "Processor"); + if (g_cpuCount == 0) { + g_cpuCount = 1; + } + } + + D("found cpuCount = %d\n", g_cpuCount); + +#ifdef __ARM_ARCH__ + { + char* features = NULL; + char* architecture = NULL; + + /* Extract architecture from the "CPU Architecture" field. + * The list is well-known, unlike the the output of + * the 'Processor' field which can vary greatly. + * + * See the definition of the 'proc_arch' array in + * $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in + * same file. + */ + char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture"); + + if (cpuArch != NULL) { + char* end; + long archNumber; + int hasARMv7 = 0; + + D("found cpuArch = '%s'\n", cpuArch); + + /* read the initial decimal number, ignore the rest */ + archNumber = strtol(cpuArch, &end, 10); + + /* Here we assume that ARMv8 will be upwards compatible with v7 + * in the future. Unfortunately, there is no 'Features' field to + * indicate that Thumb-2 is supported. + */ + if (end > cpuArch && archNumber >= 7) { + hasARMv7 = 1; + } + + /* Unfortunately, it seems that certain ARMv6-based CPUs + * report an incorrect architecture number of 7! + * + * See http://code.google.com/p/android/issues/detail?id=10812 + * + * We try to correct this by looking at the 'elf_format' + * field reported by the 'Processor' field, which is of the + * form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for + * an ARMv6-one. + */ + if (hasARMv7) { + char* cpuProc = extract_cpuinfo_field(cpuinfo, cpuinfo_len, + "Processor"); + if (cpuProc != NULL) { + D("found cpuProc = '%s'\n", cpuProc); + if (has_list_item(cpuProc, "(v6l)")) { + D("CPU processor and architecture mismatch!!\n"); + hasARMv7 = 0; + } + free(cpuProc); + } + } + + if (hasARMv7) { + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_ARMv7; + } + + /* The LDREX / STREX instructions are available from ARMv6 */ + if (archNumber >= 6) { + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_LDREX_STREX; + } + + free(cpuArch); + } + + /* Extract the list of CPU features from 'Features' field */ + char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Features"); + + if (cpuFeatures != NULL) { + + D("found cpuFeatures = '%s'\n", cpuFeatures); + + if (has_list_item(cpuFeatures, "vfpv3")) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3; + + else if (has_list_item(cpuFeatures, "vfpv3d16")) + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3; + + if (has_list_item(cpuFeatures, "neon")) { + /* Note: Certain kernels only report neon but not vfpv3 + * in their features list. However, ARM mandates + * that if Neon is implemented, so must be VFPv3 + * so always set the flag. + */ + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_NEON | + ANDROID_CPU_ARM_FEATURE_VFPv3; + } + free(cpuFeatures); + } + } +#endif /* __ARM_ARCH__ */ + +#ifdef __i386__ + g_cpuFamily = ANDROID_CPU_FAMILY_X86; + + int regs[4]; + +/* According to http://en.wikipedia.org/wiki/CPUID */ +#define VENDOR_INTEL_b 0x756e6547 +#define VENDOR_INTEL_c 0x6c65746e +#define VENDOR_INTEL_d 0x49656e69 + + x86_cpuid(0, regs); + int vendorIsIntel = (regs[1] == VENDOR_INTEL_b && + regs[2] == VENDOR_INTEL_c && + regs[3] == VENDOR_INTEL_d); + + x86_cpuid(1, regs); + if ((regs[2] & (1 << 9)) != 0) { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSSE3; + } + if ((regs[2] & (1 << 23)) != 0) { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_POPCNT; + } + if (vendorIsIntel && (regs[2] & (1 << 22)) != 0) { + g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_MOVBE; + } +#endif +} + + +AndroidCpuFamily +android_getCpuFamily(void) +{ + pthread_once(&g_once, android_cpuInit); + return g_cpuFamily; +} + + +uint64_t +android_getCpuFeatures(void) +{ + pthread_once(&g_once, android_cpuInit); + return g_cpuFeatures; +} + + +int +android_getCpuCount(void) +{ + pthread_once(&g_once, android_cpuInit); + return g_cpuCount; +} diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/droid-cpu-features.h b/third_party/libwebrtc/webrtc/system_wrappers/source/droid-cpu-features.h new file mode 100644 index 0000000000..f20c0bc4d9 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/droid-cpu-features.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// You can download Android source at +// http://source.android.com/source/downloading.html +// Original files are in ndk/sources/android/cpufeatures +// Revision is Change-Id: I9a0629efba36a6023f05e5f092e7addcc1b7d2a9 + +#ifndef CPU_FEATURES_H +#define CPU_FEATURES_H + +#include <sys/cdefs.h> +#include <stdint.h> + +__BEGIN_DECLS + +typedef enum { + ANDROID_CPU_FAMILY_UNKNOWN = 0, + ANDROID_CPU_FAMILY_ARM, + ANDROID_CPU_FAMILY_X86, + + ANDROID_CPU_FAMILY_MAX /* do not remove */ + +} AndroidCpuFamily; + +/* Return family of the device's CPU */ +extern AndroidCpuFamily android_getCpuFamily(void); + +enum { + ANDROID_CPU_ARM_FEATURE_ARMv7 = (1 << 0), + ANDROID_CPU_ARM_FEATURE_VFPv3 = (1 << 1), + ANDROID_CPU_ARM_FEATURE_NEON = (1 << 2), + ANDROID_CPU_ARM_FEATURE_LDREX_STREX = (1 << 3), +}; + +enum { + ANDROID_CPU_X86_FEATURE_SSSE3 = (1 << 0), + ANDROID_CPU_X86_FEATURE_POPCNT = (1 << 1), + ANDROID_CPU_X86_FEATURE_MOVBE = (1 << 2), +}; + +extern uint64_t android_getCpuFeatures(void); + +/* Return the number of CPU cores detected on this device. */ +extern int android_getCpuCount(void); + +__END_DECLS + +#endif /* CPU_FEATURES_H */ diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/event.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/event.cc new file mode 100644 index 0000000000..aac69f6e02 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/event.cc @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/event_wrapper.h" + +#if defined(_WIN32) +#include <windows.h> +#include "system_wrappers/source/event_timer_win.h" +#elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +#include <ApplicationServices/ApplicationServices.h> +#include <pthread.h> +#include "system_wrappers/source/event_timer_posix.h" +#else +#include <pthread.h> +#include "system_wrappers/source/event_timer_posix.h" +#endif + +#include "rtc_base/event.h" + +namespace webrtc { + +class EventWrapperImpl : public EventWrapper { + public: + EventWrapperImpl() : event_(false, false) {} + ~EventWrapperImpl() override {} + + bool Set() override { + event_.Set(); + return true; + } + + EventTypeWrapper Wait(unsigned long max_time) override { + int to_wait = max_time == WEBRTC_EVENT_INFINITE + ? rtc::Event::kForever + : static_cast<int>(max_time); + return event_.Wait(to_wait) ? kEventSignaled : kEventTimeout; + } + + private: + rtc::Event event_; +}; + +// static +EventWrapper* EventWrapper::Create() { + return new EventWrapperImpl(); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/event_timer_posix.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/event_timer_posix.cc new file mode 100644 index 0000000000..2a6a580c64 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/event_timer_posix.cc @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/source/event_timer_posix.h" + +#include <errno.h> +#include <pthread.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <sys/time.h> +#include <unistd.h> + +#include "rtc_base/checks.h" + +namespace webrtc { + +// static +EventTimerWrapper* EventTimerWrapper::Create() { + return new EventTimerPosix(); +} + +const int64_t kNanosecondsPerMillisecond = 1000000; +const int64_t kNanosecondsPerSecond = 1000000000; + +EventTimerPosix::EventTimerPosix() + : event_set_(false), + timer_thread_(nullptr), + created_at_(), + periodic_(false), + time_ms_(0), + count_(0), + is_stopping_(false) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mutex_, &attr); + pthread_condattr_t cond_attr; + pthread_condattr_init(&cond_attr); +// TODO(sprang): Remove HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC special case once +// all supported Android platforms support pthread_condattr_setclock. +// TODO(sprang): Add support for monotonic clock on Apple platforms. +#if !(defined(WEBRTC_MAC) || defined(WEBRTC_IOS)) && \ + !(defined(WEBRTC_ANDROID) && \ + defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)) + pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC); +#endif + pthread_cond_init(&cond_, &cond_attr); + pthread_condattr_destroy(&cond_attr); +} + +EventTimerPosix::~EventTimerPosix() { + StopTimer(); + pthread_cond_destroy(&cond_); + pthread_mutex_destroy(&mutex_); +} + +// TODO(pbos): Make this void. +bool EventTimerPosix::Set() { + RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_)); + event_set_ = true; + pthread_cond_signal(&cond_); + pthread_mutex_unlock(&mutex_); + return true; +} + +EventTypeWrapper EventTimerPosix::Wait(unsigned long timeout_ms) { + int ret_val = 0; + RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_)); + + if (!event_set_) { + if (WEBRTC_EVENT_INFINITE != timeout_ms) { + timespec end_at; +#ifndef WEBRTC_MAC + clock_gettime(CLOCK_MONOTONIC, &end_at); +#else + timeval value; + struct timezone time_zone; + time_zone.tz_minuteswest = 0; + time_zone.tz_dsttime = 0; + gettimeofday(&value, &time_zone); + TIMEVAL_TO_TIMESPEC(&value, &end_at); +#endif + end_at.tv_sec += timeout_ms / 1000; + end_at.tv_nsec += (timeout_ms % 1000) * kNanosecondsPerMillisecond; + + if (end_at.tv_nsec >= kNanosecondsPerSecond) { + end_at.tv_sec++; + end_at.tv_nsec -= kNanosecondsPerSecond; + } + while (ret_val == 0 && !event_set_) { +#if defined(WEBRTC_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC) + ret_val = pthread_cond_timedwait_monotonic_np(&cond_, &mutex_, &end_at); +#else + ret_val = pthread_cond_timedwait(&cond_, &mutex_, &end_at); +#endif // WEBRTC_ANDROID && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC + } + } else { + while (ret_val == 0 && !event_set_) + ret_val = pthread_cond_wait(&cond_, &mutex_); + } + } + + RTC_DCHECK(ret_val == 0 || ret_val == ETIMEDOUT); + + // Reset and signal if set, regardless of why the thread woke up. + if (event_set_) { + ret_val = 0; + event_set_ = false; + } + pthread_mutex_unlock(&mutex_); + + return ret_val == 0 ? kEventSignaled : kEventTimeout; +} + +EventTypeWrapper EventTimerPosix::Wait(timespec* end_at, bool reset_event) { + int ret_val = 0; + RTC_CHECK_EQ(0, pthread_mutex_lock(&mutex_)); + if (reset_event) { + // Only wake for new events or timeouts. + event_set_ = false; + } + + while (ret_val == 0 && !event_set_) { +#if defined(WEBRTC_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC) + ret_val = pthread_cond_timedwait_monotonic_np(&cond_, &mutex_, end_at); +#else + ret_val = pthread_cond_timedwait(&cond_, &mutex_, end_at); +#endif // WEBRTC_ANDROID && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC + } + + RTC_DCHECK(ret_val == 0 || ret_val == ETIMEDOUT); + + // Reset and signal if set, regardless of why the thread woke up. + if (event_set_) { + ret_val = 0; + event_set_ = false; + } + pthread_mutex_unlock(&mutex_); + + return ret_val == 0 ? kEventSignaled : kEventTimeout; +} + +rtc::PlatformThread* EventTimerPosix::CreateThread() { + const char* kThreadName = "WebRtc_event_timer_thread"; + return new rtc::PlatformThread(Run, this, kThreadName); +} + +bool EventTimerPosix::StartTimer(bool periodic, unsigned long time_ms) { + pthread_mutex_lock(&mutex_); + if (timer_thread_) { + if (periodic_) { + // Timer already started. + pthread_mutex_unlock(&mutex_); + return false; + } else { + // New one shot timer. + time_ms_ = time_ms; + created_at_.tv_sec = 0; + timer_event_->Set(); + pthread_mutex_unlock(&mutex_); + return true; + } + } + + // Start the timer thread. + timer_event_.reset(new EventTimerPosix()); + timer_thread_.reset(CreateThread()); + periodic_ = periodic; + time_ms_ = time_ms; + timer_thread_->Start(); + timer_thread_->SetPriority(rtc::kRealtimePriority); + pthread_mutex_unlock(&mutex_); + + return true; +} + +bool EventTimerPosix::Run(void* obj) { + return static_cast<EventTimerPosix*>(obj)->Process(); +} + +bool EventTimerPosix::Process() { + pthread_mutex_lock(&mutex_); + if (is_stopping_) { + pthread_mutex_unlock(&mutex_); + return false; + } + if (created_at_.tv_sec == 0) { +#ifndef WEBRTC_MAC + RTC_CHECK_EQ(0, clock_gettime(CLOCK_MONOTONIC, &created_at_)); +#else + timeval value; + struct timezone time_zone; + time_zone.tz_minuteswest = 0; + time_zone.tz_dsttime = 0; + gettimeofday(&value, &time_zone); + TIMEVAL_TO_TIMESPEC(&value, &created_at_); +#endif + count_ = 0; + } + + timespec end_at; + unsigned long long total_delta_ms = time_ms_ * ++count_; + if (!periodic_ && count_ >= 1) { + // No need to wake up often if we're not going to signal waiting threads. + total_delta_ms = + std::min<uint64_t>(total_delta_ms, 60 * kNanosecondsPerSecond); + } + + end_at.tv_sec = created_at_.tv_sec + total_delta_ms / 1000; + end_at.tv_nsec = created_at_.tv_nsec + + (total_delta_ms % 1000) * kNanosecondsPerMillisecond; + + if (end_at.tv_nsec >= kNanosecondsPerSecond) { + end_at.tv_sec++; + end_at.tv_nsec -= kNanosecondsPerSecond; + } + + pthread_mutex_unlock(&mutex_); + // Reset event on first call so that we don't immediately return here if this + // thread was not blocked on timer_event_->Wait when the StartTimer() call + // was made. + if (timer_event_->Wait(&end_at, count_ == 1) == kEventSignaled) + return true; + + pthread_mutex_lock(&mutex_); + if (periodic_ || count_ == 1) + Set(); + pthread_mutex_unlock(&mutex_); + + return true; +} + +bool EventTimerPosix::StopTimer() { + pthread_mutex_lock(&mutex_); + is_stopping_ = true; + pthread_mutex_unlock(&mutex_); + + if (timer_event_) + timer_event_->Set(); + + if (timer_thread_) { + timer_thread_->Stop(); + timer_thread_.reset(); + } + timer_event_.reset(); + + // Set time to zero to force new reference time for the timer. + memset(&created_at_, 0, sizeof(created_at_)); + count_ = 0; + return true; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/event_timer_posix.h b/third_party/libwebrtc/webrtc/system_wrappers/source/event_timer_posix.h new file mode 100644 index 0000000000..72d6753e53 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/event_timer_posix.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SYSTEM_WRAPPERS_SOURCE_EVENT_POSIX_H_ +#define SYSTEM_WRAPPERS_SOURCE_EVENT_POSIX_H_ + +#include "system_wrappers/include/event_wrapper.h" + +#include <memory> + +#include <pthread.h> +#include <time.h> + +#include "rtc_base/platform_thread.h" + +namespace webrtc { + +enum State { kUp = 1, kDown = 2 }; + +class EventTimerPosix : public EventTimerWrapper { + public: + EventTimerPosix(); + ~EventTimerPosix() override; + + EventTypeWrapper Wait(unsigned long max_time) override; + bool Set() override; + + bool StartTimer(bool periodic, unsigned long time) override; + bool StopTimer() override; + + private: + friend class EventTimerPosixTest; + + static bool Run(void* obj); + bool Process(); + EventTypeWrapper Wait(timespec* end_at, bool reset_state); + + virtual rtc::PlatformThread* CreateThread(); + + pthread_cond_t cond_; + pthread_mutex_t mutex_; + bool event_set_; + + // TODO(pbos): Remove unique_ptr and use PlatformThread directly. + std::unique_ptr<rtc::PlatformThread> timer_thread_; + std::unique_ptr<EventTimerPosix> timer_event_; + timespec created_at_; + + bool periodic_; + unsigned long time_ms_; + unsigned long count_; + bool is_stopping_; +}; + +} // namespace webrtc + +#endif // SYSTEM_WRAPPERS_SOURCE_EVENT_POSIX_H_ diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/event_timer_posix_unittest.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/event_timer_posix_unittest.cc new file mode 100644 index 0000000000..e0c5cbc477 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/event_timer_posix_unittest.cc @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/source/event_timer_posix.h" + +#include "rtc_base/criticalsection.h" +#include "rtc_base/event.h" +#include "test/gtest.h" + +namespace webrtc { + +enum class ThreadState { + kNotStarted, + kWaiting, + kRequestProcessCall, + kCallingProcess, + kProcessDone, + kContinue, + kExiting, + kDead +}; + +class EventTimerPosixTest : public testing::Test, public EventTimerPosix { + public: + EventTimerPosixTest() + : thread_state_(ThreadState::kNotStarted), + process_event_(false, true), + main_event_(false, true), + process_thread_id_(0), + process_thread_(nullptr) {} + virtual ~EventTimerPosixTest() {} + + rtc::PlatformThread* CreateThread() override { + EXPECT_TRUE(process_thread_ == nullptr); + process_thread_ = + new rtc::PlatformThread(Run, this, "EventTimerPosixTestThread"); + return process_thread_; + } + + static bool Run(void* obj) { + return static_cast<EventTimerPosixTest*>(obj)->Process(); + } + + bool Process() { + bool res = ProcessInternal(); + if (!res) { + rtc::CritScope cs(&lock_); + thread_state_ = ThreadState::kDead; + main_event_.Set(); + } + return res; + } + + bool ProcessInternal() { + { + rtc::CritScope cs(&lock_); + if (thread_state_ == ThreadState::kNotStarted) { + if (!ChangeThreadState(ThreadState::kNotStarted, + ThreadState::kContinue)) { + ADD_FAILURE() << "Unable to start process thread"; + return false; + } + process_thread_id_ = rtc::CurrentThreadId(); + } + } + + if (!ChangeThreadState(ThreadState::kContinue, ThreadState::kWaiting)) + return false; + + if (!AwaitThreadState(ThreadState::kRequestProcessCall, + rtc::Event::kForever)) + return false; + + if (!ChangeThreadState(ThreadState::kRequestProcessCall, + ThreadState::kCallingProcess)) + return false; + + EventTimerPosix::Process(); + + if (!ChangeThreadState(ThreadState::kCallingProcess, + ThreadState::kProcessDone)) + return false; + + if (!AwaitThreadState(ThreadState::kContinue, rtc::Event::kForever)) + return false; + + return true; + } + + bool IsProcessThread() { + rtc::CritScope cs(&lock_); + return process_thread_id_ == rtc::CurrentThreadId(); + } + + bool ChangeThreadState(ThreadState prev_state, ThreadState new_state) { + rtc::CritScope cs(&lock_); + if (thread_state_ != prev_state) + return false; + thread_state_ = new_state; + if (IsProcessThread()) { + main_event_.Set(); + } else { + process_event_.Set(); + } + return true; + } + + bool AwaitThreadState(ThreadState state, int timeout) { + rtc::Event* event = IsProcessThread() ? &process_event_ : &main_event_; + do { + rtc::CritScope cs(&lock_); + if (state != ThreadState::kDead && thread_state_ == ThreadState::kExiting) + return false; + if (thread_state_ == state) + return true; + } while (event->Wait(timeout)); + return false; + } + + bool CallProcess(int timeout_ms) { + return AwaitThreadState(ThreadState::kWaiting, timeout_ms) && + ChangeThreadState(ThreadState::kWaiting, + ThreadState::kRequestProcessCall); + } + + bool AwaitProcessDone(int timeout_ms) { + return AwaitThreadState(ThreadState::kProcessDone, timeout_ms) && + ChangeThreadState(ThreadState::kProcessDone, ThreadState::kContinue); + } + + void TearDown() override { + if (process_thread_) { + { + rtc::CritScope cs(&lock_); + if (thread_state_ != ThreadState::kDead) { + thread_state_ = ThreadState::kExiting; + process_event_.Set(); + } + } + ASSERT_TRUE(AwaitThreadState(ThreadState::kDead, 5000)); + } + } + + ThreadState thread_state_; + rtc::CriticalSection lock_; + rtc::Event process_event_; + rtc::Event main_event_; + rtc::PlatformThreadId process_thread_id_; + rtc::PlatformThread* process_thread_; +}; + +TEST_F(EventTimerPosixTest, WaiterBlocksUntilTimeout) { + const int kTimerIntervalMs = 100; + const int kTimeoutMs = 5000; + ASSERT_TRUE(StartTimer(false, kTimerIntervalMs)); + ASSERT_TRUE(CallProcess(kTimeoutMs)); + EventTypeWrapper res = Wait(kTimeoutMs); + EXPECT_EQ(kEventSignaled, res); + ASSERT_TRUE(AwaitProcessDone(kTimeoutMs)); +} + +TEST_F(EventTimerPosixTest, WaiterWakesImmediatelyAfterTimeout) { + const int kTimerIntervalMs = 100; + const int kTimeoutMs = 5000; + ASSERT_TRUE(StartTimer(false, kTimerIntervalMs)); + ASSERT_TRUE(CallProcess(kTimeoutMs)); + ASSERT_TRUE(AwaitProcessDone(kTimeoutMs)); + EventTypeWrapper res = Wait(0); + EXPECT_EQ(kEventSignaled, res); +} + +TEST_F(EventTimerPosixTest, WaiterBlocksUntilTimeoutProcessInactiveOnStart) { + const int kTimerIntervalMs = 100; + const int kTimeoutMs = 5000; + // First call to StartTimer initializes thread. + ASSERT_TRUE(StartTimer(false, kTimerIntervalMs)); + + // Process thread currently _not_ blocking on Process() call. + ASSERT_TRUE(AwaitThreadState(ThreadState::kWaiting, kTimeoutMs)); + + // Start new one-off timer, then call Process(). + ASSERT_TRUE(StartTimer(false, kTimerIntervalMs)); + ASSERT_TRUE(CallProcess(kTimeoutMs)); + + EventTypeWrapper res = Wait(kTimeoutMs); + EXPECT_EQ(kEventSignaled, res); + + ASSERT_TRUE(AwaitProcessDone(kTimeoutMs)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/event_timer_win.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/event_timer_win.cc new file mode 100644 index 0000000000..b6a93feb82 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/event_timer_win.cc @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/source/event_timer_win.h" + +#include "Mmsystem.h" + +namespace webrtc { + +// static +EventTimerWrapper* EventTimerWrapper::Create() { + return new EventTimerWin(); +} + +EventTimerWin::EventTimerWin() + : event_(::CreateEvent(NULL, // security attributes + FALSE, // manual reset + FALSE, // initial state + NULL)), // name of event + timerID_(NULL) {} + +EventTimerWin::~EventTimerWin() { + StopTimer(); + CloseHandle(event_); +} + +bool EventTimerWin::Set() { + // Note: setting an event that is already set has no effect. + return SetEvent(event_) == 1; +} + +EventTypeWrapper EventTimerWin::Wait(unsigned long max_time) { + unsigned long res = WaitForSingleObject(event_, max_time); + switch (res) { + case WAIT_OBJECT_0: + return kEventSignaled; + case WAIT_TIMEOUT: + return kEventTimeout; + default: + return kEventError; + } +} + +bool EventTimerWin::StartTimer(bool periodic, unsigned long time) { + if (timerID_ != NULL) { + timeKillEvent(timerID_); + timerID_ = NULL; + } + + if (periodic) { + timerID_ = timeSetEvent(time, 0, (LPTIMECALLBACK)HANDLE(event_), 0, + TIME_PERIODIC | TIME_CALLBACK_EVENT_PULSE); + } else { + timerID_ = timeSetEvent(time, 0, (LPTIMECALLBACK)HANDLE(event_), 0, + TIME_ONESHOT | TIME_CALLBACK_EVENT_SET); + } + + return timerID_ != NULL; +} + +bool EventTimerWin::StopTimer() { + if (timerID_ != NULL) { + timeKillEvent(timerID_); + timerID_ = NULL; + } + + return true; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/event_timer_win.h b/third_party/libwebrtc/webrtc/system_wrappers/source/event_timer_win.h new file mode 100644 index 0000000000..5631a3fbc4 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/event_timer_win.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SYSTEM_WRAPPERS_SOURCE_EVENT_WIN_H_ +#define SYSTEM_WRAPPERS_SOURCE_EVENT_WIN_H_ + +#include <windows.h> + +#include "system_wrappers/include/event_wrapper.h" + +#include "typedefs.h" // NOLINT(build/include) + +namespace webrtc { + +class EventTimerWin : public EventTimerWrapper { + public: + EventTimerWin(); + virtual ~EventTimerWin(); + + virtual EventTypeWrapper Wait(unsigned long max_time); + virtual bool Set(); + + virtual bool StartTimer(bool periodic, unsigned long time); + virtual bool StopTimer(); + + private: + HANDLE event_; + uint32_t timerID_; +}; + +} // namespace webrtc + +#endif // SYSTEM_WRAPPERS_SOURCE_EVENT_WIN_H_ diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/field_trial_default.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/field_trial_default.cc new file mode 100644 index 0000000000..e8d8917874 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/field_trial_default.cc @@ -0,0 +1,65 @@ +// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// + +#include "system_wrappers/include/field_trial_default.h" +#include "system_wrappers/include/field_trial.h" + +#include <string> + +// Simple field trial implementation, which allows client to +// specify desired flags in InitFieldTrialsFromString. +namespace webrtc { +namespace field_trial { + +static const char* trials_init_string = NULL; + +std::string FindFullName(const std::string& name) { + if (trials_init_string == NULL) + return std::string(); + + std::string trials_string(trials_init_string); + if (trials_string.empty()) + return std::string(); + + static const char kPersistentStringSeparator = '/'; + size_t next_item = 0; + while (next_item < trials_string.length()) { + // Find next name/value pair in field trial configuration string. + size_t field_name_end = + trials_string.find(kPersistentStringSeparator, next_item); + if (field_name_end == trials_string.npos || field_name_end == next_item) + break; + size_t field_value_end = + trials_string.find(kPersistentStringSeparator, field_name_end + 1); + if (field_value_end == trials_string.npos || + field_value_end == field_name_end + 1) + break; + std::string field_name(trials_string, next_item, + field_name_end - next_item); + std::string field_value(trials_string, field_name_end + 1, + field_value_end - field_name_end - 1); + next_item = field_value_end + 1; + + if (name == field_name) + return field_value; + } + return std::string(); +} + +// Optionally initialize field trial from a string. +void InitFieldTrialsFromString(const char* trials_string) { + trials_init_string = trials_string; +} + +const char* GetFieldTrialString() { + return trials_init_string; +} + +} // namespace field_trial +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/file_impl.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/file_impl.cc new file mode 100644 index 0000000000..350aaeb15b --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/file_impl.cc @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/file_wrapper.h" + +#ifdef _WIN32 +#include <Windows.h> +#else +#include <stdarg.h> +#include <string.h> +#endif + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { +FILE* FileOpen(const char* file_name_utf8, bool read_only) { +#if defined(_WIN32) + int len = MultiByteToWideChar(CP_UTF8, 0, file_name_utf8, -1, nullptr, 0); + std::wstring wstr(len, 0); + MultiByteToWideChar(CP_UTF8, 0, file_name_utf8, -1, &wstr[0], len); + FILE* file = _wfopen(wstr.c_str(), read_only ? L"rb" : L"wb"); +#else + FILE* file = fopen(file_name_utf8, read_only ? "rb" : "wb"); +#endif + return file; +} +} // namespace + +// static +FileWrapper* FileWrapper::Create() { + return new FileWrapper(); +} + +// static +FileWrapper FileWrapper::Open(const char* file_name_utf8, bool read_only) { + return FileWrapper(FileOpen(file_name_utf8, read_only), 0); +} + +FileWrapper::FileWrapper() {} + +FileWrapper::FileWrapper(FILE* file, size_t max_size) + : file_(file), max_size_in_bytes_(max_size) {} + +FileWrapper::~FileWrapper() { + CloseFileImpl(); +} + +FileWrapper::FileWrapper(FileWrapper&& other) { + operator=(std::move(other)); +} + +FileWrapper& FileWrapper::operator=(FileWrapper&& other) { + file_ = other.file_; + max_size_in_bytes_ = other.max_size_in_bytes_; + position_ = other.position_; + other.file_ = nullptr; + return *this; +} + +void FileWrapper::CloseFile() { + rtc::CritScope lock(&lock_); + CloseFileImpl(); +} + +int FileWrapper::Rewind() { + rtc::CritScope lock(&lock_); + if (file_ != nullptr) { + position_ = 0; + return fseek(file_, 0, SEEK_SET); + } + return -1; +} + +void FileWrapper::SetMaxFileSize(size_t bytes) { + rtc::CritScope lock(&lock_); + max_size_in_bytes_ = bytes; +} + +int FileWrapper::Flush() { + rtc::CritScope lock(&lock_); + return FlushImpl(); +} + +bool FileWrapper::OpenFile(const char* file_name_utf8, bool read_only) { + size_t length = strlen(file_name_utf8); + if (length > kMaxFileNameSize - 1) + return false; + + rtc::CritScope lock(&lock_); + if (file_ != nullptr) + return false; + + file_ = FileOpen(file_name_utf8, read_only); + return file_ != nullptr; +} + +bool FileWrapper::OpenFromFileHandle(FILE* handle) { + if (!handle) + return false; + rtc::CritScope lock(&lock_); + CloseFileImpl(); + file_ = handle; + return true; +} + +int FileWrapper::Read(void* buf, size_t length) { + rtc::CritScope lock(&lock_); + if (file_ == nullptr) + return -1; + + size_t bytes_read = fread(buf, 1, length, file_); + return static_cast<int>(bytes_read); +} + +bool FileWrapper::Write(const void* buf, size_t length) { + if (buf == nullptr) + return false; + + rtc::CritScope lock(&lock_); + + if (file_ == nullptr) + return false; + + // Check if it's time to stop writing. + if (max_size_in_bytes_ > 0 && (position_ + length) > max_size_in_bytes_) + return false; + + size_t num_bytes = fwrite(buf, 1, length, file_); + position_ += num_bytes; + + return num_bytes == length; +} + +void FileWrapper::CloseFileImpl() { + if (file_ != nullptr) + fclose(file_); + file_ = nullptr; +} + +int FileWrapper::FlushImpl() { + return (file_ != nullptr) ? fflush(file_) : -1; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/metrics_default.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/metrics_default.cc new file mode 100644 index 0000000000..fbb2956301 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/metrics_default.cc @@ -0,0 +1,303 @@ +// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// + +#include "system_wrappers/include/metrics_default.h" + +#include <algorithm> + +#include "rtc_base/criticalsection.h" +#include "rtc_base/thread_annotations.h" +#include "system_wrappers/include/metrics.h" + +// Default implementation of histogram methods for WebRTC clients that do not +// want to provide their own implementation. + +namespace webrtc { +namespace metrics { +class Histogram; + +namespace { +// Limit for the maximum number of sample values that can be stored. +// TODO(asapersson): Consider using bucket count (and set up +// linearly/exponentially spaced buckets) if samples are logged more frequently. +const int kMaxSampleMapSize = 300; + +class RtcHistogram { + public: + RtcHistogram(const std::string& name, int min, int max, int bucket_count) + : min_(min), max_(max), info_(name, min, max, bucket_count) { + RTC_DCHECK_GT(bucket_count, 0); + } + + void Add(int sample) { + sample = std::min(sample, max_); + sample = std::max(sample, min_ - 1); // Underflow bucket. + + rtc::CritScope cs(&crit_); + if (info_.samples.size() == kMaxSampleMapSize && + info_.samples.find(sample) == info_.samples.end()) { + return; + } + ++info_.samples[sample]; + } + + // Returns a copy (or nullptr if there are no samples) and clears samples. + std::unique_ptr<SampleInfo> GetAndReset() { + rtc::CritScope cs(&crit_); + if (info_.samples.empty()) + return nullptr; + + SampleInfo* copy = + new SampleInfo(info_.name, info_.min, info_.max, info_.bucket_count); + + std::swap(info_.samples, copy->samples); + + return std::unique_ptr<SampleInfo>(copy); + } + + const std::string& name() const { return info_.name; } + + // Functions only for testing. + void Reset() { + rtc::CritScope cs(&crit_); + info_.samples.clear(); + } + + int NumEvents(int sample) const { + rtc::CritScope cs(&crit_); + const auto it = info_.samples.find(sample); + return (it == info_.samples.end()) ? 0 : it->second; + } + + int NumSamples() const { + int num_samples = 0; + rtc::CritScope cs(&crit_); + for (const auto& sample : info_.samples) { + num_samples += sample.second; + } + return num_samples; + } + + int MinSample() const { + rtc::CritScope cs(&crit_); + return (info_.samples.empty()) ? -1 : info_.samples.begin()->first; + } + + private: + rtc::CriticalSection crit_; + const int min_; + const int max_; + SampleInfo info_ RTC_GUARDED_BY(crit_); + + RTC_DISALLOW_COPY_AND_ASSIGN(RtcHistogram); +}; + +class RtcHistogramMap { + public: + RtcHistogramMap() {} + ~RtcHistogramMap() {} + + Histogram* GetCountsHistogram(const std::string& name, + int min, + int max, + int bucket_count) { + rtc::CritScope cs(&crit_); + const auto& it = map_.find(name); + if (it != map_.end()) + return reinterpret_cast<Histogram*>(it->second.get()); + + RtcHistogram* hist = new RtcHistogram(name, min, max, bucket_count); + map_[name].reset(hist); + return reinterpret_cast<Histogram*>(hist); + } + + Histogram* GetEnumerationHistogram(const std::string& name, int boundary) { + rtc::CritScope cs(&crit_); + const auto& it = map_.find(name); + if (it != map_.end()) + return reinterpret_cast<Histogram*>(it->second.get()); + + RtcHistogram* hist = new RtcHistogram(name, 1, boundary, boundary + 1); + map_[name].reset(hist); + return reinterpret_cast<Histogram*>(hist); + } + + void GetAndReset( + std::map<std::string, std::unique_ptr<SampleInfo>>* histograms) { + rtc::CritScope cs(&crit_); + for (const auto& kv : map_) { + std::unique_ptr<SampleInfo> info = kv.second->GetAndReset(); + if (info) + histograms->insert(std::make_pair(kv.first, std::move(info))); + } + } + + // Functions only for testing. + void Reset() { + rtc::CritScope cs(&crit_); + for (const auto& kv : map_) + kv.second->Reset(); + } + + int NumEvents(const std::string& name, int sample) const { + rtc::CritScope cs(&crit_); + const auto& it = map_.find(name); + return (it == map_.end()) ? 0 : it->second->NumEvents(sample); + } + + int NumSamples(const std::string& name) const { + rtc::CritScope cs(&crit_); + const auto& it = map_.find(name); + return (it == map_.end()) ? 0 : it->second->NumSamples(); + } + + int MinSample(const std::string& name) const { + rtc::CritScope cs(&crit_); + const auto& it = map_.find(name); + return (it == map_.end()) ? -1 : it->second->MinSample(); + } + + private: + rtc::CriticalSection crit_; + std::map<std::string, std::unique_ptr<RtcHistogram>> map_ + RTC_GUARDED_BY(crit_); + + RTC_DISALLOW_COPY_AND_ASSIGN(RtcHistogramMap); +}; + +// RtcHistogramMap is allocated upon call to Enable(). +// The histogram getter functions, which return pointer values to the histograms +// in the map, are cached in WebRTC. Therefore, this memory is not freed by the +// application (the memory will be reclaimed by the OS). +static RtcHistogramMap* volatile g_rtc_histogram_map = nullptr; + +void CreateMap() { + RtcHistogramMap* map = rtc::AtomicOps::AcquireLoadPtr(&g_rtc_histogram_map); + if (map == nullptr) { + RtcHistogramMap* new_map = new RtcHistogramMap(); + RtcHistogramMap* old_map = rtc::AtomicOps::CompareAndSwapPtr( + &g_rtc_histogram_map, static_cast<RtcHistogramMap*>(nullptr), new_map); + if (old_map != nullptr) + delete new_map; + } +} + +// Set the first time we start using histograms. Used to make sure Enable() is +// not called thereafter. +#if RTC_DCHECK_IS_ON +static volatile int g_rtc_histogram_called = 0; +#endif + +// Gets the map (or nullptr). +RtcHistogramMap* GetMap() { +#if RTC_DCHECK_IS_ON + rtc::AtomicOps::ReleaseStore(&g_rtc_histogram_called, 1); +#endif + return g_rtc_histogram_map; +} +} // namespace + +// Implementation of histogram methods in +// webrtc/system_wrappers/interface/metrics.h. + +// Histogram with exponentially spaced buckets. +// Creates (or finds) histogram. +// The returned histogram pointer is cached (and used for adding samples in +// subsequent calls). +Histogram* HistogramFactoryGetCounts(const std::string& name, + int min, + int max, + int bucket_count) { + // TODO(asapersson): Alternative implementation will be needed if this + // histogram type should be truly exponential. + return HistogramFactoryGetCountsLinear(name, min, max, bucket_count); +} + +// Histogram with linearly spaced buckets. +// Creates (or finds) histogram. +// The returned histogram pointer is cached (and used for adding samples in +// subsequent calls). +Histogram* HistogramFactoryGetCountsLinear(const std::string& name, + int min, + int max, + int bucket_count) { + RtcHistogramMap* map = GetMap(); + if (!map) + return nullptr; + + return map->GetCountsHistogram(name, min, max, bucket_count); +} + +// Histogram with linearly spaced buckets. +// Creates (or finds) histogram. +// The returned histogram pointer is cached (and used for adding samples in +// subsequent calls). +Histogram* HistogramFactoryGetEnumeration(const std::string& name, + int boundary) { + RtcHistogramMap* map = GetMap(); + if (!map) + return nullptr; + + return map->GetEnumerationHistogram(name, boundary); +} + +// Fast path. Adds |sample| to cached |histogram_pointer|. +void HistogramAdd(Histogram* histogram_pointer, int sample) { + RtcHistogram* ptr = reinterpret_cast<RtcHistogram*>(histogram_pointer); + ptr->Add(sample); +} + +SampleInfo::SampleInfo(const std::string& name, + int min, + int max, + size_t bucket_count) + : name(name), min(min), max(max), bucket_count(bucket_count) {} + +SampleInfo::~SampleInfo() {} + +// Implementation of global functions in metrics_default.h. +void Enable() { + RTC_DCHECK(g_rtc_histogram_map == nullptr); +#if RTC_DCHECK_IS_ON + RTC_DCHECK_EQ(0, rtc::AtomicOps::AcquireLoad(&g_rtc_histogram_called)); +#endif + CreateMap(); +} + +void GetAndReset( + std::map<std::string, std::unique_ptr<SampleInfo>>* histograms) { + histograms->clear(); + RtcHistogramMap* map = GetMap(); + if (map) + map->GetAndReset(histograms); +} + +void Reset() { + RtcHistogramMap* map = GetMap(); + if (map) + map->Reset(); +} + +int NumEvents(const std::string& name, int sample) { + RtcHistogramMap* map = GetMap(); + return map ? map->NumEvents(name, sample) : 0; +} + +int NumSamples(const std::string& name) { + RtcHistogramMap* map = GetMap(); + return map ? map->NumSamples(name) : 0; +} + +int MinSample(const std::string& name) { + RtcHistogramMap* map = GetMap(); + return map ? map->MinSample(name) : -1; +} + +} // namespace metrics +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/metrics_default_unittest.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/metrics_default_unittest.cc new file mode 100644 index 0000000000..fa253a9bfe --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/metrics_default_unittest.cc @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/metrics_default.h" +#include "system_wrappers/include/metrics.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { +const int kSample = 22; +const char kName[] = "Name"; + +int NumSamples( + const std::string& name, + const std::map<std::string, std::unique_ptr<metrics::SampleInfo>>& + histograms) { + const auto it = histograms.find(name); + if (it == histograms.end()) + return 0; + + int num_samples = 0; + for (const auto& sample : it->second->samples) + num_samples += sample.second; + + return num_samples; +} + +int NumEvents(const std::string& name, + int sample, + const std::map<std::string, std::unique_ptr<metrics::SampleInfo>>& + histograms) { + const auto it = histograms.find(name); + if (it == histograms.end()) + return 0; + + const auto it_sample = it->second->samples.find(sample); + if (it_sample == it->second->samples.end()) + return 0; + + return it_sample->second; +} +} // namespace + +class MetricsDefaultTest : public ::testing::Test { + public: + MetricsDefaultTest() {} + + protected: + virtual void SetUp() { metrics::Reset(); } +}; + +TEST_F(MetricsDefaultTest, Reset) { + RTC_HISTOGRAM_PERCENTAGE(kName, kSample); + EXPECT_EQ(1, metrics::NumSamples(kName)); + metrics::Reset(); + EXPECT_EQ(0, metrics::NumSamples(kName)); +} + +TEST_F(MetricsDefaultTest, NumSamples) { + RTC_HISTOGRAM_PERCENTAGE(kName, 5); + RTC_HISTOGRAM_PERCENTAGE(kName, 5); + RTC_HISTOGRAM_PERCENTAGE(kName, 10); + EXPECT_EQ(3, metrics::NumSamples(kName)); + EXPECT_EQ(0, metrics::NumSamples("NonExisting")); +} + +TEST_F(MetricsDefaultTest, NumEvents) { + RTC_HISTOGRAM_PERCENTAGE(kName, 5); + RTC_HISTOGRAM_PERCENTAGE(kName, 5); + RTC_HISTOGRAM_PERCENTAGE(kName, 10); + EXPECT_EQ(2, metrics::NumEvents(kName, 5)); + EXPECT_EQ(1, metrics::NumEvents(kName, 10)); + EXPECT_EQ(0, metrics::NumEvents(kName, 11)); + EXPECT_EQ(0, metrics::NumEvents("NonExisting", 5)); +} + +TEST_F(MetricsDefaultTest, MinSample) { + RTC_HISTOGRAM_PERCENTAGE(kName, kSample); + RTC_HISTOGRAM_PERCENTAGE(kName, kSample + 1); + EXPECT_EQ(kSample, metrics::MinSample(kName)); + EXPECT_EQ(-1, metrics::MinSample("NonExisting")); +} + +TEST_F(MetricsDefaultTest, Overflow) { + const std::string kName = "Overflow"; + // Samples should end up in overflow bucket. + RTC_HISTOGRAM_PERCENTAGE(kName, 101); + EXPECT_EQ(1, metrics::NumSamples(kName)); + EXPECT_EQ(1, metrics::NumEvents(kName, 101)); + RTC_HISTOGRAM_PERCENTAGE(kName, 102); + EXPECT_EQ(2, metrics::NumSamples(kName)); + EXPECT_EQ(2, metrics::NumEvents(kName, 101)); +} + +TEST_F(MetricsDefaultTest, Underflow) { + const std::string kName = "Underflow"; + // Samples should end up in underflow bucket. + RTC_HISTOGRAM_COUNTS_10000(kName, 0); + EXPECT_EQ(1, metrics::NumSamples(kName)); + EXPECT_EQ(1, metrics::NumEvents(kName, 0)); + RTC_HISTOGRAM_COUNTS_10000(kName, -1); + EXPECT_EQ(2, metrics::NumSamples(kName)); + EXPECT_EQ(2, metrics::NumEvents(kName, 0)); +} + +TEST_F(MetricsDefaultTest, GetAndReset) { + std::map<std::string, std::unique_ptr<metrics::SampleInfo>> histograms; + metrics::GetAndReset(&histograms); + EXPECT_EQ(0u, histograms.size()); + RTC_HISTOGRAM_PERCENTAGE("Histogram1", 4); + RTC_HISTOGRAM_PERCENTAGE("Histogram1", 5); + RTC_HISTOGRAM_PERCENTAGE("Histogram1", 5); + RTC_HISTOGRAM_PERCENTAGE("Histogram2", 10); + EXPECT_EQ(3, metrics::NumSamples("Histogram1")); + EXPECT_EQ(1, metrics::NumSamples("Histogram2")); + + metrics::GetAndReset(&histograms); + EXPECT_EQ(2u, histograms.size()); + EXPECT_EQ(0, metrics::NumSamples("Histogram1")); + EXPECT_EQ(0, metrics::NumSamples("Histogram2")); + + EXPECT_EQ(3, NumSamples("Histogram1", histograms)); + EXPECT_EQ(1, NumSamples("Histogram2", histograms)); + EXPECT_EQ(1, NumEvents("Histogram1", 4, histograms)); + EXPECT_EQ(2, NumEvents("Histogram1", 5, histograms)); + EXPECT_EQ(1, NumEvents("Histogram2", 10, histograms)); + + // Add samples after reset. + metrics::GetAndReset(&histograms); + EXPECT_EQ(0u, histograms.size()); + RTC_HISTOGRAM_PERCENTAGE("Histogram1", 50); + RTC_HISTOGRAM_PERCENTAGE("Histogram2", 8); + EXPECT_EQ(1, metrics::NumSamples("Histogram1")); + EXPECT_EQ(1, metrics::NumSamples("Histogram2")); + EXPECT_EQ(1, metrics::NumEvents("Histogram1", 50)); + EXPECT_EQ(1, metrics::NumEvents("Histogram2", 8)); +} + +TEST_F(MetricsDefaultTest, TestMinMaxBucket) { + const std::string kName = "MinMaxCounts100"; + RTC_HISTOGRAM_COUNTS_100(kName, 4); + + std::map<std::string, std::unique_ptr<metrics::SampleInfo>> histograms; + metrics::GetAndReset(&histograms); + EXPECT_EQ(1u, histograms.size()); + EXPECT_EQ(kName, histograms.begin()->second->name); + EXPECT_EQ(1, histograms.begin()->second->min); + EXPECT_EQ(100, histograms.begin()->second->max); + EXPECT_EQ(50u, histograms.begin()->second->bucket_count); + EXPECT_EQ(1u, histograms.begin()->second->samples.size()); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/metrics_unittest.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/metrics_unittest.cc new file mode 100644 index 0000000000..53d43cd2b1 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/metrics_unittest.cc @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/metrics.h" +#include "system_wrappers/include/metrics_default.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { +const int kSample = 22; + +void AddSparseSample(const std::string& name, int sample) { + RTC_HISTOGRAM_COUNTS_SPARSE_100(name, sample); +} +void AddSampleWithVaryingName(int index, const std::string& name, int sample) { + RTC_HISTOGRAMS_COUNTS_100(index, name, sample); +} +} // namespace + +class MetricsTest : public ::testing::Test { + public: + MetricsTest() {} + + protected: + virtual void SetUp() { metrics::Reset(); } +}; + +TEST_F(MetricsTest, InitiallyNoSamples) { + EXPECT_EQ(0, metrics::NumSamples("NonExisting")); + EXPECT_EQ(0, metrics::NumEvents("NonExisting", kSample)); +} + +TEST_F(MetricsTest, RtcHistogramPercent_AddSample) { + const std::string kName = "Percentage"; + RTC_HISTOGRAM_PERCENTAGE(kName, kSample); + EXPECT_EQ(1, metrics::NumSamples(kName)); + EXPECT_EQ(1, metrics::NumEvents(kName, kSample)); +} + +TEST_F(MetricsTest, RtcHistogramEnumeration_AddSample) { + const std::string kName = "Enumeration"; + RTC_HISTOGRAM_ENUMERATION(kName, kSample, kSample + 1); + EXPECT_EQ(1, metrics::NumSamples(kName)); + EXPECT_EQ(1, metrics::NumEvents(kName, kSample)); +} + +TEST_F(MetricsTest, RtcHistogramBoolean_AddSample) { + const std::string kName = "Boolean"; + const int kSample = 0; + RTC_HISTOGRAM_BOOLEAN(kName, kSample); + EXPECT_EQ(1, metrics::NumSamples(kName)); + EXPECT_EQ(1, metrics::NumEvents(kName, kSample)); +} + +TEST_F(MetricsTest, RtcHistogramCountsSparse_AddSample) { + const std::string kName = "CountsSparse100"; + RTC_HISTOGRAM_COUNTS_SPARSE_100(kName, kSample); + EXPECT_EQ(1, metrics::NumSamples(kName)); + EXPECT_EQ(1, metrics::NumEvents(kName, kSample)); +} + +TEST_F(MetricsTest, RtcHistogramCounts_AddSample) { + const std::string kName = "Counts100"; + RTC_HISTOGRAM_COUNTS_100(kName, kSample); + EXPECT_EQ(1, metrics::NumSamples(kName)); + EXPECT_EQ(1, metrics::NumEvents(kName, kSample)); +} + +TEST_F(MetricsTest, RtcHistogramCounts_AddMultipleSamples) { + const std::string kName = "Counts200"; + const int kNumSamples = 10; + for (int i = 1; i <= kNumSamples; ++i) { + RTC_HISTOGRAM_COUNTS_200(kName, i); + EXPECT_EQ(1, metrics::NumEvents(kName, i)); + EXPECT_EQ(i, metrics::NumSamples(kName)); + } +} + +TEST_F(MetricsTest, RtcHistogramsCounts_AddSample) { + AddSampleWithVaryingName(0, "Name1", kSample); + AddSampleWithVaryingName(1, "Name2", kSample + 1); + AddSampleWithVaryingName(2, "Name3", kSample + 2); + EXPECT_EQ(1, metrics::NumSamples("Name1")); + EXPECT_EQ(1, metrics::NumSamples("Name2")); + EXPECT_EQ(1, metrics::NumSamples("Name3")); + EXPECT_EQ(1, metrics::NumEvents("Name1", kSample + 0)); + EXPECT_EQ(1, metrics::NumEvents("Name2", kSample + 1)); + EXPECT_EQ(1, metrics::NumEvents("Name3", kSample + 2)); +} + +#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) +TEST_F(MetricsTest, RtcHistogramsCounts_InvalidIndex) { + EXPECT_DEATH(RTC_HISTOGRAMS_COUNTS_1000(-1, "Name", kSample), ""); + EXPECT_DEATH(RTC_HISTOGRAMS_COUNTS_1000(3, "Name", kSample), ""); + EXPECT_DEATH(RTC_HISTOGRAMS_COUNTS_1000(3u, "Name", kSample), ""); +} +#endif + +TEST_F(MetricsTest, RtcHistogramSparse_NonConstantNameWorks) { + AddSparseSample("Sparse1", kSample); + AddSparseSample("Sparse2", kSample); + EXPECT_EQ(1, metrics::NumSamples("Sparse1")); + EXPECT_EQ(1, metrics::NumSamples("Sparse2")); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/ntp_time_unittest.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/ntp_time_unittest.cc new file mode 100644 index 0000000000..7be464d02d --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/ntp_time_unittest.cc @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/ntp_time.h" +#include "system_wrappers/include/clock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +const uint32_t kNtpSec = 0x12345678; +const uint32_t kNtpFrac = 0x23456789; + +TEST(NtpTimeTest, NoValueMeansInvalid) { + NtpTime ntp; + EXPECT_FALSE(ntp.Valid()); +} + +TEST(NtpTimeTest, CanResetValue) { + NtpTime ntp(kNtpSec, kNtpFrac); + EXPECT_TRUE(ntp.Valid()); + ntp.Reset(); + EXPECT_FALSE(ntp.Valid()); +} + +TEST(NtpTimeTest, CanGetWhatIsSet) { + NtpTime ntp; + ntp.Set(kNtpSec, kNtpFrac); + EXPECT_EQ(kNtpSec, ntp.seconds()); + EXPECT_EQ(kNtpFrac, ntp.fractions()); +} + +TEST(NtpTimeTest, SetIsSameAs2ParameterConstructor) { + NtpTime ntp1(kNtpSec, kNtpFrac); + NtpTime ntp2; + EXPECT_NE(ntp1, ntp2); + + ntp2.Set(kNtpSec, kNtpFrac); + EXPECT_EQ(ntp1, ntp2); +} + +TEST(NtpTimeTest, ToMsMeansToNtpMilliseconds) { + SimulatedClock clock(0x123456789abc); + + NtpTime ntp = clock.CurrentNtpTime(); + EXPECT_EQ(ntp.ToMs(), Clock::NtpToMs(ntp.seconds(), ntp.fractions())); + EXPECT_EQ(ntp.ToMs(), clock.CurrentNtpInMilliseconds()); +} + +TEST(NtpTimeTest, CanExplicitlyConvertToAndFromUint64) { + uint64_t untyped_time = 0x123456789; + NtpTime time(untyped_time); + EXPECT_EQ(untyped_time, static_cast<uint64_t>(time)); + EXPECT_EQ(NtpTime(0x12345678, 0x90abcdef), NtpTime(0x1234567890abcdef)); +} + +} // namespace +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/rtp_to_ntp_estimator.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/rtp_to_ntp_estimator.cc new file mode 100644 index 0000000000..21f64eca25 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/rtp_to_ntp_estimator.cc @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/rtp_to_ntp_estimator.h" + +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "system_wrappers/include/clock.h" + +namespace webrtc { +namespace { +// Number of RTCP SR reports to use to map between RTP and NTP. +const size_t kNumRtcpReportsToUse = 2; +// Number of parameters samples used to smooth. +const size_t kNumSamplesToSmooth = 20; + + +// Calculates the RTP timestamp frequency from two pairs of NTP/RTP timestamps. +bool CalculateFrequency(int64_t ntp_ms1, + uint32_t rtp_timestamp1, + int64_t ntp_ms2, + uint32_t rtp_timestamp2, + double* frequency_khz) { + if (ntp_ms1 <= ntp_ms2) + return false; + + *frequency_khz = static_cast<double>(rtp_timestamp1 - rtp_timestamp2) / + static_cast<double>(ntp_ms1 - ntp_ms2); + return true; +} + +bool Contains(const std::list<RtpToNtpEstimator::RtcpMeasurement>& measurements, + const RtpToNtpEstimator::RtcpMeasurement& other) { + for (const auto& measurement : measurements) { + if (measurement.IsEqual(other)) + return true; + } + return false; +} +} // namespace + +bool RtpToNtpEstimator::Parameters::operator<(const Parameters& other) const { + if (frequency_khz < other.frequency_khz - 1e-6) { + return true; + } else if (frequency_khz > other.frequency_khz + 1e-6) { + return false; + } else { + return offset_ms < other.offset_ms - 1e-6; + } +} + +bool RtpToNtpEstimator::Parameters::operator==(const Parameters& other) const { + return !(other < *this || *this < other); +} + +bool RtpToNtpEstimator::Parameters::operator!=(const Parameters& other) const { + return other < *this || *this < other; +} + +bool RtpToNtpEstimator::Parameters::operator<=(const Parameters& other) const { + return !(other < *this); +} + +RtpToNtpEstimator::RtcpMeasurement::RtcpMeasurement(uint32_t ntp_secs, + uint32_t ntp_frac, + int64_t unwrapped_timestamp) + : ntp_time(ntp_secs, ntp_frac), + unwrapped_rtp_timestamp(unwrapped_timestamp) {} + +bool RtpToNtpEstimator::RtcpMeasurement::IsEqual( + const RtcpMeasurement& other) const { + // Use || since two equal timestamps will result in zero frequency and in + // RtpToNtpMs, |rtp_timestamp_ms| is estimated by dividing by the frequency. + return (ntp_time == other.ntp_time) || + (unwrapped_rtp_timestamp == other.unwrapped_rtp_timestamp); +} + +// Class for converting an RTP timestamp to the NTP domain. +RtpToNtpEstimator::RtpToNtpEstimator() + : consecutive_invalid_samples_(0), + smoothing_filter_(kNumSamplesToSmooth), + params_calculated_(false) {} + +RtpToNtpEstimator::~RtpToNtpEstimator() {} + +void RtpToNtpEstimator::UpdateParameters() { + if (measurements_.size() != kNumRtcpReportsToUse) + return; + + Parameters params; + int64_t timestamp_new = measurements_.front().unwrapped_rtp_timestamp; + int64_t timestamp_old = measurements_.back().unwrapped_rtp_timestamp; + + int64_t ntp_ms_new = measurements_.front().ntp_time.ToMs(); + int64_t ntp_ms_old = measurements_.back().ntp_time.ToMs(); + + if (!CalculateFrequency(ntp_ms_new, timestamp_new, ntp_ms_old, timestamp_old, + ¶ms.frequency_khz)) { + return; + } + params.offset_ms = timestamp_new - params.frequency_khz * ntp_ms_new; + params_calculated_ = true; + smoothing_filter_.Insert(params); +} + +bool RtpToNtpEstimator::UpdateMeasurements(uint32_t ntp_secs, + uint32_t ntp_frac, + uint32_t rtp_timestamp, + bool* new_rtcp_sr) { + *new_rtcp_sr = false; + + int64_t unwrapped_rtp_timestamp = unwrapper_.Unwrap(rtp_timestamp); + + RtcpMeasurement new_measurement(ntp_secs, ntp_frac, unwrapped_rtp_timestamp); + + if (Contains(measurements_, new_measurement)) { + // RTCP SR report already added. + return true; + } + + if (!new_measurement.ntp_time.Valid()) + return false; + + int64_t ntp_ms_new = new_measurement.ntp_time.ToMs(); + bool invalid_sample = false; + if (!measurements_.empty()) { + int64_t old_rtp_timestamp = measurements_.front().unwrapped_rtp_timestamp; + int64_t old_ntp_ms = measurements_.front().ntp_time.ToMs(); + if (ntp_ms_new <= old_ntp_ms) { + invalid_sample = true; + } else if (unwrapped_rtp_timestamp <= old_rtp_timestamp) { + RTC_LOG(LS_WARNING) + << "Newer RTCP SR report with older RTP timestamp, dropping"; + invalid_sample = true; + } else if (unwrapped_rtp_timestamp - old_rtp_timestamp > (1 << 25)) { + // Sanity check. No jumps too far into the future in rtp. + invalid_sample = true; + } + } + + if (invalid_sample) { + ++consecutive_invalid_samples_; + if (consecutive_invalid_samples_ < kMaxInvalidSamples) { + return false; + } + RTC_LOG(LS_WARNING) << "Multiple consecutively invalid RTCP SR reports, " + "clearing measurements."; + measurements_.clear(); + smoothing_filter_.Reset(); + params_calculated_ = false; + } + consecutive_invalid_samples_ = 0; + + // Insert new RTCP SR report. + if (measurements_.size() == kNumRtcpReportsToUse) + measurements_.pop_back(); + + measurements_.push_front(new_measurement); + *new_rtcp_sr = true; + + // List updated, calculate new parameters. + UpdateParameters(); + return true; +} + +bool RtpToNtpEstimator::Estimate(int64_t rtp_timestamp, + int64_t* rtp_timestamp_ms) const { + if (!params_calculated_) + return false; + + int64_t rtp_timestamp_unwrapped = unwrapper_.Unwrap(rtp_timestamp); + + Parameters params = smoothing_filter_.GetFilteredValue(); + + // params_calculated_ should not be true unless ms params.frequency_khz has + // been calculated to something non zero. + RTC_DCHECK_NE(params.frequency_khz, 0.0); + double rtp_ms = + (static_cast<double>(rtp_timestamp_unwrapped) - params.offset_ms) / + params.frequency_khz + + 0.5f; + + if (rtp_ms < 0) + return false; + + *rtp_timestamp_ms = rtp_ms; + return true; +} + +const rtc::Optional<RtpToNtpEstimator::Parameters> RtpToNtpEstimator::params() + const { + rtc::Optional<Parameters> res; + if (params_calculated_) { + res.emplace(smoothing_filter_.GetFilteredValue()); + } + return res; +} +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/rtp_to_ntp_estimator_unittest.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/rtp_to_ntp_estimator_unittest.cc new file mode 100644 index 0000000000..0647ec8cad --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/rtp_to_ntp_estimator_unittest.cc @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/rtp_to_ntp_estimator.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { +const uint32_t kOneMsInNtpFrac = 4294967; +const uint32_t kTimestampTicksPerMs = 90; +} // namespace + +TEST(WrapAroundTests, OldRtcpWrapped_OldRtpTimestamp) { + RtpToNtpEstimator estimator; + bool new_sr; + uint32_t ntp_sec = 0; + uint32_t ntp_frac = 1; + uint32_t timestamp = 0; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + ntp_frac += kOneMsInNtpFrac; + timestamp -= kTimestampTicksPerMs; + // No wraparound will be detected, since we are not allowed to wrap below 0, + // but there will be huge rtp timestamp jump, e.g. old_timestamp = 0, + // new_timestamp = 4294967295, which should be detected. + EXPECT_FALSE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); +} + +TEST(WrapAroundTests, OldRtcpWrapped_OldRtpTimestamp_Wraparound_Detected) { + RtpToNtpEstimator estimator; + bool new_sr; + uint32_t ntp_sec = 0; + uint32_t ntp_frac = 1; + uint32_t timestamp = 0xFFFFFFFE; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + ntp_frac += 2 * kOneMsInNtpFrac; + timestamp += 2 * kTimestampTicksPerMs; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + ntp_frac += kOneMsInNtpFrac; + timestamp -= kTimestampTicksPerMs; + // Expected to fail since the older RTCP has a smaller RTP timestamp than the + // newer (old:10, new:4294967206). + EXPECT_FALSE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); +} + +TEST(WrapAroundTests, NewRtcpWrapped) { + RtpToNtpEstimator estimator; + bool new_sr; + uint32_t ntp_sec = 0; + uint32_t ntp_frac = 1; + uint32_t timestamp = 0xFFFFFFFF; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + ntp_frac += kOneMsInNtpFrac; + timestamp += kTimestampTicksPerMs; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + int64_t timestamp_ms = -1; + EXPECT_TRUE(estimator.Estimate(0xFFFFFFFF, ×tamp_ms)); + // Since this RTP packet has the same timestamp as the RTCP packet constructed + // at time 0 it should be mapped to 0 as well. + EXPECT_EQ(0, timestamp_ms); +} + +TEST(WrapAroundTests, RtpWrapped) { + RtpToNtpEstimator estimator; + bool new_sr; + uint32_t ntp_sec = 0; + uint32_t ntp_frac = 1; + uint32_t timestamp = 0xFFFFFFFF - 2 * kTimestampTicksPerMs; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + ntp_frac += kOneMsInNtpFrac; + timestamp += kTimestampTicksPerMs; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + + int64_t timestamp_ms = -1; + EXPECT_TRUE( + estimator.Estimate(0xFFFFFFFF - 2 * kTimestampTicksPerMs, ×tamp_ms)); + // Since this RTP packet has the same timestamp as the RTCP packet constructed + // at time 0 it should be mapped to 0 as well. + EXPECT_EQ(0, timestamp_ms); + // Two kTimestampTicksPerMs advanced. + timestamp += kTimestampTicksPerMs; + EXPECT_TRUE(estimator.Estimate(timestamp, ×tamp_ms)); + EXPECT_EQ(2, timestamp_ms); + // Wrapped rtp. + timestamp += kTimestampTicksPerMs; + EXPECT_TRUE(estimator.Estimate(timestamp, ×tamp_ms)); + EXPECT_EQ(3, timestamp_ms); +} + +TEST(WrapAroundTests, OldRtp_RtcpsWrapped) { + RtpToNtpEstimator estimator; + bool new_sr; + uint32_t ntp_sec = 0; + uint32_t ntp_frac = 1; + uint32_t timestamp = 0xFFFFFFFF; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + ntp_frac += kOneMsInNtpFrac; + timestamp += kTimestampTicksPerMs; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + timestamp -= 2 * kTimestampTicksPerMs; + int64_t timestamp_ms = 0xFFFFFFFF; + EXPECT_FALSE(estimator.Estimate(timestamp, ×tamp_ms)); +} + +TEST(WrapAroundTests, OldRtp_NewRtcpWrapped) { + RtpToNtpEstimator estimator; + bool new_sr; + uint32_t ntp_sec = 0; + uint32_t ntp_frac = 1; + uint32_t timestamp = 0xFFFFFFFF; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + ntp_frac += kOneMsInNtpFrac; + timestamp += kTimestampTicksPerMs; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + timestamp -= kTimestampTicksPerMs; + int64_t timestamp_ms = -1; + EXPECT_TRUE(estimator.Estimate(timestamp, ×tamp_ms)); + // Constructed at the same time as the first RTCP and should therefore be + // mapped to zero. + EXPECT_EQ(0, timestamp_ms); +} + +TEST(WrapAroundTests, GracefullyHandleRtpJump) { + RtpToNtpEstimator estimator; + bool new_sr; + uint32_t ntp_sec = 0; + uint32_t ntp_frac = 1; + uint32_t timestamp = 0; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + ntp_frac += kOneMsInNtpFrac; + timestamp += kTimestampTicksPerMs; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + ntp_frac += kOneMsInNtpFrac; + timestamp -= kTimestampTicksPerMs; + int64_t timestamp_ms = -1; + EXPECT_TRUE(estimator.Estimate(timestamp, ×tamp_ms)); + // Constructed at the same time as the first RTCP and should therefore be + // mapped to zero. + EXPECT_EQ(0, timestamp_ms); + + timestamp -= 0xFFFFF; + for (int i = 0; i < RtpToNtpEstimator::kMaxInvalidSamples - 1; ++i) { + EXPECT_FALSE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + ntp_frac += kOneMsInNtpFrac; + timestamp += kTimestampTicksPerMs; + } + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + ntp_frac += kOneMsInNtpFrac; + timestamp += kTimestampTicksPerMs; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + ntp_frac += kOneMsInNtpFrac; + timestamp += kTimestampTicksPerMs; + + timestamp_ms = -1; + EXPECT_TRUE(estimator.Estimate(timestamp, ×tamp_ms)); + // 6 milliseconds has passed since the start of the test. + EXPECT_EQ(6, timestamp_ms); +} + +TEST(UpdateRtcpMeasurementTests, FailsForZeroNtp) { + RtpToNtpEstimator estimator; + uint32_t ntp_sec = 0; + uint32_t ntp_frac = 0; + uint32_t timestamp = 0x12345678; + bool new_sr; + EXPECT_FALSE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + EXPECT_FALSE(new_sr); +} + +TEST(UpdateRtcpMeasurementTests, FailsForEqualNtp) { + RtpToNtpEstimator estimator; + uint32_t ntp_sec = 0; + uint32_t ntp_frac = 699925050; + uint32_t timestamp = 0x12345678; + bool new_sr; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + EXPECT_TRUE(new_sr); + // Ntp time already added, list not updated. + ++timestamp; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + EXPECT_FALSE(new_sr); +} + +TEST(UpdateRtcpMeasurementTests, FailsForOldNtp) { + RtpToNtpEstimator estimator; + uint32_t ntp_sec = 1; + uint32_t ntp_frac = 699925050; + uint32_t timestamp = 0x12345678; + bool new_sr; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + EXPECT_TRUE(new_sr); + // Old ntp time, list not updated. + ntp_frac -= kOneMsInNtpFrac; + timestamp += kTimestampTicksPerMs; + EXPECT_FALSE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); +} + +TEST(UpdateRtcpMeasurementTests, FailsForEqualTimestamp) { + RtpToNtpEstimator estimator; + uint32_t ntp_sec = 0; + uint32_t ntp_frac = 2; + uint32_t timestamp = 0x12345678; + bool new_sr; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + EXPECT_TRUE(new_sr); + // Timestamp already added, list not updated. + ++ntp_frac; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + EXPECT_FALSE(new_sr); +} + +TEST(UpdateRtcpMeasurementTests, FailsForOldRtpTimestamp) { + RtpToNtpEstimator estimator; + uint32_t ntp_sec = 0; + uint32_t ntp_frac = 2; + uint32_t timestamp = 0x12345678; + bool new_sr; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + EXPECT_TRUE(new_sr); + // Old timestamp, list not updated. + ntp_frac += kOneMsInNtpFrac; + timestamp -= kTimestampTicksPerMs; + EXPECT_FALSE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + EXPECT_FALSE(new_sr); +} + +TEST(UpdateRtcpMeasurementTests, VerifyParameters) { + RtpToNtpEstimator estimator; + uint32_t ntp_sec = 1; + uint32_t ntp_frac = 2; + uint32_t timestamp = 0x12345678; + bool new_sr; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + EXPECT_TRUE(new_sr); + EXPECT_FALSE(estimator.params()); + // Add second report, parameters should be calculated. + ntp_frac += kOneMsInNtpFrac; + timestamp += kTimestampTicksPerMs; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + EXPECT_TRUE(estimator.params()); + EXPECT_DOUBLE_EQ(90.0, estimator.params()->frequency_khz); + EXPECT_NE(0.0, estimator.params()->offset_ms); +} + +TEST(RtpToNtpTests, FailsForNoParameters) { + RtpToNtpEstimator estimator; + uint32_t ntp_sec = 1; + uint32_t ntp_frac = 2; + uint32_t timestamp = 0x12345678; + bool new_sr; + EXPECT_TRUE( + estimator.UpdateMeasurements(ntp_sec, ntp_frac, timestamp, &new_sr)); + EXPECT_TRUE(new_sr); + // Parameters are not calculated, conversion of RTP to NTP time should fail. + EXPECT_FALSE(estimator.params()); + int64_t timestamp_ms = -1; + EXPECT_FALSE(estimator.Estimate(timestamp, ×tamp_ms)); +} + +}; // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/rw_lock.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/rw_lock.cc new file mode 100644 index 0000000000..c38c44ad75 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/rw_lock.cc @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/rw_lock_wrapper.h" + +#include <assert.h> + +#if defined(_WIN32) +#include "system_wrappers/source/rw_lock_win.h" +#else +#include "system_wrappers/source/rw_lock_posix.h" +#endif + +namespace webrtc { + +RWLockWrapper* RWLockWrapper::CreateRWLock() { +#ifdef _WIN32 + return RWLockWin::Create(); +#else + return RWLockPosix::Create(); +#endif +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/rw_lock_posix.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/rw_lock_posix.cc new file mode 100644 index 0000000000..412873c770 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/rw_lock_posix.cc @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/source/rw_lock_posix.h" + +namespace webrtc { + +RWLockPosix::RWLockPosix() : lock_() {} + +RWLockPosix::~RWLockPosix() { + pthread_rwlock_destroy(&lock_); +} + +RWLockPosix* RWLockPosix::Create() { + RWLockPosix* ret_val = new RWLockPosix(); + if (!ret_val->Init()) { + delete ret_val; + return NULL; + } + return ret_val; +} + +bool RWLockPosix::Init() { + return pthread_rwlock_init(&lock_, 0) == 0; +} + +void RWLockPosix::AcquireLockExclusive() { + pthread_rwlock_wrlock(&lock_); +} + +void RWLockPosix::ReleaseLockExclusive() { + pthread_rwlock_unlock(&lock_); +} + +void RWLockPosix::AcquireLockShared() { + pthread_rwlock_rdlock(&lock_); +} + +void RWLockPosix::ReleaseLockShared() { + pthread_rwlock_unlock(&lock_); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/rw_lock_posix.h b/third_party/libwebrtc/webrtc/system_wrappers/source/rw_lock_posix.h new file mode 100644 index 0000000000..d15682b928 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/rw_lock_posix.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SYSTEM_WRAPPERS_SOURCE_RW_LOCK_POSIX_H_ +#define SYSTEM_WRAPPERS_SOURCE_RW_LOCK_POSIX_H_ + +#include "system_wrappers/include/rw_lock_wrapper.h" +#include "typedefs.h" // NOLINT(build/include) + +#include <pthread.h> + +namespace webrtc { + +class RWLockPosix : public RWLockWrapper { + public: + static RWLockPosix* Create(); + ~RWLockPosix() override; + + void AcquireLockExclusive() override; + void ReleaseLockExclusive() override; + + void AcquireLockShared() override; + void ReleaseLockShared() override; + + private: + RWLockPosix(); + bool Init(); + + pthread_rwlock_t lock_; +}; + +} // namespace webrtc + +#endif // SYSTEM_WRAPPERS_SOURCE_RW_LOCK_POSIX_H_ diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/rw_lock_win.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/rw_lock_win.cc new file mode 100644 index 0000000000..23df15a275 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/rw_lock_win.cc @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/source/rw_lock_win.h" + +#include "rtc_base/logging.h" + +namespace webrtc { + +static bool native_rw_locks_supported = false; +static bool module_load_attempted = false; +static HMODULE library = NULL; + +typedef void(WINAPI* InitializeSRWLock)(PSRWLOCK); + +typedef void(WINAPI* AcquireSRWLockExclusive)(PSRWLOCK); +typedef void(WINAPI* ReleaseSRWLockExclusive)(PSRWLOCK); + +typedef void(WINAPI* AcquireSRWLockShared)(PSRWLOCK); +typedef void(WINAPI* ReleaseSRWLockShared)(PSRWLOCK); + +InitializeSRWLock initialize_srw_lock; +AcquireSRWLockExclusive acquire_srw_lock_exclusive; +AcquireSRWLockShared acquire_srw_lock_shared; +ReleaseSRWLockShared release_srw_lock_shared; +ReleaseSRWLockExclusive release_srw_lock_exclusive; + +RWLockWin::RWLockWin() { + initialize_srw_lock(&lock_); +} + +RWLockWin* RWLockWin::Create() { + if (!LoadModule()) { + return NULL; + } + return new RWLockWin(); +} + +void RWLockWin::AcquireLockExclusive() { + acquire_srw_lock_exclusive(&lock_); +} + +void RWLockWin::ReleaseLockExclusive() { + release_srw_lock_exclusive(&lock_); +} + +void RWLockWin::AcquireLockShared() { + acquire_srw_lock_shared(&lock_); +} + +void RWLockWin::ReleaseLockShared() { + release_srw_lock_shared(&lock_); +} + +bool RWLockWin::LoadModule() { + if (module_load_attempted) { + return native_rw_locks_supported; + } + module_load_attempted = true; + // Use native implementation if supported (i.e Vista+) + library = LoadLibrary(TEXT("Kernel32.dll")); + if (!library) { + return false; + } + RTC_LOG(LS_VERBOSE) << "Loaded Kernel.dll"; + + initialize_srw_lock = + (InitializeSRWLock)GetProcAddress(library, "InitializeSRWLock"); + + acquire_srw_lock_exclusive = (AcquireSRWLockExclusive)GetProcAddress( + library, "AcquireSRWLockExclusive"); + release_srw_lock_exclusive = (ReleaseSRWLockExclusive)GetProcAddress( + library, "ReleaseSRWLockExclusive"); + acquire_srw_lock_shared = + (AcquireSRWLockShared)GetProcAddress(library, "AcquireSRWLockShared"); + release_srw_lock_shared = + (ReleaseSRWLockShared)GetProcAddress(library, "ReleaseSRWLockShared"); + + if (initialize_srw_lock && acquire_srw_lock_exclusive && + release_srw_lock_exclusive && acquire_srw_lock_shared && + release_srw_lock_shared) { + RTC_LOG(LS_VERBOSE) << "Loaded Native RW Lock"; + native_rw_locks_supported = true; + } + return native_rw_locks_supported; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/rw_lock_win.h b/third_party/libwebrtc/webrtc/system_wrappers/source/rw_lock_win.h new file mode 100644 index 0000000000..41537ba10b --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/rw_lock_win.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SYSTEM_WRAPPERS_SOURCE_RW_LOCK_WIN_H_ +#define SYSTEM_WRAPPERS_SOURCE_RW_LOCK_WIN_H_ + +#include "system_wrappers/include/rw_lock_wrapper.h" + +#include <Windows.h> + +namespace webrtc { + +class RWLockWin : public RWLockWrapper { + public: + static RWLockWin* Create(); + ~RWLockWin() {} + + virtual void AcquireLockExclusive(); + virtual void ReleaseLockExclusive(); + + virtual void AcquireLockShared(); + virtual void ReleaseLockShared(); + + private: + RWLockWin(); + static bool LoadModule(); + + SRWLOCK lock_; +}; + +} // namespace webrtc + +#endif // SYSTEM_WRAPPERS_SOURCE_RW_LOCK_WIN_H_ diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/sleep.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/sleep.cc new file mode 100644 index 0000000000..e2fa486118 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/sleep.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +// An OS-independent sleep function. + +#include "system_wrappers/include/sleep.h" + +#ifdef _WIN32 +// For Sleep() +#include <windows.h> +#else +// For nanosleep() +#include <time.h> +#endif + +namespace webrtc { + +void SleepMs(int msecs) { +#ifdef _WIN32 + Sleep(msecs); +#else + struct timespec short_wait; + struct timespec remainder; + short_wait.tv_sec = msecs / 1000; + short_wait.tv_nsec = (msecs % 1000) * 1000 * 1000; + nanosleep(&short_wait, &remainder); +#endif +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/system_wrappers/source/timestamp_extrapolator.cc b/third_party/libwebrtc/webrtc/system_wrappers/source/timestamp_extrapolator.cc new file mode 100644 index 0000000000..b8c6ba0934 --- /dev/null +++ b/third_party/libwebrtc/webrtc/system_wrappers/source/timestamp_extrapolator.cc @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "system_wrappers/include/timestamp_extrapolator.h" + +#include <algorithm> + +namespace webrtc { + +TimestampExtrapolator::TimestampExtrapolator(int64_t start_ms) + : _rwLock(RWLockWrapper::CreateRWLock()), + _startMs(0), + _firstTimestamp(0), + _wrapArounds(0), + _prevUnwrappedTimestamp(-1), + _prevWrapTimestamp(-1), + _lambda(1), + _firstAfterReset(true), + _packetCount(0), + _startUpFilterDelayInPackets(2), + _detectorAccumulatorPos(0), + _detectorAccumulatorNeg(0), + _alarmThreshold(60e3), + _accDrift(6600), // in timestamp ticks, i.e. 15 ms + _accMaxError(7000), + _pP11(1e10) { + Reset(start_ms); +} + +TimestampExtrapolator::~TimestampExtrapolator() { + delete _rwLock; +} + +void TimestampExtrapolator::Reset(int64_t start_ms) { + WriteLockScoped wl(*_rwLock); + _startMs = start_ms; + _prevMs = _startMs; + _firstTimestamp = 0; + _w[0] = 90.0; + _w[1] = 0; + _pP[0][0] = 1; + _pP[1][1] = _pP11; + _pP[0][1] = _pP[1][0] = 0; + _firstAfterReset = true; + _prevUnwrappedTimestamp = -1; + _prevWrapTimestamp = -1; + _wrapArounds = 0; + _packetCount = 0; + _detectorAccumulatorPos = 0; + _detectorAccumulatorNeg = 0; +} + +void TimestampExtrapolator::Update(int64_t tMs, uint32_t ts90khz) { + _rwLock->AcquireLockExclusive(); + if (tMs - _prevMs > 10e3) { + // Ten seconds without a complete frame. + // Reset the extrapolator + _rwLock->ReleaseLockExclusive(); + Reset(tMs); + _rwLock->AcquireLockExclusive(); + } else { + _prevMs = tMs; + } + + // Remove offset to prevent badly scaled matrices + tMs -= _startMs; + + CheckForWrapArounds(ts90khz); + + int64_t unwrapped_ts90khz = + static_cast<int64_t>(ts90khz) + + _wrapArounds * ((static_cast<int64_t>(1) << 32) - 1); + + if (_firstAfterReset) { + // Make an initial guess of the offset, + // should be almost correct since tMs - _startMs + // should about zero at this time. + _w[1] = -_w[0] * tMs; + _firstTimestamp = unwrapped_ts90khz; + _firstAfterReset = false; + } + + double residual = (static_cast<double>(unwrapped_ts90khz) - _firstTimestamp) - + static_cast<double>(tMs) * _w[0] - _w[1]; + if (DelayChangeDetection(residual) && + _packetCount >= _startUpFilterDelayInPackets) { + // A sudden change of average network delay has been detected. + // Force the filter to adjust its offset parameter by changing + // the offset uncertainty. Don't do this during startup. + _pP[1][1] = _pP11; + } + + if (_prevUnwrappedTimestamp >= 0 && + unwrapped_ts90khz < _prevUnwrappedTimestamp) { + // Drop reordered frames. + _rwLock->ReleaseLockExclusive(); + return; + } + + // T = [t(k) 1]'; + // that = T'*w; + // K = P*T/(lambda + T'*P*T); + double K[2]; + K[0] = _pP[0][0] * tMs + _pP[0][1]; + K[1] = _pP[1][0] * tMs + _pP[1][1]; + double TPT = _lambda + tMs * K[0] + K[1]; + K[0] /= TPT; + K[1] /= TPT; + // w = w + K*(ts(k) - that); + _w[0] = _w[0] + K[0] * residual; + _w[1] = _w[1] + K[1] * residual; + // P = 1/lambda*(P - K*T'*P); + double p00 = + 1 / _lambda * (_pP[0][0] - (K[0] * tMs * _pP[0][0] + K[0] * _pP[1][0])); + double p01 = + 1 / _lambda * (_pP[0][1] - (K[0] * tMs * _pP[0][1] + K[0] * _pP[1][1])); + _pP[1][0] = + 1 / _lambda * (_pP[1][0] - (K[1] * tMs * _pP[0][0] + K[1] * _pP[1][0])); + _pP[1][1] = + 1 / _lambda * (_pP[1][1] - (K[1] * tMs * _pP[0][1] + K[1] * _pP[1][1])); + _pP[0][0] = p00; + _pP[0][1] = p01; + _prevUnwrappedTimestamp = unwrapped_ts90khz; + if (_packetCount < _startUpFilterDelayInPackets) { + _packetCount++; + } + _rwLock->ReleaseLockExclusive(); +} + +int64_t TimestampExtrapolator::ExtrapolateLocalTime(uint32_t timestamp90khz) { + ReadLockScoped rl(*_rwLock); + int64_t localTimeMs = 0; + CheckForWrapArounds(timestamp90khz); + double unwrapped_ts90khz = + static_cast<double>(timestamp90khz) + + _wrapArounds * ((static_cast<int64_t>(1) << 32) - 1); + if (_packetCount == 0) { + localTimeMs = -1; + } else if (_packetCount < _startUpFilterDelayInPackets) { + localTimeMs = + _prevMs + + static_cast<int64_t>( + static_cast<double>(unwrapped_ts90khz - _prevUnwrappedTimestamp) / + 90.0 + + 0.5); + } else { + if (_w[0] < 1e-3) { + localTimeMs = _startMs; + } else { + double timestampDiff = + unwrapped_ts90khz - static_cast<double>(_firstTimestamp); + localTimeMs = static_cast<int64_t>(static_cast<double>(_startMs) + + (timestampDiff - _w[1]) / _w[0] + 0.5); + } + } + return localTimeMs; +} + +// Investigates if the timestamp clock has overflowed since the last timestamp +// and keeps track of the number of wrap arounds since reset. +void TimestampExtrapolator::CheckForWrapArounds(uint32_t ts90khz) { + if (_prevWrapTimestamp == -1) { + _prevWrapTimestamp = ts90khz; + return; + } + if (ts90khz < _prevWrapTimestamp) { + // This difference will probably be less than -2^31 if we have had a wrap + // around (e.g. timestamp = 1, _previousTimestamp = 2^32 - 1). Since it is + // casted to a Word32, it should be positive. + if (static_cast<int32_t>(ts90khz - _prevWrapTimestamp) > 0) { + // Forward wrap around + _wrapArounds++; + } + } + // This difference will probably be less than -2^31 if we have had a backward + // wrap around. Since it is casted to a Word32, it should be positive. + else if (static_cast<int32_t>(_prevWrapTimestamp - ts90khz) > 0) { + // Backward wrap around + _wrapArounds--; + } + _prevWrapTimestamp = ts90khz; +} + +bool TimestampExtrapolator::DelayChangeDetection(double error) { + // CUSUM detection of sudden delay changes + error = (error > 0) ? std::min(error, _accMaxError) + : std::max(error, -_accMaxError); + _detectorAccumulatorPos = + std::max(_detectorAccumulatorPos + error - _accDrift, (double)0); + _detectorAccumulatorNeg = + std::min(_detectorAccumulatorNeg + error + _accDrift, (double)0); + if (_detectorAccumulatorPos > _alarmThreshold || + _detectorAccumulatorNeg < -_alarmThreshold) { + // Alarm + _detectorAccumulatorPos = _detectorAccumulatorNeg = 0; + return true; + } + return false; +} + +} // namespace webrtc |