Uprev of APM and changes to allow running the AGC and NS effects
This CL uprevs the CrAS APM to match that of WebRTC commit
e37fa1970292641149e56a332be2bf9faea0b23a
The main motivation behind the uprev is to include
-The ability to emulate an analog mic gain, which is a
prerequisite for activating APM in CrAS on all Chrome OS
devices.
-The ability to reduce the complexity of APM when the
client is muted.
Apart from the uprev, the CL also
-adds some missing APM parameter settings and sorts the settings to
match that of the APM config struct.
-changes the default parameter values of the
fixed_capture_delay_samples AEC parameter and the noise
suppression level to match what should be used i CrAS.
-adds functionality allowing the AEC, NS and AGC effects to be
forced to be on, disregarding any settings already present in the
.ini config files.
-adds functionality that activates the analog AGC emulation code in
APM when the AGC is activated.
BUG=b:177830918
TEST=Manually tested in calls on a Buddy and an Atlas device.
Change-Id: Ib1b333536efab700fe23216fe61f6cba86750b7b
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/webrtc-apm/+/2792582
Reviewed-by: Hsinyu Chao <hychao@chromium.org>
Reviewed-by: Per Åhgren <peah@chromium.org>
Tested-by: Per Åhgren <peah@chromium.org>
Auto-Submit: Per Åhgren <peah@chromium.org>
Commit-Queue: Hsinyu Chao <hychao@chromium.org>
diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel
index 6da20c4..c9d4a2d 100644
--- a/absl/BUILD.bazel
+++ b/absl/BUILD.bazel
@@ -26,6 +26,22 @@
)
config_setting(
+ name = "msvc_compiler",
+ flag_values = {
+ "@bazel_tools//tools/cpp:compiler": "msvc-cl",
+ },
+ visibility = [":__subpackages__"],
+)
+
+config_setting(
+ name = "clang-cl_compiler",
+ flag_values = {
+ "@bazel_tools//tools/cpp:compiler": "clang-cl",
+ },
+ visibility = [":__subpackages__"],
+)
+
+config_setting(
name = "osx",
constraint_values = [
"@bazel_tools//platforms:osx",
@@ -40,15 +56,6 @@
)
config_setting(
- name = "windows",
- constraint_values = [
- "@bazel_tools//platforms:x86_64",
- "@bazel_tools//platforms:windows",
- ],
- visibility = [":__subpackages__"],
-)
-
-config_setting(
name = "ppc",
values = {
"cpu": "ppc",
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel
index 9d96abe..65ff0dd 100644
--- a/absl/base/BUILD.bazel
+++ b/absl/base/BUILD.bazel
@@ -160,7 +160,8 @@
],
copts = ABSL_DEFAULT_COPTS,
linkopts = select({
- "//absl:windows": [],
+ "//absl:msvc_compiler": [],
+ "//absl:clang-cl_compiler": [],
"//absl:wasm": [],
"//conditions:default": ["-pthread"],
}) + ABSL_DEFAULT_LINKOPTS,
@@ -220,7 +221,10 @@
],
copts = ABSL_DEFAULT_COPTS,
linkopts = select({
- "//absl:windows": [
+ "//absl:msvc_compiler": [
+ "-DEFAULTLIB:advapi32.lib",
+ ],
+ "//absl:clang-cl_compiler": [
"-DEFAULTLIB:advapi32.lib",
],
"//absl:wasm": [],
@@ -479,6 +483,7 @@
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
+ ":base",
":config",
":core_headers",
],
@@ -551,7 +556,9 @@
srcs = ["internal/low_level_alloc_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
- tags = ["no_test_ios_x86_64"],
+ tags = [
+ "no_test_ios_x86_64",
+ ],
deps = [
":malloc_internal",
"//absl/container:node_hash_map",
@@ -587,31 +594,6 @@
)
cc_library(
- name = "bits",
- hdrs = ["internal/bits.h"],
- linkopts = ABSL_DEFAULT_LINKOPTS,
- visibility = [
- "//absl:__subpackages__",
- ],
- deps = [
- ":config",
- ":core_headers",
- ],
-)
-
-cc_test(
- name = "bits_test",
- size = "small",
- srcs = ["internal/bits_test.cc"],
- copts = ABSL_TEST_COPTS,
- linkopts = ABSL_DEFAULT_LINKOPTS,
- deps = [
- ":bits",
- "@com_google_googletest//:gtest_main",
- ],
-)
-
-cc_library(
name = "exponential_biased",
srcs = ["internal/exponential_biased.cc"],
hdrs = ["internal/exponential_biased.h"],
diff --git a/absl/base/BUILD.gn b/absl/base/BUILD.gn
index 346a47d..9d9dfca 100644
--- a/absl/base/BUILD.gn
+++ b/absl/base/BUILD.gn
@@ -225,20 +225,12 @@
"internal/unaligned_access.h",
]
public_deps = [
+ ":base",
":config",
":core_headers",
]
}
-absl_source_set("bits") {
- public = [ "internal/bits.h" ]
- public_deps = [
- ":config",
- ":core_headers",
- ]
- visibility = [ "//third_party/abseil-cpp/absl/*" ]
-}
-
absl_source_set("exponential_biased") {
sources = [ "internal/exponential_biased.cc" ]
public = [ "internal/exponential_biased.h" ]
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt
index 9ff5aa2..981b8cc 100644
--- a/absl/base/CMakeLists.txt
+++ b/absl/base/CMakeLists.txt
@@ -418,6 +418,7 @@
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
+ absl::base
absl::config
absl::core_headers
PUBLIC
@@ -520,30 +521,6 @@
absl_cc_library(
NAME
- bits
- HDRS
- "internal/bits.h"
- COPTS
- ${ABSL_DEFAULT_COPTS}
- DEPS
- absl::config
- absl::core_headers
-)
-
-absl_cc_test(
- NAME
- bits_test
- SRCS
- "internal/bits_test.cc"
- COPTS
- ${ABSL_TEST_COPTS}
- DEPS
- absl::bits
- gtest_main
-)
-
-absl_cc_library(
- NAME
exponential_biased
SRCS
"internal/exponential_biased.cc"
diff --git a/absl/base/attributes.h b/absl/base/attributes.h
index f1d3cfe..cf2cb55 100644
--- a/absl/base/attributes.h
+++ b/absl/base/attributes.h
@@ -18,8 +18,6 @@
// These macros are used within Abseil and allow the compiler to optimize, where
// applicable, certain function calls.
//
-// This file is used for both C and C++!
-//
// Most macros here are exposing GCC or Clang features, and are stubbed out for
// other compilers.
//
@@ -121,7 +119,7 @@
#if ABSL_HAVE_ATTRIBUTE(disable_tail_calls)
#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1
#define ABSL_ATTRIBUTE_NO_TAIL_CALL __attribute__((disable_tail_calls))
-#elif defined(__GNUC__) && !defined(__clang__)
+#elif defined(__GNUC__) && !defined(__clang__) && !defined(__e2k__)
#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1
#define ABSL_ATTRIBUTE_NO_TAIL_CALL \
__attribute__((optimize("no-optimize-sibling-calls")))
@@ -646,7 +644,7 @@
// Every usage of a deprecated entity will trigger a warning when compiled with
// clang's `-Wdeprecated-declarations` option. This option is turned off by
// default, but the warnings will be reported by clang-tidy.
-#if defined(__clang__) && __cplusplus >= 201103L
+#if defined(__clang__) && defined(__cplusplus) && __cplusplus >= 201103L
#define ABSL_DEPRECATED(message) __attribute__((deprecated(message)))
#endif
@@ -680,4 +678,25 @@
#define ABSL_CONST_INIT
#endif // ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization)
+// ABSL_ATTRIBUTE_PURE_FUNCTION
+//
+// ABSL_ATTRIBUTE_PURE_FUNCTION is used to annotate declarations of "pure"
+// functions. A function is pure if its return value is only a function of its
+// arguments. The pure attribute prohibits a function from modifying the state
+// of the program that is observable by means other than inspecting the
+// function's return value. Declaring such functions with the pure attribute
+// allows the compiler to avoid emitting some calls in repeated invocations of
+// the function with the same argument values.
+//
+// Example:
+//
+// ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Milliseconds(Duration d);
+#if ABSL_HAVE_CPP_ATTRIBUTE(gnu::pure)
+#define ABSL_ATTRIBUTE_PURE_FUNCTION [[gnu::pure]]
+#elif ABSL_HAVE_ATTRIBUTE(pure)
+#define ABSL_ATTRIBUTE_PURE_FUNCTION __attribute__((pure))
+#else
+#define ABSL_ATTRIBUTE_PURE_FUNCTION
+#endif
+
#endif // ABSL_BASE_ATTRIBUTES_H_
diff --git a/absl/base/call_once.h b/absl/base/call_once.h
index 5b468af..96109f5 100644
--- a/absl/base/call_once.h
+++ b/absl/base/call_once.h
@@ -177,15 +177,8 @@
scheduling_mode) == kOnceInit) {
base_internal::invoke(std::forward<Callable>(fn),
std::forward<Args>(args)...);
- // The call to SpinLockWake below is an optimization, because the waiter
- // in SpinLockWait is waiting with a short timeout. The atomic load/store
- // sequence is slightly faster than an atomic exchange:
- // old_control = control->exchange(base_internal::kOnceDone,
- // std::memory_order_release);
- // We opt for a slightly faster case when there are no waiters, in spite
- // of longer tail latency when there are waiters.
- old_control = control->load(std::memory_order_relaxed);
- control->store(base_internal::kOnceDone, std::memory_order_release);
+ old_control =
+ control->exchange(base_internal::kOnceDone, std::memory_order_release);
if (old_control == base_internal::kOnceWaiter) {
base_internal::SpinLockWake(control, true);
}
diff --git a/absl/base/config.h b/absl/base/config.h
index 3f7f32b..9544996 100644
--- a/absl/base/config.h
+++ b/absl/base/config.h
@@ -121,10 +121,16 @@
#if ABSL_OPTION_USE_INLINE_NAMESPACE == 0
#define ABSL_NAMESPACE_BEGIN
#define ABSL_NAMESPACE_END
+#define ABSL_INTERNAL_C_SYMBOL(x) x
#elif ABSL_OPTION_USE_INLINE_NAMESPACE == 1
#define ABSL_NAMESPACE_BEGIN \
inline namespace ABSL_OPTION_INLINE_NAMESPACE_NAME {
#define ABSL_NAMESPACE_END }
+#define ABSL_INTERNAL_C_SYMBOL_HELPER_2(x, v) x##_##v
+#define ABSL_INTERNAL_C_SYMBOL_HELPER_1(x, v) \
+ ABSL_INTERNAL_C_SYMBOL_HELPER_2(x, v)
+#define ABSL_INTERNAL_C_SYMBOL(x) \
+ ABSL_INTERNAL_C_SYMBOL_HELPER_1(x, ABSL_OPTION_INLINE_NAMESPACE_NAME)
#else
#error options.h is misconfigured.
#endif
@@ -216,6 +222,8 @@
#if ABSL_INTERNAL_HAS_KEYWORD(__builtin_LINE) && \
ABSL_INTERNAL_HAS_KEYWORD(__builtin_FILE)
#define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1
+#elif defined(__GNUC__) && __GNUC__ >= 5
+#define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1
#endif
#endif
@@ -379,6 +387,15 @@
#define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1
#endif
+// ABSL_HAVE_SCHED_GETCPU
+//
+// Checks whether sched_getcpu is available.
+#ifdef ABSL_HAVE_SCHED_GETCPU
+#error ABSL_HAVE_SCHED_GETCPU cannot be directly set
+#elif defined(__linux__)
+#define ABSL_HAVE_SCHED_GETCPU 1
+#endif
+
// ABSL_HAVE_SCHED_YIELD
//
// Checks whether the platform implements sched_yield(2) as defined in
@@ -490,7 +507,7 @@
#endif
#ifdef __has_include
-#if __has_include(<any>) && __cplusplus >= 201703L && \
+#if __has_include(<any>) && defined(__cplusplus) && __cplusplus >= 201703L && \
!ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
#define ABSL_HAVE_STD_ANY 1
#endif
@@ -504,8 +521,8 @@
#endif
#ifdef __has_include
-#if __has_include(<optional>) && __cplusplus >= 201703L && \
- !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
+#if __has_include(<optional>) && defined(__cplusplus) && \
+ __cplusplus >= 201703L && !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
#define ABSL_HAVE_STD_OPTIONAL 1
#endif
#endif
@@ -518,8 +535,8 @@
#endif
#ifdef __has_include
-#if __has_include(<variant>) && __cplusplus >= 201703L && \
- !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
+#if __has_include(<variant>) && defined(__cplusplus) && \
+ __cplusplus >= 201703L && !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
#define ABSL_HAVE_STD_VARIANT 1
#endif
#endif
@@ -532,7 +549,8 @@
#endif
#ifdef __has_include
-#if __has_include(<string_view>) && __cplusplus >= 201703L
+#if __has_include(<string_view>) && defined(__cplusplus) && \
+ __cplusplus >= 201703L
#define ABSL_HAVE_STD_STRING_VIEW 1
#endif
#endif
@@ -544,8 +562,9 @@
// not correctly set by MSVC, so we use `_MSVC_LANG` to check the language
// version.
// TODO(zhangxy): fix tests before enabling aliasing for `std::any`.
-#if defined(_MSC_VER) && _MSC_VER >= 1910 && \
- ((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || __cplusplus > 201402)
+#if defined(_MSC_VER) && _MSC_VER >= 1910 && \
+ ((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || \
+ (defined(__cplusplus) && __cplusplus > 201402))
// #define ABSL_HAVE_STD_ANY 1
#define ABSL_HAVE_STD_OPTIONAL 1
#define ABSL_HAVE_STD_VARIANT 1
@@ -711,4 +730,13 @@
#define ABSL_HAVE_ADDRESS_SANITIZER 1
#endif
+// ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION
+//
+// Class template argument deduction is a language feature added in C++17.
+#ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION
+#error "ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION cannot be directly set."
+#elif defined(__cpp_deduction_guides)
+#define ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION 1
+#endif
+
#endif // ABSL_BASE_CONFIG_H_
diff --git a/absl/base/dynamic_annotations.h b/absl/base/dynamic_annotations.h
index 94488ac..bf874db 100644
--- a/absl/base/dynamic_annotations.h
+++ b/absl/base/dynamic_annotations.h
@@ -107,6 +107,9 @@
// Define race annotations.
#if ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 1
+// Some of the symbols used in this section (e.g. AnnotateBenignRaceSized) are
+// defined by the compiler-based santizer implementation, not by the Abseil
+// library. Therefore they do not use ABSL_INTERNAL_C_SYMBOL.
// -------------------------------------------------------------
// Annotations that suppress errors. It is usually better to express the
@@ -283,17 +286,22 @@
// Define IGNORE_READS_BEGIN/_END annotations.
#if ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED == 1
+// Some of the symbols used in this section (e.g. AnnotateIgnoreReadsBegin) are
+// defined by the compiler-based implementation, not by the Abseil
+// library. Therefore they do not use ABSL_INTERNAL_C_SYMBOL.
// Request the analysis tool to ignore all reads in the current thread until
// ABSL_ANNOTATE_IGNORE_READS_END is called. Useful to ignore intentional racey
// reads, while still checking other reads and all writes.
// See also ABSL_ANNOTATE_UNPROTECTED_READ.
-#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \
- ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsBegin)(__FILE__, __LINE__)
+#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \
+ ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsBegin) \
+ (__FILE__, __LINE__)
// Stop ignoring reads.
-#define ABSL_ANNOTATE_IGNORE_READS_END() \
- ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsEnd)(__FILE__, __LINE__)
+#define ABSL_ANNOTATE_IGNORE_READS_END() \
+ ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsEnd) \
+ (__FILE__, __LINE__)
// Function prototypes of annotations provided by the compiler-based sanitizer
// implementation.
@@ -313,16 +321,22 @@
// TODO(delesley) -- The exclusive lock here ignores writes as well, but
// allows IGNORE_READS_AND_WRITES to work properly.
-#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \
- ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsBegin)()
+#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \
+ ABSL_INTERNAL_GLOBAL_SCOPED( \
+ ABSL_INTERNAL_C_SYMBOL(AbslInternalAnnotateIgnoreReadsBegin)) \
+ ()
-#define ABSL_ANNOTATE_IGNORE_READS_END() \
- ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsEnd)()
+#define ABSL_ANNOTATE_IGNORE_READS_END() \
+ ABSL_INTERNAL_GLOBAL_SCOPED( \
+ ABSL_INTERNAL_C_SYMBOL(AbslInternalAnnotateIgnoreReadsEnd)) \
+ ()
-ABSL_INTERNAL_STATIC_INLINE void AbslInternalAnnotateIgnoreReadsBegin()
+ABSL_INTERNAL_STATIC_INLINE void ABSL_INTERNAL_C_SYMBOL(
+ AbslInternalAnnotateIgnoreReadsBegin)()
ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE {}
-ABSL_INTERNAL_STATIC_INLINE void AbslInternalAnnotateIgnoreReadsEnd()
+ABSL_INTERNAL_STATIC_INLINE void ABSL_INTERNAL_C_SYMBOL(
+ AbslInternalAnnotateIgnoreReadsEnd)()
ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE {}
#else
diff --git a/absl/base/internal/bits.h b/absl/base/internal/bits.h
deleted file mode 100644
index 81648e2..0000000
--- a/absl/base/internal/bits.h
+++ /dev/null
@@ -1,219 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_BASE_INTERNAL_BITS_H_
-#define ABSL_BASE_INTERNAL_BITS_H_
-
-// This file contains bitwise ops which are implementation details of various
-// absl libraries.
-
-#include <cstdint>
-
-#include "absl/base/config.h"
-
-// Clang on Windows has __builtin_clzll; otherwise we need to use the
-// windows intrinsic functions.
-#if defined(_MSC_VER) && !defined(__clang__)
-#include <intrin.h>
-#if defined(_M_X64)
-#pragma intrinsic(_BitScanReverse64)
-#pragma intrinsic(_BitScanForward64)
-#endif
-#pragma intrinsic(_BitScanReverse)
-#pragma intrinsic(_BitScanForward)
-#endif
-
-#include "absl/base/attributes.h"
-
-#if defined(_MSC_VER) && !defined(__clang__)
-// We can achieve something similar to attribute((always_inline)) with MSVC by
-// using the __forceinline keyword, however this is not perfect. MSVC is
-// much less aggressive about inlining, and even with the __forceinline keyword.
-#define ABSL_BASE_INTERNAL_FORCEINLINE __forceinline
-#else
-// Use default attribute inline.
-#define ABSL_BASE_INTERNAL_FORCEINLINE inline ABSL_ATTRIBUTE_ALWAYS_INLINE
-#endif
-
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64Slow(uint64_t n) {
- int zeroes = 60;
- if (n >> 32) {
- zeroes -= 32;
- n >>= 32;
- }
- if (n >> 16) {
- zeroes -= 16;
- n >>= 16;
- }
- if (n >> 8) {
- zeroes -= 8;
- n >>= 8;
- }
- if (n >> 4) {
- zeroes -= 4;
- n >>= 4;
- }
- return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
-}
-
-ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) {
-#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_X64)
- // MSVC does not have __buitin_clzll. Use _BitScanReverse64.
- unsigned long result = 0; // NOLINT(runtime/int)
- if (_BitScanReverse64(&result, n)) {
- return 63 - result;
- }
- return 64;
-#elif defined(_MSC_VER) && !defined(__clang__)
- // MSVC does not have __buitin_clzll. Compose two calls to _BitScanReverse
- unsigned long result = 0; // NOLINT(runtime/int)
- if ((n >> 32) &&
- _BitScanReverse(&result, static_cast<unsigned long>(n >> 32))) {
- return 31 - result;
- }
- if (_BitScanReverse(&result, static_cast<unsigned long>(n))) {
- return 63 - result;
- }
- return 64;
-#elif defined(__GNUC__) || defined(__clang__)
- // Use __builtin_clzll, which uses the following instructions:
- // x86: bsr
- // ARM64: clz
- // PPC: cntlzd
- static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int)
- "__builtin_clzll does not take 64-bit arg");
-
- // Handle 0 as a special case because __builtin_clzll(0) is undefined.
- if (n == 0) {
- return 64;
- }
- return __builtin_clzll(n);
-#else
- return CountLeadingZeros64Slow(n);
-#endif
-}
-
-ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32Slow(uint64_t n) {
- int zeroes = 28;
- if (n >> 16) {
- zeroes -= 16;
- n >>= 16;
- }
- if (n >> 8) {
- zeroes -= 8;
- n >>= 8;
- }
- if (n >> 4) {
- zeroes -= 4;
- n >>= 4;
- }
- return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
-}
-
-ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32(uint32_t n) {
-#if defined(_MSC_VER) && !defined(__clang__)
- unsigned long result = 0; // NOLINT(runtime/int)
- if (_BitScanReverse(&result, n)) {
- return 31 - result;
- }
- return 32;
-#elif defined(__GNUC__) || defined(__clang__)
- // Use __builtin_clz, which uses the following instructions:
- // x86: bsr
- // ARM64: clz
- // PPC: cntlzd
- static_assert(sizeof(int) == sizeof(n),
- "__builtin_clz does not take 32-bit arg");
-
- // Handle 0 as a special case because __builtin_clz(0) is undefined.
- if (n == 0) {
- return 32;
- }
- return __builtin_clz(n);
-#else
- return CountLeadingZeros32Slow(n);
-#endif
-}
-
-ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64Slow(uint64_t n) {
- int c = 63;
- n &= ~n + 1;
- if (n & 0x00000000FFFFFFFF) c -= 32;
- if (n & 0x0000FFFF0000FFFF) c -= 16;
- if (n & 0x00FF00FF00FF00FF) c -= 8;
- if (n & 0x0F0F0F0F0F0F0F0F) c -= 4;
- if (n & 0x3333333333333333) c -= 2;
- if (n & 0x5555555555555555) c -= 1;
- return c;
-}
-
-ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64(uint64_t n) {
-#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_X64)
- unsigned long result = 0; // NOLINT(runtime/int)
- _BitScanForward64(&result, n);
- return result;
-#elif defined(_MSC_VER) && !defined(__clang__)
- unsigned long result = 0; // NOLINT(runtime/int)
- if (static_cast<uint32_t>(n) == 0) {
- _BitScanForward(&result, static_cast<unsigned long>(n >> 32));
- return result + 32;
- }
- _BitScanForward(&result, static_cast<unsigned long>(n));
- return result;
-#elif defined(__GNUC__) || defined(__clang__)
- static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int)
- "__builtin_ctzll does not take 64-bit arg");
- return __builtin_ctzll(n);
-#else
- return CountTrailingZerosNonZero64Slow(n);
-#endif
-}
-
-ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32Slow(uint32_t n) {
- int c = 31;
- n &= ~n + 1;
- if (n & 0x0000FFFF) c -= 16;
- if (n & 0x00FF00FF) c -= 8;
- if (n & 0x0F0F0F0F) c -= 4;
- if (n & 0x33333333) c -= 2;
- if (n & 0x55555555) c -= 1;
- return c;
-}
-
-ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) {
-#if defined(_MSC_VER) && !defined(__clang__)
- unsigned long result = 0; // NOLINT(runtime/int)
- _BitScanForward(&result, n);
- return result;
-#elif defined(__GNUC__) || defined(__clang__)
- static_assert(sizeof(int) == sizeof(n),
- "__builtin_ctz does not take 32-bit arg");
- return __builtin_ctz(n);
-#else
- return CountTrailingZerosNonZero32Slow(n);
-#endif
-}
-
-#undef ABSL_BASE_INTERNAL_FORCEINLINE
-
-} // namespace base_internal
-ABSL_NAMESPACE_END
-} // namespace absl
-
-#endif // ABSL_BASE_INTERNAL_BITS_H_
diff --git a/absl/base/internal/bits_test.cc b/absl/base/internal/bits_test.cc
deleted file mode 100644
index 7855fa6..0000000
--- a/absl/base/internal/bits_test.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2018 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/base/internal/bits.h"
-
-#include "gtest/gtest.h"
-
-namespace {
-
-int CLZ64(uint64_t n) {
- int fast = absl::base_internal::CountLeadingZeros64(n);
- int slow = absl::base_internal::CountLeadingZeros64Slow(n);
- EXPECT_EQ(fast, slow) << n;
- return fast;
-}
-
-TEST(BitsTest, CountLeadingZeros64) {
- EXPECT_EQ(64, CLZ64(uint64_t{}));
- EXPECT_EQ(0, CLZ64(~uint64_t{}));
-
- for (int index = 0; index < 64; index++) {
- uint64_t x = static_cast<uint64_t>(1) << index;
- const auto cnt = 63 - index;
- ASSERT_EQ(cnt, CLZ64(x)) << index;
- ASSERT_EQ(cnt, CLZ64(x + x - 1)) << index;
- }
-}
-
-int CLZ32(uint32_t n) {
- int fast = absl::base_internal::CountLeadingZeros32(n);
- int slow = absl::base_internal::CountLeadingZeros32Slow(n);
- EXPECT_EQ(fast, slow) << n;
- return fast;
-}
-
-TEST(BitsTest, CountLeadingZeros32) {
- EXPECT_EQ(32, CLZ32(uint32_t{}));
- EXPECT_EQ(0, CLZ32(~uint32_t{}));
-
- for (int index = 0; index < 32; index++) {
- uint32_t x = static_cast<uint32_t>(1) << index;
- const auto cnt = 31 - index;
- ASSERT_EQ(cnt, CLZ32(x)) << index;
- ASSERT_EQ(cnt, CLZ32(x + x - 1)) << index;
- ASSERT_EQ(CLZ64(x), CLZ32(x) + 32);
- }
-}
-
-int CTZ64(uint64_t n) {
- int fast = absl::base_internal::CountTrailingZerosNonZero64(n);
- int slow = absl::base_internal::CountTrailingZerosNonZero64Slow(n);
- EXPECT_EQ(fast, slow) << n;
- return fast;
-}
-
-TEST(BitsTest, CountTrailingZerosNonZero64) {
- EXPECT_EQ(0, CTZ64(~uint64_t{}));
-
- for (int index = 0; index < 64; index++) {
- uint64_t x = static_cast<uint64_t>(1) << index;
- const auto cnt = index;
- ASSERT_EQ(cnt, CTZ64(x)) << index;
- ASSERT_EQ(cnt, CTZ64(~(x - 1))) << index;
- }
-}
-
-int CTZ32(uint32_t n) {
- int fast = absl::base_internal::CountTrailingZerosNonZero32(n);
- int slow = absl::base_internal::CountTrailingZerosNonZero32Slow(n);
- EXPECT_EQ(fast, slow) << n;
- return fast;
-}
-
-TEST(BitsTest, CountTrailingZerosNonZero32) {
- EXPECT_EQ(0, CTZ32(~uint32_t{}));
-
- for (int index = 0; index < 32; index++) {
- uint32_t x = static_cast<uint32_t>(1) << index;
- const auto cnt = index;
- ASSERT_EQ(cnt, CTZ32(x)) << index;
- ASSERT_EQ(cnt, CTZ32(~(x - 1))) << index;
- }
-}
-
-
-} // namespace
diff --git a/absl/base/internal/direct_mmap.h b/absl/base/internal/direct_mmap.h
index 16accf0..274054c 100644
--- a/absl/base/internal/direct_mmap.h
+++ b/absl/base/internal/direct_mmap.h
@@ -74,10 +74,13 @@
inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd,
off64_t offset) noexcept {
#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \
+ defined(__m68k__) || defined(__sh__) || \
+ (defined(__hppa__) && !defined(__LP64__)) || \
(defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \
(defined(__PPC__) && !defined(__PPC64__)) || \
(defined(__riscv) && __riscv_xlen == 32) || \
- (defined(__s390__) && !defined(__s390x__))
+ (defined(__s390__) && !defined(__s390x__)) || \
+ (defined(__sparc__) && !defined(__arch64__))
// On these architectures, implement mmap with mmap2.
static int pagesize = 0;
if (pagesize == 0) {
diff --git a/absl/base/internal/endian.h b/absl/base/internal/endian.h
index 9677530..dad0e9a 100644
--- a/absl/base/internal/endian.h
+++ b/absl/base/internal/endian.h
@@ -26,6 +26,7 @@
#endif
#include <cstdint>
+#include "absl/base/casts.h"
#include "absl/base/config.h"
#include "absl/base/internal/unaligned_access.h"
#include "absl/base/port.h"
@@ -173,6 +174,36 @@
#endif /* ENDIAN */
+inline uint8_t FromHost(uint8_t x) { return x; }
+inline uint16_t FromHost(uint16_t x) { return FromHost16(x); }
+inline uint32_t FromHost(uint32_t x) { return FromHost32(x); }
+inline uint64_t FromHost(uint64_t x) { return FromHost64(x); }
+inline uint8_t ToHost(uint8_t x) { return x; }
+inline uint16_t ToHost(uint16_t x) { return ToHost16(x); }
+inline uint32_t ToHost(uint32_t x) { return ToHost32(x); }
+inline uint64_t ToHost(uint64_t x) { return ToHost64(x); }
+
+inline int8_t FromHost(int8_t x) { return x; }
+inline int16_t FromHost(int16_t x) {
+ return bit_cast<int16_t>(FromHost16(bit_cast<uint16_t>(x)));
+}
+inline int32_t FromHost(int32_t x) {
+ return bit_cast<int32_t>(FromHost32(bit_cast<uint32_t>(x)));
+}
+inline int64_t FromHost(int64_t x) {
+ return bit_cast<int64_t>(FromHost64(bit_cast<uint64_t>(x)));
+}
+inline int8_t ToHost(int8_t x) { return x; }
+inline int16_t ToHost(int16_t x) {
+ return bit_cast<int16_t>(ToHost16(bit_cast<uint16_t>(x)));
+}
+inline int32_t ToHost(int32_t x) {
+ return bit_cast<int32_t>(ToHost32(bit_cast<uint32_t>(x)));
+}
+inline int64_t ToHost(int64_t x) {
+ return bit_cast<int64_t>(ToHost64(bit_cast<uint64_t>(x)));
+}
+
// Functions to do unaligned loads and stores in little-endian order.
inline uint16_t Load16(const void *p) {
return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p));
@@ -233,6 +264,36 @@
#endif /* ENDIAN */
+inline uint8_t FromHost(uint8_t x) { return x; }
+inline uint16_t FromHost(uint16_t x) { return FromHost16(x); }
+inline uint32_t FromHost(uint32_t x) { return FromHost32(x); }
+inline uint64_t FromHost(uint64_t x) { return FromHost64(x); }
+inline uint8_t ToHost(uint8_t x) { return x; }
+inline uint16_t ToHost(uint16_t x) { return ToHost16(x); }
+inline uint32_t ToHost(uint32_t x) { return ToHost32(x); }
+inline uint64_t ToHost(uint64_t x) { return ToHost64(x); }
+
+inline int8_t FromHost(int8_t x) { return x; }
+inline int16_t FromHost(int16_t x) {
+ return bit_cast<int16_t>(FromHost16(bit_cast<uint16_t>(x)));
+}
+inline int32_t FromHost(int32_t x) {
+ return bit_cast<int32_t>(FromHost32(bit_cast<uint32_t>(x)));
+}
+inline int64_t FromHost(int64_t x) {
+ return bit_cast<int64_t>(FromHost64(bit_cast<uint64_t>(x)));
+}
+inline int8_t ToHost(int8_t x) { return x; }
+inline int16_t ToHost(int16_t x) {
+ return bit_cast<int16_t>(ToHost16(bit_cast<uint16_t>(x)));
+}
+inline int32_t ToHost(int32_t x) {
+ return bit_cast<int32_t>(ToHost32(bit_cast<uint32_t>(x)));
+}
+inline int64_t ToHost(int64_t x) {
+ return bit_cast<int64_t>(ToHost64(bit_cast<uint64_t>(x)));
+}
+
// Functions to do unaligned loads and stores in big-endian order.
inline uint16_t Load16(const void *p) {
return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p));
diff --git a/absl/base/internal/low_level_alloc_test.cc b/absl/base/internal/low_level_alloc_test.cc
index 2f2eaff..31abb88 100644
--- a/absl/base/internal/low_level_alloc_test.cc
+++ b/absl/base/internal/low_level_alloc_test.cc
@@ -21,6 +21,10 @@
#include <unordered_map>
#include <utility>
+#ifdef __EMSCRIPTEN__
+#include <emscripten.h>
+#endif
+
#include "absl/container/node_hash_map.h"
namespace absl {
@@ -158,5 +162,20 @@
int main(int argc, char *argv[]) {
// The actual test runs in the global constructor of `before_main`.
printf("PASS\n");
+#ifdef __EMSCRIPTEN__
+ // clang-format off
+// This is JS here. Don't try to format it.
+ MAIN_THREAD_EM_ASM({
+ if (ENVIRONMENT_IS_WEB) {
+ if (typeof TEST_FINISH === 'function') {
+ TEST_FINISH($0);
+ } else {
+ console.error('Attempted to exit with status ' + $0);
+ console.error('But TEST_FINSIHED is not a function.');
+ }
+ }
+ }, 0);
+// clang-format on
+#endif
return 0;
}
diff --git a/absl/base/internal/low_level_scheduling.h b/absl/base/internal/low_level_scheduling.h
index 6ef79fb..9baccc0 100644
--- a/absl/base/internal/low_level_scheduling.h
+++ b/absl/base/internal/low_level_scheduling.h
@@ -61,6 +61,8 @@
public:
// Returns true iff the calling thread may be cooperatively rescheduled.
static bool ReschedulingIsAllowed();
+ SchedulingGuard(const SchedulingGuard&) = delete;
+ SchedulingGuard& operator=(const SchedulingGuard&) = delete;
private:
// Disable cooperative rescheduling of the calling thread. It may still
@@ -101,9 +103,6 @@
friend class SchedulingHelper;
friend class SpinLock;
friend int absl::synchronization_internal::MutexDelay(int32_t c, int mode);
-
- SchedulingGuard(const SchedulingGuard&) = delete;
- SchedulingGuard& operator=(const SchedulingGuard&) = delete;
};
//------------------------------------------------------------------------------
diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc
index ae8754c..074e026 100644
--- a/absl/base/internal/raw_logging.cc
+++ b/absl/base/internal/raw_logging.cc
@@ -67,28 +67,32 @@
#undef ABSL_HAVE_RAW_IO
#endif
-// TODO(gfalcon): We want raw-logging to work on as many platforms as possible.
-// Explicitly #error out when not ABSL_LOW_LEVEL_WRITE_SUPPORTED, except for a
-// selected set of platforms for which we expect not to be able to raw log.
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace raw_logging_internal {
+namespace {
-ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static absl::base_internal::AtomicHook<
- absl::raw_logging_internal::LogPrefixHook>
- log_prefix_hook;
-ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static absl::base_internal::AtomicHook<
- absl::raw_logging_internal::AbortHook>
- abort_hook;
+// TODO(gfalcon): We want raw-logging to work on as many platforms as possible.
+// Explicitly `#error` out when not `ABSL_LOW_LEVEL_WRITE_SUPPORTED`, except for
+// a selected set of platforms for which we expect not to be able to raw log.
+
+ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES
+ absl::base_internal::AtomicHook<LogPrefixHook>
+ log_prefix_hook;
+ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES
+ absl::base_internal::AtomicHook<AbortHook>
+ abort_hook;
#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
-static const char kTruncated[] = " ... (message truncated)\n";
+constexpr char kTruncated[] = " ... (message truncated)\n";
// sprintf the format to the buffer, adjusting *buf and *size to reflect the
// consumed bytes, and return whether the message fit without truncation. If
// truncation occurred, if possible leave room in the buffer for the message
// kTruncated[].
-inline static bool VADoRawLog(char** buf, int* size, const char* format,
- va_list ap) ABSL_PRINTF_ATTRIBUTE(3, 0);
-inline static bool VADoRawLog(char** buf, int* size,
- const char* format, va_list ap) {
+bool VADoRawLog(char** buf, int* size, const char* format, va_list ap)
+ ABSL_PRINTF_ATTRIBUTE(3, 0);
+bool VADoRawLog(char** buf, int* size, const char* format, va_list ap) {
int n = vsnprintf(*buf, *size, format, ap);
bool result = true;
if (n < 0 || n > *size) {
@@ -96,7 +100,7 @@
if (static_cast<size_t>(*size) > sizeof(kTruncated)) {
n = *size - sizeof(kTruncated); // room for truncation message
} else {
- n = 0; // no room for truncation message
+ n = 0; // no room for truncation message
}
}
*size -= n;
@@ -105,9 +109,7 @@
}
#endif // ABSL_LOW_LEVEL_WRITE_SUPPORTED
-static constexpr int kLogBufSize = 3000;
-
-namespace {
+constexpr int kLogBufSize = 3000;
// CAVEAT: vsnprintf called from *DoRawLog below has some (exotic) code paths
// that invoke malloc() and getenv() that might acquire some locks.
@@ -166,7 +168,7 @@
} else {
DoRawLog(&buf, &size, "%s", kTruncated);
}
- absl::raw_logging_internal::SafeWriteToStderr(buffer, strlen(buffer));
+ SafeWriteToStderr(buffer, strlen(buffer));
}
#else
static_cast<void>(format);
@@ -181,11 +183,18 @@
}
}
+// Non-formatting version of RawLog().
+//
+// TODO(gfalcon): When string_view no longer depends on base, change this
+// interface to take its message as a string_view instead.
+void DefaultInternalLog(absl::LogSeverity severity, const char* file, int line,
+ const std::string& message) {
+ RawLog(severity, file, line, "%.*s", static_cast<int>(message.size()),
+ message.data());
+}
+
} // namespace
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace raw_logging_internal {
void SafeWriteToStderr(const char *s, size_t len) {
#if defined(ABSL_HAVE_SYSCALL_WRITE)
syscall(SYS_write, STDERR_FILENO, s, len);
@@ -201,8 +210,6 @@
}
void RawLog(absl::LogSeverity severity, const char* file, int line,
- const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5);
-void RawLog(absl::LogSeverity severity, const char* file, int line,
const char* format, ...) {
va_list ap;
va_start(ap, format);
@@ -210,15 +217,6 @@
va_end(ap);
}
-// Non-formatting version of RawLog().
-//
-// TODO(gfalcon): When string_view no longer depends on base, change this
-// interface to take its message as a string_view instead.
-static void DefaultInternalLog(absl::LogSeverity severity, const char* file,
- int line, const std::string& message) {
- RawLog(severity, file, line, "%s", message.c_str());
-}
-
bool RawLoggingFullySupported() {
#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
return true;
@@ -231,6 +229,10 @@
absl::base_internal::AtomicHook<InternalLogFunction>
internal_log_function(DefaultInternalLog);
+void RegisterLogPrefixHook(LogPrefixHook func) { log_prefix_hook.Store(func); }
+
+void RegisterAbortHook(AbortHook func) { abort_hook.Store(func); }
+
void RegisterInternalLogFunction(InternalLogFunction func) {
internal_log_function.Store(func);
}
diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h
index 2508f3c..2bf7aab 100644
--- a/absl/base/internal/raw_logging.h
+++ b/absl/base/internal/raw_logging.h
@@ -72,12 +72,14 @@
//
// The API is a subset of the above: each macro only takes two arguments. Use
// StrCat if you need to build a richer message.
-#define ABSL_INTERNAL_LOG(severity, message) \
- do { \
- constexpr const char* absl_raw_logging_internal_filename = __FILE__; \
- ::absl::raw_logging_internal::internal_log_function( \
- ABSL_RAW_LOGGING_INTERNAL_##severity, \
- absl_raw_logging_internal_filename, __LINE__, message); \
+#define ABSL_INTERNAL_LOG(severity, message) \
+ do { \
+ constexpr const char* absl_raw_logging_internal_filename = __FILE__; \
+ ::absl::raw_logging_internal::internal_log_function( \
+ ABSL_RAW_LOGGING_INTERNAL_##severity, \
+ absl_raw_logging_internal_filename, __LINE__, message); \
+ if (ABSL_RAW_LOGGING_INTERNAL_##severity == ::absl::LogSeverity::kFatal) \
+ ABSL_INTERNAL_UNREACHABLE; \
} while (0)
#define ABSL_INTERNAL_CHECK(condition, message) \
@@ -176,6 +178,14 @@
InternalLogFunction>
internal_log_function;
+// Registers hooks of the above types. Only a single hook of each type may be
+// registered. It is an error to call these functions multiple times with
+// different input arguments.
+//
+// These functions are safe to call at any point during initialization; they do
+// not block or malloc, and are async-signal safe.
+void RegisterLogPrefixHook(LogPrefixHook func);
+void RegisterAbortHook(AbortHook func);
void RegisterInternalLogFunction(InternalLogFunction func);
} // namespace raw_logging_internal
diff --git a/absl/base/internal/spinlock.cc b/absl/base/internal/spinlock.cc
index a7d44f3..35c0696 100644
--- a/absl/base/internal/spinlock.cc
+++ b/absl/base/internal/spinlock.cc
@@ -125,8 +125,9 @@
// it as having a sleeper.
if ((lock_value & kWaitTimeMask) == 0) {
// Here, just "mark" that the thread is going to sleep. Don't store the
- // lock wait time in the lock as that will cause the current lock
- // owner to think it experienced contention.
+ // lock wait time in the lock -- the lock word stores the amount of time
+ // that the current holder waited before acquiring the lock, not the wait
+ // time of any thread currently waiting to acquire it.
if (lockword_.compare_exchange_strong(
lock_value, lock_value | kSpinLockSleeper,
std::memory_order_relaxed, std::memory_order_relaxed)) {
@@ -140,6 +141,14 @@
// this thread obtains the lock.
lock_value = TryLockInternal(lock_value, wait_cycles);
continue; // Skip the delay at the end of the loop.
+ } else if ((lock_value & kWaitTimeMask) == 0) {
+ // The lock is still held, without a waiter being marked, but something
+ // else about the lock word changed, causing our CAS to fail. For
+ // example, a new lock holder may have acquired the lock with
+ // kSpinLockDisabledScheduling set, whereas the previous holder had not
+ // set that flag. In this case, attempt again to mark ourselves as a
+ // waiter.
+ continue;
}
}
diff --git a/absl/base/internal/spinlock.h b/absl/base/internal/spinlock.h
index e6ac9e6..c73b5e0 100644
--- a/absl/base/internal/spinlock.h
+++ b/absl/base/internal/spinlock.h
@@ -15,11 +15,8 @@
//
// Most users requiring mutual exclusion should use Mutex.
-// SpinLock is provided for use in three situations:
+// SpinLock is provided for use in two situations:
// - for use in code that Mutex itself depends on
-// - to get a faster fast-path release under low contention (without an
-// atomic read-modify-write) In return, SpinLock has worse behaviour under
-// contention, which is why Mutex is preferred in most situations.
// - for async signal safety (see below)
// SpinLock is async signal safe. If a spinlock is used within a signal
@@ -140,8 +137,20 @@
//
// bit[0] encodes whether a lock is being held.
// bit[1] encodes whether a lock uses cooperative scheduling.
- // bit[2] encodes whether a lock disables scheduling.
+ // bit[2] encodes whether the current lock holder disabled scheduling when
+ // acquiring the lock. Only set when kSpinLockHeld is also set.
// bit[3:31] encodes time a lock spent on waiting as a 29-bit unsigned int.
+ // This is set by the lock holder to indicate how long it waited on
+ // the lock before eventually acquiring it. The number of cycles is
+ // encoded as a 29-bit unsigned int, or in the case that the current
+ // holder did not wait but another waiter is queued, the LSB
+ // (kSpinLockSleeper) is set. The implementation does not explicitly
+ // track the number of queued waiters beyond this. It must always be
+ // assumed that waiters may exist if the current holder was required to
+ // queue.
+ //
+ // Invariant: if the lock is not held, the value is either 0 or
+ // kSpinLockCooperative.
static constexpr uint32_t kSpinLockHeld = 1;
static constexpr uint32_t kSpinLockCooperative = 2;
static constexpr uint32_t kSpinLockDisabledScheduling = 4;
diff --git a/absl/base/internal/spinlock_akaros.inc b/absl/base/internal/spinlock_akaros.inc
index bc46894..7b0cada 100644
--- a/absl/base/internal/spinlock_akaros.inc
+++ b/absl/base/internal/spinlock_akaros.inc
@@ -20,7 +20,7 @@
extern "C" {
-ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay(
+ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)(
std::atomic<uint32_t>* /* lock_word */, uint32_t /* value */,
int /* loop */, absl::base_internal::SchedulingMode /* mode */) {
// In Akaros, one must take care not to call anything that could cause a
@@ -29,7 +29,7 @@
// arbitrary code.
}
-ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(
+ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(
std::atomic<uint32_t>* /* lock_word */, bool /* all */) {}
} // extern "C"
diff --git a/absl/base/internal/spinlock_linux.inc b/absl/base/internal/spinlock_linux.inc
index e31c6ed..202f7cd 100644
--- a/absl/base/internal/spinlock_linux.inc
+++ b/absl/base/internal/spinlock_linux.inc
@@ -56,7 +56,7 @@
extern "C" {
-ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay(
+ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)(
std::atomic<uint32_t> *w, uint32_t value, int loop,
absl::base_internal::SchedulingMode) {
absl::base_internal::ErrnoSaver errno_saver;
@@ -66,8 +66,8 @@
syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm);
}
-ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(std::atomic<uint32_t> *w,
- bool all) {
+ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(
+ std::atomic<uint32_t> *w, bool all) {
syscall(SYS_futex, w, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, all ? INT_MAX : 1, 0);
}
diff --git a/absl/base/internal/spinlock_posix.inc b/absl/base/internal/spinlock_posix.inc
index fcd21b1..4f6f887 100644
--- a/absl/base/internal/spinlock_posix.inc
+++ b/absl/base/internal/spinlock_posix.inc
@@ -25,7 +25,7 @@
extern "C" {
-ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay(
+ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)(
std::atomic<uint32_t>* /* lock_word */, uint32_t /* value */, int loop,
absl::base_internal::SchedulingMode /* mode */) {
absl::base_internal::ErrnoSaver errno_saver;
@@ -40,7 +40,7 @@
}
}
-ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(
+ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(
std::atomic<uint32_t>* /* lock_word */, bool /* all */) {}
} // extern "C"
diff --git a/absl/base/internal/spinlock_wait.h b/absl/base/internal/spinlock_wait.h
index 169bc74..579bd09 100644
--- a/absl/base/internal/spinlock_wait.h
+++ b/absl/base/internal/spinlock_wait.h
@@ -43,18 +43,16 @@
const SpinLockWaitTransition trans[],
SchedulingMode scheduling_mode);
-// If possible, wake some thread that has called SpinLockDelay(w, ...). If
-// "all" is true, wake all such threads. This call is a hint, and on some
-// systems it may be a no-op; threads calling SpinLockDelay() will always wake
-// eventually even if SpinLockWake() is never called.
+// If possible, wake some thread that has called SpinLockDelay(w, ...). If `all`
+// is true, wake all such threads. On some systems, this may be a no-op; on
+// those systems, threads calling SpinLockDelay() will always wake eventually
+// even if SpinLockWake() is never called.
void SpinLockWake(std::atomic<uint32_t> *w, bool all);
// Wait for an appropriate spin delay on iteration "loop" of a
// spin loop on location *w, whose previously observed value was "value".
// SpinLockDelay() may do nothing, may yield the CPU, may sleep a clock tick,
-// or may wait for a delay that can be truncated by a call to SpinLockWake(w).
-// In all cases, it must return in bounded time even if SpinLockWake() is not
-// called.
+// or may wait for a call to SpinLockWake(w).
void SpinLockDelay(std::atomic<uint32_t> *w, uint32_t value, int loop,
base_internal::SchedulingMode scheduling_mode);
@@ -73,21 +71,23 @@
// By changing our extension points to be extern "C", we dodge this
// check.
extern "C" {
-void AbslInternalSpinLockWake(std::atomic<uint32_t> *w, bool all);
-void AbslInternalSpinLockDelay(
+void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(std::atomic<uint32_t> *w,
+ bool all);
+void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)(
std::atomic<uint32_t> *w, uint32_t value, int loop,
absl::base_internal::SchedulingMode scheduling_mode);
}
inline void absl::base_internal::SpinLockWake(std::atomic<uint32_t> *w,
bool all) {
- AbslInternalSpinLockWake(w, all);
+ ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(w, all);
}
inline void absl::base_internal::SpinLockDelay(
std::atomic<uint32_t> *w, uint32_t value, int loop,
absl::base_internal::SchedulingMode scheduling_mode) {
- AbslInternalSpinLockDelay(w, value, loop, scheduling_mode);
+ ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)
+ (w, value, loop, scheduling_mode);
}
#endif // ABSL_BASE_INTERNAL_SPINLOCK_WAIT_H_
diff --git a/absl/base/internal/spinlock_win32.inc b/absl/base/internal/spinlock_win32.inc
index 78654b5..9d22481 100644
--- a/absl/base/internal/spinlock_win32.inc
+++ b/absl/base/internal/spinlock_win32.inc
@@ -20,9 +20,9 @@
extern "C" {
-void AbslInternalSpinLockDelay(std::atomic<uint32_t>* /* lock_word */,
- uint32_t /* value */, int loop,
- absl::base_internal::SchedulingMode /* mode */) {
+void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)(
+ std::atomic<uint32_t>* /* lock_word */, uint32_t /* value */, int loop,
+ absl::base_internal::SchedulingMode /* mode */) {
if (loop == 0) {
} else if (loop == 1) {
Sleep(0);
@@ -31,7 +31,7 @@
}
}
-void AbslInternalSpinLockWake(std::atomic<uint32_t>* /* lock_word */,
- bool /* all */) {}
+void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(
+ std::atomic<uint32_t>* /* lock_word */, bool /* all */) {}
} // extern "C"
diff --git a/absl/base/internal/strerror.cc b/absl/base/internal/strerror.cc
index d66ba12..0d6226f 100644
--- a/absl/base/internal/strerror.cc
+++ b/absl/base/internal/strerror.cc
@@ -51,7 +51,6 @@
}
std::string StrErrorInternal(int errnum) {
- absl::base_internal::ErrnoSaver errno_saver;
char buf[100];
const char* str = StrErrorAdaptor(errnum, buf, sizeof buf);
if (*str == '\0') {
@@ -76,6 +75,7 @@
} // namespace
std::string StrError(int errnum) {
+ absl::base_internal::ErrnoSaver errno_saver;
static const auto* table = NewStrErrorTable();
if (errnum >= 0 && errnum < static_cast<int>(table->size())) {
return (*table)[errnum];
diff --git a/absl/base/internal/strerror_test.cc b/absl/base/internal/strerror_test.cc
index a53da97..e32d5b5 100644
--- a/absl/base/internal/strerror_test.cc
+++ b/absl/base/internal/strerror_test.cc
@@ -62,12 +62,14 @@
++counter;
errno = ERANGE;
const std::string value = absl::base_internal::StrError(i);
+ // EXPECT_* could change errno. Stash it first.
+ int check_err = errno;
+ EXPECT_THAT(check_err, Eq(ERANGE));
// Only the GNU implementation is guaranteed to provide the
// string "Unknown error nnn". POSIX doesn't say anything.
if (!absl::StartsWith(value, "Unknown error ")) {
- EXPECT_THAT(absl::base_internal::StrError(i), Eq(expected_strings[i]));
+ EXPECT_THAT(value, Eq(expected_strings[i]));
}
- EXPECT_THAT(errno, Eq(ERANGE));
}
};
diff --git a/absl/base/internal/sysinfo_test.cc b/absl/base/internal/sysinfo_test.cc
index fa8b88b..5f9e45f 100644
--- a/absl/base/internal/sysinfo_test.cc
+++ b/absl/base/internal/sysinfo_test.cc
@@ -37,17 +37,28 @@
<< "NumCPUs() should not have the default value of 0";
}
+// Ensure that NominalCPUFrequency returns a reasonable value, or 1.00 on
+// platforms where the CPU frequency is not available through sysfs.
+//
+// POWER is particularly problematic here; some Linux kernels expose the CPU
+// frequency, while others do not. Since we can't predict a priori what a given
+// machine is going to do, just disable this test on POWER on Linux.
+#if !(defined(__linux) && (defined(__ppc64__) || defined(__PPC64__)))
TEST(SysinfoTest, NominalCPUFrequency) {
-#if !(defined(__aarch64__) && defined(__linux__)) && !defined(__EMSCRIPTEN__)
- EXPECT_GE(NominalCPUFrequency(), 1000.0)
- << "NominalCPUFrequency() did not return a reasonable value";
-#else
- // Aarch64 cannot read the CPU frequency from sysfs, so we get back 1.0.
- // Emscripten does not have a sysfs to read from at all.
+ // Linux only exposes the CPU frequency on certain architectures, and
+ // Emscripten doesn't expose it at all.
+#if defined(__linux__) && \
+ (defined(__aarch64__) || defined(__hppa__) || defined(__mips__) || \
+ defined(__riscv) || defined(__s390x__)) || \
+ defined(__EMSCRIPTEN__)
EXPECT_EQ(NominalCPUFrequency(), 1.0)
<< "CPU frequency detection was fixed! Please update unittest.";
+#else
+ EXPECT_GE(NominalCPUFrequency(), 1000.0)
+ << "NominalCPUFrequency() did not return a reasonable value";
#endif
}
+#endif
TEST(SysinfoTest, GetTID) {
EXPECT_EQ(GetTID(), GetTID()); // Basic compile and equality test.
diff --git a/absl/base/internal/thread_identity.h b/absl/base/internal/thread_identity.h
index d2a65fd..9ee651a 100644
--- a/absl/base/internal/thread_identity.h
+++ b/absl/base/internal/thread_identity.h
@@ -143,7 +143,7 @@
// Private: Reserved for absl::synchronization_internal::Waiter.
struct WaiterState {
- char data[128];
+ alignas(void*) char data[128];
} waiter_state;
// Used by PerThreadSem::{Get,Set}ThreadBlockedCounter().
diff --git a/absl/base/internal/unaligned_access.h b/absl/base/internal/unaligned_access.h
index 080c197..093dd9b 100644
--- a/absl/base/internal/unaligned_access.h
+++ b/absl/base/internal/unaligned_access.h
@@ -31,70 +31,6 @@
// The unaligned API is C++ only. The declarations use C++ features
// (namespaces, inline) which are absent or incompatible in C.
#if defined(__cplusplus)
-
-#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
- defined(ABSL_HAVE_THREAD_SANITIZER) || defined(ABSL_HAVE_MEMORY_SANITIZER)
-// Consider we have an unaligned load/store of 4 bytes from address 0x...05.
-// AddressSanitizer will treat it as a 3-byte access to the range 05:07 and
-// will miss a bug if 08 is the first unaddressable byte.
-// ThreadSanitizer will also treat this as a 3-byte access to 05:07 and will
-// miss a race between this access and some other accesses to 08.
-// MemorySanitizer will correctly propagate the shadow on unaligned stores
-// and correctly report bugs on unaligned loads, but it may not properly
-// update and report the origin of the uninitialized memory.
-// For all three tools, replacing an unaligned access with a tool-specific
-// callback solves the problem.
-
-#include <sanitizer/common_interface_defs.h>
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-inline uint16_t UnalignedLoad16(const void *p) {
- return __sanitizer_unaligned_load16(p);
-}
-
-inline uint32_t UnalignedLoad32(const void *p) {
- return __sanitizer_unaligned_load32(p);
-}
-
-inline uint64_t UnalignedLoad64(const void *p) {
- return __sanitizer_unaligned_load64(p);
-}
-
-inline void UnalignedStore16(void *p, uint16_t v) {
- __sanitizer_unaligned_store16(p, v);
-}
-
-inline void UnalignedStore32(void *p, uint32_t v) {
- __sanitizer_unaligned_store32(p, v);
-}
-
-inline void UnalignedStore64(void *p, uint64_t v) {
- __sanitizer_unaligned_store64(p, v);
-}
-
-} // namespace base_internal
-ABSL_NAMESPACE_END
-} // namespace absl
-
-#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \
- (absl::base_internal::UnalignedLoad16(_p))
-#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \
- (absl::base_internal::UnalignedLoad32(_p))
-#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \
- (absl::base_internal::UnalignedLoad64(_p))
-
-#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \
- (absl::base_internal::UnalignedStore16(_p, _val))
-#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \
- (absl::base_internal::UnalignedStore32(_p, _val))
-#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \
- (absl::base_internal::UnalignedStore64(_p, _val))
-
-#else
-
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace base_internal {
@@ -141,8 +77,6 @@
#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \
(absl::base_internal::UnalignedStore64(_p, _val))
-#endif
-
#endif // defined(__cplusplus), end of unaligned API
#endif // ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_
diff --git a/absl/base/log_severity.h b/absl/base/log_severity.h
index 045f17f..2236422 100644
--- a/absl/base/log_severity.h
+++ b/absl/base/log_severity.h
@@ -36,7 +36,7 @@
// such values to a defined severity level, however in some cases values other
// than the defined levels are useful for comparison.
//
-// Exmaple:
+// Example:
//
// // Effectively disables all logging:
// SetMinLogLevel(static_cast<absl::LogSeverity>(100));
diff --git a/absl/base/macros.h b/absl/base/macros.h
index 02dd9ff..3e085a9 100644
--- a/absl/base/macros.h
+++ b/absl/base/macros.h
@@ -144,4 +144,15 @@
#define ABSL_INTERNAL_RETHROW do {} while (false)
#endif // ABSL_HAVE_EXCEPTIONS
+// `ABSL_INTERNAL_UNREACHABLE` is an unreachable statement. A program which
+// reaches one has undefined behavior, and the compiler may optimize
+// accordingly.
+#if defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable)
+#define ABSL_INTERNAL_UNREACHABLE __builtin_unreachable()
+#elif defined(_MSC_VER)
+#define ABSL_INTERNAL_UNREACHABLE __assume(0)
+#else
+#define ABSL_INTERNAL_UNREACHABLE
+#endif
+
#endif // ABSL_BASE_MACROS_H_
diff --git a/absl/base/optimization.h b/absl/base/optimization.h
index 2e31376..6332b62 100644
--- a/absl/base/optimization.h
+++ b/absl/base/optimization.h
@@ -22,13 +22,15 @@
#ifndef ABSL_BASE_OPTIMIZATION_H_
#define ABSL_BASE_OPTIMIZATION_H_
+#include <assert.h>
+
#include "absl/base/config.h"
// ABSL_BLOCK_TAIL_CALL_OPTIMIZATION
//
-// Instructs the compiler to avoid optimizing tail-call recursion. Use of this
-// macro is useful when you wish to preserve the existing function order within
-// a stack trace for logging, debugging, or profiling purposes.
+// Instructs the compiler to avoid optimizing tail-call recursion. This macro is
+// useful when you wish to preserve the existing function order within a stack
+// trace for logging, debugging, or profiling purposes.
//
// Example:
//
@@ -179,7 +181,7 @@
#endif
// ABSL_INTERNAL_ASSUME(cond)
-// Informs the compiler than a condition is always true and that it can assume
+// Informs the compiler that a condition is always true and that it can assume
// it to be true for optimization purposes. The call has undefined behavior if
// the condition is false.
// In !NDEBUG mode, the condition is checked with an assert().
@@ -216,7 +218,7 @@
// This macro forces small unique name on a static file level symbols like
// static local variables or static functions. This is intended to be used in
// macro definitions to optimize the cost of generated code. Do NOT use it on
-// symbols exported from translation unit since it may casue a link time
+// symbols exported from translation unit since it may cause a link time
// conflict.
//
// Example:
diff --git a/absl/base/port.h b/absl/base/port.h
index 6c28068..5bc4d6c 100644
--- a/absl/base/port.h
+++ b/absl/base/port.h
@@ -14,7 +14,6 @@
//
// This files is a forwarding header for other headers containing various
// portability macros and functions.
-// This file is used for both C and C++!
#ifndef ABSL_BASE_PORT_H_
#define ABSL_BASE_PORT_H_
diff --git a/absl/base/spinlock_test_common.cc b/absl/base/spinlock_test_common.cc
index dee266e..2b572c5 100644
--- a/absl/base/spinlock_test_common.cc
+++ b/absl/base/spinlock_test_common.cc
@@ -92,6 +92,7 @@
static void ThreadedTest(SpinLock* spinlock) {
std::vector<std::thread> threads;
+ threads.reserve(kNumThreads);
for (int i = 0; i < kNumThreads; ++i) {
threads.push_back(std::thread(TestFunction, i, spinlock));
}
diff --git a/absl/cleanup/BUILD.bazel b/absl/cleanup/BUILD.bazel
new file mode 100644
index 0000000..5cca898
--- /dev/null
+++ b/absl/cleanup/BUILD.bazel
@@ -0,0 +1,66 @@
+# Copyright 2021 The Abseil Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
+load(
+ "//absl:copts/configure_copts.bzl",
+ "ABSL_DEFAULT_COPTS",
+ "ABSL_DEFAULT_LINKOPTS",
+ "ABSL_TEST_COPTS",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])
+
+cc_library(
+ name = "cleanup_internal",
+ hdrs = ["internal/cleanup.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ "//absl/base:base_internal",
+ "//absl/base:core_headers",
+ "//absl/utility",
+ ],
+)
+
+cc_library(
+ name = "cleanup",
+ hdrs = [
+ "cleanup.h",
+ ],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ ":cleanup_internal",
+ "//absl/base:config",
+ "//absl/base:core_headers",
+ ],
+)
+
+cc_test(
+ name = "cleanup_test",
+ size = "small",
+ srcs = [
+ "cleanup_test.cc",
+ ],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":cleanup",
+ "//absl/base:config",
+ "//absl/utility",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/absl/cleanup/BUILD.gn b/absl/cleanup/BUILD.gn
new file mode 100644
index 0000000..3b7b6c0
--- /dev/null
+++ b/absl/cleanup/BUILD.gn
@@ -0,0 +1,35 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/abseil-cpp/absl.gni")
+
+absl_source_set("cleanup_internal") {
+ public = [ "internal/cleanup.h" ]
+ deps = [
+ "//third_party/abseil-cpp/absl/base:base_internal",
+ "//third_party/abseil-cpp/absl/base:core_headers",
+ "//third_party/abseil-cpp/absl/utility",
+ ]
+ visibility = [ "//third_party/abseil-cpp/absl/*" ]
+}
+
+absl_source_set("cleanup") {
+ public = [ "cleanup.h" ]
+ deps = [
+ ":cleanup_internal",
+ "//third_party/abseil-cpp/absl/base:config",
+ "//third_party/abseil-cpp/absl/base:core_headers",
+ ]
+}
+
+absl_source_set("cleanup_test") {
+ testonly = true
+ sources = [ "cleanup_test.cc" ]
+ deps = [
+ ":cleanup",
+ "//third_party/abseil-cpp/absl/base:config",
+ "//third_party/abseil-cpp/absl/utility",
+ "//third_party/googletest:gtest",
+ ]
+}
diff --git a/absl/cleanup/CMakeLists.txt b/absl/cleanup/CMakeLists.txt
new file mode 100644
index 0000000..a2dd78a
--- /dev/null
+++ b/absl/cleanup/CMakeLists.txt
@@ -0,0 +1,55 @@
+# Copyright 2021 The Abseil Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+absl_cc_library(
+ NAME
+ cleanup_internal
+ HDRS
+ "internal/cleanup.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::base_internal
+ absl::core_headers
+ absl::utility
+ PUBLIC
+)
+
+absl_cc_library(
+ NAME
+ cleanup
+ HDRS
+ "cleanup.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::cleanup_internal
+ absl::config
+ absl::core_headers
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ cleanup_test
+ SRCS
+ "cleanup_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::cleanup
+ absl::config
+ absl::utility
+ gmock_main
+)
diff --git a/absl/cleanup/cleanup.h b/absl/cleanup/cleanup.h
new file mode 100644
index 0000000..61b53d5
--- /dev/null
+++ b/absl/cleanup/cleanup.h
@@ -0,0 +1,140 @@
+// Copyright 2021 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: cleanup.h
+// -----------------------------------------------------------------------------
+//
+// `absl::Cleanup` implements the scope guard idiom, invoking the contained
+// callback's `operator()() &&` on scope exit.
+//
+// Example:
+//
+// ```
+// absl::Status CopyGoodData(const char* source_path, const char* sink_path) {
+// FILE* source_file = fopen(source_path, "r");
+// if (source_file == nullptr) {
+// return absl::NotFoundError("No source file"); // No cleanups execute
+// }
+//
+// // C++17 style cleanup using class template argument deduction
+// absl::Cleanup source_closer = [source_file] { fclose(source_file); };
+//
+// FILE* sink_file = fopen(sink_path, "w");
+// if (sink_file == nullptr) {
+// return absl::NotFoundError("No sink file"); // First cleanup executes
+// }
+//
+// // C++11 style cleanup using the factory function
+// auto sink_closer = absl::MakeCleanup([sink_file] { fclose(sink_file); });
+//
+// Data data;
+// while (ReadData(source_file, &data)) {
+// if (!data.IsGood()) {
+// absl::Status result = absl::FailedPreconditionError("Read bad data");
+// return result; // Both cleanups execute
+// }
+// SaveData(sink_file, &data);
+// }
+//
+// return absl::OkStatus(); // Both cleanups execute
+// }
+// ```
+//
+// Methods:
+//
+// `std::move(cleanup).Cancel()` will prevent the callback from executing.
+//
+// `std::move(cleanup).Invoke()` will execute the callback early, before
+// destruction, and prevent the callback from executing in the destructor.
+//
+// Usage:
+//
+// `absl::Cleanup` is not an interface type. It is only intended to be used
+// within the body of a function. It is not a value type and instead models a
+// control flow construct. Check out `defer` in Golang for something similar.
+
+#ifndef ABSL_CLEANUP_CLEANUP_H_
+#define ABSL_CLEANUP_CLEANUP_H_
+
+#include <utility>
+
+#include "absl/base/config.h"
+#include "absl/base/macros.h"
+#include "absl/cleanup/internal/cleanup.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+template <typename Arg, typename Callback = void()>
+class ABSL_MUST_USE_RESULT Cleanup final {
+ static_assert(cleanup_internal::WasDeduced<Arg>(),
+ "Explicit template parameters are not supported.");
+
+ static_assert(cleanup_internal::ReturnsVoid<Callback>(),
+ "Callbacks that return values are not supported.");
+
+ public:
+ Cleanup(Callback callback) // NOLINT
+ : storage_(std::move(callback), /* is_callback_engaged = */ true) {}
+
+ Cleanup(Cleanup&& other) = default;
+
+ void Cancel() && {
+ ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged());
+ storage_.DisengageCallback();
+ }
+
+ void Invoke() && {
+ ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged());
+ storage_.DisengageCallback();
+ storage_.InvokeCallback();
+ }
+
+ ~Cleanup() {
+ if (storage_.IsCallbackEngaged()) {
+ storage_.InvokeCallback();
+ }
+ }
+
+ private:
+ cleanup_internal::Storage<Callback> storage_;
+};
+
+// `absl::Cleanup c = /* callback */;`
+//
+// C++17 type deduction API for creating an instance of `absl::Cleanup`
+#if defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION)
+template <typename Callback>
+Cleanup(Callback callback) -> Cleanup<cleanup_internal::Tag, Callback>;
+#endif // defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION)
+
+// `auto c = absl::MakeCleanup(/* callback */);`
+//
+// C++11 type deduction API for creating an instance of `absl::Cleanup`
+template <typename... Args, typename Callback>
+absl::Cleanup<cleanup_internal::Tag, Callback> MakeCleanup(Callback callback) {
+ static_assert(cleanup_internal::WasDeduced<cleanup_internal::Tag, Args...>(),
+ "Explicit template parameters are not supported.");
+
+ static_assert(cleanup_internal::ReturnsVoid<Callback>(),
+ "Callbacks that return values are not supported.");
+
+ return {std::move(callback)};
+}
+
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_CLEANUP_CLEANUP_H_
diff --git a/absl/cleanup/cleanup_test.cc b/absl/cleanup/cleanup_test.cc
new file mode 100644
index 0000000..792595d
--- /dev/null
+++ b/absl/cleanup/cleanup_test.cc
@@ -0,0 +1,267 @@
+// Copyright 2021 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/cleanup/cleanup.h"
+
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/utility/utility.h"
+
+namespace {
+
+using Tag = absl::cleanup_internal::Tag;
+
+template <typename Type1, typename Type2>
+constexpr bool IsSame() {
+ return (std::is_same<Type1, Type2>::value);
+}
+
+struct IdentityFactory {
+ template <typename Callback>
+ static Callback AsCallback(Callback callback) {
+ return Callback(std::move(callback));
+ }
+};
+
+// `FunctorClass` is a type used for testing `absl::Cleanup`. It is intended to
+// represent users that make their own move-only callback types outside of
+// `std::function` and lambda literals.
+class FunctorClass {
+ using Callback = std::function<void()>;
+
+ public:
+ explicit FunctorClass(Callback callback) : callback_(std::move(callback)) {}
+
+ FunctorClass(FunctorClass&& other)
+ : callback_(absl::exchange(other.callback_, Callback())) {}
+
+ FunctorClass(const FunctorClass&) = delete;
+
+ FunctorClass& operator=(const FunctorClass&) = delete;
+
+ FunctorClass& operator=(FunctorClass&&) = delete;
+
+ void operator()() const& = delete;
+
+ void operator()() && {
+ ASSERT_TRUE(callback_);
+ callback_();
+ callback_ = nullptr;
+ }
+
+ private:
+ Callback callback_;
+};
+
+struct FunctorClassFactory {
+ template <typename Callback>
+ static FunctorClass AsCallback(Callback callback) {
+ return FunctorClass(std::move(callback));
+ }
+};
+
+struct StdFunctionFactory {
+ template <typename Callback>
+ static std::function<void()> AsCallback(Callback callback) {
+ return std::function<void()>(std::move(callback));
+ }
+};
+
+using CleanupTestParams =
+ ::testing::Types<IdentityFactory, FunctorClassFactory, StdFunctionFactory>;
+template <typename>
+struct CleanupTest : public ::testing::Test {};
+TYPED_TEST_SUITE(CleanupTest, CleanupTestParams);
+
+bool fn_ptr_called = false;
+void FnPtrFunction() { fn_ptr_called = true; }
+
+TYPED_TEST(CleanupTest, FactoryProducesCorrectType) {
+ {
+ auto callback = TypeParam::AsCallback([] {});
+ auto cleanup = absl::MakeCleanup(std::move(callback));
+
+ static_assert(
+ IsSame<absl::Cleanup<Tag, decltype(callback)>, decltype(cleanup)>(),
+ "");
+ }
+
+ {
+ auto cleanup = absl::MakeCleanup(&FnPtrFunction);
+
+ static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(),
+ "");
+ }
+
+ {
+ auto cleanup = absl::MakeCleanup(FnPtrFunction);
+
+ static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(),
+ "");
+ }
+}
+
+#if defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION)
+TYPED_TEST(CleanupTest, CTADProducesCorrectType) {
+ {
+ auto callback = TypeParam::AsCallback([] {});
+ absl::Cleanup cleanup = std::move(callback);
+
+ static_assert(
+ IsSame<absl::Cleanup<Tag, decltype(callback)>, decltype(cleanup)>(),
+ "");
+ }
+
+ {
+ absl::Cleanup cleanup = &FnPtrFunction;
+
+ static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(),
+ "");
+ }
+
+ {
+ absl::Cleanup cleanup = FnPtrFunction;
+
+ static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(),
+ "");
+ }
+}
+
+TYPED_TEST(CleanupTest, FactoryAndCTADProduceSameType) {
+ {
+ auto callback = IdentityFactory::AsCallback([] {});
+ auto factory_cleanup = absl::MakeCleanup(callback);
+ absl::Cleanup deduction_cleanup = callback;
+
+ static_assert(
+ IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
+ }
+
+ {
+ auto factory_cleanup =
+ absl::MakeCleanup(FunctorClassFactory::AsCallback([] {}));
+ absl::Cleanup deduction_cleanup = FunctorClassFactory::AsCallback([] {});
+
+ static_assert(
+ IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
+ }
+
+ {
+ auto factory_cleanup =
+ absl::MakeCleanup(StdFunctionFactory::AsCallback([] {}));
+ absl::Cleanup deduction_cleanup = StdFunctionFactory::AsCallback([] {});
+
+ static_assert(
+ IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
+ }
+
+ {
+ auto factory_cleanup = absl::MakeCleanup(&FnPtrFunction);
+ absl::Cleanup deduction_cleanup = &FnPtrFunction;
+
+ static_assert(
+ IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
+ }
+
+ {
+ auto factory_cleanup = absl::MakeCleanup(FnPtrFunction);
+ absl::Cleanup deduction_cleanup = FnPtrFunction;
+
+ static_assert(
+ IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
+ }
+}
+#endif // defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION)
+
+TYPED_TEST(CleanupTest, BasicUsage) {
+ bool called = false;
+
+ {
+ auto cleanup =
+ absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; }));
+ EXPECT_FALSE(called); // Constructor shouldn't invoke the callback
+ }
+
+ EXPECT_TRUE(called); // Destructor should invoke the callback
+}
+
+TYPED_TEST(CleanupTest, BasicUsageWithFunctionPointer) {
+ fn_ptr_called = false;
+
+ {
+ auto cleanup = absl::MakeCleanup(TypeParam::AsCallback(&FnPtrFunction));
+ EXPECT_FALSE(fn_ptr_called); // Constructor shouldn't invoke the callback
+ }
+
+ EXPECT_TRUE(fn_ptr_called); // Destructor should invoke the callback
+}
+
+TYPED_TEST(CleanupTest, Cancel) {
+ bool called = false;
+
+ {
+ auto cleanup =
+ absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; }));
+ EXPECT_FALSE(called); // Constructor shouldn't invoke the callback
+
+ std::move(cleanup).Cancel();
+ EXPECT_FALSE(called); // Cancel shouldn't invoke the callback
+ }
+
+ EXPECT_FALSE(called); // Destructor shouldn't invoke the callback
+}
+
+TYPED_TEST(CleanupTest, Invoke) {
+ bool called = false;
+
+ {
+ auto cleanup =
+ absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; }));
+ EXPECT_FALSE(called); // Constructor shouldn't invoke the callback
+
+ std::move(cleanup).Invoke();
+ EXPECT_TRUE(called); // Invoke should invoke the callback
+
+ called = false; // Reset tracker before destructor runs
+ }
+
+ EXPECT_FALSE(called); // Destructor shouldn't invoke the callback
+}
+
+TYPED_TEST(CleanupTest, Move) {
+ bool called = false;
+
+ {
+ auto moved_from_cleanup =
+ absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; }));
+ EXPECT_FALSE(called); // Constructor shouldn't invoke the callback
+
+ {
+ auto moved_to_cleanup = std::move(moved_from_cleanup);
+ EXPECT_FALSE(called); // Move shouldn't invoke the callback
+ }
+
+ EXPECT_TRUE(called); // Destructor should invoke the callback
+
+ called = false; // Reset tracker before destructor runs
+ }
+
+ EXPECT_FALSE(called); // Destructor shouldn't invoke the callback
+}
+
+} // namespace
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel
index 8e72ad0..f22fdc6 100644
--- a/absl/container/BUILD.bazel
+++ b/absl/container/BUILD.bazel
@@ -599,12 +599,12 @@
":hashtablez_sampler",
":have_sse",
":layout",
- "//absl/base:bits",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:endian",
"//absl/memory",
"//absl/meta:type_traits",
+ "//absl/numeric:bits",
"//absl/utility",
],
)
@@ -630,6 +630,45 @@
],
)
+cc_binary(
+ name = "raw_hash_set_benchmark",
+ testonly = 1,
+ srcs = ["internal/raw_hash_set_benchmark.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ tags = ["benchmark"],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":hash_function_defaults",
+ ":raw_hash_set",
+ "//absl/base:raw_logging_internal",
+ "//absl/strings:str_format",
+ "@com_github_google_benchmark//:benchmark_main",
+ ],
+)
+
+cc_binary(
+ name = "raw_hash_set_probe_benchmark",
+ testonly = 1,
+ srcs = ["internal/raw_hash_set_probe_benchmark.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = select({
+ "//conditions:default": [],
+ }) + ABSL_DEFAULT_LINKOPTS,
+ tags = ["benchmark"],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":flat_hash_map",
+ ":hash_function_defaults",
+ ":hashtable_debug",
+ ":raw_hash_set",
+ "//absl/random",
+ "//absl/random:distributions",
+ "//absl/strings",
+ "//absl/strings:str_format",
+ ],
+)
+
cc_test(
name = "raw_hash_set_allocator_test",
size = "small",
@@ -677,6 +716,22 @@
],
)
+cc_binary(
+ name = "layout_benchmark",
+ testonly = 1,
+ srcs = ["internal/layout_benchmark.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ tags = ["benchmark"],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":layout",
+ "//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
+ "@com_github_google_benchmark//:benchmark_main",
+ ],
+)
+
cc_library(
name = "tracked",
testonly = 1,
diff --git a/absl/container/BUILD.gn b/absl/container/BUILD.gn
index 2d3c6b3..3e4c68d 100644
--- a/absl/container/BUILD.gn
+++ b/absl/container/BUILD.gn
@@ -219,12 +219,12 @@
":hashtablez_sampler",
":have_sse",
":layout",
- "//third_party/abseil-cpp/absl/base:bits",
"//third_party/abseil-cpp/absl/base:config",
"//third_party/abseil-cpp/absl/base:core_headers",
"//third_party/abseil-cpp/absl/base:endian",
"//third_party/abseil-cpp/absl/memory",
"//third_party/abseil-cpp/absl/meta:type_traits",
+ "//third_party/abseil-cpp/absl/numeric:bits",
"//third_party/abseil-cpp/absl/utility",
]
}
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt
index eb202c4..2d7d0e6 100644
--- a/absl/container/CMakeLists.txt
+++ b/absl/container/CMakeLists.txt
@@ -14,15 +14,6 @@
# limitations under the License.
#
-# This is deprecated and will be removed in the future. It also doesn't do
-# anything anyways. Prefer to use the library associated with the API you are
-# using.
-absl_cc_library(
- NAME
- container
- PUBLIC
-)
-
absl_cc_library(
NAME
btree
diff --git a/absl/container/btree_benchmark.cc b/absl/container/btree_benchmark.cc
index 4679867..41f13f5 100644
--- a/absl/container/btree_benchmark.cc
+++ b/absl/container/btree_benchmark.cc
@@ -101,39 +101,6 @@
BM_InsertImpl<T>(state, true);
}
-// container::insert sometimes returns a pair<iterator, bool> and sometimes
-// returns an iterator (for multi- containers).
-template <typename Iter>
-Iter GetIterFromInsert(const std::pair<Iter, bool>& pair) {
- return pair.first;
-}
-template <typename Iter>
-Iter GetIterFromInsert(const Iter iter) {
- return iter;
-}
-
-// Benchmark insertion of values into a container at the end.
-template <typename T>
-void BM_InsertEnd(benchmark::State& state) {
- using V = typename remove_pair_const<typename T::value_type>::type;
- typename KeyOfValue<typename T::key_type, V>::type key_of_value;
-
- T container;
- const int kSize = 10000;
- for (int i = 0; i < kSize; ++i) {
- container.insert(Generator<V>(kSize)(i));
- }
- V v = Generator<V>(kSize)(kSize - 1);
- typename T::key_type k = key_of_value(v);
-
- auto it = container.find(k);
- while (state.KeepRunning()) {
- // Repeatedly removing then adding v.
- container.erase(it);
- it = GetIterFromInsert(container.insert(v));
- }
-}
-
// Benchmark inserting the first few elements in a container. In b-tree, this is
// when the root node grows.
template <typename T>
@@ -513,7 +480,6 @@
#define MY_BENCHMARK3(type) \
MY_BENCHMARK4(type, Insert); \
MY_BENCHMARK4(type, InsertSorted); \
- MY_BENCHMARK4(type, InsertEnd); \
MY_BENCHMARK4(type, InsertSmall); \
MY_BENCHMARK4(type, Lookup); \
MY_BENCHMARK4(type, FullLookup); \
diff --git a/absl/container/btree_map.h b/absl/container/btree_map.h
index abc09b0..ea49d44 100644
--- a/absl/container/btree_map.h
+++ b/absl/container/btree_map.h
@@ -384,9 +384,8 @@
// btree_map::equal_range()
//
- // Returns a closed range [first, last], defined by a `std::pair` of two
- // iterators, containing all elements with the passed key in the
- // `btree_map`.
+ // Returns a half-open range [first, last), defined by a `std::pair` of two
+ // iterators, containing all elements with the passed key in the `btree_map`.
using Base::equal_range;
// btree_map::find()
@@ -709,7 +708,7 @@
// btree_multimap::equal_range()
//
- // Returns a closed range [first, last], defined by a `std::pair` of two
+ // Returns a half-open range [first, last), defined by a `std::pair` of two
// iterators, containing all elements with the passed key in the
// `btree_multimap`.
using Base::equal_range;
diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc
index 9b1b643..367d75b 100644
--- a/absl/container/btree_test.cc
+++ b/absl/container/btree_test.cc
@@ -2038,6 +2038,30 @@
EXPECT_EQ(res, ++other.begin());
}
+TEST(Btree, ExtractMultiMapEquivalentKeys) {
+ // Note: using string keys means a three-way comparator.
+ absl::btree_multimap<std::string, int> map;
+ for (int i = 0; i < 100; ++i) {
+ for (int j = 0; j < 100; ++j) {
+ map.insert({absl::StrCat(i), j});
+ }
+ }
+
+ for (int i = 0; i < 100; ++i) {
+ const std::string key = absl::StrCat(i);
+ auto node_handle = map.extract(key);
+ EXPECT_EQ(node_handle.key(), key);
+ EXPECT_EQ(node_handle.mapped(), 0) << i;
+ }
+
+ for (int i = 0; i < 100; ++i) {
+ const std::string key = absl::StrCat(i);
+ auto node_handle = map.extract(key);
+ EXPECT_EQ(node_handle.key(), key);
+ EXPECT_EQ(node_handle.mapped(), 1) << i;
+ }
+}
+
// For multisets, insert with hint also affects correctness because we need to
// insert immediately before the hint if possible.
struct InsertMultiHintData {
@@ -2696,9 +2720,36 @@
bool operator()(const MultiKey a, const int b) const { return a.i1 < b; }
};
-TEST(Btree, MultiKeyEqualRange) {
- absl::btree_set<MultiKey, MultiKeyComp> set;
+// A heterogeneous, three-way comparator that has different equivalence classes
+// for different lookup types.
+struct MultiKeyThreeWayComp {
+ using is_transparent = void;
+ absl::weak_ordering operator()(const MultiKey a, const MultiKey b) const {
+ if (a.i1 < b.i1) return absl::weak_ordering::less;
+ if (a.i1 > b.i1) return absl::weak_ordering::greater;
+ if (a.i2 < b.i2) return absl::weak_ordering::less;
+ if (a.i2 > b.i2) return absl::weak_ordering::greater;
+ return absl::weak_ordering::equivalent;
+ }
+ absl::weak_ordering operator()(const int a, const MultiKey b) const {
+ if (a < b.i1) return absl::weak_ordering::less;
+ if (a > b.i1) return absl::weak_ordering::greater;
+ return absl::weak_ordering::equivalent;
+ }
+ absl::weak_ordering operator()(const MultiKey a, const int b) const {
+ if (a.i1 < b) return absl::weak_ordering::less;
+ if (a.i1 > b) return absl::weak_ordering::greater;
+ return absl::weak_ordering::equivalent;
+ }
+};
+template <typename Compare>
+class BtreeMultiKeyTest : public ::testing::Test {};
+using MultiKeyComps = ::testing::Types<MultiKeyComp, MultiKeyThreeWayComp>;
+TYPED_TEST_SUITE(BtreeMultiKeyTest, MultiKeyComps);
+
+TYPED_TEST(BtreeMultiKeyTest, EqualRange) {
+ absl::btree_set<MultiKey, TypeParam> set;
for (int i = 0; i < 100; ++i) {
for (int j = 0; j < 100; ++j) {
set.insert({i, j});
@@ -2708,20 +2759,41 @@
for (int i = 0; i < 100; ++i) {
auto equal_range = set.equal_range(i);
EXPECT_EQ(equal_range.first->i1, i);
- EXPECT_EQ(equal_range.first->i2, 0);
+ EXPECT_EQ(equal_range.first->i2, 0) << i;
EXPECT_EQ(std::distance(equal_range.first, equal_range.second), 100) << i;
}
}
-TEST(Btree, MultiKeyErase) {
- absl::btree_set<MultiKey, MultiKeyComp> set = {
+TYPED_TEST(BtreeMultiKeyTest, Extract) {
+ absl::btree_set<MultiKey, TypeParam> set;
+ for (int i = 0; i < 100; ++i) {
+ for (int j = 0; j < 100; ++j) {
+ set.insert({i, j});
+ }
+ }
+
+ for (int i = 0; i < 100; ++i) {
+ auto node_handle = set.extract(i);
+ EXPECT_EQ(node_handle.value().i1, i);
+ EXPECT_EQ(node_handle.value().i2, 0) << i;
+ }
+
+ for (int i = 0; i < 100; ++i) {
+ auto node_handle = set.extract(i);
+ EXPECT_EQ(node_handle.value().i1, i);
+ EXPECT_EQ(node_handle.value().i2, 1) << i;
+ }
+}
+
+TYPED_TEST(BtreeMultiKeyTest, Erase) {
+ absl::btree_set<MultiKey, TypeParam> set = {
{1, 1}, {2, 1}, {2, 2}, {3, 1}};
EXPECT_EQ(set.erase(2), 2);
EXPECT_THAT(set, ElementsAre(MultiKey{1, 1}, MultiKey{3, 1}));
}
-TEST(Btree, MultiKeyCount) {
- const absl::btree_set<MultiKey, MultiKeyComp> set = {
+TYPED_TEST(BtreeMultiKeyTest, Count) {
+ const absl::btree_set<MultiKey, TypeParam> set = {
{1, 1}, {2, 1}, {2, 2}, {3, 1}};
EXPECT_EQ(set.count(2), 2);
}
diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h
index 81e145a..6b89da6 100644
--- a/absl/container/flat_hash_set.h
+++ b/absl/container/flat_hash_set.h
@@ -324,7 +324,7 @@
// flat_hash_set::merge()
//
- // Extracts elements from a given `source` flat hash map into this
+ // Extracts elements from a given `source` flat hash set into this
// `flat_hash_set`. If the destination `flat_hash_set` already contains an
// element with an equivalent key, that element is not extracted.
using Base::merge;
diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h
index 90bb96e..7c18234 100644
--- a/absl/container/inlined_vector.h
+++ b/absl/container/inlined_vector.h
@@ -167,11 +167,13 @@
// Creates an inlined vector by copying the contents of `other` using `alloc`.
InlinedVector(const InlinedVector& other, const allocator_type& alloc)
: storage_(alloc) {
- if (IsMemcpyOk::value && !other.storage_.GetIsAllocated()) {
+ if (other.empty()) {
+ // Empty; nothing to do.
+ } else if (IsMemcpyOk::value && !other.storage_.GetIsAllocated()) {
+ // Memcpy-able and do not need allocation.
storage_.MemcpyFrom(other.storage_);
} else {
- storage_.Initialize(IteratorValueAdapter<const_pointer>(other.data()),
- other.size());
+ storage_.InitFrom(other.storage_);
}
}
diff --git a/absl/container/inlined_vector_benchmark.cc b/absl/container/inlined_vector_benchmark.cc
index b8dafe9..e256fad 100644
--- a/absl/container/inlined_vector_benchmark.cc
+++ b/absl/container/inlined_vector_benchmark.cc
@@ -534,6 +534,28 @@
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromMove, TrivialType);
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromMove, NontrivialType);
+// Measure cost of copy-constructor+destructor.
+void BM_CopyTrivial(benchmark::State& state) {
+ const int n = state.range(0);
+ InlVec<int64_t> src(n);
+ for (auto s : state) {
+ InlVec<int64_t> copy(src);
+ benchmark::DoNotOptimize(copy);
+ }
+}
+BENCHMARK(BM_CopyTrivial)->Arg(0)->Arg(1)->Arg(kLargeSize);
+
+// Measure cost of copy-constructor+destructor.
+void BM_CopyNonTrivial(benchmark::State& state) {
+ const int n = state.range(0);
+ InlVec<InlVec<int64_t>> src(n);
+ for (auto s : state) {
+ InlVec<InlVec<int64_t>> copy(src);
+ benchmark::DoNotOptimize(copy);
+ }
+}
+BENCHMARK(BM_CopyNonTrivial)->Arg(0)->Arg(1)->Arg(kLargeSize);
+
template <typename T, size_t FromSize, size_t ToSize>
void BM_AssignSizeRef(benchmark::State& state) {
auto size = ToSize;
diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc
index 415c60d..98aff33 100644
--- a/absl/container/inlined_vector_test.cc
+++ b/absl/container/inlined_vector_test.cc
@@ -736,22 +736,26 @@
// In particular, ensure that std::allocator doesn't cost anything to store.
// The union should be absorbing some of the allocation bookkeeping overhead
// in the larger vectors, leaving only the size_ field as overhead.
- EXPECT_EQ(2 * sizeof(int*),
- sizeof(absl::InlinedVector<int*, 1>) - 1 * sizeof(int*));
- EXPECT_EQ(1 * sizeof(int*),
- sizeof(absl::InlinedVector<int*, 2>) - 2 * sizeof(int*));
- EXPECT_EQ(1 * sizeof(int*),
- sizeof(absl::InlinedVector<int*, 3>) - 3 * sizeof(int*));
- EXPECT_EQ(1 * sizeof(int*),
- sizeof(absl::InlinedVector<int*, 4>) - 4 * sizeof(int*));
- EXPECT_EQ(1 * sizeof(int*),
- sizeof(absl::InlinedVector<int*, 5>) - 5 * sizeof(int*));
- EXPECT_EQ(1 * sizeof(int*),
- sizeof(absl::InlinedVector<int*, 6>) - 6 * sizeof(int*));
- EXPECT_EQ(1 * sizeof(int*),
- sizeof(absl::InlinedVector<int*, 7>) - 7 * sizeof(int*));
- EXPECT_EQ(1 * sizeof(int*),
- sizeof(absl::InlinedVector<int*, 8>) - 8 * sizeof(int*));
+
+ struct T { void* val; };
+ size_t expected_overhead = sizeof(T);
+
+ EXPECT_EQ((2 * expected_overhead),
+ sizeof(absl::InlinedVector<T, 1>) - sizeof(T[1]));
+ EXPECT_EQ(expected_overhead,
+ sizeof(absl::InlinedVector<T, 2>) - sizeof(T[2]));
+ EXPECT_EQ(expected_overhead,
+ sizeof(absl::InlinedVector<T, 3>) - sizeof(T[3]));
+ EXPECT_EQ(expected_overhead,
+ sizeof(absl::InlinedVector<T, 4>) - sizeof(T[4]));
+ EXPECT_EQ(expected_overhead,
+ sizeof(absl::InlinedVector<T, 5>) - sizeof(T[5]));
+ EXPECT_EQ(expected_overhead,
+ sizeof(absl::InlinedVector<T, 6>) - sizeof(T[6]));
+ EXPECT_EQ(expected_overhead,
+ sizeof(absl::InlinedVector<T, 7>) - sizeof(T[7]));
+ EXPECT_EQ(expected_overhead,
+ sizeof(absl::InlinedVector<T, 8>) - sizeof(T[8]));
}
TEST(IntVec, Clear) {
diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h
index f2fc31d..6f5f01b 100644
--- a/absl/container/internal/btree.h
+++ b/absl/container/internal/btree.h
@@ -220,9 +220,6 @@
// If Compare is a common comparator for a string-like type, then we adapt it
// to use heterogeneous lookup and to be a key-compare-to comparator.
using key_compare = typename key_compare_to_adapter<Compare>::type;
- // True when key_compare has been adapted to StringBtreeDefault{Less,Greater}.
- using is_key_compare_adapted =
- absl::negation<std::is_same<key_compare, Compare>>;
// A type which indicates if we have a key-compare-to functor or a plain old
// key-compare functor.
using is_key_compare_to = btree_is_key_compare_to<key_compare, Key>;
@@ -232,9 +229,6 @@
using size_type = std::make_signed<size_t>::type;
using difference_type = ptrdiff_t;
- // True if this is a multiset or multimap.
- using is_multi_container = std::integral_constant<bool, Multi>;
-
using slot_policy = SlotPolicy;
using slot_type = typename slot_policy::slot_type;
using value_type = typename slot_policy::value_type;
@@ -244,6 +238,23 @@
using reference = value_type &;
using const_reference = const value_type &;
+ // For the given lookup key type, returns whether we can have multiple
+ // equivalent keys in the btree. If this is a multi-container, then we can.
+ // Otherwise, we can have multiple equivalent keys only if all of the
+ // following conditions are met:
+ // - The comparator is transparent.
+ // - The lookup key type is not the same as key_type.
+ // - The comparator is not a StringBtreeDefault{Less,Greater} comparator
+ // that we know has the same equivalence classes for all lookup types.
+ template <typename LookupKey>
+ constexpr static bool can_have_multiple_equivalent_keys() {
+ return Multi ||
+ (IsTransparent<key_compare>::value &&
+ !std::is_same<LookupKey, Key>::value &&
+ !std::is_same<key_compare, StringBtreeDefaultLess>::value &&
+ !std::is_same<key_compare, StringBtreeDefaultGreater>::value);
+ }
+
enum {
kTargetNodeSize = TargetNodeSize,
@@ -439,7 +450,6 @@
template <typename Params>
class btree_node {
using is_key_compare_to = typename Params::is_key_compare_to;
- using is_multi_container = typename Params::is_multi_container;
using field_type = typename Params::node_count_type;
using allocator_type = typename Params::allocator_type;
using slot_type = typename Params::slot_type;
@@ -759,7 +769,7 @@
SearchResult<int, true> binary_search_impl(
const K &k, int s, int e, const CompareTo &comp,
std::true_type /* IsCompareTo */) const {
- if (is_multi_container::value) {
+ if (params_type::template can_have_multiple_equivalent_keys<K>()) {
MatchKind exact_match = MatchKind::kNe;
while (s != e) {
const int mid = (s + e) >> 1;
@@ -770,14 +780,14 @@
e = mid;
if (c == 0) {
// Need to return the first value whose key is not less than k,
- // which requires continuing the binary search if this is a
- // multi-container.
+ // which requires continuing the binary search if there could be
+ // multiple equivalent keys.
exact_match = MatchKind::kEq;
}
}
}
return {s, exact_match};
- } else { // Not a multi-container.
+ } else { // Can't have multiple equivalent keys.
while (s != e) {
const int mid = (s + e) >> 1;
const absl::weak_ordering c = comp(key(mid), k);
@@ -910,6 +920,7 @@
using key_type = typename Node::key_type;
using size_type = typename Node::size_type;
using params_type = typename Node::params_type;
+ using is_map_container = typename params_type::is_map_container;
using node_type = Node;
using normal_node = typename std::remove_const<Node>::type;
@@ -921,7 +932,7 @@
using slot_type = typename params_type::slot_type;
using iterator =
- btree_iterator<normal_node, normal_reference, normal_pointer>;
+ btree_iterator<normal_node, normal_reference, normal_pointer>;
using const_iterator =
btree_iterator<const_node, const_reference, const_pointer>;
@@ -938,20 +949,19 @@
btree_iterator(Node *n, int p) : node(n), position(p) {}
// NOTE: this SFINAE allows for implicit conversions from iterator to
- // const_iterator, but it specifically avoids defining copy constructors so
- // that btree_iterator can be trivially copyable. This is for performance and
- // binary size reasons.
+ // const_iterator, but it specifically avoids hiding the copy constructor so
+ // that the trivial one will be used when possible.
template <typename N, typename R, typename P,
absl::enable_if_t<
std::is_same<btree_iterator<N, R, P>, iterator>::value &&
std::is_same<btree_iterator, const_iterator>::value,
int> = 0>
- btree_iterator(const btree_iterator<N, R, P> &other) // NOLINT
+ btree_iterator(const btree_iterator<N, R, P> other) // NOLINT
: node(other.node), position(other.position) {}
private:
// This SFINAE allows explicit conversions from const_iterator to
- // iterator, but also avoids defining a copy constructor.
+ // iterator, but also avoids hiding the copy constructor.
// NOTE: the const_cast is safe because this constructor is only called by
// non-const methods and the container owns the nodes.
template <typename N, typename R, typename P,
@@ -959,7 +969,7 @@
std::is_same<btree_iterator<N, R, P>, const_iterator>::value &&
std::is_same<btree_iterator, iterator>::value,
int> = 0>
- explicit btree_iterator(const btree_iterator<N, R, P> &other)
+ explicit btree_iterator(const btree_iterator<N, R, P> other)
: node(const_cast<node_type *>(other.node)), position(other.position) {}
// Increment/decrement the iterator.
@@ -1022,6 +1032,8 @@
}
private:
+ friend iterator;
+ friend const_iterator;
template <typename Params>
friend class btree;
template <typename Tree>
@@ -1032,8 +1044,6 @@
friend class btree_map_container;
template <typename Tree>
friend class btree_multiset_container;
- template <typename N, typename R, typename P>
- friend struct btree_iterator;
template <typename TreeType, typename CheckerType>
friend class base_checker;
@@ -1054,8 +1064,6 @@
using is_key_compare_to = typename Params::is_key_compare_to;
using init_type = typename Params::init_type;
using field_type = typename node_type::field_type;
- using is_multi_container = typename Params::is_multi_container;
- using is_key_compare_adapted = typename Params::is_key_compare_adapted;
// We use a static empty node for the root/leftmost/rightmost of empty btrees
// in order to avoid branching in begin()/end().
@@ -1122,7 +1130,8 @@
using const_reference = typename Params::const_reference;
using pointer = typename Params::pointer;
using const_pointer = typename Params::const_pointer;
- using iterator = btree_iterator<node_type, reference, pointer>;
+ using iterator =
+ typename btree_iterator<node_type, reference, pointer>::iterator;
using const_iterator = typename iterator::const_iterator;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
@@ -1135,7 +1144,11 @@
private:
// For use in copy_or_move_values_in_order.
const value_type &maybe_move_from_iterator(const_iterator it) { return *it; }
- value_type &&maybe_move_from_iterator(iterator it) { return std::move(*it); }
+ value_type &&maybe_move_from_iterator(iterator it) {
+ // This is a destructive operation on the other container so it's safe for
+ // us to const_cast and move from the keys here even if it's a set.
+ return std::move(const_cast<value_type &>(*it));
+ }
// Copies or moves (depending on the template parameter) the values in
// other into this btree in their order in other. This btree must be empty
@@ -1198,7 +1211,7 @@
return const_reverse_iterator(begin());
}
- // Finds the first element whose key is not less than key.
+ // Finds the first element whose key is not less than `key`.
template <typename K>
iterator lower_bound(const K &key) {
return internal_end(internal_lower_bound(key).value);
@@ -1208,7 +1221,12 @@
return internal_end(internal_lower_bound(key).value);
}
- // Finds the first element whose key is greater than key.
+ // Finds the first element whose key is not less than `key` and also returns
+ // whether that element is equal to `key`.
+ template <typename K>
+ std::pair<iterator, bool> lower_bound_equal(const K &key) const;
+
+ // Finds the first element whose key is greater than `key`.
template <typename K>
iterator upper_bound(const K &key) {
return internal_end(internal_upper_bound(key));
@@ -1290,8 +1308,8 @@
// to the element after the last erased element.
std::pair<size_type, iterator> erase_range(iterator begin, iterator end);
- // Finds the iterator corresponding to a key or returns end() if the key is
- // not present.
+ // Finds an element with key equivalent to `key` or returns `end()` if `key`
+ // is not present.
template <typename K>
iterator find(const K &key) {
return internal_end(internal_find(key));
@@ -1892,23 +1910,28 @@
template <typename P>
template <typename K>
-auto btree<P>::equal_range(const K &key) -> std::pair<iterator, iterator> {
+auto btree<P>::lower_bound_equal(const K &key) const
+ -> std::pair<iterator, bool> {
const SearchResult<iterator, is_key_compare_to::value> res =
internal_lower_bound(key);
- const iterator lower = internal_end(res.value);
- if (res.HasMatch() ? !res.IsEq()
- : lower == end() || compare_keys(key, lower.key())) {
+ const iterator lower = iterator(internal_end(res.value));
+ const bool equal = res.HasMatch()
+ ? res.IsEq()
+ : lower != end() && !compare_keys(key, lower.key());
+ return {lower, equal};
+}
+
+template <typename P>
+template <typename K>
+auto btree<P>::equal_range(const K &key) -> std::pair<iterator, iterator> {
+ const std::pair<iterator, bool> lower_and_equal = lower_bound_equal(key);
+ const iterator lower = lower_and_equal.first;
+ if (!lower_and_equal.second) {
return {lower, lower};
}
const iterator next = std::next(lower);
- // When the comparator is heterogeneous, we can't assume that comparison with
- // non-`key_type` will be equivalent to `key_type` comparisons so there
- // could be multiple equivalent keys even in a unique-container. But for
- // heterogeneous comparisons from the default string adapted comparators, we
- // don't need to worry about this.
- if (!is_multi_container::value &&
- (std::is_same<K, key_type>::value || is_key_compare_adapted::value)) {
+ if (!params_type::template can_have_multiple_equivalent_keys<K>()) {
// The next iterator after lower must point to a key greater than `key`.
// Note: if this assert fails, then it may indicate that the comparator does
// not meet the equivalence requirements for Compare
@@ -2503,14 +2526,17 @@
template <typename K>
auto btree<P>::internal_lower_bound(const K &key) const
-> SearchResult<iterator, is_key_compare_to::value> {
+ if (!params_type::template can_have_multiple_equivalent_keys<K>()) {
+ SearchResult<iterator, is_key_compare_to::value> ret = internal_locate(key);
+ ret.value = internal_last(ret.value);
+ return ret;
+ }
iterator iter(const_cast<node_type *>(root()));
SearchResult<int, is_key_compare_to::value> res;
bool seen_eq = false;
for (;;) {
res = iter.node->lower_bound(key, key_comp());
iter.position = res.value;
- // TODO(ezb): we should be able to terminate early on IsEq() if there can't
- // be multiple equivalent keys in container for this lookup type.
if (iter.node->leaf()) {
break;
}
diff --git a/absl/container/internal/btree_container.h b/absl/container/internal/btree_container.h
index 887eda4..03be708 100644
--- a/absl/container/internal/btree_container.h
+++ b/absl/container/internal/btree_container.h
@@ -338,13 +338,12 @@
}
// Node extraction routines.
- // TODO(ezb): when the comparator is heterogeneous and has different
- // equivalence classes for different lookup types, we should extract the first
- // equivalent value if there are multiple.
template <typename K = key_type>
node_type extract(const key_arg<K> &key) {
- auto it = this->find(key);
- return it == this->end() ? node_type() : extract(it);
+ const std::pair<iterator, bool> lower_and_equal =
+ this->tree_.lower_bound_equal(key);
+ return lower_and_equal.second ? extract(lower_and_equal.first)
+ : node_type();
}
using super_type::extract;
@@ -621,12 +620,12 @@
}
// Node extraction routines.
- // TODO(ezb): we are supposed to extract the first equivalent key if there are
- // multiple, but this isn't guaranteed to extract the first one.
template <typename K = key_type>
node_type extract(const key_arg<K> &key) {
- auto it = this->find(key);
- return it == this->end() ? node_type() : extract(it);
+ const std::pair<iterator, bool> lower_and_equal =
+ this->tree_.lower_bound_equal(key);
+ return lower_and_equal.second ? extract(lower_and_equal.first)
+ : node_type();
}
using super_type::extract;
diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc
index e4484fb..5a29bed 100644
--- a/absl/container/internal/hashtablez_sampler.cc
+++ b/absl/container/internal/hashtablez_sampler.cc
@@ -72,6 +72,7 @@
total_probe_length.store(0, std::memory_order_relaxed);
hashes_bitwise_or.store(0, std::memory_order_relaxed);
hashes_bitwise_and.store(~size_t{}, std::memory_order_relaxed);
+ hashes_bitwise_xor.store(0, std::memory_order_relaxed);
create_time = absl::Now();
// The inliner makes hardcoded skip_count difficult (especially when combined
@@ -180,7 +181,9 @@
if (ABSL_PREDICT_TRUE(state == kDontForce)) return false;
if (state == kUninitialized) {
- state = AbslContainerInternalSampleEverything() ? kForce : kDontForce;
+ state = ABSL_INTERNAL_C_SYMBOL(AbslContainerInternalSampleEverything)()
+ ? kForce
+ : kDontForce;
global_state.store(state, std::memory_order_relaxed);
}
return state == kForce;
@@ -235,6 +238,7 @@
info->hashes_bitwise_and.fetch_and(hash, std::memory_order_relaxed);
info->hashes_bitwise_or.fetch_or(hash, std::memory_order_relaxed);
+ info->hashes_bitwise_xor.fetch_xor(hash, std::memory_order_relaxed);
info->max_probe_length.store(
std::max(info->max_probe_length.load(std::memory_order_relaxed),
probe_length),
diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h
index 394348d..85685f7 100644
--- a/absl/container/internal/hashtablez_sampler.h
+++ b/absl/container/internal/hashtablez_sampler.h
@@ -78,6 +78,7 @@
std::atomic<size_t> total_probe_length;
std::atomic<size_t> hashes_bitwise_or;
std::atomic<size_t> hashes_bitwise_and;
+ std::atomic<size_t> hashes_bitwise_xor;
// `HashtablezSampler` maintains intrusive linked lists for all samples. See
// comments on `HashtablezSampler::all_` for details on these. `init_mu`
@@ -312,7 +313,7 @@
// initialization of static storage duration objects.
// The definition of this constant is weak, which allows us to inject a
// different value for it at link time.
-extern "C" bool AbslContainerInternalSampleEverything();
+extern "C" bool ABSL_INTERNAL_C_SYMBOL(AbslContainerInternalSampleEverything)();
} // namespace container_internal
ABSL_NAMESPACE_END
diff --git a/absl/container/internal/hashtablez_sampler_force_weak_definition.cc b/absl/container/internal/hashtablez_sampler_force_weak_definition.cc
index 78b9d36..ed35a7e 100644
--- a/absl/container/internal/hashtablez_sampler_force_weak_definition.cc
+++ b/absl/container/internal/hashtablez_sampler_force_weak_definition.cc
@@ -21,7 +21,8 @@
namespace container_internal {
// See hashtablez_sampler.h for details.
-extern "C" ABSL_ATTRIBUTE_WEAK bool AbslContainerInternalSampleEverything() {
+extern "C" ABSL_ATTRIBUTE_WEAK bool ABSL_INTERNAL_C_SYMBOL(
+ AbslContainerInternalSampleEverything)() {
return false;
}
diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc
index 8d10a1e..5f4c83b 100644
--- a/absl/container/internal/hashtablez_sampler_test.cc
+++ b/absl/container/internal/hashtablez_sampler_test.cc
@@ -89,6 +89,7 @@
EXPECT_EQ(info.total_probe_length.load(), 0);
EXPECT_EQ(info.hashes_bitwise_or.load(), 0);
EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{});
+ EXPECT_EQ(info.hashes_bitwise_xor.load(), 0);
EXPECT_GE(info.create_time, test_start);
info.capacity.store(1, std::memory_order_relaxed);
@@ -98,6 +99,7 @@
info.total_probe_length.store(1, std::memory_order_relaxed);
info.hashes_bitwise_or.store(1, std::memory_order_relaxed);
info.hashes_bitwise_and.store(1, std::memory_order_relaxed);
+ info.hashes_bitwise_xor.store(1, std::memory_order_relaxed);
info.create_time = test_start - absl::Hours(20);
info.PrepareForSampling();
@@ -109,6 +111,7 @@
EXPECT_EQ(info.total_probe_length.load(), 0);
EXPECT_EQ(info.hashes_bitwise_or.load(), 0);
EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{});
+ EXPECT_EQ(info.hashes_bitwise_xor.load(), 0);
EXPECT_GE(info.create_time, test_start);
}
@@ -133,14 +136,17 @@
EXPECT_EQ(info.max_probe_length.load(), 6);
EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000FF00);
EXPECT_EQ(info.hashes_bitwise_or.load(), 0x0000FF00);
+ EXPECT_EQ(info.hashes_bitwise_xor.load(), 0x0000FF00);
RecordInsertSlow(&info, 0x000FF000, 4 * kProbeLength);
EXPECT_EQ(info.max_probe_length.load(), 6);
EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000F000);
EXPECT_EQ(info.hashes_bitwise_or.load(), 0x000FFF00);
+ EXPECT_EQ(info.hashes_bitwise_xor.load(), 0x000F0F00);
RecordInsertSlow(&info, 0x00FF0000, 12 * kProbeLength);
EXPECT_EQ(info.max_probe_length.load(), 12);
EXPECT_EQ(info.hashes_bitwise_and.load(), 0x00000000);
EXPECT_EQ(info.hashes_bitwise_or.load(), 0x00FFFF00);
+ EXPECT_EQ(info.hashes_bitwise_xor.load(), 0x00F00F00);
}
TEST(HashtablezInfoTest, RecordErase) {
diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h
index 4d80b72..b8aec45 100644
--- a/absl/container/internal/inlined_vector.h
+++ b/absl/container/internal/inlined_vector.h
@@ -33,6 +33,12 @@
ABSL_NAMESPACE_BEGIN
namespace inlined_vector_internal {
+// GCC does not deal very well with the below code
+#if !defined(__clang__) && defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+
template <typename Iterator>
using IsAtLeastForwardIterator = std::is_convertible<
typename std::iterator_traits<Iterator>::iterator_category,
@@ -75,6 +81,23 @@
}
}
+// If kUseMemcpy is true, memcpy(dst, src, n); else do nothing.
+// Useful to avoid compiler warnings when memcpy() is used for T values
+// that are not trivially copyable in non-reachable code.
+template <bool kUseMemcpy>
+inline void MemcpyIfAllowed(void* dst, const void* src, size_t n);
+
+// memcpy when allowed.
+template <>
+inline void MemcpyIfAllowed<true>(void* dst, const void* src, size_t n) {
+ memcpy(dst, src, n);
+}
+
+// Do nothing for types that are not memcpy-able. This function is only
+// called from non-reachable branches.
+template <>
+inline void MemcpyIfAllowed<false>(void*, const void*, size_t) {}
+
template <typename AllocatorType, typename Pointer, typename ValueAdapter,
typename SizeType>
void ConstructElements(AllocatorType* alloc_ptr, Pointer construct_first,
@@ -298,14 +321,20 @@
// Storage Constructors and Destructor
// ---------------------------------------------------------------------------
- Storage() : metadata_() {}
+ Storage() : metadata_(allocator_type(), /* size and is_allocated */ 0) {}
- explicit Storage(const allocator_type& alloc) : metadata_(alloc, {}) {}
+ explicit Storage(const allocator_type& alloc)
+ : metadata_(alloc, /* size and is_allocated */ 0) {}
~Storage() {
- pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData();
- inlined_vector_internal::DestroyElements(GetAllocPtr(), data, GetSize());
- DeallocateIfAllocated();
+ if (GetSizeAndIsAllocated() == 0) {
+ // Empty and not allocated; nothing to do.
+ } else if (IsMemcpyOk::value) {
+ // No destructors need to be run; just deallocate if necessary.
+ DeallocateIfAllocated();
+ } else {
+ DestroyContents();
+ }
}
// ---------------------------------------------------------------------------
@@ -363,6 +392,8 @@
// Storage Member Mutators
// ---------------------------------------------------------------------------
+ ABSL_ATTRIBUTE_NOINLINE void InitFrom(const Storage& other);
+
template <typename ValueAdapter>
void Initialize(ValueAdapter values, size_type new_size);
@@ -445,6 +476,8 @@
}
private:
+ ABSL_ATTRIBUTE_NOINLINE void DestroyContents();
+
using Metadata =
container_internal::CompressedTuple<allocator_type, size_type>;
@@ -462,11 +495,48 @@
Inlined inlined;
};
+ template <typename... Args>
+ ABSL_ATTRIBUTE_NOINLINE reference EmplaceBackSlow(Args&&... args);
+
Metadata metadata_;
Data data_;
};
template <typename T, size_t N, typename A>
+void Storage<T, N, A>::DestroyContents() {
+ pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData();
+ inlined_vector_internal::DestroyElements(GetAllocPtr(), data, GetSize());
+ DeallocateIfAllocated();
+}
+
+template <typename T, size_t N, typename A>
+void Storage<T, N, A>::InitFrom(const Storage& other) {
+ const auto n = other.GetSize();
+ assert(n > 0); // Empty sources handled handled in caller.
+ const_pointer src;
+ pointer dst;
+ if (!other.GetIsAllocated()) {
+ dst = GetInlinedData();
+ src = other.GetInlinedData();
+ } else {
+ // Because this is only called from the `InlinedVector` constructors, it's
+ // safe to take on the allocation with size `0`. If `ConstructElements(...)`
+ // throws, deallocation will be automatically handled by `~Storage()`.
+ size_type new_capacity = ComputeCapacity(GetInlinedCapacity(), n);
+ dst = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity);
+ SetAllocatedData(dst, new_capacity);
+ src = other.GetAllocatedData();
+ }
+ if (IsMemcpyOk::value) {
+ MemcpyIfAllowed<IsMemcpyOk::value>(dst, src, sizeof(dst[0]) * n);
+ } else {
+ auto values = IteratorValueAdapter<const_pointer>(src);
+ inlined_vector_internal::ConstructElements(GetAllocPtr(), dst, &values, n);
+ }
+ GetSizeAndIsAllocated() = other.GetSizeAndIsAllocated();
+}
+
+template <typename T, size_t N, typename A>
template <typename ValueAdapter>
auto Storage<T, N, A>::Initialize(ValueAdapter values, size_type new_size)
-> void {
@@ -542,48 +612,42 @@
template <typename ValueAdapter>
auto Storage<T, N, A>::Resize(ValueAdapter values, size_type new_size) -> void {
StorageView storage_view = MakeStorageView();
-
- IteratorValueAdapter<MoveIterator> move_values(
- MoveIterator(storage_view.data));
-
- AllocationTransaction allocation_tx(GetAllocPtr());
- ConstructionTransaction construction_tx(GetAllocPtr());
-
- absl::Span<value_type> construct_loop;
- absl::Span<value_type> move_construct_loop;
- absl::Span<value_type> destroy_loop;
-
- if (new_size > storage_view.capacity) {
+ auto* const base = storage_view.data;
+ const size_type size = storage_view.size;
+ auto* alloc = GetAllocPtr();
+ if (new_size <= size) {
+ // Destroy extra old elements.
+ inlined_vector_internal::DestroyElements(alloc, base + new_size,
+ size - new_size);
+ } else if (new_size <= storage_view.capacity) {
+ // Construct new elements in place.
+ inlined_vector_internal::ConstructElements(alloc, base + size, &values,
+ new_size - size);
+ } else {
+ // Steps:
+ // a. Allocate new backing store.
+ // b. Construct new elements in new backing store.
+ // c. Move existing elements from old backing store to now.
+ // d. Destroy all elements in old backing store.
+ // Use transactional wrappers for the first two steps so we can roll
+ // back if necessary due to exceptions.
+ AllocationTransaction allocation_tx(alloc);
size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size);
pointer new_data = allocation_tx.Allocate(new_capacity);
- construct_loop = {new_data + storage_view.size,
- new_size - storage_view.size};
- move_construct_loop = {new_data, storage_view.size};
- destroy_loop = {storage_view.data, storage_view.size};
- } else if (new_size > storage_view.size) {
- construct_loop = {storage_view.data + storage_view.size,
- new_size - storage_view.size};
- } else {
- destroy_loop = {storage_view.data + new_size, storage_view.size - new_size};
- }
- construction_tx.Construct(construct_loop.data(), &values,
- construct_loop.size());
+ ConstructionTransaction construction_tx(alloc);
+ construction_tx.Construct(new_data + size, &values, new_size - size);
- inlined_vector_internal::ConstructElements(
- GetAllocPtr(), move_construct_loop.data(), &move_values,
- move_construct_loop.size());
+ IteratorValueAdapter<MoveIterator> move_values((MoveIterator(base)));
+ inlined_vector_internal::ConstructElements(alloc, new_data, &move_values,
+ size);
- inlined_vector_internal::DestroyElements(GetAllocPtr(), destroy_loop.data(),
- destroy_loop.size());
-
- construction_tx.Commit();
- if (allocation_tx.DidAllocate()) {
+ inlined_vector_internal::DestroyElements(alloc, base, size);
+ construction_tx.Commit();
DeallocateIfAllocated();
AcquireAllocatedData(&allocation_tx);
SetIsAllocated();
}
-
SetSize(new_size);
}
@@ -684,44 +748,50 @@
template <typename... Args>
auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> reference {
StorageView storage_view = MakeStorageView();
+ const auto n = storage_view.size;
+ if (ABSL_PREDICT_TRUE(n != storage_view.capacity)) {
+ // Fast path; new element fits.
+ pointer last_ptr = storage_view.data + n;
+ AllocatorTraits::construct(*GetAllocPtr(), last_ptr,
+ std::forward<Args>(args)...);
+ AddSize(1);
+ return *last_ptr;
+ }
+ // TODO(b/173712035): Annotate with musttail attribute to prevent regression.
+ return EmplaceBackSlow(std::forward<Args>(args)...);
+}
+template <typename T, size_t N, typename A>
+template <typename... Args>
+auto Storage<T, N, A>::EmplaceBackSlow(Args&&... args) -> reference {
+ StorageView storage_view = MakeStorageView();
AllocationTransaction allocation_tx(GetAllocPtr());
-
IteratorValueAdapter<MoveIterator> move_values(
MoveIterator(storage_view.data));
-
- pointer construct_data;
- if (storage_view.size == storage_view.capacity) {
- size_type new_capacity = NextCapacity(storage_view.capacity);
- construct_data = allocation_tx.Allocate(new_capacity);
- } else {
- construct_data = storage_view.data;
- }
-
+ size_type new_capacity = NextCapacity(storage_view.capacity);
+ pointer construct_data = allocation_tx.Allocate(new_capacity);
pointer last_ptr = construct_data + storage_view.size;
+ // Construct new element.
AllocatorTraits::construct(*GetAllocPtr(), last_ptr,
std::forward<Args>(args)...);
-
- if (allocation_tx.DidAllocate()) {
- ABSL_INTERNAL_TRY {
- inlined_vector_internal::ConstructElements(
- GetAllocPtr(), allocation_tx.GetData(), &move_values,
- storage_view.size);
- }
- ABSL_INTERNAL_CATCH_ANY {
- AllocatorTraits::destroy(*GetAllocPtr(), last_ptr);
- ABSL_INTERNAL_RETHROW;
- }
-
- inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data,
- storage_view.size);
-
- DeallocateIfAllocated();
- AcquireAllocatedData(&allocation_tx);
- SetIsAllocated();
+ // Move elements from old backing store to new backing store.
+ ABSL_INTERNAL_TRY {
+ inlined_vector_internal::ConstructElements(
+ GetAllocPtr(), allocation_tx.GetData(), &move_values,
+ storage_view.size);
}
+ ABSL_INTERNAL_CATCH_ANY {
+ AllocatorTraits::destroy(*GetAllocPtr(), last_ptr);
+ ABSL_INTERNAL_RETHROW;
+ }
+ // Destroy elements in old backing store.
+ inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data,
+ storage_view.size);
+ DeallocateIfAllocated();
+ AcquireAllocatedData(&allocation_tx);
+ SetIsAllocated();
AddSize(1);
return *last_ptr;
}
@@ -885,6 +955,11 @@
swap(*GetAllocPtr(), *other_storage_ptr->GetAllocPtr());
}
+// End ignore "maybe-uninitialized"
+#if !defined(__clang__) && defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+
} // namespace inlined_vector_internal
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/layout_benchmark.cc b/absl/container/internal/layout_benchmark.cc
new file mode 100644
index 0000000..d8636e8
--- /dev/null
+++ b/absl/container/internal/layout_benchmark.cc
@@ -0,0 +1,122 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Every benchmark should have the same performance as the corresponding
+// headroom benchmark.
+
+#include "absl/base/internal/raw_logging.h"
+#include "absl/container/internal/layout.h"
+#include "benchmark/benchmark.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace container_internal {
+namespace {
+
+using ::benchmark::DoNotOptimize;
+
+using Int128 = int64_t[2];
+
+// This benchmark provides the upper bound on performance for BM_OffsetConstant.
+template <size_t Offset, class... Ts>
+void BM_OffsetConstantHeadroom(benchmark::State& state) {
+ for (auto _ : state) {
+ DoNotOptimize(Offset);
+ }
+}
+
+template <size_t Offset, class... Ts>
+void BM_OffsetConstant(benchmark::State& state) {
+ using L = Layout<Ts...>;
+ ABSL_RAW_CHECK(L::Partial(3, 5, 7).template Offset<3>() == Offset,
+ "Invalid offset");
+ for (auto _ : state) {
+ DoNotOptimize(L::Partial(3, 5, 7).template Offset<3>());
+ }
+}
+
+template <class... Ts>
+size_t VariableOffset(size_t n, size_t m, size_t k);
+
+template <>
+size_t VariableOffset<int8_t, int16_t, int32_t, Int128>(size_t n, size_t m,
+ size_t k) {
+ auto Align = [](size_t n, size_t m) { return (n + m - 1) & ~(m - 1); };
+ return Align(Align(Align(n * 1, 2) + m * 2, 4) + k * 4, 8);
+}
+
+template <>
+size_t VariableOffset<Int128, int32_t, int16_t, int8_t>(size_t n, size_t m,
+ size_t k) {
+ // No alignment is necessary.
+ return n * 16 + m * 4 + k * 2;
+}
+
+// This benchmark provides the upper bound on performance for BM_OffsetVariable.
+template <size_t Offset, class... Ts>
+void BM_OffsetVariableHeadroom(benchmark::State& state) {
+ size_t n = 3;
+ size_t m = 5;
+ size_t k = 7;
+ ABSL_RAW_CHECK(VariableOffset<Ts...>(n, m, k) == Offset, "Invalid offset");
+ for (auto _ : state) {
+ DoNotOptimize(n);
+ DoNotOptimize(m);
+ DoNotOptimize(k);
+ DoNotOptimize(VariableOffset<Ts...>(n, m, k));
+ }
+}
+
+template <size_t Offset, class... Ts>
+void BM_OffsetVariable(benchmark::State& state) {
+ using L = Layout<Ts...>;
+ size_t n = 3;
+ size_t m = 5;
+ size_t k = 7;
+ ABSL_RAW_CHECK(L::Partial(n, m, k).template Offset<3>() == Offset,
+ "Inavlid offset");
+ for (auto _ : state) {
+ DoNotOptimize(n);
+ DoNotOptimize(m);
+ DoNotOptimize(k);
+ DoNotOptimize(L::Partial(n, m, k).template Offset<3>());
+ }
+}
+
+// Run all benchmarks in two modes:
+//
+// Layout with padding: int8_t[3], int16_t[5], int32_t[7], Int128[?].
+// Layout without padding: Int128[3], int32_t[5], int16_t[7], int8_t[?].
+
+#define OFFSET_BENCHMARK(NAME, OFFSET, T1, T2, T3, T4) \
+ auto& NAME##_##OFFSET##_##T1##_##T2##_##T3##_##T4 = \
+ NAME<OFFSET, T1, T2, T3, T4>; \
+ BENCHMARK(NAME##_##OFFSET##_##T1##_##T2##_##T3##_##T4)
+
+OFFSET_BENCHMARK(BM_OffsetConstantHeadroom, 48, int8_t, int16_t, int32_t,
+ Int128);
+OFFSET_BENCHMARK(BM_OffsetConstant, 48, int8_t, int16_t, int32_t, Int128);
+OFFSET_BENCHMARK(BM_OffsetConstantHeadroom, 82, Int128, int32_t, int16_t,
+ int8_t);
+OFFSET_BENCHMARK(BM_OffsetConstant, 82, Int128, int32_t, int16_t, int8_t);
+OFFSET_BENCHMARK(BM_OffsetVariableHeadroom, 48, int8_t, int16_t, int32_t,
+ Int128);
+OFFSET_BENCHMARK(BM_OffsetVariable, 48, int8_t, int16_t, int32_t, Int128);
+OFFSET_BENCHMARK(BM_OffsetVariableHeadroom, 82, Int128, int32_t, int16_t,
+ int8_t);
+OFFSET_BENCHMARK(BM_OffsetVariable, 82, Int128, int32_t, int16_t, int8_t);
+} // namespace
+} // namespace container_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc
index 2f744a6..bfef071 100644
--- a/absl/container/internal/raw_hash_set.cc
+++ b/absl/container/internal/raw_hash_set.cc
@@ -43,6 +43,19 @@
return (H1(hash, ctrl) ^ RandomSeed()) % 13 > 6;
}
+void ConvertDeletedToEmptyAndFullToDeleted(
+ ctrl_t* ctrl, size_t capacity) {
+ assert(ctrl[capacity] == kSentinel);
+ assert(IsValidCapacity(capacity));
+ for (ctrl_t* pos = ctrl; pos != ctrl + capacity + 1; pos += Group::kWidth) {
+ Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos);
+ }
+ // Copy the cloned ctrl bytes.
+ std::memcpy(ctrl + capacity + 1, ctrl, Group::kWidth);
+ ctrl[capacity] = kSentinel;
+}
+
+
} // namespace container_internal
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h
index 67364b7..80fc2cb 100644
--- a/absl/container/internal/raw_hash_set.h
+++ b/absl/container/internal/raw_hash_set.h
@@ -102,7 +102,6 @@
#include <type_traits>
#include <utility>
-#include "absl/base/internal/bits.h"
#include "absl/base/internal/endian.h"
#include "absl/base/optimization.h"
#include "absl/base/port.h"
@@ -116,6 +115,7 @@
#include "absl/container/internal/layout.h"
#include "absl/memory/memory.h"
#include "absl/meta/type_traits.h"
+#include "absl/numeric/bits.h"
#include "absl/utility/utility.h"
namespace absl {
@@ -189,18 +189,9 @@
}
template <typename T>
-int TrailingZeros(T x) {
- return sizeof(T) == 8 ? base_internal::CountTrailingZerosNonZero64(
- static_cast<uint64_t>(x))
- : base_internal::CountTrailingZerosNonZero32(
- static_cast<uint32_t>(x));
-}
-
-template <typename T>
-int LeadingZeros(T x) {
- return sizeof(T) == 8
- ? base_internal::CountLeadingZeros64(static_cast<uint64_t>(x))
- : base_internal::CountLeadingZeros32(static_cast<uint32_t>(x));
+uint32_t TrailingZeros(T x) {
+ ABSL_INTERNAL_ASSUME(x != 0);
+ return countr_zero(x);
}
// An abstraction over a bitmask. It provides an easy way to iterate through the
@@ -230,26 +221,24 @@
}
explicit operator bool() const { return mask_ != 0; }
int operator*() const { return LowestBitSet(); }
- int LowestBitSet() const {
+ uint32_t LowestBitSet() const {
return container_internal::TrailingZeros(mask_) >> Shift;
}
- int HighestBitSet() const {
- return (sizeof(T) * CHAR_BIT - container_internal::LeadingZeros(mask_) -
- 1) >>
- Shift;
+ uint32_t HighestBitSet() const {
+ return static_cast<uint32_t>((bit_width(mask_) - 1) >> Shift);
}
BitMask begin() const { return *this; }
BitMask end() const { return BitMask(0); }
- int TrailingZeros() const {
+ uint32_t TrailingZeros() const {
return container_internal::TrailingZeros(mask_) >> Shift;
}
- int LeadingZeros() const {
+ uint32_t LeadingZeros() const {
constexpr int total_significant_bits = SignificantBits << Shift;
constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits;
- return container_internal::LeadingZeros(mask_ << extra_bits) >> Shift;
+ return countl_zero(mask_ << extra_bits) >> Shift;
}
private:
@@ -380,8 +369,8 @@
// Returns the number of trailing empty or deleted elements in the group.
uint32_t CountLeadingEmptyOrDeleted() const {
auto special = _mm_set1_epi8(kSentinel);
- return TrailingZeros(
- _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1);
+ return TrailingZeros(static_cast<uint32_t>(
+ _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1));
}
void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const {
@@ -472,25 +461,23 @@
// DELETED -> EMPTY
// EMPTY -> EMPTY
// FULL -> DELETED
-inline void ConvertDeletedToEmptyAndFullToDeleted(
- ctrl_t* ctrl, size_t capacity) {
- assert(ctrl[capacity] == kSentinel);
- assert(IsValidCapacity(capacity));
- for (ctrl_t* pos = ctrl; pos != ctrl + capacity + 1; pos += Group::kWidth) {
- Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos);
- }
- // Copy the cloned ctrl bytes.
- std::memcpy(ctrl + capacity + 1, ctrl, Group::kWidth);
- ctrl[capacity] = kSentinel;
-}
+void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity);
// Rounds up the capacity to the next power of 2 minus 1, with a minimum of 1.
inline size_t NormalizeCapacity(size_t n) {
- return n ? ~size_t{} >> LeadingZeros(n) : 1;
+ return n ? ~size_t{} >> countl_zero(n) : 1;
}
-// We use 7/8th as maximum load factor.
-// For 16-wide groups, that gives an average of two empty slots per group.
+// General notes on capacity/growth methods below:
+// - We use 7/8th as maximum load factor. For 16-wide groups, that gives an
+// average of two empty slots per group.
+// - For (capacity+1) >= Group::kWidth, growth is 7/8*capacity.
+// - For (capacity+1) < Group::kWidth, growth == capacity. In this case, we
+// never need to probe (the whole table fits in one group) so we don't need a
+// load factor less than 1.
+
+// Given `capacity` of the table, returns the size (i.e. number of full slots)
+// at which we should grow the capacity.
inline size_t CapacityToGrowth(size_t capacity) {
assert(IsValidCapacity(capacity));
// `capacity*7/8`
@@ -501,7 +488,7 @@
return capacity - capacity / 8;
}
// From desired "growth" to a lowerbound of the necessary capacity.
-// Might not be a valid one and required NormalizeCapacity().
+// Might not be a valid one and requires NormalizeCapacity().
inline size_t GrowthToLowerboundCapacity(size_t growth) {
// `growth*8/7`
if (Group::kWidth == 8 && growth == 7) {
@@ -523,6 +510,64 @@
"been erased, or the table might have rehashed.");
}
+struct FindInfo {
+ size_t offset;
+ size_t probe_length;
+};
+
+// The representation of the object has two modes:
+// - small: For capacities < kWidth-1
+// - large: For the rest.
+//
+// Differences:
+// - In small mode we are able to use the whole capacity. The extra control
+// bytes give us at least one "empty" control byte to stop the iteration.
+// This is important to make 1 a valid capacity.
+//
+// - In small mode only the first `capacity()` control bytes after the
+// sentinel are valid. The rest contain dummy kEmpty values that do not
+// represent a real slot. This is important to take into account on
+// find_first_non_full(), where we never try ShouldInsertBackwards() for
+// small tables.
+inline bool is_small(size_t capacity) { return capacity < Group::kWidth - 1; }
+
+inline probe_seq<Group::kWidth> probe(ctrl_t* ctrl, size_t hash,
+ size_t capacity) {
+ return probe_seq<Group::kWidth>(H1(hash, ctrl), capacity);
+}
+
+// Probes the raw_hash_set with the probe sequence for hash and returns the
+// pointer to the first empty or deleted slot.
+// NOTE: this function must work with tables having both kEmpty and kDelete
+// in one group. Such tables appears during drop_deletes_without_resize.
+//
+// This function is very useful when insertions happen and:
+// - the input is already a set
+// - there are enough slots
+// - the element with the hash is not in the table
+inline FindInfo find_first_non_full(ctrl_t* ctrl, size_t hash,
+ size_t capacity) {
+ auto seq = probe(ctrl, hash, capacity);
+ while (true) {
+ Group g{ctrl + seq.offset()};
+ auto mask = g.MatchEmptyOrDeleted();
+ if (mask) {
+#if !defined(NDEBUG)
+ // We want to add entropy even when ASLR is not enabled.
+ // In debug build we will randomly insert in either the front or back of
+ // the group.
+ // TODO(kfm,sbenza): revisit after we do unconditional mixing
+ if (!is_small(capacity) && ShouldInsertBackwards(hash, ctrl)) {
+ return {seq.offset(mask.HighestBitSet()), seq.index()};
+ }
+#endif
+ return {seq.offset(mask.LowestBitSet()), seq.index()};
+ }
+ seq.next();
+ assert(seq.index() < capacity && "full table!");
+ }
+}
+
// Policy: a policy defines how to perform different operations on
// the slots of the hashtable (see hash_policy_traits.h for the full interface
// of policy).
@@ -750,7 +795,6 @@
: ctrl_(EmptyGroup()), settings_(0, hash, eq, alloc) {
if (bucket_count) {
capacity_ = NormalizeCapacity(bucket_count);
- reset_growth_left();
initialize_slots();
}
}
@@ -856,7 +900,7 @@
// than a full `insert`.
for (const auto& v : that) {
const size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, v);
- auto target = find_first_non_full(hash);
+ auto target = find_first_non_full(ctrl_, hash, capacity_);
set_ctrl(target.offset, H2(hash));
emplace_at(target.offset, v);
infoz_.RecordInsert(hash, target.probe_length);
@@ -1038,7 +1082,7 @@
template <class InputIt>
void insert(InputIt first, InputIt last) {
- for (; first != last; ++first) insert(*first);
+ for (; first != last; ++first) emplace(*first);
}
template <class T, RequiresNotInit<T> = 0, RequiresInsertable<const T&> = 0>
@@ -1278,7 +1322,12 @@
}
}
- void reserve(size_t n) { rehash(GrowthToLowerboundCapacity(n)); }
+ void reserve(size_t n) {
+ size_t m = GrowthToLowerboundCapacity(n);
+ if (m > capacity_) {
+ resize(NormalizeCapacity(m));
+ }
+ }
// Extension API: support for heterogeneous keys.
//
@@ -1303,7 +1352,7 @@
void prefetch(const key_arg<K>& key) const {
(void)key;
#if defined(__GNUC__)
- auto seq = probe(hash_ref()(key));
+ auto seq = probe(ctrl_, hash_ref()(key), capacity_);
__builtin_prefetch(static_cast<const void*>(ctrl_ + seq.offset()));
__builtin_prefetch(static_cast<const void*>(slots_ + seq.offset()));
#endif // __GNUC__
@@ -1318,7 +1367,7 @@
// called heterogeneous key support.
template <class K = key_type>
iterator find(const key_arg<K>& key, size_t hash) {
- auto seq = probe(hash);
+ auto seq = probe(ctrl_, hash, capacity_);
while (true) {
Group g{ctrl_ + seq.offset()};
for (int i : g.Match(H2(hash))) {
@@ -1540,7 +1589,7 @@
if (IsFull(old_ctrl[i])) {
size_t hash = PolicyTraits::apply(HashElement{hash_ref()},
PolicyTraits::element(old_slots + i));
- auto target = find_first_non_full(hash);
+ auto target = find_first_non_full(ctrl_, hash, capacity_);
size_t new_i = target.offset;
total_probe_length += target.probe_length;
set_ctrl(new_i, H2(hash));
@@ -1559,7 +1608,7 @@
void drop_deletes_without_resize() ABSL_ATTRIBUTE_NOINLINE {
assert(IsValidCapacity(capacity_));
- assert(!is_small());
+ assert(!is_small(capacity_));
// Algorithm:
// - mark all DELETED slots as EMPTY
// - mark all FULL slots as DELETED
@@ -1584,7 +1633,7 @@
if (!IsDeleted(ctrl_[i])) continue;
size_t hash = PolicyTraits::apply(HashElement{hash_ref()},
PolicyTraits::element(slots_ + i));
- auto target = find_first_non_full(hash);
+ auto target = find_first_non_full(ctrl_, hash, capacity_);
size_t new_i = target.offset;
total_probe_length += target.probe_length;
@@ -1592,7 +1641,8 @@
// If they do, we don't need to move the object as it falls already in the
// best probe we can.
const auto probe_index = [&](size_t pos) {
- return ((pos - probe(hash).offset()) & capacity_) / Group::kWidth;
+ return ((pos - probe(ctrl_, hash, capacity_).offset()) & capacity_) /
+ Group::kWidth;
};
// Element doesn't move.
@@ -1636,7 +1686,7 @@
bool has_element(const value_type& elem) const {
size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, elem);
- auto seq = probe(hash);
+ auto seq = probe(ctrl_, hash, capacity_);
while (true) {
Group g{ctrl_ + seq.offset()};
for (int i : g.Match(H2(hash))) {
@@ -1651,41 +1701,6 @@
return false;
}
- // Probes the raw_hash_set with the probe sequence for hash and returns the
- // pointer to the first empty or deleted slot.
- // NOTE: this function must work with tables having both kEmpty and kDelete
- // in one group. Such tables appears during drop_deletes_without_resize.
- //
- // This function is very useful when insertions happen and:
- // - the input is already a set
- // - there are enough slots
- // - the element with the hash is not in the table
- struct FindInfo {
- size_t offset;
- size_t probe_length;
- };
- FindInfo find_first_non_full(size_t hash) {
- auto seq = probe(hash);
- while (true) {
- Group g{ctrl_ + seq.offset()};
- auto mask = g.MatchEmptyOrDeleted();
- if (mask) {
-#if !defined(NDEBUG)
- // We want to add entropy even when ASLR is not enabled.
- // In debug build we will randomly insert in either the front or back of
- // the group.
- // TODO(kfm,sbenza): revisit after we do unconditional mixing
- if (!is_small() && ShouldInsertBackwards(hash, ctrl_)) {
- return {seq.offset(mask.HighestBitSet()), seq.index()};
- }
-#endif
- return {seq.offset(mask.LowestBitSet()), seq.index()};
- }
- seq.next();
- assert(seq.index() < capacity_ && "full table!");
- }
- }
-
// TODO(alkis): Optimize this assuming *this and that don't overlap.
raw_hash_set& move_assign(raw_hash_set&& that, std::true_type) {
raw_hash_set tmp(std::move(that));
@@ -1702,7 +1717,7 @@
template <class K>
std::pair<size_t, bool> find_or_prepare_insert(const K& key) {
auto hash = hash_ref()(key);
- auto seq = probe(hash);
+ auto seq = probe(ctrl_, hash, capacity_);
while (true) {
Group g{ctrl_ + seq.offset()};
for (int i : g.Match(H2(hash))) {
@@ -1719,11 +1734,11 @@
}
size_t prepare_insert(size_t hash) ABSL_ATTRIBUTE_NOINLINE {
- auto target = find_first_non_full(hash);
+ auto target = find_first_non_full(ctrl_, hash, capacity_);
if (ABSL_PREDICT_FALSE(growth_left() == 0 &&
!IsDeleted(ctrl_[target.offset]))) {
rehash_and_grow_if_necessary();
- target = find_first_non_full(hash);
+ target = find_first_non_full(ctrl_, hash, capacity_);
}
++size_;
growth_left() -= IsEmpty(ctrl_[target.offset]);
@@ -1756,10 +1771,6 @@
private:
friend struct RawHashSetTestOnlyAccess;
- probe_seq<Group::kWidth> probe(size_t hash) const {
- return probe_seq<Group::kWidth>(H1(hash, ctrl_), capacity_);
- }
-
// Reset all ctrl bytes back to kEmpty, except the sentinel.
void reset_ctrl() {
std::memset(ctrl_, kEmpty, capacity_ + Group::kWidth);
@@ -1789,22 +1800,6 @@
size_t& growth_left() { return settings_.template get<0>(); }
- // The representation of the object has two modes:
- // - small: For capacities < kWidth-1
- // - large: For the rest.
- //
- // Differences:
- // - In small mode we are able to use the whole capacity. The extra control
- // bytes give us at least one "empty" control byte to stop the iteration.
- // This is important to make 1 a valid capacity.
- //
- // - In small mode only the first `capacity()` control bytes after the
- // sentinel are valid. The rest contain dummy kEmpty values that do not
- // represent a real slot. This is important to take into account on
- // find_first_non_full(), where we never try ShouldInsertBackwards() for
- // small tables.
- bool is_small() const { return capacity_ < Group::kWidth - 1; }
-
hasher& hash_ref() { return settings_.template get<1>(); }
const hasher& hash_ref() const { return settings_.template get<1>(); }
key_equal& eq_ref() { return settings_.template get<2>(); }
@@ -1848,7 +1843,7 @@
const typename Set::key_type& key) {
size_t num_probes = 0;
size_t hash = set.hash_ref()(key);
- auto seq = set.probe(hash);
+ auto seq = probe(set.ctrl_, hash, set.capacity_);
while (true) {
container_internal::Group g{set.ctrl_ + seq.offset()};
for (int i : g.Match(container_internal::H2(hash))) {
diff --git a/absl/container/internal/raw_hash_set_benchmark.cc b/absl/container/internal/raw_hash_set_benchmark.cc
new file mode 100644
index 0000000..f9be2c5
--- /dev/null
+++ b/absl/container/internal/raw_hash_set_benchmark.cc
@@ -0,0 +1,396 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/container/internal/raw_hash_set.h"
+
+#include <numeric>
+#include <random>
+
+#include "absl/base/internal/raw_logging.h"
+#include "absl/container/internal/hash_function_defaults.h"
+#include "absl/strings/str_format.h"
+#include "benchmark/benchmark.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace container_internal {
+
+struct RawHashSetTestOnlyAccess {
+ template <typename C>
+ static auto GetSlots(const C& c) -> decltype(c.slots_) {
+ return c.slots_;
+ }
+};
+
+namespace {
+
+struct IntPolicy {
+ using slot_type = int64_t;
+ using key_type = int64_t;
+ using init_type = int64_t;
+
+ static void construct(void*, int64_t* slot, int64_t v) { *slot = v; }
+ static void destroy(void*, int64_t*) {}
+ static void transfer(void*, int64_t* new_slot, int64_t* old_slot) {
+ *new_slot = *old_slot;
+ }
+
+ static int64_t& element(slot_type* slot) { return *slot; }
+
+ template <class F>
+ static auto apply(F&& f, int64_t x) -> decltype(std::forward<F>(f)(x, x)) {
+ return std::forward<F>(f)(x, x);
+ }
+};
+
+class StringPolicy {
+ template <class F, class K, class V,
+ class = typename std::enable_if<
+ std::is_convertible<const K&, absl::string_view>::value>::type>
+ decltype(std::declval<F>()(
+ std::declval<const absl::string_view&>(), std::piecewise_construct,
+ std::declval<std::tuple<K>>(),
+ std::declval<V>())) static apply_impl(F&& f,
+ std::pair<std::tuple<K>, V> p) {
+ const absl::string_view& key = std::get<0>(p.first);
+ return std::forward<F>(f)(key, std::piecewise_construct, std::move(p.first),
+ std::move(p.second));
+ }
+
+ public:
+ struct slot_type {
+ struct ctor {};
+
+ template <class... Ts>
+ slot_type(ctor, Ts&&... ts) : pair(std::forward<Ts>(ts)...) {}
+
+ std::pair<std::string, std::string> pair;
+ };
+
+ using key_type = std::string;
+ using init_type = std::pair<std::string, std::string>;
+
+ template <class allocator_type, class... Args>
+ static void construct(allocator_type* alloc, slot_type* slot, Args... args) {
+ std::allocator_traits<allocator_type>::construct(
+ *alloc, slot, typename slot_type::ctor(), std::forward<Args>(args)...);
+ }
+
+ template <class allocator_type>
+ static void destroy(allocator_type* alloc, slot_type* slot) {
+ std::allocator_traits<allocator_type>::destroy(*alloc, slot);
+ }
+
+ template <class allocator_type>
+ static void transfer(allocator_type* alloc, slot_type* new_slot,
+ slot_type* old_slot) {
+ construct(alloc, new_slot, std::move(old_slot->pair));
+ destroy(alloc, old_slot);
+ }
+
+ static std::pair<std::string, std::string>& element(slot_type* slot) {
+ return slot->pair;
+ }
+
+ template <class F, class... Args>
+ static auto apply(F&& f, Args&&... args)
+ -> decltype(apply_impl(std::forward<F>(f),
+ PairArgs(std::forward<Args>(args)...))) {
+ return apply_impl(std::forward<F>(f),
+ PairArgs(std::forward<Args>(args)...));
+ }
+};
+
+struct StringHash : container_internal::hash_default_hash<absl::string_view> {
+ using is_transparent = void;
+};
+struct StringEq : std::equal_to<absl::string_view> {
+ using is_transparent = void;
+};
+
+struct StringTable
+ : raw_hash_set<StringPolicy, StringHash, StringEq, std::allocator<int>> {
+ using Base = typename StringTable::raw_hash_set;
+ StringTable() {}
+ using Base::Base;
+};
+
+struct IntTable
+ : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
+ std::equal_to<int64_t>, std::allocator<int64_t>> {
+ using Base = typename IntTable::raw_hash_set;
+ IntTable() {}
+ using Base::Base;
+};
+
+struct string_generator {
+ template <class RNG>
+ std::string operator()(RNG& rng) const {
+ std::string res;
+ res.resize(12);
+ std::uniform_int_distribution<uint32_t> printable_ascii(0x20, 0x7E);
+ std::generate(res.begin(), res.end(), [&] { return printable_ascii(rng); });
+ return res;
+ }
+
+ size_t size;
+};
+
+// Model a cache in steady state.
+//
+// On a table of size N, keep deleting the LRU entry and add a random one.
+void BM_CacheInSteadyState(benchmark::State& state) {
+ std::random_device rd;
+ std::mt19937 rng(rd());
+ string_generator gen{12};
+ StringTable t;
+ std::deque<std::string> keys;
+ while (t.size() < state.range(0)) {
+ auto x = t.emplace(gen(rng), gen(rng));
+ if (x.second) keys.push_back(x.first->first);
+ }
+ ABSL_RAW_CHECK(state.range(0) >= 10, "");
+ while (state.KeepRunning()) {
+ // Some cache hits.
+ std::deque<std::string>::const_iterator it;
+ for (int i = 0; i != 90; ++i) {
+ if (i % 10 == 0) it = keys.end();
+ ::benchmark::DoNotOptimize(t.find(*--it));
+ }
+ // Some cache misses.
+ for (int i = 0; i != 10; ++i) ::benchmark::DoNotOptimize(t.find(gen(rng)));
+ ABSL_RAW_CHECK(t.erase(keys.front()), keys.front().c_str());
+ keys.pop_front();
+ while (true) {
+ auto x = t.emplace(gen(rng), gen(rng));
+ if (x.second) {
+ keys.push_back(x.first->first);
+ break;
+ }
+ }
+ }
+ state.SetItemsProcessed(state.iterations());
+ state.SetLabel(absl::StrFormat("load_factor=%.2f", t.load_factor()));
+}
+
+template <typename Benchmark>
+void CacheInSteadyStateArgs(Benchmark* bm) {
+ // The default.
+ const float max_load_factor = 0.875;
+ // When the cache is at the steady state, the probe sequence will equal
+ // capacity if there is no reclamation of deleted slots. Pick a number large
+ // enough to make the benchmark slow for that case.
+ const size_t capacity = 1 << 10;
+
+ // Check N data points to cover load factors in [0.4, 0.8).
+ const size_t kNumPoints = 10;
+ for (size_t i = 0; i != kNumPoints; ++i)
+ bm->Arg(std::ceil(
+ capacity * (max_load_factor + i * max_load_factor / kNumPoints) / 2));
+}
+BENCHMARK(BM_CacheInSteadyState)->Apply(CacheInSteadyStateArgs);
+
+void BM_EndComparison(benchmark::State& state) {
+ std::random_device rd;
+ std::mt19937 rng(rd());
+ string_generator gen{12};
+ StringTable t;
+ while (t.size() < state.range(0)) {
+ t.emplace(gen(rng), gen(rng));
+ }
+
+ for (auto _ : state) {
+ for (auto it = t.begin(); it != t.end(); ++it) {
+ benchmark::DoNotOptimize(it);
+ benchmark::DoNotOptimize(t);
+ benchmark::DoNotOptimize(it != t.end());
+ }
+ }
+}
+BENCHMARK(BM_EndComparison)->Arg(400);
+
+void BM_CopyCtor(benchmark::State& state) {
+ std::random_device rd;
+ std::mt19937 rng(rd());
+ IntTable t;
+ std::uniform_int_distribution<uint64_t> dist(0, ~uint64_t{});
+
+ while (t.size() < state.range(0)) {
+ t.emplace(dist(rng));
+ }
+
+ for (auto _ : state) {
+ IntTable t2 = t;
+ benchmark::DoNotOptimize(t2);
+ }
+}
+BENCHMARK(BM_CopyCtor)->Range(128, 4096);
+
+void BM_CopyAssign(benchmark::State& state) {
+ std::random_device rd;
+ std::mt19937 rng(rd());
+ IntTable t;
+ std::uniform_int_distribution<uint64_t> dist(0, ~uint64_t{});
+ while (t.size() < state.range(0)) {
+ t.emplace(dist(rng));
+ }
+
+ IntTable t2;
+ for (auto _ : state) {
+ t2 = t;
+ benchmark::DoNotOptimize(t2);
+ }
+}
+BENCHMARK(BM_CopyAssign)->Range(128, 4096);
+
+void BM_NoOpReserveIntTable(benchmark::State& state) {
+ IntTable t;
+ t.reserve(100000);
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(t);
+ t.reserve(100000);
+ }
+}
+BENCHMARK(BM_NoOpReserveIntTable);
+
+void BM_NoOpReserveStringTable(benchmark::State& state) {
+ StringTable t;
+ t.reserve(100000);
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(t);
+ t.reserve(100000);
+ }
+}
+BENCHMARK(BM_NoOpReserveStringTable);
+
+void BM_ReserveIntTable(benchmark::State& state) {
+ int reserve_size = state.range(0);
+ for (auto _ : state) {
+ state.PauseTiming();
+ IntTable t;
+ state.ResumeTiming();
+ benchmark::DoNotOptimize(t);
+ t.reserve(reserve_size);
+ }
+}
+BENCHMARK(BM_ReserveIntTable)->Range(128, 4096);
+
+void BM_ReserveStringTable(benchmark::State& state) {
+ int reserve_size = state.range(0);
+ for (auto _ : state) {
+ state.PauseTiming();
+ StringTable t;
+ state.ResumeTiming();
+ benchmark::DoNotOptimize(t);
+ t.reserve(reserve_size);
+ }
+}
+BENCHMARK(BM_ReserveStringTable)->Range(128, 4096);
+
+void BM_Group_Match(benchmark::State& state) {
+ std::array<ctrl_t, Group::kWidth> group;
+ std::iota(group.begin(), group.end(), -4);
+ Group g{group.data()};
+ h2_t h = 1;
+ for (auto _ : state) {
+ ::benchmark::DoNotOptimize(h);
+ ::benchmark::DoNotOptimize(g.Match(h));
+ }
+}
+BENCHMARK(BM_Group_Match);
+
+void BM_Group_MatchEmpty(benchmark::State& state) {
+ std::array<ctrl_t, Group::kWidth> group;
+ std::iota(group.begin(), group.end(), -4);
+ Group g{group.data()};
+ for (auto _ : state) ::benchmark::DoNotOptimize(g.MatchEmpty());
+}
+BENCHMARK(BM_Group_MatchEmpty);
+
+void BM_Group_MatchEmptyOrDeleted(benchmark::State& state) {
+ std::array<ctrl_t, Group::kWidth> group;
+ std::iota(group.begin(), group.end(), -4);
+ Group g{group.data()};
+ for (auto _ : state) ::benchmark::DoNotOptimize(g.MatchEmptyOrDeleted());
+}
+BENCHMARK(BM_Group_MatchEmptyOrDeleted);
+
+void BM_Group_CountLeadingEmptyOrDeleted(benchmark::State& state) {
+ std::array<ctrl_t, Group::kWidth> group;
+ std::iota(group.begin(), group.end(), -2);
+ Group g{group.data()};
+ for (auto _ : state)
+ ::benchmark::DoNotOptimize(g.CountLeadingEmptyOrDeleted());
+}
+BENCHMARK(BM_Group_CountLeadingEmptyOrDeleted);
+
+void BM_Group_MatchFirstEmptyOrDeleted(benchmark::State& state) {
+ std::array<ctrl_t, Group::kWidth> group;
+ std::iota(group.begin(), group.end(), -2);
+ Group g{group.data()};
+ for (auto _ : state) ::benchmark::DoNotOptimize(*g.MatchEmptyOrDeleted());
+}
+BENCHMARK(BM_Group_MatchFirstEmptyOrDeleted);
+
+void BM_DropDeletes(benchmark::State& state) {
+ constexpr size_t capacity = (1 << 20) - 1;
+ std::vector<ctrl_t> ctrl(capacity + 1 + Group::kWidth);
+ ctrl[capacity] = kSentinel;
+ std::vector<ctrl_t> pattern = {kEmpty, 2, kDeleted, 2, kEmpty, 1, kDeleted};
+ for (size_t i = 0; i != capacity; ++i) {
+ ctrl[i] = pattern[i % pattern.size()];
+ }
+ while (state.KeepRunning()) {
+ state.PauseTiming();
+ std::vector<ctrl_t> ctrl_copy = ctrl;
+ state.ResumeTiming();
+ ConvertDeletedToEmptyAndFullToDeleted(ctrl_copy.data(), capacity);
+ ::benchmark::DoNotOptimize(ctrl_copy[capacity]);
+ }
+}
+BENCHMARK(BM_DropDeletes);
+
+} // namespace
+} // namespace container_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+// These methods are here to make it easy to examine the assembly for targeted
+// parts of the API.
+auto CodegenAbslRawHashSetInt64Find(absl::container_internal::IntTable* table,
+ int64_t key) -> decltype(table->find(key)) {
+ return table->find(key);
+}
+
+bool CodegenAbslRawHashSetInt64FindNeEnd(
+ absl::container_internal::IntTable* table, int64_t key) {
+ return table->find(key) != table->end();
+}
+
+bool CodegenAbslRawHashSetInt64Contains(
+ absl::container_internal::IntTable* table, int64_t key) {
+ return table->contains(key);
+}
+
+void CodegenAbslRawHashSetInt64Iterate(
+ absl::container_internal::IntTable* table) {
+ for (auto x : *table) benchmark::DoNotOptimize(x);
+}
+
+int odr =
+ (::benchmark::DoNotOptimize(std::make_tuple(
+ &CodegenAbslRawHashSetInt64Find, &CodegenAbslRawHashSetInt64FindNeEnd,
+ &CodegenAbslRawHashSetInt64Contains,
+ &CodegenAbslRawHashSetInt64Iterate)),
+ 1);
diff --git a/absl/container/internal/raw_hash_set_probe_benchmark.cc b/absl/container/internal/raw_hash_set_probe_benchmark.cc
new file mode 100644
index 0000000..7169a2e
--- /dev/null
+++ b/absl/container/internal/raw_hash_set_probe_benchmark.cc
@@ -0,0 +1,590 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Generates probe length statistics for many combinations of key types and key
+// distributions, all using the default hash function for swisstable.
+
+#include <memory>
+#include <regex> // NOLINT
+#include <vector>
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/container/internal/hash_function_defaults.h"
+#include "absl/container/internal/hashtable_debug.h"
+#include "absl/container/internal/raw_hash_set.h"
+#include "absl/random/distributions.h"
+#include "absl/random/random.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
+#include "absl/strings/strip.h"
+
+namespace {
+
+enum class OutputStyle { kRegular, kBenchmark };
+
+// The --benchmark command line flag.
+// This is populated from main().
+// When run in "benchmark" mode, we have different output. This allows
+// A/B comparisons with tools like `benchy`.
+absl::string_view benchmarks;
+
+OutputStyle output() {
+ return !benchmarks.empty() ? OutputStyle::kBenchmark : OutputStyle::kRegular;
+}
+
+template <class T>
+struct Policy {
+ using slot_type = T;
+ using key_type = T;
+ using init_type = T;
+
+ template <class allocator_type, class Arg>
+ static void construct(allocator_type* alloc, slot_type* slot,
+ const Arg& arg) {
+ std::allocator_traits<allocator_type>::construct(*alloc, slot, arg);
+ }
+
+ template <class allocator_type>
+ static void destroy(allocator_type* alloc, slot_type* slot) {
+ std::allocator_traits<allocator_type>::destroy(*alloc, slot);
+ }
+
+ static slot_type& element(slot_type* slot) { return *slot; }
+
+ template <class F, class... Args>
+ static auto apply(F&& f, const slot_type& arg)
+ -> decltype(std::forward<F>(f)(arg, arg)) {
+ return std::forward<F>(f)(arg, arg);
+ }
+};
+
+absl::BitGen& GlobalBitGen() {
+ static auto* value = new absl::BitGen;
+ return *value;
+}
+
+// Keeps a pool of allocations and randomly gives one out.
+// This introduces more randomization to the addresses given to swisstable and
+// should help smooth out this factor from probe length calculation.
+template <class T>
+class RandomizedAllocator {
+ public:
+ using value_type = T;
+
+ RandomizedAllocator() = default;
+ template <typename U>
+ RandomizedAllocator(RandomizedAllocator<U>) {} // NOLINT
+
+ static T* allocate(size_t n) {
+ auto& pointers = GetPointers(n);
+ // Fill the pool
+ while (pointers.size() < kRandomPool) {
+ pointers.push_back(std::allocator<T>{}.allocate(n));
+ }
+
+ // Choose a random one.
+ size_t i = absl::Uniform<size_t>(GlobalBitGen(), 0, pointers.size());
+ T* result = pointers[i];
+ pointers[i] = pointers.back();
+ pointers.pop_back();
+ return result;
+ }
+
+ static void deallocate(T* p, size_t n) {
+ // Just put it back on the pool. No need to release the memory.
+ GetPointers(n).push_back(p);
+ }
+
+ private:
+ // We keep at least kRandomPool allocations for each size.
+ static constexpr size_t kRandomPool = 20;
+
+ static std::vector<T*>& GetPointers(size_t n) {
+ static auto* m = new absl::flat_hash_map<size_t, std::vector<T*>>();
+ return (*m)[n];
+ }
+};
+
+template <class T>
+struct DefaultHash {
+ using type = absl::container_internal::hash_default_hash<T>;
+};
+
+template <class T>
+using DefaultHashT = typename DefaultHash<T>::type;
+
+template <class T>
+struct Table : absl::container_internal::raw_hash_set<
+ Policy<T>, DefaultHashT<T>,
+ absl::container_internal::hash_default_eq<T>,
+ RandomizedAllocator<T>> {};
+
+struct LoadSizes {
+ size_t min_load;
+ size_t max_load;
+};
+
+LoadSizes GetMinMaxLoadSizes() {
+ static const auto sizes = [] {
+ Table<int> t;
+
+ // First, fill enough to have a good distribution.
+ constexpr size_t kMinSize = 10000;
+ while (t.size() < kMinSize) t.insert(t.size());
+
+ const auto reach_min_load_factor = [&] {
+ const double lf = t.load_factor();
+ while (lf <= t.load_factor()) t.insert(t.size());
+ };
+
+ // Then, insert until we reach min load factor.
+ reach_min_load_factor();
+ const size_t min_load_size = t.size();
+
+ // Keep going until we hit min load factor again, then go back one.
+ t.insert(t.size());
+ reach_min_load_factor();
+
+ return LoadSizes{min_load_size, t.size() - 1};
+ }();
+ return sizes;
+}
+
+struct Ratios {
+ double min_load;
+ double avg_load;
+ double max_load;
+};
+
+// See absl/container/internal/hashtable_debug.h for details on
+// probe length calculation.
+template <class ElemFn>
+Ratios CollectMeanProbeLengths() {
+ const auto min_max_sizes = GetMinMaxLoadSizes();
+
+ ElemFn elem;
+ using Key = decltype(elem());
+ Table<Key> t;
+
+ Ratios result;
+ while (t.size() < min_max_sizes.min_load) t.insert(elem());
+ result.min_load =
+ absl::container_internal::GetHashtableDebugProbeSummary(t).mean;
+
+ while (t.size() < (min_max_sizes.min_load + min_max_sizes.max_load) / 2)
+ t.insert(elem());
+ result.avg_load =
+ absl::container_internal::GetHashtableDebugProbeSummary(t).mean;
+
+ while (t.size() < min_max_sizes.max_load) t.insert(elem());
+ result.max_load =
+ absl::container_internal::GetHashtableDebugProbeSummary(t).mean;
+
+ return result;
+}
+
+template <int Align>
+uintptr_t PointerForAlignment() {
+ alignas(Align) static constexpr uintptr_t kInitPointer = 0;
+ return reinterpret_cast<uintptr_t>(&kInitPointer);
+}
+
+// This incomplete type is used for testing hash of pointers of different
+// alignments.
+// NOTE: We are generating invalid pointer values on the fly with
+// reinterpret_cast. There are not "safely derived" pointers so using them is
+// technically UB. It is unlikely to be a problem, though.
+template <int Align>
+struct Ptr;
+
+template <int Align>
+Ptr<Align>* MakePtr(uintptr_t v) {
+ if (sizeof(v) == 8) {
+ constexpr int kCopyBits = 16;
+ // Ensure high bits are all the same.
+ v = static_cast<uintptr_t>(static_cast<intptr_t>(v << kCopyBits) >>
+ kCopyBits);
+ }
+ return reinterpret_cast<Ptr<Align>*>(v);
+}
+
+struct IntIdentity {
+ uint64_t i;
+ friend bool operator==(IntIdentity a, IntIdentity b) { return a.i == b.i; }
+ IntIdentity operator++(int) { return IntIdentity{i++}; }
+};
+
+template <int Align>
+struct PtrIdentity {
+ explicit PtrIdentity(uintptr_t val = PointerForAlignment<Align>()) : i(val) {}
+ uintptr_t i;
+ friend bool operator==(PtrIdentity a, PtrIdentity b) { return a.i == b.i; }
+ PtrIdentity operator++(int) {
+ PtrIdentity p(i);
+ i += Align;
+ return p;
+ }
+};
+
+constexpr char kStringFormat[] = "/path/to/file/name-%07d-of-9999999.txt";
+
+template <bool small>
+struct String {
+ std::string value;
+ static std::string Make(uint32_t v) {
+ return {small ? absl::StrCat(v) : absl::StrFormat(kStringFormat, v)};
+ }
+};
+
+template <>
+struct DefaultHash<IntIdentity> {
+ struct type {
+ size_t operator()(IntIdentity t) const { return t.i; }
+ };
+};
+
+template <int Align>
+struct DefaultHash<PtrIdentity<Align>> {
+ struct type {
+ size_t operator()(PtrIdentity<Align> t) const { return t.i; }
+ };
+};
+
+template <class T>
+struct Sequential {
+ T operator()() const { return current++; }
+ mutable T current{};
+};
+
+template <int Align>
+struct Sequential<Ptr<Align>*> {
+ Ptr<Align>* operator()() const {
+ auto* result = MakePtr<Align>(current);
+ current += Align;
+ return result;
+ }
+ mutable uintptr_t current = PointerForAlignment<Align>();
+};
+
+
+template <bool small>
+struct Sequential<String<small>> {
+ std::string operator()() const { return String<small>::Make(current++); }
+ mutable uint32_t current = 0;
+};
+
+template <class T, class U>
+struct Sequential<std::pair<T, U>> {
+ mutable Sequential<T> tseq;
+ mutable Sequential<U> useq;
+
+ using RealT = decltype(tseq());
+ using RealU = decltype(useq());
+
+ mutable std::vector<RealT> ts;
+ mutable std::vector<RealU> us;
+ mutable size_t ti = 0, ui = 0;
+
+ std::pair<RealT, RealU> operator()() const {
+ std::pair<RealT, RealU> value{get_t(), get_u()};
+ if (ti == 0) {
+ ti = ui + 1;
+ ui = 0;
+ } else {
+ --ti;
+ ++ui;
+ }
+ return value;
+ }
+
+ RealT get_t() const {
+ while (ti >= ts.size()) ts.push_back(tseq());
+ return ts[ti];
+ }
+
+ RealU get_u() const {
+ while (ui >= us.size()) us.push_back(useq());
+ return us[ui];
+ }
+};
+
+template <class T, int percent_skip>
+struct AlmostSequential {
+ mutable Sequential<T> current;
+
+ auto operator()() const -> decltype(current()) {
+ while (absl::Uniform(GlobalBitGen(), 0.0, 1.0) <= percent_skip / 100.)
+ current();
+ return current();
+ }
+};
+
+struct Uniform {
+ template <typename T>
+ T operator()(T) const {
+ return absl::Uniform<T>(absl::IntervalClosed, GlobalBitGen(), T{0}, ~T{0});
+ }
+};
+
+struct Gaussian {
+ template <typename T>
+ T operator()(T) const {
+ double d;
+ do {
+ d = absl::Gaussian<double>(GlobalBitGen(), 1e6, 1e4);
+ } while (d <= 0 || d > std::numeric_limits<T>::max() / 2);
+ return static_cast<T>(d);
+ }
+};
+
+struct Zipf {
+ template <typename T>
+ T operator()(T) const {
+ return absl::Zipf<T>(GlobalBitGen(), std::numeric_limits<T>::max(), 1.6);
+ }
+};
+
+template <class T, class Dist>
+struct Random {
+ T operator()() const { return Dist{}(T{}); }
+};
+
+template <class Dist, int Align>
+struct Random<Ptr<Align>*, Dist> {
+ Ptr<Align>* operator()() const {
+ return MakePtr<Align>(Random<uintptr_t, Dist>{}() * Align);
+ }
+};
+
+template <class Dist>
+struct Random<IntIdentity, Dist> {
+ IntIdentity operator()() const {
+ return IntIdentity{Random<uint64_t, Dist>{}()};
+ }
+};
+
+template <class Dist, int Align>
+struct Random<PtrIdentity<Align>, Dist> {
+ PtrIdentity<Align> operator()() const {
+ return PtrIdentity<Align>{Random<uintptr_t, Dist>{}() * Align};
+ }
+};
+
+template <class Dist, bool small>
+struct Random<String<small>, Dist> {
+ std::string operator()() const {
+ return String<small>::Make(Random<uint32_t, Dist>{}());
+ }
+};
+
+template <class T, class U, class Dist>
+struct Random<std::pair<T, U>, Dist> {
+ auto operator()() const
+ -> decltype(std::make_pair(Random<T, Dist>{}(), Random<U, Dist>{}())) {
+ return std::make_pair(Random<T, Dist>{}(), Random<U, Dist>{}());
+ }
+};
+
+template <typename>
+std::string Name();
+
+std::string Name(uint32_t*) { return "u32"; }
+std::string Name(uint64_t*) { return "u64"; }
+std::string Name(IntIdentity*) { return "IntIdentity"; }
+
+template <int Align>
+std::string Name(Ptr<Align>**) {
+ return absl::StrCat("Ptr", Align);
+}
+
+template <int Align>
+std::string Name(PtrIdentity<Align>*) {
+ return absl::StrCat("PtrIdentity", Align);
+}
+
+template <bool small>
+std::string Name(String<small>*) {
+ return small ? "StrS" : "StrL";
+}
+
+template <class T, class U>
+std::string Name(std::pair<T, U>*) {
+ if (output() == OutputStyle::kBenchmark)
+ return absl::StrCat("P_", Name<T>(), "_", Name<U>());
+ return absl::StrCat("P<", Name<T>(), ",", Name<U>(), ">");
+}
+
+template <class T>
+std::string Name(Sequential<T>*) {
+ return "Sequential";
+}
+
+template <class T, int P>
+std::string Name(AlmostSequential<T, P>*) {
+ return absl::StrCat("AlmostSeq_", P);
+}
+
+template <class T>
+std::string Name(Random<T, Uniform>*) {
+ return "UnifRand";
+}
+
+template <class T>
+std::string Name(Random<T, Gaussian>*) {
+ return "GausRand";
+}
+
+template <class T>
+std::string Name(Random<T, Zipf>*) {
+ return "ZipfRand";
+}
+
+template <typename T>
+std::string Name() {
+ return Name(static_cast<T*>(nullptr));
+}
+
+constexpr int kNameWidth = 15;
+constexpr int kDistWidth = 16;
+
+bool CanRunBenchmark(absl::string_view name) {
+ static std::regex* const filter = []() -> std::regex* {
+ return benchmarks.empty() || benchmarks == "all"
+ ? nullptr
+ : new std::regex(std::string(benchmarks));
+ }();
+ return filter == nullptr || std::regex_search(std::string(name), *filter);
+}
+
+struct Result {
+ std::string name;
+ std::string dist_name;
+ Ratios ratios;
+};
+
+template <typename T, typename Dist>
+void RunForTypeAndDistribution(std::vector<Result>& results) {
+ std::string name = absl::StrCat(Name<T>(), "/", Name<Dist>());
+ // We have to check against all three names (min/avg/max) before we run it.
+ // If any of them is enabled, we run it.
+ if (!CanRunBenchmark(absl::StrCat(name, "/min")) &&
+ !CanRunBenchmark(absl::StrCat(name, "/avg")) &&
+ !CanRunBenchmark(absl::StrCat(name, "/max"))) {
+ return;
+ }
+ results.push_back({Name<T>(), Name<Dist>(), CollectMeanProbeLengths<Dist>()});
+}
+
+template <class T>
+void RunForType(std::vector<Result>& results) {
+ RunForTypeAndDistribution<T, Sequential<T>>(results);
+ RunForTypeAndDistribution<T, AlmostSequential<T, 20>>(results);
+ RunForTypeAndDistribution<T, AlmostSequential<T, 50>>(results);
+ RunForTypeAndDistribution<T, Random<T, Uniform>>(results);
+#ifdef NDEBUG
+ // Disable these in non-opt mode because they take too long.
+ RunForTypeAndDistribution<T, Random<T, Gaussian>>(results);
+ RunForTypeAndDistribution<T, Random<T, Zipf>>(results);
+#endif // NDEBUG
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ // Parse the benchmark flags. Ignore all of them except the regex pattern.
+ for (int i = 1; i < argc; ++i) {
+ absl::string_view arg = argv[i];
+ const auto next = [&] { return argv[std::min(i + 1, argc - 1)]; };
+
+ if (absl::ConsumePrefix(&arg, "--benchmark_filter")) {
+ if (arg == "") {
+ // --benchmark_filter X
+ benchmarks = next();
+ } else if (absl::ConsumePrefix(&arg, "=")) {
+ // --benchmark_filter=X
+ benchmarks = arg;
+ }
+ }
+
+ // Any --benchmark flag turns on the mode.
+ if (absl::ConsumePrefix(&arg, "--benchmark")) {
+ if (benchmarks.empty()) benchmarks="all";
+ }
+ }
+
+ std::vector<Result> results;
+ RunForType<uint64_t>(results);
+ RunForType<IntIdentity>(results);
+ RunForType<Ptr<8>*>(results);
+ RunForType<Ptr<16>*>(results);
+ RunForType<Ptr<32>*>(results);
+ RunForType<Ptr<64>*>(results);
+ RunForType<PtrIdentity<8>>(results);
+ RunForType<PtrIdentity<16>>(results);
+ RunForType<PtrIdentity<32>>(results);
+ RunForType<PtrIdentity<64>>(results);
+ RunForType<std::pair<uint32_t, uint32_t>>(results);
+ RunForType<String<true>>(results);
+ RunForType<String<false>>(results);
+ RunForType<std::pair<uint64_t, String<true>>>(results);
+ RunForType<std::pair<String<true>, uint64_t>>(results);
+ RunForType<std::pair<uint64_t, String<false>>>(results);
+ RunForType<std::pair<String<false>, uint64_t>>(results);
+
+ switch (output()) {
+ case OutputStyle::kRegular:
+ absl::PrintF("%-*s%-*s Min Avg Max\n%s\n", kNameWidth,
+ "Type", kDistWidth, "Distribution",
+ std::string(kNameWidth + kDistWidth + 10 * 3, '-'));
+ for (const auto& result : results) {
+ absl::PrintF("%-*s%-*s %8.4f %8.4f %8.4f\n", kNameWidth, result.name,
+ kDistWidth, result.dist_name, result.ratios.min_load,
+ result.ratios.avg_load, result.ratios.max_load);
+ }
+ break;
+ case OutputStyle::kBenchmark: {
+ absl::PrintF("{\n");
+ absl::PrintF(" \"benchmarks\": [\n");
+ absl::string_view comma;
+ for (const auto& result : results) {
+ auto print = [&](absl::string_view stat, double Ratios::*val) {
+ std::string name =
+ absl::StrCat(result.name, "/", result.dist_name, "/", stat);
+ // Check the regex again. We might had have enabled only one of the
+ // stats for the benchmark.
+ if (!CanRunBenchmark(name)) return;
+ absl::PrintF(" %s{\n", comma);
+ absl::PrintF(" \"cpu_time\": %f,\n", 1e9 * result.ratios.*val);
+ absl::PrintF(" \"real_time\": %f,\n", 1e9 * result.ratios.*val);
+ absl::PrintF(" \"iterations\": 1,\n");
+ absl::PrintF(" \"name\": \"%s\",\n", name);
+ absl::PrintF(" \"time_unit\": \"ns\"\n");
+ absl::PrintF(" }\n");
+ comma = ",";
+ };
+ print("min", &Ratios::min_load);
+ print("avg", &Ratios::avg_load);
+ print("max", &Ratios::max_load);
+ }
+ absl::PrintF(" ],\n");
+ absl::PrintF(" \"context\": {\n");
+ absl::PrintF(" }\n");
+ absl::PrintF("}\n");
+ break;
+ }
+ }
+
+ return 0;
+}
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc
index 33d2773..81c4b47 100644
--- a/absl/container/internal/raw_hash_set_test.cc
+++ b/absl/container/internal/raw_hash_set_test.cc
@@ -14,6 +14,7 @@
#include "absl/container/internal/raw_hash_set.h"
+#include <atomic>
#include <cmath>
#include <cstdint>
#include <deque>
@@ -22,6 +23,8 @@
#include <numeric>
#include <random>
#include <string>
+#include <unordered_map>
+#include <unordered_set>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@@ -48,11 +51,10 @@
namespace {
-using ::testing::DoubleNear;
using ::testing::ElementsAre;
+using ::testing::Eq;
using ::testing::Ge;
using ::testing::Lt;
-using ::testing::Optional;
using ::testing::Pair;
using ::testing::UnorderedElementsAre;
@@ -75,8 +77,14 @@
for (size_t growth = 0; growth < 10000; ++growth) {
SCOPED_TRACE(growth);
size_t capacity = NormalizeCapacity(GrowthToLowerboundCapacity(growth));
- // The capacity is large enough for `growth`
+ // The capacity is large enough for `growth`.
EXPECT_THAT(CapacityToGrowth(capacity), Ge(growth));
+ // For (capacity+1) < kWidth, growth should equal capacity.
+ if (capacity + 1 < Group::kWidth) {
+ EXPECT_THAT(CapacityToGrowth(capacity), Eq(capacity));
+ } else {
+ EXPECT_THAT(CapacityToGrowth(capacity), Lt(capacity));
+ }
if (growth != 0 && capacity > 1) {
// There is no smaller capacity that works.
EXPECT_THAT(CapacityToGrowth(capacity / 2), Lt(growth));
@@ -250,25 +258,43 @@
}
}
-struct IntPolicy {
- using slot_type = int64_t;
- using key_type = int64_t;
- using init_type = int64_t;
+template <class T>
+struct ValuePolicy {
+ using slot_type = T;
+ using key_type = T;
+ using init_type = T;
- static void construct(void*, int64_t* slot, int64_t v) { *slot = v; }
- static void destroy(void*, int64_t*) {}
- static void transfer(void*, int64_t* new_slot, int64_t* old_slot) {
- *new_slot = *old_slot;
+ template <class Allocator, class... Args>
+ static void construct(Allocator* alloc, slot_type* slot, Args&&... args) {
+ absl::allocator_traits<Allocator>::construct(*alloc, slot,
+ std::forward<Args>(args)...);
}
- static int64_t& element(slot_type* slot) { return *slot; }
+ template <class Allocator>
+ static void destroy(Allocator* alloc, slot_type* slot) {
+ absl::allocator_traits<Allocator>::destroy(*alloc, slot);
+ }
- template <class F>
- static auto apply(F&& f, int64_t x) -> decltype(std::forward<F>(f)(x, x)) {
- return std::forward<F>(f)(x, x);
+ template <class Allocator>
+ static void transfer(Allocator* alloc, slot_type* new_slot,
+ slot_type* old_slot) {
+ construct(alloc, new_slot, std::move(*old_slot));
+ destroy(alloc, old_slot);
+ }
+
+ static T& element(slot_type* slot) { return *slot; }
+
+ template <class F, class... Args>
+ static decltype(absl::container_internal::DecomposeValue(
+ std::declval<F>(), std::declval<Args>()...))
+ apply(F&& f, Args&&... args) {
+ return absl::container_internal::DecomposeValue(
+ std::forward<F>(f), std::forward<Args>(args)...);
}
};
+using IntPolicy = ValuePolicy<int64_t>;
+
class StringPolicy {
template <class F, class K, class V,
class = typename std::enable_if<
@@ -1657,6 +1683,38 @@
EXPECT_THAT(t2, UnorderedElementsAre(Pair("0", "~0")));
}
+TEST(Table, IteratorEmplaceConstructibleRequirement) {
+ struct Value {
+ explicit Value(absl::string_view view) : value(view) {}
+ std::string value;
+
+ bool operator==(const Value& other) const { return value == other.value; }
+ };
+ struct H {
+ size_t operator()(const Value& v) const {
+ return absl::Hash<std::string>{}(v.value);
+ }
+ };
+
+ struct Table : raw_hash_set<ValuePolicy<Value>, H, std::equal_to<Value>,
+ std::allocator<Value>> {
+ using Base = typename Table::raw_hash_set;
+ using Base::Base;
+ };
+
+ std::string input[3]{"A", "B", "C"};
+
+ Table t(std::begin(input), std::end(input));
+ EXPECT_THAT(t, UnorderedElementsAre(Value{"A"}, Value{"B"}, Value{"C"}));
+
+ input[0] = "D";
+ input[1] = "E";
+ input[2] = "F";
+ t.insert(std::begin(input), std::end(input));
+ EXPECT_THAT(t, UnorderedElementsAre(Value{"A"}, Value{"B"}, Value{"C"},
+ Value{"D"}, Value{"E"}, Value{"F"}));
+}
+
TEST(Nodes, EmptyNodeType) {
using node_type = StringTable::node_type;
node_type n;
@@ -1825,18 +1883,34 @@
auto& sampler = HashtablezSampler::Global();
size_t start_size = 0;
- start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; });
+ std::unordered_set<const HashtablezInfo*> preexisting_info;
+ start_size += sampler.Iterate([&](const HashtablezInfo& info) {
+ preexisting_info.insert(&info);
+ ++start_size;
+ });
std::vector<IntTable> tables;
for (int i = 0; i < 1000000; ++i) {
tables.emplace_back();
tables.back().insert(1);
+ tables.back().insert(i % 5);
}
size_t end_size = 0;
- end_size += sampler.Iterate([&](const HashtablezInfo&) { ++end_size; });
+ std::unordered_map<size_t, int> observed_checksums;
+ end_size += sampler.Iterate([&](const HashtablezInfo& info) {
+ if (preexisting_info.count(&info) == 0) {
+ observed_checksums[info.hashes_bitwise_xor.load(
+ std::memory_order_relaxed)]++;
+ }
+ ++end_size;
+ });
EXPECT_NEAR((end_size - start_size) / static_cast<double>(tables.size()),
0.01, 0.005);
+ EXPECT_EQ(observed_checksums.size(), 5);
+ for (const auto& [_, count] : observed_checksums) {
+ EXPECT_NEAR((100 * count) / static_cast<double>(tables.size()), 0.2, 0.05);
+ }
}
#endif // ABSL_INTERNAL_HASHTABLEZ_SAMPLE
diff --git a/absl/container/internal/unordered_map_constructor_test.h b/absl/container/internal/unordered_map_constructor_test.h
index 76ee95e..3f90ad7 100644
--- a/absl/container/internal/unordered_map_constructor_test.h
+++ b/absl/container/internal/unordered_map_constructor_test.h
@@ -16,6 +16,7 @@
#define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_
#include <algorithm>
+#include <unordered_map>
#include <vector>
#include "gmock/gmock.h"
diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h
index 56ce3b6..93b15f4 100644
--- a/absl/container/node_hash_set.h
+++ b/absl/container/node_hash_set.h
@@ -18,7 +18,7 @@
//
// An `absl::node_hash_set<T>` is an unordered associative container designed to
// be a more efficient replacement for `std::unordered_set`. Like
-// `unordered_set`, search, insertion, and deletion of map elements can be done
+// `unordered_set`, search, insertion, and deletion of set elements can be done
// as an `O(1)` operation. However, `node_hash_set` (and other unordered
// associative containers known as the collection of Abseil "Swiss tables")
// contain other optimizations that result in both memory and computation
@@ -60,7 +60,7 @@
// following notable differences:
//
// * Supports heterogeneous lookup, through `find()`, `operator[]()` and
-// `insert()`, provided that the map is provided a compatible heterogeneous
+// `insert()`, provided that the set is provided a compatible heterogeneous
// hashing function and equality operator.
// * Contains a `capacity()` member function indicating the number of element
// slots (open, deleted, and empty) within the hash set.
@@ -76,13 +76,13 @@
// Example:
//
// // Create a node hash set of three strings
-// absl::node_hash_map<std::string, std::string> ducks =
+// absl::node_hash_set<std::string> ducks =
// {"huey", "dewey", "louie"};
//
-// // Insert a new element into the node hash map
-// ducks.insert("donald"};
+// // Insert a new element into the node hash set
+// ducks.insert("donald");
//
-// // Force a rehash of the node hash map
+// // Force a rehash of the node hash set
// ducks.rehash(0);
//
// // See if "dewey" is present
@@ -100,7 +100,7 @@
public:
// Constructors and Assignment Operators
//
- // A node_hash_set supports the same overload set as `std::unordered_map`
+ // A node_hash_set supports the same overload set as `std::unordered_set`
// for construction and assignment:
//
// * Default constructor
@@ -167,7 +167,7 @@
// available within the `node_hash_set`.
//
// NOTE: this member function is particular to `absl::node_hash_set` and is
- // not provided in the `std::unordered_map` API.
+ // not provided in the `std::unordered_set` API.
using Base::capacity;
// node_hash_set::empty()
@@ -208,7 +208,7 @@
// `void`.
//
// NOTE: this return behavior is different than that of STL containers in
- // general and `std::unordered_map` in particular.
+ // general and `std::unordered_set` in particular.
//
// iterator erase(const_iterator first, const_iterator last):
//
@@ -314,7 +314,7 @@
// node_hash_set::merge()
//
- // Extracts elements from a given `source` flat hash map into this
+ // Extracts elements from a given `source` node hash set into this
// `node_hash_set`. If the destination `node_hash_set` already contains an
// element with an equivalent key, that element is not extracted.
using Base::merge;
@@ -322,15 +322,15 @@
// node_hash_set::swap(node_hash_set& other)
//
// Exchanges the contents of this `node_hash_set` with those of the `other`
- // flat hash map, avoiding invocation of any move, copy, or swap operations on
+ // node hash set, avoiding invocation of any move, copy, or swap operations on
// individual elements.
//
// All iterators and references on the `node_hash_set` remain valid, excepting
// for the past-the-end iterator, which is invalidated.
//
- // `swap()` requires that the flat hash set's hashing and key equivalence
+ // `swap()` requires that the node hash set's hashing and key equivalence
// functions be Swappable, and are exchaged using unqualified calls to
- // non-member `swap()`. If the map's allocator has
+ // non-member `swap()`. If the set's allocator has
// `std::allocator_traits<allocator_type>::propagate_on_container_swap::value`
// set to `true`, the allocators are also exchanged using an unqualified call
// to non-member `swap()`; otherwise, the allocators are not swapped.
@@ -385,14 +385,14 @@
// node_hash_set::bucket_count()
//
// Returns the number of "buckets" within the `node_hash_set`. Note that
- // because a flat hash map contains all elements within its internal storage,
+ // because a node hash set contains all elements within its internal storage,
// this value simply equals the current capacity of the `node_hash_set`.
using Base::bucket_count;
// node_hash_set::load_factor()
//
// Returns the current load factor of the `node_hash_set` (the average number
- // of slots occupied with a value within the hash map).
+ // of slots occupied with a value within the hash set).
using Base::load_factor;
// node_hash_set::max_load_factor()
diff --git a/absl/copts/AbseilConfigureCopts.cmake b/absl/copts/AbseilConfigureCopts.cmake
index acd46d0..9cd6fd1 100644
--- a/absl/copts/AbseilConfigureCopts.cmake
+++ b/absl/copts/AbseilConfigureCopts.cmake
@@ -12,16 +12,16 @@
set(ABSL_BUILD_DLL FALSE)
endif()
-if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "x86_64|amd64|AMD64")
+if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
if (MSVC)
set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_MSVC_X64_FLAGS}")
else()
set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_X64_FLAGS}")
endif()
-elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "arm.*|aarch64")
- if ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*|aarch64")
+ if (CMAKE_SIZEOF_VOID_P STREQUAL "8")
set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_ARM64_FLAGS}")
- elseif("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
+ elseif(CMAKE_SIZEOF_VOID_P STREQUAL "4")
set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_ARM32_FLAGS}")
else()
message(WARNING "Value of CMAKE_SIZEOF_VOID_P (${CMAKE_SIZEOF_VOID_P}) is not supported.")
@@ -32,20 +32,19 @@
endif()
-if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(ABSL_DEFAULT_COPTS "${ABSL_GCC_FLAGS}")
set(ABSL_TEST_COPTS "${ABSL_GCC_FLAGS};${ABSL_GCC_TEST_FLAGS}")
-elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
+elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# MATCHES so we get both Clang and AppleClang
if(MSVC)
# clang-cl is half MSVC, half LLVM
set(ABSL_DEFAULT_COPTS "${ABSL_CLANG_CL_FLAGS}")
set(ABSL_TEST_COPTS "${ABSL_CLANG_CL_FLAGS};${ABSL_CLANG_CL_TEST_FLAGS}")
- set(ABSL_DEFAULT_LINKOPTS "${ABSL_MSVC_LINKOPTS}")
else()
set(ABSL_DEFAULT_COPTS "${ABSL_LLVM_FLAGS}")
set(ABSL_TEST_COPTS "${ABSL_LLVM_FLAGS};${ABSL_LLVM_TEST_FLAGS}")
- if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# AppleClang doesn't have lsan
# https://developer.apple.com/documentation/code_diagnostics
if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5)
@@ -54,7 +53,7 @@
endif()
endif()
endif()
-elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
+elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(ABSL_DEFAULT_COPTS "${ABSL_MSVC_FLAGS}")
set(ABSL_TEST_COPTS "${ABSL_MSVC_FLAGS};${ABSL_MSVC_TEST_FLAGS}")
set(ABSL_DEFAULT_LINKOPTS "${ABSL_MSVC_LINKOPTS}")
diff --git a/absl/copts/configure_copts.bzl b/absl/copts/configure_copts.bzl
index 4d34254..669a906 100644
--- a/absl/copts/configure_copts.bzl
+++ b/absl/copts/configure_copts.bzl
@@ -22,19 +22,21 @@
)
ABSL_DEFAULT_COPTS = select({
- "//absl:windows": ABSL_MSVC_FLAGS,
+ "//absl:msvc_compiler": ABSL_MSVC_FLAGS,
+ "//absl:clang-cl_compiler": ABSL_CLANG_CL_FLAGS,
"//absl:clang_compiler": ABSL_LLVM_FLAGS,
"//conditions:default": ABSL_GCC_FLAGS,
})
ABSL_TEST_COPTS = ABSL_DEFAULT_COPTS + select({
- "//absl:windows": ABSL_MSVC_TEST_FLAGS,
+ "//absl:msvc_compiler": ABSL_MSVC_TEST_FLAGS,
+ "//absl:clang-cl_compiler": ABSL_CLANG_CL_TEST_FLAGS,
"//absl:clang_compiler": ABSL_LLVM_TEST_FLAGS,
"//conditions:default": ABSL_GCC_TEST_FLAGS,
})
ABSL_DEFAULT_LINKOPTS = select({
- "//absl:windows": ABSL_MSVC_LINKOPTS,
+ "//absl:msvc_compiler": ABSL_MSVC_LINKOPTS,
"//conditions:default": [],
})
diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel
index cd6e454..e7fd115 100644
--- a/absl/debugging/BUILD.bazel
+++ b/absl/debugging/BUILD.bazel
@@ -66,7 +66,8 @@
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS + select({
- "//absl:windows": ["-DEFAULTLIB:dbghelp.lib"],
+ "//absl:msvc_compiler": ["-DEFAULTLIB:dbghelp.lib"],
+ "//absl:clang-cl_compiler": ["-DEFAULTLIB:dbghelp.lib"],
"//conditions:default": [],
}),
deps = [
@@ -86,11 +87,13 @@
name = "symbolize_test",
srcs = ["symbolize_test.cc"],
copts = ABSL_TEST_COPTS + select({
- "//absl:windows": ["/Z7"],
+ "//absl:msvc_compiler": ["/Z7"],
+ "//absl:clang-cl_compiler": ["/Z7"],
"//conditions:default": [],
}),
linkopts = ABSL_DEFAULT_LINKOPTS + select({
- "//absl:windows": ["/DEBUG"],
+ "//absl:msvc_compiler": ["/DEBUG"],
+ "//absl:clang-cl_compiler": ["/DEBUG"],
"//conditions:default": [],
}),
deps = [
@@ -148,7 +151,8 @@
srcs = ["failure_signal_handler_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = select({
- "//absl:windows": [],
+ "//absl:msvc_compiler": [],
+ "//absl:clang-cl_compiler": [],
"//absl:wasm": [],
"//conditions:default": ["-pthread"],
}) + ABSL_DEFAULT_LINKOPTS,
diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc
index 5d13bdb..a9ed6ef 100644
--- a/absl/debugging/failure_signal_handler.cc
+++ b/absl/debugging/failure_signal_handler.cc
@@ -21,6 +21,7 @@
#ifdef _WIN32
#include <windows.h>
#else
+#include <sched.h>
#include <unistd.h>
#endif
@@ -219,17 +220,24 @@
absl::raw_logging_internal::SafeWriteToStderr(data, strlen(data));
}
-static void WriteSignalMessage(int signo, void (*writerfn)(const char*)) {
- char buf[64];
+static void WriteSignalMessage(int signo, int cpu,
+ void (*writerfn)(const char*)) {
+ char buf[96];
+ char on_cpu[32] = {0};
+ if (cpu != -1) {
+ snprintf(on_cpu, sizeof(on_cpu), " on cpu %d", cpu);
+ }
const char* const signal_string =
debugging_internal::FailureSignalToString(signo);
if (signal_string != nullptr && signal_string[0] != '\0') {
- snprintf(buf, sizeof(buf), "*** %s received at time=%ld ***\n",
+ snprintf(buf, sizeof(buf), "*** %s received at time=%ld%s ***\n",
signal_string,
- static_cast<long>(time(nullptr))); // NOLINT(runtime/int)
+ static_cast<long>(time(nullptr)), // NOLINT(runtime/int)
+ on_cpu);
} else {
- snprintf(buf, sizeof(buf), "*** Signal %d received at time=%ld ***\n",
- signo, static_cast<long>(time(nullptr))); // NOLINT(runtime/int)
+ snprintf(buf, sizeof(buf), "*** Signal %d received at time=%ld%s ***\n",
+ signo, static_cast<long>(time(nullptr)), // NOLINT(runtime/int)
+ on_cpu);
}
writerfn(buf);
}
@@ -269,10 +277,10 @@
// Called by AbslFailureSignalHandler() to write the failure info. It is
// called once with writerfn set to WriteToStderr() and then possibly
// with writerfn set to the user provided function.
-static void WriteFailureInfo(int signo, void* ucontext,
+static void WriteFailureInfo(int signo, void* ucontext, int cpu,
void (*writerfn)(const char*)) {
WriterFnStruct writerfn_struct{writerfn};
- WriteSignalMessage(signo, writerfn);
+ WriteSignalMessage(signo, cpu, writerfn);
WriteStackTrace(ucontext, fsh_options.symbolize_stacktrace, WriterFnWrapper,
&writerfn_struct);
}
@@ -334,6 +342,14 @@
}
}
+ // Increase the chance that the CPU we report was the same CPU on which the
+ // signal was received by doing this as early as possible, i.e. after
+ // verifying that this is not a recursive signal handler invocation.
+ int my_cpu = -1;
+#ifdef ABSL_HAVE_SCHED_GETCPU
+ my_cpu = sched_getcpu();
+#endif
+
#ifdef ABSL_HAVE_ALARM
// Set an alarm to abort the program in case this code hangs or deadlocks.
if (fsh_options.alarm_on_failure_secs > 0) {
@@ -344,12 +360,12 @@
#endif
// First write to stderr.
- WriteFailureInfo(signo, ucontext, WriteToStderr);
+ WriteFailureInfo(signo, ucontext, my_cpu, WriteToStderr);
// Riskier code (because it is less likely to be async-signal-safe)
// goes after this point.
if (fsh_options.writerfn != nullptr) {
- WriteFailureInfo(signo, ucontext, fsh_options.writerfn);
+ WriteFailureInfo(signo, ucontext, my_cpu, fsh_options.writerfn);
}
if (fsh_options.call_previous_handler) {
diff --git a/absl/debugging/failure_signal_handler_test.cc b/absl/debugging/failure_signal_handler_test.cc
index d8283b2..6a62428 100644
--- a/absl/debugging/failure_signal_handler_test.cc
+++ b/absl/debugging/failure_signal_handler_test.cc
@@ -122,6 +122,12 @@
"*** ", absl::debugging_internal::FailureSignalToString(signo),
" received at ")));
+ // On platforms where it is possible to get the current CPU, the
+ // CPU number is also logged. Check that it is present in output.
+#if defined(__linux__)
+ EXPECT_THAT(error_line, testing::HasSubstr(" on cpu "));
+#endif
+
if (absl::debugging_internal::StackTraceWorksForTest()) {
std::getline(error_output, error_line);
EXPECT_THAT(error_line, StartsWith("PC: "));
diff --git a/absl/debugging/internal/examine_stack.cc b/absl/debugging/internal/examine_stack.cc
index 6e5ff1f..589a3ef 100644
--- a/absl/debugging/internal/examine_stack.cc
+++ b/absl/debugging/internal/examine_stack.cc
@@ -46,26 +46,42 @@
ucontext_t* context = reinterpret_cast<ucontext_t*>(vuc);
#if defined(__aarch64__)
return reinterpret_cast<void*>(context->uc_mcontext.pc);
+#elif defined(__alpha__)
+ return reinterpret_cast<void*>(context->uc_mcontext.sc_pc);
#elif defined(__arm__)
return reinterpret_cast<void*>(context->uc_mcontext.arm_pc);
+#elif defined(__hppa__)
+ return reinterpret_cast<void*>(context->uc_mcontext.sc_iaoq[0]);
#elif defined(__i386__)
if (14 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs))
return reinterpret_cast<void*>(context->uc_mcontext.gregs[14]);
+#elif defined(__ia64__)
+ return reinterpret_cast<void*>(context->uc_mcontext.sc_ip);
+#elif defined(__m68k__)
+ return reinterpret_cast<void*>(context->uc_mcontext.gregs[16]);
#elif defined(__mips__)
return reinterpret_cast<void*>(context->uc_mcontext.pc);
#elif defined(__powerpc64__)
return reinterpret_cast<void*>(context->uc_mcontext.gp_regs[32]);
#elif defined(__powerpc__)
- return reinterpret_cast<void*>(context->uc_mcontext.regs->nip);
+ return reinterpret_cast<void*>(context->uc_mcontext.uc_regs->gregs[32]);
#elif defined(__riscv)
return reinterpret_cast<void*>(context->uc_mcontext.__gregs[REG_PC]);
#elif defined(__s390__) && !defined(__s390x__)
return reinterpret_cast<void*>(context->uc_mcontext.psw.addr & 0x7fffffff);
#elif defined(__s390__) && defined(__s390x__)
return reinterpret_cast<void*>(context->uc_mcontext.psw.addr);
+#elif defined(__sh__)
+ return reinterpret_cast<void*>(context->uc_mcontext.pc);
+#elif defined(__sparc__) && !defined(__arch64__)
+ return reinterpret_cast<void*>(context->uc_mcontext.gregs[19]);
+#elif defined(__sparc__) && defined(__arch64__)
+ return reinterpret_cast<void*>(context->uc_mcontext.mc_gregs[19]);
#elif defined(__x86_64__)
if (16 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs))
return reinterpret_cast<void*>(context->uc_mcontext.gregs[16]);
+#elif defined(__e2k__)
+ return reinterpret_cast<void*>(context->uc_mcontext.cr0_hi);
#else
#error "Undefined Architecture."
#endif
diff --git a/absl/debugging/internal/stacktrace_aarch64-inl.inc b/absl/debugging/internal/stacktrace_aarch64-inl.inc
index 14a76f1..f4859d7 100644
--- a/absl/debugging/internal/stacktrace_aarch64-inl.inc
+++ b/absl/debugging/internal/stacktrace_aarch64-inl.inc
@@ -37,8 +37,11 @@
absl::debugging_internal::VDSOSupport vdso;
if (vdso.IsPresent()) {
absl::debugging_internal::VDSOSupport::SymbolInfo symbol_info;
- if (!vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_2.6.39", STT_FUNC,
- &symbol_info) ||
+ auto lookup = [&](int type) {
+ return vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_2.6.39", type,
+ &symbol_info);
+ };
+ if ((!lookup(STT_FUNC) && !lookup(STT_NOTYPE)) ||
symbol_info.address == nullptr) {
// Unexpected: VDSO is present, yet the expected symbol is missing
// or null.
diff --git a/absl/debugging/internal/stacktrace_config.h b/absl/debugging/internal/stacktrace_config.h
index 90af852..cca410d 100644
--- a/absl/debugging/internal/stacktrace_config.h
+++ b/absl/debugging/internal/stacktrace_config.h
@@ -21,6 +21,8 @@
#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_
#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_
+#include "absl/base/config.h"
+
#if defined(ABSL_STACKTRACE_INL_HEADER)
#error ABSL_STACKTRACE_INL_HEADER cannot be directly set
@@ -29,19 +31,8 @@
"absl/debugging/internal/stacktrace_win32-inl.inc"
#elif defined(__APPLE__)
+#ifdef ABSL_HAVE_THREAD_LOCAL
// Thread local support required for UnwindImpl.
-// Notes:
-// * Xcode's clang did not support `thread_local` until version 8, and
-// even then not for all iOS < 9.0.
-// * Xcode 9.3 started disallowing `thread_local` for 32-bit iOS simulator
-// targeting iOS 9.x.
-// * Xcode 10 moves the deployment target check for iOS < 9.0 to link time
-// making __has_feature unreliable there.
-//
-// Otherwise, `__has_feature` is only supported by Clang so it has be inside
-// `defined(__APPLE__)` check.
-#if __has_feature(cxx_thread_local) && \
- !(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0)
#define ABSL_STACKTRACE_INL_HEADER \
"absl/debugging/internal/stacktrace_generic-inl.inc"
#endif
diff --git a/absl/debugging/internal/stacktrace_powerpc-inl.inc b/absl/debugging/internal/stacktrace_powerpc-inl.inc
index 2e7c2f4..cf8c051 100644
--- a/absl/debugging/internal/stacktrace_powerpc-inl.inc
+++ b/absl/debugging/internal/stacktrace_powerpc-inl.inc
@@ -131,7 +131,12 @@
const ucontext_t* signal_context =
reinterpret_cast<const ucontext_t*>(uc);
void **const sp_before_signal =
- reinterpret_cast<void**>(signal_context->uc_mcontext.gp_regs[PT_R1]);
+#if defined(__PPC64__)
+ reinterpret_cast<void **>(signal_context->uc_mcontext.gp_regs[PT_R1]);
+#else
+ reinterpret_cast<void **>(
+ signal_context->uc_mcontext.uc_regs->gregs[PT_R1]);
+#endif
// Check that alleged sp before signal is nonnull and is reasonably
// aligned.
if (sp_before_signal != nullptr &&
diff --git a/absl/debugging/leak_check.h b/absl/debugging/leak_check.h
index 7a5a22d..b66a81c 100644
--- a/absl/debugging/leak_check.h
+++ b/absl/debugging/leak_check.h
@@ -62,7 +62,8 @@
//
// If the passed `ptr` does not point to an actively allocated object at the
// time `IgnoreLeak()` is called, the call is a no-op; if it is actively
-// allocated, the object must not get deallocated later.
+// allocated, leak sanitizer will assume this object is referenced even if
+// there is no actual reference in user memory.
//
template <typename T>
T* IgnoreLeak(T* ptr) {
diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel
index 2bd9478..147249e 100644
--- a/absl/flags/BUILD.bazel
+++ b/absl/flags/BUILD.bazel
@@ -191,6 +191,7 @@
],
hdrs = [
"internal/flag.h",
+ "internal/sequence_lock.h",
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
@@ -376,9 +377,13 @@
"flag_benchmark.cc",
],
copts = ABSL_TEST_COPTS,
+ linkopts = select({
+ "//conditions:default": [],
+ }) + ABSL_DEFAULT_LINKOPTS,
tags = ["benchmark"],
visibility = ["//visibility:private"],
deps = [
+ "flag_benchmark.lds",
":flag",
":marshalling",
":parse",
@@ -416,6 +421,7 @@
":flag",
":parse",
":reflection",
+ ":usage_internal",
"//absl/base:raw_logging_internal",
"//absl/base:scoped_set_env",
"//absl/strings",
@@ -474,6 +480,25 @@
)
cc_test(
+ name = "sequence_lock_test",
+ size = "small",
+ timeout = "moderate",
+ srcs = [
+ "internal/sequence_lock_test.cc",
+ ],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ shard_count = 31,
+ deps = [
+ ":flag_internal",
+ "//absl/base",
+ "//absl/container:fixed_array",
+ "//absl/time",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
name = "usage_config_test",
size = "small",
srcs = [
diff --git a/absl/flags/BUILD.gn b/absl/flags/BUILD.gn
index 8fa0603..0a87d9c 100644
--- a/absl/flags/BUILD.gn
+++ b/absl/flags/BUILD.gn
@@ -26,8 +26,16 @@
visibility = [ ":*" ]
}
+# Since absl/flags are only used by some test binaries (e.g. in WebRTC),
+# there is no need to strip flags from mobile platforms binaries.
+# This does not affect Chromium.
+config("absl_flags_config") {
+ defines = [ "ABSL_FLAGS_STRIP_NAMES=0" ]
+}
+
absl_source_set("config") {
sources = [ "usage_config.cc" ]
+ public_configs = [ ":absl_flags_config" ]
public = [
"config.h",
"usage_config.h",
@@ -108,7 +116,10 @@
absl_source_set("flag_internal") {
sources = [ "internal/flag.cc" ]
- public = [ "internal/flag.h" ]
+ public = [
+ "internal/flag.h",
+ "internal/sequence_lock.h",
+ ]
deps = [
":commandlineflag",
":commandlineflag_internal",
diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt
index 8855191..caac69c 100644
--- a/absl/flags/CMakeLists.txt
+++ b/absl/flags/CMakeLists.txt
@@ -176,6 +176,7 @@
"internal/flag.cc"
HDRS
"internal/flag.h"
+ "internal/sequence_lock.h"
COPTS
${ABSL_DEFAULT_COPTS}
LINKOPTS
@@ -366,6 +367,7 @@
absl::flags
absl::flags_parse
absl::flags_reflection
+ absl::flags_usage_internal
absl::raw_logging_internal
absl::scoped_set_env
absl::span
@@ -417,6 +419,20 @@
absl_cc_test(
NAME
+ flags_sequence_lock_test
+ SRCS
+ "internal/sequence_lock_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::base
+ absl::flags_internal
+ absl::time
+ gmock_main
+)
+
+absl_cc_test(
+ NAME
flags_usage_config_test
SRCS
"usage_config_test.cc"
diff --git a/absl/flags/config.h b/absl/flags/config.h
index 813a925..5ab1f31 100644
--- a/absl/flags/config.h
+++ b/absl/flags/config.h
@@ -45,17 +45,6 @@
#define ABSL_FLAGS_STRIP_HELP ABSL_FLAGS_STRIP_NAMES
#endif
-// ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD macro is used for using atomics with
-// double words, e.g. absl::Duration.
-// For reasons in bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80878, modern
-// versions of GCC do not support cmpxchg16b instruction in standard atomics.
-#ifdef ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD
-#error "ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD should not be defined."
-#elif defined(__clang__) && defined(__x86_64__) && \
- defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16)
-#define ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD 1
-#endif
-
// ABSL_FLAGS_INTERNAL_HAS_RTTI macro is used for selecting if we can use RTTI
// for flag type identification.
#ifdef ABSL_FLAGS_INTERNAL_HAS_RTTI
diff --git a/absl/flags/flag.h b/absl/flags/flag.h
index a9cb2b7..f09580b 100644
--- a/absl/flags/flag.h
+++ b/absl/flags/flag.h
@@ -301,13 +301,15 @@
#if ABSL_FLAGS_STRIP_NAMES
#define ABSL_FLAG_IMPL_FLAGNAME(txt) ""
#define ABSL_FLAG_IMPL_FILENAME() ""
-#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
- absl::flags_internal::FlagRegistrar<T, false>(ABSL_FLAG_IMPL_FLAG_PTR(flag))
+#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
+ absl::flags_internal::FlagRegistrar<T, false>(ABSL_FLAG_IMPL_FLAG_PTR(flag), \
+ nullptr)
#else
#define ABSL_FLAG_IMPL_FLAGNAME(txt) txt
#define ABSL_FLAG_IMPL_FILENAME() __FILE__
-#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
- absl::flags_internal::FlagRegistrar<T, true>(ABSL_FLAG_IMPL_FLAG_PTR(flag))
+#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
+ absl::flags_internal::FlagRegistrar<T, true>(ABSL_FLAG_IMPL_FLAG_PTR(flag), \
+ __FILE__)
#endif
// ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_HELP
diff --git a/absl/flags/flag_benchmark.cc b/absl/flags/flag_benchmark.cc
index 9982b60..57584f8 100644
--- a/absl/flags/flag_benchmark.cc
+++ b/absl/flags/flag_benchmark.cc
@@ -103,8 +103,17 @@
#define FLAG_DEF(T) ABSL_FLAG(T, T##_flag, {}, "");
+#if defined(__clang__) && defined(__linux__)
+// Force the flags used for benchmarks into a separate ELF section.
+// This ensures that, even when other parts of the code might change size,
+// the layout of the flags across cachelines is kept constant. This makes
+// benchmark results more reproducible across unrelated code changes.
+#pragma clang section data = ".benchmark_flags"
+#endif
BENCHMARKED_TYPES(FLAG_DEF)
-
+#if defined(__clang__) && defined(__linux__)
+#pragma clang section data = ""
+#endif
// Register thousands of flags to bloat up the size of the registry.
// This mimics real life production binaries.
#define DEFINE_FLAG_0(name) ABSL_FLAG(int, name, 0, "");
@@ -130,7 +139,7 @@
benchmark::DoNotOptimize(absl::GetFlag(FLAGS_##T##_flag)); \
} \
} \
- BENCHMARK(BM_GetFlag_##T);
+ BENCHMARK(BM_GetFlag_##T)->ThreadRange(1, 16);
BENCHMARKED_TYPES(BM_GetFlag)
diff --git a/absl/flags/flag_benchmark.lds b/absl/flags/flag_benchmark.lds
new file mode 100644
index 0000000..af115df
--- /dev/null
+++ b/absl/flags/flag_benchmark.lds
@@ -0,0 +1,13 @@
+/* This linker script forces the flags used by flags_benchmark
+ * into a separate page-aligned section. This isn't necessary for
+ * correctness but ensures that the benchmark results are more
+ * reproducible across unrelated code changes.
+ */
+SECTIONS {
+ .benchmark_flags : {
+ . = ALIGN(0x1000);
+ * (.benchmark_flags);
+ }
+}
+
+INSERT AFTER .data
diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc
index 654c812..6912b54 100644
--- a/absl/flags/flag_test.cc
+++ b/absl/flags/flag_test.cc
@@ -18,6 +18,7 @@
#include <stddef.h>
#include <stdint.h>
+#include <atomic>
#include <cmath>
#include <new>
#include <string>
@@ -26,6 +27,7 @@
#include "gtest/gtest.h"
#include "absl/base/attributes.h"
+#include "absl/base/macros.h"
#include "absl/flags/config.h"
#include "absl/flags/declare.h"
#include "absl/flags/internal/flag.h"
@@ -108,16 +110,16 @@
EXPECT_EQ(flags::StorageKind<int64_t>(),
flags::FlagValueStorageKind::kOneWordAtomic);
-#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD)
EXPECT_EQ(flags::StorageKind<S1>(),
- flags::FlagValueStorageKind::kTwoWordsAtomic);
+ flags::FlagValueStorageKind::kSequenceLocked);
EXPECT_EQ(flags::StorageKind<S2>(),
- flags::FlagValueStorageKind::kTwoWordsAtomic);
-#else
- EXPECT_EQ(flags::StorageKind<S1>(),
- flags::FlagValueStorageKind::kAlignedBuffer);
- EXPECT_EQ(flags::StorageKind<S2>(),
- flags::FlagValueStorageKind::kAlignedBuffer);
+ flags::FlagValueStorageKind::kSequenceLocked);
+// Make sure absl::Duration uses the sequence-locked code path. MSVC 2015
+// doesn't consider absl::Duration to be trivially-copyable so we just
+// restrict this to clang as it seems to be a well-behaved compiler.
+#ifdef __clang__
+ EXPECT_EQ(flags::StorageKind<absl::Duration>(),
+ flags::FlagValueStorageKind::kSequenceLocked);
#endif
EXPECT_EQ(flags::StorageKind<std::string>(),
@@ -175,7 +177,7 @@
EXPECT_EQ(absl::GetFlagReflectionHandle(f1).Help(), "literal help");
EXPECT_EQ(absl::GetFlagReflectionHandle(f1).Filename(), "file");
- flags::FlagRegistrar<T, false>(ABSL_FLAG_IMPL_FLAG_PTR(f2))
+ flags::FlagRegistrar<T, false>(ABSL_FLAG_IMPL_FLAG_PTR(f2), nullptr)
.OnUpdate(TestCallback);
EXPECT_EQ(absl::GetFlagReflectionHandle(f2).Name(), "f2");
@@ -583,6 +585,43 @@
// --------------------------------------------------------------------
+TEST_F(FlagTest, ConcurrentSetAndGet) {
+ static constexpr int kNumThreads = 8;
+ // Two arbitrary durations. One thread will concurrently flip the flag
+ // between these two values, while the other threads read it and verify
+ // that no other value is seen.
+ static const absl::Duration kValidDurations[] = {
+ absl::Seconds(int64_t{0x6cebf47a9b68c802}) + absl::Nanoseconds(229702057),
+ absl::Seconds(int64_t{0x23fec0307e4e9d3}) + absl::Nanoseconds(44555374)};
+ absl::SetFlag(&FLAGS_test_flag_12, kValidDurations[0]);
+
+ std::atomic<bool> stop{false};
+ std::vector<std::thread> threads;
+ auto* handle = absl::FindCommandLineFlag("test_flag_12");
+ for (int i = 0; i < kNumThreads; i++) {
+ threads.emplace_back([&]() {
+ while (!stop.load(std::memory_order_relaxed)) {
+ // Try loading the flag both directly and via a reflection
+ // handle.
+ absl::Duration v = absl::GetFlag(FLAGS_test_flag_12);
+ EXPECT_TRUE(v == kValidDurations[0] || v == kValidDurations[1]);
+ v = *handle->TryGet<absl::Duration>();
+ EXPECT_TRUE(v == kValidDurations[0] || v == kValidDurations[1]);
+ }
+ });
+ }
+ absl::Time end_time = absl::Now() + absl::Seconds(1);
+ int i = 0;
+ while (absl::Now() < end_time) {
+ absl::SetFlag(&FLAGS_test_flag_12,
+ kValidDurations[i++ % ABSL_ARRAYSIZE(kValidDurations)]);
+ }
+ stop.store(true, std::memory_order_relaxed);
+ for (auto& t : threads) t.join();
+}
+
+// --------------------------------------------------------------------
+
int GetDflt1() { return 1; }
} // namespace
diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h
index cb46fe2..ebfe81b 100644
--- a/absl/flags/internal/commandlineflag.h
+++ b/absl/flags/internal/commandlineflag.h
@@ -24,7 +24,7 @@
namespace flags_internal {
// An alias for flag fast type id. This value identifies the flag value type
-// simialarly to typeid(T), without relying on RTTI being available. In most
+// similarly to typeid(T), without relying on RTTI being available. In most
// cases this id is enough to uniquely identify the flag's value type. In a few
// cases we'll have to resort to using actual RTTI implementation if it is
// available.
diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc
index 1502e7f..f83c1fe 100644
--- a/absl/flags/internal/flag.cc
+++ b/absl/flags/internal/flag.cc
@@ -96,7 +96,8 @@
counter_(counter) {}
~FlagState() override {
- if (flag_impl_.ValueStorageKind() != FlagValueStorageKind::kAlignedBuffer)
+ if (flag_impl_.ValueStorageKind() != FlagValueStorageKind::kAlignedBuffer &&
+ flag_impl_.ValueStorageKind() != FlagValueStorageKind::kSequenceLocked)
return;
flags_internal::Delete(flag_impl_.op_, value_.heap_allocated);
}
@@ -118,11 +119,9 @@
union SavedValue {
explicit SavedValue(void* v) : heap_allocated(v) {}
explicit SavedValue(int64_t v) : one_word(v) {}
- explicit SavedValue(flags_internal::AlignedTwoWords v) : two_words(v) {}
void* heap_allocated;
int64_t one_word;
- flags_internal::AlignedTwoWords two_words;
} value_;
bool modified_;
bool on_command_line_;
@@ -164,17 +163,15 @@
std::memory_order_release);
break;
}
- case FlagValueStorageKind::kTwoWordsAtomic: {
+ case FlagValueStorageKind::kSequenceLocked: {
// For this storage kind the default_value_ always points to gen_func
// during initialization.
assert(def_kind == FlagDefaultKind::kGenFunc);
- alignas(AlignedTwoWords) std::array<char, sizeof(AlignedTwoWords)> buf{};
- (*default_value_.gen_func)(buf.data());
- auto atomic_value = absl::bit_cast<AlignedTwoWords>(buf);
- TwoWordsValue().store(atomic_value, std::memory_order_release);
+ (*default_value_.gen_func)(AtomicBufferValue());
break;
}
}
+ seq_lock_.MarkInitialized();
}
absl::Mutex* FlagImpl::DataGuard() const {
@@ -231,23 +228,21 @@
switch (ValueStorageKind()) {
case FlagValueStorageKind::kAlignedBuffer:
Copy(op_, src, AlignedBufferValue());
+ seq_lock_.IncrementModificationCount();
break;
case FlagValueStorageKind::kOneWordAtomic: {
int64_t one_word_val = 0;
std::memcpy(&one_word_val, src, Sizeof(op_));
OneWordValue().store(one_word_val, std::memory_order_release);
+ seq_lock_.IncrementModificationCount();
break;
}
- case FlagValueStorageKind::kTwoWordsAtomic: {
- AlignedTwoWords two_words_val{0, 0};
- std::memcpy(&two_words_val, src, Sizeof(op_));
- TwoWordsValue().store(two_words_val, std::memory_order_release);
+ case FlagValueStorageKind::kSequenceLocked: {
+ seq_lock_.Write(AtomicBufferValue(), src, Sizeof(op_));
break;
}
}
-
modified_ = true;
- ++counter_;
InvokeCallback();
}
@@ -266,6 +261,10 @@
return flags_internal::FastTypeId(op_);
}
+int64_t FlagImpl::ModificationCount() const {
+ return seq_lock_.ModificationCount();
+}
+
bool FlagImpl::IsSpecifiedOnCommandLine() const {
absl::MutexLock l(DataGuard());
return on_command_line_;
@@ -291,11 +290,11 @@
OneWordValue().load(std::memory_order_acquire));
return flags_internal::Unparse(op_, one_word_val.data());
}
- case FlagValueStorageKind::kTwoWordsAtomic: {
- const auto two_words_val =
- absl::bit_cast<std::array<char, sizeof(AlignedTwoWords)>>(
- TwoWordsValue().load(std::memory_order_acquire));
- return flags_internal::Unparse(op_, two_words_val.data());
+ case FlagValueStorageKind::kSequenceLocked: {
+ std::unique_ptr<void, DynValueDeleter> cloned(flags_internal::Alloc(op_),
+ DynValueDeleter{op_});
+ ReadSequenceLockedData(cloned.get());
+ return flags_internal::Unparse(op_, cloned.get());
}
}
@@ -345,17 +344,22 @@
case FlagValueStorageKind::kAlignedBuffer: {
return absl::make_unique<FlagState>(
*this, flags_internal::Clone(op_, AlignedBufferValue()), modified,
- on_command_line, counter_);
+ on_command_line, ModificationCount());
}
case FlagValueStorageKind::kOneWordAtomic: {
return absl::make_unique<FlagState>(
*this, OneWordValue().load(std::memory_order_acquire), modified,
- on_command_line, counter_);
+ on_command_line, ModificationCount());
}
- case FlagValueStorageKind::kTwoWordsAtomic: {
- return absl::make_unique<FlagState>(
- *this, TwoWordsValue().load(std::memory_order_acquire), modified,
- on_command_line, counter_);
+ case FlagValueStorageKind::kSequenceLocked: {
+ void* cloned = flags_internal::Alloc(op_);
+ // Read is guaranteed to be successful because we hold the lock.
+ bool success =
+ seq_lock_.TryRead(cloned, AtomicBufferValue(), Sizeof(op_));
+ assert(success);
+ static_cast<void>(success);
+ return absl::make_unique<FlagState>(*this, cloned, modified,
+ on_command_line, ModificationCount());
}
}
return nullptr;
@@ -363,21 +367,18 @@
bool FlagImpl::RestoreState(const FlagState& flag_state) {
absl::MutexLock l(DataGuard());
-
- if (flag_state.counter_ == counter_) {
+ if (flag_state.counter_ == ModificationCount()) {
return false;
}
switch (ValueStorageKind()) {
case FlagValueStorageKind::kAlignedBuffer:
+ case FlagValueStorageKind::kSequenceLocked:
StoreValue(flag_state.value_.heap_allocated);
break;
case FlagValueStorageKind::kOneWordAtomic:
StoreValue(&flag_state.value_.one_word);
break;
- case FlagValueStorageKind::kTwoWordsAtomic:
- StoreValue(&flag_state.value_.two_words);
- break;
}
modified_ = flag_state.modified_;
@@ -400,16 +401,16 @@
return OffsetValue<void>();
}
+std::atomic<uint64_t>* FlagImpl::AtomicBufferValue() const {
+ assert(ValueStorageKind() == FlagValueStorageKind::kSequenceLocked);
+ return OffsetValue<std::atomic<uint64_t>>();
+}
+
std::atomic<int64_t>& FlagImpl::OneWordValue() const {
assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic);
return OffsetValue<FlagOneWordValue>()->value;
}
-std::atomic<AlignedTwoWords>& FlagImpl::TwoWordsValue() const {
- assert(ValueStorageKind() == FlagValueStorageKind::kTwoWordsAtomic);
- return OffsetValue<FlagTwoWordsValue>()->value;
-}
-
// Attempts to parse supplied `value` string using parsing routine in the `flag`
// argument. If parsing successful, this function replaces the dst with newly
// parsed value. In case if any error is encountered in either step, the error
@@ -443,15 +444,27 @@
std::memcpy(dst, &one_word_val, Sizeof(op_));
break;
}
- case FlagValueStorageKind::kTwoWordsAtomic: {
- const AlignedTwoWords two_words_val =
- TwoWordsValue().load(std::memory_order_acquire);
- std::memcpy(dst, &two_words_val, Sizeof(op_));
+ case FlagValueStorageKind::kSequenceLocked: {
+ ReadSequenceLockedData(dst);
break;
}
}
}
+void FlagImpl::ReadSequenceLockedData(void* dst) const {
+ int size = Sizeof(op_);
+ // Attempt to read using the sequence lock.
+ if (ABSL_PREDICT_TRUE(seq_lock_.TryRead(dst, AtomicBufferValue(), size))) {
+ return;
+ }
+ // We failed due to contention. Acquire the lock to prevent contention
+ // and try again.
+ absl::ReaderMutexLock l(DataGuard());
+ bool success = seq_lock_.TryRead(dst, AtomicBufferValue(), size);
+ assert(success);
+ static_cast<void>(success);
+}
+
void FlagImpl::Write(const void* src) {
absl::MutexLock l(DataGuard());
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h
index 370d8a0..e6bade0 100644
--- a/absl/flags/internal/flag.h
+++ b/absl/flags/internal/flag.h
@@ -36,6 +36,7 @@
#include "absl/flags/config.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/registry.h"
+#include "absl/flags/internal/sequence_lock.h"
#include "absl/flags/marshalling.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h"
@@ -308,59 +309,23 @@
bool, absl::type_traits_internal::is_trivially_copyable<T>::value &&
(sizeof(T) <= 8)>;
-#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD)
-// Clang does not always produce cmpxchg16b instruction when alignment of a 16
-// bytes type is not 16.
-struct alignas(16) AlignedTwoWords {
- int64_t first;
- int64_t second;
-
- bool IsInitialized() const {
- return first != flags_internal::UninitializedFlagValue();
- }
-};
-
-template <typename T>
-using FlagUseTwoWordsStorage = std::integral_constant<
+template <class T>
+using FlagShouldUseSequenceLock = std::integral_constant<
bool, absl::type_traits_internal::is_trivially_copyable<T>::value &&
- (sizeof(T) > 8) && (sizeof(T) <= 16)>;
-#else
-// This is actually unused and only here to avoid ifdefs in other palces.
-struct AlignedTwoWords {
- constexpr AlignedTwoWords() noexcept : dummy() {}
- constexpr AlignedTwoWords(int64_t, int64_t) noexcept : dummy() {}
- char dummy;
-
- bool IsInitialized() const {
- std::abort();
- return true;
- }
-};
-
-// This trait should be type dependent, otherwise SFINAE below will fail
-template <typename T>
-using FlagUseTwoWordsStorage =
- std::integral_constant<bool, sizeof(T) != sizeof(T)>;
-#endif
-
-template <typename T>
-using FlagUseBufferStorage =
- std::integral_constant<bool, !FlagUseOneWordStorage<T>::value &&
- !FlagUseTwoWordsStorage<T>::value>;
+ (sizeof(T) > 8)>;
enum class FlagValueStorageKind : uint8_t {
kAlignedBuffer = 0,
kOneWordAtomic = 1,
- kTwoWordsAtomic = 2
+ kSequenceLocked = 2,
};
template <typename T>
static constexpr FlagValueStorageKind StorageKind() {
- return FlagUseBufferStorage<T>::value
- ? FlagValueStorageKind::kAlignedBuffer
- : FlagUseOneWordStorage<T>::value
- ? FlagValueStorageKind::kOneWordAtomic
- : FlagValueStorageKind::kTwoWordsAtomic;
+ return FlagUseOneWordStorage<T>::value ? FlagValueStorageKind::kOneWordAtomic
+ : FlagShouldUseSequenceLock<T>::value
+ ? FlagValueStorageKind::kSequenceLocked
+ : FlagValueStorageKind::kAlignedBuffer;
}
struct FlagOneWordValue {
@@ -369,27 +334,20 @@
std::atomic<int64_t> value;
};
-struct FlagTwoWordsValue {
- constexpr FlagTwoWordsValue()
- : value(AlignedTwoWords{UninitializedFlagValue(), 0}) {}
-
- std::atomic<AlignedTwoWords> value;
-};
-
template <typename T,
FlagValueStorageKind Kind = flags_internal::StorageKind<T>()>
struct FlagValue;
template <typename T>
struct FlagValue<T, FlagValueStorageKind::kAlignedBuffer> {
- bool Get(T&) const { return false; }
+ bool Get(const SequenceLock&, T&) const { return false; }
alignas(T) char value[sizeof(T)];
};
template <typename T>
struct FlagValue<T, FlagValueStorageKind::kOneWordAtomic> : FlagOneWordValue {
- bool Get(T& dst) const {
+ bool Get(const SequenceLock&, T& dst) const {
int64_t one_word_val = value.load(std::memory_order_acquire);
if (ABSL_PREDICT_FALSE(one_word_val == UninitializedFlagValue())) {
return false;
@@ -400,15 +358,16 @@
};
template <typename T>
-struct FlagValue<T, FlagValueStorageKind::kTwoWordsAtomic> : FlagTwoWordsValue {
- bool Get(T& dst) const {
- AlignedTwoWords two_words_val = value.load(std::memory_order_acquire);
- if (ABSL_PREDICT_FALSE(!two_words_val.IsInitialized())) {
- return false;
- }
- std::memcpy(&dst, static_cast<const void*>(&two_words_val), sizeof(T));
- return true;
+struct FlagValue<T, FlagValueStorageKind::kSequenceLocked> {
+ bool Get(const SequenceLock& lock, T& dst) const {
+ return lock.TryRead(&dst, value_words, sizeof(T));
}
+
+ static constexpr int kNumWords =
+ flags_internal::AlignUp(sizeof(T), sizeof(uint64_t)) / sizeof(uint64_t);
+
+ alignas(T) alignas(
+ std::atomic<uint64_t>) std::atomic<uint64_t> value_words[kNumWords];
};
///////////////////////////////////////////////////////////////////////////////
@@ -451,7 +410,6 @@
def_kind_(static_cast<uint8_t>(default_arg.kind)),
modified_(false),
on_command_line_(false),
- counter_(0),
callback_(nullptr),
default_value_(default_arg.source),
data_guard_{} {}
@@ -498,15 +456,17 @@
// flag.cc, we can define it in that file as well.
template <typename StorageT>
StorageT* OffsetValue() const;
- // This is an accessor for a value stored in an aligned buffer storage.
+ // This is an accessor for a value stored in an aligned buffer storage
+ // used for non-trivially-copyable data types.
// Returns a mutable pointer to the start of a buffer.
void* AlignedBufferValue() const;
+
+ // The same as above, but used for sequencelock-protected storage.
+ std::atomic<uint64_t>* AtomicBufferValue() const;
+
// This is an accessor for a value stored as one word atomic. Returns a
// mutable reference to an atomic value.
std::atomic<int64_t>& OneWordValue() const;
- // This is an accessor for a value stored as two words atomic. Returns a
- // mutable reference to an atomic value.
- std::atomic<AlignedTwoWords>& TwoWordsValue() const;
// Attempts to parse supplied `value` string. If parsing is successful,
// returns new value. Otherwise returns nullptr.
@@ -516,6 +476,12 @@
// Stores the flag value based on the pointer to the source.
void StoreValue(const void* src) ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+ // Copy the flag data, protected by `seq_lock_` into `dst`.
+ //
+ // REQUIRES: ValueStorageKind() == kSequenceLocked.
+ void ReadSequenceLockedData(void* dst) const
+ ABSL_LOCKS_EXCLUDED(*DataGuard());
+
FlagHelpKind HelpSourceKind() const {
return static_cast<FlagHelpKind>(help_source_kind_);
}
@@ -541,6 +507,8 @@
void CheckDefaultValueParsingRoundtrip() const override
ABSL_LOCKS_EXCLUDED(*DataGuard());
+ int64_t ModificationCount() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+
// Interfaces to save and restore flags to/from persistent state.
// Returns current flag state or nullptr if flag does not support
// saving and restoring a state.
@@ -587,8 +555,9 @@
// Unique tag for absl::call_once call to initialize this flag.
absl::once_flag init_control_;
- // Mutation counter
- int64_t counter_ ABSL_GUARDED_BY(*DataGuard());
+ // Sequence lock / mutation counter.
+ flags_internal::SequenceLock seq_lock_;
+
// Optional flag's callback and absl::Mutex to guard the invocations.
FlagCallback* callback_ ABSL_GUARDED_BY(*DataGuard());
// Either a pointer to the function generating the default value based on the
@@ -649,7 +618,9 @@
impl_.AssertValidType(base_internal::FastTypeId<T>(), &GenRuntimeTypeId<T>);
#endif
- if (!value_.Get(u.value)) impl_.Read(&u.value);
+ if (ABSL_PREDICT_FALSE(!value_.Get(impl_.seq_lock_, u.value))) {
+ impl_.Read(&u.value);
+ }
return std::move(u.value);
}
void Set(const T& v) {
@@ -750,8 +721,9 @@
template <typename T, bool do_register>
class FlagRegistrar {
public:
- explicit FlagRegistrar(Flag<T>& flag) : flag_(flag) {
- if (do_register) flags_internal::RegisterCommandLineFlag(flag_.impl_);
+ explicit FlagRegistrar(Flag<T>& flag, const char* filename) : flag_(flag) {
+ if (do_register)
+ flags_internal::RegisterCommandLineFlag(flag_.impl_, filename);
}
FlagRegistrar OnUpdate(FlagCallbackFunc cb) && {
diff --git a/absl/flags/internal/registry.h b/absl/flags/internal/registry.h
index a8d9eb9..4b68c85 100644
--- a/absl/flags/internal/registry.h
+++ b/absl/flags/internal/registry.h
@@ -36,7 +36,7 @@
//-----------------------------------------------------------------------------
-bool RegisterCommandLineFlag(CommandLineFlag&);
+bool RegisterCommandLineFlag(CommandLineFlag&, const char* filename);
void FinalizeRegistry();
diff --git a/absl/flags/internal/sequence_lock.h b/absl/flags/internal/sequence_lock.h
new file mode 100644
index 0000000..807b2a7
--- /dev/null
+++ b/absl/flags/internal/sequence_lock.h
@@ -0,0 +1,187 @@
+//
+// Copyright 2020 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_FLAGS_INTERNAL_SEQUENCE_LOCK_H_
+#define ABSL_FLAGS_INTERNAL_SEQUENCE_LOCK_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+
+#include "absl/base/optimization.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace flags_internal {
+
+// Align 'x' up to the nearest 'align' bytes.
+inline constexpr size_t AlignUp(size_t x, size_t align) {
+ return align * ((x + align - 1) / align);
+}
+
+// A SequenceLock implements lock-free reads. A sequence counter is incremented
+// before and after each write, and readers access the counter before and after
+// accessing the protected data. If the counter is verified to not change during
+// the access, and the sequence counter value was even, then the reader knows
+// that the read was race-free and valid. Otherwise, the reader must fall back
+// to a Mutex-based code path.
+//
+// This particular SequenceLock starts in an "uninitialized" state in which
+// TryRead() returns false. It must be enabled by calling MarkInitialized().
+// This serves as a marker that the associated flag value has not yet been
+// initialized and a slow path needs to be taken.
+//
+// The memory reads and writes protected by this lock must use the provided
+// `TryRead()` and `Write()` functions. These functions behave similarly to
+// `memcpy()`, with one oddity: the protected data must be an array of
+// `std::atomic<int64>`. This is to comply with the C++ standard, which
+// considers data races on non-atomic objects to be undefined behavior. See "Can
+// Seqlocks Get Along With Programming Language Memory Models?"[1] by Hans J.
+// Boehm for more details.
+//
+// [1] https://www.hpl.hp.com/techreports/2012/HPL-2012-68.pdf
+class SequenceLock {
+ public:
+ constexpr SequenceLock() : lock_(kUninitialized) {}
+
+ // Mark that this lock is ready for use.
+ void MarkInitialized() {
+ assert(lock_.load(std::memory_order_relaxed) == kUninitialized);
+ lock_.store(0, std::memory_order_release);
+ }
+
+ // Copy "size" bytes of data from "src" to "dst", protected as a read-side
+ // critical section of the sequence lock.
+ //
+ // Unlike traditional sequence lock implementations which loop until getting a
+ // clean read, this implementation returns false in the case of concurrent
+ // calls to `Write`. In such a case, the caller should fall back to a
+ // locking-based slow path.
+ //
+ // Returns false if the sequence lock was not yet marked as initialized.
+ //
+ // NOTE: If this returns false, "dst" may be overwritten with undefined
+ // (potentially uninitialized) data.
+ bool TryRead(void* dst, const std::atomic<uint64_t>* src, size_t size) const {
+ // Acquire barrier ensures that no loads done by f() are reordered
+ // above the first load of the sequence counter.
+ int64_t seq_before = lock_.load(std::memory_order_acquire);
+ if (ABSL_PREDICT_FALSE(seq_before & 1) == 1) return false;
+ RelaxedCopyFromAtomic(dst, src, size);
+ // Another acquire fence ensures that the load of 'lock_' below is
+ // strictly ordered after the RelaxedCopyToAtomic call above.
+ std::atomic_thread_fence(std::memory_order_acquire);
+ int64_t seq_after = lock_.load(std::memory_order_relaxed);
+ return ABSL_PREDICT_TRUE(seq_before == seq_after);
+ }
+
+ // Copy "size" bytes from "src" to "dst" as a write-side critical section
+ // of the sequence lock. Any concurrent readers will be forced to retry
+ // until they get a read that does not conflict with this write.
+ //
+ // This call must be externally synchronized against other calls to Write,
+ // but may proceed concurrently with reads.
+ void Write(std::atomic<uint64_t>* dst, const void* src, size_t size) {
+ // We can use relaxed instructions to increment the counter since we
+ // are extenally synchronized. The std::atomic_thread_fence below
+ // ensures that the counter updates don't get interleaved with the
+ // copy to the data.
+ int64_t orig_seq = lock_.load(std::memory_order_relaxed);
+ assert((orig_seq & 1) == 0); // Must be initially unlocked.
+ lock_.store(orig_seq + 1, std::memory_order_relaxed);
+
+ // We put a release fence between update to lock_ and writes to shared data.
+ // Thus all stores to shared data are effectively release operations and
+ // update to lock_ above cannot be re-ordered past any of them. Note that
+ // this barrier is not for the fetch_add above. A release barrier for the
+ // fetch_add would be before it, not after.
+ std::atomic_thread_fence(std::memory_order_release);
+ RelaxedCopyToAtomic(dst, src, size);
+ // "Release" semantics ensure that none of the writes done by
+ // RelaxedCopyToAtomic() can be reordered after the following modification.
+ lock_.store(orig_seq + 2, std::memory_order_release);
+ }
+
+ // Return the number of times that Write() has been called.
+ //
+ // REQUIRES: This must be externally synchronized against concurrent calls to
+ // `Write()` or `IncrementModificationCount()`.
+ // REQUIRES: `MarkInitialized()` must have been previously called.
+ int64_t ModificationCount() const {
+ int64_t val = lock_.load(std::memory_order_relaxed);
+ assert(val != kUninitialized && (val & 1) == 0);
+ return val / 2;
+ }
+
+ // REQUIRES: This must be externally synchronized against concurrent calls to
+ // `Write()` or `ModificationCount()`.
+ // REQUIRES: `MarkInitialized()` must have been previously called.
+ void IncrementModificationCount() {
+ int64_t val = lock_.load(std::memory_order_relaxed);
+ assert(val != kUninitialized);
+ lock_.store(val + 2, std::memory_order_relaxed);
+ }
+
+ private:
+ // Perform the equivalent of "memcpy(dst, src, size)", but using relaxed
+ // atomics.
+ static void RelaxedCopyFromAtomic(void* dst, const std::atomic<uint64_t>* src,
+ size_t size) {
+ char* dst_byte = static_cast<char*>(dst);
+ while (size >= sizeof(uint64_t)) {
+ uint64_t word = src->load(std::memory_order_relaxed);
+ std::memcpy(dst_byte, &word, sizeof(word));
+ dst_byte += sizeof(word);
+ src++;
+ size -= sizeof(word);
+ }
+ if (size > 0) {
+ uint64_t word = src->load(std::memory_order_relaxed);
+ std::memcpy(dst_byte, &word, size);
+ }
+ }
+
+ // Perform the equivalent of "memcpy(dst, src, size)", but using relaxed
+ // atomics.
+ static void RelaxedCopyToAtomic(std::atomic<uint64_t>* dst, const void* src,
+ size_t size) {
+ const char* src_byte = static_cast<const char*>(src);
+ while (size >= sizeof(uint64_t)) {
+ uint64_t word;
+ std::memcpy(&word, src_byte, sizeof(word));
+ dst->store(word, std::memory_order_relaxed);
+ src_byte += sizeof(word);
+ dst++;
+ size -= sizeof(word);
+ }
+ if (size > 0) {
+ uint64_t word = 0;
+ std::memcpy(&word, src_byte, size);
+ dst->store(word, std::memory_order_relaxed);
+ }
+ }
+
+ static constexpr int64_t kUninitialized = -1;
+ std::atomic<int64_t> lock_;
+};
+
+} // namespace flags_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_FLAGS_INTERNAL_SEQUENCE_LOCK_H_
diff --git a/absl/flags/internal/sequence_lock_test.cc b/absl/flags/internal/sequence_lock_test.cc
new file mode 100644
index 0000000..c3ec372
--- /dev/null
+++ b/absl/flags/internal/sequence_lock_test.cc
@@ -0,0 +1,169 @@
+// Copyright 2020 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "absl/flags/internal/sequence_lock.h"
+
+#include <algorithm>
+#include <atomic>
+#include <thread> // NOLINT(build/c++11)
+#include <tuple>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "absl/base/internal/sysinfo.h"
+#include "absl/container/fixed_array.h"
+#include "absl/time/clock.h"
+
+namespace {
+
+namespace flags = absl::flags_internal;
+
+class ConcurrentSequenceLockTest
+ : public testing::TestWithParam<std::tuple<int, int>> {
+ public:
+ ConcurrentSequenceLockTest()
+ : buf_bytes_(std::get<0>(GetParam())),
+ num_threads_(std::get<1>(GetParam())) {}
+
+ protected:
+ const int buf_bytes_;
+ const int num_threads_;
+};
+
+TEST_P(ConcurrentSequenceLockTest, ReadAndWrite) {
+ const int buf_words =
+ flags::AlignUp(buf_bytes_, sizeof(uint64_t)) / sizeof(uint64_t);
+
+ // The buffer that will be protected by the SequenceLock.
+ absl::FixedArray<std::atomic<uint64_t>> protected_buf(buf_words);
+ for (auto& v : protected_buf) v = -1;
+
+ flags::SequenceLock seq_lock;
+ std::atomic<bool> stop{false};
+ std::atomic<int64_t> bad_reads{0};
+ std::atomic<int64_t> good_reads{0};
+ std::atomic<int64_t> unsuccessful_reads{0};
+
+ // Start a bunch of threads which read 'protected_buf' under the sequence
+ // lock. The main thread will concurrently update 'protected_buf'. The updates
+ // always consist of an array of identical integers. The reader ensures that
+ // any data it reads matches that pattern (i.e. the reads are not "torn").
+ std::vector<std::thread> threads;
+ for (int i = 0; i < num_threads_; i++) {
+ threads.emplace_back([&]() {
+ absl::FixedArray<char> local_buf(buf_bytes_);
+ while (!stop.load(std::memory_order_relaxed)) {
+ if (seq_lock.TryRead(local_buf.data(), protected_buf.data(),
+ buf_bytes_)) {
+ bool good = true;
+ for (const auto& v : local_buf) {
+ if (v != local_buf[0]) good = false;
+ }
+ if (good) {
+ good_reads.fetch_add(1, std::memory_order_relaxed);
+ } else {
+ bad_reads.fetch_add(1, std::memory_order_relaxed);
+ }
+ } else {
+ unsuccessful_reads.fetch_add(1, std::memory_order_relaxed);
+ }
+ }
+ });
+ }
+ while (unsuccessful_reads.load(std::memory_order_relaxed) < num_threads_) {
+ absl::SleepFor(absl::Milliseconds(1));
+ }
+ seq_lock.MarkInitialized();
+
+ // Run a maximum of 5 seconds. On Windows, the scheduler behavior seems
+ // somewhat unfair and without an explicit timeout for this loop, the tests
+ // can run a long time.
+ absl::Time deadline = absl::Now() + absl::Seconds(5);
+ for (int i = 0; i < 100 && absl::Now() < deadline; i++) {
+ absl::FixedArray<char> writer_buf(buf_bytes_);
+ for (auto& v : writer_buf) v = i;
+ seq_lock.Write(protected_buf.data(), writer_buf.data(), buf_bytes_);
+ absl::SleepFor(absl::Microseconds(10));
+ }
+ stop.store(true, std::memory_order_relaxed);
+ for (auto& t : threads) t.join();
+ ASSERT_GE(good_reads, 0);
+ ASSERT_EQ(bad_reads, 0);
+}
+
+// Simple helper for generating a range of thread counts.
+// Generates [low, low*scale, low*scale^2, ...high)
+// (even if high is between low*scale^k and low*scale^(k+1)).
+std::vector<int> MultiplicativeRange(int low, int high, int scale) {
+ std::vector<int> result;
+ for (int current = low; current < high; current *= scale) {
+ result.push_back(current);
+ }
+ result.push_back(high);
+ return result;
+}
+
+#ifndef ABSL_HAVE_THREAD_SANITIZER
+const int kMaxThreads = absl::base_internal::NumCPUs();
+#else
+// With TSAN, a lot of threads contending for atomic access on the sequence
+// lock make this test run too slowly.
+const int kMaxThreads = std::min(absl::base_internal::NumCPUs(), 4);
+#endif
+
+// Return all of the interesting buffer sizes worth testing:
+// powers of two and adjacent values.
+std::vector<int> InterestingBufferSizes() {
+ std::vector<int> ret;
+ for (int v : MultiplicativeRange(1, 128, 2)) {
+ ret.push_back(v);
+ if (v > 1) {
+ ret.push_back(v - 1);
+ }
+ ret.push_back(v + 1);
+ }
+ return ret;
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ TestManyByteSizes, ConcurrentSequenceLockTest,
+ testing::Combine(
+ // Buffer size (bytes).
+ testing::ValuesIn(InterestingBufferSizes()),
+ // Number of reader threads.
+ testing::ValuesIn(MultiplicativeRange(1, kMaxThreads, 2))));
+
+// Simple single-threaded test, parameterized by the size of the buffer to be
+// protected.
+class SequenceLockTest : public testing::TestWithParam<int> {};
+
+TEST_P(SequenceLockTest, SingleThreaded) {
+ const int size = GetParam();
+ absl::FixedArray<std::atomic<uint64_t>> protected_buf(
+ flags::AlignUp(size, sizeof(uint64_t)) / sizeof(uint64_t));
+
+ flags::SequenceLock seq_lock;
+ seq_lock.MarkInitialized();
+
+ std::vector<char> src_buf(size, 'x');
+ seq_lock.Write(protected_buf.data(), src_buf.data(), size);
+
+ std::vector<char> dst_buf(size, '0');
+ ASSERT_TRUE(seq_lock.TryRead(dst_buf.data(), protected_buf.data(), size));
+ ASSERT_EQ(src_buf, dst_buf);
+}
+INSTANTIATE_TEST_SUITE_P(TestManyByteSizes, SequenceLockTest,
+ // Buffer size (bytes).
+ testing::Range(1, 128));
+
+} // namespace
diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc
index 7557322..a588c7f 100644
--- a/absl/flags/internal/usage.cc
+++ b/absl/flags/internal/usage.cc
@@ -37,26 +37,26 @@
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
-ABSL_FLAG(bool, help, false,
- "show help on important flags for this binary [tip: all flags can "
- "have two dashes]");
-ABSL_FLAG(bool, helpfull, false, "show help on all flags");
-ABSL_FLAG(bool, helpshort, false,
- "show help on only the main module for this program");
-ABSL_FLAG(bool, helppackage, false,
- "show help on all modules in the main package");
-ABSL_FLAG(bool, version, false, "show version and build info and exit");
-ABSL_FLAG(bool, only_check_args, false, "exit after checking all flags");
-ABSL_FLAG(std::string, helpon, "",
- "show help on the modules named by this flag value");
-ABSL_FLAG(std::string, helpmatch, "",
- "show help on modules whose name contains the specified substr");
+// Dummy global variables to prevent anyone else defining these.
+bool FLAGS_help = false;
+bool FLAGS_helpfull = false;
+bool FLAGS_helpshort = false;
+bool FLAGS_helppackage = false;
+bool FLAGS_version = false;
+bool FLAGS_only_check_args = false;
+bool FLAGS_helpon = false;
+bool FLAGS_helpmatch = false;
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace flags_internal {
namespace {
+using PerFlagFilter = std::function<bool(const absl::CommandLineFlag&)>;
+
+// Maximum length size in a human readable format.
+constexpr size_t kHrfMaxLineLength = 80;
+
// This class is used to emit an XML element with `tag` and `text`.
// It adds opening and closing tags and escapes special characters in the text.
// For example:
@@ -109,9 +109,12 @@
public:
// Pretty printer holds on to the std::ostream& reference to direct an output
// to that stream.
- FlagHelpPrettyPrinter(int max_line_len, std::ostream& out)
+ FlagHelpPrettyPrinter(size_t max_line_len, size_t min_line_len,
+ size_t wrapped_line_indent, std::ostream& out)
: out_(out),
max_line_len_(max_line_len),
+ min_line_len_(min_line_len),
+ wrapped_line_indent_(wrapped_line_indent),
line_len_(0),
first_line_(true) {}
@@ -165,13 +168,12 @@
void StartLine() {
if (first_line_) {
- out_ << " ";
- line_len_ = 4;
+ line_len_ = min_line_len_;
first_line_ = false;
} else {
- out_ << " ";
- line_len_ = 6;
+ line_len_ = min_line_len_ + wrapped_line_indent_;
}
+ out_ << std::string(line_len_, ' ');
}
void EndLine() {
out_ << '\n';
@@ -180,13 +182,15 @@
private:
std::ostream& out_;
- const int max_line_len_;
- int line_len_;
+ const size_t max_line_len_;
+ const size_t min_line_len_;
+ const size_t wrapped_line_indent_;
+ size_t line_len_;
bool first_line_;
};
void FlagHelpHumanReadable(const CommandLineFlag& flag, std::ostream& out) {
- FlagHelpPrettyPrinter printer(80, out); // Max line length is 80.
+ FlagHelpPrettyPrinter printer(kHrfMaxLineLength, 4, 2, out);
// Flag name.
printer.Write(absl::StrCat("--", flag.Name()));
@@ -222,7 +226,7 @@
// If a flag's help message has been stripped (e.g. by adding '#define
// STRIP_FLAG_HELP 1' then this flag will not be displayed by '--help'
// and its variants.
-void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
+void FlagsHelpImpl(std::ostream& out, PerFlagFilter filter_cb,
HelpFormat format, absl::string_view program_usage_message) {
if (format == HelpFormat::kHumanReadable) {
out << flags_internal::ShortProgramInvocationName() << ": "
@@ -257,10 +261,10 @@
// If the flag has been stripped, pretend that it doesn't exist.
if (flag.Help() == flags_internal::kStrippedFlagHelp) return;
- std::string flag_filename = flag.Filename();
-
// Make sure flag satisfies the filter
- if (!filter_cb || !filter_cb(flag_filename)) return;
+ if (!filter_cb(flag)) return;
+
+ std::string flag_filename = flag.Filename();
matching_flags[std::string(flags_internal::Package(flag_filename))]
[flag_filename]
@@ -290,15 +294,34 @@
}
if (format == HelpFormat::kHumanReadable) {
+ FlagHelpPrettyPrinter printer(kHrfMaxLineLength, 0, 0, out);
+
if (filter_cb && matching_flags.empty()) {
- out << " No modules matched: use -helpfull\n";
+ printer.Write("No flags matched.\n", true);
}
+ printer.EndLine();
+ printer.Write(
+ "Try --helpfull to get a list of all flags or --help=substring "
+ "shows help for flags which include specified substring in either "
+ "in the name, or description or path.\n",
+ true);
} else {
// The end of the document.
out << "</AllFlags>\n";
}
}
+void FlagsHelpImpl(std::ostream& out,
+ flags_internal::FlagKindFilter filename_filter_cb,
+ HelpFormat format, absl::string_view program_usage_message) {
+ FlagsHelpImpl(
+ out,
+ [&](const absl::CommandLineFlag& flag) {
+ return filename_filter_cb && filename_filter_cb(flag.Filename());
+ },
+ format, program_usage_message);
+}
+
} // namespace
// --------------------------------------------------------------------
@@ -310,7 +333,7 @@
}
// --------------------------------------------------------------------
-// Produces the help messages for all flags matching the filter.
+// Produces the help messages for all flags matching the filename filter.
// If filter is empty produces help messages for all flags.
void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format,
absl::string_view program_usage_message) {
@@ -325,68 +348,171 @@
// If so, handles them appropriately.
int HandleUsageFlags(std::ostream& out,
absl::string_view program_usage_message) {
- if (absl::GetFlag(FLAGS_helpshort)) {
- flags_internal::FlagsHelpImpl(
- out, flags_internal::GetUsageConfig().contains_helpshort_flags,
- HelpFormat::kHumanReadable, program_usage_message);
- return 1;
- }
+ switch (GetFlagsHelpMode()) {
+ case HelpMode::kNone:
+ break;
+ case HelpMode::kImportant:
+ flags_internal::FlagsHelpImpl(
+ out, flags_internal::GetUsageConfig().contains_help_flags,
+ GetFlagsHelpFormat(), program_usage_message);
+ return 1;
- if (absl::GetFlag(FLAGS_helpfull)) {
- // show all options
- flags_internal::FlagsHelp(out, "", HelpFormat::kHumanReadable,
- program_usage_message);
- return 1;
- }
+ case HelpMode::kShort:
+ flags_internal::FlagsHelpImpl(
+ out, flags_internal::GetUsageConfig().contains_helpshort_flags,
+ GetFlagsHelpFormat(), program_usage_message);
+ return 1;
- if (!absl::GetFlag(FLAGS_helpon).empty()) {
- flags_internal::FlagsHelp(
- out, absl::StrCat("/", absl::GetFlag(FLAGS_helpon), "."),
- HelpFormat::kHumanReadable, program_usage_message);
- return 1;
- }
+ case HelpMode::kFull:
+ flags_internal::FlagsHelp(out, "", GetFlagsHelpFormat(),
+ program_usage_message);
+ return 1;
- if (!absl::GetFlag(FLAGS_helpmatch).empty()) {
- flags_internal::FlagsHelp(out, absl::GetFlag(FLAGS_helpmatch),
- HelpFormat::kHumanReadable,
- program_usage_message);
- return 1;
- }
+ case HelpMode::kPackage:
+ flags_internal::FlagsHelpImpl(
+ out, flags_internal::GetUsageConfig().contains_helppackage_flags,
+ GetFlagsHelpFormat(), program_usage_message);
- if (absl::GetFlag(FLAGS_help)) {
- flags_internal::FlagsHelpImpl(
- out, flags_internal::GetUsageConfig().contains_help_flags,
- HelpFormat::kHumanReadable, program_usage_message);
+ return 1;
- out << "\nTry --helpfull to get a list of all flags.\n";
+ case HelpMode::kMatch: {
+ std::string substr = GetFlagsHelpMatchSubstr();
+ if (substr.empty()) {
+ // show all options
+ flags_internal::FlagsHelp(out, substr, GetFlagsHelpFormat(),
+ program_usage_message);
+ } else {
+ auto filter_cb = [&substr](const absl::CommandLineFlag& flag) {
+ if (absl::StrContains(flag.Name(), substr)) return true;
+ if (absl::StrContains(flag.Filename(), substr)) return true;
+ if (absl::StrContains(flag.Help(), substr)) return true;
- return 1;
- }
+ return false;
+ };
+ flags_internal::FlagsHelpImpl(
+ out, filter_cb, HelpFormat::kHumanReadable, program_usage_message);
+ }
- if (absl::GetFlag(FLAGS_helppackage)) {
- flags_internal::FlagsHelpImpl(
- out, flags_internal::GetUsageConfig().contains_helppackage_flags,
- HelpFormat::kHumanReadable, program_usage_message);
+ return 1;
+ }
+ case HelpMode::kVersion:
+ if (flags_internal::GetUsageConfig().version_string)
+ out << flags_internal::GetUsageConfig().version_string();
+ // Unlike help, we may be asking for version in a script, so return 0
+ return 0;
- out << "\nTry --helpfull to get a list of all flags.\n";
-
- return 1;
- }
-
- if (absl::GetFlag(FLAGS_version)) {
- if (flags_internal::GetUsageConfig().version_string)
- out << flags_internal::GetUsageConfig().version_string();
- // Unlike help, we may be asking for version in a script, so return 0
- return 0;
- }
-
- if (absl::GetFlag(FLAGS_only_check_args)) {
- return 0;
+ case HelpMode::kOnlyCheckArgs:
+ return 0;
}
return -1;
}
+// --------------------------------------------------------------------
+// Globals representing usage reporting flags
+
+namespace {
+
+ABSL_CONST_INIT absl::Mutex help_attributes_guard(absl::kConstInit);
+ABSL_CONST_INIT std::string* match_substr
+ ABSL_GUARDED_BY(help_attributes_guard) = nullptr;
+ABSL_CONST_INIT HelpMode help_mode ABSL_GUARDED_BY(help_attributes_guard) =
+ HelpMode::kNone;
+ABSL_CONST_INIT HelpFormat help_format ABSL_GUARDED_BY(help_attributes_guard) =
+ HelpFormat::kHumanReadable;
+
+} // namespace
+
+std::string GetFlagsHelpMatchSubstr() {
+ absl::MutexLock l(&help_attributes_guard);
+ if (match_substr == nullptr) return "";
+ return *match_substr;
+}
+
+void SetFlagsHelpMatchSubstr(absl::string_view substr) {
+ absl::MutexLock l(&help_attributes_guard);
+ if (match_substr == nullptr) match_substr = new std::string;
+ match_substr->assign(substr.data(), substr.size());
+}
+
+HelpMode GetFlagsHelpMode() {
+ absl::MutexLock l(&help_attributes_guard);
+ return help_mode;
+}
+
+void SetFlagsHelpMode(HelpMode mode) {
+ absl::MutexLock l(&help_attributes_guard);
+ help_mode = mode;
+}
+
+HelpFormat GetFlagsHelpFormat() {
+ absl::MutexLock l(&help_attributes_guard);
+ return help_format;
+}
+
+void SetFlagsHelpFormat(HelpFormat format) {
+ absl::MutexLock l(&help_attributes_guard);
+ help_format = format;
+}
+
+// Deduces usage flags from the input argument in a form --name=value or
+// --name. argument is already split into name and value before we call this
+// function.
+bool DeduceUsageFlags(absl::string_view name, absl::string_view value) {
+ if (absl::ConsumePrefix(&name, "help")) {
+ if (name == "") {
+ if (value.empty()) {
+ SetFlagsHelpMode(HelpMode::kImportant);
+ } else {
+ SetFlagsHelpMode(HelpMode::kMatch);
+ SetFlagsHelpMatchSubstr(value);
+ }
+ return true;
+ }
+
+ if (name == "match") {
+ SetFlagsHelpMode(HelpMode::kMatch);
+ SetFlagsHelpMatchSubstr(value);
+ return true;
+ }
+
+ if (name == "on") {
+ SetFlagsHelpMode(HelpMode::kMatch);
+ SetFlagsHelpMatchSubstr(absl::StrCat("/", value, "."));
+ return true;
+ }
+
+ if (name == "full") {
+ SetFlagsHelpMode(HelpMode::kFull);
+ return true;
+ }
+
+ if (name == "short") {
+ SetFlagsHelpMode(HelpMode::kShort);
+ return true;
+ }
+
+ if (name == "package") {
+ SetFlagsHelpMode(HelpMode::kPackage);
+ return true;
+ }
+
+ return false;
+ }
+
+ if (name == "version") {
+ SetFlagsHelpMode(HelpMode::kVersion);
+ return true;
+ }
+
+ if (name == "only_check_args") {
+ SetFlagsHelpMode(HelpMode::kOnlyCheckArgs);
+ return true;
+ }
+
+ return false;
+}
+
} // namespace flags_internal
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/internal/usage.h b/absl/flags/internal/usage.h
index 619ccce..c0bcac5 100644
--- a/absl/flags/internal/usage.h
+++ b/absl/flags/internal/usage.h
@@ -66,17 +66,39 @@
int HandleUsageFlags(std::ostream& out,
absl::string_view program_usage_message);
+// --------------------------------------------------------------------
+// Globals representing usage reporting flags
+
+enum class HelpMode {
+ kNone,
+ kImportant,
+ kShort,
+ kFull,
+ kPackage,
+ kMatch,
+ kVersion,
+ kOnlyCheckArgs
+};
+
+// Returns substring to filter help output (--help=substr argument)
+std::string GetFlagsHelpMatchSubstr();
+// Returns the requested help mode.
+HelpMode GetFlagsHelpMode();
+// Returns the requested help format.
+HelpFormat GetFlagsHelpFormat();
+
+// These are corresponding setters to the attributes above.
+void SetFlagsHelpMatchSubstr(absl::string_view);
+void SetFlagsHelpMode(HelpMode);
+void SetFlagsHelpFormat(HelpFormat);
+
+// Deduces usage flags from the input argument in a form --name=value or
+// --name. argument is already split into name and value before we call this
+// function.
+bool DeduceUsageFlags(absl::string_view name, absl::string_view value);
+
} // namespace flags_internal
ABSL_NAMESPACE_END
} // namespace absl
-ABSL_DECLARE_FLAG(bool, help);
-ABSL_DECLARE_FLAG(bool, helpfull);
-ABSL_DECLARE_FLAG(bool, helpshort);
-ABSL_DECLARE_FLAG(bool, helppackage);
-ABSL_DECLARE_FLAG(bool, version);
-ABSL_DECLARE_FLAG(bool, only_check_args);
-ABSL_DECLARE_FLAG(std::string, helpon);
-ABSL_DECLARE_FLAG(std::string, helpmatch);
-
#endif // ABSL_FLAGS_INTERNAL_USAGE_H_
diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc
index 6e583fb..b5c2487 100644
--- a/absl/flags/internal/usage_test.cc
+++ b/absl/flags/internal/usage_test.cc
@@ -87,6 +87,11 @@
default_config.normalize_filename = &NormalizeFileName;
absl::SetFlagsUsageConfig(default_config);
}
+ ~UsageReportingTest() override {
+ flags::SetFlagsHelpMode(flags::HelpMode::kNone);
+ flags::SetFlagsHelpMatchSubstr("");
+ flags::SetFlagsHelpFormat(flags::HelpFormat::kHumanReadable);
+ }
private:
absl::FlagSaver flag_saver_;
@@ -191,6 +196,10 @@
Some more help.
Even more long long long long long long long long long long long long help
message.); default: "";
+
+Try --helpfull to get a list of all flags or --help=substring shows help for
+flags which include specified substring in either in the name, or description or
+path.
)";
std::stringstream test_buf_01;
@@ -214,7 +223,11 @@
EXPECT_EQ(test_buf_04.str(),
R"(usage_test: Custom usage message
- No modules matched: use -helpfull
+No flags matched.
+
+Try --helpfull to get a list of all flags or --help=substring shows help for
+flags which include specified substring in either in the name, or description or
+path.
)");
std::stringstream test_buf_05;
@@ -226,12 +239,8 @@
absl::StartsWith(test_out_str, "usage_test: Custom usage message"));
EXPECT_TRUE(absl::StrContains(
test_out_str, "Flags from absl/flags/internal/usage_test.cc:"));
- EXPECT_TRUE(absl::StrContains(test_out_str,
- "Flags from absl/flags/internal/usage.cc:"));
EXPECT_TRUE(
absl::StrContains(test_out_str, "-usage_reporting_test_flag_01 "));
- EXPECT_TRUE(absl::StrContains(test_out_str, "-help (show help"))
- << test_out_str;
}
// --------------------------------------------------------------------
@@ -244,7 +253,7 @@
// --------------------------------------------------------------------
TEST_F(UsageReportingTest, TestUsageFlag_helpshort) {
- absl::SetFlag(&FLAGS_helpshort, true);
+ flags::SetFlagsHelpMode(flags::HelpMode::kShort);
std::stringstream test_buf;
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
@@ -267,13 +276,17 @@
Some more help.
Even more long long long long long long long long long long long long help
message.); default: "";
+
+Try --helpfull to get a list of all flags or --help=substring shows help for
+flags which include specified substring in either in the name, or description or
+path.
)");
}
// --------------------------------------------------------------------
-TEST_F(UsageReportingTest, TestUsageFlag_help) {
- absl::SetFlag(&FLAGS_help, true);
+TEST_F(UsageReportingTest, TestUsageFlag_help_simple) {
+ flags::SetFlagsHelpMode(flags::HelpMode::kImportant);
std::stringstream test_buf;
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
@@ -297,14 +310,74 @@
Even more long long long long long long long long long long long long help
message.); default: "";
-Try --helpfull to get a list of all flags.
+Try --helpfull to get a list of all flags or --help=substring shows help for
+flags which include specified substring in either in the name, or description or
+path.
+)");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(UsageReportingTest, TestUsageFlag_help_one_flag) {
+ flags::SetFlagsHelpMode(flags::HelpMode::kMatch);
+ flags::SetFlagsHelpMatchSubstr("usage_reporting_test_flag_06");
+
+ std::stringstream test_buf;
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
+ EXPECT_EQ(test_buf.str(),
+ R"(usage_test: Custom usage message
+
+ Flags from absl/flags/internal/usage_test.cc:
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+
+ Some more help.
+ Even more long long long long long long long long long long long long help
+ message.); default: "";
+
+Try --helpfull to get a list of all flags or --help=substring shows help for
+flags which include specified substring in either in the name, or description or
+path.
+)");
+}
+
+// --------------------------------------------------------------------
+
+TEST_F(UsageReportingTest, TestUsageFlag_help_multiple_flag) {
+ flags::SetFlagsHelpMode(flags::HelpMode::kMatch);
+ flags::SetFlagsHelpMatchSubstr("test_flag");
+
+ std::stringstream test_buf;
+ EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
+ EXPECT_EQ(test_buf.str(),
+ R"(usage_test: Custom usage message
+
+ Flags from absl/flags/internal/usage_test.cc:
+ --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ default: 101;
+ --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ default: false;
+ --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ default: 1.03;
+ --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ default: 1000000000000004;
+ --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ default: UDT{};
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+
+ Some more help.
+ Even more long long long long long long long long long long long long help
+ message.); default: "";
+
+Try --helpfull to get a list of all flags or --help=substring shows help for
+flags which include specified substring in either in the name, or description or
+path.
)");
}
// --------------------------------------------------------------------
TEST_F(UsageReportingTest, TestUsageFlag_helppackage) {
- absl::SetFlag(&FLAGS_helppackage, true);
+ flags::SetFlagsHelpMode(flags::HelpMode::kPackage);
std::stringstream test_buf;
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1);
@@ -328,14 +401,16 @@
Even more long long long long long long long long long long long long help
message.); default: "";
-Try --helpfull to get a list of all flags.
+Try --helpfull to get a list of all flags or --help=substring shows help for
+flags which include specified substring in either in the name, or description or
+path.
)");
}
// --------------------------------------------------------------------
TEST_F(UsageReportingTest, TestUsageFlag_version) {
- absl::SetFlag(&FLAGS_version, true);
+ flags::SetFlagsHelpMode(flags::HelpMode::kVersion);
std::stringstream test_buf;
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0);
@@ -349,7 +424,7 @@
// --------------------------------------------------------------------
TEST_F(UsageReportingTest, TestUsageFlag_only_check_args) {
- absl::SetFlag(&FLAGS_only_check_args, true);
+ flags::SetFlagsHelpMode(flags::HelpMode::kOnlyCheckArgs);
std::stringstream test_buf;
EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0);
@@ -359,17 +434,22 @@
// --------------------------------------------------------------------
TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
- absl::SetFlag(&FLAGS_helpon, "bla-bla");
+ flags::SetFlagsHelpMode(flags::HelpMode::kMatch);
+ flags::SetFlagsHelpMatchSubstr("/bla-bla.");
std::stringstream test_buf_01;
EXPECT_EQ(flags::HandleUsageFlags(test_buf_01, kTestUsageMessage), 1);
EXPECT_EQ(test_buf_01.str(),
R"(usage_test: Custom usage message
- No modules matched: use -helpfull
+No flags matched.
+
+Try --helpfull to get a list of all flags or --help=substring shows help for
+flags which include specified substring in either in the name, or description or
+path.
)");
- absl::SetFlag(&FLAGS_helpon, "usage_test");
+ flags::SetFlagsHelpMatchSubstr("/usage_test.");
std::stringstream test_buf_02;
EXPECT_EQ(flags::HandleUsageFlags(test_buf_02, kTestUsageMessage), 1);
@@ -392,6 +472,10 @@
Some more help.
Even more long long long long long long long long long long long long help
message.); default: "";
+
+Try --helpfull to get a list of all flags or --help=substring shows help for
+flags which include specified substring in either in the name, or description or
+path.
)");
}
diff --git a/absl/flags/marshalling.h b/absl/flags/marshalling.h
index 0b50335..7cbc136 100644
--- a/absl/flags/marshalling.h
+++ b/absl/flags/marshalling.h
@@ -83,7 +83,7 @@
// // AbslParseFlag converts from a string to OutputMode.
// // Must be in same namespace as OutputMode.
//
-// // Parses an OutputMode from the command line flag value `text. Returns
+// // Parses an OutputMode from the command line flag value `text`. Returns
// // `true` and sets `*mode` on success; returns `false` and sets `*error`
// // on failure.
// bool AbslParseFlag(absl::string_view text,
@@ -139,7 +139,7 @@
//
// // Within the implementation, `AbslParseFlag()` will, in turn invoke
// // `absl::ParseFlag()` on its constituent `int` and `std::string` types
-// // (which have built-in Abseil flag support.
+// // (which have built-in Abseil flag support).
//
// bool AbslParseFlag(absl::string_view text, MyFlagType* flag,
// std::string* err) {
diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc
index 1835a83..dd1a679 100644
--- a/absl/flags/parse.cc
+++ b/absl/flags/parse.cc
@@ -713,6 +713,11 @@
std::tie(flag, is_negative) = LocateFlag(flag_name);
if (flag == nullptr) {
+ // Usage flags are not modeled as Abseil flags. Locate them separately.
+ if (flags_internal::DeduceUsageFlags(flag_name, value)) {
+ continue;
+ }
+
if (on_undef_flag != OnUndefinedFlag::kIgnoreUndefined) {
undefined_flag_names.emplace_back(arg_from_argv,
std::string(flag_name));
diff --git a/absl/flags/parse_test.cc b/absl/flags/parse_test.cc
index d35a6e4..41bc0bc 100644
--- a/absl/flags/parse_test.cc
+++ b/absl/flags/parse_test.cc
@@ -28,6 +28,7 @@
#include "absl/flags/declare.h"
#include "absl/flags/flag.h"
#include "absl/flags/internal/parse.h"
+#include "absl/flags/internal/usage.h"
#include "absl/flags/reflection.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
@@ -207,6 +208,9 @@
using testing::ElementsAreArray;
class ParseTest : public testing::Test {
+ public:
+ ~ParseTest() override { flags::SetFlagsHelpMode(flags::HelpMode::kNone); }
+
private:
absl::FlagSaver flag_saver_;
};
@@ -851,7 +855,7 @@
// --------------------------------------------------------------------
-TEST_F(ParseDeathTest, TestHelpFlagHandling) {
+TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) {
const char* in_args1[] = {
"testbin",
"--help",
@@ -870,11 +874,38 @@
flags::UsageFlagsAction::kIgnoreUsage,
flags::OnUndefinedFlag::kAbortIfUndefined);
+ EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 3);
}
// --------------------------------------------------------------------
+TEST_F(ParseDeathTest, TestSubstringHelpFlagHandling) {
+ const char* in_args1[] = {
+ "testbin",
+ "--help=abcd",
+ };
+
+ auto out_args1 = flags::ParseCommandLineImpl(
+ 2, const_cast<char**>(in_args1), flags::ArgvListAction::kRemoveParsedArgs,
+ flags::UsageFlagsAction::kIgnoreUsage,
+ flags::OnUndefinedFlag::kAbortIfUndefined);
+
+ EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kMatch);
+ EXPECT_EQ(flags::GetFlagsHelpMatchSubstr(), "abcd");
+
+ const char* in_args2[] = {"testbin", "--help", "some_positional_arg"};
+
+ auto out_args2 = flags::ParseCommandLineImpl(
+ 3, const_cast<char**>(in_args2), flags::ArgvListAction::kRemoveParsedArgs,
+ flags::UsageFlagsAction::kIgnoreUsage,
+ flags::OnUndefinedFlag::kAbortIfUndefined);
+
+ EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant);
+}
+
+// --------------------------------------------------------------------
+
TEST_F(ParseTest, WasPresentOnCommandLine) {
const char* in_args1[] = {
"testbin", "arg1", "--bool_flag",
diff --git a/absl/flags/reflection.cc b/absl/flags/reflection.cc
index c6bf8aa..0c76110 100644
--- a/absl/flags/reflection.cc
+++ b/absl/flags/reflection.cc
@@ -50,7 +50,7 @@
~FlagRegistry() = default;
// Store a flag in this registry. Takes ownership of *flag.
- void RegisterFlag(CommandLineFlag& flag);
+ void RegisterFlag(CommandLineFlag& flag, const char* filename);
void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); }
void Unlock() ABSL_UNLOCK_FUNCTION(lock_) { lock_.Unlock(); }
@@ -110,7 +110,20 @@
return it != flags_.end() ? it->second : nullptr;
}
-void FlagRegistry::RegisterFlag(CommandLineFlag& flag) {
+void FlagRegistry::RegisterFlag(CommandLineFlag& flag, const char* filename) {
+ if (filename != nullptr &&
+ flag.Filename() != GetUsageConfig().normalize_filename(filename)) {
+ flags_internal::ReportUsageError(
+ absl::StrCat(
+ "Inconsistency between flag object and registration for flag '",
+ flag.Name(),
+ "', likely due to duplicate flags or an ODR violation. Relevant "
+ "files: ",
+ flag.Filename(), " and ", filename),
+ true);
+ std::exit(1);
+ }
+
FlagRegistryLock registry_lock(*this);
std::pair<FlagIterator, bool> ins =
@@ -175,8 +188,8 @@
// --------------------------------------------------------------------
-bool RegisterCommandLineFlag(CommandLineFlag& flag) {
- FlagRegistry::GlobalRegistry().RegisterFlag(flag);
+bool RegisterCommandLineFlag(CommandLineFlag& flag, const char* filename) {
+ FlagRegistry::GlobalRegistry().RegisterFlag(flag, filename);
return true;
}
@@ -266,7 +279,7 @@
static_assert(alignof(RetiredFlagObj) == kRetiredFlagObjAlignment, "");
auto* flag = ::new (static_cast<void*>(buf))
flags_internal::RetiredFlagObj(name, type_id);
- FlagRegistry::GlobalRegistry().RegisterFlag(*flag);
+ FlagRegistry::GlobalRegistry().RegisterFlag(*flag, nullptr);
}
// --------------------------------------------------------------------
@@ -328,7 +341,7 @@
absl::flat_hash_map<absl::string_view, absl::CommandLineFlag*> GetAllFlags() {
absl::flat_hash_map<absl::string_view, absl::CommandLineFlag*> res;
flags_internal::ForEachFlag([&](CommandLineFlag& flag) {
- res.insert({flag.Name(), &flag});
+ if (!flag.IsRetired()) res.insert({flag.Name(), &flag});
});
return res;
}
diff --git a/absl/flags/reflection_test.cc b/absl/flags/reflection_test.cc
index 1a1dcb4..79cfa90 100644
--- a/absl/flags/reflection_test.cc
+++ b/absl/flags/reflection_test.cc
@@ -32,12 +32,8 @@
ABSL_FLAG(std::string, string_flag, "dflt", "string_flag help");
ABSL_RETIRED_FLAG(bool, bool_retired_flag, false, "bool_retired_flag help");
-ABSL_DECLARE_FLAG(bool, help);
-
namespace {
-namespace flags = absl::flags_internal;
-
class ReflectionTest : public testing::Test {
protected:
void SetUp() override { flag_saver_ = absl::make_unique<absl::FlagSaver>(); }
@@ -66,12 +62,9 @@
// --------------------------------------------------------------------
TEST_F(ReflectionTest, TestGetAllFlags) {
- (void)absl::GetFlag(FLAGS_help); // Force linking of usage flags.
-
auto all_flags = absl::GetAllFlags();
EXPECT_NE(all_flags.find("int_flag"), all_flags.end());
- EXPECT_NE(all_flags.find("bool_retired_flag"), all_flags.end());
- EXPECT_NE(all_flags.find("help"), all_flags.end());
+ EXPECT_EQ(all_flags.find("bool_retired_flag"), all_flags.end());
EXPECT_EQ(all_flags.find("some_undefined_flag"), all_flags.end());
std::vector<absl::string_view> flag_names_first_attempt;
diff --git a/absl/flags/usage_config.cc b/absl/flags/usage_config.cc
index ae2f548..5d7426d 100644
--- a/absl/flags/usage_config.cc
+++ b/absl/flags/usage_config.cc
@@ -34,7 +34,8 @@
// Additional report of fatal usage error message before we std::exit. Error is
// fatal if is_fatal argument to ReportUsageError is true.
-ABSL_ATTRIBUTE_WEAK void AbslInternalReportFatalUsageError(absl::string_view) {}
+ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(
+ AbslInternalReportFatalUsageError)(absl::string_view) {}
} // extern "C"
@@ -128,7 +129,7 @@
std::cerr << "ERROR: " << msg << std::endl;
if (is_fatal) {
- AbslInternalReportFatalUsageError(msg);
+ ABSL_INTERNAL_C_SYMBOL(AbslInternalReportFatalUsageError)(msg);
}
}
diff --git a/absl/flags/usage_config.h b/absl/flags/usage_config.h
index 96eecea..ded7030 100644
--- a/absl/flags/usage_config.h
+++ b/absl/flags/usage_config.h
@@ -127,7 +127,8 @@
// Additional report of fatal usage error message before we std::exit. Error is
// fatal if is_fatal argument to ReportUsageError is true.
-void AbslInternalReportFatalUsageError(absl::string_view);
+void ABSL_INTERNAL_C_SYMBOL(AbslInternalReportFatalUsageError)(
+ absl::string_view);
} // extern "C"
diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel
index 5b1e2d0..4b2c220 100644
--- a/absl/hash/BUILD.bazel
+++ b/absl/hash/BUILD.bazel
@@ -37,6 +37,8 @@
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":city",
+ ":wyhash",
+ "//absl/base:config",
"//absl/base:core_headers",
"//absl/base:endian",
"//absl/container:fixed_array",
@@ -81,6 +83,25 @@
],
)
+cc_binary(
+ name = "hash_benchmark",
+ testonly = 1,
+ srcs = ["hash_benchmark.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ tags = ["benchmark"],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":hash",
+ "//absl/base:core_headers",
+ "//absl/random",
+ "//absl/strings",
+ "//absl/strings:cord",
+ "//absl/strings:cord_test_helpers",
+ "@com_github_google_benchmark//:benchmark_main",
+ ],
+)
+
cc_library(
name = "spy_hash_state",
testonly = 1,
@@ -120,3 +141,30 @@
"@com_google_googletest//:gtest_main",
],
)
+
+cc_library(
+ name = "wyhash",
+ srcs = ["internal/wyhash.cc"],
+ hdrs = ["internal/wyhash.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ "//absl/base:config",
+ "//absl/base:endian",
+ "//absl/numeric:int128",
+ ],
+)
+
+cc_test(
+ name = "wyhash_test",
+ srcs = ["internal/wyhash_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":wyhash",
+ "//absl/strings",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/absl/hash/BUILD.gn b/absl/hash/BUILD.gn
index ca4a05b..3e239b4 100644
--- a/absl/hash/BUILD.gn
+++ b/absl/hash/BUILD.gn
@@ -13,6 +13,8 @@
public = [ "hash.h" ]
deps = [
":city",
+ ":wyhash",
+ "//third_party/abseil-cpp/absl/base:config",
"//third_party/abseil-cpp/absl/base:core_headers",
"//third_party/abseil-cpp/absl/base:endian",
"//third_party/abseil-cpp/absl/container:fixed_array",
@@ -57,3 +59,14 @@
"//third_party/abseil-cpp/absl/base:endian",
]
}
+
+absl_source_set("wyhash") {
+ public = [ "internal/wyhash.h" ]
+ sources = [ "internal/wyhash.cc" ]
+ deps = [
+ "//third_party/abseil-cpp/absl/base:config",
+ "//third_party/abseil-cpp/absl/base:endian",
+ "//third_party/abseil-cpp/absl/numeric:int128",
+ ]
+ visibility = [ ":*" ]
+}
diff --git a/absl/hash/CMakeLists.txt b/absl/hash/CMakeLists.txt
index 61365e9..b43bfa5 100644
--- a/absl/hash/CMakeLists.txt
+++ b/absl/hash/CMakeLists.txt
@@ -24,7 +24,9 @@
"internal/hash.h"
COPTS
${ABSL_DEFAULT_COPTS}
- DEPS
+ DEPS
+ absl::city
+ absl::config
absl::core_headers
absl::endian
absl::fixed_array
@@ -34,7 +36,7 @@
absl::optional
absl::variant
absl::utility
- absl::city
+ absl::wyhash
PUBLIC
)
@@ -114,3 +116,30 @@
gmock_main
)
+absl_cc_library(
+ NAME
+ wyhash
+ HDRS
+ "internal/wyhash.h"
+ SRCS
+ "internal/wyhash.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
+ absl::endian
+ absl::int128
+)
+
+absl_cc_test(
+ NAME
+ wyhash_test
+ SRCS
+ "internal/wyhash_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::wyhash
+ absl::strings
+ gmock_main
+)
diff --git a/absl/hash/hash_benchmark.cc b/absl/hash/hash_benchmark.cc
new file mode 100644
index 0000000..d498ac2
--- /dev/null
+++ b/absl/hash/hash_benchmark.cc
@@ -0,0 +1,254 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+#include <type_traits>
+#include <typeindex>
+#include <utility>
+#include <vector>
+
+#include "absl/base/attributes.h"
+#include "absl/hash/hash.h"
+#include "absl/random/random.h"
+#include "absl/strings/cord.h"
+#include "absl/strings/cord_test_helpers.h"
+#include "absl/strings/string_view.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+using absl::Hash;
+
+template <template <typename> class H, typename T>
+void RunBenchmark(benchmark::State& state, T value) {
+ H<T> h;
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(value);
+ benchmark::DoNotOptimize(h(value));
+ }
+}
+
+} // namespace
+
+template <typename T>
+using AbslHash = absl::Hash<T>;
+
+class TypeErasedInterface {
+ public:
+ virtual ~TypeErasedInterface() = default;
+
+ template <typename H>
+ friend H AbslHashValue(H state, const TypeErasedInterface& wrapper) {
+ state = H::combine(std::move(state), std::type_index(typeid(wrapper)));
+ wrapper.HashValue(absl::HashState::Create(&state));
+ return state;
+ }
+
+ private:
+ virtual void HashValue(absl::HashState state) const = 0;
+};
+
+template <typename T>
+struct TypeErasedAbslHash {
+ class Wrapper : public TypeErasedInterface {
+ public:
+ explicit Wrapper(const T& value) : value_(value) {}
+
+ private:
+ void HashValue(absl::HashState state) const override {
+ absl::HashState::combine(std::move(state), value_);
+ }
+
+ const T& value_;
+ };
+
+ size_t operator()(const T& value) {
+ return absl::Hash<Wrapper>{}(Wrapper(value));
+ }
+};
+
+template <typename FuncType>
+inline FuncType* ODRUseFunction(FuncType* ptr) {
+ volatile FuncType* dummy = ptr;
+ return dummy;
+}
+
+absl::Cord FlatCord(size_t size) {
+ absl::Cord result(std::string(size, 'a'));
+ result.Flatten();
+ return result;
+}
+
+absl::Cord FragmentedCord(size_t size) {
+ const size_t orig_size = size;
+ std::vector<std::string> chunks;
+ size_t chunk_size = std::max<size_t>(1, size / 10);
+ while (size > chunk_size) {
+ chunks.push_back(std::string(chunk_size, 'a'));
+ size -= chunk_size;
+ }
+ if (size > 0) {
+ chunks.push_back(std::string(size, 'a'));
+ }
+ absl::Cord result = absl::MakeFragmentedCord(chunks);
+ (void) orig_size;
+ assert(result.size() == orig_size);
+ return result;
+}
+
+// Generates a benchmark and a codegen method for the provided types. The
+// codegen method provides a well known entrypoint for dumping assembly.
+#define MAKE_BENCHMARK(hash, name, ...) \
+ namespace { \
+ void BM_##hash##_##name(benchmark::State& state) { \
+ RunBenchmark<hash>(state, __VA_ARGS__); \
+ } \
+ BENCHMARK(BM_##hash##_##name); \
+ } \
+ size_t Codegen##hash##name(const decltype(__VA_ARGS__)& arg); \
+ size_t Codegen##hash##name(const decltype(__VA_ARGS__)& arg) { \
+ return hash<decltype(__VA_ARGS__)>{}(arg); \
+ } \
+ bool absl_hash_test_odr_use##hash##name = \
+ ODRUseFunction(&Codegen##hash##name);
+
+MAKE_BENCHMARK(AbslHash, Int32, int32_t{});
+MAKE_BENCHMARK(AbslHash, Int64, int64_t{});
+MAKE_BENCHMARK(AbslHash, Double, 1.2);
+MAKE_BENCHMARK(AbslHash, DoubleZero, 0.0);
+MAKE_BENCHMARK(AbslHash, PairInt32Int32, std::pair<int32_t, int32_t>{});
+MAKE_BENCHMARK(AbslHash, PairInt64Int64, std::pair<int64_t, int64_t>{});
+MAKE_BENCHMARK(AbslHash, TupleInt32BoolInt64,
+ std::tuple<int32_t, bool, int64_t>{});
+MAKE_BENCHMARK(AbslHash, String_0, std::string());
+MAKE_BENCHMARK(AbslHash, String_10, std::string(10, 'a'));
+MAKE_BENCHMARK(AbslHash, String_30, std::string(30, 'a'));
+MAKE_BENCHMARK(AbslHash, String_90, std::string(90, 'a'));
+MAKE_BENCHMARK(AbslHash, String_200, std::string(200, 'a'));
+MAKE_BENCHMARK(AbslHash, String_5000, std::string(5000, 'a'));
+MAKE_BENCHMARK(AbslHash, Cord_Flat_0, absl::Cord());
+MAKE_BENCHMARK(AbslHash, Cord_Flat_10, FlatCord(10));
+MAKE_BENCHMARK(AbslHash, Cord_Flat_30, FlatCord(30));
+MAKE_BENCHMARK(AbslHash, Cord_Flat_90, FlatCord(90));
+MAKE_BENCHMARK(AbslHash, Cord_Flat_200, FlatCord(200));
+MAKE_BENCHMARK(AbslHash, Cord_Flat_5000, FlatCord(5000));
+MAKE_BENCHMARK(AbslHash, Cord_Fragmented_200, FragmentedCord(200));
+MAKE_BENCHMARK(AbslHash, Cord_Fragmented_5000, FragmentedCord(5000));
+MAKE_BENCHMARK(AbslHash, VectorInt64_10, std::vector<int64_t>(10));
+MAKE_BENCHMARK(AbslHash, VectorInt64_100, std::vector<int64_t>(100));
+MAKE_BENCHMARK(AbslHash, VectorDouble_10, std::vector<double>(10, 1.1));
+MAKE_BENCHMARK(AbslHash, VectorDouble_100, std::vector<double>(100, 1.1));
+MAKE_BENCHMARK(AbslHash, PairStringString_0,
+ std::make_pair(std::string(), std::string()));
+MAKE_BENCHMARK(AbslHash, PairStringString_10,
+ std::make_pair(std::string(10, 'a'), std::string(10, 'b')));
+MAKE_BENCHMARK(AbslHash, PairStringString_30,
+ std::make_pair(std::string(30, 'a'), std::string(30, 'b')));
+MAKE_BENCHMARK(AbslHash, PairStringString_90,
+ std::make_pair(std::string(90, 'a'), std::string(90, 'b')));
+MAKE_BENCHMARK(AbslHash, PairStringString_200,
+ std::make_pair(std::string(200, 'a'), std::string(200, 'b')));
+MAKE_BENCHMARK(AbslHash, PairStringString_5000,
+ std::make_pair(std::string(5000, 'a'), std::string(5000, 'b')));
+
+MAKE_BENCHMARK(TypeErasedAbslHash, Int32, int32_t{});
+MAKE_BENCHMARK(TypeErasedAbslHash, Int64, int64_t{});
+MAKE_BENCHMARK(TypeErasedAbslHash, PairInt32Int32,
+ std::pair<int32_t, int32_t>{});
+MAKE_BENCHMARK(TypeErasedAbslHash, PairInt64Int64,
+ std::pair<int64_t, int64_t>{});
+MAKE_BENCHMARK(TypeErasedAbslHash, TupleInt32BoolInt64,
+ std::tuple<int32_t, bool, int64_t>{});
+MAKE_BENCHMARK(TypeErasedAbslHash, String_0, std::string());
+MAKE_BENCHMARK(TypeErasedAbslHash, String_10, std::string(10, 'a'));
+MAKE_BENCHMARK(TypeErasedAbslHash, String_30, std::string(30, 'a'));
+MAKE_BENCHMARK(TypeErasedAbslHash, String_90, std::string(90, 'a'));
+MAKE_BENCHMARK(TypeErasedAbslHash, String_200, std::string(200, 'a'));
+MAKE_BENCHMARK(TypeErasedAbslHash, String_5000, std::string(5000, 'a'));
+MAKE_BENCHMARK(TypeErasedAbslHash, VectorDouble_10,
+ std::vector<double>(10, 1.1));
+MAKE_BENCHMARK(TypeErasedAbslHash, VectorDouble_100,
+ std::vector<double>(100, 1.1));
+
+// The latency benchmark attempts to model the speed of the hash function in
+// production. When a hash function is used for hashtable lookups it is rarely
+// used to hash N items in a tight loop nor on constant sized strings. Instead,
+// after hashing there is a potential equality test plus a (usually) large
+// amount of user code. To simulate this effectively we introduce a data
+// dependency between elements we hash by using the hash of the Nth element as
+// the selector of the N+1th element to hash. This isolates the hash function
+// code much like in production. As a bonus we use the hash to generate strings
+// of size [1,N] (instead of fixed N) to disable perfect branch predictions in
+// hash function implementations.
+namespace {
+// 16kb fits in L1 cache of most CPUs we care about. Keeping memory latency low
+// will allow us to attribute most time to CPU which means more accurate
+// measurements.
+static constexpr size_t kEntropySize = 16 << 10;
+static char entropy[kEntropySize + 1024];
+ABSL_ATTRIBUTE_UNUSED static const bool kInitialized = [] {
+ absl::BitGen gen;
+ static_assert(sizeof(entropy) % sizeof(uint64_t) == 0, "");
+ for (int i = 0; i != sizeof(entropy); i += sizeof(uint64_t)) {
+ auto rand = absl::Uniform<uint64_t>(gen);
+ memcpy(&entropy[i], &rand, sizeof(uint64_t));
+ }
+ return true;
+}();
+} // namespace
+
+template <class T>
+struct PodRand {
+ static_assert(std::is_pod<T>::value, "");
+ static_assert(kEntropySize + sizeof(T) < sizeof(entropy), "");
+
+ T Get(size_t i) const {
+ T v;
+ memcpy(&v, &entropy[i % kEntropySize], sizeof(T));
+ return v;
+ }
+};
+
+template <size_t N>
+struct StringRand {
+ static_assert(kEntropySize + N < sizeof(entropy), "");
+
+ absl::string_view Get(size_t i) const {
+ // This has a small bias towards small numbers. Because max N is ~200 this
+ // is very small and prefer to be very fast instead of absolutely accurate.
+ // Also we pass N = 2^K+1 so that mod reduces to a bitand.
+ size_t s = (i % (N - 1)) + 1;
+ return {&entropy[i % kEntropySize], s};
+ }
+};
+
+#define MAKE_LATENCY_BENCHMARK(hash, name, ...) \
+ namespace { \
+ void BM_latency_##hash##_##name(benchmark::State& state) { \
+ __VA_ARGS__ r; \
+ hash<decltype(r.Get(0))> h; \
+ size_t i = 871401241; \
+ for (auto _ : state) { \
+ benchmark::DoNotOptimize(i = h(r.Get(i))); \
+ } \
+ } \
+ BENCHMARK(BM_latency_##hash##_##name); \
+ } // namespace
+
+MAKE_LATENCY_BENCHMARK(AbslHash, Int32, PodRand<int32_t>);
+MAKE_LATENCY_BENCHMARK(AbslHash, Int64, PodRand<int64_t>);
+MAKE_LATENCY_BENCHMARK(AbslHash, String9, StringRand<9>);
+MAKE_LATENCY_BENCHMARK(AbslHash, String33, StringRand<33>);
+MAKE_LATENCY_BENCHMARK(AbslHash, String65, StringRand<65>);
+MAKE_LATENCY_BENCHMARK(AbslHash, String257, StringRand<257>);
diff --git a/absl/hash/internal/city.cc b/absl/hash/internal/city.cc
index 58d4bcb..5460134 100644
--- a/absl/hash/internal/city.cc
+++ b/absl/hash/internal/city.cc
@@ -200,10 +200,6 @@
static uint64_t ShiftMix(uint64_t val) { return val ^ (val >> 47); }
-static uint64_t HashLen16(uint64_t u, uint64_t v) {
- return Hash128to64(uint128(u, v));
-}
-
static uint64_t HashLen16(uint64_t u, uint64_t v, uint64_t mul) {
// Murmur-inspired hashing.
uint64_t a = (u ^ v) * mul;
@@ -214,6 +210,11 @@
return b;
}
+static uint64_t HashLen16(uint64_t u, uint64_t v) {
+ const uint64_t kMul = 0x9ddfea08eb382d69ULL;
+ return HashLen16(u, v, kMul);
+}
+
static uint64_t HashLen0to16(const char *s, size_t len) {
if (len >= 8) {
uint64_t mul = k2 + len * 2;
diff --git a/absl/hash/internal/city.h b/absl/hash/internal/city.h
index 9c1e7a5..393da0b 100644
--- a/absl/hash/internal/city.h
+++ b/absl/hash/internal/city.h
@@ -56,11 +56,6 @@
ABSL_NAMESPACE_BEGIN
namespace hash_internal {
-typedef std::pair<uint64_t, uint64_t> uint128;
-
-inline uint64_t Uint128Low64(const uint128 &x) { return x.first; }
-inline uint64_t Uint128High64(const uint128 &x) { return x.second; }
-
// Hash function for a byte array.
uint64_t CityHash64(const char *s, size_t len);
@@ -76,19 +71,6 @@
// Hash function for a byte array. Most useful in 32-bit binaries.
uint32_t CityHash32(const char *s, size_t len);
-// Hash 128 input bits down to 64 bits of output.
-// This is intended to be a reasonably good hash function.
-inline uint64_t Hash128to64(const uint128 &x) {
- // Murmur-inspired hashing.
- const uint64_t kMul = 0x9ddfea08eb382d69ULL;
- uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
- a ^= (a >> 47);
- uint64_t b = (Uint128High64(x) ^ a) * kMul;
- b ^= (b >> 47);
- b *= kMul;
- return b;
-}
-
} // namespace hash_internal
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/hash/internal/hash.cc b/absl/hash/internal/hash.cc
index b44ecb3..1433eb9 100644
--- a/absl/hash/internal/hash.cc
+++ b/absl/hash/internal/hash.cc
@@ -18,9 +18,9 @@
ABSL_NAMESPACE_BEGIN
namespace hash_internal {
-uint64_t CityHashState::CombineLargeContiguousImpl32(uint64_t state,
- const unsigned char* first,
- size_t len) {
+uint64_t HashState::CombineLargeContiguousImpl32(uint64_t state,
+ const unsigned char* first,
+ size_t len) {
while (len >= PiecewiseChunkSize()) {
state =
Mix(state, absl::hash_internal::CityHash32(reinterpret_cast<const char*>(first),
@@ -33,13 +33,11 @@
std::integral_constant<int, 4>{});
}
-uint64_t CityHashState::CombineLargeContiguousImpl64(uint64_t state,
- const unsigned char* first,
- size_t len) {
+uint64_t HashState::CombineLargeContiguousImpl64(uint64_t state,
+ const unsigned char* first,
+ size_t len) {
while (len >= PiecewiseChunkSize()) {
- state =
- Mix(state, absl::hash_internal::CityHash64(reinterpret_cast<const char*>(first),
- PiecewiseChunkSize()));
+ state = Mix(state, Hash64(first, PiecewiseChunkSize()));
len -= PiecewiseChunkSize();
first += PiecewiseChunkSize();
}
@@ -48,7 +46,24 @@
std::integral_constant<int, 8>{});
}
-ABSL_CONST_INIT const void* const CityHashState::kSeed = &kSeed;
+ABSL_CONST_INIT const void* const HashState::kSeed = &kSeed;
+
+// The salt array used by Wyhash. This array is NOT the mechanism used to make
+// absl::Hash non-deterministic between program invocations. See `Seed()` for
+// that mechanism.
+//
+// Any random values are fine. These values are just digits from the decimal
+// part of pi.
+// https://en.wikipedia.org/wiki/Nothing-up-my-sleeve_number
+constexpr uint64_t kWyhashSalt[5] = {
+ uint64_t{0x243F6A8885A308D3}, uint64_t{0x13198A2E03707344},
+ uint64_t{0xA4093822299F31D0}, uint64_t{0x082EFA98EC4E6C89},
+ uint64_t{0x452821E638D01377},
+};
+
+uint64_t HashState::WyhashImpl(const unsigned char* data, size_t len) {
+ return Wyhash(data, len, Seed(), kWyhashSalt);
+}
} // namespace hash_internal
ABSL_NAMESPACE_END
diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h
index b0132da..7fb0af0 100644
--- a/absl/hash/internal/hash.h
+++ b/absl/hash/internal/hash.h
@@ -38,9 +38,11 @@
#include <utility>
#include <vector>
-#include "absl/base/internal/endian.h"
+#include "absl/base/config.h"
+#include "absl/base/internal/unaligned_access.h"
#include "absl/base/port.h"
#include "absl/container/fixed_array.h"
+#include "absl/hash/internal/wyhash.h"
#include "absl/meta/type_traits.h"
#include "absl/numeric/int128.h"
#include "absl/strings/string_view.h"
@@ -712,9 +714,8 @@
struct is_hashable
: std::integral_constant<bool, HashSelect::template Apply<T>::value> {};
-// CityHashState
-class ABSL_DLL CityHashState
- : public HashStateBase<CityHashState> {
+// HashState
+class ABSL_DLL HashState : public HashStateBase<HashState> {
// absl::uint128 is not an alias or a thin wrapper around the intrinsic.
// We use the intrinsic when available to improve performance.
#ifdef ABSL_HAVE_INTRINSIC_INT128
@@ -733,23 +734,22 @@
public:
// Move only
- CityHashState(CityHashState&&) = default;
- CityHashState& operator=(CityHashState&&) = default;
+ HashState(HashState&&) = default;
+ HashState& operator=(HashState&&) = default;
- // CityHashState::combine_contiguous()
+ // HashState::combine_contiguous()
//
// Fundamental base case for hash recursion: mixes the given range of bytes
// into the hash state.
- static CityHashState combine_contiguous(CityHashState hash_state,
- const unsigned char* first,
- size_t size) {
- return CityHashState(
+ static HashState combine_contiguous(HashState hash_state,
+ const unsigned char* first, size_t size) {
+ return HashState(
CombineContiguousImpl(hash_state.state_, first, size,
std::integral_constant<int, sizeof(size_t)>{}));
}
- using CityHashState::HashStateBase::combine_contiguous;
+ using HashState::HashStateBase::combine_contiguous;
- // CityHashState::hash()
+ // HashState::hash()
//
// For performance reasons in non-opt mode, we specialize this for
// integral types.
@@ -761,24 +761,24 @@
return static_cast<size_t>(Mix(Seed(), static_cast<uint64_t>(value)));
}
- // Overload of CityHashState::hash()
+ // Overload of HashState::hash()
template <typename T, absl::enable_if_t<!IntegralFastPath<T>::value, int> = 0>
static size_t hash(const T& value) {
- return static_cast<size_t>(combine(CityHashState{}, value).state_);
+ return static_cast<size_t>(combine(HashState{}, value).state_);
}
private:
// Invoked only once for a given argument; that plus the fact that this is
// move-only ensures that there is only one non-moved-from object.
- CityHashState() : state_(Seed()) {}
+ HashState() : state_(Seed()) {}
// Workaround for MSVC bug.
// We make the type copyable to fix the calling convention, even though we
// never actually copy it. Keep it private to not affect the public API of the
// type.
- CityHashState(const CityHashState&) = default;
+ HashState(const HashState&) = default;
- explicit CityHashState(uint64_t state) : state_(state) {}
+ explicit HashState(uint64_t state) : state_(state) {}
// Implementation of the base case for combine_contiguous where we actually
// mix the bytes into the state.
@@ -791,7 +791,8 @@
static uint64_t CombineContiguousImpl(uint64_t state,
const unsigned char* first, size_t len,
std::integral_constant<int, 8>
- /* sizeof_size_t*/);
+ /* sizeof_size_t */);
+
// Slow dispatch path for calls to CombineContiguousImpl with a size argument
// larger than PiecewiseChunkSize(). Has the same effect as calling
@@ -804,26 +805,54 @@
size_t len);
// Reads 9 to 16 bytes from p.
- // The first 8 bytes are in .first, the rest (zero padded) bytes are in
- // .second.
+ // The least significant 8 bytes are in .first, the rest (zero padded) bytes
+ // are in .second.
static std::pair<uint64_t, uint64_t> Read9To16(const unsigned char* p,
size_t len) {
- uint64_t high = little_endian::Load64(p + len - 8);
- return {little_endian::Load64(p), high >> (128 - len * 8)};
+ uint64_t low_mem = absl::base_internal::UnalignedLoad64(p);
+ uint64_t high_mem = absl::base_internal::UnalignedLoad64(p + len - 8);
+#ifdef ABSL_IS_LITTLE_ENDIAN
+ uint64_t most_significant = high_mem;
+ uint64_t least_significant = low_mem;
+#else
+ uint64_t most_significant = low_mem;
+ uint64_t least_significant = high_mem;
+#endif
+ return {least_significant, most_significant >> (128 - len * 8)};
}
// Reads 4 to 8 bytes from p. Zero pads to fill uint64_t.
static uint64_t Read4To8(const unsigned char* p, size_t len) {
- return (static_cast<uint64_t>(little_endian::Load32(p + len - 4))
- << (len - 4) * 8) |
- little_endian::Load32(p);
+ uint32_t low_mem = absl::base_internal::UnalignedLoad32(p);
+ uint32_t high_mem = absl::base_internal::UnalignedLoad32(p + len - 4);
+#ifdef ABSL_IS_LITTLE_ENDIAN
+ uint32_t most_significant = high_mem;
+ uint32_t least_significant = low_mem;
+#else
+ uint32_t most_significant = low_mem;
+ uint32_t least_significant = high_mem;
+#endif
+ return (static_cast<uint64_t>(most_significant) << (len - 4) * 8) |
+ least_significant;
}
// Reads 1 to 3 bytes from p. Zero pads to fill uint32_t.
static uint32_t Read1To3(const unsigned char* p, size_t len) {
- return static_cast<uint32_t>((p[0]) | //
- (p[len / 2] << (len / 2 * 8)) | //
- (p[len - 1] << ((len - 1) * 8)));
+ unsigned char mem0 = p[0];
+ unsigned char mem1 = p[len / 2];
+ unsigned char mem2 = p[len - 1];
+#ifdef ABSL_IS_LITTLE_ENDIAN
+ unsigned char significant2 = mem2;
+ unsigned char significant1 = mem1;
+ unsigned char significant0 = mem0;
+#else
+ unsigned char significant2 = mem0;
+ unsigned char significant1 = mem1;
+ unsigned char significant0 = mem2;
+#endif
+ return static_cast<uint32_t>(significant0 | //
+ (significant1 << (len / 2 * 8)) | //
+ (significant2 << ((len - 1) * 8)));
}
ABSL_ATTRIBUTE_ALWAYS_INLINE static uint64_t Mix(uint64_t state, uint64_t v) {
@@ -838,6 +867,19 @@
return static_cast<uint64_t>(m ^ (m >> (sizeof(m) * 8 / 2)));
}
+ // An extern to avoid bloat on a direct call to Wyhash() with fixed values for
+ // both the seed and salt parameters.
+ static uint64_t WyhashImpl(const unsigned char* data, size_t len);
+
+ ABSL_ATTRIBUTE_ALWAYS_INLINE static uint64_t Hash64(const unsigned char* data,
+ size_t len) {
+#ifdef ABSL_HAVE_INTRINSIC_INT128
+ return WyhashImpl(data, len);
+#else
+ return absl::hash_internal::CityHash64(reinterpret_cast<const char*>(data), len);
+#endif
+ }
+
// Seed()
//
// A non-deterministic seed.
@@ -869,8 +911,8 @@
uint64_t state_;
};
-// CityHashState::CombineContiguousImpl()
-inline uint64_t CityHashState::CombineContiguousImpl(
+// HashState::CombineContiguousImpl()
+inline uint64_t HashState::CombineContiguousImpl(
uint64_t state, const unsigned char* first, size_t len,
std::integral_constant<int, 4> /* sizeof_size_t */) {
// For large values we use CityHash, for small ones we just use a
@@ -892,18 +934,18 @@
return Mix(state, v);
}
-// Overload of CityHashState::CombineContiguousImpl()
-inline uint64_t CityHashState::CombineContiguousImpl(
+// Overload of HashState::CombineContiguousImpl()
+inline uint64_t HashState::CombineContiguousImpl(
uint64_t state, const unsigned char* first, size_t len,
std::integral_constant<int, 8> /* sizeof_size_t */) {
- // For large values we use CityHash, for small ones we just use a
- // multiplicative hash.
+ // For large values we use Wyhash or CityHash depending on the platform, for
+ // small ones we just use a multiplicative hash.
uint64_t v;
if (len > 16) {
if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) {
return CombineLargeContiguousImpl64(state, first, len);
}
- v = absl::hash_internal::CityHash64(reinterpret_cast<const char*>(first), len);
+ v = Hash64(first, len);
} else if (len > 8) {
auto p = Read9To16(first, len);
state = Mix(state, p.first);
@@ -934,7 +976,7 @@
template <typename T>
struct HashImpl {
- size_t operator()(const T& value) const { return CityHashState::hash(value); }
+ size_t operator()(const T& value) const { return HashState::hash(value); }
};
template <typename T>
diff --git a/absl/hash/internal/wyhash.cc b/absl/hash/internal/wyhash.cc
new file mode 100644
index 0000000..642bde4
--- /dev/null
+++ b/absl/hash/internal/wyhash.cc
@@ -0,0 +1,111 @@
+// Copyright 2020 The Abseil Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/hash/internal/wyhash.h"
+
+#include "absl/base/internal/unaligned_access.h"
+#include "absl/numeric/int128.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace hash_internal {
+
+static uint64_t WyhashMix(uint64_t v0, uint64_t v1) {
+ absl::uint128 p = v0;
+ p *= v1;
+ return absl::Uint128Low64(p) ^ absl::Uint128High64(p);
+}
+
+uint64_t Wyhash(const void* data, size_t len, uint64_t seed,
+ const uint64_t salt[]) {
+ const uint8_t* ptr = static_cast<const uint8_t*>(data);
+ uint64_t starting_length = static_cast<uint64_t>(len);
+ uint64_t current_state = seed ^ salt[0];
+
+ if (len > 64) {
+ // If we have more than 64 bytes, we're going to handle chunks of 64
+ // bytes at a time. We're going to build up two separate hash states
+ // which we will then hash together.
+ uint64_t duplicated_state = current_state;
+
+ do {
+ uint64_t a = absl::base_internal::UnalignedLoad64(ptr);
+ uint64_t b = absl::base_internal::UnalignedLoad64(ptr + 8);
+ uint64_t c = absl::base_internal::UnalignedLoad64(ptr + 16);
+ uint64_t d = absl::base_internal::UnalignedLoad64(ptr + 24);
+ uint64_t e = absl::base_internal::UnalignedLoad64(ptr + 32);
+ uint64_t f = absl::base_internal::UnalignedLoad64(ptr + 40);
+ uint64_t g = absl::base_internal::UnalignedLoad64(ptr + 48);
+ uint64_t h = absl::base_internal::UnalignedLoad64(ptr + 56);
+
+ uint64_t cs0 = WyhashMix(a ^ salt[1], b ^ current_state);
+ uint64_t cs1 = WyhashMix(c ^ salt[2], d ^ current_state);
+ current_state = (cs0 ^ cs1);
+
+ uint64_t ds0 = WyhashMix(e ^ salt[3], f ^ duplicated_state);
+ uint64_t ds1 = WyhashMix(g ^ salt[4], h ^ duplicated_state);
+ duplicated_state = (ds0 ^ ds1);
+
+ ptr += 64;
+ len -= 64;
+ } while (len > 64);
+
+ current_state = current_state ^ duplicated_state;
+ }
+
+ // We now have a data `ptr` with at most 64 bytes and the current state
+ // of the hashing state machine stored in current_state.
+ while (len > 16) {
+ uint64_t a = absl::base_internal::UnalignedLoad64(ptr);
+ uint64_t b = absl::base_internal::UnalignedLoad64(ptr + 8);
+
+ current_state = WyhashMix(a ^ salt[1], b ^ current_state);
+
+ ptr += 16;
+ len -= 16;
+ }
+
+ // We now have a data `ptr` with at most 16 bytes.
+ uint64_t a = 0;
+ uint64_t b = 0;
+ if (len > 8) {
+ // When we have at least 9 and at most 16 bytes, set A to the first 64
+ // bits of the input and B to the last 64 bits of the input. Yes, they will
+ // overlap in the middle if we are working with less than the full 16
+ // bytes.
+ a = absl::base_internal::UnalignedLoad64(ptr);
+ b = absl::base_internal::UnalignedLoad64(ptr + len - 8);
+ } else if (len > 3) {
+ // If we have at least 4 and at most 8 bytes, set A to the first 32
+ // bits and B to the last 32 bits.
+ a = absl::base_internal::UnalignedLoad32(ptr);
+ b = absl::base_internal::UnalignedLoad32(ptr + len - 4);
+ } else if (len > 0) {
+ // If we have at least 1 and at most 3 bytes, read all of the provided
+ // bits into A, with some adjustments.
+ a = ((ptr[0] << 16) | (ptr[len >> 1] << 8) | ptr[len - 1]);
+ b = 0;
+ } else {
+ a = 0;
+ b = 0;
+ }
+
+ uint64_t w = WyhashMix(a ^ salt[1], b ^ current_state);
+ uint64_t z = salt[1] ^ starting_length;
+ return WyhashMix(w, z);
+}
+
+} // namespace hash_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/hash/internal/wyhash.h b/absl/hash/internal/wyhash.h
new file mode 100644
index 0000000..4aff4e9
--- /dev/null
+++ b/absl/hash/internal/wyhash.h
@@ -0,0 +1,48 @@
+// Copyright 2020 The Abseil Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// This file provides the Google-internal implementation of the Wyhash
+// algorithm.
+//
+// Wyhash is a fast hash function for hash tables, the fastest we've currently
+// (late 2020) found that passes the SMHasher tests. The algorithm relies on
+// intrinsic 128-bit multiplication for speed. This is not meant to be secure -
+// just fast.
+
+#ifndef ABSL_HASH_INTERNAL_WYHASH_H_
+#define ABSL_HASH_INTERNAL_WYHASH_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace hash_internal {
+
+// Hash function for a byte array. A 64-bit seed and a set of five 64-bit
+// integers are hashed into the result.
+//
+// To allow all hashable types (including string_view and Span) to depend on
+// this algoritm, we keep the API low-level, with as few dependencies as
+// possible.
+uint64_t Wyhash(const void* data, size_t len, uint64_t seed,
+ const uint64_t salt[5]);
+
+} // namespace hash_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_HASH_INTERNAL_WYHASH_H_
diff --git a/absl/hash/internal/wyhash_test.cc b/absl/hash/internal/wyhash_test.cc
new file mode 100644
index 0000000..9fb06d2
--- /dev/null
+++ b/absl/hash/internal/wyhash_test.cc
@@ -0,0 +1,486 @@
+// Copyright 2020 The Abseil Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/hash/internal/wyhash.h"
+
+#include "absl/strings/escaping.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+static const uint64_t kCurrentSeed = 0;
+static const uint64_t kSalt[5] = {0xa0761d6478bd642f, 0xe7037ed1a0b428dbl,
+ 0x8ebc6af09c88c6e3, 0x589965cc75374cc3l,
+ 0x1d8e4e27c47d124f};
+
+// Note: We don't account for endianness, so the values here are only correct if
+// you're also running on a little endian platform.
+
+TEST(WyhashTest, EmptyString) {
+ const std::string s = "";
+ EXPECT_EQ(
+ absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt),
+ 4808886099364463827);
+}
+
+TEST(WyhashTest, Spaces) {
+ const std::string s = " ";
+ EXPECT_EQ(
+ absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt),
+ 1686201463024549249);
+}
+
+TEST(WyhashTest, RepeatingString) {
+ const std::string s = "aaaa";
+ EXPECT_EQ(
+ absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt),
+ 6646112255271966632);
+}
+
+TEST(WyhashTest, HexString) {
+ const std::string small = "\x01\x02\x03";
+ const std::string med = "\x01\x02\x03\x04";
+
+ EXPECT_EQ(absl::hash_internal::Wyhash(small.c_str(), small.length(),
+ kCurrentSeed, kSalt),
+ 11989428023081740911ULL);
+ EXPECT_EQ(absl::hash_internal::Wyhash(med.c_str(), med.length(), kCurrentSeed,
+ kSalt),
+ 9765997711188871556ULL);
+}
+
+TEST(WyhashTest, Words) {
+ const std::string s = "third_party|wyhash|64";
+ EXPECT_EQ(
+ absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt),
+ 3702018632387611330);
+}
+
+TEST(WyhashTest, LongString) {
+ const std::string s =
+ "AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz"
+ "0123456789AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOp"
+ "QrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEf"
+ "GhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz012345"
+ "6789AbCdEfGhIjKlMnOpQrStUvWxYz0123456789";
+
+ EXPECT_EQ(
+ absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt),
+ 9245411362605796064ULL);
+}
+
+TEST(WyhashTest, BigReference) {
+ struct ExpectedResult {
+ absl::string_view base64_data;
+ uint64_t seed;
+ uint64_t hash;
+ } expected_results[] = {
+ {"", uint64_t{0xec42b7ab404b8acb}, uint64_t{0xe5a40d39ab796423}},
+ {"Zw==", uint64_t{0xeeee074043a3ee0f}, uint64_t{0xa6564b468248c683}},
+ {"xmk=", uint64_t{0x857902089c393de}, uint64_t{0xef192f401b116e1c}},
+ {"c1H/", uint64_t{0x993df040024ca3af}, uint64_t{0xbe8dc0c54617639d}},
+ {"SuwpzQ==", uint64_t{0xc4e4c2acea740e96}, uint64_t{0x93d7f665b5521c8e}},
+ {"uqvy++M=", uint64_t{0x6a214b3db872d0cf}, uint64_t{0x646d70bb42445f28}},
+ {"RnzCVPgb", uint64_t{0x44343db6a89dba4d}, uint64_t{0x96a7b1e3cc9bd426}},
+ {"6OeNdlouYw==", uint64_t{0x77b5d6d1ae1dd483},
+ uint64_t{0x76020289ab0790c4}},
+ {"M5/JmmYyDbc=", uint64_t{0x89ab8ecb44d221f1},
+ uint64_t{0x39f842e4133b9b44}},
+ {"MVijWiVdBRdY", uint64_t{0x60244b17577ca81b},
+ uint64_t{0x2b8d7047be4bcaab}},
+ {"6V7Uq7LNxpu0VA==", uint64_t{0x59a08dcee0717067},
+ uint64_t{0x99628abef6716a97}},
+ {"EQ6CdEEhPdyHcOk=", uint64_t{0xf5f20db3ade57396},
+ uint64_t{0x4432e02ba42b2740}},
+ {"PqFB4fxnPgF+l+rc", uint64_t{0xbf8dee0751ad3efb},
+ uint64_t{0x74d810efcad7918a}},
+ {"a5aPOFwq7LA7+zKvPA==", uint64_t{0x6b7a06b268d63e30},
+ uint64_t{0x88c84e986002507f}},
+ {"VOwY21wCGv5D+/qqOvs=", uint64_t{0xb8c37f0ae0f54c82},
+ uint64_t{0x4f99acf193cf39b9}},
+ {"KdHmBTx8lHXYvmGJ+Vy7", uint64_t{0x9fcbed0c38e50eef},
+ uint64_t{0xd90e7a3655891e37}},
+ {"qJkPlbHr8bMF7/cA6aE65Q==", uint64_t{0x2af4bade1d8e3a1d},
+ uint64_t{0x3bb378b1d4df8fcf}},
+ {"ygvL0EhHZL0fIx6oHHtkxRQ=", uint64_t{0x714e3aa912da2f2c},
+ uint64_t{0xf78e94045c052d47}},
+ {"c1rFXkt5YztwZCQRngncqtSs", uint64_t{0xf5ee75e3cbb82c1c},
+ uint64_t{0x26da0b2130da6b40}},
+ {"8hsQrzszzeNQSEcVXLtvIhm6mw==", uint64_t{0x620e7007321b93b9},
+ uint64_t{0x30b4d426af8c6986}},
+ {"ffUL4RocfyP4KfikGxO1yk7omDI=", uint64_t{0xc08528cac2e551fc},
+ uint64_t{0x5413b4aaf3baaeae}},
+ {"OOB5TT00vF9Od/rLbAWshiErqhpV", uint64_t{0x6a1debf9cc3ad39},
+ uint64_t{0x756ab265370a1597}},
+ {"or5wtXM7BFzTNpSzr+Lw5J5PMhVJ/Q==", uint64_t{0x7e0a3c88111fc226},
+ uint64_t{0xdaf5f4b7d09814fb}},
+ {"gk6pCHDUsoopVEiaCrzVDhioRKxb844=", uint64_t{0x1301fef15df39edb},
+ uint64_t{0x8f874ae37742b75e}},
+ {"TNctmwlC5QbEM6/No4R/La3UdkfeMhzs", uint64_t{0x64e181f3d5817ab},
+ uint64_t{0x8fecd03956121ce8}},
+ {"SsQw9iAjhWz7sgcE9OwLuSC6hsM+BfHs2Q==", uint64_t{0xafafc44961078ecb},
+ uint64_t{0x229c292ea7a08285}},
+ {"ZzO3mVCj4xTT2TT3XqDyEKj2BZQBvrS8RHg=", uint64_t{0x4f7bb45549250094},
+ uint64_t{0xbb4bf0692d14bae}},
+ {"+klp5iPQGtppan5MflEls0iEUzqU+zGZkDJX", uint64_t{0xa30061abaa2818c},
+ uint64_t{0x207b24ca3bdac1db}},
+ {"RO6bvOnlJc8I9eniXlNgqtKy0IX6VNg16NRmgg==", uint64_t{0xd902ee3e44a5705f},
+ uint64_t{0x64f6cd6745d3825b}},
+ {"ZJjZqId1ZXBaij9igClE3nyliU5XWdNRrayGlYA=", uint64_t{0x316d36da516f583},
+ uint64_t{0xa2b2e1656b58df1e}},
+ {"7BfkhfGMDGbxfMB8uyL85GbaYQtjr2K8g7RpLzr/", uint64_t{0x402d83f9f834f616},
+ uint64_t{0xd01d30d9ee7a148}},
+ {"rycWk6wHH7htETQtje9PidS2YzXBx+Qkg2fY7ZYS7A==",
+ uint64_t{0x9c604164c016b72c}, uint64_t{0x1cb4cd00ab804e3b}},
+ {"RTkC2OUK+J13CdGllsH0H5WqgspsSa6QzRZouqx6pvI=",
+ uint64_t{0x3f4507e01f9e73ba}, uint64_t{0x4697f2637fd90999}},
+ {"tKjKmbLCNyrLCM9hycOAXm4DKNpM12oZ7dLTmUx5iwAi",
+ uint64_t{0xc3fe0d5be8d2c7c7}, uint64_t{0x8383a756b5688c07}},
+ {"VprUGNH+5NnNRaORxgH/ySrZFQFDL+4VAodhfBNinmn8cg==",
+ uint64_t{0x531858a40bfa7ea1}, uint64_t{0x695c29cb3696a975}},
+ {"gc1xZaY+q0nPcUvOOnWnT3bqfmT/geth/f7Dm2e/DemMfk4=",
+ uint64_t{0x86689478a7a7e8fa}, uint64_t{0xda2e5a5a5e971521}},
+ {"Mr35fIxqx1ukPAL0su1yFuzzAU3wABCLZ8+ZUFsXn47UmAph",
+ uint64_t{0x4ec948b8e7f27288}, uint64_t{0x7935d4befa056b2b}},
+ {"A9G8pw2+m7+rDtWYAdbl8tb2fT7FFo4hLi2vAsa5Y8mKH3CX3g==",
+ uint64_t{0xce46c7213c10032}, uint64_t{0x38dd541ca95420fe}},
+ {"DFaJGishGwEHDdj9ixbCoaTjz9KS0phLNWHVVdFsM93CvPft3hM=",
+ uint64_t{0xf63e96ee6f32a8b6}, uint64_t{0xcc06c7a4963f967f}},
+ {"7+Ugx+Kr3aRNgYgcUxru62YkTDt5Hqis+2po81hGBkcrJg4N0uuy",
+ uint64_t{0x1cfe85e65fc5225}, uint64_t{0xbf0f6f66e232fb20}},
+ {"H2w6O8BUKqu6Tvj2xxaecxEI2wRgIgqnTTG1WwOgDSINR13Nm4d4Vg==",
+ uint64_t{0x45c474f1cee1d2e8}, uint64_t{0xf7efb32d373fe71a}},
+ {"1XBMnIbqD5jy65xTDaf6WtiwtdtQwv1dCVoqpeKj+7cTR1SaMWMyI04=",
+ uint64_t{0x6e024e14015f329c}, uint64_t{0xe2e64634b1c12660}},
+ {"znZbdXG2TSFrKHEuJc83gPncYpzXGbAebUpP0XxzH0rpe8BaMQ17nDbt",
+ uint64_t{0x760c40502103ae1c}, uint64_t{0x285b8fd1638e306d}},
+ {"ylu8Atu13j1StlcC1MRMJJXIl7USgDDS22HgVv0WQ8hx/8pNtaiKB17hCQ==",
+ uint64_t{0x17fd05c3c560c320}, uint64_t{0x658e8a4e3b714d6c}},
+ {"M6ZVVzsd7vAvbiACSYHioH/440dp4xG2mLlBnxgiqEvI/aIEGpD0Sf4VS0g=",
+ uint64_t{0x8b34200a6f8e90d9}, uint64_t{0xf391fb968e0eb398}},
+ {"li3oFSXLXI+ubUVGJ4blP6mNinGKLHWkvGruun85AhVn6iuMtocbZPVhqxzn",
+ uint64_t{0x6be89e50818bdf69}, uint64_t{0x744a9ea0cc144bf2}},
+ {"kFuQHuUCqBF3Tc3hO4dgdIp223ShaCoog48d5Do5zMqUXOh5XpGK1t5XtxnfGA==",
+ uint64_t{0xfb389773315b47d8}, uint64_t{0x12636f2be11012f1}},
+ {"jWmOad0v0QhXVJd1OdGuBZtDYYS8wBVHlvOeTQx9ZZnm8wLEItPMeihj72E0nWY=",
+ uint64_t{0x4f2512a23f61efee}, uint64_t{0x29c57de825948f80}},
+ {"z+DHU52HaOQdW4JrZwDQAebEA6rm13Zg/9lPYA3txt3NjTBqFZlOMvTRnVzRbl23",
+ uint64_t{0x59ccd92fc16c6fda}, uint64_t{0x58c6f99ab0d1c021}},
+ {"MmBiGDfYeTayyJa/tVycg+rN7f9mPDFaDc+23j0TlW9094er0ADigsl4QX7V3gG/qw==",
+ uint64_t{0x25c5a7f5bd330919}, uint64_t{0x13e7b5a7b82fe3bb}},
+ {"774RK+9rOL4iFvs1q2qpo/JVc/I39buvNjqEFDtDvyoB0FXxPI2vXqOrk08VPfIHkmU=",
+ uint64_t{0x51df4174d34c97d7}, uint64_t{0x10fbc87901e02b63}},
+ {"+slatXiQ7/2lK0BkVUI1qzNxOOLP3I1iK6OfHaoxgqT63FpzbElwEXSwdsryq3UlHK0I",
+ uint64_t{0x80ce6d76f89cb57}, uint64_t{0xa24c9184901b748b}},
+ {"64mVTbQ47dHjHlOHGS/hjJwr/"
+ "K2frCNpn87exOqMzNUVYiPKmhCbfS7vBUce5tO6Ec9osQ==",
+ uint64_t{0x20961c911965f684}, uint64_t{0xcac4fd4c5080e581}},
+ {"fIsaG1r530SFrBqaDj1kqE0AJnvvK8MNEZbII2Yw1OK77v0V59xabIh0B5axaz/"
+ "+a2V5WpA=",
+ uint64_t{0x4e5b926ec83868e7}, uint64_t{0xc38bdb7483ba68e1}},
+ {"PGih0zDEOWCYGxuHGDFu9Ivbff/"
+ "iE7BNUq65tycTR2R76TerrXALRosnzaNYO5fjFhTi+CiS",
+ uint64_t{0x3927b30b922eecef}, uint64_t{0xdb2a8069b2ceaffa}},
+ {"RnpA/"
+ "zJnEnnLjmICORByRVb9bCOgxF44p3VMiW10G7PvW7IhwsWajlP9kIwNA9FjAD2GoQHk2Q="
+ "=",
+ uint64_t{0xbd0291284a49b61c}, uint64_t{0xdf9fe91d0d1c7887}},
+ {"qFklMceaTHqJpy2qavJE+EVBiNFOi6OxjOA3LeIcBop1K7w8xQi3TrDk+"
+ "BrWPRIbfprszSaPfrI=",
+ uint64_t{0x73a77c575bcc956}, uint64_t{0xe83f49e96e2e6a08}},
+ {"cLbfUtLl3EcQmITWoTskUR8da/VafRDYF/ylPYwk7/"
+ "zazk6ssyrzxMN3mmSyvrXR2yDGNZ3WDrTT",
+ uint64_t{0x766a0e2ade6d09a6}, uint64_t{0xc69e61b62ca2b62}},
+ {"s/"
+ "Jf1+"
+ "FbsbCpXWPTUSeWyMH6e4CvTFvPE5Fs6Z8hvFITGyr0dtukHzkI84oviVLxhM1xMxrMAy1db"
+ "w==",
+ uint64_t{0x2599f4f905115869}, uint64_t{0xb4a4f3f85f8298fe}},
+ {"FvyQ00+j7nmYZVQ8hI1Edxd0AWplhTfWuFGiu34AK5X8u2hLX1bE97sZM0CmeLe+"
+ "7LgoUT1fJ/axybE=",
+ uint64_t{0xd8256e5444d21e53}, uint64_t{0x167a1b39e1e95f41}},
+ {"L8ncxMaYLBH3g9buPu8hfpWZNlOF7nvWLNv9IozH07uQsIBWSKxoPy8+"
+ "LW4tTuzC6CIWbRGRRD1sQV/4",
+ uint64_t{0xf664a91333fb8dfd}, uint64_t{0xf8a2a5649855ee41}},
+ {"CDK0meI07yrgV2kQlZZ+"
+ "wuVqhc2NmzqeLH7bmcA6kchsRWFPeVF5Wqjjaj556ABeUoUr3yBmfU3kWOakkg==",
+ uint64_t{0x9625b859be372cd1}, uint64_t{0x27992565b595c498}},
+ {"d23/vc5ONh/"
+ "HkMiq+gYk4gaCNYyuFKwUkvn46t+dfVcKfBTYykr4kdvAPNXGYLjM4u1YkAEFpJP+"
+ "nX7eOvs=",
+ uint64_t{0x7b99940782e29898}, uint64_t{0x3e08cca5b71f9346}},
+ {"NUR3SRxBkxTSbtQORJpu/GdR6b/h6sSGfsMj/KFd99ahbh+9r7LSgSGmkGVB/"
+ "mGoT0pnMTQst7Lv2q6QN6Vm",
+ uint64_t{0x4fe12fa5383b51a8}, uint64_t{0xad406b10c770a6d2}},
+ {"2BOFlcI3Z0RYDtS9T9Ie9yJoXlOdigpPeeT+CRujb/"
+ "O39Ih5LPC9hP6RQk1kYESGyaLZZi3jtabHs7DiVx/VDg==",
+ uint64_t{0xe2ccb09ac0f5b4b6}, uint64_t{0xd1713ce6e552bcf2}},
+ {"FF2HQE1FxEvWBpg6Z9zAMH+Zlqx8S1JD/"
+ "wIlViL6ZDZY63alMDrxB0GJQahmAtjlm26RGLnjW7jmgQ4Ie3I+014=",
+ uint64_t{0x7d0a37adbd7b753b}, uint64_t{0x753b287194c73ad3}},
+ {"tHmO7mqVL/PX11nZrz50Hc+M17Poj5lpnqHkEN+4bpMx/"
+ "YGbkrGOaYjoQjgmt1X2QyypK7xClFrjeWrCMdlVYtbW",
+ uint64_t{0xd3ae96ef9f7185f2}, uint64_t{0x5ae41a95f600af1c}},
+ {"/WiHi9IQcxRImsudkA/KOTqGe8/"
+ "gXkhKIHkjddv5S9hi02M049dIK3EUyAEjkjpdGLUs+BN0QzPtZqjIYPOgwsYE9g==",
+ uint64_t{0x4fb88ea63f79a0d8}, uint64_t{0x4a61163b86a8bb4c}},
+ {"qds+1ExSnU11L4fTSDz/QE90g4Jh6ioqSh3KDOTOAo2pQGL1k/"
+ "9CCC7J23YF27dUTzrWsCQA2m4epXoCc3yPHb3xElA=",
+ uint64_t{0xed564e259bb5ebe9}, uint64_t{0x42eeaa79e760c7e4}},
+ {"8FVYHx40lSQPTHheh08Oq0/"
+ "pGm2OlG8BEf8ezvAxHuGGdgCkqpXIueJBF2mQJhTfDy5NncO8ntS7vaKs7sCNdDaNGOEi",
+ uint64_t{0x3e3256b60c428000}, uint64_t{0x698df622ef465b0a}},
+ {"4ZoEIrJtstiCkeew3oRzmyJHVt/pAs2pj0HgHFrBPztbQ10NsQ/"
+ "lM6DM439QVxpznnBSiHMgMQJhER+70l72LqFTO1JiIQ==",
+ uint64_t{0xfb05bad59ec8705}, uint64_t{0x157583111e1a6026}},
+ {"hQPtaYI+wJyxXgwD5n8jGIKFKaFA/"
+ "P83KqCKZfPthnjwdOFysqEOYwAaZuaaiv4cDyi9TyS8hk5cEbNP/jrI7q6pYGBLbsM=",
+ uint64_t{0xafdc251dbf97b5f8}, uint64_t{0xaa1388f078e793e0}},
+ {"S4gpMSKzMD7CWPsSfLeYyhSpfWOntyuVZdX1xSBjiGvsspwOZcxNKCRIOqAA0moUfOh3I5+"
+ "juQV4rsqYElMD/gWfDGpsWZKQ",
+ uint64_t{0x10ec9c92ddb5dcbc}, uint64_t{0xf10d68d0f3309360}},
+ {"oswxop+"
+ "bthuDLT4j0PcoSKby4LhF47ZKg8K17xxHf74UsGCzTBbOz0MM8hQEGlyqDT1iUiAYnaPaUp"
+ "L2mRK0rcIUYA4qLt5uOw==",
+ uint64_t{0x9a767d5822c7dac4}, uint64_t{0x2af056184457a3de}},
+ {"0II/"
+ "697p+"
+ "BtLSjxj5989OXI004TogEb94VUnDzOVSgMXie72cuYRvTFNIBgtXlKfkiUjeqVpd4a+"
+ "n5bxNOD1TGrjQtzKU5r7obo=",
+ uint64_t{0xee46254080d6e2db}, uint64_t{0x6d0058e1590b2489}},
+ {"E84YZW2qipAlMPmctrg7TKlwLZ68l4L+c0xRDUfyyFrA4MAti0q9sHq3TDFviH0Y+"
+ "Kq3tEE5srWFA8LM9oomtmvm5PYxoaarWPLc",
+ uint64_t{0xbbb669588d8bf398}, uint64_t{0x638f287f68817f12}},
+ {"x3pa4HIElyZG0Nj7Vdy9IdJIR4izLmypXw5PCmZB5y68QQ4uRaVVi3UthsoJROvbjDJkP2D"
+ "Q6L/eN8pFeLFzNPKBYzcmuMOb5Ull7w==",
+ uint64_t{0xdc2afaa529beef44}, uint64_t{0xc46b71fecefd5467}},
+ {"jVDKGYIuWOP/"
+ "QKLdd2wi8B2VJA8Wh0c8PwrXJVM8FOGM3voPDVPyDJOU6QsBDPseoR8uuKd19OZ/"
+ "zAvSCB+zlf6upAsBlheUKgCfKww=",
+ uint64_t{0xf1f67391d45013a8}, uint64_t{0x2c8e94679d964e0a}},
+ {"mkquunhmYe1aR2wmUz4vcvLEcKBoe6H+kjUok9VUn2+eTSkWs4oDDtJvNCWtY5efJwg/"
+ "j4PgjRYWtqnrCkhaqJaEvkkOwVfgMIwF3e+d",
+ uint64_t{0x16fce2b8c65a3429}, uint64_t{0x8612b797ce22503a}},
+ {"fRelvKYonTQ+s+rnnvQw+JzGfFoPixtna0vzcSjiDqX5s2Kg2//"
+ "UGrK+AVCyMUhO98WoB1DDbrsOYSw2QzrcPe0+3ck9sePvb+Q/IRaHbw==",
+ uint64_t{0xf4b096699f49fe67}, uint64_t{0x59f929babfba7170}},
+ {"DUwXFJzagljo44QeJ7/"
+ "6ZKw4QXV18lhkYT2jglMr8WB3CHUU4vdsytvw6AKv42ZcG6fRkZkq9fpnmXy6xG0aO3WPT1"
+ "eHuyFirAlkW+zKtwg=",
+ uint64_t{0xca584c4bc8198682}, uint64_t{0x9527556923fb49a0}},
+ {"cYmZCrOOBBongNTr7e4nYn52uQUy2mfe48s50JXx2AZ6cRAt/"
+ "xRHJ5QbEoEJOeOHsJyM4nbzwFm++SlT6gFZZHJpkXJ92JkR86uS/eV1hJUR",
+ uint64_t{0xed269fc3818b6aad}, uint64_t{0x1039ab644f5e150b}},
+ {"EXeHBDfhwzAKFhsMcH9+2RHwV+mJaN01+9oacF6vgm8mCXRd6jeN9U2oAb0of5c5cO4i+"
+ "Vb/LlHZSMI490SnHU0bejhSCC2gsC5d2K30ER3iNA==",
+ uint64_t{0x33f253cbb8fe66a8}, uint64_t{0x7816c83f3aa05e6d}},
+ {"FzkzRYoNjkxFhZDso94IHRZaJUP61nFYrh5MwDwv9FNoJ5jyNCY/"
+ "eazPZk+tbmzDyJIGw2h3GxaWZ9bSlsol/vK98SbkMKCQ/wbfrXRLcDzdd/8=",
+ uint64_t{0xd0b76b2c1523d99c}, uint64_t{0xf51d2f564518c619}},
+ {"Re4aXISCMlYY/XsX7zkIFR04ta03u4zkL9dVbLXMa/q6hlY/CImVIIYRN3VKP4pnd0AUr/"
+ "ugkyt36JcstAInb4h9rpAGQ7GMVOgBniiMBZ/MGU7H",
+ uint64_t{0xfd28f0811a2a237f}, uint64_t{0x67d494cff03ac004}},
+ {"ueLyMcqJXX+MhO4UApylCN9WlTQ+"
+ "ltJmItgG7vFUtqs2qNwBMjmAvr5u0sAKd8jpzV0dDPTwchbIeAW5zbtkA2NABJV6hFM48ib"
+ "4/J3A5mseA3cS8w==",
+ uint64_t{0x6261fb136482e84}, uint64_t{0x2802d636ced1cfbb}},
+ {"6Si7Yi11L+jZMkwaN+GUuzXMrlvEqviEkGOilNq0h8TdQyYKuFXzkYc/"
+ "q74gP3pVCyiwz9KpVGMM9vfnq36riMHRknkmhQutxLZs5fbmOgEO69HglCU=",
+ uint64_t{0x458efc750bca7c3a}, uint64_t{0xf64e20bad771cb12}},
+ {"Q6AbOofGuTJOegPh9Clm/"
+ "9crtUMQqylKrTc1fhfJo1tqvpXxhU4k08kntL1RG7woRnFrVh2UoMrL1kjin+s9CanT+"
+ "y4hHwLqRranl9FjvxfVKm3yvg68",
+ uint64_t{0xa7e69ff84e5e7c27}, uint64_t{0xb9a6cf84a83e15e}},
+ {"ieQEbIPvqY2YfIjHnqfJiO1/MIVRk0RoaG/WWi3kFrfIGiNLCczYoklgaecHMm/"
+ "1sZ96AjO+a5stQfZbJQwS7Sc1ODABEdJKcTsxeW2hbh9A6CFzpowP1A==",
+ uint64_t{0x3c59bfd0c29efe9e}, uint64_t{0x8da6630319609301}},
+ {"zQUv8hFB3zh2GGl3KTvCmnfzE+"
+ "SUgQPVaSVIELFX5H9cE3FuVFGmymkPQZJLAyzC90Cmi8GqYCvPqTuAAB//"
+ "XTJxy4bCcVArgZG9zJXpjowpNBfr3ngWrSE=",
+ uint64_t{0x10befacc6afd298d}, uint64_t{0x40946a86e2a996f3}},
+ {"US4hcC1+op5JKGC7eIs8CUgInjKWKlvKQkapulxW262E/"
+ "B2ye79QxOexf188u2mFwwe3WTISJHRZzS61IwljqAWAWoBAqkUnW8SHmIDwHUP31J0p5sGd"
+ "P47L",
+ uint64_t{0x41d5320b0a38efa7}, uint64_t{0xcab7f5997953fa76}},
+ {"9bHUWFna2LNaGF6fQLlkx1Hkt24nrkLE2CmFdWgTQV3FFbUe747SSqYw6ebpTa07MWSpWRP"
+ "sHesVo2B9tqHbe7eQmqYebPDFnNqrhSdZwFm9arLQVs+7a3Ic6A==",
+ uint64_t{0x58db1c7450fe17f3}, uint64_t{0x39129ca0e04fc465}},
+ {"Kb3DpHRUPhtyqgs3RuXjzA08jGb59hjKTOeFt1qhoINfYyfTt2buKhD6YVffRCPsgK9SeqZ"
+ "qRPJSyaqsa0ovyq1WnWW8jI/NhvAkZTVHUrX2pC+cD3OPYT05Dag=",
+ uint64_t{0x6098c055a335b7a6}, uint64_t{0x5238221fd685e1b8}},
+ {"gzxyMJIPlU+bJBwhFUCHSofZ/"
+ "319LxqMoqnt3+L6h2U2+ZXJCSsYpE80xmR0Ta77Jq54o92SMH87HV8dGOaCTuAYF+"
+ "lDL42SY1P316Cl0sZTS2ow3ZqwGbcPNs/1",
+ uint64_t{0x1bbacec67845a801}, uint64_t{0x175130c407dbcaab}},
+ {"uR7V0TW+FGVMpsifnaBAQ3IGlr1wx5sKd7TChuqRe6OvUXTlD4hKWy8S+"
+ "8yyOw8lQabism19vOQxfmocEOW/"
+ "vzY0pEa87qHrAZy4s9fH2Bltu8vaOIe+agYohhYORQ==",
+ uint64_t{0xc419cfc7442190}, uint64_t{0x2f20e7536c0b0df}},
+ {"1UR5eoo2aCwhacjZHaCh9bkOsITp6QunUxHQ2SfeHv0imHetzt/"
+ "Z70mhyWZBalv6eAx+YfWKCUib2SHDtz/"
+ "A2dc3hqUWX5VfAV7FQsghPUAtu6IiRatq4YSLpDvKZBQ=",
+ uint64_t{0xc95e510d94ba270c}, uint64_t{0x2742cb488a04ad56}},
+ {"opubR7H63BH7OtY+Avd7QyQ25UZ8kLBdFDsBTwZlY6gA/"
+ "u+x+"
+ "czC9AaZMgmQrUy15DH7YMGsvdXnviTtI4eVI4aF1H9Rl3NXMKZgwFOsdTfdcZeeHVRzBBKX"
+ "8jUfh1il",
+ uint64_t{0xff1ae05c98089c3f}, uint64_t{0xd6afb593879ff93b}},
+ {"DC0kXcSXtfQ9FbSRwirIn5tgPri0sbzHSa78aDZVDUKCMaBGyFU6BmrulywYX8yzvwprdLs"
+ "oOwTWN2wMjHlPDqrvVHNEjnmufRDblW+nSS+xtKNs3N5xsxXdv6JXDrAB/Q==",
+ uint64_t{0x90c02b8dceced493}, uint64_t{0xf50ad64caac0ca7f}},
+ {"BXRBk+3wEP3Lpm1y75wjoz+PgB0AMzLe8tQ1AYU2/"
+ "oqrQB2YMC6W+9QDbcOfkGbeH+b7IBkt/"
+ "gwCMw2HaQsRFEsurXtcQ3YwRuPz5XNaw5NAvrNa67Fm7eRzdE1+hWLKtA8=",
+ uint64_t{0x9f8a76697ab1aa36}, uint64_t{0x2ade95c4261364ae}},
+ {"RRBSvEGYnzR9E45Aps/+WSnpCo/X7gJLO4DRnUqFrJCV/kzWlusLE/"
+ "6ZU6RoUf2ROwcgEvUiXTGjLs7ts3t9SXnJHxC1KiOzxHdYLMhVvgNd3hVSAXODpKFSkVXND"
+ "55G2L1W",
+ uint64_t{0x6ba1bf3d811a531d}, uint64_t{0x5c4f3299faacd07a}},
+ {"jeh6Qazxmdi57pa9S3XSnnZFIRrnc6s8QLrah5OX3SB/V2ErSPoEAumavzQPkdKF1/"
+ "SfvmdL+qgF1C+Yawy562QaFqwVGq7+tW0yxP8FStb56ZRgNI4IOmI30s1Ei7iops9Uuw==",
+ uint64_t{0x6a418974109c67b4}, uint64_t{0xfffe3bff0ae5e9bc}},
+ {"6QO5nnDrY2/"
+ "wrUXpltlKy2dSBcmK15fOY092CR7KxAjNfaY+"
+ "aAmtWbbzQk3MjBg03x39afSUN1fkrWACdyQKRaGxgwq6MGNxI6W+8DLWJBHzIXrntrE/"
+ "ml6fnNXEpxplWJ1vEs4=",
+ uint64_t{0x8472f1c2b3d230a3}, uint64_t{0x1db785c0005166e4}},
+ {"0oPxeEHhqhcFuwonNfLd5jF3RNATGZS6NPoS0WklnzyokbTqcl4BeBkMn07+fDQv83j/"
+ "BpGUwcWO05f3+DYzocfnizpFjLJemFGsls3gxcBYxcbqWYev51tG3lN9EvRE+X9+Pwww",
+ uint64_t{0x5e06068f884e73a7}, uint64_t{0xea000d962ad18418}},
+ {"naSBSjtOKgAOg8XVbR5cHAW3Y+QL4Pb/JO9/"
+ "oy6L08wvVRZqo0BrssMwhzBP401Um7A4ppAupbQeJFdMrysY34AuSSNvtNUy5VxjNECwiNt"
+ "gwYHw7yakDUv8WvonctmnoSPKENegQg==",
+ uint64_t{0x55290b1a8f170f59}, uint64_t{0xe42aef38359362d9}},
+ {"vPyl8DxVeRe1OpilKb9KNwpGkQRtA94UpAHetNh+"
+ "95V7nIW38v7PpzhnTWIml5kw3So1Si0TXtIUPIbsu32BNhoH7QwFvLM+"
+ "JACgSpc5e3RjsL6Qwxxi11npwxRmRUqATDeMUfRAjxg=",
+ uint64_t{0x5501cfd83dfe706a}, uint64_t{0xc8e95657348a3891}},
+ {"QC9i2GjdTMuNC1xQJ74ngKfrlA4w3o58FhvNCltdIpuMhHP1YsDA78scQPLbZ3OCUgeQguY"
+ "f/vw6zAaVKSgwtaykqg5ka/4vhz4hYqWU5ficdXqClHl+zkWEY26slCNYOM5nnDlly8Cj",
+ uint64_t{0xe43ed13d13a66990}, uint64_t{0xc162eca864f238c6}},
+ {"7CNIgQhAHX27nxI0HeB5oUTnTdgKpRDYDKwRcXfSFGP1XeT9nQF6WKCMjL1tBV6x7KuJ91G"
+ "Zz11F4c+8s+MfqEAEpd4FHzamrMNjGcjCyrVtU6y+7HscMVzr7Q/"
+ "ODLcPEFztFnwjvCjmHw==",
+ uint64_t{0xdf43bc375cf5283f}, uint64_t{0xbe1fb373e20579ad}},
+ {"Qa/hC2RPXhANSospe+gUaPfjdK/yhQvfm4cCV6/pdvCYWPv8p1kMtKOX3h5/"
+ "8oZ31fsmx4Axphu5qXJokuhZKkBUJueuMpxRyXpwSWz2wELx5glxF7CM0Fn+"
+ "OevnkhUn5jsPlG2r5jYlVn8=",
+ uint64_t{0x8112b806d288d7b5}, uint64_t{0x628a1d4f40aa6ffd}},
+ {"kUw/0z4l3a89jTwN5jpG0SHY5km/"
+ "IVhTjgM5xCiPRLncg40aqWrJ5vcF891AOq5hEpSq0bUCJUMFXgct7kvnys905HjerV7Vs1G"
+ "y84tgVJ70/2+pAZTsB/PzNOE/G6sOj4+GbTzkQu819OLB",
+ uint64_t{0xd52a18abb001cb46}, uint64_t{0xa87bdb7456340f90}},
+ {"VDdfSDbO8Tdj3T5W0XM3EI7iHh5xpIutiM6dvcJ/fhe23V/srFEkDy5iZf/"
+ "VnA9kfi2C79ENnFnbOReeuZW1b3MUXB9lgC6U4pOTuC+"
+ "jHK3Qnpyiqzj7h3ISJSuo2pob7vY6VHZo6Fn7exEqHg==",
+ uint64_t{0xe12b76a2433a1236}, uint64_t{0x5960ef3ba982c801}},
+ {"Ldfvy3ORdquM/R2fIkhH/ONi69mcP1AEJ6n/"
+ "oropwecAsLJzQSgezSY8bEiEs0VnFTBBsW+RtZY6tDj03fnb3amNUOq1b7jbqyQkL9hpl+"
+ "2Z2J8IaVSeownWl+bQcsR5/xRktIMckC5AtF4YHfU=",
+ uint64_t{0x175bf7319cf1fa00}, uint64_t{0x5026586df9a431ec}},
+ {"BrbNpb42+"
+ "VzZAjJw6QLirXzhweCVRfwlczzZ0VX2xluskwBqyfnGovz5EuX79JJ31VNXa5hTkAyQat3l"
+ "YKRADTdAdwE5PqM1N7YaMqqsqoAAAeuYVXuk5eWCykYmClNdSspegwgCuT+403JigBzi",
+ uint64_t{0xd63d57b3f67525ae}, uint64_t{0xfe4b8a20fdf0840b}},
+ {"gB3NGHJJvVcuPyF0ZSvHwnWSIfmaI7La24VMPQVoIIWF7Z74NltPZZpx2f+cocESM+"
+ "ILzQW9p+BC8x5IWz7N4Str2WLGKMdgmaBfNkEhSHQDU0IJEOnpUt0HmjhFaBlx0/"
+ "LTmhua+rQ6Wup8ezLwfg==",
+ uint64_t{0x933faea858832b73}, uint64_t{0xdcb761867da7072f}},
+ {"hTKHlRxx6Pl4gjG+6ksvvj0CWFicUg3WrPdSJypDpq91LUWRni2KF6+"
+ "81ZoHBFhEBrCdogKqeK+hy9bLDnx7g6rAFUjtn1+cWzQ2YjiOpz4+"
+ "ROBB7lnwjyTGWzJD1rXtlso1g2qVH8XJVigC5M9AIxM=",
+ uint64_t{0x53d061e5f8e7c04f}, uint64_t{0xc10d4653667275b7}},
+ {"IWQBelSQnhrr0F3BhUpXUIDauhX6f95Qp+A0diFXiUK7irwPG1oqBiqHyK/SH/"
+ "9S+"
+ "rln9DlFROAmeFdH0OCJi2tFm4afxYzJTFR4HnR4cG4x12JqHaZLQx6iiu6CE3rtWBVz99oA"
+ "wCZUOEXIsLU24o2Y",
+ uint64_t{0xdb4124556dd515e0}, uint64_t{0x727720deec13110b}},
+ {"TKo+l+"
+ "1dOXdLvIrFqeLaHdm0HZnbcdEgOoLVcGRiCbAMR0j5pIFw8D36tefckAS1RCFOH5IgP8yiF"
+ "T0Gd0a2hI3+"
+ "fTKA7iK96NekxWeoeqzJyctc6QsoiyBlkZerRxs5RplrxoeNg29kKDTM0K94mnhD9g==",
+ uint64_t{0x4fb31a0dd681ee71}, uint64_t{0x710b009662858dc9}},
+ {"YU4e7G6EfQYvxCFoCrrT0EFgVLHFfOWRTJQJ5gxM3G2b+"
+ "1kJf9YPrpsxF6Xr6nYtS8reEEbDoZJYqnlk9lXSkVArm88Cqn6d25VCx3+"
+ "49MqC0trIlXtb7SXUUhwpJK16T0hJUfPH7s5cMZXc6YmmbFuBNPE=",
+ uint64_t{0x27cc72eefa138e4c}, uint64_t{0xfbf8f7a3ecac1eb7}},
+ {"/I/"
+ "eImMwPo1U6wekNFD1Jxjk9XQVi1D+"
+ "FPdqcHifYXQuP5aScNQfxMAmaPR2XhuOQhADV5tTVbBKwCDCX4E3jcDNHzCiPvViZF1W27t"
+ "xaf2BbFQdwKrNCmrtzcluBFYu0XZfc7RU1RmxK/RtnF1qHsq/O4pp",
+ uint64_t{0x44bc2dfba4bd3ced}, uint64_t{0xb6fc4fcd0722e3df}},
+ {"CJTT9WGcY2XykTdo8KodRIA29qsqY0iHzWZRjKHb9alwyJ7RZAE3V5Juv4MY3MeYEr1EPCC"
+ "MxO7yFXqT8XA8YTjaMp3bafRt17Pw8JC4iKJ1zN+WWKOESrj+"
+ "3aluGQqn8z1EzqY4PH7rLG575PYeWsP98BugdA==",
+ uint64_t{0x242da1e3a439bed8}, uint64_t{0x7cb86dcc55104aac}},
+ {"ZlhyQwLhXQyIUEnMH/"
+ "AEW27vh9xrbNKJxpWGtrEmKhd+nFqAfbeNBQjW0SfG1YI0xQkQMHXjuTt4P/"
+ "EpZRtA47ibZDVS8TtaxwyBjuIDwqcN09eCtpC+Ls+"
+ "vWDTLmBeDM3u4hmzz4DQAYsLiZYSJcldg9Q3wszw=",
+ uint64_t{0xdc559c746e35c139}, uint64_t{0x19e71e9b45c3a51e}},
+ {"v2KU8y0sCrBghmnm8lzGJlwo6D6ObccAxCf10heoDtYLosk4ztTpLlpSFEyu23MLA1tJkcg"
+ "Rko04h19QMG0mOw/"
+ "wc93EXAweriBqXfvdaP85sZABwiKO+6rtS9pacRVpYYhHJeVTQ5NzrvBvi1huxAr+"
+ "xswhVMfL",
+ uint64_t{0xd0b0350275b9989}, uint64_t{0x51de38573c2bea48}},
+ {"QhKlnIS6BuVCTQsnoE67E/"
+ "yrgogE8EwO7xLaEGei26m0gEU4OksefJgppDh3X0x0Cs78Dr9IHK5b977CmZlrTRmwhlP8p"
+ "M+UzXPNRNIZuN3ntOum/QhUWP8SGpirheXENWsXMQ/"
+ "nxtxakyEtrNkKk471Oov9juP8oQ==",
+ uint64_t{0xb04489e41d17730c}, uint64_t{0xa73ab6996d6df158}},
+ {"/ZRMgnoRt+Uo6fUPr9FqQvKX7syhgVqWu+"
+ "WUSsiQ68UlN0efSP6Eced5gJZL6tg9gcYJIkhjuQNITU0Q3TjVAnAcobgbJikCn6qZ6pRxK"
+ "BY4MTiAlfGD3T7R7hwJwx554MAy++Zb/YUFlnCaCJiwQMnowF7aQzwYFCo=",
+ uint64_t{0x2217285eb4572156}, uint64_t{0x55ef2b8c930817b2}},
+ {"NB7tU5fNE8nI+SXGfipc7sRkhnSkUF1krjeo6k+8FITaAtdyz+"
+ "o7mONgXmGLulBPH9bEwyYhKNVY0L+njNQrZ9YC2aXsFD3PdZsxAFaBT3VXEzh+"
+ "NGBTjDASNL3mXyS8Yv1iThGfHoY7T4aR0NYGJ+k+pR6f+KrPC96M",
+ uint64_t{0x12c2e8e68aede73b}, uint64_t{0xb2850bf5fae87157}},
+ {"8T6wrqCtEO6/rwxF6lvMeyuigVOLwPipX/FULvwyu+1wa5sQGav/"
+ "2FsLHUVn6cGSi0LlFwLewGHPFJDLR0u4t7ZUyM//"
+ "x6da0sWgOa5hzDqjsVGmjxEHXiaXKW3i4iSZNuxoNbMQkIbVML+"
+ "DkYu9ND0O2swg4itGeVSzXA==",
+ uint64_t{0x4d612125bdc4fd00}, uint64_t{0xecf3de1acd04651f}},
+ {"Ntf1bMRdondtMv1CYr3G80iDJ4WSAlKy5H34XdGruQiCrnRGDBa+"
+ "eUi7vKp4gp3BBcVGl8eYSasVQQjn7MLvb3BjtXx6c/"
+ "bCL7JtpzQKaDnPr9GWRxpBXVxKREgMM7d8lm35EODv0w+"
+ "hQLfVSh8OGs7fsBb68nNWPLeeSOo=",
+ uint64_t{0x81826b553954464e}, uint64_t{0xcc0a40552559ff32}},
+ {"VsSAw72Ro6xks02kaiLuiTEIWBC5bgqr4WDnmP8vglXzAhixk7td926rm9jNimL+"
+ "kroPSygZ9gl63aF5DCPOACXmsbmhDrAQuUzoh9ZKhWgElLQsrqo1KIjWoZT5b5QfVUXY9lS"
+ "IBg3U75SqORoTPq7HalxxoIT5diWOcJQi",
+ uint64_t{0xc2e5d345dc0ddd2d}, uint64_t{0xc385c374f20315b1}},
+ {"j+loZ+C87+"
+ "bJxNVebg94gU0mSLeDulcHs84tQT7BZM2rzDSLiCNxUedHr1ZWJ9ejTiBa0dqy2I2ABc++"
+ "xzOLcv+//YfibtjKtYggC6/3rv0XCc7xu6d/"
+ "O6xO+XOBhOWAQ+IHJVHf7wZnDxIXB8AUHsnjEISKj7823biqXjyP3g==",
+ uint64_t{0x3da6830a9e32631e}, uint64_t{0xb90208a4c7234183}},
+ {"f3LlpcPElMkspNtDq5xXyWU62erEaKn7RWKlo540gR6mZsNpK1czV/"
+ "sOmqaq8XAQLEn68LKj6/"
+ "cFkJukxRzCa4OF1a7cCAXYFp9+wZDu0bw4y63qbpjhdCl8GO6Z2lkcXy7KOzbPE01ukg7+"
+ "gN+7uKpoohgAhIwpAKQXmX5xtd0=",
+ uint64_t{0xc9ae5c8759b4877a}, uint64_t{0x58aa1ca7a4c075d9}},
+ };
+
+ for (const auto& expected_result : expected_results) {
+ std::string str;
+ ASSERT_TRUE(absl::Base64Unescape(expected_result.base64_data, &str));
+ EXPECT_EQ(absl::hash_internal::Wyhash(str.data(), str.size(),
+ expected_result.seed, kSalt),
+ expected_result.hash);
+ }
+}
+
+} // namespace
diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h
index 75689bb..d5cb5f3 100644
--- a/absl/meta/type_traits.h
+++ b/absl/meta/type_traits.h
@@ -610,8 +610,22 @@
template <typename T>
using underlying_type_t = typename std::underlying_type<T>::type;
-template <typename T>
-using result_of_t = typename std::result_of<T>::type;
+
+namespace type_traits_internal {
+
+#if __cplusplus >= 201703L
+// std::result_of is deprecated (C++17) or removed (C++20)
+template<typename> struct result_of;
+template<typename F, typename... Args>
+struct result_of<F(Args...)> : std::invoke_result<F, Args...> {};
+#else
+template<typename F> using result_of = std::result_of<F>;
+#endif
+
+} // namespace type_traits_internal
+
+template<typename F>
+using result_of_t = typename type_traits_internal::result_of<F>::type;
namespace type_traits_internal {
// In MSVC we can't probe std::hash or stdext::hash because it triggers a
diff --git a/absl/numeric/BUILD.bazel b/absl/numeric/BUILD.bazel
index f808f5d..ea587bf 100644
--- a/absl/numeric/BUILD.bazel
+++ b/absl/numeric/BUILD.bazel
@@ -25,6 +25,35 @@
licenses(["notice"])
cc_library(
+ name = "bits",
+ hdrs = [
+ "bits.h",
+ "internal/bits.h",
+ ],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ "//absl/base:config",
+ "//absl/base:core_headers",
+ ],
+)
+
+cc_test(
+ name = "bits_test",
+ size = "small",
+ srcs = [
+ "bits_test.cc",
+ ],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ ":bits",
+ "//absl/random",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
name = "int128",
srcs = [
"int128.cc",
@@ -35,7 +64,7 @@
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
- "//absl/base:bits",
+ ":bits",
"//absl/base:config",
"//absl/base:core_headers",
],
@@ -72,3 +101,15 @@
"@com_github_google_benchmark//:benchmark_main",
],
)
+
+cc_library(
+ name = "representation",
+ hdrs = [
+ "internal/representation.h",
+ ],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ "//absl/base:config",
+ ],
+)
diff --git a/absl/numeric/BUILD.gn b/absl/numeric/BUILD.gn
index 85a160e..6368869 100644
--- a/absl/numeric/BUILD.gn
+++ b/absl/numeric/BUILD.gn
@@ -4,6 +4,17 @@
import("//third_party/abseil-cpp/absl.gni")
+absl_source_set("bits") {
+ public = [
+ "bits.h",
+ "internal/bits.h",
+ ]
+ deps = [
+ "//third_party/abseil-cpp/absl/base:config",
+ "//third_party/abseil-cpp/absl/base:core_headers",
+ ]
+}
+
absl_source_set("int128") {
sources = [
"int128.cc",
@@ -12,8 +23,13 @@
]
public = [ "int128.h" ]
deps = [
- "//third_party/abseil-cpp/absl/base:bits",
+ ":bits",
"//third_party/abseil-cpp/absl/base:config",
"//third_party/abseil-cpp/absl/base:core_headers",
]
}
+
+absl_source_set("representation") {
+ public = [ "internal/representation.h" ]
+ deps = [ "//third_party/abseil-cpp/absl/base:config" ]
+}
diff --git a/absl/numeric/CMakeLists.txt b/absl/numeric/CMakeLists.txt
index 1e12d80..781987d 100644
--- a/absl/numeric/CMakeLists.txt
+++ b/absl/numeric/CMakeLists.txt
@@ -16,6 +16,33 @@
absl_cc_library(
NAME
+ bits
+ HDRS
+ "bits.h"
+ "internal/bits.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::core_headers
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ bits_test
+ SRCS
+ "bits_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::bits
+ absl::core_headers
+ absl::random_random
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
int128
HDRS
"int128.h"
@@ -26,9 +53,9 @@
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
- absl::bits
absl::config
absl::core_headers
+ absl::bits
PUBLIC
)
@@ -59,3 +86,15 @@
absl::int128
PUBLIC
)
+
+absl_cc_library(
+ NAME
+ numeric_representation
+ HDRS
+ "internal/representation.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
+ PUBLIC
+)
diff --git a/absl/numeric/bits.h b/absl/numeric/bits.h
new file mode 100644
index 0000000..52013ad
--- /dev/null
+++ b/absl/numeric/bits.h
@@ -0,0 +1,177 @@
+// Copyright 2020 The Abseil Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: bits.h
+// -----------------------------------------------------------------------------
+//
+// This file contains implementations of C++20's bitwise math functions, as
+// defined by:
+//
+// P0553R4:
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0553r4.html
+// P0556R3:
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0556r3.html
+// P1355R2:
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1355r2.html
+// P1956R1:
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1956r1.pdf
+//
+// When using a standard library that implements these functions, we use the
+// standard library's implementation.
+
+#ifndef ABSL_NUMERIC_BITS_H_
+#define ABSL_NUMERIC_BITS_H_
+
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+
+#if (defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L) || \
+ (defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L)
+#include <bit>
+#endif
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/numeric/internal/bits.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+#if !(defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L)
+// rotating
+template <class T>
+ABSL_MUST_USE_RESULT constexpr
+ typename std::enable_if<std::is_unsigned<T>::value, T>::type
+ rotl(T x, int s) noexcept {
+ return numeric_internal::RotateLeft(x, s);
+}
+
+template <class T>
+ABSL_MUST_USE_RESULT constexpr
+ typename std::enable_if<std::is_unsigned<T>::value, T>::type
+ rotr(T x, int s) noexcept {
+ return numeric_internal::RotateRight(x, s);
+}
+
+// Counting functions
+//
+// While these functions are typically constexpr, on some platforms, they may
+// not be marked as constexpr due to constraints of the compiler/available
+// intrinsics.
+template <class T>
+ABSL_INTERNAL_CONSTEXPR_CLZ inline
+ typename std::enable_if<std::is_unsigned<T>::value, int>::type
+ countl_zero(T x) noexcept {
+ return numeric_internal::CountLeadingZeroes(x);
+}
+
+template <class T>
+ABSL_INTERNAL_CONSTEXPR_CLZ inline
+ typename std::enable_if<std::is_unsigned<T>::value, int>::type
+ countl_one(T x) noexcept {
+ // Avoid integer promotion to a wider type
+ return countl_zero(static_cast<T>(~x));
+}
+
+template <class T>
+ABSL_INTERNAL_CONSTEXPR_CTZ inline
+ typename std::enable_if<std::is_unsigned<T>::value, int>::type
+ countr_zero(T x) noexcept {
+ return numeric_internal::CountTrailingZeroes(x);
+}
+
+template <class T>
+ABSL_INTERNAL_CONSTEXPR_CTZ inline
+ typename std::enable_if<std::is_unsigned<T>::value, int>::type
+ countr_one(T x) noexcept {
+ // Avoid integer promotion to a wider type
+ return countr_zero(static_cast<T>(~x));
+}
+
+template <class T>
+ABSL_INTERNAL_CONSTEXPR_POPCOUNT inline
+ typename std::enable_if<std::is_unsigned<T>::value, int>::type
+ popcount(T x) noexcept {
+ return numeric_internal::Popcount(x);
+}
+#else // defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L
+
+using std::countl_one;
+using std::countl_zero;
+using std::countr_one;
+using std::countr_zero;
+using std::popcount;
+using std::rotl;
+using std::rotr;
+
+#endif
+
+#if !(defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L)
+// Returns: true if x is an integral power of two; false otherwise.
+template <class T>
+constexpr inline typename std::enable_if<std::is_unsigned<T>::value, bool>::type
+has_single_bit(T x) noexcept {
+ return x != 0 && (x & (x - 1)) == 0;
+}
+
+// Returns: If x == 0, 0; otherwise one plus the base-2 logarithm of x, with any
+// fractional part discarded.
+template <class T>
+ABSL_INTERNAL_CONSTEXPR_CLZ inline
+ typename std::enable_if<std::is_unsigned<T>::value, T>::type
+ bit_width(T x) noexcept {
+ return std::numeric_limits<T>::digits - countl_zero(x);
+}
+
+// Returns: If x == 0, 0; otherwise the maximal value y such that
+// has_single_bit(y) is true and y <= x.
+template <class T>
+ABSL_INTERNAL_CONSTEXPR_CLZ inline
+ typename std::enable_if<std::is_unsigned<T>::value, T>::type
+ bit_floor(T x) noexcept {
+ return x == 0 ? 0 : T{1} << (bit_width(x) - 1);
+}
+
+// Returns: N, where N is the smallest power of 2 greater than or equal to x.
+//
+// Preconditions: N is representable as a value of type T.
+template <class T>
+ABSL_INTERNAL_CONSTEXPR_CLZ inline
+ typename std::enable_if<std::is_unsigned<T>::value, T>::type
+ bit_ceil(T x) {
+ // If T is narrower than unsigned, T{1} << bit_width will be promoted. We
+ // want to force it to wraparound so that bit_ceil of an invalid value are not
+ // core constant expressions.
+ //
+ // BitCeilNonPowerOf2 triggers an overflow in constexpr contexts if we would
+ // undergo promotion to unsigned but not fit the result into T without
+ // truncation.
+ return has_single_bit(x) ? T{1} << (bit_width(x) - 1)
+ : numeric_internal::BitCeilNonPowerOf2(x);
+}
+#else // defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L
+
+using std::bit_ceil;
+using std::bit_floor;
+using std::bit_width;
+using std::has_single_bit;
+
+#endif
+
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_NUMERIC_BITS_H_
diff --git a/absl/numeric/bits_test.cc b/absl/numeric/bits_test.cc
new file mode 100644
index 0000000..7c942aa
--- /dev/null
+++ b/absl/numeric/bits_test.cc
@@ -0,0 +1,573 @@
+// Copyright 2020 The Abseil Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/numeric/bits.h"
+
+#include <limits>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/random/random.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace {
+
+TEST(Rotate, Left) {
+ static_assert(rotl(uint8_t{0x12}, 0) == uint8_t{0x12}, "");
+ static_assert(rotl(uint16_t{0x1234}, 0) == uint16_t{0x1234}, "");
+ static_assert(rotl(uint32_t{0x12345678UL}, 0) == uint32_t{0x12345678UL}, "");
+ static_assert(rotl(uint64_t{0x12345678ABCDEF01ULL}, 0) ==
+ uint64_t{0x12345678ABCDEF01ULL},
+ "");
+
+ EXPECT_EQ(rotl(uint8_t{0x12}, 0), uint8_t{0x12});
+ EXPECT_EQ(rotl(uint16_t{0x1234}, 0), uint16_t{0x1234});
+ EXPECT_EQ(rotl(uint32_t{0x12345678UL}, 0), uint32_t{0x12345678UL});
+ EXPECT_EQ(rotl(uint64_t{0x12345678ABCDEF01ULL}, 0),
+ uint64_t{0x12345678ABCDEF01ULL});
+
+ EXPECT_EQ(rotl(uint8_t{0x12}, 8), uint8_t{0x12});
+ EXPECT_EQ(rotl(uint16_t{0x1234}, 16), uint16_t{0x1234});
+ EXPECT_EQ(rotl(uint32_t{0x12345678UL}, 32), uint32_t{0x12345678UL});
+ EXPECT_EQ(rotl(uint64_t{0x12345678ABCDEF01ULL}, 64),
+ uint64_t{0x12345678ABCDEF01ULL});
+
+ EXPECT_EQ(rotl(uint8_t{0x12}, -8), uint8_t{0x12});
+ EXPECT_EQ(rotl(uint16_t{0x1234}, -16), uint16_t{0x1234});
+ EXPECT_EQ(rotl(uint32_t{0x12345678UL}, -32), uint32_t{0x12345678UL});
+ EXPECT_EQ(rotl(uint64_t{0x12345678ABCDEF01ULL}, -64),
+ uint64_t{0x12345678ABCDEF01ULL});
+
+ EXPECT_EQ(rotl(uint8_t{0x12}, 4), uint8_t{0x21});
+ EXPECT_EQ(rotl(uint16_t{0x1234}, 4), uint16_t{0x2341});
+ EXPECT_EQ(rotl(uint32_t{0x12345678UL}, 4), uint32_t{0x23456781UL});
+ EXPECT_EQ(rotl(uint64_t{0x12345678ABCDEF01ULL}, 4),
+ uint64_t{0x2345678ABCDEF011ULL});
+
+ EXPECT_EQ(rotl(uint8_t{0x12}, -4), uint8_t{0x21});
+ EXPECT_EQ(rotl(uint16_t{0x1234}, -4), uint16_t{0x4123});
+ EXPECT_EQ(rotl(uint32_t{0x12345678UL}, -4), uint32_t{0x81234567UL});
+ EXPECT_EQ(rotl(uint64_t{0x12345678ABCDEF01ULL}, -4),
+ uint64_t{0x112345678ABCDEF0ULL});
+}
+
+TEST(Rotate, Right) {
+ static_assert(rotr(uint8_t{0x12}, 0) == uint8_t{0x12}, "");
+ static_assert(rotr(uint16_t{0x1234}, 0) == uint16_t{0x1234}, "");
+ static_assert(rotr(uint32_t{0x12345678UL}, 0) == uint32_t{0x12345678UL}, "");
+ static_assert(rotr(uint64_t{0x12345678ABCDEF01ULL}, 0) ==
+ uint64_t{0x12345678ABCDEF01ULL},
+ "");
+
+ EXPECT_EQ(rotr(uint8_t{0x12}, 0), uint8_t{0x12});
+ EXPECT_EQ(rotr(uint16_t{0x1234}, 0), uint16_t{0x1234});
+ EXPECT_EQ(rotr(uint32_t{0x12345678UL}, 0), uint32_t{0x12345678UL});
+ EXPECT_EQ(rotr(uint64_t{0x12345678ABCDEF01ULL}, 0),
+ uint64_t{0x12345678ABCDEF01ULL});
+
+ EXPECT_EQ(rotr(uint8_t{0x12}, 8), uint8_t{0x12});
+ EXPECT_EQ(rotr(uint16_t{0x1234}, 16), uint16_t{0x1234});
+ EXPECT_EQ(rotr(uint32_t{0x12345678UL}, 32), uint32_t{0x12345678UL});
+ EXPECT_EQ(rotr(uint64_t{0x12345678ABCDEF01ULL}, 64),
+ uint64_t{0x12345678ABCDEF01ULL});
+
+ EXPECT_EQ(rotr(uint8_t{0x12}, -8), uint8_t{0x12});
+ EXPECT_EQ(rotr(uint16_t{0x1234}, -16), uint16_t{0x1234});
+ EXPECT_EQ(rotr(uint32_t{0x12345678UL}, -32), uint32_t{0x12345678UL});
+ EXPECT_EQ(rotr(uint64_t{0x12345678ABCDEF01ULL}, -64),
+ uint64_t{0x12345678ABCDEF01ULL});
+
+ EXPECT_EQ(rotr(uint8_t{0x12}, 4), uint8_t{0x21});
+ EXPECT_EQ(rotr(uint16_t{0x1234}, 4), uint16_t{0x4123});
+ EXPECT_EQ(rotr(uint32_t{0x12345678UL}, 4), uint32_t{0x81234567UL});
+ EXPECT_EQ(rotr(uint64_t{0x12345678ABCDEF01ULL}, 4),
+ uint64_t{0x112345678ABCDEF0ULL});
+
+ EXPECT_EQ(rotr(uint8_t{0x12}, -4), uint8_t{0x21});
+ EXPECT_EQ(rotr(uint16_t{0x1234}, -4), uint16_t{0x2341});
+ EXPECT_EQ(rotr(uint32_t{0x12345678UL}, -4), uint32_t{0x23456781UL});
+ EXPECT_EQ(rotr(uint64_t{0x12345678ABCDEF01ULL}, -4),
+ uint64_t{0x2345678ABCDEF011ULL});
+}
+
+TEST(Rotate, Symmetry) {
+ // rotr(x, s) is equivalent to rotl(x, -s)
+ absl::BitGen rng;
+ constexpr int kTrials = 100;
+
+ for (int i = 0; i < kTrials; ++i) {
+ uint8_t value = absl::Uniform(rng, std::numeric_limits<uint8_t>::min(),
+ std::numeric_limits<uint8_t>::max());
+ int shift = absl::Uniform(rng, -2 * std::numeric_limits<uint8_t>::digits,
+ 2 * std::numeric_limits<uint8_t>::digits);
+
+ EXPECT_EQ(rotl(value, shift), rotr(value, -shift));
+ }
+
+ for (int i = 0; i < kTrials; ++i) {
+ uint16_t value = absl::Uniform(rng, std::numeric_limits<uint16_t>::min(),
+ std::numeric_limits<uint16_t>::max());
+ int shift = absl::Uniform(rng, -2 * std::numeric_limits<uint16_t>::digits,
+ 2 * std::numeric_limits<uint16_t>::digits);
+
+ EXPECT_EQ(rotl(value, shift), rotr(value, -shift));
+ }
+
+ for (int i = 0; i < kTrials; ++i) {
+ uint32_t value = absl::Uniform(rng, std::numeric_limits<uint32_t>::min(),
+ std::numeric_limits<uint32_t>::max());
+ int shift = absl::Uniform(rng, -2 * std::numeric_limits<uint32_t>::digits,
+ 2 * std::numeric_limits<uint32_t>::digits);
+
+ EXPECT_EQ(rotl(value, shift), rotr(value, -shift));
+ }
+
+ for (int i = 0; i < kTrials; ++i) {
+ uint64_t value = absl::Uniform(rng, std::numeric_limits<uint64_t>::min(),
+ std::numeric_limits<uint64_t>::max());
+ int shift = absl::Uniform(rng, -2 * std::numeric_limits<uint64_t>::digits,
+ 2 * std::numeric_limits<uint64_t>::digits);
+
+ EXPECT_EQ(rotl(value, shift), rotr(value, -shift));
+ }
+}
+
+TEST(Counting, LeadingZeroes) {
+#if ABSL_INTERNAL_HAS_CONSTEXPR_CLZ
+ static_assert(countl_zero(uint8_t{}) == 8, "");
+ static_assert(countl_zero(static_cast<uint8_t>(-1)) == 0, "");
+ static_assert(countl_zero(uint16_t{}) == 16, "");
+ static_assert(countl_zero(static_cast<uint16_t>(-1)) == 0, "");
+ static_assert(countl_zero(uint32_t{}) == 32, "");
+ static_assert(countl_zero(~uint32_t{}) == 0, "");
+ static_assert(countl_zero(uint64_t{}) == 64, "");
+ static_assert(countl_zero(~uint64_t{}) == 0, "");
+#endif
+
+ EXPECT_EQ(countl_zero(uint8_t{}), 8);
+ EXPECT_EQ(countl_zero(static_cast<uint8_t>(-1)), 0);
+ EXPECT_EQ(countl_zero(uint16_t{}), 16);
+ EXPECT_EQ(countl_zero(static_cast<uint16_t>(-1)), 0);
+ EXPECT_EQ(countl_zero(uint32_t{}), 32);
+ EXPECT_EQ(countl_zero(~uint32_t{}), 0);
+ EXPECT_EQ(countl_zero(uint64_t{}), 64);
+ EXPECT_EQ(countl_zero(~uint64_t{}), 0);
+
+ for (int i = 0; i < 8; i++) {
+ EXPECT_EQ(countl_zero(static_cast<uint8_t>(1u << i)), 7 - i);
+ }
+
+ for (int i = 0; i < 16; i++) {
+ EXPECT_EQ(countl_zero(static_cast<uint16_t>(1u << i)), 15 - i);
+ }
+
+ for (int i = 0; i < 32; i++) {
+ EXPECT_EQ(countl_zero(uint32_t{1} << i), 31 - i);
+ }
+
+ for (int i = 0; i < 64; i++) {
+ EXPECT_EQ(countl_zero(uint64_t{1} << i), 63 - i);
+ }
+}
+
+TEST(Counting, LeadingOnes) {
+#if ABSL_INTERNAL_HAS_CONSTEXPR_CLZ
+ static_assert(countl_one(uint8_t{}) == 0, "");
+ static_assert(countl_one(static_cast<uint8_t>(-1)) == 8, "");
+ static_assert(countl_one(uint16_t{}) == 0, "");
+ static_assert(countl_one(static_cast<uint16_t>(-1)) == 16, "");
+ static_assert(countl_one(uint32_t{}) == 0, "");
+ static_assert(countl_one(~uint32_t{}) == 32, "");
+ static_assert(countl_one(uint64_t{}) == 0, "");
+ static_assert(countl_one(~uint64_t{}) == 64, "");
+#endif
+
+ EXPECT_EQ(countl_one(uint8_t{}), 0);
+ EXPECT_EQ(countl_one(static_cast<uint8_t>(-1)), 8);
+ EXPECT_EQ(countl_one(uint16_t{}), 0);
+ EXPECT_EQ(countl_one(static_cast<uint16_t>(-1)), 16);
+ EXPECT_EQ(countl_one(uint32_t{}), 0);
+ EXPECT_EQ(countl_one(~uint32_t{}), 32);
+ EXPECT_EQ(countl_one(uint64_t{}), 0);
+ EXPECT_EQ(countl_one(~uint64_t{}), 64);
+}
+
+TEST(Counting, TrailingZeroes) {
+#if ABSL_INTERNAL_HAS_CONSTEXPR_CTZ
+ static_assert(countr_zero(uint8_t{}) == 8, "");
+ static_assert(countr_zero(static_cast<uint8_t>(-1)) == 0, "");
+ static_assert(countr_zero(uint16_t{}) == 16, "");
+ static_assert(countr_zero(static_cast<uint16_t>(-1)) == 0, "");
+ static_assert(countr_zero(uint32_t{}) == 32, "");
+ static_assert(countr_zero(~uint32_t{}) == 0, "");
+ static_assert(countr_zero(uint64_t{}) == 64, "");
+ static_assert(countr_zero(~uint64_t{}) == 0, "");
+#endif
+
+ EXPECT_EQ(countr_zero(uint8_t{}), 8);
+ EXPECT_EQ(countr_zero(static_cast<uint8_t>(-1)), 0);
+ EXPECT_EQ(countr_zero(uint16_t{}), 16);
+ EXPECT_EQ(countr_zero(static_cast<uint16_t>(-1)), 0);
+ EXPECT_EQ(countr_zero(uint32_t{}), 32);
+ EXPECT_EQ(countr_zero(~uint32_t{}), 0);
+ EXPECT_EQ(countr_zero(uint64_t{}), 64);
+ EXPECT_EQ(countr_zero(~uint64_t{}), 0);
+}
+
+TEST(Counting, TrailingOnes) {
+#if ABSL_INTERNAL_HAS_CONSTEXPR_CTZ
+ static_assert(countr_one(uint8_t{}) == 0, "");
+ static_assert(countr_one(static_cast<uint8_t>(-1)) == 8, "");
+ static_assert(countr_one(uint16_t{}) == 0, "");
+ static_assert(countr_one(static_cast<uint16_t>(-1)) == 16, "");
+ static_assert(countr_one(uint32_t{}) == 0, "");
+ static_assert(countr_one(~uint32_t{}) == 32, "");
+ static_assert(countr_one(uint64_t{}) == 0, "");
+ static_assert(countr_one(~uint64_t{}) == 64, "");
+#endif
+
+ EXPECT_EQ(countr_one(uint8_t{}), 0);
+ EXPECT_EQ(countr_one(static_cast<uint8_t>(-1)), 8);
+ EXPECT_EQ(countr_one(uint16_t{}), 0);
+ EXPECT_EQ(countr_one(static_cast<uint16_t>(-1)), 16);
+ EXPECT_EQ(countr_one(uint32_t{}), 0);
+ EXPECT_EQ(countr_one(~uint32_t{}), 32);
+ EXPECT_EQ(countr_one(uint64_t{}), 0);
+ EXPECT_EQ(countr_one(~uint64_t{}), 64);
+}
+
+TEST(Counting, Popcount) {
+#if ABSL_INTERNAL_HAS_CONSTEXPR_POPCOUNT
+ static_assert(popcount(uint8_t{}) == 0, "");
+ static_assert(popcount(uint8_t{1}) == 1, "");
+ static_assert(popcount(static_cast<uint8_t>(-1)) == 8, "");
+ static_assert(popcount(uint16_t{}) == 0, "");
+ static_assert(popcount(uint16_t{1}) == 1, "");
+ static_assert(popcount(static_cast<uint16_t>(-1)) == 16, "");
+ static_assert(popcount(uint32_t{}) == 0, "");
+ static_assert(popcount(uint32_t{1}) == 1, "");
+ static_assert(popcount(~uint32_t{}) == 32, "");
+ static_assert(popcount(uint64_t{}) == 0, "");
+ static_assert(popcount(uint64_t{1}) == 1, "");
+ static_assert(popcount(~uint64_t{}) == 64, "");
+#endif // ABSL_INTERNAL_HAS_CONSTEXPR_POPCOUNT
+
+ EXPECT_EQ(popcount(uint8_t{}), 0);
+ EXPECT_EQ(popcount(uint8_t{1}), 1);
+ EXPECT_EQ(popcount(static_cast<uint8_t>(-1)), 8);
+ EXPECT_EQ(popcount(uint16_t{}), 0);
+ EXPECT_EQ(popcount(uint16_t{1}), 1);
+ EXPECT_EQ(popcount(static_cast<uint16_t>(-1)), 16);
+ EXPECT_EQ(popcount(uint32_t{}), 0);
+ EXPECT_EQ(popcount(uint32_t{1}), 1);
+ EXPECT_EQ(popcount(~uint32_t{}), 32);
+ EXPECT_EQ(popcount(uint64_t{}), 0);
+ EXPECT_EQ(popcount(uint64_t{1}), 1);
+ EXPECT_EQ(popcount(~uint64_t{}), 64);
+
+ for (int i = 0; i < 8; i++) {
+ EXPECT_EQ(popcount(static_cast<uint8_t>(uint8_t{1} << i)), 1);
+ EXPECT_EQ(popcount(static_cast<uint8_t>(static_cast<uint8_t>(-1) ^
+ (uint8_t{1} << i))),
+ 7);
+ }
+
+ for (int i = 0; i < 16; i++) {
+ EXPECT_EQ(popcount(static_cast<uint16_t>(uint16_t{1} << i)), 1);
+ EXPECT_EQ(popcount(static_cast<uint16_t>(static_cast<uint16_t>(-1) ^
+ (uint16_t{1} << i))),
+ 15);
+ }
+
+ for (int i = 0; i < 32; i++) {
+ EXPECT_EQ(popcount(uint32_t{1} << i), 1);
+ EXPECT_EQ(popcount(static_cast<uint32_t>(-1) ^ (uint32_t{1} << i)), 31);
+ }
+
+ for (int i = 0; i < 64; i++) {
+ EXPECT_EQ(popcount(uint64_t{1} << i), 1);
+ EXPECT_EQ(popcount(static_cast<uint64_t>(-1) ^ (uint64_t{1} << i)), 63);
+ }
+}
+
+template <typename T>
+struct PopcountInput {
+ T value = 0;
+ int expected = 0;
+};
+
+template <typename T>
+PopcountInput<T> GeneratePopcountInput(absl::BitGen& gen) {
+ PopcountInput<T> ret;
+ for (int i = 0; i < std::numeric_limits<T>::digits; i++) {
+ bool coin = absl::Bernoulli(gen, 0.2);
+ if (coin) {
+ ret.value |= T{1} << i;
+ ret.expected++;
+ }
+ }
+ return ret;
+}
+
+TEST(Counting, PopcountFuzz) {
+ absl::BitGen rng;
+ constexpr int kTrials = 100;
+
+ for (int i = 0; i < kTrials; ++i) {
+ auto input = GeneratePopcountInput<uint8_t>(rng);
+ EXPECT_EQ(popcount(input.value), input.expected);
+ }
+
+ for (int i = 0; i < kTrials; ++i) {
+ auto input = GeneratePopcountInput<uint16_t>(rng);
+ EXPECT_EQ(popcount(input.value), input.expected);
+ }
+
+ for (int i = 0; i < kTrials; ++i) {
+ auto input = GeneratePopcountInput<uint32_t>(rng);
+ EXPECT_EQ(popcount(input.value), input.expected);
+ }
+
+ for (int i = 0; i < kTrials; ++i) {
+ auto input = GeneratePopcountInput<uint64_t>(rng);
+ EXPECT_EQ(popcount(input.value), input.expected);
+ }
+}
+
+TEST(IntegralPowersOfTwo, SingleBit) {
+ EXPECT_FALSE(has_single_bit(uint8_t{}));
+ EXPECT_FALSE(has_single_bit(static_cast<uint8_t>(-1)));
+ EXPECT_FALSE(has_single_bit(uint16_t{}));
+ EXPECT_FALSE(has_single_bit(static_cast<uint16_t>(-1)));
+ EXPECT_FALSE(has_single_bit(uint32_t{}));
+ EXPECT_FALSE(has_single_bit(~uint32_t{}));
+ EXPECT_FALSE(has_single_bit(uint64_t{}));
+ EXPECT_FALSE(has_single_bit(~uint64_t{}));
+
+ static_assert(!has_single_bit(0u), "");
+ static_assert(has_single_bit(1u), "");
+ static_assert(has_single_bit(2u), "");
+ static_assert(!has_single_bit(3u), "");
+ static_assert(has_single_bit(4u), "");
+ static_assert(!has_single_bit(1337u), "");
+ static_assert(has_single_bit(65536u), "");
+ static_assert(has_single_bit(uint32_t{1} << 30), "");
+ static_assert(has_single_bit(uint64_t{1} << 42), "");
+
+ EXPECT_FALSE(has_single_bit(0u));
+ EXPECT_TRUE(has_single_bit(1u));
+ EXPECT_TRUE(has_single_bit(2u));
+ EXPECT_FALSE(has_single_bit(3u));
+ EXPECT_TRUE(has_single_bit(4u));
+ EXPECT_FALSE(has_single_bit(1337u));
+ EXPECT_TRUE(has_single_bit(65536u));
+ EXPECT_TRUE(has_single_bit(uint32_t{1} << 30));
+ EXPECT_TRUE(has_single_bit(uint64_t{1} << 42));
+
+ EXPECT_TRUE(has_single_bit(
+ static_cast<uint8_t>(std::numeric_limits<uint8_t>::max() / 2 + 1)));
+ EXPECT_TRUE(has_single_bit(
+ static_cast<uint16_t>(std::numeric_limits<uint16_t>::max() / 2 + 1)));
+ EXPECT_TRUE(has_single_bit(
+ static_cast<uint32_t>(std::numeric_limits<uint32_t>::max() / 2 + 1)));
+ EXPECT_TRUE(has_single_bit(
+ static_cast<uint64_t>(std::numeric_limits<uint64_t>::max() / 2 + 1)));
+}
+
+template <typename T, T arg, T = bit_ceil(arg)>
+bool IsBitCeilConstantExpression(int) {
+ return true;
+}
+template <typename T, T arg>
+bool IsBitCeilConstantExpression(char) {
+ return false;
+}
+
+TEST(IntegralPowersOfTwo, Ceiling) {
+#if ABSL_INTERNAL_HAS_CONSTEXPR_CLZ
+ static_assert(bit_ceil(0u) == 1, "");
+ static_assert(bit_ceil(1u) == 1, "");
+ static_assert(bit_ceil(2u) == 2, "");
+ static_assert(bit_ceil(3u) == 4, "");
+ static_assert(bit_ceil(4u) == 4, "");
+ static_assert(bit_ceil(1337u) == 2048, "");
+ static_assert(bit_ceil(65536u) == 65536, "");
+ static_assert(bit_ceil(65536u - 1337u) == 65536, "");
+ static_assert(bit_ceil(uint32_t{0x80000000}) == uint32_t{0x80000000}, "");
+ static_assert(bit_ceil(uint64_t{0x40000000000}) == uint64_t{0x40000000000},
+ "");
+ static_assert(
+ bit_ceil(uint64_t{0x8000000000000000}) == uint64_t{0x8000000000000000},
+ "");
+
+ EXPECT_TRUE((IsBitCeilConstantExpression<uint8_t, uint8_t{0x0}>(0)));
+ EXPECT_TRUE((IsBitCeilConstantExpression<uint8_t, uint8_t{0x80}>(0)));
+ EXPECT_FALSE((IsBitCeilConstantExpression<uint8_t, uint8_t{0x81}>(0)));
+ EXPECT_FALSE((IsBitCeilConstantExpression<uint8_t, uint8_t{0xff}>(0)));
+
+ EXPECT_TRUE((IsBitCeilConstantExpression<uint16_t, uint16_t{0x0}>(0)));
+ EXPECT_TRUE((IsBitCeilConstantExpression<uint16_t, uint16_t{0x8000}>(0)));
+ EXPECT_FALSE((IsBitCeilConstantExpression<uint16_t, uint16_t{0x8001}>(0)));
+ EXPECT_FALSE((IsBitCeilConstantExpression<uint16_t, uint16_t{0xffff}>(0)));
+
+ EXPECT_TRUE((IsBitCeilConstantExpression<uint32_t, uint32_t{0x0}>(0)));
+ EXPECT_TRUE((IsBitCeilConstantExpression<uint32_t, uint32_t{0x80000000}>(0)));
+ EXPECT_FALSE(
+ (IsBitCeilConstantExpression<uint32_t, uint32_t{0x80000001}>(0)));
+ EXPECT_FALSE(
+ (IsBitCeilConstantExpression<uint32_t, uint32_t{0xffffffff}>(0)));
+
+ EXPECT_TRUE((IsBitCeilConstantExpression<uint64_t, uint64_t{0x0}>(0)));
+ EXPECT_TRUE(
+ (IsBitCeilConstantExpression<uint64_t, uint64_t{0x8000000000000000}>(0)));
+ EXPECT_FALSE(
+ (IsBitCeilConstantExpression<uint64_t, uint64_t{0x8000000000000001}>(0)));
+ EXPECT_FALSE(
+ (IsBitCeilConstantExpression<uint64_t, uint64_t{0xffffffffffffffff}>(0)));
+#endif
+
+ EXPECT_EQ(bit_ceil(0u), 1);
+ EXPECT_EQ(bit_ceil(1u), 1);
+ EXPECT_EQ(bit_ceil(2u), 2);
+ EXPECT_EQ(bit_ceil(3u), 4);
+ EXPECT_EQ(bit_ceil(4u), 4);
+ EXPECT_EQ(bit_ceil(1337u), 2048);
+ EXPECT_EQ(bit_ceil(65536u), 65536);
+ EXPECT_EQ(bit_ceil(65536u - 1337u), 65536);
+ EXPECT_EQ(bit_ceil(uint64_t{0x40000000000}), uint64_t{0x40000000000});
+}
+
+TEST(IntegralPowersOfTwo, Floor) {
+#if ABSL_INTERNAL_HAS_CONSTEXPR_CLZ
+ static_assert(bit_floor(0u) == 0, "");
+ static_assert(bit_floor(1u) == 1, "");
+ static_assert(bit_floor(2u) == 2, "");
+ static_assert(bit_floor(3u) == 2, "");
+ static_assert(bit_floor(4u) == 4, "");
+ static_assert(bit_floor(1337u) == 1024, "");
+ static_assert(bit_floor(65536u) == 65536, "");
+ static_assert(bit_floor(65536u - 1337u) == 32768, "");
+ static_assert(bit_floor(uint64_t{0x40000000000}) == uint64_t{0x40000000000},
+ "");
+#endif
+
+ EXPECT_EQ(bit_floor(0u), 0);
+ EXPECT_EQ(bit_floor(1u), 1);
+ EXPECT_EQ(bit_floor(2u), 2);
+ EXPECT_EQ(bit_floor(3u), 2);
+ EXPECT_EQ(bit_floor(4u), 4);
+ EXPECT_EQ(bit_floor(1337u), 1024);
+ EXPECT_EQ(bit_floor(65536u), 65536);
+ EXPECT_EQ(bit_floor(65536u - 1337u), 32768);
+ EXPECT_EQ(bit_floor(uint64_t{0x40000000000}), uint64_t{0x40000000000});
+
+ for (int i = 0; i < 8; i++) {
+ uint8_t input = uint8_t{1} << i;
+ EXPECT_EQ(bit_floor(input), input);
+ if (i > 0) {
+ EXPECT_EQ(bit_floor(static_cast<uint8_t>(input + 1)), input);
+ }
+ }
+
+ for (int i = 0; i < 16; i++) {
+ uint16_t input = uint16_t{1} << i;
+ EXPECT_EQ(bit_floor(input), input);
+ if (i > 0) {
+ EXPECT_EQ(bit_floor(static_cast<uint16_t>(input + 1)), input);
+ }
+ }
+
+ for (int i = 0; i < 32; i++) {
+ uint32_t input = uint32_t{1} << i;
+ EXPECT_EQ(bit_floor(input), input);
+ if (i > 0) {
+ EXPECT_EQ(bit_floor(input + 1), input);
+ }
+ }
+
+ for (int i = 0; i < 64; i++) {
+ uint64_t input = uint64_t{1} << i;
+ EXPECT_EQ(bit_floor(input), input);
+ if (i > 0) {
+ EXPECT_EQ(bit_floor(input + 1), input);
+ }
+ }
+}
+
+TEST(IntegralPowersOfTwo, Width) {
+#if ABSL_INTERNAL_HAS_CONSTEXPR_CLZ
+ static_assert(bit_width(uint8_t{}) == 0, "");
+ static_assert(bit_width(uint8_t{1}) == 1, "");
+ static_assert(bit_width(uint8_t{3}) == 2, "");
+ static_assert(bit_width(static_cast<uint8_t>(-1)) == 8, "");
+ static_assert(bit_width(uint16_t{}) == 0, "");
+ static_assert(bit_width(uint16_t{1}) == 1, "");
+ static_assert(bit_width(uint16_t{3}) == 2, "");
+ static_assert(bit_width(static_cast<uint16_t>(-1)) == 16, "");
+ static_assert(bit_width(uint32_t{}) == 0, "");
+ static_assert(bit_width(uint32_t{1}) == 1, "");
+ static_assert(bit_width(uint32_t{3}) == 2, "");
+ static_assert(bit_width(~uint32_t{}) == 32, "");
+ static_assert(bit_width(uint64_t{}) == 0, "");
+ static_assert(bit_width(uint64_t{1}) == 1, "");
+ static_assert(bit_width(uint64_t{3}) == 2, "");
+ static_assert(bit_width(~uint64_t{}) == 64, "");
+#endif
+
+ EXPECT_EQ(bit_width(uint8_t{}), 0);
+ EXPECT_EQ(bit_width(uint8_t{1}), 1);
+ EXPECT_EQ(bit_width(uint8_t{3}), 2);
+ EXPECT_EQ(bit_width(static_cast<uint8_t>(-1)), 8);
+ EXPECT_EQ(bit_width(uint16_t{}), 0);
+ EXPECT_EQ(bit_width(uint16_t{1}), 1);
+ EXPECT_EQ(bit_width(uint16_t{3}), 2);
+ EXPECT_EQ(bit_width(static_cast<uint16_t>(-1)), 16);
+ EXPECT_EQ(bit_width(uint32_t{}), 0);
+ EXPECT_EQ(bit_width(uint32_t{1}), 1);
+ EXPECT_EQ(bit_width(uint32_t{3}), 2);
+ EXPECT_EQ(bit_width(~uint32_t{}), 32);
+ EXPECT_EQ(bit_width(uint64_t{}), 0);
+ EXPECT_EQ(bit_width(uint64_t{1}), 1);
+ EXPECT_EQ(bit_width(uint64_t{3}), 2);
+ EXPECT_EQ(bit_width(~uint64_t{}), 64);
+
+ for (int i = 0; i < 8; i++) {
+ EXPECT_EQ(bit_width(static_cast<uint8_t>(uint8_t{1} << i)), i + 1);
+ }
+
+ for (int i = 0; i < 16; i++) {
+ EXPECT_EQ(bit_width(static_cast<uint16_t>(uint16_t{1} << i)), i + 1);
+ }
+
+ for (int i = 0; i < 32; i++) {
+ EXPECT_EQ(bit_width(uint32_t{1} << i), i + 1);
+ }
+
+ for (int i = 0; i < 64; i++) {
+ EXPECT_EQ(bit_width(uint64_t{1} << i), i + 1);
+ }
+}
+
+// On GCC and Clang, anticiapte that implementations will be constexpr
+#if defined(__GNUC__)
+static_assert(ABSL_INTERNAL_HAS_CONSTEXPR_POPCOUNT,
+ "popcount should be constexpr");
+static_assert(ABSL_INTERNAL_HAS_CONSTEXPR_CLZ, "clz should be constexpr");
+static_assert(ABSL_INTERNAL_HAS_CONSTEXPR_CTZ, "ctz should be constexpr");
+#endif
+
+} // namespace
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/numeric/int128.cc b/absl/numeric/int128.cc
index e21e5e9..5160df7 100644
--- a/absl/numeric/int128.cc
+++ b/absl/numeric/int128.cc
@@ -23,8 +23,8 @@
#include <string>
#include <type_traits>
-#include "absl/base/internal/bits.h"
#include "absl/base/optimization.h"
+#include "absl/numeric/bits.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -43,11 +43,11 @@
inline ABSL_ATTRIBUTE_ALWAYS_INLINE int Fls128(uint128 n) {
if (uint64_t hi = Uint128High64(n)) {
ABSL_INTERNAL_ASSUME(hi != 0);
- return 127 - base_internal::CountLeadingZeros64(hi);
+ return 127 - countl_zero(hi);
}
const uint64_t low = Uint128Low64(n);
ABSL_INTERNAL_ASSUME(low != 0);
- return 63 - base_internal::CountLeadingZeros64(low);
+ return 63 - countl_zero(low);
}
// Long division/modulo for uint128 implemented using the shift-subtract
diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel
index 81e150e..66ffcbc 100644
--- a/absl/random/BUILD.bazel
+++ b/absl/random/BUILD.bazel
@@ -69,6 +69,7 @@
"//absl/base:config",
"//absl/base:core_headers",
"//absl/meta:type_traits",
+ "//absl/numeric:bits",
"//absl/random/internal:distribution_caller",
"//absl/random/internal:fast_uniform_bits",
"//absl/random/internal:fastmath",
@@ -187,6 +188,7 @@
":distributions",
":random",
"//absl/base:raw_logging_internal",
+ "//absl/numeric:representation",
"//absl/random/internal:distribution_test_util",
"//absl/random/internal:pcg_engine",
"//absl/random/internal:sequence_urbg",
@@ -307,6 +309,7 @@
":random",
"//absl/base:core_headers",
"//absl/base:raw_logging_internal",
+ "//absl/numeric:representation",
"//absl/random/internal:distribution_test_util",
"//absl/random/internal:pcg_engine",
"//absl/random/internal:sequence_urbg",
@@ -330,6 +333,7 @@
":random",
"//absl/base:core_headers",
"//absl/base:raw_logging_internal",
+ "//absl/numeric:representation",
"//absl/random/internal:distribution_test_util",
"//absl/random/internal:sequence_urbg",
"//absl/strings",
@@ -376,6 +380,7 @@
":distributions",
":random",
"//absl/base:raw_logging_internal",
+ "//absl/numeric:representation",
"//absl/random/internal:distribution_test_util",
"//absl/random/internal:pcg_engine",
"//absl/random/internal:sequence_urbg",
diff --git a/absl/random/BUILD.gn b/absl/random/BUILD.gn
index e3143ae..e5aeea2 100644
--- a/absl/random/BUILD.gn
+++ b/absl/random/BUILD.gn
@@ -39,6 +39,7 @@
"//third_party/abseil-cpp/absl/base:config",
"//third_party/abseil-cpp/absl/base:core_headers",
"//third_party/abseil-cpp/absl/meta:type_traits",
+ "//third_party/abseil-cpp/absl/numeric:bits",
"//third_party/abseil-cpp/absl/random/internal:distribution_caller",
"//third_party/abseil-cpp/absl/random/internal:fast_uniform_bits",
"//third_party/abseil-cpp/absl/random/internal:fastmath",
diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt
index 7d7bec8..3009a03 100644
--- a/absl/random/CMakeLists.txt
+++ b/absl/random/CMakeLists.txt
@@ -259,6 +259,7 @@
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::numeric_representation
absl::random_distributions
absl::random_random
absl::random_internal_distribution_test_util
@@ -381,6 +382,7 @@
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::core_headers
+ absl::numeric_representation
absl::random_distributions
absl::random_internal_distribution_test_util
absl::random_internal_pcg_engine
@@ -404,6 +406,7 @@
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::core_headers
+ absl::numeric_representation
absl::random_distributions
absl::random_internal_distribution_test_util
absl::random_internal_sequence_urbg
@@ -446,6 +449,7 @@
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::numeric_representation
absl::random_distributions
absl::random_internal_distribution_test_util
absl::random_internal_pcg_engine
@@ -611,6 +615,7 @@
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::config
+ absl::endian
TESTONLY
)
@@ -758,6 +763,7 @@
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::endian
absl::random_internal_iostream_state_saver
absl::random_internal_randen
absl::raw_logging_internal
@@ -1119,6 +1125,7 @@
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::endian
absl::random_internal_randen_slow
gtest_main
)
diff --git a/absl/random/beta_distribution_test.cc b/absl/random/beta_distribution_test.cc
index 277e4dc..44cdfdd 100644
--- a/absl/random/beta_distribution_test.cc
+++ b/absl/random/beta_distribution_test.cc
@@ -21,12 +21,14 @@
#include <random>
#include <sstream>
#include <string>
+#include <type_traits>
#include <unordered_map>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/raw_logging.h"
+#include "absl/numeric/internal/representation.h"
#include "absl/random/internal/chi_square.h"
#include "absl/random/internal/distribution_test_util.h"
#include "absl/random/internal/pcg_engine.h"
@@ -42,7 +44,15 @@
template <typename IntType>
class BetaDistributionInterfaceTest : public ::testing::Test {};
-using RealTypes = ::testing::Types<float, double, long double>;
+// double-double arithmetic is not supported well by either GCC or Clang; see
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99048,
+// https://bugs.llvm.org/show_bug.cgi?id=49131, and
+// https://bugs.llvm.org/show_bug.cgi?id=49132. Don't bother running these tests
+// with double doubles until compiler support is better.
+using RealTypes =
+ std::conditional<absl::numeric_internal::IsDoubleDouble(),
+ ::testing::Types<float, double>,
+ ::testing::Types<float, double, long double>>::type;
TYPED_TEST_CASE(BetaDistributionInterfaceTest, RealTypes);
TYPED_TEST(BetaDistributionInterfaceTest, SerializeTest) {
@@ -53,9 +63,6 @@
const TypeParam kLargeA =
std::exp(std::log((std::numeric_limits<TypeParam>::max)()) -
std::log(std::log((std::numeric_limits<TypeParam>::max)())));
- const TypeParam kLargeAPPC = std::exp(
- std::log((std::numeric_limits<TypeParam>::max)()) -
- std::log(std::log((std::numeric_limits<TypeParam>::max)())) - 10.0f);
using param_type = typename absl::beta_distribution<TypeParam>::param_type;
constexpr int kCount = 1000;
@@ -76,9 +83,6 @@
kLargeA, //
std::nextafter(kLargeA, TypeParam(0)), //
std::nextafter(kLargeA, std::numeric_limits<TypeParam>::max()),
- kLargeAPPC, //
- std::nextafter(kLargeAPPC, TypeParam(0)),
- std::nextafter(kLargeAPPC, std::numeric_limits<TypeParam>::max()),
// Boundary cases.
std::numeric_limits<TypeParam>::max(),
std::numeric_limits<TypeParam>::epsilon(),
@@ -125,28 +129,6 @@
ss >> after;
-#if defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \
- defined(__ppc__) || defined(__PPC__)
- if (std::is_same<TypeParam, long double>::value) {
- // Roundtripping floating point values requires sufficient precision
- // to reconstruct the exact value. It turns out that long double
- // has some errors doing this on ppc.
- if (alpha <= std::numeric_limits<double>::max() &&
- alpha >= std::numeric_limits<double>::lowest()) {
- EXPECT_EQ(static_cast<double>(before.alpha()),
- static_cast<double>(after.alpha()))
- << ss.str();
- }
- if (beta <= std::numeric_limits<double>::max() &&
- beta >= std::numeric_limits<double>::lowest()) {
- EXPECT_EQ(static_cast<double>(before.beta()),
- static_cast<double>(after.beta()))
- << ss.str();
- }
- continue;
- }
-#endif
-
EXPECT_EQ(before.alpha(), after.alpha());
EXPECT_EQ(before.beta(), after.beta());
EXPECT_EQ(before, after) //
diff --git a/absl/random/exponential_distribution_test.cc b/absl/random/exponential_distribution_test.cc
index 8e9e69b..af11d61 100644
--- a/absl/random/exponential_distribution_test.cc
+++ b/absl/random/exponential_distribution_test.cc
@@ -30,6 +30,7 @@
#include "gtest/gtest.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/macros.h"
+#include "absl/numeric/internal/representation.h"
#include "absl/random/internal/chi_square.h"
#include "absl/random/internal/distribution_test_util.h"
#include "absl/random/internal/pcg_engine.h"
@@ -47,11 +48,15 @@
template <typename RealType>
class ExponentialDistributionTypedTest : public ::testing::Test {};
-#if defined(__EMSCRIPTEN__)
-using RealTypes = ::testing::Types<float, double>;
-#else
-using RealTypes = ::testing::Types<float, double, long double>;
-#endif // defined(__EMSCRIPTEN__)
+// double-double arithmetic is not supported well by either GCC or Clang; see
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99048,
+// https://bugs.llvm.org/show_bug.cgi?id=49131, and
+// https://bugs.llvm.org/show_bug.cgi?id=49132. Don't bother running these tests
+// with double doubles until compiler support is better.
+using RealTypes =
+ std::conditional<absl::numeric_internal::IsDoubleDouble(),
+ ::testing::Types<float, double>,
+ ::testing::Types<float, double, long double>>::type;
TYPED_TEST_CASE(ExponentialDistributionTypedTest, RealTypes);
TYPED_TEST(ExponentialDistributionTypedTest, SerializeTest) {
@@ -130,23 +135,6 @@
ss >> after;
-#if defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \
- defined(__ppc__) || defined(__PPC__)
- if (std::is_same<TypeParam, long double>::value) {
- // Roundtripping floating point values requires sufficient precision to
- // reconstruct the exact value. It turns out that long double has some
- // errors doing this on ppc, particularly for values
- // near {1.0 +/- epsilon}.
- if (lambda <= std::numeric_limits<double>::max() &&
- lambda >= std::numeric_limits<double>::lowest()) {
- EXPECT_EQ(static_cast<double>(before.lambda()),
- static_cast<double>(after.lambda()))
- << ss.str();
- }
- continue;
- }
-#endif
-
EXPECT_EQ(before.lambda(), after.lambda()) //
<< ss.str() << " " //
<< (ss.good() ? "good " : "") //
diff --git a/absl/random/gaussian_distribution_test.cc b/absl/random/gaussian_distribution_test.cc
index 02ac578..c0bac2b 100644
--- a/absl/random/gaussian_distribution_test.cc
+++ b/absl/random/gaussian_distribution_test.cc
@@ -21,12 +21,14 @@
#include <iterator>
#include <random>
#include <string>
+#include <type_traits>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/macros.h"
+#include "absl/numeric/internal/representation.h"
#include "absl/random/internal/chi_square.h"
#include "absl/random/internal/distribution_test_util.h"
#include "absl/random/internal/sequence_urbg.h"
@@ -43,7 +45,15 @@
template <typename RealType>
class GaussianDistributionInterfaceTest : public ::testing::Test {};
-using RealTypes = ::testing::Types<float, double, long double>;
+// double-double arithmetic is not supported well by either GCC or Clang; see
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99048,
+// https://bugs.llvm.org/show_bug.cgi?id=49131, and
+// https://bugs.llvm.org/show_bug.cgi?id=49132. Don't bother running these tests
+// with double doubles until compiler support is better.
+using RealTypes =
+ std::conditional<absl::numeric_internal::IsDoubleDouble(),
+ ::testing::Types<float, double>,
+ ::testing::Types<float, double, long double>>::type;
TYPED_TEST_CASE(GaussianDistributionInterfaceTest, RealTypes);
TYPED_TEST(GaussianDistributionInterfaceTest, SerializeTest) {
@@ -129,32 +139,6 @@
ss >> after;
-#if defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \
- defined(__ppc__) || defined(__PPC__) || defined(__EMSCRIPTEN__)
- if (std::is_same<TypeParam, long double>::value) {
- // Roundtripping floating point values requires sufficient precision
- // to reconstruct the exact value. It turns out that long double
- // has some errors doing this on ppc, particularly for values
- // near {1.0 +/- epsilon}.
- //
- // Emscripten is even worse, implementing long double as a 128-bit
- // type, but shipping with a strtold() that doesn't support that.
- if (mean <= std::numeric_limits<double>::max() &&
- mean >= std::numeric_limits<double>::lowest()) {
- EXPECT_EQ(static_cast<double>(before.mean()),
- static_cast<double>(after.mean()))
- << ss.str();
- }
- if (stddev <= std::numeric_limits<double>::max() &&
- stddev >= std::numeric_limits<double>::lowest()) {
- EXPECT_EQ(static_cast<double>(before.stddev()),
- static_cast<double>(after.stddev()))
- << ss.str();
- }
- continue;
- }
-#endif
-
EXPECT_EQ(before.mean(), after.mean());
EXPECT_EQ(before.stddev(), after.stddev()) //
<< ss.str() << " " //
diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel
index 8485e28..612b150 100644
--- a/absl/random/internal/BUILD.bazel
+++ b/absl/random/internal/BUILD.bazel
@@ -75,7 +75,8 @@
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS + select({
- "//absl:windows": ["-DEFAULTLIB:bcrypt.lib"],
+ "//absl:msvc_compiler": ["-DEFAULTLIB:bcrypt.lib"],
+ "//absl:clang-cl_compiler": ["-DEFAULTLIB:bcrypt.lib"],
"//conditions:default": [],
}),
deps = [
@@ -98,7 +99,8 @@
],
copts = ABSL_DEFAULT_COPTS,
linkopts = select({
- "//absl:windows": [],
+ "//absl:msvc_compiler": [],
+ "//absl:clang-cl_compiler": [],
"//absl:wasm": [],
"//conditions:default": ["-pthread"],
}) + ABSL_DEFAULT_LINKOPTS,
@@ -124,7 +126,10 @@
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
- deps = ["//absl/base:config"],
+ deps = [
+ "//absl/base:config",
+ "//absl/base:endian",
+ ],
)
cc_library(
@@ -175,8 +180,8 @@
deps = [
":fastmath",
":traits",
- "//absl/base:bits",
"//absl/meta:type_traits",
+ "//absl/numeric:bits",
],
)
@@ -187,7 +192,7 @@
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
- deps = ["//absl/base:bits"],
+ deps = ["//absl/numeric:bits"],
)
cc_library(
@@ -197,8 +202,8 @@
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":traits",
- "//absl/base:bits",
"//absl/base:config",
+ "//absl/numeric:bits",
"//absl/numeric:int128",
],
)
@@ -229,6 +234,7 @@
":iostream_state_saver",
"//absl/base:config",
"//absl/meta:type_traits",
+ "//absl/numeric:bits",
"//absl/numeric:int128",
],
)
@@ -241,6 +247,7 @@
deps = [
":iostream_state_saver",
":randen",
+ "//absl/base:endian",
"//absl/meta:type_traits",
],
)
@@ -320,7 +327,8 @@
"randen_hwaes.h",
],
copts = ABSL_DEFAULT_COPTS + ABSL_RANDOM_RANDEN_COPTS + select({
- "//absl:windows": [],
+ "//absl:msvc_compiler": [],
+ "//absl:clang-cl_compiler": [],
"//conditions:default": ["-Wno-pass-failed"],
}),
linkopts = ABSL_DEFAULT_LINKOPTS,
@@ -400,8 +408,8 @@
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":generate_real",
- "//absl/base:bits",
"//absl/flags:flag",
+ "//absl/numeric:bits",
"@com_google_googletest//:gtest_main",
],
)
@@ -605,6 +613,7 @@
deps = [
":platform",
":randen_slow",
+ "//absl/base:endian",
"@com_google_googletest//:gtest_main",
],
)
@@ -634,7 +643,7 @@
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":wide_multiply",
- "//absl/base:bits",
+ "//absl/numeric:bits",
"//absl/numeric:int128",
"@com_google_googletest//:gtest_main",
],
diff --git a/absl/random/internal/BUILD.gn b/absl/random/internal/BUILD.gn
index b97592c..8dbe709 100644
--- a/absl/random/internal/BUILD.gn
+++ b/absl/random/internal/BUILD.gn
@@ -63,7 +63,10 @@
absl_source_set("explicit_seed_seq") {
testonly = true
public = [ "explicit_seed_seq.h" ]
- deps = [ "//third_party/abseil-cpp/absl/base:config" ]
+ deps = [
+ "//third_party/abseil-cpp/absl/base:config",
+ "//third_party/abseil-cpp/absl/base:endian",
+ ]
}
absl_source_set("sequence_urbg") {
@@ -96,22 +99,22 @@
deps = [
":fastmath",
":traits",
- "//third_party/abseil-cpp/absl/base:bits",
"//third_party/abseil-cpp/absl/meta:type_traits",
+ "//third_party/abseil-cpp/absl/numeric:bits",
]
}
absl_source_set("fastmath") {
public = [ "fastmath.h" ]
- deps = [ "//third_party/abseil-cpp/absl/base:bits" ]
+ deps = [ "//third_party/abseil-cpp/absl/numeric:bits" ]
}
absl_source_set("wide_multiply") {
public = [ "wide_multiply.h" ]
deps = [
":traits",
- "//third_party/abseil-cpp/absl/base:bits",
"//third_party/abseil-cpp/absl/base:config",
+ "//third_party/abseil-cpp/absl/numeric:bits",
"//third_party/abseil-cpp/absl/numeric:int128",
]
}
@@ -136,6 +139,7 @@
":iostream_state_saver",
"//third_party/abseil-cpp/absl/base:config",
"//third_party/abseil-cpp/absl/meta:type_traits",
+ "//third_party/abseil-cpp/absl/numeric:bits",
"//third_party/abseil-cpp/absl/numeric:int128",
]
}
@@ -145,6 +149,7 @@
deps = [
":iostream_state_saver",
":randen",
+ "//third_party/abseil-cpp/absl/base:endian",
"//third_party/abseil-cpp/absl/meta:type_traits",
]
}
diff --git a/absl/random/internal/explicit_seed_seq.h b/absl/random/internal/explicit_seed_seq.h
index 6a743ea..e3aa31a 100644
--- a/absl/random/internal/explicit_seed_seq.h
+++ b/absl/random/internal/explicit_seed_seq.h
@@ -23,6 +23,7 @@
#include <vector>
#include "absl/base/config.h"
+#include "absl/base/internal/endian.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -73,7 +74,7 @@
template <typename OutIterator>
void generate(OutIterator begin, OutIterator end) {
for (size_t index = 0; begin != end; begin++) {
- *begin = state_.empty() ? 0 : state_[index++];
+ *begin = state_.empty() ? 0 : little_endian::FromHost32(state_[index++]);
if (index >= state_.size()) {
index = 0;
}
diff --git a/absl/random/internal/fastmath.h b/absl/random/internal/fastmath.h
index 6baeb5a..963b769 100644
--- a/absl/random/internal/fastmath.h
+++ b/absl/random/internal/fastmath.h
@@ -22,27 +22,22 @@
#include <cmath>
#include <cstdint>
-#include "absl/base/internal/bits.h"
+#include "absl/numeric/bits.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace random_internal {
-// Returns the position of the first bit set.
-inline int LeadingSetBit(uint64_t n) {
- return 64 - base_internal::CountLeadingZeros64(n);
-}
-
// Compute log2(n) using integer operations.
// While std::log2 is more accurate than std::log(n) / std::log(2), for
// very large numbers--those close to std::numeric_limits<uint64_t>::max() - 2,
// for instance--std::log2 rounds up rather than down, which introduces
// definite skew in the results.
inline int IntLog2Floor(uint64_t n) {
- return (n <= 1) ? 0 : (63 - base_internal::CountLeadingZeros64(n));
+ return (n <= 1) ? 0 : (63 - countl_zero(n));
}
inline int IntLog2Ceil(uint64_t n) {
- return (n <= 1) ? 0 : (64 - base_internal::CountLeadingZeros64(n - 1));
+ return (n <= 1) ? 0 : (64 - countl_zero(n - 1));
}
inline double StirlingLogFactorial(double n) {
@@ -55,18 +50,6 @@
(1.0 / 360.0) * ninv * ninv * ninv;
}
-// Rotate value right.
-//
-// We only implement the uint32_t / uint64_t versions because
-// 1) those are the only ones we use, and
-// 2) those are the only ones where clang detects the rotate idiom correctly.
-inline constexpr uint32_t rotr(uint32_t value, uint8_t bits) {
- return (value >> (bits & 31)) | (value << ((-bits) & 31));
-}
-inline constexpr uint64_t rotr(uint64_t value, uint8_t bits) {
- return (value >> (bits & 63)) | (value << ((-bits) & 63));
-}
-
} // namespace random_internal
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/random/internal/fastmath_test.cc b/absl/random/internal/fastmath_test.cc
index 65859c2..0d6f9dc 100644
--- a/absl/random/internal/fastmath_test.cc
+++ b/absl/random/internal/fastmath_test.cc
@@ -27,19 +27,6 @@
namespace {
-TEST(DistributionImplTest, LeadingSetBit) {
- using absl::random_internal::LeadingSetBit;
- constexpr uint64_t kZero = 0;
- EXPECT_EQ(0, LeadingSetBit(kZero));
- EXPECT_EQ(64, LeadingSetBit(~kZero));
-
- for (int index = 0; index < 64; index++) {
- uint64_t x = static_cast<uint64_t>(1) << index;
- EXPECT_EQ(index + 1, LeadingSetBit(x)) << index;
- EXPECT_EQ(index + 1, LeadingSetBit(x + x - 1)) << index;
- }
-}
-
TEST(FastMathTest, IntLog2FloorTest) {
using absl::random_internal::IntLog2Floor;
constexpr uint64_t kZero = 0;
diff --git a/absl/random/internal/generate_real.h b/absl/random/internal/generate_real.h
index 20f6d20..4f62873 100644
--- a/absl/random/internal/generate_real.h
+++ b/absl/random/internal/generate_real.h
@@ -23,8 +23,8 @@
#include <limits>
#include <type_traits>
-#include "absl/base/internal/bits.h"
#include "absl/meta/type_traits.h"
+#include "absl/numeric/bits.h"
#include "absl/random/internal/fastmath.h"
#include "absl/random/internal/traits.h"
@@ -120,7 +120,7 @@
// Number of leading zeros is mapped to the exponent: 2^-clz
// bits is 0..01xxxxxx. After shifting, we're left with 1xxx...0..0
- int clz = base_internal::CountLeadingZeros64(bits);
+ int clz = countl_zero(bits);
bits <<= (IncludeZero ? clz : (clz & 63)); // remove 0-bits.
exp -= clz; // set the exponent.
bits >>= (63 - kExp);
diff --git a/absl/random/internal/generate_real_test.cc b/absl/random/internal/generate_real_test.cc
index 4bdc453..b099dbf 100644
--- a/absl/random/internal/generate_real_test.cc
+++ b/absl/random/internal/generate_real_test.cc
@@ -20,8 +20,8 @@
#include <string>
#include "gtest/gtest.h"
-#include "absl/base/internal/bits.h"
#include "absl/flags/flag.h"
+#include "absl/numeric/bits.h"
ABSL_FLAG(int64_t, absl_random_test_trials, 50000,
"Number of trials for the probability tests.");
@@ -413,7 +413,6 @@
}
TEST(GenerateRealTest, ExhaustiveFloat) {
- using absl::base_internal::CountLeadingZeros64;
auto ToFloat = [](uint64_t a) {
return GenerateRealFromBits<float, GeneratePositiveTag, true>(a);
};
@@ -464,7 +463,7 @@
// Adjust decrement and check value based on how many leading 0
// bits are set in the current value.
- const int clz = CountLeadingZeros64(x);
+ const int clz = absl::countl_zero(x);
if (clz < kDig) {
dec <<= (kDig - clz);
chk = (~uint64_t(0)) >> (clz + 1);
diff --git a/absl/random/internal/iostream_state_saver_test.cc b/absl/random/internal/iostream_state_saver_test.cc
index 7bb8ad9..6e66266 100644
--- a/absl/random/internal/iostream_state_saver_test.cc
+++ b/absl/random/internal/iostream_state_saver_test.cc
@@ -14,6 +14,9 @@
#include "absl/random/internal/iostream_state_saver.h"
+#include <errno.h>
+#include <stdio.h>
+
#include <sstream>
#include <string>
@@ -272,7 +275,6 @@
}
}
-#if !defined(__EMSCRIPTEN__)
TEST(IOStreamStateSaver, RoundTripLongDoubles) {
// Technically, C++ only guarantees that long double is at least as large as a
// double. Practically it varies from 64-bits to 128-bits.
@@ -350,7 +352,6 @@
}
}
}
-#endif // !defined(__EMSCRIPTEN__)
TEST(StrToDTest, DoubleMin) {
const char kV[] = "2.22507385850720138e-308";
diff --git a/absl/random/internal/mock_helpers.h b/absl/random/internal/mock_helpers.h
index 9af27ab..a412ff2 100644
--- a/absl/random/internal/mock_helpers.h
+++ b/absl/random/internal/mock_helpers.h
@@ -80,6 +80,13 @@
}
public:
+ // InvokeMock is private; this provides access for some specialized use cases.
+ template <typename URBG>
+ static inline bool PrivateInvokeMock(URBG* urbg, IdType type,
+ void* args_tuple, void* result) {
+ return urbg->InvokeMock(type, args_tuple, result);
+ }
+
// Invoke a mock for the KeyT (may or may not be a signature).
//
// KeyT is used to generate a typeid-based lookup key for the mock.
@@ -109,11 +116,11 @@
// The mocked function signature will be composed from KeyT as:
// result_type(args...)
template <typename KeyT, typename MockURBG>
- static auto MockFor(MockURBG& m) -> decltype(
- std::declval<MockURBG>()
- .template RegisterMock<typename KeySignature<KeyT>::result_type,
- typename KeySignature<KeyT>::arg_tuple_type>(
- std::declval<IdType>())) {
+ static auto MockFor(MockURBG& m)
+ -> decltype(m.template RegisterMock<
+ typename KeySignature<KeyT>::result_type,
+ typename KeySignature<KeyT>::arg_tuple_type>(
+ std::declval<IdType>())) {
return m.template RegisterMock<typename KeySignature<KeyT>::result_type,
typename KeySignature<KeyT>::arg_tuple_type>(
::absl::base_internal::FastTypeId<KeyT>());
diff --git a/absl/random/internal/mock_overload_set.h b/absl/random/internal/mock_overload_set.h
index dccc6ce..c5ce358 100644
--- a/absl/random/internal/mock_overload_set.h
+++ b/absl/random/internal/mock_overload_set.h
@@ -45,9 +45,8 @@
"Overload signature must have return type matching the "
"distribution result_type.");
using KeyT = Ret(DistrT, std::tuple<Args...>);
- auto gmock_Call(
- absl::MockingBitGen& gen, // NOLINT(google-runtime-references)
- const ::testing::Matcher<Args>&... matchers)
+ auto gmock_Call(absl::MockingBitGen& gen,
+ const ::testing::Matcher<Args>&... matchers)
-> decltype(MockHelpers::MockFor<KeyT>(gen).gmock_Call(matchers...)) {
return MockHelpers::MockFor<KeyT>(gen).gmock_Call(matchers...);
}
@@ -59,10 +58,9 @@
"Overload signature must have return type matching the "
"distribution result_type.");
using KeyT = Ret(DistrT, std::tuple<Arg, Args...>);
- auto gmock_Call(
- const ::testing::Matcher<Arg>& matcher,
- absl::MockingBitGen& gen, // NOLINT(google-runtime-references)
- const ::testing::Matcher<Args>&... matchers)
+ auto gmock_Call(const ::testing::Matcher<Arg>& matcher,
+ absl::MockingBitGen& gen,
+ const ::testing::Matcher<Args>&... matchers)
-> decltype(MockHelpers::MockFor<KeyT>(gen).gmock_Call(matcher,
matchers...)) {
return MockHelpers::MockFor<KeyT>(gen).gmock_Call(matcher, matchers...);
diff --git a/absl/random/internal/pcg_engine.h b/absl/random/internal/pcg_engine.h
index 53c23fe..8efaf2e 100644
--- a/absl/random/internal/pcg_engine.h
+++ b/absl/random/internal/pcg_engine.h
@@ -19,6 +19,7 @@
#include "absl/base/config.h"
#include "absl/meta/type_traits.h"
+#include "absl/numeric/bits.h"
#include "absl/numeric/int128.h"
#include "absl/random/internal/fastmath.h"
#include "absl/random/internal/iostream_state_saver.h"
@@ -261,7 +262,7 @@
uint64_t rotate = h >> 58u;
uint64_t s = Uint128Low64(state) ^ h;
#endif
- return random_internal::rotr(s, rotate);
+ return rotr(s, rotate);
}
};
@@ -281,8 +282,8 @@
using state_type = uint64_t;
using result_type = uint32_t;
inline uint32_t operator()(uint64_t state) {
- return random_internal::rotr(
- static_cast<uint32_t>(((state >> 18) ^ state) >> 27), state >> 59);
+ return rotr(static_cast<uint32_t>(((state >> 18) ^ state) >> 27),
+ state >> 59);
}
};
diff --git a/absl/random/internal/randen.cc b/absl/random/internal/randen.cc
index 78a1e00..c1bc044 100644
--- a/absl/random/internal/randen.cc
+++ b/absl/random/internal/randen.cc
@@ -17,7 +17,7 @@
#include "absl/base/internal/raw_logging.h"
#include "absl/random/internal/randen_detect.h"
-// RANDen = RANDom generator or beetroots in Swiss German.
+// RANDen = RANDom generator or beetroots in Swiss High German.
// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random
// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32.
//
diff --git a/absl/random/internal/randen.h b/absl/random/internal/randen.h
index c2834aa..9a3840b 100644
--- a/absl/random/internal/randen.h
+++ b/absl/random/internal/randen.h
@@ -26,7 +26,7 @@
ABSL_NAMESPACE_BEGIN
namespace random_internal {
-// RANDen = RANDom generator or beetroots in Swiss German.
+// RANDen = RANDom generator or beetroots in Swiss High German.
// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random
// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32.
//
diff --git a/absl/random/internal/randen_engine.h b/absl/random/internal/randen_engine.h
index 6b33731..92bb890 100644
--- a/absl/random/internal/randen_engine.h
+++ b/absl/random/internal/randen_engine.h
@@ -23,6 +23,7 @@
#include <limits>
#include <type_traits>
+#include "absl/base/internal/endian.h"
#include "absl/meta/type_traits.h"
#include "absl/random/internal/iostream_state_saver.h"
#include "absl/random/internal/randen.h"
@@ -76,7 +77,7 @@
impl_.Generate(state_);
}
- return state_[next_++];
+ return little_endian::ToHost(state_[next_++]);
}
template <class SeedSequence>
@@ -181,7 +182,8 @@
// In the case that `elem` is `uint8_t`, it must be cast to something
// larger so that it prints as an integer rather than a character. For
// simplicity, apply the cast all circumstances.
- os << static_cast<numeric_type>(elem) << os.fill();
+ os << static_cast<numeric_type>(little_endian::FromHost(elem))
+ << os.fill();
}
os << engine.next_;
return os;
@@ -200,7 +202,7 @@
// necessary to read a wider type and then cast it to uint8_t.
numeric_type value;
is >> value;
- elem = static_cast<result_type>(value);
+ elem = little_endian::ToHost(static_cast<result_type>(value));
}
is >> next;
if (is.fail()) {
diff --git a/absl/random/internal/randen_hwaes.h b/absl/random/internal/randen_hwaes.h
index bce36b5..71a7f69 100644
--- a/absl/random/internal/randen_hwaes.h
+++ b/absl/random/internal/randen_hwaes.h
@@ -26,7 +26,7 @@
ABSL_NAMESPACE_BEGIN
namespace random_internal {
-// RANDen = RANDom generator or beetroots in Swiss German.
+// RANDen = RANDom generator or beetroots in Swiss High German.
// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random
// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32.
//
diff --git a/absl/random/internal/randen_slow.h b/absl/random/internal/randen_slow.h
index b6f137e..532c3a8 100644
--- a/absl/random/internal/randen_slow.h
+++ b/absl/random/internal/randen_slow.h
@@ -23,7 +23,7 @@
ABSL_NAMESPACE_BEGIN
namespace random_internal {
-// RANDen = RANDom generator or beetroots in Swiss German.
+// RANDen = RANDom generator or beetroots in Swiss High German.
// RandenSlow implements the basic state manipulation methods for
// architectures lacking AES hardware acceleration intrinsics.
class RandenSlow {
diff --git a/absl/random/internal/randen_slow_test.cc b/absl/random/internal/randen_slow_test.cc
index 4a53583..4861ffa 100644
--- a/absl/random/internal/randen_slow_test.cc
+++ b/absl/random/internal/randen_slow_test.cc
@@ -17,6 +17,7 @@
#include <cstring>
#include "gtest/gtest.h"
+#include "absl/base/internal/endian.h"
#include "absl/random/internal/randen_traits.h"
namespace {
@@ -56,7 +57,7 @@
uint64_t* id = d.state;
for (const auto& elem : kGolden) {
- EXPECT_EQ(elem, *id++);
+ EXPECT_EQ(absl::little_endian::FromHost64(elem), *id++);
}
}
diff --git a/absl/random/internal/randen_traits.h b/absl/random/internal/randen_traits.h
index 53caa93..120022c 100644
--- a/absl/random/internal/randen_traits.h
+++ b/absl/random/internal/randen_traits.h
@@ -28,7 +28,7 @@
ABSL_NAMESPACE_BEGIN
namespace random_internal {
-// RANDen = RANDom generator or beetroots in Swiss German.
+// RANDen = RANDom generator or beetroots in Swiss High German.
// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random
// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32.
//
diff --git a/absl/random/internal/wide_multiply.h b/absl/random/internal/wide_multiply.h
index 0afcbe0..b6e6c4b 100644
--- a/absl/random/internal/wide_multiply.h
+++ b/absl/random/internal/wide_multiply.h
@@ -26,7 +26,7 @@
#endif
#include "absl/base/config.h"
-#include "absl/base/internal/bits.h"
+#include "absl/numeric/bits.h"
#include "absl/numeric/int128.h"
#include "absl/random/internal/traits.h"
diff --git a/absl/random/internal/wide_multiply_test.cc b/absl/random/internal/wide_multiply_test.cc
index ca8ce92..e276cb5 100644
--- a/absl/random/internal/wide_multiply_test.cc
+++ b/absl/random/internal/wide_multiply_test.cc
@@ -15,7 +15,6 @@
#include "absl/random/internal/wide_multiply.h"
#include "gtest/gtest.h"
-#include "absl/base/internal/bits.h"
#include "absl/numeric/int128.h"
using absl::random_internal::MultiplyU64ToU128;
diff --git a/absl/random/log_uniform_int_distribution.h b/absl/random/log_uniform_int_distribution.h
index 960816e..43e1011 100644
--- a/absl/random/log_uniform_int_distribution.h
+++ b/absl/random/log_uniform_int_distribution.h
@@ -23,6 +23,7 @@
#include <ostream>
#include <type_traits>
+#include "absl/numeric/bits.h"
#include "absl/random/internal/fastmath.h"
#include "absl/random/internal/generate_real.h"
#include "absl/random/internal/iostream_state_saver.h"
@@ -68,8 +69,10 @@
if (base_ == 2) {
// Determine where the first set bit is on range(), giving a log2(range)
// value which can be used to construct bounds.
- log_range_ = (std::min)(random_internal::LeadingSetBit(range()),
- std::numeric_limits<unsigned_type>::digits);
+ log_range_ =
+ (std::min)(bit_width(range()),
+ static_cast<unsigned_type>(
+ std::numeric_limits<unsigned_type>::digits));
} else {
// NOTE: Computing the logN(x) introduces error from 2 sources:
// 1. Conversion of int to double loses precision for values >=
diff --git a/absl/random/mocking_bit_gen.h b/absl/random/mocking_bit_gen.h
index 6d2f2c8..6815ca4 100644
--- a/absl/random/mocking_bit_gen.h
+++ b/absl/random/mocking_bit_gen.h
@@ -104,10 +104,7 @@
class MockingBitGen {
public:
MockingBitGen() = default;
-
- ~MockingBitGen() {
- for (const auto& del : deleters_) del();
- }
+ ~MockingBitGen() = default;
// URBG interface
using result_type = absl::BitGen::result_type;
@@ -117,14 +114,6 @@
result_type operator()() { return gen_(); }
private:
- using match_impl_fn = void (*)(void* mock_fn, void* t_erased_arg_tuple,
- void* t_erased_result);
-
- struct MockData {
- void* mock_fn = nullptr;
- match_impl_fn match_impl = nullptr;
- };
-
// GetMockFnType returns the testing::MockFunction for a result and tuple.
// This method only exists for type deduction and is otherwise unimplemented.
template <typename ResultT, typename... Args>
@@ -136,17 +125,46 @@
// NOTE: MockFnCaller is essentially equivalent to the lambda:
// [fn](auto... args) { return fn->Call(std::move(args)...)}
// however that fails to build on some supported platforms.
- template <typename ResultT, typename MockFnType, typename Tuple>
+ template <typename MockFnType, typename ResultT, typename Tuple>
struct MockFnCaller;
+
// specialization for std::tuple.
- template <typename ResultT, typename MockFnType, typename... Args>
- struct MockFnCaller<ResultT, MockFnType, std::tuple<Args...>> {
+ template <typename MockFnType, typename ResultT, typename... Args>
+ struct MockFnCaller<MockFnType, ResultT, std::tuple<Args...>> {
MockFnType* fn;
inline ResultT operator()(Args... args) {
return fn->Call(std::move(args)...);
}
};
+ // FunctionHolder owns a particular ::testing::MockFunction associated with
+ // a mocked type signature, and implement the type-erased Apply call, which
+ // applies type-erased arguments to the mock.
+ class FunctionHolder {
+ public:
+ virtual ~FunctionHolder() = default;
+
+ // Call is a dispatch function which converts the
+ // generic type-erased parameters into a specific mock invocation call.
+ virtual void Apply(/*ArgTupleT*/ void* args_tuple,
+ /*ResultT*/ void* result) = 0;
+ };
+
+ template <typename MockFnType, typename ResultT, typename ArgTupleT>
+ class FunctionHolderImpl final : public FunctionHolder {
+ public:
+ void Apply(void* args_tuple, void* result) override {
+ // Requires tuple_args to point to a ArgTupleT, which is a
+ // std::tuple<Args...> used to invoke the mock function. Requires result
+ // to point to a ResultT, which is the result of the call.
+ *static_cast<ResultT*>(result) =
+ absl::apply(MockFnCaller<MockFnType, ResultT, ArgTupleT>{&mock_fn_},
+ *static_cast<ArgTupleT*>(args_tuple));
+ }
+
+ MockFnType mock_fn_;
+ };
+
// MockingBitGen::RegisterMock
//
// RegisterMock<ResultT, ArgTupleT>(FastTypeIdType) is the main extension
@@ -161,33 +179,14 @@
auto RegisterMock(base_internal::FastTypeIdType type)
-> decltype(GetMockFnType(std::declval<ResultT>(),
std::declval<ArgTupleT>()))& {
- using MockFnType = decltype(
- GetMockFnType(std::declval<ResultT>(), std::declval<ArgTupleT>()));
+ using MockFnType = decltype(GetMockFnType(std::declval<ResultT>(),
+ std::declval<ArgTupleT>()));
+ using ImplT = FunctionHolderImpl<MockFnType, ResultT, ArgTupleT>;
auto& mock = mocks_[type];
- if (!mock.mock_fn) {
- auto* mock_fn = new MockFnType;
- mock.mock_fn = mock_fn;
- mock.match_impl = &MatchImpl<ResultT, ArgTupleT>;
- deleters_.emplace_back([mock_fn] { delete mock_fn; });
+ if (!mock) {
+ mock = absl::make_unique<ImplT>();
}
- return *static_cast<MockFnType*>(mock.mock_fn);
- }
-
- // MockingBitGen::MatchImpl<> is a dispatch function which converts the
- // generic type-erased parameters into a specific mock invocation call.
- // Requires tuple_args to point to a ArgTupleT, which is a std::tuple<Args...>
- // used to invoke the mock function.
- // Requires result to point to a ResultT, which is the result of the call.
- template <typename ResultT, typename ArgTupleT>
- static void MatchImpl(/*MockFnType<ResultT, Args...>*/ void* mock_fn,
- /*ArgTupleT*/ void* args_tuple,
- /*ResultT*/ void* result) {
- using MockFnType = decltype(
- GetMockFnType(std::declval<ResultT>(), std::declval<ArgTupleT>()));
- *static_cast<ResultT*>(result) = absl::apply(
- MockFnCaller<ResultT, MockFnType, ArgTupleT>{
- static_cast<MockFnType*>(mock_fn)},
- *static_cast<ArgTupleT*>(args_tuple));
+ return static_cast<ImplT*>(mock.get())->mock_fn_;
}
// MockingBitGen::InvokeMock
@@ -206,13 +205,13 @@
// Trigger a mock, if there exists one that matches `param`.
auto it = mocks_.find(type);
if (it == mocks_.end()) return false;
- auto* mock_data = static_cast<MockData*>(&it->second);
- mock_data->match_impl(mock_data->mock_fn, args_tuple, result);
+ it->second->Apply(args_tuple, result);
return true;
}
- absl::flat_hash_map<base_internal::FastTypeIdType, MockData> mocks_;
- std::vector<std::function<void()>> deleters_;
+ absl::flat_hash_map<base_internal::FastTypeIdType,
+ std::unique_ptr<FunctionHolder>>
+ mocks_;
absl::BitGen gen_;
template <typename>
diff --git a/absl/random/uniform_int_distribution.h b/absl/random/uniform_int_distribution.h
index da66564..c1f54cc 100644
--- a/absl/random/uniform_int_distribution.h
+++ b/absl/random/uniform_int_distribution.h
@@ -196,7 +196,7 @@
uniform_int_distribution<IntType>::Generate(
URBG& g, // NOLINT(runtime/references)
typename random_internal::make_unsigned_bits<IntType>::type R) {
- random_internal::FastUniformBits<unsigned_type> fast_bits;
+ random_internal::FastUniformBits<unsigned_type> fast_bits;
unsigned_type bits = fast_bits(g);
const unsigned_type Lim = R + 1;
if ((R & Lim) == 0) {
diff --git a/absl/random/uniform_real_distribution_test.cc b/absl/random/uniform_real_distribution_test.cc
index be107cd..18bcd3b 100644
--- a/absl/random/uniform_real_distribution_test.cc
+++ b/absl/random/uniform_real_distribution_test.cc
@@ -20,11 +20,13 @@
#include <random>
#include <sstream>
#include <string>
+#include <type_traits>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/raw_logging.h"
+#include "absl/numeric/internal/representation.h"
#include "absl/random/internal/chi_square.h"
#include "absl/random/internal/distribution_test_util.h"
#include "absl/random/internal/pcg_engine.h"
@@ -55,11 +57,15 @@
template <typename RealType>
class UniformRealDistributionTest : public ::testing::Test {};
-#if defined(__EMSCRIPTEN__)
-using RealTypes = ::testing::Types<float, double>;
-#else
-using RealTypes = ::testing::Types<float, double, long double>;
-#endif // defined(__EMSCRIPTEN__)
+// double-double arithmetic is not supported well by either GCC or Clang; see
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99048,
+// https://bugs.llvm.org/show_bug.cgi?id=49131, and
+// https://bugs.llvm.org/show_bug.cgi?id=49132. Don't bother running these tests
+// with double doubles until compiler support is better.
+using RealTypes =
+ std::conditional<absl::numeric_internal::IsDoubleDouble(),
+ ::testing::Types<float, double>,
+ ::testing::Types<float, double, long double>>::type;
TYPED_TEST_SUITE(UniformRealDistributionTest, RealTypes);
diff --git a/absl/status/internal/status_internal.h b/absl/status/internal/status_internal.h
index 1f82b8e..279f8f5 100644
--- a/absl/status/internal/status_internal.h
+++ b/absl/status/internal/status_internal.h
@@ -36,6 +36,13 @@
// Reference-counted representation of Status data.
struct StatusRep {
+ StatusRep(absl::StatusCode code, std::string message,
+ std::unique_ptr<status_internal::Payloads> payloads)
+ : ref(int32_t{1}),
+ code(code),
+ message(std::move(message)),
+ payloads(std::move(payloads)) {}
+
std::atomic<int32_t> ref;
absl::StatusCode code;
std::string message;
diff --git a/absl/status/internal/statusor_internal.h b/absl/status/internal/statusor_internal.h
index 7cc7625..eaac2c0 100644
--- a/absl/status/internal/statusor_internal.h
+++ b/absl/status/internal/statusor_internal.h
@@ -17,6 +17,7 @@
#include <type_traits>
#include <utility>
+#include "absl/base/attributes.h"
#include "absl/meta/type_traits.h"
#include "absl/status/status.h"
#include "absl/utility/utility.h"
@@ -135,18 +136,14 @@
public:
// Move type-agnostic error handling to the .cc.
static void HandleInvalidStatusCtorArg(Status*);
- static void Crash(const absl::Status& status);
+ ABSL_ATTRIBUTE_NORETURN static void Crash(const absl::Status& status);
};
// Construct an instance of T in `p` through placement new, passing Args... to
// the constructor.
// This abstraction is here mostly for the gcc performance fix.
template <typename T, typename... Args>
-void PlacementNew(void* p, Args&&... args) {
-#if defined(__GNUC__) && !defined(__clang__)
- // Teach gcc that 'p' cannot be null, fixing code size issues.
- if (p == nullptr) __builtin_unreachable();
-#endif
+ABSL_ATTRIBUTE_NONNULL(1) void PlacementNew(void* p, Args&&... args) {
new (p) T(std::forward<Args>(args)...);
}
diff --git a/absl/status/status.cc b/absl/status/status.cc
index a27fd8b..51a0d26 100644
--- a/absl/status/status.cc
+++ b/absl/status/status.cc
@@ -207,13 +207,12 @@
}
}
-uintptr_t Status::NewRep(absl::StatusCode code, absl::string_view msg,
- std::unique_ptr<status_internal::Payloads> payloads) {
- status_internal::StatusRep* rep = new status_internal::StatusRep;
- rep->ref.store(1, std::memory_order_relaxed);
- rep->code = code;
- rep->message.assign(msg.data(), msg.size());
- rep->payloads = std::move(payloads);
+uintptr_t Status::NewRep(
+ absl::StatusCode code, absl::string_view msg,
+ std::unique_ptr<status_internal::Payloads> payloads) {
+ status_internal::StatusRep* rep = new status_internal::StatusRep(
+ code, std::string(msg.data(), msg.size()),
+ std::move(payloads));
return PointerToRep(rep);
}
@@ -239,8 +238,9 @@
void Status::PrepareToModify() {
ABSL_RAW_CHECK(!ok(), "PrepareToModify shouldn't be called on OK status.");
if (IsInlined(rep_)) {
- rep_ = NewRep(static_cast<absl::StatusCode>(raw_code()),
- absl::string_view(), nullptr);
+ rep_ =
+ NewRep(static_cast<absl::StatusCode>(raw_code()), absl::string_view(),
+ nullptr);
return;
}
@@ -251,7 +251,8 @@
if (rep->payloads) {
payloads = absl::make_unique<status_internal::Payloads>(*rep->payloads);
}
- rep_ = NewRep(rep->code, message(), std::move(payloads));
+ rep_ = NewRep(rep->code, message(),
+ std::move(payloads));
UnrefNonInlined(rep_i);
}
}
@@ -290,20 +291,26 @@
return true;
}
-std::string Status::ToStringSlow() const {
+std::string Status::ToStringSlow(StatusToStringMode mode) const {
std::string text;
absl::StrAppend(&text, absl::StatusCodeToString(code()), ": ", message());
- status_internal::StatusPayloadPrinter printer =
- status_internal::GetStatusPayloadPrinter();
- this->ForEachPayload([&](absl::string_view type_url,
- const absl::Cord& payload) {
- absl::optional<std::string> result;
- if (printer) result = printer(type_url, payload);
- absl::StrAppend(
- &text, " [", type_url, "='",
- result.has_value() ? *result : absl::CHexEscape(std::string(payload)),
- "']");
- });
+
+ const bool with_payload = (mode & StatusToStringMode::kWithPayload) ==
+ StatusToStringMode::kWithPayload;
+
+ if (with_payload) {
+ status_internal::StatusPayloadPrinter printer =
+ status_internal::GetStatusPayloadPrinter();
+ this->ForEachPayload([&](absl::string_view type_url,
+ const absl::Cord& payload) {
+ absl::optional<std::string> result;
+ if (printer) result = printer(type_url, payload);
+ absl::StrAppend(
+ &text, " [", type_url, "='",
+ result.has_value() ? *result : absl::CHexEscape(std::string(payload)),
+ "']");
+ });
+ }
return text;
}
diff --git a/absl/status/status.h b/absl/status/status.h
index c4d6fce..df9e330 100644
--- a/absl/status/status.h
+++ b/absl/status/status.h
@@ -57,6 +57,7 @@
#include "absl/container/inlined_vector.h"
#include "absl/status/internal/status_internal.h"
#include "absl/strings/cord.h"
+#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
namespace absl {
@@ -279,6 +280,55 @@
// Streams StatusCodeToString(code) to `os`.
std::ostream& operator<<(std::ostream& os, StatusCode code);
+// absl::StatusToStringMode
+//
+// An `absl::StatusToStringMode` is an enumerated type indicating how
+// `absl::Status::ToString()` should construct the output string for an non-ok
+// status.
+enum class StatusToStringMode : int {
+ // ToString will not contain any extra data (such as payloads). It will only
+ // contain the error code and message, if any.
+ kWithNoExtraData = 0,
+ // ToString will contain the payloads.
+ kWithPayload = 1 << 0,
+};
+
+// absl::StatusToStringMode is specified as a bitmask type, which means the
+// following operations must be provided:
+inline constexpr StatusToStringMode operator&(StatusToStringMode lhs,
+ StatusToStringMode rhs) {
+ return static_cast<StatusToStringMode>(static_cast<int>(lhs) &
+ static_cast<int>(rhs));
+}
+inline constexpr StatusToStringMode operator|(StatusToStringMode lhs,
+ StatusToStringMode rhs) {
+ return static_cast<StatusToStringMode>(static_cast<int>(lhs) |
+ static_cast<int>(rhs));
+}
+inline constexpr StatusToStringMode operator^(StatusToStringMode lhs,
+ StatusToStringMode rhs) {
+ return static_cast<StatusToStringMode>(static_cast<int>(lhs) ^
+ static_cast<int>(rhs));
+}
+inline constexpr StatusToStringMode operator~(StatusToStringMode arg) {
+ return static_cast<StatusToStringMode>(~static_cast<int>(arg));
+}
+inline StatusToStringMode& operator&=(StatusToStringMode& lhs,
+ StatusToStringMode rhs) {
+ lhs = lhs & rhs;
+ return lhs;
+}
+inline StatusToStringMode& operator|=(StatusToStringMode& lhs,
+ StatusToStringMode rhs) {
+ lhs = lhs | rhs;
+ return lhs;
+}
+inline StatusToStringMode& operator^=(StatusToStringMode& lhs,
+ StatusToStringMode rhs) {
+ lhs = lhs ^ rhs;
+ return lhs;
+}
+
// absl::Status
//
// The `absl::Status` class is generally used to gracefully handle errors
@@ -370,10 +420,10 @@
Status();
// Creates a status in the canonical error space with the specified
- // `absl::StatusCode` and error message. If `code == absl::StatusCode::kOk`,
+ // `absl::StatusCode` and error message. If `code == absl::StatusCode::kOk`, // NOLINT
// `msg` is ignored and an object identical to an OK status is constructed.
//
- // The `msg` string must be in UTF-8. The implementation may complain (e.g.,
+ // The `msg` string must be in UTF-8. The implementation may complain (e.g., // NOLINT
// by printing a warning) if it is not.
Status(absl::StatusCode code, absl::string_view msg);
@@ -442,15 +492,17 @@
// Status::ToString()
//
- // Returns a combination of the error code name, the message and any
- // associated payload messages. This string is designed simply to be human
- // readable and its exact format should not be load bearing. Do not depend on
- // the exact format of the result of `ToString()` which is subject to change.
+ // Returns a string based on the `mode`. By default, it returns combination of
+ // the error code name, the message and any associated payload messages. This
+ // string is designed simply to be human readable and its exact format should
+ // not be load bearing. Do not depend on the exact format of the result of
+ // `ToString()` which is subject to change.
//
// The printed code name and the message are generally substrings of the
// result, and the payloads to be printed use the status payload printer
// mechanism (which is internal).
- std::string ToString() const;
+ std::string ToString(
+ StatusToStringMode mode = StatusToStringMode::kWithPayload) const;
// Status::IgnoreError()
//
@@ -550,8 +602,9 @@
status_internal::Payloads* GetPayloads();
// Takes ownership of payload.
- static uintptr_t NewRep(absl::StatusCode code, absl::string_view msg,
- std::unique_ptr<status_internal::Payloads> payload);
+ static uintptr_t NewRep(
+ absl::StatusCode code, absl::string_view msg,
+ std::unique_ptr<status_internal::Payloads> payload);
static bool EqualsSlow(const absl::Status& a, const absl::Status& b);
// MSVC 14.0 limitation requires the const.
@@ -580,8 +633,7 @@
static uintptr_t PointerToRep(status_internal::StatusRep* r);
static status_internal::StatusRep* RepToPointer(uintptr_t r);
- // Returns string for non-ok Status.
- std::string ToStringSlow() const;
+ std::string ToStringSlow(StatusToStringMode mode) const;
// Status supports two different representations.
// - When the low bit is off it is an inlined representation.
@@ -704,9 +756,11 @@
inline Status& Status::operator=(Status&& x) {
uintptr_t old_rep = rep_;
- rep_ = x.rep_;
- x.rep_ = MovedFromRep();
- Unref(old_rep);
+ if (x.rep_ != old_rep) {
+ rep_ = x.rep_;
+ x.rep_ = MovedFromRep();
+ Unref(old_rep);
+ }
return *this;
}
@@ -743,8 +797,8 @@
return !(lhs == rhs);
}
-inline std::string Status::ToString() const {
- return ok() ? "OK" : ToStringSlow();
+inline std::string Status::ToString(StatusToStringMode mode) const {
+ return ok() ? "OK" : ToStringSlow(mode);
}
inline void Status::IgnoreError() const {
diff --git a/absl/status/status_test.cc b/absl/status/status_test.cc
index ca9488a..7116ba6 100644
--- a/absl/status/status_test.cc
+++ b/absl/status/status_test.cc
@@ -280,6 +280,23 @@
HasSubstr("[bar='\\xff']")));
}
+TEST(Status, ToStringMode) {
+ absl::Status s(absl::StatusCode::kInternal, "fail");
+ s.SetPayload("foo", absl::Cord("bar"));
+ s.SetPayload("bar", absl::Cord("\377"));
+
+ EXPECT_EQ("INTERNAL: fail",
+ s.ToString(absl::StatusToStringMode::kWithNoExtraData));
+
+ EXPECT_THAT(s.ToString(absl::StatusToStringMode::kWithPayload),
+ AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
+ HasSubstr("[bar='\\xff']")));
+
+ EXPECT_THAT(s.ToString(~absl::StatusToStringMode::kWithPayload),
+ AllOf(HasSubstr("INTERNAL: fail"), Not(HasSubstr("[foo='bar']")),
+ Not(HasSubstr("[bar='\\xff']"))));
+}
+
absl::Status EraseAndReturn(const absl::Status& base) {
absl::Status copy = base;
EXPECT_TRUE(copy.ErasePayload(kUrl1));
@@ -397,6 +414,12 @@
assignee = std::move(status);
EXPECT_EQ(assignee, copy);
}
+ {
+ absl::Status status(absl::StatusCode::kInvalidArgument, "message");
+ absl::Status copy(status);
+ status = static_cast<absl::Status&&>(status);
+ EXPECT_EQ(status, copy);
+ }
}
TEST(Status, Update) {
@@ -454,5 +477,4 @@
test_swap(no_payload, with_payload);
test_swap(with_payload, no_payload);
}
-
} // namespace
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel
index 30a8dd2..123b5ef 100644
--- a/absl/strings/BUILD.bazel
+++ b/absl/strings/BUILD.bazel
@@ -69,7 +69,6 @@
deps = [
":internal",
"//absl/base",
- "//absl/base:bits",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:endian",
@@ -77,6 +76,7 @@
"//absl/base:throw_delegate",
"//absl/memory",
"//absl/meta:type_traits",
+ "//absl/numeric:bits",
"//absl/numeric:int128",
],
)
@@ -267,13 +267,31 @@
cc_library(
name = "cord_internal",
- hdrs = ["internal/cord_internal.h"],
+ srcs = [
+ "internal/cord_internal.cc",
+ "internal/cord_rep_ring.cc",
+ ],
+ hdrs = [
+ "internal/cord_internal.h",
+ "internal/cord_rep_flat.h",
+ "internal/cord_rep_ring.h",
+ "internal/cord_rep_ring_reader.h",
+ ],
copts = ABSL_DEFAULT_COPTS,
- visibility = ["//visibility:private"],
+ visibility = [
+ "//visibility:private",
+ ],
deps = [
":strings",
"//absl/base:base_internal",
+ "//absl/base:config",
+ "//absl/base:core_headers",
+ "//absl/base:endian",
+ "//absl/base:raw_logging_internal",
+ "//absl/base:throw_delegate",
"//absl/container:compressed_tuple",
+ "//absl/container:inlined_vector",
+ "//absl/container:layout",
"//absl/meta:type_traits",
],
)
@@ -338,6 +356,38 @@
)
cc_test(
+ name = "cord_ring_test",
+ size = "medium",
+ srcs = ["cord_ring_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":cord_internal",
+ ":strings",
+ "//absl/base:config",
+ "//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
+ "//absl/debugging:leak_check",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "cord_ring_reader_test",
+ size = "medium",
+ srcs = ["cord_ring_reader_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":cord_internal",
+ ":strings",
+ "//absl/base:core_headers",
+ "//absl/debugging:leak_check",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
name = "substitute_test",
size = "small",
srcs = ["substitute_test.cc"],
@@ -653,12 +703,13 @@
visibility = ["//visibility:private"],
deps = [
":strings",
- "//absl/base:bits",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/functional:function_ref",
"//absl/meta:type_traits",
+ "//absl/numeric:bits",
"//absl/numeric:int128",
+ "//absl/numeric:representation",
"//absl/types:optional",
"//absl/types:span",
],
diff --git a/absl/strings/BUILD.gn b/absl/strings/BUILD.gn
index 4495790..404cc03 100644
--- a/absl/strings/BUILD.gn
+++ b/absl/strings/BUILD.gn
@@ -45,7 +45,6 @@
deps = [
":internal",
"//third_party/abseil-cpp/absl/base",
- "//third_party/abseil-cpp/absl/base:bits",
"//third_party/abseil-cpp/absl/base:config",
"//third_party/abseil-cpp/absl/base:core_headers",
"//third_party/abseil-cpp/absl/base:endian",
@@ -53,6 +52,7 @@
"//third_party/abseil-cpp/absl/base:throw_delegate",
"//third_party/abseil-cpp/absl/memory",
"//third_party/abseil-cpp/absl/meta:type_traits",
+ "//third_party/abseil-cpp/absl/numeric:bits",
"//third_party/abseil-cpp/absl/numeric:int128",
]
}
@@ -105,24 +105,41 @@
visibility = [ ":*" ]
deps = [
":strings",
- "//third_party/abseil-cpp/absl/base:bits",
"//third_party/abseil-cpp/absl/base:config",
"//third_party/abseil-cpp/absl/base:core_headers",
"//third_party/abseil-cpp/absl/functional:function_ref",
"//third_party/abseil-cpp/absl/meta:type_traits",
+ "//third_party/abseil-cpp/absl/numeric:bits",
"//third_party/abseil-cpp/absl/numeric:int128",
+ "//third_party/abseil-cpp/absl/numeric:representation",
"//third_party/abseil-cpp/absl/types:optional",
"//third_party/abseil-cpp/absl/types:span",
]
}
absl_source_set("cord_internal") {
- public = [ "internal/cord_internal.h" ]
+ sources = [
+ "internal/cord_internal.cc",
+ "internal/cord_rep_ring.cc",
+ ]
+ public = [
+ "internal/cord_internal.h",
+ "internal/cord_rep_flat.h",
+ "internal/cord_rep_ring.h",
+ "internal/cord_rep_ring_reader.h",
+ ]
visibility = [ ":*" ]
deps = [
":strings",
"//third_party/abseil-cpp/absl/base:base_internal",
+ "//third_party/abseil-cpp/absl/base:config",
+ "//third_party/abseil-cpp/absl/base:core_headers",
+ "//third_party/abseil-cpp/absl/base:endian",
+ "//third_party/abseil-cpp/absl/base:raw_logging_internal",
+ "//third_party/abseil-cpp/absl/base:throw_delegate",
"//third_party/abseil-cpp/absl/container:compressed_tuple",
+ "//third_party/abseil-cpp/absl/container:inlined_vector",
+ "//third_party/abseil-cpp/absl/container:layout",
"//third_party/abseil-cpp/absl/meta:type_traits",
]
}
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
index 2b994a7..3b7ae63 100644
--- a/absl/strings/CMakeLists.txt
+++ b/absl/strings/CMakeLists.txt
@@ -410,6 +410,7 @@
absl::strings
absl::config
absl::core_headers
+ absl::numeric_representation
absl::type_traits
absl::int128
absl::span
@@ -556,13 +557,19 @@
"cord.h"
SRCS
"cord.cc"
+ "internal/cord_internal.cc"
"internal/cord_internal.h"
+ "internal/cord_rep_ring.h"
+ "internal/cord_rep_ring.cc"
+ "internal/cord_rep_ring_reader.h"
+ "internal/cord_rep_flat.h"
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
absl::base
absl::base_internal
absl::compressed_tuple
+ absl::config
absl::core_headers
absl::endian
absl::fixed_array
@@ -572,6 +579,7 @@
absl::raw_logging_internal
absl::strings
absl::strings_internal
+ absl::throw_delegate
absl::type_traits
PUBLIC
)
@@ -607,3 +615,35 @@
absl::fixed_array
gmock_main
)
+
+absl_cc_test(
+ NAME
+ cord_ring_test
+ SRCS
+ "cord_ring_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::config
+ absl::cord
+ absl::strings
+ absl::base
+ absl::core_headers
+ absl::raw_logging_internal
+ gmock_main
+)
+
+absl_cc_test(
+ NAME
+ cord_ring_reader_test
+ SRCS
+ "cord_ring_reader_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::cord
+ absl::strings
+ absl::base
+ absl::core_headers
+ gmock_main
+)
diff --git a/absl/strings/ascii_test.cc b/absl/strings/ascii_test.cc
index 5ecd23f..83af782 100644
--- a/absl/strings/ascii_test.cc
+++ b/absl/strings/ascii_test.cc
@@ -197,11 +197,15 @@
const std::string str("GHIJKL");
const std::string str2("MNOPQR");
const absl::string_view sp(str2);
+ std::string mutable_str("STUVWX");
EXPECT_EQ("abcdef", absl::AsciiStrToLower(buf));
EXPECT_EQ("ghijkl", absl::AsciiStrToLower(str));
EXPECT_EQ("mnopqr", absl::AsciiStrToLower(sp));
+ absl::AsciiStrToLower(&mutable_str);
+ EXPECT_EQ("stuvwx", mutable_str);
+
char mutable_buf[] = "Mutable";
std::transform(mutable_buf, mutable_buf + strlen(mutable_buf),
mutable_buf, absl::ascii_tolower);
diff --git a/absl/strings/charconv.cc b/absl/strings/charconv.cc
index 3613a65..b8674c2 100644
--- a/absl/strings/charconv.cc
+++ b/absl/strings/charconv.cc
@@ -20,7 +20,7 @@
#include <cstring>
#include "absl/base/casts.h"
-#include "absl/base/internal/bits.h"
+#include "absl/numeric/bits.h"
#include "absl/numeric/int128.h"
#include "absl/strings/internal/charconv_bigint.h"
#include "absl/strings/internal/charconv_parse.h"
@@ -242,11 +242,11 @@
// Returns the bit width of the given uint128. (Equivalently, returns 128
// minus the number of leading zero bits.)
-int BitWidth(uint128 value) {
+unsigned BitWidth(uint128 value) {
if (Uint128High64(value) == 0) {
- return 64 - base_internal::CountLeadingZeros64(Uint128Low64(value));
+ return static_cast<unsigned>(bit_width(Uint128Low64(value)));
}
- return 128 - base_internal::CountLeadingZeros64(Uint128High64(value));
+ return 128 - countl_zero(Uint128High64(value));
}
// Calculates how far to the right a mantissa needs to be shifted to create a
@@ -519,7 +519,7 @@
const strings_internal::ParsedFloat& parsed_hex) {
uint64_t mantissa = parsed_hex.mantissa;
int exponent = parsed_hex.exponent;
- int mantissa_width = 64 - base_internal::CountLeadingZeros64(mantissa);
+ auto mantissa_width = static_cast<unsigned>(bit_width(mantissa));
const int shift = NormalizedShiftSize<FloatType>(mantissa_width, exponent);
bool result_exact;
exponent += shift;
diff --git a/absl/strings/charconv_test.cc b/absl/strings/charconv_test.cc
index 9090e9c..b83de5a 100644
--- a/absl/strings/charconv_test.cc
+++ b/absl/strings/charconv_test.cc
@@ -653,7 +653,9 @@
negative_from_chars_float);
EXPECT_TRUE(std::signbit(negative_from_chars_float));
EXPECT_FALSE(Identical(negative_from_chars_float, from_chars_float));
- from_chars_float = std::copysign(from_chars_float, -1.0);
+ // Use the (float, float) overload of std::copysign to prevent narrowing;
+ // see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98251.
+ from_chars_float = std::copysign(from_chars_float, -1.0f);
EXPECT_TRUE(Identical(negative_from_chars_float, from_chars_float));
}
}
diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc
index 9efd135..9353375 100644
--- a/absl/strings/cord.cc
+++ b/absl/strings/cord.cc
@@ -36,6 +36,8 @@
#include "absl/container/inlined_vector.h"
#include "absl/strings/escaping.h"
#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_flat.h"
+#include "absl/strings/internal/cord_rep_ring.h"
#include "absl/strings/internal/resize_uninitialized.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
@@ -48,92 +50,20 @@
using ::absl::cord_internal::CordRep;
using ::absl::cord_internal::CordRepConcat;
using ::absl::cord_internal::CordRepExternal;
+using ::absl::cord_internal::CordRepFlat;
+using ::absl::cord_internal::CordRepRing;
using ::absl::cord_internal::CordRepSubstring;
+using ::absl::cord_internal::kMinFlatLength;
+using ::absl::cord_internal::kMaxFlatLength;
using ::absl::cord_internal::CONCAT;
using ::absl::cord_internal::EXTERNAL;
using ::absl::cord_internal::FLAT;
+using ::absl::cord_internal::RING;
using ::absl::cord_internal::SUBSTRING;
-namespace cord_internal {
-
-inline CordRepConcat* CordRep::concat() {
- assert(tag == CONCAT);
- return static_cast<CordRepConcat*>(this);
-}
-
-inline const CordRepConcat* CordRep::concat() const {
- assert(tag == CONCAT);
- return static_cast<const CordRepConcat*>(this);
-}
-
-inline CordRepSubstring* CordRep::substring() {
- assert(tag == SUBSTRING);
- return static_cast<CordRepSubstring*>(this);
-}
-
-inline const CordRepSubstring* CordRep::substring() const {
- assert(tag == SUBSTRING);
- return static_cast<const CordRepSubstring*>(this);
-}
-
-inline CordRepExternal* CordRep::external() {
- assert(tag == EXTERNAL);
- return static_cast<CordRepExternal*>(this);
-}
-
-inline const CordRepExternal* CordRep::external() const {
- assert(tag == EXTERNAL);
- return static_cast<const CordRepExternal*>(this);
-}
-
-} // namespace cord_internal
-
-static const size_t kFlatOverhead = offsetof(CordRep, data);
-
-// Largest and smallest flat node lengths we are willing to allocate
-// Flat allocation size is stored in tag, which currently can encode sizes up
-// to 4K, encoded as multiple of either 8 or 32 bytes.
-// If we allow for larger sizes, we need to change this to 8/64, 16/128, etc.
-static constexpr size_t kMaxFlatSize = 4096;
-static constexpr size_t kMaxFlatLength = kMaxFlatSize - kFlatOverhead;
-static constexpr size_t kMinFlatLength = 32 - kFlatOverhead;
-
-// Prefer copying blocks of at most this size, otherwise reference count.
-static const size_t kMaxBytesToCopy = 511;
-
-// Helper functions for rounded div, and rounding to exact sizes.
-static size_t DivUp(size_t n, size_t m) { return (n + m - 1) / m; }
-static size_t RoundUp(size_t n, size_t m) { return DivUp(n, m) * m; }
-
-// Returns the size to the nearest equal or larger value that can be
-// expressed exactly as a tag value.
-static size_t RoundUpForTag(size_t size) {
- return RoundUp(size, (size <= 1024) ? 8 : 32);
-}
-
-// Converts the allocated size to a tag, rounding down if the size
-// does not exactly match a 'tag expressible' size value. The result is
-// undefined if the size exceeds the maximum size that can be encoded in
-// a tag, i.e., if size is larger than TagToAllocatedSize(<max tag>).
-static uint8_t AllocatedSizeToTag(size_t size) {
- const size_t tag = (size <= 1024) ? size / 8 : 128 + size / 32 - 1024 / 32;
- assert(tag <= std::numeric_limits<uint8_t>::max());
- return tag;
-}
-
-// Converts the provided tag to the corresponding allocated size
-static constexpr size_t TagToAllocatedSize(uint8_t tag) {
- return (tag <= 128) ? (tag * 8) : (1024 + (tag - 128) * 32);
-}
-
-// Converts the provided tag to the corresponding available data length
-static constexpr size_t TagToLength(uint8_t tag) {
- return TagToAllocatedSize(tag) - kFlatOverhead;
-}
-
-// Enforce that kMaxFlatSize maps to a well-known exact tag value.
-static_assert(TagToAllocatedSize(224) == kMaxFlatSize, "Bad tag logic");
+using ::absl::cord_internal::kInlinedVectorSize;
+using ::absl::cord_internal::kMaxBytesToCopy;
constexpr uint64_t Fibonacci(unsigned char n, uint64_t a = 0, uint64_t b = 1) {
return n == 0 ? a : Fibonacci(n - 1, b, a + b);
@@ -165,16 +95,10 @@
static const int kMinLengthSize = ABSL_ARRAYSIZE(min_length);
-// The inlined size to use with absl::InlinedVector.
-//
-// Note: The InlinedVectors in this file (and in cord.h) do not need to use
-// the same value for their inlined size. The fact that they do is historical.
-// It may be desirable for each to use a different inlined size optimized for
-// that InlinedVector's usage.
-//
-// TODO(jgm): Benchmark to see if there's a more optimal value than 47 for
-// the inlined vector size (47 exists for backward compatibility).
-static const int kInlinedVectorSize = 47;
+static inline bool cord_ring_enabled() {
+ return cord_internal::cord_ring_buffer_enabled.load(
+ std::memory_order_relaxed);
+}
static inline bool IsRootBalanced(CordRep* node) {
if (node->tag != CONCAT) {
@@ -191,7 +115,8 @@
}
static CordRep* Rebalance(CordRep* node);
-static void DumpNode(CordRep* rep, bool include_data, std::ostream* os);
+static void DumpNode(CordRep* rep, bool include_data, std::ostream* os,
+ int indent = 0);
static bool VerifyNode(CordRep* root, CordRep* start_node,
bool full_validation);
@@ -211,98 +136,6 @@
return node;
}
-// --------------------------------------------------------------------
-// Memory management
-
-inline CordRep* Ref(CordRep* rep) {
- if (rep != nullptr) {
- rep->refcount.Increment();
- }
- return rep;
-}
-
-// This internal routine is called from the cold path of Unref below. Keeping it
-// in a separate routine allows good inlining of Unref into many profitable call
-// sites. However, the call to this function can be highly disruptive to the
-// register pressure in those callers. To minimize the cost to callers, we use
-// a special LLVM calling convention that preserves most registers. This allows
-// the call to this routine in cold paths to not disrupt the caller's register
-// pressure. This calling convention is not available on all platforms; we
-// intentionally allow LLVM to ignore the attribute rather than attempting to
-// hardcode the list of supported platforms.
-#if defined(__clang__) && !defined(__i386__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wattributes"
-__attribute__((preserve_most))
-#pragma clang diagnostic pop
-#endif
-static void UnrefInternal(CordRep* rep) {
- assert(rep != nullptr);
-
- absl::InlinedVector<CordRep*, kInlinedVectorSize> pending;
- while (true) {
- assert(!rep->refcount.IsImmortal());
- if (rep->tag == CONCAT) {
- CordRepConcat* rep_concat = rep->concat();
- CordRep* right = rep_concat->right;
- if (!right->refcount.Decrement()) {
- pending.push_back(right);
- }
- CordRep* left = rep_concat->left;
- delete rep_concat;
- rep = nullptr;
- if (!left->refcount.Decrement()) {
- rep = left;
- continue;
- }
- } else if (rep->tag == EXTERNAL) {
- CordRepExternal* rep_external = rep->external();
- assert(rep_external->releaser_invoker != nullptr);
- rep_external->releaser_invoker(rep_external);
- rep = nullptr;
- } else if (rep->tag == SUBSTRING) {
- CordRepSubstring* rep_substring = rep->substring();
- CordRep* child = rep_substring->child;
- delete rep_substring;
- rep = nullptr;
- if (!child->refcount.Decrement()) {
- rep = child;
- continue;
- }
- } else {
- // Flat CordReps are allocated and constructed with raw ::operator new
- // and placement new, and must be destructed and deallocated
- // accordingly.
-#if defined(__cpp_sized_deallocation)
- size_t size = TagToAllocatedSize(rep->tag);
- rep->~CordRep();
- ::operator delete(rep, size);
-#else
- rep->~CordRep();
- ::operator delete(rep);
-#endif
- rep = nullptr;
- }
-
- if (!pending.empty()) {
- rep = pending.back();
- pending.pop_back();
- } else {
- break;
- }
- }
-}
-
-inline void Unref(CordRep* rep) {
- // Fast-path for two common, hot cases: a null rep and a shared root.
- if (ABSL_PREDICT_TRUE(rep == nullptr ||
- rep->refcount.DecrementExpectHighRefcount())) {
- return;
- }
-
- UnrefInternal(rep);
-}
-
// Return the depth of a node
static int Depth(const CordRep* rep) {
if (rep->tag == CONCAT) {
@@ -326,12 +159,14 @@
// The returned node has a refcount of 1.
static CordRep* RawConcat(CordRep* left, CordRep* right) {
// Avoid making degenerate concat nodes (one child is empty)
- if (left == nullptr || left->length == 0) {
- Unref(left);
+ if (left == nullptr) return right;
+ if (right == nullptr) return left;
+ if (left->length == 0) {
+ CordRep::Unref(left);
return right;
}
- if (right == nullptr || right->length == 0) {
- Unref(right);
+ if (right->length == 0) {
+ CordRep::Unref(right);
return left;
}
@@ -370,20 +205,27 @@
return reps[0];
}
-// Create a new flat node.
-static CordRep* NewFlat(size_t length_hint) {
- if (length_hint <= kMinFlatLength) {
- length_hint = kMinFlatLength;
- } else if (length_hint > kMaxFlatLength) {
- length_hint = kMaxFlatLength;
- }
+static CordRepFlat* CreateFlat(const char* data, size_t length,
+ size_t alloc_hint) {
+ CordRepFlat* flat = CordRepFlat::New(length + alloc_hint);
+ flat->length = length;
+ memcpy(flat->Data(), data, length);
+ return flat;
+}
- // Round size up so it matches a size we can exactly express in a tag.
- const size_t size = RoundUpForTag(length_hint + kFlatOverhead);
- void* const raw_rep = ::operator new(size);
- CordRep* rep = new (raw_rep) CordRep();
- rep->tag = AllocatedSizeToTag(size);
- return VerifyTree(rep);
+// Creates a new flat or ringbuffer out of the specified array.
+// The returned node has a refcount of 1.
+static CordRep* RingNewTree(const char* data, size_t length,
+ size_t alloc_hint) {
+ if (length <= kMaxFlatLength) {
+ return CreateFlat(data, length, alloc_hint);
+ }
+ CordRepFlat* flat = CreateFlat(data, kMaxFlatLength, 0);
+ data += kMaxFlatLength;
+ length -= kMaxFlatLength;
+ size_t extra = (length - 1) / kMaxFlatLength + 1;
+ auto* root = CordRepRing::Create(flat, extra);
+ return CordRepRing::Append(root, {data, length}, alloc_hint);
}
// Create a new tree out of the specified array.
@@ -392,13 +234,16 @@
size_t length,
size_t alloc_hint) {
if (length == 0) return nullptr;
+ if (cord_ring_enabled()) {
+ return RingNewTree(data, length, alloc_hint);
+ }
absl::FixedArray<CordRep*> reps((length - 1) / kMaxFlatLength + 1);
size_t n = 0;
do {
const size_t len = std::min(length, kMaxFlatLength);
- CordRep* rep = NewFlat(len + alloc_hint);
+ CordRepFlat* rep = CordRepFlat::New(len + alloc_hint);
rep->length = len;
- memcpy(rep->data, data, len);
+ memcpy(rep->Data(), data, len);
reps[n++] = VerifyTree(rep);
data += len;
length -= len;
@@ -421,7 +266,7 @@
static CordRep* NewSubstring(CordRep* child, size_t offset, size_t length) {
// Never create empty substring nodes
if (length == 0) {
- Unref(child);
+ CordRep::Unref(child);
return nullptr;
} else {
CordRepSubstring* rep = new CordRepSubstring();
@@ -443,51 +288,58 @@
bool nullify_tail) {
static_assert(kMaxInline == 15, "set_data is hard-coded for a length of 15");
- cord_internal::SmallMemmove(data_.as_chars, data, n, nullify_tail);
- set_tagged_size(static_cast<char>(n));
+ cord_internal::SmallMemmove(data_.as_chars(), data, n, nullify_tail);
+ set_inline_size(n);
}
inline char* Cord::InlineRep::set_data(size_t n) {
assert(n <= kMaxInline);
ResetToEmpty();
- set_tagged_size(static_cast<char>(n));
- return data_.as_chars;
+ set_inline_size(n);
+ return data_.as_chars();
}
inline CordRep* Cord::InlineRep::force_tree(size_t extra_hint) {
- size_t len = tagged_size();
- if (len > kMaxInline) {
- return data_.as_tree.rep;
+ if (data_.is_tree()) {
+ return data_.as_tree();
}
- CordRep* result = NewFlat(len + extra_hint);
+ size_t len = inline_size();
+ CordRepFlat* result = CordRepFlat::New(len + extra_hint);
result->length = len;
- static_assert(kMinFlatLength >= sizeof(data_.as_chars), "");
- memcpy(result->data, data_.as_chars, sizeof(data_.as_chars));
+ static_assert(kMinFlatLength >= sizeof(data_), "");
+ memcpy(result->Data(), data_.as_chars(), sizeof(data_));
set_tree(result);
return result;
}
inline void Cord::InlineRep::reduce_size(size_t n) {
- size_t tag = tagged_size();
+ size_t tag = inline_size();
assert(tag <= kMaxInline);
assert(tag >= n);
tag -= n;
- memset(data_.as_chars + tag, 0, n);
- set_tagged_size(static_cast<char>(tag));
+ memset(data_.as_chars() + tag, 0, n);
+ set_inline_size(static_cast<char>(tag));
}
inline void Cord::InlineRep::remove_prefix(size_t n) {
- cord_internal::SmallMemmove(data_.as_chars, data_.as_chars + n,
- tagged_size() - n);
+ cord_internal::SmallMemmove(data_.as_chars(), data_.as_chars() + n,
+ inline_size() - n);
reduce_size(n);
}
+// Returns `rep` converted into a CordRepRing.
+// Directly returns `rep` if `rep` is already a CordRepRing.
+static CordRepRing* ForceRing(CordRep* rep, size_t extra) {
+ return (rep->tag == RING) ? rep->ring() : CordRepRing::Create(rep, extra);
+}
+
void Cord::InlineRep::AppendTree(CordRep* tree) {
if (tree == nullptr) return;
- size_t len = tagged_size();
- if (len == 0) {
+ if (data_.is_empty()) {
set_tree(tree);
+ } else if (cord_ring_enabled()) {
+ set_tree(CordRepRing::Append(ForceRing(force_tree(0), 1), tree));
} else {
set_tree(Concat(force_tree(0), tree));
}
@@ -495,9 +347,10 @@
void Cord::InlineRep::PrependTree(CordRep* tree) {
assert(tree != nullptr);
- size_t len = tagged_size();
- if (len == 0) {
+ if (data_.is_empty()) {
set_tree(tree);
+ } else if (cord_ring_enabled()) {
+ set_tree(CordRepRing::Prepend(ForceRing(force_tree(0), 1), tree));
} else {
set_tree(Concat(tree, force_tree(0)));
}
@@ -509,6 +362,15 @@
// written to region and the actual size increase will be written to size.
static inline bool PrepareAppendRegion(CordRep* root, char** region,
size_t* size, size_t max_length) {
+ if (root->tag == RING && root->refcount.IsOne()) {
+ Span<char> span = root->ring()->GetAppendBuffer(max_length);
+ if (!span.empty()) {
+ *region = span.data();
+ *size = span.size();
+ return true;
+ }
+ }
+
// Search down the right-hand path for a non-full FLAT node.
CordRep* dst = root;
while (dst->tag == CONCAT && dst->refcount.IsOne()) {
@@ -522,7 +384,7 @@
}
const size_t in_use = dst->length;
- const size_t capacity = TagToLength(dst->tag);
+ const size_t capacity = dst->flat()->Capacity();
if (in_use == capacity) {
*region = nullptr;
*size = 0;
@@ -537,7 +399,7 @@
}
dst->length += size_increase;
- *region = dst->data + in_use;
+ *region = dst->flat()->Data() + in_use;
*size = size_increase;
return true;
}
@@ -551,12 +413,14 @@
}
// Try to fit in the inline buffer if possible.
- size_t inline_length = tagged_size();
- if (inline_length < kMaxInline && max_length <= kMaxInline - inline_length) {
- *region = data_.as_chars + inline_length;
- *size = max_length;
- set_tagged_size(static_cast<char>(inline_length + max_length));
- return;
+ if (!is_tree()) {
+ size_t inline_length = inline_size();
+ if (max_length <= kMaxInline - inline_length) {
+ *region = data_.as_chars() + inline_length;
+ *size = max_length;
+ set_inline_size(inline_length + max_length);
+ return;
+ }
}
CordRep* root = force_tree(max_length);
@@ -566,12 +430,16 @@
}
// Allocate new node.
- CordRep* new_node =
- NewFlat(std::max(static_cast<size_t>(root->length), max_length));
- new_node->length =
- std::min(static_cast<size_t>(TagToLength(new_node->tag)), max_length);
- *region = new_node->data;
+ CordRepFlat* new_node =
+ CordRepFlat::New(std::max(static_cast<size_t>(root->length), max_length));
+ new_node->length = std::min(new_node->Capacity(), max_length);
+ *region = new_node->Data();
*size = new_node->length;
+
+ if (cord_ring_enabled()) {
+ replace_tree(CordRepRing::Append(ForceRing(root, 1), new_node));
+ return;
+ }
replace_tree(Concat(root, new_node));
}
@@ -579,12 +447,14 @@
const size_t max_length = std::numeric_limits<size_t>::max();
// Try to fit in the inline buffer if possible.
- size_t inline_length = tagged_size();
- if (inline_length < kMaxInline) {
- *region = data_.as_chars + inline_length;
- *size = kMaxInline - inline_length;
- set_tagged_size(kMaxInline);
- return;
+ if (!data_.is_tree()) {
+ size_t inline_length = inline_size();
+ if (inline_length < kMaxInline) {
+ *region = data_.as_chars() + inline_length;
+ *size = kMaxInline - inline_length;
+ set_inline_size(kMaxInline);
+ return;
+ }
}
CordRep* root = force_tree(max_length);
@@ -594,10 +464,15 @@
}
// Allocate new node.
- CordRep* new_node = NewFlat(root->length);
- new_node->length = TagToLength(new_node->tag);
- *region = new_node->data;
+ CordRepFlat* new_node = CordRepFlat::New(root->length);
+ new_node->length = new_node->Capacity();
+ *region = new_node->Data();
*size = new_node->length;
+
+ if (cord_ring_enabled()) {
+ replace_tree(CordRepRing::Append(ForceRing(root, 1), new_node));
+ return;
+ }
replace_tree(Concat(root, new_node));
}
@@ -605,7 +480,7 @@
// will return true.
static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) {
if (rep->tag >= FLAT) {
- *total_mem_usage += TagToAllocatedSize(rep->tag);
+ *total_mem_usage += rep->flat()->AllocatedSize();
return true;
}
if (rep->tag == EXTERNAL) {
@@ -620,13 +495,15 @@
data_ = src.data_;
if (is_tree()) {
- Ref(tree());
+ data_.set_profiled(false);
+ CordRep::Ref(tree());
+ clear_cordz_info();
}
}
void Cord::InlineRep::ClearSlow() {
if (is_tree()) {
- Unref(tree());
+ CordRep::Unref(tree());
}
ResetToEmpty();
}
@@ -634,10 +511,6 @@
// --------------------------------------------------------------------
// Constructors and destructors
-Cord::Cord(const Cord& src) : contents_(src.contents_) {
- Ref(contents_.tree()); // Does nothing if contents_ has embedded data
-}
-
Cord::Cord(absl::string_view src) {
const size_t n = src.size();
if (n <= InlineRep::kMaxInline) {
@@ -681,14 +554,18 @@
// The destruction code is separate so that the compiler can determine
// that it does not need to call the destructor on a moved-from Cord.
void Cord::DestroyCordSlow() {
- Unref(VerifyTree(contents_.tree()));
+ if (CordRep* tree = contents_.tree()) {
+ CordRep::Unref(VerifyTree(tree));
+ }
}
// --------------------------------------------------------------------
// Mutators
void Cord::Clear() {
- Unref(contents_.clear());
+ if (CordRep* tree = contents_.clear()) {
+ CordRep::Unref(tree);
+ }
}
Cord& Cord::operator=(absl::string_view src) {
@@ -699,19 +576,20 @@
if (length <= InlineRep::kMaxInline) {
// Embed into this->contents_
contents_.set_data(data, length, true);
- Unref(tree);
+ if (tree) CordRep::Unref(tree);
return *this;
}
if (tree != nullptr && tree->tag >= FLAT &&
- TagToLength(tree->tag) >= length && tree->refcount.IsOne()) {
+ tree->flat()->Capacity() >= length &&
+ tree->refcount.IsOne()) {
// Copy in place if the existing FLAT node is reusable.
- memmove(tree->data, data, length);
+ memmove(tree->flat()->Data(), data, length);
tree->length = length;
VerifyTree(tree);
return *this;
}
contents_.set_tree(NewTree(data, length, 0));
- Unref(tree);
+ if (tree) CordRep::Unref(tree);
return *this;
}
@@ -731,24 +609,25 @@
// we keep it here to make diffs easier.
void Cord::InlineRep::AppendArray(const char* src_data, size_t src_size) {
if (src_size == 0) return; // memcpy(_, nullptr, 0) is undefined.
- // Try to fit in the inline buffer if possible.
- size_t inline_length = tagged_size();
- if (inline_length < kMaxInline && src_size <= kMaxInline - inline_length) {
- // Append new data to embedded array
- set_tagged_size(static_cast<char>(inline_length + src_size));
- memcpy(data_.as_chars + inline_length, src_data, src_size);
- return;
- }
-
- CordRep* root = tree();
size_t appended = 0;
- if (root) {
+ CordRep* root = nullptr;
+ if (is_tree()) {
+ root = data_.as_tree();
char* region;
if (PrepareAppendRegion(root, ®ion, &appended, src_size)) {
memcpy(region, src_data, appended);
}
} else {
+ // Try to fit in the inline buffer if possible.
+ size_t inline_length = inline_size();
+ if (src_size <= kMaxInline - inline_length) {
+ // Append new data to embedded array
+ memcpy(data_.as_chars() + inline_length, src_data, src_size);
+ set_inline_size(inline_length + src_size);
+ return;
+ }
+
// It is possible that src_data == data_, but when we transition from an
// InlineRep to a tree we need to assign data_ = root via set_tree. To
// avoid corrupting the source data before we copy it, delay calling
@@ -757,10 +636,11 @@
// either double the inlined size, or the added size + 10%.
const size_t size1 = inline_length * 2 + src_size;
const size_t size2 = inline_length + src_size / 10;
- root = NewFlat(std::max<size_t>(size1, size2));
- appended = std::min(src_size, TagToLength(root->tag) - inline_length);
- memcpy(root->data, data_.as_chars, inline_length);
- memcpy(root->data + inline_length, src_data, appended);
+ root = CordRepFlat::New(std::max<size_t>(size1, size2));
+ appended = std::min(
+ src_size, root->flat()->Capacity() - inline_length);
+ memcpy(root->flat()->Data(), data_.as_chars(), inline_length);
+ memcpy(root->flat()->Data() + inline_length, src_data, appended);
root->length = inline_length + appended;
set_tree(root);
}
@@ -771,6 +651,13 @@
return;
}
+ if (cord_ring_enabled()) {
+ absl::string_view data(src_data, src_size);
+ root = ForceRing(root, (data.size() - 1) / kMaxFlatLength + 1);
+ replace_tree(CordRepRing::Append(root->ring(), data));
+ return;
+ }
+
// Use new block(s) for any remaining bytes that were not handled above.
// Alloc extra memory only if the right child of the root of the new tree is
// going to be a FLAT node, which will permit further inplace appends.
@@ -787,7 +674,7 @@
}
inline CordRep* Cord::TakeRep() const& {
- return Ref(contents_.tree());
+ return CordRep::Ref(contents_.tree());
}
inline CordRep* Cord::TakeRep() && {
@@ -816,7 +703,7 @@
}
if (src_tree->tag >= FLAT) {
// src tree just has one flat node.
- contents_.AppendArray(src_tree->data, src_size);
+ contents_.AppendArray(src_tree->flat()->Data(), src_size);
return;
}
if (&src == this) {
@@ -831,6 +718,7 @@
return;
}
+ // Guaranteed to be a tree (kMaxBytesToCopy > kInlinedSize)
contents_.AppendTree(std::forward<C>(src).TakeRep());
}
@@ -852,7 +740,7 @@
void Cord::Prepend(const Cord& src) {
CordRep* src_tree = src.contents_.tree();
if (src_tree != nullptr) {
- Ref(src_tree);
+ CordRep::Ref(src_tree);
contents_.PrependTree(src_tree);
return;
}
@@ -864,18 +752,19 @@
void Cord::Prepend(absl::string_view src) {
if (src.empty()) return; // memcpy(_, nullptr, 0) is undefined.
- size_t cur_size = contents_.size();
- if (!contents_.is_tree() && cur_size + src.size() <= InlineRep::kMaxInline) {
- // Use embedded storage.
- char data[InlineRep::kMaxInline + 1] = {0};
- data[InlineRep::kMaxInline] = cur_size + src.size(); // set size
- memcpy(data, src.data(), src.size());
- memcpy(data + src.size(), contents_.data(), cur_size);
- memcpy(reinterpret_cast<void*>(&contents_), data,
- InlineRep::kMaxInline + 1);
- } else {
- contents_.PrependTree(NewTree(src.data(), src.size(), 0));
+ if (!contents_.is_tree()) {
+ size_t cur_size = contents_.inline_size();
+ if (cur_size + src.size() <= InlineRep::kMaxInline) {
+ // Use embedded storage.
+ char data[InlineRep::kMaxInline + 1] = {0};
+ memcpy(data, src.data(), src.size());
+ memcpy(data + src.size(), contents_.data(), cur_size);
+ memcpy(contents_.data_.as_chars(), data, InlineRep::kMaxInline + 1);
+ contents_.set_inline_size(cur_size + src.size());
+ return;
+ }
}
+ contents_.PrependTree(NewTree(src.data(), src.size(), 0));
}
template <typename T, Cord::EnableIfString<T>>
@@ -891,7 +780,7 @@
static CordRep* RemovePrefixFrom(CordRep* node, size_t n) {
if (n >= node->length) return nullptr;
- if (n == 0) return Ref(node);
+ if (n == 0) return CordRep::Ref(node);
absl::InlinedVector<CordRep*, kInlinedVectorSize> rhs_stack;
while (node->tag == CONCAT) {
@@ -909,7 +798,7 @@
assert(n <= node->length);
if (n == 0) {
- Ref(node);
+ CordRep::Ref(node);
} else {
size_t start = n;
size_t len = node->length - n;
@@ -918,10 +807,10 @@
start += node->substring()->start;
node = node->substring()->child;
}
- node = NewSubstring(Ref(node), start, len);
+ node = NewSubstring(CordRep::Ref(node), start, len);
}
while (!rhs_stack.empty()) {
- node = Concat(node, Ref(rhs_stack.back()));
+ node = Concat(node, CordRep::Ref(rhs_stack.back()));
rhs_stack.pop_back();
}
return node;
@@ -932,7 +821,7 @@
// edited in place iff that node and all its ancestors have a refcount of 1.
static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) {
if (n >= node->length) return nullptr;
- if (n == 0) return Ref(node);
+ if (n == 0) return CordRep::Ref(node);
absl::InlinedVector<CordRep*, kInlinedVectorSize> lhs_stack;
bool inplace_ok = node->refcount.IsOne();
@@ -952,11 +841,11 @@
assert(n <= node->length);
if (n == 0) {
- Ref(node);
+ CordRep::Ref(node);
} else if (inplace_ok && node->tag != EXTERNAL) {
// Consider making a new buffer if the current node capacity is much
// larger than the new length.
- Ref(node);
+ CordRep::Ref(node);
node->length -= n;
} else {
size_t start = 0;
@@ -965,10 +854,10 @@
start = node->substring()->start;
node = node->substring()->child;
}
- node = NewSubstring(Ref(node), start, len);
+ node = NewSubstring(CordRep::Ref(node), start, len);
}
while (!lhs_stack.empty()) {
- node = Concat(Ref(lhs_stack.back()), node);
+ node = Concat(CordRep::Ref(lhs_stack.back()), node);
lhs_stack.pop_back();
}
return node;
@@ -981,9 +870,11 @@
CordRep* tree = contents_.tree();
if (tree == nullptr) {
contents_.remove_prefix(n);
+ } else if (tree->tag == RING) {
+ contents_.replace_tree(CordRepRing::RemovePrefix(tree->ring(), n));
} else {
CordRep* newrep = RemovePrefixFrom(tree, n);
- Unref(tree);
+ CordRep::Unref(tree);
contents_.replace_tree(VerifyTree(newrep));
}
}
@@ -995,9 +886,11 @@
CordRep* tree = contents_.tree();
if (tree == nullptr) {
contents_.reduce_size(n);
+ } else if (tree->tag == RING) {
+ contents_.replace_tree(CordRepRing::RemoveSuffix(tree->ring(), n));
} else {
CordRep* newrep = RemoveSuffixFrom(tree, n);
- Unref(tree);
+ CordRep::Unref(tree);
contents_.replace_tree(VerifyTree(newrep));
}
}
@@ -1030,13 +923,13 @@
results.pop_back();
results.push_back(Concat(left, right));
} else if (pos == 0 && n == node->length) {
- results.push_back(Ref(node));
+ results.push_back(CordRep::Ref(node));
} else if (node->tag != CONCAT) {
if (node->tag == SUBSTRING) {
pos += node->substring()->start;
node = node->substring()->child;
}
- results.push_back(NewSubstring(Ref(node), pos, n));
+ results.push_back(NewSubstring(CordRep::Ref(node), pos, n));
} else if (pos + n <= node->concat()->left->length) {
todo.push_back(SubRange(node->concat()->left, pos, n));
} else if (pos >= node->concat()->left->length) {
@@ -1068,7 +961,7 @@
} else if (new_size <= InlineRep::kMaxInline) {
Cord::ChunkIterator it = chunk_begin();
it.AdvanceBytes(pos);
- char* dest = sub_cord.contents_.data_.as_chars;
+ char* dest = sub_cord.contents_.data_.as_chars();
size_t remaining_size = new_size;
while (remaining_size > it->size()) {
cord_internal::SmallMemmove(dest, it->data(), it->size());
@@ -1077,7 +970,10 @@
++it;
}
cord_internal::SmallMemmove(dest, it->data(), remaining_size);
- sub_cord.contents_.set_tagged_size(new_size);
+ sub_cord.contents_.set_inline_size(new_size);
+ } else if (tree->tag == RING) {
+ tree = CordRepRing::SubRing(CordRep::Ref(tree)->ring(), pos, new_size);
+ sub_cord.contents_.set_tree(tree);
} else {
sub_cord.contents_.set_tree(NewSubRange(tree, pos, new_size));
}
@@ -1114,9 +1010,9 @@
concat_node->left = concat_freelist_;
concat_freelist_ = concat_node;
} else {
- Ref(concat_node->right);
- Ref(concat_node->left);
- Unref(concat_node);
+ CordRep::Ref(concat_node->right);
+ CordRep::Ref(concat_node->left);
+ CordRep::Unref(concat_node);
}
} else {
AddNode(node);
@@ -1266,20 +1162,23 @@
// Helper routine. Locates the first flat chunk of the Cord without
// initializing the iterator.
inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const {
- size_t n = tagged_size();
- if (n <= kMaxInline) {
- return absl::string_view(data_.as_chars, n);
+ if (!is_tree()) {
+ return absl::string_view(data_.as_chars(), data_.inline_size());
}
CordRep* node = tree();
if (node->tag >= FLAT) {
- return absl::string_view(node->data, node->length);
+ return absl::string_view(node->flat()->Data(), node->length);
}
if (node->tag == EXTERNAL) {
return absl::string_view(node->external()->base, node->length);
}
+ if (node->tag == RING) {
+ return node->ring()->entry_data(node->ring()->head());
+ }
+
// Walk down the left branches until we hit a non-CONCAT node.
while (node->tag == CONCAT) {
node = node->concat()->left;
@@ -1296,7 +1195,7 @@
}
if (node->tag >= FLAT) {
- return absl::string_view(node->data + offset, length);
+ return absl::string_view(node->flat()->Data() + offset, length);
}
assert((node->tag == EXTERNAL) && "Expect FLAT or EXTERNAL node here");
@@ -1479,26 +1378,22 @@
}
}
-Cord::ChunkIterator& Cord::ChunkIterator::operator++() {
- ABSL_HARDENING_ASSERT(bytes_remaining_ > 0 &&
- "Attempted to iterate past `end()`");
- assert(bytes_remaining_ >= current_chunk_.size());
- bytes_remaining_ -= current_chunk_.size();
-
- if (stack_of_right_children_.empty()) {
+Cord::ChunkIterator& Cord::ChunkIterator::AdvanceStack() {
+ auto& stack_of_right_children = stack_of_right_children_;
+ if (stack_of_right_children.empty()) {
assert(!current_chunk_.empty()); // Called on invalid iterator.
// We have reached the end of the Cord.
return *this;
}
// Process the next node on the stack.
- CordRep* node = stack_of_right_children_.back();
- stack_of_right_children_.pop_back();
+ CordRep* node = stack_of_right_children.back();
+ stack_of_right_children.pop_back();
// Walk down the left branches until we hit a non-CONCAT node. Save the
// right children to the stack for subsequent traversal.
while (node->tag == CONCAT) {
- stack_of_right_children_.push_back(node->concat()->right);
+ stack_of_right_children.push_back(node->concat()->right);
node = node->concat()->left;
}
@@ -1513,7 +1408,7 @@
assert(node->tag == EXTERNAL || node->tag >= FLAT);
assert(length != 0);
const char* data =
- node->tag == EXTERNAL ? node->external()->base : node->data;
+ node->tag == EXTERNAL ? node->external()->base : node->flat()->Data();
current_chunk_ = absl::string_view(data + offset, length);
current_leaf_ = node;
return *this;
@@ -1541,12 +1436,32 @@
}
return subcord;
}
+
+ if (ring_reader_) {
+ size_t chunk_size = current_chunk_.size();
+ if (n <= chunk_size && n <= kMaxBytesToCopy) {
+ subcord = Cord(current_chunk_.substr(0, n));
+ } else {
+ auto* ring = CordRep::Ref(ring_reader_.ring())->ring();
+ size_t offset = ring_reader_.length() - bytes_remaining_;
+ subcord.contents_.set_tree(CordRepRing::SubRing(ring, offset, n));
+ }
+ if (n < chunk_size) {
+ bytes_remaining_ -= n;
+ current_chunk_.remove_prefix(n);
+ } else {
+ AdvanceBytesRing(n);
+ }
+ return subcord;
+ }
+
+ auto& stack_of_right_children = stack_of_right_children_;
if (n < current_chunk_.size()) {
// Range to read is a proper subrange of the current chunk.
assert(current_leaf_ != nullptr);
- CordRep* subnode = Ref(current_leaf_);
- const char* data =
- subnode->tag == EXTERNAL ? subnode->external()->base : subnode->data;
+ CordRep* subnode = CordRep::Ref(current_leaf_);
+ const char* data = subnode->tag == EXTERNAL ? subnode->external()->base
+ : subnode->flat()->Data();
subnode = NewSubstring(subnode, current_chunk_.data() - data, n);
subcord.contents_.set_tree(VerifyTree(subnode));
RemoveChunkPrefix(n);
@@ -1556,10 +1471,10 @@
// Range to read begins with a proper subrange of the current chunk.
assert(!current_chunk_.empty());
assert(current_leaf_ != nullptr);
- CordRep* subnode = Ref(current_leaf_);
+ CordRep* subnode = CordRep::Ref(current_leaf_);
if (current_chunk_.size() < subnode->length) {
- const char* data =
- subnode->tag == EXTERNAL ? subnode->external()->base : subnode->data;
+ const char* data = subnode->tag == EXTERNAL ? subnode->external()->base
+ : subnode->flat()->Data();
subnode = NewSubstring(subnode, current_chunk_.data() - data,
current_chunk_.size());
}
@@ -1569,20 +1484,20 @@
// Process the next node(s) on the stack, reading whole subtrees depending on
// their length and how many bytes we are advancing.
CordRep* node = nullptr;
- while (!stack_of_right_children_.empty()) {
- node = stack_of_right_children_.back();
- stack_of_right_children_.pop_back();
+ while (!stack_of_right_children.empty()) {
+ node = stack_of_right_children.back();
+ stack_of_right_children.pop_back();
if (node->length > n) break;
// TODO(qrczak): This might unnecessarily recreate existing concat nodes.
// Avoiding that would need pretty complicated logic (instead of
- // current_leaf_, keep current_subtree_ which points to the highest node
+ // current_leaf, keep current_subtree_ which points to the highest node
// such that the current leaf can be found on the path of left children
// starting from current_subtree_; delay creating subnode while node is
// below current_subtree_; find the proper node along the path of left
// children starting from current_subtree_ if this loop exits while staying
// below current_subtree_; etc.; alternatively, push parents instead of
// right children on the stack).
- subnode = Concat(subnode, Ref(node));
+ subnode = Concat(subnode, CordRep::Ref(node));
n -= node->length;
bytes_remaining_ -= node->length;
node = nullptr;
@@ -1600,11 +1515,11 @@
while (node->tag == CONCAT) {
if (node->concat()->left->length > n) {
// Push right, descend left.
- stack_of_right_children_.push_back(node->concat()->right);
+ stack_of_right_children.push_back(node->concat()->right);
node = node->concat()->left;
} else {
// Read left, descend right.
- subnode = Concat(subnode, Ref(node->concat()->left));
+ subnode = Concat(subnode, CordRep::Ref(node->concat()->left));
n -= node->concat()->left->length;
bytes_remaining_ -= node->concat()->left->length;
node = node->concat()->right;
@@ -1623,9 +1538,11 @@
// chunk.
assert(node->tag == EXTERNAL || node->tag >= FLAT);
assert(length > n);
- if (n > 0) subnode = Concat(subnode, NewSubstring(Ref(node), offset, n));
+ if (n > 0) {
+ subnode = Concat(subnode, NewSubstring(CordRep::Ref(node), offset, n));
+ }
const char* data =
- node->tag == EXTERNAL ? node->external()->base : node->data;
+ node->tag == EXTERNAL ? node->external()->base : node->flat()->Data();
current_chunk_ = absl::string_view(data + offset + n, length - n);
current_leaf_ = node;
bytes_remaining_ -= n;
@@ -1641,12 +1558,19 @@
n -= current_chunk_.size();
bytes_remaining_ -= current_chunk_.size();
+ if (stack_of_right_children_.empty()) {
+ // We have reached the end of the Cord.
+ assert(bytes_remaining_ == 0);
+ return;
+ }
+
// Process the next node(s) on the stack, skipping whole subtrees depending on
// their length and how many bytes we are advancing.
CordRep* node = nullptr;
- while (!stack_of_right_children_.empty()) {
- node = stack_of_right_children_.back();
- stack_of_right_children_.pop_back();
+ auto& stack_of_right_children = stack_of_right_children_;
+ while (!stack_of_right_children.empty()) {
+ node = stack_of_right_children.back();
+ stack_of_right_children.pop_back();
if (node->length > n) break;
n -= node->length;
bytes_remaining_ -= node->length;
@@ -1664,7 +1588,7 @@
while (node->tag == CONCAT) {
if (node->concat()->left->length > n) {
// Push right, descend left.
- stack_of_right_children_.push_back(node->concat()->right);
+ stack_of_right_children.push_back(node->concat()->right);
node = node->concat()->left;
} else {
// Skip left, descend right.
@@ -1685,7 +1609,7 @@
assert(node->tag == EXTERNAL || node->tag >= FLAT);
assert(length > n);
const char* data =
- node->tag == EXTERNAL ? node->external()->base : node->data;
+ node->tag == EXTERNAL ? node->external()->base : node->flat()->Data();
current_chunk_ = absl::string_view(data + offset + n, length - n);
current_leaf_ = node;
bytes_remaining_ -= n;
@@ -1703,7 +1627,9 @@
assert(offset < rep->length);
if (rep->tag >= FLAT) {
// Get the "i"th character directly from the flat array.
- return rep->data[offset];
+ return rep->flat()->Data()[offset];
+ } else if (rep->tag == RING) {
+ return rep->ring()->GetCharacter(offset);
} else if (rep->tag == EXTERNAL) {
// Get the "i"th character from the external array.
return rep->external()->base[offset];
@@ -1734,9 +1660,9 @@
// Try to put the contents into a new flat rep. If they won't fit in the
// biggest possible flat node, use an external rep instead.
if (total_size <= kMaxFlatLength) {
- new_rep = NewFlat(total_size);
+ new_rep = CordRepFlat::New(total_size);
new_rep->length = total_size;
- new_buffer = new_rep->data;
+ new_buffer = new_rep->flat()->Data();
CopyToArraySlowPath(new_buffer);
} else {
new_buffer = std::allocator<char>().allocate(total_size);
@@ -1747,7 +1673,9 @@
s.size());
});
}
- Unref(contents_.tree());
+ if (CordRep* tree = contents_.tree()) {
+ CordRep::Unref(tree);
+ }
contents_.set_tree(new_rep);
return absl::string_view(new_buffer, total_size);
}
@@ -1755,7 +1683,7 @@
/* static */ bool Cord::GetFlatAux(CordRep* rep, absl::string_view* fragment) {
assert(rep != nullptr);
if (rep->tag >= FLAT) {
- *fragment = absl::string_view(rep->data, rep->length);
+ *fragment = absl::string_view(rep->flat()->Data(), rep->length);
return true;
} else if (rep->tag == EXTERNAL) {
*fragment = absl::string_view(rep->external()->base, rep->length);
@@ -1763,8 +1691,8 @@
} else if (rep->tag == SUBSTRING) {
CordRep* child = rep->substring()->child;
if (child->tag >= FLAT) {
- *fragment =
- absl::string_view(child->data + rep->substring()->start, rep->length);
+ *fragment = absl::string_view(
+ child->flat()->Data() + rep->substring()->start, rep->length);
return true;
} else if (child->tag == EXTERNAL) {
*fragment = absl::string_view(
@@ -1778,6 +1706,15 @@
/* static */ void Cord::ForEachChunkAux(
absl::cord_internal::CordRep* rep,
absl::FunctionRef<void(absl::string_view)> callback) {
+ if (rep->tag == RING) {
+ ChunkIterator it(rep), end;
+ while (it != end) {
+ callback(*it);
+ ++it;
+ }
+ return;
+ }
+
assert(rep != nullptr);
int stack_pos = 0;
constexpr int stack_max = 128;
@@ -1819,9 +1756,9 @@
}
}
-static void DumpNode(CordRep* rep, bool include_data, std::ostream* os) {
+static void DumpNode(CordRep* rep, bool include_data, std::ostream* os,
+ int indent) {
const int kIndentStep = 1;
- int indent = 0;
absl::InlinedVector<CordRep*, kInlinedVectorSize> stack;
absl::InlinedVector<int, kInlinedVectorSize> indents;
for (;;) {
@@ -1842,17 +1779,28 @@
*os << "SUBSTRING @ " << rep->substring()->start << "\n";
indent += kIndentStep;
rep = rep->substring()->child;
- } else { // Leaf
+ } else { // Leaf or ring
if (rep->tag == EXTERNAL) {
*os << "EXTERNAL [";
if (include_data)
*os << absl::CEscape(std::string(rep->external()->base, rep->length));
*os << "]\n";
- } else {
- *os << "FLAT cap=" << TagToLength(rep->tag) << " [";
+ } else if (rep->tag >= FLAT) {
+ *os << "FLAT cap=" << rep->flat()->Capacity()
+ << " [";
if (include_data)
- *os << absl::CEscape(std::string(rep->data, rep->length));
+ *os << absl::CEscape(std::string(rep->flat()->Data(), rep->length));
*os << "]\n";
+ } else {
+ assert(rep->tag == RING);
+ auto* ring = rep->ring();
+ *os << "RING, entries = " << ring->entries() << "\n";
+ CordRepRing::index_type head = ring->head();
+ do {
+ DumpNode(ring->entry_child(head), include_data, os,
+ indent + kIndentStep);
+ head = ring->advance(head);;
+ } while (head != ring->tail());
}
if (stack.empty()) break;
rep = stack.back();
@@ -1897,8 +1845,9 @@
worklist.push_back(node->concat()->left);
}
} else if (node->tag >= FLAT) {
- ABSL_INTERNAL_CHECK(node->length <= TagToLength(node->tag),
- ReportError(root, node));
+ ABSL_INTERNAL_CHECK(
+ node->length <= node->flat()->Capacity(),
+ ReportError(root, node));
} else if (node->tag == EXTERNAL) {
ABSL_INTERNAL_CHECK(node->external()->base != nullptr,
ReportError(root, node));
@@ -1945,6 +1894,15 @@
}
next_node = right;
}
+ } else if (cur_node->tag == RING) {
+ total_mem_usage += CordRepRing::AllocSize(cur_node->ring()->capacity());
+ const CordRepRing* ring = cur_node->ring();
+ CordRepRing::index_type pos = ring->head(), tail = ring->tail();
+ do {
+ CordRep* node = ring->entry_child(pos);
+ assert(node->tag >= FLAT || node->tag == EXTERNAL);
+ RepMemoryUsageLeaf(node, &total_mem_usage);
+ } while ((pos = ring->advance(pos)) != tail);
} else {
// Since cur_node is not a leaf or a concat node it must be a substring.
assert(cur_node->tag == SUBSTRING);
@@ -1974,14 +1932,14 @@
}
namespace strings_internal {
-size_t CordTestAccess::FlatOverhead() { return kFlatOverhead; }
-size_t CordTestAccess::MaxFlatLength() { return kMaxFlatLength; }
+size_t CordTestAccess::FlatOverhead() { return cord_internal::kFlatOverhead; }
+size_t CordTestAccess::MaxFlatLength() { return cord_internal::kMaxFlatLength; }
size_t CordTestAccess::FlatTagToLength(uint8_t tag) {
- return TagToLength(tag);
+ return cord_internal::TagToLength(tag);
}
uint8_t CordTestAccess::LengthToTag(size_t s) {
ABSL_INTERNAL_CHECK(s <= kMaxFlatLength, absl::StrCat("Invalid length ", s));
- return AllocatedSizeToTag(s + kFlatOverhead);
+ return cord_internal::AllocatedSizeToTag(s + cord_internal::kFlatOverhead);
}
size_t CordTestAccess::SizeofCordRepConcat() { return sizeof(CordRepConcat); }
size_t CordTestAccess::SizeofCordRepExternal() {
diff --git a/absl/strings/cord.h b/absl/strings/cord.h
index 5d5c897..fa9cb91 100644
--- a/absl/strings/cord.h
+++ b/absl/strings/cord.h
@@ -25,7 +25,7 @@
//
// Because a Cord consists of these chunks, data can be added to or removed from
// a Cord during its lifetime. Chunks may also be shared between Cords. Unlike a
-// `std::string`, a Cord can therefore accomodate data that changes over its
+// `std::string`, a Cord can therefore accommodate data that changes over its
// lifetime, though it's not quite "mutable"; it can change only in the
// attachment, detachment, or rearrangement of chunks of its constituent data.
//
@@ -78,6 +78,8 @@
#include "absl/functional/function_ref.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_ring.h"
+#include "absl/strings/internal/cord_rep_ring_reader.h"
#include "absl/strings/internal/resize_uninitialized.h"
#include "absl/strings/internal/string_constant.h"
#include "absl/strings/string_view.h"
@@ -287,7 +289,7 @@
bool StartsWith(const Cord& rhs) const;
bool StartsWith(absl::string_view rhs) const;
- // Cord::EndsWidth()
+ // Cord::EndsWith()
//
// Determines whether the Cord ends with the passed string data `rhs`.
bool EndsWith(absl::string_view rhs) const;
@@ -361,14 +363,38 @@
friend class CharIterator;
private:
+ using CordRep = absl::cord_internal::CordRep;
+ using CordRepRing = absl::cord_internal::CordRepRing;
+ using CordRepRingReader = absl::cord_internal::CordRepRingReader;
+
+ // Stack of right children of concat nodes that we have to visit.
+ // Keep this at the end of the structure to avoid cache-thrashing.
+ // TODO(jgm): Benchmark to see if there's a more optimal value than 47 for
+ // the inlined vector size (47 exists for backward compatibility).
+ using Stack = absl::InlinedVector<absl::cord_internal::CordRep*, 47>;
+
+ // Constructs a `begin()` iterator from `tree`. `tree` must not be null.
+ explicit ChunkIterator(cord_internal::CordRep* tree);
+
// Constructs a `begin()` iterator from `cord`.
explicit ChunkIterator(const Cord* cord);
+ // Initializes this instance from a tree. Invoked by constructors.
+ void InitTree(cord_internal::CordRep* tree);
+
// Removes `n` bytes from `current_chunk_`. Expects `n` to be smaller than
// `current_chunk_.size()`.
void RemoveChunkPrefix(size_t n);
Cord AdvanceAndReadBytes(size_t n);
void AdvanceBytes(size_t n);
+
+ // Stack specific operator++
+ ChunkIterator& AdvanceStack();
+
+ // Ring buffer specific operator++
+ ChunkIterator& AdvanceRing();
+ void AdvanceBytesRing(size_t n);
+
// Iterates `n` bytes, where `n` is expected to be greater than or equal to
// `current_chunk_.size()`.
void AdvanceBytesSlowPath(size_t n);
@@ -382,8 +408,12 @@
absl::cord_internal::CordRep* current_leaf_ = nullptr;
// The number of bytes left in the `Cord` over which we are iterating.
size_t bytes_remaining_ = 0;
- absl::InlinedVector<absl::cord_internal::CordRep*, 4>
- stack_of_right_children_;
+
+ // Cord reader for ring buffers. Empty if not traversing a ring buffer.
+ CordRepRingReader ring_reader_;
+
+ // See 'Stack' alias definition.
+ Stack stack_of_right_children_;
};
// Cord::ChunkIterator::chunk_begin()
@@ -655,8 +685,6 @@
public:
static constexpr unsigned char kMaxInline = cord_internal::kMaxInline;
static_assert(kMaxInline >= sizeof(absl::cord_internal::CordRep*), "");
- static constexpr unsigned char kTreeFlag = cord_internal::kTreeFlag;
- static constexpr unsigned char kProfiledFlag = cord_internal::kProfiledFlag;
constexpr InlineRep() : data_() {}
InlineRep(const InlineRep& src);
@@ -675,6 +703,7 @@
char* set_data(size_t n); // Write data to the result
// Returns nullptr if holding bytes
absl::cord_internal::CordRep* tree() const;
+ absl::cord_internal::CordRep* as_tree() const;
// Discards old pointer, if any
void set_tree(absl::cord_internal::CordRep* rep);
// Replaces a tree with a new root. This is faster than set_tree, but it
@@ -718,13 +747,30 @@
memcpy(&(*dst)[0], &data_, sizeof(data_) - 1);
// erase is faster than resize because the logic for memory allocation is
// not needed.
- dst->erase(tagged_size());
+ dst->erase(inline_size());
}
// Copies the inline contents into `dst`. Assumes the cord is not empty.
void CopyToArray(char* dst) const;
- bool is_tree() const { return tagged_size() > kMaxInline; }
+ bool is_tree() const { return data_.is_tree(); }
+
+ // Returns true if the Cord is being profiled by cordz.
+ bool is_profiled() const { return data_.is_tree() && data_.is_profiled(); }
+
+ // Returns the profiled CordzInfo, or nullptr if not sampled.
+ absl::cord_internal::CordzInfo* cordz_info() const {
+ return data_.cordz_info();
+ }
+
+ // Sets the profiled CordzInfo. `cordz_info` must not be null.
+ void set_cordz_info(cord_internal::CordzInfo* cordz_info) {
+ assert(cordz_info != nullptr);
+ data_.set_cordz_info(cordz_info);
+ }
+
+ // Resets the current cordz_info to null / empty.
+ void clear_cordz_info() { data_.clear_cordz_info(); }
private:
friend class Cord;
@@ -735,14 +781,8 @@
void ResetToEmpty() { data_ = {}; }
- // This uses reinterpret_cast instead of the union to avoid accessing the
- // inactive union element. The tagged size is not a common prefix.
- void set_tagged_size(char new_tag) {
- reinterpret_cast<char*>(&data_)[kMaxInline] = new_tag;
- }
- char tagged_size() const {
- return reinterpret_cast<const char*>(&data_)[kMaxInline];
- }
+ void set_inline_size(size_t size) { data_.set_inline_size(size); }
+ size_t inline_size() const { return data_.inline_size(); }
cord_internal::InlineData data_;
};
@@ -898,8 +938,12 @@
constexpr Cord::InlineRep::InlineRep(cord_internal::InlineData data)
: data_(data) {}
-inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src) {
- data_ = src.data_;
+inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src)
+ : data_(src.data_) {
+ if (is_tree()) {
+ data_.clear_cordz_info();
+ absl::cord_internal::CordRep::Ref(as_tree());
+ }
}
inline Cord::InlineRep::InlineRep(Cord::InlineRep&& src) {
@@ -933,40 +977,43 @@
if (rhs == this) {
return;
}
-
std::swap(data_, rhs->data_);
}
inline const char* Cord::InlineRep::data() const {
- return is_tree() ? nullptr : data_.as_chars;
+ return is_tree() ? nullptr : data_.as_chars();
+}
+
+inline absl::cord_internal::CordRep* Cord::InlineRep::as_tree() const {
+ assert(data_.is_tree());
+ return data_.as_tree();
}
inline absl::cord_internal::CordRep* Cord::InlineRep::tree() const {
if (is_tree()) {
- return data_.as_tree.rep;
+ return as_tree();
} else {
return nullptr;
}
}
-inline bool Cord::InlineRep::empty() const { return tagged_size() == 0; }
+inline bool Cord::InlineRep::empty() const { return data_.is_empty(); }
inline size_t Cord::InlineRep::size() const {
- const char tag = tagged_size();
- if (tag <= kMaxInline) return tag;
- return static_cast<size_t>(tree()->length);
+ return is_tree() ? as_tree()->length : inline_size();
}
inline void Cord::InlineRep::set_tree(absl::cord_internal::CordRep* rep) {
if (rep == nullptr) {
ResetToEmpty();
} else {
- bool was_tree = is_tree();
- data_.as_tree = {rep, {}, tagged_size()};
- if (!was_tree) {
- // If we were not a tree already, set the tag.
- // Otherwise, leave it alone because it might have the profile bit on.
- set_tagged_size(kTreeFlag);
+ if (data_.is_tree()) {
+ // `data_` already holds a 'tree' value and an optional cordz_info value.
+ // Replace the tree value only, leaving the cordz_info value unchanged.
+ data_.set_tree(rep);
+ } else {
+ // `data_` contains inlined data: initialize data_ to tree value `rep`.
+ data_.make_tree(rep);
}
}
}
@@ -977,7 +1024,7 @@
set_tree(rep);
return;
}
- data_.as_tree = {rep, {}, tagged_size()};
+ data_.set_tree(rep);
}
inline absl::cord_internal::CordRep* Cord::InlineRep::clear() {
@@ -988,9 +1035,9 @@
inline void Cord::InlineRep::CopyToArray(char* dst) const {
assert(!is_tree());
- size_t n = tagged_size();
+ size_t n = inline_size();
assert(n != 0);
- cord_internal::SmallMemmove(dst, data_.as_chars, n);
+ cord_internal::SmallMemmove(dst, data_.as_chars(), n);
}
constexpr inline Cord::Cord() noexcept {}
@@ -1001,17 +1048,17 @@
cord_internal::kMaxInline
? cord_internal::InlineData(
strings_internal::StringConstant<T>::value)
- : cord_internal::InlineData(cord_internal::AsTree{
+ : cord_internal::InlineData(
&cord_internal::ConstInitExternalStorage<
- strings_internal::StringConstant<T>>::value,
- {},
- cord_internal::kTreeFlag})) {}
+ strings_internal::StringConstant<T>>::value)) {}
inline Cord& Cord::operator=(const Cord& x) {
contents_ = x.contents_;
return *this;
}
+inline Cord::Cord(const Cord& src) : contents_(src.contents_) {}
+
inline Cord::Cord(Cord&& src) noexcept : contents_(std::move(src.contents_)) {}
inline void Cord::swap(Cord& other) noexcept {
@@ -1095,17 +1142,64 @@
return EqualsImpl(rhs, rhs_size);
}
+inline void Cord::ChunkIterator::InitTree(cord_internal::CordRep* tree) {
+ if (tree->tag == cord_internal::RING) {
+ current_chunk_ = ring_reader_.Reset(tree->ring());
+ return;
+ }
+
+ stack_of_right_children_.push_back(tree);
+ operator++();
+}
+
+inline Cord::ChunkIterator::ChunkIterator(cord_internal::CordRep* tree)
+ : bytes_remaining_(tree->length) {
+ InitTree(tree);
+}
+
inline Cord::ChunkIterator::ChunkIterator(const Cord* cord)
: bytes_remaining_(cord->size()) {
- if (cord->empty()) return;
if (cord->contents_.is_tree()) {
- stack_of_right_children_.push_back(cord->contents_.tree());
- operator++();
+ InitTree(cord->contents_.as_tree());
} else {
- current_chunk_ = absl::string_view(cord->contents_.data(), cord->size());
+ current_chunk_ =
+ absl::string_view(cord->contents_.data(), bytes_remaining_);
}
}
+inline Cord::ChunkIterator& Cord::ChunkIterator::AdvanceRing() {
+ current_chunk_ = ring_reader_.Next();
+ return *this;
+}
+
+inline void Cord::ChunkIterator::AdvanceBytesRing(size_t n) {
+ assert(n >= current_chunk_.size());
+ bytes_remaining_ -= n;
+ if (bytes_remaining_) {
+ if (n == current_chunk_.size()) {
+ current_chunk_ = ring_reader_.Next();
+ } else {
+ size_t offset = ring_reader_.length() - bytes_remaining_;
+ current_chunk_ = ring_reader_.Seek(offset);
+ }
+ } else {
+ current_chunk_ = {};
+ }
+}
+
+inline Cord::ChunkIterator& Cord::ChunkIterator::operator++() {
+ ABSL_HARDENING_ASSERT(bytes_remaining_ > 0 &&
+ "Attempted to iterate past `end()`");
+ assert(bytes_remaining_ >= current_chunk_.size());
+ bytes_remaining_ -= current_chunk_.size();
+ if (bytes_remaining_ > 0) {
+ return ring_reader_ ? AdvanceRing() : AdvanceStack();
+ } else {
+ current_chunk_ = {};
+ }
+ return *this;
+}
+
inline Cord::ChunkIterator Cord::ChunkIterator::operator++(int) {
ChunkIterator tmp(*this);
operator++();
@@ -1137,10 +1231,11 @@
}
inline void Cord::ChunkIterator::AdvanceBytes(size_t n) {
+ assert(bytes_remaining_ >= n);
if (ABSL_PREDICT_TRUE(n < current_chunk_.size())) {
RemoveChunkPrefix(n);
} else if (n != 0) {
- AdvanceBytesSlowPath(n);
+ ring_reader_ ? AdvanceBytesRing(n) : AdvanceBytesSlowPath(n);
}
}
diff --git a/absl/strings/cord_ring_reader_test.cc b/absl/strings/cord_ring_reader_test.cc
new file mode 100644
index 0000000..585616f
--- /dev/null
+++ b/absl/strings/cord_ring_reader_test.cc
@@ -0,0 +1,173 @@
+// Copyright 2020 The Abseil Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdlib>
+#include <ctime>
+#include <memory>
+#include <random>
+#include <sstream>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/debugging/leak_check.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_ring.h"
+#include "absl/strings/internal/cord_rep_ring_reader.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+namespace {
+
+using testing::Eq;
+
+// Creates a flat for testing
+CordRep* MakeFlat(absl::string_view s) {
+ CordRepFlat* flat = CordRepFlat::New(s.length());
+ memcpy(flat->Data(), s.data(), s.length());
+ flat->length = s.length();
+ return flat;
+}
+
+CordRepRing* FromFlats(Span<absl::string_view const> flats) {
+ CordRepRing* ring = CordRepRing::Create(MakeFlat(flats[0]), flats.size() - 1);
+ for (int i = 1; i < flats.size(); ++i) {
+ ring = CordRepRing::Append(ring, MakeFlat(flats[i]));
+ }
+ return ring;
+}
+
+std::array<absl::string_view, 12> TestFlats() {
+ return {"abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ",
+ "KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_",
+ "+-=", "[]\\{}|;':", ",/<>?", "."};
+}
+
+TEST(CordRingReaderTest, DefaultInstance) {
+ CordRepRingReader reader;
+ EXPECT_FALSE(static_cast<bool>(reader));
+ EXPECT_THAT(reader.ring(), Eq(nullptr));
+#ifndef NDEBUG
+ EXPECT_DEATH_IF_SUPPORTED(reader.length(), ".*");
+ EXPECT_DEATH_IF_SUPPORTED(reader.consumed(), ".*");
+ EXPECT_DEATH_IF_SUPPORTED(reader.remaining(), ".*");
+ EXPECT_DEATH_IF_SUPPORTED(reader.Next(), ".*");
+ EXPECT_DEATH_IF_SUPPORTED(reader.Seek(0), ".*");
+#endif
+}
+
+TEST(CordRingReaderTest, Reset) {
+ CordRepRingReader reader;
+ auto flats = TestFlats();
+ CordRepRing* ring = FromFlats(flats);
+
+ absl::string_view first = reader.Reset(ring);
+ EXPECT_THAT(first, Eq(flats[0]));
+ EXPECT_TRUE(static_cast<bool>(reader));
+ EXPECT_THAT(reader.ring(), Eq(ring));
+ EXPECT_THAT(reader.index(), Eq(ring->head()));
+ EXPECT_THAT(reader.length(), Eq(ring->length));
+ EXPECT_THAT(reader.consumed(), Eq(flats[0].length()));
+ EXPECT_THAT(reader.remaining(), Eq(ring->length - reader.consumed()));
+
+ reader.Reset();
+ EXPECT_FALSE(static_cast<bool>(reader));
+ EXPECT_THAT(reader.ring(), Eq(nullptr));
+
+ CordRep::Unref(ring);
+}
+
+TEST(CordRingReaderTest, Next) {
+ CordRepRingReader reader;
+ auto flats = TestFlats();
+ CordRepRing* ring = FromFlats(flats);
+ CordRepRing::index_type head = ring->head();
+
+ reader.Reset(ring);
+ size_t consumed = reader.consumed();
+ size_t remaining = reader.remaining();
+ for (int i = 1; i < flats.size(); ++i) {
+ consumed += flats[i].length();
+ remaining -= flats[i].length();
+ absl::string_view next = reader.Next();
+ ASSERT_THAT(next, Eq(flats[i]));
+ ASSERT_THAT(reader.index(), Eq(ring->advance(head, i)));
+ ASSERT_THAT(reader.consumed(), Eq(consumed));
+ ASSERT_THAT(reader.remaining(), Eq(remaining));
+ }
+
+#ifndef NDEBUG
+ EXPECT_DEATH_IF_SUPPORTED(reader.Next(), ".*");
+#endif
+
+ CordRep::Unref(ring);
+}
+
+TEST(CordRingReaderTest, SeekForward) {
+ CordRepRingReader reader;
+ auto flats = TestFlats();
+ CordRepRing* ring = FromFlats(flats);
+ CordRepRing::index_type head = ring->head();
+
+ reader.Reset(ring);
+ size_t consumed = 0;
+ size_t remaining = ring->length;;
+ for (int i = 0; i < flats.size(); ++i) {
+ size_t offset = consumed;
+ consumed += flats[i].length();
+ remaining -= flats[i].length();
+ for (int off = 0; off < flats[i].length(); ++off) {
+ absl::string_view chunk = reader.Seek(offset + off);
+ ASSERT_THAT(chunk, Eq(flats[i].substr(off)));
+ ASSERT_THAT(reader.index(), Eq(ring->advance(head, i)));
+ ASSERT_THAT(reader.consumed(), Eq(consumed));
+ ASSERT_THAT(reader.remaining(), Eq(remaining));
+ }
+ }
+
+ CordRep::Unref(ring);
+}
+
+TEST(CordRingReaderTest, SeekBackward) {
+ CordRepRingReader reader;
+ auto flats = TestFlats();
+ CordRepRing* ring = FromFlats(flats);
+ CordRepRing::index_type head = ring->head();
+
+ reader.Reset(ring);
+ size_t consumed = ring->length;
+ size_t remaining = 0;
+ for (int i = flats.size() - 1; i >= 0; --i) {
+ size_t offset = consumed - flats[i].length();
+ for (int off = 0; off < flats[i].length(); ++off) {
+ absl::string_view chunk = reader.Seek(offset + off);
+ ASSERT_THAT(chunk, Eq(flats[i].substr(off)));
+ ASSERT_THAT(reader.index(), Eq(ring->advance(head, i)));
+ ASSERT_THAT(reader.consumed(), Eq(consumed));
+ ASSERT_THAT(reader.remaining(), Eq(remaining));
+ }
+ consumed -= flats[i].length();
+ remaining += flats[i].length();
+ }
+#ifndef NDEBUG
+ EXPECT_DEATH_IF_SUPPORTED(reader.Seek(ring->length), ".*");
+#endif
+ CordRep::Unref(ring);
+}
+
+} // namespace
+} // namespace cord_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/strings/cord_ring_test.cc b/absl/strings/cord_ring_test.cc
new file mode 100644
index 0000000..7d75e10
--- /dev/null
+++ b/absl/strings/cord_ring_test.cc
@@ -0,0 +1,1572 @@
+// Copyright 2020 The Abseil Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdlib>
+#include <ctime>
+#include <memory>
+#include <random>
+#include <sstream>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/base/macros.h"
+#include "absl/debugging/leak_check.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_ring.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+
+extern thread_local bool cord_ring;
+
+// TOOD(b/177688959): weird things happened with the original test
+#define ASAN_BUG_177688959_FIXED false
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace {
+
+using RandomEngine = std::mt19937_64;
+
+using ::absl::cord_internal::CordRep;
+using ::absl::cord_internal::CordRepConcat;
+using ::absl::cord_internal::CordRepExternal;
+using ::absl::cord_internal::CordRepFlat;
+using ::absl::cord_internal::CordRepRing;
+using ::absl::cord_internal::CordRepSubstring;
+
+using ::absl::cord_internal::CONCAT;
+using ::absl::cord_internal::EXTERNAL;
+using ::absl::cord_internal::SUBSTRING;
+
+using testing::ElementsAre;
+using testing::ElementsAreArray;
+using testing::Eq;
+using testing::Ge;
+using testing::Le;
+using testing::Lt;
+using testing::Ne;
+using testing::SizeIs;
+
+using index_type = CordRepRing::index_type;
+
+enum InputShareMode { kPrivate, kShared, kSharedIndirect };
+
+// TestParam class used by all test fixtures.
+// Not all fixtures use all possible input combinations
+struct TestParam {
+ TestParam() = default;
+ explicit TestParam(InputShareMode input_share_mode)
+ : input_share_mode(input_share_mode) {}
+
+ // Run the test with the 'rep under test' to be privately owned.
+ // Otherwise, the rep has a shared ref count of 2 or higher.
+ bool refcount_is_one = true;
+
+ // Run the test with the 'rep under test' being allocated with enough capacity
+ // to accommodate any modifications made to it. Otherwise, the rep has zero
+ // extra (reserve) capacity.
+ bool with_capacity = true;
+
+ // For test providing possibly shared input such as Append(.., CordpRep*),
+ // this field defines if that input is adopted with a refcount of one
+ // (privately owned / donated), or shared. For composite inputs such as
+ // 'substring of flat', we also have the 'shared indirect' value which means
+ // the top level node is not shared, but the contained child node is shared.
+ InputShareMode input_share_mode = kPrivate;
+
+ std::string ToString() const {
+ return absl::StrCat(refcount_is_one ? "Private" : "Shared",
+ with_capacity ? "" : "_NoCapacity",
+ (input_share_mode == kPrivate) ? ""
+ : (input_share_mode == kShared)
+ ? "_SharedInput"
+ : "_IndirectSharedInput");
+ }
+};
+using TestParams = std::vector<TestParam>;
+
+// Matcher validating when mutable copies are required / performed.
+MATCHER_P2(EqIfPrivate, param, rep,
+ absl::StrCat("Equal 0x", absl::Hex(rep), " if private")) {
+ return param.refcount_is_one ? arg == rep : arg != rep;
+}
+
+// Matcher validating when mutable copies are required / performed.
+MATCHER_P2(EqIfPrivateAndCapacity, param, rep,
+ absl::StrCat("Equal 0x", absl::Hex(rep),
+ " if private and capacity")) {
+ return (param.refcount_is_one && param.with_capacity) ? arg == rep
+ : arg != rep;
+}
+
+MATCHER_P2(EqIfInputPrivate, param, rep, "Equal if input is private") {
+ return param.input_share_mode == kPrivate ? arg == rep : arg != rep;
+}
+
+// Matcher validating the core in-variants of the CordRepRing instance.
+MATCHER(IsValidRingBuffer, "RingBuffer is valid") {
+ std::stringstream ss;
+ if (!arg->IsValid(ss)) {
+ *result_listener << "\nERROR: " << ss.str() << "\nRING = " << *arg;
+ return false;
+ }
+ return true;
+}
+
+// Returns the flats contained in the provided CordRepRing
+std::vector<string_view> ToFlats(const CordRepRing* r) {
+ std::vector<string_view> flats;
+ flats.reserve(r->entries());
+ index_type pos = r->head();
+ do {
+ flats.push_back(r->entry_data(pos));
+ } while ((pos = r->advance(pos)) != r->tail());
+ return flats;
+}
+
+class not_a_string_view {
+ public:
+ explicit not_a_string_view(absl::string_view s)
+ : data_(s.data()), size_(s.size()) {}
+ explicit not_a_string_view(const void* data, size_t size)
+ : data_(data), size_(size) {}
+
+ not_a_string_view remove_prefix(size_t n) const {
+ return not_a_string_view(static_cast<const char*>(data_) + n, size_ - n);
+ }
+
+ not_a_string_view remove_suffix(size_t n) const {
+ return not_a_string_view(data_, size_ - n);
+ }
+
+ const void* data() const { return data_; }
+ size_t size() const { return size_; }
+
+ private:
+ const void* data_;
+ size_t size_;
+};
+
+bool operator==(not_a_string_view lhs, not_a_string_view rhs) {
+ return lhs.data() == rhs.data() && lhs.size() == rhs.size();
+}
+
+std::ostream& operator<<(std::ostream& s, not_a_string_view rhs) {
+ return s << "{ data: " << rhs.data() << " size: " << rhs.size() << "}";
+}
+
+std::vector<not_a_string_view> ToRawFlats(const CordRepRing* r) {
+ std::vector<not_a_string_view> flats;
+ flats.reserve(r->entries());
+ index_type pos = r->head();
+ do {
+ flats.emplace_back(r->entry_data(pos));
+ } while ((pos = r->advance(pos)) != r->tail());
+ return flats;
+}
+
+// Returns the value contained in the provided CordRepRing
+std::string ToString(const CordRepRing* r) {
+ std::string value;
+ value.reserve(r->length);
+ index_type pos = r->head();
+ do {
+ absl::string_view sv = r->entry_data(pos);
+ value.append(sv.data(), sv.size());
+ } while ((pos = r->advance(pos)) != r->tail());
+ return value;
+}
+
+// Creates a flat for testing
+CordRep* MakeFlat(absl::string_view s, size_t extra = 0) {
+ CordRepFlat* flat = CordRepFlat::New(s.length() + extra);
+ memcpy(flat->Data(), s.data(), s.length());
+ flat->length = s.length();
+ return flat;
+}
+
+// Creates an external node for testing
+CordRepExternal* MakeExternal(absl::string_view s) {
+ struct Rep : public CordRepExternal {
+ std::string s;
+ explicit Rep(absl::string_view s) : s(s) {
+ this->tag = EXTERNAL;
+ this->base = s.data();
+ this->length = s.length();
+ this->releaser_invoker = [](CordRepExternal* self) {
+ delete static_cast<Rep*>(self);
+ };
+ }
+ };
+ return new Rep(s);
+}
+
+CordRepExternal* MakeFakeExternal(size_t length) {
+ struct Rep : public CordRepExternal {
+ std::string s;
+ explicit Rep(size_t len) {
+ this->tag = EXTERNAL;
+ this->base = this->storage;
+ this->length = len;
+ this->releaser_invoker = [](CordRepExternal* self) {
+ delete static_cast<Rep*>(self);
+ };
+ }
+ };
+ return new Rep(length);
+}
+
+// Creates a flat or an external node for testing depending on the size.
+CordRep* MakeLeaf(absl::string_view s, size_t extra = 0) {
+ if (s.size() <= absl::cord_internal::kMaxFlatLength) {
+ return MakeFlat(s, extra);
+ } else {
+ return MakeExternal(s);
+ }
+}
+
+// Creates a substring node
+CordRepSubstring* MakeSubstring(size_t start, size_t len, CordRep* rep) {
+ auto* sub = new CordRepSubstring;
+ sub->tag = SUBSTRING;
+ sub->start = start;
+ sub->length = (len <= 0) ? rep->length - start + len : len;
+ sub->child = rep;
+ return sub;
+}
+
+// Creates a substring node removing the specified prefix
+CordRepSubstring* RemovePrefix(size_t start, CordRep* rep) {
+ return MakeSubstring(start, rep->length - start, rep);
+}
+
+// Creates a substring node removing the specified suffix
+CordRepSubstring* RemoveSuffix(size_t length, CordRep* rep) {
+ return MakeSubstring(0, rep->length - length, rep);
+}
+
+CordRepConcat* MakeConcat(CordRep* left, CordRep* right, int depth = 0) {
+ auto* concat = new CordRepConcat;
+ concat->tag = CONCAT;
+ concat->length = left->length + right->length;
+ concat->left = left;
+ concat->right = right;
+ concat->set_depth(depth);
+ return concat;
+}
+
+enum Composition { kMix, kAppend, kPrepend };
+
+Composition RandomComposition() {
+ RandomEngine rng(testing::GTEST_FLAG(random_seed));
+ return (rng() & 1) ? kMix : ((rng() & 1) ? kAppend : kPrepend);
+}
+
+absl::string_view ToString(Composition composition) {
+ switch (composition) {
+ case kAppend:
+ return "Append";
+ case kPrepend:
+ return "Prepend";
+ case kMix:
+ return "Mix";
+ }
+ assert(false);
+ return "???";
+}
+
+constexpr const char* kFox = "The quick brown fox jumps over the lazy dog";
+constexpr const char* kFoxFlats[] = {"The ", "quick ", "brown ",
+ "fox ", "jumps ", "over ",
+ "the ", "lazy ", "dog"};
+constexpr const char* kAlphabet = "abcdefghijklmnopqrstuvwxyz";
+
+CordRepRing* FromFlats(Span<const char* const> flats,
+ Composition composition = kAppend) {
+ if (flats.empty()) return nullptr;
+ CordRepRing* ring = nullptr;
+ switch (composition) {
+ case kAppend:
+ ring = CordRepRing::Create(MakeLeaf(flats.front()), flats.size() - 1);
+ for (int i = 1; i < flats.size(); ++i) {
+ ring = CordRepRing::Append(ring, MakeLeaf(flats[i]));
+ }
+ break;
+ case kPrepend:
+ ring = CordRepRing::Create(MakeLeaf(flats.back()), flats.size() - 1);
+ for (int i = static_cast<int>(flats.size() - 2); i >= 0; --i) {
+ ring = CordRepRing::Prepend(ring, MakeLeaf(flats[i]));
+ }
+ break;
+ case kMix:
+ size_t middle1 = flats.size() / 2, middle2 = middle1;
+ ring = CordRepRing::Create(MakeLeaf(flats[middle1]), flats.size() - 1);
+ if (!flats.empty()) {
+ if ((flats.size() & 1) == 0) {
+ ring = CordRepRing::Prepend(ring, MakeLeaf(flats[--middle1]));
+ }
+ for (int i = 1; i <= middle1; ++i) {
+ ring = CordRepRing::Prepend(ring, MakeLeaf(flats[middle1 - i]));
+ ring = CordRepRing::Append(ring, MakeLeaf(flats[middle2 + i]));
+ }
+ }
+ break;
+ }
+ EXPECT_THAT(ToFlats(ring), ElementsAreArray(flats));
+ return ring;
+}
+
+std::ostream& operator<<(std::ostream& s, const TestParam& param) {
+ return s << param.ToString();
+}
+
+std::string TestParamToString(const testing::TestParamInfo<TestParam>& info) {
+ return info.param.ToString();
+}
+
+class CordRingTest : public testing::Test {
+ public:
+ ~CordRingTest() override {
+#if ASAN_BUG_177688959_FIXED
+ for (CordRep* rep : unrefs_) {
+ CordRep::Unref(rep);
+ }
+#endif
+ }
+
+ template <typename CordRepType>
+ CordRepType* NeedsUnref(CordRepType* rep) {
+ assert(rep);
+#if ASAN_BUG_177688959_FIXED
+ unrefs_.push_back(rep);
+#endif
+ return rep;
+ }
+
+ template <typename CordRepType>
+ CordRepType* Ref(CordRepType* rep) {
+ CordRep::Ref(rep);
+ return NeedsUnref(rep);
+ }
+
+ void Unref(CordRep* rep) {
+#if !ASAN_BUG_177688959_FIXED
+ CordRep::Unref(rep);
+#endif
+ }
+
+ private:
+#if ASAN_BUG_177688959_FIXED
+ std::vector<CordRep*> unrefs_;
+#endif
+};
+
+class CordRingTestWithParam : public testing::TestWithParam<TestParam> {
+ public:
+ ~CordRingTestWithParam() override {
+#if ASAN_BUG_177688959_FIXED
+ for (CordRep* rep : unrefs_) {
+ CordRep::Unref(rep);
+ }
+#endif
+ }
+
+ CordRepRing* CreateWithCapacity(CordRep* child, size_t extra_capacity) {
+ if (!GetParam().with_capacity) extra_capacity = 0;
+ CordRepRing* ring = CordRepRing::Create(child, extra_capacity);
+ ring->SetCapacityForTesting(1 + extra_capacity);
+ return RefIfShared(ring);
+ }
+
+ bool Shared() const { return !GetParam().refcount_is_one; }
+ bool InputShared() const { return GetParam().input_share_mode == kShared; }
+ bool InputSharedIndirect() const {
+ return GetParam().input_share_mode == kSharedIndirect;
+ }
+
+ template <typename CordRepType>
+ CordRepType* NeedsUnref(CordRepType* rep) {
+ assert(rep);
+#if ASAN_BUG_177688959_FIXED
+ unrefs_.push_back(rep);
+#endif
+ return rep;
+ }
+
+ template <typename CordRepType>
+ CordRepType* Ref(CordRepType* rep) {
+ CordRep::Ref(rep);
+ return NeedsUnref(rep);
+ }
+
+ void Unref(CordRep* rep) {
+#if !ASAN_BUG_177688959_FIXED
+ CordRep::Unref(rep);
+#endif
+ }
+
+ template <typename CordRepType>
+ CordRepType* RefIfShared(CordRepType* rep) {
+ return Shared() ? Ref(rep) : rep;
+ }
+
+ void UnrefIfShared(CordRep* rep) {
+ if (Shared()) Unref(rep);
+ }
+
+ template <typename CordRepType>
+ CordRepType* RefIfInputShared(CordRepType* rep) {
+ return InputShared() ? Ref(rep) : rep;
+ }
+
+ void UnrefIfInputShared(CordRep* rep) {
+ if (InputShared()) Unref(rep);
+ }
+
+ template <typename CordRepType>
+ CordRepType* RefIfInputSharedIndirect(CordRepType* rep) {
+ return InputSharedIndirect() ? Ref(rep) : rep;
+ }
+
+ void UnrefIfInputSharedIndirect(CordRep* rep) {
+ if (InputSharedIndirect()) Unref(rep);
+ }
+
+ private:
+#if ASAN_BUG_177688959_FIXED
+ std::vector<CordRep*> unrefs_;
+#endif
+};
+
+class CordRingCreateTest : public CordRingTestWithParam {
+ public:
+ static TestParams CreateTestParams() {
+ TestParams params;
+ params.emplace_back(InputShareMode::kPrivate);
+ params.emplace_back(InputShareMode::kShared);
+ return params;
+ }
+};
+
+class CordRingSubTest : public CordRingTestWithParam {
+ public:
+ static TestParams CreateTestParams() {
+ TestParams params;
+ for (bool refcount_is_one : {true, false}) {
+ TestParam param;
+ param.refcount_is_one = refcount_is_one;
+ params.push_back(param);
+ }
+ return params;
+ }
+};
+
+class CordRingBuildTest : public CordRingTestWithParam {
+ public:
+ static TestParams CreateTestParams() {
+ TestParams params;
+ for (bool refcount_is_one : {true, false}) {
+ for (bool with_capacity : {true, false}) {
+ TestParam param;
+ param.refcount_is_one = refcount_is_one;
+ param.with_capacity = with_capacity;
+ params.push_back(param);
+ }
+ }
+ return params;
+ }
+};
+
+class CordRingCreateFromTreeTest : public CordRingTestWithParam {
+ public:
+ static TestParams CreateTestParams() {
+ TestParams params;
+ params.emplace_back(InputShareMode::kPrivate);
+ params.emplace_back(InputShareMode::kShared);
+ params.emplace_back(InputShareMode::kSharedIndirect);
+ return params;
+ }
+};
+
+class CordRingBuildInputTest : public CordRingTestWithParam {
+ public:
+ static TestParams CreateTestParams() {
+ TestParams params;
+ for (bool refcount_is_one : {true, false}) {
+ for (bool with_capacity : {true, false}) {
+ for (InputShareMode share_mode : {kPrivate, kShared, kSharedIndirect}) {
+ TestParam param;
+ param.refcount_is_one = refcount_is_one;
+ param.with_capacity = with_capacity;
+ param.input_share_mode = share_mode;
+ params.push_back(param);
+ }
+ }
+ }
+ return params;
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(WithParam, CordRingSubTest,
+ testing::ValuesIn(CordRingSubTest::CreateTestParams()),
+ TestParamToString);
+
+INSTANTIATE_TEST_CASE_P(
+ WithParam, CordRingCreateTest,
+ testing::ValuesIn(CordRingCreateTest::CreateTestParams()),
+ TestParamToString);
+
+INSTANTIATE_TEST_CASE_P(
+ WithParam, CordRingCreateFromTreeTest,
+ testing::ValuesIn(CordRingCreateFromTreeTest::CreateTestParams()),
+ TestParamToString);
+
+INSTANTIATE_TEST_CASE_P(
+ WithParam, CordRingBuildTest,
+ testing::ValuesIn(CordRingBuildTest::CreateTestParams()),
+ TestParamToString);
+
+INSTANTIATE_TEST_CASE_P(
+ WithParam, CordRingBuildInputTest,
+ testing::ValuesIn(CordRingBuildInputTest::CreateTestParams()),
+ TestParamToString);
+
+TEST_P(CordRingCreateTest, CreateFromFlat) {
+ absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
+ CordRepRing* result = NeedsUnref(CordRepRing::Create(MakeFlat(str1)));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result->length, Eq(str1.size()));
+ EXPECT_THAT(ToFlats(result), ElementsAre(str1));
+ Unref(result);
+}
+
+TEST_P(CordRingCreateTest, CreateFromRing) {
+ CordRepRing* ring = RefIfShared(FromFlats(kFoxFlats));
+ CordRepRing* result = NeedsUnref(CordRepRing::Create(ring));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats));
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringRing) {
+ CordRepRing* ring = RefIfInputSharedIndirect(FromFlats(kFoxFlats));
+ CordRep* sub = RefIfInputShared(MakeSubstring(2, 11, ring));
+ CordRepRing* result = NeedsUnref(CordRepRing::Create(sub));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfInputPrivate(GetParam(), ring));
+ EXPECT_THAT(ToString(result), string_view(kFox).substr(2, 11));
+ UnrefIfInputSharedIndirect(ring);
+ UnrefIfInputShared(sub);
+ Unref(result);
+}
+
+TEST_F(CordRingTest, CreateWithIllegalExtraCapacity) {
+ CordRep* flat = NeedsUnref(MakeFlat("Hello world"));
+#if defined(ABSL_HAVE_EXCEPTIONS)
+ try {
+ CordRepRing::Create(flat, CordRepRing::kMaxCapacity);
+ GTEST_FAIL() << "expected std::length_error exception";
+ } catch (const std::length_error&) {
+ }
+#elif defined(GTEST_HAS_DEATH_TEST)
+ EXPECT_DEATH(CordRepRing::Create(flat, CordRepRing::kMaxCapacity), ".*");
+#endif
+ Unref(flat);
+}
+
+TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfFlat) {
+ absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
+ auto* flat = RefIfInputShared(MakeFlat(str1));
+ auto* child = RefIfInputSharedIndirect(MakeSubstring(4, 20, flat));
+ CordRepRing* result = NeedsUnref(CordRepRing::Create(child));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result->length, Eq(20));
+ EXPECT_THAT(ToFlats(result), ElementsAre(str1.substr(4, 20)));
+ Unref(result);
+ UnrefIfInputShared(flat);
+ UnrefIfInputSharedIndirect(child);
+}
+
+TEST_P(CordRingCreateTest, CreateFromExternal) {
+ absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
+ auto* child = RefIfInputShared(MakeExternal(str1));
+ CordRepRing* result = NeedsUnref(CordRepRing::Create(child));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result->length, Eq(str1.size()));
+ EXPECT_THAT(ToFlats(result), ElementsAre(str1));
+ Unref(result);
+ UnrefIfInputShared(child);
+}
+
+TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfExternal) {
+ absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
+ auto* external = RefIfInputShared(MakeExternal(str1));
+ auto* child = RefIfInputSharedIndirect(MakeSubstring(1, 24, external));
+ CordRepRing* result = NeedsUnref(CordRepRing::Create(child));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result->length, Eq(24));
+ EXPECT_THAT(ToFlats(result), ElementsAre(str1.substr(1, 24)));
+ Unref(result);
+ UnrefIfInputShared(external);
+ UnrefIfInputSharedIndirect(child);
+}
+
+TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfLargeExternal) {
+ auto* external = RefIfInputShared(MakeFakeExternal(1 << 20));
+ auto str = not_a_string_view(external->base, 1 << 20)
+ .remove_prefix(1 << 19)
+ .remove_suffix(6);
+ auto* child =
+ RefIfInputSharedIndirect(MakeSubstring(1 << 19, (1 << 19) - 6, external));
+ CordRepRing* result = NeedsUnref(CordRepRing::Create(child));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result->length, Eq(str.size()));
+ EXPECT_THAT(ToRawFlats(result), ElementsAre(str));
+ Unref(result);
+ UnrefIfInputShared(external);
+ UnrefIfInputSharedIndirect(child);
+}
+
+TEST_P(CordRingBuildInputTest, CreateFromConcat) {
+ CordRep* flats[] = {MakeFlat("abcdefgh"), MakeFlat("ijklm"),
+ MakeFlat("nopqrstuv"), MakeFlat("wxyz")};
+ auto* left = MakeConcat(RefIfInputSharedIndirect(flats[0]), flats[1]);
+ auto* right = MakeConcat(flats[2], RefIfInputSharedIndirect(flats[3]));
+ auto* concat = RefIfInputShared(MakeConcat(left, right));
+ CordRepRing* result = NeedsUnref(CordRepRing::Create(concat));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result->length, Eq(26));
+ EXPECT_THAT(ToString(result), Eq(kAlphabet));
+ UnrefIfInputSharedIndirect(flats[0]);
+ UnrefIfInputSharedIndirect(flats[3]);
+ UnrefIfInputShared(concat);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildInputTest, CreateFromSubstringConcat) {
+ for (size_t off = 0; off < 26; ++off) {
+ for (size_t len = 1; len < 26 - off; ++len) {
+ CordRep* flats[] = {MakeFlat("abcdefgh"), MakeFlat("ijklm"),
+ MakeFlat("nopqrstuv"), MakeFlat("wxyz")};
+ auto* left = MakeConcat(RefIfInputSharedIndirect(flats[0]), flats[1]);
+ auto* right = MakeConcat(flats[2], RefIfInputSharedIndirect(flats[3]));
+ auto* concat = MakeConcat(left, right);
+ auto* child = RefIfInputShared(MakeSubstring(off, len, concat));
+ CordRepRing* result = NeedsUnref(CordRepRing::Create(child));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ ASSERT_THAT(result->length, Eq(len));
+ ASSERT_THAT(ToString(result), string_view(kAlphabet).substr(off, len));
+ UnrefIfInputSharedIndirect(flats[0]);
+ UnrefIfInputSharedIndirect(flats[3]);
+ UnrefIfInputShared(child);
+ Unref(result);
+ }
+ }
+}
+
+TEST_P(CordRingCreateTest, Properties) {
+ absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
+ CordRepRing* result = NeedsUnref(CordRepRing::Create(MakeFlat(str1), 120));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result->head(), Eq(0));
+ EXPECT_THAT(result->tail(), Eq(1));
+ EXPECT_THAT(result->capacity(), Ge(120 + 1));
+ EXPECT_THAT(result->capacity(), Le(2 * 120 + 1));
+ EXPECT_THAT(result->entries(), Eq(1));
+ EXPECT_THAT(result->begin_pos(), Eq(0));
+ Unref(result);
+}
+
+TEST_P(CordRingCreateTest, EntryForNewFlat) {
+ absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
+ CordRep* child = MakeFlat(str1);
+ CordRepRing* result = NeedsUnref(CordRepRing::Create(child, 120));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result->entry_child(0), Eq(child));
+ EXPECT_THAT(result->entry_end_pos(0), Eq(str1.length()));
+ EXPECT_THAT(result->entry_data_offset(0), Eq(0));
+ Unref(result);
+}
+
+TEST_P(CordRingCreateTest, EntryForNewFlatSubstring) {
+ absl::string_view str1 = "1234567890abcdefghijklmnopqrstuvwxyz";
+ CordRep* child = MakeFlat(str1);
+ CordRep* substring = MakeSubstring(10, 26, child);
+ CordRepRing* result = NeedsUnref(CordRepRing::Create(substring, 1));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result->entry_child(0), Eq(child));
+ EXPECT_THAT(result->entry_end_pos(0), Eq(26));
+ EXPECT_THAT(result->entry_data_offset(0), Eq(10));
+ Unref(result);
+}
+
+TEST_P(CordRingBuildTest, AppendFlat) {
+ absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
+ absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1);
+ CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, MakeFlat(str2)));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
+ EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2));
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildTest, PrependFlat) {
+ absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
+ absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1);
+ CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, MakeFlat(str2)));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
+ EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1));
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildTest, AppendString) {
+ absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
+ absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1);
+ CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
+ EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2));
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildTest, AppendStringHavingExtra) {
+ absl::string_view str1 = "1234";
+ absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ CordRepRing* ring = CreateWithCapacity(MakeFlat(str1, 26), 0);
+ CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
+ EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildTest, AppendStringHavingPartialExtra) {
+ absl::string_view str1 = "1234";
+ absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ // Create flat with at least one extra byte. We don't expect to have sized
+ // alloc and capacity rounding to grant us enough to not make it partial.
+ auto* flat = MakeFlat(str1, 1);
+ size_t avail = flat->flat()->Capacity() - flat->length;
+ ASSERT_THAT(avail, Lt(str2.size())) << " adjust test for larger flats!";
+
+ // Construct the flats we do expect using all of `avail`.
+ absl::string_view str1a = str2.substr(0, avail);
+ absl::string_view str2a = str2.substr(avail);
+
+ CordRepRing* ring = CreateWithCapacity(flat, 1);
+ CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ if (GetParam().refcount_is_one) {
+ EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str1, str1a), str2a));
+ } else {
+ EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2));
+ }
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildTest, AppendStringHavingExtraInSubstring) {
+ absl::string_view str1 = "123456789_1234";
+ absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ CordRep* flat = RemovePrefix(10, MakeFlat(str1, 26));
+ CordRepRing* ring = CreateWithCapacity(flat, 0);
+ CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+ EXPECT_THAT(result->length, Eq(4 + str2.size()));
+ if (GetParam().refcount_is_one) {
+ EXPECT_THAT(ToFlats(result), ElementsAre(StrCat("1234", str2)));
+ } else {
+ EXPECT_THAT(ToFlats(result), ElementsAre("1234", str2));
+ }
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildTest, AppendStringHavingSharedExtra) {
+ absl::string_view str1 = "123456789_1234";
+ absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ for (int shared_type = 0; shared_type < 2; ++shared_type) {
+ SCOPED_TRACE(absl::StrCat("Shared extra type ", shared_type));
+
+ // Create a flat that is shared in some way.
+ CordRep* flat = nullptr;
+ CordRep* flat1 = nullptr;
+ if (shared_type == 0) {
+ // Shared flat
+ flat = CordRep::Ref(MakeFlat(str1.substr(10), 100));
+ } else if (shared_type == 1) {
+ // Shared flat inside private substring
+ flat1 = CordRep::Ref(MakeFlat(str1));
+ flat = RemovePrefix(10, flat1);
+ } else {
+ // Private flat inside shared substring
+ flat = CordRep::Ref(RemovePrefix(10, MakeFlat(str1, 100)));
+ }
+
+ CordRepRing* ring = CreateWithCapacity(flat, 1);
+ CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(result->length, Eq(4 + str2.size()));
+ EXPECT_THAT(ToFlats(result), ElementsAre("1234", str2));
+ UnrefIfShared(ring);
+ Unref(result);
+
+ CordRep::Unref(shared_type == 1 ? flat1 : flat);
+ }
+}
+
+TEST_P(CordRingBuildTest, AppendStringWithExtra) {
+ absl::string_view str1 = "1234";
+ absl::string_view str2 = "1234567890";
+ absl::string_view str3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1);
+ CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2, 26));
+ result = CordRepRing::Append(result, str3);
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result->length, Eq(str1.size() + str2.size() + str3.size()));
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result), ElementsAre(str1, StrCat(str2, str3)));
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildTest, PrependString) {
+ absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
+ absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ // Use external rep to avoid appending to first flat
+ CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1);
+ CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ if (GetParam().with_capacity && GetParam().refcount_is_one) {
+ EXPECT_THAT(result, Eq(ring));
+ } else {
+ EXPECT_THAT(result, Ne(ring));
+ }
+ EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
+ EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1));
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildTest, PrependStringHavingExtra) {
+ absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz1234";
+ absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ CordRep* flat = RemovePrefix(26, MakeFlat(str1));
+ CordRepRing* ring = CreateWithCapacity(flat, 0);
+ CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+ EXPECT_THAT(result->length, Eq(4 + str2.size()));
+ if (GetParam().refcount_is_one) {
+ EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str2, "1234")));
+ } else {
+ EXPECT_THAT(ToFlats(result), ElementsAre(str2, "1234"));
+ }
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildTest, PrependStringHavingSharedExtra) {
+ absl::string_view str1 = "123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ absl::string_view str2 = "abcdefghij";
+ absl::string_view str1a = str1.substr(10);
+ for (int shared_type = 1; shared_type < 2; ++shared_type) {
+ SCOPED_TRACE(absl::StrCat("Shared extra type ", shared_type));
+
+ // Create a flat that is shared in some way.
+ CordRep* flat = nullptr;
+ CordRep* flat1 = nullptr;
+ if (shared_type == 1) {
+ // Shared flat inside private substring
+ flat = RemovePrefix(10, flat1 = CordRep::Ref(MakeFlat(str1)));
+ } else {
+ // Private flat inside shared substring
+ flat = CordRep::Ref(RemovePrefix(10, MakeFlat(str1, 100)));
+ }
+
+ CordRepRing* ring = CreateWithCapacity(flat, 1);
+ CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result->length, Eq(str1a.size() + str2.size()));
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1a));
+ UnrefIfShared(ring);
+ Unref(result);
+ CordRep::Unref(shared_type == 1 ? flat1 : flat);
+ }
+}
+
+TEST_P(CordRingBuildTest, PrependStringWithExtra) {
+ absl::string_view str1 = "1234";
+ absl::string_view str2 = "1234567890";
+ absl::string_view str3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1);
+ CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2, 26));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ result = CordRepRing::Prepend(result, str3);
+ EXPECT_THAT(result->length, Eq(str1.size() + str2.size() + str3.size()));
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str3, str2), str1));
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildTest, AppendPrependStringMix) {
+ const auto& flats = kFoxFlats;
+ CordRepRing* ring = CreateWithCapacity(MakeFlat(flats[4]), 8);
+ CordRepRing* result = ring;
+ for (int i = 1; i <= 4; ++i) {
+ result = CordRepRing::Prepend(result, flats[4 - i]);
+ result = CordRepRing::Append(result, flats[4 + i]);
+ }
+ UnrefIfShared(ring);
+ NeedsUnref(result);
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToString(result), kFox);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildTest, AppendPrependStringMixWithExtra) {
+ const auto& flats = kFoxFlats;
+ CordRepRing* ring = CreateWithCapacity(MakeFlat(flats[4], 100), 8);
+ CordRepRing* result = ring;
+ for (int i = 1; i <= 4; ++i) {
+ result = CordRepRing::Prepend(result, flats[4 - i], 100);
+ result = CordRepRing::Append(result, flats[4 + i], 100);
+ }
+ NeedsUnref(result);
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ if (GetParam().refcount_is_one) {
+ EXPECT_THAT(ToFlats(result),
+ ElementsAre("The quick brown fox ", "jumps over the lazy dog"));
+ } else {
+ EXPECT_THAT(ToFlats(result), ElementsAre("The quick brown fox ", "jumps ",
+ "over the lazy dog"));
+ }
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildTest, AppendPrependStringMixWithPrependedExtra) {
+ const auto& flats = kFoxFlats;
+ CordRep* flat = MakeFlat(StrCat(std::string(50, '.'), flats[4]), 50);
+ CordRepRing* ring = CreateWithCapacity(RemovePrefix(50, flat), 0);
+ CordRepRing* result = ring;
+ for (int i = 1; i <= 4; ++i) {
+ result = CordRepRing::Prepend(result, flats[4 - i], 100);
+ result = CordRepRing::Append(result, flats[4 + i], 100);
+ }
+ result = NeedsUnref(result);
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+ if (GetParam().refcount_is_one) {
+ EXPECT_THAT(ToFlats(result), ElementsAre(kFox));
+ } else {
+ EXPECT_THAT(ToFlats(result), ElementsAre("The quick brown fox ", "jumps ",
+ "over the lazy dog"));
+ }
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingSubTest, SubRing) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto flats = MakeSpan(kFoxFlats);
+ string_view all = kFox;
+ for (size_t offset = 0; offset < all.size() - 1; ++offset) {
+ CordRepRing* ring = RefIfShared(FromFlats(flats, composition));
+ CordRepRing* result = CordRepRing::SubRing(ring, offset, 0);
+ EXPECT_THAT(result, nullptr);
+ UnrefIfShared(ring);
+
+ for (size_t len = 1; len < all.size() - offset; ++len) {
+ ring = RefIfShared(FromFlats(flats, composition));
+ result = NeedsUnref(CordRepRing::SubRing(ring, offset, len));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ ASSERT_THAT(result, EqIfPrivate(GetParam(), ring));
+ ASSERT_THAT(ToString(result), Eq(all.substr(offset, len)));
+ UnrefIfShared(ring);
+ Unref(result);
+ }
+ }
+}
+
+TEST_P(CordRingSubTest, SubRingFromLargeExternal) {
+ auto composition = RandomComposition();
+ std::string large_string(1 << 20, '.');
+ const char* flats[] = {
+ "abcdefghijklmnopqrstuvwxyz",
+ large_string.c_str(),
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ };
+ std::string buffer = absl::StrCat(flats[0], flats[1], flats[2]);
+ absl::string_view all = buffer;
+ for (size_t offset = 0; offset < 30; ++offset) {
+ CordRepRing* ring = RefIfShared(FromFlats(flats, composition));
+ CordRepRing* result = CordRepRing::SubRing(ring, offset, 0);
+ EXPECT_THAT(result, nullptr);
+ UnrefIfShared(ring);
+
+ for (size_t len = all.size() - 30; len < all.size() - offset; ++len) {
+ ring = RefIfShared(FromFlats(flats, composition));
+ result = NeedsUnref(CordRepRing::SubRing(ring, offset, len));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ ASSERT_THAT(result, EqIfPrivate(GetParam(), ring));
+ auto str = ToString(result);
+ ASSERT_THAT(str, SizeIs(len));
+ ASSERT_THAT(str, Eq(all.substr(offset, len)));
+ UnrefIfShared(ring);
+ Unref(result);
+ }
+ }
+}
+
+TEST_P(CordRingSubTest, RemovePrefix) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto flats = MakeSpan(kFoxFlats);
+ string_view all = kFox;
+ CordRepRing* ring = RefIfShared(FromFlats(flats, composition));
+ CordRepRing* result = CordRepRing::RemovePrefix(ring, all.size());
+ EXPECT_THAT(result, nullptr);
+ UnrefIfShared(ring);
+
+ for (size_t len = 1; len < all.size(); ++len) {
+ ring = RefIfShared(FromFlats(flats, composition));
+ result = NeedsUnref(CordRepRing::RemovePrefix(ring, len));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+ EXPECT_THAT(ToString(result), Eq(all.substr(len)));
+ UnrefIfShared(ring);
+ Unref(result);
+ }
+}
+
+TEST_P(CordRingSubTest, RemovePrefixFromLargeExternal) {
+ CordRepExternal* external1 = MakeFakeExternal(1 << 20);
+ CordRepExternal* external2 = MakeFakeExternal(1 << 20);
+ CordRepRing* ring = CordRepRing::Create(external1, 1);
+ ring = CordRepRing::Append(ring, external2);
+ CordRepRing* result = NeedsUnref(CordRepRing::RemovePrefix(ring, 1 << 16));
+ EXPECT_THAT(
+ ToRawFlats(result),
+ ElementsAre(
+ not_a_string_view(external1->base, 1 << 20).remove_prefix(1 << 16),
+ not_a_string_view(external2->base, 1 << 20)));
+ Unref(result);
+}
+
+TEST_P(CordRingSubTest, RemoveSuffix) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto flats = MakeSpan(kFoxFlats);
+ string_view all = kFox;
+ CordRepRing* ring = RefIfShared(FromFlats(flats, composition));
+ CordRepRing* result = CordRepRing::RemoveSuffix(ring, all.size());
+ EXPECT_THAT(result, nullptr);
+ UnrefIfShared(ring);
+
+ for (size_t len = 1; len < all.size(); ++len) {
+ ring = RefIfShared(FromFlats(flats, composition));
+ result = NeedsUnref(CordRepRing::RemoveSuffix(ring, len));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+ EXPECT_THAT(ToString(result), Eq(all.substr(0, all.size() - len)));
+ UnrefIfShared(ring);
+ Unref(result);
+ }
+}
+
+TEST_P(CordRingSubTest, AppendRing) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto flats = MakeSpan(kFoxFlats).subspan(1);
+ CordRepRing* ring = CreateWithCapacity(MakeFlat(kFoxFlats[0]), flats.size());
+ CordRepRing* child = FromFlats(flats, composition);
+ CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, child));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats));
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildInputTest, AppendRingWithFlatOffset) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto flats = MakeSpan(kFoxFlats);
+ CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size());
+ CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
+ CordRep* stripped = RemovePrefix(10, child);
+ CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result), ElementsAre("Head", "brown ", "fox ", "jumps ",
+ "over ", "the ", "lazy ", "dog"));
+ UnrefIfInputSharedIndirect(child);
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildInputTest, AppendRingWithBrokenOffset) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto flats = MakeSpan(kFoxFlats);
+ CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size());
+ CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
+ CordRep* stripped = RemovePrefix(21, child);
+ CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result),
+ ElementsAre("Head", "umps ", "over ", "the ", "lazy ", "dog"));
+ UnrefIfInputSharedIndirect(child);
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildInputTest, AppendRingWithFlatLength) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto flats = MakeSpan(kFoxFlats);
+ CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size());
+ CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
+ CordRep* stripped = RemoveSuffix(8, child);
+ CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result), ElementsAre("Head", "The ", "quick ", "brown ",
+ "fox ", "jumps ", "over ", "the "));
+ UnrefIfInputSharedIndirect(child);
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildTest, AppendRingWithBrokenFlatLength) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto flats = MakeSpan(kFoxFlats);
+ CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size());
+ CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
+ CordRep* stripped = RemoveSuffix(15, child);
+ CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result), ElementsAre("Head", "The ", "quick ", "brown ",
+ "fox ", "jumps ", "ov"));
+ UnrefIfInputSharedIndirect(child);
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildTest, AppendRingMiddlePiece) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto flats = MakeSpan(kFoxFlats);
+ CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size());
+ CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
+ CordRep* stripped = MakeSubstring(7, child->length - 27, child);
+ CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result),
+ ElementsAre("Head", "ck ", "brown ", "fox ", "jum"));
+ UnrefIfInputSharedIndirect(child);
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildTest, AppendRingSinglePiece) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto flats = MakeSpan(kFoxFlats);
+ CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size());
+ CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
+ CordRep* stripped = RefIfInputShared(MakeSubstring(11, 3, child));
+ CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result), ElementsAre("Head", "row"));
+ UnrefIfInputSharedIndirect(child);
+ UnrefIfInputShared(stripped);
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildInputTest, AppendRingSinglePieceWithPrefix) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto flats = MakeSpan(kFoxFlats);
+ size_t extra_capacity = 1 + (GetParam().with_capacity ? flats.size() : 0);
+ CordRepRing* ring = CordRepRing::Create(MakeFlat("Head"), extra_capacity);
+ ring->SetCapacityForTesting(1 + extra_capacity);
+ ring = RefIfShared(CordRepRing::Prepend(ring, MakeFlat("Prepend")));
+ assert(ring->IsValid(std::cout));
+ CordRepRing* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
+ CordRep* stripped = RefIfInputShared(MakeSubstring(11, 3, child));
+ CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result), ElementsAre("Prepend", "Head", "row"));
+ UnrefIfInputSharedIndirect(child);
+ UnrefIfInputShared(stripped);
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildInputTest, PrependRing) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto fox = MakeSpan(kFoxFlats);
+ auto flats = MakeSpan(fox).subspan(0, fox.size() - 1);
+ CordRepRing* ring = CreateWithCapacity(MakeFlat(fox.back()), flats.size());
+ CordRepRing* child = RefIfInputShared(FromFlats(flats, composition));
+ CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, child));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats));
+ UnrefIfInputShared(child);
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildInputTest, PrependRingWithFlatOffset) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto flats = MakeSpan(kFoxFlats);
+ CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size());
+ CordRep* child = RefIfInputShared(FromFlats(flats, composition));
+ CordRep* stripped = RefIfInputSharedIndirect(RemovePrefix(10, child));
+ CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result), ElementsAre("brown ", "fox ", "jumps ", "over ",
+ "the ", "lazy ", "dog", "Tail"));
+ UnrefIfInputShared(child);
+ UnrefIfInputSharedIndirect(stripped);
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildInputTest, PrependRingWithBrokenOffset) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto flats = MakeSpan(kFoxFlats);
+ CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size());
+ CordRep* child = RefIfInputShared(FromFlats(flats, composition));
+ CordRep* stripped = RefIfInputSharedIndirect(RemovePrefix(21, child));
+ CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result),
+ ElementsAre("umps ", "over ", "the ", "lazy ", "dog", "Tail"));
+ UnrefIfInputShared(child);
+ UnrefIfInputSharedIndirect(stripped);
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildInputTest, PrependRingWithFlatLength) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto flats = MakeSpan(kFoxFlats);
+ CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size());
+ CordRep* child = RefIfInputShared(FromFlats(flats, composition));
+ CordRep* stripped = RefIfInputSharedIndirect(RemoveSuffix(8, child));
+ CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result), ElementsAre("The ", "quick ", "brown ", "fox ",
+ "jumps ", "over ", "the ", "Tail"));
+ UnrefIfShared(ring);
+ UnrefIfInputShared(child);
+ UnrefIfInputSharedIndirect(stripped);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildInputTest, PrependRingWithBrokenFlatLength) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto flats = MakeSpan(kFoxFlats);
+ CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size());
+ CordRep* child = RefIfInputShared(FromFlats(flats, composition));
+ CordRep* stripped = RefIfInputSharedIndirect(RemoveSuffix(15, child));
+ CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result), ElementsAre("The ", "quick ", "brown ", "fox ",
+ "jumps ", "ov", "Tail"));
+ UnrefIfInputShared(child);
+ UnrefIfInputSharedIndirect(stripped);
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildInputTest, PrependRingMiddlePiece) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto flats = MakeSpan(kFoxFlats);
+ CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size());
+ CordRep* child = RefIfInputShared(FromFlats(flats, composition));
+ CordRep* stripped =
+ RefIfInputSharedIndirect(MakeSubstring(7, child->length - 27, child));
+ CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result),
+ ElementsAre("ck ", "brown ", "fox ", "jum", "Tail"));
+ UnrefIfInputShared(child);
+ UnrefIfInputSharedIndirect(stripped);
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildInputTest, PrependRingSinglePiece) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto flats = MakeSpan(kFoxFlats);
+ CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size());
+ CordRep* child = RefIfInputShared(FromFlats(flats, composition));
+ CordRep* stripped = RefIfInputSharedIndirect(MakeSubstring(11, 3, child));
+ CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result), ElementsAre("row", "Tail"));
+ UnrefIfInputShared(child);
+ UnrefIfInputSharedIndirect(stripped);
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_P(CordRingBuildInputTest, PrependRingSinglePieceWithPrefix) {
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ auto flats = MakeSpan(kFoxFlats);
+ size_t extra_capacity = 1 + (GetParam().with_capacity ? flats.size() : 0);
+ CordRepRing* ring = CordRepRing::Create(MakeFlat("Tail"), extra_capacity);
+ ring->SetCapacityForTesting(1 + extra_capacity);
+ ring = RefIfShared(CordRepRing::Prepend(ring, MakeFlat("Prepend")));
+ CordRep* child = RefIfInputShared(FromFlats(flats, composition));
+ CordRep* stripped = RefIfInputSharedIndirect(MakeSubstring(11, 3, child));
+ CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+ EXPECT_THAT(ToFlats(result), ElementsAre("row", "Prepend", "Tail"));
+ UnrefIfInputShared(child);
+ UnrefIfInputSharedIndirect(stripped);
+ UnrefIfShared(ring);
+ Unref(result);
+}
+
+TEST_F(CordRingTest, Find) {
+ constexpr const char* flats[] = {
+ "abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ",
+ "KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_",
+ "+-=", "[]\\{}|;':", ",/<>?", "."};
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ CordRepRing* ring = NeedsUnref(FromFlats(flats, composition));
+ std::string value = ToString(ring);
+ for (int i = 0; i < value.length(); ++i) {
+ CordRepRing::Position found = ring->Find(i);
+ auto data = ring->entry_data(found.index);
+ ASSERT_THAT(found.offset, Lt(data.length()));
+ ASSERT_THAT(data[found.offset], Eq(value[i]));
+ }
+ Unref(ring);
+}
+
+TEST_F(CordRingTest, FindWithHint) {
+ constexpr const char* flats[] = {
+ "abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ",
+ "KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_",
+ "+-=", "[]\\{}|;':", ",/<>?", "."};
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ CordRepRing* ring = NeedsUnref(FromFlats(flats, composition));
+ std::string value = ToString(ring);
+
+#if defined(GTEST_HAS_DEATH_TEST)
+ // Test hint beyond valid position
+ index_type head = ring->head();
+ EXPECT_DEBUG_DEATH(ring->Find(ring->advance(head), 0), ".*");
+ EXPECT_DEBUG_DEATH(ring->Find(ring->advance(head), 9), ".*");
+ EXPECT_DEBUG_DEATH(ring->Find(ring->advance(head, 3), 24), ".*");
+#endif
+
+ int flat_pos = 0;
+ size_t flat_offset = 0;
+ for (auto sflat : flats) {
+ string_view flat(sflat);
+ for (int offset = 0; offset < flat.length(); ++offset) {
+ for (int start = 0; start <= flat_pos; ++start) {
+ index_type hint = ring->advance(ring->head(), start);
+ CordRepRing::Position found = ring->Find(hint, flat_offset + offset);
+ ASSERT_THAT(found.index, Eq(ring->advance(ring->head(), flat_pos)));
+ ASSERT_THAT(found.offset, Eq(offset));
+ }
+ }
+ ++flat_pos;
+ flat_offset += flat.length();
+ }
+ Unref(ring);
+}
+
+TEST_F(CordRingTest, FindInLargeRing) {
+ constexpr const char* flats[] = {
+ "abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ",
+ "KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_",
+ "+-=", "[]\\{}|;':", ",/<>?", "."};
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ CordRepRing* ring = FromFlats(flats, composition);
+ for (int i = 0; i < 13; ++i) {
+ ring = CordRepRing::Append(ring, FromFlats(flats, composition));
+ }
+ NeedsUnref(ring);
+ std::string value = ToString(ring);
+ for (int i = 0; i < value.length(); ++i) {
+ CordRepRing::Position pos = ring->Find(i);
+ auto data = ring->entry_data(pos.index);
+ ASSERT_THAT(pos.offset, Lt(data.length()));
+ ASSERT_THAT(data[pos.offset], Eq(value[i]));
+ }
+ Unref(ring);
+}
+
+TEST_F(CordRingTest, FindTail) {
+ constexpr const char* flats[] = {
+ "abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ",
+ "KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_",
+ "+-=", "[]\\{}|;':", ",/<>?", "."};
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ CordRepRing* ring = NeedsUnref(FromFlats(flats, composition));
+ std::string value = ToString(ring);
+
+ for (int i = 0; i < value.length(); ++i) {
+ CordRepRing::Position pos = ring->FindTail(i + 1);
+ auto data = ring->entry_data(ring->retreat(pos.index));
+ ASSERT_THAT(pos.offset, Lt(data.length()));
+ ASSERT_THAT(data[data.length() - pos.offset - 1], Eq(value[i]));
+ }
+ Unref(ring);
+}
+
+TEST_F(CordRingTest, FindTailWithHint) {
+ constexpr const char* flats[] = {
+ "abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ",
+ "KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_",
+ "+-=", "[]\\{}|;':", ",/<>?", "."};
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ CordRepRing* ring = NeedsUnref(FromFlats(flats, composition));
+ std::string value = ToString(ring);
+
+ // Test hint beyond valid position
+#if defined(GTEST_HAS_DEATH_TEST)
+ index_type head = ring->head();
+ EXPECT_DEBUG_DEATH(ring->FindTail(ring->advance(head), 1), ".*");
+ EXPECT_DEBUG_DEATH(ring->FindTail(ring->advance(head), 10), ".*");
+ EXPECT_DEBUG_DEATH(ring->FindTail(ring->advance(head, 3), 26), ".*");
+#endif
+
+ for (int i = 0; i < value.length(); ++i) {
+ CordRepRing::Position pos = ring->FindTail(i + 1);
+ auto data = ring->entry_data(ring->retreat(pos.index));
+ ASSERT_THAT(pos.offset, Lt(data.length()));
+ ASSERT_THAT(data[data.length() - pos.offset - 1], Eq(value[i]));
+ }
+ Unref(ring);
+}
+
+TEST_F(CordRingTest, FindTailInLargeRing) {
+ constexpr const char* flats[] = {
+ "abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ",
+ "KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_",
+ "+-=", "[]\\{}|;':", ",/<>?", "."};
+ auto composition = RandomComposition();
+ SCOPED_TRACE(ToString(composition));
+ CordRepRing* ring = FromFlats(flats, composition);
+ for (int i = 0; i < 13; ++i) {
+ ring = CordRepRing::Append(ring, FromFlats(flats, composition));
+ }
+ NeedsUnref(ring);
+ std::string value = ToString(ring);
+ for (int i = 0; i < value.length(); ++i) {
+ CordRepRing::Position pos = ring->FindTail(i + 1);
+ auto data = ring->entry_data(ring->retreat(pos.index));
+ ASSERT_THAT(pos.offset, Lt(data.length()));
+ ASSERT_THAT(data[data.length() - pos.offset - 1], Eq(value[i]));
+ }
+ Unref(ring);
+}
+
+TEST_F(CordRingTest, GetCharacter) {
+ auto flats = MakeSpan(kFoxFlats);
+ CordRepRing* ring = CordRepRing::Create(MakeFlat("Tail"), flats.size());
+ CordRep* child = FromFlats(flats, kAppend);
+ CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, child));
+ std::string value = ToString(result);
+ for (int i = 0; i < value.length(); ++i) {
+ ASSERT_THAT(result->GetCharacter(i), Eq(value[i]));
+ }
+ Unref(result);
+}
+
+TEST_F(CordRingTest, GetCharacterWithSubstring) {
+ absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
+ auto* child = MakeSubstring(4, 20, MakeFlat(str1));
+ CordRepRing* result = NeedsUnref(CordRepRing::Create(child));
+ ASSERT_THAT(result, IsValidRingBuffer());
+ std::string value = ToString(result);
+ for (int i = 0; i < value.length(); ++i) {
+ ASSERT_THAT(result->GetCharacter(i), Eq(value[i]));
+ }
+ Unref(result);
+}
+
+TEST_F(CordRingTest, Dump) {
+ std::stringstream ss;
+ auto flats = MakeSpan(kFoxFlats);
+ CordRepRing* ring = NeedsUnref(FromFlats(flats, kPrepend));
+ ss << *ring;
+ Unref(ring);
+}
+
+} // namespace
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc
index 7942bfc..f998242 100644
--- a/absl/strings/cord_test.cc
+++ b/absl/strings/cord_test.cc
@@ -183,6 +183,10 @@
}
static bool IsTree(const Cord& c) { return c.contents_.is_tree(); }
+
+ static cord_internal::CordzInfo* GetCordzInfo(const Cord& c) {
+ return c.contents_.cordz_info();
+ }
};
ABSL_NAMESPACE_END
@@ -367,7 +371,7 @@
for (size_t end_pos : positions) {
if (end_pos < pos || end_pos > a.size()) continue;
absl::Cord sa = a.Subcord(pos, end_pos - pos);
- EXPECT_EQ(absl::string_view(s).substr(pos, end_pos - pos),
+ ASSERT_EQ(absl::string_view(s).substr(pos, end_pos - pos),
std::string(sa))
<< a;
}
@@ -379,7 +383,7 @@
for (size_t pos = 0; pos <= sh.size(); ++pos) {
for (size_t n = 0; n <= sh.size() - pos; ++n) {
absl::Cord sc = c.Subcord(pos, n);
- EXPECT_EQ(sh.substr(pos, n), std::string(sc)) << c;
+ ASSERT_EQ(sh.substr(pos, n), std::string(sc)) << c;
}
}
@@ -389,7 +393,7 @@
while (sa.size() > 1) {
sa = sa.Subcord(1, sa.size() - 2);
ss = ss.substr(1, ss.size() - 2);
- EXPECT_EQ(ss, std::string(sa)) << a;
+ ASSERT_EQ(ss, std::string(sa)) << a;
if (HasFailure()) break; // halt cascade
}
diff --git a/absl/strings/escaping.cc b/absl/strings/escaping.cc
index 9fceeef..18b20b8 100644
--- a/absl/strings/escaping.cc
+++ b/absl/strings/escaping.cc
@@ -137,7 +137,7 @@
// Copy the escape sequence for the null character
const ptrdiff_t octal_size = p + 1 - octal_start;
*d++ = '\\';
- memcpy(d, octal_start, octal_size);
+ memmove(d, octal_start, octal_size);
d += octal_size;
break;
}
@@ -170,7 +170,7 @@
// Copy the escape sequence for the null character
const ptrdiff_t hex_size = p + 1 - hex_start;
*d++ = '\\';
- memcpy(d, hex_start, hex_size);
+ memmove(d, hex_start, hex_size);
d += hex_size;
break;
}
@@ -203,7 +203,7 @@
if ((rune == 0) && leave_nulls_escaped) {
// Copy the escape sequence for the null character
*d++ = '\\';
- memcpy(d, hex_start, 5); // u0000
+ memmove(d, hex_start, 5); // u0000
d += 5;
break;
}
@@ -251,7 +251,7 @@
if ((rune == 0) && leave_nulls_escaped) {
// Copy the escape sequence for the null character
*d++ = '\\';
- memcpy(d, hex_start, 9); // U00000000
+ memmove(d, hex_start, 9); // U00000000
d += 9;
break;
}
diff --git a/absl/strings/internal/cord_internal.cc b/absl/strings/internal/cord_internal.cc
new file mode 100644
index 0000000..905ffd0
--- /dev/null
+++ b/absl/strings/internal/cord_internal.cc
@@ -0,0 +1,83 @@
+// Copyright 2020 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "absl/strings/internal/cord_internal.h"
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+
+#include "absl/container/inlined_vector.h"
+#include "absl/strings/internal/cord_rep_flat.h"
+#include "absl/strings/internal/cord_rep_ring.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+ABSL_CONST_INIT std::atomic<bool> cord_ring_buffer_enabled(
+ kCordEnableRingBufferDefault);
+ABSL_CONST_INIT std::atomic<bool> shallow_subcords_enabled(
+ kCordShallowSubcordsDefault);
+
+void CordRep::Destroy(CordRep* rep) {
+ assert(rep != nullptr);
+
+ absl::InlinedVector<CordRep*, Constants::kInlinedVectorSize> pending;
+ while (true) {
+ assert(!rep->refcount.IsImmortal());
+ if (rep->tag == CONCAT) {
+ CordRepConcat* rep_concat = rep->concat();
+ CordRep* right = rep_concat->right;
+ if (!right->refcount.Decrement()) {
+ pending.push_back(right);
+ }
+ CordRep* left = rep_concat->left;
+ delete rep_concat;
+ rep = nullptr;
+ if (!left->refcount.Decrement()) {
+ rep = left;
+ continue;
+ }
+ } else if (rep->tag == RING) {
+ CordRepRing::Destroy(rep->ring());
+ rep = nullptr;
+ } else if (rep->tag == EXTERNAL) {
+ CordRepExternal::Delete(rep);
+ rep = nullptr;
+ } else if (rep->tag == SUBSTRING) {
+ CordRepSubstring* rep_substring = rep->substring();
+ CordRep* child = rep_substring->child;
+ delete rep_substring;
+ rep = nullptr;
+ if (!child->refcount.Decrement()) {
+ rep = child;
+ continue;
+ }
+ } else {
+ CordRepFlat::Delete(rep);
+ rep = nullptr;
+ }
+
+ if (!pending.empty()) {
+ rep = pending.back();
+ pending.pop_back();
+ } else {
+ break;
+ }
+ }
+}
+
+} // namespace cord_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h
index aa91a69..a1ba67f 100644
--- a/absl/strings/internal/cord_internal.h
+++ b/absl/strings/internal/cord_internal.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Abseil Authors.
+// Copyright 2021 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -21,7 +21,10 @@
#include <cstdint>
#include <type_traits>
+#include "absl/base/config.h"
+#include "absl/base/internal/endian.h"
#include "absl/base/internal/invoke.h"
+#include "absl/base/optimization.h"
#include "absl/container/internal/compressed_tuple.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h"
@@ -30,6 +33,41 @@
ABSL_NAMESPACE_BEGIN
namespace cord_internal {
+class CordzInfo;
+
+// Default feature enable states for cord ring buffers
+enum CordFeatureDefaults {
+ kCordEnableRingBufferDefault = false,
+ kCordShallowSubcordsDefault = false
+};
+
+extern std::atomic<bool> cord_ring_buffer_enabled;
+extern std::atomic<bool> shallow_subcords_enabled;
+
+inline void enable_cord_ring_buffer(bool enable) {
+ cord_ring_buffer_enabled.store(enable, std::memory_order_relaxed);
+}
+
+inline void enable_shallow_subcords(bool enable) {
+ shallow_subcords_enabled.store(enable, std::memory_order_relaxed);
+}
+
+enum Constants {
+ // The inlined size to use with absl::InlinedVector.
+ //
+ // Note: The InlinedVectors in this file (and in cord.h) do not need to use
+ // the same value for their inlined size. The fact that they do is historical.
+ // It may be desirable for each to use a different inlined size optimized for
+ // that InlinedVector's usage.
+ //
+ // TODO(jgm): Benchmark to see if there's a more optimal value than 47 for
+ // the inlined vector size (47 exists for backward compatibility).
+ kInlinedVectorSize = 47,
+
+ // Prefer copying blocks of at most this size, otherwise reference count.
+ kMaxBytesToCopy = 511
+};
+
// Wraps std::atomic for reference counting.
class Refcount {
public:
@@ -108,24 +146,32 @@
// functions in the base class.
struct CordRepConcat;
-struct CordRepSubstring;
struct CordRepExternal;
+struct CordRepFlat;
+struct CordRepSubstring;
+class CordRepRing;
// Various representations that we allow
enum CordRepKind {
- CONCAT = 0,
- EXTERNAL = 1,
- SUBSTRING = 2,
+ CONCAT = 0,
+ EXTERNAL = 1,
+ SUBSTRING = 2,
+ RING = 3,
// We have different tags for different sized flat arrays,
- // starting with FLAT
- FLAT = 3,
+ // starting with FLAT, and limited to MAX_FLAT_TAG. The 224 value is based on
+ // the current 'size to tag' encoding of 8 / 32 bytes. If a new tag is needed
+ // in the future, then 'FLAT' and 'MAX_FLAT_TAG' should be adjusted as well
+ // as the Tag <---> Size logic so that FLAT stil represents the minimum flat
+ // allocation size. (32 bytes as of now).
+ FLAT = 4,
+ MAX_FLAT_TAG = 224
};
struct CordRep {
CordRep() = default;
constexpr CordRep(Refcount::Immortal immortal, size_t l)
- : length(l), refcount(immortal), tag(EXTERNAL), data{} {}
+ : length(l), refcount(immortal), tag(EXTERNAL), storage{} {}
// The following three fields have to be less than 32 bytes since
// that is the smallest supported flat node size.
@@ -134,22 +180,40 @@
// If tag < FLAT, it represents CordRepKind and indicates the type of node.
// Otherwise, the node type is CordRepFlat and the tag is the encoded size.
uint8_t tag;
- char data[1]; // Starting point for flat array: MUST BE LAST FIELD of CordRep
+ char storage[1]; // Starting point for flat array: MUST BE LAST FIELD
+ inline CordRepRing* ring();
+ inline const CordRepRing* ring() const;
inline CordRepConcat* concat();
inline const CordRepConcat* concat() const;
inline CordRepSubstring* substring();
inline const CordRepSubstring* substring() const;
inline CordRepExternal* external();
inline const CordRepExternal* external() const;
+ inline CordRepFlat* flat();
+ inline const CordRepFlat* flat() const;
+
+ // --------------------------------------------------------------------
+ // Memory management
+
+ // Destroys the provided `rep`.
+ static void Destroy(CordRep* rep);
+
+ // Increments the reference count of `rep`.
+ // Requires `rep` to be a non-null pointer value.
+ static inline CordRep* Ref(CordRep* rep);
+
+ // Decrements the reference count of `rep`. Destroys rep if count reaches
+ // zero. Requires `rep` to be a non-null pointer value.
+ static inline void Unref(CordRep* rep);
};
struct CordRepConcat : public CordRep {
CordRep* left;
CordRep* right;
- uint8_t depth() const { return static_cast<uint8_t>(data[0]); }
- void set_depth(uint8_t depth) { data[0] = static_cast<char>(depth); }
+ uint8_t depth() const { return static_cast<uint8_t>(storage[0]); }
+ void set_depth(uint8_t depth) { storage[0] = static_cast<char>(depth); }
};
struct CordRepSubstring : public CordRep {
@@ -174,6 +238,10 @@
const char* base;
// Pointer to function that knows how to call and destroy the releaser.
ExternalReleaserInvoker releaser_invoker;
+
+ // Deletes (releases) the external rep.
+ // Requires rep != nullptr and rep->tag == EXTERNAL
+ static void Delete(CordRep* rep);
};
struct Rank1 {};
@@ -214,6 +282,13 @@
}
};
+inline void CordRepExternal::Delete(CordRep* rep) {
+ assert(rep != nullptr && rep->tag == EXTERNAL);
+ auto* rep_external = static_cast<CordRepExternal*>(rep);
+ assert(rep_external->releaser_invoker != nullptr);
+ rep_external->releaser_invoker(rep_external);
+}
+
template <typename Str>
struct ConstInitExternalStorage {
ABSL_CONST_INIT static CordRepExternal value;
@@ -224,47 +299,245 @@
enum {
kMaxInline = 15,
- // Tag byte & kMaxInline means we are storing a pointer.
- kTreeFlag = 1 << 4,
- // Tag byte & kProfiledFlag means we are profiling the Cord.
- kProfiledFlag = 1 << 5
-};
-
-// If the data has length <= kMaxInline, we store it in `as_chars`, and
-// store the size in `tagged_size`.
-// Else we store it in a tree and store a pointer to that tree in
-// `as_tree.rep` and store a tag in `tagged_size`.
-struct AsTree {
- absl::cord_internal::CordRep* rep;
- char padding[kMaxInline + 1 - sizeof(absl::cord_internal::CordRep*) - 1];
- char tagged_size;
};
constexpr char GetOrNull(absl::string_view data, size_t pos) {
return pos < data.size() ? data[pos] : '\0';
}
-union InlineData {
- constexpr InlineData() : as_chars{} {}
- explicit constexpr InlineData(AsTree tree) : as_tree(tree) {}
- explicit constexpr InlineData(absl::string_view chars)
- : as_chars{GetOrNull(chars, 0), GetOrNull(chars, 1),
- GetOrNull(chars, 2), GetOrNull(chars, 3),
- GetOrNull(chars, 4), GetOrNull(chars, 5),
- GetOrNull(chars, 6), GetOrNull(chars, 7),
- GetOrNull(chars, 8), GetOrNull(chars, 9),
- GetOrNull(chars, 10), GetOrNull(chars, 11),
- GetOrNull(chars, 12), GetOrNull(chars, 13),
- GetOrNull(chars, 14), static_cast<char>(chars.size())} {}
+// We store cordz_info as 64 bit pointer value in big endian format. This
+// guarantees that the least significant byte of cordz_info matches the last
+// byte of the inline data representation in as_chars_, which holds the inlined
+// size or the 'is_tree' bit.
+using cordz_info_t = int64_t;
- AsTree as_tree;
- char as_chars[kMaxInline + 1];
+// Assert that the `cordz_info` pointer value perfectly overlaps the last half
+// of `as_chars_` and can hold a pointer value.
+static_assert(sizeof(cordz_info_t) * 2 == kMaxInline + 1, "");
+static_assert(sizeof(cordz_info_t) >= sizeof(intptr_t), "");
+
+// BigEndianByte() creates a big endian representation of 'value', i.e.: a big
+// endian value where the last byte in the host's representation holds 'value`,
+// with all other bytes being 0.
+static constexpr cordz_info_t BigEndianByte(unsigned char value) {
+#if defined(ABSL_IS_BIG_ENDIAN)
+ return value;
+#else
+ return static_cast<cordz_info_t>(value) << ((sizeof(cordz_info_t) - 1) * 8);
+#endif
+}
+
+class InlineData {
+ public:
+ // kNullCordzInfo holds the big endian representation of intptr_t(1)
+ // This is the 'null' / initial value of 'cordz_info'. The null value
+ // is specifically big endian 1 as with 64-bit pointers, the last
+ // byte of cordz_info overlaps with the last byte holding the tag.
+ static constexpr cordz_info_t kNullCordzInfo = BigEndianByte(1);
+
+ // kFakeCordzInfo holds a 'fake', non-null cordz-info value we use to
+ // emulate the previous 'kProfiled' tag logic in 'set_profiled' until
+ // cord code is changed to store cordz_info values in InlineData.
+ static constexpr cordz_info_t kFakeCordzInfo = BigEndianByte(9);
+
+ constexpr InlineData() : as_chars_{0} {}
+ explicit constexpr InlineData(CordRep* rep) : as_tree_(rep) {}
+ explicit constexpr InlineData(absl::string_view chars)
+ : as_chars_{
+ GetOrNull(chars, 0), GetOrNull(chars, 1),
+ GetOrNull(chars, 2), GetOrNull(chars, 3),
+ GetOrNull(chars, 4), GetOrNull(chars, 5),
+ GetOrNull(chars, 6), GetOrNull(chars, 7),
+ GetOrNull(chars, 8), GetOrNull(chars, 9),
+ GetOrNull(chars, 10), GetOrNull(chars, 11),
+ GetOrNull(chars, 12), GetOrNull(chars, 13),
+ GetOrNull(chars, 14), static_cast<char>((chars.size() << 1))} {}
+
+ // Returns true if the current instance is empty.
+ // The 'empty value' is an inlined data value of zero length.
+ bool is_empty() const { return tag() == 0; }
+
+ // Returns true if the current instance holds a tree value.
+ bool is_tree() const { return (tag() & 1) != 0; }
+
+ // Returns true if the current instance holds a cordz_info value.
+ // Requires the current instance to hold a tree value.
+ bool is_profiled() const {
+ assert(is_tree());
+ return as_tree_.cordz_info != kNullCordzInfo;
+ }
+
+ // Returns the cordz_info sampling instance for this instance, or nullptr
+ // if the current instance is not sampled and does not have CordzInfo data.
+ // Requires the current instance to hold a tree value.
+ CordzInfo* cordz_info() const {
+ assert(is_tree());
+ intptr_t info =
+ static_cast<intptr_t>(absl::big_endian::ToHost64(as_tree_.cordz_info));
+ assert(info & 1);
+ return reinterpret_cast<CordzInfo*>(info - 1);
+ }
+
+ // Sets the current cordz_info sampling instance for this instance, or nullptr
+ // if the current instance is not sampled and does not have CordzInfo data.
+ // Requires the current instance to hold a tree value.
+ void set_cordz_info(CordzInfo* cordz_info) {
+ assert(is_tree());
+ intptr_t info = reinterpret_cast<intptr_t>(cordz_info) | 1;
+ as_tree_.cordz_info = absl::big_endian::FromHost64(info);
+ }
+
+ // Resets the current cordz_info to null / empty.
+ void clear_cordz_info() {
+ assert(is_tree());
+ as_tree_.cordz_info = kNullCordzInfo;
+ }
+
+ // Returns a read only pointer to the character data inside this instance.
+ // Requires the current instance to hold inline data.
+ const char* as_chars() const {
+ assert(!is_tree());
+ return as_chars_;
+ }
+
+ // Returns a mutable pointer to the character data inside this instance.
+ // Should be used for 'write only' operations setting an inlined value.
+ // Applications can set the value of inlined data either before or after
+ // setting the inlined size, i.e., both of the below are valid:
+ //
+ // // Set inlined data and inline size
+ // memcpy(data_.as_chars(), data, size);
+ // data_.set_inline_size(size);
+ //
+ // // Set inlined size and inline data
+ // data_.set_inline_size(size);
+ // memcpy(data_.as_chars(), data, size);
+ //
+ // It's an error to read from the returned pointer without a preceding write
+ // if the current instance does not hold inline data, i.e.: is_tree() == true.
+ char* as_chars() { return as_chars_; }
+
+ // Returns the tree value of this value.
+ // Requires the current instance to hold a tree value.
+ CordRep* as_tree() const {
+ assert(is_tree());
+ return as_tree_.rep;
+ }
+
+ // Initialize this instance to holding the tree value `rep`,
+ // initializing the cordz_info to null, i.e.: 'not profiled'.
+ void make_tree(CordRep* rep) {
+ as_tree_.rep = rep;
+ as_tree_.cordz_info = kNullCordzInfo;
+ }
+
+ // Set the tree value of this instance to 'rep`.
+ // Requires the current instance to already hold a tree value.
+ // Does not affect the value of cordz_info.
+ void set_tree(CordRep* rep) {
+ assert(is_tree());
+ as_tree_.rep = rep;
+ }
+
+ // Returns the size of the inlined character data inside this instance.
+ // Requires the current instance to hold inline data.
+ size_t inline_size() const {
+ assert(!is_tree());
+ return tag() >> 1;
+ }
+
+ // Sets the size of the inlined character data inside this instance.
+ // Requires `size` to be <= kMaxInline.
+ // See the documentation on 'as_chars()' for more information and examples.
+ void set_inline_size(size_t size) {
+ ABSL_ASSERT(size <= kMaxInline);
+ tag() = static_cast<char>(size << 1);
+ }
+
+ // Sets or unsets the 'is_profiled' state of this instance.
+ // Requires the current instance to hold a tree value.
+ void set_profiled(bool profiled) {
+ assert(is_tree());
+ as_tree_.cordz_info = profiled ? kFakeCordzInfo : kNullCordzInfo;
+ }
+
+ private:
+ // See cordz_info_t for forced alignment and size of `cordz_info` details.
+ struct AsTree {
+ explicit constexpr AsTree(absl::cord_internal::CordRep* tree)
+ : rep(tree), cordz_info(kNullCordzInfo) {}
+ // This union uses up extra space so that whether rep is 32 or 64 bits,
+ // cordz_info will still start at the eighth byte, and the last
+ // byte of cordz_info will still be the last byte of InlineData.
+ union {
+ absl::cord_internal::CordRep* rep;
+ cordz_info_t unused_aligner;
+ };
+ cordz_info_t cordz_info;
+ };
+
+ char& tag() { return reinterpret_cast<char*>(this)[kMaxInline]; }
+ char tag() const { return reinterpret_cast<const char*>(this)[kMaxInline]; }
+
+ // If the data has length <= kMaxInline, we store it in `as_chars_`, and
+ // store the size in the last char of `as_chars_` shifted left + 1.
+ // Else we store it in a tree and store a pointer to that tree in
+ // `as_tree_.rep` and store a tag in `tagged_size`.
+ union {
+ char as_chars_[kMaxInline + 1];
+ AsTree as_tree_;
+ };
};
+
static_assert(sizeof(InlineData) == kMaxInline + 1, "");
-static_assert(sizeof(AsTree) == sizeof(InlineData), "");
-static_assert(offsetof(AsTree, tagged_size) == kMaxInline, "");
+
+inline CordRepConcat* CordRep::concat() {
+ assert(tag == CONCAT);
+ return static_cast<CordRepConcat*>(this);
+}
+
+inline const CordRepConcat* CordRep::concat() const {
+ assert(tag == CONCAT);
+ return static_cast<const CordRepConcat*>(this);
+}
+
+inline CordRepSubstring* CordRep::substring() {
+ assert(tag == SUBSTRING);
+ return static_cast<CordRepSubstring*>(this);
+}
+
+inline const CordRepSubstring* CordRep::substring() const {
+ assert(tag == SUBSTRING);
+ return static_cast<const CordRepSubstring*>(this);
+}
+
+inline CordRepExternal* CordRep::external() {
+ assert(tag == EXTERNAL);
+ return static_cast<CordRepExternal*>(this);
+}
+
+inline const CordRepExternal* CordRep::external() const {
+ assert(tag == EXTERNAL);
+ return static_cast<const CordRepExternal*>(this);
+}
+
+inline CordRep* CordRep::Ref(CordRep* rep) {
+ assert(rep != nullptr);
+ rep->refcount.Increment();
+ return rep;
+}
+
+inline void CordRep::Unref(CordRep* rep) {
+ assert(rep != nullptr);
+ // Expect refcount to be 0. Avoiding the cost of an atomic decrement should
+ // typically outweigh the cost of an extra branch checking for ref == 1.
+ if (ABSL_PREDICT_FALSE(!rep->refcount.DecrementExpectHighRefcount())) {
+ Destroy(rep);
+ }
+}
} // namespace cord_internal
+
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_
diff --git a/absl/strings/internal/cord_rep_flat.h b/absl/strings/internal/cord_rep_flat.h
new file mode 100644
index 0000000..a98aa9d
--- /dev/null
+++ b/absl/strings/internal/cord_rep_flat.h
@@ -0,0 +1,146 @@
+// Copyright 2020 The Abseil Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_FLAT_H_
+#define ABSL_STRINGS_INTERNAL_CORD_REP_FLAT_H_
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+
+#include "absl/strings/internal/cord_internal.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+// Note: all constants below are never ODR used and internal to cord, we define
+// these as static constexpr to avoid 'in struct' definition and usage clutter.
+
+// Largest and smallest flat node lengths we are willing to allocate
+// Flat allocation size is stored in tag, which currently can encode sizes up
+// to 4K, encoded as multiple of either 8 or 32 bytes.
+// If we allow for larger sizes, we need to change this to 8/64, 16/128, etc.
+// kMinFlatSize is bounded by tag needing to be at least FLAT * 8 bytes, and
+// ideally a 'nice' size aligning with allocation and cacheline sizes like 32.
+// kMaxFlatSize is bounded by the size resulting in a computed tag no greater
+// than MAX_FLAT_TAG. MAX_FLAT_TAG provides for additional 'high' tag values.
+static constexpr size_t kFlatOverhead = offsetof(CordRep, storage);
+static constexpr size_t kMinFlatSize = 32;
+static constexpr size_t kMaxFlatSize = 4096;
+static constexpr size_t kMaxFlatLength = kMaxFlatSize - kFlatOverhead;
+static constexpr size_t kMinFlatLength = kMinFlatSize - kFlatOverhead;
+
+constexpr uint8_t AllocatedSizeToTagUnchecked(size_t size) {
+ return static_cast<uint8_t>((size <= 1024) ? size / 8
+ : 128 + size / 32 - 1024 / 32);
+}
+
+static_assert(kMinFlatSize / 8 >= FLAT, "");
+static_assert(AllocatedSizeToTagUnchecked(kMaxFlatSize) <= MAX_FLAT_TAG, "");
+
+// Helper functions for rounded div, and rounding to exact sizes.
+constexpr size_t DivUp(size_t n, size_t m) { return (n + m - 1) / m; }
+constexpr size_t RoundUp(size_t n, size_t m) { return DivUp(n, m) * m; }
+
+// Returns the size to the nearest equal or larger value that can be
+// expressed exactly as a tag value.
+inline size_t RoundUpForTag(size_t size) {
+ return RoundUp(size, (size <= 1024) ? 8 : 32);
+}
+
+// Converts the allocated size to a tag, rounding down if the size
+// does not exactly match a 'tag expressible' size value. The result is
+// undefined if the size exceeds the maximum size that can be encoded in
+// a tag, i.e., if size is larger than TagToAllocatedSize(<max tag>).
+inline uint8_t AllocatedSizeToTag(size_t size) {
+ const uint8_t tag = AllocatedSizeToTagUnchecked(size);
+ assert(tag <= MAX_FLAT_TAG);
+ return tag;
+}
+
+// Converts the provided tag to the corresponding allocated size
+constexpr size_t TagToAllocatedSize(uint8_t tag) {
+ return (tag <= 128) ? (tag * 8) : (1024 + (tag - 128) * 32);
+}
+
+// Converts the provided tag to the corresponding available data length
+constexpr size_t TagToLength(uint8_t tag) {
+ return TagToAllocatedSize(tag) - kFlatOverhead;
+}
+
+// Enforce that kMaxFlatSize maps to a well-known exact tag value.
+static_assert(TagToAllocatedSize(224) == kMaxFlatSize, "Bad tag logic");
+
+struct CordRepFlat : public CordRep {
+ // Creates a new flat node.
+ static CordRepFlat* New(size_t len) {
+ if (len <= kMinFlatLength) {
+ len = kMinFlatLength;
+ } else if (len > kMaxFlatLength) {
+ len = kMaxFlatLength;
+ }
+
+ // Round size up so it matches a size we can exactly express in a tag.
+ const size_t size = RoundUpForTag(len + kFlatOverhead);
+ void* const raw_rep = ::operator new(size);
+ CordRepFlat* rep = new (raw_rep) CordRepFlat();
+ rep->tag = AllocatedSizeToTag(size);
+ return rep;
+ }
+
+ // Deletes a CordRepFlat instance created previously through a call to New().
+ // Flat CordReps are allocated and constructed with raw ::operator new and
+ // placement new, and must be destructed and deallocated accordingly.
+ static void Delete(CordRep*rep) {
+ assert(rep->tag >= FLAT && rep->tag <= MAX_FLAT_TAG);
+
+#if defined(__cpp_sized_deallocation)
+ size_t size = TagToAllocatedSize(rep->tag);
+ rep->~CordRep();
+ ::operator delete(rep, size);
+#else
+ rep->~CordRep();
+ ::operator delete(rep);
+#endif
+ }
+
+ // Returns a pointer to the data inside this flat rep.
+ char* Data() { return storage; }
+ const char* Data() const { return storage; }
+
+ // Returns the maximum capacity (payload size) of this instance.
+ size_t Capacity() const { return TagToLength(tag); }
+
+ // Returns the allocated size (payload + overhead) of this instance.
+ size_t AllocatedSize() const { return TagToAllocatedSize(tag); }
+};
+
+// Now that CordRepFlat is defined, we can define CordRep's helper casts:
+inline CordRepFlat* CordRep::flat() {
+ assert(tag >= FLAT && tag <= MAX_FLAT_TAG);
+ return reinterpret_cast<CordRepFlat*>(this);
+}
+
+inline const CordRepFlat* CordRep::flat() const {
+ assert(tag >= FLAT && tag <= MAX_FLAT_TAG);
+ return reinterpret_cast<const CordRepFlat*>(this);
+}
+
+} // namespace cord_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_STRINGS_INTERNAL_CORD_REP_FLAT_H_
diff --git a/absl/strings/internal/cord_rep_ring.cc b/absl/strings/internal/cord_rep_ring.cc
new file mode 100644
index 0000000..4d31d1d
--- /dev/null
+++ b/absl/strings/internal/cord_rep_ring.cc
@@ -0,0 +1,897 @@
+// Copyright 2020 The Abseil Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "absl/strings/internal/cord_rep_ring.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <iostream>
+#include <limits>
+#include <memory>
+#include <string>
+
+#include "absl/base/internal/raw_logging.h"
+#include "absl/base/internal/throw_delegate.h"
+#include "absl/base/macros.h"
+#include "absl/container/inlined_vector.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_flat.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+// See https://bugs.llvm.org/show_bug.cgi?id=48477
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wshadow"
+#if __has_warning("-Wshadow-field")
+#pragma clang diagnostic ignored "-Wshadow-field"
+#endif
+#endif
+
+namespace {
+
+using index_type = CordRepRing::index_type;
+
+enum class Direction { kForward, kReversed };
+
+inline bool IsFlatOrExternal(CordRep* rep) {
+ return rep->tag >= FLAT || rep->tag == EXTERNAL;
+}
+
+// Verifies that n + extra <= kMaxCapacity: throws std::length_error otherwise.
+inline void CheckCapacity(size_t n, size_t extra) {
+ if (ABSL_PREDICT_FALSE(extra > CordRepRing::kMaxCapacity - n)) {
+ base_internal::ThrowStdLengthError("Maximum capacity exceeded");
+ }
+}
+
+// Removes a reference from `rep` only.
+// Asserts that the refcount after decrement is not zero.
+inline bool UnrefNeverOne(CordRep* rep) {
+ bool result = rep->refcount.Decrement();
+ assert(result);
+ return result;
+}
+
+// Creates a flat from the provided string data, allocating up to `extra`
+// capacity in the returned flat depending on kMaxFlatLength limitations.
+// Requires `len` to be less or equal to `kMaxFlatLength`
+CordRepFlat* CreateFlat(const char* s, size_t n, size_t extra = 0) { // NOLINT
+ assert(n <= kMaxFlatLength);
+ auto* rep = CordRepFlat::New(n + extra);
+ rep->length = n;
+ memcpy(rep->Data(), s, n);
+ return rep;
+}
+
+// Unrefs the provided `substring`, and returns `substring->child`
+// Adds or assumes a reference on `substring->child`
+CordRep* ClipSubstring(CordRepSubstring* substring) {
+ CordRep* child = substring->child;
+ if (substring->refcount.IsOne()) {
+ delete substring;
+ } else {
+ CordRep::Ref(child);
+ if (ABSL_PREDICT_FALSE(!substring->refcount.Decrement())) {
+ UnrefNeverOne(child);
+ delete substring;
+ }
+ }
+ return child;
+}
+
+// Unrefs the provided `concat`, and returns `{concat->left, concat->right}`
+// Adds or assumes a reference on `concat->left` and `concat->right`.
+std::pair<CordRep*, CordRep*> ClipConcat(CordRepConcat* concat) {
+ auto result = std::make_pair(concat->left, concat->right);
+ if (concat->refcount.IsOne()) {
+ delete concat;
+ } else {
+ CordRep::Ref(result.first);
+ CordRep::Ref(result.second);
+ if (ABSL_PREDICT_FALSE(!concat->refcount.Decrement())) {
+ UnrefNeverOne(result.first);
+ UnrefNeverOne(result.second);
+ delete concat;
+ }
+ }
+ return result;
+}
+
+// Unrefs the entries in `[head, tail)`.
+// Requires all entries to be a FLAT or EXTERNAL node.
+void UnrefEntries(const CordRepRing* rep, index_type head, index_type tail) {
+ rep->ForEach(head, tail, [rep](index_type ix) {
+ CordRep* child = rep->entry_child(ix);
+ if (!child->refcount.Decrement()) {
+ if (child->tag >= FLAT) {
+ CordRepFlat::Delete(child->flat());
+ } else {
+ CordRepExternal::Delete(child->external());
+ }
+ }
+ });
+}
+
+template <typename F>
+void Consume(Direction direction, CordRep* rep, F&& fn) {
+ size_t offset = 0;
+ size_t length = rep->length;
+ struct Entry {
+ CordRep* rep;
+ size_t offset;
+ size_t length;
+ };
+ absl::InlinedVector<Entry, 40> stack;
+
+ for (;;) {
+ if (rep->tag >= FLAT || rep->tag == EXTERNAL || rep->tag == RING) {
+ fn(rep, offset, length);
+ if (stack.empty()) return;
+
+ rep = stack.back().rep;
+ offset = stack.back().offset;
+ length = stack.back().length;
+ stack.pop_back();
+ } else if (rep->tag == SUBSTRING) {
+ offset += rep->substring()->start;
+ rep = ClipSubstring(rep->substring());
+ } else if (rep->tag == CONCAT) {
+ auto res = ClipConcat(rep->concat());
+ CordRep* left = res.first;
+ CordRep* right = res.second;
+
+ if (left->length <= offset) {
+ // Don't need left node
+ offset -= left->length;
+ CordRep::Unref(left);
+ rep = right;
+ continue;
+ }
+
+ size_t length_left = left->length - offset;
+ if (length_left >= length) {
+ // Don't need right node
+ CordRep::Unref(right);
+ rep = left;
+ continue;
+ }
+
+ // Need both nodes
+ size_t length_right = length - length_left;
+ if (direction == Direction::kReversed) {
+ stack.push_back({left, offset, length_left});
+ rep = right;
+ offset = 0;
+ length = length_right;
+ } else {
+ stack.push_back({right, 0, length_right});
+ rep = left;
+ length = length_left;
+ }
+ } else {
+ assert("Valid tag" == nullptr);
+ return;
+ }
+ }
+}
+
+template <typename F>
+void Consume(CordRep* rep, F&& fn) {
+ return Consume(Direction::kForward, rep, std::forward<F>(fn));
+}
+
+template <typename F>
+void RConsume(CordRep* rep, F&& fn) {
+ return Consume(Direction::kReversed, rep, std::forward<F>(fn));
+}
+
+} // namespace
+
+std::ostream& operator<<(std::ostream& s, const CordRepRing& rep) {
+ // Note: 'pos' values are defined as size_t (for overflow reasons), but that
+ // prints really awkward for small prepended values such as -5. ssize_t is not
+ // portable (POSIX), so we use ptrdiff_t instead to cast to signed values.
+ s << " CordRepRing(" << &rep << ", length = " << rep.length
+ << ", head = " << rep.head_ << ", tail = " << rep.tail_
+ << ", cap = " << rep.capacity_ << ", rc = " << rep.refcount.Get()
+ << ", begin_pos_ = " << static_cast<ptrdiff_t>(rep.begin_pos_) << ") {\n";
+ CordRepRing::index_type head = rep.head();
+ do {
+ CordRep* child = rep.entry_child(head);
+ s << " entry[" << head << "] length = " << rep.entry_length(head)
+ << ", child " << child << ", clen = " << child->length
+ << ", tag = " << static_cast<int>(child->tag)
+ << ", rc = " << child->refcount.Get()
+ << ", offset = " << rep.entry_data_offset(head)
+ << ", end_pos = " << static_cast<ptrdiff_t>(rep.entry_end_pos(head))
+ << "\n";
+ head = rep.advance(head);
+ } while (head != rep.tail());
+ return s << "}\n";
+}
+
+void CordRepRing::AddDataOffset(index_type index, size_t n) {
+ entry_data_offset()[index] += static_cast<offset_type>(n);
+}
+
+void CordRepRing::SubLength(index_type index, size_t n) {
+ entry_end_pos()[index] -= n;
+}
+
+class CordRepRing::Filler {
+ public:
+ Filler(CordRepRing* rep, index_type pos) : rep_(rep), head_(pos), pos_(pos) {}
+
+ index_type head() const { return head_; }
+ index_type pos() const { return pos_; }
+
+ void Add(CordRep* child, size_t offset, pos_type end_pos) {
+ rep_->entry_end_pos()[pos_] = end_pos;
+ rep_->entry_child()[pos_] = child;
+ rep_->entry_data_offset()[pos_] = static_cast<offset_type>(offset);
+ pos_ = rep_->advance(pos_);
+ }
+
+ private:
+ CordRepRing* rep_;
+ index_type head_;
+ index_type pos_;
+};
+
+constexpr size_t CordRepRing::kMaxCapacity; // NOLINT: needed for c++11
+
+bool CordRepRing::IsValid(std::ostream& output) const {
+ if (capacity_ == 0) {
+ output << "capacity == 0";
+ return false;
+ }
+
+ if (head_ >= capacity_ || tail_ >= capacity_) {
+ output << "head " << head_ << " and/or tail " << tail_ << "exceed capacity "
+ << capacity_;
+ return false;
+ }
+
+ const index_type back = retreat(tail_);
+ size_t pos_length = Distance(begin_pos_, entry_end_pos(back));
+ if (pos_length != length) {
+ output << "length " << length << " does not match positional length "
+ << pos_length << " from begin_pos " << begin_pos_ << " and entry["
+ << back << "].end_pos " << entry_end_pos(back);
+ return false;
+ }
+
+ index_type head = head_;
+ pos_type begin_pos = begin_pos_;
+ do {
+ pos_type end_pos = entry_end_pos(head);
+ size_t entry_length = Distance(begin_pos, end_pos);
+ if (entry_length == 0) {
+ output << "entry[" << head << "] has an invalid length " << entry_length
+ << " from begin_pos " << begin_pos << " and end_pos " << end_pos;
+ return false;
+ }
+
+ CordRep* child = entry_child(head);
+ if (child == nullptr) {
+ output << "entry[" << head << "].child == nullptr";
+ return false;
+ }
+ if (child->tag < FLAT && child->tag != EXTERNAL) {
+ output << "entry[" << head << "].child has an invalid tag "
+ << static_cast<int>(child->tag);
+ return false;
+ }
+
+ size_t offset = entry_data_offset(head);
+ if (offset >= child->length || entry_length > child->length - offset) {
+ output << "entry[" << head << "] has offset " << offset
+ << " and entry length " << entry_length
+ << " which are outside of the childs length of " << child->length;
+ return false;
+ }
+
+ begin_pos = end_pos;
+ head = advance(head);
+ } while (head != tail_);
+
+ return true;
+}
+
+#ifdef EXTRA_CORD_RING_VALIDATION
+CordRepRing* CordRepRing::Validate(CordRepRing* rep, const char* file,
+ int line) {
+ if (!rep->IsValid(std::cerr)) {
+ std::cerr << "\nERROR: CordRepRing corrupted";
+ if (line) std::cerr << " at line " << line;
+ if (file) std::cerr << " in file " << file;
+ std::cerr << "\nContent = " << *rep;
+ abort();
+ }
+ return rep;
+}
+#endif // EXTRA_CORD_RING_VALIDATION
+
+CordRepRing* CordRepRing::New(size_t capacity, size_t extra) {
+ CheckCapacity(capacity, extra);
+
+ size_t size = AllocSize(capacity += extra);
+ void* mem = ::operator new(size);
+ auto* rep = new (mem) CordRepRing(static_cast<index_type>(capacity));
+ rep->tag = RING;
+ rep->capacity_ = static_cast<index_type>(capacity);
+ rep->begin_pos_ = 0;
+ return rep;
+}
+
+void CordRepRing::SetCapacityForTesting(size_t capacity) {
+ // Adjust for the changed layout
+ assert(capacity <= capacity_);
+ assert(head() == 0 || head() < tail());
+ memmove(Layout::Partial(capacity).Pointer<1>(data_) + head(),
+ Layout::Partial(capacity_).Pointer<1>(data_) + head(),
+ entries() * sizeof(Layout::ElementType<1>));
+ memmove(Layout::Partial(capacity, capacity).Pointer<2>(data_) + head(),
+ Layout::Partial(capacity_, capacity_).Pointer<2>(data_) + head(),
+ entries() * sizeof(Layout::ElementType<2>));
+ capacity_ = static_cast<index_type>(capacity);
+}
+
+void CordRepRing::Delete(CordRepRing* rep) {
+ assert(rep != nullptr && rep->tag == RING);
+#if defined(__cpp_sized_deallocation)
+ size_t size = AllocSize(rep->capacity_);
+ rep->~CordRepRing();
+ ::operator delete(rep, size);
+#else
+ rep->~CordRepRing();
+ ::operator delete(rep);
+#endif
+}
+
+void CordRepRing::Destroy(CordRepRing* rep) {
+ UnrefEntries(rep, rep->head(), rep->tail());
+ Delete(rep);
+}
+
+template <bool ref>
+void CordRepRing::Fill(const CordRepRing* src, index_type head,
+ index_type tail) {
+ this->length = src->length;
+ head_ = 0;
+ tail_ = advance(0, src->entries(head, tail));
+ begin_pos_ = src->begin_pos_;
+
+ // TODO(mvels): there may be opportunities here for large buffers.
+ auto* dst_pos = entry_end_pos();
+ auto* dst_child = entry_child();
+ auto* dst_offset = entry_data_offset();
+ src->ForEach(head, tail, [&](index_type index) {
+ *dst_pos++ = src->entry_end_pos(index);
+ CordRep* child = src->entry_child(index);
+ *dst_child++ = ref ? CordRep::Ref(child) : child;
+ *dst_offset++ = src->entry_data_offset(index);
+ });
+}
+
+CordRepRing* CordRepRing::Copy(CordRepRing* rep, index_type head,
+ index_type tail, size_t extra) {
+ CordRepRing* newrep = CordRepRing::New(rep->entries(head, tail), extra);
+ newrep->Fill<true>(rep, head, tail);
+ CordRep::Unref(rep);
+ return newrep;
+}
+
+CordRepRing* CordRepRing::Mutable(CordRepRing* rep, size_t extra) {
+ // Get current number of entries, and check for max capacity.
+ size_t entries = rep->entries();
+
+ size_t min_extra = (std::max)(extra, rep->capacity() * 2 - entries);
+ if (!rep->refcount.IsOne()) {
+ return Copy(rep, rep->head(), rep->tail(), min_extra);
+ } else if (entries + extra > rep->capacity()) {
+ CordRepRing* newrep = CordRepRing::New(entries, min_extra);
+ newrep->Fill<false>(rep, rep->head(), rep->tail());
+ CordRepRing::Delete(rep);
+ return newrep;
+ } else {
+ return rep;
+ }
+}
+
+Span<char> CordRepRing::GetAppendBuffer(size_t size) {
+ assert(refcount.IsOne());
+ index_type back = retreat(tail_);
+ CordRep* child = entry_child(back);
+ if (child->tag >= FLAT && child->refcount.IsOne()) {
+ size_t capacity = child->flat()->Capacity();
+ pos_type end_pos = entry_end_pos(back);
+ size_t data_offset = entry_data_offset(back);
+ size_t entry_length = Distance(entry_begin_pos(back), end_pos);
+ size_t used = data_offset + entry_length;
+ if (size_t n = (std::min)(capacity - used, size)) {
+ child->length = data_offset + entry_length + n;
+ entry_end_pos()[back] = end_pos + n;
+ this->length += n;
+ return {child->flat()->Data() + used, n};
+ }
+ }
+ return {nullptr, 0};
+}
+
+Span<char> CordRepRing::GetPrependBuffer(size_t size) {
+ assert(refcount.IsOne());
+ CordRep* child = entry_child(head_);
+ size_t data_offset = entry_data_offset(head_);
+ if (data_offset && child->refcount.IsOne() && child->tag >= FLAT) {
+ size_t n = (std::min)(data_offset, size);
+ this->length += n;
+ begin_pos_ -= n;
+ data_offset -= n;
+ entry_data_offset()[head_] = static_cast<offset_type>(data_offset);
+ return {child->flat()->Data() + data_offset, n};
+ }
+ return {nullptr, 0};
+}
+
+CordRepRing* CordRepRing::CreateFromLeaf(CordRep* child, size_t offset,
+ size_t length, size_t extra) {
+ CordRepRing* rep = CordRepRing::New(1, extra);
+ rep->head_ = 0;
+ rep->tail_ = rep->advance(0);
+ rep->length = length;
+ rep->entry_end_pos()[0] = length;
+ rep->entry_child()[0] = child;
+ rep->entry_data_offset()[0] = static_cast<offset_type>(offset);
+ return Validate(rep);
+}
+
+CordRepRing* CordRepRing::CreateSlow(CordRep* child, size_t extra) {
+ CordRepRing* rep = nullptr;
+ Consume(child, [&](CordRep* child, size_t offset, size_t length) {
+ if (IsFlatOrExternal(child)) {
+ rep = rep ? AppendLeaf(rep, child, offset, length)
+ : CreateFromLeaf(child, offset, length, extra);
+ } else if (rep) {
+ rep = AddRing<AddMode::kAppend>(rep, child->ring(), offset, length);
+ } else if (offset == 0 && child->length == length) {
+ rep = Mutable(child->ring(), extra);
+ } else {
+ rep = SubRing(child->ring(), offset, length, extra);
+ }
+ });
+ return Validate(rep, nullptr, __LINE__);
+}
+
+CordRepRing* CordRepRing::Create(CordRep* child, size_t extra) {
+ size_t length = child->length;
+ if (IsFlatOrExternal(child)) {
+ return CreateFromLeaf(child, 0, length, extra);
+ }
+ if (child->tag == RING) {
+ return Mutable(child->ring(), extra);
+ }
+ return CreateSlow(child, extra);
+}
+
+template <CordRepRing::AddMode mode>
+CordRepRing* CordRepRing::AddRing(CordRepRing* rep, CordRepRing* ring,
+ size_t offset, size_t length) {
+ assert(offset < ring->length);
+ constexpr bool append = mode == AddMode::kAppend;
+ Position head = ring->Find(offset);
+ Position tail = ring->FindTail(head.index, offset + length);
+ const index_type entries = ring->entries(head.index, tail.index);
+
+ rep = Mutable(rep, entries);
+
+ // The delta for making ring[head].end_pos into 'len - offset'
+ const pos_type delta_length =
+ (append ? rep->begin_pos_ + rep->length : rep->begin_pos_ - length) -
+ ring->entry_begin_pos(head.index) - head.offset;
+
+ // Start filling at `tail`, or `entries` before `head`
+ Filler filler(rep, append ? rep->tail_ : rep->retreat(rep->head_, entries));
+
+ if (ring->refcount.IsOne()) {
+ // Copy entries from source stealing the ref and adjusting the end position.
+ // Commit the filler as this is no-op.
+ ring->ForEach(head.index, tail.index, [&](index_type ix) {
+ filler.Add(ring->entry_child(ix), ring->entry_data_offset(ix),
+ ring->entry_end_pos(ix) + delta_length);
+ });
+
+ // Unref entries we did not copy over, and delete source.
+ if (head.index != ring->head_) UnrefEntries(ring, ring->head_, head.index);
+ if (tail.index != ring->tail_) UnrefEntries(ring, tail.index, ring->tail_);
+ CordRepRing::Delete(ring);
+ } else {
+ ring->ForEach(head.index, tail.index, [&](index_type ix) {
+ CordRep* child = ring->entry_child(ix);
+ filler.Add(child, ring->entry_data_offset(ix),
+ ring->entry_end_pos(ix) + delta_length);
+ CordRep::Ref(child);
+ });
+ CordRepRing::Unref(ring);
+ }
+
+ if (head.offset) {
+ // Increase offset of first 'source' entry appended or prepended.
+ // This is always the entry in `filler.head()`
+ rep->AddDataOffset(filler.head(), head.offset);
+ }
+
+ if (tail.offset) {
+ // Reduce length of last 'source' entry appended or prepended.
+ // This is always the entry tailed by `filler.pos()`
+ rep->SubLength(rep->retreat(filler.pos()), tail.offset);
+ }
+
+ // Commit changes
+ rep->length += length;
+ if (append) {
+ rep->tail_ = filler.pos();
+ } else {
+ rep->head_ = filler.head();
+ rep->begin_pos_ -= length;
+ }
+
+ return Validate(rep);
+}
+
+CordRepRing* CordRepRing::AppendSlow(CordRepRing* rep, CordRep* child) {
+ Consume(child, [&rep](CordRep* child, size_t offset, size_t length) {
+ if (child->tag == RING) {
+ rep = AddRing<AddMode::kAppend>(rep, child->ring(), offset, length);
+ } else {
+ rep = AppendLeaf(rep, child, offset, length);
+ }
+ });
+ return rep;
+}
+
+CordRepRing* CordRepRing::AppendLeaf(CordRepRing* rep, CordRep* child,
+ size_t offset, size_t length) {
+ rep = Mutable(rep, 1);
+ index_type back = rep->tail_;
+ const pos_type begin_pos = rep->begin_pos_ + rep->length;
+ rep->tail_ = rep->advance(rep->tail_);
+ rep->length += length;
+ rep->entry_end_pos()[back] = begin_pos + length;
+ rep->entry_child()[back] = child;
+ rep->entry_data_offset()[back] = static_cast<offset_type>(offset);
+ return Validate(rep, nullptr, __LINE__);
+}
+
+CordRepRing* CordRepRing::Append(CordRepRing* rep, CordRep* child) {
+ size_t length = child->length;
+ if (IsFlatOrExternal(child)) {
+ return AppendLeaf(rep, child, 0, length);
+ }
+ if (child->tag == RING) {
+ return AddRing<AddMode::kAppend>(rep, child->ring(), 0, length);
+ }
+ return AppendSlow(rep, child);
+}
+
+CordRepRing* CordRepRing::PrependSlow(CordRepRing* rep, CordRep* child) {
+ RConsume(child, [&](CordRep* child, size_t offset, size_t length) {
+ if (IsFlatOrExternal(child)) {
+ rep = PrependLeaf(rep, child, offset, length);
+ } else {
+ rep = AddRing<AddMode::kPrepend>(rep, child->ring(), offset, length);
+ }
+ });
+ return Validate(rep);
+}
+
+CordRepRing* CordRepRing::PrependLeaf(CordRepRing* rep, CordRep* child,
+ size_t offset, size_t length) {
+ rep = Mutable(rep, 1);
+ index_type head = rep->retreat(rep->head_);
+ pos_type end_pos = rep->begin_pos_;
+ rep->head_ = head;
+ rep->length += length;
+ rep->begin_pos_ -= length;
+ rep->entry_end_pos()[head] = end_pos;
+ rep->entry_child()[head] = child;
+ rep->entry_data_offset()[head] = static_cast<offset_type>(offset);
+ return Validate(rep);
+}
+
+CordRepRing* CordRepRing::Prepend(CordRepRing* rep, CordRep* child) {
+ size_t length = child->length;
+ if (IsFlatOrExternal(child)) {
+ return PrependLeaf(rep, child, 0, length);
+ }
+ if (child->tag == RING) {
+ return AddRing<AddMode::kPrepend>(rep, child->ring(), 0, length);
+ }
+ return PrependSlow(rep, child);
+}
+
+CordRepRing* CordRepRing::Append(CordRepRing* rep, absl::string_view data,
+ size_t extra) {
+ if (rep->refcount.IsOne()) {
+ Span<char> avail = rep->GetAppendBuffer(data.length());
+ if (!avail.empty()) {
+ memcpy(avail.data(), data.data(), avail.length());
+ data.remove_prefix(avail.length());
+ }
+ }
+ if (data.empty()) return Validate(rep);
+
+ const size_t flats = (data.length() - 1) / kMaxFlatLength + 1;
+ rep = Mutable(rep, flats);
+
+ Filler filler(rep, rep->tail_);
+ pos_type pos = rep->begin_pos_ + rep->length;
+
+ while (data.length() >= kMaxFlatLength) {
+ auto* flat = CreateFlat(data.data(), kMaxFlatLength);
+ filler.Add(flat, 0, pos += kMaxFlatLength);
+ data.remove_prefix(kMaxFlatLength);
+ }
+
+ if (data.length()) {
+ auto* flat = CreateFlat(data.data(), data.length(), extra);
+ filler.Add(flat, 0, pos += data.length());
+ }
+
+ rep->length = pos - rep->begin_pos_;
+ rep->tail_ = filler.pos();
+
+ return Validate(rep);
+}
+
+CordRepRing* CordRepRing::Prepend(CordRepRing* rep, absl::string_view data,
+ size_t extra) {
+ if (rep->refcount.IsOne()) {
+ Span<char> avail = rep->GetPrependBuffer(data.length());
+ if (!avail.empty()) {
+ const char* tail = data.data() + data.length() - avail.length();
+ memcpy(avail.data(), tail, avail.length());
+ data.remove_suffix(avail.length());
+ }
+ }
+ if (data.empty()) return rep;
+
+ const size_t flats = (data.length() - 1) / kMaxFlatLength + 1;
+ rep = Mutable(rep, flats);
+ pos_type pos = rep->begin_pos_;
+ Filler filler(rep, rep->retreat(rep->head_, static_cast<index_type>(flats)));
+
+ size_t first_size = data.size() - (flats - 1) * kMaxFlatLength;
+ CordRepFlat* flat = CordRepFlat::New(first_size + extra);
+ flat->length = first_size + extra;
+ memcpy(flat->Data() + extra, data.data(), first_size);
+ data.remove_prefix(first_size);
+ filler.Add(flat, extra, pos);
+ pos -= first_size;
+
+ while (!data.empty()) {
+ assert(data.size() >= kMaxFlatLength);
+ flat = CreateFlat(data.data(), kMaxFlatLength);
+ filler.Add(flat, 0, pos);
+ pos -= kMaxFlatLength;
+ data.remove_prefix(kMaxFlatLength);
+ }
+
+ rep->head_ = filler.head();
+ rep->length += rep->begin_pos_ - pos;
+ rep->begin_pos_ = pos;
+
+ return Validate(rep);
+}
+
+// 32 entries is 32 * sizeof(pos_type) = 4 cache lines on x86
+static constexpr index_type kBinarySearchThreshold = 32;
+static constexpr index_type kBinarySearchEndCount = 8;
+
+template <bool wrap>
+CordRepRing::index_type CordRepRing::FindBinary(index_type head,
+ index_type tail,
+ size_t offset) const {
+ index_type count = tail + (wrap ? capacity_ : 0) - head;
+ do {
+ count = (count - 1) / 2;
+ assert(count < entries(head, tail_));
+ index_type mid = wrap ? advance(head, count) : head + count;
+ index_type after_mid = wrap ? advance(mid) : mid + 1;
+ bool larger = (offset >= entry_end_offset(mid));
+ head = larger ? after_mid : head;
+ tail = larger ? tail : mid;
+ assert(head != tail);
+ } while (ABSL_PREDICT_TRUE(count > kBinarySearchEndCount));
+ return head;
+}
+
+CordRepRing::Position CordRepRing::FindSlow(index_type head,
+ size_t offset) const {
+ index_type tail = tail_;
+
+ // Binary search until we are good for linear search
+ // Optimize for branchless / non wrapping ops
+ if (tail > head) {
+ index_type count = tail - head;
+ if (count > kBinarySearchThreshold) {
+ head = FindBinary<false>(head, tail, offset);
+ }
+ } else {
+ index_type count = capacity_ + tail - head;
+ if (count > kBinarySearchThreshold) {
+ head = FindBinary<true>(head, tail, offset);
+ }
+ }
+
+ pos_type pos = entry_begin_pos(head);
+ pos_type end_pos = entry_end_pos(head);
+ while (offset >= Distance(begin_pos_, end_pos)) {
+ head = advance(head);
+ pos = end_pos;
+ end_pos = entry_end_pos(head);
+ }
+
+ return {head, offset - Distance(begin_pos_, pos)};
+}
+
+CordRepRing::Position CordRepRing::FindTailSlow(index_type head,
+ size_t offset) const {
+ index_type tail = tail_;
+ const size_t tail_offset = offset - 1;
+
+ // Binary search until we are good for linear search
+ // Optimize for branchless / non wrapping ops
+ if (tail > head) {
+ index_type count = tail - head;
+ if (count > kBinarySearchThreshold) {
+ head = FindBinary<false>(head, tail, tail_offset);
+ }
+ } else {
+ index_type count = capacity_ + tail - head;
+ if (count > kBinarySearchThreshold) {
+ head = FindBinary<true>(head, tail, tail_offset);
+ }
+ }
+
+ size_t end_offset = entry_end_offset(head);
+ while (tail_offset >= end_offset) {
+ head = advance(head);
+ end_offset = entry_end_offset(head);
+ }
+
+ return {advance(head), end_offset - offset};
+}
+
+char CordRepRing::GetCharacter(size_t offset) const {
+ assert(offset < length);
+
+ Position pos = Find(offset);
+ size_t data_offset = entry_data_offset(pos.index) + pos.offset;
+ return GetRepData(entry_child(pos.index))[data_offset];
+}
+
+CordRepRing* CordRepRing::SubRing(CordRepRing* rep, size_t offset,
+ size_t length, size_t extra) {
+ assert(offset <= rep->length);
+ assert(offset <= rep->length - length);
+
+ if (length == 0) {
+ CordRep::Unref(rep);
+ return nullptr;
+ }
+
+ // Find position of first byte
+ Position head = rep->Find(offset);
+ Position tail = rep->FindTail(head.index, offset + length);
+ const size_t new_entries = rep->entries(head.index, tail.index);
+
+ if (rep->refcount.IsOne() && extra <= (rep->capacity() - new_entries)) {
+ // We adopt a privately owned rep and no extra entries needed.
+ if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index);
+ if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_);
+ rep->head_ = head.index;
+ rep->tail_ = tail.index;
+ } else {
+ // Copy subset to new rep
+ rep = Copy(rep, head.index, tail.index, extra);
+ head.index = rep->head_;
+ tail.index = rep->tail_;
+ }
+
+ // Adjust begin_pos and length
+ rep->length = length;
+ rep->begin_pos_ += offset;
+
+ // Adjust head and tail blocks
+ if (head.offset) {
+ rep->AddDataOffset(head.index, head.offset);
+ }
+ if (tail.offset) {
+ rep->SubLength(rep->retreat(tail.index), tail.offset);
+ }
+
+ return Validate(rep);
+}
+
+CordRepRing* CordRepRing::RemovePrefix(CordRepRing* rep, size_t len,
+ size_t extra) {
+ assert(len <= rep->length);
+ if (len == rep->length) {
+ CordRep::Unref(rep);
+ return nullptr;
+ }
+
+ Position head = rep->Find(len);
+ if (rep->refcount.IsOne()) {
+ if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index);
+ rep->head_ = head.index;
+ } else {
+ rep = Copy(rep, head.index, rep->tail_, extra);
+ head.index = rep->head_;
+ }
+
+ // Adjust begin_pos and length
+ rep->length -= len;
+ rep->begin_pos_ += len;
+
+ // Adjust head block
+ if (head.offset) {
+ rep->AddDataOffset(head.index, head.offset);
+ }
+
+ return Validate(rep);
+}
+
+CordRepRing* CordRepRing::RemoveSuffix(CordRepRing* rep, size_t len,
+ size_t extra) {
+ assert(len <= rep->length);
+
+ if (len == rep->length) {
+ CordRep::Unref(rep);
+ return nullptr;
+ }
+
+ Position tail = rep->FindTail(rep->length - len);
+ if (rep->refcount.IsOne()) {
+ // We adopt a privately owned rep, scrub.
+ if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_);
+ rep->tail_ = tail.index;
+ } else {
+ // Copy subset to new rep
+ rep = Copy(rep, rep->head_, tail.index, extra);
+ tail.index = rep->tail_;
+ }
+
+ // Adjust length
+ rep->length -= len;
+
+ // Adjust tail block
+ if (tail.offset) {
+ rep->SubLength(rep->retreat(tail.index), tail.offset);
+ }
+
+ return Validate(rep);
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+} // namespace cord_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/strings/internal/cord_rep_ring.h b/absl/strings/internal/cord_rep_ring.h
new file mode 100644
index 0000000..c74d335
--- /dev/null
+++ b/absl/strings/internal/cord_rep_ring.h
@@ -0,0 +1,589 @@
+// Copyright 2020 The Abseil Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_
+#define ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <iosfwd>
+#include <limits>
+#include <memory>
+
+#include "absl/container/internal/layout.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_flat.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+// See https://bugs.llvm.org/show_bug.cgi?id=48477
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wshadow"
+#if __has_warning("-Wshadow-field")
+#pragma clang diagnostic ignored "-Wshadow-field"
+#endif
+#endif
+
+// All operations modifying a ring buffer are implemented as static methods
+// requiring a CordRepRing instance with a reference adopted by the method.
+//
+// The methods return the modified ring buffer, which may be equal to the input
+// if the input was not shared, and having large enough capacity to accommodate
+// any newly added node(s). Otherwise, a copy of the input rep with the new
+// node(s) added is returned.
+//
+// Any modification on non shared ring buffers with enough capacity will then
+// require minimum atomic operations. Caller should where possible provide
+// reasonable `extra` hints for both anticipated extra `flat` byte space, as
+// well as anticipated extra nodes required for complex operations.
+//
+// Example of code creating a ring buffer, adding some data to it,
+// and discarding the buffer when done:
+//
+// void FunWithRings() {
+// // Create ring with 3 flats
+// CordRep* flat = CreateFlat("Hello");
+// CordRepRing* ring = CordRepRing::Create(flat, 2);
+// ring = CordRepRing::Append(ring, CreateFlat(" "));
+// ring = CordRepRing::Append(ring, CreateFlat("world"));
+// DoSomethingWithRing(ring);
+// CordRep::Unref(ring);
+// }
+//
+// Example of code Copying an existing ring buffer and modifying it:
+//
+// void MoreFunWithRings(CordRepRing* src) {
+// CordRepRing* ring = CordRep::Ref(src)->ring();
+// ring = CordRepRing::Append(ring, CreateFlat("Hello"));
+// ring = CordRepRing::Append(ring, CreateFlat(" "));
+// ring = CordRepRing::Append(ring, CreateFlat("world"));
+// DoSomethingWithRing(ring);
+// CordRep::Unref(ring);
+// }
+//
+class CordRepRing : public CordRep {
+ public:
+ // `pos_type` represents a 'logical position'. A CordRepRing instance has a
+ // `begin_pos` (default 0), and each node inside the buffer will have an
+ // `end_pos` which is the `end_pos` of the previous node (or `begin_pos`) plus
+ // this node's length. The purpose is to allow for a binary search on this
+ // position, while allowing O(1) prepend and append operations.
+ using pos_type = size_t;
+
+ // `index_type` is the type for the `head`, `tail` and `capacity` indexes.
+ // Ring buffers are limited to having no more than four billion entries.
+ using index_type = uint32_t;
+
+ // `offset_type` is the type for the data offset inside a child rep's data.
+ using offset_type = uint32_t;
+
+ // Position holds the node index and relative offset into the node for
+ // some physical offset in the contained data as returned by the Find()
+ // and FindTail() methods.
+ struct Position {
+ index_type index;
+ size_t offset;
+ };
+
+ // The maximum # of child nodes that can be hosted inside a CordRepRing.
+ static constexpr size_t kMaxCapacity = (std::numeric_limits<uint32_t>::max)();
+
+ // CordRepring can not be default constructed, moved, copied or assigned.
+ CordRepRing() = delete;
+ CordRepRing(const CordRepRing&) = delete;
+ CordRepRing& operator=(const CordRepRing&) = delete;
+
+ // Returns true if this instance is valid, false if some or all of the
+ // invariants are broken. Intended for debug purposes only.
+ // `output` receives an explanation of the broken invariants.
+ bool IsValid(std::ostream& output) const;
+
+ // Returns the size in bytes for a CordRepRing with `capacity' entries.
+ static constexpr size_t AllocSize(size_t capacity);
+
+ // Returns the distance in bytes from `pos` to `end_pos`.
+ static constexpr size_t Distance(pos_type pos, pos_type end_pos);
+
+ // Creates a new ring buffer from the provided `rep`. Adopts a reference
+ // on `rep`. The returned ring buffer has a capacity of at least `extra + 1`
+ static CordRepRing* Create(CordRep* child, size_t extra = 0);
+
+ // `head`, `tail` and `capacity` indexes defining the ring buffer boundaries.
+ index_type head() const { return head_; }
+ index_type tail() const { return tail_; }
+ index_type capacity() const { return capacity_; }
+
+ // Returns the number of entries in this instance.
+ index_type entries() const { return entries(head_, tail_); }
+
+ // Returns the logical begin position of this instance.
+ pos_type begin_pos() const { return begin_pos_; }
+
+ // Returns the number of entries for a given head-tail range.
+ // Requires `head` and `tail` values to be less than `capacity()`.
+ index_type entries(index_type head, index_type tail) const {
+ assert(head < capacity_ && tail < capacity_);
+ return tail - head + ((tail > head) ? 0 : capacity_);
+ }
+
+ // Returns the logical end position of entry `index`.
+ pos_type const& entry_end_pos(index_type index) const {
+ assert(IsValidIndex(index));
+ return Layout::Partial().Pointer<0>(data_)[index];
+ }
+
+ // Returns the child pointer of entry `index`.
+ CordRep* const& entry_child(index_type index) const {
+ assert(IsValidIndex(index));
+ return Layout::Partial(capacity()).Pointer<1>(data_)[index];
+ }
+
+ // Returns the data offset of entry `index`
+ offset_type const& entry_data_offset(index_type index) const {
+ assert(IsValidIndex(index));
+ return Layout::Partial(capacity(), capacity()).Pointer<2>(data_)[index];
+ }
+
+ // Appends the provided child node to the `rep` instance.
+ // Adopts a reference from `rep` and `child` which may not be null.
+ // If the provided child is a FLAT or EXTERNAL node, or a SUBSTRING node
+ // containing a FLAT or EXTERNAL node, then flat or external the node is added
+ // 'as is', with an offset added for the SUBSTRING case.
+ // If the provided child is a RING or CONCAT tree, or a SUBSTRING of a RING or
+ // CONCAT tree, then all child nodes not excluded by any start offset or
+ // length values are added recursively.
+ static CordRepRing* Append(CordRepRing* rep, CordRep* child);
+
+ // Appends the provided string data to the `rep` instance.
+ // This function will attempt to utilize any remaining capacity in the last
+ // node of the input if that node is not shared (directly or indirectly), and
+ // of type FLAT. Remaining data will be added as one or more FLAT nodes.
+ // Any last node added to the ring buffer will be allocated with up to
+ // `extra` bytes of capacity for (anticipated) subsequent append actions.
+ static CordRepRing* Append(CordRepRing* rep, string_view data,
+ size_t extra = 0);
+
+ // Prepends the provided child node to the `rep` instance.
+ // Adopts a reference from `rep` and `child` which may not be null.
+ // If the provided child is a FLAT or EXTERNAL node, or a SUBSTRING node
+ // containing a FLAT or EXTERNAL node, then flat or external the node is
+ // prepended 'as is', with an optional offset added for the SUBSTRING case.
+ // If the provided child is a RING or CONCAT tree, or a SUBSTRING of a RING
+ // or CONCAT tree, then all child nodes not excluded by any start offset or
+ // length values are added recursively.
+ static CordRepRing* Prepend(CordRepRing* rep, CordRep* child);
+
+ // Prepends the provided string data to the `rep` instance.
+ // This function will attempt to utilize any remaining capacity in the first
+ // node of the input if that node is not shared (directly or indirectly), and
+ // of type FLAT. Remaining data will be added as one or more FLAT nodes.
+ // Any first node prepnded to the ring buffer will be allocated with up to
+ // `extra` bytes of capacity for (anticipated) subsequent prepend actions.
+ static CordRepRing* Prepend(CordRepRing* rep, string_view data,
+ size_t extra = 0);
+
+ // Returns a span referencing potentially unused capacity in the last node.
+ // The returned span may be empty if no such capacity is available, or if the
+ // current instance is shared. Else, a span of size `n <= size` is returned.
+ // If non empty, the ring buffer is adjusted to the new length, with the newly
+ // added capacity left uninitialized. Callers should assign a value to the
+ // entire span before any other operations on this instance.
+ Span<char> GetAppendBuffer(size_t size);
+
+ // Returns a span referencing potentially unused capacity in the first node.
+ // This function is identical to GetAppendBuffer except that it returns a span
+ // referencing up to `size` capacity directly before the existing data.
+ Span<char> GetPrependBuffer(size_t size);
+
+ // Returns a cord ring buffer containing `length` bytes of data starting at
+ // `offset`. If the input is not shared, this function will remove all head
+ // and tail child nodes outside of the requested range, and adjust the new
+ // head and tail nodes as required. If the input is shared, this function
+ // returns a new instance sharing some or all of the nodes from the input.
+ static CordRepRing* SubRing(CordRepRing* r, size_t offset, size_t length,
+ size_t extra = 0);
+
+ // Returns a cord ring buffer with the first `length` bytes removed.
+ // If the input is not shared, this function will remove all head child nodes
+ // fully inside the first `length` bytes, and adjust the new head as required.
+ // If the input is shared, this function returns a new instance sharing some
+ // or all of the nodes from the input.
+ static CordRepRing* RemoveSuffix(CordRepRing* r, size_t length,
+ size_t extra = 0);
+
+ // Returns a cord ring buffer with the last `length` bytes removed.
+ // If the input is not shared, this function will remove all head child nodes
+ // fully inside the first `length` bytes, and adjust the new head as required.
+ // If the input is shared, this function returns a new instance sharing some
+ // or all of the nodes from the input.
+ static CordRepRing* RemovePrefix(CordRepRing* r, size_t len,
+ size_t extra = 0);
+
+ // Returns the character at `offset`. Requires that `offset < length`.
+ char GetCharacter(size_t offset) const;
+
+ // Testing only: set capacity to requested capacity.
+ void SetCapacityForTesting(size_t capacity);
+
+ // Returns the CordRep data pointer for the provided CordRep.
+ // Requires that the provided `rep` is either a FLAT or EXTERNAL CordRep.
+ static const char* GetLeafData(const CordRep* rep);
+
+ // Returns the CordRep data pointer for the provided CordRep.
+ // Requires that `rep` is either a FLAT, EXTERNAL, or SUBSTRING CordRep.
+ static const char* GetRepData(const CordRep* rep);
+
+ // Advances the provided position, wrapping around capacity as needed.
+ // Requires `index` < capacity()
+ inline index_type advance(index_type index) const;
+
+ // Advances the provided position by 'n`, wrapping around capacity as needed.
+ // Requires `index` < capacity() and `n` <= capacity.
+ inline index_type advance(index_type index, index_type n) const;
+
+ // Retreats the provided position, wrapping around 0 as needed.
+ // Requires `index` < capacity()
+ inline index_type retreat(index_type index) const;
+
+ // Retreats the provided position by 'n', wrapping around 0 as needed.
+ // Requires `index` < capacity()
+ inline index_type retreat(index_type index, index_type n) const;
+
+ // Returns the logical begin position of entry `index`
+ pos_type const& entry_begin_pos(index_type index) const {
+ return (index == head_) ? begin_pos_ : entry_end_pos(retreat(index));
+ }
+
+ // Returns the physical start offset of entry `index`
+ size_t entry_start_offset(index_type index) const {
+ return Distance(begin_pos_, entry_begin_pos(index));
+ }
+
+ // Returns the physical end offset of entry `index`
+ size_t entry_end_offset(index_type index) const {
+ return Distance(begin_pos_, entry_end_pos(index));
+ }
+
+ // Returns the data length for entry `index`
+ size_t entry_length(index_type index) const {
+ return Distance(entry_begin_pos(index), entry_end_pos(index));
+ }
+
+ // Returns the data for entry `index`
+ absl::string_view entry_data(index_type index) const;
+
+ // Returns the position for `offset` as {index, prefix}. `index` holds the
+ // index of the entry at the specified offset and `prefix` holds the relative
+ // offset inside that entry.
+ // Requires `offset` < length.
+ //
+ // For example we can implement GetCharacter(offset) as:
+ // char GetCharacter(size_t offset) {
+ // Position pos = this->Find(offset);
+ // return this->entry_data(pos.pos)[pos.offset];
+ // }
+ inline Position Find(size_t offset) const;
+
+ // Find starting at `head`
+ inline Position Find(index_type head, size_t offset) const;
+
+ // Returns the tail position for `offset` as {tail index, suffix}.
+ // `tail index` holds holds the index of the entry holding the offset directly
+ // before 'offset` advanced by one. 'suffix` holds the relative offset from
+ // that relative offset in the entry to the end of the entry.
+ // For example, FindTail(length) will return {tail(), 0}, FindTail(length - 5)
+ // will return {retreat(tail), 5)} provided the preceding entry contains at
+ // least 5 bytes of data.
+ // Requires offset >= 1 && offset <= length.
+ //
+ // This function is very useful in functions that need to clip the end of some
+ // ring buffer such as 'RemovePrefix'.
+ // For example, we could implement RemovePrefix for non shared instances as:
+ // void RemoveSuffix(size_t n) {
+ // Position pos = FindTail(length - n);
+ // UnrefEntries(pos.pos, this->tail_);
+ // this->tail_ = pos.pos;
+ // entry(retreat(pos.pos)).end_pos -= pos.offset;
+ // }
+ inline Position FindTail(size_t offset) const;
+
+ // Find tail starting at `head`
+ inline Position FindTail(index_type head, size_t offset) const;
+
+ // Invokes f(index_type index) for each entry inside the range [head, tail>
+ template <typename F>
+ void ForEach(index_type head, index_type tail, F&& f) const {
+ index_type n1 = (tail > head) ? tail : capacity_;
+ for (index_type i = head; i < n1; ++i) f(i);
+ if (tail <= head) {
+ for (index_type i = 0; i < tail; ++i) f(i);
+ }
+ }
+
+ // Invokes f(index_type index) for each entry inside this instance.
+ template <typename F>
+ void ForEach(F&& f) const {
+ ForEach(head_, tail_, std::forward<F>(f));
+ }
+
+ // Dump this instance's data tp stream `s` in human readable format, excluding
+ // the actual data content itself. Intended for debug purposes only.
+ friend std::ostream& operator<<(std::ostream& s, const CordRepRing& rep);
+
+ private:
+ enum class AddMode { kAppend, kPrepend };
+
+ using Layout = container_internal::Layout<pos_type, CordRep*, offset_type>;
+
+ class Filler;
+ class Transaction;
+ class CreateTransaction;
+
+ static constexpr size_t kLayoutAlignment = Layout::Partial().Alignment();
+
+ // Creates a new CordRepRing.
+ explicit CordRepRing(index_type capacity) : capacity_(capacity) {}
+
+ // Returns true if `index` is a valid index into this instance.
+ bool IsValidIndex(index_type index) const;
+
+ // Debug use only: validates the provided CordRepRing invariants.
+ // Verification of all CordRepRing methods can be enabled by defining
+ // EXTRA_CORD_RING_VALIDATION, i.e.: `--copts=-DEXTRA_CORD_RING_VALIDATION`
+ // Verification is VERY expensive, so only do it for debugging purposes.
+ static CordRepRing* Validate(CordRepRing* rep, const char* file = nullptr,
+ int line = 0);
+
+ // Allocates a CordRepRing large enough to hold `capacity + extra' entries.
+ // The returned capacity may be larger if the allocated memory allows for it.
+ // The maximum capacity of a CordRepRing is capped at kMaxCapacity.
+ // Throws `std::length_error` if `capacity + extra' exceeds kMaxCapacity.
+ static CordRepRing* New(size_t capacity, size_t extra);
+
+ // Deallocates (but does not destroy) the provided ring buffer.
+ static void Delete(CordRepRing* rep);
+
+ // Destroys the provided ring buffer, decrementing the reference count of all
+ // contained child CordReps. The provided 1\`rep` should have a ref count of
+ // one (pre decrement destroy call observing `refcount.IsOne()`) or zero (post
+ // decrement destroy call observing `!refcount.Decrement()`).
+ static void Destroy(CordRepRing* rep);
+
+ // Returns a mutable reference to the logical end position array.
+ pos_type* entry_end_pos() {
+ return Layout::Partial().Pointer<0>(data_);
+ }
+
+ // Returns a mutable reference to the child pointer array.
+ CordRep** entry_child() {
+ return Layout::Partial(capacity()).Pointer<1>(data_);
+ }
+
+ // Returns a mutable reference to the data offset array.
+ offset_type* entry_data_offset() {
+ return Layout::Partial(capacity(), capacity()).Pointer<2>(data_);
+ }
+
+ // Find implementations for the non fast path 0 / length cases.
+ Position FindSlow(index_type head, size_t offset) const;
+ Position FindTailSlow(index_type head, size_t offset) const;
+
+ // Finds the index of the first node that is inside a reasonable distance
+ // of the node at `offset` from which we can continue with a linear search.
+ template <bool wrap>
+ index_type FindBinary(index_type head, index_type tail, size_t offset) const;
+
+ // Fills the current (initialized) instance from the provided source, copying
+ // entries [head, tail). Adds a reference to copied entries if `ref` is true.
+ template <bool ref>
+ void Fill(const CordRepRing* src, index_type head, index_type tail);
+
+ // Create a copy of 'rep', copying all entries [head, tail), allocating room
+ // for `extra` entries. Adds a reference on all copied entries.
+ static CordRepRing* Copy(CordRepRing* rep, index_type head, index_type tail,
+ size_t extra = 0);
+
+ // Returns a Mutable CordRepRing reference from `rep` with room for at least
+ // `extra` additional nodes. Adopts a reference count from `rep`.
+ // This function will return `rep` if, and only if:
+ // - rep.entries + extra <= rep.capacity
+ // - rep.refcount == 1
+ // Otherwise, this function will create a new copy of `rep` with additional
+ // capacity to satisfy `extra` extra nodes, and unref the old `rep` instance.
+ //
+ // If a new CordRepRing can not be allocated, or the new capacity would exceed
+ // the maxmimum capacity, then the input is consumed only, and an exception is
+ // thrown.
+ static CordRepRing* Mutable(CordRepRing* rep, size_t extra);
+
+ // Slow path for Append(CordRepRing* rep, CordRep* child). This function is
+ // exercised if the provided `child` in Append() is not a leaf node, i.e., a
+ // ring buffer or old (concat) cord tree.
+ static CordRepRing* AppendSlow(CordRepRing* rep, CordRep* child);
+
+ // Appends the provided leaf node. Requires `child` to be FLAT or EXTERNAL.
+ static CordRepRing* AppendLeaf(CordRepRing* rep, CordRep* child,
+ size_t offset, size_t length);
+
+ // Prepends the provided leaf node. Requires `child` to be FLAT or EXTERNAL.
+ static CordRepRing* PrependLeaf(CordRepRing* rep, CordRep* child,
+ size_t offset, size_t length);
+
+ // Slow path for Prepend(CordRepRing* rep, CordRep* child). This function is
+ // exercised if the provided `child` in Prepend() is not a leaf node, i.e., a
+ // ring buffer or old (concat) cord tree.
+ static CordRepRing* PrependSlow(CordRepRing* rep, CordRep* child);
+
+ // Slow path for Create(CordRep* child, size_t extra). This function is
+ // exercised if the provided `child` in Prepend() is not a leaf node, i.e., a
+ // ring buffer or old (concat) cord tree.
+ static CordRepRing* CreateSlow(CordRep* child, size_t extra);
+
+ // Creates a new ring buffer from the provided `child` leaf node. Requires
+ // `child` to be FLAT or EXTERNAL. on `rep`.
+ // The returned ring buffer has a capacity of at least `1 + extra`
+ static CordRepRing* CreateFromLeaf(CordRep* child, size_t offset,
+ size_t length, size_t extra);
+
+ // Appends or prepends (depending on AddMode) the ring buffer in `ring' to
+ // `rep` starting at `offset` with length `length`.
+ template <AddMode mode>
+ static CordRepRing* AddRing(CordRepRing* rep, CordRepRing* ring,
+ size_t offset, size_t length);
+
+ // Increases the data offset for entry `index` by `n`.
+ void AddDataOffset(index_type index, size_t n);
+
+ // Descreases the length for entry `index` by `n`.
+ void SubLength(index_type index, size_t n);
+
+ index_type head_;
+ index_type tail_;
+ index_type capacity_;
+ pos_type begin_pos_;
+
+ alignas(kLayoutAlignment) char data_[kLayoutAlignment];
+
+ friend struct CordRep;
+};
+
+constexpr size_t CordRepRing::AllocSize(size_t capacity) {
+ return sizeof(CordRepRing) - sizeof(data_) +
+ Layout(capacity, capacity, capacity).AllocSize();
+}
+
+inline constexpr size_t CordRepRing::Distance(pos_type pos, pos_type end_pos) {
+ return (end_pos - pos);
+}
+
+inline const char* CordRepRing::GetLeafData(const CordRep* rep) {
+ return rep->tag != EXTERNAL ? rep->flat()->Data() : rep->external()->base;
+}
+
+inline const char* CordRepRing::GetRepData(const CordRep* rep) {
+ if (rep->tag >= FLAT) return rep->flat()->Data();
+ if (rep->tag == EXTERNAL) return rep->external()->base;
+ return GetLeafData(rep->substring()->child) + rep->substring()->start;
+}
+
+inline CordRepRing::index_type CordRepRing::advance(index_type index) const {
+ assert(index < capacity_);
+ return ++index == capacity_ ? 0 : index;
+}
+
+inline CordRepRing::index_type CordRepRing::advance(index_type index,
+ index_type n) const {
+ assert(index < capacity_ && n <= capacity_);
+ return (index += n) >= capacity_ ? index - capacity_ : index;
+}
+
+inline CordRepRing::index_type CordRepRing::retreat(index_type index) const {
+ assert(index < capacity_);
+ return (index > 0 ? index : capacity_) - 1;
+}
+
+inline CordRepRing::index_type CordRepRing::retreat(index_type index,
+ index_type n) const {
+ assert(index < capacity_ && n <= capacity_);
+ return index >= n ? index - n : capacity_ - n + index;
+}
+
+inline absl::string_view CordRepRing::entry_data(index_type index) const {
+ size_t data_offset = entry_data_offset(index);
+ return {GetRepData(entry_child(index)) + data_offset, entry_length(index)};
+}
+
+inline bool CordRepRing::IsValidIndex(index_type index) const {
+ if (index >= capacity_) return false;
+ return (tail_ > head_) ? (index >= head_ && index < tail_)
+ : (index >= head_ || index < tail_);
+}
+
+#ifndef EXTRA_CORD_RING_VALIDATION
+inline CordRepRing* CordRepRing::Validate(CordRepRing* rep,
+ const char* /*file*/, int /*line*/) {
+ return rep;
+}
+#endif
+
+inline CordRepRing::Position CordRepRing::Find(size_t offset) const {
+ assert(offset < length);
+ return (offset == 0) ? Position{head_, 0} : FindSlow(head_, offset);
+}
+
+inline CordRepRing::Position CordRepRing::Find(index_type head,
+ size_t offset) const {
+ assert(offset < length);
+ assert(IsValidIndex(head) && offset >= entry_start_offset(head));
+ return (offset == 0) ? Position{head_, 0} : FindSlow(head, offset);
+}
+
+inline CordRepRing::Position CordRepRing::FindTail(size_t offset) const {
+ assert(offset > 0 && offset <= length);
+ return (offset == length) ? Position{tail_, 0} : FindTailSlow(head_, offset);
+}
+
+inline CordRepRing::Position CordRepRing::FindTail(index_type head,
+ size_t offset) const {
+ assert(offset > 0 && offset <= length);
+ assert(IsValidIndex(head) && offset >= entry_start_offset(head) + 1);
+ return (offset == length) ? Position{tail_, 0} : FindTailSlow(head, offset);
+}
+
+// Now that CordRepRing is defined, we can define CordRep's helper casts:
+inline CordRepRing* CordRep::ring() {
+ assert(tag == RING);
+ return static_cast<CordRepRing*>(this);
+}
+
+inline const CordRepRing* CordRep::ring() const {
+ assert(tag == RING);
+ return static_cast<const CordRepRing*>(this);
+}
+
+std::ostream& operator<<(std::ostream& s, const CordRepRing& rep);
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+} // namespace cord_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_
diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc
index 375db0a..926283c 100644
--- a/absl/strings/internal/str_format/convert_test.cc
+++ b/absl/strings/internal/str_format/convert_test.cc
@@ -554,7 +554,8 @@
}
template <typename Floating>
-void TestWithMultipleFormatsHelper(const std::vector<Floating> &floats) {
+void TestWithMultipleFormatsHelper(const std::vector<Floating> &floats,
+ const std::set<Floating> &skip_verify) {
const NativePrintfTraits &native_traits = VerifyNativeImplementation();
// Reserve the space to ensure we don't allocate memory in the output itself.
std::string str_format_result;
@@ -602,7 +603,16 @@
AppendPack(&str_format_result, format, absl::MakeSpan(args));
}
- if (string_printf_result != str_format_result) {
+#ifdef _MSC_VER
+ // MSVC has a different rounding policy than us so we can't test our
+ // implementation against the native one there.
+ continue;
+#elif defined(__APPLE__)
+ // Apple formats NaN differently (+nan) vs. (nan)
+ if (std::isnan(d)) continue;
+#endif
+ if (string_printf_result != str_format_result &&
+ skip_verify.find(d) == skip_verify.end()) {
// We use ASSERT_EQ here because failures are usually correlated and a
// bug would print way too many failed expectations causing the test
// to time out.
@@ -616,12 +626,6 @@
}
TEST_F(FormatConvertTest, Float) {
-#ifdef _MSC_VER
- // MSVC has a different rounding policy than us so we can't test our
- // implementation against the native one there.
- return;
-#endif // _MSC_VER
-
std::vector<float> floats = {0.0f,
-0.0f,
.9999999f,
@@ -635,7 +639,8 @@
std::numeric_limits<float>::epsilon(),
std::numeric_limits<float>::epsilon() + 1.0f,
std::numeric_limits<float>::infinity(),
- -std::numeric_limits<float>::infinity()};
+ -std::numeric_limits<float>::infinity(),
+ std::nanf("")};
// Some regression tests.
floats.push_back(0.999999989f);
@@ -664,21 +669,14 @@
std::sort(floats.begin(), floats.end());
floats.erase(std::unique(floats.begin(), floats.end()), floats.end());
-#ifndef __APPLE__
- // Apple formats NaN differently (+nan) vs. (nan)
- floats.push_back(std::nan(""));
-#endif
-
- TestWithMultipleFormatsHelper(floats);
+ TestWithMultipleFormatsHelper(floats, {});
}
TEST_F(FormatConvertTest, Double) {
-#ifdef _MSC_VER
- // MSVC has a different rounding policy than us so we can't test our
- // implementation against the native one there.
- return;
-#endif // _MSC_VER
-
+ // For values that we know won't match the standard library implementation we
+ // skip verification, but still run the algorithm to catch asserts/sanitizer
+ // bugs.
+ std::set<double> skip_verify;
std::vector<double> doubles = {0.0,
-0.0,
.99999999999999,
@@ -692,7 +690,8 @@
std::numeric_limits<double>::epsilon(),
std::numeric_limits<double>::epsilon() + 1,
std::numeric_limits<double>::infinity(),
- -std::numeric_limits<double>::infinity()};
+ -std::numeric_limits<double>::infinity(),
+ std::nan("")};
// Some regression tests.
doubles.push_back(0.99999999999999989);
@@ -722,33 +721,29 @@
"5084551339423045832369032229481658085593321233482747978262041447231"
"68738177180919299881250404026184124858368.000000";
- if (!gcc_bug_22142) {
- for (int exp = -300; exp <= 300; ++exp) {
- const double all_ones_mantissa = 0x1fffffffffffff;
- doubles.push_back(std::ldexp(all_ones_mantissa, exp));
+ for (int exp = -300; exp <= 300; ++exp) {
+ const double all_ones_mantissa = 0x1fffffffffffff;
+ doubles.push_back(std::ldexp(all_ones_mantissa, exp));
+ if (gcc_bug_22142) {
+ skip_verify.insert(doubles.back());
}
}
if (gcc_bug_22142) {
- for (auto &d : doubles) {
- using L = std::numeric_limits<double>;
- double d2 = std::abs(d);
- if (d2 == L::max() || d2 == L::min() || d2 == L::denorm_min()) {
- d = 0;
- }
- }
+ using L = std::numeric_limits<double>;
+ skip_verify.insert(L::max());
+ skip_verify.insert(L::min()); // NOLINT
+ skip_verify.insert(L::denorm_min());
+ skip_verify.insert(-L::max());
+ skip_verify.insert(-L::min()); // NOLINT
+ skip_verify.insert(-L::denorm_min());
}
// Remove duplicates to speed up the logic below.
std::sort(doubles.begin(), doubles.end());
doubles.erase(std::unique(doubles.begin(), doubles.end()), doubles.end());
-#ifndef __APPLE__
- // Apple formats NaN differently (+nan) vs. (nan)
- doubles.push_back(std::nan(""));
-#endif
-
- TestWithMultipleFormatsHelper(doubles);
+ TestWithMultipleFormatsHelper(doubles, skip_verify);
}
TEST_F(FormatConvertTest, DoubleRound) {
@@ -1069,11 +1064,6 @@
}
TEST_F(FormatConvertTest, LongDouble) {
-#ifdef _MSC_VER
- // MSVC has a different rounding policy than us so we can't test our
- // implementation against the native one there.
- return;
-#endif // _MSC_VER
const NativePrintfTraits &native_traits = VerifyNativeImplementation();
const char *const kFormats[] = {"%", "%.3", "%8.5", "%9", "%.5000",
"%.60", "%+", "% ", "%-10"};
@@ -1134,10 +1124,18 @@
for (auto d : doubles) {
FormatArgImpl arg(d);
UntypedFormatSpecImpl format(fmt_str);
+ std::string result = FormatPack(format, {&arg, 1});
+
+#ifdef _MSC_VER
+ // MSVC has a different rounding policy than us so we can't test our
+ // implementation against the native one there.
+ continue;
+#endif // _MSC_VER
+
// We use ASSERT_EQ here because failures are usually correlated and a
// bug would print way too many failed expectations causing the test to
// time out.
- ASSERT_EQ(StrPrint(fmt_str.c_str(), d), FormatPack(format, {&arg, 1}))
+ ASSERT_EQ(StrPrint(fmt_str.c_str(), d), result)
<< fmt_str << " " << StrPrint("%.18Lg", d) << " "
<< StrPrint("%La", d) << " " << StrPrint("%.1080Lf", d);
}
diff --git a/absl/strings/internal/str_format/float_conversion.cc b/absl/strings/internal/str_format/float_conversion.cc
index 0ded0a6..b1c4068 100644
--- a/absl/strings/internal/str_format/float_conversion.cc
+++ b/absl/strings/internal/str_format/float_conversion.cc
@@ -24,11 +24,12 @@
#include "absl/base/attributes.h"
#include "absl/base/config.h"
-#include "absl/base/internal/bits.h"
#include "absl/base/optimization.h"
#include "absl/functional/function_ref.h"
#include "absl/meta/type_traits.h"
+#include "absl/numeric/bits.h"
#include "absl/numeric/int128.h"
+#include "absl/numeric/internal/representation.h"
#include "absl/strings/numbers.h"
#include "absl/types/optional.h"
#include "absl/types/span.h"
@@ -39,6 +40,8 @@
namespace {
+using ::absl::numeric_internal::IsDoubleDouble;
+
// The code below wants to avoid heap allocations.
// To do so it needs to allocate memory on the stack.
// `StackArray` will allocate memory on the stack in the form of a uint32_t
@@ -112,12 +115,15 @@
return next_carry % divisor;
}
+using MaxFloatType =
+ typename std::conditional<IsDoubleDouble(), double, long double>::type;
+
// Generates the decimal representation for an integer of the form `v * 2^exp`,
// where `v` and `exp` are both positive integers.
// It generates the digits from the left (ie the most significant digit first)
// to allow for direct printing into the sink.
//
-// Requires `0 <= exp` and `exp <= numeric_limits<long double>::max_exponent`.
+// Requires `0 <= exp` and `exp <= numeric_limits<MaxFloatType>::max_exponent`.
class BinaryToDecimal {
static constexpr int ChunksNeeded(int exp) {
// We will left shift a uint128 by `exp` bits, so we need `128+exp` total
@@ -132,10 +138,10 @@
static void RunConversion(uint128 v, int exp,
absl::FunctionRef<void(BinaryToDecimal)> f) {
assert(exp > 0);
- assert(exp <= std::numeric_limits<long double>::max_exponent);
+ assert(exp <= std::numeric_limits<MaxFloatType>::max_exponent);
static_assert(
static_cast<int>(StackArray::kMaxCapacity) >=
- ChunksNeeded(std::numeric_limits<long double>::max_exponent),
+ ChunksNeeded(std::numeric_limits<MaxFloatType>::max_exponent),
"");
StackArray::RunWithCapacity(
@@ -232,14 +238,14 @@
// Converts a value of the form `x * 2^-exp` into a sequence of decimal digits.
// Requires `-exp < 0` and
-// `-exp >= limits<long double>::min_exponent - limits<long double>::digits`.
+// `-exp >= limits<MaxFloatType>::min_exponent - limits<MaxFloatType>::digits`.
class FractionalDigitGenerator {
public:
// Run the conversion for `v * 2^exp` and call `f(generator)`.
// This function will allocate enough stack space to perform the conversion.
static void RunConversion(
uint128 v, int exp, absl::FunctionRef<void(FractionalDigitGenerator)> f) {
- using Limits = std::numeric_limits<long double>;
+ using Limits = std::numeric_limits<MaxFloatType>;
assert(-exp < 0);
assert(-exp >= Limits::min_exponent - 128);
static_assert(StackArray::kMaxCapacity >=
@@ -315,12 +321,11 @@
};
// Count the number of leading zero bits.
-int LeadingZeros(uint64_t v) { return base_internal::CountLeadingZeros64(v); }
+int LeadingZeros(uint64_t v) { return countl_zero(v); }
int LeadingZeros(uint128 v) {
auto high = static_cast<uint64_t>(v >> 64);
auto low = static_cast<uint64_t>(v);
- return high != 0 ? base_internal::CountLeadingZeros64(high)
- : 64 + base_internal::CountLeadingZeros64(low);
+ return high != 0 ? countl_zero(high) : 64 + countl_zero(low);
}
// Round up the text digits starting at `p`.
@@ -872,10 +877,10 @@
// This buffer holds the "0x1.ab1de3" portion of "0x1.ab1de3pe+2". Compute the
// size with long double which is the largest of the floats.
constexpr size_t kBufSizeForHexFloatRepr =
- 2 // 0x
- + std::numeric_limits<long double>::digits / 4 // number of hex digits
- + 1 // round up
- + 1; // "." (dot)
+ 2 // 0x
+ + std::numeric_limits<MaxFloatType>::digits / 4 // number of hex digits
+ + 1 // round up
+ + 1; // "." (dot)
char digits_buffer[kBufSizeForHexFloatRepr];
char *digits_iter = digits_buffer;
const char *const digits =
@@ -1394,10 +1399,9 @@
bool ConvertFloatImpl(long double v, const FormatConversionSpecImpl &conv,
FormatSinkImpl *sink) {
- if (std::numeric_limits<long double>::digits ==
- 2 * std::numeric_limits<double>::digits) {
- // This is the `double-double` representation of `long double`.
- // We do not handle it natively. Fallback to snprintf.
+ if (IsDoubleDouble()) {
+ // This is the `double-double` representation of `long double`. We do not
+ // handle it natively. Fallback to snprintf.
return FallbackToSnprintf(v, conv, sink);
}
diff --git a/absl/strings/internal/str_split_internal.h b/absl/strings/internal/str_split_internal.h
index 7692477..a2f41c1 100644
--- a/absl/strings/internal/str_split_internal.h
+++ b/absl/strings/internal/str_split_internal.h
@@ -51,9 +51,9 @@
namespace strings_internal {
// This class is implicitly constructible from everything that absl::string_view
-// is implicitly constructible from. If it's constructed from a temporary
-// string, the data is moved into a data member so its lifetime matches that of
-// the ConvertibleToStringView instance.
+// is implicitly constructible from, except for rvalue strings. This means it
+// can be used as a function parameter in places where passing a temporary
+// string might cause memory lifetime issues.
class ConvertibleToStringView {
public:
ConvertibleToStringView(const char* s) // NOLINT(runtime/explicit)
@@ -65,57 +65,13 @@
: value_(s) {}
// Matches rvalue strings and moves their data to a member.
- ConvertibleToStringView(std::string&& s) // NOLINT(runtime/explicit)
- : copy_(std::move(s)), value_(copy_), self_referential_(true) {}
-
- ConvertibleToStringView(const ConvertibleToStringView& other)
- : value_(other.value_), self_referential_(other.self_referential_) {
- if (other.self_referential_) {
- new (©_) std::string(other.copy_);
- value_ = copy_;
- }
- }
-
- ConvertibleToStringView(ConvertibleToStringView&& other)
- : value_(other.value_), self_referential_(other.self_referential_) {
- if (other.self_referential_) {
- new (©_) std::string(std::move(other.copy_));
- value_ = copy_;
- }
- }
-
- ConvertibleToStringView& operator=(ConvertibleToStringView other) {
- this->~ConvertibleToStringView();
- new (this) ConvertibleToStringView(std::move(other));
- return *this;
- }
+ ConvertibleToStringView(std::string&& s) = delete;
+ ConvertibleToStringView(const std::string&& s) = delete;
absl::string_view value() const { return value_; }
- ~ConvertibleToStringView() { MaybeReleaseCopy(); }
-
private:
- void MaybeReleaseCopy() {
- if (self_referential_) {
- // An explicit destructor call cannot be a qualified name such as
- // std::string. The "using" declaration works around this
- // issue by creating an unqualified name for the destructor.
- using string_type = std::string;
- copy_.~string_type();
- }
- }
- struct Unused { // MSVC disallows unions with only 1 member.
- };
- // Holds the data moved from temporary std::string arguments. Declared first
- // so that 'value' can refer to 'copy_'.
- union {
- std::string copy_;
- Unused unused_;
- };
-
absl::string_view value_;
- // true if value_ refers to the internal copy_ member.
- bool self_referential_ = false;
};
// An iterator that enumerates the parts of a string from a Splitter. The text
@@ -288,7 +244,11 @@
// the split strings: only strings for which the predicate returns true will be
// kept. A Predicate object is any unary functor that takes an absl::string_view
// and returns bool.
-template <typename Delimiter, typename Predicate>
+//
+// The StringType parameter can be either string_view or string, depending on
+// whether the Splitter refers to a string stored elsewhere, or if the string
+// resides inside the Splitter itself.
+template <typename Delimiter, typename Predicate, typename StringType>
class Splitter {
public:
using DelimiterType = Delimiter;
@@ -296,12 +256,12 @@
using const_iterator = strings_internal::SplitIterator<Splitter>;
using value_type = typename std::iterator_traits<const_iterator>::value_type;
- Splitter(ConvertibleToStringView input_text, Delimiter d, Predicate p)
+ Splitter(StringType input_text, Delimiter d, Predicate p)
: text_(std::move(input_text)),
delimiter_(std::move(d)),
predicate_(std::move(p)) {}
- absl::string_view text() const { return text_.value(); }
+ absl::string_view text() const { return text_; }
const Delimiter& delimiter() const { return delimiter_; }
const Predicate& predicate() const { return predicate_; }
@@ -458,7 +418,7 @@
};
};
- ConvertibleToStringView text_;
+ StringType text_;
Delimiter delimiter_;
Predicate predicate_;
};
diff --git a/absl/strings/internal/string_constant.h b/absl/strings/internal/string_constant.h
index b15f1d9..a11336b 100644
--- a/absl/strings/internal/string_constant.h
+++ b/absl/strings/internal/string_constant.h
@@ -35,18 +35,12 @@
// below.
template <typename T>
struct StringConstant {
- private:
- // Returns true if `view` points to constant data.
- // Otherwise, it can't be constant evaluated.
- static constexpr bool ValidateConstant(absl::string_view view) {
- return view.empty() || 2 * view[0] != 1;
- }
-
- public:
static constexpr absl::string_view value = T{}();
constexpr absl::string_view operator()() const { return value; }
- static_assert(ValidateConstant(value),
+ // Check to be sure `view` points to constant data.
+ // Otherwise, it can't be constant evaluated.
+ static_assert(value.empty() || 2 * value[0] != 1,
"The input string_view must point to constant data.");
};
diff --git a/absl/strings/match.cc b/absl/strings/match.cc
index 8127cb0..2d67250 100644
--- a/absl/strings/match.cc
+++ b/absl/strings/match.cc
@@ -19,19 +19,22 @@
namespace absl {
ABSL_NAMESPACE_BEGIN
-bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2) {
+bool EqualsIgnoreCase(absl::string_view piece1,
+ absl::string_view piece2) noexcept {
return (piece1.size() == piece2.size() &&
0 == absl::strings_internal::memcasecmp(piece1.data(), piece2.data(),
piece1.size()));
// memcasecmp uses absl::ascii_tolower().
}
-bool StartsWithIgnoreCase(absl::string_view text, absl::string_view prefix) {
+bool StartsWithIgnoreCase(absl::string_view text,
+ absl::string_view prefix) noexcept {
return (text.size() >= prefix.size()) &&
EqualsIgnoreCase(text.substr(0, prefix.size()), prefix);
}
-bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix) {
+bool EndsWithIgnoreCase(absl::string_view text,
+ absl::string_view suffix) noexcept {
return (text.size() >= suffix.size()) &&
EqualsIgnoreCase(text.substr(text.size() - suffix.size()), suffix);
}
diff --git a/absl/strings/match.h b/absl/strings/match.h
index 90fca98..038cbb3 100644
--- a/absl/strings/match.h
+++ b/absl/strings/match.h
@@ -43,14 +43,20 @@
// StrContains()
//
// Returns whether a given string `haystack` contains the substring `needle`.
-inline bool StrContains(absl::string_view haystack, absl::string_view needle) {
+inline bool StrContains(absl::string_view haystack,
+ absl::string_view needle) noexcept {
return haystack.find(needle, 0) != haystack.npos;
}
+inline bool StrContains(absl::string_view haystack, char needle) noexcept {
+ return haystack.find(needle) != haystack.npos;
+}
+
// StartsWith()
//
// Returns whether a given string `text` begins with `prefix`.
-inline bool StartsWith(absl::string_view text, absl::string_view prefix) {
+inline bool StartsWith(absl::string_view text,
+ absl::string_view prefix) noexcept {
return prefix.empty() ||
(text.size() >= prefix.size() &&
memcmp(text.data(), prefix.data(), prefix.size()) == 0);
@@ -59,7 +65,8 @@
// EndsWith()
//
// Returns whether a given string `text` ends with `suffix`.
-inline bool EndsWith(absl::string_view text, absl::string_view suffix) {
+inline bool EndsWith(absl::string_view text,
+ absl::string_view suffix) noexcept {
return suffix.empty() ||
(text.size() >= suffix.size() &&
memcmp(text.data() + (text.size() - suffix.size()), suffix.data(),
@@ -70,19 +77,22 @@
//
// Returns whether given ASCII strings `piece1` and `piece2` are equal, ignoring
// case in the comparison.
-bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2);
+bool EqualsIgnoreCase(absl::string_view piece1,
+ absl::string_view piece2) noexcept;
// StartsWithIgnoreCase()
//
// Returns whether a given ASCII string `text` starts with `prefix`,
// ignoring case in the comparison.
-bool StartsWithIgnoreCase(absl::string_view text, absl::string_view prefix);
+bool StartsWithIgnoreCase(absl::string_view text,
+ absl::string_view prefix) noexcept;
// EndsWithIgnoreCase()
//
// Returns whether a given ASCII string `text` ends with `suffix`, ignoring
// case in the comparison.
-bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix);
+bool EndsWithIgnoreCase(absl::string_view text,
+ absl::string_view suffix) noexcept;
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/match_test.cc b/absl/strings/match_test.cc
index 4c313dd..5841bc1 100644
--- a/absl/strings/match_test.cc
+++ b/absl/strings/match_test.cc
@@ -66,6 +66,23 @@
EXPECT_FALSE(absl::StrContains("", "a"));
}
+TEST(MatchTest, ContainsChar) {
+ absl::string_view a("abcdefg");
+ absl::string_view b("abcd");
+ EXPECT_TRUE(absl::StrContains(a, 'a'));
+ EXPECT_TRUE(absl::StrContains(a, 'b'));
+ EXPECT_TRUE(absl::StrContains(a, 'e'));
+ EXPECT_FALSE(absl::StrContains(a, 'h'));
+
+ EXPECT_TRUE(absl::StrContains(b, 'a'));
+ EXPECT_TRUE(absl::StrContains(b, 'b'));
+ EXPECT_FALSE(absl::StrContains(b, 'e'));
+ EXPECT_FALSE(absl::StrContains(b, 'h'));
+
+ EXPECT_FALSE(absl::StrContains("", 'a'));
+ EXPECT_FALSE(absl::StrContains("", 'a'));
+}
+
TEST(MatchTest, ContainsNull) {
const std::string s = "foo";
const char* cs = "foo";
diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc
index 3da1059..966d94b 100644
--- a/absl/strings/numbers.cc
+++ b/absl/strings/numbers.cc
@@ -31,8 +31,8 @@
#include <utility>
#include "absl/base/attributes.h"
-#include "absl/base/internal/bits.h"
#include "absl/base/internal/raw_logging.h"
+#include "absl/numeric/bits.h"
#include "absl/strings/ascii.h"
#include "absl/strings/charconv.h"
#include "absl/strings/escaping.h"
@@ -46,8 +46,13 @@
bool SimpleAtof(absl::string_view str, float* out) {
*out = 0.0;
str = StripAsciiWhitespace(str);
+ // std::from_chars doesn't accept an initial +, but SimpleAtof does, so if one
+ // is present, skip it, while avoiding accepting "+-0" as valid.
if (!str.empty() && str[0] == '+') {
str.remove_prefix(1);
+ if (!str.empty() && str[0] == '-') {
+ return false;
+ }
}
auto result = absl::from_chars(str.data(), str.data() + str.size(), *out);
if (result.ec == std::errc::invalid_argument) {
@@ -72,8 +77,13 @@
bool SimpleAtod(absl::string_view str, double* out) {
*out = 0.0;
str = StripAsciiWhitespace(str);
+ // std::from_chars doesn't accept an initial +, but SimpleAtod does, so if one
+ // is present, skip it, while avoiding accepting "+-0" as valid.
if (!str.empty() && str[0] == '+') {
str.remove_prefix(1);
+ if (!str.empty() && str[0] == '-') {
+ return false;
+ }
}
auto result = absl::from_chars(str.data(), str.data() + str.size(), *out);
if (result.ec == std::errc::invalid_argument) {
@@ -303,7 +313,7 @@
uint64_t bits128_up = (bits96_127 >> 32) + (bits64_127 < bits64_95);
if (bits128_up == 0) return {bits64_127, bits0_63};
- int shift = 64 - base_internal::CountLeadingZeros64(bits128_up);
+ auto shift = static_cast<unsigned>(bit_width(bits128_up));
uint64_t lo = (bits0_63 >> shift) + (bits64_127 << (64 - shift));
uint64_t hi = (bits64_127 >> shift) + (bits128_up << (64 - shift));
return {hi, lo};
@@ -334,7 +344,7 @@
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5};
result = Mul32(result, powers_of_five[expfive & 15]);
- int shift = base_internal::CountLeadingZeros64(result.first);
+ int shift = countl_zero(result.first);
if (shift != 0) {
result.first = (result.first << shift) + (result.second >> (64 - shift));
result.second = (result.second << shift);
diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h
index 2e004b4..ffc738f 100644
--- a/absl/strings/numbers.h
+++ b/absl/strings/numbers.h
@@ -1,4 +1,3 @@
-//
// Copyright 2017 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -37,7 +36,6 @@
#include <type_traits>
#include "absl/base/config.h"
-#include "absl/base/internal/bits.h"
#ifdef __SSE4_2__
// TODO(jorg): Remove this when we figure out the right way
// to swap bytes on SSE 4.2 that works with the compilers
@@ -48,6 +46,7 @@
#endif
#include "absl/base/macros.h"
#include "absl/base/port.h"
+#include "absl/numeric/bits.h"
#include "absl/numeric/int128.h"
#include "absl/strings/string_view.h"
@@ -240,18 +239,11 @@
}
#endif
// | 0x1 so that even 0 has 1 digit.
- return 16 - absl::base_internal::CountLeadingZeros64(val | 0x1) / 4;
+ return 16 - countl_zero(val | 0x1) / 4;
}
} // namespace numbers_internal
-// SimpleAtoi()
-//
-// Converts a string to an integer, using `safe_strto?()` functions for actual
-// parsing, returning `true` if successful. The `safe_strto?()` functions apply
-// strict checking; the string must be a base-10 integer, optionally followed or
-// preceded by ASCII whitespace, with a value in the range of the corresponding
-// integer type.
template <typename int_type>
ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out) {
return numbers_internal::safe_strtoi_base(str, out, 10);
diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc
index 4ab67fb..f310310 100644
--- a/absl/strings/numbers_test.cc
+++ b/absl/strings/numbers_test.cc
@@ -46,6 +46,7 @@
namespace {
+using absl::SimpleAtoi;
using absl::numbers_internal::kSixDigitsToBufferSize;
using absl::numbers_internal::safe_strto32_base;
using absl::numbers_internal::safe_strto64_base;
@@ -55,7 +56,6 @@
using absl::strings_internal::Itoa;
using absl::strings_internal::strtouint32_test_cases;
using absl::strings_internal::strtouint64_test_cases;
-using absl::SimpleAtoi;
using testing::Eq;
using testing::MatchesRegex;
@@ -380,7 +380,7 @@
VerifySimpleAtoiGood<uint32_t>(42, 42);
VerifySimpleAtoiGood<unsigned int>(42, 42);
VerifySimpleAtoiGood<int64_t>(-42, -42);
- VerifySimpleAtoiGood<long>(-42, -42); // NOLINT(runtime/int)
+ VerifySimpleAtoiGood<long>(-42, -42); // NOLINT: runtime-int
VerifySimpleAtoiGood<uint64_t>(42, 42);
VerifySimpleAtoiGood<size_t>(42, 42);
VerifySimpleAtoiGood<std::string::size_type>(42, 42);
@@ -392,6 +392,28 @@
EXPECT_TRUE(std::isnan(d));
}
+TEST(NumbersTest, Prefixes) {
+ double d;
+ EXPECT_FALSE(absl::SimpleAtod("++1", &d));
+ EXPECT_FALSE(absl::SimpleAtod("+-1", &d));
+ EXPECT_FALSE(absl::SimpleAtod("-+1", &d));
+ EXPECT_FALSE(absl::SimpleAtod("--1", &d));
+ EXPECT_TRUE(absl::SimpleAtod("-1", &d));
+ EXPECT_EQ(d, -1.);
+ EXPECT_TRUE(absl::SimpleAtod("+1", &d));
+ EXPECT_EQ(d, +1.);
+
+ float f;
+ EXPECT_FALSE(absl::SimpleAtof("++1", &f));
+ EXPECT_FALSE(absl::SimpleAtof("+-1", &f));
+ EXPECT_FALSE(absl::SimpleAtof("-+1", &f));
+ EXPECT_FALSE(absl::SimpleAtof("--1", &f));
+ EXPECT_TRUE(absl::SimpleAtof("-1", &f));
+ EXPECT_EQ(f, -1.f);
+ EXPECT_TRUE(absl::SimpleAtof("+1", &f));
+ EXPECT_EQ(f, +1.f);
+}
+
TEST(NumbersTest, Atoenum) {
enum E01 {
E01_zero = 0,
diff --git a/absl/strings/str_split.h b/absl/strings/str_split.h
index 1ce17f3..bfbca42 100644
--- a/absl/strings/str_split.h
+++ b/absl/strings/str_split.h
@@ -369,6 +369,12 @@
}
};
+template <typename T>
+using EnableSplitIfString =
+ typename std::enable_if<std::is_same<T, std::string>::value ||
+ std::is_same<T, const std::string>::value,
+ int>::type;
+
//------------------------------------------------------------------------------
// StrSplit()
//------------------------------------------------------------------------------
@@ -489,22 +495,50 @@
// Try not to depend on this distinction because the bug may one day be fixed.
template <typename Delimiter>
strings_internal::Splitter<
- typename strings_internal::SelectDelimiter<Delimiter>::type, AllowEmpty>
+ typename strings_internal::SelectDelimiter<Delimiter>::type, AllowEmpty,
+ absl::string_view>
StrSplit(strings_internal::ConvertibleToStringView text, Delimiter d) {
using DelimiterType =
typename strings_internal::SelectDelimiter<Delimiter>::type;
- return strings_internal::Splitter<DelimiterType, AllowEmpty>(
+ return strings_internal::Splitter<DelimiterType, AllowEmpty,
+ absl::string_view>(
+ text.value(), DelimiterType(d), AllowEmpty());
+}
+
+template <typename Delimiter, typename StringType,
+ EnableSplitIfString<StringType> = 0>
+strings_internal::Splitter<
+ typename strings_internal::SelectDelimiter<Delimiter>::type, AllowEmpty,
+ std::string>
+StrSplit(StringType&& text, Delimiter d) {
+ using DelimiterType =
+ typename strings_internal::SelectDelimiter<Delimiter>::type;
+ return strings_internal::Splitter<DelimiterType, AllowEmpty, std::string>(
std::move(text), DelimiterType(d), AllowEmpty());
}
template <typename Delimiter, typename Predicate>
strings_internal::Splitter<
- typename strings_internal::SelectDelimiter<Delimiter>::type, Predicate>
+ typename strings_internal::SelectDelimiter<Delimiter>::type, Predicate,
+ absl::string_view>
StrSplit(strings_internal::ConvertibleToStringView text, Delimiter d,
Predicate p) {
using DelimiterType =
typename strings_internal::SelectDelimiter<Delimiter>::type;
- return strings_internal::Splitter<DelimiterType, Predicate>(
+ return strings_internal::Splitter<DelimiterType, Predicate,
+ absl::string_view>(
+ text.value(), DelimiterType(d), std::move(p));
+}
+
+template <typename Delimiter, typename Predicate, typename StringType,
+ EnableSplitIfString<StringType> = 0>
+strings_internal::Splitter<
+ typename strings_internal::SelectDelimiter<Delimiter>::type, Predicate,
+ std::string>
+StrSplit(StringType&& text, Delimiter d, Predicate p) {
+ using DelimiterType =
+ typename strings_internal::SelectDelimiter<Delimiter>::type;
+ return strings_internal::Splitter<DelimiterType, Predicate, std::string>(
std::move(text), DelimiterType(d), std::move(p));
}
diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc
index dcebb15..643af8f 100644
--- a/absl/strings/string_view_test.cc
+++ b/absl/strings/string_view_test.cc
@@ -915,9 +915,9 @@
EXPECT_EQ(abc.at(1), 'b');
EXPECT_EQ(abc.at(2), 'c');
#ifdef ABSL_HAVE_EXCEPTIONS
- EXPECT_THROW(abc.at(3), std::out_of_range);
+ EXPECT_THROW((void)abc.at(3), std::out_of_range);
#else
- ABSL_EXPECT_DEATH_IF_SUPPORTED(abc.at(3), "absl::string_view::at");
+ ABSL_EXPECT_DEATH_IF_SUPPORTED((void)abc.at(3), "absl::string_view::at");
#endif
}
diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel
index cd4009a..5ce1695 100644
--- a/absl/synchronization/BUILD.bazel
+++ b/absl/synchronization/BUILD.bazel
@@ -88,7 +88,8 @@
],
copts = ABSL_DEFAULT_COPTS,
linkopts = select({
- "//absl:windows": [],
+ "//absl:msvc_compiler": [],
+ "//absl:clang-cl_compiler": [],
"//absl:wasm": [],
"//conditions:default": ["-pthread"],
}) + ABSL_DEFAULT_LINKOPTS,
diff --git a/absl/synchronization/internal/per_thread_sem.cc b/absl/synchronization/internal/per_thread_sem.cc
index 821ca9b..a603178 100644
--- a/absl/synchronization/internal/per_thread_sem.cc
+++ b/absl/synchronization/internal/per_thread_sem.cc
@@ -68,12 +68,12 @@
extern "C" {
-ABSL_ATTRIBUTE_WEAK void AbslInternalPerThreadSemPost(
+ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemPost)(
absl::base_internal::ThreadIdentity *identity) {
absl::synchronization_internal::Waiter::GetWaiter(identity)->Post();
}
-ABSL_ATTRIBUTE_WEAK bool AbslInternalPerThreadSemWait(
+ABSL_ATTRIBUTE_WEAK bool ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemWait)(
absl::synchronization_internal::KernelTimeout t) {
bool timeout = false;
absl::base_internal::ThreadIdentity *identity;
diff --git a/absl/synchronization/internal/per_thread_sem.h b/absl/synchronization/internal/per_thread_sem.h
index 2228b6e..7beae8e 100644
--- a/absl/synchronization/internal/per_thread_sem.h
+++ b/absl/synchronization/internal/per_thread_sem.h
@@ -96,20 +96,20 @@
// By changing our extension points to be extern "C", we dodge this
// check.
extern "C" {
-void AbslInternalPerThreadSemPost(
+void ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemPost)(
absl::base_internal::ThreadIdentity* identity);
-bool AbslInternalPerThreadSemWait(
+bool ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemWait)(
absl::synchronization_internal::KernelTimeout t);
} // extern "C"
void absl::synchronization_internal::PerThreadSem::Post(
absl::base_internal::ThreadIdentity* identity) {
- AbslInternalPerThreadSemPost(identity);
+ ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemPost)(identity);
}
bool absl::synchronization_internal::PerThreadSem::Wait(
absl::synchronization_internal::KernelTimeout t) {
- return AbslInternalPerThreadSemWait(t);
+ return ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemWait)(t);
}
#endif // ABSL_SYNCHRONIZATION_INTERNAL_PER_THREAD_SEM_H_
diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc
index 9e01393..30264a3 100644
--- a/absl/synchronization/mutex.cc
+++ b/absl/synchronization/mutex.cc
@@ -70,7 +70,9 @@
using absl::synchronization_internal::PerThreadSem;
extern "C" {
-ABSL_ATTRIBUTE_WEAK void AbslInternalMutexYield() { std::this_thread::yield(); }
+ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalMutexYield)() {
+ std::this_thread::yield();
+}
} // extern "C"
namespace absl {
@@ -124,35 +126,44 @@
symbolizer.Store(fn);
}
+namespace {
+// Represents the strategy for spin and yield.
+// See the comment in GetMutexGlobals() for more information.
+enum DelayMode { AGGRESSIVE, GENTLE };
+
struct ABSL_CACHELINE_ALIGNED MutexGlobals {
absl::once_flag once;
- int num_cpus = 0;
int spinloop_iterations = 0;
+ int32_t mutex_sleep_limit[2] = {};
};
-static const MutexGlobals& GetMutexGlobals() {
+const MutexGlobals &GetMutexGlobals() {
ABSL_CONST_INIT static MutexGlobals data;
absl::base_internal::LowLevelCallOnce(&data.once, [&]() {
- data.num_cpus = absl::base_internal::NumCPUs();
- data.spinloop_iterations = data.num_cpus > 1 ? 1500 : 0;
+ const int num_cpus = absl::base_internal::NumCPUs();
+ data.spinloop_iterations = num_cpus > 1 ? 1500 : 0;
+ // If this a uniprocessor, only yield/sleep. Otherwise, if the mode is
+ // aggressive then spin many times before yielding. If the mode is
+ // gentle then spin only a few times before yielding. Aggressive spinning
+ // is used to ensure that an Unlock() call, which must get the spin lock
+ // for any thread to make progress gets it without undue delay.
+ if (num_cpus > 1) {
+ data.mutex_sleep_limit[AGGRESSIVE] = 5000;
+ data.mutex_sleep_limit[GENTLE] = 250;
+ } else {
+ data.mutex_sleep_limit[AGGRESSIVE] = 0;
+ data.mutex_sleep_limit[GENTLE] = 0;
+ }
});
return data;
}
-
-// Spinlock delay on iteration c. Returns new c.
-namespace {
- enum DelayMode { AGGRESSIVE, GENTLE };
-};
+} // namespace
namespace synchronization_internal {
+// Returns the Mutex delay on iteration `c` depending on the given `mode`.
+// The returned value should be used as `c` for the next call to `MutexDelay`.
int MutexDelay(int32_t c, int mode) {
- // If this a uniprocessor, only yield/sleep. Otherwise, if the mode is
- // aggressive then spin many times before yielding. If the mode is
- // gentle then spin only a few times before yielding. Aggressive spinning is
- // used to ensure that an Unlock() call, which must get the spin lock for
- // any thread to make progress gets it without undue delay.
- const int32_t limit =
- GetMutexGlobals().num_cpus > 1 ? (mode == AGGRESSIVE ? 5000 : 250) : 0;
+ const int32_t limit = GetMutexGlobals().mutex_sleep_limit[mode];
if (c < limit) {
// Spin.
c++;
@@ -161,7 +172,7 @@
ABSL_TSAN_MUTEX_PRE_DIVERT(nullptr, 0);
if (c == limit) {
// Yield once.
- AbslInternalMutexYield();
+ ABSL_INTERNAL_C_SYMBOL(AbslInternalMutexYield)();
c++;
} else {
// Then wait.
@@ -752,11 +763,13 @@
synch_deadlock_detection.store(mode, std::memory_order_release);
}
-// Return true iff threads x and y are waiting on the same condition for the
-// same type of lock. Requires that x and y be waiting on the same Mutex
-// queue.
-static bool MuSameCondition(PerThreadSynch *x, PerThreadSynch *y) {
- return x->waitp->how == y->waitp->how &&
+// Return true iff threads x and y are part of the same equivalence
+// class of waiters. An equivalence class is defined as the set of
+// waiters with the same condition, type of lock, and thread priority.
+//
+// Requires that x and y be waiting on the same Mutex queue.
+static bool MuEquivalentWaiter(PerThreadSynch *x, PerThreadSynch *y) {
+ return x->waitp->how == y->waitp->how && x->priority == y->priority &&
Condition::GuaranteedEqual(x->waitp->cond, y->waitp->cond);
}
@@ -775,18 +788,19 @@
// - invalid (iff x is not in a Mutex wait queue),
// - null, or
// - a pointer to a distinct thread waiting later in the same Mutex queue
-// such that all threads in [x, x->skip] have the same condition and
-// lock type (MuSameCondition() is true for all pairs in [x, x->skip]).
+// such that all threads in [x, x->skip] have the same condition, priority
+// and lock type (MuEquivalentWaiter() is true for all pairs in [x,
+// x->skip]).
// In addition, if x->skip is valid, (x->may_skip || x->skip == null)
//
-// By the spec of MuSameCondition(), it is not necessary when removing the
+// By the spec of MuEquivalentWaiter(), it is not necessary when removing the
// first runnable thread y from the front a Mutex queue to adjust the skip
// field of another thread x because if x->skip==y, x->skip must (have) become
// invalid before y is removed. The function TryRemove can remove a specified
// thread from an arbitrary position in the queue whether runnable or not, so
// it fixes up skip fields that would otherwise be left dangling.
// The statement
-// if (x->may_skip && MuSameCondition(x, x->next)) { x->skip = x->next; }
+// if (x->may_skip && MuEquivalentWaiter(x, x->next)) { x->skip = x->next; }
// maintains the invariant provided x is not the last waiter in a Mutex queue
// The statement
// if (x->skip != null) { x->skip = x->skip->skip; }
@@ -920,24 +934,17 @@
if (s->priority > head->priority) { // s's priority is above head's
// try to put s in priority-fifo order, or failing that at the front.
if (!head->maybe_unlocking) {
- // No unlocker can be scanning the queue, so we can insert between
- // skip-chains, and within a skip-chain if it has the same condition as
- // s. We insert in priority-fifo order, examining the end of every
- // skip-chain, plus every element with the same condition as s.
+ // No unlocker can be scanning the queue, so we can insert into the
+ // middle of the queue.
+ //
+ // Within a skip chain, all waiters have the same priority, so we can
+ // skip forward through the chains until we find one with a lower
+ // priority than the waiter to be enqueued.
PerThreadSynch *advance_to = head; // next value of enqueue_after
- PerThreadSynch *cur; // successor of enqueue_after
do {
enqueue_after = advance_to;
- cur = enqueue_after->next; // this advance ensures progress
- advance_to = Skip(cur); // normally, advance to end of skip chain
- // (side-effect: optimizes skip chain)
- if (advance_to != cur && s->priority > advance_to->priority &&
- MuSameCondition(s, cur)) {
- // but this skip chain is not a singleton, s has higher priority
- // than its tail and has the same condition as the chain,
- // so we can insert within the skip-chain
- advance_to = cur; // advance by just one
- }
+ // (side-effect: optimizes skip chain)
+ advance_to = Skip(enqueue_after->next);
} while (s->priority <= advance_to->priority);
// termination guaranteed because s->priority > head->priority
// and head is the end of a skip chain
@@ -956,21 +963,21 @@
// enqueue_after can be: head, Skip(...), or cur.
// The first two imply enqueue_after->skip == nullptr, and
- // the last is used only if MuSameCondition(s, cur).
+ // the last is used only if MuEquivalentWaiter(s, cur).
// We require this because clearing enqueue_after->skip
// is impossible; enqueue_after's predecessors might also
// incorrectly skip over s if we were to allow other
// insertion points.
- ABSL_RAW_CHECK(
- enqueue_after->skip == nullptr || MuSameCondition(enqueue_after, s),
- "Mutex Enqueue failure");
+ ABSL_RAW_CHECK(enqueue_after->skip == nullptr ||
+ MuEquivalentWaiter(enqueue_after, s),
+ "Mutex Enqueue failure");
if (enqueue_after != head && enqueue_after->may_skip &&
- MuSameCondition(enqueue_after, enqueue_after->next)) {
+ MuEquivalentWaiter(enqueue_after, enqueue_after->next)) {
// enqueue_after can skip to its new successor, s
enqueue_after->skip = enqueue_after->next;
}
- if (MuSameCondition(s, s->next)) { // s->may_skip is known to be true
+ if (MuEquivalentWaiter(s, s->next)) { // s->may_skip is known to be true
s->skip = s->next; // s may skip to its successor
}
} else { // enqueue not done any other way, so
@@ -980,7 +987,7 @@
head->next = s;
s->readers = head->readers; // reader count is from previous head
s->maybe_unlocking = head->maybe_unlocking; // same for unlock hint
- if (head->may_skip && MuSameCondition(head, s)) {
+ if (head->may_skip && MuEquivalentWaiter(head, s)) {
// head now has successor; may skip
head->skip = s;
}
@@ -1000,7 +1007,7 @@
pw->next = w->next; // snip w out of list
if (head == w) { // we removed the head
head = (pw == w) ? nullptr : pw; // either emptied list, or pw is new head
- } else if (pw != head && MuSameCondition(pw, pw->next)) {
+ } else if (pw != head && MuEquivalentWaiter(pw, pw->next)) {
// pw can skip to its new successor
if (pw->next->skip !=
nullptr) { // either skip to its successors skip target
@@ -1070,11 +1077,13 @@
PerThreadSynch *w;
if ((w = pw->next) != s) { // search for thread,
do { // processing at least one element
- if (!MuSameCondition(s, w)) { // seeking different condition
+ // If the current element isn't equivalent to the waiter to be
+ // removed, we can skip the entire chain.
+ if (!MuEquivalentWaiter(s, w)) {
pw = Skip(w); // so skip all that won't match
// we don't have to worry about dangling skip fields
// in the threads we skipped; none can point to s
- // because their condition differs from s
+ // because they are in a different equivalence class.
} else { // seeking same condition
FixSkip(w, s); // fix up any skip pointer from w to s
pw = w;
@@ -1365,7 +1374,9 @@
len += static_cast<int>(strlen(&b->buf[len]));
}
}
- ABSL_RAW_LOG(ERROR, "Acquiring %p Mutexes held: %s",
+ ABSL_RAW_LOG(ERROR,
+ "Acquiring absl::Mutex %p while holding %s; a cycle in the "
+ "historical lock ordering graph has been observed",
static_cast<void *>(mu), b->buf);
ABSL_RAW_LOG(ERROR, "Cycle: ");
int path_len = deadlock_graph->FindPath(
@@ -2139,7 +2150,7 @@
!old_h->may_skip) { // we used old_h as a terminator
old_h->may_skip = true; // allow old_h to skip once more
ABSL_RAW_CHECK(old_h->skip == nullptr, "illegal skip from head");
- if (h != old_h && MuSameCondition(old_h, old_h->next)) {
+ if (h != old_h && MuEquivalentWaiter(old_h, old_h->next)) {
old_h->skip = old_h->next; // old_h not head & can skip to successor
}
}
diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h
index 598d1e0..73c5bf5 100644
--- a/absl/synchronization/mutex.h
+++ b/absl/synchronization/mutex.h
@@ -147,7 +147,7 @@
//
// Example usage:
// namespace foo {
- // ABSL_CONST_INIT Mutex mu(absl::kConstInit);
+ // ABSL_CONST_INIT absl::Mutex mu(absl::kConstInit);
// }
explicit constexpr Mutex(absl::ConstInitType);
@@ -162,7 +162,7 @@
// Mutex::Unlock()
//
// Releases this `Mutex` and returns it from the exclusive/write state to the
- // free state. Caller must hold the `Mutex` exclusively.
+ // free state. Calling thread must hold the `Mutex` exclusively.
void Unlock() ABSL_UNLOCK_FUNCTION();
// Mutex::TryLock()
@@ -1078,7 +1078,7 @@
// By changing our extension points to be extern "C", we dodge this
// check.
extern "C" {
-void AbslInternalMutexYield();
+void ABSL_INTERNAL_C_SYMBOL(AbslInternalMutexYield)();
} // extern "C"
#endif // ABSL_SYNCHRONIZATION_MUTEX_H_
diff --git a/absl/synchronization/mutex_benchmark.cc b/absl/synchronization/mutex_benchmark.cc
index 933ea14..e35aed8 100644
--- a/absl/synchronization/mutex_benchmark.cc
+++ b/absl/synchronization/mutex_benchmark.cc
@@ -61,8 +61,124 @@
std::mutex* mu_;
};
+// RAII object to change the Mutex priority of the running thread.
+class ScopedThreadMutexPriority {
+ public:
+ explicit ScopedThreadMutexPriority(int priority) {
+ absl::base_internal::ThreadIdentity* identity =
+ absl::synchronization_internal::GetOrCreateCurrentThreadIdentity();
+ identity->per_thread_synch.priority = priority;
+ // Bump next_priority_read_cycles to the infinite future so that the
+ // implementation doesn't re-read the thread's actual scheduler priority
+ // and replace our temporary scoped priority.
+ identity->per_thread_synch.next_priority_read_cycles =
+ std::numeric_limits<int64_t>::max();
+ }
+ ~ScopedThreadMutexPriority() {
+ // Reset the "next priority read time" back to the infinite past so that
+ // the next time the Mutex implementation wants to know this thread's
+ // priority, it re-reads it from the OS instead of using our overridden
+ // priority.
+ absl::synchronization_internal::GetOrCreateCurrentThreadIdentity()
+ ->per_thread_synch.next_priority_read_cycles =
+ std::numeric_limits<int64_t>::min();
+ }
+};
+
+void BM_MutexEnqueue(benchmark::State& state) {
+ // In the "multiple priorities" variant of the benchmark, one of the
+ // threads runs with Mutex priority 0 while the rest run at elevated priority.
+ // This benchmarks the performance impact of the presence of a low priority
+ // waiter when a higher priority waiter adds itself of the queue
+ // (b/175224064).
+ //
+ // NOTE: The actual scheduler priority is not modified in this benchmark:
+ // all of the threads get CPU slices with the same priority. Only the
+ // Mutex queueing behavior is modified.
+ const bool multiple_priorities = state.range(0);
+ ScopedThreadMutexPriority priority_setter(
+ (multiple_priorities && state.thread_index != 0) ? 1 : 0);
+
+ struct Shared {
+ absl::Mutex mu;
+ std::atomic<int> looping_threads{0};
+ std::atomic<int> blocked_threads{0};
+ std::atomic<bool> thread_has_mutex{false};
+ };
+ static Shared* shared = new Shared;
+
+ // Set up 'blocked_threads' to count how many threads are currently blocked
+ // in Abseil synchronization code.
+ //
+ // NOTE: Blocking done within the Google Benchmark library itself (e.g.
+ // the barrier which synchronizes threads entering and exiting the benchmark
+ // loop) does _not_ get registered in this counter. This is because Google
+ // Benchmark uses its own synchronization primitives based on std::mutex, not
+ // Abseil synchronization primitives. If at some point the benchmark library
+ // merges into Abseil, this code may break.
+ absl::synchronization_internal::PerThreadSem::SetThreadBlockedCounter(
+ &shared->blocked_threads);
+
+ // The benchmark framework may run several iterations in the same process,
+ // reusing the same static-initialized 'shared' object. Given the semantics
+ // of the members, here, we expect everything to be reset to zero by the
+ // end of any iteration. Assert that's the case, just to be sure.
+ ABSL_RAW_CHECK(
+ shared->looping_threads.load(std::memory_order_relaxed) == 0 &&
+ shared->blocked_threads.load(std::memory_order_relaxed) == 0 &&
+ !shared->thread_has_mutex.load(std::memory_order_relaxed),
+ "Shared state isn't zeroed at start of benchmark iteration");
+
+ static constexpr int kBatchSize = 1000;
+ while (state.KeepRunningBatch(kBatchSize)) {
+ shared->looping_threads.fetch_add(1);
+ for (int i = 0; i < kBatchSize; i++) {
+ {
+ absl::MutexLock l(&shared->mu);
+ shared->thread_has_mutex.store(true, std::memory_order_relaxed);
+ // Spin until all other threads are either out of the benchmark loop
+ // or blocked on the mutex. This ensures that the mutex queue is kept
+ // at its maximal length to benchmark the performance of queueing on
+ // a highly contended mutex.
+ while (shared->looping_threads.load(std::memory_order_relaxed) -
+ shared->blocked_threads.load(std::memory_order_relaxed) !=
+ 1) {
+ }
+ shared->thread_has_mutex.store(false);
+ }
+ // Spin until some other thread has acquired the mutex before we block
+ // again. This ensures that we always go through the slow (queueing)
+ // acquisition path rather than reacquiring the mutex we just released.
+ while (!shared->thread_has_mutex.load(std::memory_order_relaxed) &&
+ shared->looping_threads.load(std::memory_order_relaxed) > 1) {
+ }
+ }
+ // The benchmark framework uses a barrier to ensure that all of the threads
+ // complete their benchmark loop together before any of the threads exit
+ // the loop. So, we need to remove ourselves from the "looping threads"
+ // counter here before potentially blocking on that barrier. Otherwise,
+ // another thread spinning above might wait forever for this thread to
+ // block on the mutex while we in fact are waiting to exit.
+ shared->looping_threads.fetch_add(-1);
+ }
+ absl::synchronization_internal::PerThreadSem::SetThreadBlockedCounter(
+ nullptr);
+}
+
+BENCHMARK(BM_MutexEnqueue)
+ ->Threads(4)
+ ->Threads(64)
+ ->Threads(128)
+ ->Threads(512)
+ ->ArgName("multiple_priorities")
+ ->Arg(false)
+ ->Arg(true);
+
template <typename MutexType>
void BM_Contended(benchmark::State& state) {
+ int priority = state.thread_index % state.range(1);
+ ScopedThreadMutexPriority priority_setter(priority);
+
struct Shared {
MutexType mu;
int data = 0;
@@ -85,81 +201,51 @@
DelayNs(state.range(0), &shared->data);
}
}
+void SetupBenchmarkArgs(benchmark::internal::Benchmark* bm,
+ bool do_test_priorities) {
+ const int max_num_priorities = do_test_priorities ? 2 : 1;
+ bm->UseRealTime()
+ // ThreadPerCpu poorly handles non-power-of-two CPU counts.
+ ->Threads(1)
+ ->Threads(2)
+ ->Threads(4)
+ ->Threads(6)
+ ->Threads(8)
+ ->Threads(12)
+ ->Threads(16)
+ ->Threads(24)
+ ->Threads(32)
+ ->Threads(48)
+ ->Threads(64)
+ ->Threads(96)
+ ->Threads(128)
+ ->Threads(192)
+ ->Threads(256)
+ ->ArgNames({"cs_ns", "num_prios"});
+ // Some empirically chosen amounts of work in critical section.
+ // 1 is low contention, 2000 is high contention and few values in between.
+ for (int critical_section_ns : {1, 20, 50, 200, 2000}) {
+ for (int num_priorities = 1; num_priorities <= max_num_priorities;
+ num_priorities++) {
+ bm->ArgPair(critical_section_ns, num_priorities);
+ }
+ }
+}
BENCHMARK_TEMPLATE(BM_Contended, absl::Mutex)
- ->UseRealTime()
- // ThreadPerCpu poorly handles non-power-of-two CPU counts.
- ->Threads(1)
- ->Threads(2)
- ->Threads(4)
- ->Threads(6)
- ->Threads(8)
- ->Threads(12)
- ->Threads(16)
- ->Threads(24)
- ->Threads(32)
- ->Threads(48)
- ->Threads(64)
- ->Threads(96)
- ->Threads(128)
- ->Threads(192)
- ->Threads(256)
- // Some empirically chosen amounts of work in critical section.
- // 1 is low contention, 200 is high contention and few values in between.
- ->Arg(1)
- ->Arg(20)
- ->Arg(50)
- ->Arg(200);
+ ->Apply([](benchmark::internal::Benchmark* bm) {
+ SetupBenchmarkArgs(bm, /*do_test_priorities=*/true);
+ });
BENCHMARK_TEMPLATE(BM_Contended, absl::base_internal::SpinLock)
- ->UseRealTime()
- // ThreadPerCpu poorly handles non-power-of-two CPU counts.
- ->Threads(1)
- ->Threads(2)
- ->Threads(4)
- ->Threads(6)
- ->Threads(8)
- ->Threads(12)
- ->Threads(16)
- ->Threads(24)
- ->Threads(32)
- ->Threads(48)
- ->Threads(64)
- ->Threads(96)
- ->Threads(128)
- ->Threads(192)
- ->Threads(256)
- // Some empirically chosen amounts of work in critical section.
- // 1 is low contention, 200 is high contention and few values in between.
- ->Arg(1)
- ->Arg(20)
- ->Arg(50)
- ->Arg(200);
+ ->Apply([](benchmark::internal::Benchmark* bm) {
+ SetupBenchmarkArgs(bm, /*do_test_priorities=*/false);
+ });
BENCHMARK_TEMPLATE(BM_Contended, std::mutex)
- ->UseRealTime()
- // ThreadPerCpu poorly handles non-power-of-two CPU counts.
- ->Threads(1)
- ->Threads(2)
- ->Threads(4)
- ->Threads(6)
- ->Threads(8)
- ->Threads(12)
- ->Threads(16)
- ->Threads(24)
- ->Threads(32)
- ->Threads(48)
- ->Threads(64)
- ->Threads(96)
- ->Threads(128)
- ->Threads(192)
- ->Threads(256)
- // Some empirically chosen amounts of work in critical section.
- // 1 is low contention, 200 is high contention and few values in between.
- ->Arg(1)
- ->Arg(20)
- ->Arg(50)
- ->Arg(200);
+ ->Apply([](benchmark::internal::Benchmark* bm) {
+ SetupBenchmarkArgs(bm, /*do_test_priorities=*/false);
+ });
// Measure the overhead of conditions on mutex release (when they must be
// evaluated). Mutex has (some) support for equivalence classes allowing
diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel
index 991241a..3e25ca2 100644
--- a/absl/time/BUILD.bazel
+++ b/absl/time/BUILD.bazel
@@ -119,6 +119,7 @@
":time",
"//absl/base",
"//absl/base:core_headers",
+ "//absl/flags:flag",
"//absl/hash",
"@com_github_google_benchmark//:benchmark_main",
],
diff --git a/absl/time/clock.cc b/absl/time/clock.cc
index 6862e01..7b204c4 100644
--- a/absl/time/clock.cc
+++ b/absl/time/clock.cc
@@ -15,6 +15,7 @@
#include "absl/time/clock.h"
#include "absl/base/attributes.h"
+#include "absl/base/optimization.h"
#ifdef _WIN32
#include <windows.h>
@@ -85,13 +86,6 @@
::absl::time_internal::UnscaledCycleClockWrapperForGetCurrentTime::Now()
#endif
-// The following counters are used only by the test code.
-static int64_t stats_initializations;
-static int64_t stats_reinitializations;
-static int64_t stats_calibrations;
-static int64_t stats_slow_paths;
-static int64_t stats_fast_slow_paths;
-
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace time_internal {
@@ -105,72 +99,6 @@
// uint64_t is used in this module to provide an extra bit in multiplications
-// Return the time in ns as told by the kernel interface. Place in *cycleclock
-// the value of the cycleclock at about the time of the syscall.
-// This call represents the time base that this module synchronizes to.
-// Ensures that *cycleclock does not step back by up to (1 << 16) from
-// last_cycleclock, to discard small backward counter steps. (Larger steps are
-// assumed to be complete resyncs, which shouldn't happen. If they do, a full
-// reinitialization of the outer algorithm should occur.)
-static int64_t GetCurrentTimeNanosFromKernel(uint64_t last_cycleclock,
- uint64_t *cycleclock) {
- // We try to read clock values at about the same time as the kernel clock.
- // This value gets adjusted up or down as estimate of how long that should
- // take, so we can reject attempts that take unusually long.
- static std::atomic<uint64_t> approx_syscall_time_in_cycles{10 * 1000};
-
- uint64_t local_approx_syscall_time_in_cycles = // local copy
- approx_syscall_time_in_cycles.load(std::memory_order_relaxed);
-
- int64_t current_time_nanos_from_system;
- uint64_t before_cycles;
- uint64_t after_cycles;
- uint64_t elapsed_cycles;
- int loops = 0;
- do {
- before_cycles = GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW();
- current_time_nanos_from_system = GET_CURRENT_TIME_NANOS_FROM_SYSTEM();
- after_cycles = GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW();
- // elapsed_cycles is unsigned, so is large on overflow
- elapsed_cycles = after_cycles - before_cycles;
- if (elapsed_cycles >= local_approx_syscall_time_in_cycles &&
- ++loops == 20) { // clock changed frequencies? Back off.
- loops = 0;
- if (local_approx_syscall_time_in_cycles < 1000 * 1000) {
- local_approx_syscall_time_in_cycles =
- (local_approx_syscall_time_in_cycles + 1) << 1;
- }
- approx_syscall_time_in_cycles.store(
- local_approx_syscall_time_in_cycles,
- std::memory_order_relaxed);
- }
- } while (elapsed_cycles >= local_approx_syscall_time_in_cycles ||
- last_cycleclock - after_cycles < (static_cast<uint64_t>(1) << 16));
-
- // Number of times in a row we've seen a kernel time call take substantially
- // less than approx_syscall_time_in_cycles.
- static std::atomic<uint32_t> seen_smaller{ 0 };
-
- // Adjust approx_syscall_time_in_cycles to be within a factor of 2
- // of the typical time to execute one iteration of the loop above.
- if ((local_approx_syscall_time_in_cycles >> 1) < elapsed_cycles) {
- // measured time is no smaller than half current approximation
- seen_smaller.store(0, std::memory_order_relaxed);
- } else if (seen_smaller.fetch_add(1, std::memory_order_relaxed) >= 3) {
- // smaller delays several times in a row; reduce approximation by 12.5%
- const uint64_t new_approximation =
- local_approx_syscall_time_in_cycles -
- (local_approx_syscall_time_in_cycles >> 3);
- approx_syscall_time_in_cycles.store(new_approximation,
- std::memory_order_relaxed);
- seen_smaller.store(0, std::memory_order_relaxed);
- }
-
- *cycleclock = after_cycles;
- return current_time_nanos_from_system;
-}
-
-
// ---------------------------------------------------------------------
// An implementation of reader-write locks that use no atomic ops in the read
// case. This is a generalization of Lamport's method for reading a multiword
@@ -222,32 +150,110 @@
kMinNSBetweenSamples,
"cannot represent kMaxBetweenSamplesNSScaled");
-// A reader-writer lock protecting the static locations below.
-// See SeqAcquire() and SeqRelease() above.
-ABSL_CONST_INIT static absl::base_internal::SpinLock lock(
- absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY);
-ABSL_CONST_INIT static std::atomic<uint64_t> seq(0);
-
// data from a sample of the kernel's time value
struct TimeSampleAtomic {
- std::atomic<uint64_t> raw_ns; // raw kernel time
- std::atomic<uint64_t> base_ns; // our estimate of time
- std::atomic<uint64_t> base_cycles; // cycle counter reading
- std::atomic<uint64_t> nsscaled_per_cycle; // cycle period
+ std::atomic<uint64_t> raw_ns{0}; // raw kernel time
+ std::atomic<uint64_t> base_ns{0}; // our estimate of time
+ std::atomic<uint64_t> base_cycles{0}; // cycle counter reading
+ std::atomic<uint64_t> nsscaled_per_cycle{0}; // cycle period
// cycles before we'll sample again (a scaled reciprocal of the period,
// to avoid a division on the fast path).
- std::atomic<uint64_t> min_cycles_per_sample;
+ std::atomic<uint64_t> min_cycles_per_sample{0};
};
// Same again, but with non-atomic types
struct TimeSample {
- uint64_t raw_ns; // raw kernel time
- uint64_t base_ns; // our estimate of time
- uint64_t base_cycles; // cycle counter reading
- uint64_t nsscaled_per_cycle; // cycle period
- uint64_t min_cycles_per_sample; // approx cycles before next sample
+ uint64_t raw_ns = 0; // raw kernel time
+ uint64_t base_ns = 0; // our estimate of time
+ uint64_t base_cycles = 0; // cycle counter reading
+ uint64_t nsscaled_per_cycle = 0; // cycle period
+ uint64_t min_cycles_per_sample = 0; // approx cycles before next sample
};
-static struct TimeSampleAtomic last_sample; // the last sample; under seq
+struct ABSL_CACHELINE_ALIGNED TimeState {
+ std::atomic<uint64_t> seq{0};
+ TimeSampleAtomic last_sample; // the last sample; under seq
+
+ // The following counters are used only by the test code.
+ int64_t stats_initializations{0};
+ int64_t stats_reinitializations{0};
+ int64_t stats_calibrations{0};
+ int64_t stats_slow_paths{0};
+ int64_t stats_fast_slow_paths{0};
+
+ uint64_t last_now_cycles ABSL_GUARDED_BY(lock){0};
+
+ // Used by GetCurrentTimeNanosFromKernel().
+ // We try to read clock values at about the same time as the kernel clock.
+ // This value gets adjusted up or down as estimate of how long that should
+ // take, so we can reject attempts that take unusually long.
+ std::atomic<uint64_t> approx_syscall_time_in_cycles{10 * 1000};
+ // Number of times in a row we've seen a kernel time call take substantially
+ // less than approx_syscall_time_in_cycles.
+ std::atomic<uint32_t> kernel_time_seen_smaller{0};
+
+ // A reader-writer lock protecting the static locations below.
+ // See SeqAcquire() and SeqRelease() above.
+ absl::base_internal::SpinLock lock{absl::kConstInit,
+ base_internal::SCHEDULE_KERNEL_ONLY};
+};
+ABSL_CONST_INIT static TimeState time_state{};
+
+// Return the time in ns as told by the kernel interface. Place in *cycleclock
+// the value of the cycleclock at about the time of the syscall.
+// This call represents the time base that this module synchronizes to.
+// Ensures that *cycleclock does not step back by up to (1 << 16) from
+// last_cycleclock, to discard small backward counter steps. (Larger steps are
+// assumed to be complete resyncs, which shouldn't happen. If they do, a full
+// reinitialization of the outer algorithm should occur.)
+static int64_t GetCurrentTimeNanosFromKernel(uint64_t last_cycleclock,
+ uint64_t *cycleclock)
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(time_state.lock) {
+ uint64_t local_approx_syscall_time_in_cycles = // local copy
+ time_state.approx_syscall_time_in_cycles.load(std::memory_order_relaxed);
+
+ int64_t current_time_nanos_from_system;
+ uint64_t before_cycles;
+ uint64_t after_cycles;
+ uint64_t elapsed_cycles;
+ int loops = 0;
+ do {
+ before_cycles = GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW();
+ current_time_nanos_from_system = GET_CURRENT_TIME_NANOS_FROM_SYSTEM();
+ after_cycles = GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW();
+ // elapsed_cycles is unsigned, so is large on overflow
+ elapsed_cycles = after_cycles - before_cycles;
+ if (elapsed_cycles >= local_approx_syscall_time_in_cycles &&
+ ++loops == 20) { // clock changed frequencies? Back off.
+ loops = 0;
+ if (local_approx_syscall_time_in_cycles < 1000 * 1000) {
+ local_approx_syscall_time_in_cycles =
+ (local_approx_syscall_time_in_cycles + 1) << 1;
+ }
+ time_state.approx_syscall_time_in_cycles.store(
+ local_approx_syscall_time_in_cycles, std::memory_order_relaxed);
+ }
+ } while (elapsed_cycles >= local_approx_syscall_time_in_cycles ||
+ last_cycleclock - after_cycles < (static_cast<uint64_t>(1) << 16));
+
+ // Adjust approx_syscall_time_in_cycles to be within a factor of 2
+ // of the typical time to execute one iteration of the loop above.
+ if ((local_approx_syscall_time_in_cycles >> 1) < elapsed_cycles) {
+ // measured time is no smaller than half current approximation
+ time_state.kernel_time_seen_smaller.store(0, std::memory_order_relaxed);
+ } else if (time_state.kernel_time_seen_smaller.fetch_add(
+ 1, std::memory_order_relaxed) >= 3) {
+ // smaller delays several times in a row; reduce approximation by 12.5%
+ const uint64_t new_approximation =
+ local_approx_syscall_time_in_cycles -
+ (local_approx_syscall_time_in_cycles >> 3);
+ time_state.approx_syscall_time_in_cycles.store(new_approximation,
+ std::memory_order_relaxed);
+ time_state.kernel_time_seen_smaller.store(0, std::memory_order_relaxed);
+ }
+
+ *cycleclock = after_cycles;
+ return current_time_nanos_from_system;
+}
static int64_t GetCurrentTimeNanosSlowPath() ABSL_ATTRIBUTE_COLD;
@@ -315,14 +321,15 @@
// Acquire pairs with the barrier in SeqRelease - if this load sees that
// store, the shared-data reads necessarily see that SeqRelease's updates
// to the same shared data.
- seq_read0 = seq.load(std::memory_order_acquire);
+ seq_read0 = time_state.seq.load(std::memory_order_acquire);
- base_ns = last_sample.base_ns.load(std::memory_order_relaxed);
- base_cycles = last_sample.base_cycles.load(std::memory_order_relaxed);
+ base_ns = time_state.last_sample.base_ns.load(std::memory_order_relaxed);
+ base_cycles =
+ time_state.last_sample.base_cycles.load(std::memory_order_relaxed);
nsscaled_per_cycle =
- last_sample.nsscaled_per_cycle.load(std::memory_order_relaxed);
- min_cycles_per_sample =
- last_sample.min_cycles_per_sample.load(std::memory_order_relaxed);
+ time_state.last_sample.nsscaled_per_cycle.load(std::memory_order_relaxed);
+ min_cycles_per_sample = time_state.last_sample.min_cycles_per_sample.load(
+ std::memory_order_relaxed);
// This acquire fence pairs with the release fence in SeqAcquire. Since it
// is sequenced between reads of shared data and seq_read1, the reads of
@@ -333,7 +340,7 @@
// shared-data writes are effectively release ordered. Therefore if our
// shared-data reads see any of a particular update's shared-data writes,
// seq_read1 is guaranteed to see that update's SeqAcquire.
- seq_read1 = seq.load(std::memory_order_relaxed);
+ seq_read1 = time_state.seq.load(std::memory_order_relaxed);
// Fast path. Return if min_cycles_per_sample has not yet elapsed since the
// last sample, and we read a consistent sample. The fast path activates
@@ -346,9 +353,9 @@
// last_sample was updated). This is harmless, because delta_cycles will wrap
// and report a time much much bigger than min_cycles_per_sample. In that case
// we will take the slow path.
- uint64_t delta_cycles = now_cycles - base_cycles;
+ uint64_t delta_cycles;
if (seq_read0 == seq_read1 && (seq_read0 & 1) == 0 &&
- delta_cycles < min_cycles_per_sample) {
+ (delta_cycles = now_cycles - base_cycles) < min_cycles_per_sample) {
return base_ns + ((delta_cycles * nsscaled_per_cycle) >> kScale);
}
return GetCurrentTimeNanosSlowPath();
@@ -388,24 +395,25 @@
// TODO(absl-team): Remove this attribute when our compiler is smart enough
// to do the right thing.
ABSL_ATTRIBUTE_NOINLINE
-static int64_t GetCurrentTimeNanosSlowPath() ABSL_LOCKS_EXCLUDED(lock) {
+static int64_t GetCurrentTimeNanosSlowPath()
+ ABSL_LOCKS_EXCLUDED(time_state.lock) {
// Serialize access to slow-path. Fast-path readers are not blocked yet, and
// code below must not modify last_sample until the seqlock is acquired.
- lock.Lock();
+ time_state.lock.Lock();
// Sample the kernel time base. This is the definition of
// "now" if we take the slow path.
- static uint64_t last_now_cycles; // protected by lock
uint64_t now_cycles;
- uint64_t now_ns = GetCurrentTimeNanosFromKernel(last_now_cycles, &now_cycles);
- last_now_cycles = now_cycles;
+ uint64_t now_ns =
+ GetCurrentTimeNanosFromKernel(time_state.last_now_cycles, &now_cycles);
+ time_state.last_now_cycles = now_cycles;
uint64_t estimated_base_ns;
// ----------
// Read the "last_sample" values again; this time holding the write lock.
struct TimeSample sample;
- ReadTimeSampleAtomic(&last_sample, &sample);
+ ReadTimeSampleAtomic(&time_state.last_sample, &sample);
// ----------
// Try running the fast path again; another thread may have updated the
@@ -416,13 +424,13 @@
// so that blocked readers can make progress without blocking new readers.
estimated_base_ns = sample.base_ns +
((delta_cycles * sample.nsscaled_per_cycle) >> kScale);
- stats_fast_slow_paths++;
+ time_state.stats_fast_slow_paths++;
} else {
estimated_base_ns =
UpdateLastSample(now_cycles, now_ns, delta_cycles, &sample);
}
- lock.Unlock();
+ time_state.lock.Unlock();
return estimated_base_ns;
}
@@ -433,9 +441,10 @@
static uint64_t UpdateLastSample(uint64_t now_cycles, uint64_t now_ns,
uint64_t delta_cycles,
const struct TimeSample *sample)
- ABSL_EXCLUSIVE_LOCKS_REQUIRED(lock) {
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(time_state.lock) {
uint64_t estimated_base_ns = now_ns;
- uint64_t lock_value = SeqAcquire(&seq); // acquire seqlock to block readers
+ uint64_t lock_value =
+ SeqAcquire(&time_state.seq); // acquire seqlock to block readers
// The 5s in the next if-statement limits the time for which we will trust
// the cycle counter and our last sample to give a reasonable result.
@@ -445,12 +454,16 @@
sample->raw_ns + static_cast<uint64_t>(5) * 1000 * 1000 * 1000 < now_ns ||
now_ns < sample->raw_ns || now_cycles < sample->base_cycles) {
// record this sample, and forget any previously known slope.
- last_sample.raw_ns.store(now_ns, std::memory_order_relaxed);
- last_sample.base_ns.store(estimated_base_ns, std::memory_order_relaxed);
- last_sample.base_cycles.store(now_cycles, std::memory_order_relaxed);
- last_sample.nsscaled_per_cycle.store(0, std::memory_order_relaxed);
- last_sample.min_cycles_per_sample.store(0, std::memory_order_relaxed);
- stats_initializations++;
+ time_state.last_sample.raw_ns.store(now_ns, std::memory_order_relaxed);
+ time_state.last_sample.base_ns.store(estimated_base_ns,
+ std::memory_order_relaxed);
+ time_state.last_sample.base_cycles.store(now_cycles,
+ std::memory_order_relaxed);
+ time_state.last_sample.nsscaled_per_cycle.store(0,
+ std::memory_order_relaxed);
+ time_state.last_sample.min_cycles_per_sample.store(
+ 0, std::memory_order_relaxed);
+ time_state.stats_initializations++;
} else if (sample->raw_ns + 500 * 1000 * 1000 < now_ns &&
sample->base_cycles + 50 < now_cycles) {
// Enough time has passed to compute the cycle time.
@@ -493,28 +506,32 @@
if (new_nsscaled_per_cycle != 0 &&
diff_ns < 100 * 1000 * 1000 && -diff_ns < 100 * 1000 * 1000) {
// record the cycle time measurement
- last_sample.nsscaled_per_cycle.store(
+ time_state.last_sample.nsscaled_per_cycle.store(
new_nsscaled_per_cycle, std::memory_order_relaxed);
uint64_t new_min_cycles_per_sample =
SafeDivideAndScale(kMinNSBetweenSamples, new_nsscaled_per_cycle);
- last_sample.min_cycles_per_sample.store(
+ time_state.last_sample.min_cycles_per_sample.store(
new_min_cycles_per_sample, std::memory_order_relaxed);
- stats_calibrations++;
+ time_state.stats_calibrations++;
} else { // something went wrong; forget the slope
- last_sample.nsscaled_per_cycle.store(0, std::memory_order_relaxed);
- last_sample.min_cycles_per_sample.store(0, std::memory_order_relaxed);
+ time_state.last_sample.nsscaled_per_cycle.store(
+ 0, std::memory_order_relaxed);
+ time_state.last_sample.min_cycles_per_sample.store(
+ 0, std::memory_order_relaxed);
estimated_base_ns = now_ns;
- stats_reinitializations++;
+ time_state.stats_reinitializations++;
}
- last_sample.raw_ns.store(now_ns, std::memory_order_relaxed);
- last_sample.base_ns.store(estimated_base_ns, std::memory_order_relaxed);
- last_sample.base_cycles.store(now_cycles, std::memory_order_relaxed);
+ time_state.last_sample.raw_ns.store(now_ns, std::memory_order_relaxed);
+ time_state.last_sample.base_ns.store(estimated_base_ns,
+ std::memory_order_relaxed);
+ time_state.last_sample.base_cycles.store(now_cycles,
+ std::memory_order_relaxed);
} else {
// have a sample, but no slope; waiting for enough time for a calibration
- stats_slow_paths++;
+ time_state.stats_slow_paths++;
}
- SeqRelease(&seq, lock_value); // release the readers
+ SeqRelease(&time_state.seq, lock_value); // release the readers
return estimated_base_ns;
}
@@ -556,7 +573,8 @@
extern "C" {
-ABSL_ATTRIBUTE_WEAK void AbslInternalSleepFor(absl::Duration duration) {
+ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSleepFor)(
+ absl::Duration duration) {
while (duration > absl::ZeroDuration()) {
absl::Duration to_sleep = std::min(duration, absl::MaxSleep());
absl::SleepOnce(to_sleep);
diff --git a/absl/time/clock.h b/absl/time/clock.h
index 27764a9..5fe244d 100644
--- a/absl/time/clock.h
+++ b/absl/time/clock.h
@@ -64,11 +64,11 @@
// By changing our extension points to be extern "C", we dodge this
// check.
extern "C" {
-void AbslInternalSleepFor(absl::Duration duration);
+void ABSL_INTERNAL_C_SYMBOL(AbslInternalSleepFor)(absl::Duration duration);
} // extern "C"
inline void absl::SleepFor(absl::Duration duration) {
- AbslInternalSleepFor(duration);
+ ABSL_INTERNAL_C_SYMBOL(AbslInternalSleepFor)(duration);
}
#endif // ABSL_TIME_CLOCK_H_
diff --git a/absl/time/duration_benchmark.cc b/absl/time/duration_benchmark.cc
index 83a836c..56820f3 100644
--- a/absl/time/duration_benchmark.cc
+++ b/absl/time/duration_benchmark.cc
@@ -18,9 +18,14 @@
#include <string>
#include "absl/base/attributes.h"
+#include "absl/flags/flag.h"
#include "absl/time/time.h"
#include "benchmark/benchmark.h"
+ABSL_FLAG(absl::Duration, absl_duration_flag_for_benchmark,
+ absl::Milliseconds(1),
+ "Flag to use for benchmarking duration flag access speed.");
+
namespace {
//
@@ -425,4 +430,15 @@
}
BENCHMARK(BM_Duration_ParseDuration)->DenseRange(0, kNumDurations - 1);
+//
+// Flag access
+//
+void BM_Duration_GetFlag(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ benchmark::DoNotOptimize(
+ absl::GetFlag(FLAGS_absl_duration_flag_for_benchmark));
+ }
+}
+BENCHMARK(BM_Duration_GetFlag);
+
} // namespace
diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc
index 4d85a2c..fb28fa9 100644
--- a/absl/time/duration_test.cc
+++ b/absl/time/duration_test.cc
@@ -1369,10 +1369,13 @@
EXPECT_THAT(ToTimeval(absl::Nanoseconds(2000)), TimevalMatcher(tv));
}
-void VerifySameAsMul(double time_as_seconds, int* const misses) {
+void VerifyApproxSameAsMul(double time_as_seconds, int* const misses) {
auto direct_seconds = absl::Seconds(time_as_seconds);
auto mul_by_one_second = time_as_seconds * absl::Seconds(1);
- if (direct_seconds != mul_by_one_second) {
+ // These are expected to differ by up to one tick due to fused multiply/add
+ // contraction.
+ if (absl::AbsDuration(direct_seconds - mul_by_one_second) >
+ absl::time_internal::MakeDuration(0, 1u)) {
if (*misses > 10) return;
ASSERT_LE(++(*misses), 10) << "Too many errors, not reporting more.";
EXPECT_EQ(direct_seconds, mul_by_one_second)
@@ -1384,7 +1387,8 @@
// For a variety of interesting durations, we find the exact point
// where one double converts to that duration, and the very next double
// converts to the next duration. For both of those points, verify that
-// Seconds(point) returns the same duration as point * Seconds(1.0)
+// Seconds(point) returns a duration near point * Seconds(1.0). (They may
+// not be exactly equal due to fused multiply/add contraction.)
TEST(Duration, ToDoubleSecondsCheckEdgeCases) {
constexpr uint32_t kTicksPerSecond = absl::time_internal::kTicksPerSecond;
constexpr auto duration_tick = absl::time_internal::MakeDuration(0, 1u);
@@ -1423,8 +1427,8 @@
}
// Now low_edge is the highest double that converts to Duration d,
// and high_edge is the lowest double that converts to Duration after_d.
- VerifySameAsMul(low_edge, &misses);
- VerifySameAsMul(high_edge, &misses);
+ VerifyApproxSameAsMul(low_edge, &misses);
+ VerifyApproxSameAsMul(high_edge, &misses);
}
}
}
@@ -1444,8 +1448,8 @@
int misses = 0;
for (int i = 0; i < 1000000; ++i) {
double d = std::exp(uniform(gen));
- VerifySameAsMul(d, &misses);
- VerifySameAsMul(-d, &misses);
+ VerifyApproxSameAsMul(d, &misses);
+ VerifyApproxSameAsMul(-d, &misses);
}
}
diff --git a/absl/time/internal/cctz/src/time_zone_libc.cc b/absl/time/internal/cctz/src/time_zone_libc.cc
index a14982a..887dd09 100644
--- a/absl/time/internal/cctz/src/time_zone_libc.cc
+++ b/absl/time/internal/cctz/src/time_zone_libc.cc
@@ -27,6 +27,12 @@
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
+#if defined(_AIX)
+extern "C" {
+extern long altzone;
+}
+#endif
+
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace time_internal {
@@ -44,7 +50,7 @@
const bool is_dst = tm.tm_isdst > 0;
return _tzname[is_dst];
}
-#elif defined(__sun)
+#elif defined(__sun) || defined(_AIX)
// Uses the globals: 'timezone', 'altzone' and 'tzname'.
auto tm_gmtoff(const std::tm& tm) -> decltype(timezone) {
const bool is_dst = tm.tm_isdst > 0;
diff --git a/absl/time/internal/cctz/src/tzfile.h b/absl/time/internal/cctz/src/tzfile.h
index 659f84c..269fa36 100644
--- a/absl/time/internal/cctz/src/tzfile.h
+++ b/absl/time/internal/cctz/src/tzfile.h
@@ -108,15 +108,15 @@
#ifndef TZ_MAX_TYPES
/* This must be at least 17 for Europe/Samara and Europe/Vilnius. */
#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
-#endif /* !defined TZ_MAX_TYPES */
+#endif /* !defined TZ_MAX_TYPES */
#ifndef TZ_MAX_CHARS
#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
-/* (limited by what unsigned chars can hold) */
-#endif /* !defined TZ_MAX_CHARS */
+ /* (limited by what unsigned chars can hold) */
+#endif /* !defined TZ_MAX_CHARS */
#ifndef TZ_MAX_LEAPS
#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
-#endif /* !defined TZ_MAX_LEAPS */
+#endif /* !defined TZ_MAX_LEAPS */
#endif /* !defined TZFILE_H */
diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version
index b4410dc..1d59095 100644
--- a/absl/time/internal/cctz/testdata/version
+++ b/absl/time/internal/cctz/testdata/version
@@ -1 +1 @@
-2020d
+2021a
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra
index 9ca907b..c39ae38 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa
index d3c0bb3..5f4ebcb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara
index d3c0bb3..5f4ebcb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera
index d3c0bb3..5f4ebcb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui
index 2f2ce2f..3d7a71b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville
index 2f2ce2f..3d7a71b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam
index d3c0bb3..5f4ebcb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti
index d3c0bb3..5f4ebcb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala
index 2f2ce2f..3d7a71b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba
index 36b0522..0aba9ff 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala
index d3c0bb3..5f4ebcb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa
index 2f2ce2f..3d7a71b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos
index 2f2ce2f..3d7a71b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville
index 2f2ce2f..3d7a71b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda
index 2f2ce2f..3d7a71b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo
index 2f2ce2f..3d7a71b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu
index d3c0bb3..5f4ebcb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi
index d3c0bb3..5f4ebcb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey
index 2f2ce2f..3d7a71b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo
index 2f2ce2f..3d7a71b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Belize b/absl/time/internal/cctz/testdata/zoneinfo/America/Belize
index de99b84..bfc19f4 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Belize
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Belize
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk b/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk
index 062fcae..9d90e74 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau b/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau
index cf1e92d..2ef2aa8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie
index 232717b..3fc1f23 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
index 4278ffa..58e9fdf 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
index e55318a..aeda06b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem
index e6e6cc6..4c49bbf 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv
index e6e6cc6..4c49bbf 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda
index aa33014..abc75ea 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT b/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT
index f235d0d..1975a3a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide
index f397b3b..3bfbbc5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane
index c7915db..dc9a980 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill
index ed0d17a..947b509 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra
index f235d0d..1975a3a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie
index 55ceaef..dc2ef55 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin
index 7114153..a6a6730 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla
index 9fbc01f..9080f5c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart
index 21ef2d3..dc2ef55 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman
index e271d5b..131d77b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne
index c7160da..d3f195a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW b/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW
index f235d0d..1975a3a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/North b/absl/time/internal/cctz/testdata/zoneinfo/Australia/North
index 7114153..a6a6730 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/North
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/North
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth
index e449b03..4f77182 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland
index c7915db..dc9a980 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/South b/absl/time/internal/cctz/testdata/zoneinfo/Australia/South
index f397b3b..3bfbbc5 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/South
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/South
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney
index f235d0d..1975a3a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania
index 21ef2d3..dc2ef55 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria
index c7160da..d3f195a 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/West b/absl/time/internal/cctz/testdata/zoneinfo/Australia/West
index e449b03..4f77182 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/West
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/West
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna
index ed0d17a..947b509 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd
index a486ad4..c517002 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo
index d3c0bb3..5f4ebcb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro
index d3c0bb3..5f4ebcb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe
index 75362bb..e7fccf8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte
index d3c0bb3..5f4ebcb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Israel b/absl/time/internal/cctz/testdata/zoneinfo/Israel
index e6e6cc6..4c49bbf 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Israel
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Israel
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate
index 906971e..bf7471d 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
index 53ee77e..396e4d3 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
+++ b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
@@ -63,8 +63,7 @@
AT +4813+01620 Europe/Vienna
AU -3133+15905 Australia/Lord_Howe Lord Howe Island
AU -5430+15857 Antarctica/Macquarie Macquarie Island
-AU -4253+14719 Australia/Hobart Tasmania (most areas)
-AU -3956+14352 Australia/Currie Tasmania (King Island)
+AU -4253+14719 Australia/Hobart Tasmania
AU -3749+14458 Australia/Melbourne Victoria
AU -3352+15113 Australia/Sydney New South Wales (most areas)
AU -3157+14127 Australia/Broken_Hill New South Wales (Yancowinna)
@@ -127,9 +126,9 @@
CA +4906-11631 America/Creston MST - BC (Creston)
CA +5946-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John)
CA +5848-12242 America/Fort_Nelson MST - BC (Ft Nelson)
+CA +6043-13503 America/Whitehorse MST - Yukon (east)
+CA +6404-13925 America/Dawson MST - Yukon (west)
CA +4916-12307 America/Vancouver Pacific - BC (most areas)
-CA +6043-13503 America/Whitehorse Pacific - Yukon (east)
-CA +6404-13925 America/Dawson Pacific - Yukon (west)
CC -1210+09655 Indian/Cocos
CH,DE,LI +4723+00832 Europe/Zurich Swiss time
CI,BF,GM,GN,ML,MR,SH,SL,SN,TG +0519-00402 Africa/Abidjan
@@ -292,8 +291,8 @@
# Mention RU and UA alphabetically. See "territorial claims" above.
RU,UA +4457+03406 Europe/Simferopol Crimea
RU +5836+04939 Europe/Kirov MSK+00 - Kirov
+RU +4844+04425 Europe/Volgograd MSK+00 - Volgograd
RU +4621+04803 Europe/Astrakhan MSK+01 - Astrakhan
-RU +4844+04425 Europe/Volgograd MSK+01 - Volgograd
RU +5134+04602 Europe/Saratov MSK+01 - Saratov
RU +5420+04824 Europe/Ulyanovsk MSK+01 - Ulyanovsk
RU +5312+05009 Europe/Samara MSK+01 - Samara, Udmurtia
diff --git a/absl/time/time.h b/absl/time/time.h
index 7250803..d9ad1ae 100644
--- a/absl/time/time.h
+++ b/absl/time/time.h
@@ -458,12 +458,12 @@
//
// absl::Duration d = absl::Milliseconds(1500);
// int64_t isec = absl::ToInt64Seconds(d); // isec == 1
-int64_t ToInt64Nanoseconds(Duration d);
-int64_t ToInt64Microseconds(Duration d);
-int64_t ToInt64Milliseconds(Duration d);
-int64_t ToInt64Seconds(Duration d);
-int64_t ToInt64Minutes(Duration d);
-int64_t ToInt64Hours(Duration d);
+ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Nanoseconds(Duration d);
+ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Microseconds(Duration d);
+ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Milliseconds(Duration d);
+ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Seconds(Duration d);
+ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Minutes(Duration d);
+ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Hours(Duration d);
// ToDoubleNanoSeconds()
// ToDoubleMicroseconds()
@@ -480,12 +480,12 @@
//
// absl::Duration d = absl::Milliseconds(1500);
// double dsec = absl::ToDoubleSeconds(d); // dsec == 1.5
-double ToDoubleNanoseconds(Duration d);
-double ToDoubleMicroseconds(Duration d);
-double ToDoubleMilliseconds(Duration d);
-double ToDoubleSeconds(Duration d);
-double ToDoubleMinutes(Duration d);
-double ToDoubleHours(Duration d);
+ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleNanoseconds(Duration d);
+ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleMicroseconds(Duration d);
+ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleMilliseconds(Duration d);
+ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleSeconds(Duration d);
+ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleMinutes(Duration d);
+ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleHours(Duration d);
// FromChrono()
//
diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt
index 3f99ad8..c356b21 100644
--- a/absl/types/CMakeLists.txt
+++ b/absl/types/CMakeLists.txt
@@ -353,9 +353,6 @@
gmock_main
)
-# TODO(cohenjon,zhangxy) Figure out why this test is failing on gcc 4.8
-if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
-else()
absl_cc_test(
NAME
variant_exception_safety_test
@@ -370,4 +367,3 @@
absl::memory
gmock_main
)
-endif()
diff --git a/api/audio/BUILD.gn b/api/audio/BUILD.gn
new file mode 100644
index 0000000..d0465bb
--- /dev/null
+++ b/api/audio/BUILD.gn
@@ -0,0 +1,108 @@
+# Copyright (c) 2018 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.
+
+import("../../webrtc.gni")
+
+rtc_library("audio_frame_api") {
+ visibility = [ "*" ]
+ sources = [
+ "audio_frame.cc",
+ "audio_frame.h",
+ "channel_layout.cc",
+ "channel_layout.h",
+ ]
+
+ deps = [
+ "..:rtp_packet_info",
+ "../../rtc_base:checks",
+ "../../rtc_base:rtc_base_approved",
+ ]
+}
+
+rtc_source_set("audio_frame_processor") {
+ visibility = [ "*" ]
+ sources = [ "audio_frame_processor.h" ]
+}
+
+rtc_source_set("audio_mixer_api") {
+ visibility = [ "*" ]
+ sources = [ "audio_mixer.h" ]
+
+ deps = [
+ ":audio_frame_api",
+ "../../rtc_base:rtc_base_approved",
+ ]
+}
+
+rtc_library("aec3_config") {
+ visibility = [ "*" ]
+ sources = [
+ "echo_canceller3_config.cc",
+ "echo_canceller3_config.h",
+ ]
+ deps = [
+ "../../rtc_base:checks",
+ "../../rtc_base:rtc_base_approved",
+ "../../rtc_base:safe_minmax",
+ "../../rtc_base/system:rtc_export",
+ ]
+}
+
+rtc_library("aec3_config_json") {
+ visibility = [ "*" ]
+ allow_poison = [ "rtc_json" ]
+ sources = [
+ "echo_canceller3_config_json.cc",
+ "echo_canceller3_config_json.h",
+ ]
+ deps = [
+ ":aec3_config",
+ "../../rtc_base:checks",
+ "../../rtc_base:rtc_base_approved",
+ "../../rtc_base:rtc_json",
+ "../../rtc_base/system:rtc_export",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
+}
+
+rtc_library("aec3_factory") {
+ visibility = [ "*" ]
+ configs += [ "../../modules/audio_processing:apm_debug_dump" ]
+ sources = [
+ "echo_canceller3_factory.cc",
+ "echo_canceller3_factory.h",
+ ]
+
+ deps = [
+ ":aec3_config",
+ ":echo_control",
+ "../../modules/audio_processing/aec3",
+ "../../rtc_base:rtc_base_approved",
+ "../../rtc_base/system:rtc_export",
+ ]
+}
+
+rtc_source_set("echo_control") {
+ visibility = [ "*" ]
+ sources = [ "echo_control.h" ]
+ deps = [ "../../rtc_base:checks" ]
+}
+
+rtc_source_set("echo_detector_creator") {
+ visibility = [ "*" ]
+ sources = [
+ "echo_detector_creator.cc",
+ "echo_detector_creator.h",
+ ]
+ deps = [
+ "../../api:scoped_refptr",
+ "../../modules/audio_processing:api",
+ "../../modules/audio_processing:audio_processing",
+ "../../rtc_base:refcount",
+ ]
+}
diff --git a/api/audio/OWNERS b/api/audio/OWNERS
new file mode 100644
index 0000000..bb499b4
--- /dev/null
+++ b/api/audio/OWNERS
@@ -0,0 +1,2 @@
+gustaf@webrtc.org
+peah@webrtc.org
diff --git a/api/audio/echo_canceller3_config.h b/api/audio/echo_canceller3_config.h
index 55281af..2ccc9ac 100644
--- a/api/audio/echo_canceller3_config.h
+++ b/api/audio/echo_canceller3_config.h
@@ -90,6 +90,7 @@
bool conservative_initial_phase = false;
bool enable_coarse_filter_output_usage = true;
bool use_linear_filter = true;
+ bool high_pass_filter_echo_reference = false;
bool export_linear_aec_output = false;
} filter;
diff --git a/api/audio/echo_canceller3_config_json.cc b/api/audio/echo_canceller3_config_json.cc
index 4768e36..9e15e3a 100644
--- a/api/audio/echo_canceller3_config_json.cc
+++ b/api/audio/echo_canceller3_config_json.cc
@@ -156,16 +156,9 @@
*parsing_successful = true;
Json::Value root;
- Json::CharReaderBuilder builder;
- std::unique_pre<Json::CharReader> reader(builder.newCharReader());
- std::string error;
-
- bool success = reader->parse(json_string.data(),
- json_string.data() + json_string.size(),
- &root, &error);
+ bool success = Json::Reader().parse(std::string(json_string), root);
if (!success) {
- RTC_LOG(LS_ERROR) << "Incorrect JSON format: " << json_string
- << " error: " << error;
+ RTC_LOG(LS_ERROR) << "Incorrect JSON format: " << json_string;
*parsing_successful = false;
return;
}
@@ -237,6 +230,8 @@
ReadParam(section, "enable_coarse_filter_output_usage",
&cfg.filter.enable_coarse_filter_output_usage);
ReadParam(section, "use_linear_filter", &cfg.filter.use_linear_filter);
+ ReadParam(section, "high_pass_filter_echo_reference",
+ &cfg.filter.high_pass_filter_echo_reference);
ReadParam(section, "export_linear_aec_output",
&cfg.filter.export_linear_aec_output);
}
@@ -520,6 +515,9 @@
<< ",";
ost << "\"use_linear_filter\": "
<< (config.filter.use_linear_filter ? "true" : "false") << ",";
+ ost << "\"high_pass_filter_echo_reference\": "
+ << (config.filter.high_pass_filter_echo_reference ? "true" : "false")
+ << ",";
ost << "\"export_linear_aec_output\": "
<< (config.filter.export_linear_aec_output ? "true" : "false");
diff --git a/api/audio/echo_control.h b/api/audio/echo_control.h
index 8d567bf..74fbc27 100644
--- a/api/audio/echo_control.h
+++ b/api/audio/echo_control.h
@@ -48,6 +48,13 @@
// Provides an optional external estimate of the audio buffer delay.
virtual void SetAudioBufferDelay(int delay_ms) = 0;
+ // Specifies whether the capture output will be used. The purpose of this is
+ // to allow the echo controller to deactivate some of the processing when the
+ // resulting output is anyway not used, for instance when the endpoint is
+ // muted.
+ // TODO(b/177830919): Make pure virtual.
+ virtual void SetCaptureOutputUsage(bool capture_output_used) {}
+
// Returns wheter the signal is altered.
virtual bool ActiveProcessing() const = 0;
diff --git a/api/audio/test/BUILD.gn b/api/audio/test/BUILD.gn
new file mode 100644
index 0000000..d62baf1
--- /dev/null
+++ b/api/audio/test/BUILD.gn
@@ -0,0 +1,31 @@
+# Copyright (c) 2018 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.
+
+import("../../../webrtc.gni")
+if (is_android) {
+ import("//build/config/android/config.gni")
+ import("//build/config/android/rules.gni")
+}
+
+if (rtc_include_tests) {
+ rtc_library("audio_api_unittests") {
+ testonly = true
+ sources = [
+ "audio_frame_unittest.cc",
+ "echo_canceller3_config_json_unittest.cc",
+ "echo_canceller3_config_unittest.cc",
+ ]
+ deps = [
+ "..:aec3_config",
+ "..:aec3_config_json",
+ "..:audio_frame_api",
+ "../../../rtc_base:rtc_base_approved",
+ "../../../test:test_support",
+ ]
+ }
+}
diff --git a/api/audio/test/audio_frame_unittest.cc b/api/audio/test/audio_frame_unittest.cc
new file mode 100644
index 0000000..f8d3318
--- /dev/null
+++ b/api/audio/test/audio_frame_unittest.cc
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2018 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 "api/audio/audio_frame.h"
+
+#include <stdint.h>
+#include <string.h> // memcmp
+
+#include "test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+
+bool AllSamplesAre(int16_t sample, const AudioFrame& frame) {
+ const int16_t* frame_data = frame.data();
+ for (size_t i = 0; i < frame.max_16bit_samples(); i++) {
+ if (frame_data[i] != sample) {
+ return false;
+ }
+ }
+ return true;
+}
+
+constexpr uint32_t kTimestamp = 27;
+constexpr int kSampleRateHz = 16000;
+constexpr size_t kNumChannelsMono = 1;
+constexpr size_t kNumChannelsStereo = 2;
+constexpr size_t kNumChannels5_1 = 6;
+constexpr size_t kSamplesPerChannel = kSampleRateHz / 100;
+
+} // namespace
+
+TEST(AudioFrameTest, FrameStartsMuted) {
+ AudioFrame frame;
+ EXPECT_TRUE(frame.muted());
+ EXPECT_TRUE(AllSamplesAre(0, frame));
+}
+
+TEST(AudioFrameTest, UnmutedFrameIsInitiallyZeroed) {
+ AudioFrame frame;
+ frame.mutable_data();
+ EXPECT_FALSE(frame.muted());
+ EXPECT_TRUE(AllSamplesAre(0, frame));
+}
+
+TEST(AudioFrameTest, MutedFrameBufferIsZeroed) {
+ AudioFrame frame;
+ int16_t* frame_data = frame.mutable_data();
+ for (size_t i = 0; i < frame.max_16bit_samples(); i++) {
+ frame_data[i] = 17;
+ }
+ ASSERT_TRUE(AllSamplesAre(17, frame));
+ frame.Mute();
+ EXPECT_TRUE(frame.muted());
+ EXPECT_TRUE(AllSamplesAre(0, frame));
+}
+
+TEST(AudioFrameTest, UpdateFrameMono) {
+ AudioFrame frame;
+ int16_t samples[kNumChannelsMono * kSamplesPerChannel] = {17};
+ frame.UpdateFrame(kTimestamp, samples, kSamplesPerChannel, kSampleRateHz,
+ AudioFrame::kPLC, AudioFrame::kVadActive, kNumChannelsMono);
+
+ EXPECT_EQ(kTimestamp, frame.timestamp_);
+ EXPECT_EQ(kSamplesPerChannel, frame.samples_per_channel());
+ EXPECT_EQ(kSampleRateHz, frame.sample_rate_hz());
+ EXPECT_EQ(AudioFrame::kPLC, frame.speech_type_);
+ EXPECT_EQ(AudioFrame::kVadActive, frame.vad_activity_);
+ EXPECT_EQ(kNumChannelsMono, frame.num_channels());
+ EXPECT_EQ(CHANNEL_LAYOUT_MONO, frame.channel_layout());
+
+ EXPECT_FALSE(frame.muted());
+ EXPECT_EQ(0, memcmp(samples, frame.data(), sizeof(samples)));
+
+ frame.UpdateFrame(kTimestamp, nullptr /* data*/, kSamplesPerChannel,
+ kSampleRateHz, AudioFrame::kPLC, AudioFrame::kVadActive,
+ kNumChannelsMono);
+ EXPECT_TRUE(frame.muted());
+ EXPECT_TRUE(AllSamplesAre(0, frame));
+}
+
+TEST(AudioFrameTest, UpdateFrameMultiChannel) {
+ AudioFrame frame;
+ frame.UpdateFrame(kTimestamp, nullptr /* data */, kSamplesPerChannel,
+ kSampleRateHz, AudioFrame::kPLC, AudioFrame::kVadActive,
+ kNumChannelsStereo);
+ EXPECT_EQ(kSamplesPerChannel, frame.samples_per_channel());
+ EXPECT_EQ(kNumChannelsStereo, frame.num_channels());
+ EXPECT_EQ(CHANNEL_LAYOUT_STEREO, frame.channel_layout());
+
+ frame.UpdateFrame(kTimestamp, nullptr /* data */, kSamplesPerChannel,
+ kSampleRateHz, AudioFrame::kPLC, AudioFrame::kVadActive,
+ kNumChannels5_1);
+ EXPECT_EQ(kSamplesPerChannel, frame.samples_per_channel());
+ EXPECT_EQ(kNumChannels5_1, frame.num_channels());
+ EXPECT_EQ(CHANNEL_LAYOUT_5_1, frame.channel_layout());
+}
+
+TEST(AudioFrameTest, CopyFrom) {
+ AudioFrame frame1;
+ AudioFrame frame2;
+
+ int16_t samples[kNumChannelsMono * kSamplesPerChannel] = {17};
+ frame2.UpdateFrame(kTimestamp, samples, kSamplesPerChannel, kSampleRateHz,
+ AudioFrame::kPLC, AudioFrame::kVadActive,
+ kNumChannelsMono);
+ frame1.CopyFrom(frame2);
+
+ EXPECT_EQ(frame2.timestamp_, frame1.timestamp_);
+ EXPECT_EQ(frame2.samples_per_channel_, frame1.samples_per_channel_);
+ EXPECT_EQ(frame2.sample_rate_hz_, frame1.sample_rate_hz_);
+ EXPECT_EQ(frame2.speech_type_, frame1.speech_type_);
+ EXPECT_EQ(frame2.vad_activity_, frame1.vad_activity_);
+ EXPECT_EQ(frame2.num_channels_, frame1.num_channels_);
+
+ EXPECT_EQ(frame2.muted(), frame1.muted());
+ EXPECT_EQ(0, memcmp(frame2.data(), frame1.data(), sizeof(samples)));
+
+ frame2.UpdateFrame(kTimestamp, nullptr /* data */, kSamplesPerChannel,
+ kSampleRateHz, AudioFrame::kPLC, AudioFrame::kVadActive,
+ kNumChannelsMono);
+ frame1.CopyFrom(frame2);
+
+ EXPECT_EQ(frame2.muted(), frame1.muted());
+ EXPECT_EQ(0, memcmp(frame2.data(), frame1.data(), sizeof(samples)));
+}
+
+TEST(AudioFrameTest, SwapFrames) {
+ AudioFrame frame1, frame2;
+ int16_t samples1[kNumChannelsMono * kSamplesPerChannel];
+ for (size_t i = 0; i < kNumChannelsMono * kSamplesPerChannel; ++i) {
+ samples1[i] = i;
+ }
+ frame1.UpdateFrame(kTimestamp, samples1, kSamplesPerChannel, kSampleRateHz,
+ AudioFrame::kPLC, AudioFrame::kVadActive,
+ kNumChannelsMono);
+ frame1.set_absolute_capture_timestamp_ms(12345678);
+ const auto frame1_channel_layout = frame1.channel_layout();
+
+ int16_t samples2[(kNumChannelsMono + 1) * (kSamplesPerChannel + 1)];
+ for (size_t i = 0; i < (kNumChannelsMono + 1) * (kSamplesPerChannel + 1);
+ ++i) {
+ samples2[i] = 1000 + i;
+ }
+ frame2.UpdateFrame(kTimestamp + 1, samples2, kSamplesPerChannel + 1,
+ kSampleRateHz + 1, AudioFrame::kNormalSpeech,
+ AudioFrame::kVadPassive, kNumChannelsMono + 1);
+ const auto frame2_channel_layout = frame2.channel_layout();
+
+ swap(frame1, frame2);
+
+ EXPECT_EQ(kTimestamp + 1, frame1.timestamp_);
+ ASSERT_EQ(kSamplesPerChannel + 1, frame1.samples_per_channel_);
+ EXPECT_EQ(kSampleRateHz + 1, frame1.sample_rate_hz_);
+ EXPECT_EQ(AudioFrame::kNormalSpeech, frame1.speech_type_);
+ EXPECT_EQ(AudioFrame::kVadPassive, frame1.vad_activity_);
+ ASSERT_EQ(kNumChannelsMono + 1, frame1.num_channels_);
+ for (size_t i = 0; i < (kNumChannelsMono + 1) * (kSamplesPerChannel + 1);
+ ++i) {
+ EXPECT_EQ(samples2[i], frame1.data()[i]);
+ }
+ EXPECT_FALSE(frame1.absolute_capture_timestamp_ms());
+ EXPECT_EQ(frame2_channel_layout, frame1.channel_layout());
+
+ EXPECT_EQ(kTimestamp, frame2.timestamp_);
+ ASSERT_EQ(kSamplesPerChannel, frame2.samples_per_channel_);
+ EXPECT_EQ(kSampleRateHz, frame2.sample_rate_hz_);
+ EXPECT_EQ(AudioFrame::kPLC, frame2.speech_type_);
+ EXPECT_EQ(AudioFrame::kVadActive, frame2.vad_activity_);
+ ASSERT_EQ(kNumChannelsMono, frame2.num_channels_);
+ for (size_t i = 0; i < kNumChannelsMono * kSamplesPerChannel; ++i) {
+ EXPECT_EQ(samples1[i], frame2.data()[i]);
+ }
+ EXPECT_EQ(12345678, frame2.absolute_capture_timestamp_ms());
+ EXPECT_EQ(frame1_channel_layout, frame2.channel_layout());
+}
+
+} // namespace webrtc
diff --git a/api/audio/test/echo_canceller3_config_json_unittest.cc b/api/audio/test/echo_canceller3_config_json_unittest.cc
new file mode 100644
index 0000000..d6edd07
--- /dev/null
+++ b/api/audio/test/echo_canceller3_config_json_unittest.cc
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2018 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 "api/audio/echo_canceller3_config_json.h"
+
+#include "api/audio/echo_canceller3_config.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(EchoCanceller3JsonHelpers, ToStringAndParseJson) {
+ EchoCanceller3Config cfg;
+ cfg.delay.down_sampling_factor = 1u;
+ cfg.delay.log_warning_on_delay_changes = true;
+ cfg.filter.refined.error_floor = 2.f;
+ cfg.filter.coarse_initial.length_blocks = 3u;
+ cfg.filter.high_pass_filter_echo_reference =
+ !cfg.filter.high_pass_filter_echo_reference;
+ cfg.comfort_noise.noise_floor_dbfs = 100.f;
+ cfg.echo_model.model_reverb_in_nonlinear_mode = false;
+ cfg.suppressor.normal_tuning.mask_hf.enr_suppress = .5f;
+ cfg.suppressor.subband_nearend_detection.nearend_average_blocks = 3;
+ cfg.suppressor.subband_nearend_detection.subband1 = {1, 3};
+ cfg.suppressor.subband_nearend_detection.subband1 = {4, 5};
+ cfg.suppressor.subband_nearend_detection.nearend_threshold = 2.f;
+ cfg.suppressor.subband_nearend_detection.snr_threshold = 100.f;
+ std::string json_string = Aec3ConfigToJsonString(cfg);
+ EchoCanceller3Config cfg_transformed = Aec3ConfigFromJsonString(json_string);
+
+ // Expect unchanged values to remain default.
+ EXPECT_EQ(cfg.ep_strength.default_len,
+ cfg_transformed.ep_strength.default_len);
+ EXPECT_EQ(cfg.suppressor.normal_tuning.mask_lf.enr_suppress,
+ cfg_transformed.suppressor.normal_tuning.mask_lf.enr_suppress);
+
+ // Expect changed values to carry through the transformation.
+ EXPECT_EQ(cfg.delay.down_sampling_factor,
+ cfg_transformed.delay.down_sampling_factor);
+ EXPECT_EQ(cfg.delay.log_warning_on_delay_changes,
+ cfg_transformed.delay.log_warning_on_delay_changes);
+ EXPECT_EQ(cfg.filter.coarse_initial.length_blocks,
+ cfg_transformed.filter.coarse_initial.length_blocks);
+ EXPECT_EQ(cfg.filter.refined.error_floor,
+ cfg_transformed.filter.refined.error_floor);
+ EXPECT_EQ(cfg.filter.high_pass_filter_echo_reference,
+ cfg_transformed.filter.high_pass_filter_echo_reference);
+ EXPECT_EQ(cfg.comfort_noise.noise_floor_dbfs,
+ cfg_transformed.comfort_noise.noise_floor_dbfs);
+ EXPECT_EQ(cfg.echo_model.model_reverb_in_nonlinear_mode,
+ cfg_transformed.echo_model.model_reverb_in_nonlinear_mode);
+ EXPECT_EQ(cfg.suppressor.normal_tuning.mask_hf.enr_suppress,
+ cfg_transformed.suppressor.normal_tuning.mask_hf.enr_suppress);
+ EXPECT_EQ(cfg.suppressor.subband_nearend_detection.nearend_average_blocks,
+ cfg_transformed.suppressor.subband_nearend_detection
+ .nearend_average_blocks);
+ EXPECT_EQ(cfg.suppressor.subband_nearend_detection.subband1.low,
+ cfg_transformed.suppressor.subband_nearend_detection.subband1.low);
+ EXPECT_EQ(cfg.suppressor.subband_nearend_detection.subband1.high,
+ cfg_transformed.suppressor.subband_nearend_detection.subband1.high);
+ EXPECT_EQ(cfg.suppressor.subband_nearend_detection.subband2.low,
+ cfg_transformed.suppressor.subband_nearend_detection.subband2.low);
+ EXPECT_EQ(cfg.suppressor.subband_nearend_detection.subband2.high,
+ cfg_transformed.suppressor.subband_nearend_detection.subband2.high);
+ EXPECT_EQ(
+ cfg.suppressor.subband_nearend_detection.nearend_threshold,
+ cfg_transformed.suppressor.subband_nearend_detection.nearend_threshold);
+ EXPECT_EQ(cfg.suppressor.subband_nearend_detection.snr_threshold,
+ cfg_transformed.suppressor.subband_nearend_detection.snr_threshold);
+}
+} // namespace webrtc
diff --git a/api/audio/test/echo_canceller3_config_unittest.cc b/api/audio/test/echo_canceller3_config_unittest.cc
new file mode 100644
index 0000000..91312a0
--- /dev/null
+++ b/api/audio/test/echo_canceller3_config_unittest.cc
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018 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 "api/audio/echo_canceller3_config.h"
+
+#include "api/audio/echo_canceller3_config_json.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(EchoCanceller3Config, ValidConfigIsNotModified) {
+ EchoCanceller3Config config;
+ EXPECT_TRUE(EchoCanceller3Config::Validate(&config));
+ EchoCanceller3Config default_config;
+ EXPECT_EQ(Aec3ConfigToJsonString(config),
+ Aec3ConfigToJsonString(default_config));
+}
+
+TEST(EchoCanceller3Config, InvalidConfigIsCorrected) {
+ // Change a parameter and validate.
+ EchoCanceller3Config config;
+ config.echo_model.min_noise_floor_power = -1600000.f;
+ EXPECT_FALSE(EchoCanceller3Config::Validate(&config));
+ EXPECT_GE(config.echo_model.min_noise_floor_power, 0.f);
+ // Verify remaining parameters are unchanged.
+ EchoCanceller3Config default_config;
+ config.echo_model.min_noise_floor_power =
+ default_config.echo_model.min_noise_floor_power;
+ EXPECT_EQ(Aec3ConfigToJsonString(config),
+ Aec3ConfigToJsonString(default_config));
+}
+
+TEST(EchoCanceller3Config, ValidatedConfigsAreValid) {
+ EchoCanceller3Config config;
+ config.delay.down_sampling_factor = 983;
+ EXPECT_FALSE(EchoCanceller3Config::Validate(&config));
+ EXPECT_TRUE(EchoCanceller3Config::Validate(&config));
+}
+} // namespace webrtc
diff --git a/api/ice_transport_factory.cc b/api/ice_transport_factory.cc
index c32d7d2..31b4faa 100644
--- a/api/ice_transport_factory.cc
+++ b/api/ice_transport_factory.cc
@@ -41,7 +41,7 @@
}
private:
- const rtc::ThreadChecker thread_checker_{};
+ const SequenceChecker thread_checker_{};
const std::unique_ptr<cricket::IceTransportInternal> internal_
RTC_GUARDED_BY(thread_checker_);
};
diff --git a/api/jsep.h b/api/jsep.h
index dcf8213..b56cf1d 100644
--- a/api/jsep.h
+++ b/api/jsep.h
@@ -28,7 +28,6 @@
#include "absl/types/optional.h"
#include "api/rtc_error.h"
-#include "rtc_base/deprecation.h"
#include "rtc_base/ref_count.h"
#include "rtc_base/system/rtc_export.h"
diff --git a/api/jsep_session_description.h b/api/jsep_session_description.h
index e13d85e..70ac939 100644
--- a/api/jsep_session_description.h
+++ b/api/jsep_session_description.h
@@ -23,7 +23,6 @@
#include "api/jsep.h"
#include "api/jsep_ice_candidate.h"
#include "rtc_base/constructor_magic.h"
-#include "rtc_base/deprecation.h"
namespace cricket {
class SessionDescription;
diff --git a/api/media_stream_proxy.h b/api/media_stream_proxy.h
index 8ee33ca..773c5d8 100644
--- a/api/media_stream_proxy.h
+++ b/api/media_stream_proxy.h
@@ -20,8 +20,8 @@
// TODO(deadbeef): Move this to .cc file and out of api/. What threads methods
// are called on is an implementation detail.
-BEGIN_SIGNALING_PROXY_MAP(MediaStream)
-PROXY_SIGNALING_THREAD_DESTRUCTOR()
+BEGIN_PRIMARY_PROXY_MAP(MediaStream)
+PROXY_PRIMARY_THREAD_DESTRUCTOR()
BYPASS_PROXY_CONSTMETHOD0(std::string, id)
PROXY_METHOD0(AudioTrackVector, GetAudioTracks)
PROXY_METHOD0(VideoTrackVector, GetVideoTracks)
diff --git a/api/media_stream_track_proxy.h b/api/media_stream_track_proxy.h
index 59dcb77..a0fe676 100644
--- a/api/media_stream_track_proxy.h
+++ b/api/media_stream_track_proxy.h
@@ -24,8 +24,8 @@
// TODO(deadbeef): Move this to .cc file and out of api/. What threads methods
// are called on is an implementation detail.
-BEGIN_SIGNALING_PROXY_MAP(AudioTrack)
-PROXY_SIGNALING_THREAD_DESTRUCTOR()
+BEGIN_PRIMARY_PROXY_MAP(AudioTrack)
+PROXY_PRIMARY_THREAD_DESTRUCTOR()
BYPASS_PROXY_CONSTMETHOD0(std::string, kind)
BYPASS_PROXY_CONSTMETHOD0(std::string, id)
PROXY_CONSTMETHOD0(TrackState, state)
@@ -41,7 +41,7 @@
END_PROXY_MAP()
BEGIN_PROXY_MAP(VideoTrack)
-PROXY_SIGNALING_THREAD_DESTRUCTOR()
+PROXY_PRIMARY_THREAD_DESTRUCTOR()
BYPASS_PROXY_CONSTMETHOD0(std::string, kind)
BYPASS_PROXY_CONSTMETHOD0(std::string, id)
PROXY_CONSTMETHOD0(TrackState, state)
@@ -49,11 +49,11 @@
PROXY_METHOD1(bool, set_enabled, bool)
PROXY_CONSTMETHOD0(ContentHint, content_hint)
PROXY_METHOD1(void, set_content_hint, ContentHint)
-PROXY_WORKER_METHOD2(void,
- AddOrUpdateSink,
- rtc::VideoSinkInterface<VideoFrame>*,
- const rtc::VideoSinkWants&)
-PROXY_WORKER_METHOD1(void, RemoveSink, rtc::VideoSinkInterface<VideoFrame>*)
+PROXY_SECONDARY_METHOD2(void,
+ AddOrUpdateSink,
+ rtc::VideoSinkInterface<VideoFrame>*,
+ const rtc::VideoSinkWants&)
+PROXY_SECONDARY_METHOD1(void, RemoveSink, rtc::VideoSinkInterface<VideoFrame>*)
PROXY_CONSTMETHOD0(VideoTrackSourceInterface*, GetSource)
PROXY_METHOD1(void, RegisterObserver, ObserverInterface*)
diff --git a/api/peer_connection_factory_proxy.h b/api/peer_connection_factory_proxy.h
index be098e3..9056495 100644
--- a/api/peer_connection_factory_proxy.h
+++ b/api/peer_connection_factory_proxy.h
@@ -17,14 +17,13 @@
#include "api/peer_connection_interface.h"
#include "api/proxy.h"
-#include "rtc_base/bind.h"
namespace webrtc {
// TODO(deadbeef): Move this to .cc file and out of api/. What threads methods
// are called on is an implementation detail.
-BEGIN_SIGNALING_PROXY_MAP(PeerConnectionFactory)
-PROXY_SIGNALING_THREAD_DESTRUCTOR()
+BEGIN_PRIMARY_PROXY_MAP(PeerConnectionFactory)
+PROXY_PRIMARY_THREAD_DESTRUCTOR()
PROXY_METHOD1(void, SetOptions, const Options&)
PROXY_METHOD4(rtc::scoped_refptr<PeerConnectionInterface>,
CreatePeerConnection,
diff --git a/api/peer_connection_interface.h b/api/peer_connection_interface.h
index 92d965b..cc65592 100644
--- a/api/peer_connection_interface.h
+++ b/api/peer_connection_interface.h
@@ -621,12 +621,8 @@
absl::optional<CryptoOptions> crypto_options;
// Configure if we should include the SDP attribute extmap-allow-mixed in
- // our offer. Although we currently do support this, it's not included in
- // our offer by default due to a previous bug that caused the SDP parser to
- // abort parsing if this attribute was present. This is fixed in Chrome 71.
- // TODO(webrtc:9985): Change default to true once sufficient time has
- // passed.
- bool offer_extmap_allow_mixed = false;
+ // our offer on session level.
+ bool offer_extmap_allow_mixed = true;
// TURN logging identifier.
// This identifier is added to a TURN allocation
@@ -1060,7 +1056,10 @@
// Removes a group of remote candidates from the ICE agent. Needed mainly for
// continual gathering, to avoid an ever-growing list of candidates as
- // networks come and go.
+ // networks come and go. Note that the candidates' transport_name must be set
+ // to the MID of the m= section that generated the candidate.
+ // TODO(bugs.webrtc.org/8395): Use IceCandidateInterface instead of
+ // cricket::Candidate, which would avoid the transport_name oddity.
virtual bool RemoveIceCandidates(
const std::vector<cricket::Candidate>& candidates) = 0;
diff --git a/api/peer_connection_proxy.h b/api/peer_connection_proxy.h
index 2d4cb5c..cc9df10 100644
--- a/api/peer_connection_proxy.h
+++ b/api/peer_connection_proxy.h
@@ -20,10 +20,13 @@
namespace webrtc {
+// PeerConnection proxy objects will be constructed with two thread pointers,
+// signaling and network. The proxy macros don't have 'network' specific macros
+// and support for a secondary thread is provided via 'SECONDARY' macros.
// TODO(deadbeef): Move this to .cc file and out of api/. What threads methods
// are called on is an implementation detail.
-BEGIN_SIGNALING_PROXY_MAP(PeerConnection)
-PROXY_SIGNALING_THREAD_DESTRUCTOR()
+BEGIN_PROXY_MAP(PeerConnection)
+PROXY_PRIMARY_THREAD_DESTRUCTOR()
PROXY_METHOD0(rtc::scoped_refptr<StreamCollectionInterface>, local_streams)
PROXY_METHOD0(rtc::scoped_refptr<StreamCollectionInterface>, remote_streams)
PROXY_METHOD1(bool, AddStream, MediaStreamInterface*)
@@ -130,10 +133,15 @@
PROXY_METHOD1(RTCError, SetBitrate, const BitrateSettings&)
PROXY_METHOD1(void, SetAudioPlayout, bool)
PROXY_METHOD1(void, SetAudioRecording, bool)
-PROXY_METHOD1(rtc::scoped_refptr<DtlsTransportInterface>,
- LookupDtlsTransportByMid,
- const std::string&)
-PROXY_CONSTMETHOD0(rtc::scoped_refptr<SctpTransportInterface>, GetSctpTransport)
+// This method will be invoked on the network thread. See
+// PeerConnectionFactory::CreatePeerConnectionOrError for more details.
+PROXY_SECONDARY_METHOD1(rtc::scoped_refptr<DtlsTransportInterface>,
+ LookupDtlsTransportByMid,
+ const std::string&)
+// This method will be invoked on the network thread. See
+// PeerConnectionFactory::CreatePeerConnectionOrError for more details.
+PROXY_SECONDARY_CONSTMETHOD0(rtc::scoped_refptr<SctpTransportInterface>,
+ GetSctpTransport)
PROXY_METHOD0(SignalingState, signaling_state)
PROXY_METHOD0(IceConnectionState, ice_connection_state)
PROXY_METHOD0(IceConnectionState, standardized_ice_connection_state)
diff --git a/api/proxy.h b/api/proxy.h
index 05f7414..3be9f93 100644
--- a/api/proxy.h
+++ b/api/proxy.h
@@ -12,6 +12,13 @@
// PeerConnection classes.
// TODO(deadbeef): Move this to pc/; this is part of the implementation.
+// The proxied objects are initialized with either one or two thread
+// objects that operations can be proxied to: The primary and secondary
+// threads.
+// In common usage, the primary thread will be the PeerConnection's
+// signaling thread, and the secondary thread will be either the
+// PeerConnection's worker thread or the PeerConnection's network thread.
+
//
// Example usage:
//
@@ -29,22 +36,22 @@
// };
//
// BEGIN_PROXY_MAP(Test)
-// PROXY_SIGNALING_THREAD_DESTRUCTOR()
+// PROXY_PRIMARY_THREAD_DESTRUCTOR()
// PROXY_METHOD0(std::string, FooA)
// PROXY_CONSTMETHOD1(std::string, FooB, arg1)
-// PROXY_WORKER_METHOD1(std::string, FooC, arg1)
+// PROXY_SECONDARY_METHOD1(std::string, FooC, arg1)
// END_PROXY_MAP()
//
-// Where the destructor and first two methods are invoked on the signaling
-// thread, and the third is invoked on the worker thread.
+// Where the destructor and first two methods are invoked on the primary
+// thread, and the third is invoked on the secondary thread.
//
// The proxy can be created using
//
// TestProxy::Create(Thread* signaling_thread, Thread* worker_thread,
// TestInterface*).
//
-// The variant defined with BEGIN_SIGNALING_PROXY_MAP is unaware of
-// the worker thread, and invokes all methods on the signaling thread.
+// The variant defined with BEGIN_PRIMARY_PROXY_MAP is unaware of
+// the secondary thread, and invokes all methods on the primary thread.
//
// The variant defined with BEGIN_OWNED_PROXY_MAP does not use
// refcounting, and instead just takes ownership of the object being proxied.
@@ -195,25 +202,25 @@
};
// clang-format on
-#define SIGNALING_PROXY_MAP_BOILERPLATE(c) \
+#define PRIMARY_PROXY_MAP_BOILERPLATE(c) \
+ protected: \
+ c##ProxyWithInternal(rtc::Thread* primary_thread, INTERNAL_CLASS* c) \
+ : primary_thread_(primary_thread), c_(c) {} \
+ \
+ private: \
+ mutable rtc::Thread* primary_thread_;
+
+#define SECONDARY_PROXY_MAP_BOILERPLATE(c) \
protected: \
- c##ProxyWithInternal(rtc::Thread* signaling_thread, INTERNAL_CLASS* c) \
- : signaling_thread_(signaling_thread), c_(c) {} \
+ c##ProxyWithInternal(rtc::Thread* primary_thread, \
+ rtc::Thread* secondary_thread, INTERNAL_CLASS* c) \
+ : primary_thread_(primary_thread), \
+ secondary_thread_(secondary_thread), \
+ c_(c) {} \
\
private: \
- mutable rtc::Thread* signaling_thread_;
-
-#define WORKER_PROXY_MAP_BOILERPLATE(c) \
- protected: \
- c##ProxyWithInternal(rtc::Thread* signaling_thread, \
- rtc::Thread* worker_thread, INTERNAL_CLASS* c) \
- : signaling_thread_(signaling_thread), \
- worker_thread_(worker_thread), \
- c_(c) {} \
- \
- private: \
- mutable rtc::Thread* signaling_thread_; \
- mutable rtc::Thread* worker_thread_;
+ mutable rtc::Thread* primary_thread_; \
+ mutable rtc::Thread* secondary_thread_;
// Note that the destructor is protected so that the proxy can only be
// destroyed via RefCountInterface.
@@ -246,89 +253,88 @@
void DestroyInternal() { delete c_; } \
INTERNAL_CLASS* c_;
-#define BEGIN_SIGNALING_PROXY_MAP(c) \
- PROXY_MAP_BOILERPLATE(c) \
- SIGNALING_PROXY_MAP_BOILERPLATE(c) \
- REFCOUNTED_PROXY_MAP_BOILERPLATE(c) \
- public: \
- static rtc::scoped_refptr<c##ProxyWithInternal> Create( \
- rtc::Thread* signaling_thread, INTERNAL_CLASS* c) { \
- return new rtc::RefCountedObject<c##ProxyWithInternal>(signaling_thread, \
- c); \
+#define BEGIN_PRIMARY_PROXY_MAP(c) \
+ PROXY_MAP_BOILERPLATE(c) \
+ PRIMARY_PROXY_MAP_BOILERPLATE(c) \
+ REFCOUNTED_PROXY_MAP_BOILERPLATE(c) \
+ public: \
+ static rtc::scoped_refptr<c##ProxyWithInternal> Create( \
+ rtc::Thread* primary_thread, INTERNAL_CLASS* c) { \
+ return new rtc::RefCountedObject<c##ProxyWithInternal>(primary_thread, c); \
}
-#define BEGIN_PROXY_MAP(c) \
- PROXY_MAP_BOILERPLATE(c) \
- WORKER_PROXY_MAP_BOILERPLATE(c) \
- REFCOUNTED_PROXY_MAP_BOILERPLATE(c) \
- public: \
- static rtc::scoped_refptr<c##ProxyWithInternal> Create( \
- rtc::Thread* signaling_thread, rtc::Thread* worker_thread, \
- INTERNAL_CLASS* c) { \
- return new rtc::RefCountedObject<c##ProxyWithInternal>(signaling_thread, \
- worker_thread, c); \
+#define BEGIN_PROXY_MAP(c) \
+ PROXY_MAP_BOILERPLATE(c) \
+ SECONDARY_PROXY_MAP_BOILERPLATE(c) \
+ REFCOUNTED_PROXY_MAP_BOILERPLATE(c) \
+ public: \
+ static rtc::scoped_refptr<c##ProxyWithInternal> Create( \
+ rtc::Thread* primary_thread, rtc::Thread* secondary_thread, \
+ INTERNAL_CLASS* c) { \
+ return new rtc::RefCountedObject<c##ProxyWithInternal>( \
+ primary_thread, secondary_thread, c); \
}
#define BEGIN_OWNED_PROXY_MAP(c) \
PROXY_MAP_BOILERPLATE(c) \
- WORKER_PROXY_MAP_BOILERPLATE(c) \
+ SECONDARY_PROXY_MAP_BOILERPLATE(c) \
OWNED_PROXY_MAP_BOILERPLATE(c) \
public: \
static std::unique_ptr<c##Interface> Create( \
- rtc::Thread* signaling_thread, rtc::Thread* worker_thread, \
+ rtc::Thread* primary_thread, rtc::Thread* secondary_thread, \
std::unique_ptr<INTERNAL_CLASS> c) { \
return std::unique_ptr<c##Interface>(new c##ProxyWithInternal( \
- signaling_thread, worker_thread, c.release())); \
+ primary_thread, secondary_thread, c.release())); \
}
-#define PROXY_SIGNALING_THREAD_DESTRUCTOR() \
+#define PROXY_PRIMARY_THREAD_DESTRUCTOR() \
+ private: \
+ rtc::Thread* destructor_thread() const { return primary_thread_; } \
+ \
+ public: // NOLINTNEXTLINE
+
+#define PROXY_SECONDARY_THREAD_DESTRUCTOR() \
private: \
- rtc::Thread* destructor_thread() const { return signaling_thread_; } \
+ rtc::Thread* destructor_thread() const { return secondary_thread_; } \
\
public: // NOLINTNEXTLINE
-#define PROXY_WORKER_THREAD_DESTRUCTOR() \
- private: \
- rtc::Thread* destructor_thread() const { return worker_thread_; } \
- \
- public: // NOLINTNEXTLINE
-
-#define PROXY_METHOD0(r, method) \
- r method() override { \
- MethodCall<C, r> call(c_, &C::method); \
- return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
+#define PROXY_METHOD0(r, method) \
+ r method() override { \
+ MethodCall<C, r> call(c_, &C::method); \
+ return call.Marshal(RTC_FROM_HERE, primary_thread_); \
}
-#define PROXY_CONSTMETHOD0(r, method) \
- r method() const override { \
- ConstMethodCall<C, r> call(c_, &C::method); \
- return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
+#define PROXY_CONSTMETHOD0(r, method) \
+ r method() const override { \
+ ConstMethodCall<C, r> call(c_, &C::method); \
+ return call.Marshal(RTC_FROM_HERE, primary_thread_); \
}
#define PROXY_METHOD1(r, method, t1) \
r method(t1 a1) override { \
MethodCall<C, r, t1> call(c_, &C::method, std::move(a1)); \
- return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
+ return call.Marshal(RTC_FROM_HERE, primary_thread_); \
}
#define PROXY_CONSTMETHOD1(r, method, t1) \
r method(t1 a1) const override { \
ConstMethodCall<C, r, t1> call(c_, &C::method, std::move(a1)); \
- return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
+ return call.Marshal(RTC_FROM_HERE, primary_thread_); \
}
#define PROXY_METHOD2(r, method, t1, t2) \
r method(t1 a1, t2 a2) override { \
MethodCall<C, r, t1, t2> call(c_, &C::method, std::move(a1), \
std::move(a2)); \
- return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
+ return call.Marshal(RTC_FROM_HERE, primary_thread_); \
}
#define PROXY_METHOD3(r, method, t1, t2, t3) \
r method(t1 a1, t2 a2, t3 a3) override { \
MethodCall<C, r, t1, t2, t3> call(c_, &C::method, std::move(a1), \
std::move(a2), std::move(a3)); \
- return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
+ return call.Marshal(RTC_FROM_HERE, primary_thread_); \
}
#define PROXY_METHOD4(r, method, t1, t2, t3, t4) \
@@ -336,7 +342,7 @@
MethodCall<C, r, t1, t2, t3, t4> call(c_, &C::method, std::move(a1), \
std::move(a2), std::move(a3), \
std::move(a4)); \
- return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
+ return call.Marshal(RTC_FROM_HERE, primary_thread_); \
}
#define PROXY_METHOD5(r, method, t1, t2, t3, t4, t5) \
@@ -344,60 +350,60 @@
MethodCall<C, r, t1, t2, t3, t4, t5> call(c_, &C::method, std::move(a1), \
std::move(a2), std::move(a3), \
std::move(a4), std::move(a5)); \
- return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
+ return call.Marshal(RTC_FROM_HERE, primary_thread_); \
}
-// Define methods which should be invoked on the worker thread.
-#define PROXY_WORKER_METHOD0(r, method) \
- r method() override { \
- MethodCall<C, r> call(c_, &C::method); \
- return call.Marshal(RTC_FROM_HERE, worker_thread_); \
+// Define methods which should be invoked on the secondary thread.
+#define PROXY_SECONDARY_METHOD0(r, method) \
+ r method() override { \
+ MethodCall<C, r> call(c_, &C::method); \
+ return call.Marshal(RTC_FROM_HERE, secondary_thread_); \
}
-#define PROXY_WORKER_CONSTMETHOD0(r, method) \
- r method() const override { \
- ConstMethodCall<C, r> call(c_, &C::method); \
- return call.Marshal(RTC_FROM_HERE, worker_thread_); \
+#define PROXY_SECONDARY_CONSTMETHOD0(r, method) \
+ r method() const override { \
+ ConstMethodCall<C, r> call(c_, &C::method); \
+ return call.Marshal(RTC_FROM_HERE, secondary_thread_); \
}
-#define PROXY_WORKER_METHOD1(r, method, t1) \
+#define PROXY_SECONDARY_METHOD1(r, method, t1) \
r method(t1 a1) override { \
MethodCall<C, r, t1> call(c_, &C::method, std::move(a1)); \
- return call.Marshal(RTC_FROM_HERE, worker_thread_); \
+ return call.Marshal(RTC_FROM_HERE, secondary_thread_); \
}
-#define PROXY_WORKER_CONSTMETHOD1(r, method, t1) \
+#define PROXY_SECONDARY_CONSTMETHOD1(r, method, t1) \
r method(t1 a1) const override { \
ConstMethodCall<C, r, t1> call(c_, &C::method, std::move(a1)); \
- return call.Marshal(RTC_FROM_HERE, worker_thread_); \
+ return call.Marshal(RTC_FROM_HERE, secondary_thread_); \
}
-#define PROXY_WORKER_METHOD2(r, method, t1, t2) \
+#define PROXY_SECONDARY_METHOD2(r, method, t1, t2) \
r method(t1 a1, t2 a2) override { \
MethodCall<C, r, t1, t2> call(c_, &C::method, std::move(a1), \
std::move(a2)); \
- return call.Marshal(RTC_FROM_HERE, worker_thread_); \
+ return call.Marshal(RTC_FROM_HERE, secondary_thread_); \
}
-#define PROXY_WORKER_CONSTMETHOD2(r, method, t1, t2) \
+#define PROXY_SECONDARY_CONSTMETHOD2(r, method, t1, t2) \
r method(t1 a1, t2 a2) const override { \
ConstMethodCall<C, r, t1, t2> call(c_, &C::method, std::move(a1), \
std::move(a2)); \
- return call.Marshal(RTC_FROM_HERE, worker_thread_); \
+ return call.Marshal(RTC_FROM_HERE, secondary_thread_); \
}
-#define PROXY_WORKER_METHOD3(r, method, t1, t2, t3) \
+#define PROXY_SECONDARY_METHOD3(r, method, t1, t2, t3) \
r method(t1 a1, t2 a2, t3 a3) override { \
MethodCall<C, r, t1, t2, t3> call(c_, &C::method, std::move(a1), \
std::move(a2), std::move(a3)); \
- return call.Marshal(RTC_FROM_HERE, worker_thread_); \
+ return call.Marshal(RTC_FROM_HERE, secondary_thread_); \
}
-#define PROXY_WORKER_CONSTMETHOD3(r, method, t1, t2) \
+#define PROXY_SECONDARY_CONSTMETHOD3(r, method, t1, t2) \
r method(t1 a1, t2 a2, t3 a3) const override { \
ConstMethodCall<C, r, t1, t2, t3> call(c_, &C::method, std::move(a1), \
std::move(a2), std::move(a3)); \
- return call.Marshal(RTC_FROM_HERE, worker_thread_); \
+ return call.Marshal(RTC_FROM_HERE, secondary_thread_); \
}
// For use when returning purely const state (set during construction).
diff --git a/api/rtp_receiver_interface.h b/api/rtp_receiver_interface.h
index 786ea3a..e0ace54 100644
--- a/api/rtp_receiver_interface.h
+++ b/api/rtp_receiver_interface.h
@@ -26,7 +26,6 @@
#include "api/rtp_parameters.h"
#include "api/scoped_refptr.h"
#include "api/transport/rtp/rtp_source.h"
-#include "rtc_base/deprecation.h"
#include "rtc_base/ref_count.h"
#include "rtc_base/system/rtc_export.h"
@@ -121,8 +120,8 @@
// Define proxy for RtpReceiverInterface.
// TODO(deadbeef): Move this to .cc file and out of api/. What threads methods
// are called on is an implementation detail.
-BEGIN_SIGNALING_PROXY_MAP(RtpReceiver)
-PROXY_SIGNALING_THREAD_DESTRUCTOR()
+BEGIN_PRIMARY_PROXY_MAP(RtpReceiver)
+PROXY_PRIMARY_THREAD_DESTRUCTOR()
PROXY_CONSTMETHOD0(rtc::scoped_refptr<MediaStreamTrackInterface>, track)
PROXY_CONSTMETHOD0(rtc::scoped_refptr<DtlsTransportInterface>, dtls_transport)
PROXY_CONSTMETHOD0(std::vector<std::string>, stream_ids)
diff --git a/api/rtp_sender_interface.h b/api/rtp_sender_interface.h
index a33b800..dd93792 100644
--- a/api/rtp_sender_interface.h
+++ b/api/rtp_sender_interface.h
@@ -104,8 +104,8 @@
// Define proxy for RtpSenderInterface.
// TODO(deadbeef): Move this to .cc file and out of api/. What threads methods
// are called on is an implementation detail.
-BEGIN_SIGNALING_PROXY_MAP(RtpSender)
-PROXY_SIGNALING_THREAD_DESTRUCTOR()
+BEGIN_PRIMARY_PROXY_MAP(RtpSender)
+PROXY_PRIMARY_THREAD_DESTRUCTOR()
PROXY_METHOD1(bool, SetTrack, MediaStreamTrackInterface*)
PROXY_CONSTMETHOD0(rtc::scoped_refptr<MediaStreamTrackInterface>, track)
PROXY_CONSTMETHOD0(rtc::scoped_refptr<DtlsTransportInterface>, dtls_transport)
diff --git a/api/rtp_transceiver_interface.h b/api/rtp_transceiver_interface.h
index 9b46846..4799c4b 100644
--- a/api/rtp_transceiver_interface.h
+++ b/api/rtp_transceiver_interface.h
@@ -14,6 +14,7 @@
#include <string>
#include <vector>
+#include "absl/base/attributes.h"
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "api/media_types.h"
@@ -111,8 +112,8 @@
// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-direction
// TODO(hta): Deprecate SetDirection without error and rename
// SetDirectionWithError to SetDirection, remove default implementations.
- RTC_DEPRECATED virtual void SetDirection(
- RtpTransceiverDirection new_direction);
+ ABSL_DEPRECATED("Use SetDirectionWithError instead")
+ virtual void SetDirection(RtpTransceiverDirection new_direction);
virtual RTCError SetDirectionWithError(RtpTransceiverDirection new_direction);
// The current_direction attribute indicates the current direction negotiated
@@ -140,7 +141,7 @@
// This is an internal function, and is exposed for historical reasons.
// https://w3c.github.io/webrtc-pc/#dfn-stop-the-rtcrtptransceiver
virtual void StopInternal();
- RTC_DEPRECATED virtual void Stop();
+ ABSL_DEPRECATED("Use StopStandard instead") virtual void Stop();
// The SetCodecPreferences method overrides the default codec preferences used
// by WebRTC for this transceiver.
diff --git a/api/scoped_refptr.h b/api/scoped_refptr.h
index fa4e83d..4e3f0eb 100644
--- a/api/scoped_refptr.h
+++ b/api/scoped_refptr.h
@@ -104,6 +104,7 @@
T* get() const { return ptr_; }
operator T*() const { return ptr_; }
+ T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_; }
// Returns the (possibly null) raw pointer, and makes the scoped_refptr hold a
diff --git a/api/sctp_transport_interface.h b/api/sctp_transport_interface.h
index 6af0bfc..7080889 100644
--- a/api/sctp_transport_interface.h
+++ b/api/sctp_transport_interface.h
@@ -35,6 +35,8 @@
// http://w3c.github.io/webrtc-pc/#rtcsctptransport-interface
class RTC_EXPORT SctpTransportInformation {
public:
+ SctpTransportInformation() = default;
+ SctpTransportInformation(const SctpTransportInformation&) = default;
explicit SctpTransportInformation(SctpTransportState state);
SctpTransportInformation(
SctpTransportState state,
diff --git a/api/sequence_checker.h b/api/sequence_checker.h
new file mode 100644
index 0000000..6b46128
--- /dev/null
+++ b/api/sequence_checker.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2019 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 API_SEQUENCE_CHECKER_H_
+#define API_SEQUENCE_CHECKER_H_
+
+#include "rtc_base/checks.h"
+#include "rtc_base/synchronization/sequence_checker_internal.h"
+#include "rtc_base/thread_annotations.h"
+
+namespace webrtc {
+
+// SequenceChecker is a helper class used to help verify that some methods
+// of a class are called on the same task queue or thread. A
+// SequenceChecker is bound to a a task queue if the object is
+// created on a task queue, or a thread otherwise.
+//
+//
+// Example:
+// class MyClass {
+// public:
+// void Foo() {
+// RTC_DCHECK_RUN_ON(sequence_checker_);
+// ... (do stuff) ...
+// }
+//
+// private:
+// SequenceChecker sequence_checker_;
+// }
+//
+// In Release mode, IsCurrent will always return true.
+class RTC_LOCKABLE SequenceChecker
+#if RTC_DCHECK_IS_ON
+ : public webrtc_sequence_checker_internal::SequenceCheckerImpl {
+ using Impl = webrtc_sequence_checker_internal::SequenceCheckerImpl;
+#else
+ : public webrtc_sequence_checker_internal::SequenceCheckerDoNothing {
+ using Impl = webrtc_sequence_checker_internal::SequenceCheckerDoNothing;
+#endif
+ public:
+ // Returns true if sequence checker is attached to the current sequence.
+ bool IsCurrent() const { return Impl::IsCurrent(); }
+ // Detaches checker from sequence to which it is attached. Next attempt
+ // to do a check with this checker will result in attaching this checker
+ // to the sequence on which check was performed.
+ void Detach() { Impl::Detach(); }
+};
+
+} // namespace webrtc
+
+// RTC_RUN_ON/RTC_GUARDED_BY/RTC_DCHECK_RUN_ON macros allows to annotate
+// variables are accessed from same thread/task queue.
+// Using tools designed to check mutexes, it checks at compile time everywhere
+// variable is access, there is a run-time dcheck thread/task queue is correct.
+//
+// class SequenceCheckerExample {
+// public:
+// int CalledFromPacer() RTC_RUN_ON(pacer_sequence_checker_) {
+// return var2_;
+// }
+//
+// void CallMeFromPacer() {
+// RTC_DCHECK_RUN_ON(&pacer_sequence_checker_)
+// << "Should be called from pacer";
+// CalledFromPacer();
+// }
+//
+// private:
+// int pacer_var_ RTC_GUARDED_BY(pacer_sequence_checker_);
+// SequenceChecker pacer_sequence_checker_;
+// };
+//
+// class TaskQueueExample {
+// public:
+// class Encoder {
+// public:
+// rtc::TaskQueueBase& Queue() { return encoder_queue_; }
+// void Encode() {
+// RTC_DCHECK_RUN_ON(&encoder_queue_);
+// DoSomething(var_);
+// }
+//
+// private:
+// rtc::TaskQueueBase& encoder_queue_;
+// Frame var_ RTC_GUARDED_BY(encoder_queue_);
+// };
+//
+// void Encode() {
+// // Will fail at runtime when DCHECK is enabled:
+// // encoder_->Encode();
+// // Will work:
+// rtc::scoped_refptr<Encoder> encoder = encoder_;
+// encoder_->Queue().PostTask([encoder] { encoder->Encode(); });
+// }
+//
+// private:
+// rtc::scoped_refptr<Encoder> encoder_;
+// }
+
+// Document if a function expected to be called from same thread/task queue.
+#define RTC_RUN_ON(x) \
+ RTC_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(x))
+
+#define RTC_DCHECK_RUN_ON(x) \
+ webrtc::webrtc_sequence_checker_internal::SequenceCheckerScope \
+ seq_check_scope(x); \
+ RTC_DCHECK((x)->IsCurrent()) \
+ << webrtc::webrtc_sequence_checker_internal::ExpectationToString(x)
+
+#endif // API_SEQUENCE_CHECKER_H_
diff --git a/rtc_base/synchronization/sequence_checker_unittest.cc b/api/sequence_checker_unittest.cc
similarity index 97%
rename from rtc_base/synchronization/sequence_checker_unittest.cc
rename to api/sequence_checker_unittest.cc
index 6fcb522..4029b8c 100644
--- a/rtc_base/synchronization/sequence_checker_unittest.cc
+++ b/api/sequence_checker_unittest.cc
@@ -8,7 +8,7 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-#include "rtc_base/synchronization/sequence_checker.h"
+#include "api/sequence_checker.h"
#include <memory>
#include <utility>
@@ -17,7 +17,6 @@
#include "rtc_base/event.h"
#include "rtc_base/platform_thread.h"
#include "rtc_base/task_queue_for_test.h"
-#include "rtc_base/thread_checker.h"
#include "test/gtest.h"
namespace webrtc {
diff --git a/api/stats_types.h b/api/stats_types.h
index c1922a8..f910b4a 100644
--- a/api/stats_types.h
+++ b/api/stats_types.h
@@ -21,11 +21,11 @@
#include <vector>
#include "api/scoped_refptr.h"
+#include "api/sequence_checker.h"
#include "rtc_base/constructor_magic.h"
#include "rtc_base/ref_count.h"
#include "rtc_base/string_encode.h"
#include "rtc_base/system/rtc_export.h"
-#include "rtc_base/thread_checker.h"
namespace webrtc {
@@ -344,7 +344,7 @@
const StatsValueName name;
private:
- rtc::ThreadChecker thread_checker_;
+ webrtc::SequenceChecker thread_checker_;
mutable int ref_count_ RTC_GUARDED_BY(thread_checker_) = 0;
const Type type_;
@@ -447,7 +447,7 @@
private:
Container list_;
- rtc::ThreadChecker thread_checker_;
+ webrtc::SequenceChecker thread_checker_;
};
} // namespace webrtc
diff --git a/api/task_queue/task_queue_base.h b/api/task_queue/task_queue_base.h
index 90b1efd..88419ed 100644
--- a/api/task_queue/task_queue_base.h
+++ b/api/task_queue/task_queue_base.h
@@ -27,12 +27,14 @@
// Starts destruction of the task queue.
// On return ensures no task are running and no new tasks are able to start
// on the task queue.
- // Responsible for deallocation. Deallocation may happen syncrhoniously during
+ // Responsible for deallocation. Deallocation may happen synchronously during
// Delete or asynchronously after Delete returns.
// Code not running on the TaskQueue should not make any assumption when
// TaskQueue is deallocated and thus should not call any methods after Delete.
// Code running on the TaskQueue should not call Delete, but can assume
// TaskQueue still exists and may call other methods, e.g. PostTask.
+ // Should be called on the same task queue or thread that this task queue
+ // was created on.
virtual void Delete() = 0;
// Schedules a task to execute. Tasks are executed in FIFO order.
@@ -43,17 +45,20 @@
// TaskQueue or it may happen asynchronously after TaskQueue is deleted.
// This may vary from one implementation to the next so assumptions about
// lifetimes of pending tasks should not be made.
+ // May be called on any thread or task queue, including this task queue.
virtual void PostTask(std::unique_ptr<QueuedTask> task) = 0;
// Schedules a task to execute a specified number of milliseconds from when
// the call is made. The precision should be considered as "best effort"
// and in some cases, such as on Windows when all high precision timers have
// been used up, can be off by as much as 15 millseconds.
+ // May be called on any thread or task queue, including this task queue.
virtual void PostDelayedTask(std::unique_ptr<QueuedTask> task,
uint32_t milliseconds) = 0;
// Returns the task queue that is running the current thread.
// Returns nullptr if this thread is not associated with any task queue.
+ // May be called on any thread or task queue, including this task queue.
static TaskQueueBase* Current();
bool IsCurrent() const { return Current() == this; }
diff --git a/api/transport/goog_cc_factory.h b/api/transport/goog_cc_factory.h
index b14d6dc..e12755d 100644
--- a/api/transport/goog_cc_factory.h
+++ b/api/transport/goog_cc_factory.h
@@ -12,9 +12,9 @@
#define API_TRANSPORT_GOOG_CC_FACTORY_H_
#include <memory>
+#include "absl/base/attributes.h"
#include "api/network_state_predictor.h"
#include "api/transport/network_control.h"
-#include "rtc_base/deprecation.h"
namespace webrtc {
class RtcEventLog;
@@ -31,8 +31,8 @@
: public NetworkControllerFactoryInterface {
public:
GoogCcNetworkControllerFactory() = default;
- explicit RTC_DEPRECATED GoogCcNetworkControllerFactory(
- RtcEventLog* event_log);
+ ABSL_DEPRECATED("")
+ explicit GoogCcNetworkControllerFactory(RtcEventLog* event_log);
explicit GoogCcNetworkControllerFactory(
NetworkStatePredictorFactoryInterface* network_state_predictor_factory);
@@ -49,7 +49,8 @@
// Deprecated, use GoogCcFactoryConfig to enable feedback only mode instead.
// Factory to create packet feedback only GoogCC, this can be used for
// connections providing packet receive time feedback but no other reports.
-class RTC_DEPRECATED GoogCcFeedbackNetworkControllerFactory
+class ABSL_DEPRECATED("use GoogCcFactoryConfig instead")
+ GoogCcFeedbackNetworkControllerFactory
: public GoogCcNetworkControllerFactory {
public:
explicit GoogCcFeedbackNetworkControllerFactory(RtcEventLog* event_log);
diff --git a/api/transport/network_types.h b/api/transport/network_types.h
index 10fc0be..38a8917 100644
--- a/api/transport/network_types.h
+++ b/api/transport/network_types.h
@@ -19,7 +19,6 @@
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
-#include "rtc_base/deprecation.h"
namespace webrtc {
diff --git a/api/transport/stun.cc b/api/transport/stun.cc
index e1bf03b..1b5bf0c 100644
--- a/api/transport/stun.cc
+++ b/api/transport/stun.cc
@@ -246,6 +246,31 @@
GetAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES));
}
+StunMessage::IntegrityStatus StunMessage::ValidateMessageIntegrity(
+ const std::string& password) {
+ password_ = password;
+ if (GetByteString(STUN_ATTR_MESSAGE_INTEGRITY)) {
+ if (ValidateMessageIntegrityOfType(
+ STUN_ATTR_MESSAGE_INTEGRITY, kStunMessageIntegritySize,
+ buffer_.c_str(), buffer_.size(), password)) {
+ integrity_ = IntegrityStatus::kIntegrityOk;
+ } else {
+ integrity_ = IntegrityStatus::kIntegrityBad;
+ }
+ } else if (GetByteString(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32)) {
+ if (ValidateMessageIntegrityOfType(
+ STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32, kStunMessageIntegrity32Size,
+ buffer_.c_str(), buffer_.size(), password)) {
+ integrity_ = IntegrityStatus::kIntegrityOk;
+ } else {
+ integrity_ = IntegrityStatus::kIntegrityBad;
+ }
+ } else {
+ integrity_ = IntegrityStatus::kNoIntegrity;
+ }
+ return integrity_;
+}
+
bool StunMessage::ValidateMessageIntegrity(const char* data,
size_t size,
const std::string& password) {
@@ -353,11 +378,6 @@
password.size());
}
-bool StunMessage::AddMessageIntegrity(const char* key, size_t keylen) {
- return AddMessageIntegrityOfType(STUN_ATTR_MESSAGE_INTEGRITY,
- kStunMessageIntegritySize, key, keylen);
-}
-
bool StunMessage::AddMessageIntegrity32(absl::string_view password) {
return AddMessageIntegrityOfType(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32,
kStunMessageIntegrity32Size, password.data(),
@@ -395,6 +415,8 @@
// Insert correct HMAC into the attribute.
msg_integrity_attr->CopyBytes(hmac, attr_size);
+ password_.assign(key, keylen);
+ integrity_ = IntegrityStatus::kIntegrityOk;
return true;
}
@@ -473,6 +495,9 @@
}
bool StunMessage::Read(ByteBufferReader* buf) {
+ // Keep a copy of the buffer data around for later verification.
+ buffer_.assign(buf->Data(), buf->Length());
+
if (!buf->ReadUInt16(&type_)) {
return false;
}
diff --git a/api/transport/stun.h b/api/transport/stun.h
index 8893b2a..682a17a 100644
--- a/api/transport/stun.h
+++ b/api/transport/stun.h
@@ -16,6 +16,7 @@
#include <stddef.h>
#include <stdint.h>
+
#include <functional>
#include <memory>
#include <string>
@@ -149,15 +150,24 @@
StunMessage();
virtual ~StunMessage();
+ // The verification status of the message. This is checked on parsing,
+ // or set by AddMessageIntegrity.
+ enum class IntegrityStatus {
+ kNotSet,
+ kNoIntegrity, // Message-integrity attribute missing
+ kIntegrityOk, // Message-integrity checked OK
+ kIntegrityBad, // Message-integrity verification failed
+ };
+
int type() const { return type_; }
size_t length() const { return length_; }
const std::string& transaction_id() const { return transaction_id_; }
uint32_t reduced_transaction_id() const { return reduced_transaction_id_; }
// Returns true if the message confirms to RFC3489 rather than
- // RFC5389. The main difference between two version of the STUN
+ // RFC5389. The main difference between the two versions of the STUN
// protocol is the presence of the magic cookie and different length
- // of transaction ID. For outgoing packets version of the protocol
+ // of transaction ID. For outgoing packets the version of the protocol
// is determined by the lengths of the transaction ID.
bool IsLegacy() const;
@@ -191,19 +201,27 @@
// Remote all attributes and releases them.
void ClearAttributes();
- // Validates that a raw STUN message has a correct MESSAGE-INTEGRITY value.
- // This can't currently be done on a StunMessage, since it is affected by
- // padding data (which we discard when reading a StunMessage).
- static bool ValidateMessageIntegrity(const char* data,
- size_t size,
- const std::string& password);
- static bool ValidateMessageIntegrity32(const char* data,
- size_t size,
- const std::string& password);
+ // Validates that a STUN message has a correct MESSAGE-INTEGRITY value.
+ // This uses the buffered raw-format message stored by Read().
+ IntegrityStatus ValidateMessageIntegrity(const std::string& password);
+
+ // Returns the current integrity status of the message.
+ IntegrityStatus integrity() const { return integrity_; }
+
+ // Shortcut for checking if integrity is verified.
+ bool IntegrityOk() const {
+ return integrity_ == IntegrityStatus::kIntegrityOk;
+ }
+
+ // Returns the password attribute used to set or check the integrity.
+ // Can only be called after adding or checking the integrity.
+ std::string password() const {
+ RTC_DCHECK(integrity_ != IntegrityStatus::kNotSet);
+ return password_;
+ }
// Adds a MESSAGE-INTEGRITY attribute that is valid for the current message.
bool AddMessageIntegrity(const std::string& password);
- bool AddMessageIntegrity(const char* key, size_t keylen);
// Adds a STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32 attribute that is valid for the
// current message.
@@ -244,6 +262,30 @@
bool EqualAttributes(const StunMessage* other,
std::function<bool(int type)> attribute_type_mask) const;
+ // Expose raw-buffer ValidateMessageIntegrity function for testing.
+ static bool ValidateMessageIntegrityForTesting(const char* data,
+ size_t size,
+ const std::string& password) {
+ return ValidateMessageIntegrity(data, size, password);
+ }
+ // Expose raw-buffer ValidateMessageIntegrity function for testing.
+ static bool ValidateMessageIntegrity32ForTesting(
+ const char* data,
+ size_t size,
+ const std::string& password) {
+ return ValidateMessageIntegrity32(data, size, password);
+ }
+ // Validates that a STUN message in byte buffer form
+ // has a correct MESSAGE-INTEGRITY value.
+ // These functions are not recommended and will be deprecated; use
+ // ValidateMessageIntegrity(password) on the parsed form instead.
+ static bool ValidateMessageIntegrity(const char* data,
+ size_t size,
+ const std::string& password);
+ static bool ValidateMessageIntegrity32(const char* data,
+ size_t size,
+ const std::string& password);
+
protected:
// Verifies that the given attribute is allowed for this message.
virtual StunAttributeValueType GetAttributeValueType(int type) const;
@@ -269,6 +311,10 @@
std::string transaction_id_;
uint32_t reduced_transaction_id_;
uint32_t stun_magic_cookie_;
+ // The original buffer for messages created by Read().
+ std::string buffer_;
+ IntegrityStatus integrity_ = IntegrityStatus::kNotSet;
+ std::string password_;
};
// Base class for all STUN/TURN attributes.
diff --git a/api/transport/stun_unittest.cc b/api/transport/stun_unittest.cc
index bf2717e..bf791f2 100644
--- a/api/transport/stun_unittest.cc
+++ b/api/transport/stun_unittest.cc
@@ -1196,24 +1196,24 @@
// Check our STUN message validation code against the RFC5769 test messages.
TEST_F(StunTest, ValidateMessageIntegrity) {
// Try the messages from RFC 5769.
- EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kRfc5769SampleRequest),
sizeof(kRfc5769SampleRequest), kRfc5769SampleMsgPassword));
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kRfc5769SampleRequest),
sizeof(kRfc5769SampleRequest), "InvalidPassword"));
- EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kRfc5769SampleResponse),
sizeof(kRfc5769SampleResponse), kRfc5769SampleMsgPassword));
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kRfc5769SampleResponse),
sizeof(kRfc5769SampleResponse), "InvalidPassword"));
- EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kRfc5769SampleResponseIPv6),
sizeof(kRfc5769SampleResponseIPv6), kRfc5769SampleMsgPassword));
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kRfc5769SampleResponseIPv6),
sizeof(kRfc5769SampleResponseIPv6), "InvalidPassword"));
@@ -1222,40 +1222,40 @@
ComputeStunCredentialHash(kRfc5769SampleMsgWithAuthUsername,
kRfc5769SampleMsgWithAuthRealm,
kRfc5769SampleMsgWithAuthPassword, &key);
- EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kRfc5769SampleRequestLongTermAuth),
sizeof(kRfc5769SampleRequestLongTermAuth), key));
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kRfc5769SampleRequestLongTermAuth),
sizeof(kRfc5769SampleRequestLongTermAuth), "InvalidPassword"));
// Try some edge cases.
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kStunMessageWithZeroLength),
sizeof(kStunMessageWithZeroLength), kRfc5769SampleMsgPassword));
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kStunMessageWithExcessLength),
sizeof(kStunMessageWithExcessLength), kRfc5769SampleMsgPassword));
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kStunMessageWithSmallLength),
sizeof(kStunMessageWithSmallLength), kRfc5769SampleMsgPassword));
// Again, but with the lengths matching what is claimed in the headers.
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kStunMessageWithZeroLength),
kStunHeaderSize + rtc::GetBE16(&kStunMessageWithZeroLength[2]),
kRfc5769SampleMsgPassword));
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kStunMessageWithExcessLength),
kStunHeaderSize + rtc::GetBE16(&kStunMessageWithExcessLength[2]),
kRfc5769SampleMsgPassword));
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kStunMessageWithSmallLength),
kStunHeaderSize + rtc::GetBE16(&kStunMessageWithSmallLength[2]),
kRfc5769SampleMsgPassword));
// Check that a too-short HMAC doesn't cause buffer overflow.
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(kStunMessageWithBadHmacAtEnd),
sizeof(kStunMessageWithBadHmacAtEnd), kRfc5769SampleMsgPassword));
@@ -1268,8 +1268,8 @@
if (i > 0)
buf[i - 1] ^= 0x01;
EXPECT_EQ(i >= sizeof(buf) - 8,
- StunMessage::ValidateMessageIntegrity(buf, sizeof(buf),
- kRfc5769SampleMsgPassword));
+ StunMessage::ValidateMessageIntegrityForTesting(
+ buf, sizeof(buf), kRfc5769SampleMsgPassword));
}
}
@@ -1291,7 +1291,7 @@
rtc::ByteBufferWriter buf1;
EXPECT_TRUE(msg.Write(&buf1));
- EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(buf1.Data()), buf1.Length(),
kRfc5769SampleMsgPassword));
@@ -1309,7 +1309,7 @@
rtc::ByteBufferWriter buf3;
EXPECT_TRUE(msg2.Write(&buf3));
- EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(buf3.Data()), buf3.Length(),
kRfc5769SampleMsgPassword));
}
@@ -1317,40 +1317,40 @@
// Check our STUN message validation code against the RFC5769 test messages.
TEST_F(StunTest, ValidateMessageIntegrity32) {
// Try the messages from RFC 5769.
- EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32(
+ EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(kSampleRequestMI32),
sizeof(kSampleRequestMI32), kRfc5769SampleMsgPassword));
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(kSampleRequestMI32),
sizeof(kSampleRequestMI32), "InvalidPassword"));
// Try some edge cases.
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(kStunMessageWithZeroLength),
sizeof(kStunMessageWithZeroLength), kRfc5769SampleMsgPassword));
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(kStunMessageWithExcessLength),
sizeof(kStunMessageWithExcessLength), kRfc5769SampleMsgPassword));
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(kStunMessageWithSmallLength),
sizeof(kStunMessageWithSmallLength), kRfc5769SampleMsgPassword));
// Again, but with the lengths matching what is claimed in the headers.
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(kStunMessageWithZeroLength),
kStunHeaderSize + rtc::GetBE16(&kStunMessageWithZeroLength[2]),
kRfc5769SampleMsgPassword));
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(kStunMessageWithExcessLength),
kStunHeaderSize + rtc::GetBE16(&kStunMessageWithExcessLength[2]),
kRfc5769SampleMsgPassword));
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(kStunMessageWithSmallLength),
kStunHeaderSize + rtc::GetBE16(&kStunMessageWithSmallLength[2]),
kRfc5769SampleMsgPassword));
// Check that a too-short HMAC doesn't cause buffer overflow.
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(kStunMessageWithBadHmacAtEnd),
sizeof(kStunMessageWithBadHmacAtEnd), kRfc5769SampleMsgPassword));
@@ -1363,7 +1363,7 @@
if (i > 0)
buf[i - 1] ^= 0x01;
EXPECT_EQ(i >= sizeof(buf) - 8,
- StunMessage::ValidateMessageIntegrity32(
+ StunMessage::ValidateMessageIntegrity32ForTesting(
buf, sizeof(buf), kRfc5769SampleMsgPassword));
}
}
@@ -1384,7 +1384,7 @@
rtc::ByteBufferWriter buf1;
EXPECT_TRUE(msg.Write(&buf1));
- EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32(
+ EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(buf1.Data()), buf1.Length(),
kRfc5769SampleMsgPassword));
@@ -1402,7 +1402,7 @@
rtc::ByteBufferWriter buf3;
EXPECT_TRUE(msg2.Write(&buf3));
- EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32(
+ EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(buf3.Data()), buf3.Length(),
kRfc5769SampleMsgPassword));
}
@@ -1420,14 +1420,14 @@
rtc::ByteBufferWriter buf1;
EXPECT_TRUE(msg.Write(&buf1));
- EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32(
+ EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(buf1.Data()), buf1.Length(), "password1"));
- EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_TRUE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(buf1.Data()), buf1.Length(), "password2"));
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting(
reinterpret_cast<const char*>(buf1.Data()), buf1.Length(), "password2"));
- EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
+ EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting(
reinterpret_cast<const char*>(buf1.Data()), buf1.Length(), "password1"));
}
diff --git a/api/uma_metrics.h b/api/uma_metrics.h
index 30543b6..3e0deb0 100644
--- a/api/uma_metrics.h
+++ b/api/uma_metrics.h
@@ -167,6 +167,41 @@
kSimulcastApiVersionMax
};
+// Metrics for reporting usage of BUNDLE.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum BundleUsage {
+ // There are no m-lines in the SDP, only a session description.
+ kBundleUsageEmpty = 0,
+ // Only a data channel is negotiated but BUNDLE is not negotiated.
+ kBundleUsageNoBundleDatachannelOnly = 1,
+ // BUNDLE is not negotiated and there is at most one m-line per media type,
+ kBundleUsageNoBundleSimple = 2,
+ // BUNDLE is not negotiated and there are multiple m-lines per media type,
+ kBundleUsageNoBundleComplex = 3,
+ // Only a data channel is negotiated and BUNDLE is negotiated.
+ kBundleUsageBundleDatachannelOnly = 4,
+ // BUNDLE is negotiated but there is at most one m-line per media type,
+ kBundleUsageBundleSimple = 5,
+ // BUNDLE is negotiated and there are multiple m-lines per media type,
+ kBundleUsageBundleComplex = 6,
+ // Legacy plan-b metrics.
+ kBundleUsageNoBundlePlanB = 7,
+ kBundleUsageBundlePlanB = 8,
+ kBundleUsageMax
+};
+
+// Metrics for reporting configured BUNDLE policy, mapping directly to
+// https://w3c.github.io/webrtc-pc/#rtcbundlepolicy-enum
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum BundlePolicyUsage {
+ kBundlePolicyUsageBalanced = 0,
+ kBundlePolicyUsageMaxBundle = 1,
+ kBundlePolicyUsageMaxCompat = 2,
+ kBundlePolicyUsageMax
+};
+
// When adding new metrics please consider using the style described in
// https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md#usage
// instead of the legacy enums used above.
diff --git a/api/video/encoded_frame.h b/api/video/encoded_frame.h
index 6a2b1f8..44a1430 100644
--- a/api/video/encoded_frame.h
+++ b/api/video/encoded_frame.h
@@ -19,36 +19,6 @@
namespace webrtc {
namespace video_coding {
-// NOTE: This class is still under development and may change without notice.
-struct VideoLayerFrameId {
- // TODO(philipel): The default ctor is currently used internaly, but have a
- // look if we can remove it.
- VideoLayerFrameId() : picture_id(-1), spatial_layer(0) {}
- VideoLayerFrameId(int64_t picture_id, uint8_t spatial_layer)
- : picture_id(picture_id), spatial_layer(spatial_layer) {}
-
- bool operator==(const VideoLayerFrameId& rhs) const {
- return picture_id == rhs.picture_id && spatial_layer == rhs.spatial_layer;
- }
-
- bool operator!=(const VideoLayerFrameId& rhs) const {
- return !(*this == rhs);
- }
-
- bool operator<(const VideoLayerFrameId& rhs) const {
- if (picture_id == rhs.picture_id)
- return spatial_layer < rhs.spatial_layer;
- return picture_id < rhs.picture_id;
- }
-
- bool operator<=(const VideoLayerFrameId& rhs) const { return !(rhs < *this); }
- bool operator>(const VideoLayerFrameId& rhs) const { return rhs < *this; }
- bool operator>=(const VideoLayerFrameId& rhs) const { return rhs <= *this; }
-
- int64_t picture_id;
- uint8_t spatial_layer;
-};
-
// TODO(philipel): Remove webrtc::VCMEncodedFrame inheritance.
// TODO(philipel): Move transport specific info out of EncodedFrame.
// NOTE: This class is still under development and may change without notice.
@@ -73,7 +43,8 @@
bool is_keyframe() const { return num_references == 0; }
- VideoLayerFrameId id;
+ void SetId(int64_t id) { id_ = id; }
+ int64_t Id() const { return id_; }
// TODO(philipel): Add simple modify/access functions to prevent adding too
// many |references|.
@@ -82,6 +53,11 @@
// Is this subframe the last one in the superframe (In RTP stream that would
// mean that the last packet has a marker bit set).
bool is_last_spatial_layer = true;
+
+ private:
+ // The ID of the frame is determined from RTP level information. The IDs are
+ // used to describe order and dependencies between frames.
+ int64_t id_ = -1;
};
} // namespace video_coding
diff --git a/api/video/encoded_image.h b/api/video/encoded_image.h
index 650766a..60e6625 100644
--- a/api/video/encoded_image.h
+++ b/api/video/encoded_image.h
@@ -16,6 +16,7 @@
#include <map>
#include <utility>
+#include "absl/base/attributes.h"
#include "absl/types/optional.h"
#include "api/rtp_packet_infos.h"
#include "api/scoped_refptr.h"
@@ -26,7 +27,6 @@
#include "api/video/video_rotation.h"
#include "api/video/video_timing.h"
#include "rtc_base/checks.h"
-#include "rtc_base/deprecation.h"
#include "rtc_base/ref_count.h"
#include "rtc_base/system/rtc_export.h"
@@ -73,7 +73,8 @@
EncodedImage();
EncodedImage(EncodedImage&&);
EncodedImage(const EncodedImage&);
- RTC_DEPRECATED EncodedImage(uint8_t* buffer, size_t length, size_t capacity);
+ ABSL_DEPRECATED("")
+ EncodedImage(uint8_t* buffer, size_t length, size_t capacity);
~EncodedImage();
diff --git a/api/video/video_frame.h b/api/video/video_frame.h
index e62aae8..e073fd5 100644
--- a/api/video/video_frame.h
+++ b/api/video/video_frame.h
@@ -134,11 +134,11 @@
// Get frame size in pixels.
uint32_t size() const;
- // Get frame ID. Returns 0 if ID is not set. Not guarantee to be transferred
- // from the sender to the receiver, but preserved on single side. The id
+ // Get frame ID. Returns 0 if ID is not set. Not guaranteed to be transferred
+ // from the sender to the receiver, but preserved on the sender side. The id
// should be propagated between all frame modifications during its lifetime
// from capturing to sending as encoded image. It is intended to be unique
- // over a time window of a few minutes for peer connection, to which
+ // over a time window of a few minutes for the peer connection to which the
// corresponding video stream belongs to.
uint16_t id() const { return id_; }
void set_id(uint16_t id) { id_ = id; }
diff --git a/api/video/video_source_interface.h b/api/video/video_source_interface.h
index b03d7c5..8b5823f 100644
--- a/api/video/video_source_interface.h
+++ b/api/video/video_source_interface.h
@@ -12,6 +12,7 @@
#define API_VIDEO_VIDEO_SOURCE_INTERFACE_H_
#include <limits>
+#include <vector>
#include "absl/types/optional.h"
#include "api/video/video_sink_interface.h"
@@ -22,6 +23,15 @@
// VideoSinkWants is used for notifying the source of properties a video frame
// should have when it is delivered to a certain sink.
struct RTC_EXPORT VideoSinkWants {
+ struct FrameSize {
+ FrameSize(int width, int height) : width(width), height(height) {}
+ FrameSize(const FrameSize&) = default;
+ ~FrameSize() = default;
+
+ int width;
+ int height;
+ };
+
VideoSinkWants();
VideoSinkWants(const VideoSinkWants&);
~VideoSinkWants();
@@ -49,8 +59,34 @@
// Note that this field is unrelated to any horizontal or vertical stride
// requirements the encoder has on the incoming video frame buffers.
int resolution_alignment = 1;
+
+ // The resolutions that sink is configured to consume. If the sink is an
+ // encoder this is what the encoder is configured to encode. In singlecast we
+ // only encode one resolution, but in simulcast and SVC this can mean multiple
+ // resolutions per frame.
+ //
+ // The sink is always configured to consume a subset of the
+ // webrtc::VideoFrame's resolution. In the case of encoding, we usually encode
+ // at webrtc::VideoFrame's resolution but this may not always be the case due
+ // to scaleResolutionDownBy or turning off simulcast or SVC layers.
+ //
+ // For example, we may capture at 720p and due to adaptation (e.g. applying
+ // |max_pixel_count| constraints) create webrtc::VideoFrames of size 480p, but
+ // if we do scaleResolutionDownBy:2 then the only resolution we end up
+ // encoding is 240p. In this case we still need to provide webrtc::VideoFrames
+ // of size 480p but we can optimize internal buffers for 240p, avoiding
+ // downsampling to 480p if possible.
+ //
+ // Note that the |resolutions| can change while frames are in flight and
+ // should only be used as a hint when constructing the webrtc::VideoFrame.
+ std::vector<FrameSize> resolutions;
};
+inline bool operator==(const VideoSinkWants::FrameSize& a,
+ const VideoSinkWants::FrameSize& b) {
+ return a.width == b.width && a.height == b.height;
+}
+
template <typename VideoFrameT>
class VideoSourceInterface {
public:
diff --git a/api/video/video_stream_decoder.h b/api/video/video_stream_decoder.h
index 4bf8b98..a1f4406 100644
--- a/api/video/video_stream_decoder.h
+++ b/api/video/video_stream_decoder.h
@@ -38,9 +38,7 @@
// Called when the VideoStreamDecoder enters a non-decodable state.
virtual void OnNonDecodableState() = 0;
- // Called with the last continuous frame.
- virtual void OnContinuousUntil(
- const video_coding::VideoLayerFrameId& key) = 0;
+ virtual void OnContinuousUntil(int64_t frame_id) {}
virtual void OnDecodedFrame(VideoFrame frame,
const FrameInfo& frame_info) = 0;
diff --git a/api/video/video_stream_decoder_create_unittest.cc b/api/video/video_stream_decoder_create_unittest.cc
index 93edb4b..849a054 100644
--- a/api/video/video_stream_decoder_create_unittest.cc
+++ b/api/video/video_stream_decoder_create_unittest.cc
@@ -21,7 +21,6 @@
public:
~NullCallbacks() override = default;
void OnNonDecodableState() override {}
- void OnContinuousUntil(const video_coding::VideoLayerFrameId& key) override {}
void OnDecodedFrame(VideoFrame frame,
const VideoStreamDecoderInterface::Callbacks::FrameInfo&
frame_info) override {}
diff --git a/api/video_track_source_proxy.h b/api/video_track_source_proxy.h
index 692ff64..0b60d20 100644
--- a/api/video_track_source_proxy.h
+++ b/api/video_track_source_proxy.h
@@ -21,27 +21,27 @@
// TODO(deadbeef): Move this to .cc file and out of api/. What threads methods
// are called on is an implementation detail.
BEGIN_PROXY_MAP(VideoTrackSource)
-PROXY_SIGNALING_THREAD_DESTRUCTOR()
+PROXY_PRIMARY_THREAD_DESTRUCTOR()
PROXY_CONSTMETHOD0(SourceState, state)
BYPASS_PROXY_CONSTMETHOD0(bool, remote)
BYPASS_PROXY_CONSTMETHOD0(bool, is_screencast)
PROXY_CONSTMETHOD0(absl::optional<bool>, needs_denoising)
PROXY_METHOD1(bool, GetStats, Stats*)
-PROXY_WORKER_METHOD2(void,
- AddOrUpdateSink,
- rtc::VideoSinkInterface<VideoFrame>*,
- const rtc::VideoSinkWants&)
-PROXY_WORKER_METHOD1(void, RemoveSink, rtc::VideoSinkInterface<VideoFrame>*)
+PROXY_SECONDARY_METHOD2(void,
+ AddOrUpdateSink,
+ rtc::VideoSinkInterface<VideoFrame>*,
+ const rtc::VideoSinkWants&)
+PROXY_SECONDARY_METHOD1(void, RemoveSink, rtc::VideoSinkInterface<VideoFrame>*)
PROXY_METHOD1(void, RegisterObserver, ObserverInterface*)
PROXY_METHOD1(void, UnregisterObserver, ObserverInterface*)
PROXY_CONSTMETHOD0(bool, SupportsEncodedOutput)
-PROXY_WORKER_METHOD0(void, GenerateKeyFrame)
-PROXY_WORKER_METHOD1(void,
- AddEncodedSink,
- rtc::VideoSinkInterface<RecordableEncodedFrame>*)
-PROXY_WORKER_METHOD1(void,
- RemoveEncodedSink,
- rtc::VideoSinkInterface<RecordableEncodedFrame>*)
+PROXY_SECONDARY_METHOD0(void, GenerateKeyFrame)
+PROXY_SECONDARY_METHOD1(void,
+ AddEncodedSink,
+ rtc::VideoSinkInterface<RecordableEncodedFrame>*)
+PROXY_SECONDARY_METHOD1(void,
+ RemoveEncodedSink,
+ rtc::VideoSinkInterface<RecordableEncodedFrame>*)
END_PROXY_MAP()
} // namespace webrtc
diff --git a/audio/BUILD.gn b/audio/BUILD.gn
index 6901e33..ccbf9fd 100644
--- a/audio/BUILD.gn
+++ b/audio/BUILD.gn
@@ -47,6 +47,7 @@
"../api:rtp_headers",
"../api:rtp_parameters",
"../api:scoped_refptr",
+ "../api:sequence_checker",
"../api:transport_api",
"../api/audio:aec3_factory",
"../api/audio:audio_frame_api",
@@ -90,9 +91,9 @@
"../rtc_base:rtc_base_approved",
"../rtc_base:rtc_task_queue",
"../rtc_base:safe_minmax",
+ "../rtc_base:threading",
"../rtc_base/experiments:field_trial_parser",
"../rtc_base/synchronization:mutex",
- "../rtc_base/synchronization:sequence_checker",
"../rtc_base/system:no_unique_address",
"../rtc_base/task_utils:to_queued_task",
"../system_wrappers",
@@ -191,7 +192,7 @@
]
}
- if (rtc_enable_protobuf) {
+ if (rtc_enable_protobuf && !build_with_chromium) {
rtc_test("low_bandwidth_audio_test") {
testonly = true
@@ -219,8 +220,8 @@
"../test:test_support",
"../test/pc/e2e:network_quality_metrics_reporter",
"//testing/gtest",
- "//third_party/abseil-cpp/absl/flags:flag",
]
+ absl_deps = [ "//third_party/abseil-cpp/absl/flags:flag" ]
if (is_android) {
deps += [ "//testing/android/native_test:native_test_native_code" ]
}
@@ -278,30 +279,32 @@
}
}
- rtc_library("audio_perf_tests") {
- testonly = true
+ if (!build_with_chromium) {
+ rtc_library("audio_perf_tests") {
+ testonly = true
- sources = [
- "test/audio_bwe_integration_test.cc",
- "test/audio_bwe_integration_test.h",
- ]
- deps = [
- "../api:simulated_network_api",
- "../api/task_queue",
- "../call:fake_network",
- "../call:simulated_network",
- "../common_audio",
- "../rtc_base:rtc_base_approved",
- "../rtc_base:task_queue_for_test",
- "../system_wrappers",
- "../test:field_trial",
- "../test:fileutils",
- "../test:test_common",
- "../test:test_main",
- "../test:test_support",
- "//testing/gtest",
- ]
+ sources = [
+ "test/audio_bwe_integration_test.cc",
+ "test/audio_bwe_integration_test.h",
+ ]
+ deps = [
+ "../api:simulated_network_api",
+ "../api/task_queue",
+ "../call:fake_network",
+ "../call:simulated_network",
+ "../common_audio",
+ "../rtc_base:rtc_base_approved",
+ "../rtc_base:task_queue_for_test",
+ "../system_wrappers",
+ "../test:field_trial",
+ "../test:fileutils",
+ "../test:test_common",
+ "../test:test_main",
+ "../test:test_support",
+ "//testing/gtest",
+ ]
- data = [ "//resources/voice_engine/audio_dtx16.wav" ]
+ data = [ "//resources/voice_engine/audio_dtx16.wav" ]
+ }
}
}
diff --git a/audio/audio_receive_stream.cc b/audio/audio_receive_stream.cc
index 54c8a02..e037ddc 100644
--- a/audio/audio_receive_stream.cc
+++ b/audio/audio_receive_stream.cc
@@ -24,6 +24,7 @@
#include "audio/conversion.h"
#include "call/rtp_config.h"
#include "call/rtp_stream_receiver_controller_interface.h"
+#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/strings/string_builder.h"
@@ -118,8 +119,8 @@
webrtc::RtcEventLog* event_log,
std::unique_ptr<voe::ChannelReceiveInterface> channel_receive)
: audio_state_(audio_state),
- channel_receive_(std::move(channel_receive)),
- source_tracker_(clock) {
+ source_tracker_(clock),
+ channel_receive_(std::move(channel_receive)) {
RTC_LOG(LS_INFO) << "AudioReceiveStream: " << config.rtp.remote_ssrc;
RTC_DCHECK(config.decoder_factory);
RTC_DCHECK(config.rtcp_send_transport);
@@ -133,6 +134,11 @@
// Configure bandwidth estimation.
channel_receive_->RegisterReceiverCongestionControlObjects(packet_router);
+ // When output is muted, ChannelReceive will directly notify the source
+ // tracker of "delivered" frames, so RtpReceiver information will continue to
+ // be updated.
+ channel_receive_->SetSourceTracker(&source_tracker_);
+
// Register with transport.
rtp_stream_receiver_ = receiver_controller->CreateReceiver(
config.rtp.remote_ssrc, channel_receive_.get());
@@ -173,6 +179,11 @@
audio_state()->RemoveReceivingStream(this);
}
+bool AudioReceiveStream::IsRunning() const {
+ RTC_DCHECK_RUN_ON(&worker_thread_checker_);
+ return playing_;
+}
+
webrtc::AudioReceiveStream::Stats AudioReceiveStream::GetStats(
bool get_and_clear_legacy_stats) const {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
@@ -336,6 +347,7 @@
}
void AudioReceiveStream::AssociateSendStream(AudioSendStream* send_stream) {
+ // TODO(bugs.webrtc.org/11993): Expect to be called on the network thread.
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
channel_receive_->SetAssociatedSendChannel(
send_stream ? send_stream->GetChannel() : nullptr);
@@ -357,6 +369,8 @@
const AudioSendStream* AudioReceiveStream::GetAssociatedSendStreamForTesting()
const {
+ // TODO(bugs.webrtc.org/11993): Expect to be called on the network thread or
+ // remove test method and |associated_send_stream_| variable.
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
return associated_send_stream_;
}
diff --git a/audio/audio_receive_stream.h b/audio/audio_receive_stream.h
index 32f8b60..545a120 100644
--- a/audio/audio_receive_stream.h
+++ b/audio/audio_receive_stream.h
@@ -17,11 +17,11 @@
#include "api/audio/audio_mixer.h"
#include "api/neteq/neteq_factory.h"
#include "api/rtp_headers.h"
+#include "api/sequence_checker.h"
#include "audio/audio_state.h"
#include "call/audio_receive_stream.h"
#include "call/syncable.h"
#include "modules/rtp_rtcp/source/source_tracker.h"
-#include "rtc_base/thread_checker.h"
#include "system_wrappers/include/clock.h"
namespace webrtc {
@@ -71,6 +71,8 @@
void Reconfigure(const webrtc::AudioReceiveStream::Config& config) override;
void Start() override;
void Stop() override;
+ bool IsRunning() const override;
+
webrtc::AudioReceiveStream::Stats GetStats(
bool get_and_clear_legacy_stats) const override;
void SetSink(AudioSinkInterface* sink) override;
@@ -106,12 +108,12 @@
AudioState* audio_state() const;
- rtc::ThreadChecker worker_thread_checker_;
- rtc::ThreadChecker module_process_thread_checker_;
+ SequenceChecker worker_thread_checker_;
+ SequenceChecker module_process_thread_checker_;
webrtc::AudioReceiveStream::Config config_;
rtc::scoped_refptr<webrtc::AudioState> audio_state_;
- const std::unique_ptr<voe::ChannelReceiveInterface> channel_receive_;
SourceTracker source_tracker_;
+ const std::unique_ptr<voe::ChannelReceiveInterface> channel_receive_;
AudioSendStream* associated_send_stream_ = nullptr;
bool playing_ RTC_GUARDED_BY(worker_thread_checker_) = false;
diff --git a/audio/audio_receive_stream_unittest.cc b/audio/audio_receive_stream_unittest.cc
index fcd691e..99e3a56 100644
--- a/audio/audio_receive_stream_unittest.cc
+++ b/audio/audio_receive_stream_unittest.cc
@@ -106,6 +106,7 @@
}));
EXPECT_CALL(*channel_receive_, SetDepacketizerToDecoderFrameTransformer(_))
.Times(1);
+ EXPECT_CALL(*channel_receive_, SetSourceTracker(_));
stream_config_.rtp.local_ssrc = kLocalSsrc;
stream_config_.rtp.remote_ssrc = kRemoteSsrc;
diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc
index 4e21b1f..b769569 100644
--- a/audio/audio_send_stream.cc
+++ b/audio/audio_send_stream.cc
@@ -168,13 +168,14 @@
RTC_DCHECK(rtp_rtcp_module_);
+ RTC_DCHECK_RUN_ON(&worker_thread_checker_);
ConfigureStream(config, true);
-
+ UpdateCachedTargetAudioBitrateConstraints();
pacer_thread_checker_.Detach();
}
AudioSendStream::~AudioSendStream() {
- RTC_DCHECK(worker_thread_checker_.IsCurrent());
+ RTC_DCHECK_RUN_ON(&worker_thread_checker_);
RTC_LOG(LS_INFO) << "~AudioSendStream: " << config_.rtp.ssrc;
RTC_DCHECK(!sending_);
channel_send_->ResetSenderCongestionControlObjects();
@@ -186,13 +187,13 @@
}
const webrtc::AudioSendStream::Config& AudioSendStream::GetConfig() const {
- RTC_DCHECK(worker_thread_checker_.IsCurrent());
+ RTC_DCHECK_RUN_ON(&worker_thread_checker_);
return config_;
}
void AudioSendStream::Reconfigure(
const webrtc::AudioSendStream::Config& new_config) {
- RTC_DCHECK(worker_thread_checker_.IsCurrent());
+ RTC_DCHECK_RUN_ON(&worker_thread_checker_);
ConfigureStream(new_config, false);
}
@@ -351,20 +352,22 @@
}
channel_send_->CallEncoder([this](AudioEncoder* encoder) {
+ RTC_DCHECK_RUN_ON(&worker_thread_checker_);
if (!encoder) {
return;
}
- worker_queue_->PostTask(
- [this, length_range = encoder->GetFrameLengthRange()] {
- RTC_DCHECK_RUN_ON(worker_queue_);
- frame_length_range_ = length_range;
- });
+ frame_length_range_ = encoder->GetFrameLengthRange();
+ UpdateCachedTargetAudioBitrateConstraints();
});
if (sending_) {
ReconfigureBitrateObserver(new_config);
}
+
config_ = new_config;
+ if (!first_time) {
+ UpdateCachedTargetAudioBitrateConstraints();
+ }
}
void AudioSendStream::Start() {
@@ -379,13 +382,7 @@
if (send_side_bwe_with_overhead_)
rtp_transport_->IncludeOverheadInPacedSender();
rtp_rtcp_module_->SetAsPartOfAllocation(true);
- rtc::Event thread_sync_event;
- worker_queue_->PostTask([&] {
- RTC_DCHECK_RUN_ON(worker_queue_);
- ConfigureBitrateObserver();
- thread_sync_event.Set();
- });
- thread_sync_event.Wait(rtc::Event::kForever);
+ ConfigureBitrateObserver();
} else {
rtp_rtcp_module_->SetAsPartOfAllocation(false);
}
@@ -396,7 +393,7 @@
}
void AudioSendStream::Stop() {
- RTC_DCHECK(worker_thread_checker_.IsCurrent());
+ RTC_DCHECK_RUN_ON(&worker_thread_checker_);
if (!sending_) {
return;
}
@@ -431,14 +428,14 @@
int payload_frequency,
int event,
int duration_ms) {
- RTC_DCHECK(worker_thread_checker_.IsCurrent());
+ RTC_DCHECK_RUN_ON(&worker_thread_checker_);
channel_send_->SetSendTelephoneEventPayloadType(payload_type,
payload_frequency);
return channel_send_->SendTelephoneEventOutband(event, duration_ms);
}
void AudioSendStream::SetMuted(bool muted) {
- RTC_DCHECK(worker_thread_checker_.IsCurrent());
+ RTC_DCHECK_RUN_ON(&worker_thread_checker_);
channel_send_->SetInputMute(muted);
}
@@ -448,7 +445,7 @@
webrtc::AudioSendStream::Stats AudioSendStream::GetStats(
bool has_remote_tracks) const {
- RTC_DCHECK(worker_thread_checker_.IsCurrent());
+ RTC_DCHECK_RUN_ON(&worker_thread_checker_);
webrtc::AudioSendStream::Stats stats;
stats.local_ssrc = config_.rtp.ssrc;
stats.target_bitrate_bps = channel_send_->GetBitrate();
@@ -509,12 +506,14 @@
void AudioSendStream::DeliverRtcp(const uint8_t* packet, size_t length) {
RTC_DCHECK_RUN_ON(&worker_thread_checker_);
channel_send_->ReceivedRTCPPacket(packet, length);
- worker_queue_->PostTask([&]() {
+
+ {
// Poll if overhead has changed, which it can do if ack triggers us to stop
// sending mid/rid.
MutexLock lock(&overhead_per_packet_lock_);
UpdateOverheadForEncoder();
- });
+ }
+ UpdateCachedTargetAudioBitrateConstraints();
}
uint32_t AudioSendStream::OnBitrateUpdated(BitrateAllocationUpdate update) {
@@ -523,9 +522,11 @@
// Pick a target bitrate between the constraints. Overrules the allocator if
// it 1) allocated a bitrate of zero to disable the stream or 2) allocated a
// higher than max to allow for e.g. extra FEC.
- auto constraints = GetMinMaxBitrateConstraints();
- update.target_bitrate.Clamp(constraints.min, constraints.max);
- update.stable_target_bitrate.Clamp(constraints.min, constraints.max);
+ RTC_DCHECK(cached_constraints_.has_value());
+ update.target_bitrate.Clamp(cached_constraints_->min,
+ cached_constraints_->max);
+ update.stable_target_bitrate.Clamp(cached_constraints_->min,
+ cached_constraints_->max);
channel_send_->OnBitrateAllocation(update);
@@ -536,13 +537,17 @@
void AudioSendStream::SetTransportOverhead(
int transport_overhead_per_packet_bytes) {
- RTC_DCHECK(worker_thread_checker_.IsCurrent());
- MutexLock lock(&overhead_per_packet_lock_);
- transport_overhead_per_packet_bytes_ = transport_overhead_per_packet_bytes;
- UpdateOverheadForEncoder();
+ RTC_DCHECK_RUN_ON(&worker_thread_checker_);
+ {
+ MutexLock lock(&overhead_per_packet_lock_);
+ transport_overhead_per_packet_bytes_ = transport_overhead_per_packet_bytes;
+ UpdateOverheadForEncoder();
+ }
+ UpdateCachedTargetAudioBitrateConstraints();
}
void AudioSendStream::UpdateOverheadForEncoder() {
+ RTC_DCHECK_RUN_ON(&worker_thread_checker_);
size_t overhead_per_packet_bytes = GetPerPacketOverheadBytes();
if (overhead_per_packet_ == overhead_per_packet_bytes) {
return;
@@ -552,19 +557,11 @@
channel_send_->CallEncoder([&](AudioEncoder* encoder) {
encoder->OnReceivedOverhead(overhead_per_packet_bytes);
});
- auto update_task = [this, overhead_per_packet_bytes] {
- RTC_DCHECK_RUN_ON(worker_queue_);
- if (total_packet_overhead_bytes_ != overhead_per_packet_bytes) {
- total_packet_overhead_bytes_ = overhead_per_packet_bytes;
- if (registered_with_allocator_) {
- ConfigureBitrateObserver();
- }
+ if (total_packet_overhead_bytes_ != overhead_per_packet_bytes) {
+ total_packet_overhead_bytes_ = overhead_per_packet_bytes;
+ if (registered_with_allocator_) {
+ ConfigureBitrateObserver();
}
- };
- if (worker_queue_->IsCurrent()) {
- update_task();
- } else {
- worker_queue_->PostTask(update_task);
}
}
@@ -602,7 +599,6 @@
void AudioSendStream::StoreEncoderProperties(int sample_rate_hz,
size_t num_channels) {
- RTC_DCHECK(worker_thread_checker_.IsCurrent());
encoder_sample_rate_hz_ = sample_rate_hz;
encoder_num_channels_ = num_channels;
if (sending_) {
@@ -800,7 +796,6 @@
void AudioSendStream::ReconfigureBitrateObserver(
const webrtc::AudioSendStream::Config& new_config) {
- RTC_DCHECK_RUN_ON(&worker_thread_checker_);
// Since the Config's default is for both of these to be -1, this test will
// allow us to configure the bitrate observer if the new config has bitrate
// limits set, but would only have us call RemoveBitrateObserver if we were
@@ -819,20 +814,13 @@
rtp_transport_->AccountForAudioPacketsInPacedSender(true);
if (send_side_bwe_with_overhead_)
rtp_transport_->IncludeOverheadInPacedSender();
- rtc::Event thread_sync_event;
- worker_queue_->PostTask([&] {
- RTC_DCHECK_RUN_ON(worker_queue_);
- // We may get a callback immediately as the observer is registered, so
- // make
- // sure the bitrate limits in config_ are up-to-date.
- config_.min_bitrate_bps = new_config.min_bitrate_bps;
- config_.max_bitrate_bps = new_config.max_bitrate_bps;
+ // We may get a callback immediately as the observer is registered, so
+ // make sure the bitrate limits in config_ are up-to-date.
+ config_.min_bitrate_bps = new_config.min_bitrate_bps;
+ config_.max_bitrate_bps = new_config.max_bitrate_bps;
- config_.bitrate_priority = new_config.bitrate_priority;
- ConfigureBitrateObserver();
- thread_sync_event.Set();
- });
- thread_sync_event.Wait(rtc::Event::kForever);
+ config_.bitrate_priority = new_config.bitrate_priority;
+ ConfigureBitrateObserver();
rtp_rtcp_module_->SetAsPartOfAllocation(true);
} else {
rtp_transport_->AccountForAudioPacketsInPacedSender(false);
@@ -845,6 +833,7 @@
// This either updates the current observer or adds a new observer.
// TODO(srte): Add overhead compensation here.
auto constraints = GetMinMaxBitrateConstraints();
+ RTC_DCHECK(constraints.has_value());
DataRate priority_bitrate = allocation_settings_.priority_bitrate;
if (send_side_bwe_with_overhead_) {
@@ -866,30 +855,40 @@
if (allocation_settings_.priority_bitrate_raw)
priority_bitrate = *allocation_settings_.priority_bitrate_raw;
- bitrate_allocator_->AddObserver(
- this,
- MediaStreamAllocationConfig{
- constraints.min.bps<uint32_t>(), constraints.max.bps<uint32_t>(), 0,
- priority_bitrate.bps(), true,
- allocation_settings_.bitrate_priority.value_or(
- config_.bitrate_priority)});
+ worker_queue_->PostTask([this, constraints, priority_bitrate,
+ config_bitrate_priority = config_.bitrate_priority] {
+ RTC_DCHECK_RUN_ON(worker_queue_);
+ bitrate_allocator_->AddObserver(
+ this,
+ MediaStreamAllocationConfig{
+ constraints->min.bps<uint32_t>(), constraints->max.bps<uint32_t>(),
+ 0, priority_bitrate.bps(), true,
+ allocation_settings_.bitrate_priority.value_or(
+ config_bitrate_priority)});
+ });
registered_with_allocator_ = true;
}
void AudioSendStream::RemoveBitrateObserver() {
- RTC_DCHECK(worker_thread_checker_.IsCurrent());
+ registered_with_allocator_ = false;
rtc::Event thread_sync_event;
worker_queue_->PostTask([this, &thread_sync_event] {
RTC_DCHECK_RUN_ON(worker_queue_);
- registered_with_allocator_ = false;
bitrate_allocator_->RemoveObserver(this);
thread_sync_event.Set();
});
thread_sync_event.Wait(rtc::Event::kForever);
}
-AudioSendStream::TargetAudioBitrateConstraints
+absl::optional<AudioSendStream::TargetAudioBitrateConstraints>
AudioSendStream::GetMinMaxBitrateConstraints() const {
+ if (config_.min_bitrate_bps < 0 || config_.max_bitrate_bps < 0) {
+ RTC_LOG(LS_WARNING) << "Config is invalid: min_bitrate_bps="
+ << config_.min_bitrate_bps
+ << "; max_bitrate_bps=" << config_.max_bitrate_bps
+ << "; both expected greater or equal to 0";
+ return absl::nullopt;
+ }
TargetAudioBitrateConstraints constraints{
DataRate::BitsPerSec(config_.min_bitrate_bps),
DataRate::BitsPerSec(config_.max_bitrate_bps)};
@@ -902,7 +901,11 @@
RTC_DCHECK_GE(constraints.min, DataRate::Zero());
RTC_DCHECK_GE(constraints.max, DataRate::Zero());
- RTC_DCHECK_GE(constraints.max, constraints.min);
+ if (constraints.max < constraints.min) {
+ RTC_LOG(LS_WARNING) << "TargetAudioBitrateConstraints::max is less than "
+ << "TargetAudioBitrateConstraints::min";
+ return absl::nullopt;
+ }
if (send_side_bwe_with_overhead_) {
if (use_legacy_overhead_calculation_) {
// OverheadPerPacket = Ipv4(20B) + UDP(8B) + SRTP(10B) + RTP(12)
@@ -913,7 +916,10 @@
constraints.min += kMinOverhead;
constraints.max += kMinOverhead;
} else {
- RTC_DCHECK(frame_length_range_);
+ if (!frame_length_range_.has_value()) {
+ RTC_LOG(LS_WARNING) << "frame_length_range_ is not set";
+ return absl::nullopt;
+ }
const DataSize kOverheadPerPacket =
DataSize::Bytes(total_packet_overhead_bytes_);
constraints.min += kOverheadPerPacket / frame_length_range_->second;
@@ -927,5 +933,18 @@
int clockrate_hz) {
channel_send_->RegisterCngPayloadType(payload_type, clockrate_hz);
}
+
+void AudioSendStream::UpdateCachedTargetAudioBitrateConstraints() {
+ absl::optional<AudioSendStream::TargetAudioBitrateConstraints>
+ new_constraints = GetMinMaxBitrateConstraints();
+ if (!new_constraints.has_value()) {
+ return;
+ }
+ worker_queue_->PostTask([this, new_constraints]() {
+ RTC_DCHECK_RUN_ON(worker_queue_);
+ cached_constraints_ = new_constraints;
+ });
+}
+
} // namespace internal
} // namespace webrtc
diff --git a/audio/audio_send_stream.h b/audio/audio_send_stream.h
index 1e6982e..25346ae 100644
--- a/audio/audio_send_stream.h
+++ b/audio/audio_send_stream.h
@@ -15,6 +15,7 @@
#include <utility>
#include <vector>
+#include "api/sequence_checker.h"
#include "audio/audio_level.h"
#include "audio/channel_send.h"
#include "call/audio_send_stream.h"
@@ -25,7 +26,6 @@
#include "rtc_base/race_checker.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/task_queue.h"
-#include "rtc_base/thread_checker.h"
namespace webrtc {
class RtcEventLog;
@@ -121,22 +121,29 @@
internal::AudioState* audio_state();
const internal::AudioState* audio_state() const;
- void StoreEncoderProperties(int sample_rate_hz, size_t num_channels);
+ void StoreEncoderProperties(int sample_rate_hz, size_t num_channels)
+ RTC_RUN_ON(worker_thread_checker_);
- void ConfigureStream(const Config& new_config, bool first_time);
- bool SetupSendCodec(const Config& new_config);
- bool ReconfigureSendCodec(const Config& new_config);
- void ReconfigureANA(const Config& new_config);
- void ReconfigureCNG(const Config& new_config);
- void ReconfigureBitrateObserver(const Config& new_config);
+ void ConfigureStream(const Config& new_config, bool first_time)
+ RTC_RUN_ON(worker_thread_checker_);
+ bool SetupSendCodec(const Config& new_config)
+ RTC_RUN_ON(worker_thread_checker_);
+ bool ReconfigureSendCodec(const Config& new_config)
+ RTC_RUN_ON(worker_thread_checker_);
+ void ReconfigureANA(const Config& new_config)
+ RTC_RUN_ON(worker_thread_checker_);
+ void ReconfigureCNG(const Config& new_config)
+ RTC_RUN_ON(worker_thread_checker_);
+ void ReconfigureBitrateObserver(const Config& new_config)
+ RTC_RUN_ON(worker_thread_checker_);
- void ConfigureBitrateObserver() RTC_RUN_ON(worker_queue_);
- void RemoveBitrateObserver();
+ void ConfigureBitrateObserver() RTC_RUN_ON(worker_thread_checker_);
+ void RemoveBitrateObserver() RTC_RUN_ON(worker_thread_checker_);
// Returns bitrate constraints, maybe including overhead when enabled by
// field trial.
- TargetAudioBitrateConstraints GetMinMaxBitrateConstraints() const
- RTC_RUN_ON(worker_queue_);
+ absl::optional<TargetAudioBitrateConstraints> GetMinMaxBitrateConstraints()
+ const RTC_RUN_ON(worker_thread_checker_);
// Sets per-packet overhead on encoded (for ANA) based on current known values
// of transport and packetization overheads.
@@ -147,11 +154,16 @@
size_t GetPerPacketOverheadBytes() const
RTC_EXCLUSIVE_LOCKS_REQUIRED(overhead_per_packet_lock_);
- void RegisterCngPayloadType(int payload_type, int clockrate_hz);
+ void RegisterCngPayloadType(int payload_type, int clockrate_hz)
+ RTC_RUN_ON(worker_thread_checker_);
+
+ void UpdateCachedTargetAudioBitrateConstraints()
+ RTC_RUN_ON(worker_thread_checker_);
+
Clock* clock_;
- rtc::ThreadChecker worker_thread_checker_;
- rtc::ThreadChecker pacer_thread_checker_;
+ SequenceChecker worker_thread_checker_;
+ SequenceChecker pacer_thread_checker_;
rtc::RaceChecker audio_capture_race_checker_;
rtc::TaskQueue* worker_queue_;
@@ -161,15 +173,16 @@
const bool send_side_bwe_with_overhead_;
const AudioAllocationConfig allocation_settings_;
- webrtc::AudioSendStream::Config config_;
+ webrtc::AudioSendStream::Config config_
+ RTC_GUARDED_BY(worker_thread_checker_);
rtc::scoped_refptr<webrtc::AudioState> audio_state_;
const std::unique_ptr<voe::ChannelSendInterface> channel_send_;
RtcEventLog* const event_log_;
const bool use_legacy_overhead_calculation_;
- int encoder_sample_rate_hz_ = 0;
- size_t encoder_num_channels_ = 0;
- bool sending_ = false;
+ int encoder_sample_rate_hz_ RTC_GUARDED_BY(worker_thread_checker_) = 0;
+ size_t encoder_num_channels_ RTC_GUARDED_BY(worker_thread_checker_) = 0;
+ bool sending_ RTC_GUARDED_BY(worker_thread_checker_) = false;
mutable Mutex audio_level_lock_;
// Keeps track of audio level, total audio energy and total samples duration.
// https://w3c.github.io/webrtc-stats/#dom-rtcaudiohandlerstats-totalaudioenergy
@@ -177,6 +190,9 @@
BitrateAllocatorInterface* const bitrate_allocator_
RTC_GUARDED_BY(worker_queue_);
+ // Constrains cached to be accessed from |worker_queue_|.
+ absl::optional<AudioSendStream::TargetAudioBitrateConstraints>
+ cached_constraints_ RTC_GUARDED_BY(worker_queue_) = absl::nullopt;
RtpTransportControllerSendInterface* const rtp_transport_;
RtpRtcpInterface* const rtp_rtcp_module_;
@@ -205,10 +221,12 @@
size_t transport_overhead_per_packet_bytes_
RTC_GUARDED_BY(overhead_per_packet_lock_) = 0;
- bool registered_with_allocator_ RTC_GUARDED_BY(worker_queue_) = false;
- size_t total_packet_overhead_bytes_ RTC_GUARDED_BY(worker_queue_) = 0;
+ bool registered_with_allocator_ RTC_GUARDED_BY(worker_thread_checker_) =
+ false;
+ size_t total_packet_overhead_bytes_ RTC_GUARDED_BY(worker_thread_checker_) =
+ 0;
absl::optional<std::pair<TimeDelta, TimeDelta>> frame_length_range_
- RTC_GUARDED_BY(worker_queue_);
+ RTC_GUARDED_BY(worker_thread_checker_);
};
} // namespace internal
} // namespace webrtc
diff --git a/audio/audio_state.h b/audio/audio_state.h
index 5e76642..89c748d 100644
--- a/audio/audio_state.h
+++ b/audio/audio_state.h
@@ -15,11 +15,11 @@
#include <memory>
#include <unordered_set>
+#include "api/sequence_checker.h"
#include "audio/audio_transport_impl.h"
#include "audio/null_audio_poller.h"
#include "call/audio_state.h"
#include "rtc_base/ref_count.h"
-#include "rtc_base/thread_checker.h"
namespace webrtc {
@@ -65,8 +65,8 @@
void UpdateAudioTransportWithSendingStreams();
void UpdateNullAudioPollerState();
- rtc::ThreadChecker thread_checker_;
- rtc::ThreadChecker process_thread_checker_;
+ SequenceChecker thread_checker_;
+ SequenceChecker process_thread_checker_;
const webrtc::AudioState::Config config_;
bool recording_enabled_ = true;
bool playout_enabled_ = true;
diff --git a/audio/channel_receive.cc b/audio/channel_receive.cc
index 2788dac..8b3c924 100644
--- a/audio/channel_receive.cc
+++ b/audio/channel_receive.cc
@@ -22,6 +22,7 @@
#include "api/crypto/frame_decryptor_interface.h"
#include "api/frame_transformer_interface.h"
#include "api/rtc_event_log/rtc_event_log.h"
+#include "api/sequence_checker.h"
#include "audio/audio_level.h"
#include "audio/channel_receive_frame_transformer_delegate.h"
#include "audio/channel_send.h"
@@ -46,7 +47,6 @@
#include "rtc_base/numerics/safe_minmax.h"
#include "rtc_base/race_checker.h"
#include "rtc_base/synchronization/mutex.h"
-#include "rtc_base/thread_checker.h"
#include "rtc_base/time_utils.h"
#include "system_wrappers/include/metrics.h"
@@ -162,6 +162,8 @@
int PreferredSampleRate() const override;
+ void SetSourceTracker(SourceTracker* source_tracker) override;
+
// Associate to a send channel.
// Used for obtaining RTT for a receive-only channel.
void SetAssociatedSendChannel(const ChannelSendInterface* channel) override;
@@ -197,8 +199,8 @@
// we know about. The goal is to eventually split up voe::ChannelReceive into
// parts with single-threaded semantics, and thereby reduce the need for
// locks.
- rtc::ThreadChecker worker_thread_checker_;
- rtc::ThreadChecker module_process_thread_checker_;
+ SequenceChecker worker_thread_checker_;
+ SequenceChecker module_process_thread_checker_;
// Methods accessed from audio and video threads are checked for sequential-
// only access. We don't necessarily own and control these threads, so thread
// checkers cannot be used. E.g. Chromium may transfer "ownership" from one
@@ -219,6 +221,7 @@
std::unique_ptr<ReceiveStatistics> rtp_receive_statistics_;
std::unique_ptr<ModuleRtpRtcpImpl2> rtp_rtcp_;
const uint32_t remote_ssrc_;
+ SourceTracker* source_tracker_ = nullptr;
// Info for GetSyncInfo is updated on network or worker thread, and queried on
// the worker thread.
@@ -233,6 +236,7 @@
AudioSinkInterface* audio_sink_ = nullptr;
AudioLevel _outputAudioLevel;
+ Clock* const clock_;
RemoteNtpTimeEstimator ntp_estimator_ RTC_GUARDED_BY(ts_stats_lock_);
// Timestamp of the audio pulled from NetEq.
@@ -269,7 +273,7 @@
PacketRouter* packet_router_ = nullptr;
- rtc::ThreadChecker construction_thread_;
+ SequenceChecker construction_thread_;
// E2EE Audio Frame Decryption
rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor_;
@@ -287,6 +291,21 @@
if (!Playing()) {
// Avoid inserting into NetEQ when we are not playing. Count the
// packet as discarded.
+
+ // If we have a source_tracker_, tell it that the frame has been
+ // "delivered". Normally, this happens in AudioReceiveStream when audio
+ // frames are pulled out, but when playout is muted, nothing is pulling
+ // frames. The downside of this approach is that frames delivered this way
+ // won't be delayed for playout, and therefore will be unsynchronized with
+ // (a) audio delay when playing and (b) any audio/video synchronization. But
+ // the alternative is that muting playout also stops the SourceTracker from
+ // updating RtpSource information.
+ if (source_tracker_) {
+ RtpPacketInfos::vector_type packet_vector = {
+ RtpPacketInfo(rtpHeader, clock_->TimeInMilliseconds())};
+ source_tracker_->OnFrameDelivered(RtpPacketInfos(packet_vector));
+ }
+
return;
}
@@ -442,6 +461,10 @@
acm_receiver_.last_output_sample_rate_hz());
}
+void ChannelReceive::SetSourceTracker(SourceTracker* source_tracker) {
+ source_tracker_ = source_tracker;
+}
+
ChannelReceive::ChannelReceive(
Clock* clock,
ProcessThread* module_process_thread,
@@ -469,6 +492,7 @@
jitter_buffer_max_packets,
jitter_buffer_fast_playout)),
_outputAudioLevel(),
+ clock_(clock),
ntp_estimator_(clock),
playout_timestamp_rtp_(0),
playout_delay_ms_(0),
@@ -671,8 +695,10 @@
uint32_t ntp_secs = 0;
uint32_t ntp_frac = 0;
uint32_t rtp_timestamp = 0;
- if (0 !=
- rtp_rtcp_->RemoteNTP(&ntp_secs, &ntp_frac, NULL, NULL, &rtp_timestamp)) {
+ if (rtp_rtcp_->RemoteNTP(&ntp_secs, &ntp_frac,
+ /*rtcp_arrival_time_secs=*/nullptr,
+ /*rtcp_arrival_time_frac=*/nullptr,
+ &rtp_timestamp) != 0) {
// Waiting for RTCP.
return;
}
@@ -680,6 +706,12 @@
{
MutexLock lock(&ts_stats_lock_);
ntp_estimator_.UpdateRtcpTimestamp(rtt, ntp_secs, ntp_frac, rtp_timestamp);
+ absl::optional<int64_t> remote_to_local_clock_offset_ms =
+ ntp_estimator_.EstimateRemoteToLocalClockOffsetMs();
+ if (remote_to_local_clock_offset_ms.has_value()) {
+ absolute_capture_time_receiver_.SetRemoteToLocalClockOffset(
+ Int64MsToQ32x32(*remote_to_local_clock_offset_ms));
+ }
}
}
@@ -723,11 +755,10 @@
CallReceiveStatistics ChannelReceive::GetRTCPStatistics() const {
RTC_DCHECK(worker_thread_checker_.IsCurrent());
- // --- RtcpStatistics
CallReceiveStatistics stats;
- // The jitter statistics is updated for each received RTP packet and is
- // based on received packets.
+ // The jitter statistics is updated for each received RTP packet and is based
+ // on received packets.
RtpReceiveStats rtp_stats;
StreamStatistician* statistician =
rtp_receive_statistics_->GetStatistician(remote_ssrc_);
@@ -738,10 +769,9 @@
stats.cumulativeLost = rtp_stats.packets_lost;
stats.jitterSamples = rtp_stats.jitter;
- // --- RTT
stats.rttMs = GetRTT();
- // --- Data counters
+ // Data counters.
if (statistician) {
stats.payload_bytes_rcvd = rtp_stats.packet_counter.payload_bytes;
@@ -758,11 +788,24 @@
stats.last_packet_received_timestamp_ms = absl::nullopt;
}
- // --- Timestamps
+ // Timestamps.
{
MutexLock lock(&ts_stats_lock_);
stats.capture_start_ntp_time_ms_ = capture_start_ntp_time_ms_;
}
+
+ absl::optional<RtpRtcpInterface::SenderReportStats> rtcp_sr_stats =
+ rtp_rtcp_->GetSenderReportStats();
+ if (rtcp_sr_stats.has_value()) {
+ stats.last_sender_report_timestamp_ms =
+ rtcp_sr_stats->last_arrival_timestamp.ToMs();
+ stats.last_sender_report_remote_timestamp_ms =
+ rtcp_sr_stats->last_remote_timestamp.ToMs();
+ stats.sender_reports_packets_sent = rtcp_sr_stats->packets_sent;
+ stats.sender_reports_bytes_sent = rtcp_sr_stats->bytes_sent;
+ stats.sender_reports_reports_count = rtcp_sr_stats->reports_count;
+ }
+
return stats;
}
@@ -787,6 +830,7 @@
void ChannelReceive::SetAssociatedSendChannel(
const ChannelSendInterface* channel) {
+ // TODO(bugs.webrtc.org/11993): Expect to be called on the network thread.
RTC_DCHECK(worker_thread_checker_.IsCurrent());
MutexLock lock(&assoc_send_channel_lock_);
associated_send_channel_ = channel;
@@ -882,7 +926,9 @@
RTC_DCHECK(module_process_thread_checker_.IsCurrent());
Syncable::Info info;
if (rtp_rtcp_->RemoteNTP(&info.capture_time_ntp_secs,
- &info.capture_time_ntp_frac, nullptr, nullptr,
+ &info.capture_time_ntp_frac,
+ /*rtcp_arrival_time_secs=*/nullptr,
+ /*rtcp_arrival_time_frac=*/nullptr,
&info.capture_time_source_clock) != 0) {
return absl::nullopt;
}
@@ -945,11 +991,9 @@
}
int64_t ChannelReceive::GetRTT() const {
- std::vector<RTCPReportBlock> report_blocks;
- rtp_rtcp_->RemoteRTCPStat(&report_blocks);
+ std::vector<ReportBlockData> report_blocks =
+ rtp_rtcp_->GetLatestReportBlockData();
- // TODO(nisse): Could we check the return value from the ->RTT() call below,
- // instead of checking if we have any report blocks?
if (report_blocks.empty()) {
MutexLock lock(&assoc_send_channel_lock_);
// Tries to get RTT from an associated channel.
@@ -959,16 +1003,14 @@
return associated_send_channel_->GetRTT();
}
- int64_t rtt = 0;
- int64_t avg_rtt = 0;
- int64_t max_rtt = 0;
- int64_t min_rtt = 0;
// TODO(nisse): This method computes RTT based on sender reports, even though
// a receive stream is not supposed to do that.
- if (rtp_rtcp_->RTT(remote_ssrc_, &rtt, &avg_rtt, &min_rtt, &max_rtt) != 0) {
- return 0;
+ for (const ReportBlockData& data : report_blocks) {
+ if (data.report_block().sender_ssrc == remote_ssrc_) {
+ return data.last_rtt_ms();
+ }
}
- return rtt;
+ return 0;
}
} // namespace
diff --git a/audio/channel_receive.h b/audio/channel_receive.h
index eef2db4..261357d 100644
--- a/audio/channel_receive.h
+++ b/audio/channel_receive.h
@@ -28,6 +28,7 @@
#include "call/rtp_packet_sink_interface.h"
#include "call/syncable.h"
#include "modules/audio_coding/include/audio_coding_module_typedefs.h"
+#include "modules/rtp_rtcp/source/source_tracker.h"
#include "system_wrappers/include/clock.h"
// TODO(solenberg, nisse): This file contains a few NOLINT marks, to silence
@@ -64,6 +65,13 @@
// local clock when it was received - not the RTP timestamp of that packet.
// https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-lastpacketreceivedtimestamp
absl::optional<int64_t> last_packet_received_timestamp_ms;
+ // Remote outbound stats derived by the received RTCP sender reports.
+ // https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*
+ absl::optional<int64_t> last_sender_report_timestamp_ms;
+ absl::optional<int64_t> last_sender_report_remote_timestamp_ms;
+ uint32_t sender_reports_packets_sent = 0;
+ uint64_t sender_reports_bytes_sent = 0;
+ uint64_t sender_reports_reports_count = 0;
};
namespace voe {
@@ -135,6 +143,10 @@
virtual int PreferredSampleRate() const = 0;
+ // Sets the source tracker to notify about "delivered" packets when output is
+ // muted.
+ virtual void SetSourceTracker(SourceTracker* source_tracker) = 0;
+
// Associate to a send channel.
// Used for obtaining RTT for a receive-only channel.
virtual void SetAssociatedSendChannel(
diff --git a/audio/channel_receive_frame_transformer_delegate.h b/audio/channel_receive_frame_transformer_delegate.h
index 3227c55..0af748e 100644
--- a/audio/channel_receive_frame_transformer_delegate.h
+++ b/audio/channel_receive_frame_transformer_delegate.h
@@ -14,7 +14,7 @@
#include <memory>
#include "api/frame_transformer_interface.h"
-#include "rtc_base/synchronization/sequence_checker.h"
+#include "api/sequence_checker.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/task_queue.h"
#include "rtc_base/thread.h"
diff --git a/audio/channel_send.cc b/audio/channel_send.cc
index d331f01..0434e48 100644
--- a/audio/channel_send.cc
+++ b/audio/channel_send.cc
@@ -21,6 +21,7 @@
#include "api/call/transport.h"
#include "api/crypto/frame_encryptor_interface.h"
#include "api/rtc_event_log/rtc_event_log.h"
+#include "api/sequence_checker.h"
#include "audio/channel_send_frame_transformer_delegate.h"
#include "audio/utility/audio_frame_operations.h"
#include "call/rtp_transport_controller_send_interface.h"
@@ -41,7 +42,6 @@
#include "rtc_base/rate_limiter.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/task_queue.h"
-#include "rtc_base/thread_checker.h"
#include "rtc_base/time_utils.h"
#include "system_wrappers/include/clock.h"
#include "system_wrappers/include/field_trial.h"
@@ -179,8 +179,8 @@
// specific threads we know about. The goal is to eventually split up
// voe::Channel into parts with single-threaded semantics, and thereby reduce
// the need for locks.
- rtc::ThreadChecker worker_thread_checker_;
- rtc::ThreadChecker module_process_thread_checker_;
+ SequenceChecker worker_thread_checker_;
+ SequenceChecker module_process_thread_checker_;
// Methods accessed from audio and video threads are checked for sequential-
// only access. We don't necessarily own and control these threads, so thread
// checkers cannot be used. E.g. Chromium may transfer "ownership" from one
@@ -218,8 +218,7 @@
const std::unique_ptr<RtpPacketSenderProxy> rtp_packet_pacer_proxy_;
const std::unique_ptr<RateLimiter> retransmission_rate_limiter_;
- rtc::ThreadChecker construction_thread_;
-
+ SequenceChecker construction_thread_;
bool encoder_queue_is_active_ RTC_GUARDED_BY(encoder_queue_) = false;
@@ -264,7 +263,7 @@
}
private:
- rtc::ThreadChecker thread_checker_;
+ SequenceChecker thread_checker_;
Mutex mutex_;
RtpPacketSender* rtp_packet_pacer_ RTC_GUARDED_BY(&mutex_);
};
@@ -750,25 +749,20 @@
// Get the report blocks from the latest received RTCP Sender or Receiver
// Report. Each element in the vector contains the sender's SSRC and a
// report block according to RFC 3550.
- std::vector<RTCPReportBlock> rtcp_report_blocks;
-
- int ret = rtp_rtcp_->RemoteRTCPStat(&rtcp_report_blocks);
- RTC_DCHECK_EQ(0, ret);
-
std::vector<ReportBlock> report_blocks;
-
- std::vector<RTCPReportBlock>::const_iterator it = rtcp_report_blocks.begin();
- for (; it != rtcp_report_blocks.end(); ++it) {
+ for (const ReportBlockData& data : rtp_rtcp_->GetLatestReportBlockData()) {
ReportBlock report_block;
- report_block.sender_SSRC = it->sender_ssrc;
- report_block.source_SSRC = it->source_ssrc;
- report_block.fraction_lost = it->fraction_lost;
- report_block.cumulative_num_packets_lost = it->packets_lost;
+ report_block.sender_SSRC = data.report_block().sender_ssrc;
+ report_block.source_SSRC = data.report_block().source_ssrc;
+ report_block.fraction_lost = data.report_block().fraction_lost;
+ report_block.cumulative_num_packets_lost = data.report_block().packets_lost;
report_block.extended_highest_sequence_number =
- it->extended_highest_sequence_number;
- report_block.interarrival_jitter = it->jitter;
- report_block.last_SR_timestamp = it->last_sender_report_timestamp;
- report_block.delay_since_last_SR = it->delay_since_last_sender_report;
+ data.report_block().extended_highest_sequence_number;
+ report_block.interarrival_jitter = data.report_block().jitter;
+ report_block.last_SR_timestamp =
+ data.report_block().last_sender_report_timestamp;
+ report_block.delay_since_last_SR =
+ data.report_block().delay_since_last_sender_report;
report_blocks.push_back(report_block);
}
return report_blocks;
@@ -869,24 +863,15 @@
}
int64_t ChannelSend::GetRTT() const {
- std::vector<RTCPReportBlock> report_blocks;
- rtp_rtcp_->RemoteRTCPStat(&report_blocks);
-
+ std::vector<ReportBlockData> report_blocks =
+ rtp_rtcp_->GetLatestReportBlockData();
if (report_blocks.empty()) {
return 0;
}
- int64_t rtt = 0;
- int64_t avg_rtt = 0;
- int64_t max_rtt = 0;
- int64_t min_rtt = 0;
// We don't know in advance the remote ssrc used by the other end's receiver
- // reports, so use the SSRC of the first report block for calculating the RTT.
- if (rtp_rtcp_->RTT(report_blocks[0].sender_ssrc, &rtt, &avg_rtt, &min_rtt,
- &max_rtt) != 0) {
- return 0;
- }
- return rtt;
+ // reports, so use the first report block for the RTT.
+ return report_blocks.front().last_rtt_ms();
}
void ChannelSend::SetFrameEncryptor(
diff --git a/audio/channel_send_frame_transformer_delegate.h b/audio/channel_send_frame_transformer_delegate.h
index 531d1bc..9b7eb33 100644
--- a/audio/channel_send_frame_transformer_delegate.h
+++ b/audio/channel_send_frame_transformer_delegate.h
@@ -14,10 +14,10 @@
#include <memory>
#include "api/frame_transformer_interface.h"
+#include "api/sequence_checker.h"
#include "modules/audio_coding/include/audio_coding_module_typedefs.h"
#include "rtc_base/buffer.h"
#include "rtc_base/synchronization/mutex.h"
-#include "rtc_base/synchronization/sequence_checker.h"
#include "rtc_base/task_queue.h"
namespace webrtc {
diff --git a/audio/mock_voe_channel_proxy.h b/audio/mock_voe_channel_proxy.h
index 52e5b2f..7f140d4 100644
--- a/audio/mock_voe_channel_proxy.h
+++ b/audio/mock_voe_channel_proxy.h
@@ -59,6 +59,7 @@
(int sample_rate_hz, AudioFrame*),
(override));
MOCK_METHOD(int, PreferredSampleRate, (), (const, override));
+ MOCK_METHOD(void, SetSourceTracker, (SourceTracker*), (override));
MOCK_METHOD(void,
SetAssociatedSendChannel,
(const voe::ChannelSendInterface*),
diff --git a/audio/null_audio_poller.h b/audio/null_audio_poller.h
index 97cd2c7..47e67a9 100644
--- a/audio/null_audio_poller.h
+++ b/audio/null_audio_poller.h
@@ -13,9 +13,9 @@
#include <stdint.h>
+#include "api/sequence_checker.h"
#include "modules/audio_device/include/audio_device_defines.h"
#include "rtc_base/message_handler.h"
-#include "rtc_base/thread_checker.h"
namespace webrtc {
namespace internal {
@@ -29,7 +29,7 @@
void OnMessage(rtc::Message* msg) override;
private:
- rtc::ThreadChecker thread_checker_;
+ SequenceChecker thread_checker_;
AudioTransport* const audio_transport_;
int64_t reschedule_at_;
};
diff --git a/audio/utility/BUILD.gn b/audio/utility/BUILD.gn
index 54ca046..933553d 100644
--- a/audio/utility/BUILD.gn
+++ b/audio/utility/BUILD.gn
@@ -26,10 +26,10 @@
"../../api/audio:audio_frame_api",
"../../common_audio",
"../../rtc_base:checks",
- "../../rtc_base:deprecation",
"../../rtc_base:rtc_base_approved",
"../../system_wrappers:field_trial",
]
+ absl_deps = [ "//third_party/abseil-cpp/absl/base:core_headers" ]
}
if (rtc_include_tests) {
diff --git a/audio/utility/audio_frame_operations.h b/audio/utility/audio_frame_operations.h
index 65c310c..2f1540b 100644
--- a/audio/utility/audio_frame_operations.h
+++ b/audio/utility/audio_frame_operations.h
@@ -14,8 +14,8 @@
#include <stddef.h>
#include <stdint.h>
+#include "absl/base/attributes.h"
#include "api/audio/audio_frame.h"
-#include "rtc_base/deprecation.h"
namespace webrtc {
@@ -36,12 +36,14 @@
// |frame.num_channels_| will be updated. This version checks for sufficient
// buffer size and that |num_channels_| is mono. Use UpmixChannels
// instead. TODO(bugs.webrtc.org/8649): remove.
- RTC_DEPRECATED static int MonoToStereo(AudioFrame* frame);
+ ABSL_DEPRECATED("bugs.webrtc.org/8649")
+ static int MonoToStereo(AudioFrame* frame);
// |frame.num_channels_| will be updated. This version checks that
// |num_channels_| is stereo. Use DownmixChannels
// instead. TODO(bugs.webrtc.org/8649): remove.
- RTC_DEPRECATED static int StereoToMono(AudioFrame* frame);
+ ABSL_DEPRECATED("bugs.webrtc.org/8649")
+ static int StereoToMono(AudioFrame* frame);
// Downmixes 4 channels |src_audio| to stereo |dst_audio|. This is an in-place
// operation, meaning |src_audio| and |dst_audio| may point to the same
diff --git a/audio/voip/BUILD.gn b/audio/voip/BUILD.gn
index ed0508f..5311d72 100644
--- a/audio/voip/BUILD.gn
+++ b/audio/voip/BUILD.gn
@@ -89,6 +89,7 @@
]
deps = [
"..:audio",
+ "../../api:sequence_checker",
"../../api/audio_codecs:audio_codecs_api",
"../../api/task_queue",
"../../call:audio_sender_interface",
@@ -97,7 +98,6 @@
"../../modules/rtp_rtcp:rtp_rtcp_format",
"../../rtc_base:logging",
"../../rtc_base:rtc_task_queue",
- "../../rtc_base:thread_checker",
"../../rtc_base:timeutils",
"../../rtc_base/synchronization:mutex",
"../../rtc_base/system:no_unique_address",
diff --git a/audio/voip/audio_egress.h b/audio/voip/audio_egress.h
index fcd9ed0..a39c7e2 100644
--- a/audio/voip/audio_egress.h
+++ b/audio/voip/audio_egress.h
@@ -15,6 +15,7 @@
#include <string>
#include "api/audio_codecs/audio_format.h"
+#include "api/sequence_checker.h"
#include "api/task_queue/task_queue_factory.h"
#include "audio/audio_level.h"
#include "audio/utility/audio_frame_operations.h"
@@ -25,7 +26,6 @@
#include "modules/rtp_rtcp/source/rtp_sender_audio.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/task_queue.h"
-#include "rtc_base/thread_checker.h"
#include "rtc_base/time_utils.h"
namespace webrtc {
diff --git a/common_audio/BUILD.gn b/common_audio/BUILD.gn
index a03e9ab..5b1e581 100644
--- a/common_audio/BUILD.gn
+++ b/common_audio/BUILD.gn
@@ -335,7 +335,7 @@
}
}
-if (rtc_include_tests) {
+if (rtc_include_tests && !build_with_chromium) {
rtc_test("common_audio_unittests") {
visibility += webrtc_default_visibility
testonly = true
diff --git a/common_audio/signal_processing/include/signal_processing_library.h b/common_audio/signal_processing/include/signal_processing_library.h
index 4ad92c4..0c13071 100644
--- a/common_audio/signal_processing/include/signal_processing_library.h
+++ b/common_audio/signal_processing/include/signal_processing_library.h
@@ -228,6 +228,25 @@
int32_t WebRtcSpl_MinValueW32_mips(const int32_t* vector, size_t length);
#endif
+// Returns both the minimum and maximum values of a 16-bit vector.
+//
+// Input:
+// - vector : 16-bit input vector.
+// - length : Number of samples in vector.
+// Ouput:
+// - max_val : Maximum sample value in |vector|.
+// - min_val : Minimum sample value in |vector|.
+void WebRtcSpl_MinMaxW16(const int16_t* vector,
+ size_t length,
+ int16_t* min_val,
+ int16_t* max_val);
+#if defined(WEBRTC_HAS_NEON)
+void WebRtcSpl_MinMaxW16Neon(const int16_t* vector,
+ size_t length,
+ int16_t* min_val,
+ int16_t* max_val);
+#endif
+
// Returns the vector index to the largest absolute value of a 16-bit vector.
//
// Input:
@@ -240,6 +259,17 @@
// -32768 presenting an int16 absolute value of 32767).
size_t WebRtcSpl_MaxAbsIndexW16(const int16_t* vector, size_t length);
+// Returns the element with the largest absolute value of a 16-bit vector. Note
+// that this function can return a negative value.
+//
+// Input:
+// - vector : 16-bit input vector.
+// - length : Number of samples in vector.
+//
+// Return value : The element with the largest absolute value. Note that this
+// may be a negative value.
+int16_t WebRtcSpl_MaxAbsElementW16(const int16_t* vector, size_t length);
+
// Returns the vector index to the maximum sample value of a 16-bit vector.
//
// Input:
diff --git a/common_audio/signal_processing/min_max_operations.c b/common_audio/signal_processing/min_max_operations.c
index d249a02..1b9542e 100644
--- a/common_audio/signal_processing/min_max_operations.c
+++ b/common_audio/signal_processing/min_max_operations.c
@@ -155,6 +155,15 @@
return index;
}
+int16_t WebRtcSpl_MaxAbsElementW16(const int16_t* vector, size_t length) {
+ int16_t min_val, max_val;
+ WebRtcSpl_MinMaxW16(vector, length, &min_val, &max_val);
+ if (min_val == max_val || min_val < -max_val) {
+ return min_val;
+ }
+ return max_val;
+}
+
// Index of maximum value in a word16 vector.
size_t WebRtcSpl_MaxIndexW16(const int16_t* vector, size_t length) {
size_t i = 0, index = 0;
@@ -222,3 +231,26 @@
return index;
}
+
+// Finds both the minimum and maximum elements in an array of 16-bit integers.
+void WebRtcSpl_MinMaxW16(const int16_t* vector, size_t length,
+ int16_t* min_val, int16_t* max_val) {
+#if defined(WEBRTC_HAS_NEON)
+ return WebRtcSpl_MinMaxW16Neon(vector, length, min_val, max_val);
+#else
+ int16_t minimum = WEBRTC_SPL_WORD16_MAX;
+ int16_t maximum = WEBRTC_SPL_WORD16_MIN;
+ size_t i = 0;
+
+ RTC_DCHECK_GT(length, 0);
+
+ for (i = 0; i < length; i++) {
+ if (vector[i] < minimum)
+ minimum = vector[i];
+ if (vector[i] > maximum)
+ maximum = vector[i];
+ }
+ *min_val = minimum;
+ *max_val = maximum;
+#endif
+}
diff --git a/common_audio/signal_processing/min_max_operations_neon.c b/common_audio/signal_processing/min_max_operations_neon.c
index 53217df..e5b4b7c 100644
--- a/common_audio/signal_processing/min_max_operations_neon.c
+++ b/common_audio/signal_processing/min_max_operations_neon.c
@@ -281,3 +281,53 @@
return minimum;
}
+// Finds both the minimum and maximum elements in an array of 16-bit integers.
+void WebRtcSpl_MinMaxW16Neon(const int16_t* vector, size_t length,
+ int16_t* min_val, int16_t* max_val) {
+ int16_t minimum = WEBRTC_SPL_WORD16_MAX;
+ int16_t maximum = WEBRTC_SPL_WORD16_MIN;
+ size_t i = 0;
+ size_t residual = length & 0x7;
+
+ RTC_DCHECK_GT(length, 0);
+
+ const int16_t* p_start = vector;
+ int16x8_t min16x8 = vdupq_n_s16(WEBRTC_SPL_WORD16_MAX);
+ int16x8_t max16x8 = vdupq_n_s16(WEBRTC_SPL_WORD16_MIN);
+
+ // First part, unroll the loop 8 times.
+ for (i = 0; i < length - residual; i += 8) {
+ int16x8_t in16x8 = vld1q_s16(p_start);
+ min16x8 = vminq_s16(min16x8, in16x8);
+ max16x8 = vmaxq_s16(max16x8, in16x8);
+ p_start += 8;
+ }
+
+#if defined(WEBRTC_ARCH_ARM64)
+ minimum = vminvq_s16(min16x8);
+ maximum = vmaxvq_s16(max16x8);
+#else
+ int16x4_t min16x4 = vmin_s16(vget_low_s16(min16x8), vget_high_s16(min16x8));
+ min16x4 = vpmin_s16(min16x4, min16x4);
+ min16x4 = vpmin_s16(min16x4, min16x4);
+
+ minimum = vget_lane_s16(min16x4, 0);
+
+ int16x4_t max16x4 = vmax_s16(vget_low_s16(max16x8), vget_high_s16(max16x8));
+ max16x4 = vpmax_s16(max16x4, max16x4);
+ max16x4 = vpmax_s16(max16x4, max16x4);
+
+ maximum = vget_lane_s16(max16x4, 0);
+#endif
+
+ // Second part, do the remaining iterations (if any).
+ for (i = residual; i > 0; i--) {
+ if (*p_start < minimum)
+ minimum = *p_start;
+ if (*p_start > maximum)
+ maximum = *p_start;
+ p_start++;
+ }
+ *min_val = minimum;
+ *max_val = maximum;
+}
diff --git a/common_audio/signal_processing/signal_processing_unittest.cc b/common_audio/signal_processing/signal_processing_unittest.cc
index 3106c47..9ec8590 100644
--- a/common_audio/signal_processing/signal_processing_unittest.cc
+++ b/common_audio/signal_processing/signal_processing_unittest.cc
@@ -289,6 +289,12 @@
WebRtcSpl_MinValueW32(vector32, kVectorSize));
EXPECT_EQ(kVectorSize - 1, WebRtcSpl_MinIndexW16(vector16, kVectorSize));
EXPECT_EQ(kVectorSize - 1, WebRtcSpl_MinIndexW32(vector32, kVectorSize));
+ EXPECT_EQ(WEBRTC_SPL_WORD16_MIN,
+ WebRtcSpl_MaxAbsElementW16(vector16, kVectorSize));
+ int16_t min_value, max_value;
+ WebRtcSpl_MinMaxW16(vector16, kVectorSize, &min_value, &max_value);
+ EXPECT_EQ(WEBRTC_SPL_WORD16_MIN, min_value);
+ EXPECT_EQ(12334, max_value);
// Test the cases where maximum values have to be caught
// outside of the unrolled loops in ARM-Neon.
@@ -306,6 +312,11 @@
EXPECT_EQ(kVectorSize - 1, WebRtcSpl_MaxAbsIndexW16(vector16, kVectorSize));
EXPECT_EQ(kVectorSize - 1, WebRtcSpl_MaxIndexW16(vector16, kVectorSize));
EXPECT_EQ(kVectorSize - 1, WebRtcSpl_MaxIndexW32(vector32, kVectorSize));
+ EXPECT_EQ(WEBRTC_SPL_WORD16_MAX,
+ WebRtcSpl_MaxAbsElementW16(vector16, kVectorSize));
+ WebRtcSpl_MinMaxW16(vector16, kVectorSize, &min_value, &max_value);
+ EXPECT_EQ(-29871, min_value);
+ EXPECT_EQ(WEBRTC_SPL_WORD16_MAX, max_value);
// Test the cases where multiple maximum and minimum values are present.
vector16[1] = WEBRTC_SPL_WORD16_MAX;
@@ -332,6 +343,43 @@
EXPECT_EQ(1u, WebRtcSpl_MaxIndexW32(vector32, kVectorSize));
EXPECT_EQ(6u, WebRtcSpl_MinIndexW16(vector16, kVectorSize));
EXPECT_EQ(6u, WebRtcSpl_MinIndexW32(vector32, kVectorSize));
+ EXPECT_EQ(WEBRTC_SPL_WORD16_MIN,
+ WebRtcSpl_MaxAbsElementW16(vector16, kVectorSize));
+ WebRtcSpl_MinMaxW16(vector16, kVectorSize, &min_value, &max_value);
+ EXPECT_EQ(WEBRTC_SPL_WORD16_MIN, min_value);
+ EXPECT_EQ(WEBRTC_SPL_WORD16_MAX, max_value);
+
+ // Test a one-element vector.
+ int16_t single_element_vector = 0;
+ EXPECT_EQ(0, WebRtcSpl_MaxAbsValueW16(&single_element_vector, 1));
+ EXPECT_EQ(0, WebRtcSpl_MaxValueW16(&single_element_vector, 1));
+ EXPECT_EQ(0, WebRtcSpl_MinValueW16(&single_element_vector, 1));
+ EXPECT_EQ(0u, WebRtcSpl_MaxAbsIndexW16(&single_element_vector, 1));
+ EXPECT_EQ(0u, WebRtcSpl_MaxIndexW16(&single_element_vector, 1));
+ EXPECT_EQ(0u, WebRtcSpl_MinIndexW16(&single_element_vector, 1));
+ EXPECT_EQ(0, WebRtcSpl_MaxAbsElementW16(&single_element_vector, 1));
+ WebRtcSpl_MinMaxW16(&single_element_vector, 1, &min_value, &max_value);
+ EXPECT_EQ(0, min_value);
+ EXPECT_EQ(0, max_value);
+
+ // Test a two-element vector with the values WEBRTC_SPL_WORD16_MIN and
+ // WEBRTC_SPL_WORD16_MAX.
+ int16_t two_element_vector[2] = {WEBRTC_SPL_WORD16_MIN,
+ WEBRTC_SPL_WORD16_MAX};
+ EXPECT_EQ(WEBRTC_SPL_WORD16_MAX,
+ WebRtcSpl_MaxAbsValueW16(two_element_vector, 2));
+ EXPECT_EQ(WEBRTC_SPL_WORD16_MAX,
+ WebRtcSpl_MaxValueW16(two_element_vector, 2));
+ EXPECT_EQ(WEBRTC_SPL_WORD16_MIN,
+ WebRtcSpl_MinValueW16(two_element_vector, 2));
+ EXPECT_EQ(0u, WebRtcSpl_MaxAbsIndexW16(two_element_vector, 2));
+ EXPECT_EQ(1u, WebRtcSpl_MaxIndexW16(two_element_vector, 2));
+ EXPECT_EQ(0u, WebRtcSpl_MinIndexW16(two_element_vector, 2));
+ EXPECT_EQ(WEBRTC_SPL_WORD16_MIN,
+ WebRtcSpl_MaxAbsElementW16(two_element_vector, 2));
+ WebRtcSpl_MinMaxW16(two_element_vector, 2, &min_value, &max_value);
+ EXPECT_EQ(WEBRTC_SPL_WORD16_MIN, min_value);
+ EXPECT_EQ(WEBRTC_SPL_WORD16_MAX, max_value);
}
TEST(SplTest, VectorOperationsTest) {
diff --git a/cras-config/aec_config.cc b/cras-config/aec_config.cc
index 5ce9581..fcf6ae4 100644
--- a/cras-config/aec_config.cc
+++ b/cras-config/aec_config.cc
@@ -24,12 +24,16 @@
if (ini == NULL)
return;
+ syslog(LOG_ERR, "Reading AEC3 ini");
+
+ /* Keys for EchoCanceller3Config:Buffering */
config->buffering.excess_render_detection_interval_blocks =
AEC_GET_INT(ini, BUFFERING,
EXCESS_RENDER_DETECTION_INTERVAL_BLOCKS);
config->buffering.max_allowed_excess_render_blocks =
AEC_GET_INT(ini, BUFFERING, MAX_ALLOWED_EXCESS_RENDER_BLOCKS);
+ /* Keys for EchoCanceller3Config:Delay */
config->delay.default_delay =
AEC_GET_INT(ini, DELAY, DEFAULT_DELAY);
config->delay.down_sampling_factor =
@@ -44,17 +48,19 @@
AEC_GET_INT(ini, DELAY, FIXED_CAPTURE_DELAY_SAMPLES);
config->delay.delay_estimate_smoothing =
AEC_GET_FLOAT(ini, DELAY, DELAY_ESTIMATE_SMOOTHING);
+
config->delay.delay_candidate_detection_threshold =
AEC_GET_FLOAT(ini, DELAY, DELAY_CANDIDATE_DETECTION_THRESHOLD);
config->delay.delay_selection_thresholds.initial =
AEC_GET_INT(ini, DELAY, DELAY_SELECTION_THRESHOLD_INITIAL);
config->delay.delay_selection_thresholds.converged =
AEC_GET_INT(ini, DELAY, DELAY_SELECTION_THRESHOLD_CONVERGED);
+
config->delay.use_external_delay_estimator =
AEC_GET_INT(ini, DELAY, USE_EXTERNAL_DELAY_ESTIMATOR);
-
config->delay.log_warning_on_delay_changes =
AEC_GET_INT(ini, DELAY, LOG_WARNING_ON_DELAY_CHANGES);
+
config->delay.render_alignment_mixing.downmix =
AEC_GET_INT(ini, DELAY, RENDER_ALIGNMENT_MIXING_DOWNMIX);
config->delay.render_alignment_mixing.adaptive_selection =
@@ -76,6 +82,7 @@
AEC_GET_INT(ini, DELAY,
CAPTURE_ALIGNMENT_MIXING_PREFER_FIRST_TWO_CHANNELS);
+ /* Keys for EchoCanceller3Config:Filter */
config->filter.refined.length_blocks =
AEC_GET_INT(ini, FILTER_REFINED, LENGTH_BLOCKS);
config->filter.refined.leakage_converged =
@@ -128,9 +135,12 @@
AEC_GET_INT(ini, FILTER, ENABLE_COARSE_FILTER_OUTPUT_USAGE);
config->filter.use_linear_filter =
AEC_GET_INT(ini, FILTER, USE_LINEAR_FILTER);
+ config->filter.high_pass_filter_echo_reference =
+ AEC_GET_INT(ini, FILTER, HIGH_PASS_FILTER_ECHO_REFERENCE);
config->filter.export_linear_aec_output =
AEC_GET_INT(ini, FILTER, EXPORT_LINEAR_AEC_OUTPUT);
+ /* Keys for EchoCanceller3Config:Erle */
config->erle.min =
AEC_GET_FLOAT(ini, ERLE, MIN);
config->erle.max_l =
@@ -146,6 +156,7 @@
config->erle.clamp_quality_estimate_to_one =
AEC_GET_INT(ini, ERLE, CLAMP_QUALITY_ESTIMATE_TO_ONE);
+ /* Keys for EchoCanceller3Config:EpStrength */
config->ep_strength.default_gain =
AEC_GET_FLOAT(ini, EP_STRENGTH, DEFAULT_GAIN);
config->ep_strength.default_len =
@@ -155,6 +166,7 @@
config->ep_strength.echo_can_saturate =
AEC_GET_INT(ini, EP_STRENGTH, ECHO_CAN_SATURATE);
+ /* Keys for EchoCanceller3Config:EchoAudibility */
config->echo_audibility.low_render_limit =
AEC_GET_FLOAT(ini, ECHO_AUDIBILITY, LOW_RENDER_LIMIT);
config->echo_audibility.normal_render_limit =
@@ -173,6 +185,7 @@
AEC_GET_INT(ini, ECHO_AUDIBILITY,
USE_STATIONARITY_PROPERTIES_AT_INIT);
+ /* Keys for EchoCanceller3Config:RenderLevels */
config->render_levels.active_render_limit =
AEC_GET_FLOAT(ini, RENDER_LEVELS, ACTIVE_RENDER_LIMIT);
config->render_levels.poor_excitation_render_limit =
@@ -182,11 +195,13 @@
config->render_levels.render_power_gain_db =
AEC_GET_FLOAT(ini, RENDER_LEVELS, RENDER_POWER_GAIN_DB);
+ /* Keys for EchoCanceller3Config:EchoRemovalControl */
config->echo_removal_control.has_clock_drift =
AEC_GET_INT(ini, ECHO_REMOVAL_CTL, HAS_CLOCK_DRIFT);
config->echo_removal_control.linear_and_stable_echo_path =
AEC_GET_INT(ini, ECHO_REMOVAL_CTL, LINEAR_AND_STABLE_ECHO_PATH);
+ /* Keys for EchoCanceller3Config:EchoModel */
config->echo_model.noise_floor_hold =
AEC_GET_INT(ini, ECHO_MODEL, NOISE_FLOOR_HOLD);
config->echo_model.min_noise_floor_power =
@@ -204,9 +219,11 @@
config->echo_model.model_reverb_in_nonlinear_mode =
AEC_GET_INT(ini, ECHO_MODEL, MODEL_REVERB_IN_NONLINEAR_MODE);
+ /* Keys for EchoCanceller3Config:ComfortNoise */
config->comfort_noise.noise_floor_dbfs =
AEC_GET_FLOAT(ini, COMFORT_NOISE, NOISE_FLOOR_DBFS);
+ /* Keys for EchoCanceller3Config:Suppressor */
config->suppressor.nearend_average_blocks =
AEC_GET_INT(ini, SUPPRESSOR, NEAREND_AVERAGE_BLOCKS);
@@ -413,6 +430,8 @@
config.filter.enable_coarse_filter_output_usage);
syslog(LOG_ERR, " use_linear_filter %d",
config.filter.use_linear_filter);
+ syslog(LOG_ERR, " high_pass_filter_echo_reference %d",
+ config.filter.high_pass_filter_echo_reference);
syslog(LOG_ERR, " export_linear_aec_output %d",
config.filter.export_linear_aec_output);
diff --git a/cras-config/aec_config.h b/cras-config/aec_config.h
index 4f1b46a..8210bce 100644
--- a/cras-config/aec_config.h
+++ b/cras-config/aec_config.h
@@ -10,6 +10,7 @@
#include "api/audio/echo_canceller3_config.h"
+/* Keys for EchoCanceller3Config:Buffering */
#define AEC_BUFFERING_EXCESS_RENDER_DETECTION_INTERVAL_BLOCKS \
"buffering:excess_render_detection_interval_blocks"
#define AEC_BUFFERING_EXCESS_RENDER_DETECTION_INTERVAL_BLOCKS_VALUE 250
@@ -17,6 +18,7 @@
"buffering:max_allowed_excess_render_blocks"
#define AEC_BUFFERING_MAX_ALLOWED_EXCESS_RENDER_BLOCKS_VALUE 8
+/* Keys for EchoCanceller3Config:Delay */
#define AEC_DELAY_DEFAULT_DELAY "delay:default_delay"
#define AEC_DELAY_DEFAULT_DELAY_VALUE 5
#define AEC_DELAY_DOWN_SAMPLING_FACTOR "delay:down_sampling_factor"
@@ -29,10 +31,11 @@
#define AEC_DELAY_HYSTERESIS_LIMIT_BLOCKS_VALUE 1
#define AEC_DELAY_FIXED_CAPTURE_DELAY_SAMPLES \
"delay:fixed_capture_delay_samples"
-#define AEC_DELAY_FIXED_CAPTURE_DELAY_SAMPLES_VALUE 0
+#define AEC_DELAY_FIXED_CAPTURE_DELAY_SAMPLES_VALUE 1000
#define AEC_DELAY_DELAY_ESTIMATE_SMOOTHING "delay:delay_estimate_smoothing"
#define AEC_DELAY_DELAY_ESTIMATE_SMOOTHING_VALUE 0.7f
-#define AEC_DELAY_DELAY_CANDIDATE_DETECTION_THRESHOLD \
+
+#define AEC_DELAY_DELAY_CANDIDATE_DETECTION_THRESHOLD \
"delay:delay_candidate_detection_threshold"
#define AEC_DELAY_DELAY_CANDIDATE_DETECTION_THRESHOLD_VALUE 0.2f
#define AEC_DELAY_DELAY_SELECTION_THRESHOLD_INITIAL \
@@ -41,12 +44,14 @@
#define AEC_DELAY_DELAY_SELECTION_THRESHOLD_CONVERGED \
"delay:delay_selection_thresholds_converged"
#define AEC_DELAY_DELAY_SELECTION_THRESHOLD_CONVERGED_VALUE 20
-#define AEC_DELAY_USE_EXTERNAL_DELAY_ESTIMATOR \
+
+#define AEC_DELAY_USE_EXTERNAL_DELAY_ESTIMATOR \
"delay:use_external_delay_estimator"
#define AEC_DELAY_USE_EXTERNAL_DELAY_ESTIMATOR_VALUE 0
#define AEC_DELAY_LOG_WARNING_ON_DELAY_CHANGES \
"delay:log_warning_on_delay_changes"
#define AEC_DELAY_LOG_WARNING_ON_DELAY_CHANGES_VALUE 0
+
#define AEC_DELAY_RENDER_ALIGNMENT_MIXING_DOWNMIX \
"delay:render_alignment_mixing_downmix"
#define AEC_DELAY_RENDER_ALIGNMENT_MIXING_DOWNMIX_VALUE 0
@@ -72,6 +77,7 @@
"delay:capture_alignment_mixing_prefer_first_two_channels"
#define AEC_DELAY_CAPTURE_ALIGNMENT_MIXING_PREFER_FIRST_TWO_CHANNELS_VALUE 0
+/* Keys for EchoCanceller3Config:Filter */
// Filter refined configuration
#define AEC_FILTER_REFINED_LENGTH_BLOCKS "filter.refined:length_blocks"
#define AEC_FILTER_REFINED_LENGTH_BLOCKS_VALUE 13
@@ -136,6 +142,9 @@
#define AEC_FILTER_USE_LINEAR_FILTER \
"filter:use_linear_filter"
#define AEC_FILTER_USE_LINEAR_FILTER_VALUE 1
+#define AEC_FILTER_HIGH_PASS_FILTER_ECHO_REFERENCE \
+ "filter:high_pass_filter_echo_reference"
+#define AEC_FILTER_HIGH_PASS_FILTER_ECHO_REFERENCE_VALUE 0
#define AEC_FILTER_EXPORT_LINEAR_AEC_OUTPUT \
"filter:export_linear_aec_output"
#define AEC_FILTER_EXPORT_LINEAR_AEC_OUTPUT_VALUE 0
@@ -168,6 +177,7 @@
#define AEC_EP_STRENGTH_BOUNDED_ERL "ep_strength:bounded_erl"
#define AEC_EP_STRENGTH_BOUNDED_ERL_VALUE 0
+// EchoAudibility
#define AEC_ECHO_AUDIBILITY_LOW_RENDER_LIMIT "echo_audibility:low_render_limit"
#define AEC_ECHO_AUDIBILITY_LOW_RENDER_LIMIT_VALUE 4 * 64.f
#define AEC_ECHO_AUDIBILITY_NORMAL_RENDER_LIMIT \
@@ -234,9 +244,11 @@
"echo_model:model_reverb_in_nonlinear_mode"
#define AEC_ECHO_MODEL_MODEL_REVERB_IN_NONLINEAR_MODE_VALUE 1
+// ComfortNoise
#define AEC_COMFORT_NOISE_NOISE_FLOOR_DBFS "comfort_noise:noise_floor_dbfs"
#define AEC_COMFORT_NOISE_NOISE_FLOOR_DBFS_VALUE -96.03406f
+// Suppressor
#define AEC_SUPPRESSOR_NEAREND_AVERAGE_BLOCKS \
"suppressor:nearend_average_blocks"
#define AEC_SUPPRESSOR_NEAREND_AVERAGE_BLOCKS_VALUE 4
diff --git a/cras-config/apm_config.cc b/cras-config/apm_config.cc
index c921ee7..6ab13c0 100644
--- a/cras-config/apm_config.cc
+++ b/cras-config/apm_config.cc
@@ -20,122 +20,191 @@
typedef webrtc::AudioProcessing::Config ApConfig;
-void apm_config_apply(dictionary *ini, webrtc::AudioProcessing *apm)
+void apm_config_set(dictionary *ini, webrtc::AudioProcessing::Config *config)
{
- ApConfig config;
int level_estimator;
if (ini == NULL)
return;
- config.residual_echo_detector.enabled =
- APM_GET_INT(ini, APM_RESIDUAL_ECHO_DETECTOR_ENABLED);
- config.high_pass_filter.enabled =
- APM_GET_INT(ini, APM_HIGH_PASS_FILTER_ENABLED);
- config.high_pass_filter.apply_in_full_band =
- APM_GET_INT(ini, APM_HIGH_PASS_FILTER_APPLY_IN_FULL_BAND);
- config.pre_amplifier.enabled =
- APM_GET_INT(ini, APM_PRE_AMPLIFIER_ENABLED);
- config.pre_amplifier.fixed_gain_factor =
- APM_GET_FLOAT(ini, APM_PRE_AMPLIFIER_FIXED_GAIN_FACTOR);
+ syslog(LOG_ERR, "Reading APM ini");
- config.echo_canceller.enabled =
+ /* Keys for AudioProcessing::Pipeline */
+ config->pipeline.maximum_internal_processing_rate =
+ APM_GET_INT(ini, PIPELINE_MAXIMUM_INTERNAL_PROCESSING_RATE);
+ config->pipeline.multi_channel_render =
+ APM_GET_INT(ini, PIPELINE_MULTI_CHANNEL_RENDER);
+ config->pipeline.multi_channel_capture =
+ APM_GET_INT(ini, PIPELINE_MULTI_CHANNEL_CAPTURE);
+
+ /* Keys for AudioProcessing::PreAmplifier */
+ config->pre_amplifier.enabled =
+ APM_GET_INT(ini, APM_PRE_AMPLIFIER_ENABLED);
+ config->pre_amplifier.fixed_gain_factor =
+ APM_GET_FLOAT(ini, APM_PRE_AMPLIFIER_FIXED_GAIN_FACTOR);
+
+ /* Keys for AudioProcessing::CaptureLevelAdjustment */
+ config->capture_level_adjustment.enabled =
+ APM_GET_INT(ini, CAPTURE_LEVEL_ADJUSTMENT_ENABLED);
+ config->capture_level_adjustment.pre_gain_factor =
+ APM_GET_FLOAT(ini, CAPTURE_LEVEL_ADJUSTMENT_PRE_GAIN_FACTOR);
+ config->capture_level_adjustment.post_gain_factor =
+ APM_GET_FLOAT(ini, CAPTURE_LEVEL_ADJUSTMENT_POST_GAIN_FACTOR);
+
+ /* Keys for AudioProcessing::CaptureLevelAdjustment::AnalogMicGainEmulation */
+ config->capture_level_adjustment.analog_mic_gain_emulation.enabled =
+ APM_GET_INT(ini, CAPTURE_LEVEL_ADJUSTMENT_ANALOG_MIC_GAIN_EMULATION_ENABLED);
+ config->capture_level_adjustment.analog_mic_gain_emulation.initial_level =
+ APM_GET_INT(ini, CAPTURE_LEVEL_ADJUSTMENT_ANALOG_MIC_GAIN_EMULATION_INITIAL_LEVEL);
+
+ /* Keys for AudioProcessing::HighPassFilter */
+ config->high_pass_filter.enabled =
+ APM_GET_INT(ini, APM_HIGH_PASS_FILTER_ENABLED);
+ config->high_pass_filter.apply_in_full_band =
+ APM_GET_INT(ini, APM_HIGH_PASS_FILTER_APPLY_IN_FULL_BAND);
+
+ /* Keys for AudioProcessing::EchoCanceller */
+ config->echo_canceller.enabled =
APM_GET_INT(ini, APM_ECHO_CANCELLER_ENABLED);
- config.echo_canceller.mobile_mode =
+ config->echo_canceller.mobile_mode =
APM_GET_INT(ini, APM_ECHO_CANCELLER_MOBILE_MODE);
- config.echo_canceller.export_linear_aec_output =
+ config->echo_canceller.export_linear_aec_output =
APM_GET_INT(ini, APM_ECHO_CANCELLER_EXPORT_LINEAR_AEC_OUTPUT);
- config.echo_canceller.enforce_high_pass_filtering =
+ config->echo_canceller.enforce_high_pass_filtering =
APM_GET_INT(ini, APM_ECHO_CANCELLER_ENFORCE_HIGH_PASS_FILTERING);
- config.transient_suppression.enabled =
- APM_GET_INT(ini, APM_TRANSIENT_SUPPRESSION_ENABLED);
- config.voice_detection.enabled =
- APM_GET_INT(ini, APM_VOICE_DETECTION_ENABLED);
-
- config.gain_controller1.enabled =
- APM_GET_INT(ini, APM_GAIN_CONTROL_ENABLED);
- config.gain_controller1.mode = static_cast<ApConfig::GainController1::Mode>(
- APM_GET_INT(ini, APM_GAIN_CONTROL_MODE));
- config.gain_controller1.compression_gain_db =
- APM_GET_INT(ini, APM_GAIN_CONTROL_COMPRESSION_GAIN_DB);
- config.gain_controller1.target_level_dbfs =
- APM_GET_INT(ini, APM_GAIN_CONTROL_TARGET_LEVEL_DBFS);
- config.gain_controller1.enable_limiter =
- APM_GET_INT(ini, APM_GAIN_CONTROL_ENABLE_LIMITER);
- config.gain_controller1.analog_level_minimum =
- APM_GET_INT(ini, APM_GAIN_CONTROL_ANALOG_LEVEL_MINIMUM);
- config.gain_controller1.analog_level_maximum =
- APM_GET_INT(ini, APM_GAIN_CONTROL_ANALOG_LEVEL_MAXIMUM);
-
- /* GainController1::AnalogGainController */
- config.gain_controller1.analog_gain_controller.enabled =
- APM_GET_INT(ini, APM_ANALOG_GAIN_CONTROLLER_ENABLED);
- config.gain_controller1.analog_gain_controller.startup_min_volume =
- APM_GET_INT(ini, APM_ANALOG_GAIN_CONTROLLER_STARTUP_MIN_VOLUME);
- config.gain_controller1.analog_gain_controller.clipped_level_min =
- APM_GET_INT(ini, APM_ANALOG_GAIN_CONTROLLER_CLIPPED_LEVEL_MIN);
- config.gain_controller1.analog_gain_controller.enable_agc2_level_estimator =
- APM_GET_INT(ini, APM_ANALOG_GAIN_CONTROLLER_ENABLE_AGC2_LEVEL_ESTIMATOR);
- config.gain_controller1.analog_gain_controller.enable_digital_adaptive =
- APM_GET_INT(ini, APM_ANALOG_GAIN_CONTROLLER_ENABLE_DIGITAL_ADAPTIVE);
-
- config.gain_controller2.enabled =
- APM_GET_INT(ini, APM_GAIN_CONTROLLER2_ENABLED);
- config.gain_controller2.adaptive_digital.enabled =
- APM_GET_INT(ini, ADAPTIVE_DIGITAL_ENABLED);
- config.gain_controller2.adaptive_digital.vad_probability_attack =
- APM_GET_FLOAT(ini, ADAPTIVE_DIGITAL_VAD_PROBABILITY_ATTACK);
- config.gain_controller2.adaptive_digital.level_estimator_adjacent_speech_frames_threshold =
- APM_GET_INT(ini, ADAPTIVE_DIGITAL_LEVEL_ESTIMATOR_ADJACENT_SPEECH_FRAMES_THRESHOLD);
- config.gain_controller2.adaptive_digital.initial_saturation_margin_db =
- APM_GET_FLOAT(ini, ADAPTIVE_DIGITAL_INITIAL_SATURATION_MARGIN_DB);
- config.gain_controller2.adaptive_digital.gain_applier_adjacent_speech_frames_threshold =
- APM_GET_INT(ini, ADAPTIVE_DIGITAL_GAIN_APPLIER_ADJACENT_SPEECH_FRAMES_THRESHOLD);
- config.gain_controller2.adaptive_digital.max_gain_change_db_per_second =
- APM_GET_FLOAT(ini, ADAPTIVE_DIGITAL_MAX_GAIN_CHANGE_DB_PER_SECOND);
- config.gain_controller2.adaptive_digital.max_output_noise_level_dbfs =
- APM_GET_FLOAT(ini, ADAPTIVE_DIGITAL_MAX_OUTPUT_NOISE_LEVEL_DBFS);
- config.gain_controller2.adaptive_digital.extra_saturation_margin_db =
- APM_GET_FLOAT(ini, ADAPTIVE_DIGITAL_EXTRA_SATURATION_MARGIN_DB);
- level_estimator = APM_GET_INT(
- ini, ADAPTIVE_DIGITAL_LEVEL_ESTIMATOR);
- config.gain_controller2.adaptive_digital.level_estimator =
- static_cast<ApConfig::GainController2::LevelEstimator>(
- level_estimator);
- config.gain_controller2.adaptive_digital.use_saturation_protector =
- APM_GET_INT(ini, ADAPTIVE_DIGITAL_USE_SATURATION_PROTECTOR);
- config.gain_controller2.fixed_digital.gain_db =
- APM_GET_FLOAT(ini, APM_GAIN_CONTROLLER2_FIXED_DIGITAL_GAIN_DB);
-
- config.level_estimation.enabled =
- APM_GET_INT(ini, APM_LEVEL_ESTIMATION_ENABLED);
-
- config.noise_suppression.enabled =
+ /* Keys for AudioProcessing::NoiseSuppression */
+ config->noise_suppression.enabled =
APM_GET_INT(ini, APM_NOISE_SUPPRESSION_ENABLED);
- config.noise_suppression.level = static_cast<ApConfig::NoiseSuppression::Level>(
+ config->noise_suppression.level = static_cast<ApConfig::NoiseSuppression::Level>(
APM_GET_INT(ini, APM_NOISE_SUPPRESSION_LEVEL));
- config.noise_suppression.analyze_linear_aec_output_when_available =
+ config->noise_suppression.analyze_linear_aec_output_when_available =
APM_GET_INT(ini, APM_NOISE_SUPPRESSION_ANALYZE_LINEAR_AEC_OUTPUT_WHEN_AVAILABLE);
- apm->ApplyConfig(config);
+ /* Keys for AudioProcessing::TransientSuppression */
+ config->transient_suppression.enabled =
+ APM_GET_INT(ini, APM_TRANSIENT_SUPPRESSION_ENABLED);
+
+ /* Keys for AudioProcessing::VoiceDetection */
+ config->voice_detection.enabled =
+ APM_GET_INT(ini, APM_VOICE_DETECTION_ENABLED);
+
+ /* Keys for AudioProcessing::GainController1 */
+ config->gain_controller1.enabled =
+ APM_GET_INT(ini, APM_GAIN_CONTROL_ENABLED);
+ config->gain_controller1.mode =
+ static_cast<ApConfig::GainController1::Mode>(
+ APM_GET_INT(ini, APM_GAIN_CONTROL_MODE));
+ config->gain_controller1.target_level_dbfs =
+ APM_GET_INT(ini, APM_GAIN_CONTROL_TARGET_LEVEL_DBFS);
+ config->gain_controller1.compression_gain_db =
+ APM_GET_INT(ini, APM_GAIN_CONTROL_COMPRESSION_GAIN_DB);
+ config->gain_controller1.enable_limiter =
+ APM_GET_INT(ini, APM_GAIN_CONTROL_ENABLE_LIMITER);
+ config->gain_controller1.analog_level_minimum =
+ APM_GET_INT(ini, APM_GAIN_CONTROL_ANALOG_LEVEL_MINIMUM);
+ config->gain_controller1.analog_level_maximum =
+ APM_GET_INT(ini, APM_GAIN_CONTROL_ANALOG_LEVEL_MAXIMUM);
+
+ /* Keys for AudioProcessing::GainController1::AnalogGainController */
+ config->gain_controller1.analog_gain_controller.enabled =
+ APM_GET_INT(ini, APM_ANALOG_GAIN_CONTROLLER_ENABLED);
+ config->gain_controller1.analog_gain_controller.startup_min_volume =
+ APM_GET_INT(ini, APM_ANALOG_GAIN_CONTROLLER_STARTUP_MIN_VOLUME);
+ config->gain_controller1.analog_gain_controller.clipped_level_min =
+ APM_GET_INT(ini, APM_ANALOG_GAIN_CONTROLLER_CLIPPED_LEVEL_MIN);
+ config->gain_controller1.analog_gain_controller.enable_digital_adaptive =
+ APM_GET_INT(ini,
+ APM_ANALOG_GAIN_CONTROLLER_ENABLE_DIGITAL_ADAPTIVE);
+
+ /* Keys for AudioProcessing::GainController2 */
+ config->gain_controller2.enabled =
+ APM_GET_INT(ini, APM_GAIN_CONTROLLER2_ENABLED);
+
+ /* Keys for AudioProcessing::GainController2::FixedDigital */
+ config->gain_controller2.fixed_digital.gain_db =
+ APM_GET_FLOAT(ini, APM_GAIN_CONTROLLER2_FIXED_DIGITAL_GAIN_DB);
+
+ /* Keys for AudioProcessing::GainController2::AdaptiveDigital */
+ config->gain_controller2.adaptive_digital.enabled =
+ APM_GET_INT(ini, ADAPTIVE_DIGITAL_ENABLED);
+ config->gain_controller2.adaptive_digital.vad_probability_attack =
+ APM_GET_FLOAT(ini, ADAPTIVE_DIGITAL_VAD_PROBABILITY_ATTACK);
+ level_estimator = APM_GET_INT(
+ ini, ADAPTIVE_DIGITAL_LEVEL_ESTIMATOR);
+ config->gain_controller2.adaptive_digital.level_estimator =
+ static_cast<ApConfig::GainController2::LevelEstimator>(level_estimator);
+ config->gain_controller2.adaptive_digital.level_estimator_adjacent_speech_frames_threshold =
+ APM_GET_INT(ini, ADAPTIVE_DIGITAL_LEVEL_ESTIMATOR_ADJACENT_SPEECH_FRAMES_THRESHOLD);
+ config->gain_controller2.adaptive_digital.use_saturation_protector =
+ APM_GET_INT(ini, ADAPTIVE_DIGITAL_USE_SATURATION_PROTECTOR);
+ config->gain_controller2.adaptive_digital.initial_saturation_margin_db =
+ APM_GET_FLOAT(ini, ADAPTIVE_DIGITAL_INITIAL_SATURATION_MARGIN_DB);
+ config->gain_controller2.adaptive_digital.extra_saturation_margin_db =
+ APM_GET_FLOAT(ini, ADAPTIVE_DIGITAL_EXTRA_SATURATION_MARGIN_DB);
+ config->gain_controller2.adaptive_digital.gain_applier_adjacent_speech_frames_threshold =
+ APM_GET_INT(ini, ADAPTIVE_DIGITAL_GAIN_APPLIER_ADJACENT_SPEECH_FRAMES_THRESHOLD);
+ config->gain_controller2.adaptive_digital.max_gain_change_db_per_second =
+ APM_GET_FLOAT(ini, ADAPTIVE_DIGITAL_MAX_GAIN_CHANGE_DB_PER_SECOND);
+ config->gain_controller2.adaptive_digital.max_output_noise_level_dbfs =
+ APM_GET_FLOAT(ini, ADAPTIVE_DIGITAL_MAX_OUTPUT_NOISE_LEVEL_DBFS);
+ config->gain_controller2.adaptive_digital.sse2_allowed =
+ APM_GET_INT(ini, ADAPTIVE_DIGITAL_SSE2_ALLOWED);
+ config->gain_controller2.adaptive_digital.avx2_allowed =
+ APM_GET_INT(ini, ADAPTIVE_DIGITAL_AVX2_ALLOWED);
+ config->gain_controller2.adaptive_digital.neon_allowed =
+ APM_GET_INT(ini, ADAPTIVE_DIGITAL_NEON_ALLOWED);
+
+ /* Keys for AudioProcessing::ResidualEchoDetector */
+ config->residual_echo_detector.enabled =
+ APM_GET_INT(ini, APM_RESIDUAL_ECHO_DETECTOR_ENABLED);
+
+ /* Keys for AudioProcessing::LevelEstimation */
+ config->level_estimation.enabled =
+ APM_GET_INT(ini, APM_LEVEL_ESTIMATION_ENABLED);
+
}
void apm_config_dump(dictionary *ini)
{
syslog(LOG_ERR, "---- apm config dump ----");
- syslog(LOG_ERR, "residual_echo_detector_enabled %u",
- APM_GET_INT(ini, APM_RESIDUAL_ECHO_DETECTOR_ENABLED));
- syslog(LOG_ERR, "high_pass_filter_enabled %u",
- APM_GET_INT(ini, APM_HIGH_PASS_FILTER_ENABLED));
- syslog(LOG_ERR, "high_pass_filter_apply_in_full_band %d",
- APM_GET_INT(ini, APM_HIGH_PASS_FILTER_APPLY_IN_FULL_BAND));
+
+ /* Keys for AudioProcessing::Pipeline */
+ syslog(LOG_ERR, "pipeline_maximum_internal_processing_rate %u",
+ APM_GET_INT(ini, PIPELINE_MAXIMUM_INTERNAL_PROCESSING_RATE));
+ syslog(LOG_ERR, "pipeline_multi_channel_render %u",
+ APM_GET_INT(ini, PIPELINE_MULTI_CHANNEL_RENDER));
+ syslog(LOG_ERR, "pipeline_multi_channel_capture %u",
+ APM_GET_INT(ini, PIPELINE_MULTI_CHANNEL_CAPTURE));
+
+ /* Keys for AudioProcessing::PreAmplifier */
syslog(LOG_ERR, "pre_amplifier_enabled %u",
APM_GET_INT(ini, APM_PRE_AMPLIFIER_ENABLED));
syslog(LOG_ERR, "pre_amplifier_fixed_gain_factor %f",
APM_GET_FLOAT(ini, APM_PRE_AMPLIFIER_FIXED_GAIN_FACTOR));
+ /* Keys for AudioProcessing::CaptureLevelAdjustment */
+ syslog(LOG_ERR, "capture_level_adjustment_enabled %u",
+ APM_GET_INT(ini, CAPTURE_LEVEL_ADJUSTMENT_ENABLED));
+ syslog(LOG_ERR, "capture_level_adjustment_pre_gain_factor %f",
+ APM_GET_FLOAT(ini, CAPTURE_LEVEL_ADJUSTMENT_PRE_GAIN_FACTOR));
+ syslog(LOG_ERR, "capture_level_adjustment_post_gain_factor %f",
+ APM_GET_FLOAT(ini, CAPTURE_LEVEL_ADJUSTMENT_POST_GAIN_FACTOR));
+
+ /* Keys for AudioProcessing::CaptureLevelAdjustment::AnalogMicGainEmulation */
+ syslog(LOG_ERR, "capture_level_adjustment_analog_mic_gain_emulation_enabled %u",
+ APM_GET_INT(ini, CAPTURE_LEVEL_ADJUSTMENT_ANALOG_MIC_GAIN_EMULATION_ENABLED));
+ syslog(LOG_ERR, "capture_level_adjustment_analog_mic_gain_emulation_initial_level %u",
+ APM_GET_INT(ini, CAPTURE_LEVEL_ADJUSTMENT_ANALOG_MIC_GAIN_EMULATION_INITIAL_LEVEL));
+
+ /* Keys for AudioProcessing::HighPassFilter */
+ syslog(LOG_ERR, "high_pass_filter_enabled %u",
+ APM_GET_INT(ini, APM_HIGH_PASS_FILTER_ENABLED));
+ syslog(LOG_ERR, "high_pass_filter_apply_in_full_band %d",
+ APM_GET_INT(ini, APM_HIGH_PASS_FILTER_APPLY_IN_FULL_BAND));
+
+ /* Keys for AudioProcessing::EchoCanceller */
syslog(LOG_ERR, "echo_canceller_enabled %d",
APM_GET_INT(ini, APM_ECHO_CANCELLER_ENABLED));
syslog(LOG_ERR, "echo_canceller_mobile_mode %d",
@@ -145,75 +214,90 @@
syslog(LOG_ERR, "echo_canceller_enforce_high_pass_filtering %d",
APM_GET_INT(ini, APM_ECHO_CANCELLER_ENFORCE_HIGH_PASS_FILTERING));
+ /* Keys for AudioProcessing::NoiseSuppression */
+ syslog(LOG_ERR, "noise_suppression_enabled %u",
+ APM_GET_INT(ini, APM_NOISE_SUPPRESSION_ENABLED));
+ syslog(LOG_ERR, "noise_suppression_level %u",
+ APM_GET_INT(ini, APM_NOISE_SUPPRESSION_LEVEL));
+ syslog(LOG_ERR, "noise_suppression_analyze_linear_aec_output_when_available %u",
+ APM_GET_INT(ini, APM_NOISE_SUPPRESSION_ANALYZE_LINEAR_AEC_OUTPUT_WHEN_AVAILABLE));
+
+ /* Keys for AudioProcessing::TransientSuppression */
syslog(LOG_ERR, "transient_suppression_enabled %d",
APM_GET_INT(ini, APM_TRANSIENT_SUPPRESSION_ENABLED));
+ /* Keys for AudioProcessing::VoiceDetection */
syslog(LOG_ERR, "voice_detection_enabled %d",
APM_GET_INT(ini, APM_VOICE_DETECTION_ENABLED));
- syslog(LOG_ERR, "gain_controller2_enabled %u",
- APM_GET_INT(ini, APM_GAIN_CONTROLLER2_ENABLED));
- syslog(LOG_ERR, "adaptive_digital_enabled %d",
- APM_GET_INT(ini, ADAPTIVE_DIGITAL_ENABLED));
- syslog(LOG_ERR, "adaptive_digital_vad_probability_attack %f",
- APM_GET_FLOAT(ini, ADAPTIVE_DIGITAL_VAD_PROBABILITY_ATTACK));
- syslog(LOG_ERR, "adaptive_digital_level_estimator_adjacent_speech_frames_threshold %d",
- APM_GET_INT(ini, ADAPTIVE_DIGITAL_LEVEL_ESTIMATOR_ADJACENT_SPEECH_FRAMES_THRESHOLD));
- syslog(LOG_ERR, "adaptive_digital_initial_saturation_margin_db %f",
- APM_GET_FLOAT(ini, ADAPTIVE_DIGITAL_INITIAL_SATURATION_MARGIN_DB));
- syslog(LOG_ERR, "adaptive_digital_gain_applier_adjacent_speech_frames_threshold %d",
- APM_GET_INT(ini, ADAPTIVE_DIGITAL_GAIN_APPLIER_ADJACENT_SPEECH_FRAMES_THRESHOLD));
- syslog(LOG_ERR, "adaptive_digital_max_gain_change_db_per_second %f",
- APM_GET_FLOAT(ini, ADAPTIVE_DIGITAL_MAX_GAIN_CHANGE_DB_PER_SECOND));
- syslog(LOG_ERR, "adaptive_digital_max_output_noise_level_dbfs %f",
- APM_GET_FLOAT(ini, ADAPTIVE_DIGITAL_MAX_OUTPUT_NOISE_LEVEL_DBFS));
- syslog(LOG_ERR, "adaptive_digital_extra_saturation_margin_db %f",
- APM_GET_FLOAT(ini,
- ADAPTIVE_DIGITAL_EXTRA_SATURATION_MARGIN_DB));
- syslog(LOG_ERR, "adaptive_digital_level_estimator %d",
- APM_GET_INT(ini, ADAPTIVE_DIGITAL_LEVEL_ESTIMATOR));
- syslog(LOG_ERR, "adaptive_digital_use_saturation_protector %d",
- APM_GET_INT(ini, ADAPTIVE_DIGITAL_USE_SATURATION_PROTECTOR));
- syslog(LOG_ERR, "adaptive_digital_sse2_allowed %d",
- APM_GET_INT(ini, ADAPTIVE_DIGITAL_SSE2_ALLOWED));
- syslog(LOG_ERR, "adaptive_digital_avx2_allowed %d",
- APM_GET_INT(ini, ADAPTIVE_DIGITAL_AVX2_ALLOWED));
- syslog(LOG_ERR, "adaptive_digital_neon_allowed %d",
- APM_GET_INT(ini, ADAPTIVE_DIGITAL_NEON_ALLOWED));
- syslog(LOG_ERR, "gain_controller2_fixed_digital_gain_db %f",
- APM_GET_FLOAT(ini, APM_GAIN_CONTROLLER2_FIXED_DIGITAL_GAIN_DB));
-
- syslog(LOG_ERR, "level_estimation_enabled %d",
- APM_GET_INT(ini, APM_LEVEL_ESTIMATION_ENABLED));
-
- syslog(LOG_ERR, "gain_control_compression_gain_db %u",
- APM_GET_INT(ini, APM_GAIN_CONTROL_COMPRESSION_GAIN_DB));
+ /* Keys for AudioProcessing::GainController1 */
+ syslog(LOG_ERR, "gain_control_enabled %u",
+ APM_GET_INT(ini, APM_GAIN_CONTROL_ENABLED));
+ syslog(LOG_ERR, "gain_control_mode %u",
+ APM_GET_INT(ini, APM_GAIN_CONTROL_MODE));
syslog(LOG_ERR, "gain_control_target_level_dbfs %d",
APM_GET_INT(ini, APM_GAIN_CONTROL_TARGET_LEVEL_DBFS));
+ syslog(LOG_ERR, "gain_control_compression_gain_db %u",
+ APM_GET_INT(ini, APM_GAIN_CONTROL_COMPRESSION_GAIN_DB));
syslog(LOG_ERR, "gain_control_enable_limiter %d",
APM_GET_INT(ini, APM_GAIN_CONTROL_ENABLE_LIMITER));
syslog(LOG_ERR, "gain_control_analog_level_minimum %d",
APM_GET_INT(ini, APM_GAIN_CONTROL_ANALOG_LEVEL_MINIMUM));
syslog(LOG_ERR, "gain_control_analog_level_maximumc %d",
APM_GET_INT(ini, APM_GAIN_CONTROL_ANALOG_LEVEL_MAXIMUM));
- syslog(LOG_ERR, "gain_control_mode %u",
- APM_GET_INT(ini, APM_GAIN_CONTROL_MODE));
- syslog(LOG_ERR, "gain_control_enabled %u",
- APM_GET_INT(ini, APM_GAIN_CONTROL_ENABLED));
+ /* Keys for AudioProcessing::GainController1::AnalogGainController */
syslog(LOG_ERR, "analog_gain_controller_enabled %d",
APM_GET_INT(ini, APM_ANALOG_GAIN_CONTROLLER_ENABLED));
syslog(LOG_ERR, "analog_gain_controller_startup_min_volume %d",
APM_GET_INT(ini, APM_ANALOG_GAIN_CONTROLLER_STARTUP_MIN_VOLUME));
syslog(LOG_ERR, "analog_gain_controller_clipped_level_min %d",
APM_GET_INT(ini, APM_ANALOG_GAIN_CONTROLLER_CLIPPED_LEVEL_MIN));
- syslog(LOG_ERR, "analog_gain_controller_enable_agc2_level_estimator %d",
- APM_GET_INT(ini, APM_ANALOG_GAIN_CONTROLLER_ENABLE_AGC2_LEVEL_ESTIMATOR));
syslog(LOG_ERR, "analog_gain_controller_enable_digital_adaptive %d",
APM_GET_INT(ini, APM_ANALOG_GAIN_CONTROLLER_ENABLE_DIGITAL_ADAPTIVE));
- syslog(LOG_ERR, "noise_suppression_level %u",
- APM_GET_INT(ini, APM_NOISE_SUPPRESSION_LEVEL));
- syslog(LOG_ERR, "noise_suppression_enabled %u",
- APM_GET_INT(ini, APM_NOISE_SUPPRESSION_ENABLED));
+ /* Keys for AudioProcessing::GainController2 */
+ syslog(LOG_ERR, "gain_controller2_enabled %u",
+ APM_GET_INT(ini, APM_GAIN_CONTROLLER2_ENABLED));
+
+ /* Keys for AudioProcessing::GainController2::FixedDigital */
+ syslog(LOG_ERR, "gain_controller2_fixed_digital_gain_db %f",
+ APM_GET_FLOAT(ini, APM_GAIN_CONTROLLER2_FIXED_DIGITAL_GAIN_DB));
+
+ /* Keys for AudioProcessing::GainController2::AdaptiveDigital */
+ syslog(LOG_ERR, "adaptive_digital_enabled %d",
+ APM_GET_INT(ini, ADAPTIVE_DIGITAL_ENABLED));
+ syslog(LOG_ERR, "adaptive_digital_vad_probability_attack %f",
+ APM_GET_FLOAT(ini, ADAPTIVE_DIGITAL_VAD_PROBABILITY_ATTACK));
+ syslog(LOG_ERR, "adaptive_digital_level_estimator %d",
+ APM_GET_INT(ini, ADAPTIVE_DIGITAL_LEVEL_ESTIMATOR));
+ syslog(LOG_ERR, "adaptive_digital_level_estimator_adjacent_speech_frames_threshold %d",
+ APM_GET_INT(ini, ADAPTIVE_DIGITAL_LEVEL_ESTIMATOR_ADJACENT_SPEECH_FRAMES_THRESHOLD));
+ syslog(LOG_ERR, "adaptive_digital_use_saturation_protector %d",
+ APM_GET_INT(ini, ADAPTIVE_DIGITAL_USE_SATURATION_PROTECTOR));
+ syslog(LOG_ERR, "adaptive_digital_initial_saturation_margin_db %f",
+ APM_GET_FLOAT(ini, ADAPTIVE_DIGITAL_INITIAL_SATURATION_MARGIN_DB));
+ syslog(LOG_ERR, "adaptive_digital_extra_saturation_margin_db %f",
+ APM_GET_FLOAT(ini,
+ ADAPTIVE_DIGITAL_EXTRA_SATURATION_MARGIN_DB));
+ syslog(LOG_ERR, "adaptive_digital_gain_applier_adjacent_speech_frames_threshold %d",
+ APM_GET_INT(ini, ADAPTIVE_DIGITAL_GAIN_APPLIER_ADJACENT_SPEECH_FRAMES_THRESHOLD));
+ syslog(LOG_ERR, "adaptive_digital_max_gain_change_db_per_second %f",
+ APM_GET_FLOAT(ini, ADAPTIVE_DIGITAL_MAX_GAIN_CHANGE_DB_PER_SECOND));
+ syslog(LOG_ERR, "adaptive_digital_max_output_noise_level_dbfs %f",
+ APM_GET_FLOAT(ini, ADAPTIVE_DIGITAL_MAX_OUTPUT_NOISE_LEVEL_DBFS));
+ syslog(LOG_ERR, "adaptive_digital_sse2_allowed %d",
+ APM_GET_INT(ini, ADAPTIVE_DIGITAL_SSE2_ALLOWED));
+ syslog(LOG_ERR, "adaptive_digital_avx2_allowed %d",
+ APM_GET_INT(ini, ADAPTIVE_DIGITAL_AVX2_ALLOWED));
+ syslog(LOG_ERR, "adaptive_digital_neon_allowed %d",
+ APM_GET_INT(ini, ADAPTIVE_DIGITAL_NEON_ALLOWED));
+
+ /* Keys for AudioProcessing::ResidualEchoDetector */
+ syslog(LOG_ERR, "residual_echo_detector_enabled %u",
+ APM_GET_INT(ini, APM_RESIDUAL_ECHO_DETECTOR_ENABLED));
+
+ /* Keys for AudioProcessing::LevelEstimation */
+ syslog(LOG_ERR, "level_estimation_enabled %d",
+ APM_GET_INT(ini, APM_LEVEL_ESTIMATION_ENABLED));
}
diff --git a/cras-config/apm_config.h b/cras-config/apm_config.h
index 35c76f6..e5469a4 100644
--- a/cras-config/apm_config.h
+++ b/cras-config/apm_config.h
@@ -10,17 +10,41 @@
#include "modules/audio_processing/include/audio_processing.h"
-#define APM_RESIDUAL_ECHO_DETECTOR_ENABLED "apm:residual_echo_detector_enabled"
-#define APM_RESIDUAL_ECHO_DETECTOR_ENABLED_VALUE 1
-#define APM_HIGH_PASS_FILTER_ENABLED "apm:high_pass_filter_enabled"
-#define APM_HIGH_PASS_FILTER_ENABLED_VALUE 0
-#define APM_HIGH_PASS_FILTER_APPLY_IN_FULL_BAND "apm:high_pass_filter_apply_in_full_band"
-#define APM_HIGH_PASS_FILTER_APPLY_IN_FULL_BAND_VALUE 1
+/* Keys for AudioProcessing::Pipeline */
+#define PIPELINE_MAXIMUM_INTERNAL_PROCESSING_RATE "apm:pipeline_maximum_internal_processing_rate"
+#define PIPELINE_MAXIMUM_INTERNAL_PROCESSING_RATE_VALUE 48000
+#define PIPELINE_MULTI_CHANNEL_RENDER "apm:pipeline_multi_channel_render"
+#define PIPELINE_MULTI_CHANNEL_RENDER_VALUE 0
+#define PIPELINE_MULTI_CHANNEL_CAPTURE "apm:pipeline_multi_channel_capture"
+#define PIPELINE_MULTI_CHANNEL_CAPTURE_VALUE 0
+
+/* Keys for AudioProcessing::PreAmplifier */
#define APM_PRE_AMPLIFIER_ENABLED "apm:pre_amplifier_enabled"
#define APM_PRE_AMPLIFIER_ENABLED_VALUE 0
#define APM_PRE_AMPLIFIER_FIXED_GAIN_FACTOR "apm:pre_amplifier_fixed_gain_factor"
#define APM_PRE_AMPLIFIER_FIXED_GAIN_FACTOR_VALUE 1.f
+/* Keys for AudioProcessing::CaptureLevelAdjustment */
+#define CAPTURE_LEVEL_ADJUSTMENT_ENABLED "apm:capture_level_adjustment_enabled"
+#define CAPTURE_LEVEL_ADJUSTMENT_ENABLED_VALUE 0
+#define CAPTURE_LEVEL_ADJUSTMENT_PRE_GAIN_FACTOR "apm:capture_level_adjustment_pre_gain_factor"
+#define CAPTURE_LEVEL_ADJUSTMENT_PRE_GAIN_FACTOR_VALUE 1.f
+#define CAPTURE_LEVEL_ADJUSTMENT_POST_GAIN_FACTOR "apm:capture_level_adjustment_post_gain_factor"
+#define CAPTURE_LEVEL_ADJUSTMENT_POST_GAIN_FACTOR_VALUE 1.f
+
+/* Keys for AudioProcessing::CaptureLevelAdjustment::AnalogMicGainEmulation */
+#define CAPTURE_LEVEL_ADJUSTMENT_ANALOG_MIC_GAIN_EMULATION_ENABLED "apm:capture_level_adjustment_analog_mic_gain_emulation_enabled"
+#define CAPTURE_LEVEL_ADJUSTMENT_ANALOG_MIC_GAIN_EMULATION_ENABLED_VALUE 0
+#define CAPTURE_LEVEL_ADJUSTMENT_ANALOG_MIC_GAIN_EMULATION_INITIAL_LEVEL "apm:capture_level_adjustment_analog_mic_gain_emulation_initial_level"
+#define CAPTURE_LEVEL_ADJUSTMENT_ANALOG_MIC_GAIN_EMULATION_INITIAL_LEVEL_VALUE 255
+
+/* Keys for AudioProcessing::HighPassFilter */
+#define APM_HIGH_PASS_FILTER_ENABLED "apm:high_pass_filter_enabled"
+#define APM_HIGH_PASS_FILTER_ENABLED_VALUE 0
+#define APM_HIGH_PASS_FILTER_APPLY_IN_FULL_BAND "apm:high_pass_filter_apply_in_full_band"
+#define APM_HIGH_PASS_FILTER_APPLY_IN_FULL_BAND_VALUE 1
+
+/* Keys for AudioProcessing::EchoCanceller */
#define APM_ECHO_CANCELLER_ENABLED "apm:echo_canceller_enabled"
#define APM_ECHO_CANCELLER_ENABLED_VALUE 0
#define APM_ECHO_CANCELLER_MOBILE_MODE "apm:echo_canceller_mobile_mode"
@@ -32,50 +56,99 @@
"apm:echo_canceller_enforce_high_pass_filtering"
#define APM_ECHO_CANCELLER_ENFORCE_HIGH_PASS_FILTERING_VALUE 1
+/* Keys for AudioProcessing::NoiseSuppression */
+#define APM_NOISE_SUPPRESSION_ENABLED "apm:noise_suppression_enabled"
+#define APM_NOISE_SUPPRESSION_ENABLED_VALUE 0
+/* 0: low, 1: moderate, 2: high, 3: very high*/
+#define APM_NOISE_SUPPRESSION_LEVEL "apm:noise_suppression_level"
+#define APM_NOISE_SUPPRESSION_LEVEL_VALUE 2
+#define APM_NOISE_SUPPRESSION_ANALYZE_LINEAR_AEC_OUTPUT_WHEN_AVAILABLE \
+ "apm:noise_suppression_analyze_linear_aec_output_when_available"
+#define APM_NOISE_SUPPRESSION_ANALYZE_LINEAR_AEC_OUTPUT_WHEN_AVAILABLE_VALUE 0
+
+/* Keys for AudioProcessing::TransientSuppression */
#define APM_TRANSIENT_SUPPRESSION_ENABLED "apm:transient_suppression_enabled"
#define APM_TRANSIENT_SUPPRESSION_ENABLED_VALUE 0
+
+/* Keys for AudioProcessing::VoiceDetection */
#define APM_VOICE_DETECTION_ENABLED "apm:voice_detection_enabled"
#define APM_VOICE_DETECTION_ENABLED_VALUE 0
+
+/* Keys for AudioProcessing::GainController1 */
+/* 0: adaptive analog, 1: adaptive digital, 2: fixed digital */
+#define APM_GAIN_CONTROL_ENABLED "apm:gain_control_enabled"
+#define APM_GAIN_CONTROL_ENABLED_VALUE 0
+/* 0: kAdaptiveAnalog, 1: kAdaptiveDigital, 2: kFixedDigital */
+#define APM_GAIN_CONTROL_MODE "apm:gain_control_mode"
+#define APM_GAIN_CONTROL_MODE_VALUE 0
+#define APM_GAIN_CONTROL_TARGET_LEVEL_DBFS "apm:gain_control_target_level_dbfs"
+#define APM_GAIN_CONTROL_TARGET_LEVEL_DBFS_VALUE 3
+#define APM_GAIN_CONTROL_COMPRESSION_GAIN_DB \
+ "apm:gain_control_compression_gain_db"
+#define APM_GAIN_CONTROL_COMPRESSION_GAIN_DB_VALUE 9
+#define APM_GAIN_CONTROL_ENABLE_LIMITER "apm:gain_control_enable_limiter"
+#define APM_GAIN_CONTROL_ENABLE_LIMITER_VALUE 1
+#define APM_GAIN_CONTROL_ANALOG_LEVEL_MINIMUM \
+ "apm:gain_control_analog_level_minimum"
+#define APM_GAIN_CONTROL_ANALOG_LEVEL_MINIMUM_VALUE 0
+#define APM_GAIN_CONTROL_ANALOG_LEVEL_MAXIMUM \
+ "apm:gain_control_analog_level_maximum"
+#define APM_GAIN_CONTROL_ANALOG_LEVEL_MAXIMUM_VALUE 255
+
+/* Keys for AudioProcessing::GainController1::AnalogGainController */
+#define APM_ANALOG_GAIN_CONTROLLER_ENABLED "apm:analog_gain_controller_enabled"
+#define APM_ANALOG_GAIN_CONTROLLER_ENABLED_VALUE 1
+#define APM_ANALOG_GAIN_CONTROLLER_STARTUP_MIN_VOLUME \
+ "apm:analog_gain_controller_startup_min_volume"
+#define APM_ANALOG_GAIN_CONTROLLER_STARTUP_MIN_VOLUME_VALUE 85
+#define APM_ANALOG_GAIN_CONTROLLER_CLIPPED_LEVEL_MIN \
+ "apm:analog_gain_controller_clipped_level_min"
+#define APM_ANALOG_GAIN_CONTROLLER_CLIPPED_LEVEL_MIN_VALUE 70
+#define APM_ANALOG_GAIN_CONTROLLER_ENABLE_DIGITAL_ADAPTIVE \
+ "apm:analog_gain_controller_enable_digital_adaptive"
+#define APM_ANALOG_GAIN_CONTROLLER_ENABLE_DIGITAL_ADAPTIVE_VALUE 1
+
+/* Keys for AudioProcessing::GainController2 */
#define APM_GAIN_CONTROLLER2_ENABLED "apm:gain_controller2_enabled"
#define APM_GAIN_CONTROLLER2_ENABLED_VALUE 0
+
+/* Keys for AudioProcessing::GainController2::FixedDigital */
#define APM_GAIN_CONTROLLER2_FIXED_DIGITAL_GAIN_DB \
"apm:gain_controller2_fixed_digital_gain_db"
#define APM_GAIN_CONTROLLER2_FIXED_DIGITAL_GAIN_DB_VALUE 0.f
-#define APM_LEVEL_ESTIMATION_ENABLED "apm:level_estimation_enabled"
-#define APM_LEVEL_ESTIMATION_ENABLED_VALUE 0
-
-/* Keys for AudioProcessing::GainController2 */
+/* Keys for AudioProcessing::GainController2::AdaptiveDigital */
#define ADAPTIVE_DIGITAL_ENABLED "apm:adaptive_digital_enabled"
#define ADAPTIVE_DIGITAL_ENABLED_VALUE 0
#define ADAPTIVE_DIGITAL_VAD_PROBABILITY_ATTACK \
"apm:adaptive_digital_vad_probability_attack"
-#define ADAPTIVE_DIGITAL_VAD_PROBABILITY_ATTACK_VALUE 1.f
+#define ADAPTIVE_DIGITAL_VAD_PROBABILITY_ATTACK_VALUE 0.3f
+/* 0: kRms, 1: kPeak */
#define ADAPTIVE_DIGITAL_LEVEL_ESTIMATOR \
"apm:adaptive_digital_level_estimator"
#define ADAPTIVE_DIGITAL_LEVEL_ESTIMATOR_VALUE 0
-#define ADAPTIVE_DIGITAL_EXTRA_SATURATION_MARGIN_DB \
- "apm:adaptive_digital_extra_saturation_margin_db"
-#define ADAPTIVE_DIGITAL_EXTRA_SATURATION_MARGIN_DB_VALUE 2.f
#define ADAPTIVE_DIGITAL_LEVEL_ESTIMATOR_ADJACENT_SPEECH_FRAMES_THRESHOLD \
"apm:adaptive_digital_level_estimator_adjacent_speech_frames_threshold"
-#define ADAPTIVE_DIGITAL_LEVEL_ESTIMATOR_ADJACENT_SPEECH_FRAMES_THRESHOLD_VALUE 1
+#define ADAPTIVE_DIGITAL_LEVEL_ESTIMATOR_ADJACENT_SPEECH_FRAMES_THRESHOLD_VALUE 6
#define ADAPTIVE_DIGITAL_USE_SATURATION_PROTECTOR \
"apm:adaptive_digital_use_saturation_protector"
#define ADAPTIVE_DIGITAL_USE_SATURATION_PROTECTOR_VALUE 1
#define ADAPTIVE_DIGITAL_INITIAL_SATURATION_MARGIN_DB \
"apm:adaptive_digital_initial_saturation_margin_db"
#define ADAPTIVE_DIGITAL_INITIAL_SATURATION_MARGIN_DB_VALUE 20.f
+#define ADAPTIVE_DIGITAL_EXTRA_SATURATION_MARGIN_DB \
+ "apm:adaptive_digital_extra_saturation_margin_db"
+#define ADAPTIVE_DIGITAL_EXTRA_SATURATION_MARGIN_DB_VALUE 5.f
#define ADAPTIVE_DIGITAL_GAIN_APPLIER_ADJACENT_SPEECH_FRAMES_THRESHOLD \
"apm:adaptive_digital_gain_applier_adjacent_speech_frames_threshold"
-#define ADAPTIVE_DIGITAL_GAIN_APPLIER_ADJACENT_SPEECH_FRAMES_THRESHOLD_VALUE 1
+#define ADAPTIVE_DIGITAL_GAIN_APPLIER_ADJACENT_SPEECH_FRAMES_THRESHOLD_VALUE 6
#define ADAPTIVE_DIGITAL_MAX_GAIN_CHANGE_DB_PER_SECOND \
"apm:adaptive_digital_max_gain_change_db_per_second"
#define ADAPTIVE_DIGITAL_MAX_GAIN_CHANGE_DB_PER_SECOND_VALUE 3.f
#define ADAPTIVE_DIGITAL_MAX_OUTPUT_NOISE_LEVEL_DBFS \
"apm:adaptive_digital_max_output_noise_level_dbfs"
-#define ADAPTIVE_DIGITAL_MAX_OUTPUT_NOISE_LEVEL_DBFS_VALUE -50.f
+#define ADAPTIVE_DIGITAL_MAX_OUTPUT_NOISE_LEVEL_DBFS_VALUE -55.f
#define ADAPTIVE_DIGITAL_SSE2_ALLOWED \
"apm:adaptive_digital_sse2_allowed"
#define ADAPTIVE_DIGITAL_SSE2_ALLOWED_VALUE 1
@@ -86,48 +159,24 @@
"apm:adaptive_digital_neon_allowed"
#define ADAPTIVE_DIGITAL_NEON_ALLOWED_VALUE 1
-#define APM_GAIN_CONTROL_COMPRESSION_GAIN_DB "apm:gain_control_compression_gain_db"
-#define APM_GAIN_CONTROL_COMPRESSION_GAIN_DB_VALUE 9
-/* 0: adaptive analog, 1: adaptive digital, 2: fixed digital */
-#define APM_GAIN_CONTROL_MODE "apm:gain_control_mode"
-#define APM_GAIN_CONTROL_MODE_VALUE 0
-#define APM_GAIN_CONTROL_ENABLED "apm:gain_control_enabled"
-#define APM_GAIN_CONTROL_ENABLED_VALUE 0
-#define APM_GAIN_CONTROL_TARGET_LEVEL_DBFS "apm:gain_control_target_level_dbfs"
-#define APM_GAIN_CONTROL_TARGET_LEVEL_DBFS_VALUE 3
-#define APM_GAIN_CONTROL_ENABLE_LIMITER "apm:gain_control_enable_limiter"
-#define APM_GAIN_CONTROL_ENABLE_LIMITER_VALUE 1
-#define APM_GAIN_CONTROL_ANALOG_LEVEL_MINIMUM "apm:gain_control_analog_level_minimum"
-#define APM_GAIN_CONTROL_ANALOG_LEVEL_MINIMUM_VALUE 0
-#define APM_GAIN_CONTROL_ANALOG_LEVEL_MAXIMUM "apm:gain_control_analog_level_maximum"
-#define APM_GAIN_CONTROL_ANALOG_LEVEL_MAXIMUM_VALUE 255
+/* Keys for AudioProcessing::ResidualEchoDetector */
+#define APM_RESIDUAL_ECHO_DETECTOR_ENABLED "apm:residual_echo_detector_enabled"
+#define APM_RESIDUAL_ECHO_DETECTOR_ENABLED_VALUE 1
-#define APM_ANALOG_GAIN_CONTROLLER_ENABLED "apm:analog_gain_controller_enabled"
-#define APM_ANALOG_GAIN_CONTROLLER_ENABLED_VALUE 1
-#define APM_ANALOG_GAIN_CONTROLLER_STARTUP_MIN_VOLUME \
- "apm:analog_gain_controller_startup_min_volume"
-#define APM_ANALOG_GAIN_CONTROLLER_STARTUP_MIN_VOLUME_VALUE 0
-#define APM_ANALOG_GAIN_CONTROLLER_CLIPPED_LEVEL_MIN \
- "apm:analog_gain_controller_clipped_level_min"
-#define APM_ANALOG_GAIN_CONTROLLER_CLIPPED_LEVEL_MIN_VALUE 70
-#define APM_ANALOG_GAIN_CONTROLLER_ENABLE_AGC2_LEVEL_ESTIMATOR \
- "apm:analog_gain_controller_enable_agc2_level_estimator"
-#define APM_ANALOG_GAIN_CONTROLLER_ENABLE_AGC2_LEVEL_ESTIMATOR_VALUE 0
-#define APM_ANALOG_GAIN_CONTROLLER_ENABLE_DIGITAL_ADAPTIVE \
- "apm:analog_gain_controller_enable_digital_adaptive"
-#define APM_ANALOG_GAIN_CONTROLLER_ENABLE_DIGITAL_ADAPTIVE_VALUE 1
+/* Keys for AudioProcessing::LevelEstimation */
+#define APM_LEVEL_ESTIMATION_ENABLED "apm:level_estimation_enabled"
+#define APM_LEVEL_ESTIMATION_ENABLED_VALUE 0
-/* 0: low, 1: moderate, 2: high, 3: very high*/
-#define APM_NOISE_SUPPRESSION_LEVEL "apm:noise_suppression_level"
-#define APM_NOISE_SUPPRESSION_LEVEL_VALUE 1
-#define APM_NOISE_SUPPRESSION_ENABLED "apm:noise_suppression_enabled"
-#define APM_NOISE_SUPPRESSION_ENABLED_VALUE 0
-#define APM_NOISE_SUPPRESSION_ANALYZE_LINEAR_AEC_OUTPUT_WHEN_AVAILABLE \
- "apm:noise_suppression_analyze_linear_aec_output_when_available"
-#define APM_NOISE_SUPPRESSION_ANALYZE_LINEAR_AEC_OUTPUT_WHEN_AVAILABLE_VALUE 0
+/* Sets the default agc config values when no ini file is present. */
+void agc_set_default_config(dictionary *ini,
+ webrtc::EchoCanceller3Config *config);
-/* Gets apm config from given config directory. */
-void apm_config_apply(dictionary *ini, webrtc::AudioProcessing *apm);
+/* Sets the default ns config values when no ini file is present. */
+void ns_set_default_config(dictionary *ini,
+ webrtc::EchoCanceller3Config *config);
+
+/* Sets the config fields from a config. */
+void apm_config_set(dictionary *ini, webrtc::AudioProcessing::Config *config);
/* Prints config content to syslog. */
void apm_config_dump(dictionary *ini);
diff --git a/modules/audio_coding/BUILD.gn b/modules/audio_coding/BUILD.gn
index e440b43..c817905 100644
--- a/modules/audio_coding/BUILD.gn
+++ b/modules/audio_coding/BUILD.gn
@@ -17,7 +17,6 @@
rtc_source_set("audio_coding_module_typedefs") {
visibility += [ "*" ]
sources = [ "include/audio_coding_module_typedefs.h" ]
- deps = [ "../../rtc_base:deprecation" ]
}
rtc_library("audio_coding") {
@@ -52,7 +51,6 @@
"../../common_audio:common_audio_c",
"../../rtc_base:audio_format_to_string",
"../../rtc_base:checks",
- "../../rtc_base:deprecation",
"../../rtc_base:rtc_base_approved",
"../../rtc_base/synchronization:mutex",
"../../system_wrappers",
@@ -368,8 +366,8 @@
"../../rtc_base:rtc_base_approved",
"../../rtc_base:sanitizer",
"../../rtc_base/system:arch",
- "../../rtc_base/system:unused",
]
+ absl_deps = [ "//third_party/abseil-cpp/absl/base:core_headers" ]
}
rtc_source_set("isac_common") {
@@ -831,6 +829,7 @@
}
deps = [
+ "../../api:array_view",
"../../rtc_base:checks",
"../../rtc_base:ignore_wundef",
"../../rtc_base:rtc_base_approved",
@@ -1301,36 +1300,39 @@
]
}
- group("audio_coding_tests") {
- visibility += webrtc_default_visibility
- testonly = true
- public_deps = [
- ":acm_receive_test",
- ":acm_send_test",
- ":audio_codec_speed_tests",
- ":audio_decoder_unittests",
- ":audio_decoder_unittests",
- ":g711_test",
- ":g722_test",
- ":ilbc_test",
- ":isac_api_test",
- ":isac_fix_test",
- ":isac_switch_samprate_test",
- ":isac_test",
- ":neteq_ilbc_quality_test",
- ":neteq_isac_quality_test",
- ":neteq_opus_quality_test",
- ":neteq_pcm16b_quality_test",
- ":neteq_pcmu_quality_test",
- ":neteq_speed_test",
- ":rtp_analyze",
- ":rtp_encode",
- ":rtp_jitter",
- ":rtpcat",
- ":webrtc_opus_fec_test",
- ]
- if (rtc_enable_protobuf) {
- public_deps += [ ":neteq_rtpplay" ]
+ if (!build_with_chromium) {
+ group("audio_coding_tests") {
+ visibility += webrtc_default_visibility
+ testonly = true
+ public_deps = [ # no-presubmit-check TODO(webrtc:8603)
+ ":acm_receive_test",
+ ":acm_send_test",
+ ":audio_codec_speed_tests",
+ ":audio_decoder_unittests",
+ ":audio_decoder_unittests",
+ ":g711_test",
+ ":g722_test",
+ ":ilbc_test",
+ ":isac_api_test",
+ ":isac_fix_test",
+ ":isac_switch_samprate_test",
+ ":isac_test",
+ ":neteq_ilbc_quality_test",
+ ":neteq_isac_quality_test",
+ ":neteq_opus_quality_test",
+ ":neteq_pcm16b_quality_test",
+ ":neteq_pcmu_quality_test",
+ ":neteq_speed_test",
+ ":rtp_analyze",
+ ":rtp_encode",
+ ":rtp_jitter",
+ ":rtpcat",
+ ":webrtc_opus_fec_test",
+ ]
+ if (rtc_enable_protobuf) {
+ public_deps += # no-presubmit-check TODO(webrtc:8603)
+ [ ":neteq_rtpplay" ]
+ }
}
}
@@ -1454,7 +1456,6 @@
defines = audio_coding_defines
deps = audio_coding_deps + [
- "//third_party/abseil-cpp/absl/strings",
"../../api/audio:audio_frame_api",
"../../rtc_base:checks",
":audio_coding",
@@ -1466,49 +1467,53 @@
"../../test:test_support",
"//testing/gtest",
]
+
+ absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
}
- audio_decoder_unittests_resources =
- [ "../../resources/audio_coding/testfile32kHz.pcm" ]
+ if (!build_with_chromium) {
+ audio_decoder_unittests_resources =
+ [ "../../resources/audio_coding/testfile32kHz.pcm" ]
- if (is_ios) {
- bundle_data("audio_decoder_unittests_bundle_data") {
- testonly = true
- sources = audio_decoder_unittests_resources
- outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ]
- }
- }
-
- rtc_test("audio_decoder_unittests") {
- testonly = true
- sources = [ "neteq/audio_decoder_unittest.cc" ]
-
- defines = neteq_defines
-
- deps = [
- ":ilbc",
- ":isac",
- ":isac_fix",
- ":neteq",
- ":neteq_tools",
- "../../test:fileutils",
- "../../api/audio_codecs:audio_codecs_api",
- "../../api/audio_codecs/opus:audio_encoder_opus",
- "../../common_audio",
- "../../rtc_base/system:arch",
- "../../test:test_main",
- "//testing/gtest",
- "../../test:test_support",
- ] + audio_coding_deps
-
- data = audio_decoder_unittests_resources
-
- if (is_android) {
- deps += [ "//testing/android/native_test:native_test_native_code" ]
- shard_timeout = 900
- }
if (is_ios) {
- deps += [ ":audio_decoder_unittests_bundle_data" ]
+ bundle_data("audio_decoder_unittests_bundle_data") {
+ testonly = true
+ sources = audio_decoder_unittests_resources
+ outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ]
+ }
+ }
+
+ rtc_test("audio_decoder_unittests") {
+ testonly = true
+ sources = [ "neteq/audio_decoder_unittest.cc" ]
+
+ defines = neteq_defines
+
+ deps = [
+ ":ilbc",
+ ":isac",
+ ":isac_fix",
+ ":neteq",
+ ":neteq_tools",
+ "../../test:fileutils",
+ "../../api/audio_codecs:audio_codecs_api",
+ "../../api/audio_codecs/opus:audio_encoder_opus",
+ "../../common_audio",
+ "../../rtc_base/system:arch",
+ "../../test:test_main",
+ "//testing/gtest",
+ "../../test:test_support",
+ ] + audio_coding_deps
+
+ data = audio_decoder_unittests_resources
+
+ if (is_android) {
+ deps += [ "//testing/android/native_test:native_test_native_code" ]
+ shard_timeout = 900
+ }
+ if (is_ios) {
+ deps += [ ":audio_decoder_unittests_bundle_data" ]
+ }
}
}
@@ -1538,7 +1543,9 @@
"../../test:test_support",
]
}
+ }
+ if (rtc_enable_protobuf && !build_with_chromium) {
rtc_executable("neteq_rtpplay") {
testonly = true
visibility += [ "*" ]
@@ -1559,51 +1566,53 @@
}
}
- audio_codec_speed_tests_resources = [
- "//resources/audio_coding/music_stereo_48kHz.pcm",
- "//resources/audio_coding/speech_mono_16kHz.pcm",
- "//resources/audio_coding/speech_mono_32_48kHz.pcm",
- ]
-
- if (is_ios) {
- bundle_data("audio_codec_speed_tests_data") {
- testonly = true
- sources = audio_codec_speed_tests_resources
- outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ]
- }
- }
-
- rtc_test("audio_codec_speed_tests") {
- testonly = true
- defines = []
- deps = [ "../../test:fileutils" ]
- sources = [
- "codecs/isac/fix/test/isac_speed_test.cc",
- "codecs/opus/opus_speed_test.cc",
- "codecs/tools/audio_codec_speed_test.cc",
- "codecs/tools/audio_codec_speed_test.h",
+ if (!build_with_chromium) {
+ audio_codec_speed_tests_resources = [
+ "//resources/audio_coding/music_stereo_48kHz.pcm",
+ "//resources/audio_coding/speech_mono_16kHz.pcm",
+ "//resources/audio_coding/speech_mono_32_48kHz.pcm",
]
- data = audio_codec_speed_tests_resources
-
- if (is_android) {
- deps += [ "//testing/android/native_test:native_test_native_code" ]
- shard_timeout = 900
- }
-
if (is_ios) {
- deps += [ ":audio_codec_speed_tests_data" ]
+ bundle_data("audio_codec_speed_tests_data") {
+ testonly = true
+ sources = audio_codec_speed_tests_resources
+ outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ]
+ }
}
- deps += [
- ":isac_fix",
- ":webrtc_opus",
- "../../rtc_base:rtc_base_approved",
- "../../test:test_main",
- "../../test:test_support",
- "../audio_processing",
- "//testing/gtest",
- ]
+ rtc_test("audio_codec_speed_tests") {
+ testonly = true
+ defines = []
+ deps = [ "../../test:fileutils" ]
+ sources = [
+ "codecs/isac/fix/test/isac_speed_test.cc",
+ "codecs/opus/opus_speed_test.cc",
+ "codecs/tools/audio_codec_speed_test.cc",
+ "codecs/tools/audio_codec_speed_test.h",
+ ]
+
+ data = audio_codec_speed_tests_resources
+
+ if (is_android) {
+ deps += [ "//testing/android/native_test:native_test_native_code" ]
+ shard_timeout = 900
+ }
+
+ if (is_ios) {
+ deps += [ ":audio_codec_speed_tests_data" ]
+ }
+
+ deps += [
+ ":isac_fix",
+ ":webrtc_opus",
+ "../../rtc_base:rtc_base_approved",
+ "../../test:test_main",
+ "../../test:test_support",
+ "../audio_processing",
+ "//testing/gtest",
+ ]
+ }
}
rtc_library("neteq_test_support") {
@@ -1631,210 +1640,212 @@
]
}
- rtc_library("neteq_quality_test_support") {
- testonly = true
- sources = [
- "neteq/tools/neteq_quality_test.cc",
- "neteq/tools/neteq_quality_test.h",
- ]
+ if (!build_with_chromium) {
+ rtc_library("neteq_quality_test_support") {
+ testonly = true
+ sources = [
+ "neteq/tools/neteq_quality_test.cc",
+ "neteq/tools/neteq_quality_test.h",
+ ]
- deps = [
- ":default_neteq_factory",
- ":neteq",
- ":neteq_test_tools",
- "../../api/audio_codecs:builtin_audio_decoder_factory",
- "../../api/neteq:neteq_api",
- "../../rtc_base:checks",
- "../../system_wrappers",
- "../../test:fileutils",
- "../../test:test_support",
- "//testing/gtest",
- ]
- absl_deps = [ "//third_party/abseil-cpp/absl/flags:flag" ]
- }
+ deps = [
+ ":default_neteq_factory",
+ ":neteq",
+ ":neteq_test_tools",
+ "../../api/audio_codecs:builtin_audio_decoder_factory",
+ "../../api/neteq:neteq_api",
+ "../../rtc_base:checks",
+ "../../system_wrappers",
+ "../../test:fileutils",
+ "../../test:test_support",
+ "//testing/gtest",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/flags:flag" ]
+ }
- rtc_executable("rtp_encode") {
- testonly = true
+ rtc_executable("rtp_encode") {
+ testonly = true
- deps = audio_coding_deps + [
- "//third_party/abseil-cpp/absl/flags:flag",
- "//third_party/abseil-cpp/absl/flags:parse",
- ":audio_coding",
- ":audio_encoder_cng",
- ":neteq_input_audio_tools",
- "../../api/audio:audio_frame_api",
- "../../api/audio_codecs/g711:audio_encoder_g711",
- "../../api/audio_codecs/L16:audio_encoder_L16",
- "../../api/audio_codecs/g722:audio_encoder_g722",
- "../../api/audio_codecs/ilbc:audio_encoder_ilbc",
- "../../api/audio_codecs/isac:audio_encoder_isac",
- "../../api/audio_codecs/opus:audio_encoder_opus",
- "../../rtc_base:safe_conversions",
- "//third_party/abseil-cpp/absl/memory",
- ]
+ deps = audio_coding_deps + [
+ ":audio_coding",
+ ":audio_encoder_cng",
+ ":neteq_input_audio_tools",
+ "../../api/audio:audio_frame_api",
+ "../../api/audio_codecs/g711:audio_encoder_g711",
+ "../../api/audio_codecs/L16:audio_encoder_L16",
+ "../../api/audio_codecs/g722:audio_encoder_g722",
+ "../../api/audio_codecs/ilbc:audio_encoder_ilbc",
+ "../../api/audio_codecs/isac:audio_encoder_isac",
+ "../../api/audio_codecs/opus:audio_encoder_opus",
+ "../../rtc_base:safe_conversions",
+ "//third_party/abseil-cpp/absl/flags:flag",
+ "//third_party/abseil-cpp/absl/flags:parse",
+ "//third_party/abseil-cpp/absl/memory",
+ ]
- sources = [ "neteq/tools/rtp_encode.cc" ]
+ sources = [ "neteq/tools/rtp_encode.cc" ]
- defines = audio_coding_defines
- }
+ defines = audio_coding_defines
+ }
- rtc_executable("rtp_jitter") {
- testonly = true
+ rtc_executable("rtp_jitter") {
+ testonly = true
- deps = audio_coding_deps + [
- "../rtp_rtcp:rtp_rtcp_format",
- "../../api:array_view",
- "../../rtc_base:rtc_base_approved",
- ]
+ deps = audio_coding_deps + [
+ "../rtp_rtcp:rtp_rtcp_format",
+ "../../api:array_view",
+ "../../rtc_base:rtc_base_approved",
+ ]
- sources = [ "neteq/tools/rtp_jitter.cc" ]
+ sources = [ "neteq/tools/rtp_jitter.cc" ]
- defines = audio_coding_defines
- }
+ defines = audio_coding_defines
+ }
- rtc_executable("rtpcat") {
- testonly = true
+ rtc_executable("rtpcat") {
+ testonly = true
- sources = [ "neteq/tools/rtpcat.cc" ]
+ sources = [ "neteq/tools/rtpcat.cc" ]
- deps = [
- "../../rtc_base:checks",
- "../../rtc_base:rtc_base_approved",
- "../../test:rtp_test_utils",
- "//testing/gtest",
- ]
- }
+ deps = [
+ "../../rtc_base:checks",
+ "../../rtc_base:rtc_base_approved",
+ "../../test:rtp_test_utils",
+ "//testing/gtest",
+ ]
+ }
- rtc_executable("rtp_analyze") {
- testonly = true
+ rtc_executable("rtp_analyze") {
+ testonly = true
- sources = [ "neteq/tools/rtp_analyze.cc" ]
+ sources = [ "neteq/tools/rtp_analyze.cc" ]
- deps = [
- ":neteq",
- ":neteq_test_tools",
- ":pcm16b",
- "//testing/gtest",
- "//third_party/abseil-cpp/absl/flags:flag",
- "//third_party/abseil-cpp/absl/flags:parse",
- ]
- }
+ deps = [
+ ":neteq",
+ ":neteq_test_tools",
+ ":pcm16b",
+ "//testing/gtest",
+ "//third_party/abseil-cpp/absl/flags:flag",
+ "//third_party/abseil-cpp/absl/flags:parse",
+ ]
+ }
- rtc_executable("neteq_opus_quality_test") {
- testonly = true
+ rtc_executable("neteq_opus_quality_test") {
+ testonly = true
- sources = [ "neteq/test/neteq_opus_quality_test.cc" ]
+ sources = [ "neteq/test/neteq_opus_quality_test.cc" ]
- deps = [
- ":neteq",
- ":neteq_quality_test_support",
- ":neteq_tools",
- ":webrtc_opus",
- "../../rtc_base:rtc_base_approved",
- "../../test:test_main",
- "//testing/gtest",
- "//third_party/abseil-cpp/absl/flags:flag",
- ]
- }
+ deps = [
+ ":neteq",
+ ":neteq_quality_test_support",
+ ":neteq_tools",
+ ":webrtc_opus",
+ "../../rtc_base:rtc_base_approved",
+ "../../test:test_main",
+ "//testing/gtest",
+ "//third_party/abseil-cpp/absl/flags:flag",
+ ]
+ }
- rtc_executable("neteq_speed_test") {
- testonly = true
+ rtc_executable("neteq_speed_test") {
+ testonly = true
- sources = [ "neteq/test/neteq_speed_test.cc" ]
+ sources = [ "neteq/test/neteq_speed_test.cc" ]
- deps = [
- ":neteq",
- ":neteq_test_support",
- "../../rtc_base:checks",
- "../../test:test_support",
- "//third_party/abseil-cpp/absl/flags:flag",
- "//third_party/abseil-cpp/absl/flags:parse",
- ]
- }
+ deps = [
+ ":neteq",
+ ":neteq_test_support",
+ "../../rtc_base:checks",
+ "../../test:test_support",
+ "//third_party/abseil-cpp/absl/flags:flag",
+ "//third_party/abseil-cpp/absl/flags:parse",
+ ]
+ }
- rtc_executable("neteq_ilbc_quality_test") {
- testonly = true
+ rtc_executable("neteq_ilbc_quality_test") {
+ testonly = true
- sources = [ "neteq/test/neteq_ilbc_quality_test.cc" ]
+ sources = [ "neteq/test/neteq_ilbc_quality_test.cc" ]
- deps = [
- ":ilbc",
- ":neteq",
- ":neteq_quality_test_support",
- ":neteq_tools",
- "../../rtc_base:checks",
- "../../rtc_base:rtc_base_approved",
- "../../test:fileutils",
- "../../test:test_main",
- "//testing/gtest",
- "//third_party/abseil-cpp/absl/flags:flag",
- ]
- }
+ deps = [
+ ":ilbc",
+ ":neteq",
+ ":neteq_quality_test_support",
+ ":neteq_tools",
+ "../../rtc_base:checks",
+ "../../rtc_base:rtc_base_approved",
+ "../../test:fileutils",
+ "../../test:test_main",
+ "//testing/gtest",
+ "//third_party/abseil-cpp/absl/flags:flag",
+ ]
+ }
- rtc_executable("neteq_isac_quality_test") {
- testonly = true
+ rtc_executable("neteq_isac_quality_test") {
+ testonly = true
- sources = [ "neteq/test/neteq_isac_quality_test.cc" ]
+ sources = [ "neteq/test/neteq_isac_quality_test.cc" ]
- deps = [
- ":isac_fix",
- ":neteq",
- ":neteq_quality_test_support",
- "../../rtc_base:rtc_base_approved",
- "../../test:test_main",
- "//testing/gtest",
- "//third_party/abseil-cpp/absl/flags:flag",
- ]
- }
+ deps = [
+ ":isac_fix",
+ ":neteq",
+ ":neteq_quality_test_support",
+ "../../rtc_base:rtc_base_approved",
+ "../../test:test_main",
+ "//testing/gtest",
+ "//third_party/abseil-cpp/absl/flags:flag",
+ ]
+ }
- rtc_executable("neteq_pcmu_quality_test") {
- testonly = true
+ rtc_executable("neteq_pcmu_quality_test") {
+ testonly = true
- sources = [ "neteq/test/neteq_pcmu_quality_test.cc" ]
+ sources = [ "neteq/test/neteq_pcmu_quality_test.cc" ]
- deps = [
- ":g711",
- ":neteq",
- ":neteq_quality_test_support",
- "../../rtc_base:checks",
- "../../rtc_base:rtc_base_approved",
- "../../test:fileutils",
- "../../test:test_main",
- "//testing/gtest",
- "//third_party/abseil-cpp/absl/flags:flag",
- ]
- }
+ deps = [
+ ":g711",
+ ":neteq",
+ ":neteq_quality_test_support",
+ "../../rtc_base:checks",
+ "../../rtc_base:rtc_base_approved",
+ "../../test:fileutils",
+ "../../test:test_main",
+ "//testing/gtest",
+ "//third_party/abseil-cpp/absl/flags:flag",
+ ]
+ }
- rtc_executable("neteq_pcm16b_quality_test") {
- testonly = true
+ rtc_executable("neteq_pcm16b_quality_test") {
+ testonly = true
- sources = [ "neteq/test/neteq_pcm16b_quality_test.cc" ]
+ sources = [ "neteq/test/neteq_pcm16b_quality_test.cc" ]
- deps = [
- ":neteq",
- ":neteq_quality_test_support",
- ":pcm16b",
- "../../rtc_base:checks",
- "../../rtc_base:rtc_base_approved",
- "../../test:fileutils",
- "../../test:test_main",
- "//testing/gtest",
- "//third_party/abseil-cpp/absl/flags:flag",
- ]
- }
+ deps = [
+ ":neteq",
+ ":neteq_quality_test_support",
+ ":pcm16b",
+ "../../rtc_base:checks",
+ "../../rtc_base:rtc_base_approved",
+ "../../test:fileutils",
+ "../../test:test_main",
+ "//testing/gtest",
+ "//third_party/abseil-cpp/absl/flags:flag",
+ ]
+ }
- rtc_executable("isac_fix_test") {
- testonly = true
+ rtc_executable("isac_fix_test") {
+ testonly = true
- sources = [ "codecs/isac/fix/test/kenny.cc" ]
+ sources = [ "codecs/isac/fix/test/kenny.cc" ]
- deps = [
- ":isac_fix",
- "../../test:perf_test",
- "../../test:test_support",
- ]
+ deps = [
+ ":isac_fix",
+ "../../test:perf_test",
+ "../../test:test_support",
+ ]
- data = [ "../../resources/speech_and_misc_wb.pcm" ]
+ data = [ "../../resources/speech_and_misc_wb.pcm" ]
+ }
}
rtc_library("isac_test_util") {
@@ -1845,16 +1856,18 @@
]
}
- rtc_executable("isac_test") {
- testonly = true
+ if (!build_with_chromium) {
+ rtc_executable("isac_test") {
+ testonly = true
- sources = [ "codecs/isac/main/test/simpleKenny.c" ]
+ sources = [ "codecs/isac/main/test/simpleKenny.c" ]
- deps = [
- ":isac",
- ":isac_test_util",
- "../../rtc_base:rtc_base_approved",
- ]
+ deps = [
+ ":isac",
+ ":isac_test_util",
+ "../../rtc_base:rtc_base_approved",
+ ]
+ }
}
rtc_executable("g711_test") {
@@ -1873,226 +1886,229 @@
deps = [ ":g722" ]
}
- rtc_executable("isac_api_test") {
- testonly = true
+ if (!build_with_chromium) {
+ rtc_executable("isac_api_test") {
+ testonly = true
- sources = [ "codecs/isac/main/test/ReleaseTest-API/ReleaseTest-API.cc" ]
+ sources = [ "codecs/isac/main/test/ReleaseTest-API/ReleaseTest-API.cc" ]
- deps = [
- ":isac",
- ":isac_test_util",
- "../../rtc_base:rtc_base_approved",
- ]
- }
-
- rtc_executable("isac_switch_samprate_test") {
- testonly = true
-
- sources = [ "codecs/isac/main/test/SwitchingSampRate/SwitchingSampRate.cc" ]
-
- deps = [
- ":isac",
- ":isac_test_util",
- "../../common_audio",
- "../../common_audio:common_audio_c",
- ]
- }
-
- rtc_executable("ilbc_test") {
- testonly = true
-
- sources = [ "codecs/ilbc/test/iLBC_test.c" ]
-
- deps = [ ":ilbc" ]
- }
-
- rtc_executable("webrtc_opus_fec_test") {
- testonly = true
-
- sources = [ "codecs/opus/opus_fec_test.cc" ]
-
- deps = [
- ":webrtc_opus",
- "../../common_audio",
- "../../rtc_base:rtc_base_approved",
- "../../test:fileutils",
- "../../test:test_main",
- "../../test:test_support",
- "//testing/gtest",
- ]
- }
-
- rtc_library("audio_coding_unittests") {
- testonly = true
- visibility += webrtc_default_visibility
-
- sources = [
- "acm2/acm_receiver_unittest.cc",
- "acm2/acm_remixing_unittest.cc",
- "acm2/audio_coding_module_unittest.cc",
- "acm2/call_statistics_unittest.cc",
- "audio_network_adaptor/audio_network_adaptor_impl_unittest.cc",
- "audio_network_adaptor/bitrate_controller_unittest.cc",
- "audio_network_adaptor/channel_controller_unittest.cc",
- "audio_network_adaptor/controller_manager_unittest.cc",
- "audio_network_adaptor/dtx_controller_unittest.cc",
- "audio_network_adaptor/event_log_writer_unittest.cc",
- "audio_network_adaptor/fec_controller_plr_based_unittest.cc",
- "audio_network_adaptor/frame_length_controller_unittest.cc",
- "audio_network_adaptor/frame_length_controller_v2_unittest.cc",
- "audio_network_adaptor/util/threshold_curve_unittest.cc",
- "codecs/builtin_audio_decoder_factory_unittest.cc",
- "codecs/builtin_audio_encoder_factory_unittest.cc",
- "codecs/cng/audio_encoder_cng_unittest.cc",
- "codecs/cng/cng_unittest.cc",
- "codecs/ilbc/ilbc_unittest.cc",
- "codecs/isac/fix/source/filterbanks_unittest.cc",
- "codecs/isac/fix/source/filters_unittest.cc",
- "codecs/isac/fix/source/lpc_masking_model_unittest.cc",
- "codecs/isac/fix/source/transform_unittest.cc",
- "codecs/isac/isac_webrtc_api_test.cc",
- "codecs/isac/main/source/audio_encoder_isac_unittest.cc",
- "codecs/isac/main/source/isac_unittest.cc",
- "codecs/legacy_encoded_audio_frame_unittest.cc",
- "codecs/opus/audio_decoder_multi_channel_opus_unittest.cc",
- "codecs/opus/audio_encoder_multi_channel_opus_unittest.cc",
- "codecs/opus/audio_encoder_opus_unittest.cc",
- "codecs/opus/opus_bandwidth_unittest.cc",
- "codecs/opus/opus_unittest.cc",
- "codecs/red/audio_encoder_copy_red_unittest.cc",
- "neteq/audio_multi_vector_unittest.cc",
- "neteq/audio_vector_unittest.cc",
- "neteq/background_noise_unittest.cc",
- "neteq/buffer_level_filter_unittest.cc",
- "neteq/comfort_noise_unittest.cc",
- "neteq/decision_logic_unittest.cc",
- "neteq/decoder_database_unittest.cc",
- "neteq/delay_manager_unittest.cc",
- "neteq/dsp_helper_unittest.cc",
- "neteq/dtmf_buffer_unittest.cc",
- "neteq/dtmf_tone_generator_unittest.cc",
- "neteq/expand_unittest.cc",
- "neteq/histogram_unittest.cc",
- "neteq/merge_unittest.cc",
- "neteq/mock/mock_buffer_level_filter.h",
- "neteq/mock/mock_decoder_database.h",
- "neteq/mock/mock_delay_manager.h",
- "neteq/mock/mock_dtmf_buffer.h",
- "neteq/mock/mock_dtmf_tone_generator.h",
- "neteq/mock/mock_expand.h",
- "neteq/mock/mock_histogram.h",
- "neteq/mock/mock_neteq_controller.h",
- "neteq/mock/mock_packet_buffer.h",
- "neteq/mock/mock_red_payload_splitter.h",
- "neteq/mock/mock_statistics_calculator.h",
- "neteq/nack_tracker_unittest.cc",
- "neteq/neteq_decoder_plc_unittest.cc",
- "neteq/neteq_impl_unittest.cc",
- "neteq/neteq_network_stats_unittest.cc",
- "neteq/neteq_stereo_unittest.cc",
- "neteq/neteq_unittest.cc",
- "neteq/normal_unittest.cc",
- "neteq/packet_buffer_unittest.cc",
- "neteq/post_decode_vad_unittest.cc",
- "neteq/random_vector_unittest.cc",
- "neteq/red_payload_splitter_unittest.cc",
- "neteq/statistics_calculator_unittest.cc",
- "neteq/sync_buffer_unittest.cc",
- "neteq/time_stretch_unittest.cc",
- "neteq/timestamp_scaler_unittest.cc",
- "neteq/tools/input_audio_file_unittest.cc",
- "neteq/tools/packet_unittest.cc",
- ]
-
- deps = [
- ":acm_receive_test",
- ":acm_send_test",
- ":audio_coding",
- ":audio_coding_module_typedefs",
- ":audio_coding_modules_tests_shared",
- ":audio_coding_opus_common",
- ":audio_encoder_cng",
- ":audio_network_adaptor",
- ":default_neteq_factory",
- ":g711",
- ":ilbc",
- ":isac",
- ":isac_c",
- ":isac_common",
- ":isac_fix",
- ":legacy_encoded_audio_frame",
- ":mocks",
- ":neteq",
- ":neteq_test_support",
- ":neteq_test_tools",
- ":pcm16b",
- ":red",
- ":webrtc_cng",
- ":webrtc_opus",
- "..:module_api",
- "..:module_api_public",
- "../../api:array_view",
- "../../api/audio:audio_frame_api",
- "../../api/audio_codecs:audio_codecs_api",
- "../../api/audio_codecs:builtin_audio_decoder_factory",
- "../../api/audio_codecs:builtin_audio_encoder_factory",
- "../../api/audio_codecs/isac:audio_decoder_isac_fix",
- "../../api/audio_codecs/isac:audio_decoder_isac_float",
- "../../api/audio_codecs/isac:audio_encoder_isac_fix",
- "../../api/audio_codecs/isac:audio_encoder_isac_float",
- "../../api/audio_codecs/opus:audio_decoder_multiopus",
- "../../api/audio_codecs/opus:audio_decoder_opus",
- "../../api/audio_codecs/opus:audio_encoder_multiopus",
- "../../api/audio_codecs/opus:audio_encoder_opus",
- "../../api/neteq:default_neteq_controller_factory",
- "../../api/neteq:neteq_api",
- "../../api/neteq:neteq_controller_api",
- "../../api/neteq:tick_timer",
- "../../api/neteq:tick_timer_unittest",
- "../../api/rtc_event_log",
- "../../common_audio",
- "../../common_audio:common_audio_c",
- "../../common_audio:mock_common_audio",
- "../../logging:mocks",
- "../../logging:rtc_event_audio",
- "../../modules/rtp_rtcp:rtp_rtcp_format",
- "../../rtc_base",
- "../../rtc_base:checks",
- "../../rtc_base:ignore_wundef",
- "../../rtc_base:rtc_base_approved",
- "../../rtc_base:rtc_base_tests_utils",
- "../../rtc_base:sanitizer",
- "../../rtc_base:timeutils",
- "../../rtc_base/synchronization:mutex",
- "../../rtc_base/system:arch",
- "../../system_wrappers",
- "../../test:audio_codec_mocks",
- "../../test:field_trial",
- "../../test:fileutils",
- "../../test:rtc_expect_death",
- "../../test:rtp_test_utils",
- "../../test:test_common",
- "../../test:test_support",
- "codecs/opus/test",
- "codecs/opus/test:test_unittest",
- "//testing/gtest",
- ]
- absl_deps = [
- "//third_party/abseil-cpp/absl/flags:flag",
- "//third_party/abseil-cpp/absl/memory",
- "//third_party/abseil-cpp/absl/strings",
- "//third_party/abseil-cpp/absl/types:optional",
- ]
-
- defines = audio_coding_defines
-
- if (rtc_enable_protobuf) {
- defines += [ "WEBRTC_NETEQ_UNITTEST_BITEXACT" ]
- deps += [
- ":ana_config_proto",
- ":neteq_unittest_proto",
+ deps = [
+ ":isac",
+ ":isac_test_util",
+ "../../rtc_base:rtc_base_approved",
]
}
+
+ rtc_executable("isac_switch_samprate_test") {
+ testonly = true
+
+ sources =
+ [ "codecs/isac/main/test/SwitchingSampRate/SwitchingSampRate.cc" ]
+
+ deps = [
+ ":isac",
+ ":isac_test_util",
+ "../../common_audio",
+ "../../common_audio:common_audio_c",
+ ]
+ }
+
+ rtc_executable("ilbc_test") {
+ testonly = true
+
+ sources = [ "codecs/ilbc/test/iLBC_test.c" ]
+
+ deps = [ ":ilbc" ]
+ }
+
+ rtc_executable("webrtc_opus_fec_test") {
+ testonly = true
+
+ sources = [ "codecs/opus/opus_fec_test.cc" ]
+
+ deps = [
+ ":webrtc_opus",
+ "../../common_audio",
+ "../../rtc_base:rtc_base_approved",
+ "../../test:fileutils",
+ "../../test:test_main",
+ "../../test:test_support",
+ "//testing/gtest",
+ ]
+ }
+
+ rtc_library("audio_coding_unittests") {
+ testonly = true
+ visibility += webrtc_default_visibility
+
+ sources = [
+ "acm2/acm_receiver_unittest.cc",
+ "acm2/acm_remixing_unittest.cc",
+ "acm2/audio_coding_module_unittest.cc",
+ "acm2/call_statistics_unittest.cc",
+ "audio_network_adaptor/audio_network_adaptor_impl_unittest.cc",
+ "audio_network_adaptor/bitrate_controller_unittest.cc",
+ "audio_network_adaptor/channel_controller_unittest.cc",
+ "audio_network_adaptor/controller_manager_unittest.cc",
+ "audio_network_adaptor/dtx_controller_unittest.cc",
+ "audio_network_adaptor/event_log_writer_unittest.cc",
+ "audio_network_adaptor/fec_controller_plr_based_unittest.cc",
+ "audio_network_adaptor/frame_length_controller_unittest.cc",
+ "audio_network_adaptor/frame_length_controller_v2_unittest.cc",
+ "audio_network_adaptor/util/threshold_curve_unittest.cc",
+ "codecs/builtin_audio_decoder_factory_unittest.cc",
+ "codecs/builtin_audio_encoder_factory_unittest.cc",
+ "codecs/cng/audio_encoder_cng_unittest.cc",
+ "codecs/cng/cng_unittest.cc",
+ "codecs/ilbc/ilbc_unittest.cc",
+ "codecs/isac/fix/source/filterbanks_unittest.cc",
+ "codecs/isac/fix/source/filters_unittest.cc",
+ "codecs/isac/fix/source/lpc_masking_model_unittest.cc",
+ "codecs/isac/fix/source/transform_unittest.cc",
+ "codecs/isac/isac_webrtc_api_test.cc",
+ "codecs/isac/main/source/audio_encoder_isac_unittest.cc",
+ "codecs/isac/main/source/isac_unittest.cc",
+ "codecs/legacy_encoded_audio_frame_unittest.cc",
+ "codecs/opus/audio_decoder_multi_channel_opus_unittest.cc",
+ "codecs/opus/audio_encoder_multi_channel_opus_unittest.cc",
+ "codecs/opus/audio_encoder_opus_unittest.cc",
+ "codecs/opus/opus_bandwidth_unittest.cc",
+ "codecs/opus/opus_unittest.cc",
+ "codecs/red/audio_encoder_copy_red_unittest.cc",
+ "neteq/audio_multi_vector_unittest.cc",
+ "neteq/audio_vector_unittest.cc",
+ "neteq/background_noise_unittest.cc",
+ "neteq/buffer_level_filter_unittest.cc",
+ "neteq/comfort_noise_unittest.cc",
+ "neteq/decision_logic_unittest.cc",
+ "neteq/decoder_database_unittest.cc",
+ "neteq/delay_manager_unittest.cc",
+ "neteq/dsp_helper_unittest.cc",
+ "neteq/dtmf_buffer_unittest.cc",
+ "neteq/dtmf_tone_generator_unittest.cc",
+ "neteq/expand_unittest.cc",
+ "neteq/histogram_unittest.cc",
+ "neteq/merge_unittest.cc",
+ "neteq/mock/mock_buffer_level_filter.h",
+ "neteq/mock/mock_decoder_database.h",
+ "neteq/mock/mock_delay_manager.h",
+ "neteq/mock/mock_dtmf_buffer.h",
+ "neteq/mock/mock_dtmf_tone_generator.h",
+ "neteq/mock/mock_expand.h",
+ "neteq/mock/mock_histogram.h",
+ "neteq/mock/mock_neteq_controller.h",
+ "neteq/mock/mock_packet_buffer.h",
+ "neteq/mock/mock_red_payload_splitter.h",
+ "neteq/mock/mock_statistics_calculator.h",
+ "neteq/nack_tracker_unittest.cc",
+ "neteq/neteq_decoder_plc_unittest.cc",
+ "neteq/neteq_impl_unittest.cc",
+ "neteq/neteq_network_stats_unittest.cc",
+ "neteq/neteq_stereo_unittest.cc",
+ "neteq/neteq_unittest.cc",
+ "neteq/normal_unittest.cc",
+ "neteq/packet_buffer_unittest.cc",
+ "neteq/post_decode_vad_unittest.cc",
+ "neteq/random_vector_unittest.cc",
+ "neteq/red_payload_splitter_unittest.cc",
+ "neteq/statistics_calculator_unittest.cc",
+ "neteq/sync_buffer_unittest.cc",
+ "neteq/time_stretch_unittest.cc",
+ "neteq/timestamp_scaler_unittest.cc",
+ "neteq/tools/input_audio_file_unittest.cc",
+ "neteq/tools/packet_unittest.cc",
+ ]
+
+ deps = [
+ ":acm_receive_test",
+ ":acm_send_test",
+ ":audio_coding",
+ ":audio_coding_module_typedefs",
+ ":audio_coding_modules_tests_shared",
+ ":audio_coding_opus_common",
+ ":audio_encoder_cng",
+ ":audio_network_adaptor",
+ ":default_neteq_factory",
+ ":g711",
+ ":ilbc",
+ ":isac",
+ ":isac_c",
+ ":isac_common",
+ ":isac_fix",
+ ":legacy_encoded_audio_frame",
+ ":mocks",
+ ":neteq",
+ ":neteq_test_support",
+ ":neteq_test_tools",
+ ":pcm16b",
+ ":red",
+ ":webrtc_cng",
+ ":webrtc_opus",
+ "..:module_api",
+ "..:module_api_public",
+ "../../api:array_view",
+ "../../api/audio:audio_frame_api",
+ "../../api/audio_codecs:audio_codecs_api",
+ "../../api/audio_codecs:builtin_audio_decoder_factory",
+ "../../api/audio_codecs:builtin_audio_encoder_factory",
+ "../../api/audio_codecs/isac:audio_decoder_isac_fix",
+ "../../api/audio_codecs/isac:audio_decoder_isac_float",
+ "../../api/audio_codecs/isac:audio_encoder_isac_fix",
+ "../../api/audio_codecs/isac:audio_encoder_isac_float",
+ "../../api/audio_codecs/opus:audio_decoder_multiopus",
+ "../../api/audio_codecs/opus:audio_decoder_opus",
+ "../../api/audio_codecs/opus:audio_encoder_multiopus",
+ "../../api/audio_codecs/opus:audio_encoder_opus",
+ "../../api/neteq:default_neteq_controller_factory",
+ "../../api/neteq:neteq_api",
+ "../../api/neteq:neteq_controller_api",
+ "../../api/neteq:tick_timer",
+ "../../api/neteq:tick_timer_unittest",
+ "../../api/rtc_event_log",
+ "../../common_audio",
+ "../../common_audio:common_audio_c",
+ "../../common_audio:mock_common_audio",
+ "../../logging:mocks",
+ "../../logging:rtc_event_audio",
+ "../../modules/rtp_rtcp:rtp_rtcp_format",
+ "../../rtc_base",
+ "../../rtc_base:checks",
+ "../../rtc_base:ignore_wundef",
+ "../../rtc_base:rtc_base_approved",
+ "../../rtc_base:rtc_base_tests_utils",
+ "../../rtc_base:sanitizer",
+ "../../rtc_base:timeutils",
+ "../../rtc_base/synchronization:mutex",
+ "../../rtc_base/system:arch",
+ "../../system_wrappers",
+ "../../test:audio_codec_mocks",
+ "../../test:field_trial",
+ "../../test:fileutils",
+ "../../test:rtc_expect_death",
+ "../../test:rtp_test_utils",
+ "../../test:test_common",
+ "../../test:test_support",
+ "codecs/opus/test",
+ "codecs/opus/test:test_unittest",
+ "//testing/gtest",
+ ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/flags:flag",
+ "//third_party/abseil-cpp/absl/memory",
+ "//third_party/abseil-cpp/absl/strings",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
+
+ defines = audio_coding_defines
+
+ if (rtc_enable_protobuf) {
+ defines += [ "WEBRTC_NETEQ_UNITTEST_BITEXACT" ]
+ deps += [
+ ":ana_config_proto",
+ ":neteq_unittest_proto",
+ ]
+ }
+ }
}
}
diff --git a/modules/audio_coding/acm2/audio_coding_module_unittest.cc b/modules/audio_coding/acm2/audio_coding_module_unittest.cc
index 590dc30..28899aa 100644
--- a/modules/audio_coding/acm2/audio_coding_module_unittest.cc
+++ b/modules/audio_coding/acm2/audio_coding_module_unittest.cc
@@ -939,58 +939,58 @@
defined(WEBRTC_CODEC_ILBC)
TEST_F(AcmReceiverBitExactnessOldApi, 8kHzOutput) {
std::string others_checksum_reference =
- GetCPUInfo(kAVX2) != 0 ? "1d7b784031599e2c01a3f575f8439f2f"
- : "c119fda4ea2c119ff2a720fd0c289071";
+ GetCPUInfo(kAVX2) != 0 ? "e0c966d7b8c36ff60167988fa35d33e0"
+ : "7d8f6b84abd1e57ec010a53bc2130652";
std::string win64_checksum_reference =
GetCPUInfo(kAVX2) != 0 ? "405a50f0bcb8827e20aa944299fc59f6"
- : "38e70d4e186f8e1a56b929fafcb7c379";
+ : "0ed5830930f5527a01bbec0ba11f8541";
Run(8000,
PlatformChecksum(others_checksum_reference, win64_checksum_reference,
- "3b03e41731e1cef5ae2b9f9618660b42",
+ "b892ed69c38b21b16c132ec2ce03aa7b",
"4598140b5e4f7ee66c5adad609e65a3e",
- "da7e76687c8c0a9509cd1d57ee1aba3b"));
+ "5fec8d770778ef7969ec98c56d9eb10f"));
}
TEST_F(AcmReceiverBitExactnessOldApi, 16kHzOutput) {
std::string others_checksum_reference =
- GetCPUInfo(kAVX2) != 0 ? "8884d910e443c244d8593c2e3cef5e63"
- : "36dc8c0532ba0efa099e2b6a689cde40";
+ GetCPUInfo(kAVX2) != 0 ? "a63c578e1195c8420f453962c6d8519c"
+ : "6bac83762c1306b932cd25a560155681";
std::string win64_checksum_reference =
GetCPUInfo(kAVX2) != 0 ? "58fd62a5c49ee513f9fa6fe7dbf62c97"
- : "07e4b388168e273fa19da0a167aff782";
+ : "0509cf0672f543efb4b050e8cffefb1d";
Run(16000,
PlatformChecksum(others_checksum_reference, win64_checksum_reference,
- "06b08d14a72f6e7c72840b1cc9ad204d",
+ "3cea9abbeabbdea9a79719941b241af5",
"f2aad418af974a3b1694d5ae5cc2c3c7",
- "1d5f9a93f3975e7e491373b81eb5fd14"));
+ "9d4b92c31c00e321a4cff29ad002d6a2"));
}
TEST_F(AcmReceiverBitExactnessOldApi, 32kHzOutput) {
std::string others_checksum_reference =
- GetCPUInfo(kAVX2) != 0 ? "73f4fe21996c0af495e2c47e3708e519"
- : "c848ce9002d3825056a1eac2a067c0d3";
+ GetCPUInfo(kAVX2) != 0 ? "8775ce387f44dc5ff4a26da295d5ee7c"
+ : "e319222ca47733709f90fdf33c8574db";
std::string win64_checksum_reference =
GetCPUInfo(kAVX2) != 0 ? "04ce6a1dac5ffdd8438d804623d0132f"
- : "0e705f6844c75fd57a84734f7c30af87";
+ : "39a4a7a1c455b35baeffb9fd193d7858";
Run(32000,
PlatformChecksum(others_checksum_reference, win64_checksum_reference,
- "c18e98e5701ec91bba5c026b720d1790",
+ "4df55b3b62bcbf4328786d474ae87f61",
"100869c8dcde51346c2073e52a272d98",
- "e35df943bfa3ca32e86b26bf1e37ed8f"));
+ "ff58d3153d2780a3df6bc2068844cb2d"));
}
TEST_F(AcmReceiverBitExactnessOldApi, 48kHzOutput) {
std::string others_checksum_reference =
- GetCPUInfo(kAVX2) != 0 ? "884243f7e1476931e93eda5de88d1326"
- : "ba0f66d538487bba377e721cfac62d1e";
+ GetCPUInfo(kAVX2) != 0 ? "7a55700b7ca9aa60237db58b33e55606"
+ : "57d1d316c88279f4f3da3511665069a9";
std::string win64_checksum_reference =
GetCPUInfo(kAVX2) != 0 ? "f59833d9b0924f4b0704707dd3589f80"
- : "6a480541fb86faa95c7563b9de08104d";
+ : "74cbe7345e2b6b45c1e455a5d1e921ca";
Run(48000,
PlatformChecksum(others_checksum_reference, win64_checksum_reference,
- "30e617e4b3c9ba1979d1b2e8eba3519b",
+ "f52bc7bf0f499c9da25932fdf176c4ec",
"bd44bf97e7899186532f91235cef444d",
- "90158462a1853b1de50873eebd68dba7"));
+ "364d403dae55d73cd69e6dbd6b723a4d"));
}
TEST_F(AcmReceiverBitExactnessOldApi, 48kHzOutputExternalDecoder) {
@@ -1069,16 +1069,16 @@
rtc::scoped_refptr<rtc::RefCountedObject<ADFactory>> factory(
new rtc::RefCountedObject<ADFactory>);
std::string others_checksum_reference =
- GetCPUInfo(kAVX2) != 0 ? "884243f7e1476931e93eda5de88d1326"
- : "ba0f66d538487bba377e721cfac62d1e";
+ GetCPUInfo(kAVX2) != 0 ? "7a55700b7ca9aa60237db58b33e55606"
+ : "57d1d316c88279f4f3da3511665069a9";
std::string win64_checksum_reference =
GetCPUInfo(kAVX2) != 0 ? "f59833d9b0924f4b0704707dd3589f80"
- : "6a480541fb86faa95c7563b9de08104d";
+ : "74cbe7345e2b6b45c1e455a5d1e921ca";
Run(48000,
PlatformChecksum(others_checksum_reference, win64_checksum_reference,
- "30e617e4b3c9ba1979d1b2e8eba3519b",
+ "f52bc7bf0f499c9da25932fdf176c4ec",
"bd44bf97e7899186532f91235cef444d",
- "90158462a1853b1de50873eebd68dba7"),
+ "364d403dae55d73cd69e6dbd6b723a4d"),
factory, [](AudioCodingModule* acm) {
acm->SetReceiveCodecs({{0, {"MockPCMu", 8000, 1}},
{103, {"ISAC", 16000, 1}},
@@ -1488,13 +1488,17 @@
"7efbfc9f8e3b4b2933ae2d01ab919028");
} // namespace
-TEST_F(AcmSenderBitExactnessOldApi, Opus_stereo_20ms) {
+// TODO(http://bugs.webrtc.org/12518): Enable the test after Opus has been
+// updated.
+TEST_F(AcmSenderBitExactnessOldApi, DISABLED_Opus_stereo_20ms) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("opus", 48000, 2, 120, 960, 960));
Run(audio_checksum, payload_checksum, 50,
test::AcmReceiveTestOldApi::kStereoOutput);
}
-TEST_F(AcmSenderBitExactnessNewApi, MAYBE_OpusFromFormat_stereo_20ms) {
+// TODO(http://bugs.webrtc.org/12518): Enable the test after Opus has been
+// updated.
+TEST_F(AcmSenderBitExactnessNewApi, DISABLED_OpusFromFormat_stereo_20ms) {
const auto config = AudioEncoderOpus::SdpToConfig(
SdpAudioFormat("opus", 48000, 2, {{"stereo", "1"}}));
ASSERT_TRUE(SetUpSender(kTestFileFakeStereo32kHz, 32000));
@@ -1551,7 +1555,9 @@
50, test::AcmReceiveTestOldApi::kQuadOutput, decoder_factory);
}
-TEST_F(AcmSenderBitExactnessNewApi, OpusFromFormat_stereo_20ms_voip) {
+// TODO(http://bugs.webrtc.org/12518): Enable the test after Opus has been
+// updated.
+TEST_F(AcmSenderBitExactnessNewApi, DISABLED_OpusFromFormat_stereo_20ms_voip) {
auto config = AudioEncoderOpus::SdpToConfig(
SdpAudioFormat("opus", 48000, 2, {{"stereo", "1"}}));
// If not set, default will be kAudio in case of stereo.
diff --git a/modules/audio_coding/codecs/ilbc/cb_construct.h b/modules/audio_coding/codecs/ilbc/cb_construct.h
index 0a4a47a..8f7c663 100644
--- a/modules/audio_coding/codecs/ilbc/cb_construct.h
+++ b/modules/audio_coding/codecs/ilbc/cb_construct.h
@@ -23,14 +23,15 @@
#include <stddef.h>
#include <stdint.h>
+#include "absl/base/attributes.h"
#include "modules/audio_coding/codecs/ilbc/defines.h"
-#include "rtc_base/system/unused.h"
/*----------------------------------------------------------------*
* Construct decoded vector from codebook and gains.
*---------------------------------------------------------------*/
// Returns true on success, false on failure.
+ABSL_MUST_USE_RESULT
bool WebRtcIlbcfix_CbConstruct(
int16_t* decvector, /* (o) Decoded vector */
const int16_t* index, /* (i) Codebook indices */
@@ -38,6 +39,6 @@
int16_t* mem, /* (i) Buffer for codevector construction */
size_t lMem, /* (i) Length of buffer */
size_t veclen /* (i) Length of vector */
- ) RTC_WARN_UNUSED_RESULT;
+);
#endif
diff --git a/modules/audio_coding/codecs/ilbc/decode.h b/modules/audio_coding/codecs/ilbc/decode.h
index d73f798..a7d2910 100644
--- a/modules/audio_coding/codecs/ilbc/decode.h
+++ b/modules/audio_coding/codecs/ilbc/decode.h
@@ -21,21 +21,22 @@
#include <stdint.h>
+#include "absl/base/attributes.h"
#include "modules/audio_coding/codecs/ilbc/defines.h"
-#include "rtc_base/system/unused.h"
/*----------------------------------------------------------------*
* main decoder function
*---------------------------------------------------------------*/
// Returns 0 on success, -1 on error.
+ABSL_MUST_USE_RESULT
int WebRtcIlbcfix_DecodeImpl(
int16_t* decblock, /* (o) decoded signal block */
const uint16_t* bytes, /* (i) encoded signal bits */
IlbcDecoder* iLBCdec_inst, /* (i/o) the decoder state
structure */
- int16_t mode /* (i) 0: bad packet, PLC,
- 1: normal */
- ) RTC_WARN_UNUSED_RESULT;
+ int16_t mode /* (i) 0: bad packet, PLC,
+ 1: normal */
+);
#endif
diff --git a/modules/audio_coding/codecs/ilbc/decode_residual.h b/modules/audio_coding/codecs/ilbc/decode_residual.h
index 30eb35f..d079577 100644
--- a/modules/audio_coding/codecs/ilbc/decode_residual.h
+++ b/modules/audio_coding/codecs/ilbc/decode_residual.h
@@ -23,8 +23,8 @@
#include <stddef.h>
#include <stdint.h>
+#include "absl/base/attributes.h"
#include "modules/audio_coding/codecs/ilbc/defines.h"
-#include "rtc_base/system/unused.h"
/*----------------------------------------------------------------*
* frame residual decoder function (subrutine to iLBC_decode)
@@ -32,6 +32,7 @@
// Returns true on success, false on failure. In case of failure, the decoder
// state may be corrupted and needs resetting.
+ABSL_MUST_USE_RESULT
bool WebRtcIlbcfix_DecodeResidual(
IlbcDecoder* iLBCdec_inst, /* (i/o) the decoder state structure */
iLBC_bits* iLBC_encbits, /* (i/o) Encoded bits, which are used
@@ -39,6 +40,6 @@
int16_t* decresidual, /* (o) decoded residual frame */
int16_t* syntdenum /* (i) the decoded synthesis filter
coefficients */
- ) RTC_WARN_UNUSED_RESULT;
+);
#endif
diff --git a/modules/audio_coding/codecs/ilbc/enhancer_interface.c b/modules/audio_coding/codecs/ilbc/enhancer_interface.c
index fb9740e..ca23e19 100644
--- a/modules/audio_coding/codecs/ilbc/enhancer_interface.c
+++ b/modules/audio_coding/codecs/ilbc/enhancer_interface.c
@@ -18,6 +18,7 @@
#include "modules/audio_coding/codecs/ilbc/enhancer_interface.h"
+#include <stdlib.h>
#include <string.h>
#include "modules/audio_coding/codecs/ilbc/constants.h"
@@ -203,11 +204,11 @@
regressor=in+tlag-1;
/* scaling */
- // Note that this is not abs-max, but it doesn't matter since we use only
- // the square of it.
- max16 = regressor[WebRtcSpl_MaxAbsIndexW16(regressor, plc_blockl + 3 - 1)];
-
- const int64_t max_val = plc_blockl * max16 * max16;
+ // Note that this is not abs-max, so we will take the absolute value below.
+ max16 = WebRtcSpl_MaxAbsElementW16(regressor, plc_blockl + 3 - 1);
+ const int16_t max_target =
+ WebRtcSpl_MaxAbsElementW16(target, plc_blockl + 3 - 1);
+ const int64_t max_val = plc_blockl * abs(max16 * max_target);
const int32_t factor = max_val >> 31;
shifts = factor == 0 ? 0 : 31 - WebRtcSpl_NormW32(factor);
diff --git a/modules/audio_coding/codecs/ilbc/get_cd_vec.h b/modules/audio_coding/codecs/ilbc/get_cd_vec.h
index 647b063..99537dd 100644
--- a/modules/audio_coding/codecs/ilbc/get_cd_vec.h
+++ b/modules/audio_coding/codecs/ilbc/get_cd_vec.h
@@ -23,17 +23,18 @@
#include <stddef.h>
#include <stdint.h>
+#include "absl/base/attributes.h"
#include "modules/audio_coding/codecs/ilbc/defines.h"
-#include "rtc_base/system/unused.h"
// Returns true on success, false on failure. In case of failure, the decoder
// state may be corrupted and needs resetting.
+ABSL_MUST_USE_RESULT
bool WebRtcIlbcfix_GetCbVec(
int16_t* cbvec, /* (o) Constructed codebook vector */
int16_t* mem, /* (i) Codebook buffer */
size_t index, /* (i) Codebook index */
size_t lMem, /* (i) Length of codebook buffer */
size_t cbveclen /* (i) Codebook vector length */
- ) RTC_WARN_UNUSED_RESULT;
+);
#endif
diff --git a/modules/audio_coding/codecs/isac/audio_encoder_isac_t_impl.h b/modules/audio_coding/codecs/isac/audio_encoder_isac_t_impl.h
index 0bde3f7..fa84515 100644
--- a/modules/audio_coding/codecs/isac/audio_encoder_isac_t_impl.h
+++ b/modules/audio_coding/codecs/isac/audio_encoder_isac_t_impl.h
@@ -140,6 +140,11 @@
kSufficientEncodeBufferSizeBytes, [&](rtc::ArrayView<uint8_t> encoded) {
int r = T::Encode(isac_state_, audio.data(), encoded.data());
+ if (T::GetErrorCode(isac_state_) == 6450) {
+ // Isac is not able to effectively compress all types of signals. This
+ // is a limitation of the codec that cannot be easily fixed.
+ r = 0;
+ }
RTC_CHECK_GE(r, 0) << "Encode failed (error code "
<< T::GetErrorCode(isac_state_) << ")";
diff --git a/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc b/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc
index 0fe87bc..6412be5 100644
--- a/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc
+++ b/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc
@@ -809,4 +809,90 @@
EXPECT_GT(max_nonspeech_frames, 15);
}
+TEST(AudioEncoderOpusTest, OpusDtxFilteringHighEnergyRefreshPackets) {
+ test::ScopedFieldTrials override_field_trials(
+ "WebRTC-Audio-OpusAvoidNoisePumpingDuringDtx/Enabled/");
+ const std::string kInputFileName =
+ webrtc::test::ResourcePath("audio_coding/testfile16kHz", "pcm");
+ constexpr int kSampleRateHz = 16000;
+ AudioEncoderOpusConfig config;
+ config.dtx_enabled = true;
+ config.sample_rate_hz = kSampleRateHz;
+ constexpr int payload_type = 17;
+ const auto encoder = AudioEncoderOpus::MakeAudioEncoder(config, payload_type);
+ test::AudioLoop audio_loop;
+ constexpr size_t kMaxLoopLengthSaples = kSampleRateHz * 11.6f;
+ constexpr size_t kInputBlockSizeSamples = kSampleRateHz / 100;
+ EXPECT_TRUE(audio_loop.Init(kInputFileName, kMaxLoopLengthSaples,
+ kInputBlockSizeSamples));
+ AudioEncoder::EncodedInfo info;
+ rtc::Buffer encoded(500);
+ // Encode the audio file and store the last part that corresponds to silence.
+ constexpr size_t kSilenceDurationSamples = kSampleRateHz * 0.2f;
+ std::array<int16_t, kSilenceDurationSamples> silence;
+ uint32_t rtp_timestamp = 0;
+ bool opus_entered_dtx = false;
+ bool silence_filled = false;
+ size_t timestamp_start_silence = 0;
+ while (!silence_filled && rtp_timestamp < kMaxLoopLengthSaples) {
+ encoded.Clear();
+ // Every second call to the encoder will generate an Opus packet.
+ for (int j = 0; j < 2; j++) {
+ auto next_frame = audio_loop.GetNextBlock();
+ info = encoder->Encode(rtp_timestamp, next_frame, &encoded);
+ if (opus_entered_dtx) {
+ size_t silence_frame_start = rtp_timestamp - timestamp_start_silence;
+ silence_filled = silence_frame_start >= kSilenceDurationSamples;
+ if (!silence_filled) {
+ std::copy(next_frame.begin(), next_frame.end(),
+ silence.begin() + silence_frame_start);
+ }
+ }
+ rtp_timestamp += kInputBlockSizeSamples;
+ }
+ if (info.encoded_bytes < 2 && !opus_entered_dtx) {
+ timestamp_start_silence = rtp_timestamp;
+ }
+ opus_entered_dtx = info.encoded_bytes < 2;
+ }
+
+ EXPECT_TRUE(silence_filled);
+ // The copied 200 ms of silence is used for creating 6 bursts that are fed to
+ // the encoder, the first three ones with a larger energy and the last three
+ // with a lower energy. This test verifies that the encoder just sends refresh
+ // DTX packets during the last bursts.
+ int number_non_empty_packets_during_increase = 0;
+ int number_non_empty_packets_during_decrease = 0;
+ for (size_t burst = 0; burst < 6; ++burst) {
+ uint32_t rtp_timestamp_start = rtp_timestamp;
+ const bool increase_noise = burst < 3;
+ const float gain = increase_noise ? 1.4f : 0.0f;
+ while (rtp_timestamp < rtp_timestamp_start + kSilenceDurationSamples) {
+ encoded.Clear();
+ // Every second call to the encoder will generate an Opus packet.
+ for (int j = 0; j < 2; j++) {
+ std::array<int16_t, kInputBlockSizeSamples> silence_frame;
+ size_t silence_frame_start = rtp_timestamp - rtp_timestamp_start;
+ std::transform(
+ silence.begin() + silence_frame_start,
+ silence.begin() + silence_frame_start + kInputBlockSizeSamples,
+ silence_frame.begin(), [gain](float s) { return gain * s; });
+ info = encoder->Encode(rtp_timestamp, silence_frame, &encoded);
+ rtp_timestamp += kInputBlockSizeSamples;
+ }
+ // Tracking the number of non empty packets.
+ if (increase_noise && info.encoded_bytes > 2) {
+ number_non_empty_packets_during_increase++;
+ }
+ if (!increase_noise && info.encoded_bytes > 2) {
+ number_non_empty_packets_during_decrease++;
+ }
+ }
+ }
+ // Check that the refresh DTX packets are just sent during the decrease energy
+ // region.
+ EXPECT_EQ(number_non_empty_packets_during_increase, 0);
+ EXPECT_GT(number_non_empty_packets_during_decrease, 0);
+}
+
} // namespace webrtc
diff --git a/modules/audio_coding/codecs/opus/opus_inst.h b/modules/audio_coding/codecs/opus/opus_inst.h
index 148baa2..2c25e43 100644
--- a/modules/audio_coding/codecs/opus/opus_inst.h
+++ b/modules/audio_coding/codecs/opus/opus_inst.h
@@ -25,6 +25,9 @@
OpusMSEncoder* multistream_encoder;
size_t channels;
int in_dtx_mode;
+ bool avoid_noise_pumping_during_dtx;
+ int sample_rate_hz;
+ float smooth_energy_non_active_frames;
};
struct WebRtcOpusDecInst {
diff --git a/modules/audio_coding/codecs/opus/opus_interface.cc b/modules/audio_coding/codecs/opus/opus_interface.cc
index ca39ed8..95c3bb9 100644
--- a/modules/audio_coding/codecs/opus/opus_interface.cc
+++ b/modules/audio_coding/codecs/opus/opus_interface.cc
@@ -12,6 +12,9 @@
#include <cstdlib>
+#include <numeric>
+
+#include "api/array_view.h"
#include "rtc_base/checks.h"
#include "system_wrappers/include/field_trial.h"
@@ -36,6 +39,9 @@
constexpr char kPlcUsePrevDecodedSamplesFieldTrial[] =
"WebRTC-Audio-OpusPlcUsePrevDecodedSamples";
+constexpr char kAvoidNoisePumpingDuringDtxFieldTrial[] =
+ "WebRTC-Audio-OpusAvoidNoisePumpingDuringDtx";
+
static int FrameSizePerChannel(int frame_size_ms, int sample_rate_hz) {
RTC_DCHECK_GT(frame_size_ms, 0);
RTC_DCHECK_EQ(frame_size_ms % 10, 0);
@@ -54,6 +60,46 @@
return FrameSizePerChannel(20, sample_rate_hz);
}
+// Returns true if the `encoded` payload corresponds to a refresh DTX packet
+// whose energy is larger than the expected for non activity packets.
+static bool WebRtcOpus_IsHighEnergyRefreshDtxPacket(
+ OpusEncInst* inst,
+ rtc::ArrayView<const int16_t> frame,
+ rtc::ArrayView<const uint8_t> encoded) {
+ if (encoded.size() <= 2) {
+ return false;
+ }
+ int number_frames =
+ frame.size() / DefaultFrameSizePerChannel(inst->sample_rate_hz);
+ if (number_frames > 0 &&
+ WebRtcOpus_PacketHasVoiceActivity(encoded.data(), encoded.size()) == 0) {
+ const float average_frame_energy =
+ std::accumulate(frame.begin(), frame.end(), 0.0f,
+ [](float a, int32_t b) { return a + b * b; }) /
+ number_frames;
+ if (WebRtcOpus_GetInDtx(inst) == 1 &&
+ average_frame_energy >= inst->smooth_energy_non_active_frames * 0.5f) {
+ // This is a refresh DTX packet as the encoder is in DTX and has
+ // produced a payload > 2 bytes. This refresh packet has a higher energy
+ // than the smooth energy of non activity frames (with a 3 dB negative
+ // margin) and, therefore, it is flagged as a high energy refresh DTX
+ // packet.
+ return true;
+ }
+ // The average energy is tracked in a similar way as the modeling of the
+ // comfort noise in the Silk decoder in Opus
+ // (third_party/opus/src/silk/CNG.c).
+ if (average_frame_energy < inst->smooth_energy_non_active_frames * 0.5f) {
+ inst->smooth_energy_non_active_frames = average_frame_energy;
+ } else {
+ inst->smooth_energy_non_active_frames +=
+ (average_frame_energy - inst->smooth_energy_non_active_frames) *
+ 0.25f;
+ }
+ }
+ return false;
+}
+
int16_t WebRtcOpus_EncoderCreate(OpusEncInst** inst,
size_t channels,
int32_t application,
@@ -88,6 +134,10 @@
state->in_dtx_mode = 0;
state->channels = channels;
+ state->sample_rate_hz = sample_rate_hz;
+ state->smooth_energy_non_active_frames = 0.0f;
+ state->avoid_noise_pumping_during_dtx =
+ webrtc::field_trial::IsEnabled(kAvoidNoisePumpingDuringDtxFieldTrial);
*inst = state;
return 0;
@@ -120,9 +170,10 @@
RTC_DCHECK(state);
int error;
- state->multistream_encoder =
- opus_multistream_encoder_create(48000, channels, streams, coupled_streams,
- channel_mapping, opus_app, &error);
+ const int sample_rate_hz = 48000;
+ state->multistream_encoder = opus_multistream_encoder_create(
+ sample_rate_hz, channels, streams, coupled_streams, channel_mapping,
+ opus_app, &error);
if (error != OPUS_OK || (!state->encoder && !state->multistream_encoder)) {
WebRtcOpus_EncoderFree(state);
@@ -131,6 +182,9 @@
state->in_dtx_mode = 0;
state->channels = channels;
+ state->sample_rate_hz = sample_rate_hz;
+ state->smooth_energy_non_active_frames = 0.0f;
+ state->avoid_noise_pumping_during_dtx = false;
*inst = state;
return 0;
@@ -188,6 +242,15 @@
}
}
+ if (inst->avoid_noise_pumping_during_dtx && WebRtcOpus_GetUseDtx(inst) == 1 &&
+ WebRtcOpus_IsHighEnergyRefreshDtxPacket(
+ inst, rtc::MakeArrayView(audio_in, samples),
+ rtc::MakeArrayView(encoded, res))) {
+ // This packet is a high energy refresh DTX packet. For avoiding an increase
+ // of the energy in the DTX region at the decoder, this packet is dropped.
+ inst->in_dtx_mode = 0;
+ return 0;
+ }
inst->in_dtx_mode = 0;
return res;
}
@@ -316,6 +379,16 @@
}
}
+int16_t WebRtcOpus_GetUseDtx(OpusEncInst* inst) {
+ if (inst) {
+ opus_int32 use_dtx;
+ if (ENCODER_CTL(inst, OPUS_GET_DTX(&use_dtx)) == 0) {
+ return use_dtx;
+ }
+ }
+ return -1;
+}
+
int16_t WebRtcOpus_EnableCbr(OpusEncInst* inst) {
if (inst) {
return ENCODER_CTL(inst, OPUS_SET_VBR(0));
diff --git a/modules/audio_coding/codecs/opus/opus_interface.h b/modules/audio_coding/codecs/opus/opus_interface.h
index 2a3ceaa..89159ce 100644
--- a/modules/audio_coding/codecs/opus/opus_interface.h
+++ b/modules/audio_coding/codecs/opus/opus_interface.h
@@ -232,6 +232,20 @@
int16_t WebRtcOpus_DisableDtx(OpusEncInst* inst);
/****************************************************************************
+ * WebRtcOpus_GetUseDtx()
+ *
+ * This function gets the DTX configuration used for encoding.
+ *
+ * Input:
+ * - inst : Encoder context
+ *
+ * Return value : 0 - Encoder does not use DTX.
+ * 1 - Encoder uses DTX.
+ * -1 - Error.
+ */
+int16_t WebRtcOpus_GetUseDtx(OpusEncInst* inst);
+
+/****************************************************************************
* WebRtcOpus_EnableCbr()
*
* This function enables CBR for encoding.
diff --git a/modules/audio_coding/include/audio_coding_module_typedefs.h b/modules/audio_coding/include/audio_coding_module_typedefs.h
index 07aa8c9..a7210da 100644
--- a/modules/audio_coding/include/audio_coding_module_typedefs.h
+++ b/modules/audio_coding/include/audio_coding_module_typedefs.h
@@ -13,8 +13,6 @@
#include <map>
-#include "rtc_base/deprecation.h"
-
namespace webrtc {
///////////////////////////////////////////////////////////////////////////
diff --git a/modules/audio_coding/neteq/audio_decoder_unittest.cc b/modules/audio_coding/neteq/audio_decoder_unittest.cc
index 56708ec..662da2f 100644
--- a/modules/audio_coding/neteq/audio_decoder_unittest.cc
+++ b/modules/audio_coding/neteq/audio_decoder_unittest.cc
@@ -639,7 +639,9 @@
TestSetAndGetTargetBitratesWithFixedCodec(audio_encoder_.get(), 128000);
}
-TEST_P(AudioDecoderOpusTest, EncodeDecode) {
+// TODO(http://bugs.webrtc.org/12518): Enable the test after Opus has been
+// updated.
+TEST_P(AudioDecoderOpusTest, DISABLED_EncodeDecode) {
constexpr int tolerance = 6176;
constexpr int channel_diff_tolerance = 6;
constexpr double mse = 238630.0;
diff --git a/modules/audio_coding/neteq/buffer_level_filter.cc b/modules/audio_coding/neteq/buffer_level_filter.cc
index 5d503e9..8901c01 100644
--- a/modules/audio_coding/neteq/buffer_level_filter.cc
+++ b/modules/audio_coding/neteq/buffer_level_filter.cc
@@ -35,14 +35,13 @@
// |level_factor_| and |filtered_current_level_| are in Q8.
// |buffer_size_samples| is in Q0.
const int64_t filtered_current_level =
- ((level_factor_ * int64_t{filtered_current_level_}) >> 8) +
- ((256 - level_factor_) * rtc::dchecked_cast<int>(buffer_size_samples));
+ (level_factor_ * int64_t{filtered_current_level_} >> 8) +
+ (256 - level_factor_) * rtc::dchecked_cast<int64_t>(buffer_size_samples);
// Account for time-scale operations (accelerate and pre-emptive expand) and
// make sure that the filtered value remains non-negative.
filtered_current_level_ = rtc::saturated_cast<int>(std::max<int64_t>(
- 0,
- filtered_current_level - (int64_t{time_stretched_samples} * (1 << 8))));
+ 0, filtered_current_level - int64_t{time_stretched_samples} * (1 << 8)));
}
void BufferLevelFilter::SetFilteredBufferLevel(int buffer_size_samples) {
diff --git a/modules/audio_coding/neteq/buffer_level_filter.h b/modules/audio_coding/neteq/buffer_level_filter.h
index 89fcaf4..218a142 100644
--- a/modules/audio_coding/neteq/buffer_level_filter.h
+++ b/modules/audio_coding/neteq/buffer_level_filter.h
@@ -12,6 +12,7 @@
#define MODULES_AUDIO_CODING_NETEQ_BUFFER_LEVEL_FILTER_H_
#include <stddef.h>
+#include <stdint.h>
#include "rtc_base/constructor_magic.h"
@@ -39,7 +40,7 @@
// Returns filtered current level in number of samples.
virtual int filtered_current_level() const {
// Round to nearest whole sample.
- return (filtered_current_level_ + (1 << 7)) >> 8;
+ return (int64_t{filtered_current_level_} + (1 << 7)) >> 8;
}
private:
diff --git a/modules/audio_coding/neteq/cross_correlation.cc b/modules/audio_coding/neteq/cross_correlation.cc
index 7ee867a..37ed937 100644
--- a/modules/audio_coding/neteq/cross_correlation.cc
+++ b/modules/audio_coding/neteq/cross_correlation.cc
@@ -25,22 +25,23 @@
size_t cross_correlation_length,
int cross_correlation_step,
int32_t* cross_correlation) {
- // Find the maximum absolute value of sequence_1 and 2.
- const int32_t max_1 =
- abs(sequence_1[WebRtcSpl_MaxAbsIndexW16(sequence_1, sequence_1_length)]);
+ // Find the element that has the maximum absolute value of sequence_1 and 2.
+ // Note that these values may be negative.
+ const int16_t max_1 =
+ WebRtcSpl_MaxAbsElementW16(sequence_1, sequence_1_length);
const int sequence_2_shift =
cross_correlation_step * (static_cast<int>(cross_correlation_length) - 1);
const int16_t* sequence_2_start =
sequence_2_shift >= 0 ? sequence_2 : sequence_2 + sequence_2_shift;
const size_t sequence_2_length =
sequence_1_length + std::abs(sequence_2_shift);
- const int32_t max_2 = abs(sequence_2_start[WebRtcSpl_MaxAbsIndexW16(
- sequence_2_start, sequence_2_length)]);
+ const int16_t max_2 =
+ WebRtcSpl_MaxAbsElementW16(sequence_2_start, sequence_2_length);
// In order to avoid overflow when computing the sum we should scale the
// samples so that (in_vector_length * max_1 * max_2) will not overflow.
const int64_t max_value =
- max_1 * max_2 * static_cast<int64_t>(sequence_1_length);
+ abs(max_1 * max_2) * static_cast<int64_t>(sequence_1_length);
const int32_t factor = max_value >> 31;
const int scaling = factor == 0 ? 0 : 31 - WebRtcSpl_NormW32(factor);
diff --git a/modules/audio_coding/neteq/decision_logic.cc b/modules/audio_coding/neteq/decision_logic.cc
index 266e675..cb6daf0 100644
--- a/modules/audio_coding/neteq/decision_logic.cc
+++ b/modules/audio_coding/neteq/decision_logic.cc
@@ -50,8 +50,8 @@
disallow_time_stretching_(!config.allow_time_stretching),
timescale_countdown_(
tick_timer_->GetNewCountdown(kMinTimescaleInterval + 1)),
- estimate_dtx_delay_("estimate_dtx_delay", false),
- time_stretch_cn_("time_stretch_cn", false),
+ estimate_dtx_delay_("estimate_dtx_delay", true),
+ time_stretch_cn_("time_stretch_cn", true),
target_level_window_ms_("target_level_window",
kDefaultTargetLevelWindowMs,
0,
diff --git a/modules/audio_coding/neteq/neteq_decoder_plc_unittest.cc b/modules/audio_coding/neteq/neteq_decoder_plc_unittest.cc
index daf81f2..cb0a3d8 100644
--- a/modules/audio_coding/neteq/neteq_decoder_plc_unittest.cc
+++ b/modules/audio_coding/neteq/neteq_decoder_plc_unittest.cc
@@ -10,7 +10,6 @@
// Test to verify correct operation when using the decoder-internal PLC.
-#include <algorithm>
#include <memory>
#include <utility>
#include <vector>
@@ -33,6 +32,9 @@
namespace test {
namespace {
+constexpr int kSampleRateHz = 32000;
+constexpr int kRunTimeMs = 10000;
+
// This class implements a fake decoder. The decoder will read audio from a file
// and present as output, both for regular decoding and for PLC.
class AudioDecoderPlc : public AudioDecoder {
@@ -48,7 +50,8 @@
int sample_rate_hz,
int16_t* decoded,
SpeechType* speech_type) override {
- RTC_CHECK_EQ(encoded_len / 2, 20 * sample_rate_hz_ / 1000);
+ RTC_CHECK_GE(encoded_len / 2, 10 * sample_rate_hz_ / 1000);
+ RTC_CHECK_LE(encoded_len / 2, 2 * 10 * sample_rate_hz_ / 1000);
RTC_CHECK_EQ(sample_rate_hz, sample_rate_hz_);
RTC_CHECK(decoded);
RTC_CHECK(speech_type);
@@ -60,17 +63,21 @@
void GeneratePlc(size_t requested_samples_per_channel,
rtc::BufferT<int16_t>* concealment_audio) override {
+ // Instead of generating random data for GeneratePlc we use the same data as
+ // the input, so we can check that we produce the same result independently
+ // of the losses.
+ RTC_DCHECK_EQ(requested_samples_per_channel, 10 * sample_rate_hz_ / 1000);
+
// Must keep a local copy of this since DecodeInternal sets it to false.
const bool last_was_plc = last_was_plc_;
- SpeechType speech_type;
+
std::vector<int16_t> decoded(5760);
- int dec_len = DecodeInternal(nullptr, 2 * 20 * sample_rate_hz_ / 1000,
+ SpeechType speech_type;
+ int dec_len = DecodeInternal(nullptr, 2 * 10 * sample_rate_hz_ / 1000,
sample_rate_hz_, decoded.data(), &speech_type);
- // This fake decoder can only generate 20 ms of PLC data each time. Make
- // sure the caller didn't ask for more.
- RTC_CHECK_GE(dec_len, requested_samples_per_channel);
concealment_audio->AppendData(decoded.data(), dec_len);
concealed_samples_ += rtc::checked_cast<size_t>(dec_len);
+
if (!last_was_plc) {
++concealment_events_;
}
@@ -103,11 +110,15 @@
};
// A NetEqInput which connects to another NetEqInput, but drops a number of
-// packets on the way.
+// consecutive packets on the way
class LossyInput : public NetEqInput {
public:
- LossyInput(int loss_cadence, std::unique_ptr<NetEqInput> input)
- : loss_cadence_(loss_cadence), input_(std::move(input)) {}
+ LossyInput(int loss_cadence,
+ int burst_length,
+ std::unique_ptr<NetEqInput> input)
+ : loss_cadence_(loss_cadence),
+ burst_length_(burst_length),
+ input_(std::move(input)) {}
absl::optional<int64_t> NextPacketTime() const override {
return input_->NextPacketTime();
@@ -119,8 +130,12 @@
std::unique_ptr<PacketData> PopPacket() override {
if (loss_cadence_ != 0 && (++count_ % loss_cadence_) == 0) {
- // Pop one extra packet to create the loss.
- input_->PopPacket();
+ // Pop `burst_length_` packets to create the loss.
+ auto packet_to_return = input_->PopPacket();
+ for (int i = 0; i < burst_length_; i++) {
+ input_->PopPacket();
+ }
+ return packet_to_return;
}
return input_->PopPacket();
}
@@ -135,6 +150,7 @@
private:
const int loss_cadence_;
+ const int burst_length_;
int count_ = 0;
const std::unique_ptr<NetEqInput> input_;
};
@@ -149,7 +165,14 @@
std::string& output_str_;
};
-NetEqNetworkStatistics RunTest(int loss_cadence, std::string* checksum) {
+struct TestStatistics {
+ NetEqNetworkStatistics network;
+ NetEqLifetimeStatistics lifetime;
+};
+
+TestStatistics RunTest(int loss_cadence,
+ int burst_length,
+ std::string* checksum) {
NetEq::Config config;
config.for_test_no_time_stretching = true;
@@ -157,20 +180,18 @@
// but the actual encoded samples will never be used by the decoder in the
// test. See below about the decoder.
auto generator = std::make_unique<ZeroSampleGenerator>();
- constexpr int kSampleRateHz = 32000;
constexpr int kPayloadType = 100;
AudioEncoderPcm16B::Config encoder_config;
encoder_config.sample_rate_hz = kSampleRateHz;
encoder_config.payload_type = kPayloadType;
auto encoder = std::make_unique<AudioEncoderPcm16B>(encoder_config);
- constexpr int kRunTimeMs = 10000;
auto input = std::make_unique<EncodeNetEqInput>(
std::move(generator), std::move(encoder), kRunTimeMs);
// Wrap the input in a loss function.
- auto lossy_input =
- std::make_unique<LossyInput>(loss_cadence, std::move(input));
+ auto lossy_input = std::make_unique<LossyInput>(loss_cadence, burst_length,
+ std::move(input));
- // Settinng up decoders.
+ // Setting up decoders.
NetEqTest::DecoderMap decoders;
// Using a fake decoder which simply reads the output audio from a file.
auto input_file = std::make_unique<InputAudioFile>(
@@ -195,24 +216,98 @@
auto lifetime_stats = neteq_test.LifetimeStats();
EXPECT_EQ(dec.concealed_samples(), lifetime_stats.concealed_samples);
EXPECT_EQ(dec.concealment_events(), lifetime_stats.concealment_events);
-
- return neteq_test.SimulationStats();
+ return {neteq_test.SimulationStats(), neteq_test.LifetimeStats()};
}
} // namespace
-TEST(NetEqDecoderPlc, Test) {
+// Check that some basic metrics are produced in the right direction. In
+// particular, expand_rate should only increase if there are losses present. Our
+// dummy decoder is designed such as the checksum should always be the same
+// regardless of the losses given that calls are executed in the right order.
+TEST(NetEqDecoderPlc, BasicMetrics) {
std::string checksum;
- auto stats = RunTest(10, &checksum);
+
+ // Drop 1 packet every 10 packets.
+ auto stats = RunTest(10, 1, &checksum);
std::string checksum_no_loss;
- auto stats_no_loss = RunTest(0, &checksum_no_loss);
+ auto stats_no_loss = RunTest(0, 0, &checksum_no_loss);
EXPECT_EQ(checksum, checksum_no_loss);
- EXPECT_EQ(stats.preemptive_rate, stats_no_loss.preemptive_rate);
- EXPECT_EQ(stats.accelerate_rate, stats_no_loss.accelerate_rate);
- EXPECT_EQ(0, stats_no_loss.expand_rate);
- EXPECT_GT(stats.expand_rate, 0);
+ EXPECT_EQ(stats.network.preemptive_rate,
+ stats_no_loss.network.preemptive_rate);
+ EXPECT_EQ(stats.network.accelerate_rate,
+ stats_no_loss.network.accelerate_rate);
+ EXPECT_EQ(0, stats_no_loss.network.expand_rate);
+ EXPECT_GT(stats.network.expand_rate, 0);
+}
+
+// Checks that interruptions are not counted in small losses but they are
+// correctly counted in long interruptions.
+TEST(NetEqDecoderPlc, CountInterruptions) {
+ std::string checksum;
+ std::string checksum_2;
+ std::string checksum_3;
+
+ // Half of the packets lost but in short interruptions.
+ auto stats_no_interruptions = RunTest(1, 1, &checksum);
+ // One lost of 500 ms (250 packets).
+ auto stats_one_interruption = RunTest(200, 250, &checksum_2);
+ // Two losses of 250ms each (125 packets).
+ auto stats_two_interruptions = RunTest(125, 125, &checksum_3);
+
+ EXPECT_EQ(checksum, checksum_2);
+ EXPECT_EQ(checksum, checksum_3);
+ EXPECT_GT(stats_no_interruptions.network.expand_rate, 0);
+ EXPECT_EQ(stats_no_interruptions.lifetime.total_interruption_duration_ms, 0);
+ EXPECT_EQ(stats_no_interruptions.lifetime.interruption_count, 0);
+
+ EXPECT_GT(stats_one_interruption.network.expand_rate, 0);
+ EXPECT_EQ(stats_one_interruption.lifetime.total_interruption_duration_ms,
+ 5000);
+ EXPECT_EQ(stats_one_interruption.lifetime.interruption_count, 1);
+
+ EXPECT_GT(stats_two_interruptions.network.expand_rate, 0);
+ EXPECT_EQ(stats_two_interruptions.lifetime.total_interruption_duration_ms,
+ 5000);
+ EXPECT_EQ(stats_two_interruptions.lifetime.interruption_count, 2);
+}
+
+// Checks that small losses do not produce interruptions.
+TEST(NetEqDecoderPlc, NoInterruptionsInSmallLosses) {
+ std::string checksum_1;
+ std::string checksum_4;
+
+ auto stats_1 = RunTest(300, 1, &checksum_1);
+ auto stats_4 = RunTest(300, 4, &checksum_4);
+
+ EXPECT_EQ(checksum_1, checksum_4);
+
+ EXPECT_EQ(stats_1.lifetime.interruption_count, 0);
+ EXPECT_EQ(stats_1.lifetime.total_interruption_duration_ms, 0);
+ EXPECT_EQ(stats_1.lifetime.concealed_samples, 640u); // 20ms of concealment.
+ EXPECT_EQ(stats_1.lifetime.concealment_events, 1u); // in just one event.
+
+ EXPECT_EQ(stats_4.lifetime.interruption_count, 0);
+ EXPECT_EQ(stats_4.lifetime.total_interruption_duration_ms, 0);
+ EXPECT_EQ(stats_4.lifetime.concealed_samples, 2560u); // 80ms of concealment.
+ EXPECT_EQ(stats_4.lifetime.concealment_events, 1u); // in just one event.
+}
+
+// Checks that interruptions of different sizes report correct duration.
+TEST(NetEqDecoderPlc, InterruptionsReportCorrectSize) {
+ std::string checksum;
+
+ for (int burst_length = 5; burst_length < 10; burst_length++) {
+ auto stats = RunTest(300, burst_length, &checksum);
+ auto duration = stats.lifetime.total_interruption_duration_ms;
+ if (burst_length < 8) {
+ EXPECT_EQ(duration, 0);
+ } else {
+ EXPECT_EQ(duration, burst_length * 20);
+ }
+ }
}
} // namespace test
diff --git a/modules/audio_coding/neteq/neteq_impl.cc b/modules/audio_coding/neteq/neteq_impl.cc
index 9ec7bd5..d156352 100644
--- a/modules/audio_coding/neteq/neteq_impl.cc
+++ b/modules/audio_coding/neteq/neteq_impl.cc
@@ -1214,6 +1214,11 @@
}
controller_->ExpandDecision(*operation);
+ if ((last_mode_ == Mode::kCodecPlc) && (*operation != Operation::kExpand)) {
+ // Getting out of the PLC expand mode, reporting interruptions.
+ // NetEq PLC reports this metrics in expand.cc
+ stats_->EndExpandEvent(fs_hz_);
+ }
// Check conditions for reset.
if (new_codec_ || *operation == Operation::kUndefined) {
@@ -2159,7 +2164,7 @@
expand_->overlap_length());
normal_.reset(new Normal(fs_hz, decoder_database_.get(), *background_noise_,
- expand_.get()));
+ expand_.get(), stats_.get()));
accelerate_.reset(
accelerate_factory_->Create(fs_hz, channels, *background_noise_));
preemptive_expand_.reset(preemptive_expand_factory_->Create(
diff --git a/modules/audio_coding/neteq/neteq_impl_unittest.cc b/modules/audio_coding/neteq/neteq_impl_unittest.cc
index c66a0e2..a073d82 100644
--- a/modules/audio_coding/neteq/neteq_impl_unittest.cc
+++ b/modules/audio_coding/neteq/neteq_impl_unittest.cc
@@ -987,15 +987,6 @@
EXPECT_TRUE(neteq_->RegisterPayloadType(kPayloadType,
SdpAudioFormat("opus", 48000, 2)));
- // Insert one packet (decoder will return speech).
- EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
-
- // Insert second packet (decoder will return CNG).
- payload[0] = 1;
- rtp_header.sequenceNumber++;
- rtp_header.timestamp += kPayloadLengthSamples;
- EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
-
const size_t kMaxOutputSize = static_cast<size_t>(10 * kSampleRateKhz);
AudioFrame output;
AudioFrame::SpeechType expected_type[8] = {
@@ -1012,11 +1003,20 @@
50 * kSampleRateKhz,
10 * kSampleRateKhz};
+ // Insert one packet (decoder will return speech).
+ EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
+
bool muted;
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
absl::optional<uint32_t> last_timestamp = neteq_->GetPlayoutTimestamp();
ASSERT_TRUE(last_timestamp);
+ // Insert second packet (decoder will return CNG).
+ payload[0] = 1;
+ rtp_header.sequenceNumber++;
+ rtp_header.timestamp += kPayloadLengthSamples;
+ EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_header, payload));
+
// Lambda for verifying the timestamps.
auto verify_timestamp = [&last_timestamp, &expected_timestamp_increment](
absl::optional<uint32_t> ts, size_t i) {
diff --git a/modules/audio_coding/neteq/neteq_unittest.cc b/modules/audio_coding/neteq/neteq_unittest.cc
index c6d514d..1369ead 100644
--- a/modules/audio_coding/neteq/neteq_unittest.cc
+++ b/modules/audio_coding/neteq/neteq_unittest.cc
@@ -84,16 +84,16 @@
webrtc::test::ResourcePath("audio_coding/neteq_universal_new", "rtp");
const std::string output_checksum =
- PlatformChecksum("68ec266d2d152dfc0d938484e7936f6af4f803e3",
- "1c243feb35e3e9ab37039eddf5b3c3ecfca3c60c", "not used",
- "68ec266d2d152dfc0d938484e7936f6af4f803e3",
- "f68c546a43bb25743297c9c0c9027e8424b8e10b");
+ PlatformChecksum("6c35140ce4d75874bdd60aa1872400b05fd05ca2",
+ "ab451bb8301d9a92fbf4de91556b56f1ea38b4ce", "not used",
+ "6c35140ce4d75874bdd60aa1872400b05fd05ca2",
+ "64b46bb3c1165537a880ae8404afce2efba456c0");
const std::string network_stats_checksum =
- PlatformChecksum("2a5516cdc1c6af9f1d9d3c2f95ed292f509311c7",
- "e96a7f081ebc111f49c7373d3728274057012ae9", "not used",
- "2a5516cdc1c6af9f1d9d3c2f95ed292f509311c7",
- "2a5516cdc1c6af9f1d9d3c2f95ed292f509311c7");
+ PlatformChecksum("90594d85fa31d3d9584d79293bf7aa4ee55ed751",
+ "77b9c3640b81aff6a38d69d07dd782d39c15321d", "not used",
+ "90594d85fa31d3d9584d79293bf7aa4ee55ed751",
+ "90594d85fa31d3d9584d79293bf7aa4ee55ed751");
DecodeAndCompare(input_rtp_file, output_checksum, network_stats_checksum,
absl::GetFlag(FLAGS_gen_ref));
@@ -105,35 +105,33 @@
#else
#define MAYBE_TestOpusBitExactness DISABLED_TestOpusBitExactness
#endif
-TEST_F(NetEqDecodingTest, MAYBE_TestOpusBitExactness) {
+// TODO(http://bugs.webrtc.org/12518): Enable the test after Opus has been
+// updated.
+TEST_F(NetEqDecodingTest, DISABLED_TestOpusBitExactness) {
const std::string input_rtp_file =
webrtc::test::ResourcePath("audio_coding/neteq_opus", "rtp");
const std::string maybe_sse =
- "554ad4133934e3920f97575579a46f674683d77c"
- "|de316e2bfb15192edb820fe5fb579d11ff5a524b";
+ "c7887ff60eecf460332c6c7a28c81561f9e8a40f"
+ "|673dd422cfc174152536d3b13af64f9722520ab5";
const std::string output_checksum = PlatformChecksum(
- maybe_sse, "b3fac4ad4f6ea384aff676ee1ea816bd70415490",
- "373ccd99c147cd3fcef0e7dcad6f87d0f8e5a1c0", maybe_sse, maybe_sse);
+ maybe_sse, "e39283dd61a89cead3786ef8642d2637cc447296",
+ "53d8073eb848b70974cba9e26424f4946508fd19", maybe_sse, maybe_sse);
const std::string network_stats_checksum =
- PlatformChecksum("ec29e047b019a86ec06e2c40643143dc1975c69f",
- "ce6f519bc1220b003944ac5d9db077665a06834e",
- "abb686d3ac6fac0001ca8d45a6ca6f5aefb2f9d6",
- "ec29e047b019a86ec06e2c40643143dc1975c69f",
- "ec29e047b019a86ec06e2c40643143dc1975c69f");
+ PlatformChecksum("c438bfa3b018f77691279eb9c63730569f54585c",
+ "8a474ed0992591e0c84f593824bb05979c3de157",
+ "9a05378dbf7e6edd56cdeb8ec45bcd6d8589623c",
+ "c438bfa3b018f77691279eb9c63730569f54585c",
+ "c438bfa3b018f77691279eb9c63730569f54585c");
DecodeAndCompare(input_rtp_file, output_checksum, network_stats_checksum,
absl::GetFlag(FLAGS_gen_ref));
}
-#if !defined(WEBRTC_IOS) && defined(WEBRTC_NETEQ_UNITTEST_BITEXACT) && \
- defined(WEBRTC_CODEC_OPUS)
-#define MAYBE_TestOpusDtxBitExactness TestOpusDtxBitExactness
-#else
-#define MAYBE_TestOpusDtxBitExactness DISABLED_TestOpusDtxBitExactness
-#endif
-TEST_F(NetEqDecodingTest, MAYBE_TestOpusDtxBitExactness) {
+// TODO(http://bugs.webrtc.org/12518): Enable the test after Opus has been
+// updated.
+TEST_F(NetEqDecodingTest, DISABLED_TestOpusDtxBitExactness) {
const std::string input_rtp_file =
webrtc::test::ResourcePath("audio_coding/neteq_opus_dtx", "rtp");
diff --git a/modules/audio_coding/neteq/normal.cc b/modules/audio_coding/neteq/normal.cc
index 967deea..3ed0e26 100644
--- a/modules/audio_coding/neteq/normal.cc
+++ b/modules/audio_coding/neteq/normal.cc
@@ -14,7 +14,6 @@
#include <algorithm> // min
-#include "api/audio_codecs/audio_decoder.h"
#include "common_audio/signal_processing/include/signal_processing_library.h"
#include "modules/audio_coding/neteq/audio_multi_vector.h"
#include "modules/audio_coding/neteq/background_noise.h"
@@ -50,6 +49,13 @@
// TODO(hlundin): Investigate this further.
const int fs_shift = 30 - WebRtcSpl_NormW32(fs_mult);
+ // If last call resulted in a CodedPlc we don't need to do cross-fading but we
+ // need to report the end of the interruption once we are back to normal
+ // operation.
+ if (last_mode == NetEq::Mode::kCodecPlc) {
+ statistics_->EndExpandEvent(fs_hz_);
+ }
+
// Check if last RecOut call resulted in an Expand. If so, we have to take
// care of some cross-fading and unmuting.
if (last_mode == NetEq::Mode::kExpand) {
diff --git a/modules/audio_coding/neteq/normal.h b/modules/audio_coding/neteq/normal.h
index d8c13e6..d6dc84a 100644
--- a/modules/audio_coding/neteq/normal.h
+++ b/modules/audio_coding/neteq/normal.h
@@ -15,6 +15,7 @@
#include <string.h> // Access to size_t.
#include "api/neteq/neteq.h"
+#include "modules/audio_coding/neteq/statistics_calculator.h"
#include "rtc_base/checks.h"
#include "rtc_base/constructor_magic.h"
#include "rtc_base/numerics/safe_conversions.h"
@@ -35,14 +36,16 @@
Normal(int fs_hz,
DecoderDatabase* decoder_database,
const BackgroundNoise& background_noise,
- Expand* expand)
+ Expand* expand,
+ StatisticsCalculator* statistics)
: fs_hz_(fs_hz),
decoder_database_(decoder_database),
background_noise_(background_noise),
expand_(expand),
samples_per_ms_(rtc::CheckedDivExact(fs_hz_, 1000)),
default_win_slope_Q14_(
- rtc::dchecked_cast<uint16_t>((1 << 14) / samples_per_ms_)) {}
+ rtc::dchecked_cast<uint16_t>((1 << 14) / samples_per_ms_)),
+ statistics_(statistics) {}
virtual ~Normal() {}
@@ -64,6 +67,7 @@
Expand* expand_;
const size_t samples_per_ms_;
const int16_t default_win_slope_Q14_;
+ StatisticsCalculator* const statistics_;
RTC_DISALLOW_COPY_AND_ASSIGN(Normal);
};
diff --git a/modules/audio_coding/neteq/normal_unittest.cc b/modules/audio_coding/neteq/normal_unittest.cc
index 36751f8..7e533bb 100644
--- a/modules/audio_coding/neteq/normal_unittest.cc
+++ b/modules/audio_coding/neteq/normal_unittest.cc
@@ -50,7 +50,7 @@
RandomVector random_vector;
StatisticsCalculator statistics;
Expand expand(&bgn, &sync_buffer, &random_vector, &statistics, fs, channels);
- Normal normal(fs, &db, bgn, &expand);
+ Normal normal(fs, &db, bgn, &expand, &statistics);
EXPECT_CALL(db, Die()); // Called when |db| goes out of scope.
}
@@ -64,7 +64,7 @@
StatisticsCalculator statistics;
MockExpand expand(&bgn, &sync_buffer, &random_vector, &statistics, fs,
channels);
- Normal normal(fs, &db, bgn, &expand);
+ Normal normal(fs, &db, bgn, &expand, &statistics);
int16_t input[1000] = {0};
AudioMultiVector output(channels);
@@ -99,7 +99,7 @@
StatisticsCalculator statistics;
MockExpand expand(&bgn, &sync_buffer, &random_vector, &statistics, fs,
channels);
- Normal normal(fs, &db, bgn, &expand);
+ Normal normal(fs, &db, bgn, &expand, &statistics);
int16_t input[1000] = {0};
AudioMultiVector output(channels);
@@ -124,7 +124,7 @@
StatisticsCalculator statistics;
MockExpand expand(&bgn, &sync_buffer, &random_vector, &statistics, kFs,
kChannels);
- Normal normal(kFs, &db, bgn, &expand);
+ Normal normal(kFs, &db, bgn, &expand, &statistics);
int16_t input[kPacketsizeBytes] = {0};
AudioMultiVector output(kChannels);
diff --git a/modules/audio_processing/BUILD.gn b/modules/audio_processing/BUILD.gn
index dbb1882..a733612 100644
--- a/modules/audio_processing/BUILD.gn
+++ b/modules/audio_processing/BUILD.gn
@@ -43,7 +43,6 @@
"../../api/audio:aec3_config",
"../../api/audio:audio_frame_api",
"../../api/audio:echo_control",
- "../../rtc_base:deprecation",
"../../rtc_base:rtc_base_approved",
"../../rtc_base/system:arch",
"../../rtc_base/system:file_wrapper",
@@ -119,8 +118,8 @@
deps = [
":api",
":audio_frame_view",
- "../../rtc_base:deprecation",
]
+ absl_deps = [ "//third_party/abseil-cpp/absl/base:core_headers" ]
}
rtc_library("audio_processing") {
@@ -177,7 +176,6 @@
"../../common_audio:common_audio_c",
"../../common_audio/third_party/ooura:fft_size_256",
"../../rtc_base:checks",
- "../../rtc_base:deprecation",
"../../rtc_base:gtest_prod",
"../../rtc_base:ignore_wundef",
"../../rtc_base:refcount",
@@ -197,6 +195,7 @@
"agc2:adaptive_digital",
"agc2:fixed_digital",
"agc2:gain_applier",
+ "capture_levels_adjuster",
"ns",
"transient:transient_suppressor_api",
"vad",
@@ -291,6 +290,7 @@
"../../rtc_base:checks",
"../../rtc_base:rtc_base_approved",
]
+ absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
defines = []
}
@@ -308,141 +308,146 @@
]
}
- group("audio_processing_tests") {
- testonly = true
- deps = [
- ":audioproc_test_utils",
- "transient:click_annotate",
- "transient:transient_suppression_test",
- ]
-
- if (rtc_enable_protobuf) {
- deps += [
- ":audioproc_unittest_proto",
- "aec_dump:aec_dump_unittests",
- "test/conversational_speech",
- "test/py_quality_assessment",
- ]
- }
- }
-
- rtc_library("audio_processing_unittests") {
- testonly = true
-
- configs += [ ":apm_debug_dump" ]
- sources = [
- "audio_buffer_unittest.cc",
- "audio_frame_view_unittest.cc",
- "config_unittest.cc",
- "echo_control_mobile_unittest.cc",
- "gain_controller2_unittest.cc",
- "splitting_filter_unittest.cc",
- "test/fake_recording_device_unittest.cc",
- ]
-
- deps = [
- ":analog_mic_simulation",
- ":api",
- ":apm_logging",
- ":audio_buffer",
- ":audio_frame_view",
- ":audio_processing",
- ":audioproc_test_utils",
- ":config",
- ":high_pass_filter",
- ":mocks",
- ":voice_detection",
- "../../api:array_view",
- "../../api:scoped_refptr",
- "../../api/audio:aec3_config",
- "../../api/audio:aec3_factory",
- "../../common_audio",
- "../../common_audio:common_audio_c",
- "../../rtc_base",
- "../../rtc_base:checks",
- "../../rtc_base:gtest_prod",
- "../../rtc_base:ignore_wundef",
- "../../rtc_base:protobuf_utils",
- "../../rtc_base:rtc_base_approved",
- "../../rtc_base:rtc_base_tests_utils",
- "../../rtc_base:safe_minmax",
- "../../rtc_base:task_queue_for_test",
- "../../rtc_base/synchronization:mutex",
- "../../rtc_base/system:arch",
- "../../rtc_base/system:file_wrapper",
- "../../system_wrappers",
- "../../test:fileutils",
- "../../test:rtc_expect_death",
- "../../test:test_support",
- "../audio_coding:neteq_input_audio_tools",
- "aec_dump:mock_aec_dump_unittests",
- "agc:agc_unittests",
- "agc2:adaptive_digital_unittests",
- "agc2:biquad_filter_unittests",
- "agc2:fixed_digital_unittests",
- "agc2:noise_estimator_unittests",
- "agc2:rnn_vad_with_level_unittests",
- "agc2:test_utils",
- "agc2/rnn_vad:unittests",
- "test/conversational_speech:unittest",
- "transient:transient_suppression_unittests",
- "utility:legacy_delay_estimator_unittest",
- "utility:pffft_wrapper_unittest",
- "vad:vad_unittests",
- "//testing/gtest",
- ]
- absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
-
- defines = []
-
- if (rtc_prefer_fixed_point) {
- defines += [ "WEBRTC_AUDIOPROC_FIXED_PROFILE" ]
- } else {
- defines += [ "WEBRTC_AUDIOPROC_FLOAT_PROFILE" ]
- }
-
- if (rtc_enable_protobuf) {
- defines += [ "WEBRTC_AUDIOPROC_DEBUG_DUMP" ]
- deps += [
- ":audioproc_debug_proto",
- ":audioproc_protobuf_utils",
+ if (!build_with_chromium) {
+ group("audio_processing_tests") {
+ testonly = true
+ deps = [
":audioproc_test_utils",
- ":audioproc_unittest_proto",
- ":optionally_built_submodule_creators",
- ":rms_level",
- ":runtime_settings_protobuf_utils",
- "../../api/audio:audio_frame_api",
- "../../api/audio:echo_control",
+ "transient:click_annotate",
+ "transient:transient_suppression_test",
+ ]
+
+ if (rtc_enable_protobuf) {
+ deps += [
+ ":audioproc_unittest_proto",
+ "aec_dump:aec_dump_unittests",
+ "test/conversational_speech",
+ "test/py_quality_assessment",
+ ]
+ }
+ }
+
+ rtc_library("audio_processing_unittests") {
+ testonly = true
+
+ configs += [ ":apm_debug_dump" ]
+ sources = [
+ "audio_buffer_unittest.cc",
+ "audio_frame_view_unittest.cc",
+ "config_unittest.cc",
+ "echo_control_mobile_unittest.cc",
+ "gain_controller2_unittest.cc",
+ "splitting_filter_unittest.cc",
+ "test/fake_recording_device_unittest.cc",
+ ]
+
+ deps = [
+ ":analog_mic_simulation",
+ ":api",
+ ":apm_logging",
+ ":audio_buffer",
+ ":audio_frame_view",
+ ":audio_processing",
+ ":audioproc_test_utils",
+ ":config",
+ ":high_pass_filter",
+ ":mocks",
+ ":voice_detection",
+ "../../api:array_view",
+ "../../api:scoped_refptr",
+ "../../api/audio:aec3_config",
+ "../../api/audio:aec3_factory",
+ "../../common_audio",
+ "../../common_audio:common_audio_c",
+ "../../rtc_base",
+ "../../rtc_base:checks",
+ "../../rtc_base:gtest_prod",
+ "../../rtc_base:ignore_wundef",
+ "../../rtc_base:protobuf_utils",
+ "../../rtc_base:rtc_base_approved",
"../../rtc_base:rtc_base_tests_utils",
- "../../rtc_base:rtc_task_queue",
- "aec_dump",
- "aec_dump:aec_dump_unittests",
+ "../../rtc_base:safe_minmax",
+ "../../rtc_base:task_queue_for_test",
+ "../../rtc_base:threading",
+ "../../rtc_base/synchronization:mutex",
+ "../../rtc_base/system:arch",
+ "../../rtc_base/system:file_wrapper",
+ "../../system_wrappers",
+ "../../test:fileutils",
+ "../../test:rtc_expect_death",
+ "../../test:test_support",
+ "../audio_coding:neteq_input_audio_tools",
+ "aec_dump:mock_aec_dump_unittests",
+ "agc:agc_unittests",
+ "agc2:adaptive_digital_unittests",
+ "agc2:biquad_filter_unittests",
+ "agc2:fixed_digital_unittests",
+ "agc2:noise_estimator_unittests",
+ "agc2:rnn_vad_with_level_unittests",
+ "agc2:test_utils",
+ "agc2/rnn_vad:unittests",
+ "capture_levels_adjuster",
+ "capture_levels_adjuster:capture_levels_adjuster_unittests",
+ "test/conversational_speech:unittest",
+ "transient:transient_suppression_unittests",
+ "utility:legacy_delay_estimator_unittest",
+ "utility:pffft_wrapper_unittest",
+ "vad:vad_unittests",
+ "//testing/gtest",
]
- absl_deps += [ "//third_party/abseil-cpp/absl/flags:flag" ]
- sources += [
- "audio_processing_impl_locking_unittest.cc",
- "audio_processing_impl_unittest.cc",
- "audio_processing_unittest.cc",
- "echo_control_mobile_bit_exact_unittest.cc",
- "echo_detector/circular_buffer_unittest.cc",
- "echo_detector/mean_variance_estimator_unittest.cc",
- "echo_detector/moving_max_unittest.cc",
- "echo_detector/normalized_covariance_estimator_unittest.cc",
- "gain_control_unittest.cc",
- "high_pass_filter_unittest.cc",
- "level_estimator_unittest.cc",
- "residual_echo_detector_unittest.cc",
- "rms_level_unittest.cc",
- "test/debug_dump_replayer.cc",
- "test/debug_dump_replayer.h",
- "test/debug_dump_test.cc",
- "test/echo_canceller_test_tools.cc",
- "test/echo_canceller_test_tools.h",
- "test/echo_canceller_test_tools_unittest.cc",
- "test/echo_control_mock.h",
- "test/test_utils.h",
- "voice_detection_unittest.cc",
- ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
+
+ defines = []
+
+ if (rtc_prefer_fixed_point) {
+ defines += [ "WEBRTC_AUDIOPROC_FIXED_PROFILE" ]
+ } else {
+ defines += [ "WEBRTC_AUDIOPROC_FLOAT_PROFILE" ]
+ }
+
+ if (rtc_enable_protobuf) {
+ defines += [ "WEBRTC_AUDIOPROC_DEBUG_DUMP" ]
+ deps += [
+ ":audioproc_debug_proto",
+ ":audioproc_protobuf_utils",
+ ":audioproc_test_utils",
+ ":audioproc_unittest_proto",
+ ":optionally_built_submodule_creators",
+ ":rms_level",
+ ":runtime_settings_protobuf_utils",
+ "../../api/audio:audio_frame_api",
+ "../../api/audio:echo_control",
+ "../../rtc_base:rtc_base_tests_utils",
+ "../../rtc_base:rtc_task_queue",
+ "aec_dump",
+ "aec_dump:aec_dump_unittests",
+ ]
+ absl_deps += [ "//third_party/abseil-cpp/absl/flags:flag" ]
+ sources += [
+ "audio_processing_impl_locking_unittest.cc",
+ "audio_processing_impl_unittest.cc",
+ "audio_processing_unittest.cc",
+ "echo_control_mobile_bit_exact_unittest.cc",
+ "echo_detector/circular_buffer_unittest.cc",
+ "echo_detector/mean_variance_estimator_unittest.cc",
+ "echo_detector/moving_max_unittest.cc",
+ "echo_detector/normalized_covariance_estimator_unittest.cc",
+ "gain_control_unittest.cc",
+ "high_pass_filter_unittest.cc",
+ "level_estimator_unittest.cc",
+ "residual_echo_detector_unittest.cc",
+ "rms_level_unittest.cc",
+ "test/debug_dump_replayer.cc",
+ "test/debug_dump_replayer.h",
+ "test/debug_dump_test.cc",
+ "test/echo_canceller_test_tools.cc",
+ "test/echo_canceller_test_tools.h",
+ "test/echo_canceller_test_tools_unittest.cc",
+ "test/echo_control_mock.h",
+ "test/test_utils.h",
+ "voice_detection_unittest.cc",
+ ]
+ }
}
}
@@ -480,7 +485,7 @@
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
- if (rtc_enable_protobuf) {
+ if (rtc_enable_protobuf && !build_with_chromium) {
rtc_library("audioproc_f_impl") {
testonly = true
configs += [ ":apm_debug_dump" ]
diff --git a/modules/audio_processing/aec3/BUILD.gn b/modules/audio_processing/aec3/BUILD.gn
index c98fa4c..3ce4943 100644
--- a/modules/audio_processing/aec3/BUILD.gn
+++ b/modules/audio_processing/aec3/BUILD.gn
@@ -302,7 +302,6 @@
"..:apm_logging",
"..:audio_buffer",
"..:audio_processing",
- "..:audio_processing_unittests",
"..:high_pass_filter",
"../../../api:array_view",
"../../../api/audio:aec3_config",
@@ -363,5 +362,9 @@
"vector_math_unittest.cc",
]
}
+
+ if (!build_with_chromium) {
+ deps += [ "..:audio_processing_unittests" ]
+ }
}
}
diff --git a/modules/audio_processing/aec3/block_processor.cc b/modules/audio_processing/aec3/block_processor.cc
index f2f3261..2ee32b8 100644
--- a/modules/audio_processing/aec3/block_processor.cc
+++ b/modules/audio_processing/aec3/block_processor.cc
@@ -63,6 +63,7 @@
void GetMetrics(EchoControl::Metrics* metrics) const override;
void SetAudioBufferDelay(int delay_ms) override;
+ void SetCaptureOutputUsage(bool capture_output_used) override;
private:
static int instance_count_;
@@ -237,6 +238,10 @@
render_buffer_->SetAudioBufferDelay(delay_ms);
}
+void BlockProcessorImpl::SetCaptureOutputUsage(bool capture_output_used) {
+ echo_remover_->SetCaptureOutputUsage(capture_output_used);
+}
+
} // namespace
BlockProcessor* BlockProcessor::Create(const EchoCanceller3Config& config,
diff --git a/modules/audio_processing/aec3/block_processor.h b/modules/audio_processing/aec3/block_processor.h
index 9bb0cf1..41ce016 100644
--- a/modules/audio_processing/aec3/block_processor.h
+++ b/modules/audio_processing/aec3/block_processor.h
@@ -69,6 +69,12 @@
// Reports whether echo leakage has been detected in the echo canceller
// output.
virtual void UpdateEchoLeakageStatus(bool leakage_detected) = 0;
+
+ // Specifies whether the capture output will be used. The purpose of this is
+ // to allow the block processor to deactivate some of the processing when the
+ // resulting output is anyway not used, for instance when the endpoint is
+ // muted.
+ virtual void SetCaptureOutputUsage(bool capture_output_used) = 0;
};
} // namespace webrtc
diff --git a/modules/audio_processing/aec3/echo_canceller3.cc b/modules/audio_processing/aec3/echo_canceller3.cc
index 98da232..35a2cff 100644
--- a/modules/audio_processing/aec3/echo_canceller3.cc
+++ b/modules/audio_processing/aec3/echo_canceller3.cc
@@ -251,6 +251,10 @@
adjusted_cfg.filter.initial_state_seconds = 2.0f;
}
+ if (field_trial::IsEnabled("WebRTC-Aec3HighPassFilterEchoReference")) {
+ adjusted_cfg.filter.high_pass_filter_echo_reference = true;
+ }
+
if (field_trial::IsEnabled("WebRTC-Aec3EchoSaturationDetectionKillSwitch")) {
adjusted_cfg.ep_strength.echo_can_saturate = false;
}
@@ -574,6 +578,7 @@
class EchoCanceller3::RenderWriter {
public:
RenderWriter(ApmDataDumper* data_dumper,
+ const EchoCanceller3Config& config,
SwapQueue<std::vector<std::vector<std::vector<float>>>,
Aec3RenderQueueItemVerifier>* render_transfer_queue,
size_t num_bands,
@@ -590,7 +595,7 @@
ApmDataDumper* data_dumper_;
const size_t num_bands_;
const size_t num_channels_;
- HighPassFilter high_pass_filter_;
+ std::unique_ptr<HighPassFilter> high_pass_filter_;
std::vector<std::vector<std::vector<float>>> render_queue_input_frame_;
SwapQueue<std::vector<std::vector<std::vector<float>>>,
Aec3RenderQueueItemVerifier>* render_transfer_queue_;
@@ -598,6 +603,7 @@
EchoCanceller3::RenderWriter::RenderWriter(
ApmDataDumper* data_dumper,
+ const EchoCanceller3Config& config,
SwapQueue<std::vector<std::vector<std::vector<float>>>,
Aec3RenderQueueItemVerifier>* render_transfer_queue,
size_t num_bands,
@@ -605,7 +611,6 @@
: data_dumper_(data_dumper),
num_bands_(num_bands),
num_channels_(num_channels),
- high_pass_filter_(16000, num_channels),
render_queue_input_frame_(
num_bands_,
std::vector<std::vector<float>>(
@@ -613,6 +618,9 @@
std::vector<float>(AudioBuffer::kSplitBandSize, 0.f))),
render_transfer_queue_(render_transfer_queue) {
RTC_DCHECK(data_dumper);
+ if (config.filter.high_pass_filter_echo_reference) {
+ high_pass_filter_ = std::make_unique<HighPassFilter>(16000, num_channels);
+ }
}
EchoCanceller3::RenderWriter::~RenderWriter() = default;
@@ -631,7 +639,9 @@
CopyBufferIntoFrame(input, num_bands_, num_channels_,
&render_queue_input_frame_);
- high_pass_filter_.Process(&render_queue_input_frame_[0]);
+ if (high_pass_filter_) {
+ high_pass_filter_->Process(&render_queue_input_frame_[0]);
+ }
static_cast<void>(render_transfer_queue_->Insert(&render_queue_input_frame_));
}
@@ -704,7 +714,7 @@
config_.delay.fixed_capture_delay_samples));
}
- render_writer_.reset(new RenderWriter(data_dumper_.get(),
+ render_writer_.reset(new RenderWriter(data_dumper_.get(), config_,
&render_transfer_queue_, num_bands_,
num_render_channels_));
@@ -823,6 +833,11 @@
block_processor_->SetAudioBufferDelay(delay_ms);
}
+void EchoCanceller3::SetCaptureOutputUsage(bool capture_output_used) {
+ RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_);
+ block_processor_->SetCaptureOutputUsage(capture_output_used);
+}
+
bool EchoCanceller3::ActiveProcessing() const {
return true;
}
diff --git a/modules/audio_processing/aec3/echo_canceller3.h b/modules/audio_processing/aec3/echo_canceller3.h
index bacd5df..a4aab49 100644
--- a/modules/audio_processing/aec3/echo_canceller3.h
+++ b/modules/audio_processing/aec3/echo_canceller3.h
@@ -118,6 +118,12 @@
// Provides an optional external estimate of the audio buffer delay.
void SetAudioBufferDelay(int delay_ms) override;
+ // Specifies whether the capture output will be used. The purpose of this is
+ // to allow the echo controller to deactivate some of the processing when the
+ // resulting output is anyway not used, for instance when the endpoint is
+ // muted.
+ void SetCaptureOutputUsage(bool capture_output_used) override;
+
bool ActiveProcessing() const override;
// Signals whether an external detector has detected echo leakage from the
diff --git a/modules/audio_processing/aec3/echo_canceller3_unittest.cc b/modules/audio_processing/aec3/echo_canceller3_unittest.cc
index a02cfa3..4a3c466 100644
--- a/modules/audio_processing/aec3/echo_canceller3_unittest.cc
+++ b/modules/audio_processing/aec3/echo_canceller3_unittest.cc
@@ -131,6 +131,8 @@
void GetMetrics(EchoControl::Metrics* metrics) const override {}
void SetAudioBufferDelay(int delay_ms) override {}
+
+ void SetCaptureOutputUsage(bool capture_output_used) {}
};
// Class for testing that the render data is properly received by the block
@@ -169,6 +171,8 @@
void SetAudioBufferDelay(int delay_ms) override {}
+ void SetCaptureOutputUsage(bool capture_output_used) {}
+
private:
std::deque<std::vector<std::vector<std::vector<float>>>>
received_render_blocks_;
@@ -252,8 +256,6 @@
capture_output.push_back(capture_buffer_.split_bands(0)[0][k]);
}
}
- HighPassFilter hp_filter(16000, 1);
- hp_filter.Process(&render_input);
EXPECT_TRUE(
VerifyOutputFrameBitexactness(render_input[0], capture_output, -64));
@@ -545,8 +547,6 @@
capture_output.push_back(capture_buffer_.split_bands(0)[0][k]);
}
}
- HighPassFilter hp_filter(16000, 1);
- hp_filter.Process(&render_input);
EXPECT_TRUE(
VerifyOutputFrameBitexactness(render_input[0], capture_output, -64));
diff --git a/modules/audio_processing/aec3/echo_remover.cc b/modules/audio_processing/aec3/echo_remover.cc
index df539bf..1a83fef 100644
--- a/modules/audio_processing/aec3/echo_remover.cc
+++ b/modules/audio_processing/aec3/echo_remover.cc
@@ -132,6 +132,10 @@
echo_leakage_detected_ = leakage_detected;
}
+ void SetCaptureOutputUsage(bool capture_output_used) override {
+ capture_output_used_ = capture_output_used;
+ }
+
private:
// Selects which of the coarse and refined linear filter outputs that is most
// appropriate to pass to the suppressor and forms the linear filter output by
@@ -155,6 +159,7 @@
RenderSignalAnalyzer render_signal_analyzer_;
ResidualEchoEstimator residual_echo_estimator_;
bool echo_leakage_detected_ = false;
+ bool capture_output_used_ = true;
AecState aec_state_;
EchoRemoverMetrics metrics_;
std::vector<std::array<float, kFftLengthBy2>> e_old_;
@@ -391,42 +396,49 @@
1);
data_dumper_->DumpWav("aec3_output_linear2", kBlockSize, &e[0][0], 16000, 1);
- // Estimate the residual echo power.
- residual_echo_estimator_.Estimate(aec_state_, *render_buffer, S2_linear, Y2,
- R2);
-
// Estimate the comfort noise.
cng_.Compute(aec_state_.SaturatedCapture(), Y2, comfort_noise,
high_band_comfort_noise);
- // Suppressor nearend estimate.
- if (aec_state_.UsableLinearEstimate()) {
- // E2 is bound by Y2.
- for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
- std::transform(E2[ch].begin(), E2[ch].end(), Y2[ch].begin(),
- E2[ch].begin(),
- [](float a, float b) { return std::min(a, b); });
- }
- }
- const auto& nearend_spectrum = aec_state_.UsableLinearEstimate() ? E2 : Y2;
-
- // Suppressor echo estimate.
- const auto& echo_spectrum =
- aec_state_.UsableLinearEstimate() ? S2_linear : R2;
-
- // Determine if the suppressor should assume clock drift.
- const bool clock_drift = config_.echo_removal_control.has_clock_drift ||
- echo_path_variability.clock_drift;
-
- // Compute preferred gains.
- float high_bands_gain;
+ // Only do the below processing if the output of the audio processing module
+ // is used.
std::array<float, kFftLengthBy2Plus1> G;
- suppression_gain_.GetGain(nearend_spectrum, echo_spectrum, R2,
- cng_.NoiseSpectrum(), render_signal_analyzer_,
- aec_state_, x, clock_drift, &high_bands_gain, &G);
+ if (capture_output_used_) {
+ // Estimate the residual echo power.
+ residual_echo_estimator_.Estimate(aec_state_, *render_buffer, S2_linear, Y2,
+ R2);
- suppression_filter_.ApplyGain(comfort_noise, high_band_comfort_noise, G,
- high_bands_gain, Y_fft, y);
+ // Suppressor nearend estimate.
+ if (aec_state_.UsableLinearEstimate()) {
+ // E2 is bound by Y2.
+ for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
+ std::transform(E2[ch].begin(), E2[ch].end(), Y2[ch].begin(),
+ E2[ch].begin(),
+ [](float a, float b) { return std::min(a, b); });
+ }
+ }
+ const auto& nearend_spectrum = aec_state_.UsableLinearEstimate() ? E2 : Y2;
+
+ // Suppressor echo estimate.
+ const auto& echo_spectrum =
+ aec_state_.UsableLinearEstimate() ? S2_linear : R2;
+
+ // Determine if the suppressor should assume clock drift.
+ const bool clock_drift = config_.echo_removal_control.has_clock_drift ||
+ echo_path_variability.clock_drift;
+
+ // Compute preferred gains.
+ float high_bands_gain;
+ suppression_gain_.GetGain(nearend_spectrum, echo_spectrum, R2,
+ cng_.NoiseSpectrum(), render_signal_analyzer_,
+ aec_state_, x, clock_drift, &high_bands_gain, &G);
+
+ suppression_filter_.ApplyGain(comfort_noise, high_band_comfort_noise, G,
+ high_bands_gain, Y_fft, y);
+
+ } else {
+ G.fill(0.f);
+ }
// Update the metrics.
metrics_.Update(aec_state_, cng_.NoiseSpectrum()[0], G);
diff --git a/modules/audio_processing/aec3/echo_remover.h b/modules/audio_processing/aec3/echo_remover.h
index ef41646..486a9a7 100644
--- a/modules/audio_processing/aec3/echo_remover.h
+++ b/modules/audio_processing/aec3/echo_remover.h
@@ -48,6 +48,12 @@
// Updates the status on whether echo leakage is detected in the output of the
// echo remover.
virtual void UpdateEchoLeakageStatus(bool leakage_detected) = 0;
+
+ // Specifies whether the capture output will be used. The purpose of this is
+ // to allow the echo remover to deactivate some of the processing when the
+ // resulting output is anyway not used, for instance when the endpoint is
+ // muted.
+ virtual void SetCaptureOutputUsage(bool capture_output_used) = 0;
};
} // namespace webrtc
diff --git a/modules/audio_processing/aec3/mock/mock_block_processor.h b/modules/audio_processing/aec3/mock/mock_block_processor.h
index e1eb267..aa61225 100644
--- a/modules/audio_processing/aec3/mock/mock_block_processor.h
+++ b/modules/audio_processing/aec3/mock/mock_block_processor.h
@@ -44,6 +44,10 @@
(EchoControl::Metrics * metrics),
(const, override));
MOCK_METHOD(void, SetAudioBufferDelay, (int delay_ms), (override));
+ MOCK_METHOD(void,
+ SetCaptureOutputUsage,
+ (bool capture_output_used),
+ (override));
};
} // namespace test
diff --git a/modules/audio_processing/aec3/mock/mock_echo_remover.h b/modules/audio_processing/aec3/mock/mock_echo_remover.h
index 8a3044b..60c5bf4 100644
--- a/modules/audio_processing/aec3/mock/mock_echo_remover.h
+++ b/modules/audio_processing/aec3/mock/mock_echo_remover.h
@@ -44,6 +44,10 @@
GetMetrics,
(EchoControl::Metrics * metrics),
(const, override));
+ MOCK_METHOD(void,
+ SetCaptureOutputUsage,
+ (bool capture_output_used),
+ (override));
};
} // namespace test
diff --git a/modules/audio_processing/aec_dump/aec_dump_impl.cc b/modules/audio_processing/aec_dump/aec_dump_impl.cc
index 18f8572..db61b36 100644
--- a/modules/audio_processing/aec_dump/aec_dump_impl.cc
+++ b/modules/audio_processing/aec_dump/aec_dump_impl.cc
@@ -186,6 +186,12 @@
setting->set_capture_pre_gain(x);
break;
}
+ case AudioProcessing::RuntimeSetting::Type::kCapturePostGain: {
+ float x;
+ runtime_setting.GetFloat(&x);
+ setting->set_capture_post_gain(x);
+ break;
+ }
case AudioProcessing::RuntimeSetting::Type::
kCustomRenderProcessingRuntimeSetting: {
float x;
diff --git a/modules/audio_processing/agc/BUILD.gn b/modules/audio_processing/agc/BUILD.gn
index 8235456..5ad6644 100644
--- a/modules/audio_processing/agc/BUILD.gn
+++ b/modules/audio_processing/agc/BUILD.gn
@@ -33,7 +33,6 @@
"../../../rtc_base:safe_minmax",
"../../../system_wrappers:field_trial",
"../../../system_wrappers:metrics",
- "../agc2:level_estimation_agc",
"../vad",
]
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
diff --git a/modules/audio_processing/agc/agc_manager_direct.cc b/modules/audio_processing/agc/agc_manager_direct.cc
index 1428d2a..2454d1b 100644
--- a/modules/audio_processing/agc/agc_manager_direct.cc
+++ b/modules/audio_processing/agc/agc_manager_direct.cc
@@ -16,7 +16,6 @@
#include "common_audio/include/audio_util.h"
#include "modules/audio_processing/agc/gain_control.h"
#include "modules/audio_processing/agc/gain_map_internal.h"
-#include "modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.h"
#include "rtc_base/atomic_ops.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
@@ -138,24 +137,18 @@
MonoAgc::MonoAgc(ApmDataDumper* data_dumper,
int startup_min_level,
int clipped_level_min,
- bool use_agc2_level_estimation,
bool disable_digital_adaptive,
int min_mic_level)
: min_mic_level_(min_mic_level),
disable_digital_adaptive_(disable_digital_adaptive),
+ agc_(std::make_unique<Agc>()),
max_level_(kMaxMicLevel),
max_compression_gain_(kMaxCompressionGain),
target_compression_(kDefaultCompressionGain),
compression_(target_compression_),
compression_accumulator_(compression_),
startup_min_level_(ClampLevel(startup_min_level, min_mic_level_)),
- clipped_level_min_(clipped_level_min) {
- if (use_agc2_level_estimation) {
- agc_ = std::make_unique<AdaptiveModeLevelEstimatorAgc>(data_dumper);
- } else {
- agc_ = std::make_unique<Agc>();
- }
-}
+ clipped_level_min_(clipped_level_min) {}
MonoAgc::~MonoAgc() = default;
@@ -165,7 +158,7 @@
target_compression_ = disable_digital_adaptive_ ? 0 : kDefaultCompressionGain;
compression_ = disable_digital_adaptive_ ? 0 : target_compression_;
compression_accumulator_ = compression_;
- capture_muted_ = false;
+ capture_output_used_ = true;
check_volume_on_next_process_ = true;
}
@@ -263,14 +256,14 @@
<< ", max_compression_gain_=" << max_compression_gain_;
}
-void MonoAgc::SetCaptureMuted(bool muted) {
- if (capture_muted_ == muted) {
+void MonoAgc::HandleCaptureOutputUsedChange(bool capture_output_used) {
+ if (capture_output_used_ == capture_output_used) {
return;
}
- capture_muted_ = muted;
+ capture_output_used_ = capture_output_used;
- if (!muted) {
- // When we unmute, we should reset things to be safe.
+ if (capture_output_used) {
+ // When we start using the output, we should reset things to be safe.
check_volume_on_next_process_ = true;
}
}
@@ -415,7 +408,6 @@
: AgcManagerDirect(/*num_capture_channels*/ 1,
startup_min_level,
clipped_level_min,
- /*use_agc2_level_estimation*/ false,
/*disable_digital_adaptive*/ false,
sample_rate_hz) {
RTC_DCHECK(channel_agcs_[0]);
@@ -426,7 +418,6 @@
AgcManagerDirect::AgcManagerDirect(int num_capture_channels,
int startup_min_level,
int clipped_level_min,
- bool use_agc2_level_estimation,
bool disable_digital_adaptive,
int sample_rate_hz)
: data_dumper_(
@@ -436,7 +427,7 @@
num_capture_channels_(num_capture_channels),
disable_digital_adaptive_(disable_digital_adaptive),
frames_since_clipped_(kClippedWaitFrames),
- capture_muted_(false),
+ capture_output_used_(true),
channel_agcs_(num_capture_channels),
new_compressions_to_set_(num_capture_channels) {
const int min_mic_level = GetMinMicLevel();
@@ -445,7 +436,7 @@
channel_agcs_[ch] = std::make_unique<MonoAgc>(
data_dumper_ch, startup_min_level, clipped_level_min,
- use_agc2_level_estimation, disable_digital_adaptive_, min_mic_level);
+ disable_digital_adaptive_, min_mic_level);
}
RTC_DCHECK_LT(0, channel_agcs_.size());
channel_agcs_[0]->ActivateLogging();
@@ -459,7 +450,7 @@
for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) {
channel_agcs_[ch]->Initialize();
}
- capture_muted_ = false;
+ capture_output_used_ = true;
AggregateChannelLevels();
}
@@ -494,7 +485,7 @@
size_t samples_per_channel) {
RTC_DCHECK(audio);
AggregateChannelLevels();
- if (capture_muted_) {
+ if (!capture_output_used_) {
return;
}
@@ -529,7 +520,7 @@
void AgcManagerDirect::Process(const AudioBuffer* audio) {
AggregateChannelLevels();
- if (capture_muted_) {
+ if (!capture_output_used_) {
return;
}
@@ -558,11 +549,11 @@
return new_compressions_to_set_[channel_controlling_gain_];
}
-void AgcManagerDirect::SetCaptureMuted(bool muted) {
+void AgcManagerDirect::HandleCaptureOutputUsedChange(bool capture_output_used) {
for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) {
- channel_agcs_[ch]->SetCaptureMuted(muted);
+ channel_agcs_[ch]->HandleCaptureOutputUsedChange(capture_output_used);
}
- capture_muted_ = muted;
+ capture_output_used_ = capture_output_used;
}
float AgcManagerDirect::voice_probability() const {
diff --git a/modules/audio_processing/agc/agc_manager_direct.h b/modules/audio_processing/agc/agc_manager_direct.h
index d3663be..f9417cf 100644
--- a/modules/audio_processing/agc/agc_manager_direct.h
+++ b/modules/audio_processing/agc/agc_manager_direct.h
@@ -38,7 +38,6 @@
AgcManagerDirect(int num_capture_channels,
int startup_min_level,
int clipped_level_min,
- bool use_agc2_level_estimation,
bool disable_digital_adaptive,
int sample_rate_hz);
@@ -52,10 +51,9 @@
void AnalyzePreProcess(const AudioBuffer* audio);
void Process(const AudioBuffer* audio);
- // Call when the capture stream has been muted/unmuted. This causes the
- // manager to disregard all incoming audio; chances are good it's background
- // noise to which we'd like to avoid adapting.
- void SetCaptureMuted(bool muted);
+ // Call when the capture stream output has been flagged to be used/not-used.
+ // If unused, the manager disregards all incoming audio.
+ void HandleCaptureOutputUsedChange(bool capture_output_used);
float voice_probability() const;
int stream_analog_level() const { return stream_analog_level_; }
@@ -73,6 +71,16 @@
DisableDigitalDisablesDigital);
FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectStandaloneTest,
AgcMinMicLevelExperiment);
+ FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectStandaloneTest,
+ AgcMinMicLevelExperimentDisabled);
+ FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectStandaloneTest,
+ AgcMinMicLevelExperimentOutOfRangeAbove);
+ FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectStandaloneTest,
+ AgcMinMicLevelExperimentOutOfRangeBelow);
+ FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectStandaloneTest,
+ AgcMinMicLevelExperimentEnabled50);
+ FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectStandaloneTest,
+ AgcMinMicLevelExperimentEnabledAboveStartupLevel);
// Dependency injection for testing. Don't delete |agc| as the memory is owned
// by the manager.
@@ -94,7 +102,7 @@
int frames_since_clipped_;
int stream_analog_level_ = 0;
- bool capture_muted_;
+ bool capture_output_used_;
int channel_controlling_gain_ = 0;
std::vector<std::unique_ptr<MonoAgc>> channel_agcs_;
@@ -106,7 +114,6 @@
MonoAgc(ApmDataDumper* data_dumper,
int startup_min_level,
int clipped_level_min,
- bool use_agc2_level_estimation,
bool disable_digital_adaptive,
int min_mic_level);
~MonoAgc();
@@ -114,7 +121,7 @@
MonoAgc& operator=(const MonoAgc&) = delete;
void Initialize();
- void SetCaptureMuted(bool muted);
+ void HandleCaptureOutputUsedChange(bool capture_output_used);
void HandleClipping();
@@ -158,7 +165,7 @@
int target_compression_;
int compression_;
float compression_accumulator_;
- bool capture_muted_ = false;
+ bool capture_output_used_ = true;
bool check_volume_on_next_process_ = true;
bool startup_ = true;
int startup_min_level_;
diff --git a/modules/audio_processing/agc/agc_manager_direct_unittest.cc b/modules/audio_processing/agc/agc_manager_direct_unittest.cc
index 995801a..1954ed4 100644
--- a/modules/audio_processing/agc/agc_manager_direct_unittest.cc
+++ b/modules/audio_processing/agc/agc_manager_direct_unittest.cc
@@ -56,6 +56,13 @@
MOCK_METHOD(bool, stream_is_saturated, (), (const, override));
};
+std::unique_ptr<AgcManagerDirect> CreateAgcManagerDirect(
+ int startup_min_level) {
+ return std::make_unique<AgcManagerDirect>(
+ /*num_capture_channels=*/1, startup_min_level, kClippedMin,
+ /*disable_digital_adaptive=*/true, kSampleRateHz);
+}
+
} // namespace
class AgcManagerDirectTest : public ::testing::Test {
@@ -368,7 +375,7 @@
}
TEST_F(AgcManagerDirectTest, NoActionWhileMuted) {
- manager_.SetCaptureMuted(true);
+ manager_.HandleCaptureOutputUsedChange(false);
manager_.Process(nullptr);
absl::optional<int> new_digital_gain = manager_.GetDigitalComressionGain();
if (new_digital_gain) {
@@ -379,8 +386,8 @@
TEST_F(AgcManagerDirectTest, UnmutingChecksVolumeWithoutRaising) {
FirstProcess();
- manager_.SetCaptureMuted(true);
- manager_.SetCaptureMuted(false);
+ manager_.HandleCaptureOutputUsedChange(false);
+ manager_.HandleCaptureOutputUsedChange(true);
ExpectCheckVolumeAndReset(127);
// SetMicVolume should not be called.
EXPECT_CALL(*agc_, GetRmsErrorDb(_)).WillOnce(Return(false));
@@ -391,8 +398,8 @@
TEST_F(AgcManagerDirectTest, UnmutingRaisesTooLowVolume) {
FirstProcess();
- manager_.SetCaptureMuted(true);
- manager_.SetCaptureMuted(false);
+ manager_.HandleCaptureOutputUsedChange(false);
+ manager_.HandleCaptureOutputUsedChange(true);
ExpectCheckVolumeAndReset(11);
EXPECT_CALL(*agc_, GetRmsErrorDb(_)).WillOnce(Return(false));
CallProcess(1);
@@ -692,77 +699,78 @@
TEST(AgcManagerDirectStandaloneTest, DisableDigitalDisablesDigital) {
auto agc = std::unique_ptr<Agc>(new ::testing::NiceMock<MockAgc>());
MockGainControl gctrl;
- AgcManagerDirect manager(/* num_capture_channels */ 1, kInitialVolume,
- kClippedMin,
- /* use agc2 level estimation */ false,
- /* disable digital adaptive */ true, kSampleRateHz);
-
EXPECT_CALL(gctrl, set_mode(GainControl::kFixedDigital));
EXPECT_CALL(gctrl, set_target_level_dbfs(0));
EXPECT_CALL(gctrl, set_compression_gain_db(0));
EXPECT_CALL(gctrl, enable_limiter(false));
- manager.Initialize();
- manager.SetupDigitalGainControl(&gctrl);
+ std::unique_ptr<AgcManagerDirect> manager =
+ CreateAgcManagerDirect(kInitialVolume);
+ manager->Initialize();
+ manager->SetupDigitalGainControl(&gctrl);
}
TEST(AgcManagerDirectStandaloneTest, AgcMinMicLevelExperiment) {
- auto agc_man = std::unique_ptr<AgcManagerDirect>(new AgcManagerDirect(
- /* num_capture_channels */ 1, kInitialVolume, kClippedMin, true, true,
- kSampleRateHz));
- EXPECT_EQ(agc_man->channel_agcs_[0]->min_mic_level(), kMinMicLevel);
- EXPECT_EQ(agc_man->channel_agcs_[0]->startup_min_level(), kInitialVolume);
- {
- test::ScopedFieldTrials field_trial(
- "WebRTC-Audio-AgcMinMicLevelExperiment/Disabled/");
- agc_man.reset(new AgcManagerDirect(
- /* num_capture_channels */ 1, kInitialVolume, kClippedMin, true, true,
- kSampleRateHz));
- EXPECT_EQ(agc_man->channel_agcs_[0]->min_mic_level(), kMinMicLevel);
- EXPECT_EQ(agc_man->channel_agcs_[0]->startup_min_level(), kInitialVolume);
- }
- {
- // Valid range of field-trial parameter is [0,255].
- test::ScopedFieldTrials field_trial(
- "WebRTC-Audio-AgcMinMicLevelExperiment/Enabled-256/");
- agc_man.reset(new AgcManagerDirect(
- /* num_capture_channels */ 1, kInitialVolume, kClippedMin, true, true,
- kSampleRateHz));
- EXPECT_EQ(agc_man->channel_agcs_[0]->min_mic_level(), kMinMicLevel);
- EXPECT_EQ(agc_man->channel_agcs_[0]->startup_min_level(), kInitialVolume);
- }
- {
- test::ScopedFieldTrials field_trial(
- "WebRTC-Audio-AgcMinMicLevelExperiment/Enabled--1/");
- agc_man.reset(new AgcManagerDirect(
- /* num_capture_channels */ 1, kInitialVolume, kClippedMin, true, true,
- kSampleRateHz));
- EXPECT_EQ(agc_man->channel_agcs_[0]->min_mic_level(), kMinMicLevel);
- EXPECT_EQ(agc_man->channel_agcs_[0]->startup_min_level(), kInitialVolume);
- }
- {
- // Verify that a valid experiment changes the minimum microphone level.
- // The start volume is larger than the min level and should therefore not
- // be changed.
- test::ScopedFieldTrials field_trial(
- "WebRTC-Audio-AgcMinMicLevelExperiment/Enabled-50/");
- agc_man.reset(new AgcManagerDirect(
- /* num_capture_channels */ 1, kInitialVolume, kClippedMin, true, true,
- kSampleRateHz));
- EXPECT_EQ(agc_man->channel_agcs_[0]->min_mic_level(), 50);
- EXPECT_EQ(agc_man->channel_agcs_[0]->startup_min_level(), kInitialVolume);
- }
- {
- // Use experiment to reduce the default minimum microphone level, start at
- // a lower level and ensure that the startup level is increased to the min
- // level set by the experiment.
- test::ScopedFieldTrials field_trial(
- "WebRTC-Audio-AgcMinMicLevelExperiment/Enabled-50/");
- agc_man.reset(new AgcManagerDirect(/* num_capture_channels */ 1, 30,
- kClippedMin, true, true, kSampleRateHz));
- EXPECT_EQ(agc_man->channel_agcs_[0]->min_mic_level(), 50);
- EXPECT_EQ(agc_man->channel_agcs_[0]->startup_min_level(), 50);
- }
+ std::unique_ptr<AgcManagerDirect> manager =
+ CreateAgcManagerDirect(kInitialVolume);
+ EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevel);
+ EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), kInitialVolume);
+}
+
+TEST(AgcManagerDirectStandaloneTest, AgcMinMicLevelExperimentDisabled) {
+ test::ScopedFieldTrials field_trial(
+ "WebRTC-Audio-AgcMinMicLevelExperiment/Disabled/");
+ std::unique_ptr<AgcManagerDirect> manager =
+ CreateAgcManagerDirect(kInitialVolume);
+ EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevel);
+ EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), kInitialVolume);
+}
+
+// Checks that a field-trial parameter outside of the valid range [0,255] is
+// ignored.
+TEST(AgcManagerDirectStandaloneTest, AgcMinMicLevelExperimentOutOfRangeAbove) {
+ test::ScopedFieldTrials field_trial(
+ "WebRTC-Audio-AgcMinMicLevelExperiment/Enabled-256/");
+ std::unique_ptr<AgcManagerDirect> manager =
+ CreateAgcManagerDirect(kInitialVolume);
+ EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevel);
+ EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), kInitialVolume);
+}
+
+// Checks that a field-trial parameter outside of the valid range [0,255] is
+// ignored.
+TEST(AgcManagerDirectStandaloneTest, AgcMinMicLevelExperimentOutOfRangeBelow) {
+ test::ScopedFieldTrials field_trial(
+ "WebRTC-Audio-AgcMinMicLevelExperiment/Enabled--1/");
+ std::unique_ptr<AgcManagerDirect> manager =
+ CreateAgcManagerDirect(kInitialVolume);
+ EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevel);
+ EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), kInitialVolume);
+}
+
+// Verifies that a valid experiment changes the minimum microphone level. The
+// start volume is larger than the min level and should therefore not be
+// changed.
+TEST(AgcManagerDirectStandaloneTest, AgcMinMicLevelExperimentEnabled50) {
+ test::ScopedFieldTrials field_trial(
+ "WebRTC-Audio-AgcMinMicLevelExperiment/Enabled-50/");
+ std::unique_ptr<AgcManagerDirect> manager =
+ CreateAgcManagerDirect(kInitialVolume);
+ EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), 50);
+ EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), kInitialVolume);
+}
+
+// Uses experiment to reduce the default minimum microphone level, start at a
+// lower level and ensure that the startup level is increased to the min level
+// set by the experiment.
+TEST(AgcManagerDirectStandaloneTest,
+ AgcMinMicLevelExperimentEnabledAboveStartupLevel) {
+ test::ScopedFieldTrials field_trial(
+ "WebRTC-Audio-AgcMinMicLevelExperiment/Enabled-50/");
+ std::unique_ptr<AgcManagerDirect> manager =
+ CreateAgcManagerDirect(/*startup_min_level=*/30);
+ EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), 50);
+ EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), 50);
}
} // namespace webrtc
diff --git a/modules/audio_processing/agc2/BUILD.gn b/modules/audio_processing/agc2/BUILD.gn
index 7b71f6a..8f2ee0f 100644
--- a/modules/audio_processing/agc2/BUILD.gn
+++ b/modules/audio_processing/agc2/BUILD.gn
@@ -15,31 +15,6 @@
]
}
-rtc_library("level_estimation_agc") {
- sources = [
- "adaptive_mode_level_estimator_agc.cc",
- "adaptive_mode_level_estimator_agc.h",
- ]
- configs += [ "..:apm_debug_dump" ]
- deps = [
- ":adaptive_digital",
- ":common",
- ":gain_applier",
- ":noise_level_estimator",
- ":rnn_vad_with_level",
- "..:api",
- "..:apm_logging",
- "..:audio_frame_view",
- "../../../api:array_view",
- "../../../common_audio",
- "../../../rtc_base:checks",
- "../../../rtc_base:rtc_base_approved",
- "../../../rtc_base:safe_minmax",
- "../agc:level_estimation",
- "../vad",
- ]
-}
-
rtc_library("adaptive_digital") {
sources = [
"adaptive_agc.cc",
diff --git a/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.cc b/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.cc
deleted file mode 100644
index 5ceeb7d..0000000
--- a/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (c) 2018 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 "modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.h"
-
-#include <cmath>
-#include <vector>
-
-#include "modules/audio_processing/agc2/agc2_common.h"
-#include "modules/audio_processing/include/audio_frame_view.h"
-
-namespace webrtc {
-
-AdaptiveModeLevelEstimatorAgc::AdaptiveModeLevelEstimatorAgc(
- ApmDataDumper* apm_data_dumper)
- : level_estimator_(apm_data_dumper) {
- set_target_level_dbfs(kDefaultAgc2LevelHeadroomDbfs);
-}
-
-// |audio| must be mono; in a multi-channel stream, provide the first (usually
-// left) channel.
-void AdaptiveModeLevelEstimatorAgc::Process(const int16_t* audio,
- size_t length,
- int sample_rate_hz) {
- std::vector<float> float_audio_frame(audio, audio + length);
- const float* const first_channel = &float_audio_frame[0];
- AudioFrameView<const float> frame_view(&first_channel, 1 /* num channels */,
- length);
- const auto vad_prob = agc2_vad_.AnalyzeFrame(frame_view);
- latest_voice_probability_ = vad_prob.speech_probability;
- if (latest_voice_probability_ > kVadConfidenceThreshold) {
- time_in_ms_since_last_estimate_ += kFrameDurationMs;
- }
- level_estimator_.Update(vad_prob);
-}
-
-// Retrieves the difference between the target RMS level and the current
-// signal RMS level in dB. Returns true if an update is available and false
-// otherwise, in which case |error| should be ignored and no action taken.
-bool AdaptiveModeLevelEstimatorAgc::GetRmsErrorDb(int* error) {
- if (time_in_ms_since_last_estimate_ <= kTimeUntilConfidentMs) {
- return false;
- }
- *error =
- std::floor(target_level_dbfs() - level_estimator_.level_dbfs() + 0.5f);
- time_in_ms_since_last_estimate_ = 0;
- return true;
-}
-
-void AdaptiveModeLevelEstimatorAgc::Reset() {
- level_estimator_.Reset();
-}
-
-float AdaptiveModeLevelEstimatorAgc::voice_probability() const {
- return latest_voice_probability_;
-}
-
-} // namespace webrtc
diff --git a/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.h b/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.h
deleted file mode 100644
index bc6fa84..0000000
--- a/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2018 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 MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_MODE_LEVEL_ESTIMATOR_AGC_H_
-#define MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_MODE_LEVEL_ESTIMATOR_AGC_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "modules/audio_processing/agc/agc.h"
-#include "modules/audio_processing/agc2/adaptive_mode_level_estimator.h"
-#include "modules/audio_processing/agc2/saturation_protector.h"
-#include "modules/audio_processing/agc2/vad_with_level.h"
-
-namespace webrtc {
-class AdaptiveModeLevelEstimatorAgc : public Agc {
- public:
- explicit AdaptiveModeLevelEstimatorAgc(ApmDataDumper* apm_data_dumper);
-
- // |audio| must be mono; in a multi-channel stream, provide the first (usually
- // left) channel.
- void Process(const int16_t* audio,
- size_t length,
- int sample_rate_hz) override;
-
- // Retrieves the difference between the target RMS level and the current
- // signal RMS level in dB. Returns true if an update is available and false
- // otherwise, in which case |error| should be ignored and no action taken.
- bool GetRmsErrorDb(int* error) override;
- void Reset() override;
-
- float voice_probability() const override;
-
- private:
- static constexpr int kTimeUntilConfidentMs = 700;
- static constexpr int kDefaultAgc2LevelHeadroomDbfs = -1;
- int32_t time_in_ms_since_last_estimate_ = 0;
- AdaptiveModeLevelEstimator level_estimator_;
- VadLevelAnalyzer agc2_vad_;
- float latest_voice_probability_ = 0.f;
-};
-} // namespace webrtc
-
-#endif // MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_MODE_LEVEL_ESTIMATOR_AGC_H_
diff --git a/modules/audio_processing/agc2/rnn_vad/BUILD.gn b/modules/audio_processing/agc2/rnn_vad/BUILD.gn
index 4732efd..bc848b3 100644
--- a/modules/audio_processing/agc2/rnn_vad/BUILD.gn
+++ b/modules/audio_processing/agc2/rnn_vad/BUILD.gn
@@ -312,20 +312,22 @@
}
}
- rtc_executable("rnn_vad_tool") {
- testonly = true
- sources = [ "rnn_vad_tool.cc" ]
- deps = [
- ":rnn_vad",
- ":rnn_vad_common",
- "..:cpu_features",
- "../../../../api:array_view",
- "../../../../common_audio",
- "../../../../rtc_base:rtc_base_approved",
- "../../../../rtc_base:safe_compare",
- "../../../../test:test_support",
- "//third_party/abseil-cpp/absl/flags:flag",
- "//third_party/abseil-cpp/absl/flags:parse",
- ]
+ if (!build_with_chromium) {
+ rtc_executable("rnn_vad_tool") {
+ testonly = true
+ sources = [ "rnn_vad_tool.cc" ]
+ deps = [
+ ":rnn_vad",
+ ":rnn_vad_common",
+ "..:cpu_features",
+ "../../../../api:array_view",
+ "../../../../common_audio",
+ "../../../../rtc_base:rtc_base_approved",
+ "../../../../rtc_base:safe_compare",
+ "../../../../test:test_support",
+ "//third_party/abseil-cpp/absl/flags:flag",
+ "//third_party/abseil-cpp/absl/flags:parse",
+ ]
+ }
}
}
diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc
index 37112f0..12646fd 100644
--- a/modules/audio_processing/audio_processing_impl.cc
+++ b/modules/audio_processing/audio_processing_impl.cc
@@ -23,7 +23,6 @@
#include "common_audio/audio_converter.h"
#include "common_audio/include/audio_util.h"
#include "modules/audio_processing/aec_dump/aec_dump_factory.h"
-#include "modules/audio_processing/agc2/gain_applier.h"
#include "modules/audio_processing/audio_buffer.h"
#include "modules/audio_processing/common.h"
#include "modules/audio_processing/include/audio_frame_view.h"
@@ -49,8 +48,6 @@
namespace webrtc {
-constexpr int kRuntimeSettingQueueSize = 100;
-
namespace {
static bool LayoutHasKeyboard(AudioProcessing::ChannelLayout layout) {
@@ -117,6 +114,10 @@
RTC_CHECK_NOTREACHED();
}
+bool MinimizeProcessingForUnusedOutput() {
+ return !field_trial::IsEnabled("WebRTC-MutedStateKillSwitch");
+}
+
// Maximum lengths that frame of samples being passed from the render side to
// the capture side can have (does not apply to AEC3).
static const size_t kMaxAllowedValuesOfSamplesPerBand = 160;
@@ -147,7 +148,7 @@
bool noise_suppressor_enabled,
bool adaptive_gain_controller_enabled,
bool gain_controller2_enabled,
- bool pre_amplifier_enabled,
+ bool gain_adjustment_enabled,
bool echo_controller_enabled,
bool voice_detector_enabled,
bool transient_suppressor_enabled) {
@@ -161,7 +162,7 @@
changed |=
(adaptive_gain_controller_enabled != adaptive_gain_controller_enabled_);
changed |= (gain_controller2_enabled != gain_controller2_enabled_);
- changed |= (pre_amplifier_enabled_ != pre_amplifier_enabled);
+ changed |= (gain_adjustment_enabled != gain_adjustment_enabled_);
changed |= (echo_controller_enabled != echo_controller_enabled_);
changed |= (voice_detector_enabled != voice_detector_enabled_);
changed |= (transient_suppressor_enabled != transient_suppressor_enabled_);
@@ -172,7 +173,7 @@
noise_suppressor_enabled_ = noise_suppressor_enabled;
adaptive_gain_controller_enabled_ = adaptive_gain_controller_enabled;
gain_controller2_enabled_ = gain_controller2_enabled;
- pre_amplifier_enabled_ = pre_amplifier_enabled;
+ gain_adjustment_enabled_ = gain_adjustment_enabled;
echo_controller_enabled_ = echo_controller_enabled;
voice_detector_enabled_ = voice_detector_enabled;
transient_suppressor_enabled_ = transient_suppressor_enabled;
@@ -204,7 +205,7 @@
bool AudioProcessingImpl::SubmoduleStates::CaptureFullBandProcessingActive()
const {
return gain_controller2_enabled_ || capture_post_processor_enabled_ ||
- pre_amplifier_enabled_;
+ gain_adjustment_enabled_;
}
bool AudioProcessingImpl::SubmoduleStates::CaptureAnalyzerActive() const {
@@ -253,8 +254,8 @@
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
use_setup_specific_default_aec3_config_(
UseSetupSpecificDefaultAec3Congfig()),
- capture_runtime_settings_(kRuntimeSettingQueueSize),
- render_runtime_settings_(kRuntimeSettingQueueSize),
+ capture_runtime_settings_(RuntimeSettingQueueSize()),
+ render_runtime_settings_(RuntimeSettingQueueSize()),
capture_runtime_settings_enqueuer_(&capture_runtime_settings_),
render_runtime_settings_enqueuer_(&render_runtime_settings_),
echo_control_factory_(std::move(echo_control_factory)),
@@ -269,7 +270,9 @@
"WebRTC-ApmExperimentalMultiChannelRenderKillSwitch"),
!field_trial::IsEnabled(
"WebRTC-ApmExperimentalMultiChannelCaptureKillSwitch"),
- EnforceSplitBandHpf()),
+ EnforceSplitBandHpf(),
+ MinimizeProcessingForUnusedOutput()),
+ capture_(),
capture_nonlocked_() {
RTC_LOG(LS_INFO) << "Injected APM submodules:"
"\nEcho control factory: "
@@ -304,8 +307,6 @@
config.Get<ExperimentalAgc>().startup_min_volume;
config_.gain_controller1.analog_gain_controller.clipped_level_min =
config.Get<ExperimentalAgc>().clipped_level_min;
- config_.gain_controller1.analog_gain_controller.enable_agc2_level_estimator =
- config.Get<ExperimentalAgc>().enabled_agc2_level_estimator;
config_.gain_controller1.analog_gain_controller.enable_digital_adaptive =
!config.Get<ExperimentalAgc>().digital_adaptive_disabled;
#endif
@@ -426,6 +427,7 @@
InitializeAnalyzer();
InitializePostProcessor();
InitializePreProcessor();
+ InitializeCaptureLevelsAdjuster();
if (aec_dump_) {
aec_dump_->WriteInitMessage(formats_.api_format, rtc::TimeUTCMillis());
@@ -567,6 +569,9 @@
config_.pre_amplifier.fixed_gain_factor !=
config.pre_amplifier.fixed_gain_factor;
+ const bool gain_adjustment_config_changed =
+ config_.capture_level_adjustment != config.capture_level_adjustment;
+
config_ = config;
if (aec_config_changed) {
@@ -598,8 +603,8 @@
InitializeGainController2();
}
- if (pre_amplifier_config_changed) {
- InitializePreAmplifier();
+ if (pre_amplifier_config_changed || gain_adjustment_config_changed) {
+ InitializeCaptureLevelsAdjuster();
}
if (config_.level_estimation.enabled && !submodules_.output_level_estimator) {
@@ -666,35 +671,60 @@
void AudioProcessingImpl::set_output_will_be_muted(bool muted) {
MutexLock lock(&mutex_capture_);
- capture_.output_will_be_muted = muted;
+ HandleCaptureOutputUsedSetting(!muted);
+}
+
+void AudioProcessingImpl::HandleCaptureOutputUsedSetting(
+ bool capture_output_used) {
+ capture_.capture_output_used =
+ capture_output_used || !constants_.minimize_processing_for_unused_output;
+
if (submodules_.agc_manager.get()) {
- submodules_.agc_manager->SetCaptureMuted(capture_.output_will_be_muted);
+ submodules_.agc_manager->HandleCaptureOutputUsedChange(
+ capture_.capture_output_used);
+ }
+ if (submodules_.echo_controller) {
+ submodules_.echo_controller->SetCaptureOutputUsage(
+ capture_.capture_output_used);
+ }
+ if (submodules_.noise_suppressor) {
+ submodules_.noise_suppressor->SetCaptureOutputUsage(
+ capture_.capture_output_used);
}
}
void AudioProcessingImpl::SetRuntimeSetting(RuntimeSetting setting) {
+ PostRuntimeSetting(setting);
+}
+
+bool AudioProcessingImpl::PostRuntimeSetting(RuntimeSetting setting) {
switch (setting.type()) {
case RuntimeSetting::Type::kCustomRenderProcessingRuntimeSetting:
case RuntimeSetting::Type::kPlayoutAudioDeviceChange:
- render_runtime_settings_enqueuer_.Enqueue(setting);
- return;
+ return render_runtime_settings_enqueuer_.Enqueue(setting);
case RuntimeSetting::Type::kCapturePreGain:
+ case RuntimeSetting::Type::kCapturePostGain:
case RuntimeSetting::Type::kCaptureCompressionGain:
case RuntimeSetting::Type::kCaptureFixedPostGain:
case RuntimeSetting::Type::kCaptureOutputUsed:
- capture_runtime_settings_enqueuer_.Enqueue(setting);
- return;
- case RuntimeSetting::Type::kPlayoutVolumeChange:
- capture_runtime_settings_enqueuer_.Enqueue(setting);
- render_runtime_settings_enqueuer_.Enqueue(setting);
- return;
+ return capture_runtime_settings_enqueuer_.Enqueue(setting);
+ case RuntimeSetting::Type::kPlayoutVolumeChange: {
+ bool enqueueing_successful;
+ enqueueing_successful =
+ capture_runtime_settings_enqueuer_.Enqueue(setting);
+ enqueueing_successful =
+ render_runtime_settings_enqueuer_.Enqueue(setting) &&
+ enqueueing_successful;
+ return enqueueing_successful;
+ }
case RuntimeSetting::Type::kNotSpecified:
RTC_NOTREACHED();
- return;
+ return true;
}
// The language allows the enum to have a non-enumerator
// value. Check that this doesn't happen.
RTC_NOTREACHED();
+ return true;
}
AudioProcessingImpl::RuntimeSettingEnqueuer::RuntimeSettingEnqueuer(
@@ -706,20 +736,15 @@
AudioProcessingImpl::RuntimeSettingEnqueuer::~RuntimeSettingEnqueuer() =
default;
-void AudioProcessingImpl::RuntimeSettingEnqueuer::Enqueue(
+bool AudioProcessingImpl::RuntimeSettingEnqueuer::Enqueue(
RuntimeSetting setting) {
- int remaining_attempts = 10;
- while (!runtime_settings_.Insert(&setting) && remaining_attempts-- > 0) {
- RuntimeSetting setting_to_discard;
- if (runtime_settings_.Remove(&setting_to_discard)) {
- RTC_LOG(LS_ERROR)
- << "The runtime settings queue is full. Oldest setting discarded.";
- }
- }
- if (remaining_attempts == 0) {
+ const bool successful_insert = runtime_settings_.Insert(&setting);
+
+ if (!successful_insert) {
RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.ApmRuntimeSettingCannotEnqueue", 1);
RTC_LOG(LS_ERROR) << "Cannot enqueue a new runtime setting.";
}
+ return successful_insert;
}
int AudioProcessingImpl::MaybeInitializeCapture(
@@ -793,17 +818,48 @@
void AudioProcessingImpl::HandleCaptureRuntimeSettings() {
RuntimeSetting setting;
+ int num_settings_processed = 0;
while (capture_runtime_settings_.Remove(&setting)) {
if (aec_dump_) {
aec_dump_->WriteRuntimeSetting(setting);
}
switch (setting.type()) {
case RuntimeSetting::Type::kCapturePreGain:
- if (config_.pre_amplifier.enabled) {
+ if (config_.pre_amplifier.enabled ||
+ config_.capture_level_adjustment.enabled) {
float value;
setting.GetFloat(&value);
- config_.pre_amplifier.fixed_gain_factor = value;
- submodules_.pre_amplifier->SetGainFactor(value);
+ // If the pre-amplifier is used, apply the new gain to the
+ // pre-amplifier regardless if the capture level adjustment is
+ // activated. This approach allows both functionalities to coexist
+ // until they have been properly merged.
+ if (config_.pre_amplifier.enabled) {
+ config_.pre_amplifier.fixed_gain_factor = value;
+ } else {
+ config_.capture_level_adjustment.pre_gain_factor = value;
+ }
+
+ // Use both the pre-amplifier and the capture level adjustment gains
+ // as pre-gains.
+ float gain = 1.f;
+ if (config_.pre_amplifier.enabled) {
+ gain *= config_.pre_amplifier.fixed_gain_factor;
+ }
+ if (config_.capture_level_adjustment.enabled) {
+ gain *= config_.capture_level_adjustment.pre_gain_factor;
+ }
+
+ submodules_.capture_levels_adjuster->SetPreGain(gain);
+ }
+ // TODO(bugs.chromium.org/9138): Log setting handling by Aec Dump.
+ break;
+ case RuntimeSetting::Type::kCapturePostGain:
+ if (config_.capture_level_adjustment.enabled) {
+ float value;
+ setting.GetFloat(&value);
+ config_.capture_level_adjustment.post_gain_factor = value;
+ submodules_.capture_levels_adjuster->SetPostGain(
+ config_.capture_level_adjustment.post_gain_factor);
}
// TODO(bugs.chromium.org/9138): Log setting handling by Aec Dump.
break;
@@ -846,11 +902,25 @@
RTC_NOTREACHED();
break;
case RuntimeSetting::Type::kCaptureOutputUsed:
- // TODO(b/154437967): Add support for reducing complexity when it is
- // known that the capture output will not be used.
+ bool value;
+ setting.GetBool(&value);
+ HandleCaptureOutputUsedSetting(value);
break;
}
+ ++num_settings_processed;
}
+
+ if (num_settings_processed >= RuntimeSettingQueueSize()) {
+ // Handle overrun of the runtime settings queue, which likely will has
+ // caused settings to be discarded.
+ HandleOverrunInCaptureRuntimeSettingsQueue();
+ }
+}
+
+void AudioProcessingImpl::HandleOverrunInCaptureRuntimeSettingsQueue() {
+ // Fall back to a safe state for the case when a setting for capture output
+ // usage setting has been missed.
+ HandleCaptureOutputUsedSetting(/*capture_output_used=*/true);
}
void AudioProcessingImpl::HandleRenderRuntimeSettings() {
@@ -868,6 +938,7 @@
}
break;
case RuntimeSetting::Type::kCapturePreGain: // fall-through
+ case RuntimeSetting::Type::kCapturePostGain: // fall-through
case RuntimeSetting::Type::kCaptureCompressionGain: // fall-through
case RuntimeSetting::Type::kCaptureFixedPostGain: // fall-through
case RuntimeSetting::Type::kCaptureOutputUsed: // fall-through
@@ -1055,10 +1126,21 @@
/*use_split_band_data=*/false);
}
- if (submodules_.pre_amplifier) {
- submodules_.pre_amplifier->ApplyGain(AudioFrameView<float>(
- capture_buffer->channels(), capture_buffer->num_channels(),
- capture_buffer->num_frames()));
+ if (submodules_.capture_levels_adjuster) {
+ // If the analog mic gain emulation is active, get the emulated analog mic
+ // gain and pass it to the analog gain control functionality.
+ if (config_.capture_level_adjustment.analog_mic_gain_emulation.enabled) {
+ int level = submodules_.capture_levels_adjuster->GetAnalogMicGainLevel();
+ if (submodules_.agc_manager) {
+ submodules_.agc_manager->set_stream_analog_level(level);
+ } else if (submodules_.gain_control) {
+ int error = submodules_.gain_control->set_stream_analog_level(level);
+ RTC_DCHECK_EQ(kNoError, error);
+ }
+ }
+
+ submodules_.capture_levels_adjuster->ApplyPreLevelAdjustment(
+ *capture_buffer);
}
capture_input_rms_.Analyze(rtc::ArrayView<const float>(
@@ -1082,14 +1164,15 @@
capture_.prev_analog_mic_level != -1;
capture_.prev_analog_mic_level = analog_mic_level;
- // Detect and flag any change in the pre-amplifier gain.
- if (submodules_.pre_amplifier) {
- float pre_amp_gain = submodules_.pre_amplifier->GetGainFactor();
+ // Detect and flag any change in the capture level adjustment pre-gain.
+ if (submodules_.capture_levels_adjuster) {
+ float pre_adjustment_gain =
+ submodules_.capture_levels_adjuster->GetPreAdjustmentGain();
capture_.echo_path_gain_change =
capture_.echo_path_gain_change ||
- (capture_.prev_pre_amp_gain != pre_amp_gain &&
- capture_.prev_pre_amp_gain >= 0.f);
- capture_.prev_pre_amp_gain = pre_amp_gain;
+ (capture_.prev_pre_adjustment_gain != pre_adjustment_gain &&
+ capture_.prev_pre_adjustment_gain >= 0.f);
+ capture_.prev_pre_adjustment_gain = pre_adjustment_gain;
}
// Detect volume change.
@@ -1204,81 +1287,95 @@
capture_buffer->MergeFrequencyBands();
}
- if (capture_.capture_fullband_audio) {
- const auto& ec = submodules_.echo_controller;
- bool ec_active = ec ? ec->ActiveProcessing() : false;
- // Only update the fullband buffer if the multiband processing has changed
- // the signal. Keep the original signal otherwise.
- if (submodule_states_.CaptureMultiBandProcessingActive(ec_active)) {
- capture_buffer->CopyTo(capture_.capture_fullband_audio.get());
+ capture_.stats.output_rms_dbfs = absl::nullopt;
+ if (capture_.capture_output_used) {
+ if (capture_.capture_fullband_audio) {
+ const auto& ec = submodules_.echo_controller;
+ bool ec_active = ec ? ec->ActiveProcessing() : false;
+ // Only update the fullband buffer if the multiband processing has changed
+ // the signal. Keep the original signal otherwise.
+ if (submodule_states_.CaptureMultiBandProcessingActive(ec_active)) {
+ capture_buffer->CopyTo(capture_.capture_fullband_audio.get());
+ }
+ capture_buffer = capture_.capture_fullband_audio.get();
}
- capture_buffer = capture_.capture_fullband_audio.get();
+
+ if (config_.residual_echo_detector.enabled) {
+ RTC_DCHECK(submodules_.echo_detector);
+ submodules_.echo_detector->AnalyzeCaptureAudio(
+ rtc::ArrayView<const float>(capture_buffer->channels()[0],
+ capture_buffer->num_frames()));
+ }
+
+ // TODO(aluebs): Investigate if the transient suppression placement should
+ // be before or after the AGC.
+ if (submodules_.transient_suppressor) {
+ float voice_probability =
+ submodules_.agc_manager.get()
+ ? submodules_.agc_manager->voice_probability()
+ : 1.f;
+
+ submodules_.transient_suppressor->Suppress(
+ capture_buffer->channels()[0], capture_buffer->num_frames(),
+ capture_buffer->num_channels(),
+ capture_buffer->split_bands_const(0)[kBand0To8kHz],
+ capture_buffer->num_frames_per_band(),
+ capture_.keyboard_info.keyboard_data,
+ capture_.keyboard_info.num_keyboard_frames, voice_probability,
+ capture_.key_pressed);
+ }
+
+ // Experimental APM sub-module that analyzes |capture_buffer|.
+ if (submodules_.capture_analyzer) {
+ submodules_.capture_analyzer->Analyze(capture_buffer);
+ }
+
+ if (submodules_.gain_controller2) {
+ submodules_.gain_controller2->NotifyAnalogLevel(
+ recommended_stream_analog_level_locked());
+ submodules_.gain_controller2->Process(capture_buffer);
+ }
+
+ if (submodules_.capture_post_processor) {
+ submodules_.capture_post_processor->Process(capture_buffer);
+ }
+
+ // The level estimator operates on the recombined data.
+ if (config_.level_estimation.enabled) {
+ submodules_.output_level_estimator->ProcessStream(*capture_buffer);
+ capture_.stats.output_rms_dbfs =
+ submodules_.output_level_estimator->RMS();
+ }
+
+ capture_output_rms_.Analyze(rtc::ArrayView<const float>(
+ capture_buffer->channels_const()[0],
+ capture_nonlocked_.capture_processing_format.num_frames()));
+ if (log_rms) {
+ RmsLevel::Levels levels = capture_output_rms_.AverageAndPeak();
+ RTC_HISTOGRAM_COUNTS_LINEAR(
+ "WebRTC.Audio.ApmCaptureOutputLevelAverageRms", levels.average, 1,
+ RmsLevel::kMinLevelDb, 64);
+ RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.ApmCaptureOutputLevelPeakRms",
+ levels.peak, 1, RmsLevel::kMinLevelDb, 64);
+ }
+
+ if (submodules_.agc_manager) {
+ int level = recommended_stream_analog_level_locked();
+ data_dumper_->DumpRaw("experimental_gain_control_stream_analog_level", 1,
+ &level);
+ }
+
+ // Compute echo-detector stats.
+ if (config_.residual_echo_detector.enabled) {
+ RTC_DCHECK(submodules_.echo_detector);
+ auto ed_metrics = submodules_.echo_detector->GetMetrics();
+ capture_.stats.residual_echo_likelihood = ed_metrics.echo_likelihood;
+ capture_.stats.residual_echo_likelihood_recent_max =
+ ed_metrics.echo_likelihood_recent_max;
+ }
}
- if (config_.residual_echo_detector.enabled) {
- RTC_DCHECK(submodules_.echo_detector);
- submodules_.echo_detector->AnalyzeCaptureAudio(rtc::ArrayView<const float>(
- capture_buffer->channels()[0], capture_buffer->num_frames()));
- }
-
- // TODO(aluebs): Investigate if the transient suppression placement should be
- // before or after the AGC.
- if (submodules_.transient_suppressor) {
- float voice_probability = submodules_.agc_manager.get()
- ? submodules_.agc_manager->voice_probability()
- : 1.f;
-
- submodules_.transient_suppressor->Suppress(
- capture_buffer->channels()[0], capture_buffer->num_frames(),
- capture_buffer->num_channels(),
- capture_buffer->split_bands_const(0)[kBand0To8kHz],
- capture_buffer->num_frames_per_band(),
- capture_.keyboard_info.keyboard_data,
- capture_.keyboard_info.num_keyboard_frames, voice_probability,
- capture_.key_pressed);
- }
-
- // Experimental APM sub-module that analyzes |capture_buffer|.
- if (submodules_.capture_analyzer) {
- submodules_.capture_analyzer->Analyze(capture_buffer);
- }
-
- if (submodules_.gain_controller2) {
- submodules_.gain_controller2->NotifyAnalogLevel(
- recommended_stream_analog_level_locked());
- submodules_.gain_controller2->Process(capture_buffer);
- }
-
- if (submodules_.capture_post_processor) {
- submodules_.capture_post_processor->Process(capture_buffer);
- }
-
- // The level estimator operates on the recombined data.
- if (config_.level_estimation.enabled) {
- submodules_.output_level_estimator->ProcessStream(*capture_buffer);
- capture_.stats.output_rms_dbfs = submodules_.output_level_estimator->RMS();
- } else {
- capture_.stats.output_rms_dbfs = absl::nullopt;
- }
-
- capture_output_rms_.Analyze(rtc::ArrayView<const float>(
- capture_buffer->channels_const()[0],
- capture_nonlocked_.capture_processing_format.num_frames()));
- if (log_rms) {
- RmsLevel::Levels levels = capture_output_rms_.AverageAndPeak();
- RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.ApmCaptureOutputLevelAverageRms",
- levels.average, 1, RmsLevel::kMinLevelDb, 64);
- RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.ApmCaptureOutputLevelPeakRms",
- levels.peak, 1, RmsLevel::kMinLevelDb, 64);
- }
-
- if (submodules_.agc_manager) {
- int level = recommended_stream_analog_level_locked();
- data_dumper_->DumpRaw("experimental_gain_control_stream_analog_level", 1,
- &level);
- }
-
- // Compute echo-related stats.
+ // Compute echo-controller stats.
if (submodules_.echo_controller) {
auto ec_metrics = submodules_.echo_controller->GetMetrics();
capture_.stats.echo_return_loss = ec_metrics.echo_return_loss;
@@ -1286,17 +1383,41 @@
ec_metrics.echo_return_loss_enhancement;
capture_.stats.delay_ms = ec_metrics.delay_ms;
}
- if (config_.residual_echo_detector.enabled) {
- RTC_DCHECK(submodules_.echo_detector);
- auto ed_metrics = submodules_.echo_detector->GetMetrics();
- capture_.stats.residual_echo_likelihood = ed_metrics.echo_likelihood;
- capture_.stats.residual_echo_likelihood_recent_max =
- ed_metrics.echo_likelihood_recent_max;
- }
// Pass stats for reporting.
stats_reporter_.UpdateStatistics(capture_.stats);
+ if (submodules_.capture_levels_adjuster) {
+ submodules_.capture_levels_adjuster->ApplyPostLevelAdjustment(
+ *capture_buffer);
+
+ // If the analog mic gain emulation is active, retrieve the level from the
+ // analog gain control and set it to mic gain emulator.
+ if (config_.capture_level_adjustment.analog_mic_gain_emulation.enabled) {
+ if (submodules_.agc_manager) {
+ submodules_.capture_levels_adjuster->SetAnalogMicGainLevel(
+ submodules_.agc_manager->stream_analog_level());
+ } else if (submodules_.gain_control) {
+ submodules_.capture_levels_adjuster->SetAnalogMicGainLevel(
+ submodules_.gain_control->stream_analog_level());
+ }
+ }
+ }
+
+ // Temporarily set the output to zero after the stream has been unmuted
+ // (capture output is again used). The purpose of this is to avoid clicks and
+ // artefacts in the audio that results when the processing again is
+ // reactivated after unmuting.
+ if (!capture_.capture_output_used_last_frame &&
+ capture_.capture_output_used) {
+ for (size_t ch = 0; ch < capture_buffer->num_channels(); ++ch) {
+ rtc::ArrayView<float> channel_view(capture_buffer->channels()[ch],
+ capture_buffer->num_frames());
+ std::fill(channel_view.begin(), channel_view.end(), 0.f);
+ }
+ }
+ capture_.capture_output_used_last_frame = capture_.capture_output_used;
+
capture_.was_stream_delay_set = false;
return kNoError;
}
@@ -1499,16 +1620,29 @@
void AudioProcessingImpl::set_stream_analog_level(int level) {
MutexLock lock_capture(&mutex_capture_);
+ if (config_.capture_level_adjustment.analog_mic_gain_emulation.enabled) {
+ // If the analog mic gain is emulated internally, simply cache the level for
+ // later reporting back as the recommended stream analog level to use.
+ capture_.cached_stream_analog_level_ = level;
+ return;
+ }
+
if (submodules_.agc_manager) {
submodules_.agc_manager->set_stream_analog_level(level);
data_dumper_->DumpRaw("experimental_gain_control_set_stream_analog_level",
1, &level);
- } else if (submodules_.gain_control) {
+ return;
+ }
+
+ if (submodules_.gain_control) {
int error = submodules_.gain_control->set_stream_analog_level(level);
RTC_DCHECK_EQ(kNoError, error);
- } else {
- capture_.cached_stream_analog_level_ = level;
+ return;
}
+
+ // If no analog mic gain control functionality is in place, cache the level
+ // for later reporting back as the recommended stream analog level to use.
+ capture_.cached_stream_analog_level_ = level;
}
int AudioProcessingImpl::recommended_stream_analog_level() const {
@@ -1517,13 +1651,19 @@
}
int AudioProcessingImpl::recommended_stream_analog_level_locked() const {
- if (submodules_.agc_manager) {
- return submodules_.agc_manager->stream_analog_level();
- } else if (submodules_.gain_control) {
- return submodules_.gain_control->stream_analog_level();
- } else {
+ if (config_.capture_level_adjustment.analog_mic_gain_emulation.enabled) {
return capture_.cached_stream_analog_level_;
}
+
+ if (submodules_.agc_manager) {
+ return submodules_.agc_manager->stream_analog_level();
+ }
+
+ if (submodules_.gain_control) {
+ return submodules_.gain_control->stream_analog_level();
+ }
+
+ return capture_.cached_stream_analog_level_;
}
bool AudioProcessingImpl::CreateAndAttachAecDump(const std::string& file_name,
@@ -1576,14 +1716,6 @@
}
}
-void AudioProcessingImpl::MutateConfig(
- rtc::FunctionView<void(AudioProcessing::Config*)> mutator) {
- MutexLock lock_render(&mutex_render_);
- MutexLock lock_capture(&mutex_capture_);
- mutator(&config_);
- ApplyConfig(config_);
-}
-
AudioProcessing::Config AudioProcessingImpl::GetConfig() const {
MutexLock lock_render(&mutex_render_);
MutexLock lock_capture(&mutex_capture_);
@@ -1595,7 +1727,8 @@
config_.high_pass_filter.enabled, !!submodules_.echo_control_mobile,
config_.residual_echo_detector.enabled, !!submodules_.noise_suppressor,
!!submodules_.gain_control, !!submodules_.gain_controller2,
- config_.pre_amplifier.enabled, capture_nonlocked_.echo_controller_enabled,
+ config_.pre_amplifier.enabled || config_.capture_level_adjustment.enabled,
+ capture_nonlocked_.echo_controller_enabled,
config_.voice_detection.enabled, !!submodules_.transient_suppressor);
}
@@ -1782,8 +1915,6 @@
num_proc_channels(),
config_.gain_controller1.analog_gain_controller.startup_min_volume,
config_.gain_controller1.analog_gain_controller.clipped_level_min,
- config_.gain_controller1.analog_gain_controller
- .enable_agc2_level_estimator,
!config_.gain_controller1.analog_gain_controller
.enable_digital_adaptive,
capture_nonlocked_.split_rate));
@@ -1794,7 +1925,8 @@
submodules_.agc_manager->Initialize();
submodules_.agc_manager->SetupDigitalGainControl(
submodules_.gain_control.get());
- submodules_.agc_manager->SetCaptureMuted(capture_.output_will_be_muted);
+ submodules_.agc_manager->HandleCaptureOutputUsedChange(
+ capture_.capture_output_used);
}
void AudioProcessingImpl::InitializeGainController2() {
@@ -1840,12 +1972,27 @@
}
}
-void AudioProcessingImpl::InitializePreAmplifier() {
- if (config_.pre_amplifier.enabled) {
- submodules_.pre_amplifier.reset(
- new GainApplier(true, config_.pre_amplifier.fixed_gain_factor));
+void AudioProcessingImpl::InitializeCaptureLevelsAdjuster() {
+ if (config_.pre_amplifier.enabled ||
+ config_.capture_level_adjustment.enabled) {
+ // Use both the pre-amplifier and the capture level adjustment gains as
+ // pre-gains.
+ float pre_gain = 1.f;
+ if (config_.pre_amplifier.enabled) {
+ pre_gain *= config_.pre_amplifier.fixed_gain_factor;
+ }
+ if (config_.capture_level_adjustment.enabled) {
+ pre_gain *= config_.capture_level_adjustment.pre_gain_factor;
+ }
+
+ submodules_.capture_levels_adjuster =
+ std::make_unique<CaptureLevelsAdjuster>(
+ config_.capture_level_adjustment.analog_mic_gain_emulation.enabled,
+ config_.capture_level_adjustment.analog_mic_gain_emulation
+ .initial_level,
+ pre_gain, config_.capture_level_adjustment.post_gain_factor);
} else {
- submodules_.pre_amplifier.reset();
+ submodules_.capture_levels_adjuster.reset();
}
}
@@ -2005,13 +2152,14 @@
AudioProcessingImpl::ApmCaptureState::ApmCaptureState()
: was_stream_delay_set(false),
- output_will_be_muted(false),
+ capture_output_used(true),
+ capture_output_used_last_frame(true),
key_pressed(false),
capture_processing_format(kSampleRate16kHz),
split_rate(kSampleRate16kHz),
echo_path_gain_change(false),
prev_analog_mic_level(-1),
- prev_pre_amp_gain(-1.f),
+ prev_pre_adjustment_gain(-1.f),
playout_volume(-1),
prev_playout_volume(-1) {}
diff --git a/modules/audio_processing/audio_processing_impl.h b/modules/audio_processing/audio_processing_impl.h
index d0eec0e..e08abd5 100644
--- a/modules/audio_processing/audio_processing_impl.h
+++ b/modules/audio_processing/audio_processing_impl.h
@@ -23,6 +23,7 @@
#include "modules/audio_processing/agc/agc_manager_direct.h"
#include "modules/audio_processing/agc/gain_control.h"
#include "modules/audio_processing/audio_buffer.h"
+#include "modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h"
#include "modules/audio_processing/echo_control_mobile_impl.h"
#include "modules/audio_processing/gain_control_impl.h"
#include "modules/audio_processing/gain_controller2.h"
@@ -82,6 +83,7 @@
void AttachAecDump(std::unique_ptr<AecDump> aec_dump) override;
void DetachAecDump() override;
void SetRuntimeSetting(RuntimeSetting setting) override;
+ bool PostRuntimeSetting(RuntimeSetting setting) override;
// Capture-side exclusive methods possibly running APM in a
// multi-threaded manner. Acquire the capture lock.
@@ -96,6 +98,8 @@
bool GetLinearAecOutput(
rtc::ArrayView<std::array<float, 160>> linear_output) const override;
void set_output_will_be_muted(bool muted) override;
+ void HandleCaptureOutputUsedSetting(bool capture_output_used)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
int set_stream_delay_ms(int delay) override;
void set_stream_key_pressed(bool key_pressed) override;
void set_stream_analog_level(int level) override;
@@ -133,8 +137,6 @@
return stats_reporter_.GetStatistics();
}
- // TODO(peah): Remove MutateConfig once the new API allows that.
- void MutateConfig(rtc::FunctionView<void(AudioProcessing::Config*)> mutator);
AudioProcessing::Config GetConfig() const override;
protected:
@@ -168,7 +170,9 @@
explicit RuntimeSettingEnqueuer(
SwapQueue<RuntimeSetting>* runtime_settings);
~RuntimeSettingEnqueuer();
- void Enqueue(RuntimeSetting setting);
+
+ // Enqueue setting and return whether the setting was successfully enqueued.
+ bool Enqueue(RuntimeSetting setting);
private:
SwapQueue<RuntimeSetting>& runtime_settings_;
@@ -199,7 +203,7 @@
bool noise_suppressor_enabled,
bool adaptive_gain_controller_enabled,
bool gain_controller2_enabled,
- bool pre_amplifier_enabled,
+ bool gain_adjustment_enabled,
bool echo_controller_enabled,
bool voice_detector_enabled,
bool transient_suppressor_enabled);
@@ -223,7 +227,7 @@
bool noise_suppressor_enabled_ = false;
bool adaptive_gain_controller_enabled_ = false;
bool gain_controller2_enabled_ = false;
- bool pre_amplifier_enabled_ = false;
+ bool gain_adjustment_enabled_ = false;
bool echo_controller_enabled_ = false;
bool voice_detector_enabled_ = false;
bool transient_suppressor_enabled_ = false;
@@ -267,7 +271,8 @@
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
void InitializeGainController2() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
void InitializeNoiseSuppressor() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
- void InitializePreAmplifier() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
+ void InitializeCaptureLevelsAdjuster()
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
void InitializePostProcessor() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
void InitializeAnalyzer() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
@@ -339,6 +344,12 @@
void RecordAudioProcessingState()
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
+ // Ensures that overruns in the capture runtime settings queue is properly
+ // handled by the code, providing safe-fallbacks to mitigate the implications
+ // of any settings being missed.
+ void HandleOverrunInCaptureRuntimeSettingsQueue()
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
+
// AecDump instance used for optionally logging APM config, input
// and output to file in the AEC-dump format defined in debug.proto.
std::unique_ptr<AecDump> aec_dump_;
@@ -383,10 +394,10 @@
std::unique_ptr<TransientSuppressor> transient_suppressor;
std::unique_ptr<CustomProcessing> capture_post_processor;
std::unique_ptr<CustomProcessing> render_pre_processor;
- std::unique_ptr<GainApplier> pre_amplifier;
std::unique_ptr<CustomAudioAnalyzer> capture_analyzer;
std::unique_ptr<LevelEstimator> output_level_estimator;
std::unique_ptr<VoiceDetection> voice_detector;
+ std::unique_ptr<CaptureLevelsAdjuster> capture_levels_adjuster;
} submodules_;
// State that is written to while holding both the render and capture locks
@@ -410,20 +421,25 @@
const struct ApmConstants {
ApmConstants(bool multi_channel_render_support,
bool multi_channel_capture_support,
- bool enforce_split_band_hpf)
+ bool enforce_split_band_hpf,
+ bool minimize_processing_for_unused_output)
: multi_channel_render_support(multi_channel_render_support),
multi_channel_capture_support(multi_channel_capture_support),
- enforce_split_band_hpf(enforce_split_band_hpf) {}
+ enforce_split_band_hpf(enforce_split_band_hpf),
+ minimize_processing_for_unused_output(
+ minimize_processing_for_unused_output) {}
bool multi_channel_render_support;
bool multi_channel_capture_support;
bool enforce_split_band_hpf;
+ bool minimize_processing_for_unused_output;
} constants_;
struct ApmCaptureState {
ApmCaptureState();
~ApmCaptureState();
bool was_stream_delay_set;
- bool output_will_be_muted;
+ bool capture_output_used;
+ bool capture_output_used_last_frame;
bool key_pressed;
std::unique_ptr<AudioBuffer> capture_audio;
std::unique_ptr<AudioBuffer> capture_fullband_audio;
@@ -435,7 +451,7 @@
int split_rate;
bool echo_path_gain_change;
int prev_analog_mic_level;
- float prev_pre_amp_gain;
+ float prev_pre_adjustment_gain;
int playout_volume;
int prev_playout_volume;
AudioProcessingStats stats;
diff --git a/modules/audio_processing/audio_processing_impl_unittest.cc b/modules/audio_processing/audio_processing_impl_unittest.cc
index e289c31..ef18303 100644
--- a/modules/audio_processing/audio_processing_impl_unittest.cc
+++ b/modules/audio_processing/audio_processing_impl_unittest.cc
@@ -14,6 +14,7 @@
#include <memory>
#include "api/scoped_refptr.h"
+#include "modules/audio_processing/common.h"
#include "modules/audio_processing/include/audio_processing.h"
#include "modules/audio_processing/optionally_built_submodule_creators.h"
#include "modules/audio_processing/test/audio_processing_builder_for_testing.h"
@@ -203,6 +204,154 @@
}
TEST(AudioProcessingImplTest,
+ LevelAdjustmentUpdateCapturePreGainRuntimeSetting) {
+ std::unique_ptr<AudioProcessing> apm(
+ AudioProcessingBuilderForTesting().Create());
+ webrtc::AudioProcessing::Config apm_config;
+ apm_config.capture_level_adjustment.enabled = true;
+ apm_config.capture_level_adjustment.pre_gain_factor = 1.f;
+ apm->ApplyConfig(apm_config);
+
+ constexpr int kSampleRateHz = 48000;
+ constexpr int16_t kAudioLevel = 10000;
+ constexpr size_t kNumChannels = 2;
+
+ std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
+ StreamConfig config(kSampleRateHz, kNumChannels, /*has_keyboard=*/false);
+ frame.fill(kAudioLevel);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+ EXPECT_EQ(frame[100], kAudioLevel)
+ << "With factor 1, frame shouldn't be modified.";
+
+ constexpr float kGainFactor = 2.f;
+ apm->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCapturePreGain(kGainFactor));
+
+ // Process for two frames to have time to ramp up gain.
+ for (int i = 0; i < 2; ++i) {
+ frame.fill(kAudioLevel);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+ }
+ EXPECT_EQ(frame[100], kGainFactor * kAudioLevel)
+ << "Frame should be amplified.";
+}
+
+TEST(AudioProcessingImplTest,
+ LevelAdjustmentUpdateCapturePostGainRuntimeSetting) {
+ std::unique_ptr<AudioProcessing> apm(
+ AudioProcessingBuilderForTesting().Create());
+ webrtc::AudioProcessing::Config apm_config;
+ apm_config.capture_level_adjustment.enabled = true;
+ apm_config.capture_level_adjustment.post_gain_factor = 1.f;
+ apm->ApplyConfig(apm_config);
+
+ constexpr int kSampleRateHz = 48000;
+ constexpr int16_t kAudioLevel = 10000;
+ constexpr size_t kNumChannels = 2;
+
+ std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
+ StreamConfig config(kSampleRateHz, kNumChannels, /*has_keyboard=*/false);
+ frame.fill(kAudioLevel);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+ EXPECT_EQ(frame[100], kAudioLevel)
+ << "With factor 1, frame shouldn't be modified.";
+
+ constexpr float kGainFactor = 2.f;
+ apm->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCapturePostGain(kGainFactor));
+
+ // Process for two frames to have time to ramp up gain.
+ for (int i = 0; i < 2; ++i) {
+ frame.fill(kAudioLevel);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+ }
+ EXPECT_EQ(frame[100], kGainFactor * kAudioLevel)
+ << "Frame should be amplified.";
+}
+
+TEST(AudioProcessingImplTest, EchoControllerObservesSetCaptureUsageChange) {
+ // Tests that the echo controller observes that the capture usage has been
+ // updated.
+ auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
+ const MockEchoControlFactory* echo_control_factory_ptr =
+ echo_control_factory.get();
+
+ std::unique_ptr<AudioProcessing> apm(
+ AudioProcessingBuilderForTesting()
+ .SetEchoControlFactory(std::move(echo_control_factory))
+ .Create());
+
+ constexpr int16_t kAudioLevel = 10000;
+ constexpr int kSampleRateHz = 48000;
+ constexpr int kNumChannels = 2;
+ std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
+ StreamConfig config(kSampleRateHz, kNumChannels, /*has_keyboard=*/false);
+ frame.fill(kAudioLevel);
+
+ MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
+
+ // Ensure that SetCaptureOutputUsage is not called when no runtime settings
+ // are passed.
+ EXPECT_CALL(*echo_control_mock, SetCaptureOutputUsage(testing::_)).Times(0);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+
+ // Ensure that SetCaptureOutputUsage is called with the right information when
+ // a runtime setting is passed.
+ EXPECT_CALL(*echo_control_mock,
+ SetCaptureOutputUsage(/*capture_output_used=*/false))
+ .Times(1);
+ EXPECT_TRUE(apm->PostRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
+ /*capture_output_used=*/false)));
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+
+ EXPECT_CALL(*echo_control_mock,
+ SetCaptureOutputUsage(/*capture_output_used=*/true))
+ .Times(1);
+ EXPECT_TRUE(apm->PostRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
+ /*capture_output_used=*/true)));
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+
+ // The number of positions to place items in the queue is equal to the queue
+ // size minus 1.
+ constexpr int kNumSlotsInQueue = RuntimeSettingQueueSize();
+
+ // Ensure that SetCaptureOutputUsage is called with the right information when
+ // many runtime settings are passed.
+ for (int k = 0; k < kNumSlotsInQueue - 1; ++k) {
+ EXPECT_TRUE(apm->PostRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
+ /*capture_output_used=*/false)));
+ }
+ EXPECT_CALL(*echo_control_mock,
+ SetCaptureOutputUsage(/*capture_output_used=*/false))
+ .Times(kNumSlotsInQueue - 1);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+
+ // Ensure that SetCaptureOutputUsage is properly called with the fallback
+ // value when the runtime settings queue becomes full.
+ for (int k = 0; k < kNumSlotsInQueue; ++k) {
+ EXPECT_TRUE(apm->PostRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
+ /*capture_output_used=*/false)));
+ }
+ EXPECT_FALSE(apm->PostRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
+ /*capture_output_used=*/false)));
+ EXPECT_FALSE(apm->PostRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
+ /*capture_output_used=*/false)));
+ EXPECT_CALL(*echo_control_mock,
+ SetCaptureOutputUsage(/*capture_output_used=*/false))
+ .Times(kNumSlotsInQueue);
+ EXPECT_CALL(*echo_control_mock,
+ SetCaptureOutputUsage(/*capture_output_used=*/true))
+ .Times(1);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+}
+
+TEST(AudioProcessingImplTest,
EchoControllerObservesPreAmplifierEchoPathGainChange) {
// Tests that the echo controller observes an echo path gain change when the
// pre-amplifier submodule changes the gain.
@@ -246,6 +395,49 @@
}
TEST(AudioProcessingImplTest,
+ EchoControllerObservesLevelAdjustmentPreGainEchoPathGainChange) {
+ // Tests that the echo controller observes an echo path gain change when the
+ // pre-amplifier submodule changes the gain.
+ auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
+ const auto* echo_control_factory_ptr = echo_control_factory.get();
+
+ std::unique_ptr<AudioProcessing> apm(
+ AudioProcessingBuilderForTesting()
+ .SetEchoControlFactory(std::move(echo_control_factory))
+ .Create());
+ // Disable AGC.
+ webrtc::AudioProcessing::Config apm_config;
+ apm_config.gain_controller1.enabled = false;
+ apm_config.gain_controller2.enabled = false;
+ apm_config.capture_level_adjustment.enabled = true;
+ apm_config.capture_level_adjustment.pre_gain_factor = 1.f;
+ apm->ApplyConfig(apm_config);
+
+ constexpr int16_t kAudioLevel = 10000;
+ constexpr size_t kSampleRateHz = 48000;
+ constexpr size_t kNumChannels = 2;
+ std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
+ StreamConfig config(kSampleRateHz, kNumChannels, /*has_keyboard=*/false);
+ frame.fill(kAudioLevel);
+
+ MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
+
+ EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
+ EXPECT_CALL(*echo_control_mock,
+ ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
+ .Times(1);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+
+ EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
+ EXPECT_CALL(*echo_control_mock,
+ ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true))
+ .Times(1);
+ apm->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCapturePreGain(2.f));
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+}
+
+TEST(AudioProcessingImplTest,
EchoControllerObservesAnalogAgc1EchoPathGainChange) {
// Tests that the echo controller observes an echo path gain change when the
// AGC1 analog adaptive submodule changes the analog gain.
diff --git a/modules/audio_processing/audio_processing_unittest.cc b/modules/audio_processing/audio_processing_unittest.cc
index 545c780..3d562df 100644
--- a/modules/audio_processing/audio_processing_unittest.cc
+++ b/modules/audio_processing/audio_processing_unittest.cc
@@ -913,6 +913,131 @@
EXPECT_EQ(config.pre_amplifier.fixed_gain_factor, 1.5f);
}
+// This test a simple test that ensures that the emulated analog mic gain
+// functionality runs without crashing.
+TEST_F(ApmTest, AnalogMicGainEmulation) {
+ // Fill the audio frame with a sawtooth pattern.
+ rtc::ArrayView<int16_t> frame_data = GetMutableFrameData(&frame_);
+ const size_t samples_per_channel = frame_.samples_per_channel;
+ for (size_t i = 0; i < samples_per_channel; i++) {
+ for (size_t ch = 0; ch < frame_.num_channels; ++ch) {
+ frame_data[i + ch * samples_per_channel] = 100 * ((i % 3) - 1);
+ }
+ }
+ // Cache the frame in tmp_frame.
+ Int16FrameData tmp_frame;
+ tmp_frame.CopyFrom(frame_);
+
+ // Enable the analog gain emulation.
+ AudioProcessing::Config config = apm_->GetConfig();
+ config.capture_level_adjustment.enabled = true;
+ config.capture_level_adjustment.analog_mic_gain_emulation.enabled = true;
+ config.capture_level_adjustment.analog_mic_gain_emulation.initial_level = 21;
+ config.gain_controller1.enabled = true;
+ config.gain_controller1.mode =
+ AudioProcessing::Config::GainController1::Mode::kAdaptiveAnalog;
+ config.gain_controller1.analog_gain_controller.enabled = true;
+ apm_->ApplyConfig(config);
+
+ // Process a number of frames to ensure that the code runs without crashes.
+ for (int i = 0; i < 20; ++i) {
+ frame_.CopyFrom(tmp_frame);
+ EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(kIntFormat));
+ }
+}
+
+// This test repeatedly reconfigures the capture level adjustment functionality
+// in APM, processes a number of frames, and checks that output signal has the
+// right level.
+TEST_F(ApmTest, CaptureLevelAdjustment) {
+ // Fill the audio frame with a sawtooth pattern.
+ rtc::ArrayView<int16_t> frame_data = GetMutableFrameData(&frame_);
+ const size_t samples_per_channel = frame_.samples_per_channel;
+ for (size_t i = 0; i < samples_per_channel; i++) {
+ for (size_t ch = 0; ch < frame_.num_channels; ++ch) {
+ frame_data[i + ch * samples_per_channel] = 100 * ((i % 3) - 1);
+ }
+ }
+ // Cache the frame in tmp_frame.
+ Int16FrameData tmp_frame;
+ tmp_frame.CopyFrom(frame_);
+
+ auto compute_power = [](const Int16FrameData& frame) {
+ rtc::ArrayView<const int16_t> data = GetFrameData(frame);
+ return std::accumulate(data.begin(), data.end(), 0.0f,
+ [](float a, float b) { return a + b * b; }) /
+ data.size() / 32768 / 32768;
+ };
+
+ const float input_power = compute_power(tmp_frame);
+ // Double-check that the input data is large compared to the error kEpsilon.
+ constexpr float kEpsilon = 1e-20f;
+ RTC_DCHECK_GE(input_power, 10 * kEpsilon);
+
+ // 1. Enable pre-amp with 0 dB gain.
+ AudioProcessing::Config config = apm_->GetConfig();
+ config.capture_level_adjustment.enabled = true;
+ config.capture_level_adjustment.pre_gain_factor = 0.5f;
+ config.capture_level_adjustment.post_gain_factor = 4.f;
+ const float expected_output_power1 =
+ config.capture_level_adjustment.pre_gain_factor *
+ config.capture_level_adjustment.pre_gain_factor *
+ config.capture_level_adjustment.post_gain_factor *
+ config.capture_level_adjustment.post_gain_factor * input_power;
+ apm_->ApplyConfig(config);
+
+ for (int i = 0; i < 20; ++i) {
+ frame_.CopyFrom(tmp_frame);
+ EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(kIntFormat));
+ }
+ float output_power = compute_power(frame_);
+ EXPECT_NEAR(output_power, expected_output_power1, kEpsilon);
+ config = apm_->GetConfig();
+ EXPECT_EQ(config.capture_level_adjustment.pre_gain_factor, 0.5f);
+ EXPECT_EQ(config.capture_level_adjustment.post_gain_factor, 4.f);
+
+ // 2. Change pre-amp gain via ApplyConfig.
+ config.capture_level_adjustment.pre_gain_factor = 1.0f;
+ config.capture_level_adjustment.post_gain_factor = 2.f;
+ const float expected_output_power2 =
+ config.capture_level_adjustment.pre_gain_factor *
+ config.capture_level_adjustment.pre_gain_factor *
+ config.capture_level_adjustment.post_gain_factor *
+ config.capture_level_adjustment.post_gain_factor * input_power;
+ apm_->ApplyConfig(config);
+
+ for (int i = 0; i < 20; ++i) {
+ frame_.CopyFrom(tmp_frame);
+ EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(kIntFormat));
+ }
+ output_power = compute_power(frame_);
+ EXPECT_NEAR(output_power, expected_output_power2, kEpsilon);
+ config = apm_->GetConfig();
+ EXPECT_EQ(config.capture_level_adjustment.pre_gain_factor, 1.0f);
+ EXPECT_EQ(config.capture_level_adjustment.post_gain_factor, 2.f);
+
+ // 3. Change pre-amp gain via a RuntimeSetting.
+ constexpr float kPreGain3 = 0.5f;
+ constexpr float kPostGain3 = 3.f;
+ const float expected_output_power3 =
+ kPreGain3 * kPreGain3 * kPostGain3 * kPostGain3 * input_power;
+
+ apm_->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCapturePreGain(kPreGain3));
+ apm_->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCapturePostGain(kPostGain3));
+
+ for (int i = 0; i < 20; ++i) {
+ frame_.CopyFrom(tmp_frame);
+ EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(kIntFormat));
+ }
+ output_power = compute_power(frame_);
+ EXPECT_NEAR(output_power, expected_output_power3, kEpsilon);
+ config = apm_->GetConfig();
+ EXPECT_EQ(config.capture_level_adjustment.pre_gain_factor, 0.5f);
+ EXPECT_EQ(config.capture_level_adjustment.post_gain_factor, 3.f);
+}
+
TEST_F(ApmTest, GainControl) {
AudioProcessing::Config config = apm_->GetConfig();
config.gain_controller1.enabled = false;
@@ -2216,7 +2341,7 @@
std::make_tuple(32000, 44100, 16000, 44100, 19, 15),
std::make_tuple(32000, 32000, 48000, 32000, 40, 35),
std::make_tuple(32000, 32000, 32000, 32000, 0, 0),
- std::make_tuple(32000, 32000, 16000, 32000, 40, 20),
+ std::make_tuple(32000, 32000, 16000, 32000, 39, 20),
std::make_tuple(32000, 16000, 48000, 16000, 25, 20),
std::make_tuple(32000, 16000, 32000, 16000, 25, 20),
std::make_tuple(32000, 16000, 16000, 16000, 25, 0),
@@ -2231,7 +2356,7 @@
std::make_tuple(16000, 32000, 32000, 32000, 25, 0),
std::make_tuple(16000, 32000, 16000, 32000, 25, 20),
std::make_tuple(16000, 16000, 48000, 16000, 39, 20),
- std::make_tuple(16000, 16000, 32000, 16000, 40, 20),
+ std::make_tuple(16000, 16000, 32000, 16000, 39, 20),
std::make_tuple(16000, 16000, 16000, 16000, 0, 0)));
#elif defined(WEBRTC_AUDIOPROC_FIXED_PROFILE)
@@ -2428,36 +2553,6 @@
EXPECT_EQ(AudioProcessing::RuntimeSetting::Type::kNotSpecified, s.type());
}
-TEST(RuntimeSettingDeathTest, TestCapturePreGain) {
- using Type = AudioProcessing::RuntimeSetting::Type;
- {
- auto s = AudioProcessing::RuntimeSetting::CreateCapturePreGain(1.25f);
- EXPECT_EQ(Type::kCapturePreGain, s.type());
- float v;
- s.GetFloat(&v);
- EXPECT_EQ(1.25f, v);
- }
-
-#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
- EXPECT_DEATH(AudioProcessing::RuntimeSetting::CreateCapturePreGain(0.1f), "");
-#endif
-}
-
-TEST(RuntimeSettingDeathTest, TestCaptureFixedPostGain) {
- using Type = AudioProcessing::RuntimeSetting::Type;
- {
- auto s = AudioProcessing::RuntimeSetting::CreateCaptureFixedPostGain(1.25f);
- EXPECT_EQ(Type::kCaptureFixedPostGain, s.type());
- float v;
- s.GetFloat(&v);
- EXPECT_EQ(1.25f, v);
- }
-
-#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
- EXPECT_DEATH(AudioProcessing::RuntimeSetting::CreateCapturePreGain(0.1f), "");
-#endif
-}
-
TEST(RuntimeSettingTest, TestUsageWithSwapQueue) {
SwapQueue<AudioProcessing::RuntimeSetting> q(1);
auto s = AudioProcessing::RuntimeSetting();
@@ -2931,10 +3026,6 @@
b_analog.clipped_level_min = a_analog.clipped_level_min;
EXPECT_EQ(a, b);
- Toggle(a_analog.enable_agc2_level_estimator);
- b_analog.enable_agc2_level_estimator = a_analog.enable_agc2_level_estimator;
- EXPECT_EQ(a, b);
-
Toggle(a_analog.enable_digital_adaptive);
b_analog.enable_digital_adaptive = a_analog.enable_digital_adaptive;
EXPECT_EQ(a, b);
@@ -2989,10 +3080,6 @@
EXPECT_NE(a, b);
a_analog.clipped_level_min = b_analog.clipped_level_min;
- Toggle(a_analog.enable_agc2_level_estimator);
- EXPECT_NE(a, b);
- a_analog.enable_agc2_level_estimator = b_analog.enable_agc2_level_estimator;
-
Toggle(a_analog.enable_digital_adaptive);
EXPECT_NE(a, b);
a_analog.enable_digital_adaptive = b_analog.enable_digital_adaptive;
diff --git a/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.cc b/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.cc
new file mode 100644
index 0000000..cb2336b
--- /dev/null
+++ b/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.cc
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2021 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 "modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.h"
+
+#include <algorithm>
+
+#include "api/array_view.h"
+#include "modules/audio_processing/audio_buffer.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/numerics/safe_minmax.h"
+
+namespace webrtc {
+
+AudioSamplesScaler::AudioSamplesScaler(float initial_gain)
+ : previous_gain_(initial_gain), target_gain_(initial_gain) {}
+
+void AudioSamplesScaler::Process(AudioBuffer& audio_buffer) {
+ if (static_cast<int>(audio_buffer.num_frames()) != samples_per_channel_) {
+ // Update the members depending on audio-buffer length if needed.
+ RTC_DCHECK_GT(audio_buffer.num_frames(), 0);
+ samples_per_channel_ = static_cast<int>(audio_buffer.num_frames());
+ one_by_samples_per_channel_ = 1.f / samples_per_channel_;
+ }
+
+ if (target_gain_ == 1.f && previous_gain_ == target_gain_) {
+ // If only a gain of 1 is to be applied, do an early return without applying
+ // any gain.
+ return;
+ }
+
+ float gain = previous_gain_;
+ if (previous_gain_ == target_gain_) {
+ // Apply a non-changing gain.
+ for (size_t channel = 0; channel < audio_buffer.num_channels(); ++channel) {
+ rtc::ArrayView<float> channel_view(audio_buffer.channels()[channel],
+ samples_per_channel_);
+ for (float& sample : channel_view) {
+ sample *= gain;
+ }
+ }
+ } else {
+ const float increment =
+ (target_gain_ - previous_gain_) * one_by_samples_per_channel_;
+
+ if (increment > 0.f) {
+ // Apply an increasing gain.
+ for (size_t channel = 0; channel < audio_buffer.num_channels();
+ ++channel) {
+ gain = previous_gain_;
+ rtc::ArrayView<float> channel_view(audio_buffer.channels()[channel],
+ samples_per_channel_);
+ for (float& sample : channel_view) {
+ gain = std::min(gain + increment, target_gain_);
+ sample *= gain;
+ }
+ }
+ } else {
+ // Apply a decreasing gain.
+ for (size_t channel = 0; channel < audio_buffer.num_channels();
+ ++channel) {
+ gain = previous_gain_;
+ rtc::ArrayView<float> channel_view(audio_buffer.channels()[channel],
+ samples_per_channel_);
+ for (float& sample : channel_view) {
+ gain = std::max(gain + increment, target_gain_);
+ sample *= gain;
+ }
+ }
+ }
+ }
+ previous_gain_ = target_gain_;
+
+ // Saturate the samples to be in the S16 range.
+ for (size_t channel = 0; channel < audio_buffer.num_channels(); ++channel) {
+ rtc::ArrayView<float> channel_view(audio_buffer.channels()[channel],
+ samples_per_channel_);
+ for (float& sample : channel_view) {
+ constexpr float kMinFloatS16Value = -32768.f;
+ constexpr float kMaxFloatS16Value = 32767.f;
+ sample = rtc::SafeClamp(sample, kMinFloatS16Value, kMaxFloatS16Value);
+ }
+ }
+}
+
+} // namespace webrtc
diff --git a/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.h b/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.h
new file mode 100644
index 0000000..2ae8533
--- /dev/null
+++ b/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021 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 MODULES_AUDIO_PROCESSING_CAPTURE_LEVELS_ADJUSTER_AUDIO_SAMPLES_SCALER_H_
+#define MODULES_AUDIO_PROCESSING_CAPTURE_LEVELS_ADJUSTER_AUDIO_SAMPLES_SCALER_H_
+
+#include <stddef.h>
+
+#include "modules/audio_processing/audio_buffer.h"
+
+namespace webrtc {
+
+// Handles and applies a gain to the samples in an audio buffer.
+// The gain is applied for each sample and any changes in the gain take effect
+// gradually (in a linear manner) over one frame.
+class AudioSamplesScaler {
+ public:
+ // C-tor. The supplied `initial_gain` is used immediately at the first call to
+ // Process(), i.e., in contrast to the gain supplied by SetGain(...) there is
+ // no gradual change to the `initial_gain`.
+ explicit AudioSamplesScaler(float initial_gain);
+ AudioSamplesScaler(const AudioSamplesScaler&) = delete;
+ AudioSamplesScaler& operator=(const AudioSamplesScaler&) = delete;
+
+ // Applies the specified gain to the audio in `audio_buffer`.
+ void Process(AudioBuffer& audio_buffer);
+
+ // Sets the gain to apply to each sample.
+ void SetGain(float gain) { target_gain_ = gain; }
+
+ private:
+ float previous_gain_ = 1.f;
+ float target_gain_ = 1.f;
+ int samples_per_channel_ = -1;
+ float one_by_samples_per_channel_ = -1.f;
+};
+} // namespace webrtc
+
+#endif // MODULES_AUDIO_PROCESSING_CAPTURE_LEVELS_ADJUSTER_AUDIO_SAMPLES_SCALER_H_
diff --git a/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler_unittest.cc b/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler_unittest.cc
new file mode 100644
index 0000000..6e5fc2c
--- /dev/null
+++ b/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler_unittest.cc
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2021 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 "modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.h"
+
+#include <tuple>
+
+#include "modules/audio_processing/test/audio_buffer_tools.h"
+#include "rtc_base/strings/string_builder.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+float SampleValueForChannel(int channel) {
+ constexpr float kSampleBaseValue = 100.f;
+ constexpr float kSampleChannelOffset = 1.f;
+ return kSampleBaseValue + channel * kSampleChannelOffset;
+}
+
+void PopulateBuffer(AudioBuffer& audio_buffer) {
+ for (size_t ch = 0; ch < audio_buffer.num_channels(); ++ch) {
+ test::FillBufferChannel(SampleValueForChannel(ch), ch, audio_buffer);
+ }
+}
+
+constexpr int kNumFramesToProcess = 10;
+
+class AudioSamplesScalerTest
+ : public ::testing::Test,
+ public ::testing::WithParamInterface<std::tuple<int, int, float>> {
+ protected:
+ int sample_rate_hz() const { return std::get<0>(GetParam()); }
+ int num_channels() const { return std::get<1>(GetParam()); }
+ float initial_gain() const { return std::get<2>(GetParam()); }
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ AudioSamplesScalerTestSuite,
+ AudioSamplesScalerTest,
+ ::testing::Combine(::testing::Values(16000, 32000, 48000),
+ ::testing::Values(1, 2, 4),
+ ::testing::Values(0.1f, 1.f, 2.f, 4.f)));
+
+TEST_P(AudioSamplesScalerTest, InitialGainIsRespected) {
+ AudioSamplesScaler scaler(initial_gain());
+
+ AudioBuffer audio_buffer(sample_rate_hz(), num_channels(), sample_rate_hz(),
+ num_channels(), sample_rate_hz(), num_channels());
+
+ for (int frame = 0; frame < kNumFramesToProcess; ++frame) {
+ PopulateBuffer(audio_buffer);
+ scaler.Process(audio_buffer);
+ for (int ch = 0; ch < num_channels(); ++ch) {
+ for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
+ EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
+ initial_gain() * SampleValueForChannel(ch));
+ }
+ }
+ }
+}
+
+TEST_P(AudioSamplesScalerTest, VerifyGainAdjustment) {
+ const float higher_gain = initial_gain();
+ const float lower_gain = higher_gain / 2.f;
+
+ AudioSamplesScaler scaler(lower_gain);
+
+ AudioBuffer audio_buffer(sample_rate_hz(), num_channels(), sample_rate_hz(),
+ num_channels(), sample_rate_hz(), num_channels());
+
+ // Allow the intial, lower, gain to take effect.
+ PopulateBuffer(audio_buffer);
+
+ scaler.Process(audio_buffer);
+
+ // Set the new, higher, gain.
+ scaler.SetGain(higher_gain);
+
+ // Ensure that the new, higher, gain is achieved gradually over one frame.
+ PopulateBuffer(audio_buffer);
+
+ scaler.Process(audio_buffer);
+ for (int ch = 0; ch < num_channels(); ++ch) {
+ for (size_t i = 0; i < audio_buffer.num_frames() - 1; ++i) {
+ EXPECT_LT(audio_buffer.channels_const()[ch][i],
+ higher_gain * SampleValueForChannel(ch));
+ EXPECT_LE(audio_buffer.channels_const()[ch][i],
+ audio_buffer.channels_const()[ch][i + 1]);
+ }
+ EXPECT_LE(audio_buffer.channels_const()[ch][audio_buffer.num_frames() - 1],
+ higher_gain * SampleValueForChannel(ch));
+ }
+
+ // Ensure that the new, higher, gain is achieved and stay unchanged.
+ for (int frame = 0; frame < kNumFramesToProcess; ++frame) {
+ PopulateBuffer(audio_buffer);
+ scaler.Process(audio_buffer);
+
+ for (int ch = 0; ch < num_channels(); ++ch) {
+ for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
+ EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
+ higher_gain * SampleValueForChannel(ch));
+ }
+ }
+ }
+
+ // Set the new, lower, gain.
+ scaler.SetGain(lower_gain);
+
+ // Ensure that the new, lower, gain is achieved gradually over one frame.
+ PopulateBuffer(audio_buffer);
+ scaler.Process(audio_buffer);
+ for (int ch = 0; ch < num_channels(); ++ch) {
+ for (size_t i = 0; i < audio_buffer.num_frames() - 1; ++i) {
+ EXPECT_GT(audio_buffer.channels_const()[ch][i],
+ lower_gain * SampleValueForChannel(ch));
+ EXPECT_GE(audio_buffer.channels_const()[ch][i],
+ audio_buffer.channels_const()[ch][i + 1]);
+ }
+ EXPECT_GE(audio_buffer.channels_const()[ch][audio_buffer.num_frames() - 1],
+ lower_gain * SampleValueForChannel(ch));
+ }
+
+ // Ensure that the new, lower, gain is achieved and stay unchanged.
+ for (int frame = 0; frame < kNumFramesToProcess; ++frame) {
+ PopulateBuffer(audio_buffer);
+ scaler.Process(audio_buffer);
+
+ for (int ch = 0; ch < num_channels(); ++ch) {
+ for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
+ EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
+ lower_gain * SampleValueForChannel(ch));
+ }
+ }
+ }
+}
+
+TEST(AudioSamplesScaler, UpwardsClamping) {
+ constexpr int kSampleRateHz = 48000;
+ constexpr int kNumChannels = 1;
+ constexpr float kGain = 10.f;
+ constexpr float kMaxClampedSampleValue = 32767.f;
+ static_assert(kGain > 1.f, "");
+
+ AudioSamplesScaler scaler(kGain);
+
+ AudioBuffer audio_buffer(kSampleRateHz, kNumChannels, kSampleRateHz,
+ kNumChannels, kSampleRateHz, kNumChannels);
+
+ for (int frame = 0; frame < kNumFramesToProcess; ++frame) {
+ for (size_t ch = 0; ch < audio_buffer.num_channels(); ++ch) {
+ test::FillBufferChannel(
+ kMaxClampedSampleValue - audio_buffer.num_channels() + 1.f + ch, ch,
+ audio_buffer);
+ }
+
+ scaler.Process(audio_buffer);
+ for (int ch = 0; ch < kNumChannels; ++ch) {
+ for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
+ EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
+ kMaxClampedSampleValue);
+ }
+ }
+ }
+}
+
+TEST(AudioSamplesScaler, DownwardsClamping) {
+ constexpr int kSampleRateHz = 48000;
+ constexpr int kNumChannels = 1;
+ constexpr float kGain = 10.f;
+ constexpr float kMinClampedSampleValue = -32768.f;
+ static_assert(kGain > 1.f, "");
+
+ AudioSamplesScaler scaler(kGain);
+
+ AudioBuffer audio_buffer(kSampleRateHz, kNumChannels, kSampleRateHz,
+ kNumChannels, kSampleRateHz, kNumChannels);
+
+ for (int frame = 0; frame < kNumFramesToProcess; ++frame) {
+ for (size_t ch = 0; ch < audio_buffer.num_channels(); ++ch) {
+ test::FillBufferChannel(
+ kMinClampedSampleValue + audio_buffer.num_channels() - 1.f + ch, ch,
+ audio_buffer);
+ }
+
+ scaler.Process(audio_buffer);
+ for (int ch = 0; ch < kNumChannels; ++ch) {
+ for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
+ EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
+ kMinClampedSampleValue);
+ }
+ }
+ }
+}
+
+} // namespace
+} // namespace webrtc
diff --git a/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.cc b/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.cc
new file mode 100644
index 0000000..dfda582
--- /dev/null
+++ b/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.cc
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2021 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 "modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h"
+
+#include "modules/audio_processing/audio_buffer.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/numerics/safe_minmax.h"
+
+namespace webrtc {
+
+namespace {
+
+constexpr int kMinAnalogMicGainLevel = 0;
+constexpr int kMaxAnalogMicGainLevel = 255;
+
+float ComputeLevelBasedGain(int emulated_analog_mic_gain_level) {
+ static_assert(
+ kMinAnalogMicGainLevel == 0,
+ "The minimum gain level must be 0 for the maths below to work.");
+ static_assert(kMaxAnalogMicGainLevel > 0,
+ "The minimum gain level must be larger than 0 for the maths "
+ "below to work.");
+ constexpr float kGainToLevelMultiplier = 1.f / kMaxAnalogMicGainLevel;
+
+ RTC_DCHECK_GE(emulated_analog_mic_gain_level, kMinAnalogMicGainLevel);
+ RTC_DCHECK_LE(emulated_analog_mic_gain_level, kMaxAnalogMicGainLevel);
+ return kGainToLevelMultiplier * emulated_analog_mic_gain_level;
+}
+
+float ComputePreGain(float pre_gain,
+ int emulated_analog_mic_gain_level,
+ bool emulated_analog_mic_gain_enabled) {
+ return emulated_analog_mic_gain_enabled
+ ? pre_gain * ComputeLevelBasedGain(emulated_analog_mic_gain_level)
+ : pre_gain;
+}
+
+} // namespace
+
+CaptureLevelsAdjuster::CaptureLevelsAdjuster(
+ bool emulated_analog_mic_gain_enabled,
+ int emulated_analog_mic_gain_level,
+ float pre_gain,
+ float post_gain)
+ : emulated_analog_mic_gain_enabled_(emulated_analog_mic_gain_enabled),
+ emulated_analog_mic_gain_level_(emulated_analog_mic_gain_level),
+ pre_gain_(pre_gain),
+ pre_adjustment_gain_(ComputePreGain(pre_gain_,
+ emulated_analog_mic_gain_level_,
+ emulated_analog_mic_gain_enabled_)),
+ pre_scaler_(pre_adjustment_gain_),
+ post_scaler_(post_gain) {}
+
+void CaptureLevelsAdjuster::ApplyPreLevelAdjustment(AudioBuffer& audio_buffer) {
+ pre_scaler_.Process(audio_buffer);
+}
+
+void CaptureLevelsAdjuster::ApplyPostLevelAdjustment(
+ AudioBuffer& audio_buffer) {
+ post_scaler_.Process(audio_buffer);
+}
+
+void CaptureLevelsAdjuster::SetPreGain(float pre_gain) {
+ pre_gain_ = pre_gain;
+ UpdatePreAdjustmentGain();
+}
+
+void CaptureLevelsAdjuster::SetPostGain(float post_gain) {
+ post_scaler_.SetGain(post_gain);
+}
+
+void CaptureLevelsAdjuster::SetAnalogMicGainLevel(int level) {
+ RTC_DCHECK_GE(level, kMinAnalogMicGainLevel);
+ RTC_DCHECK_LE(level, kMaxAnalogMicGainLevel);
+ int clamped_level =
+ rtc::SafeClamp(level, kMinAnalogMicGainLevel, kMaxAnalogMicGainLevel);
+
+ emulated_analog_mic_gain_level_ = clamped_level;
+ UpdatePreAdjustmentGain();
+}
+
+void CaptureLevelsAdjuster::UpdatePreAdjustmentGain() {
+ pre_adjustment_gain_ =
+ ComputePreGain(pre_gain_, emulated_analog_mic_gain_level_,
+ emulated_analog_mic_gain_enabled_);
+ pre_scaler_.SetGain(pre_adjustment_gain_);
+}
+
+} // namespace webrtc
diff --git a/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h b/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h
new file mode 100644
index 0000000..38b68ad
--- /dev/null
+++ b/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2021 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 MODULES_AUDIO_PROCESSING_CAPTURE_LEVELS_ADJUSTER_CAPTURE_LEVELS_ADJUSTER_H_
+#define MODULES_AUDIO_PROCESSING_CAPTURE_LEVELS_ADJUSTER_CAPTURE_LEVELS_ADJUSTER_H_
+
+#include <stddef.h>
+
+#include "modules/audio_processing/audio_buffer.h"
+#include "modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.h"
+
+namespace webrtc {
+
+// Adjusts the level of the capture signal before and after all capture-side
+// processing is done using a combination of explicitly specified gains
+// and an emulated analog gain functionality where a specified analog level
+// results in an additional gain. The pre-adjustment is achieved by combining
+// the gain value `pre_gain` and the level `emulated_analog_mic_gain_level` to
+// form a combined gain of `pre_gain`*`emulated_analog_mic_gain_level`/255 which
+// is multiplied to each sample. The intention of the
+// `emulated_analog_mic_gain_level` is to be controlled by the analog AGC
+// functionality and to produce an emulated analog mic gain equal to
+// `emulated_analog_mic_gain_level`/255. The post level adjustment is achieved
+// by multiplying each sample with the value of `post_gain`. Any changes in the
+// gains take are done smoothly over one frame and the scaled samples are
+// clamped to fit into the allowed S16 sample range.
+class CaptureLevelsAdjuster {
+ public:
+ // C-tor. The values for the level and the gains must fulfill
+ // 0 <= emulated_analog_mic_gain_level <= 255.
+ // 0.f <= pre_gain.
+ // 0.f <= post_gain.
+ CaptureLevelsAdjuster(bool emulated_analog_mic_gain_enabled,
+ int emulated_analog_mic_gain_level,
+ float pre_gain,
+ float post_gain);
+ CaptureLevelsAdjuster(const CaptureLevelsAdjuster&) = delete;
+ CaptureLevelsAdjuster& operator=(const CaptureLevelsAdjuster&) = delete;
+
+ // Adjusts the level of the signal. This should be called before any of the
+ // other processing is performed.
+ void ApplyPreLevelAdjustment(AudioBuffer& audio_buffer);
+
+ // Adjusts the level of the signal. This should be called after all of the
+ // other processing have been performed.
+ void ApplyPostLevelAdjustment(AudioBuffer& audio_buffer);
+
+ // Sets the gain to apply to each sample before any of the other processing is
+ // performed.
+ void SetPreGain(float pre_gain);
+
+ // Returns the total pre-adjustment gain applied, comprising both the pre_gain
+ // as well as the gain from the emulated analog mic, to each sample before any
+ // of the other processing is performed.
+ float GetPreAdjustmentGain() const { return pre_adjustment_gain_; }
+
+ // Sets the gain to apply to each sample after all of the other processing
+ // have been performed.
+ void SetPostGain(float post_gain);
+
+ // Sets the analog gain level to use for the emulated analog gain.
+ // `level` must be in the range [0...255].
+ void SetAnalogMicGainLevel(int level);
+
+ // Returns the current analog gain level used for the emulated analog gain.
+ int GetAnalogMicGainLevel() const { return emulated_analog_mic_gain_level_; }
+
+ private:
+ // Updates the value of `pre_adjustment_gain_` based on the supplied values
+ // for `pre_gain` and `emulated_analog_mic_gain_level_`.
+ void UpdatePreAdjustmentGain();
+
+ const bool emulated_analog_mic_gain_enabled_;
+ int emulated_analog_mic_gain_level_;
+ float pre_gain_;
+ float pre_adjustment_gain_;
+ AudioSamplesScaler pre_scaler_;
+ AudioSamplesScaler post_scaler_;
+};
+} // namespace webrtc
+
+#endif // MODULES_AUDIO_PROCESSING_CAPTURE_LEVELS_ADJUSTER_CAPTURE_LEVELS_ADJUSTER_H_
diff --git a/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster_unittest.cc b/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster_unittest.cc
new file mode 100644
index 0000000..1183441
--- /dev/null
+++ b/modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster_unittest.cc
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2021 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 "modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h"
+
+#include <algorithm>
+#include <tuple>
+
+#include "modules/audio_processing/test/audio_buffer_tools.h"
+#include "rtc_base/strings/string_builder.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+float SampleValueForChannel(int channel) {
+ constexpr float kSampleBaseValue = 100.f;
+ constexpr float kSampleChannelOffset = 1.f;
+ return kSampleBaseValue + channel * kSampleChannelOffset;
+}
+
+void PopulateBuffer(AudioBuffer& audio_buffer) {
+ for (size_t ch = 0; ch < audio_buffer.num_channels(); ++ch) {
+ test::FillBufferChannel(SampleValueForChannel(ch), ch, audio_buffer);
+ }
+}
+
+float ComputeExpectedSignalGainAfterApplyPreLevelAdjustment(
+ bool emulated_analog_mic_gain_enabled,
+ int emulated_analog_mic_gain_level,
+ float pre_gain) {
+ if (!emulated_analog_mic_gain_enabled) {
+ return pre_gain;
+ }
+ return pre_gain * std::min(emulated_analog_mic_gain_level, 255) / 255.f;
+}
+
+float ComputeExpectedSignalGainAfterApplyPostLevelAdjustment(
+ bool emulated_analog_mic_gain_enabled,
+ int emulated_analog_mic_gain_level,
+ float pre_gain,
+ float post_gain) {
+ return post_gain * ComputeExpectedSignalGainAfterApplyPreLevelAdjustment(
+ emulated_analog_mic_gain_enabled,
+ emulated_analog_mic_gain_level, pre_gain);
+}
+
+constexpr int kNumFramesToProcess = 10;
+
+class CaptureLevelsAdjusterTest
+ : public ::testing::Test,
+ public ::testing::WithParamInterface<
+ std::tuple<int, int, bool, int, float, float>> {
+ protected:
+ int sample_rate_hz() const { return std::get<0>(GetParam()); }
+ int num_channels() const { return std::get<1>(GetParam()); }
+ bool emulated_analog_mic_gain_enabled() const {
+ return std::get<2>(GetParam());
+ }
+ int emulated_analog_mic_gain_level() const { return std::get<3>(GetParam()); }
+ float pre_gain() const { return std::get<4>(GetParam()); }
+ float post_gain() const { return std::get<5>(GetParam()); }
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ CaptureLevelsAdjusterTestSuite,
+ CaptureLevelsAdjusterTest,
+ ::testing::Combine(::testing::Values(16000, 32000, 48000),
+ ::testing::Values(1, 2, 4),
+ ::testing::Values(false, true),
+ ::testing::Values(21, 255),
+ ::testing::Values(0.1f, 1.f, 4.f),
+ ::testing::Values(0.1f, 1.f, 4.f)));
+
+TEST_P(CaptureLevelsAdjusterTest, InitialGainIsInstantlyAchieved) {
+ CaptureLevelsAdjuster adjuster(emulated_analog_mic_gain_enabled(),
+ emulated_analog_mic_gain_level(), pre_gain(),
+ post_gain());
+
+ AudioBuffer audio_buffer(sample_rate_hz(), num_channels(), sample_rate_hz(),
+ num_channels(), sample_rate_hz(), num_channels());
+
+ const float expected_signal_gain_after_pre_gain =
+ ComputeExpectedSignalGainAfterApplyPreLevelAdjustment(
+ emulated_analog_mic_gain_enabled(), emulated_analog_mic_gain_level(),
+ pre_gain());
+ const float expected_signal_gain_after_post_level_adjustment =
+ ComputeExpectedSignalGainAfterApplyPostLevelAdjustment(
+ emulated_analog_mic_gain_enabled(), emulated_analog_mic_gain_level(),
+ pre_gain(), post_gain());
+
+ for (int frame = 0; frame < kNumFramesToProcess; ++frame) {
+ PopulateBuffer(audio_buffer);
+ adjuster.ApplyPreLevelAdjustment(audio_buffer);
+ EXPECT_FLOAT_EQ(adjuster.GetPreAdjustmentGain(),
+ expected_signal_gain_after_pre_gain);
+
+ for (int ch = 0; ch < num_channels(); ++ch) {
+ for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
+ EXPECT_FLOAT_EQ(
+ audio_buffer.channels_const()[ch][i],
+ expected_signal_gain_after_pre_gain * SampleValueForChannel(ch));
+ }
+ }
+ adjuster.ApplyPostLevelAdjustment(audio_buffer);
+ for (int ch = 0; ch < num_channels(); ++ch) {
+ for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
+ EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
+ expected_signal_gain_after_post_level_adjustment *
+ SampleValueForChannel(ch));
+ }
+ }
+ }
+}
+
+TEST_P(CaptureLevelsAdjusterTest, NewGainsAreAchieved) {
+ const int lower_emulated_analog_mic_gain_level =
+ emulated_analog_mic_gain_level();
+ const float lower_pre_gain = pre_gain();
+ const float lower_post_gain = post_gain();
+ const int higher_emulated_analog_mic_gain_level =
+ std::min(lower_emulated_analog_mic_gain_level * 2, 255);
+ const float higher_pre_gain = lower_pre_gain * 2.f;
+ const float higher_post_gain = lower_post_gain * 2.f;
+
+ CaptureLevelsAdjuster adjuster(emulated_analog_mic_gain_enabled(),
+ lower_emulated_analog_mic_gain_level,
+ lower_pre_gain, lower_post_gain);
+
+ AudioBuffer audio_buffer(sample_rate_hz(), num_channels(), sample_rate_hz(),
+ num_channels(), sample_rate_hz(), num_channels());
+
+ const float expected_signal_gain_after_pre_gain =
+ ComputeExpectedSignalGainAfterApplyPreLevelAdjustment(
+ emulated_analog_mic_gain_enabled(),
+ higher_emulated_analog_mic_gain_level, higher_pre_gain);
+ const float expected_signal_gain_after_post_level_adjustment =
+ ComputeExpectedSignalGainAfterApplyPostLevelAdjustment(
+ emulated_analog_mic_gain_enabled(),
+ higher_emulated_analog_mic_gain_level, higher_pre_gain,
+ higher_post_gain);
+
+ adjuster.SetPreGain(higher_pre_gain);
+ adjuster.SetPostGain(higher_post_gain);
+ adjuster.SetAnalogMicGainLevel(higher_emulated_analog_mic_gain_level);
+
+ PopulateBuffer(audio_buffer);
+ adjuster.ApplyPreLevelAdjustment(audio_buffer);
+ adjuster.ApplyPostLevelAdjustment(audio_buffer);
+ EXPECT_EQ(adjuster.GetAnalogMicGainLevel(),
+ higher_emulated_analog_mic_gain_level);
+
+ for (int frame = 1; frame < kNumFramesToProcess; ++frame) {
+ PopulateBuffer(audio_buffer);
+ adjuster.ApplyPreLevelAdjustment(audio_buffer);
+ EXPECT_FLOAT_EQ(adjuster.GetPreAdjustmentGain(),
+ expected_signal_gain_after_pre_gain);
+ for (int ch = 0; ch < num_channels(); ++ch) {
+ for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
+ EXPECT_FLOAT_EQ(
+ audio_buffer.channels_const()[ch][i],
+ expected_signal_gain_after_pre_gain * SampleValueForChannel(ch));
+ }
+ }
+
+ adjuster.ApplyPostLevelAdjustment(audio_buffer);
+ for (int ch = 0; ch < num_channels(); ++ch) {
+ for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
+ EXPECT_FLOAT_EQ(audio_buffer.channels_const()[ch][i],
+ expected_signal_gain_after_post_level_adjustment *
+ SampleValueForChannel(ch));
+ }
+ }
+
+ EXPECT_EQ(adjuster.GetAnalogMicGainLevel(),
+ higher_emulated_analog_mic_gain_level);
+ }
+}
+
+} // namespace
+} // namespace webrtc
diff --git a/modules/audio_processing/capture_levels_adjuster/module.mk b/modules/audio_processing/capture_levels_adjuster/module.mk
new file mode 100644
index 0000000..4b0571b
--- /dev/null
+++ b/modules/audio_processing/capture_levels_adjuster/module.mk
@@ -0,0 +1,5 @@
+# Copyright 2021 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+include common.mk
diff --git a/modules/audio_processing/common.h b/modules/audio_processing/common.h
index d8532c5..2c88c4e 100644
--- a/modules/audio_processing/common.h
+++ b/modules/audio_processing/common.h
@@ -16,6 +16,10 @@
namespace webrtc {
+constexpr int RuntimeSettingQueueSize() {
+ return 100;
+}
+
static inline size_t ChannelsFromLayout(AudioProcessing::ChannelLayout layout) {
switch (layout) {
case AudioProcessing::kMono:
diff --git a/modules/audio_processing/debug.pb.cc b/modules/audio_processing/debug.pb.cc
new file mode 100644
index 0000000..36aceb7
--- /dev/null
+++ b/modules/audio_processing/debug.pb.cc
@@ -0,0 +1,3094 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: debug.proto
+
+#include "debug.pb.h"
+
+#include <algorithm>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+extern PROTOBUF_INTERNAL_EXPORT_debug_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Config_debug_2eproto;
+extern PROTOBUF_INTERNAL_EXPORT_debug_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Init_debug_2eproto;
+extern PROTOBUF_INTERNAL_EXPORT_debug_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_PlayoutAudioDeviceInfo_debug_2eproto;
+extern PROTOBUF_INTERNAL_EXPORT_debug_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_ReverseStream_debug_2eproto;
+extern PROTOBUF_INTERNAL_EXPORT_debug_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_RuntimeSetting_debug_2eproto;
+extern PROTOBUF_INTERNAL_EXPORT_debug_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Stream_debug_2eproto;
+namespace webrtc {
+namespace audioproc {
+class InitDefaultTypeInternal {
+ public:
+ ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<Init> _instance;
+} _Init_default_instance_;
+class ReverseStreamDefaultTypeInternal {
+ public:
+ ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<ReverseStream> _instance;
+} _ReverseStream_default_instance_;
+class StreamDefaultTypeInternal {
+ public:
+ ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<Stream> _instance;
+} _Stream_default_instance_;
+class ConfigDefaultTypeInternal {
+ public:
+ ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<Config> _instance;
+} _Config_default_instance_;
+class PlayoutAudioDeviceInfoDefaultTypeInternal {
+ public:
+ ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<PlayoutAudioDeviceInfo> _instance;
+} _PlayoutAudioDeviceInfo_default_instance_;
+class RuntimeSettingDefaultTypeInternal {
+ public:
+ ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<RuntimeSetting> _instance;
+} _RuntimeSetting_default_instance_;
+class EventDefaultTypeInternal {
+ public:
+ ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<Event> _instance;
+} _Event_default_instance_;
+} // namespace audioproc
+} // namespace webrtc
+static void InitDefaultsscc_info_Config_debug_2eproto() {
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+ {
+ void* ptr = &::webrtc::audioproc::_Config_default_instance_;
+ new (ptr) ::webrtc::audioproc::Config();
+ ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr);
+ }
+ ::webrtc::audioproc::Config::InitAsDefaultInstance();
+}
+
+::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Config_debug_2eproto =
+ {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_Config_debug_2eproto}, {}};
+
+static void InitDefaultsscc_info_Event_debug_2eproto() {
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+ {
+ void* ptr = &::webrtc::audioproc::_Event_default_instance_;
+ new (ptr) ::webrtc::audioproc::Event();
+ ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr);
+ }
+ ::webrtc::audioproc::Event::InitAsDefaultInstance();
+}
+
+::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<5> scc_info_Event_debug_2eproto =
+ {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 5, 0, InitDefaultsscc_info_Event_debug_2eproto}, {
+ &scc_info_Init_debug_2eproto.base,
+ &scc_info_ReverseStream_debug_2eproto.base,
+ &scc_info_Stream_debug_2eproto.base,
+ &scc_info_Config_debug_2eproto.base,
+ &scc_info_RuntimeSetting_debug_2eproto.base,}};
+
+static void InitDefaultsscc_info_Init_debug_2eproto() {
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+ {
+ void* ptr = &::webrtc::audioproc::_Init_default_instance_;
+ new (ptr) ::webrtc::audioproc::Init();
+ ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr);
+ }
+ ::webrtc::audioproc::Init::InitAsDefaultInstance();
+}
+
+::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Init_debug_2eproto =
+ {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_Init_debug_2eproto}, {}};
+
+static void InitDefaultsscc_info_PlayoutAudioDeviceInfo_debug_2eproto() {
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+ {
+ void* ptr = &::webrtc::audioproc::_PlayoutAudioDeviceInfo_default_instance_;
+ new (ptr) ::webrtc::audioproc::PlayoutAudioDeviceInfo();
+ ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr);
+ }
+ ::webrtc::audioproc::PlayoutAudioDeviceInfo::InitAsDefaultInstance();
+}
+
+::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_PlayoutAudioDeviceInfo_debug_2eproto =
+ {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_PlayoutAudioDeviceInfo_debug_2eproto}, {}};
+
+static void InitDefaultsscc_info_ReverseStream_debug_2eproto() {
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+ {
+ void* ptr = &::webrtc::audioproc::_ReverseStream_default_instance_;
+ new (ptr) ::webrtc::audioproc::ReverseStream();
+ ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr);
+ }
+ ::webrtc::audioproc::ReverseStream::InitAsDefaultInstance();
+}
+
+::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_ReverseStream_debug_2eproto =
+ {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_ReverseStream_debug_2eproto}, {}};
+
+static void InitDefaultsscc_info_RuntimeSetting_debug_2eproto() {
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+ {
+ void* ptr = &::webrtc::audioproc::_RuntimeSetting_default_instance_;
+ new (ptr) ::webrtc::audioproc::RuntimeSetting();
+ ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr);
+ }
+ ::webrtc::audioproc::RuntimeSetting::InitAsDefaultInstance();
+}
+
+::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_RuntimeSetting_debug_2eproto =
+ {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, 0, InitDefaultsscc_info_RuntimeSetting_debug_2eproto}, {
+ &scc_info_PlayoutAudioDeviceInfo_debug_2eproto.base,}};
+
+static void InitDefaultsscc_info_Stream_debug_2eproto() {
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+ {
+ void* ptr = &::webrtc::audioproc::_Stream_default_instance_;
+ new (ptr) ::webrtc::audioproc::Stream();
+ ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr);
+ }
+ ::webrtc::audioproc::Stream::InitAsDefaultInstance();
+}
+
+::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Stream_debug_2eproto =
+ {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_Stream_debug_2eproto}, {}};
+
+namespace webrtc {
+namespace audioproc {
+bool Event_Type_IsValid(int value) {
+ switch (value) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<std::string> Event_Type_strings[6] = {};
+
+static const char Event_Type_names[] =
+ "CONFIG"
+ "INIT"
+ "REVERSE_STREAM"
+ "RUNTIME_SETTING"
+ "STREAM"
+ "UNKNOWN_EVENT";
+
+static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry Event_Type_entries[] = {
+ { {Event_Type_names + 0, 6}, 3 },
+ { {Event_Type_names + 6, 4}, 0 },
+ { {Event_Type_names + 10, 14}, 1 },
+ { {Event_Type_names + 24, 15}, 5 },
+ { {Event_Type_names + 39, 6}, 2 },
+ { {Event_Type_names + 45, 13}, 4 },
+};
+
+static const int Event_Type_entries_by_number[] = {
+ 1, // 0 -> INIT
+ 2, // 1 -> REVERSE_STREAM
+ 4, // 2 -> STREAM
+ 0, // 3 -> CONFIG
+ 5, // 4 -> UNKNOWN_EVENT
+ 3, // 5 -> RUNTIME_SETTING
+};
+
+const std::string& Event_Type_Name(
+ Event_Type value) {
+ static const bool dummy =
+ ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings(
+ Event_Type_entries,
+ Event_Type_entries_by_number,
+ 6, Event_Type_strings);
+ (void) dummy;
+ int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName(
+ Event_Type_entries,
+ Event_Type_entries_by_number,
+ 6, value);
+ return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() :
+ Event_Type_strings[idx].get();
+}
+bool Event_Type_Parse(
+ const std::string& name, Event_Type* value) {
+ int int_value;
+ bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue(
+ Event_Type_entries, 6, name, &int_value);
+ if (success) {
+ *value = static_cast<Event_Type>(int_value);
+ }
+ return success;
+}
+#if (__cplusplus < 201703) && (!defined(_MSC_VER) || _MSC_VER >= 1900)
+constexpr Event_Type Event::INIT;
+constexpr Event_Type Event::REVERSE_STREAM;
+constexpr Event_Type Event::STREAM;
+constexpr Event_Type Event::CONFIG;
+constexpr Event_Type Event::UNKNOWN_EVENT;
+constexpr Event_Type Event::RUNTIME_SETTING;
+constexpr Event_Type Event::Type_MIN;
+constexpr Event_Type Event::Type_MAX;
+constexpr int Event::Type_ARRAYSIZE;
+#endif // (__cplusplus < 201703) && (!defined(_MSC_VER) || _MSC_VER >= 1900)
+
+// ===================================================================
+
+void Init::InitAsDefaultInstance() {
+}
+class Init::_Internal {
+ public:
+ using HasBits = decltype(std::declval<Init>()._has_bits_);
+ static void set_has_sample_rate(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static void set_has_device_sample_rate(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+ static void set_has_num_input_channels(HasBits* has_bits) {
+ (*has_bits)[0] |= 4u;
+ }
+ static void set_has_num_output_channels(HasBits* has_bits) {
+ (*has_bits)[0] |= 8u;
+ }
+ static void set_has_num_reverse_channels(HasBits* has_bits) {
+ (*has_bits)[0] |= 16u;
+ }
+ static void set_has_reverse_sample_rate(HasBits* has_bits) {
+ (*has_bits)[0] |= 32u;
+ }
+ static void set_has_output_sample_rate(HasBits* has_bits) {
+ (*has_bits)[0] |= 64u;
+ }
+ static void set_has_reverse_output_sample_rate(HasBits* has_bits) {
+ (*has_bits)[0] |= 128u;
+ }
+ static void set_has_num_reverse_output_channels(HasBits* has_bits) {
+ (*has_bits)[0] |= 512u;
+ }
+ static void set_has_timestamp_ms(HasBits* has_bits) {
+ (*has_bits)[0] |= 256u;
+ }
+};
+
+Init::Init()
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(), _internal_metadata_(nullptr) {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:webrtc.audioproc.Init)
+}
+Init::Init(const Init& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(),
+ _internal_metadata_(nullptr),
+ _has_bits_(from._has_bits_) {
+ _internal_metadata_.MergeFrom(from._internal_metadata_);
+ ::memcpy(&sample_rate_, &from.sample_rate_,
+ static_cast<size_t>(reinterpret_cast<char*>(&num_reverse_output_channels_) -
+ reinterpret_cast<char*>(&sample_rate_)) + sizeof(num_reverse_output_channels_));
+ // @@protoc_insertion_point(copy_constructor:webrtc.audioproc.Init)
+}
+
+void Init::SharedCtor() {
+ ::memset(&sample_rate_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&num_reverse_output_channels_) -
+ reinterpret_cast<char*>(&sample_rate_)) + sizeof(num_reverse_output_channels_));
+}
+
+Init::~Init() {
+ // @@protoc_insertion_point(destructor:webrtc.audioproc.Init)
+ SharedDtor();
+}
+
+void Init::SharedDtor() {
+}
+
+void Init::SetCachedSize(int size) const {
+ _cached_size_.Set(size);
+}
+const Init& Init::default_instance() {
+ ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_Init_debug_2eproto.base);
+ return *internal_default_instance();
+}
+
+
+void Init::Clear() {
+// @@protoc_insertion_point(message_clear_start:webrtc.audioproc.Init)
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _has_bits_[0];
+ if (cached_has_bits & 0x000000ffu) {
+ ::memset(&sample_rate_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&reverse_output_sample_rate_) -
+ reinterpret_cast<char*>(&sample_rate_)) + sizeof(reverse_output_sample_rate_));
+ }
+ if (cached_has_bits & 0x00000300u) {
+ ::memset(×tamp_ms_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&num_reverse_output_channels_) -
+ reinterpret_cast<char*>(×tamp_ms_)) + sizeof(num_reverse_output_channels_));
+ }
+ _has_bits_.Clear();
+ _internal_metadata_.Clear();
+}
+
+const char* Init::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ ::PROTOBUF_NAMESPACE_ID::uint32 tag;
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);
+ CHK_(ptr);
+ switch (tag >> 3) {
+ // optional int32 sample_rate = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8)) {
+ _Internal::set_has_sample_rate(&has_bits);
+ sample_rate_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional int32 device_sample_rate = 2 [deprecated = true];
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) {
+ _Internal::set_has_device_sample_rate(&has_bits);
+ device_sample_rate_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional int32 num_input_channels = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 24)) {
+ _Internal::set_has_num_input_channels(&has_bits);
+ num_input_channels_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional int32 num_output_channels = 4;
+ case 4:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 32)) {
+ _Internal::set_has_num_output_channels(&has_bits);
+ num_output_channels_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional int32 num_reverse_channels = 5;
+ case 5:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 40)) {
+ _Internal::set_has_num_reverse_channels(&has_bits);
+ num_reverse_channels_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional int32 reverse_sample_rate = 6;
+ case 6:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 48)) {
+ _Internal::set_has_reverse_sample_rate(&has_bits);
+ reverse_sample_rate_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional int32 output_sample_rate = 7;
+ case 7:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 56)) {
+ _Internal::set_has_output_sample_rate(&has_bits);
+ output_sample_rate_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional int32 reverse_output_sample_rate = 8;
+ case 8:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 64)) {
+ _Internal::set_has_reverse_output_sample_rate(&has_bits);
+ reverse_output_sample_rate_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional int32 num_reverse_output_channels = 9;
+ case 9:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 72)) {
+ _Internal::set_has_num_reverse_output_channels(&has_bits);
+ num_reverse_output_channels_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional int64 timestamp_ms = 10;
+ case 10:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 80)) {
+ _Internal::set_has_timestamp_ms(&has_bits);
+ timestamp_ms_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ default: {
+ handle_unusual:
+ if ((tag & 7) == 4 || tag == 0) {
+ ctx->SetLastTag(tag);
+ goto success;
+ }
+ ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx);
+ CHK_(ptr != nullptr);
+ continue;
+ }
+ } // switch
+ } // while
+success:
+ _has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto success;
+#undef CHK_
+}
+
+::PROTOBUF_NAMESPACE_ID::uint8* Init::_InternalSerialize(
+ ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:webrtc.audioproc.Init)
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _has_bits_[0];
+ // optional int32 sample_rate = 1;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(1, this->_internal_sample_rate(), target);
+ }
+
+ // optional int32 device_sample_rate = 2 [deprecated = true];
+ if (cached_has_bits & 0x00000002u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(2, this->_internal_device_sample_rate(), target);
+ }
+
+ // optional int32 num_input_channels = 3;
+ if (cached_has_bits & 0x00000004u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(3, this->_internal_num_input_channels(), target);
+ }
+
+ // optional int32 num_output_channels = 4;
+ if (cached_has_bits & 0x00000008u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(4, this->_internal_num_output_channels(), target);
+ }
+
+ // optional int32 num_reverse_channels = 5;
+ if (cached_has_bits & 0x00000010u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(5, this->_internal_num_reverse_channels(), target);
+ }
+
+ // optional int32 reverse_sample_rate = 6;
+ if (cached_has_bits & 0x00000020u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(6, this->_internal_reverse_sample_rate(), target);
+ }
+
+ // optional int32 output_sample_rate = 7;
+ if (cached_has_bits & 0x00000040u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(7, this->_internal_output_sample_rate(), target);
+ }
+
+ // optional int32 reverse_output_sample_rate = 8;
+ if (cached_has_bits & 0x00000080u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(8, this->_internal_reverse_output_sample_rate(), target);
+ }
+
+ // optional int32 num_reverse_output_channels = 9;
+ if (cached_has_bits & 0x00000200u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(9, this->_internal_num_reverse_output_channels(), target);
+ }
+
+ // optional int64 timestamp_ms = 10;
+ if (cached_has_bits & 0x00000100u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64ToArray(10, this->_internal_timestamp_ms(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields().data(),
+ static_cast<int>(_internal_metadata_.unknown_fields().size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:webrtc.audioproc.Init)
+ return target;
+}
+
+size_t Init::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:webrtc.audioproc.Init)
+ size_t total_size = 0;
+
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _has_bits_[0];
+ if (cached_has_bits & 0x000000ffu) {
+ // optional int32 sample_rate = 1;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+ this->_internal_sample_rate());
+ }
+
+ // optional int32 device_sample_rate = 2 [deprecated = true];
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+ this->_internal_device_sample_rate());
+ }
+
+ // optional int32 num_input_channels = 3;
+ if (cached_has_bits & 0x00000004u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+ this->_internal_num_input_channels());
+ }
+
+ // optional int32 num_output_channels = 4;
+ if (cached_has_bits & 0x00000008u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+ this->_internal_num_output_channels());
+ }
+
+ // optional int32 num_reverse_channels = 5;
+ if (cached_has_bits & 0x00000010u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+ this->_internal_num_reverse_channels());
+ }
+
+ // optional int32 reverse_sample_rate = 6;
+ if (cached_has_bits & 0x00000020u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+ this->_internal_reverse_sample_rate());
+ }
+
+ // optional int32 output_sample_rate = 7;
+ if (cached_has_bits & 0x00000040u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+ this->_internal_output_sample_rate());
+ }
+
+ // optional int32 reverse_output_sample_rate = 8;
+ if (cached_has_bits & 0x00000080u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+ this->_internal_reverse_output_sample_rate());
+ }
+
+ }
+ if (cached_has_bits & 0x00000300u) {
+ // optional int64 timestamp_ms = 10;
+ if (cached_has_bits & 0x00000100u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int64Size(
+ this->_internal_timestamp_ms());
+ }
+
+ // optional int32 num_reverse_output_channels = 9;
+ if (cached_has_bits & 0x00000200u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+ this->_internal_num_reverse_output_channels());
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields().size();
+ }
+ int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void Init::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::PROTOBUF_NAMESPACE_ID::internal::DownCast<const Init*>(
+ &from));
+}
+
+void Init::MergeFrom(const Init& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:webrtc.audioproc.Init)
+ GOOGLE_DCHECK_NE(&from, this);
+ _internal_metadata_.MergeFrom(from._internal_metadata_);
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._has_bits_[0];
+ if (cached_has_bits & 0x000000ffu) {
+ if (cached_has_bits & 0x00000001u) {
+ sample_rate_ = from.sample_rate_;
+ }
+ if (cached_has_bits & 0x00000002u) {
+ device_sample_rate_ = from.device_sample_rate_;
+ }
+ if (cached_has_bits & 0x00000004u) {
+ num_input_channels_ = from.num_input_channels_;
+ }
+ if (cached_has_bits & 0x00000008u) {
+ num_output_channels_ = from.num_output_channels_;
+ }
+ if (cached_has_bits & 0x00000010u) {
+ num_reverse_channels_ = from.num_reverse_channels_;
+ }
+ if (cached_has_bits & 0x00000020u) {
+ reverse_sample_rate_ = from.reverse_sample_rate_;
+ }
+ if (cached_has_bits & 0x00000040u) {
+ output_sample_rate_ = from.output_sample_rate_;
+ }
+ if (cached_has_bits & 0x00000080u) {
+ reverse_output_sample_rate_ = from.reverse_output_sample_rate_;
+ }
+ _has_bits_[0] |= cached_has_bits;
+ }
+ if (cached_has_bits & 0x00000300u) {
+ if (cached_has_bits & 0x00000100u) {
+ timestamp_ms_ = from.timestamp_ms_;
+ }
+ if (cached_has_bits & 0x00000200u) {
+ num_reverse_output_channels_ = from.num_reverse_output_channels_;
+ }
+ _has_bits_[0] |= cached_has_bits;
+ }
+}
+
+void Init::CopyFrom(const Init& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:webrtc.audioproc.Init)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool Init::IsInitialized() const {
+ return true;
+}
+
+void Init::InternalSwap(Init* other) {
+ using std::swap;
+ _internal_metadata_.Swap(&other->_internal_metadata_);
+ swap(_has_bits_[0], other->_has_bits_[0]);
+ swap(sample_rate_, other->sample_rate_);
+ swap(device_sample_rate_, other->device_sample_rate_);
+ swap(num_input_channels_, other->num_input_channels_);
+ swap(num_output_channels_, other->num_output_channels_);
+ swap(num_reverse_channels_, other->num_reverse_channels_);
+ swap(reverse_sample_rate_, other->reverse_sample_rate_);
+ swap(output_sample_rate_, other->output_sample_rate_);
+ swap(reverse_output_sample_rate_, other->reverse_output_sample_rate_);
+ swap(timestamp_ms_, other->timestamp_ms_);
+ swap(num_reverse_output_channels_, other->num_reverse_output_channels_);
+}
+
+std::string Init::GetTypeName() const {
+ return "webrtc.audioproc.Init";
+}
+
+
+// ===================================================================
+
+void ReverseStream::InitAsDefaultInstance() {
+}
+class ReverseStream::_Internal {
+ public:
+ using HasBits = decltype(std::declval<ReverseStream>()._has_bits_);
+ static void set_has_data(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+};
+
+ReverseStream::ReverseStream()
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(), _internal_metadata_(nullptr) {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:webrtc.audioproc.ReverseStream)
+}
+ReverseStream::ReverseStream(const ReverseStream& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(),
+ _internal_metadata_(nullptr),
+ _has_bits_(from._has_bits_),
+ channel_(from.channel_) {
+ _internal_metadata_.MergeFrom(from._internal_metadata_);
+ data_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+ if (from._internal_has_data()) {
+ data_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.data_);
+ }
+ // @@protoc_insertion_point(copy_constructor:webrtc.audioproc.ReverseStream)
+}
+
+void ReverseStream::SharedCtor() {
+ ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_ReverseStream_debug_2eproto.base);
+ data_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+
+ReverseStream::~ReverseStream() {
+ // @@protoc_insertion_point(destructor:webrtc.audioproc.ReverseStream)
+ SharedDtor();
+}
+
+void ReverseStream::SharedDtor() {
+ data_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+
+void ReverseStream::SetCachedSize(int size) const {
+ _cached_size_.Set(size);
+}
+const ReverseStream& ReverseStream::default_instance() {
+ ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_ReverseStream_debug_2eproto.base);
+ return *internal_default_instance();
+}
+
+
+void ReverseStream::Clear() {
+// @@protoc_insertion_point(message_clear_start:webrtc.audioproc.ReverseStream)
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ channel_.Clear();
+ cached_has_bits = _has_bits_[0];
+ if (cached_has_bits & 0x00000001u) {
+ data_.ClearNonDefaultToEmptyNoArena();
+ }
+ _has_bits_.Clear();
+ _internal_metadata_.Clear();
+}
+
+const char* ReverseStream::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ ::PROTOBUF_NAMESPACE_ID::uint32 tag;
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);
+ CHK_(ptr);
+ switch (tag >> 3) {
+ // optional bytes data = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {
+ auto str = _internal_mutable_data();
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // repeated bytes channel = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ auto str = _internal_add_channel();
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<18>(ptr));
+ } else goto handle_unusual;
+ continue;
+ default: {
+ handle_unusual:
+ if ((tag & 7) == 4 || tag == 0) {
+ ctx->SetLastTag(tag);
+ goto success;
+ }
+ ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx);
+ CHK_(ptr != nullptr);
+ continue;
+ }
+ } // switch
+ } // while
+success:
+ _has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto success;
+#undef CHK_
+}
+
+::PROTOBUF_NAMESPACE_ID::uint8* ReverseStream::_InternalSerialize(
+ ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:webrtc.audioproc.ReverseStream)
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _has_bits_[0];
+ // optional bytes data = 1;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->WriteBytesMaybeAliased(
+ 1, this->_internal_data(), target);
+ }
+
+ // repeated bytes channel = 2;
+ for (int i = 0, n = this->_internal_channel_size(); i < n; i++) {
+ const auto& s = this->_internal_channel(i);
+ target = stream->WriteBytes(2, s, target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields().data(),
+ static_cast<int>(_internal_metadata_.unknown_fields().size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:webrtc.audioproc.ReverseStream)
+ return target;
+}
+
+size_t ReverseStream::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:webrtc.audioproc.ReverseStream)
+ size_t total_size = 0;
+
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ // repeated bytes channel = 2;
+ total_size += 1 *
+ ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(channel_.size());
+ for (int i = 0, n = channel_.size(); i < n; i++) {
+ total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+ channel_.Get(i));
+ }
+
+ // optional bytes data = 1;
+ cached_has_bits = _has_bits_[0];
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+ this->_internal_data());
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields().size();
+ }
+ int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void ReverseStream::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::PROTOBUF_NAMESPACE_ID::internal::DownCast<const ReverseStream*>(
+ &from));
+}
+
+void ReverseStream::MergeFrom(const ReverseStream& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:webrtc.audioproc.ReverseStream)
+ GOOGLE_DCHECK_NE(&from, this);
+ _internal_metadata_.MergeFrom(from._internal_metadata_);
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ channel_.MergeFrom(from.channel_);
+ if (from._internal_has_data()) {
+ _has_bits_[0] |= 0x00000001u;
+ data_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.data_);
+ }
+}
+
+void ReverseStream::CopyFrom(const ReverseStream& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:webrtc.audioproc.ReverseStream)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool ReverseStream::IsInitialized() const {
+ return true;
+}
+
+void ReverseStream::InternalSwap(ReverseStream* other) {
+ using std::swap;
+ _internal_metadata_.Swap(&other->_internal_metadata_);
+ swap(_has_bits_[0], other->_has_bits_[0]);
+ channel_.InternalSwap(&other->channel_);
+ data_.Swap(&other->data_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+ GetArenaNoVirtual());
+}
+
+std::string ReverseStream::GetTypeName() const {
+ return "webrtc.audioproc.ReverseStream";
+}
+
+
+// ===================================================================
+
+void Stream::InitAsDefaultInstance() {
+}
+class Stream::_Internal {
+ public:
+ using HasBits = decltype(std::declval<Stream>()._has_bits_);
+ static void set_has_input_data(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static void set_has_output_data(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+ static void set_has_delay(HasBits* has_bits) {
+ (*has_bits)[0] |= 4u;
+ }
+ static void set_has_drift(HasBits* has_bits) {
+ (*has_bits)[0] |= 8u;
+ }
+ static void set_has_level(HasBits* has_bits) {
+ (*has_bits)[0] |= 16u;
+ }
+ static void set_has_keypress(HasBits* has_bits) {
+ (*has_bits)[0] |= 32u;
+ }
+};
+
+Stream::Stream()
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(), _internal_metadata_(nullptr) {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:webrtc.audioproc.Stream)
+}
+Stream::Stream(const Stream& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(),
+ _internal_metadata_(nullptr),
+ _has_bits_(from._has_bits_),
+ input_channel_(from.input_channel_),
+ output_channel_(from.output_channel_) {
+ _internal_metadata_.MergeFrom(from._internal_metadata_);
+ input_data_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+ if (from._internal_has_input_data()) {
+ input_data_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.input_data_);
+ }
+ output_data_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+ if (from._internal_has_output_data()) {
+ output_data_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.output_data_);
+ }
+ ::memcpy(&delay_, &from.delay_,
+ static_cast<size_t>(reinterpret_cast<char*>(&keypress_) -
+ reinterpret_cast<char*>(&delay_)) + sizeof(keypress_));
+ // @@protoc_insertion_point(copy_constructor:webrtc.audioproc.Stream)
+}
+
+void Stream::SharedCtor() {
+ ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_Stream_debug_2eproto.base);
+ input_data_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+ output_data_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+ ::memset(&delay_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&keypress_) -
+ reinterpret_cast<char*>(&delay_)) + sizeof(keypress_));
+}
+
+Stream::~Stream() {
+ // @@protoc_insertion_point(destructor:webrtc.audioproc.Stream)
+ SharedDtor();
+}
+
+void Stream::SharedDtor() {
+ input_data_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+ output_data_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+
+void Stream::SetCachedSize(int size) const {
+ _cached_size_.Set(size);
+}
+const Stream& Stream::default_instance() {
+ ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_Stream_debug_2eproto.base);
+ return *internal_default_instance();
+}
+
+
+void Stream::Clear() {
+// @@protoc_insertion_point(message_clear_start:webrtc.audioproc.Stream)
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ input_channel_.Clear();
+ output_channel_.Clear();
+ cached_has_bits = _has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ input_data_.ClearNonDefaultToEmptyNoArena();
+ }
+ if (cached_has_bits & 0x00000002u) {
+ output_data_.ClearNonDefaultToEmptyNoArena();
+ }
+ }
+ if (cached_has_bits & 0x0000003cu) {
+ ::memset(&delay_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&keypress_) -
+ reinterpret_cast<char*>(&delay_)) + sizeof(keypress_));
+ }
+ _has_bits_.Clear();
+ _internal_metadata_.Clear();
+}
+
+const char* Stream::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ ::PROTOBUF_NAMESPACE_ID::uint32 tag;
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);
+ CHK_(ptr);
+ switch (tag >> 3) {
+ // optional bytes input_data = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) {
+ auto str = _internal_mutable_input_data();
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional bytes output_data = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) {
+ auto str = _internal_mutable_output_data();
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional int32 delay = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 24)) {
+ _Internal::set_has_delay(&has_bits);
+ delay_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional sint32 drift = 4;
+ case 4:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 32)) {
+ _Internal::set_has_drift(&has_bits);
+ drift_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarintZigZag32(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional int32 level = 5;
+ case 5:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 40)) {
+ _Internal::set_has_level(&has_bits);
+ level_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional bool keypress = 6;
+ case 6:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 48)) {
+ _Internal::set_has_keypress(&has_bits);
+ keypress_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // repeated bytes input_channel = 7;
+ case 7:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 58)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ auto str = _internal_add_input_channel();
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<58>(ptr));
+ } else goto handle_unusual;
+ continue;
+ // repeated bytes output_channel = 8;
+ case 8:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 66)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ auto str = _internal_add_output_channel();
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<66>(ptr));
+ } else goto handle_unusual;
+ continue;
+ default: {
+ handle_unusual:
+ if ((tag & 7) == 4 || tag == 0) {
+ ctx->SetLastTag(tag);
+ goto success;
+ }
+ ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx);
+ CHK_(ptr != nullptr);
+ continue;
+ }
+ } // switch
+ } // while
+success:
+ _has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto success;
+#undef CHK_
+}
+
+::PROTOBUF_NAMESPACE_ID::uint8* Stream::_InternalSerialize(
+ ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:webrtc.audioproc.Stream)
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _has_bits_[0];
+ // optional bytes input_data = 1;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->WriteBytesMaybeAliased(
+ 1, this->_internal_input_data(), target);
+ }
+
+ // optional bytes output_data = 2;
+ if (cached_has_bits & 0x00000002u) {
+ target = stream->WriteBytesMaybeAliased(
+ 2, this->_internal_output_data(), target);
+ }
+
+ // optional int32 delay = 3;
+ if (cached_has_bits & 0x00000004u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(3, this->_internal_delay(), target);
+ }
+
+ // optional sint32 drift = 4;
+ if (cached_has_bits & 0x00000008u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteSInt32ToArray(4, this->_internal_drift(), target);
+ }
+
+ // optional int32 level = 5;
+ if (cached_has_bits & 0x00000010u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(5, this->_internal_level(), target);
+ }
+
+ // optional bool keypress = 6;
+ if (cached_has_bits & 0x00000020u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(6, this->_internal_keypress(), target);
+ }
+
+ // repeated bytes input_channel = 7;
+ for (int i = 0, n = this->_internal_input_channel_size(); i < n; i++) {
+ const auto& s = this->_internal_input_channel(i);
+ target = stream->WriteBytes(7, s, target);
+ }
+
+ // repeated bytes output_channel = 8;
+ for (int i = 0, n = this->_internal_output_channel_size(); i < n; i++) {
+ const auto& s = this->_internal_output_channel(i);
+ target = stream->WriteBytes(8, s, target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields().data(),
+ static_cast<int>(_internal_metadata_.unknown_fields().size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:webrtc.audioproc.Stream)
+ return target;
+}
+
+size_t Stream::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:webrtc.audioproc.Stream)
+ size_t total_size = 0;
+
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ // repeated bytes input_channel = 7;
+ total_size += 1 *
+ ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(input_channel_.size());
+ for (int i = 0, n = input_channel_.size(); i < n; i++) {
+ total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+ input_channel_.Get(i));
+ }
+
+ // repeated bytes output_channel = 8;
+ total_size += 1 *
+ ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(output_channel_.size());
+ for (int i = 0, n = output_channel_.size(); i < n; i++) {
+ total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+ output_channel_.Get(i));
+ }
+
+ cached_has_bits = _has_bits_[0];
+ if (cached_has_bits & 0x0000003fu) {
+ // optional bytes input_data = 1;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+ this->_internal_input_data());
+ }
+
+ // optional bytes output_data = 2;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+ this->_internal_output_data());
+ }
+
+ // optional int32 delay = 3;
+ if (cached_has_bits & 0x00000004u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+ this->_internal_delay());
+ }
+
+ // optional sint32 drift = 4;
+ if (cached_has_bits & 0x00000008u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SInt32Size(
+ this->_internal_drift());
+ }
+
+ // optional int32 level = 5;
+ if (cached_has_bits & 0x00000010u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+ this->_internal_level());
+ }
+
+ // optional bool keypress = 6;
+ if (cached_has_bits & 0x00000020u) {
+ total_size += 1 + 1;
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields().size();
+ }
+ int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void Stream::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::PROTOBUF_NAMESPACE_ID::internal::DownCast<const Stream*>(
+ &from));
+}
+
+void Stream::MergeFrom(const Stream& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:webrtc.audioproc.Stream)
+ GOOGLE_DCHECK_NE(&from, this);
+ _internal_metadata_.MergeFrom(from._internal_metadata_);
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ input_channel_.MergeFrom(from.input_channel_);
+ output_channel_.MergeFrom(from.output_channel_);
+ cached_has_bits = from._has_bits_[0];
+ if (cached_has_bits & 0x0000003fu) {
+ if (cached_has_bits & 0x00000001u) {
+ _has_bits_[0] |= 0x00000001u;
+ input_data_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.input_data_);
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _has_bits_[0] |= 0x00000002u;
+ output_data_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.output_data_);
+ }
+ if (cached_has_bits & 0x00000004u) {
+ delay_ = from.delay_;
+ }
+ if (cached_has_bits & 0x00000008u) {
+ drift_ = from.drift_;
+ }
+ if (cached_has_bits & 0x00000010u) {
+ level_ = from.level_;
+ }
+ if (cached_has_bits & 0x00000020u) {
+ keypress_ = from.keypress_;
+ }
+ _has_bits_[0] |= cached_has_bits;
+ }
+}
+
+void Stream::CopyFrom(const Stream& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:webrtc.audioproc.Stream)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool Stream::IsInitialized() const {
+ return true;
+}
+
+void Stream::InternalSwap(Stream* other) {
+ using std::swap;
+ _internal_metadata_.Swap(&other->_internal_metadata_);
+ swap(_has_bits_[0], other->_has_bits_[0]);
+ input_channel_.InternalSwap(&other->input_channel_);
+ output_channel_.InternalSwap(&other->output_channel_);
+ input_data_.Swap(&other->input_data_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+ GetArenaNoVirtual());
+ output_data_.Swap(&other->output_data_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+ GetArenaNoVirtual());
+ swap(delay_, other->delay_);
+ swap(drift_, other->drift_);
+ swap(level_, other->level_);
+ swap(keypress_, other->keypress_);
+}
+
+std::string Stream::GetTypeName() const {
+ return "webrtc.audioproc.Stream";
+}
+
+
+// ===================================================================
+
+void Config::InitAsDefaultInstance() {
+}
+class Config::_Internal {
+ public:
+ using HasBits = decltype(std::declval<Config>()._has_bits_);
+ static void set_has_aec_enabled(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+ static void set_has_aec_delay_agnostic_enabled(HasBits* has_bits) {
+ (*has_bits)[0] |= 4u;
+ }
+ static void set_has_aec_drift_compensation_enabled(HasBits* has_bits) {
+ (*has_bits)[0] |= 8u;
+ }
+ static void set_has_aec_extended_filter_enabled(HasBits* has_bits) {
+ (*has_bits)[0] |= 16u;
+ }
+ static void set_has_aec_suppression_level(HasBits* has_bits) {
+ (*has_bits)[0] |= 32u;
+ }
+ static void set_has_aecm_enabled(HasBits* has_bits) {
+ (*has_bits)[0] |= 128u;
+ }
+ static void set_has_aecm_comfort_noise_enabled(HasBits* has_bits) {
+ (*has_bits)[0] |= 256u;
+ }
+ static void set_has_aecm_routing_mode(HasBits* has_bits) {
+ (*has_bits)[0] |= 64u;
+ }
+ static void set_has_agc_enabled(HasBits* has_bits) {
+ (*has_bits)[0] |= 512u;
+ }
+ static void set_has_agc_mode(HasBits* has_bits) {
+ (*has_bits)[0] |= 2048u;
+ }
+ static void set_has_agc_limiter_enabled(HasBits* has_bits) {
+ (*has_bits)[0] |= 1024u;
+ }
+ static void set_has_noise_robust_agc_enabled(HasBits* has_bits) {
+ (*has_bits)[0] |= 4096u;
+ }
+ static void set_has_hpf_enabled(HasBits* has_bits) {
+ (*has_bits)[0] |= 8192u;
+ }
+ static void set_has_ns_enabled(HasBits* has_bits) {
+ (*has_bits)[0] |= 16384u;
+ }
+ static void set_has_ns_level(HasBits* has_bits) {
+ (*has_bits)[0] |= 65536u;
+ }
+ static void set_has_transient_suppression_enabled(HasBits* has_bits) {
+ (*has_bits)[0] |= 32768u;
+ }
+ static void set_has_experiments_description(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static void set_has_pre_amplifier_enabled(HasBits* has_bits) {
+ (*has_bits)[0] |= 131072u;
+ }
+ static void set_has_pre_amplifier_fixed_gain_factor(HasBits* has_bits) {
+ (*has_bits)[0] |= 262144u;
+ }
+};
+
+Config::Config()
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(), _internal_metadata_(nullptr) {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:webrtc.audioproc.Config)
+}
+Config::Config(const Config& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(),
+ _internal_metadata_(nullptr),
+ _has_bits_(from._has_bits_) {
+ _internal_metadata_.MergeFrom(from._internal_metadata_);
+ experiments_description_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+ if (from._internal_has_experiments_description()) {
+ experiments_description_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.experiments_description_);
+ }
+ ::memcpy(&aec_enabled_, &from.aec_enabled_,
+ static_cast<size_t>(reinterpret_cast<char*>(&pre_amplifier_fixed_gain_factor_) -
+ reinterpret_cast<char*>(&aec_enabled_)) + sizeof(pre_amplifier_fixed_gain_factor_));
+ // @@protoc_insertion_point(copy_constructor:webrtc.audioproc.Config)
+}
+
+void Config::SharedCtor() {
+ ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_Config_debug_2eproto.base);
+ experiments_description_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+ ::memset(&aec_enabled_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&pre_amplifier_fixed_gain_factor_) -
+ reinterpret_cast<char*>(&aec_enabled_)) + sizeof(pre_amplifier_fixed_gain_factor_));
+}
+
+Config::~Config() {
+ // @@protoc_insertion_point(destructor:webrtc.audioproc.Config)
+ SharedDtor();
+}
+
+void Config::SharedDtor() {
+ experiments_description_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+
+void Config::SetCachedSize(int size) const {
+ _cached_size_.Set(size);
+}
+const Config& Config::default_instance() {
+ ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_Config_debug_2eproto.base);
+ return *internal_default_instance();
+}
+
+
+void Config::Clear() {
+// @@protoc_insertion_point(message_clear_start:webrtc.audioproc.Config)
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _has_bits_[0];
+ if (cached_has_bits & 0x00000001u) {
+ experiments_description_.ClearNonDefaultToEmptyNoArena();
+ }
+ if (cached_has_bits & 0x000000feu) {
+ ::memset(&aec_enabled_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&aecm_enabled_) -
+ reinterpret_cast<char*>(&aec_enabled_)) + sizeof(aecm_enabled_));
+ }
+ if (cached_has_bits & 0x0000ff00u) {
+ ::memset(&aecm_comfort_noise_enabled_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&transient_suppression_enabled_) -
+ reinterpret_cast<char*>(&aecm_comfort_noise_enabled_)) + sizeof(transient_suppression_enabled_));
+ }
+ if (cached_has_bits & 0x00070000u) {
+ ::memset(&ns_level_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&pre_amplifier_fixed_gain_factor_) -
+ reinterpret_cast<char*>(&ns_level_)) + sizeof(pre_amplifier_fixed_gain_factor_));
+ }
+ _has_bits_.Clear();
+ _internal_metadata_.Clear();
+}
+
+const char* Config::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ ::PROTOBUF_NAMESPACE_ID::uint32 tag;
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);
+ CHK_(ptr);
+ switch (tag >> 3) {
+ // optional bool aec_enabled = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8)) {
+ _Internal::set_has_aec_enabled(&has_bits);
+ aec_enabled_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional bool aec_delay_agnostic_enabled = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) {
+ _Internal::set_has_aec_delay_agnostic_enabled(&has_bits);
+ aec_delay_agnostic_enabled_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional bool aec_drift_compensation_enabled = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 24)) {
+ _Internal::set_has_aec_drift_compensation_enabled(&has_bits);
+ aec_drift_compensation_enabled_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional bool aec_extended_filter_enabled = 4;
+ case 4:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 32)) {
+ _Internal::set_has_aec_extended_filter_enabled(&has_bits);
+ aec_extended_filter_enabled_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional int32 aec_suppression_level = 5;
+ case 5:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 40)) {
+ _Internal::set_has_aec_suppression_level(&has_bits);
+ aec_suppression_level_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional bool aecm_enabled = 6;
+ case 6:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 48)) {
+ _Internal::set_has_aecm_enabled(&has_bits);
+ aecm_enabled_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional bool aecm_comfort_noise_enabled = 7 [deprecated = true];
+ case 7:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 56)) {
+ _Internal::set_has_aecm_comfort_noise_enabled(&has_bits);
+ aecm_comfort_noise_enabled_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional int32 aecm_routing_mode = 8 [deprecated = true];
+ case 8:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 64)) {
+ _Internal::set_has_aecm_routing_mode(&has_bits);
+ aecm_routing_mode_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional bool agc_enabled = 9;
+ case 9:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 72)) {
+ _Internal::set_has_agc_enabled(&has_bits);
+ agc_enabled_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional int32 agc_mode = 10;
+ case 10:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 80)) {
+ _Internal::set_has_agc_mode(&has_bits);
+ agc_mode_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional bool agc_limiter_enabled = 11;
+ case 11:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 88)) {
+ _Internal::set_has_agc_limiter_enabled(&has_bits);
+ agc_limiter_enabled_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional bool noise_robust_agc_enabled = 12;
+ case 12:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 96)) {
+ _Internal::set_has_noise_robust_agc_enabled(&has_bits);
+ noise_robust_agc_enabled_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional bool hpf_enabled = 13;
+ case 13:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 104)) {
+ _Internal::set_has_hpf_enabled(&has_bits);
+ hpf_enabled_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional bool ns_enabled = 14;
+ case 14:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 112)) {
+ _Internal::set_has_ns_enabled(&has_bits);
+ ns_enabled_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional int32 ns_level = 15;
+ case 15:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 120)) {
+ _Internal::set_has_ns_level(&has_bits);
+ ns_level_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional bool transient_suppression_enabled = 16;
+ case 16:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 128)) {
+ _Internal::set_has_transient_suppression_enabled(&has_bits);
+ transient_suppression_enabled_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional string experiments_description = 17;
+ case 17:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 138)) {
+ auto str = _internal_mutable_experiments_description();
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional bool pre_amplifier_enabled = 19;
+ case 19:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 152)) {
+ _Internal::set_has_pre_amplifier_enabled(&has_bits);
+ pre_amplifier_enabled_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional float pre_amplifier_fixed_gain_factor = 20;
+ case 20:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 165)) {
+ _Internal::set_has_pre_amplifier_fixed_gain_factor(&has_bits);
+ pre_amplifier_fixed_gain_factor_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<float>(ptr);
+ ptr += sizeof(float);
+ } else goto handle_unusual;
+ continue;
+ default: {
+ handle_unusual:
+ if ((tag & 7) == 4 || tag == 0) {
+ ctx->SetLastTag(tag);
+ goto success;
+ }
+ ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx);
+ CHK_(ptr != nullptr);
+ continue;
+ }
+ } // switch
+ } // while
+success:
+ _has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto success;
+#undef CHK_
+}
+
+::PROTOBUF_NAMESPACE_ID::uint8* Config::_InternalSerialize(
+ ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:webrtc.audioproc.Config)
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _has_bits_[0];
+ // optional bool aec_enabled = 1;
+ if (cached_has_bits & 0x00000002u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(1, this->_internal_aec_enabled(), target);
+ }
+
+ // optional bool aec_delay_agnostic_enabled = 2;
+ if (cached_has_bits & 0x00000004u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(2, this->_internal_aec_delay_agnostic_enabled(), target);
+ }
+
+ // optional bool aec_drift_compensation_enabled = 3;
+ if (cached_has_bits & 0x00000008u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(3, this->_internal_aec_drift_compensation_enabled(), target);
+ }
+
+ // optional bool aec_extended_filter_enabled = 4;
+ if (cached_has_bits & 0x00000010u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(4, this->_internal_aec_extended_filter_enabled(), target);
+ }
+
+ // optional int32 aec_suppression_level = 5;
+ if (cached_has_bits & 0x00000020u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(5, this->_internal_aec_suppression_level(), target);
+ }
+
+ // optional bool aecm_enabled = 6;
+ if (cached_has_bits & 0x00000080u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(6, this->_internal_aecm_enabled(), target);
+ }
+
+ // optional bool aecm_comfort_noise_enabled = 7 [deprecated = true];
+ if (cached_has_bits & 0x00000100u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(7, this->_internal_aecm_comfort_noise_enabled(), target);
+ }
+
+ // optional int32 aecm_routing_mode = 8 [deprecated = true];
+ if (cached_has_bits & 0x00000040u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(8, this->_internal_aecm_routing_mode(), target);
+ }
+
+ // optional bool agc_enabled = 9;
+ if (cached_has_bits & 0x00000200u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(9, this->_internal_agc_enabled(), target);
+ }
+
+ // optional int32 agc_mode = 10;
+ if (cached_has_bits & 0x00000800u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(10, this->_internal_agc_mode(), target);
+ }
+
+ // optional bool agc_limiter_enabled = 11;
+ if (cached_has_bits & 0x00000400u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(11, this->_internal_agc_limiter_enabled(), target);
+ }
+
+ // optional bool noise_robust_agc_enabled = 12;
+ if (cached_has_bits & 0x00001000u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(12, this->_internal_noise_robust_agc_enabled(), target);
+ }
+
+ // optional bool hpf_enabled = 13;
+ if (cached_has_bits & 0x00002000u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(13, this->_internal_hpf_enabled(), target);
+ }
+
+ // optional bool ns_enabled = 14;
+ if (cached_has_bits & 0x00004000u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(14, this->_internal_ns_enabled(), target);
+ }
+
+ // optional int32 ns_level = 15;
+ if (cached_has_bits & 0x00010000u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(15, this->_internal_ns_level(), target);
+ }
+
+ // optional bool transient_suppression_enabled = 16;
+ if (cached_has_bits & 0x00008000u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(16, this->_internal_transient_suppression_enabled(), target);
+ }
+
+ // optional string experiments_description = 17;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->WriteStringMaybeAliased(
+ 17, this->_internal_experiments_description(), target);
+ }
+
+ // optional bool pre_amplifier_enabled = 19;
+ if (cached_has_bits & 0x00020000u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(19, this->_internal_pre_amplifier_enabled(), target);
+ }
+
+ // optional float pre_amplifier_fixed_gain_factor = 20;
+ if (cached_has_bits & 0x00040000u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloatToArray(20, this->_internal_pre_amplifier_fixed_gain_factor(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields().data(),
+ static_cast<int>(_internal_metadata_.unknown_fields().size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:webrtc.audioproc.Config)
+ return target;
+}
+
+size_t Config::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:webrtc.audioproc.Config)
+ size_t total_size = 0;
+
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _has_bits_[0];
+ if (cached_has_bits & 0x000000ffu) {
+ // optional string experiments_description = 17;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 2 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+ this->_internal_experiments_description());
+ }
+
+ // optional bool aec_enabled = 1;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 + 1;
+ }
+
+ // optional bool aec_delay_agnostic_enabled = 2;
+ if (cached_has_bits & 0x00000004u) {
+ total_size += 1 + 1;
+ }
+
+ // optional bool aec_drift_compensation_enabled = 3;
+ if (cached_has_bits & 0x00000008u) {
+ total_size += 1 + 1;
+ }
+
+ // optional bool aec_extended_filter_enabled = 4;
+ if (cached_has_bits & 0x00000010u) {
+ total_size += 1 + 1;
+ }
+
+ // optional int32 aec_suppression_level = 5;
+ if (cached_has_bits & 0x00000020u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+ this->_internal_aec_suppression_level());
+ }
+
+ // optional int32 aecm_routing_mode = 8 [deprecated = true];
+ if (cached_has_bits & 0x00000040u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+ this->_internal_aecm_routing_mode());
+ }
+
+ // optional bool aecm_enabled = 6;
+ if (cached_has_bits & 0x00000080u) {
+ total_size += 1 + 1;
+ }
+
+ }
+ if (cached_has_bits & 0x0000ff00u) {
+ // optional bool aecm_comfort_noise_enabled = 7 [deprecated = true];
+ if (cached_has_bits & 0x00000100u) {
+ total_size += 1 + 1;
+ }
+
+ // optional bool agc_enabled = 9;
+ if (cached_has_bits & 0x00000200u) {
+ total_size += 1 + 1;
+ }
+
+ // optional bool agc_limiter_enabled = 11;
+ if (cached_has_bits & 0x00000400u) {
+ total_size += 1 + 1;
+ }
+
+ // optional int32 agc_mode = 10;
+ if (cached_has_bits & 0x00000800u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+ this->_internal_agc_mode());
+ }
+
+ // optional bool noise_robust_agc_enabled = 12;
+ if (cached_has_bits & 0x00001000u) {
+ total_size += 1 + 1;
+ }
+
+ // optional bool hpf_enabled = 13;
+ if (cached_has_bits & 0x00002000u) {
+ total_size += 1 + 1;
+ }
+
+ // optional bool ns_enabled = 14;
+ if (cached_has_bits & 0x00004000u) {
+ total_size += 1 + 1;
+ }
+
+ // optional bool transient_suppression_enabled = 16;
+ if (cached_has_bits & 0x00008000u) {
+ total_size += 2 + 1;
+ }
+
+ }
+ if (cached_has_bits & 0x00070000u) {
+ // optional int32 ns_level = 15;
+ if (cached_has_bits & 0x00010000u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+ this->_internal_ns_level());
+ }
+
+ // optional bool pre_amplifier_enabled = 19;
+ if (cached_has_bits & 0x00020000u) {
+ total_size += 2 + 1;
+ }
+
+ // optional float pre_amplifier_fixed_gain_factor = 20;
+ if (cached_has_bits & 0x00040000u) {
+ total_size += 2 + 4;
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields().size();
+ }
+ int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void Config::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::PROTOBUF_NAMESPACE_ID::internal::DownCast<const Config*>(
+ &from));
+}
+
+void Config::MergeFrom(const Config& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:webrtc.audioproc.Config)
+ GOOGLE_DCHECK_NE(&from, this);
+ _internal_metadata_.MergeFrom(from._internal_metadata_);
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._has_bits_[0];
+ if (cached_has_bits & 0x000000ffu) {
+ if (cached_has_bits & 0x00000001u) {
+ _has_bits_[0] |= 0x00000001u;
+ experiments_description_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.experiments_description_);
+ }
+ if (cached_has_bits & 0x00000002u) {
+ aec_enabled_ = from.aec_enabled_;
+ }
+ if (cached_has_bits & 0x00000004u) {
+ aec_delay_agnostic_enabled_ = from.aec_delay_agnostic_enabled_;
+ }
+ if (cached_has_bits & 0x00000008u) {
+ aec_drift_compensation_enabled_ = from.aec_drift_compensation_enabled_;
+ }
+ if (cached_has_bits & 0x00000010u) {
+ aec_extended_filter_enabled_ = from.aec_extended_filter_enabled_;
+ }
+ if (cached_has_bits & 0x00000020u) {
+ aec_suppression_level_ = from.aec_suppression_level_;
+ }
+ if (cached_has_bits & 0x00000040u) {
+ aecm_routing_mode_ = from.aecm_routing_mode_;
+ }
+ if (cached_has_bits & 0x00000080u) {
+ aecm_enabled_ = from.aecm_enabled_;
+ }
+ _has_bits_[0] |= cached_has_bits;
+ }
+ if (cached_has_bits & 0x0000ff00u) {
+ if (cached_has_bits & 0x00000100u) {
+ aecm_comfort_noise_enabled_ = from.aecm_comfort_noise_enabled_;
+ }
+ if (cached_has_bits & 0x00000200u) {
+ agc_enabled_ = from.agc_enabled_;
+ }
+ if (cached_has_bits & 0x00000400u) {
+ agc_limiter_enabled_ = from.agc_limiter_enabled_;
+ }
+ if (cached_has_bits & 0x00000800u) {
+ agc_mode_ = from.agc_mode_;
+ }
+ if (cached_has_bits & 0x00001000u) {
+ noise_robust_agc_enabled_ = from.noise_robust_agc_enabled_;
+ }
+ if (cached_has_bits & 0x00002000u) {
+ hpf_enabled_ = from.hpf_enabled_;
+ }
+ if (cached_has_bits & 0x00004000u) {
+ ns_enabled_ = from.ns_enabled_;
+ }
+ if (cached_has_bits & 0x00008000u) {
+ transient_suppression_enabled_ = from.transient_suppression_enabled_;
+ }
+ _has_bits_[0] |= cached_has_bits;
+ }
+ if (cached_has_bits & 0x00070000u) {
+ if (cached_has_bits & 0x00010000u) {
+ ns_level_ = from.ns_level_;
+ }
+ if (cached_has_bits & 0x00020000u) {
+ pre_amplifier_enabled_ = from.pre_amplifier_enabled_;
+ }
+ if (cached_has_bits & 0x00040000u) {
+ pre_amplifier_fixed_gain_factor_ = from.pre_amplifier_fixed_gain_factor_;
+ }
+ _has_bits_[0] |= cached_has_bits;
+ }
+}
+
+void Config::CopyFrom(const Config& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:webrtc.audioproc.Config)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool Config::IsInitialized() const {
+ return true;
+}
+
+void Config::InternalSwap(Config* other) {
+ using std::swap;
+ _internal_metadata_.Swap(&other->_internal_metadata_);
+ swap(_has_bits_[0], other->_has_bits_[0]);
+ experiments_description_.Swap(&other->experiments_description_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+ GetArenaNoVirtual());
+ swap(aec_enabled_, other->aec_enabled_);
+ swap(aec_delay_agnostic_enabled_, other->aec_delay_agnostic_enabled_);
+ swap(aec_drift_compensation_enabled_, other->aec_drift_compensation_enabled_);
+ swap(aec_extended_filter_enabled_, other->aec_extended_filter_enabled_);
+ swap(aec_suppression_level_, other->aec_suppression_level_);
+ swap(aecm_routing_mode_, other->aecm_routing_mode_);
+ swap(aecm_enabled_, other->aecm_enabled_);
+ swap(aecm_comfort_noise_enabled_, other->aecm_comfort_noise_enabled_);
+ swap(agc_enabled_, other->agc_enabled_);
+ swap(agc_limiter_enabled_, other->agc_limiter_enabled_);
+ swap(agc_mode_, other->agc_mode_);
+ swap(noise_robust_agc_enabled_, other->noise_robust_agc_enabled_);
+ swap(hpf_enabled_, other->hpf_enabled_);
+ swap(ns_enabled_, other->ns_enabled_);
+ swap(transient_suppression_enabled_, other->transient_suppression_enabled_);
+ swap(ns_level_, other->ns_level_);
+ swap(pre_amplifier_enabled_, other->pre_amplifier_enabled_);
+ swap(pre_amplifier_fixed_gain_factor_, other->pre_amplifier_fixed_gain_factor_);
+}
+
+std::string Config::GetTypeName() const {
+ return "webrtc.audioproc.Config";
+}
+
+
+// ===================================================================
+
+void PlayoutAudioDeviceInfo::InitAsDefaultInstance() {
+}
+class PlayoutAudioDeviceInfo::_Internal {
+ public:
+ using HasBits = decltype(std::declval<PlayoutAudioDeviceInfo>()._has_bits_);
+ static void set_has_id(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static void set_has_max_volume(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+};
+
+PlayoutAudioDeviceInfo::PlayoutAudioDeviceInfo()
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(), _internal_metadata_(nullptr) {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:webrtc.audioproc.PlayoutAudioDeviceInfo)
+}
+PlayoutAudioDeviceInfo::PlayoutAudioDeviceInfo(const PlayoutAudioDeviceInfo& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(),
+ _internal_metadata_(nullptr),
+ _has_bits_(from._has_bits_) {
+ _internal_metadata_.MergeFrom(from._internal_metadata_);
+ ::memcpy(&id_, &from.id_,
+ static_cast<size_t>(reinterpret_cast<char*>(&max_volume_) -
+ reinterpret_cast<char*>(&id_)) + sizeof(max_volume_));
+ // @@protoc_insertion_point(copy_constructor:webrtc.audioproc.PlayoutAudioDeviceInfo)
+}
+
+void PlayoutAudioDeviceInfo::SharedCtor() {
+ ::memset(&id_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&max_volume_) -
+ reinterpret_cast<char*>(&id_)) + sizeof(max_volume_));
+}
+
+PlayoutAudioDeviceInfo::~PlayoutAudioDeviceInfo() {
+ // @@protoc_insertion_point(destructor:webrtc.audioproc.PlayoutAudioDeviceInfo)
+ SharedDtor();
+}
+
+void PlayoutAudioDeviceInfo::SharedDtor() {
+}
+
+void PlayoutAudioDeviceInfo::SetCachedSize(int size) const {
+ _cached_size_.Set(size);
+}
+const PlayoutAudioDeviceInfo& PlayoutAudioDeviceInfo::default_instance() {
+ ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_PlayoutAudioDeviceInfo_debug_2eproto.base);
+ return *internal_default_instance();
+}
+
+
+void PlayoutAudioDeviceInfo::Clear() {
+// @@protoc_insertion_point(message_clear_start:webrtc.audioproc.PlayoutAudioDeviceInfo)
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ ::memset(&id_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&max_volume_) -
+ reinterpret_cast<char*>(&id_)) + sizeof(max_volume_));
+ }
+ _has_bits_.Clear();
+ _internal_metadata_.Clear();
+}
+
+const char* PlayoutAudioDeviceInfo::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ ::PROTOBUF_NAMESPACE_ID::uint32 tag;
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);
+ CHK_(ptr);
+ switch (tag >> 3) {
+ // optional int32 id = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8)) {
+ _Internal::set_has_id(&has_bits);
+ id_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional int32 max_volume = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) {
+ _Internal::set_has_max_volume(&has_bits);
+ max_volume_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ default: {
+ handle_unusual:
+ if ((tag & 7) == 4 || tag == 0) {
+ ctx->SetLastTag(tag);
+ goto success;
+ }
+ ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx);
+ CHK_(ptr != nullptr);
+ continue;
+ }
+ } // switch
+ } // while
+success:
+ _has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto success;
+#undef CHK_
+}
+
+::PROTOBUF_NAMESPACE_ID::uint8* PlayoutAudioDeviceInfo::_InternalSerialize(
+ ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:webrtc.audioproc.PlayoutAudioDeviceInfo)
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _has_bits_[0];
+ // optional int32 id = 1;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(1, this->_internal_id(), target);
+ }
+
+ // optional int32 max_volume = 2;
+ if (cached_has_bits & 0x00000002u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(2, this->_internal_max_volume(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields().data(),
+ static_cast<int>(_internal_metadata_.unknown_fields().size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:webrtc.audioproc.PlayoutAudioDeviceInfo)
+ return target;
+}
+
+size_t PlayoutAudioDeviceInfo::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:webrtc.audioproc.PlayoutAudioDeviceInfo)
+ size_t total_size = 0;
+
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ // optional int32 id = 1;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+ this->_internal_id());
+ }
+
+ // optional int32 max_volume = 2;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+ this->_internal_max_volume());
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields().size();
+ }
+ int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void PlayoutAudioDeviceInfo::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::PROTOBUF_NAMESPACE_ID::internal::DownCast<const PlayoutAudioDeviceInfo*>(
+ &from));
+}
+
+void PlayoutAudioDeviceInfo::MergeFrom(const PlayoutAudioDeviceInfo& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:webrtc.audioproc.PlayoutAudioDeviceInfo)
+ GOOGLE_DCHECK_NE(&from, this);
+ _internal_metadata_.MergeFrom(from._internal_metadata_);
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ id_ = from.id_;
+ }
+ if (cached_has_bits & 0x00000002u) {
+ max_volume_ = from.max_volume_;
+ }
+ _has_bits_[0] |= cached_has_bits;
+ }
+}
+
+void PlayoutAudioDeviceInfo::CopyFrom(const PlayoutAudioDeviceInfo& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:webrtc.audioproc.PlayoutAudioDeviceInfo)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool PlayoutAudioDeviceInfo::IsInitialized() const {
+ return true;
+}
+
+void PlayoutAudioDeviceInfo::InternalSwap(PlayoutAudioDeviceInfo* other) {
+ using std::swap;
+ _internal_metadata_.Swap(&other->_internal_metadata_);
+ swap(_has_bits_[0], other->_has_bits_[0]);
+ swap(id_, other->id_);
+ swap(max_volume_, other->max_volume_);
+}
+
+std::string PlayoutAudioDeviceInfo::GetTypeName() const {
+ return "webrtc.audioproc.PlayoutAudioDeviceInfo";
+}
+
+
+// ===================================================================
+
+void RuntimeSetting::InitAsDefaultInstance() {
+ ::webrtc::audioproc::_RuntimeSetting_default_instance_._instance.get_mutable()->playout_audio_device_change_ = const_cast< ::webrtc::audioproc::PlayoutAudioDeviceInfo*>(
+ ::webrtc::audioproc::PlayoutAudioDeviceInfo::internal_default_instance());
+}
+class RuntimeSetting::_Internal {
+ public:
+ using HasBits = decltype(std::declval<RuntimeSetting>()._has_bits_);
+ static void set_has_capture_pre_gain(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+ static void set_has_custom_render_processing_setting(HasBits* has_bits) {
+ (*has_bits)[0] |= 4u;
+ }
+ static void set_has_capture_fixed_post_gain(HasBits* has_bits) {
+ (*has_bits)[0] |= 8u;
+ }
+ static void set_has_playout_volume_change(HasBits* has_bits) {
+ (*has_bits)[0] |= 16u;
+ }
+ static const ::webrtc::audioproc::PlayoutAudioDeviceInfo& playout_audio_device_change(const RuntimeSetting* msg);
+ static void set_has_playout_audio_device_change(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static void set_has_capture_output_used(HasBits* has_bits) {
+ (*has_bits)[0] |= 32u;
+ }
+ static void set_has_capture_post_gain(HasBits* has_bits) {
+ (*has_bits)[0] |= 64u;
+ }
+};
+
+const ::webrtc::audioproc::PlayoutAudioDeviceInfo&
+RuntimeSetting::_Internal::playout_audio_device_change(const RuntimeSetting* msg) {
+ return *msg->playout_audio_device_change_;
+}
+RuntimeSetting::RuntimeSetting()
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(), _internal_metadata_(nullptr) {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:webrtc.audioproc.RuntimeSetting)
+}
+RuntimeSetting::RuntimeSetting(const RuntimeSetting& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(),
+ _internal_metadata_(nullptr),
+ _has_bits_(from._has_bits_) {
+ _internal_metadata_.MergeFrom(from._internal_metadata_);
+ if (from._internal_has_playout_audio_device_change()) {
+ playout_audio_device_change_ = new ::webrtc::audioproc::PlayoutAudioDeviceInfo(*from.playout_audio_device_change_);
+ } else {
+ playout_audio_device_change_ = nullptr;
+ }
+ ::memcpy(&capture_pre_gain_, &from.capture_pre_gain_,
+ static_cast<size_t>(reinterpret_cast<char*>(&capture_post_gain_) -
+ reinterpret_cast<char*>(&capture_pre_gain_)) + sizeof(capture_post_gain_));
+ // @@protoc_insertion_point(copy_constructor:webrtc.audioproc.RuntimeSetting)
+}
+
+void RuntimeSetting::SharedCtor() {
+ ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_RuntimeSetting_debug_2eproto.base);
+ ::memset(&playout_audio_device_change_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&capture_post_gain_) -
+ reinterpret_cast<char*>(&playout_audio_device_change_)) + sizeof(capture_post_gain_));
+}
+
+RuntimeSetting::~RuntimeSetting() {
+ // @@protoc_insertion_point(destructor:webrtc.audioproc.RuntimeSetting)
+ SharedDtor();
+}
+
+void RuntimeSetting::SharedDtor() {
+ if (this != internal_default_instance()) delete playout_audio_device_change_;
+}
+
+void RuntimeSetting::SetCachedSize(int size) const {
+ _cached_size_.Set(size);
+}
+const RuntimeSetting& RuntimeSetting::default_instance() {
+ ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_RuntimeSetting_debug_2eproto.base);
+ return *internal_default_instance();
+}
+
+
+void RuntimeSetting::Clear() {
+// @@protoc_insertion_point(message_clear_start:webrtc.audioproc.RuntimeSetting)
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _has_bits_[0];
+ if (cached_has_bits & 0x00000001u) {
+ GOOGLE_DCHECK(playout_audio_device_change_ != nullptr);
+ playout_audio_device_change_->Clear();
+ }
+ if (cached_has_bits & 0x0000007eu) {
+ ::memset(&capture_pre_gain_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&capture_post_gain_) -
+ reinterpret_cast<char*>(&capture_pre_gain_)) + sizeof(capture_post_gain_));
+ }
+ _has_bits_.Clear();
+ _internal_metadata_.Clear();
+}
+
+const char* RuntimeSetting::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ ::PROTOBUF_NAMESPACE_ID::uint32 tag;
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);
+ CHK_(ptr);
+ switch (tag >> 3) {
+ // optional float capture_pre_gain = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 13)) {
+ _Internal::set_has_capture_pre_gain(&has_bits);
+ capture_pre_gain_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<float>(ptr);
+ ptr += sizeof(float);
+ } else goto handle_unusual;
+ continue;
+ // optional float custom_render_processing_setting = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 21)) {
+ _Internal::set_has_custom_render_processing_setting(&has_bits);
+ custom_render_processing_setting_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<float>(ptr);
+ ptr += sizeof(float);
+ } else goto handle_unusual;
+ continue;
+ // optional float capture_fixed_post_gain = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 29)) {
+ _Internal::set_has_capture_fixed_post_gain(&has_bits);
+ capture_fixed_post_gain_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<float>(ptr);
+ ptr += sizeof(float);
+ } else goto handle_unusual;
+ continue;
+ // optional int32 playout_volume_change = 4;
+ case 4:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 32)) {
+ _Internal::set_has_playout_volume_change(&has_bits);
+ playout_volume_change_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional .webrtc.audioproc.PlayoutAudioDeviceInfo playout_audio_device_change = 5;
+ case 5:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 42)) {
+ ptr = ctx->ParseMessage(_internal_mutable_playout_audio_device_change(), ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional bool capture_output_used = 6;
+ case 6:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 48)) {
+ _Internal::set_has_capture_output_used(&has_bits);
+ capture_output_used_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional float capture_post_gain = 7;
+ case 7:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 61)) {
+ _Internal::set_has_capture_post_gain(&has_bits);
+ capture_post_gain_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<float>(ptr);
+ ptr += sizeof(float);
+ } else goto handle_unusual;
+ continue;
+ default: {
+ handle_unusual:
+ if ((tag & 7) == 4 || tag == 0) {
+ ctx->SetLastTag(tag);
+ goto success;
+ }
+ ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx);
+ CHK_(ptr != nullptr);
+ continue;
+ }
+ } // switch
+ } // while
+success:
+ _has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto success;
+#undef CHK_
+}
+
+::PROTOBUF_NAMESPACE_ID::uint8* RuntimeSetting::_InternalSerialize(
+ ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:webrtc.audioproc.RuntimeSetting)
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _has_bits_[0];
+ // optional float capture_pre_gain = 1;
+ if (cached_has_bits & 0x00000002u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloatToArray(1, this->_internal_capture_pre_gain(), target);
+ }
+
+ // optional float custom_render_processing_setting = 2;
+ if (cached_has_bits & 0x00000004u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloatToArray(2, this->_internal_custom_render_processing_setting(), target);
+ }
+
+ // optional float capture_fixed_post_gain = 3;
+ if (cached_has_bits & 0x00000008u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloatToArray(3, this->_internal_capture_fixed_post_gain(), target);
+ }
+
+ // optional int32 playout_volume_change = 4;
+ if (cached_has_bits & 0x00000010u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(4, this->_internal_playout_volume_change(), target);
+ }
+
+ // optional .webrtc.audioproc.PlayoutAudioDeviceInfo playout_audio_device_change = 5;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(
+ 5, _Internal::playout_audio_device_change(this), target, stream);
+ }
+
+ // optional bool capture_output_used = 6;
+ if (cached_has_bits & 0x00000020u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(6, this->_internal_capture_output_used(), target);
+ }
+
+ // optional float capture_post_gain = 7;
+ if (cached_has_bits & 0x00000040u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloatToArray(7, this->_internal_capture_post_gain(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields().data(),
+ static_cast<int>(_internal_metadata_.unknown_fields().size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:webrtc.audioproc.RuntimeSetting)
+ return target;
+}
+
+size_t RuntimeSetting::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:webrtc.audioproc.RuntimeSetting)
+ size_t total_size = 0;
+
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _has_bits_[0];
+ if (cached_has_bits & 0x0000007fu) {
+ // optional .webrtc.audioproc.PlayoutAudioDeviceInfo playout_audio_device_change = 5;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *playout_audio_device_change_);
+ }
+
+ // optional float capture_pre_gain = 1;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 + 4;
+ }
+
+ // optional float custom_render_processing_setting = 2;
+ if (cached_has_bits & 0x00000004u) {
+ total_size += 1 + 4;
+ }
+
+ // optional float capture_fixed_post_gain = 3;
+ if (cached_has_bits & 0x00000008u) {
+ total_size += 1 + 4;
+ }
+
+ // optional int32 playout_volume_change = 4;
+ if (cached_has_bits & 0x00000010u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size(
+ this->_internal_playout_volume_change());
+ }
+
+ // optional bool capture_output_used = 6;
+ if (cached_has_bits & 0x00000020u) {
+ total_size += 1 + 1;
+ }
+
+ // optional float capture_post_gain = 7;
+ if (cached_has_bits & 0x00000040u) {
+ total_size += 1 + 4;
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields().size();
+ }
+ int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void RuntimeSetting::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::PROTOBUF_NAMESPACE_ID::internal::DownCast<const RuntimeSetting*>(
+ &from));
+}
+
+void RuntimeSetting::MergeFrom(const RuntimeSetting& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:webrtc.audioproc.RuntimeSetting)
+ GOOGLE_DCHECK_NE(&from, this);
+ _internal_metadata_.MergeFrom(from._internal_metadata_);
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._has_bits_[0];
+ if (cached_has_bits & 0x0000007fu) {
+ if (cached_has_bits & 0x00000001u) {
+ _internal_mutable_playout_audio_device_change()->::webrtc::audioproc::PlayoutAudioDeviceInfo::MergeFrom(from._internal_playout_audio_device_change());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ capture_pre_gain_ = from.capture_pre_gain_;
+ }
+ if (cached_has_bits & 0x00000004u) {
+ custom_render_processing_setting_ = from.custom_render_processing_setting_;
+ }
+ if (cached_has_bits & 0x00000008u) {
+ capture_fixed_post_gain_ = from.capture_fixed_post_gain_;
+ }
+ if (cached_has_bits & 0x00000010u) {
+ playout_volume_change_ = from.playout_volume_change_;
+ }
+ if (cached_has_bits & 0x00000020u) {
+ capture_output_used_ = from.capture_output_used_;
+ }
+ if (cached_has_bits & 0x00000040u) {
+ capture_post_gain_ = from.capture_post_gain_;
+ }
+ _has_bits_[0] |= cached_has_bits;
+ }
+}
+
+void RuntimeSetting::CopyFrom(const RuntimeSetting& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:webrtc.audioproc.RuntimeSetting)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool RuntimeSetting::IsInitialized() const {
+ return true;
+}
+
+void RuntimeSetting::InternalSwap(RuntimeSetting* other) {
+ using std::swap;
+ _internal_metadata_.Swap(&other->_internal_metadata_);
+ swap(_has_bits_[0], other->_has_bits_[0]);
+ swap(playout_audio_device_change_, other->playout_audio_device_change_);
+ swap(capture_pre_gain_, other->capture_pre_gain_);
+ swap(custom_render_processing_setting_, other->custom_render_processing_setting_);
+ swap(capture_fixed_post_gain_, other->capture_fixed_post_gain_);
+ swap(playout_volume_change_, other->playout_volume_change_);
+ swap(capture_output_used_, other->capture_output_used_);
+ swap(capture_post_gain_, other->capture_post_gain_);
+}
+
+std::string RuntimeSetting::GetTypeName() const {
+ return "webrtc.audioproc.RuntimeSetting";
+}
+
+
+// ===================================================================
+
+void Event::InitAsDefaultInstance() {
+ ::webrtc::audioproc::_Event_default_instance_._instance.get_mutable()->init_ = const_cast< ::webrtc::audioproc::Init*>(
+ ::webrtc::audioproc::Init::internal_default_instance());
+ ::webrtc::audioproc::_Event_default_instance_._instance.get_mutable()->reverse_stream_ = const_cast< ::webrtc::audioproc::ReverseStream*>(
+ ::webrtc::audioproc::ReverseStream::internal_default_instance());
+ ::webrtc::audioproc::_Event_default_instance_._instance.get_mutable()->stream_ = const_cast< ::webrtc::audioproc::Stream*>(
+ ::webrtc::audioproc::Stream::internal_default_instance());
+ ::webrtc::audioproc::_Event_default_instance_._instance.get_mutable()->config_ = const_cast< ::webrtc::audioproc::Config*>(
+ ::webrtc::audioproc::Config::internal_default_instance());
+ ::webrtc::audioproc::_Event_default_instance_._instance.get_mutable()->runtime_setting_ = const_cast< ::webrtc::audioproc::RuntimeSetting*>(
+ ::webrtc::audioproc::RuntimeSetting::internal_default_instance());
+}
+class Event::_Internal {
+ public:
+ using HasBits = decltype(std::declval<Event>()._has_bits_);
+ static void set_has_type(HasBits* has_bits) {
+ (*has_bits)[0] |= 32u;
+ }
+ static const ::webrtc::audioproc::Init& init(const Event* msg);
+ static void set_has_init(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static const ::webrtc::audioproc::ReverseStream& reverse_stream(const Event* msg);
+ static void set_has_reverse_stream(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+ static const ::webrtc::audioproc::Stream& stream(const Event* msg);
+ static void set_has_stream(HasBits* has_bits) {
+ (*has_bits)[0] |= 4u;
+ }
+ static const ::webrtc::audioproc::Config& config(const Event* msg);
+ static void set_has_config(HasBits* has_bits) {
+ (*has_bits)[0] |= 8u;
+ }
+ static const ::webrtc::audioproc::RuntimeSetting& runtime_setting(const Event* msg);
+ static void set_has_runtime_setting(HasBits* has_bits) {
+ (*has_bits)[0] |= 16u;
+ }
+};
+
+const ::webrtc::audioproc::Init&
+Event::_Internal::init(const Event* msg) {
+ return *msg->init_;
+}
+const ::webrtc::audioproc::ReverseStream&
+Event::_Internal::reverse_stream(const Event* msg) {
+ return *msg->reverse_stream_;
+}
+const ::webrtc::audioproc::Stream&
+Event::_Internal::stream(const Event* msg) {
+ return *msg->stream_;
+}
+const ::webrtc::audioproc::Config&
+Event::_Internal::config(const Event* msg) {
+ return *msg->config_;
+}
+const ::webrtc::audioproc::RuntimeSetting&
+Event::_Internal::runtime_setting(const Event* msg) {
+ return *msg->runtime_setting_;
+}
+Event::Event()
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(), _internal_metadata_(nullptr) {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:webrtc.audioproc.Event)
+}
+Event::Event(const Event& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(),
+ _internal_metadata_(nullptr),
+ _has_bits_(from._has_bits_) {
+ _internal_metadata_.MergeFrom(from._internal_metadata_);
+ if (from._internal_has_init()) {
+ init_ = new ::webrtc::audioproc::Init(*from.init_);
+ } else {
+ init_ = nullptr;
+ }
+ if (from._internal_has_reverse_stream()) {
+ reverse_stream_ = new ::webrtc::audioproc::ReverseStream(*from.reverse_stream_);
+ } else {
+ reverse_stream_ = nullptr;
+ }
+ if (from._internal_has_stream()) {
+ stream_ = new ::webrtc::audioproc::Stream(*from.stream_);
+ } else {
+ stream_ = nullptr;
+ }
+ if (from._internal_has_config()) {
+ config_ = new ::webrtc::audioproc::Config(*from.config_);
+ } else {
+ config_ = nullptr;
+ }
+ if (from._internal_has_runtime_setting()) {
+ runtime_setting_ = new ::webrtc::audioproc::RuntimeSetting(*from.runtime_setting_);
+ } else {
+ runtime_setting_ = nullptr;
+ }
+ type_ = from.type_;
+ // @@protoc_insertion_point(copy_constructor:webrtc.audioproc.Event)
+}
+
+void Event::SharedCtor() {
+ ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_Event_debug_2eproto.base);
+ ::memset(&init_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&type_) -
+ reinterpret_cast<char*>(&init_)) + sizeof(type_));
+}
+
+Event::~Event() {
+ // @@protoc_insertion_point(destructor:webrtc.audioproc.Event)
+ SharedDtor();
+}
+
+void Event::SharedDtor() {
+ if (this != internal_default_instance()) delete init_;
+ if (this != internal_default_instance()) delete reverse_stream_;
+ if (this != internal_default_instance()) delete stream_;
+ if (this != internal_default_instance()) delete config_;
+ if (this != internal_default_instance()) delete runtime_setting_;
+}
+
+void Event::SetCachedSize(int size) const {
+ _cached_size_.Set(size);
+}
+const Event& Event::default_instance() {
+ ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_Event_debug_2eproto.base);
+ return *internal_default_instance();
+}
+
+
+void Event::Clear() {
+// @@protoc_insertion_point(message_clear_start:webrtc.audioproc.Event)
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _has_bits_[0];
+ if (cached_has_bits & 0x0000001fu) {
+ if (cached_has_bits & 0x00000001u) {
+ GOOGLE_DCHECK(init_ != nullptr);
+ init_->Clear();
+ }
+ if (cached_has_bits & 0x00000002u) {
+ GOOGLE_DCHECK(reverse_stream_ != nullptr);
+ reverse_stream_->Clear();
+ }
+ if (cached_has_bits & 0x00000004u) {
+ GOOGLE_DCHECK(stream_ != nullptr);
+ stream_->Clear();
+ }
+ if (cached_has_bits & 0x00000008u) {
+ GOOGLE_DCHECK(config_ != nullptr);
+ config_->Clear();
+ }
+ if (cached_has_bits & 0x00000010u) {
+ GOOGLE_DCHECK(runtime_setting_ != nullptr);
+ runtime_setting_->Clear();
+ }
+ }
+ type_ = 0;
+ _has_bits_.Clear();
+ _internal_metadata_.Clear();
+}
+
+const char* Event::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ ::PROTOBUF_NAMESPACE_ID::uint32 tag;
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag);
+ CHK_(ptr);
+ switch (tag >> 3) {
+ // required .webrtc.audioproc.Event.Type type = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8)) {
+ ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::webrtc::audioproc::Event_Type_IsValid(val))) {
+ _internal_set_type(static_cast<::webrtc::audioproc::Event_Type>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(1, val, mutable_unknown_fields());
+ }
+ } else goto handle_unusual;
+ continue;
+ // optional .webrtc.audioproc.Init init = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) {
+ ptr = ctx->ParseMessage(_internal_mutable_init(), ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional .webrtc.audioproc.ReverseStream reverse_stream = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) {
+ ptr = ctx->ParseMessage(_internal_mutable_reverse_stream(), ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional .webrtc.audioproc.Stream stream = 4;
+ case 4:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) {
+ ptr = ctx->ParseMessage(_internal_mutable_stream(), ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional .webrtc.audioproc.Config config = 5;
+ case 5:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 42)) {
+ ptr = ctx->ParseMessage(_internal_mutable_config(), ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ // optional .webrtc.audioproc.RuntimeSetting runtime_setting = 6;
+ case 6:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 50)) {
+ ptr = ctx->ParseMessage(_internal_mutable_runtime_setting(), ptr);
+ CHK_(ptr);
+ } else goto handle_unusual;
+ continue;
+ default: {
+ handle_unusual:
+ if ((tag & 7) == 4 || tag == 0) {
+ ctx->SetLastTag(tag);
+ goto success;
+ }
+ ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx);
+ CHK_(ptr != nullptr);
+ continue;
+ }
+ } // switch
+ } // while
+success:
+ _has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto success;
+#undef CHK_
+}
+
+::PROTOBUF_NAMESPACE_ID::uint8* Event::_InternalSerialize(
+ ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:webrtc.audioproc.Event)
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _has_bits_[0];
+ // required .webrtc.audioproc.Event.Type type = 1;
+ if (cached_has_bits & 0x00000020u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray(
+ 1, this->_internal_type(), target);
+ }
+
+ // optional .webrtc.audioproc.Init init = 2;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(
+ 2, _Internal::init(this), target, stream);
+ }
+
+ // optional .webrtc.audioproc.ReverseStream reverse_stream = 3;
+ if (cached_has_bits & 0x00000002u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(
+ 3, _Internal::reverse_stream(this), target, stream);
+ }
+
+ // optional .webrtc.audioproc.Stream stream = 4;
+ if (cached_has_bits & 0x00000004u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(
+ 4, _Internal::stream(this), target, stream);
+ }
+
+ // optional .webrtc.audioproc.Config config = 5;
+ if (cached_has_bits & 0x00000008u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(
+ 5, _Internal::config(this), target, stream);
+ }
+
+ // optional .webrtc.audioproc.RuntimeSetting runtime_setting = 6;
+ if (cached_has_bits & 0x00000010u) {
+ target = stream->EnsureSpace(target);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(
+ 6, _Internal::runtime_setting(this), target, stream);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields().data(),
+ static_cast<int>(_internal_metadata_.unknown_fields().size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:webrtc.audioproc.Event)
+ return target;
+}
+
+size_t Event::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:webrtc.audioproc.Event)
+ size_t total_size = 0;
+
+ // required .webrtc.audioproc.Event.Type type = 1;
+ if (_internal_has_type()) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_type());
+ }
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _has_bits_[0];
+ if (cached_has_bits & 0x0000001fu) {
+ // optional .webrtc.audioproc.Init init = 2;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *init_);
+ }
+
+ // optional .webrtc.audioproc.ReverseStream reverse_stream = 3;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *reverse_stream_);
+ }
+
+ // optional .webrtc.audioproc.Stream stream = 4;
+ if (cached_has_bits & 0x00000004u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *stream_);
+ }
+
+ // optional .webrtc.audioproc.Config config = 5;
+ if (cached_has_bits & 0x00000008u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *config_);
+ }
+
+ // optional .webrtc.audioproc.RuntimeSetting runtime_setting = 6;
+ if (cached_has_bits & 0x00000010u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *runtime_setting_);
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields().size();
+ }
+ int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void Event::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::PROTOBUF_NAMESPACE_ID::internal::DownCast<const Event*>(
+ &from));
+}
+
+void Event::MergeFrom(const Event& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:webrtc.audioproc.Event)
+ GOOGLE_DCHECK_NE(&from, this);
+ _internal_metadata_.MergeFrom(from._internal_metadata_);
+ ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._has_bits_[0];
+ if (cached_has_bits & 0x0000003fu) {
+ if (cached_has_bits & 0x00000001u) {
+ _internal_mutable_init()->::webrtc::audioproc::Init::MergeFrom(from._internal_init());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _internal_mutable_reverse_stream()->::webrtc::audioproc::ReverseStream::MergeFrom(from._internal_reverse_stream());
+ }
+ if (cached_has_bits & 0x00000004u) {
+ _internal_mutable_stream()->::webrtc::audioproc::Stream::MergeFrom(from._internal_stream());
+ }
+ if (cached_has_bits & 0x00000008u) {
+ _internal_mutable_config()->::webrtc::audioproc::Config::MergeFrom(from._internal_config());
+ }
+ if (cached_has_bits & 0x00000010u) {
+ _internal_mutable_runtime_setting()->::webrtc::audioproc::RuntimeSetting::MergeFrom(from._internal_runtime_setting());
+ }
+ if (cached_has_bits & 0x00000020u) {
+ type_ = from.type_;
+ }
+ _has_bits_[0] |= cached_has_bits;
+ }
+}
+
+void Event::CopyFrom(const Event& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:webrtc.audioproc.Event)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool Event::IsInitialized() const {
+ if ((_has_bits_[0] & 0x00000020) != 0x00000020) return false;
+ return true;
+}
+
+void Event::InternalSwap(Event* other) {
+ using std::swap;
+ _internal_metadata_.Swap(&other->_internal_metadata_);
+ swap(_has_bits_[0], other->_has_bits_[0]);
+ swap(init_, other->init_);
+ swap(reverse_stream_, other->reverse_stream_);
+ swap(stream_, other->stream_);
+ swap(config_, other->config_);
+ swap(runtime_setting_, other->runtime_setting_);
+ swap(type_, other->type_);
+}
+
+std::string Event::GetTypeName() const {
+ return "webrtc.audioproc.Event";
+}
+
+
+// @@protoc_insertion_point(namespace_scope)
+} // namespace audioproc
+} // namespace webrtc
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_NOINLINE ::webrtc::audioproc::Init* Arena::CreateMaybeMessage< ::webrtc::audioproc::Init >(Arena* arena) {
+ return Arena::CreateInternal< ::webrtc::audioproc::Init >(arena);
+}
+template<> PROTOBUF_NOINLINE ::webrtc::audioproc::ReverseStream* Arena::CreateMaybeMessage< ::webrtc::audioproc::ReverseStream >(Arena* arena) {
+ return Arena::CreateInternal< ::webrtc::audioproc::ReverseStream >(arena);
+}
+template<> PROTOBUF_NOINLINE ::webrtc::audioproc::Stream* Arena::CreateMaybeMessage< ::webrtc::audioproc::Stream >(Arena* arena) {
+ return Arena::CreateInternal< ::webrtc::audioproc::Stream >(arena);
+}
+template<> PROTOBUF_NOINLINE ::webrtc::audioproc::Config* Arena::CreateMaybeMessage< ::webrtc::audioproc::Config >(Arena* arena) {
+ return Arena::CreateInternal< ::webrtc::audioproc::Config >(arena);
+}
+template<> PROTOBUF_NOINLINE ::webrtc::audioproc::PlayoutAudioDeviceInfo* Arena::CreateMaybeMessage< ::webrtc::audioproc::PlayoutAudioDeviceInfo >(Arena* arena) {
+ return Arena::CreateInternal< ::webrtc::audioproc::PlayoutAudioDeviceInfo >(arena);
+}
+template<> PROTOBUF_NOINLINE ::webrtc::audioproc::RuntimeSetting* Arena::CreateMaybeMessage< ::webrtc::audioproc::RuntimeSetting >(Arena* arena) {
+ return Arena::CreateInternal< ::webrtc::audioproc::RuntimeSetting >(arena);
+}
+template<> PROTOBUF_NOINLINE ::webrtc::audioproc::Event* Arena::CreateMaybeMessage< ::webrtc::audioproc::Event >(Arena* arena) {
+ return Arena::CreateInternal< ::webrtc::audioproc::Event >(arena);
+}
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+#include <google/protobuf/port_undef.inc>
diff --git a/modules/audio_processing/debug.pb.h b/modules/audio_processing/debug.pb.h
new file mode 100644
index 0000000..102eeed
--- /dev/null
+++ b/modules/audio_processing/debug.pb.h
@@ -0,0 +1,3882 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: debug.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_debug_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_debug_2eproto
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+#if PROTOBUF_VERSION < 3011000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3011004 < PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/port_undef.inc>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_table_driven.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/inlined_string_field.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/repeated_field.h> // IWYU pragma: export
+#include <google/protobuf/extension_set.h> // IWYU pragma: export
+#include <google/protobuf/generated_enum_util.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+#define PROTOBUF_INTERNAL_EXPORT_debug_2eproto
+PROTOBUF_NAMESPACE_OPEN
+namespace internal {
+class AnyMetadata;
+} // namespace internal
+PROTOBUF_NAMESPACE_CLOSE
+
+// Internal implementation detail -- do not use these members.
+struct TableStruct_debug_2eproto {
+ static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[]
+ PROTOBUF_SECTION_VARIABLE(protodesc_cold);
+ static const ::PROTOBUF_NAMESPACE_ID::internal::AuxillaryParseTableField aux[]
+ PROTOBUF_SECTION_VARIABLE(protodesc_cold);
+ static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[7]
+ PROTOBUF_SECTION_VARIABLE(protodesc_cold);
+ static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[];
+ static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[];
+ static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[];
+};
+namespace webrtc {
+namespace audioproc {
+class Config;
+class ConfigDefaultTypeInternal;
+extern ConfigDefaultTypeInternal _Config_default_instance_;
+class Event;
+class EventDefaultTypeInternal;
+extern EventDefaultTypeInternal _Event_default_instance_;
+class Init;
+class InitDefaultTypeInternal;
+extern InitDefaultTypeInternal _Init_default_instance_;
+class PlayoutAudioDeviceInfo;
+class PlayoutAudioDeviceInfoDefaultTypeInternal;
+extern PlayoutAudioDeviceInfoDefaultTypeInternal _PlayoutAudioDeviceInfo_default_instance_;
+class ReverseStream;
+class ReverseStreamDefaultTypeInternal;
+extern ReverseStreamDefaultTypeInternal _ReverseStream_default_instance_;
+class RuntimeSetting;
+class RuntimeSettingDefaultTypeInternal;
+extern RuntimeSettingDefaultTypeInternal _RuntimeSetting_default_instance_;
+class Stream;
+class StreamDefaultTypeInternal;
+extern StreamDefaultTypeInternal _Stream_default_instance_;
+} // namespace audioproc
+} // namespace webrtc
+PROTOBUF_NAMESPACE_OPEN
+template<> ::webrtc::audioproc::Config* Arena::CreateMaybeMessage<::webrtc::audioproc::Config>(Arena*);
+template<> ::webrtc::audioproc::Event* Arena::CreateMaybeMessage<::webrtc::audioproc::Event>(Arena*);
+template<> ::webrtc::audioproc::Init* Arena::CreateMaybeMessage<::webrtc::audioproc::Init>(Arena*);
+template<> ::webrtc::audioproc::PlayoutAudioDeviceInfo* Arena::CreateMaybeMessage<::webrtc::audioproc::PlayoutAudioDeviceInfo>(Arena*);
+template<> ::webrtc::audioproc::ReverseStream* Arena::CreateMaybeMessage<::webrtc::audioproc::ReverseStream>(Arena*);
+template<> ::webrtc::audioproc::RuntimeSetting* Arena::CreateMaybeMessage<::webrtc::audioproc::RuntimeSetting>(Arena*);
+template<> ::webrtc::audioproc::Stream* Arena::CreateMaybeMessage<::webrtc::audioproc::Stream>(Arena*);
+PROTOBUF_NAMESPACE_CLOSE
+namespace webrtc {
+namespace audioproc {
+
+enum Event_Type : int {
+ Event_Type_INIT = 0,
+ Event_Type_REVERSE_STREAM = 1,
+ Event_Type_STREAM = 2,
+ Event_Type_CONFIG = 3,
+ Event_Type_UNKNOWN_EVENT = 4,
+ Event_Type_RUNTIME_SETTING = 5
+};
+bool Event_Type_IsValid(int value);
+constexpr Event_Type Event_Type_Type_MIN = Event_Type_INIT;
+constexpr Event_Type Event_Type_Type_MAX = Event_Type_RUNTIME_SETTING;
+constexpr int Event_Type_Type_ARRAYSIZE = Event_Type_Type_MAX + 1;
+
+const std::string& Event_Type_Name(Event_Type value);
+template<typename T>
+inline const std::string& Event_Type_Name(T enum_t_value) {
+ static_assert(::std::is_same<T, Event_Type>::value ||
+ ::std::is_integral<T>::value,
+ "Incorrect type passed to function Event_Type_Name.");
+ return Event_Type_Name(static_cast<Event_Type>(enum_t_value));
+}
+bool Event_Type_Parse(
+ const std::string& name, Event_Type* value);
+// ===================================================================
+
+class Init :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:webrtc.audioproc.Init) */ {
+ public:
+ Init();
+ virtual ~Init();
+
+ Init(const Init& from);
+ Init(Init&& from) noexcept
+ : Init() {
+ *this = ::std::move(from);
+ }
+
+ inline Init& operator=(const Init& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline Init& operator=(Init&& from) noexcept {
+ if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
+ if (this != &from) InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields();
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields();
+ }
+
+ static const Init& default_instance();
+
+ static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY
+ static inline const Init* internal_default_instance() {
+ return reinterpret_cast<const Init*>(
+ &_Init_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 0;
+
+ friend void swap(Init& a, Init& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(Init* other) {
+ if (other == this) return;
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ inline Init* New() const final {
+ return CreateMaybeMessage<Init>(nullptr);
+ }
+
+ Init* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
+ return CreateMaybeMessage<Init>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from)
+ final;
+ void CopyFrom(const Init& from);
+ void MergeFrom(const Init& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(
+ ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ void DiscardUnknownFields();
+ int GetCachedSize() const final { return _cached_size_.Get(); }
+
+ private:
+ inline void SharedCtor();
+ inline void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(Init* other);
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "webrtc.audioproc.Init";
+ }
+ private:
+ inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const {
+ return nullptr;
+ }
+ inline void* MaybeArenaPtr() const {
+ return nullptr;
+ }
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kSampleRateFieldNumber = 1,
+ kDeviceSampleRateFieldNumber = 2,
+ kNumInputChannelsFieldNumber = 3,
+ kNumOutputChannelsFieldNumber = 4,
+ kNumReverseChannelsFieldNumber = 5,
+ kReverseSampleRateFieldNumber = 6,
+ kOutputSampleRateFieldNumber = 7,
+ kReverseOutputSampleRateFieldNumber = 8,
+ kTimestampMsFieldNumber = 10,
+ kNumReverseOutputChannelsFieldNumber = 9,
+ };
+ // optional int32 sample_rate = 1;
+ bool has_sample_rate() const;
+ private:
+ bool _internal_has_sample_rate() const;
+ public:
+ void clear_sample_rate();
+ ::PROTOBUF_NAMESPACE_ID::int32 sample_rate() const;
+ void set_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_sample_rate() const;
+ void _internal_set_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // optional int32 device_sample_rate = 2 [deprecated = true];
+ PROTOBUF_DEPRECATED bool has_device_sample_rate() const;
+ private:
+ bool _internal_has_device_sample_rate() const;
+ public:
+ PROTOBUF_DEPRECATED void clear_device_sample_rate();
+ PROTOBUF_DEPRECATED ::PROTOBUF_NAMESPACE_ID::int32 device_sample_rate() const;
+ PROTOBUF_DEPRECATED void set_device_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_device_sample_rate() const;
+ void _internal_set_device_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // optional int32 num_input_channels = 3;
+ bool has_num_input_channels() const;
+ private:
+ bool _internal_has_num_input_channels() const;
+ public:
+ void clear_num_input_channels();
+ ::PROTOBUF_NAMESPACE_ID::int32 num_input_channels() const;
+ void set_num_input_channels(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_num_input_channels() const;
+ void _internal_set_num_input_channels(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // optional int32 num_output_channels = 4;
+ bool has_num_output_channels() const;
+ private:
+ bool _internal_has_num_output_channels() const;
+ public:
+ void clear_num_output_channels();
+ ::PROTOBUF_NAMESPACE_ID::int32 num_output_channels() const;
+ void set_num_output_channels(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_num_output_channels() const;
+ void _internal_set_num_output_channels(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // optional int32 num_reverse_channels = 5;
+ bool has_num_reverse_channels() const;
+ private:
+ bool _internal_has_num_reverse_channels() const;
+ public:
+ void clear_num_reverse_channels();
+ ::PROTOBUF_NAMESPACE_ID::int32 num_reverse_channels() const;
+ void set_num_reverse_channels(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_num_reverse_channels() const;
+ void _internal_set_num_reverse_channels(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // optional int32 reverse_sample_rate = 6;
+ bool has_reverse_sample_rate() const;
+ private:
+ bool _internal_has_reverse_sample_rate() const;
+ public:
+ void clear_reverse_sample_rate();
+ ::PROTOBUF_NAMESPACE_ID::int32 reverse_sample_rate() const;
+ void set_reverse_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_reverse_sample_rate() const;
+ void _internal_set_reverse_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // optional int32 output_sample_rate = 7;
+ bool has_output_sample_rate() const;
+ private:
+ bool _internal_has_output_sample_rate() const;
+ public:
+ void clear_output_sample_rate();
+ ::PROTOBUF_NAMESPACE_ID::int32 output_sample_rate() const;
+ void set_output_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_output_sample_rate() const;
+ void _internal_set_output_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // optional int32 reverse_output_sample_rate = 8;
+ bool has_reverse_output_sample_rate() const;
+ private:
+ bool _internal_has_reverse_output_sample_rate() const;
+ public:
+ void clear_reverse_output_sample_rate();
+ ::PROTOBUF_NAMESPACE_ID::int32 reverse_output_sample_rate() const;
+ void set_reverse_output_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_reverse_output_sample_rate() const;
+ void _internal_set_reverse_output_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // optional int64 timestamp_ms = 10;
+ bool has_timestamp_ms() const;
+ private:
+ bool _internal_has_timestamp_ms() const;
+ public:
+ void clear_timestamp_ms();
+ ::PROTOBUF_NAMESPACE_ID::int64 timestamp_ms() const;
+ void set_timestamp_ms(::PROTOBUF_NAMESPACE_ID::int64 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int64 _internal_timestamp_ms() const;
+ void _internal_set_timestamp_ms(::PROTOBUF_NAMESPACE_ID::int64 value);
+ public:
+
+ // optional int32 num_reverse_output_channels = 9;
+ bool has_num_reverse_output_channels() const;
+ private:
+ bool _internal_has_num_reverse_output_channels() const;
+ public:
+ void clear_num_reverse_output_channels();
+ ::PROTOBUF_NAMESPACE_ID::int32 num_reverse_output_channels() const;
+ void set_num_reverse_output_channels(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_num_reverse_output_channels() const;
+ void _internal_set_num_reverse_output_channels(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:webrtc.audioproc.Init)
+ private:
+ class _Internal;
+
+ ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArenaLite _internal_metadata_;
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::int32 sample_rate_;
+ ::PROTOBUF_NAMESPACE_ID::int32 device_sample_rate_;
+ ::PROTOBUF_NAMESPACE_ID::int32 num_input_channels_;
+ ::PROTOBUF_NAMESPACE_ID::int32 num_output_channels_;
+ ::PROTOBUF_NAMESPACE_ID::int32 num_reverse_channels_;
+ ::PROTOBUF_NAMESPACE_ID::int32 reverse_sample_rate_;
+ ::PROTOBUF_NAMESPACE_ID::int32 output_sample_rate_;
+ ::PROTOBUF_NAMESPACE_ID::int32 reverse_output_sample_rate_;
+ ::PROTOBUF_NAMESPACE_ID::int64 timestamp_ms_;
+ ::PROTOBUF_NAMESPACE_ID::int32 num_reverse_output_channels_;
+ friend struct ::TableStruct_debug_2eproto;
+};
+// -------------------------------------------------------------------
+
+class ReverseStream :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:webrtc.audioproc.ReverseStream) */ {
+ public:
+ ReverseStream();
+ virtual ~ReverseStream();
+
+ ReverseStream(const ReverseStream& from);
+ ReverseStream(ReverseStream&& from) noexcept
+ : ReverseStream() {
+ *this = ::std::move(from);
+ }
+
+ inline ReverseStream& operator=(const ReverseStream& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline ReverseStream& operator=(ReverseStream&& from) noexcept {
+ if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
+ if (this != &from) InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields();
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields();
+ }
+
+ static const ReverseStream& default_instance();
+
+ static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY
+ static inline const ReverseStream* internal_default_instance() {
+ return reinterpret_cast<const ReverseStream*>(
+ &_ReverseStream_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 1;
+
+ friend void swap(ReverseStream& a, ReverseStream& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(ReverseStream* other) {
+ if (other == this) return;
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ inline ReverseStream* New() const final {
+ return CreateMaybeMessage<ReverseStream>(nullptr);
+ }
+
+ ReverseStream* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
+ return CreateMaybeMessage<ReverseStream>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from)
+ final;
+ void CopyFrom(const ReverseStream& from);
+ void MergeFrom(const ReverseStream& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(
+ ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ void DiscardUnknownFields();
+ int GetCachedSize() const final { return _cached_size_.Get(); }
+
+ private:
+ inline void SharedCtor();
+ inline void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(ReverseStream* other);
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "webrtc.audioproc.ReverseStream";
+ }
+ private:
+ inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const {
+ return nullptr;
+ }
+ inline void* MaybeArenaPtr() const {
+ return nullptr;
+ }
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kChannelFieldNumber = 2,
+ kDataFieldNumber = 1,
+ };
+ // repeated bytes channel = 2;
+ int channel_size() const;
+ private:
+ int _internal_channel_size() const;
+ public:
+ void clear_channel();
+ const std::string& channel(int index) const;
+ std::string* mutable_channel(int index);
+ void set_channel(int index, const std::string& value);
+ void set_channel(int index, std::string&& value);
+ void set_channel(int index, const char* value);
+ void set_channel(int index, const void* value, size_t size);
+ std::string* add_channel();
+ void add_channel(const std::string& value);
+ void add_channel(std::string&& value);
+ void add_channel(const char* value);
+ void add_channel(const void* value, size_t size);
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>& channel() const;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>* mutable_channel();
+ private:
+ const std::string& _internal_channel(int index) const;
+ std::string* _internal_add_channel();
+ public:
+
+ // optional bytes data = 1;
+ bool has_data() const;
+ private:
+ bool _internal_has_data() const;
+ public:
+ void clear_data();
+ const std::string& data() const;
+ void set_data(const std::string& value);
+ void set_data(std::string&& value);
+ void set_data(const char* value);
+ void set_data(const void* value, size_t size);
+ std::string* mutable_data();
+ std::string* release_data();
+ void set_allocated_data(std::string* data);
+ private:
+ const std::string& _internal_data() const;
+ void _internal_set_data(const std::string& value);
+ std::string* _internal_mutable_data();
+ public:
+
+ // @@protoc_insertion_point(class_scope:webrtc.audioproc.ReverseStream)
+ private:
+ class _Internal;
+
+ ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArenaLite _internal_metadata_;
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string> channel_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr data_;
+ friend struct ::TableStruct_debug_2eproto;
+};
+// -------------------------------------------------------------------
+
+class Stream :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:webrtc.audioproc.Stream) */ {
+ public:
+ Stream();
+ virtual ~Stream();
+
+ Stream(const Stream& from);
+ Stream(Stream&& from) noexcept
+ : Stream() {
+ *this = ::std::move(from);
+ }
+
+ inline Stream& operator=(const Stream& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline Stream& operator=(Stream&& from) noexcept {
+ if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
+ if (this != &from) InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields();
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields();
+ }
+
+ static const Stream& default_instance();
+
+ static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY
+ static inline const Stream* internal_default_instance() {
+ return reinterpret_cast<const Stream*>(
+ &_Stream_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 2;
+
+ friend void swap(Stream& a, Stream& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(Stream* other) {
+ if (other == this) return;
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ inline Stream* New() const final {
+ return CreateMaybeMessage<Stream>(nullptr);
+ }
+
+ Stream* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
+ return CreateMaybeMessage<Stream>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from)
+ final;
+ void CopyFrom(const Stream& from);
+ void MergeFrom(const Stream& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(
+ ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ void DiscardUnknownFields();
+ int GetCachedSize() const final { return _cached_size_.Get(); }
+
+ private:
+ inline void SharedCtor();
+ inline void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(Stream* other);
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "webrtc.audioproc.Stream";
+ }
+ private:
+ inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const {
+ return nullptr;
+ }
+ inline void* MaybeArenaPtr() const {
+ return nullptr;
+ }
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kInputChannelFieldNumber = 7,
+ kOutputChannelFieldNumber = 8,
+ kInputDataFieldNumber = 1,
+ kOutputDataFieldNumber = 2,
+ kDelayFieldNumber = 3,
+ kDriftFieldNumber = 4,
+ kLevelFieldNumber = 5,
+ kKeypressFieldNumber = 6,
+ };
+ // repeated bytes input_channel = 7;
+ int input_channel_size() const;
+ private:
+ int _internal_input_channel_size() const;
+ public:
+ void clear_input_channel();
+ const std::string& input_channel(int index) const;
+ std::string* mutable_input_channel(int index);
+ void set_input_channel(int index, const std::string& value);
+ void set_input_channel(int index, std::string&& value);
+ void set_input_channel(int index, const char* value);
+ void set_input_channel(int index, const void* value, size_t size);
+ std::string* add_input_channel();
+ void add_input_channel(const std::string& value);
+ void add_input_channel(std::string&& value);
+ void add_input_channel(const char* value);
+ void add_input_channel(const void* value, size_t size);
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>& input_channel() const;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>* mutable_input_channel();
+ private:
+ const std::string& _internal_input_channel(int index) const;
+ std::string* _internal_add_input_channel();
+ public:
+
+ // repeated bytes output_channel = 8;
+ int output_channel_size() const;
+ private:
+ int _internal_output_channel_size() const;
+ public:
+ void clear_output_channel();
+ const std::string& output_channel(int index) const;
+ std::string* mutable_output_channel(int index);
+ void set_output_channel(int index, const std::string& value);
+ void set_output_channel(int index, std::string&& value);
+ void set_output_channel(int index, const char* value);
+ void set_output_channel(int index, const void* value, size_t size);
+ std::string* add_output_channel();
+ void add_output_channel(const std::string& value);
+ void add_output_channel(std::string&& value);
+ void add_output_channel(const char* value);
+ void add_output_channel(const void* value, size_t size);
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>& output_channel() const;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>* mutable_output_channel();
+ private:
+ const std::string& _internal_output_channel(int index) const;
+ std::string* _internal_add_output_channel();
+ public:
+
+ // optional bytes input_data = 1;
+ bool has_input_data() const;
+ private:
+ bool _internal_has_input_data() const;
+ public:
+ void clear_input_data();
+ const std::string& input_data() const;
+ void set_input_data(const std::string& value);
+ void set_input_data(std::string&& value);
+ void set_input_data(const char* value);
+ void set_input_data(const void* value, size_t size);
+ std::string* mutable_input_data();
+ std::string* release_input_data();
+ void set_allocated_input_data(std::string* input_data);
+ private:
+ const std::string& _internal_input_data() const;
+ void _internal_set_input_data(const std::string& value);
+ std::string* _internal_mutable_input_data();
+ public:
+
+ // optional bytes output_data = 2;
+ bool has_output_data() const;
+ private:
+ bool _internal_has_output_data() const;
+ public:
+ void clear_output_data();
+ const std::string& output_data() const;
+ void set_output_data(const std::string& value);
+ void set_output_data(std::string&& value);
+ void set_output_data(const char* value);
+ void set_output_data(const void* value, size_t size);
+ std::string* mutable_output_data();
+ std::string* release_output_data();
+ void set_allocated_output_data(std::string* output_data);
+ private:
+ const std::string& _internal_output_data() const;
+ void _internal_set_output_data(const std::string& value);
+ std::string* _internal_mutable_output_data();
+ public:
+
+ // optional int32 delay = 3;
+ bool has_delay() const;
+ private:
+ bool _internal_has_delay() const;
+ public:
+ void clear_delay();
+ ::PROTOBUF_NAMESPACE_ID::int32 delay() const;
+ void set_delay(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_delay() const;
+ void _internal_set_delay(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // optional sint32 drift = 4;
+ bool has_drift() const;
+ private:
+ bool _internal_has_drift() const;
+ public:
+ void clear_drift();
+ ::PROTOBUF_NAMESPACE_ID::int32 drift() const;
+ void set_drift(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_drift() const;
+ void _internal_set_drift(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // optional int32 level = 5;
+ bool has_level() const;
+ private:
+ bool _internal_has_level() const;
+ public:
+ void clear_level();
+ ::PROTOBUF_NAMESPACE_ID::int32 level() const;
+ void set_level(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_level() const;
+ void _internal_set_level(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // optional bool keypress = 6;
+ bool has_keypress() const;
+ private:
+ bool _internal_has_keypress() const;
+ public:
+ void clear_keypress();
+ bool keypress() const;
+ void set_keypress(bool value);
+ private:
+ bool _internal_keypress() const;
+ void _internal_set_keypress(bool value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:webrtc.audioproc.Stream)
+ private:
+ class _Internal;
+
+ ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArenaLite _internal_metadata_;
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string> input_channel_;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string> output_channel_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr input_data_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr output_data_;
+ ::PROTOBUF_NAMESPACE_ID::int32 delay_;
+ ::PROTOBUF_NAMESPACE_ID::int32 drift_;
+ ::PROTOBUF_NAMESPACE_ID::int32 level_;
+ bool keypress_;
+ friend struct ::TableStruct_debug_2eproto;
+};
+// -------------------------------------------------------------------
+
+class Config :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:webrtc.audioproc.Config) */ {
+ public:
+ Config();
+ virtual ~Config();
+
+ Config(const Config& from);
+ Config(Config&& from) noexcept
+ : Config() {
+ *this = ::std::move(from);
+ }
+
+ inline Config& operator=(const Config& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline Config& operator=(Config&& from) noexcept {
+ if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
+ if (this != &from) InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields();
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields();
+ }
+
+ static const Config& default_instance();
+
+ static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY
+ static inline const Config* internal_default_instance() {
+ return reinterpret_cast<const Config*>(
+ &_Config_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 3;
+
+ friend void swap(Config& a, Config& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(Config* other) {
+ if (other == this) return;
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ inline Config* New() const final {
+ return CreateMaybeMessage<Config>(nullptr);
+ }
+
+ Config* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
+ return CreateMaybeMessage<Config>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from)
+ final;
+ void CopyFrom(const Config& from);
+ void MergeFrom(const Config& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(
+ ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ void DiscardUnknownFields();
+ int GetCachedSize() const final { return _cached_size_.Get(); }
+
+ private:
+ inline void SharedCtor();
+ inline void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(Config* other);
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "webrtc.audioproc.Config";
+ }
+ private:
+ inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const {
+ return nullptr;
+ }
+ inline void* MaybeArenaPtr() const {
+ return nullptr;
+ }
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kExperimentsDescriptionFieldNumber = 17,
+ kAecEnabledFieldNumber = 1,
+ kAecDelayAgnosticEnabledFieldNumber = 2,
+ kAecDriftCompensationEnabledFieldNumber = 3,
+ kAecExtendedFilterEnabledFieldNumber = 4,
+ kAecSuppressionLevelFieldNumber = 5,
+ kAecmRoutingModeFieldNumber = 8,
+ kAecmEnabledFieldNumber = 6,
+ kAecmComfortNoiseEnabledFieldNumber = 7,
+ kAgcEnabledFieldNumber = 9,
+ kAgcLimiterEnabledFieldNumber = 11,
+ kAgcModeFieldNumber = 10,
+ kNoiseRobustAgcEnabledFieldNumber = 12,
+ kHpfEnabledFieldNumber = 13,
+ kNsEnabledFieldNumber = 14,
+ kTransientSuppressionEnabledFieldNumber = 16,
+ kNsLevelFieldNumber = 15,
+ kPreAmplifierEnabledFieldNumber = 19,
+ kPreAmplifierFixedGainFactorFieldNumber = 20,
+ };
+ // optional string experiments_description = 17;
+ bool has_experiments_description() const;
+ private:
+ bool _internal_has_experiments_description() const;
+ public:
+ void clear_experiments_description();
+ const std::string& experiments_description() const;
+ void set_experiments_description(const std::string& value);
+ void set_experiments_description(std::string&& value);
+ void set_experiments_description(const char* value);
+ void set_experiments_description(const char* value, size_t size);
+ std::string* mutable_experiments_description();
+ std::string* release_experiments_description();
+ void set_allocated_experiments_description(std::string* experiments_description);
+ private:
+ const std::string& _internal_experiments_description() const;
+ void _internal_set_experiments_description(const std::string& value);
+ std::string* _internal_mutable_experiments_description();
+ public:
+
+ // optional bool aec_enabled = 1;
+ bool has_aec_enabled() const;
+ private:
+ bool _internal_has_aec_enabled() const;
+ public:
+ void clear_aec_enabled();
+ bool aec_enabled() const;
+ void set_aec_enabled(bool value);
+ private:
+ bool _internal_aec_enabled() const;
+ void _internal_set_aec_enabled(bool value);
+ public:
+
+ // optional bool aec_delay_agnostic_enabled = 2;
+ bool has_aec_delay_agnostic_enabled() const;
+ private:
+ bool _internal_has_aec_delay_agnostic_enabled() const;
+ public:
+ void clear_aec_delay_agnostic_enabled();
+ bool aec_delay_agnostic_enabled() const;
+ void set_aec_delay_agnostic_enabled(bool value);
+ private:
+ bool _internal_aec_delay_agnostic_enabled() const;
+ void _internal_set_aec_delay_agnostic_enabled(bool value);
+ public:
+
+ // optional bool aec_drift_compensation_enabled = 3;
+ bool has_aec_drift_compensation_enabled() const;
+ private:
+ bool _internal_has_aec_drift_compensation_enabled() const;
+ public:
+ void clear_aec_drift_compensation_enabled();
+ bool aec_drift_compensation_enabled() const;
+ void set_aec_drift_compensation_enabled(bool value);
+ private:
+ bool _internal_aec_drift_compensation_enabled() const;
+ void _internal_set_aec_drift_compensation_enabled(bool value);
+ public:
+
+ // optional bool aec_extended_filter_enabled = 4;
+ bool has_aec_extended_filter_enabled() const;
+ private:
+ bool _internal_has_aec_extended_filter_enabled() const;
+ public:
+ void clear_aec_extended_filter_enabled();
+ bool aec_extended_filter_enabled() const;
+ void set_aec_extended_filter_enabled(bool value);
+ private:
+ bool _internal_aec_extended_filter_enabled() const;
+ void _internal_set_aec_extended_filter_enabled(bool value);
+ public:
+
+ // optional int32 aec_suppression_level = 5;
+ bool has_aec_suppression_level() const;
+ private:
+ bool _internal_has_aec_suppression_level() const;
+ public:
+ void clear_aec_suppression_level();
+ ::PROTOBUF_NAMESPACE_ID::int32 aec_suppression_level() const;
+ void set_aec_suppression_level(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_aec_suppression_level() const;
+ void _internal_set_aec_suppression_level(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // optional int32 aecm_routing_mode = 8 [deprecated = true];
+ PROTOBUF_DEPRECATED bool has_aecm_routing_mode() const;
+ private:
+ bool _internal_has_aecm_routing_mode() const;
+ public:
+ PROTOBUF_DEPRECATED void clear_aecm_routing_mode();
+ PROTOBUF_DEPRECATED ::PROTOBUF_NAMESPACE_ID::int32 aecm_routing_mode() const;
+ PROTOBUF_DEPRECATED void set_aecm_routing_mode(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_aecm_routing_mode() const;
+ void _internal_set_aecm_routing_mode(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // optional bool aecm_enabled = 6;
+ bool has_aecm_enabled() const;
+ private:
+ bool _internal_has_aecm_enabled() const;
+ public:
+ void clear_aecm_enabled();
+ bool aecm_enabled() const;
+ void set_aecm_enabled(bool value);
+ private:
+ bool _internal_aecm_enabled() const;
+ void _internal_set_aecm_enabled(bool value);
+ public:
+
+ // optional bool aecm_comfort_noise_enabled = 7 [deprecated = true];
+ PROTOBUF_DEPRECATED bool has_aecm_comfort_noise_enabled() const;
+ private:
+ bool _internal_has_aecm_comfort_noise_enabled() const;
+ public:
+ PROTOBUF_DEPRECATED void clear_aecm_comfort_noise_enabled();
+ PROTOBUF_DEPRECATED bool aecm_comfort_noise_enabled() const;
+ PROTOBUF_DEPRECATED void set_aecm_comfort_noise_enabled(bool value);
+ private:
+ bool _internal_aecm_comfort_noise_enabled() const;
+ void _internal_set_aecm_comfort_noise_enabled(bool value);
+ public:
+
+ // optional bool agc_enabled = 9;
+ bool has_agc_enabled() const;
+ private:
+ bool _internal_has_agc_enabled() const;
+ public:
+ void clear_agc_enabled();
+ bool agc_enabled() const;
+ void set_agc_enabled(bool value);
+ private:
+ bool _internal_agc_enabled() const;
+ void _internal_set_agc_enabled(bool value);
+ public:
+
+ // optional bool agc_limiter_enabled = 11;
+ bool has_agc_limiter_enabled() const;
+ private:
+ bool _internal_has_agc_limiter_enabled() const;
+ public:
+ void clear_agc_limiter_enabled();
+ bool agc_limiter_enabled() const;
+ void set_agc_limiter_enabled(bool value);
+ private:
+ bool _internal_agc_limiter_enabled() const;
+ void _internal_set_agc_limiter_enabled(bool value);
+ public:
+
+ // optional int32 agc_mode = 10;
+ bool has_agc_mode() const;
+ private:
+ bool _internal_has_agc_mode() const;
+ public:
+ void clear_agc_mode();
+ ::PROTOBUF_NAMESPACE_ID::int32 agc_mode() const;
+ void set_agc_mode(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_agc_mode() const;
+ void _internal_set_agc_mode(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // optional bool noise_robust_agc_enabled = 12;
+ bool has_noise_robust_agc_enabled() const;
+ private:
+ bool _internal_has_noise_robust_agc_enabled() const;
+ public:
+ void clear_noise_robust_agc_enabled();
+ bool noise_robust_agc_enabled() const;
+ void set_noise_robust_agc_enabled(bool value);
+ private:
+ bool _internal_noise_robust_agc_enabled() const;
+ void _internal_set_noise_robust_agc_enabled(bool value);
+ public:
+
+ // optional bool hpf_enabled = 13;
+ bool has_hpf_enabled() const;
+ private:
+ bool _internal_has_hpf_enabled() const;
+ public:
+ void clear_hpf_enabled();
+ bool hpf_enabled() const;
+ void set_hpf_enabled(bool value);
+ private:
+ bool _internal_hpf_enabled() const;
+ void _internal_set_hpf_enabled(bool value);
+ public:
+
+ // optional bool ns_enabled = 14;
+ bool has_ns_enabled() const;
+ private:
+ bool _internal_has_ns_enabled() const;
+ public:
+ void clear_ns_enabled();
+ bool ns_enabled() const;
+ void set_ns_enabled(bool value);
+ private:
+ bool _internal_ns_enabled() const;
+ void _internal_set_ns_enabled(bool value);
+ public:
+
+ // optional bool transient_suppression_enabled = 16;
+ bool has_transient_suppression_enabled() const;
+ private:
+ bool _internal_has_transient_suppression_enabled() const;
+ public:
+ void clear_transient_suppression_enabled();
+ bool transient_suppression_enabled() const;
+ void set_transient_suppression_enabled(bool value);
+ private:
+ bool _internal_transient_suppression_enabled() const;
+ void _internal_set_transient_suppression_enabled(bool value);
+ public:
+
+ // optional int32 ns_level = 15;
+ bool has_ns_level() const;
+ private:
+ bool _internal_has_ns_level() const;
+ public:
+ void clear_ns_level();
+ ::PROTOBUF_NAMESPACE_ID::int32 ns_level() const;
+ void set_ns_level(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_ns_level() const;
+ void _internal_set_ns_level(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // optional bool pre_amplifier_enabled = 19;
+ bool has_pre_amplifier_enabled() const;
+ private:
+ bool _internal_has_pre_amplifier_enabled() const;
+ public:
+ void clear_pre_amplifier_enabled();
+ bool pre_amplifier_enabled() const;
+ void set_pre_amplifier_enabled(bool value);
+ private:
+ bool _internal_pre_amplifier_enabled() const;
+ void _internal_set_pre_amplifier_enabled(bool value);
+ public:
+
+ // optional float pre_amplifier_fixed_gain_factor = 20;
+ bool has_pre_amplifier_fixed_gain_factor() const;
+ private:
+ bool _internal_has_pre_amplifier_fixed_gain_factor() const;
+ public:
+ void clear_pre_amplifier_fixed_gain_factor();
+ float pre_amplifier_fixed_gain_factor() const;
+ void set_pre_amplifier_fixed_gain_factor(float value);
+ private:
+ float _internal_pre_amplifier_fixed_gain_factor() const;
+ void _internal_set_pre_amplifier_fixed_gain_factor(float value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:webrtc.audioproc.Config)
+ private:
+ class _Internal;
+
+ ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArenaLite _internal_metadata_;
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr experiments_description_;
+ bool aec_enabled_;
+ bool aec_delay_agnostic_enabled_;
+ bool aec_drift_compensation_enabled_;
+ bool aec_extended_filter_enabled_;
+ ::PROTOBUF_NAMESPACE_ID::int32 aec_suppression_level_;
+ ::PROTOBUF_NAMESPACE_ID::int32 aecm_routing_mode_;
+ bool aecm_enabled_;
+ bool aecm_comfort_noise_enabled_;
+ bool agc_enabled_;
+ bool agc_limiter_enabled_;
+ ::PROTOBUF_NAMESPACE_ID::int32 agc_mode_;
+ bool noise_robust_agc_enabled_;
+ bool hpf_enabled_;
+ bool ns_enabled_;
+ bool transient_suppression_enabled_;
+ ::PROTOBUF_NAMESPACE_ID::int32 ns_level_;
+ bool pre_amplifier_enabled_;
+ float pre_amplifier_fixed_gain_factor_;
+ friend struct ::TableStruct_debug_2eproto;
+};
+// -------------------------------------------------------------------
+
+class PlayoutAudioDeviceInfo :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:webrtc.audioproc.PlayoutAudioDeviceInfo) */ {
+ public:
+ PlayoutAudioDeviceInfo();
+ virtual ~PlayoutAudioDeviceInfo();
+
+ PlayoutAudioDeviceInfo(const PlayoutAudioDeviceInfo& from);
+ PlayoutAudioDeviceInfo(PlayoutAudioDeviceInfo&& from) noexcept
+ : PlayoutAudioDeviceInfo() {
+ *this = ::std::move(from);
+ }
+
+ inline PlayoutAudioDeviceInfo& operator=(const PlayoutAudioDeviceInfo& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline PlayoutAudioDeviceInfo& operator=(PlayoutAudioDeviceInfo&& from) noexcept {
+ if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
+ if (this != &from) InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields();
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields();
+ }
+
+ static const PlayoutAudioDeviceInfo& default_instance();
+
+ static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY
+ static inline const PlayoutAudioDeviceInfo* internal_default_instance() {
+ return reinterpret_cast<const PlayoutAudioDeviceInfo*>(
+ &_PlayoutAudioDeviceInfo_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 4;
+
+ friend void swap(PlayoutAudioDeviceInfo& a, PlayoutAudioDeviceInfo& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(PlayoutAudioDeviceInfo* other) {
+ if (other == this) return;
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ inline PlayoutAudioDeviceInfo* New() const final {
+ return CreateMaybeMessage<PlayoutAudioDeviceInfo>(nullptr);
+ }
+
+ PlayoutAudioDeviceInfo* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
+ return CreateMaybeMessage<PlayoutAudioDeviceInfo>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from)
+ final;
+ void CopyFrom(const PlayoutAudioDeviceInfo& from);
+ void MergeFrom(const PlayoutAudioDeviceInfo& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(
+ ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ void DiscardUnknownFields();
+ int GetCachedSize() const final { return _cached_size_.Get(); }
+
+ private:
+ inline void SharedCtor();
+ inline void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(PlayoutAudioDeviceInfo* other);
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "webrtc.audioproc.PlayoutAudioDeviceInfo";
+ }
+ private:
+ inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const {
+ return nullptr;
+ }
+ inline void* MaybeArenaPtr() const {
+ return nullptr;
+ }
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kIdFieldNumber = 1,
+ kMaxVolumeFieldNumber = 2,
+ };
+ // optional int32 id = 1;
+ bool has_id() const;
+ private:
+ bool _internal_has_id() const;
+ public:
+ void clear_id();
+ ::PROTOBUF_NAMESPACE_ID::int32 id() const;
+ void set_id(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_id() const;
+ void _internal_set_id(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // optional int32 max_volume = 2;
+ bool has_max_volume() const;
+ private:
+ bool _internal_has_max_volume() const;
+ public:
+ void clear_max_volume();
+ ::PROTOBUF_NAMESPACE_ID::int32 max_volume() const;
+ void set_max_volume(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_max_volume() const;
+ void _internal_set_max_volume(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:webrtc.audioproc.PlayoutAudioDeviceInfo)
+ private:
+ class _Internal;
+
+ ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArenaLite _internal_metadata_;
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::int32 id_;
+ ::PROTOBUF_NAMESPACE_ID::int32 max_volume_;
+ friend struct ::TableStruct_debug_2eproto;
+};
+// -------------------------------------------------------------------
+
+class RuntimeSetting :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:webrtc.audioproc.RuntimeSetting) */ {
+ public:
+ RuntimeSetting();
+ virtual ~RuntimeSetting();
+
+ RuntimeSetting(const RuntimeSetting& from);
+ RuntimeSetting(RuntimeSetting&& from) noexcept
+ : RuntimeSetting() {
+ *this = ::std::move(from);
+ }
+
+ inline RuntimeSetting& operator=(const RuntimeSetting& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline RuntimeSetting& operator=(RuntimeSetting&& from) noexcept {
+ if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
+ if (this != &from) InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields();
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields();
+ }
+
+ static const RuntimeSetting& default_instance();
+
+ static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY
+ static inline const RuntimeSetting* internal_default_instance() {
+ return reinterpret_cast<const RuntimeSetting*>(
+ &_RuntimeSetting_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 5;
+
+ friend void swap(RuntimeSetting& a, RuntimeSetting& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(RuntimeSetting* other) {
+ if (other == this) return;
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ inline RuntimeSetting* New() const final {
+ return CreateMaybeMessage<RuntimeSetting>(nullptr);
+ }
+
+ RuntimeSetting* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
+ return CreateMaybeMessage<RuntimeSetting>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from)
+ final;
+ void CopyFrom(const RuntimeSetting& from);
+ void MergeFrom(const RuntimeSetting& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(
+ ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ void DiscardUnknownFields();
+ int GetCachedSize() const final { return _cached_size_.Get(); }
+
+ private:
+ inline void SharedCtor();
+ inline void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(RuntimeSetting* other);
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "webrtc.audioproc.RuntimeSetting";
+ }
+ private:
+ inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const {
+ return nullptr;
+ }
+ inline void* MaybeArenaPtr() const {
+ return nullptr;
+ }
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kPlayoutAudioDeviceChangeFieldNumber = 5,
+ kCapturePreGainFieldNumber = 1,
+ kCustomRenderProcessingSettingFieldNumber = 2,
+ kCaptureFixedPostGainFieldNumber = 3,
+ kPlayoutVolumeChangeFieldNumber = 4,
+ kCaptureOutputUsedFieldNumber = 6,
+ kCapturePostGainFieldNumber = 7,
+ };
+ // optional .webrtc.audioproc.PlayoutAudioDeviceInfo playout_audio_device_change = 5;
+ bool has_playout_audio_device_change() const;
+ private:
+ bool _internal_has_playout_audio_device_change() const;
+ public:
+ void clear_playout_audio_device_change();
+ const ::webrtc::audioproc::PlayoutAudioDeviceInfo& playout_audio_device_change() const;
+ ::webrtc::audioproc::PlayoutAudioDeviceInfo* release_playout_audio_device_change();
+ ::webrtc::audioproc::PlayoutAudioDeviceInfo* mutable_playout_audio_device_change();
+ void set_allocated_playout_audio_device_change(::webrtc::audioproc::PlayoutAudioDeviceInfo* playout_audio_device_change);
+ private:
+ const ::webrtc::audioproc::PlayoutAudioDeviceInfo& _internal_playout_audio_device_change() const;
+ ::webrtc::audioproc::PlayoutAudioDeviceInfo* _internal_mutable_playout_audio_device_change();
+ public:
+
+ // optional float capture_pre_gain = 1;
+ bool has_capture_pre_gain() const;
+ private:
+ bool _internal_has_capture_pre_gain() const;
+ public:
+ void clear_capture_pre_gain();
+ float capture_pre_gain() const;
+ void set_capture_pre_gain(float value);
+ private:
+ float _internal_capture_pre_gain() const;
+ void _internal_set_capture_pre_gain(float value);
+ public:
+
+ // optional float custom_render_processing_setting = 2;
+ bool has_custom_render_processing_setting() const;
+ private:
+ bool _internal_has_custom_render_processing_setting() const;
+ public:
+ void clear_custom_render_processing_setting();
+ float custom_render_processing_setting() const;
+ void set_custom_render_processing_setting(float value);
+ private:
+ float _internal_custom_render_processing_setting() const;
+ void _internal_set_custom_render_processing_setting(float value);
+ public:
+
+ // optional float capture_fixed_post_gain = 3;
+ bool has_capture_fixed_post_gain() const;
+ private:
+ bool _internal_has_capture_fixed_post_gain() const;
+ public:
+ void clear_capture_fixed_post_gain();
+ float capture_fixed_post_gain() const;
+ void set_capture_fixed_post_gain(float value);
+ private:
+ float _internal_capture_fixed_post_gain() const;
+ void _internal_set_capture_fixed_post_gain(float value);
+ public:
+
+ // optional int32 playout_volume_change = 4;
+ bool has_playout_volume_change() const;
+ private:
+ bool _internal_has_playout_volume_change() const;
+ public:
+ void clear_playout_volume_change();
+ ::PROTOBUF_NAMESPACE_ID::int32 playout_volume_change() const;
+ void set_playout_volume_change(::PROTOBUF_NAMESPACE_ID::int32 value);
+ private:
+ ::PROTOBUF_NAMESPACE_ID::int32 _internal_playout_volume_change() const;
+ void _internal_set_playout_volume_change(::PROTOBUF_NAMESPACE_ID::int32 value);
+ public:
+
+ // optional bool capture_output_used = 6;
+ bool has_capture_output_used() const;
+ private:
+ bool _internal_has_capture_output_used() const;
+ public:
+ void clear_capture_output_used();
+ bool capture_output_used() const;
+ void set_capture_output_used(bool value);
+ private:
+ bool _internal_capture_output_used() const;
+ void _internal_set_capture_output_used(bool value);
+ public:
+
+ // optional float capture_post_gain = 7;
+ bool has_capture_post_gain() const;
+ private:
+ bool _internal_has_capture_post_gain() const;
+ public:
+ void clear_capture_post_gain();
+ float capture_post_gain() const;
+ void set_capture_post_gain(float value);
+ private:
+ float _internal_capture_post_gain() const;
+ void _internal_set_capture_post_gain(float value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:webrtc.audioproc.RuntimeSetting)
+ private:
+ class _Internal;
+
+ ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArenaLite _internal_metadata_;
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::webrtc::audioproc::PlayoutAudioDeviceInfo* playout_audio_device_change_;
+ float capture_pre_gain_;
+ float custom_render_processing_setting_;
+ float capture_fixed_post_gain_;
+ ::PROTOBUF_NAMESPACE_ID::int32 playout_volume_change_;
+ bool capture_output_used_;
+ float capture_post_gain_;
+ friend struct ::TableStruct_debug_2eproto;
+};
+// -------------------------------------------------------------------
+
+class Event :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:webrtc.audioproc.Event) */ {
+ public:
+ Event();
+ virtual ~Event();
+
+ Event(const Event& from);
+ Event(Event&& from) noexcept
+ : Event() {
+ *this = ::std::move(from);
+ }
+
+ inline Event& operator=(const Event& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline Event& operator=(Event&& from) noexcept {
+ if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
+ if (this != &from) InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields();
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields();
+ }
+
+ static const Event& default_instance();
+
+ static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY
+ static inline const Event* internal_default_instance() {
+ return reinterpret_cast<const Event*>(
+ &_Event_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 6;
+
+ friend void swap(Event& a, Event& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(Event* other) {
+ if (other == this) return;
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ inline Event* New() const final {
+ return CreateMaybeMessage<Event>(nullptr);
+ }
+
+ Event* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final {
+ return CreateMaybeMessage<Event>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from)
+ final;
+ void CopyFrom(const Event& from);
+ void MergeFrom(const Event& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize(
+ ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ void DiscardUnknownFields();
+ int GetCachedSize() const final { return _cached_size_.Get(); }
+
+ private:
+ inline void SharedCtor();
+ inline void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(Event* other);
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "webrtc.audioproc.Event";
+ }
+ private:
+ inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const {
+ return nullptr;
+ }
+ inline void* MaybeArenaPtr() const {
+ return nullptr;
+ }
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ typedef Event_Type Type;
+ static constexpr Type INIT =
+ Event_Type_INIT;
+ static constexpr Type REVERSE_STREAM =
+ Event_Type_REVERSE_STREAM;
+ static constexpr Type STREAM =
+ Event_Type_STREAM;
+ static constexpr Type CONFIG =
+ Event_Type_CONFIG;
+ static constexpr Type UNKNOWN_EVENT =
+ Event_Type_UNKNOWN_EVENT;
+ static constexpr Type RUNTIME_SETTING =
+ Event_Type_RUNTIME_SETTING;
+ static inline bool Type_IsValid(int value) {
+ return Event_Type_IsValid(value);
+ }
+ static constexpr Type Type_MIN =
+ Event_Type_Type_MIN;
+ static constexpr Type Type_MAX =
+ Event_Type_Type_MAX;
+ static constexpr int Type_ARRAYSIZE =
+ Event_Type_Type_ARRAYSIZE;
+ template<typename T>
+ static inline const std::string& Type_Name(T enum_t_value) {
+ static_assert(::std::is_same<T, Type>::value ||
+ ::std::is_integral<T>::value,
+ "Incorrect type passed to function Type_Name.");
+ return Event_Type_Name(enum_t_value);
+ }
+ static inline bool Type_Parse(const std::string& name,
+ Type* value) {
+ return Event_Type_Parse(name, value);
+ }
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kInitFieldNumber = 2,
+ kReverseStreamFieldNumber = 3,
+ kStreamFieldNumber = 4,
+ kConfigFieldNumber = 5,
+ kRuntimeSettingFieldNumber = 6,
+ kTypeFieldNumber = 1,
+ };
+ // optional .webrtc.audioproc.Init init = 2;
+ bool has_init() const;
+ private:
+ bool _internal_has_init() const;
+ public:
+ void clear_init();
+ const ::webrtc::audioproc::Init& init() const;
+ ::webrtc::audioproc::Init* release_init();
+ ::webrtc::audioproc::Init* mutable_init();
+ void set_allocated_init(::webrtc::audioproc::Init* init);
+ private:
+ const ::webrtc::audioproc::Init& _internal_init() const;
+ ::webrtc::audioproc::Init* _internal_mutable_init();
+ public:
+
+ // optional .webrtc.audioproc.ReverseStream reverse_stream = 3;
+ bool has_reverse_stream() const;
+ private:
+ bool _internal_has_reverse_stream() const;
+ public:
+ void clear_reverse_stream();
+ const ::webrtc::audioproc::ReverseStream& reverse_stream() const;
+ ::webrtc::audioproc::ReverseStream* release_reverse_stream();
+ ::webrtc::audioproc::ReverseStream* mutable_reverse_stream();
+ void set_allocated_reverse_stream(::webrtc::audioproc::ReverseStream* reverse_stream);
+ private:
+ const ::webrtc::audioproc::ReverseStream& _internal_reverse_stream() const;
+ ::webrtc::audioproc::ReverseStream* _internal_mutable_reverse_stream();
+ public:
+
+ // optional .webrtc.audioproc.Stream stream = 4;
+ bool has_stream() const;
+ private:
+ bool _internal_has_stream() const;
+ public:
+ void clear_stream();
+ const ::webrtc::audioproc::Stream& stream() const;
+ ::webrtc::audioproc::Stream* release_stream();
+ ::webrtc::audioproc::Stream* mutable_stream();
+ void set_allocated_stream(::webrtc::audioproc::Stream* stream);
+ private:
+ const ::webrtc::audioproc::Stream& _internal_stream() const;
+ ::webrtc::audioproc::Stream* _internal_mutable_stream();
+ public:
+
+ // optional .webrtc.audioproc.Config config = 5;
+ bool has_config() const;
+ private:
+ bool _internal_has_config() const;
+ public:
+ void clear_config();
+ const ::webrtc::audioproc::Config& config() const;
+ ::webrtc::audioproc::Config* release_config();
+ ::webrtc::audioproc::Config* mutable_config();
+ void set_allocated_config(::webrtc::audioproc::Config* config);
+ private:
+ const ::webrtc::audioproc::Config& _internal_config() const;
+ ::webrtc::audioproc::Config* _internal_mutable_config();
+ public:
+
+ // optional .webrtc.audioproc.RuntimeSetting runtime_setting = 6;
+ bool has_runtime_setting() const;
+ private:
+ bool _internal_has_runtime_setting() const;
+ public:
+ void clear_runtime_setting();
+ const ::webrtc::audioproc::RuntimeSetting& runtime_setting() const;
+ ::webrtc::audioproc::RuntimeSetting* release_runtime_setting();
+ ::webrtc::audioproc::RuntimeSetting* mutable_runtime_setting();
+ void set_allocated_runtime_setting(::webrtc::audioproc::RuntimeSetting* runtime_setting);
+ private:
+ const ::webrtc::audioproc::RuntimeSetting& _internal_runtime_setting() const;
+ ::webrtc::audioproc::RuntimeSetting* _internal_mutable_runtime_setting();
+ public:
+
+ // required .webrtc.audioproc.Event.Type type = 1;
+ bool has_type() const;
+ private:
+ bool _internal_has_type() const;
+ public:
+ void clear_type();
+ ::webrtc::audioproc::Event_Type type() const;
+ void set_type(::webrtc::audioproc::Event_Type value);
+ private:
+ ::webrtc::audioproc::Event_Type _internal_type() const;
+ void _internal_set_type(::webrtc::audioproc::Event_Type value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:webrtc.audioproc.Event)
+ private:
+ class _Internal;
+
+ ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArenaLite _internal_metadata_;
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::webrtc::audioproc::Init* init_;
+ ::webrtc::audioproc::ReverseStream* reverse_stream_;
+ ::webrtc::audioproc::Stream* stream_;
+ ::webrtc::audioproc::Config* config_;
+ ::webrtc::audioproc::RuntimeSetting* runtime_setting_;
+ int type_;
+ friend struct ::TableStruct_debug_2eproto;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#ifdef __GNUC__
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif // __GNUC__
+// Init
+
+// optional int32 sample_rate = 1;
+inline bool Init::_internal_has_sample_rate() const {
+ bool value = (_has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool Init::has_sample_rate() const {
+ return _internal_has_sample_rate();
+}
+inline void Init::clear_sample_rate() {
+ sample_rate_ = 0;
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Init::_internal_sample_rate() const {
+ return sample_rate_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Init::sample_rate() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Init.sample_rate)
+ return _internal_sample_rate();
+}
+inline void Init::_internal_set_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00000001u;
+ sample_rate_ = value;
+}
+inline void Init::set_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_sample_rate(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Init.sample_rate)
+}
+
+// optional int32 device_sample_rate = 2 [deprecated = true];
+inline bool Init::_internal_has_device_sample_rate() const {
+ bool value = (_has_bits_[0] & 0x00000002u) != 0;
+ return value;
+}
+inline bool Init::has_device_sample_rate() const {
+ return _internal_has_device_sample_rate();
+}
+inline void Init::clear_device_sample_rate() {
+ device_sample_rate_ = 0;
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Init::_internal_device_sample_rate() const {
+ return device_sample_rate_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Init::device_sample_rate() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Init.device_sample_rate)
+ return _internal_device_sample_rate();
+}
+inline void Init::_internal_set_device_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00000002u;
+ device_sample_rate_ = value;
+}
+inline void Init::set_device_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_device_sample_rate(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Init.device_sample_rate)
+}
+
+// optional int32 num_input_channels = 3;
+inline bool Init::_internal_has_num_input_channels() const {
+ bool value = (_has_bits_[0] & 0x00000004u) != 0;
+ return value;
+}
+inline bool Init::has_num_input_channels() const {
+ return _internal_has_num_input_channels();
+}
+inline void Init::clear_num_input_channels() {
+ num_input_channels_ = 0;
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Init::_internal_num_input_channels() const {
+ return num_input_channels_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Init::num_input_channels() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Init.num_input_channels)
+ return _internal_num_input_channels();
+}
+inline void Init::_internal_set_num_input_channels(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00000004u;
+ num_input_channels_ = value;
+}
+inline void Init::set_num_input_channels(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_num_input_channels(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Init.num_input_channels)
+}
+
+// optional int32 num_output_channels = 4;
+inline bool Init::_internal_has_num_output_channels() const {
+ bool value = (_has_bits_[0] & 0x00000008u) != 0;
+ return value;
+}
+inline bool Init::has_num_output_channels() const {
+ return _internal_has_num_output_channels();
+}
+inline void Init::clear_num_output_channels() {
+ num_output_channels_ = 0;
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Init::_internal_num_output_channels() const {
+ return num_output_channels_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Init::num_output_channels() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Init.num_output_channels)
+ return _internal_num_output_channels();
+}
+inline void Init::_internal_set_num_output_channels(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00000008u;
+ num_output_channels_ = value;
+}
+inline void Init::set_num_output_channels(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_num_output_channels(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Init.num_output_channels)
+}
+
+// optional int32 num_reverse_channels = 5;
+inline bool Init::_internal_has_num_reverse_channels() const {
+ bool value = (_has_bits_[0] & 0x00000010u) != 0;
+ return value;
+}
+inline bool Init::has_num_reverse_channels() const {
+ return _internal_has_num_reverse_channels();
+}
+inline void Init::clear_num_reverse_channels() {
+ num_reverse_channels_ = 0;
+ _has_bits_[0] &= ~0x00000010u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Init::_internal_num_reverse_channels() const {
+ return num_reverse_channels_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Init::num_reverse_channels() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Init.num_reverse_channels)
+ return _internal_num_reverse_channels();
+}
+inline void Init::_internal_set_num_reverse_channels(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00000010u;
+ num_reverse_channels_ = value;
+}
+inline void Init::set_num_reverse_channels(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_num_reverse_channels(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Init.num_reverse_channels)
+}
+
+// optional int32 reverse_sample_rate = 6;
+inline bool Init::_internal_has_reverse_sample_rate() const {
+ bool value = (_has_bits_[0] & 0x00000020u) != 0;
+ return value;
+}
+inline bool Init::has_reverse_sample_rate() const {
+ return _internal_has_reverse_sample_rate();
+}
+inline void Init::clear_reverse_sample_rate() {
+ reverse_sample_rate_ = 0;
+ _has_bits_[0] &= ~0x00000020u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Init::_internal_reverse_sample_rate() const {
+ return reverse_sample_rate_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Init::reverse_sample_rate() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Init.reverse_sample_rate)
+ return _internal_reverse_sample_rate();
+}
+inline void Init::_internal_set_reverse_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00000020u;
+ reverse_sample_rate_ = value;
+}
+inline void Init::set_reverse_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_reverse_sample_rate(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Init.reverse_sample_rate)
+}
+
+// optional int32 output_sample_rate = 7;
+inline bool Init::_internal_has_output_sample_rate() const {
+ bool value = (_has_bits_[0] & 0x00000040u) != 0;
+ return value;
+}
+inline bool Init::has_output_sample_rate() const {
+ return _internal_has_output_sample_rate();
+}
+inline void Init::clear_output_sample_rate() {
+ output_sample_rate_ = 0;
+ _has_bits_[0] &= ~0x00000040u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Init::_internal_output_sample_rate() const {
+ return output_sample_rate_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Init::output_sample_rate() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Init.output_sample_rate)
+ return _internal_output_sample_rate();
+}
+inline void Init::_internal_set_output_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00000040u;
+ output_sample_rate_ = value;
+}
+inline void Init::set_output_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_output_sample_rate(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Init.output_sample_rate)
+}
+
+// optional int32 reverse_output_sample_rate = 8;
+inline bool Init::_internal_has_reverse_output_sample_rate() const {
+ bool value = (_has_bits_[0] & 0x00000080u) != 0;
+ return value;
+}
+inline bool Init::has_reverse_output_sample_rate() const {
+ return _internal_has_reverse_output_sample_rate();
+}
+inline void Init::clear_reverse_output_sample_rate() {
+ reverse_output_sample_rate_ = 0;
+ _has_bits_[0] &= ~0x00000080u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Init::_internal_reverse_output_sample_rate() const {
+ return reverse_output_sample_rate_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Init::reverse_output_sample_rate() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Init.reverse_output_sample_rate)
+ return _internal_reverse_output_sample_rate();
+}
+inline void Init::_internal_set_reverse_output_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00000080u;
+ reverse_output_sample_rate_ = value;
+}
+inline void Init::set_reverse_output_sample_rate(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_reverse_output_sample_rate(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Init.reverse_output_sample_rate)
+}
+
+// optional int32 num_reverse_output_channels = 9;
+inline bool Init::_internal_has_num_reverse_output_channels() const {
+ bool value = (_has_bits_[0] & 0x00000200u) != 0;
+ return value;
+}
+inline bool Init::has_num_reverse_output_channels() const {
+ return _internal_has_num_reverse_output_channels();
+}
+inline void Init::clear_num_reverse_output_channels() {
+ num_reverse_output_channels_ = 0;
+ _has_bits_[0] &= ~0x00000200u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Init::_internal_num_reverse_output_channels() const {
+ return num_reverse_output_channels_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Init::num_reverse_output_channels() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Init.num_reverse_output_channels)
+ return _internal_num_reverse_output_channels();
+}
+inline void Init::_internal_set_num_reverse_output_channels(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00000200u;
+ num_reverse_output_channels_ = value;
+}
+inline void Init::set_num_reverse_output_channels(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_num_reverse_output_channels(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Init.num_reverse_output_channels)
+}
+
+// optional int64 timestamp_ms = 10;
+inline bool Init::_internal_has_timestamp_ms() const {
+ bool value = (_has_bits_[0] & 0x00000100u) != 0;
+ return value;
+}
+inline bool Init::has_timestamp_ms() const {
+ return _internal_has_timestamp_ms();
+}
+inline void Init::clear_timestamp_ms() {
+ timestamp_ms_ = PROTOBUF_LONGLONG(0);
+ _has_bits_[0] &= ~0x00000100u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int64 Init::_internal_timestamp_ms() const {
+ return timestamp_ms_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int64 Init::timestamp_ms() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Init.timestamp_ms)
+ return _internal_timestamp_ms();
+}
+inline void Init::_internal_set_timestamp_ms(::PROTOBUF_NAMESPACE_ID::int64 value) {
+ _has_bits_[0] |= 0x00000100u;
+ timestamp_ms_ = value;
+}
+inline void Init::set_timestamp_ms(::PROTOBUF_NAMESPACE_ID::int64 value) {
+ _internal_set_timestamp_ms(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Init.timestamp_ms)
+}
+
+// -------------------------------------------------------------------
+
+// ReverseStream
+
+// optional bytes data = 1;
+inline bool ReverseStream::_internal_has_data() const {
+ bool value = (_has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool ReverseStream::has_data() const {
+ return _internal_has_data();
+}
+inline void ReverseStream::clear_data() {
+ data_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& ReverseStream::data() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.ReverseStream.data)
+ return _internal_data();
+}
+inline void ReverseStream::set_data(const std::string& value) {
+ _internal_set_data(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.ReverseStream.data)
+}
+inline std::string* ReverseStream::mutable_data() {
+ // @@protoc_insertion_point(field_mutable:webrtc.audioproc.ReverseStream.data)
+ return _internal_mutable_data();
+}
+inline const std::string& ReverseStream::_internal_data() const {
+ return data_.GetNoArena();
+}
+inline void ReverseStream::_internal_set_data(const std::string& value) {
+ _has_bits_[0] |= 0x00000001u;
+ data_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value);
+}
+inline void ReverseStream::set_data(std::string&& value) {
+ _has_bits_[0] |= 0x00000001u;
+ data_.SetNoArena(
+ &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
+ // @@protoc_insertion_point(field_set_rvalue:webrtc.audioproc.ReverseStream.data)
+}
+inline void ReverseStream::set_data(const char* value) {
+ GOOGLE_DCHECK(value != nullptr);
+ _has_bits_[0] |= 0x00000001u;
+ data_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:webrtc.audioproc.ReverseStream.data)
+}
+inline void ReverseStream::set_data(const void* value, size_t size) {
+ _has_bits_[0] |= 0x00000001u;
+ data_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:webrtc.audioproc.ReverseStream.data)
+}
+inline std::string* ReverseStream::_internal_mutable_data() {
+ _has_bits_[0] |= 0x00000001u;
+ return data_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline std::string* ReverseStream::release_data() {
+ // @@protoc_insertion_point(field_release:webrtc.audioproc.ReverseStream.data)
+ if (!_internal_has_data()) {
+ return nullptr;
+ }
+ _has_bits_[0] &= ~0x00000001u;
+ return data_.ReleaseNonDefaultNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline void ReverseStream::set_allocated_data(std::string* data) {
+ if (data != nullptr) {
+ _has_bits_[0] |= 0x00000001u;
+ } else {
+ _has_bits_[0] &= ~0x00000001u;
+ }
+ data_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), data);
+ // @@protoc_insertion_point(field_set_allocated:webrtc.audioproc.ReverseStream.data)
+}
+
+// repeated bytes channel = 2;
+inline int ReverseStream::_internal_channel_size() const {
+ return channel_.size();
+}
+inline int ReverseStream::channel_size() const {
+ return _internal_channel_size();
+}
+inline void ReverseStream::clear_channel() {
+ channel_.Clear();
+}
+inline std::string* ReverseStream::add_channel() {
+ // @@protoc_insertion_point(field_add_mutable:webrtc.audioproc.ReverseStream.channel)
+ return _internal_add_channel();
+}
+inline const std::string& ReverseStream::_internal_channel(int index) const {
+ return channel_.Get(index);
+}
+inline const std::string& ReverseStream::channel(int index) const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.ReverseStream.channel)
+ return _internal_channel(index);
+}
+inline std::string* ReverseStream::mutable_channel(int index) {
+ // @@protoc_insertion_point(field_mutable:webrtc.audioproc.ReverseStream.channel)
+ return channel_.Mutable(index);
+}
+inline void ReverseStream::set_channel(int index, const std::string& value) {
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.ReverseStream.channel)
+ channel_.Mutable(index)->assign(value);
+}
+inline void ReverseStream::set_channel(int index, std::string&& value) {
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.ReverseStream.channel)
+ channel_.Mutable(index)->assign(std::move(value));
+}
+inline void ReverseStream::set_channel(int index, const char* value) {
+ GOOGLE_DCHECK(value != nullptr);
+ channel_.Mutable(index)->assign(value);
+ // @@protoc_insertion_point(field_set_char:webrtc.audioproc.ReverseStream.channel)
+}
+inline void ReverseStream::set_channel(int index, const void* value, size_t size) {
+ channel_.Mutable(index)->assign(
+ reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_set_pointer:webrtc.audioproc.ReverseStream.channel)
+}
+inline std::string* ReverseStream::_internal_add_channel() {
+ return channel_.Add();
+}
+inline void ReverseStream::add_channel(const std::string& value) {
+ channel_.Add()->assign(value);
+ // @@protoc_insertion_point(field_add:webrtc.audioproc.ReverseStream.channel)
+}
+inline void ReverseStream::add_channel(std::string&& value) {
+ channel_.Add(std::move(value));
+ // @@protoc_insertion_point(field_add:webrtc.audioproc.ReverseStream.channel)
+}
+inline void ReverseStream::add_channel(const char* value) {
+ GOOGLE_DCHECK(value != nullptr);
+ channel_.Add()->assign(value);
+ // @@protoc_insertion_point(field_add_char:webrtc.audioproc.ReverseStream.channel)
+}
+inline void ReverseStream::add_channel(const void* value, size_t size) {
+ channel_.Add()->assign(reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_add_pointer:webrtc.audioproc.ReverseStream.channel)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>&
+ReverseStream::channel() const {
+ // @@protoc_insertion_point(field_list:webrtc.audioproc.ReverseStream.channel)
+ return channel_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>*
+ReverseStream::mutable_channel() {
+ // @@protoc_insertion_point(field_mutable_list:webrtc.audioproc.ReverseStream.channel)
+ return &channel_;
+}
+
+// -------------------------------------------------------------------
+
+// Stream
+
+// optional bytes input_data = 1;
+inline bool Stream::_internal_has_input_data() const {
+ bool value = (_has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool Stream::has_input_data() const {
+ return _internal_has_input_data();
+}
+inline void Stream::clear_input_data() {
+ input_data_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& Stream::input_data() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Stream.input_data)
+ return _internal_input_data();
+}
+inline void Stream::set_input_data(const std::string& value) {
+ _internal_set_input_data(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Stream.input_data)
+}
+inline std::string* Stream::mutable_input_data() {
+ // @@protoc_insertion_point(field_mutable:webrtc.audioproc.Stream.input_data)
+ return _internal_mutable_input_data();
+}
+inline const std::string& Stream::_internal_input_data() const {
+ return input_data_.GetNoArena();
+}
+inline void Stream::_internal_set_input_data(const std::string& value) {
+ _has_bits_[0] |= 0x00000001u;
+ input_data_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value);
+}
+inline void Stream::set_input_data(std::string&& value) {
+ _has_bits_[0] |= 0x00000001u;
+ input_data_.SetNoArena(
+ &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
+ // @@protoc_insertion_point(field_set_rvalue:webrtc.audioproc.Stream.input_data)
+}
+inline void Stream::set_input_data(const char* value) {
+ GOOGLE_DCHECK(value != nullptr);
+ _has_bits_[0] |= 0x00000001u;
+ input_data_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:webrtc.audioproc.Stream.input_data)
+}
+inline void Stream::set_input_data(const void* value, size_t size) {
+ _has_bits_[0] |= 0x00000001u;
+ input_data_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:webrtc.audioproc.Stream.input_data)
+}
+inline std::string* Stream::_internal_mutable_input_data() {
+ _has_bits_[0] |= 0x00000001u;
+ return input_data_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline std::string* Stream::release_input_data() {
+ // @@protoc_insertion_point(field_release:webrtc.audioproc.Stream.input_data)
+ if (!_internal_has_input_data()) {
+ return nullptr;
+ }
+ _has_bits_[0] &= ~0x00000001u;
+ return input_data_.ReleaseNonDefaultNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline void Stream::set_allocated_input_data(std::string* input_data) {
+ if (input_data != nullptr) {
+ _has_bits_[0] |= 0x00000001u;
+ } else {
+ _has_bits_[0] &= ~0x00000001u;
+ }
+ input_data_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), input_data);
+ // @@protoc_insertion_point(field_set_allocated:webrtc.audioproc.Stream.input_data)
+}
+
+// optional bytes output_data = 2;
+inline bool Stream::_internal_has_output_data() const {
+ bool value = (_has_bits_[0] & 0x00000002u) != 0;
+ return value;
+}
+inline bool Stream::has_output_data() const {
+ return _internal_has_output_data();
+}
+inline void Stream::clear_output_data() {
+ output_data_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline const std::string& Stream::output_data() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Stream.output_data)
+ return _internal_output_data();
+}
+inline void Stream::set_output_data(const std::string& value) {
+ _internal_set_output_data(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Stream.output_data)
+}
+inline std::string* Stream::mutable_output_data() {
+ // @@protoc_insertion_point(field_mutable:webrtc.audioproc.Stream.output_data)
+ return _internal_mutable_output_data();
+}
+inline const std::string& Stream::_internal_output_data() const {
+ return output_data_.GetNoArena();
+}
+inline void Stream::_internal_set_output_data(const std::string& value) {
+ _has_bits_[0] |= 0x00000002u;
+ output_data_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value);
+}
+inline void Stream::set_output_data(std::string&& value) {
+ _has_bits_[0] |= 0x00000002u;
+ output_data_.SetNoArena(
+ &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
+ // @@protoc_insertion_point(field_set_rvalue:webrtc.audioproc.Stream.output_data)
+}
+inline void Stream::set_output_data(const char* value) {
+ GOOGLE_DCHECK(value != nullptr);
+ _has_bits_[0] |= 0x00000002u;
+ output_data_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:webrtc.audioproc.Stream.output_data)
+}
+inline void Stream::set_output_data(const void* value, size_t size) {
+ _has_bits_[0] |= 0x00000002u;
+ output_data_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:webrtc.audioproc.Stream.output_data)
+}
+inline std::string* Stream::_internal_mutable_output_data() {
+ _has_bits_[0] |= 0x00000002u;
+ return output_data_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline std::string* Stream::release_output_data() {
+ // @@protoc_insertion_point(field_release:webrtc.audioproc.Stream.output_data)
+ if (!_internal_has_output_data()) {
+ return nullptr;
+ }
+ _has_bits_[0] &= ~0x00000002u;
+ return output_data_.ReleaseNonDefaultNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline void Stream::set_allocated_output_data(std::string* output_data) {
+ if (output_data != nullptr) {
+ _has_bits_[0] |= 0x00000002u;
+ } else {
+ _has_bits_[0] &= ~0x00000002u;
+ }
+ output_data_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), output_data);
+ // @@protoc_insertion_point(field_set_allocated:webrtc.audioproc.Stream.output_data)
+}
+
+// optional int32 delay = 3;
+inline bool Stream::_internal_has_delay() const {
+ bool value = (_has_bits_[0] & 0x00000004u) != 0;
+ return value;
+}
+inline bool Stream::has_delay() const {
+ return _internal_has_delay();
+}
+inline void Stream::clear_delay() {
+ delay_ = 0;
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Stream::_internal_delay() const {
+ return delay_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Stream::delay() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Stream.delay)
+ return _internal_delay();
+}
+inline void Stream::_internal_set_delay(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00000004u;
+ delay_ = value;
+}
+inline void Stream::set_delay(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_delay(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Stream.delay)
+}
+
+// optional sint32 drift = 4;
+inline bool Stream::_internal_has_drift() const {
+ bool value = (_has_bits_[0] & 0x00000008u) != 0;
+ return value;
+}
+inline bool Stream::has_drift() const {
+ return _internal_has_drift();
+}
+inline void Stream::clear_drift() {
+ drift_ = 0;
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Stream::_internal_drift() const {
+ return drift_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Stream::drift() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Stream.drift)
+ return _internal_drift();
+}
+inline void Stream::_internal_set_drift(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00000008u;
+ drift_ = value;
+}
+inline void Stream::set_drift(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_drift(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Stream.drift)
+}
+
+// optional int32 level = 5;
+inline bool Stream::_internal_has_level() const {
+ bool value = (_has_bits_[0] & 0x00000010u) != 0;
+ return value;
+}
+inline bool Stream::has_level() const {
+ return _internal_has_level();
+}
+inline void Stream::clear_level() {
+ level_ = 0;
+ _has_bits_[0] &= ~0x00000010u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Stream::_internal_level() const {
+ return level_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Stream::level() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Stream.level)
+ return _internal_level();
+}
+inline void Stream::_internal_set_level(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00000010u;
+ level_ = value;
+}
+inline void Stream::set_level(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_level(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Stream.level)
+}
+
+// optional bool keypress = 6;
+inline bool Stream::_internal_has_keypress() const {
+ bool value = (_has_bits_[0] & 0x00000020u) != 0;
+ return value;
+}
+inline bool Stream::has_keypress() const {
+ return _internal_has_keypress();
+}
+inline void Stream::clear_keypress() {
+ keypress_ = false;
+ _has_bits_[0] &= ~0x00000020u;
+}
+inline bool Stream::_internal_keypress() const {
+ return keypress_;
+}
+inline bool Stream::keypress() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Stream.keypress)
+ return _internal_keypress();
+}
+inline void Stream::_internal_set_keypress(bool value) {
+ _has_bits_[0] |= 0x00000020u;
+ keypress_ = value;
+}
+inline void Stream::set_keypress(bool value) {
+ _internal_set_keypress(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Stream.keypress)
+}
+
+// repeated bytes input_channel = 7;
+inline int Stream::_internal_input_channel_size() const {
+ return input_channel_.size();
+}
+inline int Stream::input_channel_size() const {
+ return _internal_input_channel_size();
+}
+inline void Stream::clear_input_channel() {
+ input_channel_.Clear();
+}
+inline std::string* Stream::add_input_channel() {
+ // @@protoc_insertion_point(field_add_mutable:webrtc.audioproc.Stream.input_channel)
+ return _internal_add_input_channel();
+}
+inline const std::string& Stream::_internal_input_channel(int index) const {
+ return input_channel_.Get(index);
+}
+inline const std::string& Stream::input_channel(int index) const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Stream.input_channel)
+ return _internal_input_channel(index);
+}
+inline std::string* Stream::mutable_input_channel(int index) {
+ // @@protoc_insertion_point(field_mutable:webrtc.audioproc.Stream.input_channel)
+ return input_channel_.Mutable(index);
+}
+inline void Stream::set_input_channel(int index, const std::string& value) {
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Stream.input_channel)
+ input_channel_.Mutable(index)->assign(value);
+}
+inline void Stream::set_input_channel(int index, std::string&& value) {
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Stream.input_channel)
+ input_channel_.Mutable(index)->assign(std::move(value));
+}
+inline void Stream::set_input_channel(int index, const char* value) {
+ GOOGLE_DCHECK(value != nullptr);
+ input_channel_.Mutable(index)->assign(value);
+ // @@protoc_insertion_point(field_set_char:webrtc.audioproc.Stream.input_channel)
+}
+inline void Stream::set_input_channel(int index, const void* value, size_t size) {
+ input_channel_.Mutable(index)->assign(
+ reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_set_pointer:webrtc.audioproc.Stream.input_channel)
+}
+inline std::string* Stream::_internal_add_input_channel() {
+ return input_channel_.Add();
+}
+inline void Stream::add_input_channel(const std::string& value) {
+ input_channel_.Add()->assign(value);
+ // @@protoc_insertion_point(field_add:webrtc.audioproc.Stream.input_channel)
+}
+inline void Stream::add_input_channel(std::string&& value) {
+ input_channel_.Add(std::move(value));
+ // @@protoc_insertion_point(field_add:webrtc.audioproc.Stream.input_channel)
+}
+inline void Stream::add_input_channel(const char* value) {
+ GOOGLE_DCHECK(value != nullptr);
+ input_channel_.Add()->assign(value);
+ // @@protoc_insertion_point(field_add_char:webrtc.audioproc.Stream.input_channel)
+}
+inline void Stream::add_input_channel(const void* value, size_t size) {
+ input_channel_.Add()->assign(reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_add_pointer:webrtc.audioproc.Stream.input_channel)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>&
+Stream::input_channel() const {
+ // @@protoc_insertion_point(field_list:webrtc.audioproc.Stream.input_channel)
+ return input_channel_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>*
+Stream::mutable_input_channel() {
+ // @@protoc_insertion_point(field_mutable_list:webrtc.audioproc.Stream.input_channel)
+ return &input_channel_;
+}
+
+// repeated bytes output_channel = 8;
+inline int Stream::_internal_output_channel_size() const {
+ return output_channel_.size();
+}
+inline int Stream::output_channel_size() const {
+ return _internal_output_channel_size();
+}
+inline void Stream::clear_output_channel() {
+ output_channel_.Clear();
+}
+inline std::string* Stream::add_output_channel() {
+ // @@protoc_insertion_point(field_add_mutable:webrtc.audioproc.Stream.output_channel)
+ return _internal_add_output_channel();
+}
+inline const std::string& Stream::_internal_output_channel(int index) const {
+ return output_channel_.Get(index);
+}
+inline const std::string& Stream::output_channel(int index) const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Stream.output_channel)
+ return _internal_output_channel(index);
+}
+inline std::string* Stream::mutable_output_channel(int index) {
+ // @@protoc_insertion_point(field_mutable:webrtc.audioproc.Stream.output_channel)
+ return output_channel_.Mutable(index);
+}
+inline void Stream::set_output_channel(int index, const std::string& value) {
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Stream.output_channel)
+ output_channel_.Mutable(index)->assign(value);
+}
+inline void Stream::set_output_channel(int index, std::string&& value) {
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Stream.output_channel)
+ output_channel_.Mutable(index)->assign(std::move(value));
+}
+inline void Stream::set_output_channel(int index, const char* value) {
+ GOOGLE_DCHECK(value != nullptr);
+ output_channel_.Mutable(index)->assign(value);
+ // @@protoc_insertion_point(field_set_char:webrtc.audioproc.Stream.output_channel)
+}
+inline void Stream::set_output_channel(int index, const void* value, size_t size) {
+ output_channel_.Mutable(index)->assign(
+ reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_set_pointer:webrtc.audioproc.Stream.output_channel)
+}
+inline std::string* Stream::_internal_add_output_channel() {
+ return output_channel_.Add();
+}
+inline void Stream::add_output_channel(const std::string& value) {
+ output_channel_.Add()->assign(value);
+ // @@protoc_insertion_point(field_add:webrtc.audioproc.Stream.output_channel)
+}
+inline void Stream::add_output_channel(std::string&& value) {
+ output_channel_.Add(std::move(value));
+ // @@protoc_insertion_point(field_add:webrtc.audioproc.Stream.output_channel)
+}
+inline void Stream::add_output_channel(const char* value) {
+ GOOGLE_DCHECK(value != nullptr);
+ output_channel_.Add()->assign(value);
+ // @@protoc_insertion_point(field_add_char:webrtc.audioproc.Stream.output_channel)
+}
+inline void Stream::add_output_channel(const void* value, size_t size) {
+ output_channel_.Add()->assign(reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_add_pointer:webrtc.audioproc.Stream.output_channel)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>&
+Stream::output_channel() const {
+ // @@protoc_insertion_point(field_list:webrtc.audioproc.Stream.output_channel)
+ return output_channel_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>*
+Stream::mutable_output_channel() {
+ // @@protoc_insertion_point(field_mutable_list:webrtc.audioproc.Stream.output_channel)
+ return &output_channel_;
+}
+
+// -------------------------------------------------------------------
+
+// Config
+
+// optional bool aec_enabled = 1;
+inline bool Config::_internal_has_aec_enabled() const {
+ bool value = (_has_bits_[0] & 0x00000002u) != 0;
+ return value;
+}
+inline bool Config::has_aec_enabled() const {
+ return _internal_has_aec_enabled();
+}
+inline void Config::clear_aec_enabled() {
+ aec_enabled_ = false;
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline bool Config::_internal_aec_enabled() const {
+ return aec_enabled_;
+}
+inline bool Config::aec_enabled() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.aec_enabled)
+ return _internal_aec_enabled();
+}
+inline void Config::_internal_set_aec_enabled(bool value) {
+ _has_bits_[0] |= 0x00000002u;
+ aec_enabled_ = value;
+}
+inline void Config::set_aec_enabled(bool value) {
+ _internal_set_aec_enabled(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.aec_enabled)
+}
+
+// optional bool aec_delay_agnostic_enabled = 2;
+inline bool Config::_internal_has_aec_delay_agnostic_enabled() const {
+ bool value = (_has_bits_[0] & 0x00000004u) != 0;
+ return value;
+}
+inline bool Config::has_aec_delay_agnostic_enabled() const {
+ return _internal_has_aec_delay_agnostic_enabled();
+}
+inline void Config::clear_aec_delay_agnostic_enabled() {
+ aec_delay_agnostic_enabled_ = false;
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline bool Config::_internal_aec_delay_agnostic_enabled() const {
+ return aec_delay_agnostic_enabled_;
+}
+inline bool Config::aec_delay_agnostic_enabled() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.aec_delay_agnostic_enabled)
+ return _internal_aec_delay_agnostic_enabled();
+}
+inline void Config::_internal_set_aec_delay_agnostic_enabled(bool value) {
+ _has_bits_[0] |= 0x00000004u;
+ aec_delay_agnostic_enabled_ = value;
+}
+inline void Config::set_aec_delay_agnostic_enabled(bool value) {
+ _internal_set_aec_delay_agnostic_enabled(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.aec_delay_agnostic_enabled)
+}
+
+// optional bool aec_drift_compensation_enabled = 3;
+inline bool Config::_internal_has_aec_drift_compensation_enabled() const {
+ bool value = (_has_bits_[0] & 0x00000008u) != 0;
+ return value;
+}
+inline bool Config::has_aec_drift_compensation_enabled() const {
+ return _internal_has_aec_drift_compensation_enabled();
+}
+inline void Config::clear_aec_drift_compensation_enabled() {
+ aec_drift_compensation_enabled_ = false;
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline bool Config::_internal_aec_drift_compensation_enabled() const {
+ return aec_drift_compensation_enabled_;
+}
+inline bool Config::aec_drift_compensation_enabled() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.aec_drift_compensation_enabled)
+ return _internal_aec_drift_compensation_enabled();
+}
+inline void Config::_internal_set_aec_drift_compensation_enabled(bool value) {
+ _has_bits_[0] |= 0x00000008u;
+ aec_drift_compensation_enabled_ = value;
+}
+inline void Config::set_aec_drift_compensation_enabled(bool value) {
+ _internal_set_aec_drift_compensation_enabled(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.aec_drift_compensation_enabled)
+}
+
+// optional bool aec_extended_filter_enabled = 4;
+inline bool Config::_internal_has_aec_extended_filter_enabled() const {
+ bool value = (_has_bits_[0] & 0x00000010u) != 0;
+ return value;
+}
+inline bool Config::has_aec_extended_filter_enabled() const {
+ return _internal_has_aec_extended_filter_enabled();
+}
+inline void Config::clear_aec_extended_filter_enabled() {
+ aec_extended_filter_enabled_ = false;
+ _has_bits_[0] &= ~0x00000010u;
+}
+inline bool Config::_internal_aec_extended_filter_enabled() const {
+ return aec_extended_filter_enabled_;
+}
+inline bool Config::aec_extended_filter_enabled() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.aec_extended_filter_enabled)
+ return _internal_aec_extended_filter_enabled();
+}
+inline void Config::_internal_set_aec_extended_filter_enabled(bool value) {
+ _has_bits_[0] |= 0x00000010u;
+ aec_extended_filter_enabled_ = value;
+}
+inline void Config::set_aec_extended_filter_enabled(bool value) {
+ _internal_set_aec_extended_filter_enabled(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.aec_extended_filter_enabled)
+}
+
+// optional int32 aec_suppression_level = 5;
+inline bool Config::_internal_has_aec_suppression_level() const {
+ bool value = (_has_bits_[0] & 0x00000020u) != 0;
+ return value;
+}
+inline bool Config::has_aec_suppression_level() const {
+ return _internal_has_aec_suppression_level();
+}
+inline void Config::clear_aec_suppression_level() {
+ aec_suppression_level_ = 0;
+ _has_bits_[0] &= ~0x00000020u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Config::_internal_aec_suppression_level() const {
+ return aec_suppression_level_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Config::aec_suppression_level() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.aec_suppression_level)
+ return _internal_aec_suppression_level();
+}
+inline void Config::_internal_set_aec_suppression_level(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00000020u;
+ aec_suppression_level_ = value;
+}
+inline void Config::set_aec_suppression_level(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_aec_suppression_level(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.aec_suppression_level)
+}
+
+// optional bool aecm_enabled = 6;
+inline bool Config::_internal_has_aecm_enabled() const {
+ bool value = (_has_bits_[0] & 0x00000080u) != 0;
+ return value;
+}
+inline bool Config::has_aecm_enabled() const {
+ return _internal_has_aecm_enabled();
+}
+inline void Config::clear_aecm_enabled() {
+ aecm_enabled_ = false;
+ _has_bits_[0] &= ~0x00000080u;
+}
+inline bool Config::_internal_aecm_enabled() const {
+ return aecm_enabled_;
+}
+inline bool Config::aecm_enabled() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.aecm_enabled)
+ return _internal_aecm_enabled();
+}
+inline void Config::_internal_set_aecm_enabled(bool value) {
+ _has_bits_[0] |= 0x00000080u;
+ aecm_enabled_ = value;
+}
+inline void Config::set_aecm_enabled(bool value) {
+ _internal_set_aecm_enabled(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.aecm_enabled)
+}
+
+// optional bool aecm_comfort_noise_enabled = 7 [deprecated = true];
+inline bool Config::_internal_has_aecm_comfort_noise_enabled() const {
+ bool value = (_has_bits_[0] & 0x00000100u) != 0;
+ return value;
+}
+inline bool Config::has_aecm_comfort_noise_enabled() const {
+ return _internal_has_aecm_comfort_noise_enabled();
+}
+inline void Config::clear_aecm_comfort_noise_enabled() {
+ aecm_comfort_noise_enabled_ = false;
+ _has_bits_[0] &= ~0x00000100u;
+}
+inline bool Config::_internal_aecm_comfort_noise_enabled() const {
+ return aecm_comfort_noise_enabled_;
+}
+inline bool Config::aecm_comfort_noise_enabled() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.aecm_comfort_noise_enabled)
+ return _internal_aecm_comfort_noise_enabled();
+}
+inline void Config::_internal_set_aecm_comfort_noise_enabled(bool value) {
+ _has_bits_[0] |= 0x00000100u;
+ aecm_comfort_noise_enabled_ = value;
+}
+inline void Config::set_aecm_comfort_noise_enabled(bool value) {
+ _internal_set_aecm_comfort_noise_enabled(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.aecm_comfort_noise_enabled)
+}
+
+// optional int32 aecm_routing_mode = 8 [deprecated = true];
+inline bool Config::_internal_has_aecm_routing_mode() const {
+ bool value = (_has_bits_[0] & 0x00000040u) != 0;
+ return value;
+}
+inline bool Config::has_aecm_routing_mode() const {
+ return _internal_has_aecm_routing_mode();
+}
+inline void Config::clear_aecm_routing_mode() {
+ aecm_routing_mode_ = 0;
+ _has_bits_[0] &= ~0x00000040u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Config::_internal_aecm_routing_mode() const {
+ return aecm_routing_mode_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Config::aecm_routing_mode() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.aecm_routing_mode)
+ return _internal_aecm_routing_mode();
+}
+inline void Config::_internal_set_aecm_routing_mode(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00000040u;
+ aecm_routing_mode_ = value;
+}
+inline void Config::set_aecm_routing_mode(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_aecm_routing_mode(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.aecm_routing_mode)
+}
+
+// optional bool agc_enabled = 9;
+inline bool Config::_internal_has_agc_enabled() const {
+ bool value = (_has_bits_[0] & 0x00000200u) != 0;
+ return value;
+}
+inline bool Config::has_agc_enabled() const {
+ return _internal_has_agc_enabled();
+}
+inline void Config::clear_agc_enabled() {
+ agc_enabled_ = false;
+ _has_bits_[0] &= ~0x00000200u;
+}
+inline bool Config::_internal_agc_enabled() const {
+ return agc_enabled_;
+}
+inline bool Config::agc_enabled() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.agc_enabled)
+ return _internal_agc_enabled();
+}
+inline void Config::_internal_set_agc_enabled(bool value) {
+ _has_bits_[0] |= 0x00000200u;
+ agc_enabled_ = value;
+}
+inline void Config::set_agc_enabled(bool value) {
+ _internal_set_agc_enabled(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.agc_enabled)
+}
+
+// optional int32 agc_mode = 10;
+inline bool Config::_internal_has_agc_mode() const {
+ bool value = (_has_bits_[0] & 0x00000800u) != 0;
+ return value;
+}
+inline bool Config::has_agc_mode() const {
+ return _internal_has_agc_mode();
+}
+inline void Config::clear_agc_mode() {
+ agc_mode_ = 0;
+ _has_bits_[0] &= ~0x00000800u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Config::_internal_agc_mode() const {
+ return agc_mode_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Config::agc_mode() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.agc_mode)
+ return _internal_agc_mode();
+}
+inline void Config::_internal_set_agc_mode(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00000800u;
+ agc_mode_ = value;
+}
+inline void Config::set_agc_mode(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_agc_mode(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.agc_mode)
+}
+
+// optional bool agc_limiter_enabled = 11;
+inline bool Config::_internal_has_agc_limiter_enabled() const {
+ bool value = (_has_bits_[0] & 0x00000400u) != 0;
+ return value;
+}
+inline bool Config::has_agc_limiter_enabled() const {
+ return _internal_has_agc_limiter_enabled();
+}
+inline void Config::clear_agc_limiter_enabled() {
+ agc_limiter_enabled_ = false;
+ _has_bits_[0] &= ~0x00000400u;
+}
+inline bool Config::_internal_agc_limiter_enabled() const {
+ return agc_limiter_enabled_;
+}
+inline bool Config::agc_limiter_enabled() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.agc_limiter_enabled)
+ return _internal_agc_limiter_enabled();
+}
+inline void Config::_internal_set_agc_limiter_enabled(bool value) {
+ _has_bits_[0] |= 0x00000400u;
+ agc_limiter_enabled_ = value;
+}
+inline void Config::set_agc_limiter_enabled(bool value) {
+ _internal_set_agc_limiter_enabled(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.agc_limiter_enabled)
+}
+
+// optional bool noise_robust_agc_enabled = 12;
+inline bool Config::_internal_has_noise_robust_agc_enabled() const {
+ bool value = (_has_bits_[0] & 0x00001000u) != 0;
+ return value;
+}
+inline bool Config::has_noise_robust_agc_enabled() const {
+ return _internal_has_noise_robust_agc_enabled();
+}
+inline void Config::clear_noise_robust_agc_enabled() {
+ noise_robust_agc_enabled_ = false;
+ _has_bits_[0] &= ~0x00001000u;
+}
+inline bool Config::_internal_noise_robust_agc_enabled() const {
+ return noise_robust_agc_enabled_;
+}
+inline bool Config::noise_robust_agc_enabled() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.noise_robust_agc_enabled)
+ return _internal_noise_robust_agc_enabled();
+}
+inline void Config::_internal_set_noise_robust_agc_enabled(bool value) {
+ _has_bits_[0] |= 0x00001000u;
+ noise_robust_agc_enabled_ = value;
+}
+inline void Config::set_noise_robust_agc_enabled(bool value) {
+ _internal_set_noise_robust_agc_enabled(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.noise_robust_agc_enabled)
+}
+
+// optional bool hpf_enabled = 13;
+inline bool Config::_internal_has_hpf_enabled() const {
+ bool value = (_has_bits_[0] & 0x00002000u) != 0;
+ return value;
+}
+inline bool Config::has_hpf_enabled() const {
+ return _internal_has_hpf_enabled();
+}
+inline void Config::clear_hpf_enabled() {
+ hpf_enabled_ = false;
+ _has_bits_[0] &= ~0x00002000u;
+}
+inline bool Config::_internal_hpf_enabled() const {
+ return hpf_enabled_;
+}
+inline bool Config::hpf_enabled() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.hpf_enabled)
+ return _internal_hpf_enabled();
+}
+inline void Config::_internal_set_hpf_enabled(bool value) {
+ _has_bits_[0] |= 0x00002000u;
+ hpf_enabled_ = value;
+}
+inline void Config::set_hpf_enabled(bool value) {
+ _internal_set_hpf_enabled(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.hpf_enabled)
+}
+
+// optional bool ns_enabled = 14;
+inline bool Config::_internal_has_ns_enabled() const {
+ bool value = (_has_bits_[0] & 0x00004000u) != 0;
+ return value;
+}
+inline bool Config::has_ns_enabled() const {
+ return _internal_has_ns_enabled();
+}
+inline void Config::clear_ns_enabled() {
+ ns_enabled_ = false;
+ _has_bits_[0] &= ~0x00004000u;
+}
+inline bool Config::_internal_ns_enabled() const {
+ return ns_enabled_;
+}
+inline bool Config::ns_enabled() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.ns_enabled)
+ return _internal_ns_enabled();
+}
+inline void Config::_internal_set_ns_enabled(bool value) {
+ _has_bits_[0] |= 0x00004000u;
+ ns_enabled_ = value;
+}
+inline void Config::set_ns_enabled(bool value) {
+ _internal_set_ns_enabled(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.ns_enabled)
+}
+
+// optional int32 ns_level = 15;
+inline bool Config::_internal_has_ns_level() const {
+ bool value = (_has_bits_[0] & 0x00010000u) != 0;
+ return value;
+}
+inline bool Config::has_ns_level() const {
+ return _internal_has_ns_level();
+}
+inline void Config::clear_ns_level() {
+ ns_level_ = 0;
+ _has_bits_[0] &= ~0x00010000u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Config::_internal_ns_level() const {
+ return ns_level_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 Config::ns_level() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.ns_level)
+ return _internal_ns_level();
+}
+inline void Config::_internal_set_ns_level(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00010000u;
+ ns_level_ = value;
+}
+inline void Config::set_ns_level(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_ns_level(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.ns_level)
+}
+
+// optional bool transient_suppression_enabled = 16;
+inline bool Config::_internal_has_transient_suppression_enabled() const {
+ bool value = (_has_bits_[0] & 0x00008000u) != 0;
+ return value;
+}
+inline bool Config::has_transient_suppression_enabled() const {
+ return _internal_has_transient_suppression_enabled();
+}
+inline void Config::clear_transient_suppression_enabled() {
+ transient_suppression_enabled_ = false;
+ _has_bits_[0] &= ~0x00008000u;
+}
+inline bool Config::_internal_transient_suppression_enabled() const {
+ return transient_suppression_enabled_;
+}
+inline bool Config::transient_suppression_enabled() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.transient_suppression_enabled)
+ return _internal_transient_suppression_enabled();
+}
+inline void Config::_internal_set_transient_suppression_enabled(bool value) {
+ _has_bits_[0] |= 0x00008000u;
+ transient_suppression_enabled_ = value;
+}
+inline void Config::set_transient_suppression_enabled(bool value) {
+ _internal_set_transient_suppression_enabled(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.transient_suppression_enabled)
+}
+
+// optional string experiments_description = 17;
+inline bool Config::_internal_has_experiments_description() const {
+ bool value = (_has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool Config::has_experiments_description() const {
+ return _internal_has_experiments_description();
+}
+inline void Config::clear_experiments_description() {
+ experiments_description_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& Config::experiments_description() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.experiments_description)
+ return _internal_experiments_description();
+}
+inline void Config::set_experiments_description(const std::string& value) {
+ _internal_set_experiments_description(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.experiments_description)
+}
+inline std::string* Config::mutable_experiments_description() {
+ // @@protoc_insertion_point(field_mutable:webrtc.audioproc.Config.experiments_description)
+ return _internal_mutable_experiments_description();
+}
+inline const std::string& Config::_internal_experiments_description() const {
+ return experiments_description_.GetNoArena();
+}
+inline void Config::_internal_set_experiments_description(const std::string& value) {
+ _has_bits_[0] |= 0x00000001u;
+ experiments_description_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value);
+}
+inline void Config::set_experiments_description(std::string&& value) {
+ _has_bits_[0] |= 0x00000001u;
+ experiments_description_.SetNoArena(
+ &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value));
+ // @@protoc_insertion_point(field_set_rvalue:webrtc.audioproc.Config.experiments_description)
+}
+inline void Config::set_experiments_description(const char* value) {
+ GOOGLE_DCHECK(value != nullptr);
+ _has_bits_[0] |= 0x00000001u;
+ experiments_description_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:webrtc.audioproc.Config.experiments_description)
+}
+inline void Config::set_experiments_description(const char* value, size_t size) {
+ _has_bits_[0] |= 0x00000001u;
+ experiments_description_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:webrtc.audioproc.Config.experiments_description)
+}
+inline std::string* Config::_internal_mutable_experiments_description() {
+ _has_bits_[0] |= 0x00000001u;
+ return experiments_description_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline std::string* Config::release_experiments_description() {
+ // @@protoc_insertion_point(field_release:webrtc.audioproc.Config.experiments_description)
+ if (!_internal_has_experiments_description()) {
+ return nullptr;
+ }
+ _has_bits_[0] &= ~0x00000001u;
+ return experiments_description_.ReleaseNonDefaultNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited());
+}
+inline void Config::set_allocated_experiments_description(std::string* experiments_description) {
+ if (experiments_description != nullptr) {
+ _has_bits_[0] |= 0x00000001u;
+ } else {
+ _has_bits_[0] &= ~0x00000001u;
+ }
+ experiments_description_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), experiments_description);
+ // @@protoc_insertion_point(field_set_allocated:webrtc.audioproc.Config.experiments_description)
+}
+
+// optional bool pre_amplifier_enabled = 19;
+inline bool Config::_internal_has_pre_amplifier_enabled() const {
+ bool value = (_has_bits_[0] & 0x00020000u) != 0;
+ return value;
+}
+inline bool Config::has_pre_amplifier_enabled() const {
+ return _internal_has_pre_amplifier_enabled();
+}
+inline void Config::clear_pre_amplifier_enabled() {
+ pre_amplifier_enabled_ = false;
+ _has_bits_[0] &= ~0x00020000u;
+}
+inline bool Config::_internal_pre_amplifier_enabled() const {
+ return pre_amplifier_enabled_;
+}
+inline bool Config::pre_amplifier_enabled() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.pre_amplifier_enabled)
+ return _internal_pre_amplifier_enabled();
+}
+inline void Config::_internal_set_pre_amplifier_enabled(bool value) {
+ _has_bits_[0] |= 0x00020000u;
+ pre_amplifier_enabled_ = value;
+}
+inline void Config::set_pre_amplifier_enabled(bool value) {
+ _internal_set_pre_amplifier_enabled(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.pre_amplifier_enabled)
+}
+
+// optional float pre_amplifier_fixed_gain_factor = 20;
+inline bool Config::_internal_has_pre_amplifier_fixed_gain_factor() const {
+ bool value = (_has_bits_[0] & 0x00040000u) != 0;
+ return value;
+}
+inline bool Config::has_pre_amplifier_fixed_gain_factor() const {
+ return _internal_has_pre_amplifier_fixed_gain_factor();
+}
+inline void Config::clear_pre_amplifier_fixed_gain_factor() {
+ pre_amplifier_fixed_gain_factor_ = 0;
+ _has_bits_[0] &= ~0x00040000u;
+}
+inline float Config::_internal_pre_amplifier_fixed_gain_factor() const {
+ return pre_amplifier_fixed_gain_factor_;
+}
+inline float Config::pre_amplifier_fixed_gain_factor() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Config.pre_amplifier_fixed_gain_factor)
+ return _internal_pre_amplifier_fixed_gain_factor();
+}
+inline void Config::_internal_set_pre_amplifier_fixed_gain_factor(float value) {
+ _has_bits_[0] |= 0x00040000u;
+ pre_amplifier_fixed_gain_factor_ = value;
+}
+inline void Config::set_pre_amplifier_fixed_gain_factor(float value) {
+ _internal_set_pre_amplifier_fixed_gain_factor(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Config.pre_amplifier_fixed_gain_factor)
+}
+
+// -------------------------------------------------------------------
+
+// PlayoutAudioDeviceInfo
+
+// optional int32 id = 1;
+inline bool PlayoutAudioDeviceInfo::_internal_has_id() const {
+ bool value = (_has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool PlayoutAudioDeviceInfo::has_id() const {
+ return _internal_has_id();
+}
+inline void PlayoutAudioDeviceInfo::clear_id() {
+ id_ = 0;
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 PlayoutAudioDeviceInfo::_internal_id() const {
+ return id_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 PlayoutAudioDeviceInfo::id() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.PlayoutAudioDeviceInfo.id)
+ return _internal_id();
+}
+inline void PlayoutAudioDeviceInfo::_internal_set_id(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00000001u;
+ id_ = value;
+}
+inline void PlayoutAudioDeviceInfo::set_id(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_id(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.PlayoutAudioDeviceInfo.id)
+}
+
+// optional int32 max_volume = 2;
+inline bool PlayoutAudioDeviceInfo::_internal_has_max_volume() const {
+ bool value = (_has_bits_[0] & 0x00000002u) != 0;
+ return value;
+}
+inline bool PlayoutAudioDeviceInfo::has_max_volume() const {
+ return _internal_has_max_volume();
+}
+inline void PlayoutAudioDeviceInfo::clear_max_volume() {
+ max_volume_ = 0;
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 PlayoutAudioDeviceInfo::_internal_max_volume() const {
+ return max_volume_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 PlayoutAudioDeviceInfo::max_volume() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.PlayoutAudioDeviceInfo.max_volume)
+ return _internal_max_volume();
+}
+inline void PlayoutAudioDeviceInfo::_internal_set_max_volume(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00000002u;
+ max_volume_ = value;
+}
+inline void PlayoutAudioDeviceInfo::set_max_volume(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_max_volume(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.PlayoutAudioDeviceInfo.max_volume)
+}
+
+// -------------------------------------------------------------------
+
+// RuntimeSetting
+
+// optional float capture_pre_gain = 1;
+inline bool RuntimeSetting::_internal_has_capture_pre_gain() const {
+ bool value = (_has_bits_[0] & 0x00000002u) != 0;
+ return value;
+}
+inline bool RuntimeSetting::has_capture_pre_gain() const {
+ return _internal_has_capture_pre_gain();
+}
+inline void RuntimeSetting::clear_capture_pre_gain() {
+ capture_pre_gain_ = 0;
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline float RuntimeSetting::_internal_capture_pre_gain() const {
+ return capture_pre_gain_;
+}
+inline float RuntimeSetting::capture_pre_gain() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.RuntimeSetting.capture_pre_gain)
+ return _internal_capture_pre_gain();
+}
+inline void RuntimeSetting::_internal_set_capture_pre_gain(float value) {
+ _has_bits_[0] |= 0x00000002u;
+ capture_pre_gain_ = value;
+}
+inline void RuntimeSetting::set_capture_pre_gain(float value) {
+ _internal_set_capture_pre_gain(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.RuntimeSetting.capture_pre_gain)
+}
+
+// optional float custom_render_processing_setting = 2;
+inline bool RuntimeSetting::_internal_has_custom_render_processing_setting() const {
+ bool value = (_has_bits_[0] & 0x00000004u) != 0;
+ return value;
+}
+inline bool RuntimeSetting::has_custom_render_processing_setting() const {
+ return _internal_has_custom_render_processing_setting();
+}
+inline void RuntimeSetting::clear_custom_render_processing_setting() {
+ custom_render_processing_setting_ = 0;
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline float RuntimeSetting::_internal_custom_render_processing_setting() const {
+ return custom_render_processing_setting_;
+}
+inline float RuntimeSetting::custom_render_processing_setting() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.RuntimeSetting.custom_render_processing_setting)
+ return _internal_custom_render_processing_setting();
+}
+inline void RuntimeSetting::_internal_set_custom_render_processing_setting(float value) {
+ _has_bits_[0] |= 0x00000004u;
+ custom_render_processing_setting_ = value;
+}
+inline void RuntimeSetting::set_custom_render_processing_setting(float value) {
+ _internal_set_custom_render_processing_setting(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.RuntimeSetting.custom_render_processing_setting)
+}
+
+// optional float capture_fixed_post_gain = 3;
+inline bool RuntimeSetting::_internal_has_capture_fixed_post_gain() const {
+ bool value = (_has_bits_[0] & 0x00000008u) != 0;
+ return value;
+}
+inline bool RuntimeSetting::has_capture_fixed_post_gain() const {
+ return _internal_has_capture_fixed_post_gain();
+}
+inline void RuntimeSetting::clear_capture_fixed_post_gain() {
+ capture_fixed_post_gain_ = 0;
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline float RuntimeSetting::_internal_capture_fixed_post_gain() const {
+ return capture_fixed_post_gain_;
+}
+inline float RuntimeSetting::capture_fixed_post_gain() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.RuntimeSetting.capture_fixed_post_gain)
+ return _internal_capture_fixed_post_gain();
+}
+inline void RuntimeSetting::_internal_set_capture_fixed_post_gain(float value) {
+ _has_bits_[0] |= 0x00000008u;
+ capture_fixed_post_gain_ = value;
+}
+inline void RuntimeSetting::set_capture_fixed_post_gain(float value) {
+ _internal_set_capture_fixed_post_gain(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.RuntimeSetting.capture_fixed_post_gain)
+}
+
+// optional int32 playout_volume_change = 4;
+inline bool RuntimeSetting::_internal_has_playout_volume_change() const {
+ bool value = (_has_bits_[0] & 0x00000010u) != 0;
+ return value;
+}
+inline bool RuntimeSetting::has_playout_volume_change() const {
+ return _internal_has_playout_volume_change();
+}
+inline void RuntimeSetting::clear_playout_volume_change() {
+ playout_volume_change_ = 0;
+ _has_bits_[0] &= ~0x00000010u;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 RuntimeSetting::_internal_playout_volume_change() const {
+ return playout_volume_change_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::int32 RuntimeSetting::playout_volume_change() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.RuntimeSetting.playout_volume_change)
+ return _internal_playout_volume_change();
+}
+inline void RuntimeSetting::_internal_set_playout_volume_change(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _has_bits_[0] |= 0x00000010u;
+ playout_volume_change_ = value;
+}
+inline void RuntimeSetting::set_playout_volume_change(::PROTOBUF_NAMESPACE_ID::int32 value) {
+ _internal_set_playout_volume_change(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.RuntimeSetting.playout_volume_change)
+}
+
+// optional .webrtc.audioproc.PlayoutAudioDeviceInfo playout_audio_device_change = 5;
+inline bool RuntimeSetting::_internal_has_playout_audio_device_change() const {
+ bool value = (_has_bits_[0] & 0x00000001u) != 0;
+ PROTOBUF_ASSUME(!value || playout_audio_device_change_ != nullptr);
+ return value;
+}
+inline bool RuntimeSetting::has_playout_audio_device_change() const {
+ return _internal_has_playout_audio_device_change();
+}
+inline void RuntimeSetting::clear_playout_audio_device_change() {
+ if (playout_audio_device_change_ != nullptr) playout_audio_device_change_->Clear();
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline const ::webrtc::audioproc::PlayoutAudioDeviceInfo& RuntimeSetting::_internal_playout_audio_device_change() const {
+ const ::webrtc::audioproc::PlayoutAudioDeviceInfo* p = playout_audio_device_change_;
+ return p != nullptr ? *p : *reinterpret_cast<const ::webrtc::audioproc::PlayoutAudioDeviceInfo*>(
+ &::webrtc::audioproc::_PlayoutAudioDeviceInfo_default_instance_);
+}
+inline const ::webrtc::audioproc::PlayoutAudioDeviceInfo& RuntimeSetting::playout_audio_device_change() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.RuntimeSetting.playout_audio_device_change)
+ return _internal_playout_audio_device_change();
+}
+inline ::webrtc::audioproc::PlayoutAudioDeviceInfo* RuntimeSetting::release_playout_audio_device_change() {
+ // @@protoc_insertion_point(field_release:webrtc.audioproc.RuntimeSetting.playout_audio_device_change)
+ _has_bits_[0] &= ~0x00000001u;
+ ::webrtc::audioproc::PlayoutAudioDeviceInfo* temp = playout_audio_device_change_;
+ playout_audio_device_change_ = nullptr;
+ return temp;
+}
+inline ::webrtc::audioproc::PlayoutAudioDeviceInfo* RuntimeSetting::_internal_mutable_playout_audio_device_change() {
+ _has_bits_[0] |= 0x00000001u;
+ if (playout_audio_device_change_ == nullptr) {
+ auto* p = CreateMaybeMessage<::webrtc::audioproc::PlayoutAudioDeviceInfo>(GetArenaNoVirtual());
+ playout_audio_device_change_ = p;
+ }
+ return playout_audio_device_change_;
+}
+inline ::webrtc::audioproc::PlayoutAudioDeviceInfo* RuntimeSetting::mutable_playout_audio_device_change() {
+ // @@protoc_insertion_point(field_mutable:webrtc.audioproc.RuntimeSetting.playout_audio_device_change)
+ return _internal_mutable_playout_audio_device_change();
+}
+inline void RuntimeSetting::set_allocated_playout_audio_device_change(::webrtc::audioproc::PlayoutAudioDeviceInfo* playout_audio_device_change) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual();
+ if (message_arena == nullptr) {
+ delete playout_audio_device_change_;
+ }
+ if (playout_audio_device_change) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr;
+ if (message_arena != submessage_arena) {
+ playout_audio_device_change = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, playout_audio_device_change, submessage_arena);
+ }
+ _has_bits_[0] |= 0x00000001u;
+ } else {
+ _has_bits_[0] &= ~0x00000001u;
+ }
+ playout_audio_device_change_ = playout_audio_device_change;
+ // @@protoc_insertion_point(field_set_allocated:webrtc.audioproc.RuntimeSetting.playout_audio_device_change)
+}
+
+// optional bool capture_output_used = 6;
+inline bool RuntimeSetting::_internal_has_capture_output_used() const {
+ bool value = (_has_bits_[0] & 0x00000020u) != 0;
+ return value;
+}
+inline bool RuntimeSetting::has_capture_output_used() const {
+ return _internal_has_capture_output_used();
+}
+inline void RuntimeSetting::clear_capture_output_used() {
+ capture_output_used_ = false;
+ _has_bits_[0] &= ~0x00000020u;
+}
+inline bool RuntimeSetting::_internal_capture_output_used() const {
+ return capture_output_used_;
+}
+inline bool RuntimeSetting::capture_output_used() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.RuntimeSetting.capture_output_used)
+ return _internal_capture_output_used();
+}
+inline void RuntimeSetting::_internal_set_capture_output_used(bool value) {
+ _has_bits_[0] |= 0x00000020u;
+ capture_output_used_ = value;
+}
+inline void RuntimeSetting::set_capture_output_used(bool value) {
+ _internal_set_capture_output_used(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.RuntimeSetting.capture_output_used)
+}
+
+// optional float capture_post_gain = 7;
+inline bool RuntimeSetting::_internal_has_capture_post_gain() const {
+ bool value = (_has_bits_[0] & 0x00000040u) != 0;
+ return value;
+}
+inline bool RuntimeSetting::has_capture_post_gain() const {
+ return _internal_has_capture_post_gain();
+}
+inline void RuntimeSetting::clear_capture_post_gain() {
+ capture_post_gain_ = 0;
+ _has_bits_[0] &= ~0x00000040u;
+}
+inline float RuntimeSetting::_internal_capture_post_gain() const {
+ return capture_post_gain_;
+}
+inline float RuntimeSetting::capture_post_gain() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.RuntimeSetting.capture_post_gain)
+ return _internal_capture_post_gain();
+}
+inline void RuntimeSetting::_internal_set_capture_post_gain(float value) {
+ _has_bits_[0] |= 0x00000040u;
+ capture_post_gain_ = value;
+}
+inline void RuntimeSetting::set_capture_post_gain(float value) {
+ _internal_set_capture_post_gain(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.RuntimeSetting.capture_post_gain)
+}
+
+// -------------------------------------------------------------------
+
+// Event
+
+// required .webrtc.audioproc.Event.Type type = 1;
+inline bool Event::_internal_has_type() const {
+ bool value = (_has_bits_[0] & 0x00000020u) != 0;
+ return value;
+}
+inline bool Event::has_type() const {
+ return _internal_has_type();
+}
+inline void Event::clear_type() {
+ type_ = 0;
+ _has_bits_[0] &= ~0x00000020u;
+}
+inline ::webrtc::audioproc::Event_Type Event::_internal_type() const {
+ return static_cast< ::webrtc::audioproc::Event_Type >(type_);
+}
+inline ::webrtc::audioproc::Event_Type Event::type() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Event.type)
+ return _internal_type();
+}
+inline void Event::_internal_set_type(::webrtc::audioproc::Event_Type value) {
+ assert(::webrtc::audioproc::Event_Type_IsValid(value));
+ _has_bits_[0] |= 0x00000020u;
+ type_ = value;
+}
+inline void Event::set_type(::webrtc::audioproc::Event_Type value) {
+ _internal_set_type(value);
+ // @@protoc_insertion_point(field_set:webrtc.audioproc.Event.type)
+}
+
+// optional .webrtc.audioproc.Init init = 2;
+inline bool Event::_internal_has_init() const {
+ bool value = (_has_bits_[0] & 0x00000001u) != 0;
+ PROTOBUF_ASSUME(!value || init_ != nullptr);
+ return value;
+}
+inline bool Event::has_init() const {
+ return _internal_has_init();
+}
+inline void Event::clear_init() {
+ if (init_ != nullptr) init_->Clear();
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline const ::webrtc::audioproc::Init& Event::_internal_init() const {
+ const ::webrtc::audioproc::Init* p = init_;
+ return p != nullptr ? *p : *reinterpret_cast<const ::webrtc::audioproc::Init*>(
+ &::webrtc::audioproc::_Init_default_instance_);
+}
+inline const ::webrtc::audioproc::Init& Event::init() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Event.init)
+ return _internal_init();
+}
+inline ::webrtc::audioproc::Init* Event::release_init() {
+ // @@protoc_insertion_point(field_release:webrtc.audioproc.Event.init)
+ _has_bits_[0] &= ~0x00000001u;
+ ::webrtc::audioproc::Init* temp = init_;
+ init_ = nullptr;
+ return temp;
+}
+inline ::webrtc::audioproc::Init* Event::_internal_mutable_init() {
+ _has_bits_[0] |= 0x00000001u;
+ if (init_ == nullptr) {
+ auto* p = CreateMaybeMessage<::webrtc::audioproc::Init>(GetArenaNoVirtual());
+ init_ = p;
+ }
+ return init_;
+}
+inline ::webrtc::audioproc::Init* Event::mutable_init() {
+ // @@protoc_insertion_point(field_mutable:webrtc.audioproc.Event.init)
+ return _internal_mutable_init();
+}
+inline void Event::set_allocated_init(::webrtc::audioproc::Init* init) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual();
+ if (message_arena == nullptr) {
+ delete init_;
+ }
+ if (init) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr;
+ if (message_arena != submessage_arena) {
+ init = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, init, submessage_arena);
+ }
+ _has_bits_[0] |= 0x00000001u;
+ } else {
+ _has_bits_[0] &= ~0x00000001u;
+ }
+ init_ = init;
+ // @@protoc_insertion_point(field_set_allocated:webrtc.audioproc.Event.init)
+}
+
+// optional .webrtc.audioproc.ReverseStream reverse_stream = 3;
+inline bool Event::_internal_has_reverse_stream() const {
+ bool value = (_has_bits_[0] & 0x00000002u) != 0;
+ PROTOBUF_ASSUME(!value || reverse_stream_ != nullptr);
+ return value;
+}
+inline bool Event::has_reverse_stream() const {
+ return _internal_has_reverse_stream();
+}
+inline void Event::clear_reverse_stream() {
+ if (reverse_stream_ != nullptr) reverse_stream_->Clear();
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline const ::webrtc::audioproc::ReverseStream& Event::_internal_reverse_stream() const {
+ const ::webrtc::audioproc::ReverseStream* p = reverse_stream_;
+ return p != nullptr ? *p : *reinterpret_cast<const ::webrtc::audioproc::ReverseStream*>(
+ &::webrtc::audioproc::_ReverseStream_default_instance_);
+}
+inline const ::webrtc::audioproc::ReverseStream& Event::reverse_stream() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Event.reverse_stream)
+ return _internal_reverse_stream();
+}
+inline ::webrtc::audioproc::ReverseStream* Event::release_reverse_stream() {
+ // @@protoc_insertion_point(field_release:webrtc.audioproc.Event.reverse_stream)
+ _has_bits_[0] &= ~0x00000002u;
+ ::webrtc::audioproc::ReverseStream* temp = reverse_stream_;
+ reverse_stream_ = nullptr;
+ return temp;
+}
+inline ::webrtc::audioproc::ReverseStream* Event::_internal_mutable_reverse_stream() {
+ _has_bits_[0] |= 0x00000002u;
+ if (reverse_stream_ == nullptr) {
+ auto* p = CreateMaybeMessage<::webrtc::audioproc::ReverseStream>(GetArenaNoVirtual());
+ reverse_stream_ = p;
+ }
+ return reverse_stream_;
+}
+inline ::webrtc::audioproc::ReverseStream* Event::mutable_reverse_stream() {
+ // @@protoc_insertion_point(field_mutable:webrtc.audioproc.Event.reverse_stream)
+ return _internal_mutable_reverse_stream();
+}
+inline void Event::set_allocated_reverse_stream(::webrtc::audioproc::ReverseStream* reverse_stream) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual();
+ if (message_arena == nullptr) {
+ delete reverse_stream_;
+ }
+ if (reverse_stream) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr;
+ if (message_arena != submessage_arena) {
+ reverse_stream = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, reverse_stream, submessage_arena);
+ }
+ _has_bits_[0] |= 0x00000002u;
+ } else {
+ _has_bits_[0] &= ~0x00000002u;
+ }
+ reverse_stream_ = reverse_stream;
+ // @@protoc_insertion_point(field_set_allocated:webrtc.audioproc.Event.reverse_stream)
+}
+
+// optional .webrtc.audioproc.Stream stream = 4;
+inline bool Event::_internal_has_stream() const {
+ bool value = (_has_bits_[0] & 0x00000004u) != 0;
+ PROTOBUF_ASSUME(!value || stream_ != nullptr);
+ return value;
+}
+inline bool Event::has_stream() const {
+ return _internal_has_stream();
+}
+inline void Event::clear_stream() {
+ if (stream_ != nullptr) stream_->Clear();
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline const ::webrtc::audioproc::Stream& Event::_internal_stream() const {
+ const ::webrtc::audioproc::Stream* p = stream_;
+ return p != nullptr ? *p : *reinterpret_cast<const ::webrtc::audioproc::Stream*>(
+ &::webrtc::audioproc::_Stream_default_instance_);
+}
+inline const ::webrtc::audioproc::Stream& Event::stream() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Event.stream)
+ return _internal_stream();
+}
+inline ::webrtc::audioproc::Stream* Event::release_stream() {
+ // @@protoc_insertion_point(field_release:webrtc.audioproc.Event.stream)
+ _has_bits_[0] &= ~0x00000004u;
+ ::webrtc::audioproc::Stream* temp = stream_;
+ stream_ = nullptr;
+ return temp;
+}
+inline ::webrtc::audioproc::Stream* Event::_internal_mutable_stream() {
+ _has_bits_[0] |= 0x00000004u;
+ if (stream_ == nullptr) {
+ auto* p = CreateMaybeMessage<::webrtc::audioproc::Stream>(GetArenaNoVirtual());
+ stream_ = p;
+ }
+ return stream_;
+}
+inline ::webrtc::audioproc::Stream* Event::mutable_stream() {
+ // @@protoc_insertion_point(field_mutable:webrtc.audioproc.Event.stream)
+ return _internal_mutable_stream();
+}
+inline void Event::set_allocated_stream(::webrtc::audioproc::Stream* stream) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual();
+ if (message_arena == nullptr) {
+ delete stream_;
+ }
+ if (stream) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr;
+ if (message_arena != submessage_arena) {
+ stream = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, stream, submessage_arena);
+ }
+ _has_bits_[0] |= 0x00000004u;
+ } else {
+ _has_bits_[0] &= ~0x00000004u;
+ }
+ stream_ = stream;
+ // @@protoc_insertion_point(field_set_allocated:webrtc.audioproc.Event.stream)
+}
+
+// optional .webrtc.audioproc.Config config = 5;
+inline bool Event::_internal_has_config() const {
+ bool value = (_has_bits_[0] & 0x00000008u) != 0;
+ PROTOBUF_ASSUME(!value || config_ != nullptr);
+ return value;
+}
+inline bool Event::has_config() const {
+ return _internal_has_config();
+}
+inline void Event::clear_config() {
+ if (config_ != nullptr) config_->Clear();
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline const ::webrtc::audioproc::Config& Event::_internal_config() const {
+ const ::webrtc::audioproc::Config* p = config_;
+ return p != nullptr ? *p : *reinterpret_cast<const ::webrtc::audioproc::Config*>(
+ &::webrtc::audioproc::_Config_default_instance_);
+}
+inline const ::webrtc::audioproc::Config& Event::config() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Event.config)
+ return _internal_config();
+}
+inline ::webrtc::audioproc::Config* Event::release_config() {
+ // @@protoc_insertion_point(field_release:webrtc.audioproc.Event.config)
+ _has_bits_[0] &= ~0x00000008u;
+ ::webrtc::audioproc::Config* temp = config_;
+ config_ = nullptr;
+ return temp;
+}
+inline ::webrtc::audioproc::Config* Event::_internal_mutable_config() {
+ _has_bits_[0] |= 0x00000008u;
+ if (config_ == nullptr) {
+ auto* p = CreateMaybeMessage<::webrtc::audioproc::Config>(GetArenaNoVirtual());
+ config_ = p;
+ }
+ return config_;
+}
+inline ::webrtc::audioproc::Config* Event::mutable_config() {
+ // @@protoc_insertion_point(field_mutable:webrtc.audioproc.Event.config)
+ return _internal_mutable_config();
+}
+inline void Event::set_allocated_config(::webrtc::audioproc::Config* config) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual();
+ if (message_arena == nullptr) {
+ delete config_;
+ }
+ if (config) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr;
+ if (message_arena != submessage_arena) {
+ config = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, config, submessage_arena);
+ }
+ _has_bits_[0] |= 0x00000008u;
+ } else {
+ _has_bits_[0] &= ~0x00000008u;
+ }
+ config_ = config;
+ // @@protoc_insertion_point(field_set_allocated:webrtc.audioproc.Event.config)
+}
+
+// optional .webrtc.audioproc.RuntimeSetting runtime_setting = 6;
+inline bool Event::_internal_has_runtime_setting() const {
+ bool value = (_has_bits_[0] & 0x00000010u) != 0;
+ PROTOBUF_ASSUME(!value || runtime_setting_ != nullptr);
+ return value;
+}
+inline bool Event::has_runtime_setting() const {
+ return _internal_has_runtime_setting();
+}
+inline void Event::clear_runtime_setting() {
+ if (runtime_setting_ != nullptr) runtime_setting_->Clear();
+ _has_bits_[0] &= ~0x00000010u;
+}
+inline const ::webrtc::audioproc::RuntimeSetting& Event::_internal_runtime_setting() const {
+ const ::webrtc::audioproc::RuntimeSetting* p = runtime_setting_;
+ return p != nullptr ? *p : *reinterpret_cast<const ::webrtc::audioproc::RuntimeSetting*>(
+ &::webrtc::audioproc::_RuntimeSetting_default_instance_);
+}
+inline const ::webrtc::audioproc::RuntimeSetting& Event::runtime_setting() const {
+ // @@protoc_insertion_point(field_get:webrtc.audioproc.Event.runtime_setting)
+ return _internal_runtime_setting();
+}
+inline ::webrtc::audioproc::RuntimeSetting* Event::release_runtime_setting() {
+ // @@protoc_insertion_point(field_release:webrtc.audioproc.Event.runtime_setting)
+ _has_bits_[0] &= ~0x00000010u;
+ ::webrtc::audioproc::RuntimeSetting* temp = runtime_setting_;
+ runtime_setting_ = nullptr;
+ return temp;
+}
+inline ::webrtc::audioproc::RuntimeSetting* Event::_internal_mutable_runtime_setting() {
+ _has_bits_[0] |= 0x00000010u;
+ if (runtime_setting_ == nullptr) {
+ auto* p = CreateMaybeMessage<::webrtc::audioproc::RuntimeSetting>(GetArenaNoVirtual());
+ runtime_setting_ = p;
+ }
+ return runtime_setting_;
+}
+inline ::webrtc::audioproc::RuntimeSetting* Event::mutable_runtime_setting() {
+ // @@protoc_insertion_point(field_mutable:webrtc.audioproc.Event.runtime_setting)
+ return _internal_mutable_runtime_setting();
+}
+inline void Event::set_allocated_runtime_setting(::webrtc::audioproc::RuntimeSetting* runtime_setting) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual();
+ if (message_arena == nullptr) {
+ delete runtime_setting_;
+ }
+ if (runtime_setting) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr;
+ if (message_arena != submessage_arena) {
+ runtime_setting = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, runtime_setting, submessage_arena);
+ }
+ _has_bits_[0] |= 0x00000010u;
+ } else {
+ _has_bits_[0] &= ~0x00000010u;
+ }
+ runtime_setting_ = runtime_setting;
+ // @@protoc_insertion_point(field_set_allocated:webrtc.audioproc.Event.runtime_setting)
+}
+
+#ifdef __GNUC__
+ #pragma GCC diagnostic pop
+#endif // __GNUC__
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+} // namespace audioproc
+} // namespace webrtc
+
+PROTOBUF_NAMESPACE_OPEN
+
+template <> struct is_proto_enum< ::webrtc::audioproc::Event_Type> : ::std::true_type {};
+
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+
+#include <google/protobuf/port_undef.inc>
+#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_debug_2eproto
diff --git a/modules/audio_processing/debug.proto b/modules/audio_processing/debug.proto
index 07cce23..4bc1a52 100644
--- a/modules/audio_processing/debug.proto
+++ b/modules/audio_processing/debug.proto
@@ -92,6 +92,7 @@
optional int32 playout_volume_change = 4;
optional PlayoutAudioDeviceInfo playout_audio_device_change = 5;
optional bool capture_output_used = 6;
+ optional float capture_post_gain = 7;
}
message Event {
diff --git a/modules/audio_processing/include/aec_dump.h b/modules/audio_processing/include/aec_dump.h
index ed5acb0..a7769d9 100644
--- a/modules/audio_processing/include/aec_dump.h
+++ b/modules/audio_processing/include/aec_dump.h
@@ -15,9 +15,9 @@
#include <string>
+#include "absl/base/attributes.h"
#include "modules/audio_processing/include/audio_frame_view.h"
#include "modules/audio_processing/include/audio_processing.h"
-#include "rtc_base/deprecation.h"
namespace webrtc {
@@ -76,7 +76,8 @@
// Logs Event::Type INIT message.
virtual void WriteInitMessage(const ProcessingConfig& api_format,
int64_t time_now_ms) = 0;
- RTC_DEPRECATED void WriteInitMessage(const ProcessingConfig& api_format) {
+ ABSL_DEPRECATED("")
+ void WriteInitMessage(const ProcessingConfig& api_format) {
WriteInitMessage(api_format, 0);
}
diff --git a/modules/audio_processing/include/audio_processing.cc b/modules/audio_processing/include/audio_processing.cc
index 3bc0075..6e726d9 100644
--- a/modules/audio_processing/include/audio_processing.cc
+++ b/modules/audio_processing/include/audio_processing.cc
@@ -57,14 +57,6 @@
RTC_CHECK_NOTREACHED();
}
-int GetDefaultMaxInternalRate() {
-#ifdef WEBRTC_ARCH_ARM_FAMILY
- return 32000;
-#else
- return 48000;
-#endif
-}
-
} // namespace
constexpr int AudioProcessing::kNativeSampleRatesHz[];
@@ -72,9 +64,6 @@
void CustomProcessing::SetRuntimeSetting(
AudioProcessing::RuntimeSetting setting) {}
-AudioProcessing::Config::Pipeline::Pipeline()
- : maximum_internal_processing_rate(GetDefaultMaxInternalRate()) {}
-
bool Agc1Config::operator==(const Agc1Config& rhs) const {
const auto& analog_lhs = analog_gain_controller;
const auto& analog_rhs = rhs.analog_gain_controller;
@@ -87,8 +76,6 @@
analog_lhs.enabled == analog_rhs.enabled &&
analog_lhs.startup_min_volume == analog_rhs.startup_min_volume &&
analog_lhs.clipped_level_min == analog_rhs.clipped_level_min &&
- analog_lhs.enable_agc2_level_estimator ==
- analog_rhs.enable_agc2_level_estimator &&
analog_lhs.enable_digital_adaptive ==
analog_rhs.enable_digital_adaptive;
}
@@ -119,63 +106,86 @@
adaptive_rhs.max_output_noise_level_dbfs;
}
+bool AudioProcessing::Config::CaptureLevelAdjustment::operator==(
+ const AudioProcessing::Config::CaptureLevelAdjustment& rhs) const {
+ return enabled == rhs.enabled && pre_gain_factor == rhs.pre_gain_factor &&
+ post_gain_factor && rhs.post_gain_factor &&
+ analog_mic_gain_emulation == rhs.analog_mic_gain_emulation;
+}
+
+bool AudioProcessing::Config::CaptureLevelAdjustment::AnalogMicGainEmulation::
+operator==(const AudioProcessing::Config::CaptureLevelAdjustment::
+ AnalogMicGainEmulation& rhs) const {
+ return enabled == rhs.enabled && initial_level == rhs.initial_level;
+}
+
std::string AudioProcessing::Config::ToString() const {
char buf[2048];
rtc::SimpleStringBuilder builder(buf);
- builder << "AudioProcessing::Config{ "
- "pipeline: { "
- "maximum_internal_processing_rate: "
- << pipeline.maximum_internal_processing_rate
- << ", multi_channel_render: " << pipeline.multi_channel_render
- << ", multi_channel_capture: " << pipeline.multi_channel_capture
- << " }, pre_amplifier: { enabled: " << pre_amplifier.enabled
- << ", fixed_gain_factor: " << pre_amplifier.fixed_gain_factor
- << " }, high_pass_filter: { enabled: " << high_pass_filter.enabled
- << " }, echo_canceller: { enabled: " << echo_canceller.enabled
- << ", mobile_mode: " << echo_canceller.mobile_mode
- << ", enforce_high_pass_filtering: "
- << echo_canceller.enforce_high_pass_filtering
- << " }, noise_suppression: { enabled: " << noise_suppression.enabled
- << ", level: "
- << NoiseSuppressionLevelToString(noise_suppression.level)
- << " }, transient_suppression: { enabled: "
- << transient_suppression.enabled
- << " }, voice_detection: { enabled: " << voice_detection.enabled
- << " }, gain_controller1: { enabled: " << gain_controller1.enabled
- << ", mode: " << GainController1ModeToString(gain_controller1.mode)
- << ", target_level_dbfs: " << gain_controller1.target_level_dbfs
- << ", compression_gain_db: " << gain_controller1.compression_gain_db
- << ", enable_limiter: " << gain_controller1.enable_limiter
- << ", analog_level_minimum: " << gain_controller1.analog_level_minimum
- << ", analog_level_maximum: " << gain_controller1.analog_level_maximum
- << " }, gain_controller2: { enabled: " << gain_controller2.enabled
- << ", fixed_digital: { gain_db: "
- << gain_controller2.fixed_digital.gain_db
- << " }, adaptive_digital: { enabled: "
- << gain_controller2.adaptive_digital.enabled
- << ", level_estimator: { vad_probability_attack: "
- << gain_controller2.adaptive_digital.vad_probability_attack
- << ", type: "
- << GainController2LevelEstimatorToString(
- gain_controller2.adaptive_digital.level_estimator)
- << ", adjacent_speech_frames_threshold: "
- << gain_controller2.adaptive_digital
- .level_estimator_adjacent_speech_frames_threshold
- << ", initial_saturation_margin_db: "
- << gain_controller2.adaptive_digital.initial_saturation_margin_db
- << ", extra_saturation_margin_db: "
- << gain_controller2.adaptive_digital.extra_saturation_margin_db
- << " }, gain_applier: { adjacent_speech_frames_threshold: "
- << gain_controller2.adaptive_digital
- .gain_applier_adjacent_speech_frames_threshold
- << ", max_gain_change_db_per_second: "
- << gain_controller2.adaptive_digital.max_gain_change_db_per_second
- << ", max_output_noise_level_dbfs: "
- << gain_controller2.adaptive_digital.max_output_noise_level_dbfs
- << " }}}, residual_echo_detector: { enabled: "
- << residual_echo_detector.enabled
- << " }, level_estimation: { enabled: " << level_estimation.enabled
- << " }}";
+ builder
+ << "AudioProcessing::Config{ "
+ "pipeline: { "
+ "maximum_internal_processing_rate: "
+ << pipeline.maximum_internal_processing_rate
+ << ", multi_channel_render: " << pipeline.multi_channel_render
+ << ", multi_channel_capture: " << pipeline.multi_channel_capture
+ << " }, pre_amplifier: { enabled: " << pre_amplifier.enabled
+ << ", fixed_gain_factor: " << pre_amplifier.fixed_gain_factor
+ << " },capture_level_adjustment: { enabled: "
+ << capture_level_adjustment.enabled
+ << ", pre_gain_factor: " << capture_level_adjustment.pre_gain_factor
+ << ", post_gain_factor: " << capture_level_adjustment.post_gain_factor
+ << ", analog_mic_gain_emulation: { enabled: "
+ << capture_level_adjustment.analog_mic_gain_emulation.enabled
+ << ", initial_level: "
+ << capture_level_adjustment.analog_mic_gain_emulation.initial_level
+ << " }}, high_pass_filter: { enabled: " << high_pass_filter.enabled
+ << " }, echo_canceller: { enabled: " << echo_canceller.enabled
+ << ", mobile_mode: " << echo_canceller.mobile_mode
+ << ", enforce_high_pass_filtering: "
+ << echo_canceller.enforce_high_pass_filtering
+ << " }, noise_suppression: { enabled: " << noise_suppression.enabled
+ << ", level: " << NoiseSuppressionLevelToString(noise_suppression.level)
+ << " }, transient_suppression: { enabled: "
+ << transient_suppression.enabled
+ << " }, voice_detection: { enabled: " << voice_detection.enabled
+ << " }, gain_controller1: { enabled: " << gain_controller1.enabled
+ << ", mode: " << GainController1ModeToString(gain_controller1.mode)
+ << ", target_level_dbfs: " << gain_controller1.target_level_dbfs
+ << ", compression_gain_db: " << gain_controller1.compression_gain_db
+ << ", enable_limiter: " << gain_controller1.enable_limiter
+ << ", analog_level_minimum: " << gain_controller1.analog_level_minimum
+ << ", analog_level_maximum: " << gain_controller1.analog_level_maximum
+ << " }, gain_controller2: { enabled: " << gain_controller2.enabled
+ << ", fixed_digital: { gain_db: "
+ << gain_controller2.fixed_digital.gain_db
+ << " }, adaptive_digital: { enabled: "
+ << gain_controller2.adaptive_digital.enabled
+ << ", level_estimator: { vad_probability_attack: "
+ << gain_controller2.adaptive_digital.vad_probability_attack << ", type: "
+ << GainController2LevelEstimatorToString(
+ gain_controller2.adaptive_digital.level_estimator)
+ << ", adjacent_speech_frames_threshold: "
+ << gain_controller2.adaptive_digital
+ .level_estimator_adjacent_speech_frames_threshold
+ << ", initial_saturation_margin_db: "
+ << gain_controller2.adaptive_digital.initial_saturation_margin_db
+ << ", extra_saturation_margin_db: "
+ << gain_controller2.adaptive_digital.extra_saturation_margin_db
+ << " }, gain_applier: { adjacent_speech_frames_threshold: "
+ << gain_controller2.adaptive_digital
+ .gain_applier_adjacent_speech_frames_threshold
+ << ", max_gain_change_db_per_second: "
+ << gain_controller2.adaptive_digital.max_gain_change_db_per_second
+ << ", max_output_noise_level_dbfs: "
+ << gain_controller2.adaptive_digital.max_output_noise_level_dbfs
+ << ", sse2_allowed: " << gain_controller2.adaptive_digital.sse2_allowed
+ << ", avx2_allowed: " << gain_controller2.adaptive_digital.avx2_allowed
+ << ", neon_allowed: " << gain_controller2.adaptive_digital.neon_allowed
+ << " }}}, residual_echo_detector: { enabled: "
+ << residual_echo_detector.enabled
+ << " }, level_estimation: { enabled: " << level_estimation.enabled
+ << " }}";
return builder.str();
}
diff --git a/modules/audio_processing/include/audio_processing.h b/modules/audio_processing/include/audio_processing.h
index 942e0c0..bb24a48 100644
--- a/modules/audio_processing/include/audio_processing.h
+++ b/modules/audio_processing/include/audio_processing.h
@@ -32,7 +32,6 @@
#include "modules/audio_processing/include/config.h"
#include "rtc_base/arraysize.h"
#include "rtc_base/constructor_magic.h"
-#include "rtc_base/deprecation.h"
#include "rtc_base/ref_count.h"
#include "rtc_base/system/file_wrapper.h"
#include "rtc_base/system/rtc_export.h"
@@ -72,32 +71,13 @@
struct ExperimentalAgc {
ExperimentalAgc() = default;
explicit ExperimentalAgc(bool enabled) : enabled(enabled) {}
- ExperimentalAgc(bool enabled,
- bool enabled_agc2_level_estimator,
- bool digital_adaptive_disabled)
- : enabled(enabled),
- enabled_agc2_level_estimator(enabled_agc2_level_estimator),
- digital_adaptive_disabled(digital_adaptive_disabled) {}
- // Deprecated constructor: will be removed.
- ExperimentalAgc(bool enabled,
- bool enabled_agc2_level_estimator,
- bool digital_adaptive_disabled,
- bool analyze_before_aec)
- : enabled(enabled),
- enabled_agc2_level_estimator(enabled_agc2_level_estimator),
- digital_adaptive_disabled(digital_adaptive_disabled) {}
ExperimentalAgc(bool enabled, int startup_min_volume)
: enabled(enabled), startup_min_volume(startup_min_volume) {}
- ExperimentalAgc(bool enabled, int startup_min_volume, int clipped_level_min)
- : enabled(enabled),
- startup_min_volume(startup_min_volume),
- clipped_level_min(clipped_level_min) {}
static const ConfigOptionID identifier = ConfigOptionID::kExperimentalAgc;
bool enabled = true;
int startup_min_volume = kAgcStartupMinVolume;
// Lowest microphone level that will be applied in response to clipping.
int clipped_level_min = kClippedLevelMin;
- bool enabled_agc2_level_estimator = false;
bool digital_adaptive_disabled = false;
};
@@ -214,13 +194,9 @@
// Sets the properties of the audio processing pipeline.
struct RTC_EXPORT Pipeline {
- Pipeline();
-
// Maximum allowed processing rate used internally. May only be set to
- // 32000 or 48000 and any differing values will be treated as 48000. The
- // default rate is currently selected based on the CPU architecture, but
- // that logic may change.
- int maximum_internal_processing_rate;
+ // 32000 or 48000 and any differing values will be treated as 48000.
+ int maximum_internal_processing_rate = 48000;
// Allow multi-channel processing of render audio.
bool multi_channel_render = false;
// Allow multi-channel processing of capture audio when AEC3 is active
@@ -230,11 +206,37 @@
// Enabled the pre-amplifier. It amplifies the capture signal
// before any other processing is done.
+ // TODO(webrtc:5298): Deprecate and use the pre-gain functionality in
+ // capture_level_adjustment instead.
struct PreAmplifier {
bool enabled = false;
float fixed_gain_factor = 1.f;
} pre_amplifier;
+ // Functionality for general level adjustment in the capture pipeline. This
+ // should not be used together with the legacy PreAmplifier functionality.
+ struct CaptureLevelAdjustment {
+ bool operator==(const CaptureLevelAdjustment& rhs) const;
+ bool operator!=(const CaptureLevelAdjustment& rhs) const {
+ return !(*this == rhs);
+ }
+ bool enabled = false;
+ // The `pre_gain_factor` scales the signal before any processing is done.
+ float pre_gain_factor = 1.f;
+ // The `post_gain_factor` scales the signal after all processing is done.
+ float post_gain_factor = 1.f;
+ struct AnalogMicGainEmulation {
+ bool operator==(const AnalogMicGainEmulation& rhs) const;
+ bool operator!=(const AnalogMicGainEmulation& rhs) const {
+ return !(*this == rhs);
+ }
+ bool enabled = false;
+ // Initial analog gain level to use for the emulated analog gain. Must
+ // be in the range [0...255].
+ int initial_level = 255;
+ } analog_mic_gain_emulation;
+ } capture_level_adjustment;
+
struct HighPassFilter {
bool enabled = false;
bool apply_in_full_band = true;
@@ -331,7 +333,6 @@
// Lowest analog microphone level that will be applied in response to
// clipping.
int clipped_level_min = kClippedLevelMin;
- bool enable_agc2_level_estimator = false;
bool enable_digital_adaptive = true;
} analog_gain_controller;
} gain_controller1;
@@ -355,16 +356,16 @@
} fixed_digital;
struct AdaptiveDigital {
bool enabled = false;
- float vad_probability_attack = 1.f;
+ float vad_probability_attack = 0.3f;
LevelEstimator level_estimator = kRms;
- int level_estimator_adjacent_speech_frames_threshold = 1;
+ int level_estimator_adjacent_speech_frames_threshold = 6;
// TODO(crbug.com/webrtc/7494): Remove `use_saturation_protector`.
bool use_saturation_protector = true;
float initial_saturation_margin_db = 20.f;
- float extra_saturation_margin_db = 2.f;
- int gain_applier_adjacent_speech_frames_threshold = 1;
+ float extra_saturation_margin_db = 5.f;
+ int gain_applier_adjacent_speech_frames_threshold = 6;
float max_gain_change_db_per_second = 3.f;
- float max_output_noise_level_dbfs = -50.f;
+ float max_output_noise_level_dbfs = -55.f;
bool sse2_allowed = true;
bool avx2_allowed = true;
bool neon_allowed = true;
@@ -406,6 +407,7 @@
kPlayoutVolumeChange,
kCustomRenderProcessingRuntimeSetting,
kPlayoutAudioDeviceChange,
+ kCapturePostGain,
kCaptureOutputUsed
};
@@ -419,10 +421,13 @@
~RuntimeSetting() = default;
static RuntimeSetting CreateCapturePreGain(float gain) {
- RTC_DCHECK_GE(gain, 1.f) << "Attenuation is not allowed.";
return {Type::kCapturePreGain, gain};
}
+ static RuntimeSetting CreateCapturePostGain(float gain) {
+ return {Type::kCapturePostGain, gain};
+ }
+
// Corresponds to Config::GainController1::compression_gain_db, but for
// runtime configuration.
static RuntimeSetting CreateCompressionGainDb(int gain_db) {
@@ -456,8 +461,9 @@
return {Type::kCustomRenderProcessingRuntimeSetting, payload};
}
- static RuntimeSetting CreateCaptureOutputUsedSetting(bool payload) {
- return {Type::kCaptureOutputUsed, payload};
+ static RuntimeSetting CreateCaptureOutputUsedSetting(
+ bool capture_output_used) {
+ return {Type::kCaptureOutputUsed, capture_output_used};
}
Type type() const { return type_; }
@@ -549,12 +555,17 @@
// Set to true when the output of AudioProcessing will be muted or in some
// other way not used. Ideally, the captured audio would still be processed,
// but some components may change behavior based on this information.
- // Default false.
+ // Default false. This method takes a lock. To achieve this in a lock-less
+ // manner the PostRuntimeSetting can instead be used.
virtual void set_output_will_be_muted(bool muted) = 0;
- // Enqueue a runtime setting.
+ // Enqueues a runtime setting.
virtual void SetRuntimeSetting(RuntimeSetting setting) = 0;
+ // Enqueues a runtime setting. Returns a bool indicating whether the
+ // enqueueing was successfull.
+ virtual bool PostRuntimeSetting(RuntimeSetting setting) = 0;
+
// Accepts and produces a 10 ms frame interleaved 16 bit integer audio as
// specified in |input_config| and |output_config|. |src| and |dest| may use
// the same memory, if desired.
diff --git a/modules/audio_processing/include/mock_audio_processing.h b/modules/audio_processing/include/mock_audio_processing.h
index db9ab97..46c5f0e 100644
--- a/modules/audio_processing/include/mock_audio_processing.h
+++ b/modules/audio_processing/include/mock_audio_processing.h
@@ -96,6 +96,7 @@
MOCK_METHOD(size_t, num_reverse_channels, (), (const, override));
MOCK_METHOD(void, set_output_will_be_muted, (bool muted), (override));
MOCK_METHOD(void, SetRuntimeSetting, (RuntimeSetting setting), (override));
+ MOCK_METHOD(bool, PostRuntimeSetting, (RuntimeSetting setting), (override));
MOCK_METHOD(int,
ProcessStream,
(const int16_t* const src,
diff --git a/modules/audio_processing/logging/apm_data_dumper.cc b/modules/audio_processing/logging/apm_data_dumper.cc
index 917df60..445248b 100644
--- a/modules/audio_processing/logging/apm_data_dumper.cc
+++ b/modules/audio_processing/logging/apm_data_dumper.cc
@@ -61,6 +61,7 @@
#if WEBRTC_APM_DEBUG_DUMP == 1
bool ApmDataDumper::recording_activated_ = false;
+absl::optional<int> ApmDataDumper::dump_set_to_use_;
char ApmDataDumper::output_dir_[] = "";
FILE* ApmDataDumper::GetRawFile(const char* name) {
diff --git a/modules/audio_processing/logging/apm_data_dumper.h b/modules/audio_processing/logging/apm_data_dumper.h
index 1824fdd..6d32b32 100644
--- a/modules/audio_processing/logging/apm_data_dumper.h
+++ b/modules/audio_processing/logging/apm_data_dumper.h
@@ -21,6 +21,7 @@
#include <unordered_map>
#endif
+#include "absl/types/optional.h"
#include "api/array_view.h"
#if WEBRTC_APM_DEBUG_DUMP == 1
#include "common_audio/wav_file.h"
@@ -64,6 +65,18 @@
#endif
}
+ // Default dump set.
+ static constexpr size_t kDefaultDumpSet = 0;
+
+ // Specifies what dump set to use. All dump commands with a different dump set
+ // than the one specified will be discarded. If not specificed, all dump sets
+ // will be used.
+ static void SetDumpSetToUse(int dump_set_to_use) {
+#if WEBRTC_APM_DEBUG_DUMP == 1
+ dump_set_to_use_ = dump_set_to_use;
+#endif
+ }
+
// Set an optional output directory.
static void SetOutputDirectory(const std::string& output_dir) {
#if WEBRTC_APM_DEBUG_DUMP == 1
@@ -82,8 +95,11 @@
// Methods for performing dumping of data of various types into
// various formats.
- void DumpRaw(const char* name, double v) {
+ void DumpRaw(const char* name, double v, int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
FILE* file = GetRawFile(name);
fwrite(&v, sizeof(v), 1, file);
@@ -91,8 +107,14 @@
#endif
}
- void DumpRaw(const char* name, size_t v_length, const double* v) {
+ void DumpRaw(const char* name,
+ size_t v_length,
+ const double* v,
+ int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
FILE* file = GetRawFile(name);
fwrite(v, sizeof(v[0]), v_length, file);
@@ -100,16 +122,24 @@
#endif
}
- void DumpRaw(const char* name, rtc::ArrayView<const double> v) {
+ void DumpRaw(const char* name,
+ rtc::ArrayView<const double> v,
+ int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
DumpRaw(name, v.size(), v.data());
}
#endif
}
- void DumpRaw(const char* name, float v) {
+ void DumpRaw(const char* name, float v, int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
FILE* file = GetRawFile(name);
fwrite(&v, sizeof(v), 1, file);
@@ -117,8 +147,14 @@
#endif
}
- void DumpRaw(const char* name, size_t v_length, const float* v) {
+ void DumpRaw(const char* name,
+ size_t v_length,
+ const float* v,
+ int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
FILE* file = GetRawFile(name);
fwrite(v, sizeof(v[0]), v_length, file);
@@ -126,24 +162,38 @@
#endif
}
- void DumpRaw(const char* name, rtc::ArrayView<const float> v) {
+ void DumpRaw(const char* name,
+ rtc::ArrayView<const float> v,
+ int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
DumpRaw(name, v.size(), v.data());
}
#endif
}
- void DumpRaw(const char* name, bool v) {
+ void DumpRaw(const char* name, bool v, int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
DumpRaw(name, static_cast<int16_t>(v));
}
#endif
}
- void DumpRaw(const char* name, size_t v_length, const bool* v) {
+ void DumpRaw(const char* name,
+ size_t v_length,
+ const bool* v,
+ int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
FILE* file = GetRawFile(name);
for (size_t k = 0; k < v_length; ++k) {
@@ -154,16 +204,24 @@
#endif
}
- void DumpRaw(const char* name, rtc::ArrayView<const bool> v) {
+ void DumpRaw(const char* name,
+ rtc::ArrayView<const bool> v,
+ int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
DumpRaw(name, v.size(), v.data());
}
#endif
}
- void DumpRaw(const char* name, int16_t v) {
+ void DumpRaw(const char* name, int16_t v, int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
FILE* file = GetRawFile(name);
fwrite(&v, sizeof(v), 1, file);
@@ -171,8 +229,14 @@
#endif
}
- void DumpRaw(const char* name, size_t v_length, const int16_t* v) {
+ void DumpRaw(const char* name,
+ size_t v_length,
+ const int16_t* v,
+ int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
FILE* file = GetRawFile(name);
fwrite(v, sizeof(v[0]), v_length, file);
@@ -180,16 +244,24 @@
#endif
}
- void DumpRaw(const char* name, rtc::ArrayView<const int16_t> v) {
+ void DumpRaw(const char* name,
+ rtc::ArrayView<const int16_t> v,
+ int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
DumpRaw(name, v.size(), v.data());
}
#endif
}
- void DumpRaw(const char* name, int32_t v) {
+ void DumpRaw(const char* name, int32_t v, int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
FILE* file = GetRawFile(name);
fwrite(&v, sizeof(v), 1, file);
@@ -197,8 +269,14 @@
#endif
}
- void DumpRaw(const char* name, size_t v_length, const int32_t* v) {
+ void DumpRaw(const char* name,
+ size_t v_length,
+ const int32_t* v,
+ int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
FILE* file = GetRawFile(name);
fwrite(v, sizeof(v[0]), v_length, file);
@@ -206,8 +284,11 @@
#endif
}
- void DumpRaw(const char* name, size_t v) {
+ void DumpRaw(const char* name, size_t v, int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
FILE* file = GetRawFile(name);
fwrite(&v, sizeof(v), 1, file);
@@ -215,8 +296,14 @@
#endif
}
- void DumpRaw(const char* name, size_t v_length, const size_t* v) {
+ void DumpRaw(const char* name,
+ size_t v_length,
+ const size_t* v,
+ int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
FILE* file = GetRawFile(name);
fwrite(v, sizeof(v[0]), v_length, file);
@@ -224,16 +311,26 @@
#endif
}
- void DumpRaw(const char* name, rtc::ArrayView<const int32_t> v) {
+ void DumpRaw(const char* name,
+ rtc::ArrayView<const int32_t> v,
+ int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
DumpRaw(name, v.size(), v.data());
}
#endif
}
- void DumpRaw(const char* name, rtc::ArrayView<const size_t> v) {
+ void DumpRaw(const char* name,
+ rtc::ArrayView<const size_t> v,
+ int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
DumpRaw(name, v.size(), v.data());
#endif
}
@@ -242,8 +339,12 @@
size_t v_length,
const float* v,
int sample_rate_hz,
- int num_channels) {
+ int num_channels,
+ int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
WavWriter* file = GetWavFile(name, sample_rate_hz, num_channels,
WavFile::SampleFormat::kFloat);
@@ -255,8 +356,12 @@
void DumpWav(const char* name,
rtc::ArrayView<const float> v,
int sample_rate_hz,
- int num_channels) {
+ int num_channels,
+ int dump_set = kDefaultDumpSet) {
#if WEBRTC_APM_DEBUG_DUMP == 1
+ if (dump_set_to_use_ && *dump_set_to_use_ != dump_set)
+ return;
+
if (recording_activated_) {
DumpWav(name, v.size(), v.data(), sample_rate_hz, num_channels);
}
@@ -266,6 +371,7 @@
private:
#if WEBRTC_APM_DEBUG_DUMP == 1
static bool recording_activated_;
+ static absl::optional<int> dump_set_to_use_;
static constexpr size_t kOutputDirMaxLength = 1024;
static char output_dir_[kOutputDirMaxLength];
const int instance_index_;
diff --git a/modules/audio_processing/module.mk b/modules/audio_processing/module.mk
index 51312e1..a218cc4 100644
--- a/modules/audio_processing/module.mk
+++ b/modules/audio_processing/module.mk
@@ -87,7 +87,6 @@
modules/audio_processing/agc2/adaptive_agc.o \
modules/audio_processing/agc2/adaptive_digital_gain_applier.o \
modules/audio_processing/agc2/adaptive_mode_level_estimator.o \
- modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.o \
modules/audio_processing/agc2/biquad_filter.o \
modules/audio_processing/agc2/compute_interpolated_gain_curve.o \
modules/audio_processing/agc2/down_sampler.o \
@@ -144,6 +143,10 @@
modules/audio_processing/ns/suppression_params.o \
modules/audio_processing/ns/wiener_filter.o
+capture_levels_adjuster_CXX_OBJECTS = \
+ modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.o \
+ modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.o
+
# TODO(hychao): WEBRTC_NS_FLOAT=0 for cases prefer fixed point operation
CXX_STATIC_LIBRARY(libaudio_processing.pic.a): \
CPPFLAGS += -I. -DWEBRTC_POSIX \
@@ -165,7 +168,8 @@
$(audio_frame_api_CXX_OBJECTS) \
$(aec3_factory_CXX_OBJECTS) \
$(aec3_config_CXX_OBJECTS) \
- $(ns_CXX_OBJECTS)
+ $(ns_CXX_OBJECTS) \
+ $(capture_levels_adjuster_CXX_OBJECTS)
module/libaudio_processing: CXX_STATIC_LIBRARY(libaudio_processing.pic.a)
diff --git a/modules/audio_processing/ns/BUILD.gn b/modules/audio_processing/ns/BUILD.gn
index f0842c5..eb99c77 100644
--- a/modules/audio_processing/ns/BUILD.gn
+++ b/modules/audio_processing/ns/BUILD.gn
@@ -80,7 +80,6 @@
"..:apm_logging",
"..:audio_buffer",
"..:audio_processing",
- "..:audio_processing_unittests",
"..:high_pass_filter",
"../../../api:array_view",
"../../../rtc_base:checks",
@@ -98,5 +97,9 @@
if (rtc_enable_protobuf) {
sources += []
}
+
+ if (!build_with_chromium) {
+ deps += [ "..:audio_processing_unittests" ]
+ }
}
}
diff --git a/modules/audio_processing/ns/noise_suppressor.cc b/modules/audio_processing/ns/noise_suppressor.cc
index 89e1fe0..d66faa6 100644
--- a/modules/audio_processing/ns/noise_suppressor.cc
+++ b/modules/audio_processing/ns/noise_suppressor.cc
@@ -448,6 +448,12 @@
}
}
+ // Only do the below processing if the output of the audio processing module
+ // is used.
+ if (!capture_output_used_) {
+ return;
+ }
+
// Aggregate the Wiener filters for all channels.
std::array<float, kFftSizeBy2Plus1> filter_data;
rtc::ArrayView<const float, kFftSizeBy2Plus1> filter = filter_data;
diff --git a/modules/audio_processing/ns/noise_suppressor.h b/modules/audio_processing/ns/noise_suppressor.h
index d962886..1e321cf 100644
--- a/modules/audio_processing/ns/noise_suppressor.h
+++ b/modules/audio_processing/ns/noise_suppressor.h
@@ -41,12 +41,21 @@
// Applies noise suppression.
void Process(AudioBuffer* audio);
+ // Specifies whether the capture output will be used. The purpose of this is
+ // to allow the noise suppressor to deactivate some of the processing when the
+ // resulting output is anyway not used, for instance when the endpoint is
+ // muted.
+ void SetCaptureOutputUsage(bool capture_output_used) {
+ capture_output_used_ = capture_output_used;
+ }
+
private:
const size_t num_bands_;
const size_t num_channels_;
const SuppressionParams suppression_params_;
int32_t num_analyzed_frames_ = -1;
NrFft fft_;
+ bool capture_output_used_ = true;
struct ChannelState {
ChannelState(const SuppressionParams& suppression_params, size_t num_bands);
diff --git a/modules/audio_processing/test/aec_dump_based_simulator.cc b/modules/audio_processing/test/aec_dump_based_simulator.cc
new file mode 100644
index 0000000..40fae5c
--- /dev/null
+++ b/modules/audio_processing/test/aec_dump_based_simulator.cc
@@ -0,0 +1,656 @@
+/*
+ * 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 "modules/audio_processing/test/aec_dump_based_simulator.h"
+
+#include <iostream>
+#include <memory>
+
+#include "modules/audio_processing/echo_control_mobile_impl.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "modules/audio_processing/test/aec_dump_based_simulator.h"
+#include "modules/audio_processing/test/protobuf_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/numerics/safe_conversions.h"
+
+namespace webrtc {
+namespace test {
+namespace {
+
+// Verify output bitexactness for the fixed interface.
+// TODO(peah): Check whether it would make sense to add a threshold
+// to use for checking the bitexactness in a soft manner.
+bool VerifyFixedBitExactness(const webrtc::audioproc::Stream& msg,
+ const Int16Frame& frame) {
+ if (sizeof(frame.data[0]) * frame.data.size() != msg.output_data().size()) {
+ return false;
+ } else {
+ const int16_t* frame_data = frame.data.data();
+ for (int k = 0; k < frame.num_channels * frame.samples_per_channel; ++k) {
+ if (msg.output_data().data()[k] != frame_data[k]) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// Verify output bitexactness for the float interface.
+bool VerifyFloatBitExactness(const webrtc::audioproc::Stream& msg,
+ const StreamConfig& out_config,
+ const ChannelBuffer<float>& out_buf) {
+ if (static_cast<size_t>(msg.output_channel_size()) !=
+ out_config.num_channels() ||
+ msg.output_channel(0).size() != out_config.num_frames()) {
+ return false;
+ } else {
+ for (int ch = 0; ch < msg.output_channel_size(); ++ch) {
+ for (size_t sample = 0; sample < out_config.num_frames(); ++sample) {
+ if (msg.output_channel(ch).data()[sample] !=
+ out_buf.channels()[ch][sample]) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+// Selectively reads the next proto-buf message from dump-file or string input.
+// Returns a bool indicating whether a new message was available.
+bool ReadNextMessage(bool use_dump_file,
+ FILE* dump_input_file,
+ std::stringstream& input,
+ webrtc::audioproc::Event& event_msg) {
+ if (use_dump_file) {
+ return ReadMessageFromFile(dump_input_file, &event_msg);
+ }
+ return ReadMessageFromString(&input, &event_msg);
+}
+
+} // namespace
+
+AecDumpBasedSimulator::AecDumpBasedSimulator(
+ const SimulationSettings& settings,
+ rtc::scoped_refptr<AudioProcessing> audio_processing,
+ std::unique_ptr<AudioProcessingBuilder> ap_builder)
+ : AudioProcessingSimulator(settings,
+ std::move(audio_processing),
+ std::move(ap_builder)) {
+ MaybeOpenCallOrderFile();
+}
+
+AecDumpBasedSimulator::~AecDumpBasedSimulator() = default;
+
+void AecDumpBasedSimulator::PrepareProcessStreamCall(
+ const webrtc::audioproc::Stream& msg) {
+ if (msg.has_input_data()) {
+ // Fixed interface processing.
+ // Verify interface invariance.
+ RTC_CHECK(interface_used_ == InterfaceType::kFixedInterface ||
+ interface_used_ == InterfaceType::kNotSpecified);
+ interface_used_ = InterfaceType::kFixedInterface;
+
+ // Populate input buffer.
+ RTC_CHECK_EQ(sizeof(fwd_frame_.data[0]) * fwd_frame_.data.size(),
+ msg.input_data().size());
+ memcpy(fwd_frame_.data.data(), msg.input_data().data(),
+ msg.input_data().size());
+ } else {
+ // Float interface processing.
+ // Verify interface invariance.
+ RTC_CHECK(interface_used_ == InterfaceType::kFloatInterface ||
+ interface_used_ == InterfaceType::kNotSpecified);
+ interface_used_ = InterfaceType::kFloatInterface;
+
+ RTC_CHECK_EQ(in_buf_->num_channels(),
+ static_cast<size_t>(msg.input_channel_size()));
+
+ // Populate input buffer.
+ for (size_t i = 0; i < in_buf_->num_channels(); ++i) {
+ RTC_CHECK_EQ(in_buf_->num_frames() * sizeof(*in_buf_->channels()[i]),
+ msg.input_channel(i).size());
+ std::memcpy(in_buf_->channels()[i], msg.input_channel(i).data(),
+ msg.input_channel(i).size());
+ }
+ }
+
+ if (artificial_nearend_buffer_reader_) {
+ if (artificial_nearend_buffer_reader_->Read(
+ artificial_nearend_buf_.get())) {
+ if (msg.has_input_data()) {
+ int16_t* fwd_frame_data = fwd_frame_.data.data();
+ for (size_t k = 0; k < in_buf_->num_frames(); ++k) {
+ fwd_frame_data[k] = rtc::saturated_cast<int16_t>(
+ fwd_frame_data[k] +
+ static_cast<int16_t>(32767 *
+ artificial_nearend_buf_->channels()[0][k]));
+ }
+ } else {
+ for (int i = 0; i < msg.input_channel_size(); ++i) {
+ for (size_t k = 0; k < in_buf_->num_frames(); ++k) {
+ in_buf_->channels()[i][k] +=
+ artificial_nearend_buf_->channels()[0][k];
+ in_buf_->channels()[i][k] = std::min(
+ 32767.f, std::max(-32768.f, in_buf_->channels()[i][k]));
+ }
+ }
+ }
+ } else {
+ if (!artificial_nearend_eof_reported_) {
+ std::cout << "The artificial nearend file ended before the recording.";
+ artificial_nearend_eof_reported_ = true;
+ }
+ }
+ }
+
+ if (!settings_.use_stream_delay || *settings_.use_stream_delay) {
+ if (!settings_.stream_delay) {
+ if (msg.has_delay()) {
+ RTC_CHECK_EQ(AudioProcessing::kNoError,
+ ap_->set_stream_delay_ms(msg.delay()));
+ }
+ } else {
+ RTC_CHECK_EQ(AudioProcessing::kNoError,
+ ap_->set_stream_delay_ms(*settings_.stream_delay));
+ }
+ }
+
+ if (!settings_.use_ts) {
+ if (msg.has_keypress()) {
+ ap_->set_stream_key_pressed(msg.keypress());
+ }
+ } else {
+ ap_->set_stream_key_pressed(*settings_.use_ts);
+ }
+
+ // Level is always logged in AEC dumps.
+ RTC_CHECK(msg.has_level());
+ aec_dump_mic_level_ = msg.level();
+}
+
+void AecDumpBasedSimulator::VerifyProcessStreamBitExactness(
+ const webrtc::audioproc::Stream& msg) {
+ if (bitexact_output_) {
+ if (interface_used_ == InterfaceType::kFixedInterface) {
+ bitexact_output_ = VerifyFixedBitExactness(msg, fwd_frame_);
+ } else {
+ bitexact_output_ = VerifyFloatBitExactness(msg, out_config_, *out_buf_);
+ }
+ }
+}
+
+void AecDumpBasedSimulator::PrepareReverseProcessStreamCall(
+ const webrtc::audioproc::ReverseStream& msg) {
+ if (msg.has_data()) {
+ // Fixed interface processing.
+ // Verify interface invariance.
+ RTC_CHECK(interface_used_ == InterfaceType::kFixedInterface ||
+ interface_used_ == InterfaceType::kNotSpecified);
+ interface_used_ = InterfaceType::kFixedInterface;
+
+ // Populate input buffer.
+ RTC_CHECK_EQ(sizeof(rev_frame_.data[0]) * rev_frame_.data.size(),
+ msg.data().size());
+ memcpy(rev_frame_.data.data(), msg.data().data(), msg.data().size());
+ } else {
+ // Float interface processing.
+ // Verify interface invariance.
+ RTC_CHECK(interface_used_ == InterfaceType::kFloatInterface ||
+ interface_used_ == InterfaceType::kNotSpecified);
+ interface_used_ = InterfaceType::kFloatInterface;
+
+ RTC_CHECK_EQ(reverse_in_buf_->num_channels(),
+ static_cast<size_t>(msg.channel_size()));
+
+ // Populate input buffer.
+ for (int i = 0; i < msg.channel_size(); ++i) {
+ RTC_CHECK_EQ(reverse_in_buf_->num_frames() *
+ sizeof(*reverse_in_buf_->channels()[i]),
+ msg.channel(i).size());
+ std::memcpy(reverse_in_buf_->channels()[i], msg.channel(i).data(),
+ msg.channel(i).size());
+ }
+ }
+}
+
+void AecDumpBasedSimulator::Process() {
+ ConfigureAudioProcessor();
+
+ if (settings_.artificial_nearend_filename) {
+ std::unique_ptr<WavReader> artificial_nearend_file(
+ new WavReader(settings_.artificial_nearend_filename->c_str()));
+
+ RTC_CHECK_EQ(1, artificial_nearend_file->num_channels())
+ << "Only mono files for the artificial nearend are supported, "
+ "reverted to not using the artificial nearend file";
+
+ const int sample_rate_hz = artificial_nearend_file->sample_rate();
+ artificial_nearend_buffer_reader_.reset(
+ new ChannelBufferWavReader(std::move(artificial_nearend_file)));
+ artificial_nearend_buf_.reset(new ChannelBuffer<float>(
+ rtc::CheckedDivExact(sample_rate_hz, kChunksPerSecond), 1));
+ }
+
+ const bool use_dump_file = !settings_.aec_dump_input_string.has_value();
+ std::stringstream input;
+ if (use_dump_file) {
+ dump_input_file_ =
+ OpenFile(settings_.aec_dump_input_filename->c_str(), "rb");
+ } else {
+ input << settings_.aec_dump_input_string.value();
+ }
+
+ webrtc::audioproc::Event event_msg;
+ int capture_frames_since_init = 0;
+ int init_index = 0;
+ while (ReadNextMessage(use_dump_file, dump_input_file_, input, event_msg)) {
+ SelectivelyToggleDataDumping(init_index, capture_frames_since_init);
+ HandleEvent(event_msg, capture_frames_since_init, init_index);
+
+ // Perfom an early exit if the init block to process has been fully
+ // processed
+ if (finished_processing_specified_init_block_) {
+ break;
+ }
+ RTC_CHECK(!settings_.init_to_process ||
+ *settings_.init_to_process >= init_index);
+ }
+
+ if (use_dump_file) {
+ fclose(dump_input_file_);
+ }
+
+ DetachAecDump();
+}
+
+void AecDumpBasedSimulator::Analyze() {
+ const bool use_dump_file = !settings_.aec_dump_input_string.has_value();
+ std::stringstream input;
+ if (use_dump_file) {
+ dump_input_file_ =
+ OpenFile(settings_.aec_dump_input_filename->c_str(), "rb");
+ } else {
+ input << settings_.aec_dump_input_string.value();
+ }
+
+ webrtc::audioproc::Event event_msg;
+ int num_capture_frames = 0;
+ int num_render_frames = 0;
+ int init_index = 0;
+ while (ReadNextMessage(use_dump_file, dump_input_file_, input, event_msg)) {
+ if (event_msg.type() == webrtc::audioproc::Event::INIT) {
+ ++init_index;
+ constexpr float kNumFramesPerSecond = 100.f;
+ float capture_time_seconds = num_capture_frames / kNumFramesPerSecond;
+ float render_time_seconds = num_render_frames / kNumFramesPerSecond;
+
+ std::cout << "Inits:" << std::endl;
+ std::cout << init_index << ": -->" << std::endl;
+ std::cout << " Time:" << std::endl;
+ std::cout << " Capture: " << capture_time_seconds << " s ("
+ << num_capture_frames << " frames) " << std::endl;
+ std::cout << " Render: " << render_time_seconds << " s ("
+ << num_render_frames << " frames) " << std::endl;
+ } else if (event_msg.type() == webrtc::audioproc::Event::STREAM) {
+ ++num_capture_frames;
+ } else if (event_msg.type() == webrtc::audioproc::Event::REVERSE_STREAM) {
+ ++num_render_frames;
+ }
+ }
+
+ if (use_dump_file) {
+ fclose(dump_input_file_);
+ }
+}
+
+void AecDumpBasedSimulator::HandleEvent(
+ const webrtc::audioproc::Event& event_msg,
+ int& capture_frames_since_init,
+ int& init_index) {
+ switch (event_msg.type()) {
+ case webrtc::audioproc::Event::INIT:
+ RTC_CHECK(event_msg.has_init());
+ ++init_index;
+ capture_frames_since_init = 0;
+ HandleMessage(event_msg.init(), init_index);
+ break;
+ case webrtc::audioproc::Event::STREAM:
+ RTC_CHECK(event_msg.has_stream());
+ ++capture_frames_since_init;
+ HandleMessage(event_msg.stream());
+ break;
+ case webrtc::audioproc::Event::REVERSE_STREAM:
+ RTC_CHECK(event_msg.has_reverse_stream());
+ HandleMessage(event_msg.reverse_stream());
+ break;
+ case webrtc::audioproc::Event::CONFIG:
+ RTC_CHECK(event_msg.has_config());
+ HandleMessage(event_msg.config());
+ break;
+ case webrtc::audioproc::Event::RUNTIME_SETTING:
+ HandleMessage(event_msg.runtime_setting());
+ break;
+ case webrtc::audioproc::Event::UNKNOWN_EVENT:
+ RTC_CHECK_NOTREACHED();
+ }
+}
+
+void AecDumpBasedSimulator::HandleMessage(
+ const webrtc::audioproc::Config& msg) {
+ if (settings_.use_verbose_logging) {
+ std::cout << "Config at frame:" << std::endl;
+ std::cout << " Forward: " << get_num_process_stream_calls() << std::endl;
+ std::cout << " Reverse: " << get_num_reverse_process_stream_calls()
+ << std::endl;
+ }
+
+ if (!settings_.discard_all_settings_in_aecdump) {
+ if (settings_.use_verbose_logging) {
+ std::cout << "Setting used in config:" << std::endl;
+ }
+ AudioProcessing::Config apm_config = ap_->GetConfig();
+
+ if (msg.has_aec_enabled() || settings_.use_aec) {
+ bool enable = settings_.use_aec ? *settings_.use_aec : msg.aec_enabled();
+ apm_config.echo_canceller.enabled = enable;
+ if (settings_.use_verbose_logging) {
+ std::cout << " aec_enabled: " << (enable ? "true" : "false")
+ << std::endl;
+ }
+ }
+
+ if (msg.has_aecm_enabled() || settings_.use_aecm) {
+ bool enable =
+ settings_.use_aecm ? *settings_.use_aecm : msg.aecm_enabled();
+ apm_config.echo_canceller.enabled |= enable;
+ apm_config.echo_canceller.mobile_mode = enable;
+ if (settings_.use_verbose_logging) {
+ std::cout << " aecm_enabled: " << (enable ? "true" : "false")
+ << std::endl;
+ }
+ }
+
+ if (msg.has_aecm_comfort_noise_enabled() &&
+ msg.aecm_comfort_noise_enabled()) {
+ RTC_LOG(LS_ERROR) << "Ignoring deprecated setting: AECM comfort noise";
+ }
+
+ if (msg.has_aecm_routing_mode() &&
+ static_cast<webrtc::EchoControlMobileImpl::RoutingMode>(
+ msg.aecm_routing_mode()) != EchoControlMobileImpl::kSpeakerphone) {
+ RTC_LOG(LS_ERROR) << "Ignoring deprecated setting: AECM routing mode: "
+ << msg.aecm_routing_mode();
+ }
+
+ if (msg.has_agc_enabled() || settings_.use_agc) {
+ bool enable = settings_.use_agc ? *settings_.use_agc : msg.agc_enabled();
+ apm_config.gain_controller1.enabled = enable;
+ if (settings_.use_verbose_logging) {
+ std::cout << " agc_enabled: " << (enable ? "true" : "false")
+ << std::endl;
+ }
+ }
+
+ if (msg.has_agc_mode() || settings_.agc_mode) {
+ int mode = settings_.agc_mode ? *settings_.agc_mode : msg.agc_mode();
+ apm_config.gain_controller1.mode =
+ static_cast<webrtc::AudioProcessing::Config::GainController1::Mode>(
+ mode);
+ if (settings_.use_verbose_logging) {
+ std::cout << " agc_mode: " << mode << std::endl;
+ }
+ }
+
+ if (msg.has_agc_limiter_enabled() || settings_.use_agc_limiter) {
+ bool enable = settings_.use_agc_limiter ? *settings_.use_agc_limiter
+ : msg.agc_limiter_enabled();
+ apm_config.gain_controller1.enable_limiter = enable;
+ if (settings_.use_verbose_logging) {
+ std::cout << " agc_limiter_enabled: " << (enable ? "true" : "false")
+ << std::endl;
+ }
+ }
+
+ if (settings_.use_agc2) {
+ bool enable = *settings_.use_agc2;
+ apm_config.gain_controller2.enabled = enable;
+ if (settings_.agc2_fixed_gain_db) {
+ apm_config.gain_controller2.fixed_digital.gain_db =
+ *settings_.agc2_fixed_gain_db;
+ }
+ if (settings_.use_verbose_logging) {
+ std::cout << " agc2_enabled: " << (enable ? "true" : "false")
+ << std::endl;
+ }
+ }
+
+ if (msg.has_noise_robust_agc_enabled()) {
+ apm_config.gain_controller1.analog_gain_controller.enabled =
+ settings_.use_analog_agc ? *settings_.use_analog_agc
+ : msg.noise_robust_agc_enabled();
+ if (settings_.use_verbose_logging) {
+ std::cout << " noise_robust_agc_enabled: "
+ << (msg.noise_robust_agc_enabled() ? "true" : "false")
+ << std::endl;
+ }
+ }
+
+ if (msg.has_transient_suppression_enabled() || settings_.use_ts) {
+ bool enable = settings_.use_ts ? *settings_.use_ts
+ : msg.transient_suppression_enabled();
+ apm_config.transient_suppression.enabled = enable;
+ if (settings_.use_verbose_logging) {
+ std::cout << " transient_suppression_enabled: "
+ << (enable ? "true" : "false") << std::endl;
+ }
+ }
+
+ if (msg.has_hpf_enabled() || settings_.use_hpf) {
+ bool enable = settings_.use_hpf ? *settings_.use_hpf : msg.hpf_enabled();
+ apm_config.high_pass_filter.enabled = enable;
+ if (settings_.use_verbose_logging) {
+ std::cout << " hpf_enabled: " << (enable ? "true" : "false")
+ << std::endl;
+ }
+ }
+
+ if (msg.has_ns_enabled() || settings_.use_ns) {
+ bool enable = settings_.use_ns ? *settings_.use_ns : msg.ns_enabled();
+ apm_config.noise_suppression.enabled = enable;
+ if (settings_.use_verbose_logging) {
+ std::cout << " ns_enabled: " << (enable ? "true" : "false")
+ << std::endl;
+ }
+ }
+
+ if (msg.has_ns_level() || settings_.ns_level) {
+ int level = settings_.ns_level ? *settings_.ns_level : msg.ns_level();
+ apm_config.noise_suppression.level =
+ static_cast<AudioProcessing::Config::NoiseSuppression::Level>(level);
+ if (settings_.use_verbose_logging) {
+ std::cout << " ns_level: " << level << std::endl;
+ }
+ }
+
+ if (msg.has_pre_amplifier_enabled() || settings_.use_pre_amplifier) {
+ const bool enable = settings_.use_pre_amplifier
+ ? *settings_.use_pre_amplifier
+ : msg.pre_amplifier_enabled();
+ apm_config.pre_amplifier.enabled = enable;
+ }
+
+ if (msg.has_pre_amplifier_fixed_gain_factor() ||
+ settings_.pre_amplifier_gain_factor) {
+ const float gain = settings_.pre_amplifier_gain_factor
+ ? *settings_.pre_amplifier_gain_factor
+ : msg.pre_amplifier_fixed_gain_factor();
+ apm_config.pre_amplifier.fixed_gain_factor = gain;
+ }
+
+ if (settings_.use_verbose_logging && msg.has_experiments_description() &&
+ !msg.experiments_description().empty()) {
+ std::cout << " experiments not included by default in the simulation: "
+ << msg.experiments_description() << std::endl;
+ }
+
+ if (settings_.use_ed) {
+ apm_config.residual_echo_detector.enabled = *settings_.use_ed;
+ }
+
+ ap_->ApplyConfig(apm_config);
+ }
+}
+
+void AecDumpBasedSimulator::HandleMessage(const webrtc::audioproc::Init& msg,
+ int init_index) {
+ RTC_CHECK(msg.has_sample_rate());
+ RTC_CHECK(msg.has_num_input_channels());
+ RTC_CHECK(msg.has_num_reverse_channels());
+ RTC_CHECK(msg.has_reverse_sample_rate());
+
+ // Do not perform the init if the init block to process is fully processed
+ if (settings_.init_to_process && *settings_.init_to_process < init_index) {
+ finished_processing_specified_init_block_ = true;
+ }
+
+ MaybeOpenCallOrderFile();
+
+ if (settings_.use_verbose_logging) {
+ std::cout << "Init at frame:" << std::endl;
+ std::cout << " Forward: " << get_num_process_stream_calls() << std::endl;
+ std::cout << " Reverse: " << get_num_reverse_process_stream_calls()
+ << std::endl;
+ }
+
+ int num_output_channels;
+ if (settings_.output_num_channels) {
+ num_output_channels = *settings_.output_num_channels;
+ } else {
+ num_output_channels = msg.has_num_output_channels()
+ ? msg.num_output_channels()
+ : msg.num_input_channels();
+ }
+
+ int output_sample_rate;
+ if (settings_.output_sample_rate_hz) {
+ output_sample_rate = *settings_.output_sample_rate_hz;
+ } else {
+ output_sample_rate = msg.has_output_sample_rate() ? msg.output_sample_rate()
+ : msg.sample_rate();
+ }
+
+ int num_reverse_output_channels;
+ if (settings_.reverse_output_num_channels) {
+ num_reverse_output_channels = *settings_.reverse_output_num_channels;
+ } else {
+ num_reverse_output_channels = msg.has_num_reverse_output_channels()
+ ? msg.num_reverse_output_channels()
+ : msg.num_reverse_channels();
+ }
+
+ int reverse_output_sample_rate;
+ if (settings_.reverse_output_sample_rate_hz) {
+ reverse_output_sample_rate = *settings_.reverse_output_sample_rate_hz;
+ } else {
+ reverse_output_sample_rate = msg.has_reverse_output_sample_rate()
+ ? msg.reverse_output_sample_rate()
+ : msg.reverse_sample_rate();
+ }
+
+ SetupBuffersConfigsOutputs(
+ msg.sample_rate(), output_sample_rate, msg.reverse_sample_rate(),
+ reverse_output_sample_rate, msg.num_input_channels(), num_output_channels,
+ msg.num_reverse_channels(), num_reverse_output_channels);
+}
+
+void AecDumpBasedSimulator::HandleMessage(
+ const webrtc::audioproc::Stream& msg) {
+ if (call_order_output_file_) {
+ *call_order_output_file_ << "c";
+ }
+ PrepareProcessStreamCall(msg);
+ ProcessStream(interface_used_ == InterfaceType::kFixedInterface);
+ VerifyProcessStreamBitExactness(msg);
+}
+
+void AecDumpBasedSimulator::HandleMessage(
+ const webrtc::audioproc::ReverseStream& msg) {
+ if (call_order_output_file_) {
+ *call_order_output_file_ << "r";
+ }
+ PrepareReverseProcessStreamCall(msg);
+ ProcessReverseStream(interface_used_ == InterfaceType::kFixedInterface);
+}
+
+void AecDumpBasedSimulator::HandleMessage(
+ const webrtc::audioproc::RuntimeSetting& msg) {
+ RTC_CHECK(ap_.get());
+ if (msg.has_capture_pre_gain()) {
+ // Handle capture pre-gain runtime setting only if not overridden.
+ const bool pre_amplifier_overridden =
+ (!settings_.use_pre_amplifier || *settings_.use_pre_amplifier) &&
+ !settings_.pre_amplifier_gain_factor;
+ const bool capture_level_adjustment_overridden =
+ (!settings_.use_capture_level_adjustment ||
+ *settings_.use_capture_level_adjustment) &&
+ !settings_.pre_gain_factor;
+ if (pre_amplifier_overridden || capture_level_adjustment_overridden) {
+ ap_->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCapturePreGain(
+ msg.capture_pre_gain()));
+ }
+ } else if (msg.has_capture_post_gain()) {
+ // Handle capture post-gain runtime setting only if not overridden.
+ if ((!settings_.use_capture_level_adjustment ||
+ *settings_.use_capture_level_adjustment) &&
+ !settings_.post_gain_factor) {
+ ap_->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCapturePreGain(
+ msg.capture_pre_gain()));
+ }
+ } else if (msg.has_capture_fixed_post_gain()) {
+ // Handle capture fixed-post-gain runtime setting only if not overridden.
+ if ((!settings_.use_agc2 || *settings_.use_agc2) &&
+ !settings_.agc2_fixed_gain_db) {
+ ap_->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCaptureFixedPostGain(
+ msg.capture_fixed_post_gain()));
+ }
+ } else if (msg.has_playout_volume_change()) {
+ ap_->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(
+ msg.playout_volume_change()));
+ } else if (msg.has_playout_audio_device_change()) {
+ ap_->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreatePlayoutAudioDeviceChange(
+ {msg.playout_audio_device_change().id(),
+ msg.playout_audio_device_change().max_volume()}));
+ } else if (msg.has_capture_output_used()) {
+ ap_->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
+ msg.capture_output_used()));
+ }
+}
+
+void AecDumpBasedSimulator::MaybeOpenCallOrderFile() {
+ if (settings_.call_order_output_filename.has_value()) {
+ const std::string filename = settings_.store_intermediate_output
+ ? *settings_.call_order_output_filename +
+ "_" +
+ std::to_string(output_reset_counter_)
+ : *settings_.call_order_output_filename;
+ call_order_output_file_ = std::make_unique<std::ofstream>(filename);
+ }
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/api_call_statistics.cc b/modules/audio_processing/test/api_call_statistics.cc
new file mode 100644
index 0000000..736b77b
--- /dev/null
+++ b/modules/audio_processing/test/api_call_statistics.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2019 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 "modules/audio_processing/test/api_call_statistics.h"
+
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <limits>
+#include <memory>
+
+#include "rtc_base/time_utils.h"
+
+namespace webrtc {
+namespace test {
+
+void ApiCallStatistics::Add(int64_t duration_nanos, CallType call_type) {
+ calls_.push_back(CallData(duration_nanos, call_type));
+}
+
+void ApiCallStatistics::PrintReport() const {
+ int64_t min_render = std::numeric_limits<int64_t>::max();
+ int64_t min_capture = std::numeric_limits<int64_t>::max();
+ int64_t max_render = 0;
+ int64_t max_capture = 0;
+ int64_t sum_render = 0;
+ int64_t sum_capture = 0;
+ int64_t num_render = 0;
+ int64_t num_capture = 0;
+ int64_t avg_render = 0;
+ int64_t avg_capture = 0;
+
+ for (auto v : calls_) {
+ if (v.call_type == CallType::kRender) {
+ ++num_render;
+ min_render = std::min(min_render, v.duration_nanos);
+ max_render = std::max(max_render, v.duration_nanos);
+ sum_render += v.duration_nanos;
+ } else {
+ ++num_capture;
+ min_capture = std::min(min_capture, v.duration_nanos);
+ max_capture = std::max(max_capture, v.duration_nanos);
+ sum_capture += v.duration_nanos;
+ }
+ }
+ min_render /= rtc::kNumNanosecsPerMicrosec;
+ max_render /= rtc::kNumNanosecsPerMicrosec;
+ sum_render /= rtc::kNumNanosecsPerMicrosec;
+ min_capture /= rtc::kNumNanosecsPerMicrosec;
+ max_capture /= rtc::kNumNanosecsPerMicrosec;
+ sum_capture /= rtc::kNumNanosecsPerMicrosec;
+ avg_render = num_render > 0 ? sum_render / num_render : 0;
+ avg_capture = num_capture > 0 ? sum_capture / num_capture : 0;
+
+ std::cout << std::endl
+ << "Total time: " << (sum_capture + sum_render) * 1e-6 << " s"
+ << std::endl
+ << " Render API calls:" << std::endl
+ << " min: " << min_render << " us" << std::endl
+ << " max: " << max_render << " us" << std::endl
+ << " avg: " << avg_render << " us" << std::endl
+ << " Capture API calls:" << std::endl
+ << " min: " << min_capture << " us" << std::endl
+ << " max: " << max_capture << " us" << std::endl
+ << " avg: " << avg_capture << " us" << std::endl;
+}
+
+void ApiCallStatistics::WriteReportToFile(const std::string& filename) const {
+ std::unique_ptr<std::ofstream> out =
+ std::make_unique<std::ofstream>(filename);
+ for (auto v : calls_) {
+ if (v.call_type == CallType::kRender) {
+ *out << "render, ";
+ } else {
+ *out << "capture, ";
+ }
+ *out << (v.duration_nanos / rtc::kNumNanosecsPerMicrosec) << std::endl;
+ }
+}
+
+ApiCallStatistics::CallData::CallData(int64_t duration_nanos,
+ CallType call_type)
+ : duration_nanos(duration_nanos), call_type(call_type) {}
+
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/audio_buffer_tools.cc b/modules/audio_processing/test/audio_buffer_tools.cc
new file mode 100644
index 0000000..64fb9c7
--- /dev/null
+++ b/modules/audio_processing/test/audio_buffer_tools.cc
@@ -0,0 +1,68 @@
+/*
+ * 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 "modules/audio_processing/test/audio_buffer_tools.h"
+
+#include <string.h>
+
+namespace webrtc {
+namespace test {
+
+void SetupFrame(const StreamConfig& stream_config,
+ std::vector<float*>* frame,
+ std::vector<float>* frame_samples) {
+ frame_samples->resize(stream_config.num_channels() *
+ stream_config.num_frames());
+ frame->resize(stream_config.num_channels());
+ for (size_t ch = 0; ch < stream_config.num_channels(); ++ch) {
+ (*frame)[ch] = &(*frame_samples)[ch * stream_config.num_frames()];
+ }
+}
+
+void CopyVectorToAudioBuffer(const StreamConfig& stream_config,
+ rtc::ArrayView<const float> source,
+ AudioBuffer* destination) {
+ std::vector<float*> input;
+ std::vector<float> input_samples;
+
+ SetupFrame(stream_config, &input, &input_samples);
+
+ RTC_CHECK_EQ(input_samples.size(), source.size());
+ memcpy(input_samples.data(), source.data(),
+ source.size() * sizeof(source[0]));
+
+ destination->CopyFrom(&input[0], stream_config);
+}
+
+void ExtractVectorFromAudioBuffer(const StreamConfig& stream_config,
+ AudioBuffer* source,
+ std::vector<float>* destination) {
+ std::vector<float*> output;
+
+ SetupFrame(stream_config, &output, destination);
+
+ source->CopyTo(stream_config, &output[0]);
+}
+
+void FillBuffer(float value, AudioBuffer& audio_buffer) {
+ for (size_t ch = 0; ch < audio_buffer.num_channels(); ++ch) {
+ FillBufferChannel(value, ch, audio_buffer);
+ }
+}
+
+void FillBufferChannel(float value, int channel, AudioBuffer& audio_buffer) {
+ RTC_CHECK_LT(channel, audio_buffer.num_channels());
+ for (size_t i = 0; i < audio_buffer.num_frames(); ++i) {
+ audio_buffer.channels()[channel][i] = value;
+ }
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/audio_processing_builder_for_testing.cc b/modules/audio_processing/test/audio_processing_builder_for_testing.cc
new file mode 100644
index 0000000..faab18f
--- /dev/null
+++ b/modules/audio_processing/test/audio_processing_builder_for_testing.cc
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 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 "modules/audio_processing/test/audio_processing_builder_for_testing.h"
+
+#include <memory>
+#include <utility>
+
+#include "modules/audio_processing/audio_processing_impl.h"
+#include "rtc_base/ref_counted_object.h"
+
+namespace webrtc {
+
+AudioProcessingBuilderForTesting::AudioProcessingBuilderForTesting() = default;
+AudioProcessingBuilderForTesting::~AudioProcessingBuilderForTesting() = default;
+
+#ifdef WEBRTC_EXCLUDE_AUDIO_PROCESSING_MODULE
+
+AudioProcessing* AudioProcessingBuilderForTesting::Create() {
+ webrtc::Config config;
+ return Create(config);
+}
+
+AudioProcessing* AudioProcessingBuilderForTesting::Create(
+ const webrtc::Config& config) {
+ return new rtc::RefCountedObject<AudioProcessingImpl>(
+ config, std::move(capture_post_processing_),
+ std::move(render_pre_processing_), std::move(echo_control_factory_),
+ std::move(echo_detector_), std::move(capture_analyzer_));
+}
+
+#else
+
+AudioProcessing* AudioProcessingBuilderForTesting::Create() {
+ AudioProcessingBuilder builder;
+ TransferOwnershipsToBuilder(&builder);
+ return builder.Create();
+}
+
+AudioProcessing* AudioProcessingBuilderForTesting::Create(
+ const webrtc::Config& config) {
+ AudioProcessingBuilder builder;
+ TransferOwnershipsToBuilder(&builder);
+ return builder.Create(config);
+}
+
+#endif
+
+void AudioProcessingBuilderForTesting::TransferOwnershipsToBuilder(
+ AudioProcessingBuilder* builder) {
+ builder->SetCapturePostProcessing(std::move(capture_post_processing_));
+ builder->SetRenderPreProcessing(std::move(render_pre_processing_));
+ builder->SetCaptureAnalyzer(std::move(capture_analyzer_));
+ builder->SetEchoControlFactory(std::move(echo_control_factory_));
+ builder->SetEchoDetector(std::move(echo_detector_));
+}
+
+} // namespace webrtc
diff --git a/modules/audio_processing/test/audio_processing_simulator.cc b/modules/audio_processing/test/audio_processing_simulator.cc
new file mode 100644
index 0000000..ac45148
--- /dev/null
+++ b/modules/audio_processing/test/audio_processing_simulator.cc
@@ -0,0 +1,615 @@
+/*
+ * 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 "modules/audio_processing/test/audio_processing_simulator.h"
+
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "api/audio/echo_canceller3_config_json.h"
+#include "api/audio/echo_canceller3_factory.h"
+#include "modules/audio_processing/aec_dump/aec_dump_factory.h"
+#include "modules/audio_processing/echo_control_mobile_impl.h"
+#include "modules/audio_processing/include/audio_processing.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "modules/audio_processing/test/fake_recording_device.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/strings/json.h"
+#include "rtc_base/strings/string_builder.h"
+
+namespace webrtc {
+namespace test {
+namespace {
+// Helper for reading JSON from a file and parsing it to an AEC3 configuration.
+EchoCanceller3Config ReadAec3ConfigFromJsonFile(const std::string& filename) {
+ std::string json_string;
+ std::string s;
+ std::ifstream f(filename.c_str());
+ if (f.fail()) {
+ std::cout << "Failed to open the file " << filename << std::endl;
+ RTC_CHECK_NOTREACHED();
+ }
+ while (std::getline(f, s)) {
+ json_string += s;
+ }
+
+ bool parsing_successful;
+ EchoCanceller3Config cfg;
+ Aec3ConfigFromJsonString(json_string, &cfg, &parsing_successful);
+ if (!parsing_successful) {
+ std::cout << "Parsing of json string failed: " << std::endl
+ << json_string << std::endl;
+ RTC_CHECK_NOTREACHED();
+ }
+ RTC_CHECK(EchoCanceller3Config::Validate(&cfg));
+
+ return cfg;
+}
+
+std::string GetIndexedOutputWavFilename(const std::string& wav_name,
+ int counter) {
+ rtc::StringBuilder ss;
+ ss << wav_name.substr(0, wav_name.size() - 4) << "_" << counter
+ << wav_name.substr(wav_name.size() - 4);
+ return ss.Release();
+}
+
+void WriteEchoLikelihoodGraphFileHeader(std::ofstream* output_file) {
+ (*output_file) << "import numpy as np" << std::endl
+ << "import matplotlib.pyplot as plt" << std::endl
+ << "y = np.array([";
+}
+
+void WriteEchoLikelihoodGraphFileFooter(std::ofstream* output_file) {
+ (*output_file) << "])" << std::endl
+ << "if __name__ == '__main__':" << std::endl
+ << " x = np.arange(len(y))*.01" << std::endl
+ << " plt.plot(x, y)" << std::endl
+ << " plt.ylabel('Echo likelihood')" << std::endl
+ << " plt.xlabel('Time (s)')" << std::endl
+ << " plt.show()" << std::endl;
+}
+
+// RAII class for execution time measurement. Updates the provided
+// ApiCallStatistics based on the time between ScopedTimer creation and
+// leaving the enclosing scope.
+class ScopedTimer {
+ public:
+ ScopedTimer(ApiCallStatistics* api_call_statistics_,
+ ApiCallStatistics::CallType call_type)
+ : start_time_(rtc::TimeNanos()),
+ call_type_(call_type),
+ api_call_statistics_(api_call_statistics_) {}
+
+ ~ScopedTimer() {
+ api_call_statistics_->Add(rtc::TimeNanos() - start_time_, call_type_);
+ }
+
+ private:
+ const int64_t start_time_;
+ const ApiCallStatistics::CallType call_type_;
+ ApiCallStatistics* const api_call_statistics_;
+};
+
+} // namespace
+
+SimulationSettings::SimulationSettings() = default;
+SimulationSettings::SimulationSettings(const SimulationSettings&) = default;
+SimulationSettings::~SimulationSettings() = default;
+
+AudioProcessingSimulator::AudioProcessingSimulator(
+ const SimulationSettings& settings,
+ rtc::scoped_refptr<AudioProcessing> audio_processing,
+ std::unique_ptr<AudioProcessingBuilder> ap_builder)
+ : settings_(settings),
+ ap_(std::move(audio_processing)),
+ analog_mic_level_(settings.initial_mic_level),
+ fake_recording_device_(
+ settings.initial_mic_level,
+ settings_.simulate_mic_gain ? *settings.simulated_mic_kind : 0),
+ worker_queue_("file_writer_task_queue") {
+ RTC_CHECK(!settings_.dump_internal_data || WEBRTC_APM_DEBUG_DUMP == 1);
+ if (settings_.dump_start_frame || settings_.dump_end_frame) {
+ ApmDataDumper::SetActivated(!settings_.dump_start_frame);
+ } else {
+ ApmDataDumper::SetActivated(settings_.dump_internal_data);
+ }
+
+ if (settings_.dump_set_to_use) {
+ ApmDataDumper::SetDumpSetToUse(*settings_.dump_set_to_use);
+ }
+
+ if (settings_.dump_internal_data_output_dir.has_value()) {
+ ApmDataDumper::SetOutputDirectory(
+ settings_.dump_internal_data_output_dir.value());
+ }
+
+ if (settings_.ed_graph_output_filename &&
+ !settings_.ed_graph_output_filename->empty()) {
+ residual_echo_likelihood_graph_writer_.open(
+ *settings_.ed_graph_output_filename);
+ RTC_CHECK(residual_echo_likelihood_graph_writer_.is_open());
+ WriteEchoLikelihoodGraphFileHeader(&residual_echo_likelihood_graph_writer_);
+ }
+
+ if (settings_.simulate_mic_gain)
+ RTC_LOG(LS_VERBOSE) << "Simulating analog mic gain";
+
+ // Create the audio processing object.
+ RTC_CHECK(!(ap_ && ap_builder))
+ << "The AudioProcessing and the AudioProcessingBuilder cannot both be "
+ "specified at the same time.";
+
+ if (ap_) {
+ RTC_CHECK(!settings_.aec_settings_filename);
+ RTC_CHECK(!settings_.print_aec_parameter_values);
+ } else {
+ // Use specied builder if such is provided, otherwise create a new builder.
+ std::unique_ptr<AudioProcessingBuilder> builder =
+ !!ap_builder ? std::move(ap_builder)
+ : std::make_unique<AudioProcessingBuilder>();
+
+ // Create and set an EchoCanceller3Factory if needed.
+ const bool use_aec = settings_.use_aec && *settings_.use_aec;
+ if (use_aec) {
+ EchoCanceller3Config cfg;
+ if (settings_.aec_settings_filename) {
+ if (settings_.use_verbose_logging) {
+ std::cout << "Reading AEC Parameters from JSON input." << std::endl;
+ }
+ cfg = ReadAec3ConfigFromJsonFile(*settings_.aec_settings_filename);
+ }
+
+ if (settings_.linear_aec_output_filename) {
+ cfg.filter.export_linear_aec_output = true;
+ }
+
+ if (settings_.print_aec_parameter_values) {
+ if (!settings_.use_quiet_output) {
+ std::cout << "AEC settings:" << std::endl;
+ }
+ std::cout << Aec3ConfigToJsonString(cfg) << std::endl;
+ }
+
+ auto echo_control_factory = std::make_unique<EchoCanceller3Factory>(cfg);
+ builder->SetEchoControlFactory(std::move(echo_control_factory));
+ }
+
+ // Create an audio processing object.
+ ap_ = builder->Create();
+ RTC_CHECK(ap_);
+ }
+}
+
+AudioProcessingSimulator::~AudioProcessingSimulator() {
+ if (residual_echo_likelihood_graph_writer_.is_open()) {
+ WriteEchoLikelihoodGraphFileFooter(&residual_echo_likelihood_graph_writer_);
+ residual_echo_likelihood_graph_writer_.close();
+ }
+}
+
+void AudioProcessingSimulator::ProcessStream(bool fixed_interface) {
+ // Optionally use the fake recording device to simulate analog gain.
+ if (settings_.simulate_mic_gain) {
+ if (settings_.aec_dump_input_filename) {
+ // When the analog gain is simulated and an AEC dump is used as input, set
+ // the undo level to |aec_dump_mic_level_| to virtually restore the
+ // unmodified microphone signal level.
+ fake_recording_device_.SetUndoMicLevel(aec_dump_mic_level_);
+ }
+
+ if (fixed_interface) {
+ fake_recording_device_.SimulateAnalogGain(fwd_frame_.data);
+ } else {
+ fake_recording_device_.SimulateAnalogGain(in_buf_.get());
+ }
+
+ // Notify the current mic level to AGC.
+ ap_->set_stream_analog_level(fake_recording_device_.MicLevel());
+ } else {
+ // Notify the current mic level to AGC.
+ ap_->set_stream_analog_level(settings_.aec_dump_input_filename
+ ? aec_dump_mic_level_
+ : analog_mic_level_);
+ }
+
+ // Post any scheduled runtime settings.
+ if (settings_.frame_for_sending_capture_output_used_false &&
+ *settings_.frame_for_sending_capture_output_used_false ==
+ static_cast<int>(num_process_stream_calls_)) {
+ ap_->PostRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(false));
+ }
+ if (settings_.frame_for_sending_capture_output_used_true &&
+ *settings_.frame_for_sending_capture_output_used_true ==
+ static_cast<int>(num_process_stream_calls_)) {
+ ap_->PostRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(true));
+ }
+
+ // Process the current audio frame.
+ if (fixed_interface) {
+ {
+ const auto st = ScopedTimer(&api_call_statistics_,
+ ApiCallStatistics::CallType::kCapture);
+ RTC_CHECK_EQ(
+ AudioProcessing::kNoError,
+ ap_->ProcessStream(fwd_frame_.data.data(), fwd_frame_.config,
+ fwd_frame_.config, fwd_frame_.data.data()));
+ }
+ fwd_frame_.CopyTo(out_buf_.get());
+ } else {
+ const auto st = ScopedTimer(&api_call_statistics_,
+ ApiCallStatistics::CallType::kCapture);
+ RTC_CHECK_EQ(AudioProcessing::kNoError,
+ ap_->ProcessStream(in_buf_->channels(), in_config_,
+ out_config_, out_buf_->channels()));
+ }
+
+ // Store the mic level suggested by AGC.
+ // Note that when the analog gain is simulated and an AEC dump is used as
+ // input, |analog_mic_level_| will not be used with set_stream_analog_level().
+ analog_mic_level_ = ap_->recommended_stream_analog_level();
+ if (settings_.simulate_mic_gain) {
+ fake_recording_device_.SetMicLevel(analog_mic_level_);
+ }
+ if (buffer_memory_writer_) {
+ RTC_CHECK(!buffer_file_writer_);
+ buffer_memory_writer_->Write(*out_buf_);
+ } else if (buffer_file_writer_) {
+ RTC_CHECK(!buffer_memory_writer_);
+ buffer_file_writer_->Write(*out_buf_);
+ }
+
+ if (linear_aec_output_file_writer_) {
+ bool output_available = ap_->GetLinearAecOutput(linear_aec_output_buf_);
+ RTC_CHECK(output_available);
+ RTC_CHECK_GT(linear_aec_output_buf_.size(), 0);
+ RTC_CHECK_EQ(linear_aec_output_buf_[0].size(), 160);
+ for (size_t k = 0; k < linear_aec_output_buf_[0].size(); ++k) {
+ for (size_t ch = 0; ch < linear_aec_output_buf_.size(); ++ch) {
+ RTC_CHECK_EQ(linear_aec_output_buf_[ch].size(), 160);
+ float sample = FloatToFloatS16(linear_aec_output_buf_[ch][k]);
+ linear_aec_output_file_writer_->WriteSamples(&sample, 1);
+ }
+ }
+ }
+
+ if (residual_echo_likelihood_graph_writer_.is_open()) {
+ auto stats = ap_->GetStatistics();
+ residual_echo_likelihood_graph_writer_
+ << stats.residual_echo_likelihood.value_or(-1.f) << ", ";
+ }
+
+ ++num_process_stream_calls_;
+}
+
+void AudioProcessingSimulator::ProcessReverseStream(bool fixed_interface) {
+ if (fixed_interface) {
+ {
+ const auto st = ScopedTimer(&api_call_statistics_,
+ ApiCallStatistics::CallType::kRender);
+ RTC_CHECK_EQ(
+ AudioProcessing::kNoError,
+ ap_->ProcessReverseStream(rev_frame_.data.data(), rev_frame_.config,
+ rev_frame_.config, rev_frame_.data.data()));
+ }
+ rev_frame_.CopyTo(reverse_out_buf_.get());
+ } else {
+ const auto st = ScopedTimer(&api_call_statistics_,
+ ApiCallStatistics::CallType::kRender);
+ RTC_CHECK_EQ(AudioProcessing::kNoError,
+ ap_->ProcessReverseStream(
+ reverse_in_buf_->channels(), reverse_in_config_,
+ reverse_out_config_, reverse_out_buf_->channels()));
+ }
+
+ if (reverse_buffer_file_writer_) {
+ reverse_buffer_file_writer_->Write(*reverse_out_buf_);
+ }
+
+ ++num_reverse_process_stream_calls_;
+}
+
+void AudioProcessingSimulator::SetupBuffersConfigsOutputs(
+ int input_sample_rate_hz,
+ int output_sample_rate_hz,
+ int reverse_input_sample_rate_hz,
+ int reverse_output_sample_rate_hz,
+ int input_num_channels,
+ int output_num_channels,
+ int reverse_input_num_channels,
+ int reverse_output_num_channels) {
+ in_config_ = StreamConfig(input_sample_rate_hz, input_num_channels);
+ in_buf_.reset(new ChannelBuffer<float>(
+ rtc::CheckedDivExact(input_sample_rate_hz, kChunksPerSecond),
+ input_num_channels));
+
+ reverse_in_config_ =
+ StreamConfig(reverse_input_sample_rate_hz, reverse_input_num_channels);
+ reverse_in_buf_.reset(new ChannelBuffer<float>(
+ rtc::CheckedDivExact(reverse_input_sample_rate_hz, kChunksPerSecond),
+ reverse_input_num_channels));
+
+ out_config_ = StreamConfig(output_sample_rate_hz, output_num_channels);
+ out_buf_.reset(new ChannelBuffer<float>(
+ rtc::CheckedDivExact(output_sample_rate_hz, kChunksPerSecond),
+ output_num_channels));
+
+ reverse_out_config_ =
+ StreamConfig(reverse_output_sample_rate_hz, reverse_output_num_channels);
+ reverse_out_buf_.reset(new ChannelBuffer<float>(
+ rtc::CheckedDivExact(reverse_output_sample_rate_hz, kChunksPerSecond),
+ reverse_output_num_channels));
+
+ fwd_frame_.SetFormat(input_sample_rate_hz, input_num_channels);
+ rev_frame_.SetFormat(reverse_input_sample_rate_hz,
+ reverse_input_num_channels);
+
+ if (settings_.use_verbose_logging) {
+ rtc::LogMessage::LogToDebug(rtc::LS_VERBOSE);
+
+ std::cout << "Sample rates:" << std::endl;
+ std::cout << " Forward input: " << input_sample_rate_hz << std::endl;
+ std::cout << " Forward output: " << output_sample_rate_hz << std::endl;
+ std::cout << " Reverse input: " << reverse_input_sample_rate_hz
+ << std::endl;
+ std::cout << " Reverse output: " << reverse_output_sample_rate_hz
+ << std::endl;
+ std::cout << "Number of channels: " << std::endl;
+ std::cout << " Forward input: " << input_num_channels << std::endl;
+ std::cout << " Forward output: " << output_num_channels << std::endl;
+ std::cout << " Reverse input: " << reverse_input_num_channels << std::endl;
+ std::cout << " Reverse output: " << reverse_output_num_channels
+ << std::endl;
+ }
+
+ SetupOutput();
+}
+
+void AudioProcessingSimulator::SelectivelyToggleDataDumping(
+ int init_index,
+ int capture_frames_since_init) const {
+ if (!(settings_.dump_start_frame || settings_.dump_end_frame)) {
+ return;
+ }
+
+ if (settings_.init_to_process && *settings_.init_to_process != init_index) {
+ return;
+ }
+
+ if (settings_.dump_start_frame &&
+ *settings_.dump_start_frame == capture_frames_since_init) {
+ ApmDataDumper::SetActivated(true);
+ }
+
+ if (settings_.dump_end_frame &&
+ *settings_.dump_end_frame == capture_frames_since_init) {
+ ApmDataDumper::SetActivated(false);
+ }
+}
+
+void AudioProcessingSimulator::SetupOutput() {
+ if (settings_.output_filename) {
+ std::string filename;
+ if (settings_.store_intermediate_output) {
+ filename = GetIndexedOutputWavFilename(*settings_.output_filename,
+ output_reset_counter_);
+ } else {
+ filename = *settings_.output_filename;
+ }
+
+ std::unique_ptr<WavWriter> out_file(
+ new WavWriter(filename, out_config_.sample_rate_hz(),
+ static_cast<size_t>(out_config_.num_channels()),
+ settings_.wav_output_format));
+ buffer_file_writer_.reset(new ChannelBufferWavWriter(std::move(out_file)));
+ } else if (settings_.aec_dump_input_string.has_value()) {
+ buffer_memory_writer_ = std::make_unique<ChannelBufferVectorWriter>(
+ settings_.processed_capture_samples);
+ }
+
+ if (settings_.linear_aec_output_filename) {
+ std::string filename;
+ if (settings_.store_intermediate_output) {
+ filename = GetIndexedOutputWavFilename(
+ *settings_.linear_aec_output_filename, output_reset_counter_);
+ } else {
+ filename = *settings_.linear_aec_output_filename;
+ }
+
+ linear_aec_output_file_writer_.reset(
+ new WavWriter(filename, 16000, out_config_.num_channels(),
+ settings_.wav_output_format));
+
+ linear_aec_output_buf_.resize(out_config_.num_channels());
+ }
+
+ if (settings_.reverse_output_filename) {
+ std::string filename;
+ if (settings_.store_intermediate_output) {
+ filename = GetIndexedOutputWavFilename(*settings_.reverse_output_filename,
+ output_reset_counter_);
+ } else {
+ filename = *settings_.reverse_output_filename;
+ }
+
+ std::unique_ptr<WavWriter> reverse_out_file(
+ new WavWriter(filename, reverse_out_config_.sample_rate_hz(),
+ static_cast<size_t>(reverse_out_config_.num_channels()),
+ settings_.wav_output_format));
+ reverse_buffer_file_writer_.reset(
+ new ChannelBufferWavWriter(std::move(reverse_out_file)));
+ }
+
+ ++output_reset_counter_;
+}
+
+void AudioProcessingSimulator::DetachAecDump() {
+ if (settings_.aec_dump_output_filename) {
+ ap_->DetachAecDump();
+ }
+}
+
+void AudioProcessingSimulator::ConfigureAudioProcessor() {
+ AudioProcessing::Config apm_config;
+ if (settings_.use_ts) {
+ apm_config.transient_suppression.enabled = *settings_.use_ts;
+ }
+ if (settings_.multi_channel_render) {
+ apm_config.pipeline.multi_channel_render = *settings_.multi_channel_render;
+ }
+
+ if (settings_.multi_channel_capture) {
+ apm_config.pipeline.multi_channel_capture =
+ *settings_.multi_channel_capture;
+ }
+
+ if (settings_.use_agc2) {
+ apm_config.gain_controller2.enabled = *settings_.use_agc2;
+ if (settings_.agc2_fixed_gain_db) {
+ apm_config.gain_controller2.fixed_digital.gain_db =
+ *settings_.agc2_fixed_gain_db;
+ }
+ if (settings_.agc2_use_adaptive_gain) {
+ apm_config.gain_controller2.adaptive_digital.enabled =
+ *settings_.agc2_use_adaptive_gain;
+ apm_config.gain_controller2.adaptive_digital.level_estimator =
+ settings_.agc2_adaptive_level_estimator;
+ }
+ }
+ if (settings_.use_pre_amplifier) {
+ apm_config.pre_amplifier.enabled = *settings_.use_pre_amplifier;
+ if (settings_.pre_amplifier_gain_factor) {
+ apm_config.pre_amplifier.fixed_gain_factor =
+ *settings_.pre_amplifier_gain_factor;
+ }
+ }
+
+ if (settings_.use_analog_mic_gain_emulation) {
+ if (*settings_.use_analog_mic_gain_emulation) {
+ apm_config.capture_level_adjustment.enabled = true;
+ apm_config.capture_level_adjustment.analog_mic_gain_emulation.enabled =
+ true;
+ } else {
+ apm_config.capture_level_adjustment.analog_mic_gain_emulation.enabled =
+ false;
+ }
+ }
+ if (settings_.analog_mic_gain_emulation_initial_level) {
+ apm_config.capture_level_adjustment.analog_mic_gain_emulation
+ .initial_level = *settings_.analog_mic_gain_emulation_initial_level;
+ }
+
+ if (settings_.use_capture_level_adjustment) {
+ apm_config.capture_level_adjustment.enabled =
+ *settings_.use_capture_level_adjustment;
+ }
+ if (settings_.pre_gain_factor) {
+ apm_config.capture_level_adjustment.pre_gain_factor =
+ *settings_.pre_gain_factor;
+ }
+ if (settings_.post_gain_factor) {
+ apm_config.capture_level_adjustment.post_gain_factor =
+ *settings_.post_gain_factor;
+ }
+
+ const bool use_aec = settings_.use_aec && *settings_.use_aec;
+ const bool use_aecm = settings_.use_aecm && *settings_.use_aecm;
+ if (use_aec || use_aecm) {
+ apm_config.echo_canceller.enabled = true;
+ apm_config.echo_canceller.mobile_mode = use_aecm;
+ }
+ apm_config.echo_canceller.export_linear_aec_output =
+ !!settings_.linear_aec_output_filename;
+
+ if (settings_.use_hpf) {
+ apm_config.high_pass_filter.enabled = *settings_.use_hpf;
+ }
+
+ if (settings_.use_le) {
+ apm_config.level_estimation.enabled = *settings_.use_le;
+ }
+
+ if (settings_.use_vad) {
+ apm_config.voice_detection.enabled = *settings_.use_vad;
+ }
+
+ if (settings_.use_agc) {
+ apm_config.gain_controller1.enabled = *settings_.use_agc;
+ }
+ if (settings_.agc_mode) {
+ apm_config.gain_controller1.mode =
+ static_cast<webrtc::AudioProcessing::Config::GainController1::Mode>(
+ *settings_.agc_mode);
+ }
+ if (settings_.use_agc_limiter) {
+ apm_config.gain_controller1.enable_limiter = *settings_.use_agc_limiter;
+ }
+ if (settings_.agc_target_level) {
+ apm_config.gain_controller1.target_level_dbfs = *settings_.agc_target_level;
+ }
+ if (settings_.agc_compression_gain) {
+ apm_config.gain_controller1.compression_gain_db =
+ *settings_.agc_compression_gain;
+ }
+ if (settings_.use_analog_agc) {
+ apm_config.gain_controller1.analog_gain_controller.enabled =
+ *settings_.use_analog_agc;
+ }
+ if (settings_.analog_agc_disable_digital_adaptive) {
+ apm_config.gain_controller1.analog_gain_controller.enable_digital_adaptive =
+ *settings_.analog_agc_disable_digital_adaptive;
+ }
+
+ if (settings_.use_ed) {
+ apm_config.residual_echo_detector.enabled = *settings_.use_ed;
+ }
+
+ if (settings_.maximum_internal_processing_rate) {
+ apm_config.pipeline.maximum_internal_processing_rate =
+ *settings_.maximum_internal_processing_rate;
+ }
+
+ if (settings_.use_ns) {
+ apm_config.noise_suppression.enabled = *settings_.use_ns;
+ }
+ if (settings_.ns_level) {
+ const int level = *settings_.ns_level;
+ RTC_CHECK_GE(level, 0);
+ RTC_CHECK_LE(level, 3);
+ apm_config.noise_suppression.level =
+ static_cast<AudioProcessing::Config::NoiseSuppression::Level>(level);
+ }
+ if (settings_.ns_analysis_on_linear_aec_output) {
+ apm_config.noise_suppression.analyze_linear_aec_output_when_available =
+ *settings_.ns_analysis_on_linear_aec_output;
+ }
+
+ ap_->ApplyConfig(apm_config);
+
+ if (settings_.use_ts) {
+ ap_->set_stream_key_pressed(*settings_.use_ts);
+ }
+
+ if (settings_.aec_dump_output_filename) {
+ ap_->AttachAecDump(AecDumpFactory::Create(
+ *settings_.aec_dump_output_filename, -1, &worker_queue_));
+ }
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/audioproc_float_impl.cc b/modules/audio_processing/test/audioproc_float_impl.cc
new file mode 100644
index 0000000..e4a3c67
--- /dev/null
+++ b/modules/audio_processing/test/audioproc_float_impl.cc
@@ -0,0 +1,849 @@
+/*
+ * 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 "modules/audio_processing/test/audioproc_float_impl.h"
+
+#include <string.h>
+
+#include <iostream>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/flags/flag.h"
+#include "absl/flags/parse.h"
+#include "absl/strings/string_view.h"
+#include "modules/audio_processing/include/audio_processing.h"
+#include "modules/audio_processing/test/aec_dump_based_simulator.h"
+#include "modules/audio_processing/test/audio_processing_simulator.h"
+#include "modules/audio_processing/test/wav_based_simulator.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/strings/string_builder.h"
+#include "system_wrappers/include/field_trial.h"
+
+constexpr int kParameterNotSpecifiedValue = -10000;
+
+ABSL_FLAG(std::string, dump_input, "", "Aec dump input filename");
+ABSL_FLAG(std::string, dump_output, "", "Aec dump output filename");
+ABSL_FLAG(std::string, i, "", "Forward stream input wav filename");
+ABSL_FLAG(std::string, o, "", "Forward stream output wav filename");
+ABSL_FLAG(std::string, ri, "", "Reverse stream input wav filename");
+ABSL_FLAG(std::string, ro, "", "Reverse stream output wav filename");
+ABSL_FLAG(std::string,
+ artificial_nearend,
+ "",
+ "Artificial nearend wav filename");
+ABSL_FLAG(std::string, linear_aec_output, "", "Linear AEC output wav filename");
+ABSL_FLAG(int,
+ output_num_channels,
+ kParameterNotSpecifiedValue,
+ "Number of forward stream output channels");
+ABSL_FLAG(int,
+ reverse_output_num_channels,
+ kParameterNotSpecifiedValue,
+ "Number of Reverse stream output channels");
+ABSL_FLAG(int,
+ output_sample_rate_hz,
+ kParameterNotSpecifiedValue,
+ "Forward stream output sample rate in Hz");
+ABSL_FLAG(int,
+ reverse_output_sample_rate_hz,
+ kParameterNotSpecifiedValue,
+ "Reverse stream output sample rate in Hz");
+ABSL_FLAG(bool,
+ fixed_interface,
+ false,
+ "Use the fixed interface when operating on wav files");
+ABSL_FLAG(int,
+ aec,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate(0) the echo canceller");
+ABSL_FLAG(int,
+ aecm,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate(0) the mobile echo controller");
+ABSL_FLAG(int,
+ ed,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate (0) the residual echo detector");
+ABSL_FLAG(std::string,
+ ed_graph,
+ "",
+ "Output filename for graph of echo likelihood");
+ABSL_FLAG(int,
+ agc,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate(0) the AGC");
+ABSL_FLAG(int,
+ agc2,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate(0) the AGC2");
+ABSL_FLAG(int,
+ pre_amplifier,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate(0) the pre amplifier");
+ABSL_FLAG(
+ int,
+ capture_level_adjustment,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate(0) the capture level adjustment functionality");
+ABSL_FLAG(int,
+ analog_mic_gain_emulation,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate(0) the analog mic gain emulation in the "
+ "production (non-test) code.");
+ABSL_FLAG(int,
+ hpf,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate(0) the high-pass filter");
+ABSL_FLAG(int,
+ ns,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate(0) the noise suppressor");
+ABSL_FLAG(int,
+ ts,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate(0) the transient suppressor");
+ABSL_FLAG(int,
+ analog_agc,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate(0) the transient suppressor");
+ABSL_FLAG(int,
+ vad,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate(0) the voice activity detector");
+ABSL_FLAG(int,
+ le,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate(0) the level estimator");
+ABSL_FLAG(bool,
+ all_default,
+ false,
+ "Activate all of the default components (will be overridden by any "
+ "other settings)");
+ABSL_FLAG(int,
+ analog_agc_disable_digital_adaptive,
+ kParameterNotSpecifiedValue,
+ "Force-deactivate (1) digital adaptation in "
+ "experimental AGC. Digital adaptation is active by default (0).");
+ABSL_FLAG(int,
+ agc_mode,
+ kParameterNotSpecifiedValue,
+ "Specify the AGC mode (0-2)");
+ABSL_FLAG(int,
+ agc_target_level,
+ kParameterNotSpecifiedValue,
+ "Specify the AGC target level (0-31)");
+ABSL_FLAG(int,
+ agc_limiter,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate(0) the level estimator");
+ABSL_FLAG(int,
+ agc_compression_gain,
+ kParameterNotSpecifiedValue,
+ "Specify the AGC compression gain (0-90)");
+ABSL_FLAG(int,
+ agc2_enable_adaptive_gain,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate(0) the AGC2 adaptive gain");
+ABSL_FLAG(float,
+ agc2_fixed_gain_db,
+ kParameterNotSpecifiedValue,
+ "AGC2 fixed gain (dB) to apply");
+ABSL_FLAG(std::string,
+ agc2_adaptive_level_estimator,
+ "RMS",
+ "AGC2 adaptive digital level estimator to use [RMS, peak]");
+ABSL_FLAG(float,
+ pre_amplifier_gain_factor,
+ kParameterNotSpecifiedValue,
+ "Pre-amplifier gain factor (linear) to apply");
+ABSL_FLAG(float,
+ pre_gain_factor,
+ kParameterNotSpecifiedValue,
+ "Pre-gain factor (linear) to apply in the capture level adjustment");
+ABSL_FLAG(float,
+ post_gain_factor,
+ kParameterNotSpecifiedValue,
+ "Post-gain factor (linear) to apply in the capture level adjustment");
+ABSL_FLAG(float,
+ analog_mic_gain_emulation_initial_level,
+ kParameterNotSpecifiedValue,
+ "Emulated analog mic level to apply initially in the production "
+ "(non-test) code.");
+ABSL_FLAG(int,
+ ns_level,
+ kParameterNotSpecifiedValue,
+ "Specify the NS level (0-3)");
+ABSL_FLAG(int,
+ ns_analysis_on_linear_aec_output,
+ kParameterNotSpecifiedValue,
+ "Specifies whether the noise suppression analysis is done on the "
+ "linear AEC output");
+ABSL_FLAG(int,
+ maximum_internal_processing_rate,
+ kParameterNotSpecifiedValue,
+ "Set a maximum internal processing rate (32000 or 48000) to override "
+ "the default rate");
+ABSL_FLAG(int,
+ stream_delay,
+ kParameterNotSpecifiedValue,
+ "Specify the stream delay in ms to use");
+ABSL_FLAG(int,
+ use_stream_delay,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate(0) reporting the stream delay");
+ABSL_FLAG(int,
+ stream_drift_samples,
+ kParameterNotSpecifiedValue,
+ "Specify the number of stream drift samples to use");
+ABSL_FLAG(int,
+ initial_mic_level,
+ 100,
+ "Initial mic level (0-255) for the analog mic gain simulation in the "
+ "test code");
+ABSL_FLAG(int,
+ simulate_mic_gain,
+ 0,
+ "Activate (1) or deactivate(0) the analog mic gain simulation in the "
+ "test code");
+ABSL_FLAG(int,
+ multi_channel_render,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate(0) multi-channel render processing in "
+ "APM pipeline");
+ABSL_FLAG(int,
+ multi_channel_capture,
+ kParameterNotSpecifiedValue,
+ "Activate (1) or deactivate(0) multi-channel capture processing in "
+ "APM pipeline");
+ABSL_FLAG(int,
+ simulated_mic_kind,
+ kParameterNotSpecifiedValue,
+ "Specify which microphone kind to use for microphone simulation");
+ABSL_FLAG(int,
+ frame_for_sending_capture_output_used_false,
+ kParameterNotSpecifiedValue,
+ "Capture frame index for sending a runtime setting for that the "
+ "capture output is not used.");
+ABSL_FLAG(int,
+ frame_for_sending_capture_output_used_true,
+ kParameterNotSpecifiedValue,
+ "Capture frame index for sending a runtime setting for that the "
+ "capture output is used.");
+ABSL_FLAG(bool, performance_report, false, "Report the APM performance ");
+ABSL_FLAG(std::string,
+ performance_report_output_file,
+ "",
+ "Generate a CSV file with the API call durations");
+ABSL_FLAG(bool, verbose, false, "Produce verbose output");
+ABSL_FLAG(bool,
+ quiet,
+ false,
+ "Avoid producing information about the progress.");
+ABSL_FLAG(bool,
+ bitexactness_report,
+ false,
+ "Report bitexactness for aec dump result reproduction");
+ABSL_FLAG(bool,
+ discard_settings_in_aecdump,
+ false,
+ "Discard any config settings specified in the aec dump");
+ABSL_FLAG(bool,
+ store_intermediate_output,
+ false,
+ "Creates new output files after each init");
+ABSL_FLAG(std::string,
+ custom_call_order_file,
+ "",
+ "Custom process API call order file");
+ABSL_FLAG(std::string,
+ output_custom_call_order_file,
+ "",
+ "Generate custom process API call order file from AEC dump");
+ABSL_FLAG(bool,
+ print_aec_parameter_values,
+ false,
+ "Print parameter values used in AEC in JSON-format");
+ABSL_FLAG(std::string,
+ aec_settings,
+ "",
+ "File in JSON-format with custom AEC settings");
+ABSL_FLAG(bool,
+ dump_data,
+ false,
+ "Dump internal data during the call (requires build flag)");
+ABSL_FLAG(std::string,
+ dump_data_output_dir,
+ "",
+ "Internal data dump output directory");
+ABSL_FLAG(int,
+ dump_set_to_use,
+ kParameterNotSpecifiedValue,
+ "Specifies the dump set to use (if not all the dump sets will "
+ "be used");
+ABSL_FLAG(bool,
+ analyze,
+ false,
+ "Only analyze the call setup behavior (no processing)");
+ABSL_FLAG(float,
+ dump_start_seconds,
+ kParameterNotSpecifiedValue,
+ "Start of when to dump data (seconds).");
+ABSL_FLAG(float,
+ dump_end_seconds,
+ kParameterNotSpecifiedValue,
+ "End of when to dump data (seconds).");
+ABSL_FLAG(int,
+ dump_start_frame,
+ kParameterNotSpecifiedValue,
+ "Start of when to dump data (frames).");
+ABSL_FLAG(int,
+ dump_end_frame,
+ kParameterNotSpecifiedValue,
+ "End of when to dump data (frames).");
+ABSL_FLAG(int,
+ init_to_process,
+ kParameterNotSpecifiedValue,
+ "Init index to process.");
+
+ABSL_FLAG(bool,
+ float_wav_output,
+ false,
+ "Produce floating point wav output files.");
+
+ABSL_FLAG(std::string,
+ force_fieldtrials,
+ "",
+ "Field trials control experimental feature code which can be forced. "
+ "E.g. running with --force_fieldtrials=WebRTC-FooFeature/Enable/"
+ " will assign the group Enable to field trial WebRTC-FooFeature.");
+
+namespace webrtc {
+namespace test {
+namespace {
+
+const char kUsageDescription[] =
+ "Usage: audioproc_f [options] -i <input.wav>\n"
+ " or\n"
+ " audioproc_f [options] -dump_input <aec_dump>\n"
+ "\n\n"
+ "Command-line tool to simulate a call using the audio "
+ "processing module, either based on wav files or "
+ "protobuf debug dump recordings.\n";
+
+std::vector<std::string> GetAgc2AdaptiveLevelEstimatorNames() {
+ return {"RMS", "peak"};
+}
+
+void SetSettingIfSpecified(const std::string& value,
+ absl::optional<std::string>* parameter) {
+ if (value.compare("") != 0) {
+ *parameter = value;
+ }
+}
+
+void SetSettingIfSpecified(int value, absl::optional<int>* parameter) {
+ if (value != kParameterNotSpecifiedValue) {
+ *parameter = value;
+ }
+}
+
+void SetSettingIfSpecified(float value, absl::optional<float>* parameter) {
+ constexpr float kFloatParameterNotSpecifiedValue =
+ kParameterNotSpecifiedValue;
+ if (value != kFloatParameterNotSpecifiedValue) {
+ *parameter = value;
+ }
+}
+
+void SetSettingIfFlagSet(int32_t flag, absl::optional<bool>* parameter) {
+ if (flag == 0) {
+ *parameter = false;
+ } else if (flag == 1) {
+ *parameter = true;
+ }
+}
+
+AudioProcessing::Config::GainController2::LevelEstimator
+MapAgc2AdaptiveLevelEstimator(absl::string_view name) {
+ if (name.compare("RMS") == 0) {
+ return AudioProcessing::Config::GainController2::LevelEstimator::kRms;
+ }
+ if (name.compare("peak") == 0) {
+ return AudioProcessing::Config::GainController2::LevelEstimator::kPeak;
+ }
+ auto concat_strings =
+ [](const std::vector<std::string>& strings) -> std::string {
+ rtc::StringBuilder ss;
+ for (const auto& s : strings) {
+ ss << " " << s;
+ }
+ return ss.Release();
+ };
+ RTC_CHECK(false)
+ << "Invalid value for agc2_adaptive_level_estimator, valid options:"
+ << concat_strings(GetAgc2AdaptiveLevelEstimatorNames()) << ".";
+}
+
+SimulationSettings CreateSettings() {
+ SimulationSettings settings;
+ if (absl::GetFlag(FLAGS_all_default)) {
+ settings.use_le = true;
+ settings.use_vad = true;
+ settings.use_ts = true;
+ settings.use_analog_agc = true;
+ settings.use_ns = true;
+ settings.use_hpf = true;
+ settings.use_agc = true;
+ settings.use_agc2 = false;
+ settings.use_pre_amplifier = false;
+ settings.use_aec = true;
+ settings.use_aecm = false;
+ settings.use_ed = false;
+ }
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_dump_input),
+ &settings.aec_dump_input_filename);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_dump_output),
+ &settings.aec_dump_output_filename);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_i), &settings.input_filename);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_o), &settings.output_filename);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_ri),
+ &settings.reverse_input_filename);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_ro),
+ &settings.reverse_output_filename);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_artificial_nearend),
+ &settings.artificial_nearend_filename);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_linear_aec_output),
+ &settings.linear_aec_output_filename);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_output_num_channels),
+ &settings.output_num_channels);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_reverse_output_num_channels),
+ &settings.reverse_output_num_channels);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_output_sample_rate_hz),
+ &settings.output_sample_rate_hz);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_reverse_output_sample_rate_hz),
+ &settings.reverse_output_sample_rate_hz);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_aec), &settings.use_aec);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_aecm), &settings.use_aecm);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_ed), &settings.use_ed);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_ed_graph),
+ &settings.ed_graph_output_filename);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_agc), &settings.use_agc);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_agc2), &settings.use_agc2);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_pre_amplifier),
+ &settings.use_pre_amplifier);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_capture_level_adjustment),
+ &settings.use_capture_level_adjustment);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_analog_mic_gain_emulation),
+ &settings.use_analog_mic_gain_emulation);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_hpf), &settings.use_hpf);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_ns), &settings.use_ns);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_ts), &settings.use_ts);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_analog_agc),
+ &settings.use_analog_agc);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_vad), &settings.use_vad);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_le), &settings.use_le);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_analog_agc_disable_digital_adaptive),
+ &settings.analog_agc_disable_digital_adaptive);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_agc_mode), &settings.agc_mode);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_agc_target_level),
+ &settings.agc_target_level);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_agc_limiter),
+ &settings.use_agc_limiter);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_agc_compression_gain),
+ &settings.agc_compression_gain);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_agc2_enable_adaptive_gain),
+ &settings.agc2_use_adaptive_gain);
+
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_agc2_fixed_gain_db),
+ &settings.agc2_fixed_gain_db);
+ settings.agc2_adaptive_level_estimator = MapAgc2AdaptiveLevelEstimator(
+ absl::GetFlag(FLAGS_agc2_adaptive_level_estimator));
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_pre_amplifier_gain_factor),
+ &settings.pre_amplifier_gain_factor);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_pre_gain_factor),
+ &settings.pre_gain_factor);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_post_gain_factor),
+ &settings.post_gain_factor);
+ SetSettingIfSpecified(
+ absl::GetFlag(FLAGS_analog_mic_gain_emulation_initial_level),
+ &settings.analog_mic_gain_emulation_initial_level);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_ns_level), &settings.ns_level);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_ns_analysis_on_linear_aec_output),
+ &settings.ns_analysis_on_linear_aec_output);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_maximum_internal_processing_rate),
+ &settings.maximum_internal_processing_rate);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_stream_delay),
+ &settings.stream_delay);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_use_stream_delay),
+ &settings.use_stream_delay);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_custom_call_order_file),
+ &settings.call_order_input_filename);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_output_custom_call_order_file),
+ &settings.call_order_output_filename);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_aec_settings),
+ &settings.aec_settings_filename);
+ settings.initial_mic_level = absl::GetFlag(FLAGS_initial_mic_level);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_multi_channel_render),
+ &settings.multi_channel_render);
+ SetSettingIfFlagSet(absl::GetFlag(FLAGS_multi_channel_capture),
+ &settings.multi_channel_capture);
+ settings.simulate_mic_gain = absl::GetFlag(FLAGS_simulate_mic_gain);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_simulated_mic_kind),
+ &settings.simulated_mic_kind);
+ SetSettingIfSpecified(
+ absl::GetFlag(FLAGS_frame_for_sending_capture_output_used_false),
+ &settings.frame_for_sending_capture_output_used_false);
+ SetSettingIfSpecified(
+ absl::GetFlag(FLAGS_frame_for_sending_capture_output_used_true),
+ &settings.frame_for_sending_capture_output_used_true);
+ settings.report_performance = absl::GetFlag(FLAGS_performance_report);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_performance_report_output_file),
+ &settings.performance_report_output_filename);
+ settings.use_verbose_logging = absl::GetFlag(FLAGS_verbose);
+ settings.use_quiet_output = absl::GetFlag(FLAGS_quiet);
+ settings.report_bitexactness = absl::GetFlag(FLAGS_bitexactness_report);
+ settings.discard_all_settings_in_aecdump =
+ absl::GetFlag(FLAGS_discard_settings_in_aecdump);
+ settings.fixed_interface = absl::GetFlag(FLAGS_fixed_interface);
+ settings.store_intermediate_output =
+ absl::GetFlag(FLAGS_store_intermediate_output);
+ settings.print_aec_parameter_values =
+ absl::GetFlag(FLAGS_print_aec_parameter_values);
+ settings.dump_internal_data = absl::GetFlag(FLAGS_dump_data);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_dump_data_output_dir),
+ &settings.dump_internal_data_output_dir);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_dump_set_to_use),
+ &settings.dump_set_to_use);
+ settings.wav_output_format = absl::GetFlag(FLAGS_float_wav_output)
+ ? WavFile::SampleFormat::kFloat
+ : WavFile::SampleFormat::kInt16;
+
+ settings.analysis_only = absl::GetFlag(FLAGS_analyze);
+
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_dump_start_frame),
+ &settings.dump_start_frame);
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_dump_end_frame),
+ &settings.dump_end_frame);
+
+ constexpr int kFramesPerSecond = 100;
+ absl::optional<float> start_seconds;
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_dump_start_seconds),
+ &start_seconds);
+ if (start_seconds) {
+ settings.dump_start_frame = *start_seconds * kFramesPerSecond;
+ }
+
+ absl::optional<float> end_seconds;
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_dump_end_seconds), &end_seconds);
+ if (end_seconds) {
+ settings.dump_end_frame = *end_seconds * kFramesPerSecond;
+ }
+
+ SetSettingIfSpecified(absl::GetFlag(FLAGS_init_to_process),
+ &settings.init_to_process);
+
+ return settings;
+}
+
+void ReportConditionalErrorAndExit(bool condition, const std::string& message) {
+ if (condition) {
+ std::cerr << message << std::endl;
+ exit(1);
+ }
+}
+
+void PerformBasicParameterSanityChecks(
+ const SimulationSettings& settings,
+ bool pre_constructed_ap_provided,
+ bool pre_constructed_ap_builder_provided) {
+ if (settings.input_filename || settings.reverse_input_filename) {
+ ReportConditionalErrorAndExit(
+ !!settings.aec_dump_input_filename,
+ "Error: The aec dump file cannot be specified "
+ "together with input wav files!\n");
+
+ ReportConditionalErrorAndExit(
+ !!settings.aec_dump_input_string,
+ "Error: The aec dump input string cannot be specified "
+ "together with input wav files!\n");
+
+ ReportConditionalErrorAndExit(!!settings.artificial_nearend_filename,
+ "Error: The artificial nearend cannot be "
+ "specified together with input wav files!\n");
+
+ ReportConditionalErrorAndExit(!settings.input_filename,
+ "Error: When operating at wav files, the "
+ "input wav filename must be "
+ "specified!\n");
+
+ ReportConditionalErrorAndExit(
+ settings.reverse_output_filename && !settings.reverse_input_filename,
+ "Error: When operating at wav files, the reverse input wav filename "
+ "must be specified if the reverse output wav filename is specified!\n");
+ } else {
+ ReportConditionalErrorAndExit(
+ !settings.aec_dump_input_filename && !settings.aec_dump_input_string,
+ "Error: Either the aec dump input file, the wav "
+ "input file or the aec dump input string must be specified!\n");
+ ReportConditionalErrorAndExit(
+ settings.aec_dump_input_filename && settings.aec_dump_input_string,
+ "Error: The aec dump input file cannot be specified together with the "
+ "aec dump input string!\n");
+ }
+
+ ReportConditionalErrorAndExit(settings.use_aec && !(*settings.use_aec) &&
+ settings.linear_aec_output_filename,
+ "Error: The linear AEC ouput filename cannot "
+ "be specified without the AEC being active");
+
+ ReportConditionalErrorAndExit(
+ settings.use_aec && *settings.use_aec && settings.use_aecm &&
+ *settings.use_aecm,
+ "Error: The AEC and the AECM cannot be activated at the same time!\n");
+
+ ReportConditionalErrorAndExit(
+ settings.output_sample_rate_hz && *settings.output_sample_rate_hz <= 0,
+ "Error: --output_sample_rate_hz must be positive!\n");
+
+ ReportConditionalErrorAndExit(
+ settings.reverse_output_sample_rate_hz &&
+ settings.output_sample_rate_hz &&
+ *settings.output_sample_rate_hz <= 0,
+ "Error: --reverse_output_sample_rate_hz must be positive!\n");
+
+ ReportConditionalErrorAndExit(
+ settings.output_num_channels && *settings.output_num_channels <= 0,
+ "Error: --output_num_channels must be positive!\n");
+
+ ReportConditionalErrorAndExit(
+ settings.reverse_output_num_channels &&
+ *settings.reverse_output_num_channels <= 0,
+ "Error: --reverse_output_num_channels must be positive!\n");
+
+ ReportConditionalErrorAndExit(
+ settings.agc_target_level && ((*settings.agc_target_level) < 0 ||
+ (*settings.agc_target_level) > 31),
+ "Error: --agc_target_level must be specified between 0 and 31.\n");
+
+ ReportConditionalErrorAndExit(
+ settings.agc_compression_gain && ((*settings.agc_compression_gain) < 0 ||
+ (*settings.agc_compression_gain) > 90),
+ "Error: --agc_compression_gain must be specified between 0 and 90.\n");
+
+ ReportConditionalErrorAndExit(
+ settings.agc2_fixed_gain_db && ((*settings.agc2_fixed_gain_db) < 0 ||
+ (*settings.agc2_fixed_gain_db) > 90),
+ "Error: --agc2_fixed_gain_db must be specified between 0 and 90.\n");
+
+ ReportConditionalErrorAndExit(
+ settings.ns_level &&
+ ((*settings.ns_level) < 0 || (*settings.ns_level) > 3),
+ "Error: --ns_level must be specified between 0 and 3.\n");
+
+ ReportConditionalErrorAndExit(
+ settings.report_bitexactness && !settings.aec_dump_input_filename,
+ "Error: --bitexactness_report can only be used when operating on an "
+ "aecdump\n");
+
+ ReportConditionalErrorAndExit(
+ settings.call_order_input_filename && settings.aec_dump_input_filename,
+ "Error: --custom_call_order_file cannot be used when operating on an "
+ "aecdump\n");
+
+ ReportConditionalErrorAndExit(
+ (settings.initial_mic_level < 0 || settings.initial_mic_level > 255),
+ "Error: --initial_mic_level must be specified between 0 and 255.\n");
+
+ ReportConditionalErrorAndExit(
+ settings.simulated_mic_kind && !settings.simulate_mic_gain,
+ "Error: --simulated_mic_kind cannot be specified when mic simulation is "
+ "disabled\n");
+
+ ReportConditionalErrorAndExit(
+ !settings.simulated_mic_kind && settings.simulate_mic_gain,
+ "Error: --simulated_mic_kind must be specified when mic simulation is "
+ "enabled\n");
+
+ auto valid_wav_name = [](const std::string& wav_file_name) {
+ if (wav_file_name.size() < 5) {
+ return false;
+ }
+ if ((wav_file_name.compare(wav_file_name.size() - 4, 4, ".wav") == 0) ||
+ (wav_file_name.compare(wav_file_name.size() - 4, 4, ".WAV") == 0)) {
+ return true;
+ }
+ return false;
+ };
+
+ ReportConditionalErrorAndExit(
+ settings.input_filename && (!valid_wav_name(*settings.input_filename)),
+ "Error: --i must be a valid .wav file name.\n");
+
+ ReportConditionalErrorAndExit(
+ settings.output_filename && (!valid_wav_name(*settings.output_filename)),
+ "Error: --o must be a valid .wav file name.\n");
+
+ ReportConditionalErrorAndExit(
+ settings.reverse_input_filename &&
+ (!valid_wav_name(*settings.reverse_input_filename)),
+ "Error: --ri must be a valid .wav file name.\n");
+
+ ReportConditionalErrorAndExit(
+ settings.reverse_output_filename &&
+ (!valid_wav_name(*settings.reverse_output_filename)),
+ "Error: --ro must be a valid .wav file name.\n");
+
+ ReportConditionalErrorAndExit(
+ settings.artificial_nearend_filename &&
+ !valid_wav_name(*settings.artificial_nearend_filename),
+ "Error: --artifical_nearend must be a valid .wav file name.\n");
+
+ ReportConditionalErrorAndExit(
+ settings.linear_aec_output_filename &&
+ (!valid_wav_name(*settings.linear_aec_output_filename)),
+ "Error: --linear_aec_output must be a valid .wav file name.\n");
+
+ ReportConditionalErrorAndExit(
+ WEBRTC_APM_DEBUG_DUMP == 0 && settings.dump_internal_data,
+ "Error: --dump_data cannot be set without proper build support.\n");
+
+ ReportConditionalErrorAndExit(settings.init_to_process &&
+ *settings.init_to_process != 1 &&
+ !settings.aec_dump_input_filename,
+ "Error: --init_to_process must be set to 1 for "
+ "wav-file based simulations.\n");
+
+ ReportConditionalErrorAndExit(
+ !settings.init_to_process &&
+ (settings.dump_start_frame || settings.dump_end_frame),
+ "Error: --init_to_process must be set when specifying a start and/or end "
+ "frame for when to dump internal data.\n");
+
+ ReportConditionalErrorAndExit(
+ !settings.dump_internal_data &&
+ settings.dump_internal_data_output_dir.has_value(),
+ "Error: --dump_data_output_dir cannot be set without --dump_data.\n");
+
+ ReportConditionalErrorAndExit(
+ !settings.aec_dump_input_filename &&
+ settings.call_order_output_filename.has_value(),
+ "Error: --output_custom_call_order_file needs an AEC dump input file.\n");
+
+ ReportConditionalErrorAndExit(
+ (!settings.use_pre_amplifier || !(*settings.use_pre_amplifier)) &&
+ settings.pre_amplifier_gain_factor.has_value(),
+ "Error: --pre_amplifier_gain_factor needs --pre_amplifier to be "
+ "specified and set.\n");
+
+ ReportConditionalErrorAndExit(
+ pre_constructed_ap_provided && pre_constructed_ap_builder_provided,
+ "Error: The AudioProcessing and the AudioProcessingBuilder cannot both "
+ "be specified at the same time.\n");
+
+ ReportConditionalErrorAndExit(
+ settings.aec_settings_filename && pre_constructed_ap_provided,
+ "Error: The aec_settings_filename cannot be specified when a "
+ "pre-constructed audio processing object is provided.\n");
+
+ ReportConditionalErrorAndExit(
+ settings.aec_settings_filename && pre_constructed_ap_provided,
+ "Error: The print_aec_parameter_values cannot be set when a "
+ "pre-constructed audio processing object is provided.\n");
+
+ if (settings.linear_aec_output_filename && pre_constructed_ap_provided) {
+ std::cout << "Warning: For the linear AEC output to be stored, this must "
+ "be configured in the AEC that is part of the provided "
+ "AudioProcessing object."
+ << std::endl;
+ }
+}
+
+int RunSimulation(rtc::scoped_refptr<AudioProcessing> audio_processing,
+ std::unique_ptr<AudioProcessingBuilder> ap_builder,
+ int argc,
+ char* argv[],
+ absl::string_view input_aecdump,
+ std::vector<float>* processed_capture_samples) {
+ std::vector<char*> args = absl::ParseCommandLine(argc, argv);
+ if (args.size() != 1) {
+ printf("%s", kUsageDescription);
+ return 1;
+ }
+ // InitFieldTrialsFromString stores the char*, so the char array must
+ // outlive the application.
+ const std::string field_trials = absl::GetFlag(FLAGS_force_fieldtrials);
+ webrtc::field_trial::InitFieldTrialsFromString(field_trials.c_str());
+
+ SimulationSettings settings = CreateSettings();
+ if (!input_aecdump.empty()) {
+ settings.aec_dump_input_string = input_aecdump;
+ settings.processed_capture_samples = processed_capture_samples;
+ RTC_CHECK(settings.processed_capture_samples);
+ }
+ PerformBasicParameterSanityChecks(settings, !!audio_processing, !!ap_builder);
+ std::unique_ptr<AudioProcessingSimulator> processor;
+
+ if (settings.aec_dump_input_filename || settings.aec_dump_input_string) {
+ processor.reset(new AecDumpBasedSimulator(
+ settings, std::move(audio_processing), std::move(ap_builder)));
+ } else {
+ processor.reset(new WavBasedSimulator(settings, std::move(audio_processing),
+ std::move(ap_builder)));
+ }
+
+ if (settings.analysis_only) {
+ processor->Analyze();
+ } else {
+ processor->Process();
+ }
+
+ if (settings.report_performance) {
+ processor->GetApiCallStatistics().PrintReport();
+ }
+ if (settings.performance_report_output_filename) {
+ processor->GetApiCallStatistics().WriteReportToFile(
+ *settings.performance_report_output_filename);
+ }
+
+ if (settings.report_bitexactness && settings.aec_dump_input_filename) {
+ if (processor->OutputWasBitexact()) {
+ std::cout << "The processing was bitexact.";
+ } else {
+ std::cout << "The processing was not bitexact.";
+ }
+ }
+
+ return 0;
+}
+
+} // namespace
+
+int AudioprocFloatImpl(rtc::scoped_refptr<AudioProcessing> audio_processing,
+ int argc,
+ char* argv[]) {
+ return RunSimulation(
+ std::move(audio_processing), /*ap_builder=*/nullptr, argc, argv,
+ /*input_aecdump=*/"", /*processed_capture_samples=*/nullptr);
+}
+
+int AudioprocFloatImpl(std::unique_ptr<AudioProcessingBuilder> ap_builder,
+ int argc,
+ char* argv[],
+ absl::string_view input_aecdump,
+ std::vector<float>* processed_capture_samples) {
+ return RunSimulation(/*audio_processing=*/nullptr, std::move(ap_builder),
+ argc, argv, input_aecdump, processed_capture_samples);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/bitexactness_tools.cc b/modules/audio_processing/test/bitexactness_tools.cc
new file mode 100644
index 0000000..f245c2c
--- /dev/null
+++ b/modules/audio_processing/test/bitexactness_tools.cc
@@ -0,0 +1,148 @@
+/*
+ * 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 "modules/audio_processing/test/bitexactness_tools.h"
+
+#include <math.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "api/array_view.h"
+#include "test/testsupport/file_utils.h"
+
+namespace webrtc {
+namespace test {
+
+std::string GetApmRenderTestVectorFileName(int sample_rate_hz) {
+ switch (sample_rate_hz) {
+ case 8000:
+ return ResourcePath("far8_stereo", "pcm");
+ case 16000:
+ return ResourcePath("far16_stereo", "pcm");
+ case 32000:
+ return ResourcePath("far32_stereo", "pcm");
+ case 48000:
+ return ResourcePath("far48_stereo", "pcm");
+ default:
+ RTC_NOTREACHED();
+ }
+ return "";
+}
+
+std::string GetApmCaptureTestVectorFileName(int sample_rate_hz) {
+ switch (sample_rate_hz) {
+ case 8000:
+ return ResourcePath("near8_stereo", "pcm");
+ case 16000:
+ return ResourcePath("near16_stereo", "pcm");
+ case 32000:
+ return ResourcePath("near32_stereo", "pcm");
+ case 48000:
+ return ResourcePath("near48_stereo", "pcm");
+ default:
+ RTC_NOTREACHED();
+ }
+ return "";
+}
+
+void ReadFloatSamplesFromStereoFile(size_t samples_per_channel,
+ size_t num_channels,
+ InputAudioFile* stereo_pcm_file,
+ rtc::ArrayView<float> data) {
+ RTC_DCHECK_LE(num_channels, 2);
+ RTC_DCHECK_EQ(data.size(), samples_per_channel * num_channels);
+ std::vector<int16_t> read_samples(samples_per_channel * 2);
+ stereo_pcm_file->Read(samples_per_channel * 2, read_samples.data());
+
+ // Convert samples to float and discard any channels not needed.
+ for (size_t sample = 0; sample < samples_per_channel; ++sample) {
+ for (size_t channel = 0; channel < num_channels; ++channel) {
+ data[sample * num_channels + channel] =
+ read_samples[sample * 2 + channel] / 32768.0f;
+ }
+ }
+}
+
+::testing::AssertionResult VerifyDeinterleavedArray(
+ size_t samples_per_channel,
+ size_t num_channels,
+ rtc::ArrayView<const float> reference,
+ rtc::ArrayView<const float> output,
+ float element_error_bound) {
+ // Form vectors to compare the reference to. Only the first values of the
+ // outputs are compared in order not having to specify all preceeding frames
+ // as testvectors.
+ const size_t reference_frame_length =
+ rtc::CheckedDivExact(reference.size(), num_channels);
+
+ std::vector<float> output_to_verify;
+ for (size_t channel_no = 0; channel_no < num_channels; ++channel_no) {
+ output_to_verify.insert(output_to_verify.end(),
+ output.begin() + channel_no * samples_per_channel,
+ output.begin() + channel_no * samples_per_channel +
+ reference_frame_length);
+ }
+
+ return VerifyArray(reference, output_to_verify, element_error_bound);
+}
+
+::testing::AssertionResult VerifyArray(rtc::ArrayView<const float> reference,
+ rtc::ArrayView<const float> output,
+ float element_error_bound) {
+ // The vectors are deemed to be bitexact only if
+ // a) output have a size at least as long as the reference.
+ // b) the samples in the reference are bitexact with the corresponding samples
+ // in the output.
+
+ bool equal = true;
+ if (output.size() < reference.size()) {
+ equal = false;
+ } else {
+ // Compare the first samples in the vectors.
+ for (size_t k = 0; k < reference.size(); ++k) {
+ if (fabs(output[k] - reference[k]) > element_error_bound) {
+ equal = false;
+ break;
+ }
+ }
+ }
+
+ if (equal) {
+ return ::testing::AssertionSuccess();
+ }
+
+ // Lambda function that produces a formatted string with the data in the
+ // vector.
+ auto print_vector_in_c_format = [](rtc::ArrayView<const float> v,
+ size_t num_values_to_print) {
+ std::string s = "{ ";
+ for (size_t k = 0; k < std::min(num_values_to_print, v.size()); ++k) {
+ s += std::to_string(v[k]) + "f";
+ s += (k < (num_values_to_print - 1)) ? ", " : "";
+ }
+ return s + " }";
+ };
+
+ // If the vectors are deemed not to be similar, return a report of the
+ // difference.
+ return ::testing::AssertionFailure()
+ << std::endl
+ << " Actual values : "
+ << print_vector_in_c_format(output,
+ std::min(output.size(), reference.size()))
+ << std::endl
+ << " Expected values: "
+ << print_vector_in_c_format(reference, reference.size()) << std::endl;
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/conversational_speech/config.cc b/modules/audio_processing/test/conversational_speech/config.cc
new file mode 100644
index 0000000..76d3de8
--- /dev/null
+++ b/modules/audio_processing/test/conversational_speech/config.cc
@@ -0,0 +1,31 @@
+/*
+ * 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 "modules/audio_processing/test/conversational_speech/config.h"
+
+namespace webrtc {
+namespace test {
+namespace conversational_speech {
+
+const std::string& Config::audiotracks_path() const {
+ return audiotracks_path_;
+}
+
+const std::string& Config::timing_filepath() const {
+ return timing_filepath_;
+}
+
+const std::string& Config::output_path() const {
+ return output_path_;
+}
+
+} // namespace conversational_speech
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/conversational_speech/generator.cc b/modules/audio_processing/test/conversational_speech/generator.cc
new file mode 100644
index 0000000..d0bc2f2
--- /dev/null
+++ b/modules/audio_processing/test/conversational_speech/generator.cc
@@ -0,0 +1,89 @@
+/*
+ * 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 <iostream>
+#include <vector>
+
+#include <memory>
+
+#include "absl/flags/flag.h"
+#include "absl/flags/parse.h"
+#include "modules/audio_processing/test/conversational_speech/config.h"
+#include "modules/audio_processing/test/conversational_speech/multiend_call.h"
+#include "modules/audio_processing/test/conversational_speech/simulator.h"
+#include "modules/audio_processing/test/conversational_speech/timing.h"
+#include "modules/audio_processing/test/conversational_speech/wavreader_factory.h"
+#include "test/testsupport/file_utils.h"
+
+ABSL_FLAG(std::string, i, "", "Directory containing the speech turn wav files");
+ABSL_FLAG(std::string, t, "", "Path to the timing text file");
+ABSL_FLAG(std::string, o, "", "Output wav files destination path");
+
+namespace webrtc {
+namespace test {
+namespace {
+
+const char kUsageDescription[] =
+ "Usage: conversational_speech_generator\n"
+ " -i <path/to/source/audiotracks>\n"
+ " -t <path/to/timing_file.txt>\n"
+ " -o <output/path>\n"
+ "\n\n"
+ "Command-line tool to generate multiple-end audio tracks to simulate "
+ "conversational speech with two or more participants.\n";
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ std::vector<char*> args = absl::ParseCommandLine(argc, argv);
+ if (args.size() != 1) {
+ printf("%s", kUsageDescription);
+ return 1;
+ }
+ RTC_CHECK(DirExists(absl::GetFlag(FLAGS_i)));
+ RTC_CHECK(FileExists(absl::GetFlag(FLAGS_t)));
+ RTC_CHECK(DirExists(absl::GetFlag(FLAGS_o)));
+
+ conversational_speech::Config config(
+ absl::GetFlag(FLAGS_i), absl::GetFlag(FLAGS_t), absl::GetFlag(FLAGS_o));
+
+ // Load timing.
+ std::vector<conversational_speech::Turn> timing =
+ conversational_speech::LoadTiming(config.timing_filepath());
+
+ // Parse timing and audio tracks.
+ auto wavreader_factory =
+ std::make_unique<conversational_speech::WavReaderFactory>();
+ conversational_speech::MultiEndCall multiend_call(
+ timing, config.audiotracks_path(), std::move(wavreader_factory));
+
+ // Generate output audio tracks.
+ auto generated_audiotrack_pairs =
+ conversational_speech::Simulate(multiend_call, config.output_path());
+
+ // Show paths to created audio tracks.
+ std::cout << "Output files:" << std::endl;
+ for (const auto& output_paths_entry : *generated_audiotrack_pairs) {
+ std::cout << " speaker: " << output_paths_entry.first << std::endl;
+ std::cout << " near end: " << output_paths_entry.second.near_end
+ << std::endl;
+ std::cout << " far end: " << output_paths_entry.second.far_end
+ << std::endl;
+ }
+
+ return 0;
+}
+
+} // namespace test
+} // namespace webrtc
+
+int main(int argc, char* argv[]) {
+ return webrtc::test::main(argc, argv);
+}
diff --git a/modules/audio_processing/test/conversational_speech/generator_unittest.cc b/modules/audio_processing/test/conversational_speech/generator_unittest.cc
new file mode 100644
index 0000000..c7a459c
--- /dev/null
+++ b/modules/audio_processing/test/conversational_speech/generator_unittest.cc
@@ -0,0 +1,674 @@
+/*
+ * 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.
+ */
+
+// This file consists of unit tests for webrtc::test::conversational_speech
+// members. Part of them focus on accepting or rejecting different
+// conversational speech setups. A setup is defined by a set of audio tracks and
+// timing information).
+// The docstring at the beginning of each TEST(ConversationalSpeechTest,
+// MultiEndCallSetup*) function looks like the drawing below and indicates which
+// setup is tested.
+//
+// Accept:
+// A 0****.....
+// B .....1****
+//
+// The drawing indicates the following:
+// - the illustrated setup should be accepted,
+// - there are two speakers (namely, A and B),
+// - A is the first speaking, B is the second one,
+// - each character after the speaker's letter indicates a time unit (e.g., 100
+// ms),
+// - "*" indicates speaking, "." listening,
+// - numbers indicate the turn index in std::vector<Turn>.
+//
+// Note that the same speaker can appear in multiple lines in order to depict
+// cases in which there are wrong offsets leading to self cross-talk (which is
+// rejected).
+
+// MSVC++ requires this to be set before any other includes to get M_PI.
+#define _USE_MATH_DEFINES
+
+#include <stdio.h>
+
+#include <cmath>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "common_audio/wav_file.h"
+#include "modules/audio_processing/test/conversational_speech/config.h"
+#include "modules/audio_processing/test/conversational_speech/mock_wavreader_factory.h"
+#include "modules/audio_processing/test/conversational_speech/multiend_call.h"
+#include "modules/audio_processing/test/conversational_speech/simulator.h"
+#include "modules/audio_processing/test/conversational_speech/timing.h"
+#include "modules/audio_processing/test/conversational_speech/wavreader_factory.h"
+#include "rtc_base/logging.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/testsupport/file_utils.h"
+
+namespace webrtc {
+namespace test {
+namespace {
+
+using conversational_speech::LoadTiming;
+using conversational_speech::MockWavReaderFactory;
+using conversational_speech::MultiEndCall;
+using conversational_speech::SaveTiming;
+using conversational_speech::Turn;
+using conversational_speech::WavReaderFactory;
+
+const char* const audiotracks_path = "/path/to/audiotracks";
+const char* const timing_filepath = "/path/to/timing_file.txt";
+const char* const output_path = "/path/to/output_dir";
+
+const std::vector<Turn> expected_timing = {
+ {"A", "a1", 0, 0}, {"B", "b1", 0, 0}, {"A", "a2", 100, 0},
+ {"B", "b2", -200, 0}, {"A", "a3", 0, 0}, {"A", "a3", 0, 0},
+};
+const std::size_t kNumberOfTurns = expected_timing.size();
+
+// Default arguments for MockWavReaderFactory ctor.
+// Fake audio track parameters.
+constexpr int kDefaultSampleRate = 48000;
+const std::map<std::string, const MockWavReaderFactory::Params>
+ kDefaultMockWavReaderFactoryParamsMap = {
+ {"t300", {kDefaultSampleRate, 1u, 14400u}}, // Mono, 0.3 seconds.
+ {"t500", {kDefaultSampleRate, 1u, 24000u}}, // Mono, 0.5 seconds.
+ {"t1000", {kDefaultSampleRate, 1u, 48000u}}, // Mono, 1.0 seconds.
+ {"sr8000", {8000, 1u, 8000u}}, // 8kHz sample rate, mono, 1 second.
+ {"sr16000", {16000, 1u, 16000u}}, // 16kHz sample rate, mono, 1 second.
+ {"sr16000_stereo", {16000, 2u, 16000u}}, // Like sr16000, but stereo.
+};
+const MockWavReaderFactory::Params& kDefaultMockWavReaderFactoryParams =
+ kDefaultMockWavReaderFactoryParamsMap.at("t500");
+
+std::unique_ptr<MockWavReaderFactory> CreateMockWavReaderFactory() {
+ return std::unique_ptr<MockWavReaderFactory>(
+ new MockWavReaderFactory(kDefaultMockWavReaderFactoryParams,
+ kDefaultMockWavReaderFactoryParamsMap));
+}
+
+void CreateSineWavFile(const std::string& filepath,
+ const MockWavReaderFactory::Params& params,
+ float frequency = 440.0f) {
+ // Create samples.
+ constexpr double two_pi = 2.0 * M_PI;
+ std::vector<int16_t> samples(params.num_samples);
+ for (std::size_t i = 0; i < params.num_samples; ++i) {
+ // TODO(alessiob): the produced tone is not pure, improve.
+ samples[i] = std::lround(
+ 32767.0f * std::sin(two_pi * i * frequency / params.sample_rate));
+ }
+
+ // Write samples.
+ WavWriter wav_writer(filepath, params.sample_rate, params.num_channels);
+ wav_writer.WriteSamples(samples.data(), params.num_samples);
+}
+
+// Parameters to generate audio tracks with CreateSineWavFile.
+struct SineAudioTrackParams {
+ MockWavReaderFactory::Params params;
+ float frequency;
+};
+
+// Creates a temporary directory in which sine audio tracks are written.
+std::string CreateTemporarySineAudioTracks(
+ const std::map<std::string, SineAudioTrackParams>& sine_tracks_params) {
+ // Create temporary directory.
+ std::string temp_directory =
+ OutputPath() + "TempConversationalSpeechAudioTracks";
+ CreateDir(temp_directory);
+
+ // Create sine tracks.
+ for (const auto& it : sine_tracks_params) {
+ const std::string temp_filepath = JoinFilename(temp_directory, it.first);
+ CreateSineWavFile(temp_filepath, it.second.params, it.second.frequency);
+ }
+
+ return temp_directory;
+}
+
+void CheckAudioTrackParams(const WavReaderFactory& wav_reader_factory,
+ const std::string& filepath,
+ const MockWavReaderFactory::Params& expeted_params) {
+ auto wav_reader = wav_reader_factory.Create(filepath);
+ EXPECT_EQ(expeted_params.sample_rate, wav_reader->SampleRate());
+ EXPECT_EQ(expeted_params.num_channels, wav_reader->NumChannels());
+ EXPECT_EQ(expeted_params.num_samples, wav_reader->NumSamples());
+}
+
+void DeleteFolderAndContents(const std::string& dir) {
+ if (!DirExists(dir)) {
+ return;
+ }
+ absl::optional<std::vector<std::string>> dir_content = ReadDirectory(dir);
+ EXPECT_TRUE(dir_content);
+ for (const auto& path : *dir_content) {
+ if (DirExists(path)) {
+ DeleteFolderAndContents(path);
+ } else if (FileExists(path)) {
+ // TODO(alessiob): Wrap with EXPECT_TRUE() once webrtc:7769 bug fixed.
+ RemoveFile(path);
+ } else {
+ FAIL();
+ }
+ }
+ // TODO(alessiob): Wrap with EXPECT_TRUE() once webrtc:7769 bug fixed.
+ RemoveDir(dir);
+}
+
+} // namespace
+
+using ::testing::_;
+
+TEST(ConversationalSpeechTest, Settings) {
+ const conversational_speech::Config config(audiotracks_path, timing_filepath,
+ output_path);
+
+ // Test getters.
+ EXPECT_EQ(audiotracks_path, config.audiotracks_path());
+ EXPECT_EQ(timing_filepath, config.timing_filepath());
+ EXPECT_EQ(output_path, config.output_path());
+}
+
+TEST(ConversationalSpeechTest, TimingSaveLoad) {
+ // Save test timing.
+ const std::string temporary_filepath =
+ TempFilename(OutputPath(), "TempTimingTestFile");
+ SaveTiming(temporary_filepath, expected_timing);
+
+ // Create a std::vector<Turn> instance by loading from file.
+ std::vector<Turn> actual_timing = LoadTiming(temporary_filepath);
+ RemoveFile(temporary_filepath);
+
+ // Check size.
+ EXPECT_EQ(expected_timing.size(), actual_timing.size());
+
+ // Check Turn instances.
+ for (size_t index = 0; index < expected_timing.size(); ++index) {
+ EXPECT_EQ(expected_timing[index], actual_timing[index])
+ << "turn #" << index << " not matching";
+ }
+}
+
+TEST(ConversationalSpeechTest, MultiEndCallCreate) {
+ auto mock_wavreader_factory = CreateMockWavReaderFactory();
+
+ // There are 5 unique audio tracks to read.
+ EXPECT_CALL(*mock_wavreader_factory, Create(_)).Times(5);
+
+ // Inject the mock wav reader factory.
+ conversational_speech::MultiEndCall multiend_call(
+ expected_timing, audiotracks_path, std::move(mock_wavreader_factory));
+ EXPECT_TRUE(multiend_call.valid());
+
+ // Test.
+ EXPECT_EQ(2u, multiend_call.speaker_names().size());
+ EXPECT_EQ(5u, multiend_call.audiotrack_readers().size());
+ EXPECT_EQ(6u, multiend_call.speaking_turns().size());
+}
+
+TEST(ConversationalSpeechTest, MultiEndCallSetupDifferentSampleRates) {
+ const std::vector<Turn> timing = {
+ {"A", "sr8000", 0, 0},
+ {"B", "sr16000", 0, 0},
+ };
+ auto mock_wavreader_factory = CreateMockWavReaderFactory();
+
+ // There are two unique audio tracks to read.
+ EXPECT_CALL(*mock_wavreader_factory, Create(::testing::_)).Times(2);
+
+ MultiEndCall multiend_call(timing, audiotracks_path,
+ std::move(mock_wavreader_factory));
+ EXPECT_FALSE(multiend_call.valid());
+}
+
+TEST(ConversationalSpeechTest, MultiEndCallSetupMultipleChannels) {
+ const std::vector<Turn> timing = {
+ {"A", "sr16000_stereo", 0, 0},
+ {"B", "sr16000_stereo", 0, 0},
+ };
+ auto mock_wavreader_factory = CreateMockWavReaderFactory();
+
+ // There is one unique audio track to read.
+ EXPECT_CALL(*mock_wavreader_factory, Create(::testing::_)).Times(1);
+
+ MultiEndCall multiend_call(timing, audiotracks_path,
+ std::move(mock_wavreader_factory));
+ EXPECT_FALSE(multiend_call.valid());
+}
+
+TEST(ConversationalSpeechTest,
+ MultiEndCallSetupDifferentSampleRatesAndMultipleNumChannels) {
+ const std::vector<Turn> timing = {
+ {"A", "sr8000", 0, 0},
+ {"B", "sr16000_stereo", 0, 0},
+ };
+ auto mock_wavreader_factory = CreateMockWavReaderFactory();
+
+ // There are two unique audio tracks to read.
+ EXPECT_CALL(*mock_wavreader_factory, Create(::testing::_)).Times(2);
+
+ MultiEndCall multiend_call(timing, audiotracks_path,
+ std::move(mock_wavreader_factory));
+ EXPECT_FALSE(multiend_call.valid());
+}
+
+TEST(ConversationalSpeechTest, MultiEndCallSetupFirstOffsetNegative) {
+ const std::vector<Turn> timing = {
+ {"A", "t500", -100, 0},
+ {"B", "t500", 0, 0},
+ };
+ auto mock_wavreader_factory = CreateMockWavReaderFactory();
+
+ // There is one unique audio track to read.
+ EXPECT_CALL(*mock_wavreader_factory, Create(_)).Times(1);
+
+ conversational_speech::MultiEndCall multiend_call(
+ timing, audiotracks_path, std::move(mock_wavreader_factory));
+ EXPECT_FALSE(multiend_call.valid());
+}
+
+TEST(ConversationalSpeechTest, MultiEndCallSetupSimple) {
+ // Accept:
+ // A 0****.....
+ // B .....1****
+ constexpr std::size_t expected_duration = kDefaultSampleRate;
+ const std::vector<Turn> timing = {
+ {"A", "t500", 0, 0},
+ {"B", "t500", 0, 0},
+ };
+ auto mock_wavreader_factory = CreateMockWavReaderFactory();
+
+ // There is one unique audio track to read.
+ EXPECT_CALL(*mock_wavreader_factory, Create(_)).Times(1);
+
+ conversational_speech::MultiEndCall multiend_call(
+ timing, audiotracks_path, std::move(mock_wavreader_factory));
+ EXPECT_TRUE(multiend_call.valid());
+
+ // Test.
+ EXPECT_EQ(2u, multiend_call.speaker_names().size());
+ EXPECT_EQ(1u, multiend_call.audiotrack_readers().size());
+ EXPECT_EQ(2u, multiend_call.speaking_turns().size());
+ EXPECT_EQ(expected_duration, multiend_call.total_duration_samples());
+}
+
+TEST(ConversationalSpeechTest, MultiEndCallSetupPause) {
+ // Accept:
+ // A 0****.......
+ // B .......1****
+ constexpr std::size_t expected_duration = kDefaultSampleRate * 1.2;
+ const std::vector<Turn> timing = {
+ {"A", "t500", 0, 0},
+ {"B", "t500", 200, 0},
+ };
+ auto mock_wavreader_factory = CreateMockWavReaderFactory();
+
+ // There is one unique audio track to read.
+ EXPECT_CALL(*mock_wavreader_factory, Create(_)).Times(1);
+
+ conversational_speech::MultiEndCall multiend_call(
+ timing, audiotracks_path, std::move(mock_wavreader_factory));
+ EXPECT_TRUE(multiend_call.valid());
+
+ // Test.
+ EXPECT_EQ(2u, multiend_call.speaker_names().size());
+ EXPECT_EQ(1u, multiend_call.audiotrack_readers().size());
+ EXPECT_EQ(2u, multiend_call.speaking_turns().size());
+ EXPECT_EQ(expected_duration, multiend_call.total_duration_samples());
+}
+
+TEST(ConversationalSpeechTest, MultiEndCallSetupCrossTalk) {
+ // Accept:
+ // A 0****....
+ // B ....1****
+ constexpr std::size_t expected_duration = kDefaultSampleRate * 0.9;
+ const std::vector<Turn> timing = {
+ {"A", "t500", 0, 0},
+ {"B", "t500", -100, 0},
+ };
+ auto mock_wavreader_factory = CreateMockWavReaderFactory();
+
+ // There is one unique audio track to read.
+ EXPECT_CALL(*mock_wavreader_factory, Create(_)).Times(1);
+
+ conversational_speech::MultiEndCall multiend_call(
+ timing, audiotracks_path, std::move(mock_wavreader_factory));
+ EXPECT_TRUE(multiend_call.valid());
+
+ // Test.
+ EXPECT_EQ(2u, multiend_call.speaker_names().size());
+ EXPECT_EQ(1u, multiend_call.audiotrack_readers().size());
+ EXPECT_EQ(2u, multiend_call.speaking_turns().size());
+ EXPECT_EQ(expected_duration, multiend_call.total_duration_samples());
+}
+
+TEST(ConversationalSpeechTest, MultiEndCallSetupInvalidOrder) {
+ // Reject:
+ // A ..0****
+ // B .1****. The n-th turn cannot start before the (n-1)-th one.
+ const std::vector<Turn> timing = {
+ {"A", "t500", 200, 0},
+ {"B", "t500", -600, 0},
+ };
+ auto mock_wavreader_factory = CreateMockWavReaderFactory();
+
+ // There is one unique audio track to read.
+ EXPECT_CALL(*mock_wavreader_factory, Create(_)).Times(1);
+
+ conversational_speech::MultiEndCall multiend_call(
+ timing, audiotracks_path, std::move(mock_wavreader_factory));
+ EXPECT_FALSE(multiend_call.valid());
+}
+
+TEST(ConversationalSpeechTest, MultiEndCallSetupCrossTalkThree) {
+ // Accept:
+ // A 0****2****...
+ // B ...1*********
+ constexpr std::size_t expected_duration = kDefaultSampleRate * 1.3;
+ const std::vector<Turn> timing = {
+ {"A", "t500", 0, 0},
+ {"B", "t1000", -200, 0},
+ {"A", "t500", -800, 0},
+ };
+ auto mock_wavreader_factory = CreateMockWavReaderFactory();
+
+ // There are two unique audio tracks to read.
+ EXPECT_CALL(*mock_wavreader_factory, Create(_)).Times(2);
+
+ conversational_speech::MultiEndCall multiend_call(
+ timing, audiotracks_path, std::move(mock_wavreader_factory));
+ EXPECT_TRUE(multiend_call.valid());
+
+ // Test.
+ EXPECT_EQ(2u, multiend_call.speaker_names().size());
+ EXPECT_EQ(2u, multiend_call.audiotrack_readers().size());
+ EXPECT_EQ(3u, multiend_call.speaking_turns().size());
+ EXPECT_EQ(expected_duration, multiend_call.total_duration_samples());
+}
+
+TEST(ConversationalSpeechTest, MultiEndCallSetupSelfCrossTalkNearInvalid) {
+ // Reject:
+ // A 0****......
+ // A ...1****...
+ // B ......2****
+ // ^ Turn #1 overlaps with #0 which is from the same speaker.
+ const std::vector<Turn> timing = {
+ {"A", "t500", 0, 0},
+ {"A", "t500", -200, 0},
+ {"B", "t500", -200, 0},
+ };
+ auto mock_wavreader_factory = CreateMockWavReaderFactory();
+
+ // There is one unique audio track to read.
+ EXPECT_CALL(*mock_wavreader_factory, Create(_)).Times(1);
+
+ conversational_speech::MultiEndCall multiend_call(
+ timing, audiotracks_path, std::move(mock_wavreader_factory));
+ EXPECT_FALSE(multiend_call.valid());
+}
+
+TEST(ConversationalSpeechTest, MultiEndCallSetupSelfCrossTalkFarInvalid) {
+ // Reject:
+ // A 0*********
+ // B 1**.......
+ // C ...2**....
+ // A ......3**.
+ // ^ Turn #3 overlaps with #0 which is from the same speaker.
+ const std::vector<Turn> timing = {
+ {"A", "t1000", 0, 0},
+ {"B", "t300", -1000, 0},
+ {"C", "t300", 0, 0},
+ {"A", "t300", 0, 0},
+ };
+ auto mock_wavreader_factory = CreateMockWavReaderFactory();
+
+ // There are two unique audio tracks to read.
+ EXPECT_CALL(*mock_wavreader_factory, Create(_)).Times(2);
+
+ conversational_speech::MultiEndCall multiend_call(
+ timing, audiotracks_path, std::move(mock_wavreader_factory));
+ EXPECT_FALSE(multiend_call.valid());
+}
+
+TEST(ConversationalSpeechTest, MultiEndCallSetupCrossTalkMiddleValid) {
+ // Accept:
+ // A 0*********..
+ // B ..1****.....
+ // C .......2****
+ constexpr std::size_t expected_duration = kDefaultSampleRate * 1.2;
+ const std::vector<Turn> timing = {
+ {"A", "t1000", 0, 0},
+ {"B", "t500", -800, 0},
+ {"C", "t500", 0, 0},
+ };
+ auto mock_wavreader_factory = CreateMockWavReaderFactory();
+
+ // There are two unique audio tracks to read.
+ EXPECT_CALL(*mock_wavreader_factory, Create(_)).Times(2);
+
+ conversational_speech::MultiEndCall multiend_call(
+ timing, audiotracks_path, std::move(mock_wavreader_factory));
+ EXPECT_TRUE(multiend_call.valid());
+
+ // Test.
+ EXPECT_EQ(3u, multiend_call.speaker_names().size());
+ EXPECT_EQ(2u, multiend_call.audiotrack_readers().size());
+ EXPECT_EQ(3u, multiend_call.speaking_turns().size());
+ EXPECT_EQ(expected_duration, multiend_call.total_duration_samples());
+}
+
+TEST(ConversationalSpeechTest, MultiEndCallSetupCrossTalkMiddleInvalid) {
+ // Reject:
+ // A 0*********
+ // B ..1****...
+ // C ....2****.
+ // ^ Turn #2 overlaps both with #0 and #1 (cross-talk with 3+ speakers
+ // not permitted).
+ const std::vector<Turn> timing = {
+ {"A", "t1000", 0, 0},
+ {"B", "t500", -800, 0},
+ {"C", "t500", -300, 0},
+ };
+ auto mock_wavreader_factory = CreateMockWavReaderFactory();
+
+ // There are two unique audio tracks to read.
+ EXPECT_CALL(*mock_wavreader_factory, Create(_)).Times(2);
+
+ conversational_speech::MultiEndCall multiend_call(
+ timing, audiotracks_path, std::move(mock_wavreader_factory));
+ EXPECT_FALSE(multiend_call.valid());
+}
+
+TEST(ConversationalSpeechTest, MultiEndCallSetupCrossTalkMiddleAndPause) {
+ // Accept:
+ // A 0*********..
+ // B .2****......
+ // C .......3****
+ constexpr std::size_t expected_duration = kDefaultSampleRate * 1.2;
+ const std::vector<Turn> timing = {
+ {"A", "t1000", 0, 0},
+ {"B", "t500", -900, 0},
+ {"C", "t500", 100, 0},
+ };
+ auto mock_wavreader_factory = CreateMockWavReaderFactory();
+
+ // There are two unique audio tracks to read.
+ EXPECT_CALL(*mock_wavreader_factory, Create(_)).Times(2);
+
+ conversational_speech::MultiEndCall multiend_call(
+ timing, audiotracks_path, std::move(mock_wavreader_factory));
+ EXPECT_TRUE(multiend_call.valid());
+
+ // Test.
+ EXPECT_EQ(3u, multiend_call.speaker_names().size());
+ EXPECT_EQ(2u, multiend_call.audiotrack_readers().size());
+ EXPECT_EQ(3u, multiend_call.speaking_turns().size());
+ EXPECT_EQ(expected_duration, multiend_call.total_duration_samples());
+}
+
+TEST(ConversationalSpeechTest, MultiEndCallSetupCrossTalkFullOverlapValid) {
+ // Accept:
+ // A 0****
+ // B 1****
+ const std::vector<Turn> timing = {
+ {"A", "t500", 0, 0},
+ {"B", "t500", -500, 0},
+ };
+ auto mock_wavreader_factory = CreateMockWavReaderFactory();
+
+ // There is one unique audio track to read.
+ EXPECT_CALL(*mock_wavreader_factory, Create(_)).Times(1);
+
+ conversational_speech::MultiEndCall multiend_call(
+ timing, audiotracks_path, std::move(mock_wavreader_factory));
+ EXPECT_TRUE(multiend_call.valid());
+
+ // Test.
+ EXPECT_EQ(2u, multiend_call.speaker_names().size());
+ EXPECT_EQ(1u, multiend_call.audiotrack_readers().size());
+ EXPECT_EQ(2u, multiend_call.speaking_turns().size());
+}
+
+TEST(ConversationalSpeechTest, MultiEndCallSetupLongSequence) {
+ // Accept:
+ // A 0****....3****.5**.
+ // B .....1****...4**...
+ // C ......2**.......6**..
+ constexpr std::size_t expected_duration = kDefaultSampleRate * 1.9;
+ const std::vector<Turn> timing = {
+ {"A", "t500", 0, 0}, {"B", "t500", 0, 0}, {"C", "t300", -400, 0},
+ {"A", "t500", 0, 0}, {"B", "t300", -100, 0}, {"A", "t300", -100, 0},
+ {"C", "t300", -200, 0},
+ };
+ auto mock_wavreader_factory = std::unique_ptr<MockWavReaderFactory>(
+ new MockWavReaderFactory(kDefaultMockWavReaderFactoryParams,
+ kDefaultMockWavReaderFactoryParamsMap));
+
+ // There are two unique audio tracks to read.
+ EXPECT_CALL(*mock_wavreader_factory, Create(_)).Times(2);
+
+ conversational_speech::MultiEndCall multiend_call(
+ timing, audiotracks_path, std::move(mock_wavreader_factory));
+ EXPECT_TRUE(multiend_call.valid());
+
+ // Test.
+ EXPECT_EQ(3u, multiend_call.speaker_names().size());
+ EXPECT_EQ(2u, multiend_call.audiotrack_readers().size());
+ EXPECT_EQ(7u, multiend_call.speaking_turns().size());
+ EXPECT_EQ(expected_duration, multiend_call.total_duration_samples());
+}
+
+TEST(ConversationalSpeechTest, MultiEndCallSetupLongSequenceInvalid) {
+ // Reject:
+ // A 0****....3****.6**
+ // B .....1****...4**..
+ // C ......2**.....5**..
+ // ^ Turns #4, #5 and #6 overlapping (cross-talk with 3+
+ // speakers not permitted).
+ const std::vector<Turn> timing = {
+ {"A", "t500", 0, 0}, {"B", "t500", 0, 0}, {"C", "t300", -400, 0},
+ {"A", "t500", 0, 0}, {"B", "t300", -100, 0}, {"A", "t300", -200, 0},
+ {"C", "t300", -200, 0},
+ };
+ auto mock_wavreader_factory = std::unique_ptr<MockWavReaderFactory>(
+ new MockWavReaderFactory(kDefaultMockWavReaderFactoryParams,
+ kDefaultMockWavReaderFactoryParamsMap));
+
+ // There are two unique audio tracks to read.
+ EXPECT_CALL(*mock_wavreader_factory, Create(_)).Times(2);
+
+ conversational_speech::MultiEndCall multiend_call(
+ timing, audiotracks_path, std::move(mock_wavreader_factory));
+ EXPECT_FALSE(multiend_call.valid());
+}
+
+TEST(ConversationalSpeechTest, MultiEndCallWavReaderAdaptorSine) {
+ // Parameters with which wav files are created.
+ constexpr int duration_seconds = 5;
+ const int sample_rates[] = {8000, 11025, 16000, 22050, 32000, 44100, 48000};
+
+ for (int sample_rate : sample_rates) {
+ const std::string temp_filename = OutputPath() + "TempSineWavFile_" +
+ std::to_string(sample_rate) + ".wav";
+
+ // Write wav file.
+ const std::size_t num_samples = duration_seconds * sample_rate;
+ MockWavReaderFactory::Params params = {sample_rate, 1u, num_samples};
+ CreateSineWavFile(temp_filename, params);
+
+ // Load wav file and check if params match.
+ WavReaderFactory wav_reader_factory;
+ MockWavReaderFactory::Params expeted_params = {sample_rate, 1u,
+ num_samples};
+ CheckAudioTrackParams(wav_reader_factory, temp_filename, expeted_params);
+
+ // Clean up.
+ RemoveFile(temp_filename);
+ }
+}
+
+TEST(ConversationalSpeechTest, DISABLED_MultiEndCallSimulator) {
+ // Simulated call (one character corresponding to 500 ms):
+ // A 0*********...........2*********.....
+ // B ...........1*********.....3*********
+ const std::vector<Turn> expected_timing = {
+ {"A", "t5000_440.wav", 0, 0},
+ {"B", "t5000_880.wav", 500, 0},
+ {"A", "t5000_440.wav", 0, 0},
+ {"B", "t5000_880.wav", -2500, 0},
+ };
+ const std::size_t expected_duration_seconds = 18;
+
+ // Create temporary audio track files.
+ const int sample_rate = 16000;
+ const std::map<std::string, SineAudioTrackParams> sine_tracks_params = {
+ {"t5000_440.wav", {{sample_rate, 1u, sample_rate * 5}, 440.0}},
+ {"t5000_880.wav", {{sample_rate, 1u, sample_rate * 5}, 880.0}},
+ };
+ const std::string audiotracks_path =
+ CreateTemporarySineAudioTracks(sine_tracks_params);
+
+ // Set up the multi-end call.
+ auto wavreader_factory =
+ std::unique_ptr<WavReaderFactory>(new WavReaderFactory());
+ MultiEndCall multiend_call(expected_timing, audiotracks_path,
+ std::move(wavreader_factory));
+
+ // Simulate the call.
+ std::string output_path = JoinFilename(audiotracks_path, "output");
+ CreateDir(output_path);
+ RTC_LOG(LS_VERBOSE) << "simulator output path: " << output_path;
+ auto generated_audiotrak_pairs =
+ conversational_speech::Simulate(multiend_call, output_path);
+ EXPECT_EQ(2u, generated_audiotrak_pairs->size());
+
+ // Check the output.
+ WavReaderFactory wav_reader_factory;
+ const MockWavReaderFactory::Params expeted_params = {
+ sample_rate, 1u, sample_rate * expected_duration_seconds};
+ for (const auto& it : *generated_audiotrak_pairs) {
+ RTC_LOG(LS_VERBOSE) << "checking far/near-end for <" << it.first << ">";
+ CheckAudioTrackParams(wav_reader_factory, it.second.near_end,
+ expeted_params);
+ CheckAudioTrackParams(wav_reader_factory, it.second.far_end,
+ expeted_params);
+ }
+
+ // Clean.
+ EXPECT_NO_FATAL_FAILURE(DeleteFolderAndContents(audiotracks_path));
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/conversational_speech/mock_wavreader.cc b/modules/audio_processing/test/conversational_speech/mock_wavreader.cc
new file mode 100644
index 0000000..1263e93
--- /dev/null
+++ b/modules/audio_processing/test/conversational_speech/mock_wavreader.cc
@@ -0,0 +1,34 @@
+/*
+ * 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 "modules/audio_processing/test/conversational_speech/mock_wavreader.h"
+
+namespace webrtc {
+namespace test {
+namespace conversational_speech {
+
+using ::testing::Return;
+
+MockWavReader::MockWavReader(int sample_rate,
+ size_t num_channels,
+ size_t num_samples)
+ : sample_rate_(sample_rate),
+ num_channels_(num_channels),
+ num_samples_(num_samples) {
+ ON_CALL(*this, SampleRate()).WillByDefault(Return(sample_rate_));
+ ON_CALL(*this, NumChannels()).WillByDefault(Return(num_channels_));
+ ON_CALL(*this, NumSamples()).WillByDefault(Return(num_samples_));
+}
+
+MockWavReader::~MockWavReader() = default;
+
+} // namespace conversational_speech
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/conversational_speech/mock_wavreader_factory.cc b/modules/audio_processing/test/conversational_speech/mock_wavreader_factory.cc
new file mode 100644
index 0000000..96b5894
--- /dev/null
+++ b/modules/audio_processing/test/conversational_speech/mock_wavreader_factory.cc
@@ -0,0 +1,65 @@
+/*
+ * 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 "modules/audio_processing/test/conversational_speech/mock_wavreader_factory.h"
+
+#include "modules/audio_processing/test/conversational_speech/mock_wavreader.h"
+#include "rtc_base/logging.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+namespace test {
+namespace conversational_speech {
+
+using ::testing::_;
+using ::testing::Invoke;
+
+MockWavReaderFactory::MockWavReaderFactory(
+ const Params& default_params,
+ const std::map<std::string, const Params>& params)
+ : default_params_(default_params), audiotrack_names_params_(params) {
+ ON_CALL(*this, Create(_))
+ .WillByDefault(Invoke(this, &MockWavReaderFactory::CreateMock));
+}
+
+MockWavReaderFactory::MockWavReaderFactory(const Params& default_params)
+ : MockWavReaderFactory(default_params,
+ std::map<std::string, const Params>{}) {}
+
+MockWavReaderFactory::~MockWavReaderFactory() = default;
+
+std::unique_ptr<WavReaderInterface> MockWavReaderFactory::CreateMock(
+ const std::string& filepath) {
+ // Search the parameters corresponding to filepath.
+ size_t delimiter = filepath.find_last_of("/\\"); // Either windows or posix
+ std::string filename =
+ filepath.substr(delimiter == std::string::npos ? 0 : delimiter + 1);
+ const auto it = audiotrack_names_params_.find(filename);
+
+ // If not found, use default parameters.
+ if (it == audiotrack_names_params_.end()) {
+ RTC_LOG(LS_VERBOSE) << "using default parameters for " << filepath;
+ return std::unique_ptr<WavReaderInterface>(new MockWavReader(
+ default_params_.sample_rate, default_params_.num_channels,
+ default_params_.num_samples));
+ }
+
+ // Found, use the audiotrack-specific parameters.
+ RTC_LOG(LS_VERBOSE) << "using ad-hoc parameters for " << filepath;
+ RTC_LOG(LS_VERBOSE) << "sample_rate " << it->second.sample_rate;
+ RTC_LOG(LS_VERBOSE) << "num_channels " << it->second.num_channels;
+ RTC_LOG(LS_VERBOSE) << "num_samples " << it->second.num_samples;
+ return std::unique_ptr<WavReaderInterface>(new MockWavReader(
+ it->second.sample_rate, it->second.num_channels, it->second.num_samples));
+}
+
+} // namespace conversational_speech
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/conversational_speech/multiend_call.cc b/modules/audio_processing/test/conversational_speech/multiend_call.cc
new file mode 100644
index 0000000..2ae33d7
--- /dev/null
+++ b/modules/audio_processing/test/conversational_speech/multiend_call.cc
@@ -0,0 +1,192 @@
+/*
+ * 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 "modules/audio_processing/test/conversational_speech/multiend_call.h"
+
+#include <algorithm>
+#include <iterator>
+
+#include "rtc_base/logging.h"
+#include "test/testsupport/file_utils.h"
+
+namespace webrtc {
+namespace test {
+namespace conversational_speech {
+
+MultiEndCall::MultiEndCall(
+ rtc::ArrayView<const Turn> timing,
+ const std::string& audiotracks_path,
+ std::unique_ptr<WavReaderAbstractFactory> wavreader_abstract_factory)
+ : timing_(timing),
+ audiotracks_path_(audiotracks_path),
+ wavreader_abstract_factory_(std::move(wavreader_abstract_factory)),
+ valid_(false) {
+ FindSpeakerNames();
+ if (CreateAudioTrackReaders())
+ valid_ = CheckTiming();
+}
+
+MultiEndCall::~MultiEndCall() = default;
+
+void MultiEndCall::FindSpeakerNames() {
+ RTC_DCHECK(speaker_names_.empty());
+ for (const Turn& turn : timing_) {
+ speaker_names_.emplace(turn.speaker_name);
+ }
+}
+
+bool MultiEndCall::CreateAudioTrackReaders() {
+ RTC_DCHECK(audiotrack_readers_.empty());
+ sample_rate_hz_ = 0; // Sample rate will be set when reading the first track.
+ for (const Turn& turn : timing_) {
+ auto it = audiotrack_readers_.find(turn.audiotrack_file_name);
+ if (it != audiotrack_readers_.end())
+ continue;
+
+ const std::string audiotrack_file_path =
+ test::JoinFilename(audiotracks_path_, turn.audiotrack_file_name);
+
+ // Map the audiotrack file name to a new instance of WavReaderInterface.
+ std::unique_ptr<WavReaderInterface> wavreader =
+ wavreader_abstract_factory_->Create(
+ test::JoinFilename(audiotracks_path_, turn.audiotrack_file_name));
+
+ if (sample_rate_hz_ == 0) {
+ sample_rate_hz_ = wavreader->SampleRate();
+ } else if (sample_rate_hz_ != wavreader->SampleRate()) {
+ RTC_LOG(LS_ERROR)
+ << "All the audio tracks should have the same sample rate.";
+ return false;
+ }
+
+ if (wavreader->NumChannels() != 1) {
+ RTC_LOG(LS_ERROR) << "Only mono audio tracks supported.";
+ return false;
+ }
+
+ audiotrack_readers_.emplace(turn.audiotrack_file_name,
+ std::move(wavreader));
+ }
+
+ return true;
+}
+
+bool MultiEndCall::CheckTiming() {
+ struct Interval {
+ size_t begin;
+ size_t end;
+ };
+ size_t number_of_turns = timing_.size();
+ auto millisecond_to_samples = [](int ms, int sr) -> int {
+ // Truncation may happen if the sampling rate is not an integer multiple
+ // of 1000 (e.g., 44100).
+ return ms * sr / 1000;
+ };
+ auto in_interval = [](size_t value, const Interval& interval) {
+ return interval.begin <= value && value < interval.end;
+ };
+ total_duration_samples_ = 0;
+ speaking_turns_.clear();
+
+ // Begin and end timestamps for the last two turns (unit: number of samples).
+ Interval second_last_turn = {0, 0};
+ Interval last_turn = {0, 0};
+
+ // Initialize map to store speaking turn indices of each speaker (used to
+ // detect self cross-talk).
+ std::map<std::string, std::vector<size_t>> speaking_turn_indices;
+ for (const std::string& speaker_name : speaker_names_) {
+ speaking_turn_indices.emplace(std::piecewise_construct,
+ std::forward_as_tuple(speaker_name),
+ std::forward_as_tuple());
+ }
+
+ // Parse turns.
+ for (size_t turn_index = 0; turn_index < number_of_turns; ++turn_index) {
+ const Turn& turn = timing_[turn_index];
+ auto it = audiotrack_readers_.find(turn.audiotrack_file_name);
+ RTC_CHECK(it != audiotrack_readers_.end())
+ << "Audio track reader not created";
+
+ // Begin and end timestamps for the current turn.
+ int offset_samples =
+ millisecond_to_samples(turn.offset, it->second->SampleRate());
+ std::size_t begin_timestamp = last_turn.end + offset_samples;
+ std::size_t end_timestamp = begin_timestamp + it->second->NumSamples();
+ RTC_LOG(LS_INFO) << "turn #" << turn_index << " " << begin_timestamp << "-"
+ << end_timestamp << " ms";
+
+ // The order is invalid if the offset is negative and its absolute value is
+ // larger then the duration of the previous turn.
+ if (offset_samples < 0 &&
+ -offset_samples > static_cast<int>(last_turn.end - last_turn.begin)) {
+ RTC_LOG(LS_ERROR) << "invalid order";
+ return false;
+ }
+
+ // Cross-talk with 3 or more speakers occurs when the beginning of the
+ // current interval falls in the last two turns.
+ if (turn_index > 1 && in_interval(begin_timestamp, last_turn) &&
+ in_interval(begin_timestamp, second_last_turn)) {
+ RTC_LOG(LS_ERROR) << "cross-talk with 3+ speakers";
+ return false;
+ }
+
+ // Append turn.
+ speaking_turns_.emplace_back(turn.speaker_name, turn.audiotrack_file_name,
+ begin_timestamp, end_timestamp, turn.gain);
+
+ // Save speaking turn index for self cross-talk detection.
+ RTC_DCHECK_EQ(speaking_turns_.size(), turn_index + 1);
+ speaking_turn_indices[turn.speaker_name].push_back(turn_index);
+
+ // Update total duration of the consversational speech.
+ if (total_duration_samples_ < end_timestamp)
+ total_duration_samples_ = end_timestamp;
+
+ // Update and continue with next turn.
+ second_last_turn = last_turn;
+ last_turn.begin = begin_timestamp;
+ last_turn.end = end_timestamp;
+ }
+
+ // Detect self cross-talk.
+ for (const std::string& speaker_name : speaker_names_) {
+ RTC_LOG(LS_INFO) << "checking self cross-talk for <" << speaker_name << ">";
+
+ // Copy all turns for this speaker to new vector.
+ std::vector<SpeakingTurn> speaking_turns_for_name;
+ std::copy_if(speaking_turns_.begin(), speaking_turns_.end(),
+ std::back_inserter(speaking_turns_for_name),
+ [&speaker_name](const SpeakingTurn& st) {
+ return st.speaker_name == speaker_name;
+ });
+
+ // Check for overlap between adjacent elements.
+ // This is a sufficient condition for self cross-talk since the intervals
+ // are sorted by begin timestamp.
+ auto overlap = std::adjacent_find(
+ speaking_turns_for_name.begin(), speaking_turns_for_name.end(),
+ [](const SpeakingTurn& a, const SpeakingTurn& b) {
+ return a.end > b.begin;
+ });
+
+ if (overlap != speaking_turns_for_name.end()) {
+ RTC_LOG(LS_ERROR) << "Self cross-talk detected";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace conversational_speech
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/conversational_speech/simulator.cc b/modules/audio_processing/test/conversational_speech/simulator.cc
new file mode 100644
index 0000000..0591252
--- /dev/null
+++ b/modules/audio_processing/test/conversational_speech/simulator.cc
@@ -0,0 +1,235 @@
+/*
+ * 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 "modules/audio_processing/test/conversational_speech/simulator.h"
+
+#include <math.h>
+
+#include <algorithm>
+#include <memory>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "api/array_view.h"
+#include "common_audio/include/audio_util.h"
+#include "common_audio/wav_file.h"
+#include "modules/audio_processing/test/conversational_speech/wavreader_interface.h"
+#include "rtc_base/constructor_magic.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/numerics/safe_conversions.h"
+#include "test/testsupport/file_utils.h"
+
+namespace webrtc {
+namespace test {
+namespace {
+
+using conversational_speech::MultiEndCall;
+using conversational_speech::SpeakerOutputFilePaths;
+using conversational_speech::WavReaderInterface;
+
+// Combines output path and speaker names to define the output file paths for
+// the near-end and far=end audio tracks.
+std::unique_ptr<std::map<std::string, SpeakerOutputFilePaths>>
+InitSpeakerOutputFilePaths(const std::set<std::string>& speaker_names,
+ const std::string& output_path) {
+ // Create map.
+ auto speaker_output_file_paths_map =
+ std::make_unique<std::map<std::string, SpeakerOutputFilePaths>>();
+
+ // Add near-end and far-end output paths into the map.
+ for (const auto& speaker_name : speaker_names) {
+ const std::string near_end_path =
+ test::JoinFilename(output_path, "s_" + speaker_name + "-near_end.wav");
+ RTC_LOG(LS_VERBOSE) << "The near-end audio track will be created in "
+ << near_end_path << ".";
+
+ const std::string far_end_path =
+ test::JoinFilename(output_path, "s_" + speaker_name + "-far_end.wav");
+ RTC_LOG(LS_VERBOSE) << "The far-end audio track will be created in "
+ << far_end_path << ".";
+
+ // Add to map.
+ speaker_output_file_paths_map->emplace(
+ std::piecewise_construct, std::forward_as_tuple(speaker_name),
+ std::forward_as_tuple(near_end_path, far_end_path));
+ }
+
+ return speaker_output_file_paths_map;
+}
+
+// Class that provides one WavWriter for the near-end and one for the far-end
+// output track of a speaker.
+class SpeakerWavWriters {
+ public:
+ SpeakerWavWriters(const SpeakerOutputFilePaths& output_file_paths,
+ int sample_rate)
+ : near_end_wav_writer_(output_file_paths.near_end, sample_rate, 1u),
+ far_end_wav_writer_(output_file_paths.far_end, sample_rate, 1u) {}
+ WavWriter* near_end_wav_writer() { return &near_end_wav_writer_; }
+ WavWriter* far_end_wav_writer() { return &far_end_wav_writer_; }
+
+ private:
+ WavWriter near_end_wav_writer_;
+ WavWriter far_end_wav_writer_;
+};
+
+// Initializes one WavWriter instance for each speaker and both the near-end and
+// far-end output tracks.
+std::unique_ptr<std::map<std::string, SpeakerWavWriters>>
+InitSpeakersWavWriters(const std::map<std::string, SpeakerOutputFilePaths>&
+ speaker_output_file_paths,
+ int sample_rate) {
+ // Create map.
+ auto speaker_wav_writers_map =
+ std::make_unique<std::map<std::string, SpeakerWavWriters>>();
+
+ // Add SpeakerWavWriters instance into the map.
+ for (auto it = speaker_output_file_paths.begin();
+ it != speaker_output_file_paths.end(); ++it) {
+ speaker_wav_writers_map->emplace(
+ std::piecewise_construct, std::forward_as_tuple(it->first),
+ std::forward_as_tuple(it->second, sample_rate));
+ }
+
+ return speaker_wav_writers_map;
+}
+
+// Reads all the samples for each audio track.
+std::unique_ptr<std::map<std::string, std::vector<int16_t>>> PreloadAudioTracks(
+ const std::map<std::string, std::unique_ptr<WavReaderInterface>>&
+ audiotrack_readers) {
+ // Create map.
+ auto audiotracks_map =
+ std::make_unique<std::map<std::string, std::vector<int16_t>>>();
+
+ // Add audio track vectors.
+ for (auto it = audiotrack_readers.begin(); it != audiotrack_readers.end();
+ ++it) {
+ // Add map entry.
+ audiotracks_map->emplace(std::piecewise_construct,
+ std::forward_as_tuple(it->first),
+ std::forward_as_tuple(it->second->NumSamples()));
+
+ // Read samples.
+ it->second->ReadInt16Samples(audiotracks_map->at(it->first));
+ }
+
+ return audiotracks_map;
+}
+
+// Writes all the values in |source_samples| via |wav_writer|. If the number of
+// previously written samples in |wav_writer| is less than |interval_begin|, it
+// adds zeros as left padding. The padding corresponds to intervals during which
+// a speaker is not active.
+void PadLeftWriteChunk(rtc::ArrayView<const int16_t> source_samples,
+ size_t interval_begin,
+ WavWriter* wav_writer) {
+ // Add left padding.
+ RTC_CHECK(wav_writer);
+ RTC_CHECK_GE(interval_begin, wav_writer->num_samples());
+ size_t padding_size = interval_begin - wav_writer->num_samples();
+ if (padding_size != 0) {
+ const std::vector<int16_t> padding(padding_size, 0);
+ wav_writer->WriteSamples(padding.data(), padding_size);
+ }
+
+ // Write source samples.
+ wav_writer->WriteSamples(source_samples.data(), source_samples.size());
+}
+
+// Appends zeros via |wav_writer|. The number of zeros is always non-negative
+// and equal to the difference between the previously written samples and
+// |pad_samples|.
+void PadRightWrite(WavWriter* wav_writer, size_t pad_samples) {
+ RTC_CHECK(wav_writer);
+ RTC_CHECK_GE(pad_samples, wav_writer->num_samples());
+ size_t padding_size = pad_samples - wav_writer->num_samples();
+ if (padding_size != 0) {
+ const std::vector<int16_t> padding(padding_size, 0);
+ wav_writer->WriteSamples(padding.data(), padding_size);
+ }
+}
+
+void ScaleSignal(rtc::ArrayView<const int16_t> source_samples,
+ int gain,
+ rtc::ArrayView<int16_t> output_samples) {
+ const float gain_linear = DbToRatio(gain);
+ RTC_DCHECK_EQ(source_samples.size(), output_samples.size());
+ std::transform(source_samples.begin(), source_samples.end(),
+ output_samples.begin(), [gain_linear](int16_t x) -> int16_t {
+ return rtc::saturated_cast<int16_t>(x * gain_linear);
+ });
+}
+
+} // namespace
+
+namespace conversational_speech {
+
+std::unique_ptr<std::map<std::string, SpeakerOutputFilePaths>> Simulate(
+ const MultiEndCall& multiend_call,
+ const std::string& output_path) {
+ // Set output file paths and initialize wav writers.
+ const auto& speaker_names = multiend_call.speaker_names();
+ auto speaker_output_file_paths =
+ InitSpeakerOutputFilePaths(speaker_names, output_path);
+ auto speakers_wav_writers = InitSpeakersWavWriters(
+ *speaker_output_file_paths, multiend_call.sample_rate());
+
+ // Preload all the input audio tracks.
+ const auto& audiotrack_readers = multiend_call.audiotrack_readers();
+ auto audiotracks = PreloadAudioTracks(audiotrack_readers);
+
+ // TODO(alessiob): When speaker_names.size() == 2, near-end and far-end
+ // across the 2 speakers are symmetric; hence, the code below could be
+ // replaced by only creating the near-end or the far-end. However, this would
+ // require to split the unit tests and document the behavior in README.md.
+ // In practice, it should not be an issue since the files are not expected to
+ // be signinificant.
+
+ // Write near-end and far-end output tracks.
+ for (const auto& speaking_turn : multiend_call.speaking_turns()) {
+ const std::string& active_speaker_name = speaking_turn.speaker_name;
+ const auto source_audiotrack =
+ audiotracks->at(speaking_turn.audiotrack_file_name);
+ std::vector<int16_t> scaled_audiotrack(source_audiotrack.size());
+ ScaleSignal(source_audiotrack, speaking_turn.gain, scaled_audiotrack);
+
+ // Write active speaker's chunk to active speaker's near-end.
+ PadLeftWriteChunk(
+ scaled_audiotrack, speaking_turn.begin,
+ speakers_wav_writers->at(active_speaker_name).near_end_wav_writer());
+
+ // Write active speaker's chunk to other participants' far-ends.
+ for (const std::string& speaker_name : speaker_names) {
+ if (speaker_name == active_speaker_name)
+ continue;
+ PadLeftWriteChunk(
+ scaled_audiotrack, speaking_turn.begin,
+ speakers_wav_writers->at(speaker_name).far_end_wav_writer());
+ }
+ }
+
+ // Finalize all the output tracks with right padding.
+ // This is required to make all the output tracks duration equal.
+ size_t duration_samples = multiend_call.total_duration_samples();
+ for (const std::string& speaker_name : speaker_names) {
+ PadRightWrite(speakers_wav_writers->at(speaker_name).near_end_wav_writer(),
+ duration_samples);
+ PadRightWrite(speakers_wav_writers->at(speaker_name).far_end_wav_writer(),
+ duration_samples);
+ }
+
+ return speaker_output_file_paths;
+}
+
+} // namespace conversational_speech
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/conversational_speech/timing.cc b/modules/audio_processing/test/conversational_speech/timing.cc
new file mode 100644
index 0000000..53c8142
--- /dev/null
+++ b/modules/audio_processing/test/conversational_speech/timing.cc
@@ -0,0 +1,71 @@
+/*
+ * 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 "modules/audio_processing/test/conversational_speech/timing.h"
+
+#include <fstream>
+#include <iostream>
+
+#include "rtc_base/string_encode.h"
+
+namespace webrtc {
+namespace test {
+namespace conversational_speech {
+
+bool Turn::operator==(const Turn& b) const {
+ return b.speaker_name == speaker_name &&
+ b.audiotrack_file_name == audiotrack_file_name && b.offset == offset &&
+ b.gain == gain;
+}
+
+std::vector<Turn> LoadTiming(const std::string& timing_filepath) {
+ // Line parser.
+ auto parse_line = [](const std::string& line) {
+ std::vector<std::string> fields;
+ rtc::split(line, ' ', &fields);
+ RTC_CHECK_GE(fields.size(), 3);
+ RTC_CHECK_LE(fields.size(), 4);
+ int gain = 0;
+ if (fields.size() == 4) {
+ gain = std::atof(fields[3].c_str());
+ }
+ return Turn(fields[0], fields[1], std::atol(fields[2].c_str()), gain);
+ };
+
+ // Init.
+ std::vector<Turn> timing;
+
+ // Parse lines.
+ std::string line;
+ std::ifstream infile(timing_filepath);
+ while (std::getline(infile, line)) {
+ if (line.empty())
+ continue;
+ timing.push_back(parse_line(line));
+ }
+ infile.close();
+
+ return timing;
+}
+
+void SaveTiming(const std::string& timing_filepath,
+ rtc::ArrayView<const Turn> timing) {
+ std::ofstream outfile(timing_filepath);
+ RTC_CHECK(outfile.is_open());
+ for (const Turn& turn : timing) {
+ outfile << turn.speaker_name << " " << turn.audiotrack_file_name << " "
+ << turn.offset << " " << turn.gain << std::endl;
+ }
+ outfile.close();
+}
+
+} // namespace conversational_speech
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/conversational_speech/wavreader_factory.cc b/modules/audio_processing/test/conversational_speech/wavreader_factory.cc
new file mode 100644
index 0000000..01e3c94
--- /dev/null
+++ b/modules/audio_processing/test/conversational_speech/wavreader_factory.cc
@@ -0,0 +1,64 @@
+/*
+ * 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 "modules/audio_processing/test/conversational_speech/wavreader_factory.h"
+
+#include <cstddef>
+
+#include "api/array_view.h"
+#include "common_audio/wav_file.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace test {
+namespace {
+
+using conversational_speech::WavReaderInterface;
+
+class WavReaderAdaptor final : public WavReaderInterface {
+ public:
+ explicit WavReaderAdaptor(const std::string& filepath)
+ : wav_reader_(filepath) {}
+ ~WavReaderAdaptor() override = default;
+
+ size_t ReadFloatSamples(rtc::ArrayView<float> samples) override {
+ return wav_reader_.ReadSamples(samples.size(), samples.begin());
+ }
+
+ size_t ReadInt16Samples(rtc::ArrayView<int16_t> samples) override {
+ return wav_reader_.ReadSamples(samples.size(), samples.begin());
+ }
+
+ int SampleRate() const override { return wav_reader_.sample_rate(); }
+
+ size_t NumChannels() const override { return wav_reader_.num_channels(); }
+
+ size_t NumSamples() const override { return wav_reader_.num_samples(); }
+
+ private:
+ WavReader wav_reader_;
+};
+
+} // namespace
+
+namespace conversational_speech {
+
+WavReaderFactory::WavReaderFactory() = default;
+
+WavReaderFactory::~WavReaderFactory() = default;
+
+std::unique_ptr<WavReaderInterface> WavReaderFactory::Create(
+ const std::string& filepath) const {
+ return std::unique_ptr<WavReaderAdaptor>(new WavReaderAdaptor(filepath));
+}
+
+} // namespace conversational_speech
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/debug_dump_replayer.cc b/modules/audio_processing/test/debug_dump_replayer.cc
new file mode 100644
index 0000000..754b42d
--- /dev/null
+++ b/modules/audio_processing/test/debug_dump_replayer.cc
@@ -0,0 +1,246 @@
+/*
+ * 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 "modules/audio_processing/test/debug_dump_replayer.h"
+
+#include "modules/audio_processing/test/audio_processing_builder_for_testing.h"
+#include "modules/audio_processing/test/protobuf_utils.h"
+#include "modules/audio_processing/test/runtime_setting_util.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace test {
+
+namespace {
+
+void MaybeResetBuffer(std::unique_ptr<ChannelBuffer<float>>* buffer,
+ const StreamConfig& config) {
+ auto& buffer_ref = *buffer;
+ if (!buffer_ref.get() || buffer_ref->num_frames() != config.num_frames() ||
+ buffer_ref->num_channels() != config.num_channels()) {
+ buffer_ref.reset(
+ new ChannelBuffer<float>(config.num_frames(), config.num_channels()));
+ }
+}
+
+} // namespace
+
+DebugDumpReplayer::DebugDumpReplayer()
+ : input_(nullptr), // will be created upon usage.
+ reverse_(nullptr),
+ output_(nullptr),
+ apm_(nullptr),
+ debug_file_(nullptr) {}
+
+DebugDumpReplayer::~DebugDumpReplayer() {
+ if (debug_file_)
+ fclose(debug_file_);
+}
+
+bool DebugDumpReplayer::SetDumpFile(const std::string& filename) {
+ debug_file_ = fopen(filename.c_str(), "rb");
+ LoadNextMessage();
+ return debug_file_;
+}
+
+// Get next event that has not run.
+absl::optional<audioproc::Event> DebugDumpReplayer::GetNextEvent() const {
+ if (!has_next_event_)
+ return absl::nullopt;
+ else
+ return next_event_;
+}
+
+// Run the next event. Returns the event type.
+bool DebugDumpReplayer::RunNextEvent() {
+ if (!has_next_event_)
+ return false;
+ switch (next_event_.type()) {
+ case audioproc::Event::INIT:
+ OnInitEvent(next_event_.init());
+ break;
+ case audioproc::Event::STREAM:
+ OnStreamEvent(next_event_.stream());
+ break;
+ case audioproc::Event::REVERSE_STREAM:
+ OnReverseStreamEvent(next_event_.reverse_stream());
+ break;
+ case audioproc::Event::CONFIG:
+ OnConfigEvent(next_event_.config());
+ break;
+ case audioproc::Event::RUNTIME_SETTING:
+ OnRuntimeSettingEvent(next_event_.runtime_setting());
+ break;
+ case audioproc::Event::UNKNOWN_EVENT:
+ // We do not expect to receive UNKNOWN event.
+ RTC_CHECK_NOTREACHED();
+ }
+ LoadNextMessage();
+ return true;
+}
+
+const ChannelBuffer<float>* DebugDumpReplayer::GetOutput() const {
+ return output_.get();
+}
+
+StreamConfig DebugDumpReplayer::GetOutputConfig() const {
+ return output_config_;
+}
+
+// OnInitEvent reset the input/output/reserve channel format.
+void DebugDumpReplayer::OnInitEvent(const audioproc::Init& msg) {
+ RTC_CHECK(msg.has_num_input_channels());
+ RTC_CHECK(msg.has_output_sample_rate());
+ RTC_CHECK(msg.has_num_output_channels());
+ RTC_CHECK(msg.has_reverse_sample_rate());
+ RTC_CHECK(msg.has_num_reverse_channels());
+
+ input_config_ = StreamConfig(msg.sample_rate(), msg.num_input_channels());
+ output_config_ =
+ StreamConfig(msg.output_sample_rate(), msg.num_output_channels());
+ reverse_config_ =
+ StreamConfig(msg.reverse_sample_rate(), msg.num_reverse_channels());
+
+ MaybeResetBuffer(&input_, input_config_);
+ MaybeResetBuffer(&output_, output_config_);
+ MaybeResetBuffer(&reverse_, reverse_config_);
+}
+
+// OnStreamEvent replays an input signal and verifies the output.
+void DebugDumpReplayer::OnStreamEvent(const audioproc::Stream& msg) {
+ // APM should have been created.
+ RTC_CHECK(apm_.get());
+
+ apm_->set_stream_analog_level(msg.level());
+ RTC_CHECK_EQ(AudioProcessing::kNoError,
+ apm_->set_stream_delay_ms(msg.delay()));
+
+ if (msg.has_keypress()) {
+ apm_->set_stream_key_pressed(msg.keypress());
+ } else {
+ apm_->set_stream_key_pressed(true);
+ }
+
+ RTC_CHECK_EQ(input_config_.num_channels(),
+ static_cast<size_t>(msg.input_channel_size()));
+ RTC_CHECK_EQ(input_config_.num_frames() * sizeof(float),
+ msg.input_channel(0).size());
+
+ for (int i = 0; i < msg.input_channel_size(); ++i) {
+ memcpy(input_->channels()[i], msg.input_channel(i).data(),
+ msg.input_channel(i).size());
+ }
+
+ RTC_CHECK_EQ(AudioProcessing::kNoError,
+ apm_->ProcessStream(input_->channels(), input_config_,
+ output_config_, output_->channels()));
+}
+
+void DebugDumpReplayer::OnReverseStreamEvent(
+ const audioproc::ReverseStream& msg) {
+ // APM should have been created.
+ RTC_CHECK(apm_.get());
+
+ RTC_CHECK_GT(msg.channel_size(), 0);
+ RTC_CHECK_EQ(reverse_config_.num_channels(),
+ static_cast<size_t>(msg.channel_size()));
+ RTC_CHECK_EQ(reverse_config_.num_frames() * sizeof(float),
+ msg.channel(0).size());
+
+ for (int i = 0; i < msg.channel_size(); ++i) {
+ memcpy(reverse_->channels()[i], msg.channel(i).data(),
+ msg.channel(i).size());
+ }
+
+ RTC_CHECK_EQ(
+ AudioProcessing::kNoError,
+ apm_->ProcessReverseStream(reverse_->channels(), reverse_config_,
+ reverse_config_, reverse_->channels()));
+}
+
+void DebugDumpReplayer::OnConfigEvent(const audioproc::Config& msg) {
+ MaybeRecreateApm(msg);
+ ConfigureApm(msg);
+}
+
+void DebugDumpReplayer::OnRuntimeSettingEvent(
+ const audioproc::RuntimeSetting& msg) {
+ RTC_CHECK(apm_.get());
+ ReplayRuntimeSetting(apm_.get(), msg);
+}
+
+void DebugDumpReplayer::MaybeRecreateApm(const audioproc::Config& msg) {
+ // These configurations cannot be changed on the fly.
+ Config config;
+ RTC_CHECK(msg.has_aec_delay_agnostic_enabled());
+ RTC_CHECK(msg.has_aec_extended_filter_enabled());
+
+ // We only create APM once, since changes on these fields should not
+ // happen in current implementation.
+ if (!apm_.get()) {
+ apm_.reset(AudioProcessingBuilderForTesting().Create(config));
+ }
+}
+
+void DebugDumpReplayer::ConfigureApm(const audioproc::Config& msg) {
+ AudioProcessing::Config apm_config;
+
+ // AEC2/AECM configs.
+ RTC_CHECK(msg.has_aec_enabled());
+ RTC_CHECK(msg.has_aecm_enabled());
+ apm_config.echo_canceller.enabled = msg.aec_enabled() || msg.aecm_enabled();
+ apm_config.echo_canceller.mobile_mode = msg.aecm_enabled();
+
+ // HPF configs.
+ RTC_CHECK(msg.has_hpf_enabled());
+ apm_config.high_pass_filter.enabled = msg.hpf_enabled();
+
+ // Preamp configs.
+ RTC_CHECK(msg.has_pre_amplifier_enabled());
+ apm_config.pre_amplifier.enabled = msg.pre_amplifier_enabled();
+ apm_config.pre_amplifier.fixed_gain_factor =
+ msg.pre_amplifier_fixed_gain_factor();
+
+ // NS configs.
+ RTC_CHECK(msg.has_ns_enabled());
+ RTC_CHECK(msg.has_ns_level());
+ apm_config.noise_suppression.enabled = msg.ns_enabled();
+ apm_config.noise_suppression.level =
+ static_cast<AudioProcessing::Config::NoiseSuppression::Level>(
+ msg.ns_level());
+
+ // TS configs.
+ RTC_CHECK(msg.has_transient_suppression_enabled());
+ apm_config.transient_suppression.enabled =
+ msg.transient_suppression_enabled();
+
+ // AGC configs.
+ RTC_CHECK(msg.has_agc_enabled());
+ RTC_CHECK(msg.has_agc_mode());
+ RTC_CHECK(msg.has_agc_limiter_enabled());
+ apm_config.gain_controller1.enabled = msg.agc_enabled();
+ apm_config.gain_controller1.mode =
+ static_cast<AudioProcessing::Config::GainController1::Mode>(
+ msg.agc_mode());
+ apm_config.gain_controller1.enable_limiter = msg.agc_limiter_enabled();
+ RTC_CHECK(msg.has_noise_robust_agc_enabled());
+ apm_config.gain_controller1.analog_gain_controller.enabled =
+ msg.noise_robust_agc_enabled();
+
+ apm_->ApplyConfig(apm_config);
+}
+
+void DebugDumpReplayer::LoadNextMessage() {
+ has_next_event_ =
+ debug_file_ && ReadMessageFromFile(debug_file_, &next_event_);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/debug_dump_test.cc b/modules/audio_processing/test/debug_dump_test.cc
new file mode 100644
index 0000000..2381d1e
--- /dev/null
+++ b/modules/audio_processing/test/debug_dump_test.cc
@@ -0,0 +1,553 @@
+/*
+ * 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 <stddef.h> // size_t
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "api/audio/echo_canceller3_factory.h"
+#include "modules/audio_coding/neteq/tools/resample_input_audio_file.h"
+#include "modules/audio_processing/aec_dump/aec_dump_factory.h"
+#include "modules/audio_processing/test/audio_processing_builder_for_testing.h"
+#include "modules/audio_processing/test/debug_dump_replayer.h"
+#include "modules/audio_processing/test/test_utils.h"
+#include "rtc_base/task_queue_for_test.h"
+#include "test/gtest.h"
+#include "test/testsupport/file_utils.h"
+
+namespace webrtc {
+namespace test {
+
+namespace {
+
+void MaybeResetBuffer(std::unique_ptr<ChannelBuffer<float>>* buffer,
+ const StreamConfig& config) {
+ auto& buffer_ref = *buffer;
+ if (!buffer_ref.get() || buffer_ref->num_frames() != config.num_frames() ||
+ buffer_ref->num_channels() != config.num_channels()) {
+ buffer_ref.reset(
+ new ChannelBuffer<float>(config.num_frames(), config.num_channels()));
+ }
+}
+
+class DebugDumpGenerator {
+ public:
+ DebugDumpGenerator(const std::string& input_file_name,
+ int input_rate_hz,
+ int input_channels,
+ const std::string& reverse_file_name,
+ int reverse_rate_hz,
+ int reverse_channels,
+ const Config& config,
+ const std::string& dump_file_name,
+ bool enable_pre_amplifier);
+
+ // Constructor that uses default input files.
+ explicit DebugDumpGenerator(const Config& config,
+ const AudioProcessing::Config& apm_config);
+
+ ~DebugDumpGenerator();
+
+ // Changes the sample rate of the input audio to the APM.
+ void SetInputRate(int rate_hz);
+
+ // Sets if converts stereo input signal to mono by discarding other channels.
+ void ForceInputMono(bool mono);
+
+ // Changes the sample rate of the reverse audio to the APM.
+ void SetReverseRate(int rate_hz);
+
+ // Sets if converts stereo reverse signal to mono by discarding other
+ // channels.
+ void ForceReverseMono(bool mono);
+
+ // Sets the required sample rate of the APM output.
+ void SetOutputRate(int rate_hz);
+
+ // Sets the required channels of the APM output.
+ void SetOutputChannels(int channels);
+
+ std::string dump_file_name() const { return dump_file_name_; }
+
+ void StartRecording();
+ void Process(size_t num_blocks);
+ void StopRecording();
+ AudioProcessing* apm() const { return apm_.get(); }
+
+ private:
+ static void ReadAndDeinterleave(ResampleInputAudioFile* audio,
+ int channels,
+ const StreamConfig& config,
+ float* const* buffer);
+
+ // APM input/output settings.
+ StreamConfig input_config_;
+ StreamConfig reverse_config_;
+ StreamConfig output_config_;
+
+ // Input file format.
+ const std::string input_file_name_;
+ ResampleInputAudioFile input_audio_;
+ const int input_file_channels_;
+
+ // Reverse file format.
+ const std::string reverse_file_name_;
+ ResampleInputAudioFile reverse_audio_;
+ const int reverse_file_channels_;
+
+ // Buffer for APM input/output.
+ std::unique_ptr<ChannelBuffer<float>> input_;
+ std::unique_ptr<ChannelBuffer<float>> reverse_;
+ std::unique_ptr<ChannelBuffer<float>> output_;
+
+ bool enable_pre_amplifier_;
+
+ TaskQueueForTest worker_queue_;
+ std::unique_ptr<AudioProcessing> apm_;
+
+ const std::string dump_file_name_;
+};
+
+DebugDumpGenerator::DebugDumpGenerator(const std::string& input_file_name,
+ int input_rate_hz,
+ int input_channels,
+ const std::string& reverse_file_name,
+ int reverse_rate_hz,
+ int reverse_channels,
+ const Config& config,
+ const std::string& dump_file_name,
+ bool enable_pre_amplifier)
+ : input_config_(input_rate_hz, input_channels),
+ reverse_config_(reverse_rate_hz, reverse_channels),
+ output_config_(input_rate_hz, input_channels),
+ input_audio_(input_file_name, input_rate_hz, input_rate_hz),
+ input_file_channels_(input_channels),
+ reverse_audio_(reverse_file_name, reverse_rate_hz, reverse_rate_hz),
+ reverse_file_channels_(reverse_channels),
+ input_(new ChannelBuffer<float>(input_config_.num_frames(),
+ input_config_.num_channels())),
+ reverse_(new ChannelBuffer<float>(reverse_config_.num_frames(),
+ reverse_config_.num_channels())),
+ output_(new ChannelBuffer<float>(output_config_.num_frames(),
+ output_config_.num_channels())),
+ enable_pre_amplifier_(enable_pre_amplifier),
+ worker_queue_("debug_dump_generator_worker_queue"),
+ dump_file_name_(dump_file_name) {
+ AudioProcessingBuilderForTesting apm_builder;
+ apm_.reset(apm_builder.Create(config));
+}
+
+DebugDumpGenerator::DebugDumpGenerator(
+ const Config& config,
+ const AudioProcessing::Config& apm_config)
+ : DebugDumpGenerator(ResourcePath("near32_stereo", "pcm"),
+ 32000,
+ 2,
+ ResourcePath("far32_stereo", "pcm"),
+ 32000,
+ 2,
+ config,
+ TempFilename(OutputPath(), "debug_aec"),
+ apm_config.pre_amplifier.enabled) {
+ apm_->ApplyConfig(apm_config);
+}
+
+DebugDumpGenerator::~DebugDumpGenerator() {
+ remove(dump_file_name_.c_str());
+}
+
+void DebugDumpGenerator::SetInputRate(int rate_hz) {
+ input_audio_.set_output_rate_hz(rate_hz);
+ input_config_.set_sample_rate_hz(rate_hz);
+ MaybeResetBuffer(&input_, input_config_);
+}
+
+void DebugDumpGenerator::ForceInputMono(bool mono) {
+ const int channels = mono ? 1 : input_file_channels_;
+ input_config_.set_num_channels(channels);
+ MaybeResetBuffer(&input_, input_config_);
+}
+
+void DebugDumpGenerator::SetReverseRate(int rate_hz) {
+ reverse_audio_.set_output_rate_hz(rate_hz);
+ reverse_config_.set_sample_rate_hz(rate_hz);
+ MaybeResetBuffer(&reverse_, reverse_config_);
+}
+
+void DebugDumpGenerator::ForceReverseMono(bool mono) {
+ const int channels = mono ? 1 : reverse_file_channels_;
+ reverse_config_.set_num_channels(channels);
+ MaybeResetBuffer(&reverse_, reverse_config_);
+}
+
+void DebugDumpGenerator::SetOutputRate(int rate_hz) {
+ output_config_.set_sample_rate_hz(rate_hz);
+ MaybeResetBuffer(&output_, output_config_);
+}
+
+void DebugDumpGenerator::SetOutputChannels(int channels) {
+ output_config_.set_num_channels(channels);
+ MaybeResetBuffer(&output_, output_config_);
+}
+
+void DebugDumpGenerator::StartRecording() {
+ apm_->AttachAecDump(
+ AecDumpFactory::Create(dump_file_name_.c_str(), -1, &worker_queue_));
+}
+
+void DebugDumpGenerator::Process(size_t num_blocks) {
+ for (size_t i = 0; i < num_blocks; ++i) {
+ ReadAndDeinterleave(&reverse_audio_, reverse_file_channels_,
+ reverse_config_, reverse_->channels());
+ ReadAndDeinterleave(&input_audio_, input_file_channels_, input_config_,
+ input_->channels());
+ RTC_CHECK_EQ(AudioProcessing::kNoError, apm_->set_stream_delay_ms(100));
+ apm_->set_stream_analog_level(100);
+ if (enable_pre_amplifier_) {
+ apm_->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCapturePreGain(1 + i % 10));
+ }
+ apm_->set_stream_key_pressed(i % 10 == 9);
+ RTC_CHECK_EQ(AudioProcessing::kNoError,
+ apm_->ProcessStream(input_->channels(), input_config_,
+ output_config_, output_->channels()));
+
+ RTC_CHECK_EQ(
+ AudioProcessing::kNoError,
+ apm_->ProcessReverseStream(reverse_->channels(), reverse_config_,
+ reverse_config_, reverse_->channels()));
+ }
+}
+
+void DebugDumpGenerator::StopRecording() {
+ apm_->DetachAecDump();
+}
+
+void DebugDumpGenerator::ReadAndDeinterleave(ResampleInputAudioFile* audio,
+ int channels,
+ const StreamConfig& config,
+ float* const* buffer) {
+ const size_t num_frames = config.num_frames();
+ const int out_channels = config.num_channels();
+
+ std::vector<int16_t> signal(channels * num_frames);
+
+ audio->Read(num_frames * channels, &signal[0]);
+
+ // We only allow reducing number of channels by discarding some channels.
+ RTC_CHECK_LE(out_channels, channels);
+ for (int channel = 0; channel < out_channels; ++channel) {
+ for (size_t i = 0; i < num_frames; ++i) {
+ buffer[channel][i] = S16ToFloat(signal[i * channels + channel]);
+ }
+ }
+}
+
+} // namespace
+
+class DebugDumpTest : public ::testing::Test {
+ public:
+ // VerifyDebugDump replays a debug dump using APM and verifies that the result
+ // is bit-exact-identical to the output channel in the dump. This is only
+ // guaranteed if the debug dump is started on the first frame.
+ void VerifyDebugDump(const std::string& in_filename);
+
+ private:
+ DebugDumpReplayer debug_dump_replayer_;
+};
+
+void DebugDumpTest::VerifyDebugDump(const std::string& in_filename) {
+ ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(in_filename));
+
+ while (const absl::optional<audioproc::Event> event =
+ debug_dump_replayer_.GetNextEvent()) {
+ debug_dump_replayer_.RunNextEvent();
+ if (event->type() == audioproc::Event::STREAM) {
+ const audioproc::Stream* msg = &event->stream();
+ const StreamConfig output_config = debug_dump_replayer_.GetOutputConfig();
+ const ChannelBuffer<float>* output = debug_dump_replayer_.GetOutput();
+ // Check that output of APM is bit-exact to the output in the dump.
+ ASSERT_EQ(output_config.num_channels(),
+ static_cast<size_t>(msg->output_channel_size()));
+ ASSERT_EQ(output_config.num_frames() * sizeof(float),
+ msg->output_channel(0).size());
+ for (int i = 0; i < msg->output_channel_size(); ++i) {
+ ASSERT_EQ(0,
+ memcmp(output->channels()[i], msg->output_channel(i).data(),
+ msg->output_channel(i).size()));
+ }
+ }
+ }
+}
+
+TEST_F(DebugDumpTest, SimpleCase) {
+ Config config;
+ DebugDumpGenerator generator(config, AudioProcessing::Config());
+ generator.StartRecording();
+ generator.Process(100);
+ generator.StopRecording();
+ VerifyDebugDump(generator.dump_file_name());
+}
+
+TEST_F(DebugDumpTest, ChangeInputFormat) {
+ Config config;
+ DebugDumpGenerator generator(config, AudioProcessing::Config());
+
+ generator.StartRecording();
+ generator.Process(100);
+ generator.SetInputRate(48000);
+
+ generator.ForceInputMono(true);
+ // Number of output channel should not be larger than that of input. APM will
+ // fail otherwise.
+ generator.SetOutputChannels(1);
+
+ generator.Process(100);
+ generator.StopRecording();
+ VerifyDebugDump(generator.dump_file_name());
+}
+
+TEST_F(DebugDumpTest, ChangeReverseFormat) {
+ Config config;
+ DebugDumpGenerator generator(config, AudioProcessing::Config());
+ generator.StartRecording();
+ generator.Process(100);
+ generator.SetReverseRate(48000);
+ generator.ForceReverseMono(true);
+ generator.Process(100);
+ generator.StopRecording();
+ VerifyDebugDump(generator.dump_file_name());
+}
+
+TEST_F(DebugDumpTest, ChangeOutputFormat) {
+ Config config;
+ DebugDumpGenerator generator(config, AudioProcessing::Config());
+ generator.StartRecording();
+ generator.Process(100);
+ generator.SetOutputRate(48000);
+ generator.SetOutputChannels(1);
+ generator.Process(100);
+ generator.StopRecording();
+ VerifyDebugDump(generator.dump_file_name());
+}
+
+TEST_F(DebugDumpTest, ToggleAec) {
+ Config config;
+ AudioProcessing::Config apm_config;
+ apm_config.echo_canceller.enabled = true;
+ DebugDumpGenerator generator(config, apm_config);
+ generator.StartRecording();
+ generator.Process(100);
+
+ apm_config.echo_canceller.enabled = false;
+ generator.apm()->ApplyConfig(apm_config);
+
+ generator.Process(100);
+ generator.StopRecording();
+ VerifyDebugDump(generator.dump_file_name());
+}
+
+TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringInclusive) {
+ Config config;
+ AudioProcessing::Config apm_config;
+ apm_config.echo_canceller.enabled = true;
+ apm_config.gain_controller1.analog_gain_controller.enabled = true;
+ apm_config.gain_controller1.analog_gain_controller.startup_min_volume = 0;
+ // Arbitrarily set clipping gain to 17, which will never be the default.
+ apm_config.gain_controller1.analog_gain_controller.clipped_level_min = 17;
+ DebugDumpGenerator generator(config, apm_config);
+ generator.StartRecording();
+ generator.Process(100);
+ generator.StopRecording();
+
+ DebugDumpReplayer debug_dump_replayer_;
+
+ ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(generator.dump_file_name()));
+
+ while (const absl::optional<audioproc::Event> event =
+ debug_dump_replayer_.GetNextEvent()) {
+ debug_dump_replayer_.RunNextEvent();
+ if (event->type() == audioproc::Event::CONFIG) {
+ const audioproc::Config* msg = &event->config();
+ ASSERT_TRUE(msg->has_experiments_description());
+ EXPECT_PRED_FORMAT2(::testing::IsSubstring, "EchoController",
+ msg->experiments_description().c_str());
+ EXPECT_PRED_FORMAT2(::testing::IsSubstring, "AgcClippingLevelExperiment",
+ msg->experiments_description().c_str());
+ }
+ }
+}
+
+TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringExclusive) {
+ Config config;
+ AudioProcessing::Config apm_config;
+ apm_config.echo_canceller.enabled = true;
+ DebugDumpGenerator generator(config, apm_config);
+ generator.StartRecording();
+ generator.Process(100);
+ generator.StopRecording();
+
+ DebugDumpReplayer debug_dump_replayer_;
+
+ ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(generator.dump_file_name()));
+
+ while (const absl::optional<audioproc::Event> event =
+ debug_dump_replayer_.GetNextEvent()) {
+ debug_dump_replayer_.RunNextEvent();
+ if (event->type() == audioproc::Event::CONFIG) {
+ const audioproc::Config* msg = &event->config();
+ ASSERT_TRUE(msg->has_experiments_description());
+ EXPECT_PRED_FORMAT2(::testing::IsNotSubstring,
+ "AgcClippingLevelExperiment",
+ msg->experiments_description().c_str());
+ }
+ }
+}
+
+TEST_F(DebugDumpTest, VerifyAec3ExperimentalString) {
+ Config config;
+ AudioProcessing::Config apm_config;
+ apm_config.echo_canceller.enabled = true;
+ DebugDumpGenerator generator(config, apm_config);
+ generator.StartRecording();
+ generator.Process(100);
+ generator.StopRecording();
+
+ DebugDumpReplayer debug_dump_replayer_;
+
+ ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(generator.dump_file_name()));
+
+ while (const absl::optional<audioproc::Event> event =
+ debug_dump_replayer_.GetNextEvent()) {
+ debug_dump_replayer_.RunNextEvent();
+ if (event->type() == audioproc::Event::CONFIG) {
+ const audioproc::Config* msg = &event->config();
+ ASSERT_TRUE(msg->has_experiments_description());
+ EXPECT_PRED_FORMAT2(::testing::IsSubstring, "EchoController",
+ msg->experiments_description().c_str());
+ }
+ }
+}
+
+TEST_F(DebugDumpTest, VerifyAgcClippingLevelExperimentalString) {
+ Config config;
+ AudioProcessing::Config apm_config;
+ apm_config.gain_controller1.analog_gain_controller.enabled = true;
+ apm_config.gain_controller1.analog_gain_controller.startup_min_volume = 0;
+ // Arbitrarily set clipping gain to 17, which will never be the default.
+ apm_config.gain_controller1.analog_gain_controller.clipped_level_min = 17;
+ DebugDumpGenerator generator(config, apm_config);
+ generator.StartRecording();
+ generator.Process(100);
+ generator.StopRecording();
+
+ DebugDumpReplayer debug_dump_replayer_;
+
+ ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(generator.dump_file_name()));
+
+ while (const absl::optional<audioproc::Event> event =
+ debug_dump_replayer_.GetNextEvent()) {
+ debug_dump_replayer_.RunNextEvent();
+ if (event->type() == audioproc::Event::CONFIG) {
+ const audioproc::Config* msg = &event->config();
+ ASSERT_TRUE(msg->has_experiments_description());
+ EXPECT_PRED_FORMAT2(::testing::IsSubstring, "AgcClippingLevelExperiment",
+ msg->experiments_description().c_str());
+ }
+ }
+}
+
+TEST_F(DebugDumpTest, VerifyEmptyExperimentalString) {
+ Config config;
+ DebugDumpGenerator generator(config, AudioProcessing::Config());
+ generator.StartRecording();
+ generator.Process(100);
+ generator.StopRecording();
+
+ DebugDumpReplayer debug_dump_replayer_;
+
+ ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(generator.dump_file_name()));
+
+ while (const absl::optional<audioproc::Event> event =
+ debug_dump_replayer_.GetNextEvent()) {
+ debug_dump_replayer_.RunNextEvent();
+ if (event->type() == audioproc::Event::CONFIG) {
+ const audioproc::Config* msg = &event->config();
+ ASSERT_TRUE(msg->has_experiments_description());
+ EXPECT_EQ(0u, msg->experiments_description().size());
+ }
+ }
+}
+
+// AGC is not supported on Android or iOS.
+#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
+#define MAYBE_ToggleAgc DISABLED_ToggleAgc
+#else
+#define MAYBE_ToggleAgc ToggleAgc
+#endif
+TEST_F(DebugDumpTest, MAYBE_ToggleAgc) {
+ Config config;
+ DebugDumpGenerator generator(config, AudioProcessing::Config());
+ generator.StartRecording();
+ generator.Process(100);
+
+ AudioProcessing::Config apm_config = generator.apm()->GetConfig();
+ apm_config.gain_controller1.enabled = !apm_config.gain_controller1.enabled;
+ generator.apm()->ApplyConfig(apm_config);
+
+ generator.Process(100);
+ generator.StopRecording();
+ VerifyDebugDump(generator.dump_file_name());
+}
+
+TEST_F(DebugDumpTest, ToggleNs) {
+ Config config;
+ DebugDumpGenerator generator(config, AudioProcessing::Config());
+ generator.StartRecording();
+ generator.Process(100);
+
+ AudioProcessing::Config apm_config = generator.apm()->GetConfig();
+ apm_config.noise_suppression.enabled = !apm_config.noise_suppression.enabled;
+ generator.apm()->ApplyConfig(apm_config);
+
+ generator.Process(100);
+ generator.StopRecording();
+ VerifyDebugDump(generator.dump_file_name());
+}
+
+TEST_F(DebugDumpTest, TransientSuppressionOn) {
+ Config config;
+ DebugDumpGenerator generator(config, AudioProcessing::Config());
+
+ AudioProcessing::Config apm_config = generator.apm()->GetConfig();
+ apm_config.transient_suppression.enabled = true;
+ generator.apm()->ApplyConfig(apm_config);
+
+ generator.StartRecording();
+ generator.Process(100);
+ generator.StopRecording();
+ VerifyDebugDump(generator.dump_file_name());
+}
+
+TEST_F(DebugDumpTest, PreAmplifierIsOn) {
+ Config config;
+ AudioProcessing::Config apm_config;
+ apm_config.pre_amplifier.enabled = true;
+ DebugDumpGenerator generator(config, apm_config);
+ generator.StartRecording();
+ generator.Process(100);
+ generator.StopRecording();
+ VerifyDebugDump(generator.dump_file_name());
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/echo_canceller_test_tools.cc b/modules/audio_processing/test/echo_canceller_test_tools.cc
new file mode 100644
index 0000000..1d36b95
--- /dev/null
+++ b/modules/audio_processing/test/echo_canceller_test_tools.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 "modules/audio_processing/test/echo_canceller_test_tools.h"
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+void RandomizeSampleVector(Random* random_generator, rtc::ArrayView<float> v) {
+ RandomizeSampleVector(random_generator, v,
+ /*amplitude=*/32767.f);
+}
+
+void RandomizeSampleVector(Random* random_generator,
+ rtc::ArrayView<float> v,
+ float amplitude) {
+ for (auto& v_k : v) {
+ v_k = 2 * amplitude * random_generator->Rand<float>() - amplitude;
+ }
+}
+
+template <typename T>
+void DelayBuffer<T>::Delay(rtc::ArrayView<const T> x,
+ rtc::ArrayView<T> x_delayed) {
+ RTC_DCHECK_EQ(x.size(), x_delayed.size());
+ if (buffer_.empty()) {
+ std::copy(x.begin(), x.end(), x_delayed.begin());
+ } else {
+ for (size_t k = 0; k < x.size(); ++k) {
+ x_delayed[k] = buffer_[next_insert_index_];
+ buffer_[next_insert_index_] = x[k];
+ next_insert_index_ = (next_insert_index_ + 1) % buffer_.size();
+ }
+ }
+}
+
+template class DelayBuffer<float>;
+template class DelayBuffer<int>;
+} // namespace webrtc
diff --git a/modules/audio_processing/test/echo_canceller_test_tools_unittest.cc b/modules/audio_processing/test/echo_canceller_test_tools_unittest.cc
new file mode 100644
index 0000000..164d28f
--- /dev/null
+++ b/modules/audio_processing/test/echo_canceller_test_tools_unittest.cc
@@ -0,0 +1,82 @@
+/*
+ * 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 "modules/audio_processing/test/echo_canceller_test_tools.h"
+
+#include <vector>
+
+#include "api/array_view.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/random.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(EchoCancellerTestTools, FloatDelayBuffer) {
+ constexpr size_t kDelay = 10;
+ DelayBuffer<float> delay_buffer(kDelay);
+ std::vector<float> v(1000, 0.f);
+ for (size_t k = 0; k < v.size(); ++k) {
+ v[k] = k;
+ }
+ std::vector<float> v_delayed = v;
+ constexpr size_t kBlockSize = 50;
+ for (size_t k = 0; k < rtc::CheckedDivExact(v.size(), kBlockSize); ++k) {
+ delay_buffer.Delay(
+ rtc::ArrayView<const float>(&v[k * kBlockSize], kBlockSize),
+ rtc::ArrayView<float>(&v_delayed[k * kBlockSize], kBlockSize));
+ }
+ for (size_t k = kDelay; k < v.size(); ++k) {
+ EXPECT_EQ(v[k - kDelay], v_delayed[k]);
+ }
+}
+
+TEST(EchoCancellerTestTools, IntDelayBuffer) {
+ constexpr size_t kDelay = 10;
+ DelayBuffer<int> delay_buffer(kDelay);
+ std::vector<int> v(1000, 0);
+ for (size_t k = 0; k < v.size(); ++k) {
+ v[k] = k;
+ }
+ std::vector<int> v_delayed = v;
+ const size_t kBlockSize = 50;
+ for (size_t k = 0; k < rtc::CheckedDivExact(v.size(), kBlockSize); ++k) {
+ delay_buffer.Delay(
+ rtc::ArrayView<const int>(&v[k * kBlockSize], kBlockSize),
+ rtc::ArrayView<int>(&v_delayed[k * kBlockSize], kBlockSize));
+ }
+ for (size_t k = kDelay; k < v.size(); ++k) {
+ EXPECT_EQ(v[k - kDelay], v_delayed[k]);
+ }
+}
+
+TEST(EchoCancellerTestTools, RandomizeSampleVector) {
+ Random random_generator(42U);
+ std::vector<float> v(50, 0.f);
+ std::vector<float> v_ref = v;
+ RandomizeSampleVector(&random_generator, v);
+ EXPECT_NE(v, v_ref);
+ v_ref = v;
+ RandomizeSampleVector(&random_generator, v);
+ EXPECT_NE(v, v_ref);
+}
+
+TEST(EchoCancellerTestTools, RandomizeSampleVectorWithAmplitude) {
+ Random random_generator(42U);
+ std::vector<float> v(50, 0.f);
+ RandomizeSampleVector(&random_generator, v, 1000.f);
+ EXPECT_GE(1000.f, *std::max_element(v.begin(), v.end()));
+ EXPECT_LE(-1000.f, *std::min_element(v.begin(), v.end()));
+ RandomizeSampleVector(&random_generator, v, 100.f);
+ EXPECT_GE(100.f, *std::max_element(v.begin(), v.end()));
+ EXPECT_LE(-100.f, *std::min_element(v.begin(), v.end()));
+}
+
+} // namespace webrtc
diff --git a/modules/audio_processing/test/fake_recording_device.cc b/modules/audio_processing/test/fake_recording_device.cc
new file mode 100644
index 0000000..5202014
--- /dev/null
+++ b/modules/audio_processing/test/fake_recording_device.cc
@@ -0,0 +1,190 @@
+/*
+ * 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 "modules/audio_processing/test/fake_recording_device.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "absl/types/optional.h"
+#include "modules/audio_processing/agc/gain_map_internal.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/numerics/safe_conversions.h"
+#include "rtc_base/numerics/safe_minmax.h"
+
+namespace webrtc {
+namespace test {
+
+namespace {
+
+constexpr float kFloatSampleMin = -32768.f;
+constexpr float kFloatSampleMax = 32767.0f;
+
+} // namespace
+
+// Abstract class for the different fake recording devices.
+class FakeRecordingDeviceWorker {
+ public:
+ explicit FakeRecordingDeviceWorker(const int initial_mic_level)
+ : mic_level_(initial_mic_level) {}
+ int mic_level() const { return mic_level_; }
+ void set_mic_level(const int level) { mic_level_ = level; }
+ void set_undo_mic_level(const int level) { undo_mic_level_ = level; }
+ virtual ~FakeRecordingDeviceWorker() = default;
+ virtual void ModifyBufferInt16(rtc::ArrayView<int16_t> buffer) = 0;
+ virtual void ModifyBufferFloat(ChannelBuffer<float>* buffer) = 0;
+
+ protected:
+ // Mic level to simulate.
+ int mic_level_;
+ // Optional mic level to undo.
+ absl::optional<int> undo_mic_level_;
+};
+
+namespace {
+
+// Identity fake recording device. The samples are not modified, which is
+// equivalent to a constant gain curve at 1.0 - only used for testing.
+class FakeRecordingDeviceIdentity final : public FakeRecordingDeviceWorker {
+ public:
+ explicit FakeRecordingDeviceIdentity(const int initial_mic_level)
+ : FakeRecordingDeviceWorker(initial_mic_level) {}
+ ~FakeRecordingDeviceIdentity() override = default;
+ void ModifyBufferInt16(rtc::ArrayView<int16_t> buffer) override {}
+ void ModifyBufferFloat(ChannelBuffer<float>* buffer) override {}
+};
+
+// Linear fake recording device. The gain curve is a linear function mapping the
+// mic levels range [0, 255] to [0.0, 1.0].
+class FakeRecordingDeviceLinear final : public FakeRecordingDeviceWorker {
+ public:
+ explicit FakeRecordingDeviceLinear(const int initial_mic_level)
+ : FakeRecordingDeviceWorker(initial_mic_level) {}
+ ~FakeRecordingDeviceLinear() override = default;
+ void ModifyBufferInt16(rtc::ArrayView<int16_t> buffer) override {
+ const size_t number_of_samples = buffer.size();
+ int16_t* data = buffer.data();
+ // If an undo level is specified, virtually restore the unmodified
+ // microphone level; otherwise simulate the mic gain only.
+ const float divisor =
+ (undo_mic_level_ && *undo_mic_level_ > 0) ? *undo_mic_level_ : 255.f;
+ for (size_t i = 0; i < number_of_samples; ++i) {
+ data[i] = rtc::saturated_cast<int16_t>(data[i] * mic_level_ / divisor);
+ }
+ }
+ void ModifyBufferFloat(ChannelBuffer<float>* buffer) override {
+ // If an undo level is specified, virtually restore the unmodified
+ // microphone level; otherwise simulate the mic gain only.
+ const float divisor =
+ (undo_mic_level_ && *undo_mic_level_ > 0) ? *undo_mic_level_ : 255.f;
+ for (size_t c = 0; c < buffer->num_channels(); ++c) {
+ for (size_t i = 0; i < buffer->num_frames(); ++i) {
+ buffer->channels()[c][i] =
+ rtc::SafeClamp(buffer->channels()[c][i] * mic_level_ / divisor,
+ kFloatSampleMin, kFloatSampleMax);
+ }
+ }
+ }
+};
+
+float ComputeAgc1LinearFactor(const absl::optional<int>& undo_mic_level,
+ int mic_level) {
+ // If an undo level is specified, virtually restore the unmodified
+ // microphone level; otherwise simulate the mic gain only.
+ const int undo_level =
+ (undo_mic_level && *undo_mic_level > 0) ? *undo_mic_level : 100;
+ return DbToRatio(kGainMap[mic_level] - kGainMap[undo_level]);
+}
+
+// Roughly dB-scale fake recording device. Valid levels are [0, 255]. The mic
+// applies a gain from kGainMap in agc/gain_map_internal.h.
+class FakeRecordingDeviceAgc1 final : public FakeRecordingDeviceWorker {
+ public:
+ explicit FakeRecordingDeviceAgc1(const int initial_mic_level)
+ : FakeRecordingDeviceWorker(initial_mic_level) {}
+ ~FakeRecordingDeviceAgc1() override = default;
+ void ModifyBufferInt16(rtc::ArrayView<int16_t> buffer) override {
+ const float scaling_factor =
+ ComputeAgc1LinearFactor(undo_mic_level_, mic_level_);
+ const size_t number_of_samples = buffer.size();
+ int16_t* data = buffer.data();
+ for (size_t i = 0; i < number_of_samples; ++i) {
+ data[i] = rtc::saturated_cast<int16_t>(data[i] * scaling_factor);
+ }
+ }
+ void ModifyBufferFloat(ChannelBuffer<float>* buffer) override {
+ const float scaling_factor =
+ ComputeAgc1LinearFactor(undo_mic_level_, mic_level_);
+ for (size_t c = 0; c < buffer->num_channels(); ++c) {
+ for (size_t i = 0; i < buffer->num_frames(); ++i) {
+ buffer->channels()[c][i] =
+ rtc::SafeClamp(buffer->channels()[c][i] * scaling_factor,
+ kFloatSampleMin, kFloatSampleMax);
+ }
+ }
+ }
+};
+
+} // namespace
+
+FakeRecordingDevice::FakeRecordingDevice(int initial_mic_level,
+ int device_kind) {
+ switch (device_kind) {
+ case 0:
+ worker_ =
+ std::make_unique<FakeRecordingDeviceIdentity>(initial_mic_level);
+ break;
+ case 1:
+ worker_ = std::make_unique<FakeRecordingDeviceLinear>(initial_mic_level);
+ break;
+ case 2:
+ worker_ = std::make_unique<FakeRecordingDeviceAgc1>(initial_mic_level);
+ break;
+ default:
+ RTC_NOTREACHED();
+ break;
+ }
+}
+
+FakeRecordingDevice::~FakeRecordingDevice() = default;
+
+int FakeRecordingDevice::MicLevel() const {
+ RTC_CHECK(worker_);
+ return worker_->mic_level();
+}
+
+void FakeRecordingDevice::SetMicLevel(const int level) {
+ RTC_CHECK(worker_);
+ if (level != worker_->mic_level())
+ RTC_LOG(LS_INFO) << "Simulate mic level update: " << level;
+ worker_->set_mic_level(level);
+}
+
+void FakeRecordingDevice::SetUndoMicLevel(const int level) {
+ RTC_DCHECK(worker_);
+ // TODO(alessiob): The behavior with undo level equal to zero is not clear yet
+ // and will be defined in future CLs once more FakeRecordingDeviceWorker
+ // implementations need to be added.
+ RTC_CHECK(level > 0) << "Zero undo mic level is unsupported";
+ worker_->set_undo_mic_level(level);
+}
+
+void FakeRecordingDevice::SimulateAnalogGain(rtc::ArrayView<int16_t> buffer) {
+ RTC_DCHECK(worker_);
+ worker_->ModifyBufferInt16(buffer);
+}
+
+void FakeRecordingDevice::SimulateAnalogGain(ChannelBuffer<float>* buffer) {
+ RTC_DCHECK(worker_);
+ worker_->ModifyBufferFloat(buffer);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/fake_recording_device_unittest.cc b/modules/audio_processing/test/fake_recording_device_unittest.cc
new file mode 100644
index 0000000..74bb47f
--- /dev/null
+++ b/modules/audio_processing/test/fake_recording_device_unittest.cc
@@ -0,0 +1,231 @@
+/*
+ * 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 "modules/audio_processing/test/fake_recording_device.h"
+
+#include <cmath>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "api/array_view.h"
+#include "rtc_base/strings/string_builder.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace test {
+namespace {
+
+constexpr int kInitialMicLevel = 100;
+
+// TODO(alessiob): Add new fake recording device kind values here as they are
+// added in FakeRecordingDevice::FakeRecordingDevice.
+const std::vector<int> kFakeRecDeviceKinds = {0, 1, 2};
+
+const std::vector<std::vector<float>> kTestMultiChannelSamples{
+ std::vector<float>{-10.f, -1.f, -0.1f, 0.f, 0.1f, 1.f, 10.f}};
+
+// Writes samples into ChannelBuffer<float>.
+void WritesDataIntoChannelBuffer(const std::vector<std::vector<float>>& data,
+ ChannelBuffer<float>* buff) {
+ EXPECT_EQ(data.size(), buff->num_channels());
+ EXPECT_EQ(data[0].size(), buff->num_frames());
+ for (size_t c = 0; c < buff->num_channels(); ++c) {
+ for (size_t f = 0; f < buff->num_frames(); ++f) {
+ buff->channels()[c][f] = data[c][f];
+ }
+ }
+}
+
+std::unique_ptr<ChannelBuffer<float>> CreateChannelBufferWithData(
+ const std::vector<std::vector<float>>& data) {
+ auto buff =
+ std::make_unique<ChannelBuffer<float>>(data[0].size(), data.size());
+ WritesDataIntoChannelBuffer(data, buff.get());
+ return buff;
+}
+
+// Checks that the samples modified using monotonic level values are also
+// monotonic.
+void CheckIfMonotoneSamplesModules(const ChannelBuffer<float>* prev,
+ const ChannelBuffer<float>* curr) {
+ RTC_DCHECK_EQ(prev->num_channels(), curr->num_channels());
+ RTC_DCHECK_EQ(prev->num_frames(), curr->num_frames());
+ bool valid = true;
+ for (size_t i = 0; i < prev->num_channels(); ++i) {
+ for (size_t j = 0; j < prev->num_frames(); ++j) {
+ valid = std::fabs(prev->channels()[i][j]) <=
+ std::fabs(curr->channels()[i][j]);
+ if (!valid) {
+ break;
+ }
+ }
+ if (!valid) {
+ break;
+ }
+ }
+ EXPECT_TRUE(valid);
+}
+
+// Checks that the samples in each pair have the same sign unless the sample in
+// |dst| is zero (because of zero gain).
+void CheckSameSign(const ChannelBuffer<float>* src,
+ const ChannelBuffer<float>* dst) {
+ RTC_DCHECK_EQ(src->num_channels(), dst->num_channels());
+ RTC_DCHECK_EQ(src->num_frames(), dst->num_frames());
+ const auto fsgn = [](float x) { return ((x < 0) ? -1 : (x > 0) ? 1 : 0); };
+ bool valid = true;
+ for (size_t i = 0; i < src->num_channels(); ++i) {
+ for (size_t j = 0; j < src->num_frames(); ++j) {
+ valid = dst->channels()[i][j] == 0.0f ||
+ fsgn(src->channels()[i][j]) == fsgn(dst->channels()[i][j]);
+ if (!valid) {
+ break;
+ }
+ }
+ if (!valid) {
+ break;
+ }
+ }
+ EXPECT_TRUE(valid);
+}
+
+std::string FakeRecordingDeviceKindToString(int fake_rec_device_kind) {
+ rtc::StringBuilder ss;
+ ss << "fake recording device: " << fake_rec_device_kind;
+ return ss.Release();
+}
+
+std::string AnalogLevelToString(int level) {
+ rtc::StringBuilder ss;
+ ss << "analog level: " << level;
+ return ss.Release();
+}
+
+} // namespace
+
+TEST(FakeRecordingDevice, CheckHelperFunctions) {
+ constexpr size_t kC = 0; // Channel index.
+ constexpr size_t kS = 1; // Sample index.
+
+ // Check read.
+ auto buff = CreateChannelBufferWithData(kTestMultiChannelSamples);
+ for (size_t c = 0; c < kTestMultiChannelSamples.size(); ++c) {
+ for (size_t s = 0; s < kTestMultiChannelSamples[0].size(); ++s) {
+ EXPECT_EQ(kTestMultiChannelSamples[c][s], buff->channels()[c][s]);
+ }
+ }
+
+ // Check write.
+ buff->channels()[kC][kS] = -5.0f;
+ RTC_DCHECK_NE(buff->channels()[kC][kS], kTestMultiChannelSamples[kC][kS]);
+
+ // Check reset.
+ WritesDataIntoChannelBuffer(kTestMultiChannelSamples, buff.get());
+ EXPECT_EQ(buff->channels()[kC][kS], kTestMultiChannelSamples[kC][kS]);
+}
+
+// Implicitly checks that changes to the mic and undo levels are visible to the
+// FakeRecordingDeviceWorker implementation are injected in FakeRecordingDevice.
+TEST(FakeRecordingDevice, TestWorkerAbstractClass) {
+ FakeRecordingDevice fake_recording_device(kInitialMicLevel, 1);
+
+ auto buff1 = CreateChannelBufferWithData(kTestMultiChannelSamples);
+ fake_recording_device.SetMicLevel(100);
+ fake_recording_device.SimulateAnalogGain(buff1.get());
+
+ auto buff2 = CreateChannelBufferWithData(kTestMultiChannelSamples);
+ fake_recording_device.SetMicLevel(200);
+ fake_recording_device.SimulateAnalogGain(buff2.get());
+
+ for (size_t c = 0; c < kTestMultiChannelSamples.size(); ++c) {
+ for (size_t s = 0; s < kTestMultiChannelSamples[0].size(); ++s) {
+ EXPECT_LE(std::abs(buff1->channels()[c][s]),
+ std::abs(buff2->channels()[c][s]));
+ }
+ }
+
+ auto buff3 = CreateChannelBufferWithData(kTestMultiChannelSamples);
+ fake_recording_device.SetMicLevel(200);
+ fake_recording_device.SetUndoMicLevel(100);
+ fake_recording_device.SimulateAnalogGain(buff3.get());
+
+ for (size_t c = 0; c < kTestMultiChannelSamples.size(); ++c) {
+ for (size_t s = 0; s < kTestMultiChannelSamples[0].size(); ++s) {
+ EXPECT_LE(std::abs(buff1->channels()[c][s]),
+ std::abs(buff3->channels()[c][s]));
+ EXPECT_LE(std::abs(buff2->channels()[c][s]),
+ std::abs(buff3->channels()[c][s]));
+ }
+ }
+}
+
+TEST(FakeRecordingDevice, GainCurveShouldBeMonotone) {
+ // Create input-output buffers.
+ auto buff_prev = CreateChannelBufferWithData(kTestMultiChannelSamples);
+ auto buff_curr = CreateChannelBufferWithData(kTestMultiChannelSamples);
+
+ // Test different mappings.
+ for (auto fake_rec_device_kind : kFakeRecDeviceKinds) {
+ SCOPED_TRACE(FakeRecordingDeviceKindToString(fake_rec_device_kind));
+ FakeRecordingDevice fake_recording_device(kInitialMicLevel,
+ fake_rec_device_kind);
+ // TODO(alessiob): The test below is designed for state-less recording
+ // devices. If, for instance, a device has memory, the test might need
+ // to be redesigned (e.g., re-initialize fake recording device).
+
+ // Apply lowest analog level.
+ WritesDataIntoChannelBuffer(kTestMultiChannelSamples, buff_prev.get());
+ fake_recording_device.SetMicLevel(0);
+ fake_recording_device.SimulateAnalogGain(buff_prev.get());
+
+ // Increment analog level to check monotonicity.
+ for (int i = 1; i <= 255; ++i) {
+ SCOPED_TRACE(AnalogLevelToString(i));
+ WritesDataIntoChannelBuffer(kTestMultiChannelSamples, buff_curr.get());
+ fake_recording_device.SetMicLevel(i);
+ fake_recording_device.SimulateAnalogGain(buff_curr.get());
+ CheckIfMonotoneSamplesModules(buff_prev.get(), buff_curr.get());
+
+ // Update prev.
+ buff_prev.swap(buff_curr);
+ }
+ }
+}
+
+TEST(FakeRecordingDevice, GainCurveShouldNotChangeSign) {
+ // Create view on original samples.
+ std::unique_ptr<const ChannelBuffer<float>> buff_orig =
+ CreateChannelBufferWithData(kTestMultiChannelSamples);
+
+ // Create output buffer.
+ auto buff = CreateChannelBufferWithData(kTestMultiChannelSamples);
+
+ // Test different mappings.
+ for (auto fake_rec_device_kind : kFakeRecDeviceKinds) {
+ SCOPED_TRACE(FakeRecordingDeviceKindToString(fake_rec_device_kind));
+ FakeRecordingDevice fake_recording_device(kInitialMicLevel,
+ fake_rec_device_kind);
+
+ // TODO(alessiob): The test below is designed for state-less recording
+ // devices. If, for instance, a device has memory, the test might need
+ // to be redesigned (e.g., re-initialize fake recording device).
+ for (int i = 0; i <= 255; ++i) {
+ SCOPED_TRACE(AnalogLevelToString(i));
+ WritesDataIntoChannelBuffer(kTestMultiChannelSamples, buff.get());
+ fake_recording_device.SetMicLevel(i);
+ fake_recording_device.SimulateAnalogGain(buff.get());
+ CheckSameSign(buff_orig.get(), buff.get());
+ }
+ }
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/performance_timer.cc b/modules/audio_processing/test/performance_timer.cc
new file mode 100644
index 0000000..1a82258
--- /dev/null
+++ b/modules/audio_processing/test/performance_timer.cc
@@ -0,0 +1,75 @@
+/*
+ * 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 "modules/audio_processing/test/performance_timer.h"
+
+#include <math.h>
+
+#include <numeric>
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace test {
+
+PerformanceTimer::PerformanceTimer(int num_frames_to_process)
+ : clock_(webrtc::Clock::GetRealTimeClock()) {
+ timestamps_us_.reserve(num_frames_to_process);
+}
+
+PerformanceTimer::~PerformanceTimer() = default;
+
+void PerformanceTimer::StartTimer() {
+ start_timestamp_us_ = clock_->TimeInMicroseconds();
+}
+
+void PerformanceTimer::StopTimer() {
+ RTC_DCHECK(start_timestamp_us_);
+ timestamps_us_.push_back(clock_->TimeInMicroseconds() - *start_timestamp_us_);
+}
+
+double PerformanceTimer::GetDurationAverage() const {
+ return GetDurationAverage(0);
+}
+
+double PerformanceTimer::GetDurationStandardDeviation() const {
+ return GetDurationStandardDeviation(0);
+}
+
+double PerformanceTimer::GetDurationAverage(
+ size_t number_of_warmup_samples) const {
+ RTC_DCHECK_GT(timestamps_us_.size(), number_of_warmup_samples);
+ const size_t number_of_samples =
+ timestamps_us_.size() - number_of_warmup_samples;
+ return static_cast<double>(
+ std::accumulate(timestamps_us_.begin() + number_of_warmup_samples,
+ timestamps_us_.end(), static_cast<int64_t>(0))) /
+ number_of_samples;
+}
+
+double PerformanceTimer::GetDurationStandardDeviation(
+ size_t number_of_warmup_samples) const {
+ RTC_DCHECK_GT(timestamps_us_.size(), number_of_warmup_samples);
+ const size_t number_of_samples =
+ timestamps_us_.size() - number_of_warmup_samples;
+ RTC_DCHECK_GT(number_of_samples, 0);
+ double average_duration = GetDurationAverage(number_of_warmup_samples);
+
+ double variance = std::accumulate(
+ timestamps_us_.begin() + number_of_warmup_samples, timestamps_us_.end(),
+ 0.0, [average_duration](const double& a, const int64_t& b) {
+ return a + (b - average_duration) * (b - average_duration);
+ });
+
+ return sqrt(variance / number_of_samples);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/protobuf_utils.cc b/modules/audio_processing/test/protobuf_utils.cc
new file mode 100644
index 0000000..7557496
--- /dev/null
+++ b/modules/audio_processing/test/protobuf_utils.cc
@@ -0,0 +1,79 @@
+/*
+ * 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 "modules/audio_processing/test/protobuf_utils.h"
+
+#include <memory>
+
+#include "rtc_base/system/arch.h"
+
+namespace {
+// Allocates new memory in the memory owned by the unique_ptr to fit the raw
+// message and returns the number of bytes read when having a string stream as
+// input.
+size_t ReadMessageBytesFromString(std::stringstream* input,
+ std::unique_ptr<uint8_t[]>* bytes) {
+ int32_t size = 0;
+ input->read(reinterpret_cast<char*>(&size), sizeof(int32_t));
+ int32_t size_read = input->gcount();
+ if (size_read != sizeof(int32_t))
+ return 0;
+ if (size <= 0)
+ return 0;
+
+ *bytes = std::make_unique<uint8_t[]>(size);
+ input->read(reinterpret_cast<char*>(bytes->get()),
+ size * sizeof((*bytes)[0]));
+ size_read = input->gcount();
+ return size_read == size ? size : 0;
+}
+} // namespace
+
+namespace webrtc {
+
+size_t ReadMessageBytesFromFile(FILE* file, std::unique_ptr<uint8_t[]>* bytes) {
+// The "wire format" for the size is little-endian. Assume we're running on
+// a little-endian machine.
+#ifndef WEBRTC_ARCH_LITTLE_ENDIAN
+#error "Need to convert messsage from little-endian."
+#endif
+ int32_t size = 0;
+ if (fread(&size, sizeof(size), 1, file) != 1)
+ return 0;
+ if (size <= 0)
+ return 0;
+
+ *bytes = std::make_unique<uint8_t[]>(size);
+ return fread(bytes->get(), sizeof((*bytes)[0]), size, file);
+}
+
+// Returns true on success, false on error or end-of-file.
+bool ReadMessageFromFile(FILE* file, MessageLite* msg) {
+ std::unique_ptr<uint8_t[]> bytes;
+ size_t size = ReadMessageBytesFromFile(file, &bytes);
+ if (!size)
+ return false;
+
+ msg->Clear();
+ return msg->ParseFromArray(bytes.get(), size);
+}
+
+// Returns true on success, false on error or end of string stream.
+bool ReadMessageFromString(std::stringstream* input, MessageLite* msg) {
+ std::unique_ptr<uint8_t[]> bytes;
+ size_t size = ReadMessageBytesFromString(input, &bytes);
+ if (!size)
+ return false;
+
+ msg->Clear();
+ return msg->ParseFromArray(bytes.get(), size);
+}
+
+} // namespace webrtc
diff --git a/modules/audio_processing/test/py_quality_assessment/quality_assessment/apm_vad.cc b/modules/audio_processing/test/py_quality_assessment/quality_assessment/apm_vad.cc
new file mode 100644
index 0000000..73ce4ed
--- /dev/null
+++ b/modules/audio_processing/test/py_quality_assessment/quality_assessment/apm_vad.cc
@@ -0,0 +1,96 @@
+// 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 <array>
+#include <fstream>
+#include <memory>
+
+#include "absl/flags/flag.h"
+#include "absl/flags/parse.h"
+#include "common_audio/wav_file.h"
+#include "modules/audio_processing/vad/voice_activity_detector.h"
+#include "rtc_base/logging.h"
+
+ABSL_FLAG(std::string, i, "", "Input wav file");
+ABSL_FLAG(std::string, o_probs, "", "VAD probabilities output file");
+ABSL_FLAG(std::string, o_rms, "", "VAD output file");
+
+namespace webrtc {
+namespace test {
+namespace {
+
+constexpr uint8_t kAudioFrameLengthMilliseconds = 10;
+constexpr int kMaxSampleRate = 48000;
+constexpr size_t kMaxFrameLen =
+ kAudioFrameLengthMilliseconds * kMaxSampleRate / 1000;
+
+int main(int argc, char* argv[]) {
+ absl::ParseCommandLine(argc, argv);
+ const std::string input_file = absl::GetFlag(FLAGS_i);
+ const std::string output_probs_file = absl::GetFlag(FLAGS_o_probs);
+ const std::string output_file = absl::GetFlag(FLAGS_o_rms);
+ // Open wav input file and check properties.
+ WavReader wav_reader(input_file);
+ if (wav_reader.num_channels() != 1) {
+ RTC_LOG(LS_ERROR) << "Only mono wav files supported";
+ return 1;
+ }
+ if (wav_reader.sample_rate() > kMaxSampleRate) {
+ RTC_LOG(LS_ERROR) << "Beyond maximum sample rate (" << kMaxSampleRate
+ << ")";
+ return 1;
+ }
+ const size_t audio_frame_len = rtc::CheckedDivExact(
+ kAudioFrameLengthMilliseconds * wav_reader.sample_rate(), 1000);
+ if (audio_frame_len > kMaxFrameLen) {
+ RTC_LOG(LS_ERROR) << "The frame size and/or the sample rate are too large.";
+ return 1;
+ }
+
+ // Create output file and write header.
+ std::ofstream out_probs_file(output_probs_file, std::ofstream::binary);
+ std::ofstream out_rms_file(output_file, std::ofstream::binary);
+
+ // Run VAD and write decisions.
+ VoiceActivityDetector vad;
+ std::array<int16_t, kMaxFrameLen> samples;
+
+ while (true) {
+ // Process frame.
+ const auto read_samples =
+ wav_reader.ReadSamples(audio_frame_len, samples.data());
+ if (read_samples < audio_frame_len) {
+ break;
+ }
+ vad.ProcessChunk(samples.data(), audio_frame_len, wav_reader.sample_rate());
+ // Write output.
+ auto probs = vad.chunkwise_voice_probabilities();
+ auto rms = vad.chunkwise_rms();
+ RTC_CHECK_EQ(probs.size(), rms.size());
+ RTC_CHECK_EQ(sizeof(double), 8);
+
+ for (const auto& p : probs) {
+ out_probs_file.write(reinterpret_cast<const char*>(&p), 8);
+ }
+ for (const auto& r : rms) {
+ out_rms_file.write(reinterpret_cast<const char*>(&r), 8);
+ }
+ }
+
+ out_probs_file.close();
+ out_rms_file.close();
+ return 0;
+}
+
+} // namespace
+} // namespace test
+} // namespace webrtc
+
+int main(int argc, char* argv[]) {
+ return webrtc::test::main(argc, argv);
+}
diff --git a/modules/audio_processing/test/py_quality_assessment/quality_assessment/data_access.py b/modules/audio_processing/test/py_quality_assessment/quality_assessment/data_access.py
new file mode 100644
index 0000000..c1aebb6
--- /dev/null
+++ b/modules/audio_processing/test/py_quality_assessment/quality_assessment/data_access.py
@@ -0,0 +1,154 @@
+# 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.
+"""Data access utility functions and classes.
+"""
+
+import json
+import os
+
+
+def MakeDirectory(path):
+ """Makes a directory recursively without rising exceptions if existing.
+
+ Args:
+ path: path to the directory to be created.
+ """
+ if os.path.exists(path):
+ return
+ os.makedirs(path)
+
+
+class Metadata(object):
+ """Data access class to save and load metadata.
+ """
+
+ def __init__(self):
+ pass
+
+ _GENERIC_METADATA_SUFFIX = '.mdata'
+ _AUDIO_TEST_DATA_FILENAME = 'audio_test_data.json'
+
+ @classmethod
+ def LoadFileMetadata(cls, filepath):
+ """Loads generic metadata linked to a file.
+
+ Args:
+ filepath: path to the metadata file to read.
+
+ Returns:
+ A dict.
+ """
+ with open(filepath + cls._GENERIC_METADATA_SUFFIX) as f:
+ return json.load(f)
+
+ @classmethod
+ def SaveFileMetadata(cls, filepath, metadata):
+ """Saves generic metadata linked to a file.
+
+ Args:
+ filepath: path to the metadata file to write.
+ metadata: a dict.
+ """
+ with open(filepath + cls._GENERIC_METADATA_SUFFIX, 'w') as f:
+ json.dump(metadata, f)
+
+ @classmethod
+ def LoadAudioTestDataPaths(cls, metadata_path):
+ """Loads the input and the reference audio track paths.
+
+ Args:
+ metadata_path: path to the directory containing the metadata file.
+
+ Returns:
+ Tuple with the paths to the input and output audio tracks.
+ """
+ metadata_filepath = os.path.join(metadata_path,
+ cls._AUDIO_TEST_DATA_FILENAME)
+ with open(metadata_filepath) as f:
+ return json.load(f)
+
+ @classmethod
+ def SaveAudioTestDataPaths(cls, output_path, **filepaths):
+ """Saves the input and the reference audio track paths.
+
+ Args:
+ output_path: path to the directory containing the metadata file.
+
+ Keyword Args:
+ filepaths: collection of audio track file paths to save.
+ """
+ output_filepath = os.path.join(output_path,
+ cls._AUDIO_TEST_DATA_FILENAME)
+ with open(output_filepath, 'w') as f:
+ json.dump(filepaths, f)
+
+
+class AudioProcConfigFile(object):
+ """Data access to load/save APM simulator argument lists.
+
+ The arguments stored in the config files are used to control the APM flags.
+ """
+
+ def __init__(self):
+ pass
+
+ @classmethod
+ def Load(cls, filepath):
+ """Loads a configuration file for an APM simulator.
+
+ Args:
+ filepath: path to the configuration file.
+
+ Returns:
+ A dict containing the configuration.
+ """
+ with open(filepath) as f:
+ return json.load(f)
+
+ @classmethod
+ def Save(cls, filepath, config):
+ """Saves a configuration file for an APM simulator.
+
+ Args:
+ filepath: path to the configuration file.
+ config: a dict containing the configuration.
+ """
+ with open(filepath, 'w') as f:
+ json.dump(config, f)
+
+
+class ScoreFile(object):
+ """Data access class to save and load float scalar scores.
+ """
+
+ def __init__(self):
+ pass
+
+ @classmethod
+ def Load(cls, filepath):
+ """Loads a score from file.
+
+ Args:
+ filepath: path to the score file.
+
+ Returns:
+ A float encoding the score.
+ """
+ with open(filepath) as f:
+ return float(f.readline().strip())
+
+ @classmethod
+ def Save(cls, filepath, score):
+ """Saves a score into a file.
+
+ Args:
+ filepath: path to the score file.
+ score: float encoding the score.
+ """
+ with open(filepath, 'w') as f:
+ f.write('{0:f}\n'.format(score))
diff --git a/modules/audio_processing/test/py_quality_assessment/quality_assessment/fake_polqa.cc b/modules/audio_processing/test/py_quality_assessment/quality_assessment/fake_polqa.cc
new file mode 100644
index 0000000..bae652e
--- /dev/null
+++ b/modules/audio_processing/test/py_quality_assessment/quality_assessment/fake_polqa.cc
@@ -0,0 +1,54 @@
+/*
+ * 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 <fstream>
+#include <iostream>
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace test {
+namespace {
+
+const char* const kErrorMessage = "-Out /path/to/output/file is mandatory";
+
+// Writes fake output intended to be parsed by
+// quality_assessment.eval_scores.PolqaScore.
+void WriteOutputFile(const std::string& output_file_path) {
+ RTC_CHECK_NE(output_file_path, "");
+ std::ofstream out(output_file_path);
+ RTC_CHECK(!out.bad());
+ out << "* Fake Polqa output" << std::endl;
+ out << "FakeField1\tPolqaScore\tFakeField2" << std::endl;
+ out << "FakeValue1\t3.25\tFakeValue2" << std::endl;
+ out.close();
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ // Find "-Out" and use its next argument as output file path.
+ RTC_CHECK_GE(argc, 3) << kErrorMessage;
+ const std::string kSoughtFlagName = "-Out";
+ for (int i = 1; i < argc - 1; ++i) {
+ if (kSoughtFlagName.compare(argv[i]) == 0) {
+ WriteOutputFile(argv[i + 1]);
+ return 0;
+ }
+ }
+ RTC_FATAL() << kErrorMessage;
+}
+
+} // namespace test
+} // namespace webrtc
+
+int main(int argc, char* argv[]) {
+ return webrtc::test::main(argc, argv);
+}
diff --git a/modules/audio_processing/test/py_quality_assessment/quality_assessment/sound_level.cc b/modules/audio_processing/test/py_quality_assessment/quality_assessment/sound_level.cc
new file mode 100644
index 0000000..1f24d9d
--- /dev/null
+++ b/modules/audio_processing/test/py_quality_assessment/quality_assessment/sound_level.cc
@@ -0,0 +1,127 @@
+// Copyright (c) 2018 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 <algorithm>
+#include <array>
+#include <cmath>
+#include <fstream>
+
+#include "absl/flags/flag.h"
+#include "absl/flags/parse.h"
+#include "common_audio/include/audio_util.h"
+#include "common_audio/wav_file.h"
+#include "rtc_base/logging.h"
+
+ABSL_FLAG(std::string, i, "", "Input wav file");
+ABSL_FLAG(std::string, oc, "", "Config output file");
+ABSL_FLAG(std::string, ol, "", "Levels output file");
+ABSL_FLAG(float, a, 5.f, "Attack (ms)");
+ABSL_FLAG(float, d, 20.f, "Decay (ms)");
+ABSL_FLAG(int, f, 10, "Frame length (ms)");
+
+namespace webrtc {
+namespace test {
+namespace {
+
+constexpr int kMaxSampleRate = 48000;
+constexpr uint8_t kMaxFrameLenMs = 30;
+constexpr size_t kMaxFrameLen = kMaxFrameLenMs * kMaxSampleRate / 1000;
+
+const double kOneDbReduction = DbToRatio(-1.0);
+
+int main(int argc, char* argv[]) {
+ absl::ParseCommandLine(argc, argv);
+ // Check parameters.
+ if (absl::GetFlag(FLAGS_f) < 1 || absl::GetFlag(FLAGS_f) > kMaxFrameLenMs) {
+ RTC_LOG(LS_ERROR) << "Invalid frame length (min: 1, max: " << kMaxFrameLenMs
+ << ")";
+ return 1;
+ }
+ if (absl::GetFlag(FLAGS_a) < 0 || absl::GetFlag(FLAGS_d) < 0) {
+ RTC_LOG(LS_ERROR) << "Attack and decay must be non-negative";
+ return 1;
+ }
+
+ // Open wav input file and check properties.
+ const std::string input_file = absl::GetFlag(FLAGS_i);
+ const std::string config_output_file = absl::GetFlag(FLAGS_oc);
+ const std::string levels_output_file = absl::GetFlag(FLAGS_ol);
+ WavReader wav_reader(input_file);
+ if (wav_reader.num_channels() != 1) {
+ RTC_LOG(LS_ERROR) << "Only mono wav files supported";
+ return 1;
+ }
+ if (wav_reader.sample_rate() > kMaxSampleRate) {
+ RTC_LOG(LS_ERROR) << "Beyond maximum sample rate (" << kMaxSampleRate
+ << ")";
+ return 1;
+ }
+
+ // Map from milliseconds to samples.
+ const size_t audio_frame_length = rtc::CheckedDivExact(
+ absl::GetFlag(FLAGS_f) * wav_reader.sample_rate(), 1000);
+ auto time_const = [](double c) {
+ return std::pow(kOneDbReduction, absl::GetFlag(FLAGS_f) / c);
+ };
+ const float attack =
+ absl::GetFlag(FLAGS_a) == 0.0 ? 0.0 : time_const(absl::GetFlag(FLAGS_a));
+ const float decay =
+ absl::GetFlag(FLAGS_d) == 0.0 ? 0.0 : time_const(absl::GetFlag(FLAGS_d));
+
+ // Write config to file.
+ std::ofstream out_config(config_output_file);
+ out_config << "{"
+ "'frame_len_ms': "
+ << absl::GetFlag(FLAGS_f)
+ << ", "
+ "'attack_ms': "
+ << absl::GetFlag(FLAGS_a)
+ << ", "
+ "'decay_ms': "
+ << absl::GetFlag(FLAGS_d) << "}\n";
+ out_config.close();
+
+ // Measure level frame-by-frame.
+ std::ofstream out_levels(levels_output_file, std::ofstream::binary);
+ std::array<int16_t, kMaxFrameLen> samples;
+ float level_prev = 0.f;
+ while (true) {
+ // Process frame.
+ const auto read_samples =
+ wav_reader.ReadSamples(audio_frame_length, samples.data());
+ if (read_samples < audio_frame_length)
+ break; // EOF.
+
+ // Frame peak level.
+ std::transform(samples.begin(), samples.begin() + audio_frame_length,
+ samples.begin(), [](int16_t s) { return std::abs(s); });
+ const int16_t peak_level = *std::max_element(
+ samples.cbegin(), samples.cbegin() + audio_frame_length);
+ const float level_curr = static_cast<float>(peak_level) / 32768.f;
+
+ // Temporal smoothing.
+ auto smooth = [&level_prev, &level_curr](float c) {
+ return (1.0 - c) * level_curr + c * level_prev;
+ };
+ level_prev = smooth(level_curr > level_prev ? attack : decay);
+
+ // Write output.
+ out_levels.write(reinterpret_cast<const char*>(&level_prev), sizeof(float));
+ }
+ out_levels.close();
+
+ return 0;
+}
+
+} // namespace
+} // namespace test
+} // namespace webrtc
+
+int main(int argc, char* argv[]) {
+ return webrtc::test::main(argc, argv);
+}
diff --git a/modules/audio_processing/test/py_quality_assessment/quality_assessment/vad.cc b/modules/audio_processing/test/py_quality_assessment/quality_assessment/vad.cc
new file mode 100644
index 0000000..9906eca
--- /dev/null
+++ b/modules/audio_processing/test/py_quality_assessment/quality_assessment/vad.cc
@@ -0,0 +1,103 @@
+// 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 "common_audio/vad/include/vad.h"
+
+#include <array>
+#include <fstream>
+#include <memory>
+
+#include "absl/flags/flag.h"
+#include "absl/flags/parse.h"
+#include "common_audio/wav_file.h"
+#include "rtc_base/logging.h"
+
+ABSL_FLAG(std::string, i, "", "Input wav file");
+ABSL_FLAG(std::string, o, "", "VAD output file");
+
+namespace webrtc {
+namespace test {
+namespace {
+
+// The allowed values are 10, 20 or 30 ms.
+constexpr uint8_t kAudioFrameLengthMilliseconds = 30;
+constexpr int kMaxSampleRate = 48000;
+constexpr size_t kMaxFrameLen =
+ kAudioFrameLengthMilliseconds * kMaxSampleRate / 1000;
+
+constexpr uint8_t kBitmaskBuffSize = 8;
+
+int main(int argc, char* argv[]) {
+ absl::ParseCommandLine(argc, argv);
+ const std::string input_file = absl::GetFlag(FLAGS_i);
+ const std::string output_file = absl::GetFlag(FLAGS_o);
+ // Open wav input file and check properties.
+ WavReader wav_reader(input_file);
+ if (wav_reader.num_channels() != 1) {
+ RTC_LOG(LS_ERROR) << "Only mono wav files supported";
+ return 1;
+ }
+ if (wav_reader.sample_rate() > kMaxSampleRate) {
+ RTC_LOG(LS_ERROR) << "Beyond maximum sample rate (" << kMaxSampleRate
+ << ")";
+ return 1;
+ }
+ const size_t audio_frame_length = rtc::CheckedDivExact(
+ kAudioFrameLengthMilliseconds * wav_reader.sample_rate(), 1000);
+ if (audio_frame_length > kMaxFrameLen) {
+ RTC_LOG(LS_ERROR) << "The frame size and/or the sample rate are too large.";
+ return 1;
+ }
+
+ // Create output file and write header.
+ std::ofstream out_file(output_file, std::ofstream::binary);
+ const char audio_frame_length_ms = kAudioFrameLengthMilliseconds;
+ out_file.write(&audio_frame_length_ms, 1); // Header.
+
+ // Run VAD and write decisions.
+ std::unique_ptr<Vad> vad = CreateVad(Vad::Aggressiveness::kVadNormal);
+ std::array<int16_t, kMaxFrameLen> samples;
+ char buff = 0; // Buffer to write one bit per frame.
+ uint8_t next = 0; // Points to the next bit to write in |buff|.
+ while (true) {
+ // Process frame.
+ const auto read_samples =
+ wav_reader.ReadSamples(audio_frame_length, samples.data());
+ if (read_samples < audio_frame_length)
+ break;
+ const auto is_speech = vad->VoiceActivity(
+ samples.data(), audio_frame_length, wav_reader.sample_rate());
+
+ // Write output.
+ buff = is_speech ? buff | (1 << next) : buff & ~(1 << next);
+ if (++next == kBitmaskBuffSize) {
+ out_file.write(&buff, 1); // Flush.
+ buff = 0; // Reset.
+ next = 0;
+ }
+ }
+
+ // Finalize.
+ char extra_bits = 0;
+ if (next > 0) {
+ extra_bits = kBitmaskBuffSize - next;
+ out_file.write(&buff, 1); // Flush.
+ }
+ out_file.write(&extra_bits, 1);
+ out_file.close();
+
+ return 0;
+}
+
+} // namespace
+} // namespace test
+} // namespace webrtc
+
+int main(int argc, char* argv[]) {
+ return webrtc::test::main(argc, argv);
+}
diff --git a/modules/audio_processing/test/runtime_setting_util.cc b/modules/audio_processing/test/runtime_setting_util.cc
new file mode 100644
index 0000000..4899d2d
--- /dev/null
+++ b/modules/audio_processing/test/runtime_setting_util.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018 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 "modules/audio_processing/test/runtime_setting_util.h"
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+void ReplayRuntimeSetting(AudioProcessing* apm,
+ const webrtc::audioproc::RuntimeSetting& setting) {
+ RTC_CHECK(apm);
+ // TODO(bugs.webrtc.org/9138): Add ability to handle different types
+ // of settings. Currently CapturePreGain, CaptureFixedPostGain and
+ // PlayoutVolumeChange are supported.
+ RTC_CHECK(setting.has_capture_pre_gain() ||
+ setting.has_capture_fixed_post_gain() ||
+ setting.has_playout_volume_change());
+
+ if (setting.has_capture_pre_gain()) {
+ apm->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCapturePreGain(
+ setting.capture_pre_gain()));
+ } else if (setting.has_capture_fixed_post_gain()) {
+ apm->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCaptureFixedPostGain(
+ setting.capture_fixed_post_gain()));
+ } else if (setting.has_playout_volume_change()) {
+ apm->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(
+ setting.playout_volume_change()));
+ } else if (setting.has_playout_audio_device_change()) {
+ apm->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreatePlayoutAudioDeviceChange(
+ {setting.playout_audio_device_change().id(),
+ setting.playout_audio_device_change().max_volume()}));
+ } else if (setting.has_capture_output_used()) {
+ apm->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
+ setting.capture_output_used()));
+ }
+}
+} // namespace webrtc
diff --git a/modules/audio_processing/test/simulator_buffers.cc b/modules/audio_processing/test/simulator_buffers.cc
new file mode 100644
index 0000000..e6bd6c1
--- /dev/null
+++ b/modules/audio_processing/test/simulator_buffers.cc
@@ -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 "modules/audio_processing/test/simulator_buffers.h"
+
+#include "modules/audio_processing/test/audio_buffer_tools.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace test {
+
+SimulatorBuffers::SimulatorBuffers(int render_input_sample_rate_hz,
+ int capture_input_sample_rate_hz,
+ int render_output_sample_rate_hz,
+ int capture_output_sample_rate_hz,
+ size_t num_render_input_channels,
+ size_t num_capture_input_channels,
+ size_t num_render_output_channels,
+ size_t num_capture_output_channels) {
+ Random rand_gen(42);
+ CreateConfigAndBuffer(render_input_sample_rate_hz, num_render_input_channels,
+ &rand_gen, &render_input_buffer, &render_input_config,
+ &render_input, &render_input_samples);
+
+ CreateConfigAndBuffer(render_output_sample_rate_hz,
+ num_render_output_channels, &rand_gen,
+ &render_output_buffer, &render_output_config,
+ &render_output, &render_output_samples);
+
+ CreateConfigAndBuffer(capture_input_sample_rate_hz,
+ num_capture_input_channels, &rand_gen,
+ &capture_input_buffer, &capture_input_config,
+ &capture_input, &capture_input_samples);
+
+ CreateConfigAndBuffer(capture_output_sample_rate_hz,
+ num_capture_output_channels, &rand_gen,
+ &capture_output_buffer, &capture_output_config,
+ &capture_output, &capture_output_samples);
+
+ UpdateInputBuffers();
+}
+
+SimulatorBuffers::~SimulatorBuffers() = default;
+
+void SimulatorBuffers::CreateConfigAndBuffer(
+ int sample_rate_hz,
+ size_t num_channels,
+ Random* rand_gen,
+ std::unique_ptr<AudioBuffer>* buffer,
+ StreamConfig* config,
+ std::vector<float*>* buffer_data,
+ std::vector<float>* buffer_data_samples) {
+ int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100);
+ *config = StreamConfig(sample_rate_hz, num_channels, false);
+ buffer->reset(
+ new AudioBuffer(config->sample_rate_hz(), config->num_channels(),
+ config->sample_rate_hz(), config->num_channels(),
+ config->sample_rate_hz(), config->num_channels()));
+
+ buffer_data_samples->resize(samples_per_channel * num_channels);
+ for (auto& v : *buffer_data_samples) {
+ v = rand_gen->Rand<float>();
+ }
+
+ buffer_data->resize(num_channels);
+ for (size_t ch = 0; ch < num_channels; ++ch) {
+ (*buffer_data)[ch] = &(*buffer_data_samples)[ch * samples_per_channel];
+ }
+}
+
+void SimulatorBuffers::UpdateInputBuffers() {
+ test::CopyVectorToAudioBuffer(capture_input_config, capture_input_samples,
+ capture_input_buffer.get());
+ test::CopyVectorToAudioBuffer(render_input_config, render_input_samples,
+ render_input_buffer.get());
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/test/test_utils.cc b/modules/audio_processing/test/test_utils.cc
new file mode 100644
index 0000000..839358d
--- /dev/null
+++ b/modules/audio_processing/test/test_utils.cc
@@ -0,0 +1,153 @@
+/*
+ * 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 "modules/audio_processing/test/test_utils.h"
+
+#include <utility>
+
+#include "rtc_base/checks.h"
+#include "rtc_base/system/arch.h"
+
+namespace webrtc {
+
+RawFile::RawFile(const std::string& filename)
+ : file_handle_(fopen(filename.c_str(), "wb")) {}
+
+RawFile::~RawFile() {
+ fclose(file_handle_);
+}
+
+void RawFile::WriteSamples(const int16_t* samples, size_t num_samples) {
+#ifndef WEBRTC_ARCH_LITTLE_ENDIAN
+#error "Need to convert samples to little-endian when writing to PCM file"
+#endif
+ fwrite(samples, sizeof(*samples), num_samples, file_handle_);
+}
+
+void RawFile::WriteSamples(const float* samples, size_t num_samples) {
+ fwrite(samples, sizeof(*samples), num_samples, file_handle_);
+}
+
+ChannelBufferWavReader::ChannelBufferWavReader(std::unique_ptr<WavReader> file)
+ : file_(std::move(file)) {}
+
+ChannelBufferWavReader::~ChannelBufferWavReader() = default;
+
+bool ChannelBufferWavReader::Read(ChannelBuffer<float>* buffer) {
+ RTC_CHECK_EQ(file_->num_channels(), buffer->num_channels());
+ interleaved_.resize(buffer->size());
+ if (file_->ReadSamples(interleaved_.size(), &interleaved_[0]) !=
+ interleaved_.size()) {
+ return false;
+ }
+
+ FloatS16ToFloat(&interleaved_[0], interleaved_.size(), &interleaved_[0]);
+ Deinterleave(&interleaved_[0], buffer->num_frames(), buffer->num_channels(),
+ buffer->channels());
+ return true;
+}
+
+ChannelBufferWavWriter::ChannelBufferWavWriter(std::unique_ptr<WavWriter> file)
+ : file_(std::move(file)) {}
+
+ChannelBufferWavWriter::~ChannelBufferWavWriter() = default;
+
+void ChannelBufferWavWriter::Write(const ChannelBuffer<float>& buffer) {
+ RTC_CHECK_EQ(file_->num_channels(), buffer.num_channels());
+ interleaved_.resize(buffer.size());
+ Interleave(buffer.channels(), buffer.num_frames(), buffer.num_channels(),
+ &interleaved_[0]);
+ FloatToFloatS16(&interleaved_[0], interleaved_.size(), &interleaved_[0]);
+ file_->WriteSamples(&interleaved_[0], interleaved_.size());
+}
+
+ChannelBufferVectorWriter::ChannelBufferVectorWriter(std::vector<float>* output)
+ : output_(output) {
+ RTC_DCHECK(output_);
+}
+
+ChannelBufferVectorWriter::~ChannelBufferVectorWriter() = default;
+
+void ChannelBufferVectorWriter::Write(const ChannelBuffer<float>& buffer) {
+ // Account for sample rate changes throughout a simulation.
+ interleaved_buffer_.resize(buffer.size());
+ Interleave(buffer.channels(), buffer.num_frames(), buffer.num_channels(),
+ interleaved_buffer_.data());
+ size_t old_size = output_->size();
+ output_->resize(old_size + interleaved_buffer_.size());
+ FloatToFloatS16(interleaved_buffer_.data(), interleaved_buffer_.size(),
+ output_->data() + old_size);
+}
+
+void WriteIntData(const int16_t* data,
+ size_t length,
+ WavWriter* wav_file,
+ RawFile* raw_file) {
+ if (wav_file) {
+ wav_file->WriteSamples(data, length);
+ }
+ if (raw_file) {
+ raw_file->WriteSamples(data, length);
+ }
+}
+
+void WriteFloatData(const float* const* data,
+ size_t samples_per_channel,
+ size_t num_channels,
+ WavWriter* wav_file,
+ RawFile* raw_file) {
+ size_t length = num_channels * samples_per_channel;
+ std::unique_ptr<float[]> buffer(new float[length]);
+ Interleave(data, samples_per_channel, num_channels, buffer.get());
+ if (raw_file) {
+ raw_file->WriteSamples(buffer.get(), length);
+ }
+ // TODO(aluebs): Use ScaleToInt16Range() from audio_util
+ for (size_t i = 0; i < length; ++i) {
+ buffer[i] = buffer[i] > 0
+ ? buffer[i] * std::numeric_limits<int16_t>::max()
+ : -buffer[i] * std::numeric_limits<int16_t>::min();
+ }
+ if (wav_file) {
+ wav_file->WriteSamples(buffer.get(), length);
+ }
+}
+
+FILE* OpenFile(const std::string& filename, const char* mode) {
+ FILE* file = fopen(filename.c_str(), mode);
+ if (!file) {
+ printf("Unable to open file %s\n", filename.c_str());
+ exit(1);
+ }
+ return file;
+}
+
+size_t SamplesFromRate(int rate) {
+ return static_cast<size_t>(AudioProcessing::kChunkSizeMs * rate / 1000);
+}
+
+void SetFrameSampleRate(Int16FrameData* frame, int sample_rate_hz) {
+ frame->sample_rate_hz = sample_rate_hz;
+ frame->samples_per_channel =
+ AudioProcessing::kChunkSizeMs * sample_rate_hz / 1000;
+}
+
+AudioProcessing::ChannelLayout LayoutFromChannels(size_t num_channels) {
+ switch (num_channels) {
+ case 1:
+ return AudioProcessing::kMono;
+ case 2:
+ return AudioProcessing::kStereo;
+ default:
+ RTC_CHECK_NOTREACHED();
+ }
+}
+
+} // namespace webrtc
diff --git a/modules/audio_processing/test/wav_based_simulator.cc b/modules/audio_processing/test/wav_based_simulator.cc
new file mode 100644
index 0000000..e6a6fe9
--- /dev/null
+++ b/modules/audio_processing/test/wav_based_simulator.cc
@@ -0,0 +1,204 @@
+/*
+ * 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 "modules/audio_processing/test/wav_based_simulator.h"
+
+#include <stdio.h>
+
+#include <iostream>
+
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "modules/audio_processing/test/test_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/system/file_wrapper.h"
+
+namespace webrtc {
+namespace test {
+
+std::vector<WavBasedSimulator::SimulationEventType>
+WavBasedSimulator::GetCustomEventChain(const std::string& filename) {
+ std::vector<WavBasedSimulator::SimulationEventType> call_chain;
+ FileWrapper file_wrapper = FileWrapper::OpenReadOnly(filename.c_str());
+
+ RTC_CHECK(file_wrapper.is_open())
+ << "Could not open the custom call order file, reverting "
+ "to using the default call order";
+
+ char c;
+ size_t num_read = file_wrapper.Read(&c, sizeof(char));
+ while (num_read > 0) {
+ switch (c) {
+ case 'r':
+ call_chain.push_back(SimulationEventType::kProcessReverseStream);
+ break;
+ case 'c':
+ call_chain.push_back(SimulationEventType::kProcessStream);
+ break;
+ case '\n':
+ break;
+ default:
+ RTC_FATAL()
+ << "Incorrect custom call order file, reverting to using the "
+ << "default call order";
+ return WavBasedSimulator::GetDefaultEventChain();
+ }
+
+ num_read = file_wrapper.Read(&c, sizeof(char));
+ }
+
+ return call_chain;
+}
+
+WavBasedSimulator::WavBasedSimulator(
+ const SimulationSettings& settings,
+ rtc::scoped_refptr<AudioProcessing> audio_processing,
+ std::unique_ptr<AudioProcessingBuilder> ap_builder)
+ : AudioProcessingSimulator(settings,
+ std::move(audio_processing),
+ std::move(ap_builder)) {
+ if (settings_.call_order_input_filename) {
+ call_chain_ = WavBasedSimulator::GetCustomEventChain(
+ *settings_.call_order_input_filename);
+ } else {
+ call_chain_ = WavBasedSimulator::GetDefaultEventChain();
+ }
+}
+
+WavBasedSimulator::~WavBasedSimulator() = default;
+
+std::vector<WavBasedSimulator::SimulationEventType>
+WavBasedSimulator::GetDefaultEventChain() {
+ std::vector<WavBasedSimulator::SimulationEventType> call_chain(2);
+ call_chain[0] = SimulationEventType::kProcessStream;
+ call_chain[1] = SimulationEventType::kProcessReverseStream;
+ return call_chain;
+}
+
+void WavBasedSimulator::PrepareProcessStreamCall() {
+ if (settings_.fixed_interface) {
+ fwd_frame_.CopyFrom(*in_buf_);
+ }
+ ap_->set_stream_key_pressed(settings_.use_ts && (*settings_.use_ts));
+
+ if (!settings_.use_stream_delay || *settings_.use_stream_delay) {
+ RTC_CHECK_EQ(AudioProcessing::kNoError,
+ ap_->set_stream_delay_ms(
+ settings_.stream_delay ? *settings_.stream_delay : 0));
+ }
+}
+
+void WavBasedSimulator::PrepareReverseProcessStreamCall() {
+ if (settings_.fixed_interface) {
+ rev_frame_.CopyFrom(*reverse_in_buf_);
+ }
+}
+
+void WavBasedSimulator::Process() {
+ ConfigureAudioProcessor();
+
+ Initialize();
+
+ bool samples_left_to_process = true;
+ int call_chain_index = 0;
+ int capture_frames_since_init = 0;
+ constexpr int kInitIndex = 1;
+ while (samples_left_to_process) {
+ switch (call_chain_[call_chain_index]) {
+ case SimulationEventType::kProcessStream:
+ SelectivelyToggleDataDumping(kInitIndex, capture_frames_since_init);
+
+ samples_left_to_process = HandleProcessStreamCall();
+ ++capture_frames_since_init;
+ break;
+ case SimulationEventType::kProcessReverseStream:
+ if (settings_.reverse_input_filename) {
+ samples_left_to_process = HandleProcessReverseStreamCall();
+ }
+ break;
+ default:
+ RTC_CHECK_NOTREACHED();
+ }
+
+ call_chain_index = (call_chain_index + 1) % call_chain_.size();
+ }
+
+ DetachAecDump();
+}
+
+void WavBasedSimulator::Analyze() {
+ std::cout << "Inits:" << std::endl;
+ std::cout << "1: -->" << std::endl;
+ std::cout << " Time:" << std::endl;
+ std::cout << " Capture: 0 s (0 frames) " << std::endl;
+ std::cout << " Render: 0 s (0 frames)" << std::endl;
+}
+
+bool WavBasedSimulator::HandleProcessStreamCall() {
+ bool samples_left_to_process = buffer_reader_->Read(in_buf_.get());
+ if (samples_left_to_process) {
+ PrepareProcessStreamCall();
+ ProcessStream(settings_.fixed_interface);
+ }
+ return samples_left_to_process;
+}
+
+bool WavBasedSimulator::HandleProcessReverseStreamCall() {
+ bool samples_left_to_process =
+ reverse_buffer_reader_->Read(reverse_in_buf_.get());
+ if (samples_left_to_process) {
+ PrepareReverseProcessStreamCall();
+ ProcessReverseStream(settings_.fixed_interface);
+ }
+ return samples_left_to_process;
+}
+
+void WavBasedSimulator::Initialize() {
+ std::unique_ptr<WavReader> in_file(
+ new WavReader(settings_.input_filename->c_str()));
+ int input_sample_rate_hz = in_file->sample_rate();
+ int input_num_channels = in_file->num_channels();
+ buffer_reader_.reset(new ChannelBufferWavReader(std::move(in_file)));
+
+ int output_sample_rate_hz = settings_.output_sample_rate_hz
+ ? *settings_.output_sample_rate_hz
+ : input_sample_rate_hz;
+ int output_num_channels = settings_.output_num_channels
+ ? *settings_.output_num_channels
+ : input_num_channels;
+
+ int reverse_sample_rate_hz = 48000;
+ int reverse_num_channels = 1;
+ int reverse_output_sample_rate_hz = 48000;
+ int reverse_output_num_channels = 1;
+ if (settings_.reverse_input_filename) {
+ std::unique_ptr<WavReader> reverse_in_file(
+ new WavReader(settings_.reverse_input_filename->c_str()));
+ reverse_sample_rate_hz = reverse_in_file->sample_rate();
+ reverse_num_channels = reverse_in_file->num_channels();
+ reverse_buffer_reader_.reset(
+ new ChannelBufferWavReader(std::move(reverse_in_file)));
+
+ reverse_output_sample_rate_hz =
+ settings_.reverse_output_sample_rate_hz
+ ? *settings_.reverse_output_sample_rate_hz
+ : reverse_sample_rate_hz;
+ reverse_output_num_channels = settings_.reverse_output_num_channels
+ ? *settings_.reverse_output_num_channels
+ : reverse_num_channels;
+ }
+
+ SetupBuffersConfigsOutputs(
+ input_sample_rate_hz, output_sample_rate_hz, reverse_sample_rate_hz,
+ reverse_output_sample_rate_hz, input_num_channels, output_num_channels,
+ reverse_num_channels, reverse_output_num_channels);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/modules/audio_processing/transient/BUILD.gn b/modules/audio_processing/transient/BUILD.gn
index 13e319f..5f9a139 100644
--- a/modules/audio_processing/transient/BUILD.gn
+++ b/modules/audio_processing/transient/BUILD.gn
@@ -14,10 +14,10 @@
rtc_library("transient_suppressor_impl") {
visibility = [
- "..:optionally_built_submodule_creators",
+ ":click_annotate",
":transient_suppression_test",
":transient_suppression_unittests",
- ":click_annotate",
+ "..:optionally_built_submodule_creators",
]
sources = [
"common.h",
@@ -49,42 +49,44 @@
}
if (rtc_include_tests) {
- rtc_executable("click_annotate") {
- testonly = true
- sources = [
- "click_annotate.cc",
- "file_utils.cc",
- "file_utils.h",
- ]
- deps = [
- ":transient_suppressor_impl",
- "..:audio_processing",
- "../../../rtc_base/system:file_wrapper",
- "../../../system_wrappers",
- ]
- }
+ if (!build_with_chromium) {
+ rtc_executable("click_annotate") {
+ testonly = true
+ sources = [
+ "click_annotate.cc",
+ "file_utils.cc",
+ "file_utils.h",
+ ]
+ deps = [
+ ":transient_suppressor_impl",
+ "..:audio_processing",
+ "../../../rtc_base/system:file_wrapper",
+ "../../../system_wrappers",
+ ]
+ }
- rtc_executable("transient_suppression_test") {
- testonly = true
- sources = [
- "file_utils.cc",
- "file_utils.h",
- "transient_suppression_test.cc",
- ]
- deps = [
- ":transient_suppressor_impl",
- "..:audio_processing",
- "../../../common_audio",
- "../../../rtc_base:rtc_base_approved",
- "../../../rtc_base/system:file_wrapper",
- "../../../system_wrappers",
- "../../../test:fileutils",
- "../../../test:test_support",
- "../agc:level_estimation",
- "//testing/gtest",
- "//third_party/abseil-cpp/absl/flags:flag",
- "//third_party/abseil-cpp/absl/flags:parse",
- ]
+ rtc_executable("transient_suppression_test") {
+ testonly = true
+ sources = [
+ "file_utils.cc",
+ "file_utils.h",
+ "transient_suppression_test.cc",
+ ]
+ deps = [
+ ":transient_suppressor_impl",
+ "..:audio_processing",
+ "../../../common_audio",
+ "../../../rtc_base:rtc_base_approved",
+ "../../../rtc_base/system:file_wrapper",
+ "../../../system_wrappers",
+ "../../../test:fileutils",
+ "../../../test:test_support",
+ "../agc:level_estimation",
+ "//testing/gtest",
+ "//third_party/abseil-cpp/absl/flags:flag",
+ "//third_party/abseil-cpp/absl/flags:parse",
+ ]
+ }
}
rtc_library("transient_suppression_unittests") {
diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn
index e10d846..a42c178 100644
--- a/modules/rtp_rtcp/BUILD.gn
+++ b/modules/rtp_rtcp/BUILD.gn
@@ -103,8 +103,10 @@
"..:module_api_public",
"../../api:array_view",
"../../api:function_view",
+ "../../api:refcountedbase",
"../../api:rtp_headers",
"../../api:rtp_parameters",
+ "../../api:scoped_refptr",
"../../api/audio_codecs:audio_codecs_api",
"../../api/transport:network_control",
"../../api/transport/rtp:dependency_descriptor",
@@ -114,10 +116,8 @@
"../../api/video:video_rtp_headers",
"../../common_video",
"../../rtc_base:checks",
- "../../rtc_base:deprecation",
"../../rtc_base:divide_round",
"../../rtc_base:rtc_base_approved",
- "../../rtc_base/system:unused",
"../../system_wrappers",
"../video_coding:codec_globals_headers",
]
@@ -136,7 +136,6 @@
"include/flexfec_sender.h",
"include/receive_statistics.h",
"include/remote_ntp_time_estimator.h",
- "include/rtp_rtcp.h", # deprecated
"include/ulpfec_receiver.h",
"source/absolute_capture_time_receiver.cc",
"source/absolute_capture_time_receiver.h",
@@ -146,8 +145,6 @@
"source/active_decode_targets_helper.h",
"source/create_video_rtp_depacketizer.cc",
"source/create_video_rtp_depacketizer.h",
- "source/deprecated/deprecated_rtp_sender_egress.cc",
- "source/deprecated/deprecated_rtp_sender_egress.h",
"source/dtmf_queue.cc",
"source/dtmf_queue.h",
"source/fec_private_tables_bursty.cc",
@@ -164,6 +161,8 @@
"source/forward_error_correction_internal.h",
"source/packet_loss_stats.cc",
"source/packet_loss_stats.h",
+ "source/packet_sequencer.cc",
+ "source/packet_sequencer.h",
"source/receive_statistics_impl.cc",
"source/receive_statistics_impl.h",
"source/remote_ntp_time_estimator.cc",
@@ -192,8 +191,6 @@
"source/rtp_packetizer_av1.cc",
"source/rtp_packetizer_av1.h",
"source/rtp_rtcp_config.h",
- "source/rtp_rtcp_impl.cc",
- "source/rtp_rtcp_impl.h",
"source/rtp_rtcp_impl2.cc",
"source/rtp_rtcp_impl2.h",
"source/rtp_rtcp_interface.h",
@@ -249,7 +246,6 @@
deps = [
":rtp_rtcp_format",
":rtp_video_header",
- "..:module_api",
"..:module_api_public",
"..:module_fec_api",
"../../api:array_view",
@@ -260,6 +256,7 @@
"../../api:rtp_packet_info",
"../../api:rtp_parameters",
"../../api:scoped_refptr",
+ "../../api:sequence_checker",
"../../api:transport_api",
"../../api/audio_codecs:audio_codecs_api",
"../../api/crypto:frame_encryptor_interface",
@@ -288,7 +285,6 @@
"../../logging:rtc_event_rtp_rtcp",
"../../modules/audio_coding:audio_coding_module_typedefs",
"../../rtc_base:checks",
- "../../rtc_base:deprecation",
"../../rtc_base:divide_round",
"../../rtc_base:gtest_prod",
"../../rtc_base:rate_limiter",
@@ -297,7 +293,6 @@
"../../rtc_base:safe_minmax",
"../../rtc_base/experiments:field_trial_parser",
"../../rtc_base/synchronization:mutex",
- "../../rtc_base/synchronization:sequence_checker",
"../../rtc_base/system:no_unique_address",
"../../rtc_base/task_utils:pending_task_safety_flag",
"../../rtc_base/task_utils:repeating_task",
@@ -320,8 +315,36 @@
}
rtc_source_set("rtp_rtcp_legacy") {
- # TODO(bugs.webrtc.org/11581): The files "source/rtp_rtcp_impl.cc"
- # and "source/rtp_rtcp_impl.h" should be moved to this target.
+ sources = [
+ "include/rtp_rtcp.h",
+ "source/deprecated/deprecated_rtp_sender_egress.cc",
+ "source/deprecated/deprecated_rtp_sender_egress.h",
+ "source/rtp_rtcp_impl.cc",
+ "source/rtp_rtcp_impl.h",
+ ]
+ deps = [
+ ":rtp_rtcp",
+ ":rtp_rtcp_format",
+ "..:module_api",
+ "..:module_fec_api",
+ "../../api:rtp_headers",
+ "../../api:transport_api",
+ "../../api/rtc_event_log",
+ "../../api/transport:field_trial_based_config",
+ "../../api/units:data_rate",
+ "../../api/video:video_bitrate_allocation",
+ "../../logging:rtc_event_rtp_rtcp",
+ "../../rtc_base:checks",
+ "../../rtc_base:gtest_prod",
+ "../../rtc_base:rtc_base_approved",
+ "../../rtc_base/synchronization:mutex",
+ "../../system_wrappers",
+ "../remote_bitrate_estimator",
+ ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/strings",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
}
rtc_library("rtcp_transceiver") {
@@ -352,6 +375,7 @@
]
absl_deps = [
"//third_party/abseil-cpp/absl/algorithm:container",
+ "//third_party/abseil-cpp/absl/base:core_headers",
"//third_party/abseil-cpp/absl/memory",
"//third_party/abseil-cpp/absl/types:optional",
]
@@ -412,22 +436,24 @@
}
if (rtc_include_tests) {
- rtc_executable("test_packet_masks_metrics") {
- testonly = true
+ if (!build_with_chromium) {
+ rtc_executable("test_packet_masks_metrics") {
+ testonly = true
- sources = [
- "test/testFec/average_residual_loss_xor_codes.h",
- "test/testFec/test_packet_masks_metrics.cc",
- ]
+ sources = [
+ "test/testFec/average_residual_loss_xor_codes.h",
+ "test/testFec/test_packet_masks_metrics.cc",
+ ]
- deps = [
- ":rtp_rtcp",
- "../../test:fileutils",
- "../../test:test_main",
- "../../test:test_support",
- "//testing/gtest",
- ]
- } # test_packet_masks_metrics
+ deps = [
+ ":rtp_rtcp",
+ "../../test:fileutils",
+ "../../test:test_main",
+ "../../test:test_support",
+ "//testing/gtest",
+ ]
+ } # test_packet_masks_metrics
+ }
rtc_library("rtp_rtcp_modules_tests") {
testonly = true
diff --git a/modules/rtp_rtcp/include/flexfec_receiver.h b/modules/rtp_rtcp/include/flexfec_receiver.h
index f9bac9c..b0caea6 100644
--- a/modules/rtp_rtcp/include/flexfec_receiver.h
+++ b/modules/rtp_rtcp/include/flexfec_receiver.h
@@ -15,11 +15,11 @@
#include <memory>
+#include "api/sequence_checker.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/include/ulpfec_receiver.h"
#include "modules/rtp_rtcp/source/forward_error_correction.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
-#include "rtc_base/synchronization/sequence_checker.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/thread_annotations.h"
diff --git a/modules/rtp_rtcp/include/receive_statistics.h b/modules/rtp_rtcp/include/receive_statistics.h
index 4e64413..ce87b99 100644
--- a/modules/rtp_rtcp/include/receive_statistics.h
+++ b/modules/rtp_rtcp/include/receive_statistics.h
@@ -17,11 +17,9 @@
#include "absl/types/optional.h"
#include "call/rtp_packet_sink_interface.h"
-#include "modules/include/module.h"
#include "modules/rtp_rtcp/include/rtcp_statistics.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
-#include "rtc_base/deprecation.h"
namespace webrtc {
@@ -57,7 +55,12 @@
public:
~ReceiveStatistics() override = default;
+ // Returns a thread-safe instance of ReceiveStatistics.
+ // https://chromium.googlesource.com/chromium/src/+/lkgr/docs/threading_and_tasks.md#threading-lexicon
static std::unique_ptr<ReceiveStatistics> Create(Clock* clock);
+ // Returns a thread-compatible instance of ReceiveStatistics.
+ static std::unique_ptr<ReceiveStatistics> CreateThreadCompatible(
+ Clock* clock);
// Returns a pointer to the statistician of an ssrc.
virtual StreamStatistician* GetStatistician(uint32_t ssrc) const = 0;
diff --git a/modules/rtp_rtcp/include/rtp_header_extension_map.h b/modules/rtp_rtcp/include/rtp_header_extension_map.h
index ff2d34d..72e5541 100644
--- a/modules/rtp_rtcp/include/rtp_header_extension_map.h
+++ b/modules/rtp_rtcp/include/rtp_header_extension_map.h
@@ -19,7 +19,6 @@
#include "api/rtp_parameters.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "rtc_base/checks.h"
-#include "rtc_base/deprecation.h"
namespace webrtc {
diff --git a/modules/rtp_rtcp/include/rtp_rtcp.h b/modules/rtp_rtcp/include/rtp_rtcp.h
index 8663296..727fc6e 100644
--- a/modules/rtp_rtcp/include/rtp_rtcp.h
+++ b/modules/rtp_rtcp/include/rtp_rtcp.h
@@ -12,12 +12,10 @@
#define MODULES_RTP_RTCP_INCLUDE_RTP_RTCP_H_
#include <memory>
-#include <string>
-#include <vector>
+#include "absl/base/attributes.h"
#include "modules/include/module.h"
#include "modules/rtp_rtcp/source/rtp_rtcp_interface.h"
-#include "rtc_base/deprecation.h"
namespace webrtc {
@@ -25,52 +23,14 @@
class RtpRtcp : public Module, public RtpRtcpInterface {
public:
// Instantiates a deprecated version of the RtpRtcp module.
- static std::unique_ptr<RtpRtcp> RTC_DEPRECATED
- Create(const Configuration& configuration) {
+ static std::unique_ptr<RtpRtcp> ABSL_DEPRECATED("")
+ Create(const Configuration& configuration) {
return DEPRECATED_Create(configuration);
}
static std::unique_ptr<RtpRtcp> DEPRECATED_Create(
const Configuration& configuration);
- // (TMMBR) Temporary Max Media Bit Rate
- RTC_DEPRECATED virtual bool TMMBR() const = 0;
-
- RTC_DEPRECATED virtual void SetTMMBRStatus(bool enable) = 0;
-
- // Returns -1 on failure else 0.
- RTC_DEPRECATED virtual int32_t AddMixedCNAME(uint32_t ssrc,
- const char* cname) = 0;
-
- // Returns -1 on failure else 0.
- RTC_DEPRECATED virtual int32_t RemoveMixedCNAME(uint32_t ssrc) = 0;
-
- // Returns remote CName.
- // Returns -1 on failure else 0.
- RTC_DEPRECATED virtual int32_t RemoteCNAME(
- uint32_t remote_ssrc,
- char cname[RTCP_CNAME_SIZE]) const = 0;
-
- // (De)registers RTP header extension type and id.
- // Returns -1 on failure else 0.
- RTC_DEPRECATED virtual int32_t RegisterSendRtpHeaderExtension(
- RTPExtensionType type,
- uint8_t id) = 0;
-
- // (APP) Sets application specific data.
- // Returns -1 on failure else 0.
- RTC_DEPRECATED virtual int32_t SetRTCPApplicationSpecificData(
- uint8_t sub_type,
- uint32_t name,
- const uint8_t* data,
- uint16_t length) = 0;
-
- // Returns statistics of the amount of data sent.
- // Returns -1 on failure else 0.
- RTC_DEPRECATED virtual int32_t DataCountersRTP(
- size_t* bytes_sent,
- uint32_t* packets_sent) const = 0;
-
// Requests new key frame.
// using PLI, https://tools.ietf.org/html/rfc4585#section-6.3.1.1
void SendPictureLossIndication() { SendRTCP(kRtcpPli); }
diff --git a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
index 77289c9..d523128 100644
--- a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
+++ b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
@@ -141,14 +141,14 @@
GetSendStreamDataCounters,
(StreamDataCounters*, StreamDataCounters*),
(const, override));
- MOCK_METHOD(int32_t,
- RemoteRTCPStat,
- (std::vector<RTCPReportBlock> * receive_blocks),
- (const, override));
MOCK_METHOD(std::vector<ReportBlockData>,
GetLatestReportBlockData,
(),
(const, override));
+ MOCK_METHOD(absl::optional<SenderReportStats>,
+ GetSenderReportStats,
+ (),
+ (const, override));
MOCK_METHOD(void,
SetRemb,
(int64_t bitrate, std::vector<uint32_t> ssrcs),
diff --git a/modules/rtp_rtcp/source/deprecated/deprecated_rtp_sender_egress.cc b/modules/rtp_rtcp/source/deprecated/deprecated_rtp_sender_egress.cc
index 6cb9d93..3f7d22c 100644
--- a/modules/rtp_rtcp/source/deprecated/deprecated_rtp_sender_egress.cc
+++ b/modules/rtp_rtcp/source/deprecated/deprecated_rtp_sender_egress.cc
@@ -176,8 +176,7 @@
AddPacketToTransportFeedback(*packet_id, *packet, pacing_info);
}
- options.application_data.assign(packet->application_data().begin(),
- packet->application_data().end());
+ options.additional_data = packet->additional_data();
if (packet->packet_type() != RtpPacketMediaType::kPadding &&
packet->packet_type() != RtpPacketMediaType::kRetransmission) {
diff --git a/modules/rtp_rtcp/source/packet_sequencer.cc b/modules/rtp_rtcp/source/packet_sequencer.cc
new file mode 100644
index 0000000..03ea9b8
--- /dev/null
+++ b/modules/rtp_rtcp/source/packet_sequencer.cc
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2021 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 "modules/rtp_rtcp/source/packet_sequencer.h"
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+namespace {
+// RED header is first byte of payload, if present.
+constexpr size_t kRedForFecHeaderLength = 1;
+
+// Timestamps use a 90kHz clock.
+constexpr uint32_t kTimestampTicksPerMs = 90;
+} // namespace
+
+PacketSequencer::PacketSequencer(uint32_t media_ssrc,
+ uint32_t rtx_ssrc,
+ bool require_marker_before_media_padding,
+ Clock* clock)
+ : media_ssrc_(media_ssrc),
+ rtx_ssrc_(rtx_ssrc),
+ require_marker_before_media_padding_(require_marker_before_media_padding),
+ clock_(clock),
+ media_sequence_number_(0),
+ rtx_sequence_number_(0),
+ last_payload_type_(-1),
+ last_rtp_timestamp_(0),
+ last_capture_time_ms_(0),
+ last_timestamp_time_ms_(0),
+ last_packet_marker_bit_(false) {}
+
+bool PacketSequencer::Sequence(RtpPacketToSend& packet) {
+ if (packet.packet_type() == RtpPacketMediaType::kPadding &&
+ !PopulatePaddingFields(packet)) {
+ // This padding packet can't be sent with current state, return without
+ // updating the sequence number.
+ return false;
+ }
+
+ if (packet.Ssrc() == media_ssrc_) {
+ packet.SetSequenceNumber(media_sequence_number_++);
+ if (packet.packet_type() != RtpPacketMediaType::kPadding) {
+ UpdateLastPacketState(packet);
+ }
+ return true;
+ }
+
+ RTC_DCHECK_EQ(packet.Ssrc(), rtx_ssrc_);
+ packet.SetSequenceNumber(rtx_sequence_number_++);
+ return true;
+}
+
+void PacketSequencer::SetRtpState(const RtpState& state) {
+ media_sequence_number_ = state.sequence_number;
+ last_rtp_timestamp_ = state.timestamp;
+ last_capture_time_ms_ = state.capture_time_ms;
+ last_timestamp_time_ms_ = state.last_timestamp_time_ms;
+}
+
+void PacketSequencer::PupulateRtpState(RtpState& state) const {
+ state.sequence_number = media_sequence_number_;
+ state.timestamp = last_rtp_timestamp_;
+ state.capture_time_ms = last_capture_time_ms_;
+ state.last_timestamp_time_ms = last_timestamp_time_ms_;
+}
+
+void PacketSequencer::UpdateLastPacketState(const RtpPacketToSend& packet) {
+ // Remember marker bit to determine if padding can be inserted with
+ // sequence number following |packet|.
+ last_packet_marker_bit_ = packet.Marker();
+ // Remember media payload type to use in the padding packet if rtx is
+ // disabled.
+ if (packet.is_red()) {
+ RTC_DCHECK_GE(packet.payload_size(), kRedForFecHeaderLength);
+ last_payload_type_ = packet.PayloadBuffer()[0];
+ } else {
+ last_payload_type_ = packet.PayloadType();
+ }
+ // Save timestamps to generate timestamp field and extensions for the padding.
+ last_rtp_timestamp_ = packet.Timestamp();
+ last_timestamp_time_ms_ = clock_->TimeInMilliseconds();
+ last_capture_time_ms_ = packet.capture_time_ms();
+}
+
+bool PacketSequencer::PopulatePaddingFields(RtpPacketToSend& packet) {
+ if (packet.Ssrc() == media_ssrc_) {
+ if (last_payload_type_ == -1) {
+ return false;
+ }
+
+ // Without RTX we can't send padding in the middle of frames.
+ // For audio marker bits doesn't mark the end of a frame and frames
+ // are usually a single packet, so for now we don't apply this rule
+ // for audio.
+ if (require_marker_before_media_padding_ && !last_packet_marker_bit_) {
+ return false;
+ }
+
+ packet.SetTimestamp(last_rtp_timestamp_);
+ packet.set_capture_time_ms(last_capture_time_ms_);
+ packet.SetPayloadType(last_payload_type_);
+ return true;
+ }
+
+ RTC_DCHECK_EQ(packet.Ssrc(), rtx_ssrc_);
+ if (packet.payload_size() > 0) {
+ // This is payload padding packet, don't update timestamp fields.
+ return true;
+ }
+
+ packet.SetTimestamp(last_rtp_timestamp_);
+ packet.set_capture_time_ms(last_capture_time_ms_);
+
+ // Only change the timestamp of padding packets sent over RTX.
+ // Padding only packets over RTP has to be sent as part of a media
+ // frame (and therefore the same timestamp).
+ int64_t now_ms = clock_->TimeInMilliseconds();
+ if (last_timestamp_time_ms_ > 0) {
+ packet.SetTimestamp(packet.Timestamp() +
+ (now_ms - last_timestamp_time_ms_) *
+ kTimestampTicksPerMs);
+ if (packet.capture_time_ms() > 0) {
+ packet.set_capture_time_ms(packet.capture_time_ms() +
+ (now_ms - last_timestamp_time_ms_));
+ }
+ }
+
+ return true;
+}
+
+} // namespace webrtc
diff --git a/modules/rtp_rtcp/source/packet_sequencer.h b/modules/rtp_rtcp/source/packet_sequencer.h
new file mode 100644
index 0000000..6725516
--- /dev/null
+++ b/modules/rtp_rtcp/source/packet_sequencer.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2021 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 MODULES_RTP_RTCP_SOURCE_PACKET_SEQUENCER_H_
+#define MODULES_RTP_RTCP_SOURCE_PACKET_SEQUENCER_H_
+
+#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
+#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
+#include "system_wrappers/include/clock.h"
+
+namespace webrtc {
+
+// Helper class used to assign RTP sequence numbers and populate some fields for
+// padding packets based on the last sequenced packets.
+// This class is not thread safe, the caller must provide that.
+class PacketSequencer {
+ public:
+ // If |require_marker_before_media_padding_| is true, padding packets on the
+ // media ssrc is not allowed unless the last sequenced media packet had the
+ // marker bit set (i.e. don't insert padding packets between the first and
+ // last packets of a video frame).
+ PacketSequencer(uint32_t media_ssrc,
+ uint32_t rtx_ssrc,
+ bool require_marker_before_media_padding,
+ Clock* clock);
+
+ // Assigns sequence number, and in the case of non-RTX padding also timestamps
+ // and payload type.
+ // Returns false if sequencing failed, which it can do for instance if the
+ // packet to squence is padding on the media ssrc, but the media is mid frame
+ // (the last marker bit is false).
+ bool Sequence(RtpPacketToSend& packet);
+
+ void set_media_sequence_number(uint16_t sequence_number) {
+ media_sequence_number_ = sequence_number;
+ }
+ void set_rtx_sequence_number(uint16_t sequence_number) {
+ rtx_sequence_number_ = sequence_number;
+ }
+
+ void SetRtpState(const RtpState& state);
+ void PupulateRtpState(RtpState& state) const;
+
+ uint16_t media_sequence_number() const { return media_sequence_number_; }
+ uint16_t rtx_sequence_number() const { return rtx_sequence_number_; }
+
+ private:
+ void UpdateLastPacketState(const RtpPacketToSend& packet);
+ bool PopulatePaddingFields(RtpPacketToSend& packet);
+
+ const uint32_t media_ssrc_;
+ const uint32_t rtx_ssrc_;
+ const bool require_marker_before_media_padding_;
+ Clock* const clock_;
+
+ uint16_t media_sequence_number_;
+ uint16_t rtx_sequence_number_;
+
+ int8_t last_payload_type_;
+ uint32_t last_rtp_timestamp_;
+ int64_t last_capture_time_ms_;
+ int64_t last_timestamp_time_ms_;
+ bool last_packet_marker_bit_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_RTP_RTCP_SOURCE_PACKET_SEQUENCER_H_
diff --git a/modules/rtp_rtcp/source/receive_statistics_impl.cc b/modules/rtp_rtcp/source/receive_statistics_impl.cc
index 6ec41a1..4c399a1 100644
--- a/modules/rtp_rtcp/source/receive_statistics_impl.cc
+++ b/modules/rtp_rtcp/source/receive_statistics_impl.cc
@@ -13,6 +13,7 @@
#include <cmath>
#include <cstdlib>
#include <memory>
+#include <utility>
#include <vector>
#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
@@ -100,7 +101,6 @@
}
void StreamStatisticianImpl::UpdateCounters(const RtpPacketReceived& packet) {
- MutexLock lock(&stream_lock_);
RTC_DCHECK_EQ(ssrc_, packet.Ssrc());
int64_t now_ms = clock_->TimeInMilliseconds();
@@ -159,17 +159,14 @@
void StreamStatisticianImpl::SetMaxReorderingThreshold(
int max_reordering_threshold) {
- MutexLock lock(&stream_lock_);
max_reordering_threshold_ = max_reordering_threshold;
}
void StreamStatisticianImpl::EnableRetransmitDetection(bool enable) {
- MutexLock lock(&stream_lock_);
enable_retransmit_detection_ = enable;
}
RtpReceiveStats StreamStatisticianImpl::GetStats() const {
- MutexLock lock(&stream_lock_);
RtpReceiveStats stats;
stats.packets_lost = cumulative_loss_;
// TODO(nisse): Can we return a float instead?
@@ -183,7 +180,6 @@
bool StreamStatisticianImpl::GetActiveStatisticsAndReset(
RtcpStatistics* statistics) {
- MutexLock lock(&stream_lock_);
if (clock_->TimeInMilliseconds() - last_receive_time_ms_ >=
kStatisticsTimeoutMs) {
// Not active.
@@ -192,9 +188,7 @@
if (!ReceivedRtpPacket()) {
return false;
}
-
*statistics = CalculateRtcpStatistics();
-
return true;
}
@@ -241,7 +235,6 @@
}
absl::optional<int> StreamStatisticianImpl::GetFractionLostInPercent() const {
- MutexLock lock(&stream_lock_);
if (!ReceivedRtpPacket()) {
return absl::nullopt;
}
@@ -257,12 +250,10 @@
StreamDataCounters StreamStatisticianImpl::GetReceiveStreamDataCounters()
const {
- MutexLock lock(&stream_lock_);
return receive_counters_;
}
uint32_t StreamStatisticianImpl::BitrateReceived() const {
- MutexLock lock(&stream_lock_);
return incoming_bitrate_.Rate(clock_->TimeInMilliseconds()).value_or(0);
}
@@ -295,21 +286,33 @@
}
std::unique_ptr<ReceiveStatistics> ReceiveStatistics::Create(Clock* clock) {
- return std::make_unique<ReceiveStatisticsImpl>(clock);
+ return std::make_unique<ReceiveStatisticsLocked>(
+ clock, [](uint32_t ssrc, Clock* clock, int max_reordering_threshold) {
+ return std::make_unique<StreamStatisticianLocked>(
+ ssrc, clock, max_reordering_threshold);
+ });
}
-ReceiveStatisticsImpl::ReceiveStatisticsImpl(Clock* clock)
+std::unique_ptr<ReceiveStatistics> ReceiveStatistics::CreateThreadCompatible(
+ Clock* clock) {
+ return std::make_unique<ReceiveStatisticsImpl>(
+ clock, [](uint32_t ssrc, Clock* clock, int max_reordering_threshold) {
+ return std::make_unique<StreamStatisticianImpl>(
+ ssrc, clock, max_reordering_threshold);
+ });
+}
+
+ReceiveStatisticsImpl::ReceiveStatisticsImpl(
+ Clock* clock,
+ std::function<std::unique_ptr<StreamStatisticianImplInterface>(
+ uint32_t ssrc,
+ Clock* clock,
+ int max_reordering_threshold)> stream_statistician_factory)
: clock_(clock),
+ stream_statistician_factory_(std::move(stream_statistician_factory)),
last_returned_ssrc_(0),
max_reordering_threshold_(kDefaultMaxReorderingThreshold) {}
-ReceiveStatisticsImpl::~ReceiveStatisticsImpl() {
- while (!statisticians_.empty()) {
- delete statisticians_.begin()->second;
- statisticians_.erase(statisticians_.begin());
- }
-}
-
void ReceiveStatisticsImpl::OnRtpPacket(const RtpPacketReceived& packet) {
// StreamStatisticianImpl instance is created once and only destroyed when
// this whole ReceiveStatisticsImpl is destroyed. StreamStatisticianImpl has
@@ -318,34 +321,28 @@
GetOrCreateStatistician(packet.Ssrc())->UpdateCounters(packet);
}
-StreamStatisticianImpl* ReceiveStatisticsImpl::GetStatistician(
+StreamStatistician* ReceiveStatisticsImpl::GetStatistician(
uint32_t ssrc) const {
- MutexLock lock(&receive_statistics_lock_);
const auto& it = statisticians_.find(ssrc);
if (it == statisticians_.end())
- return NULL;
- return it->second;
+ return nullptr;
+ return it->second.get();
}
-StreamStatisticianImpl* ReceiveStatisticsImpl::GetOrCreateStatistician(
+StreamStatisticianImplInterface* ReceiveStatisticsImpl::GetOrCreateStatistician(
uint32_t ssrc) {
- MutexLock lock(&receive_statistics_lock_);
- StreamStatisticianImpl*& impl = statisticians_[ssrc];
+ std::unique_ptr<StreamStatisticianImplInterface>& impl = statisticians_[ssrc];
if (impl == nullptr) { // new element
- impl = new StreamStatisticianImpl(ssrc, clock_, max_reordering_threshold_);
+ impl =
+ stream_statistician_factory_(ssrc, clock_, max_reordering_threshold_);
}
- return impl;
+ return impl.get();
}
void ReceiveStatisticsImpl::SetMaxReorderingThreshold(
int max_reordering_threshold) {
- std::map<uint32_t, StreamStatisticianImpl*> statisticians;
- {
- MutexLock lock(&receive_statistics_lock_);
- max_reordering_threshold_ = max_reordering_threshold;
- statisticians = statisticians_;
- }
- for (auto& statistician : statisticians) {
+ max_reordering_threshold_ = max_reordering_threshold;
+ for (auto& statistician : statisticians_) {
statistician.second->SetMaxReorderingThreshold(max_reordering_threshold);
}
}
@@ -364,15 +361,11 @@
std::vector<rtcp::ReportBlock> ReceiveStatisticsImpl::RtcpReportBlocks(
size_t max_blocks) {
- std::map<uint32_t, StreamStatisticianImpl*> statisticians;
- {
- MutexLock lock(&receive_statistics_lock_);
- statisticians = statisticians_;
- }
std::vector<rtcp::ReportBlock> result;
- result.reserve(std::min(max_blocks, statisticians.size()));
- auto add_report_block = [&result](uint32_t media_ssrc,
- StreamStatisticianImpl* statistician) {
+ result.reserve(std::min(max_blocks, statisticians_.size()));
+ auto add_report_block = [&result](
+ uint32_t media_ssrc,
+ StreamStatisticianImplInterface* statistician) {
// Do we have receive statistics to send?
RtcpStatistics stats;
if (!statistician->GetActiveStatisticsAndReset(&stats))
@@ -390,13 +383,13 @@
block.SetJitter(stats.jitter);
};
- const auto start_it = statisticians.upper_bound(last_returned_ssrc_);
+ const auto start_it = statisticians_.upper_bound(last_returned_ssrc_);
for (auto it = start_it;
- result.size() < max_blocks && it != statisticians.end(); ++it)
- add_report_block(it->first, it->second);
- for (auto it = statisticians.begin();
+ result.size() < max_blocks && it != statisticians_.end(); ++it)
+ add_report_block(it->first, it->second.get());
+ for (auto it = statisticians_.begin();
result.size() < max_blocks && it != start_it; ++it)
- add_report_block(it->first, it->second);
+ add_report_block(it->first, it->second.get());
if (!result.empty())
last_returned_ssrc_ = result.back().source_ssrc();
diff --git a/modules/rtp_rtcp/source/receive_statistics_impl.h b/modules/rtp_rtcp/source/receive_statistics_impl.h
index 41830b0..2456f93 100644
--- a/modules/rtp_rtcp/source/receive_statistics_impl.h
+++ b/modules/rtp_rtcp/source/receive_statistics_impl.h
@@ -12,7 +12,10 @@
#define MODULES_RTP_RTCP_SOURCE_RECEIVE_STATISTICS_IMPL_H_
#include <algorithm>
+#include <functional>
#include <map>
+#include <memory>
+#include <utility>
#include <vector>
#include "absl/types/optional.h"
@@ -24,86 +27,141 @@
namespace webrtc {
-class StreamStatisticianImpl : public StreamStatistician {
+// Extends StreamStatistician with methods needed by the implementation.
+class StreamStatisticianImplInterface : public StreamStatistician {
+ public:
+ virtual ~StreamStatisticianImplInterface() = default;
+ virtual bool GetActiveStatisticsAndReset(RtcpStatistics* statistics) = 0;
+ virtual void SetMaxReorderingThreshold(int max_reordering_threshold) = 0;
+ virtual void EnableRetransmitDetection(bool enable) = 0;
+ virtual void UpdateCounters(const RtpPacketReceived& packet) = 0;
+};
+
+// Thread-compatible implementation of StreamStatisticianImplInterface.
+class StreamStatisticianImpl : public StreamStatisticianImplInterface {
public:
StreamStatisticianImpl(uint32_t ssrc,
Clock* clock,
int max_reordering_threshold);
~StreamStatisticianImpl() override;
+ // Implements StreamStatistician
RtpReceiveStats GetStats() const override;
-
- bool GetActiveStatisticsAndReset(RtcpStatistics* statistics);
absl::optional<int> GetFractionLostInPercent() const override;
StreamDataCounters GetReceiveStreamDataCounters() const override;
uint32_t BitrateReceived() const override;
- void SetMaxReorderingThreshold(int max_reordering_threshold);
- void EnableRetransmitDetection(bool enable);
-
+ // Implements StreamStatisticianImplInterface
+ bool GetActiveStatisticsAndReset(RtcpStatistics* statistics) override;
+ void SetMaxReorderingThreshold(int max_reordering_threshold) override;
+ void EnableRetransmitDetection(bool enable) override;
// Updates StreamStatistician for incoming packets.
- void UpdateCounters(const RtpPacketReceived& packet);
+ void UpdateCounters(const RtpPacketReceived& packet) override;
private:
bool IsRetransmitOfOldPacket(const RtpPacketReceived& packet,
- int64_t now_ms) const
- RTC_EXCLUSIVE_LOCKS_REQUIRED(stream_lock_);
- RtcpStatistics CalculateRtcpStatistics()
- RTC_EXCLUSIVE_LOCKS_REQUIRED(stream_lock_);
- void UpdateJitter(const RtpPacketReceived& packet, int64_t receive_time_ms)
- RTC_EXCLUSIVE_LOCKS_REQUIRED(stream_lock_);
+ int64_t now_ms) const;
+ RtcpStatistics CalculateRtcpStatistics();
+ void UpdateJitter(const RtpPacketReceived& packet, int64_t receive_time_ms);
// Updates StreamStatistician for out of order packets.
// Returns true if packet considered to be out of order.
bool UpdateOutOfOrder(const RtpPacketReceived& packet,
int64_t sequence_number,
- int64_t now_ms)
- RTC_EXCLUSIVE_LOCKS_REQUIRED(stream_lock_);
+ int64_t now_ms);
// Checks if this StreamStatistician received any rtp packets.
- bool ReceivedRtpPacket() const RTC_EXCLUSIVE_LOCKS_REQUIRED(stream_lock_) {
- return received_seq_first_ >= 0;
- }
+ bool ReceivedRtpPacket() const { return received_seq_first_ >= 0; }
const uint32_t ssrc_;
Clock* const clock_;
- mutable Mutex stream_lock_;
- RateStatistics incoming_bitrate_ RTC_GUARDED_BY(&stream_lock_);
+ RateStatistics incoming_bitrate_;
// In number of packets or sequence numbers.
- int max_reordering_threshold_ RTC_GUARDED_BY(&stream_lock_);
- bool enable_retransmit_detection_ RTC_GUARDED_BY(&stream_lock_);
+ int max_reordering_threshold_;
+ bool enable_retransmit_detection_;
// Stats on received RTP packets.
- uint32_t jitter_q4_ RTC_GUARDED_BY(&stream_lock_);
+ uint32_t jitter_q4_;
// Cumulative loss according to RFC 3550, which may be negative (and often is,
// if packets are reordered and there are non-RTX retransmissions).
- int32_t cumulative_loss_ RTC_GUARDED_BY(&stream_lock_);
+ int32_t cumulative_loss_;
// Offset added to outgoing rtcp reports, to make ensure that the reported
// cumulative loss is non-negative. Reports with negative values confuse some
// senders, in particular, our own loss-based bandwidth estimator.
- int32_t cumulative_loss_rtcp_offset_ RTC_GUARDED_BY(&stream_lock_);
+ int32_t cumulative_loss_rtcp_offset_;
- int64_t last_receive_time_ms_ RTC_GUARDED_BY(&stream_lock_);
- uint32_t last_received_timestamp_ RTC_GUARDED_BY(&stream_lock_);
- SequenceNumberUnwrapper seq_unwrapper_ RTC_GUARDED_BY(&stream_lock_);
- int64_t received_seq_first_ RTC_GUARDED_BY(&stream_lock_);
- int64_t received_seq_max_ RTC_GUARDED_BY(&stream_lock_);
+ int64_t last_receive_time_ms_;
+ uint32_t last_received_timestamp_;
+ SequenceNumberUnwrapper seq_unwrapper_;
+ int64_t received_seq_first_;
+ int64_t received_seq_max_;
// Assume that the other side restarted when there are two sequential packets
// with large jump from received_seq_max_.
- absl::optional<uint16_t> received_seq_out_of_order_
- RTC_GUARDED_BY(&stream_lock_);
+ absl::optional<uint16_t> received_seq_out_of_order_;
// Current counter values.
- StreamDataCounters receive_counters_ RTC_GUARDED_BY(&stream_lock_);
+ StreamDataCounters receive_counters_;
// Counter values when we sent the last report.
- int32_t last_report_cumulative_loss_ RTC_GUARDED_BY(&stream_lock_);
- int64_t last_report_seq_max_ RTC_GUARDED_BY(&stream_lock_);
+ int32_t last_report_cumulative_loss_;
+ int64_t last_report_seq_max_;
};
+// Thread-safe implementation of StreamStatisticianImplInterface.
+class StreamStatisticianLocked : public StreamStatisticianImplInterface {
+ public:
+ StreamStatisticianLocked(uint32_t ssrc,
+ Clock* clock,
+ int max_reordering_threshold)
+ : impl_(ssrc, clock, max_reordering_threshold) {}
+ ~StreamStatisticianLocked() override = default;
+
+ RtpReceiveStats GetStats() const override {
+ MutexLock lock(&stream_lock_);
+ return impl_.GetStats();
+ }
+ absl::optional<int> GetFractionLostInPercent() const override {
+ MutexLock lock(&stream_lock_);
+ return impl_.GetFractionLostInPercent();
+ }
+ StreamDataCounters GetReceiveStreamDataCounters() const override {
+ MutexLock lock(&stream_lock_);
+ return impl_.GetReceiveStreamDataCounters();
+ }
+ uint32_t BitrateReceived() const override {
+ MutexLock lock(&stream_lock_);
+ return impl_.BitrateReceived();
+ }
+ bool GetActiveStatisticsAndReset(RtcpStatistics* statistics) override {
+ MutexLock lock(&stream_lock_);
+ return impl_.GetActiveStatisticsAndReset(statistics);
+ }
+ void SetMaxReorderingThreshold(int max_reordering_threshold) override {
+ MutexLock lock(&stream_lock_);
+ return impl_.SetMaxReorderingThreshold(max_reordering_threshold);
+ }
+ void EnableRetransmitDetection(bool enable) override {
+ MutexLock lock(&stream_lock_);
+ return impl_.EnableRetransmitDetection(enable);
+ }
+ void UpdateCounters(const RtpPacketReceived& packet) override {
+ MutexLock lock(&stream_lock_);
+ return impl_.UpdateCounters(packet);
+ }
+
+ private:
+ mutable Mutex stream_lock_;
+ StreamStatisticianImpl impl_ RTC_GUARDED_BY(&stream_lock_);
+};
+
+// Thread-compatible implementation.
class ReceiveStatisticsImpl : public ReceiveStatistics {
public:
- explicit ReceiveStatisticsImpl(Clock* clock);
-
- ~ReceiveStatisticsImpl() override;
+ ReceiveStatisticsImpl(
+ Clock* clock,
+ std::function<std::unique_ptr<StreamStatisticianImplInterface>(
+ uint32_t ssrc,
+ Clock* clock,
+ int max_reordering_threshold)> stream_statistician_factory);
+ ~ReceiveStatisticsImpl() override = default;
// Implements ReceiveStatisticsProvider.
std::vector<rtcp::ReportBlock> RtcpReportBlocks(size_t max_blocks) override;
@@ -112,22 +170,69 @@
void OnRtpPacket(const RtpPacketReceived& packet) override;
// Implements ReceiveStatistics.
- // Note: More specific return type for use in the implementation.
- StreamStatisticianImpl* GetStatistician(uint32_t ssrc) const override;
+ StreamStatistician* GetStatistician(uint32_t ssrc) const override;
void SetMaxReorderingThreshold(int max_reordering_threshold) override;
void SetMaxReorderingThreshold(uint32_t ssrc,
int max_reordering_threshold) override;
void EnableRetransmitDetection(uint32_t ssrc, bool enable) override;
private:
- StreamStatisticianImpl* GetOrCreateStatistician(uint32_t ssrc);
+ StreamStatisticianImplInterface* GetOrCreateStatistician(uint32_t ssrc);
Clock* const clock_;
- mutable Mutex receive_statistics_lock_;
+ std::function<std::unique_ptr<StreamStatisticianImplInterface>(
+ uint32_t ssrc,
+ Clock* clock,
+ int max_reordering_threshold)>
+ stream_statistician_factory_;
uint32_t last_returned_ssrc_;
- int max_reordering_threshold_ RTC_GUARDED_BY(receive_statistics_lock_);
- std::map<uint32_t, StreamStatisticianImpl*> statisticians_
- RTC_GUARDED_BY(receive_statistics_lock_);
+ int max_reordering_threshold_;
+ std::map<uint32_t, std::unique_ptr<StreamStatisticianImplInterface>>
+ statisticians_;
};
+
+// Thread-safe implementation wrapping access to ReceiveStatisticsImpl with a
+// mutex.
+class ReceiveStatisticsLocked : public ReceiveStatistics {
+ public:
+ explicit ReceiveStatisticsLocked(
+ Clock* clock,
+ std::function<std::unique_ptr<StreamStatisticianImplInterface>(
+ uint32_t ssrc,
+ Clock* clock,
+ int max_reordering_threshold)> stream_statitician_factory)
+ : impl_(clock, std::move(stream_statitician_factory)) {}
+ ~ReceiveStatisticsLocked() override = default;
+ std::vector<rtcp::ReportBlock> RtcpReportBlocks(size_t max_blocks) override {
+ MutexLock lock(&receive_statistics_lock_);
+ return impl_.RtcpReportBlocks(max_blocks);
+ }
+ void OnRtpPacket(const RtpPacketReceived& packet) override {
+ MutexLock lock(&receive_statistics_lock_);
+ return impl_.OnRtpPacket(packet);
+ }
+ StreamStatistician* GetStatistician(uint32_t ssrc) const override {
+ MutexLock lock(&receive_statistics_lock_);
+ return impl_.GetStatistician(ssrc);
+ }
+ void SetMaxReorderingThreshold(int max_reordering_threshold) override {
+ MutexLock lock(&receive_statistics_lock_);
+ return impl_.SetMaxReorderingThreshold(max_reordering_threshold);
+ }
+ void SetMaxReorderingThreshold(uint32_t ssrc,
+ int max_reordering_threshold) override {
+ MutexLock lock(&receive_statistics_lock_);
+ return impl_.SetMaxReorderingThreshold(ssrc, max_reordering_threshold);
+ }
+ void EnableRetransmitDetection(uint32_t ssrc, bool enable) override {
+ MutexLock lock(&receive_statistics_lock_);
+ return impl_.EnableRetransmitDetection(ssrc, enable);
+ }
+
+ private:
+ mutable Mutex receive_statistics_lock_;
+ ReceiveStatisticsImpl impl_ RTC_GUARDED_BY(&receive_statistics_lock_);
+};
+
} // namespace webrtc
#endif // MODULES_RTP_RTCP_SOURCE_RECEIVE_STATISTICS_IMPL_H_
diff --git a/modules/rtp_rtcp/source/receive_statistics_unittest.cc b/modules/rtp_rtcp/source/receive_statistics_unittest.cc
index 053460e..d40a743 100644
--- a/modules/rtp_rtcp/source/receive_statistics_unittest.cc
+++ b/modules/rtp_rtcp/source/receive_statistics_unittest.cc
@@ -65,10 +65,13 @@
IncrementSequenceNumber(packet, 1);
}
-class ReceiveStatisticsTest : public ::testing::Test {
+class ReceiveStatisticsTest : public ::testing::TestWithParam<bool> {
public:
ReceiveStatisticsTest()
- : clock_(0), receive_statistics_(ReceiveStatistics::Create(&clock_)) {
+ : clock_(0),
+ receive_statistics_(
+ GetParam() ? ReceiveStatistics::Create(&clock_)
+ : ReceiveStatistics::CreateThreadCompatible(&clock_)) {
packet1_ = CreateRtpPacket(kSsrc1, kPacketSize1);
packet2_ = CreateRtpPacket(kSsrc2, kPacketSize2);
}
@@ -80,7 +83,14 @@
RtpPacketReceived packet2_;
};
-TEST_F(ReceiveStatisticsTest, TwoIncomingSsrcs) {
+INSTANTIATE_TEST_SUITE_P(All,
+ ReceiveStatisticsTest,
+ ::testing::Bool(),
+ [](::testing::TestParamInfo<bool> info) {
+ return info.param ? "WithMutex" : "WithoutMutex";
+ });
+
+TEST_P(ReceiveStatisticsTest, TwoIncomingSsrcs) {
receive_statistics_->OnRtpPacket(packet1_);
IncrementSequenceNumber(&packet1_);
receive_statistics_->OnRtpPacket(packet2_);
@@ -133,7 +143,7 @@
EXPECT_EQ(3u, counters.transmitted.packets);
}
-TEST_F(ReceiveStatisticsTest,
+TEST_P(ReceiveStatisticsTest,
RtcpReportBlocksReturnsMaxBlocksWhenThereAreMoreStatisticians) {
RtpPacketReceived packet1 = CreateRtpPacket(kSsrc1, kPacketSize1);
RtpPacketReceived packet2 = CreateRtpPacket(kSsrc2, kPacketSize1);
@@ -147,7 +157,7 @@
EXPECT_THAT(receive_statistics_->RtcpReportBlocks(2), SizeIs(2));
}
-TEST_F(ReceiveStatisticsTest,
+TEST_P(ReceiveStatisticsTest,
RtcpReportBlocksReturnsAllObservedSsrcsWithMultipleCalls) {
RtpPacketReceived packet1 = CreateRtpPacket(kSsrc1, kPacketSize1);
RtpPacketReceived packet2 = CreateRtpPacket(kSsrc2, kPacketSize1);
@@ -174,7 +184,7 @@
UnorderedElementsAre(kSsrc1, kSsrc2, kSsrc3, kSsrc4));
}
-TEST_F(ReceiveStatisticsTest, ActiveStatisticians) {
+TEST_P(ReceiveStatisticsTest, ActiveStatisticians) {
receive_statistics_->OnRtpPacket(packet1_);
IncrementSequenceNumber(&packet1_);
clock_.AdvanceTimeMilliseconds(1000);
@@ -206,7 +216,7 @@
EXPECT_EQ(2u, counters.transmitted.packets);
}
-TEST_F(ReceiveStatisticsTest,
+TEST_P(ReceiveStatisticsTest,
DoesntCreateRtcpReportBlockUntilFirstReceivedPacketForSsrc) {
// Creates a statistician object for the ssrc.
receive_statistics_->EnableRetransmitDetection(kSsrc1, true);
@@ -217,7 +227,7 @@
EXPECT_EQ(1u, receive_statistics_->RtcpReportBlocks(3).size());
}
-TEST_F(ReceiveStatisticsTest, GetReceiveStreamDataCounters) {
+TEST_P(ReceiveStatisticsTest, GetReceiveStreamDataCounters) {
receive_statistics_->OnRtpPacket(packet1_);
StreamStatistician* statistician =
receive_statistics_->GetStatistician(kSsrc1);
@@ -233,7 +243,7 @@
EXPECT_EQ(2u, counters.transmitted.packets);
}
-TEST_F(ReceiveStatisticsTest, SimpleLossComputation) {
+TEST_P(ReceiveStatisticsTest, SimpleLossComputation) {
packet1_.SetSequenceNumber(1);
receive_statistics_->OnRtpPacket(packet1_);
packet1_.SetSequenceNumber(3);
@@ -256,7 +266,7 @@
EXPECT_EQ(20, statistician->GetFractionLostInPercent());
}
-TEST_F(ReceiveStatisticsTest, LossComputationWithReordering) {
+TEST_P(ReceiveStatisticsTest, LossComputationWithReordering) {
packet1_.SetSequenceNumber(1);
receive_statistics_->OnRtpPacket(packet1_);
packet1_.SetSequenceNumber(3);
@@ -279,7 +289,7 @@
EXPECT_EQ(20, statistician->GetFractionLostInPercent());
}
-TEST_F(ReceiveStatisticsTest, LossComputationWithDuplicates) {
+TEST_P(ReceiveStatisticsTest, LossComputationWithDuplicates) {
// Lose 2 packets, but also receive 1 duplicate. Should actually count as
// only 1 packet being lost.
packet1_.SetSequenceNumber(1);
@@ -304,7 +314,7 @@
EXPECT_EQ(20, statistician->GetFractionLostInPercent());
}
-TEST_F(ReceiveStatisticsTest, LossComputationWithSequenceNumberWrapping) {
+TEST_P(ReceiveStatisticsTest, LossComputationWithSequenceNumberWrapping) {
// First, test loss computation over a period that included a sequence number
// rollover.
packet1_.SetSequenceNumber(0xfffd);
@@ -344,7 +354,7 @@
EXPECT_EQ(28, statistician->GetFractionLostInPercent());
}
-TEST_F(ReceiveStatisticsTest, StreamRestartDoesntCountAsLoss) {
+TEST_P(ReceiveStatisticsTest, StreamRestartDoesntCountAsLoss) {
receive_statistics_->SetMaxReorderingThreshold(kSsrc1, 200);
packet1_.SetSequenceNumber(0);
@@ -377,7 +387,7 @@
EXPECT_EQ(0, statistician->GetFractionLostInPercent());
}
-TEST_F(ReceiveStatisticsTest, CountsLossAfterStreamRestart) {
+TEST_P(ReceiveStatisticsTest, CountsLossAfterStreamRestart) {
receive_statistics_->SetMaxReorderingThreshold(kSsrc1, 200);
packet1_.SetSequenceNumber(0);
@@ -405,7 +415,7 @@
EXPECT_EQ(0, statistician->GetFractionLostInPercent());
}
-TEST_F(ReceiveStatisticsTest, StreamCanRestartAtSequenceNumberWrapAround) {
+TEST_P(ReceiveStatisticsTest, StreamCanRestartAtSequenceNumberWrapAround) {
receive_statistics_->SetMaxReorderingThreshold(kSsrc1, 200);
packet1_.SetSequenceNumber(0xffff - 401);
@@ -428,7 +438,7 @@
EXPECT_EQ(1, report_blocks[0].cumulative_lost_signed());
}
-TEST_F(ReceiveStatisticsTest, StreamRestartNeedsTwoConsecutivePackets) {
+TEST_P(ReceiveStatisticsTest, StreamRestartNeedsTwoConsecutivePackets) {
receive_statistics_->SetMaxReorderingThreshold(kSsrc1, 200);
packet1_.SetSequenceNumber(400);
@@ -458,7 +468,7 @@
EXPECT_EQ(4u, report_blocks[0].extended_high_seq_num());
}
-TEST_F(ReceiveStatisticsTest, WrapsAroundExtendedHighestSequenceNumber) {
+TEST_P(ReceiveStatisticsTest, WrapsAroundExtendedHighestSequenceNumber) {
packet1_.SetSequenceNumber(0xffff);
receive_statistics_->OnRtpPacket(packet1_);
@@ -503,8 +513,7 @@
EXPECT_EQ(0x20001u, report_blocks[0].extended_high_seq_num());
}
-TEST_F(ReceiveStatisticsTest, StreamDataCounters) {
- receive_statistics_ = ReceiveStatistics::Create(&clock_);
+TEST_P(ReceiveStatisticsTest, StreamDataCounters) {
receive_statistics_->EnableRetransmitDetection(kSsrc1, true);
const size_t kHeaderLength = 20;
@@ -554,9 +563,7 @@
EXPECT_EQ(counters.retransmitted.packets, 1u);
}
-TEST_F(ReceiveStatisticsTest, LastPacketReceivedTimestamp) {
- receive_statistics_ = ReceiveStatistics::Create(&clock_);
-
+TEST_P(ReceiveStatisticsTest, LastPacketReceivedTimestamp) {
clock_.AdvanceTimeMilliseconds(42);
receive_statistics_->OnRtpPacket(packet1_);
StreamDataCounters counters = receive_statistics_->GetStatistician(kSsrc1)
diff --git a/modules/rtp_rtcp/source/rtcp_packet/extended_reports.h b/modules/rtp_rtcp/source/rtcp_packet/extended_reports.h
index 9627aac..6c804bb 100644
--- a/modules/rtp_rtcp/source/rtcp_packet/extended_reports.h
+++ b/modules/rtp_rtcp/source/rtcp_packet/extended_reports.h
@@ -62,7 +62,6 @@
void ParseRrtrBlock(const uint8_t* block, uint16_t block_length);
void ParseDlrrBlock(const uint8_t* block, uint16_t block_length);
- void ParseVoipMetricBlock(const uint8_t* block, uint16_t block_length);
void ParseTargetBitrateBlock(const uint8_t* block, uint16_t block_length);
absl::optional<Rrtr> rrtr_block_;
diff --git a/modules/rtp_rtcp/source/rtcp_packet/loss_notification.h b/modules/rtp_rtcp/source/rtcp_packet/loss_notification.h
index 2603a67..99f6d12 100644
--- a/modules/rtp_rtcp/source/rtcp_packet/loss_notification.h
+++ b/modules/rtp_rtcp/source/rtcp_packet/loss_notification.h
@@ -11,9 +11,9 @@
#ifndef MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_LOSS_NOTIFICATION_H_
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_LOSS_NOTIFICATION_H_
+#include "absl/base/attributes.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "modules/rtp_rtcp/source/rtcp_packet/psfb.h"
-#include "rtc_base/system/unused.h"
namespace webrtc {
namespace rtcp {
@@ -29,14 +29,15 @@
size_t BlockLength() const override;
+ ABSL_MUST_USE_RESULT
bool Create(uint8_t* packet,
size_t* index,
size_t max_length,
- PacketReadyCallback callback) const override
- RTC_WARN_UNUSED_RESULT;
+ PacketReadyCallback callback) const override;
// Parse assumes header is already parsed and validated.
- bool Parse(const CommonHeader& packet) RTC_WARN_UNUSED_RESULT;
+ ABSL_MUST_USE_RESULT
+ bool Parse(const CommonHeader& packet);
// Set all of the values transmitted by the loss notification message.
// If the values may not be represented by a loss notification message,
@@ -44,9 +45,10 @@
// when |last_recieved| is ahead of |last_decoded| by more than 0x7fff.
// This is because |last_recieved| is represented on the wire as a delta,
// and only 15 bits are available for that delta.
+ ABSL_MUST_USE_RESULT
bool Set(uint16_t last_decoded,
uint16_t last_received,
- bool decodability_flag) RTC_WARN_UNUSED_RESULT;
+ bool decodability_flag);
// RTP sequence number of the first packet belong to the last decoded
// non-discardable frame.
diff --git a/modules/rtp_rtcp/source/rtcp_receiver.cc b/modules/rtp_rtcp/source/rtcp_receiver.cc
index a9ec2a1..ae0e9e5 100644
--- a/modules/rtp_rtcp/source/rtcp_receiver.cc
+++ b/modules/rtp_rtcp/source/rtcp_receiver.cc
@@ -174,6 +174,9 @@
// TODO(bugs.webrtc.org/10774): Remove fallback.
remote_ssrc_(0),
remote_sender_rtp_time_(0),
+ remote_sender_packet_count_(0),
+ remote_sender_octet_count_(0),
+ remote_sender_reports_count_(0),
xr_rrtr_status_(config.non_sender_rtt_measurement),
xr_rr_rtt_ms_(0),
oldest_tmmbr_info_ms_(0),
@@ -325,7 +328,10 @@
uint32_t* received_ntp_frac,
uint32_t* rtcp_arrival_time_secs,
uint32_t* rtcp_arrival_time_frac,
- uint32_t* rtcp_timestamp) const {
+ uint32_t* rtcp_timestamp,
+ uint32_t* remote_sender_packet_count,
+ uint64_t* remote_sender_octet_count,
+ uint64_t* remote_sender_reports_count) const {
MutexLock lock(&rtcp_receiver_lock_);
if (!last_received_sr_ntp_.Valid())
return false;
@@ -335,7 +341,6 @@
*received_ntp_secs = remote_sender_ntp_time_.seconds();
if (received_ntp_frac)
*received_ntp_frac = remote_sender_ntp_time_.fractions();
-
// Rtp time from incoming SenderReport.
if (rtcp_timestamp)
*rtcp_timestamp = remote_sender_rtp_time_;
@@ -346,6 +351,14 @@
if (rtcp_arrival_time_frac)
*rtcp_arrival_time_frac = last_received_sr_ntp_.fractions();
+ // Counters.
+ if (remote_sender_packet_count)
+ *remote_sender_packet_count = remote_sender_packet_count_;
+ if (remote_sender_octet_count)
+ *remote_sender_octet_count = remote_sender_octet_count_;
+ if (remote_sender_reports_count)
+ *remote_sender_reports_count = remote_sender_reports_count_;
+
return true;
}
@@ -372,17 +385,6 @@
return last_xr_rtis;
}
-// We can get multiple receive reports when we receive the report from a CE.
-int32_t RTCPReceiver::StatisticsReceived(
- std::vector<RTCPReportBlock>* receive_blocks) const {
- RTC_DCHECK(receive_blocks);
- MutexLock lock(&rtcp_receiver_lock_);
- for (const auto& reports_per_receiver : received_report_blocks_)
- for (const auto& report : reports_per_receiver.second)
- receive_blocks->push_back(report.second.report_block());
- return 0;
-}
-
std::vector<ReportBlockData> RTCPReceiver::GetLatestReportBlockData() const {
std::vector<ReportBlockData> result;
MutexLock lock(&rtcp_receiver_lock_);
@@ -519,6 +521,9 @@
remote_sender_ntp_time_ = sender_report.ntp();
remote_sender_rtp_time_ = sender_report.rtp_timestamp();
last_received_sr_ntp_ = TimeMicrosToNtp(clock_->TimeInMicroseconds());
+ remote_sender_packet_count_ = sender_report.sender_packet_count();
+ remote_sender_octet_count_ = sender_report.sender_octet_count();
+ remote_sender_reports_count_++;
} else {
// We will only store the send report from one source, but
// we will store all the receive blocks.
@@ -714,7 +719,6 @@
}
for (const rtcp::Sdes::Chunk& chunk : sdes.chunks()) {
- received_cnames_[chunk.ssrc] = chunk.cname;
if (cname_callback_)
cname_callback_->OnCname(chunk.ssrc, chunk.cname);
}
@@ -778,7 +782,6 @@
tmmbr_info->ready_for_delete = true;
last_fir_.erase(bye.sender_ssrc());
- received_cnames_.erase(bye.sender_ssrc());
auto it = received_rrtrs_ssrc_it_.find(bye.sender_ssrc());
if (it != received_rrtrs_ssrc_it_.end()) {
received_rrtrs_.erase(it->second);
@@ -1169,20 +1172,6 @@
}
}
-int32_t RTCPReceiver::CNAME(uint32_t remoteSSRC,
- char cName[RTCP_CNAME_SIZE]) const {
- RTC_DCHECK(cName);
-
- MutexLock lock(&rtcp_receiver_lock_);
- auto received_cname_it = received_cnames_.find(remoteSSRC);
- if (received_cname_it == received_cnames_.end())
- return -1;
-
- size_t length = received_cname_it->second.copy(cName, RTCP_CNAME_SIZE - 1);
- cName[length] = 0;
- return 0;
-}
-
std::vector<rtcp::TmmbItem> RTCPReceiver::TmmbrReceived() {
MutexLock lock(&rtcp_receiver_lock_);
std::vector<rtcp::TmmbItem> candidates;
diff --git a/modules/rtp_rtcp/source/rtcp_receiver.h b/modules/rtp_rtcp/source/rtcp_receiver.h
index d735653..7b0f38b 100644
--- a/modules/rtp_rtcp/source/rtcp_receiver.h
+++ b/modules/rtp_rtcp/source/rtcp_receiver.h
@@ -67,15 +67,22 @@
void SetRemoteSSRC(uint32_t ssrc);
uint32_t RemoteSSRC() const;
- // Get received cname.
- int32_t CNAME(uint32_t remote_ssrc, char cname[RTCP_CNAME_SIZE]) const;
-
// Get received NTP.
+ // The types for the arguments below derive from the specification:
+ // - `remote_sender_packet_count`: `RTCSentRtpStreamStats.packetsSent` [1]
+ // - `remote_sender_octet_count`: `RTCSentRtpStreamStats.bytesSent` [1]
+ // - `remote_sender_reports_count`:
+ // `RTCRemoteOutboundRtpStreamStats.reportsSent` [2]
+ // [1] https://www.w3.org/TR/webrtc-stats/#remoteoutboundrtpstats-dict*
+ // [2] https://www.w3.org/TR/webrtc-stats/#dom-rtcsentrtpstreamstats
bool NTP(uint32_t* received_ntp_secs,
uint32_t* received_ntp_frac,
uint32_t* rtcp_arrival_time_secs,
uint32_t* rtcp_arrival_time_frac,
- uint32_t* rtcp_timestamp) const;
+ uint32_t* rtcp_timestamp,
+ uint32_t* remote_sender_packet_count,
+ uint64_t* remote_sender_octet_count,
+ uint64_t* remote_sender_reports_count) const;
std::vector<rtcp::ReceiveTimeInfo> ConsumeReceivedXrReferenceTimeInfo();
@@ -93,8 +100,6 @@
absl::optional<TimeDelta> OnPeriodicRttUpdate(Timestamp newer_than,
bool sending);
- // Get statistics.
- int32_t StatisticsReceived(std::vector<RTCPReportBlock>* receiveBlocks) const;
// A snapshot of Report Blocks with additional data of interest to statistics.
// Within this list, the sender-source SSRC pair is unique and per-pair the
// ReportBlockData represents the latest Report Block that was received for
@@ -242,6 +247,9 @@
uint32_t remote_sender_rtp_time_ RTC_GUARDED_BY(rtcp_receiver_lock_);
// When did we receive the last send report.
NtpTime last_received_sr_ntp_ RTC_GUARDED_BY(rtcp_receiver_lock_);
+ uint32_t remote_sender_packet_count_ RTC_GUARDED_BY(rtcp_receiver_lock_);
+ uint64_t remote_sender_octet_count_ RTC_GUARDED_BY(rtcp_receiver_lock_);
+ uint64_t remote_sender_reports_count_ RTC_GUARDED_BY(rtcp_receiver_lock_);
// Received RRTR information in ascending receive time order.
std::list<RrtrInformation> received_rrtrs_
@@ -262,8 +270,6 @@
ReportBlockMap received_report_blocks_ RTC_GUARDED_BY(rtcp_receiver_lock_);
std::map<uint32_t, LastFirStatus> last_fir_
RTC_GUARDED_BY(rtcp_receiver_lock_);
- std::map<uint32_t, std::string> received_cnames_
- RTC_GUARDED_BY(rtcp_receiver_lock_);
// The last time we received an RTCP Report block for this module.
Timestamp last_received_rb_ RTC_GUARDED_BY(rtcp_receiver_lock_) =
diff --git a/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc b/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc
index 1a1d94a..5739c51 100644
--- a/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc
+++ b/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc
@@ -208,7 +208,8 @@
RTCPReceiver receiver(DefaultConfiguration(&mocks), &mocks.rtp_rtcp_impl);
receiver.SetRemoteSSRC(kSenderSsrc);
- EXPECT_FALSE(receiver.NTP(nullptr, nullptr, nullptr, nullptr, nullptr));
+ EXPECT_FALSE(receiver.NTP(nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr));
int64_t now = mocks.clock.TimeInMilliseconds();
rtcp::SenderReport sr;
@@ -219,7 +220,8 @@
OnReceivedRtcpReceiverReport(IsEmpty(), _, now));
receiver.IncomingPacket(sr.Build());
- EXPECT_TRUE(receiver.NTP(nullptr, nullptr, nullptr, nullptr, nullptr));
+ EXPECT_TRUE(receiver.NTP(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr));
}
TEST(RtcpReceiverTest, InjectSrPacketFromUnknownSender) {
@@ -239,7 +241,8 @@
receiver.IncomingPacket(sr.Build());
// But will not flag that he's gotten sender information.
- EXPECT_FALSE(receiver.NTP(nullptr, nullptr, nullptr, nullptr, nullptr));
+ EXPECT_FALSE(receiver.NTP(nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr));
}
TEST(RtcpReceiverTest, InjectSrPacketCalculatesRTT) {
@@ -352,9 +355,7 @@
OnReceivedRtcpReceiverReport(IsEmpty(), _, now));
receiver.IncomingPacket(rr.Build());
- std::vector<RTCPReportBlock> report_blocks;
- receiver.StatisticsReceived(&report_blocks);
- EXPECT_TRUE(report_blocks.empty());
+ EXPECT_THAT(receiver.GetLatestReportBlockData(), IsEmpty());
}
TEST(RtcpReceiverTest, InjectRrPacketWithReportBlockNotToUsIgnored) {
@@ -375,9 +376,7 @@
receiver.IncomingPacket(rr.Build());
EXPECT_EQ(0, receiver.LastReceivedReportBlockMs());
- std::vector<RTCPReportBlock> received_blocks;
- receiver.StatisticsReceived(&received_blocks);
- EXPECT_TRUE(received_blocks.empty());
+ EXPECT_THAT(receiver.GetLatestReportBlockData(), IsEmpty());
}
TEST(RtcpReceiverTest, InjectRrPacketWithOneReportBlock) {
@@ -399,9 +398,7 @@
receiver.IncomingPacket(rr.Build());
EXPECT_EQ(now, receiver.LastReceivedReportBlockMs());
- std::vector<RTCPReportBlock> received_blocks;
- receiver.StatisticsReceived(&received_blocks);
- EXPECT_EQ(1u, received_blocks.size());
+ EXPECT_THAT(receiver.GetLatestReportBlockData(), SizeIs(1));
}
TEST(RtcpReceiverTest, InjectSrPacketWithOneReportBlock) {
@@ -423,9 +420,7 @@
receiver.IncomingPacket(sr.Build());
EXPECT_EQ(now, receiver.LastReceivedReportBlockMs());
- std::vector<RTCPReportBlock> received_blocks;
- receiver.StatisticsReceived(&received_blocks);
- EXPECT_EQ(1u, received_blocks.size());
+ EXPECT_THAT(receiver.GetLatestReportBlockData(), SizeIs(1));
}
TEST(RtcpReceiverTest, InjectRrPacketWithTwoReportBlocks) {
@@ -459,11 +454,12 @@
receiver.IncomingPacket(rr1.Build());
EXPECT_EQ(now, receiver.LastReceivedReportBlockMs());
- std::vector<RTCPReportBlock> received_blocks;
- receiver.StatisticsReceived(&received_blocks);
- EXPECT_THAT(received_blocks,
- UnorderedElementsAre(Field(&RTCPReportBlock::fraction_lost, 0),
- Field(&RTCPReportBlock::fraction_lost, 10)));
+ EXPECT_THAT(receiver.GetLatestReportBlockData(),
+ UnorderedElementsAre(
+ Property(&ReportBlockData::report_block,
+ Field(&RTCPReportBlock::fraction_lost, 0)),
+ Property(&ReportBlockData::report_block,
+ Field(&RTCPReportBlock::fraction_lost, 10))));
// Insert next receiver report with same ssrc but new values.
rtcp::ReportBlock rb3;
@@ -492,22 +488,23 @@
OnReceivedRtcpReceiverReport(SizeIs(2), _, now));
receiver.IncomingPacket(rr2.Build());
- received_blocks.clear();
- receiver.StatisticsReceived(&received_blocks);
- EXPECT_EQ(2u, received_blocks.size());
EXPECT_THAT(
- received_blocks,
+ receiver.GetLatestReportBlockData(),
UnorderedElementsAre(
- AllOf(Field(&RTCPReportBlock::source_ssrc, kReceiverMainSsrc),
- Field(&RTCPReportBlock::fraction_lost, kFracLost[0]),
- Field(&RTCPReportBlock::packets_lost, kCumLost[0]),
- Field(&RTCPReportBlock::extended_highest_sequence_number,
- kSequenceNumbers[0])),
- AllOf(Field(&RTCPReportBlock::source_ssrc, kReceiverExtraSsrc),
- Field(&RTCPReportBlock::fraction_lost, kFracLost[1]),
- Field(&RTCPReportBlock::packets_lost, kCumLost[1]),
- Field(&RTCPReportBlock::extended_highest_sequence_number,
- kSequenceNumbers[1]))));
+ Property(
+ &ReportBlockData::report_block,
+ AllOf(Field(&RTCPReportBlock::source_ssrc, kReceiverMainSsrc),
+ Field(&RTCPReportBlock::fraction_lost, kFracLost[0]),
+ Field(&RTCPReportBlock::packets_lost, kCumLost[0]),
+ Field(&RTCPReportBlock::extended_highest_sequence_number,
+ kSequenceNumbers[0]))),
+ Property(
+ &ReportBlockData::report_block,
+ AllOf(Field(&RTCPReportBlock::source_ssrc, kReceiverExtraSsrc),
+ Field(&RTCPReportBlock::fraction_lost, kFracLost[1]),
+ Field(&RTCPReportBlock::packets_lost, kCumLost[1]),
+ Field(&RTCPReportBlock::extended_highest_sequence_number,
+ kSequenceNumbers[1])))));
}
TEST(RtcpReceiverTest, InjectRrPacketsFromTwoRemoteSsrcs) {
@@ -537,15 +534,16 @@
EXPECT_EQ(now, receiver.LastReceivedReportBlockMs());
- std::vector<RTCPReportBlock> received_blocks;
- receiver.StatisticsReceived(&received_blocks);
- EXPECT_EQ(1u, received_blocks.size());
- EXPECT_EQ(kSenderSsrc, received_blocks[0].sender_ssrc);
- EXPECT_EQ(kReceiverMainSsrc, received_blocks[0].source_ssrc);
- EXPECT_EQ(kFracLost[0], received_blocks[0].fraction_lost);
- EXPECT_EQ(kCumLost[0], received_blocks[0].packets_lost);
- EXPECT_EQ(kSequenceNumbers[0],
- received_blocks[0].extended_highest_sequence_number);
+ EXPECT_THAT(
+ receiver.GetLatestReportBlockData(),
+ ElementsAre(Property(
+ &ReportBlockData::report_block,
+ AllOf(Field(&RTCPReportBlock::source_ssrc, kReceiverMainSsrc),
+ Field(&RTCPReportBlock::sender_ssrc, kSenderSsrc),
+ Field(&RTCPReportBlock::fraction_lost, kFracLost[0]),
+ Field(&RTCPReportBlock::packets_lost, kCumLost[0]),
+ Field(&RTCPReportBlock::extended_highest_sequence_number,
+ kSequenceNumbers[0])))));
rtcp::ReportBlock rb2;
rb2.SetMediaSsrc(kReceiverMainSsrc);
@@ -561,24 +559,25 @@
OnReceivedRtcpReceiverReport(SizeIs(1), _, now));
receiver.IncomingPacket(rr2.Build());
- received_blocks.clear();
- receiver.StatisticsReceived(&received_blocks);
- ASSERT_EQ(2u, received_blocks.size());
EXPECT_THAT(
- received_blocks,
+ receiver.GetLatestReportBlockData(),
UnorderedElementsAre(
- AllOf(Field(&RTCPReportBlock::source_ssrc, kReceiverMainSsrc),
- Field(&RTCPReportBlock::sender_ssrc, kSenderSsrc),
- Field(&RTCPReportBlock::fraction_lost, kFracLost[0]),
- Field(&RTCPReportBlock::packets_lost, kCumLost[0]),
- Field(&RTCPReportBlock::extended_highest_sequence_number,
- kSequenceNumbers[0])),
- AllOf(Field(&RTCPReportBlock::source_ssrc, kReceiverMainSsrc),
- Field(&RTCPReportBlock::sender_ssrc, kSenderSsrc2),
- Field(&RTCPReportBlock::fraction_lost, kFracLost[1]),
- Field(&RTCPReportBlock::packets_lost, kCumLost[1]),
- Field(&RTCPReportBlock::extended_highest_sequence_number,
- kSequenceNumbers[1]))));
+ Property(
+ &ReportBlockData::report_block,
+ AllOf(Field(&RTCPReportBlock::source_ssrc, kReceiverMainSsrc),
+ Field(&RTCPReportBlock::sender_ssrc, kSenderSsrc),
+ Field(&RTCPReportBlock::fraction_lost, kFracLost[0]),
+ Field(&RTCPReportBlock::packets_lost, kCumLost[0]),
+ Field(&RTCPReportBlock::extended_highest_sequence_number,
+ kSequenceNumbers[0]))),
+ Property(
+ &ReportBlockData::report_block,
+ AllOf(Field(&RTCPReportBlock::source_ssrc, kReceiverMainSsrc),
+ Field(&RTCPReportBlock::sender_ssrc, kSenderSsrc2),
+ Field(&RTCPReportBlock::fraction_lost, kFracLost[1]),
+ Field(&RTCPReportBlock::packets_lost, kCumLost[1]),
+ Field(&RTCPReportBlock::extended_highest_sequence_number,
+ kSequenceNumbers[1])))));
}
TEST(RtcpReceiverTest, GetRtt) {
@@ -648,33 +647,6 @@
EXPECT_CALL(callback, OnCname(kSenderSsrc, StrEq(kCname)));
receiver.IncomingPacket(sdes.Build());
-
- char cName[RTCP_CNAME_SIZE];
- EXPECT_EQ(0, receiver.CNAME(kSenderSsrc, cName));
- EXPECT_EQ(0, strncmp(cName, kCname, RTCP_CNAME_SIZE));
-}
-
-TEST(RtcpReceiverTest, InjectByePacket_RemovesCname) {
- ReceiverMocks mocks;
- RTCPReceiver receiver(DefaultConfiguration(&mocks), &mocks.rtp_rtcp_impl);
- receiver.SetRemoteSSRC(kSenderSsrc);
-
- const char kCname[] = "alice@host";
- rtcp::Sdes sdes;
- sdes.AddCName(kSenderSsrc, kCname);
-
- receiver.IncomingPacket(sdes.Build());
-
- char cName[RTCP_CNAME_SIZE];
- EXPECT_EQ(0, receiver.CNAME(kSenderSsrc, cName));
-
- // Verify that BYE removes the CNAME.
- rtcp::Bye bye;
- bye.SetSenderSsrc(kSenderSsrc);
-
- receiver.IncomingPacket(bye.Build());
-
- EXPECT_EQ(-1, receiver.CNAME(kSenderSsrc, cName));
}
TEST(RtcpReceiverTest, InjectByePacket_RemovesReportBlocks) {
@@ -695,9 +667,7 @@
EXPECT_CALL(mocks.bandwidth_observer, OnReceivedRtcpReceiverReport);
receiver.IncomingPacket(rr.Build());
- std::vector<RTCPReportBlock> received_blocks;
- receiver.StatisticsReceived(&received_blocks);
- EXPECT_EQ(2u, received_blocks.size());
+ EXPECT_THAT(receiver.GetLatestReportBlockData(), SizeIs(2));
// Verify that BYE removes the report blocks.
rtcp::Bye bye;
@@ -705,18 +675,14 @@
receiver.IncomingPacket(bye.Build());
- received_blocks.clear();
- receiver.StatisticsReceived(&received_blocks);
- EXPECT_TRUE(received_blocks.empty());
+ EXPECT_THAT(receiver.GetLatestReportBlockData(), IsEmpty());
// Inject packet again.
EXPECT_CALL(mocks.rtp_rtcp_impl, OnReceivedRtcpReportBlocks);
EXPECT_CALL(mocks.bandwidth_observer, OnReceivedRtcpReceiverReport);
receiver.IncomingPacket(rr.Build());
- received_blocks.clear();
- receiver.StatisticsReceived(&received_blocks);
- EXPECT_EQ(2u, received_blocks.size());
+ EXPECT_THAT(receiver.GetLatestReportBlockData(), SizeIs(2));
}
TEST(RtcpReceiverTest, InjectByePacketRemovesReferenceTimeInfo) {
diff --git a/modules/rtp_rtcp/source/rtcp_sender.cc b/modules/rtp_rtcp/source/rtcp_sender.cc
index 79f5aa6..8b519b5 100644
--- a/modules/rtp_rtcp/source/rtcp_sender.cc
+++ b/modules/rtp_rtcp/source/rtcp_sender.cc
@@ -50,36 +50,10 @@
constexpr int32_t kDefaultVideoReportInterval = 1000;
constexpr int32_t kDefaultAudioReportInterval = 5000;
-class PacketContainer : public rtcp::CompoundPacket {
- public:
- PacketContainer(Transport* transport, RtcEventLog* event_log)
- : transport_(transport), event_log_(event_log) {}
-
- PacketContainer() = delete;
- PacketContainer(const PacketContainer&) = delete;
- PacketContainer& operator=(const PacketContainer&) = delete;
-
- size_t SendPackets(size_t max_payload_length) {
- size_t bytes_sent = 0;
- Build(max_payload_length, [&](rtc::ArrayView<const uint8_t> packet) {
- if (transport_->SendRtcp(packet.data(), packet.size())) {
- bytes_sent += packet.size();
- if (event_log_) {
- event_log_->Log(std::make_unique<RtcEventRtcpPacketOutgoing>(packet));
- }
- }
- });
- return bytes_sent;
- }
-
- private:
- Transport* transport_;
- RtcEventLog* const event_log_;
-};
+} // namespace
// Helper to put several RTCP packets into lower layer datagram RTCP packet.
-// Prefer to use this class instead of PacketContainer.
-class PacketSender {
+class RTCPSender::PacketSender {
public:
PacketSender(rtcp::RtcpPacket::PacketReadyCallback callback,
size_t max_packet_size)
@@ -102,8 +76,6 @@
}
}
- bool IsEmpty() const { return index_ == 0; }
-
private:
const rtcp::RtcpPacket::PacketReadyCallback callback_;
const size_t max_packet_size_;
@@ -111,8 +83,6 @@
uint8_t buffer_[IP_PACKET_SIZE];
};
-} // namespace
-
RTCPSender::FeedbackState::FeedbackState()
: packets_sent(0),
media_bytes_sent(0),
@@ -241,21 +211,43 @@
uint16_t last_received_seq_num,
bool decodability_flag,
bool buffering_allowed) {
- MutexLock lock(&mutex_rtcp_sender_);
+ int32_t error_code = -1;
+ auto callback = [&](rtc::ArrayView<const uint8_t> packet) {
+ if (transport_->SendRtcp(packet.data(), packet.size())) {
+ error_code = 0;
+ if (event_log_) {
+ event_log_->Log(std::make_unique<RtcEventRtcpPacketOutgoing>(packet));
+ }
+ }
+ };
+ absl::optional<PacketSender> sender;
+ {
+ MutexLock lock(&mutex_rtcp_sender_);
- loss_notification_state_.last_decoded_seq_num = last_decoded_seq_num;
- loss_notification_state_.last_received_seq_num = last_received_seq_num;
- loss_notification_state_.decodability_flag = decodability_flag;
+ if (!loss_notification_.Set(last_decoded_seq_num, last_received_seq_num,
+ decodability_flag)) {
+ return -1;
+ }
- SetFlag(kRtcpLossNotification, /*is_volatile=*/true);
+ SetFlag(kRtcpLossNotification, /*is_volatile=*/true);
- if (buffering_allowed) {
- // The loss notification will be batched with additional feedback messages.
- return 0;
+ if (buffering_allowed) {
+ // The loss notification will be batched with additional feedback
+ // messages.
+ return 0;
+ }
+
+ sender.emplace(callback, max_packet_size_);
+ auto result = ComputeCompoundRTCPPacket(
+ feedback_state, RTCPPacketType::kRtcpLossNotification, 0, nullptr,
+ *sender);
+ if (result) {
+ return *result;
+ }
}
+ sender->Send();
- return SendCompoundRTCPLocked(
- feedback_state, {RTCPPacketType::kRtcpLossNotification}, 0, nullptr);
+ return error_code;
}
void RTCPSender::SetRemb(int64_t bitrate_bps, std::vector<uint32_t> ssrcs) {
@@ -281,15 +273,6 @@
return IsFlagPresent(RTCPPacketType::kRtcpTmmbr);
}
-void RTCPSender::SetTMMBRStatus(bool enable) {
- MutexLock lock(&mutex_rtcp_sender_);
- if (enable) {
- SetFlag(RTCPPacketType::kRtcpTmmbr, false);
- } else {
- ConsumeFlag(RTCPPacketType::kRtcpTmmbr, true);
- }
-}
-
void RTCPSender::SetMaxRtpPacketSize(size_t max_packet_size) {
MutexLock lock(&mutex_rtcp_sender_);
max_packet_size_ = max_packet_size;
@@ -338,31 +321,6 @@
return 0;
}
-int32_t RTCPSender::AddMixedCNAME(uint32_t SSRC, const char* c_name) {
- RTC_DCHECK(c_name);
- RTC_DCHECK_LT(strlen(c_name), RTCP_CNAME_SIZE);
- MutexLock lock(&mutex_rtcp_sender_);
- // One spot is reserved for ssrc_/cname_.
- // TODO(danilchap): Add support for more than 30 contributes by sending
- // several sdes packets.
- if (csrc_cnames_.size() >= rtcp::Sdes::kMaxNumberOfChunks - 1)
- return -1;
-
- csrc_cnames_[SSRC] = c_name;
- return 0;
-}
-
-int32_t RTCPSender::RemoveMixedCNAME(uint32_t SSRC) {
- MutexLock lock(&mutex_rtcp_sender_);
- auto it = csrc_cnames_.find(SSRC);
-
- if (it == csrc_cnames_.end())
- return -1;
-
- csrc_cnames_.erase(it);
- return 0;
-}
-
bool RTCPSender::TimeToSendRTCPReport(bool sendKeyframeBeforeRTP) const {
/*
For audio we use a configurable interval (default: 5 seconds)
@@ -438,7 +396,7 @@
return now >= next_time_to_send_rtcp_;
}
-std::unique_ptr<rtcp::RtcpPacket> RTCPSender::BuildSR(const RtcpContext& ctx) {
+void RTCPSender::BuildSR(const RtcpContext& ctx, PacketSender& sender) {
// Timestamp shouldn't be estimated before first media frame.
RTC_DCHECK_GE(last_frame_capture_time_ms_, 0);
// The timestamp of this RTCP packet should be estimated as the timestamp of
@@ -457,69 +415,58 @@
timestamp_offset_ + last_rtp_timestamp_ +
((ctx.now_us_ + 500) / 1000 - last_frame_capture_time_ms_) * rtp_rate;
- rtcp::SenderReport* report = new rtcp::SenderReport();
- report->SetSenderSsrc(ssrc_);
- report->SetNtp(TimeMicrosToNtp(ctx.now_us_));
- report->SetRtpTimestamp(rtp_timestamp);
- report->SetPacketCount(ctx.feedback_state_.packets_sent);
- report->SetOctetCount(ctx.feedback_state_.media_bytes_sent);
- report->SetReportBlocks(CreateReportBlocks(ctx.feedback_state_));
-
- return std::unique_ptr<rtcp::RtcpPacket>(report);
+ rtcp::SenderReport report;
+ report.SetSenderSsrc(ssrc_);
+ report.SetNtp(TimeMicrosToNtp(ctx.now_us_));
+ report.SetRtpTimestamp(rtp_timestamp);
+ report.SetPacketCount(ctx.feedback_state_.packets_sent);
+ report.SetOctetCount(ctx.feedback_state_.media_bytes_sent);
+ report.SetReportBlocks(CreateReportBlocks(ctx.feedback_state_));
+ sender.AppendPacket(report);
}
-std::unique_ptr<rtcp::RtcpPacket> RTCPSender::BuildSDES(
- const RtcpContext& ctx) {
+void RTCPSender::BuildSDES(const RtcpContext& ctx, PacketSender& sender) {
size_t length_cname = cname_.length();
RTC_CHECK_LT(length_cname, RTCP_CNAME_SIZE);
- rtcp::Sdes* sdes = new rtcp::Sdes();
- sdes->AddCName(ssrc_, cname_);
-
- for (const auto& it : csrc_cnames_)
- RTC_CHECK(sdes->AddCName(it.first, it.second));
-
- return std::unique_ptr<rtcp::RtcpPacket>(sdes);
+ rtcp::Sdes sdes;
+ sdes.AddCName(ssrc_, cname_);
+ sender.AppendPacket(sdes);
}
-std::unique_ptr<rtcp::RtcpPacket> RTCPSender::BuildRR(const RtcpContext& ctx) {
- rtcp::ReceiverReport* report = new rtcp::ReceiverReport();
- report->SetSenderSsrc(ssrc_);
- report->SetReportBlocks(CreateReportBlocks(ctx.feedback_state_));
-
- return std::unique_ptr<rtcp::RtcpPacket>(report);
+void RTCPSender::BuildRR(const RtcpContext& ctx, PacketSender& sender) {
+ rtcp::ReceiverReport report;
+ report.SetSenderSsrc(ssrc_);
+ report.SetReportBlocks(CreateReportBlocks(ctx.feedback_state_));
+ sender.AppendPacket(report);
}
-std::unique_ptr<rtcp::RtcpPacket> RTCPSender::BuildPLI(const RtcpContext& ctx) {
- rtcp::Pli* pli = new rtcp::Pli();
- pli->SetSenderSsrc(ssrc_);
- pli->SetMediaSsrc(remote_ssrc_);
+void RTCPSender::BuildPLI(const RtcpContext& ctx, PacketSender& sender) {
+ rtcp::Pli pli;
+ pli.SetSenderSsrc(ssrc_);
+ pli.SetMediaSsrc(remote_ssrc_);
++packet_type_counter_.pli_packets;
-
- return std::unique_ptr<rtcp::RtcpPacket>(pli);
+ sender.AppendPacket(pli);
}
-std::unique_ptr<rtcp::RtcpPacket> RTCPSender::BuildFIR(const RtcpContext& ctx) {
+void RTCPSender::BuildFIR(const RtcpContext& ctx, PacketSender& sender) {
++sequence_number_fir_;
- rtcp::Fir* fir = new rtcp::Fir();
- fir->SetSenderSsrc(ssrc_);
- fir->AddRequestTo(remote_ssrc_, sequence_number_fir_);
+ rtcp::Fir fir;
+ fir.SetSenderSsrc(ssrc_);
+ fir.AddRequestTo(remote_ssrc_, sequence_number_fir_);
++packet_type_counter_.fir_packets;
-
- return std::unique_ptr<rtcp::RtcpPacket>(fir);
+ sender.AppendPacket(fir);
}
-std::unique_ptr<rtcp::RtcpPacket> RTCPSender::BuildREMB(
- const RtcpContext& ctx) {
- rtcp::Remb* remb = new rtcp::Remb();
- remb->SetSenderSsrc(ssrc_);
- remb->SetBitrateBps(remb_bitrate_);
- remb->SetSsrcs(remb_ssrcs_);
-
- return std::unique_ptr<rtcp::RtcpPacket>(remb);
+void RTCPSender::BuildREMB(const RtcpContext& ctx, PacketSender& sender) {
+ rtcp::Remb remb;
+ remb.SetSenderSsrc(ssrc_);
+ remb.SetBitrateBps(remb_bitrate_);
+ remb.SetSsrcs(remb_ssrcs_);
+ sender.AppendPacket(remb);
}
void RTCPSender::SetTargetBitrate(unsigned int target_bitrate) {
@@ -527,10 +474,9 @@
tmmbr_send_bps_ = target_bitrate;
}
-std::unique_ptr<rtcp::RtcpPacket> RTCPSender::BuildTMMBR(
- const RtcpContext& ctx) {
+void RTCPSender::BuildTMMBR(const RtcpContext& ctx, PacketSender& sender) {
if (ctx.feedback_state_.receiver == nullptr)
- return nullptr;
+ return;
// Before sending the TMMBR check the received TMMBN, only an owner is
// allowed to raise the bitrate:
// * If the sender is an owner of the TMMBN -> send TMMBR
@@ -550,7 +496,7 @@
if (candidate.bitrate_bps() == tmmbr_send_bps_ &&
candidate.packet_overhead() == packet_oh_send_) {
// Do not send the same tuple.
- return nullptr;
+ return;
}
}
if (!tmmbr_owner) {
@@ -564,62 +510,53 @@
tmmbr_owner = TMMBRHelp::IsOwner(bounding, ssrc_);
if (!tmmbr_owner) {
// Did not enter bounding set, no meaning to send this request.
- return nullptr;
+ return;
}
}
}
if (!tmmbr_send_bps_)
- return nullptr;
+ return;
- rtcp::Tmmbr* tmmbr = new rtcp::Tmmbr();
- tmmbr->SetSenderSsrc(ssrc_);
+ rtcp::Tmmbr tmmbr;
+ tmmbr.SetSenderSsrc(ssrc_);
rtcp::TmmbItem request;
request.set_ssrc(remote_ssrc_);
request.set_bitrate_bps(tmmbr_send_bps_);
request.set_packet_overhead(packet_oh_send_);
- tmmbr->AddTmmbr(request);
-
- return std::unique_ptr<rtcp::RtcpPacket>(tmmbr);
+ tmmbr.AddTmmbr(request);
+ sender.AppendPacket(tmmbr);
}
-std::unique_ptr<rtcp::RtcpPacket> RTCPSender::BuildTMMBN(
- const RtcpContext& ctx) {
- rtcp::Tmmbn* tmmbn = new rtcp::Tmmbn();
- tmmbn->SetSenderSsrc(ssrc_);
+void RTCPSender::BuildTMMBN(const RtcpContext& ctx, PacketSender& sender) {
+ rtcp::Tmmbn tmmbn;
+ tmmbn.SetSenderSsrc(ssrc_);
for (const rtcp::TmmbItem& tmmbr : tmmbn_to_send_) {
if (tmmbr.bitrate_bps() > 0) {
- tmmbn->AddTmmbr(tmmbr);
+ tmmbn.AddTmmbr(tmmbr);
}
}
-
- return std::unique_ptr<rtcp::RtcpPacket>(tmmbn);
+ sender.AppendPacket(tmmbn);
}
-std::unique_ptr<rtcp::RtcpPacket> RTCPSender::BuildAPP(const RtcpContext& ctx) {
- rtcp::App* app = new rtcp::App();
- app->SetSenderSsrc(ssrc_);
-
- return std::unique_ptr<rtcp::RtcpPacket>(app);
+void RTCPSender::BuildAPP(const RtcpContext& ctx, PacketSender& sender) {
+ rtcp::App app;
+ app.SetSenderSsrc(ssrc_);
+ sender.AppendPacket(app);
}
-std::unique_ptr<rtcp::RtcpPacket> RTCPSender::BuildLossNotification(
- const RtcpContext& ctx) {
- auto loss_notification = std::make_unique<rtcp::LossNotification>(
- loss_notification_state_.last_decoded_seq_num,
- loss_notification_state_.last_received_seq_num,
- loss_notification_state_.decodability_flag);
- loss_notification->SetSenderSsrc(ssrc_);
- loss_notification->SetMediaSsrc(remote_ssrc_);
- return std::move(loss_notification);
+void RTCPSender::BuildLossNotification(const RtcpContext& ctx,
+ PacketSender& sender) {
+ loss_notification_.SetSenderSsrc(ssrc_);
+ loss_notification_.SetMediaSsrc(remote_ssrc_);
+ sender.AppendPacket(loss_notification_);
}
-std::unique_ptr<rtcp::RtcpPacket> RTCPSender::BuildNACK(
- const RtcpContext& ctx) {
- rtcp::Nack* nack = new rtcp::Nack();
- nack->SetSenderSsrc(ssrc_);
- nack->SetMediaSsrc(remote_ssrc_);
- nack->SetPacketIds(ctx.nack_list_, ctx.nack_size_);
+void RTCPSender::BuildNACK(const RtcpContext& ctx, PacketSender& sender) {
+ rtcp::Nack nack;
+ nack.SetSenderSsrc(ssrc_);
+ nack.SetMediaSsrc(remote_ssrc_);
+ nack.SetPacketIds(ctx.nack_list_, ctx.nack_size_);
// Report stats.
for (int idx = 0; idx < ctx.nack_size_; ++idx) {
@@ -629,31 +566,29 @@
packet_type_counter_.unique_nack_requests = nack_stats_.unique_requests();
++packet_type_counter_.nack_packets;
-
- return std::unique_ptr<rtcp::RtcpPacket>(nack);
+ sender.AppendPacket(nack);
}
-std::unique_ptr<rtcp::RtcpPacket> RTCPSender::BuildBYE(const RtcpContext& ctx) {
- rtcp::Bye* bye = new rtcp::Bye();
- bye->SetSenderSsrc(ssrc_);
- bye->SetCsrcs(csrcs_);
-
- return std::unique_ptr<rtcp::RtcpPacket>(bye);
+void RTCPSender::BuildBYE(const RtcpContext& ctx, PacketSender& sender) {
+ rtcp::Bye bye;
+ bye.SetSenderSsrc(ssrc_);
+ bye.SetCsrcs(csrcs_);
+ sender.AppendPacket(bye);
}
-std::unique_ptr<rtcp::RtcpPacket> RTCPSender::BuildExtendedReports(
- const RtcpContext& ctx) {
- std::unique_ptr<rtcp::ExtendedReports> xr(new rtcp::ExtendedReports());
- xr->SetSenderSsrc(ssrc_);
+void RTCPSender::BuildExtendedReports(const RtcpContext& ctx,
+ PacketSender& sender) {
+ rtcp::ExtendedReports xr;
+ xr.SetSenderSsrc(ssrc_);
if (!sending_ && xr_send_receiver_reference_time_enabled_) {
rtcp::Rrtr rrtr;
rrtr.SetNtp(TimeMicrosToNtp(ctx.now_us_));
- xr->SetRrtr(rrtr);
+ xr.SetRrtr(rrtr);
}
for (const rtcp::ReceiveTimeInfo& rti : ctx.feedback_state_.last_xr_rtis) {
- xr->AddDlrrItem(rti);
+ xr.AddDlrrItem(rti);
}
if (send_video_bitrate_allocation_) {
@@ -668,72 +603,53 @@
}
}
- xr->SetTargetBitrate(target_bitrate);
+ xr.SetTargetBitrate(target_bitrate);
send_video_bitrate_allocation_ = false;
}
-
- return std::move(xr);
+ sender.AppendPacket(xr);
}
int32_t RTCPSender::SendRTCP(const FeedbackState& feedback_state,
- RTCPPacketType packetType,
+ RTCPPacketType packet_type,
int32_t nack_size,
const uint16_t* nack_list) {
- return SendCompoundRTCP(
- feedback_state, std::set<RTCPPacketType>(&packetType, &packetType + 1),
- nack_size, nack_list);
-}
-
-int32_t RTCPSender::SendCompoundRTCP(
- const FeedbackState& feedback_state,
- const std::set<RTCPPacketType>& packet_types,
- int32_t nack_size,
- const uint16_t* nack_list) {
- PacketContainer container(transport_, event_log_);
- size_t max_packet_size;
-
+ int32_t error_code = -1;
+ auto callback = [&](rtc::ArrayView<const uint8_t> packet) {
+ if (transport_->SendRtcp(packet.data(), packet.size())) {
+ error_code = 0;
+ if (event_log_) {
+ event_log_->Log(std::make_unique<RtcEventRtcpPacketOutgoing>(packet));
+ }
+ }
+ };
+ absl::optional<PacketSender> sender;
{
MutexLock lock(&mutex_rtcp_sender_);
- auto result = ComputeCompoundRTCPPacket(feedback_state, packet_types,
- nack_size, nack_list, &container);
+ sender.emplace(callback, max_packet_size_);
+ auto result = ComputeCompoundRTCPPacket(feedback_state, packet_type,
+ nack_size, nack_list, *sender);
if (result) {
return *result;
}
- max_packet_size = max_packet_size_;
}
+ sender->Send();
- size_t bytes_sent = container.SendPackets(max_packet_size);
- return bytes_sent == 0 ? -1 : 0;
-}
-
-int32_t RTCPSender::SendCompoundRTCPLocked(
- const FeedbackState& feedback_state,
- const std::set<RTCPPacketType>& packet_types,
- int32_t nack_size,
- const uint16_t* nack_list) {
- PacketContainer container(transport_, event_log_);
- auto result = ComputeCompoundRTCPPacket(feedback_state, packet_types,
- nack_size, nack_list, &container);
- if (result) {
- return *result;
- }
- size_t bytes_sent = container.SendPackets(max_packet_size_);
- return bytes_sent == 0 ? -1 : 0;
+ return error_code;
}
absl::optional<int32_t> RTCPSender::ComputeCompoundRTCPPacket(
const FeedbackState& feedback_state,
- const std::set<RTCPPacketType>& packet_types,
+ RTCPPacketType packet_type,
int32_t nack_size,
const uint16_t* nack_list,
- rtcp::CompoundPacket* out_packet) {
+ PacketSender& sender) {
if (method_ == RtcpMode::kOff) {
RTC_LOG(LS_WARNING) << "Can't send rtcp if it is disabled.";
return -1;
}
- // Add all flags as volatile. Non volatile entries will not be overwritten.
- // All new volatile flags added will be consumed by the end of this call.
- SetFlags(packet_types, true);
+ // Add the flag as volatile. Non volatile entries will not be overwritten.
+ // The new volatile flag will be consumed by the end of this call.
+ SetFlag(packet_type, true);
// Prevent sending streams to send SR before any media has been sent.
const bool can_calculate_rtp_timestamp = (last_frame_capture_time_ms_ >= 0);
@@ -760,37 +676,37 @@
PrepareReport(feedback_state);
- std::unique_ptr<rtcp::RtcpPacket> packet_bye;
+ bool create_bye = false;
auto it = report_flags_.begin();
while (it != report_flags_.end()) {
- auto builder_it = builders_.find(it->type);
+ uint32_t rtcp_packet_type = it->type;
+
if (it->is_volatile) {
report_flags_.erase(it++);
} else {
++it;
}
+ // If there is a BYE, don't append now - save it and append it
+ // at the end later.
+ if (rtcp_packet_type == kRtcpBye) {
+ create_bye = true;
+ continue;
+ }
+ auto builder_it = builders_.find(rtcp_packet_type);
if (builder_it == builders_.end()) {
- RTC_NOTREACHED() << "Could not find builder for packet type " << it->type;
+ RTC_NOTREACHED() << "Could not find builder for packet type "
+ << rtcp_packet_type;
} else {
BuilderFunc func = builder_it->second;
- std::unique_ptr<rtcp::RtcpPacket> packet = (this->*func)(context);
- if (packet == nullptr)
- return -1;
- // If there is a BYE, don't append now - save it and append it
- // at the end later.
- if (builder_it->first == kRtcpBye) {
- packet_bye = std::move(packet);
- } else {
- out_packet->Append(std::move(packet));
- }
+ (this->*func)(context, sender);
}
}
// Append the BYE now at the end
- if (packet_bye) {
- out_packet->Append(std::move(packet_bye));
+ if (create_bye) {
+ BuildBYE(context, sender);
}
if (packet_type_counter_observer_ != nullptr) {
@@ -904,12 +820,6 @@
}
}
-void RTCPSender::SetFlags(const std::set<RTCPPacketType>& types,
- bool is_volatile) {
- for (RTCPPacketType type : types)
- SetFlag(type, is_volatile);
-}
-
bool RTCPSender::IsFlagPresent(uint32_t type) const {
return report_flags_.find(ReportFlag(type, false)) != report_flags_.end();
}
diff --git a/modules/rtp_rtcp/source/rtcp_sender.h b/modules/rtp_rtcp/source/rtcp_sender.h
index cc9091d..463666a 100644
--- a/modules/rtp_rtcp/source/rtcp_sender.h
+++ b/modules/rtp_rtcp/source/rtcp_sender.h
@@ -27,6 +27,7 @@
#include "modules/rtp_rtcp/source/rtcp_packet.h"
#include "modules/rtp_rtcp/source/rtcp_packet/compound_packet.h"
#include "modules/rtp_rtcp/source/rtcp_packet/dlrr.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/loss_notification.h"
#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
#include "modules/rtp_rtcp/source/rtcp_packet/tmmb_item.h"
#include "modules/rtp_rtcp/source/rtp_rtcp_interface.h"
@@ -99,12 +100,6 @@
int32_t SetCNAME(const char* cName) RTC_LOCKS_EXCLUDED(mutex_rtcp_sender_);
- int32_t AddMixedCNAME(uint32_t SSRC, const char* c_name)
- RTC_LOCKS_EXCLUDED(mutex_rtcp_sender_);
-
- int32_t RemoveMixedCNAME(uint32_t SSRC)
- RTC_LOCKS_EXCLUDED(mutex_rtcp_sender_);
-
bool TimeToSendRTCPReport(bool sendKeyframeBeforeRTP = false) const
RTC_LOCKS_EXCLUDED(mutex_rtcp_sender_);
@@ -114,12 +109,6 @@
const uint16_t* nackList = 0)
RTC_LOCKS_EXCLUDED(mutex_rtcp_sender_);
- int32_t SendCompoundRTCP(const FeedbackState& feedback_state,
- const std::set<RTCPPacketType>& packetTypes,
- int32_t nackSize = 0,
- const uint16_t* nackList = nullptr)
- RTC_LOCKS_EXCLUDED(mutex_rtcp_sender_);
-
int32_t SendLossNotification(const FeedbackState& feedback_state,
uint16_t last_decoded_seq_num,
uint16_t last_received_seq_num,
@@ -134,8 +123,6 @@
bool TMMBR() const RTC_LOCKS_EXCLUDED(mutex_rtcp_sender_);
- void SetTMMBRStatus(bool enable) RTC_LOCKS_EXCLUDED(mutex_rtcp_sender_);
-
void SetMaxRtpPacketSize(size_t max_packet_size)
RTC_LOCKS_EXCLUDED(mutex_rtcp_sender_);
@@ -155,20 +142,14 @@
private:
class RtcpContext;
-
- int32_t SendCompoundRTCPLocked(const FeedbackState& feedback_state,
- const std::set<RTCPPacketType>& packet_types,
- int32_t nack_size,
- const uint16_t* nack_list)
- RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
+ class PacketSender;
absl::optional<int32_t> ComputeCompoundRTCPPacket(
const FeedbackState& feedback_state,
- const std::set<RTCPPacketType>& packet_types,
+ RTCPPacketType packet_type,
int32_t nack_size,
const uint16_t* nack_list,
- rtcp::CompoundPacket* out_packet)
- RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
+ PacketSender& sender) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
// Determine which RTCP messages should be sent and setup flags.
void PrepareReport(const FeedbackState& feedback_state)
@@ -178,36 +159,33 @@
const FeedbackState& feedback_state)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
- std::unique_ptr<rtcp::RtcpPacket> BuildSR(const RtcpContext& context)
+ void BuildSR(const RtcpContext& context, PacketSender& sender)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
- std::unique_ptr<rtcp::RtcpPacket> BuildRR(const RtcpContext& context)
+ void BuildRR(const RtcpContext& context, PacketSender& sender)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
- std::unique_ptr<rtcp::RtcpPacket> BuildSDES(const RtcpContext& context)
+ void BuildSDES(const RtcpContext& context, PacketSender& sender)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
- std::unique_ptr<rtcp::RtcpPacket> BuildPLI(const RtcpContext& context)
+ void BuildPLI(const RtcpContext& context, PacketSender& sender)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
- std::unique_ptr<rtcp::RtcpPacket> BuildREMB(const RtcpContext& context)
+ void BuildREMB(const RtcpContext& context, PacketSender& sender)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
- std::unique_ptr<rtcp::RtcpPacket> BuildTMMBR(const RtcpContext& context)
+ void BuildTMMBR(const RtcpContext& context, PacketSender& sender)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
- std::unique_ptr<rtcp::RtcpPacket> BuildTMMBN(const RtcpContext& context)
+ void BuildTMMBN(const RtcpContext& context, PacketSender& sender)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
- std::unique_ptr<rtcp::RtcpPacket> BuildAPP(const RtcpContext& context)
+ void BuildAPP(const RtcpContext& context, PacketSender& sender)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
- std::unique_ptr<rtcp::RtcpPacket> BuildLossNotification(
- const RtcpContext& context)
+ void BuildLossNotification(const RtcpContext& context, PacketSender& sender)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
- std::unique_ptr<rtcp::RtcpPacket> BuildExtendedReports(
- const RtcpContext& context)
+ void BuildExtendedReports(const RtcpContext& context, PacketSender& sender)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
- std::unique_ptr<rtcp::RtcpPacket> BuildBYE(const RtcpContext& context)
+ void BuildBYE(const RtcpContext& context, PacketSender& sender)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
- std::unique_ptr<rtcp::RtcpPacket> BuildFIR(const RtcpContext& context)
+ void BuildFIR(const RtcpContext& context, PacketSender& sender)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
- std::unique_ptr<rtcp::RtcpPacket> BuildNACK(const RtcpContext& context)
+ void BuildNACK(const RtcpContext& context, PacketSender& sender)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
- private:
const bool audio_;
const uint32_t ssrc_;
Clock* const clock_;
@@ -233,8 +211,6 @@
ReceiveStatisticsProvider* receive_statistics_
RTC_GUARDED_BY(mutex_rtcp_sender_);
- std::map<uint32_t, std::string> csrc_cnames_
- RTC_GUARDED_BY(mutex_rtcp_sender_);
// send CSRCs
std::vector<uint32_t> csrcs_ RTC_GUARDED_BY(mutex_rtcp_sender_);
@@ -242,14 +218,7 @@
// Full intra request
uint8_t sequence_number_fir_ RTC_GUARDED_BY(mutex_rtcp_sender_);
- // Loss Notification
- struct LossNotificationState {
- uint16_t last_decoded_seq_num;
- uint16_t last_received_seq_num;
- bool decodability_flag;
- };
- LossNotificationState loss_notification_state_
- RTC_GUARDED_BY(mutex_rtcp_sender_);
+ rtcp::LossNotification loss_notification_ RTC_GUARDED_BY(mutex_rtcp_sender_);
// REMB
int64_t remb_bitrate_ RTC_GUARDED_BY(mutex_rtcp_sender_);
@@ -281,8 +250,6 @@
void SetFlag(uint32_t type, bool is_volatile)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
- void SetFlags(const std::set<RTCPPacketType>& types, bool is_volatile)
- RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
bool IsFlagPresent(uint32_t type) const
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_);
bool ConsumeFlag(uint32_t type, bool forced = false)
@@ -300,8 +267,7 @@
std::set<ReportFlag> report_flags_ RTC_GUARDED_BY(mutex_rtcp_sender_);
- typedef std::unique_ptr<rtcp::RtcpPacket> (RTCPSender::*BuilderFunc)(
- const RtcpContext&);
+ typedef void (RTCPSender::*BuilderFunc)(const RtcpContext&, PacketSender&);
// Map from RTCPPacketType to builder.
std::map<uint32_t, BuilderFunc> builders_;
};
diff --git a/modules/rtp_rtcp/source/rtcp_sender_unittest.cc b/modules/rtp_rtcp/source/rtcp_sender_unittest.cc
index 4c8038f..2c0bb2e 100644
--- a/modules/rtp_rtcp/source/rtcp_sender_unittest.cc
+++ b/modules/rtp_rtcp/source/rtcp_sender_unittest.cc
@@ -294,20 +294,6 @@
EXPECT_EQ("alice@host", parser()->sdes()->chunks()[0].cname);
}
-TEST_F(RtcpSenderTest, SendSdesWithMaxChunks) {
- auto rtcp_sender = CreateRtcpSender(GetDefaultConfig());
- rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize);
- EXPECT_EQ(0, rtcp_sender->SetCNAME("alice@host"));
- const char cname[] = "smith@host";
- for (size_t i = 0; i < 30; ++i) {
- const uint32_t csrc = 0x1234 + i;
- EXPECT_EQ(0, rtcp_sender->AddMixedCNAME(csrc, cname));
- }
- EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpSdes));
- EXPECT_EQ(1, parser()->sdes()->num_packets());
- EXPECT_EQ(31U, parser()->sdes()->chunks().size());
-}
-
TEST_F(RtcpSenderTest, SdesIncludedInCompoundPacket) {
auto rtcp_sender = CreateRtcpSender(GetDefaultConfig());
rtcp_sender->SetRTCPStatus(RtcpMode::kCompound);
@@ -588,25 +574,6 @@
// TODO(asapersson): tmmbr_item()->Overhead() looks broken, always zero.
}
-TEST_F(RtcpSenderTest, TmmbrIncludedInCompoundPacketIfEnabled) {
- const unsigned int kBitrateBps = 312000;
- auto rtcp_sender = CreateRtcpSender(GetDefaultConfig());
- rtcp_sender->SetRTCPStatus(RtcpMode::kCompound);
- EXPECT_FALSE(rtcp_sender->TMMBR());
- rtcp_sender->SetTMMBRStatus(true);
- EXPECT_TRUE(rtcp_sender->TMMBR());
- rtcp_sender->SetTargetBitrate(kBitrateBps);
- EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpReport));
- EXPECT_EQ(1, parser()->tmmbr()->num_packets());
- EXPECT_EQ(1U, parser()->tmmbr()->requests().size());
- // TMMBR should be included in each compound packet.
- EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpReport));
- EXPECT_EQ(2, parser()->tmmbr()->num_packets());
-
- rtcp_sender->SetTMMBRStatus(false);
- EXPECT_FALSE(rtcp_sender->TMMBR());
-}
-
TEST_F(RtcpSenderTest, SendTmmbn) {
auto rtcp_sender = CreateRtcpSender(GetDefaultConfig());
rtcp_sender->SetRTCPStatus(RtcpMode::kCompound);
@@ -648,21 +615,6 @@
EXPECT_EQ(0U, parser()->tmmbn()->items().size());
}
-TEST_F(RtcpSenderTest, SendCompoundPliRemb) {
- const int kBitrate = 261011;
- auto rtcp_sender = CreateRtcpSender(GetDefaultConfig());
- std::vector<uint32_t> ssrcs;
- ssrcs.push_back(kRemoteSsrc);
- rtcp_sender->SetRTCPStatus(RtcpMode::kCompound);
- rtcp_sender->SetRemb(kBitrate, ssrcs);
- std::set<RTCPPacketType> packet_types;
- packet_types.insert(kRtcpRemb);
- packet_types.insert(kRtcpPli);
- EXPECT_EQ(0, rtcp_sender->SendCompoundRTCP(feedback_state(), packet_types));
- EXPECT_EQ(1, parser()->remb()->num_packets());
- EXPECT_EQ(1, parser()->pli()->num_packets());
-}
-
// This test is written to verify that BYE is always the last packet
// type in a RTCP compoud packet. The rtcp_sender is recreated with
// mock_transport, which is used to check for whether BYE at the end
diff --git a/modules/rtp_rtcp/source/rtp_packet_received.h b/modules/rtp_rtcp/source/rtp_packet_received.h
index 6727b67..e1e1309 100644
--- a/modules/rtp_rtcp/source/rtp_packet_received.h
+++ b/modules/rtp_rtcp/source/rtp_packet_received.h
@@ -12,14 +12,18 @@
#include <stdint.h>
-#include <vector>
+#include <utility>
#include "api/array_view.h"
+#include "api/ref_counted_base.h"
#include "api/rtp_headers.h"
+#include "api/scoped_refptr.h"
#include "modules/rtp_rtcp/source/rtp_packet.h"
namespace webrtc {
// Class to hold rtp packet with metadata for receiver side.
+// The metadata is not parsed from the rtp packet, but may be derived from the
+// data that is parsed from the rtp packet.
class RtpPacketReceived : public RtpPacket {
public:
RtpPacketReceived();
@@ -50,20 +54,20 @@
payload_type_frequency_ = value;
}
- // Additional data bound to the RTP packet for use in application code,
- // outside of WebRTC.
- rtc::ArrayView<const uint8_t> application_data() const {
- return application_data_;
+ // An application can attach arbitrary data to an RTP packet using
+ // `additional_data`. The additional data does not affect WebRTC processing.
+ rtc::scoped_refptr<rtc::RefCountedBase> additional_data() const {
+ return additional_data_;
}
- void set_application_data(rtc::ArrayView<const uint8_t> data) {
- application_data_.assign(data.begin(), data.end());
+ void set_additional_data(rtc::scoped_refptr<rtc::RefCountedBase> data) {
+ additional_data_ = std::move(data);
}
private:
int64_t arrival_time_ms_ = 0;
int payload_type_frequency_ = 0;
bool recovered_ = false;
- std::vector<uint8_t> application_data_;
+ rtc::scoped_refptr<rtc::RefCountedBase> additional_data_;
};
} // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtp_packet_to_send.h b/modules/rtp_rtcp/source/rtp_packet_to_send.h
index 9aaf9a5..2411dea 100644
--- a/modules/rtp_rtcp/source/rtp_packet_to_send.h
+++ b/modules/rtp_rtcp/source/rtp_packet_to_send.h
@@ -13,10 +13,12 @@
#include <stddef.h>
#include <stdint.h>
-#include <vector>
+#include <utility>
#include "absl/types/optional.h"
#include "api/array_view.h"
+#include "api/ref_counted_base.h"
+#include "api/scoped_refptr.h"
#include "api/video/video_timing.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
@@ -24,6 +26,8 @@
namespace webrtc {
// Class to hold rtp packet with metadata for sender side.
+// The metadata is not send over the wire, but packet sender may use it to
+// create rtp header extensions or other data that is sent over the wire.
class RtpPacketToSend : public RtpPacket {
public:
// RtpPacketToSend::Type is deprecated. Use RtpPacketMediaType directly.
@@ -64,14 +68,13 @@
}
bool allow_retransmission() { return allow_retransmission_; }
- // Additional data bound to the RTP packet for use in application code,
- // outside of WebRTC.
- rtc::ArrayView<const uint8_t> application_data() const {
- return application_data_;
+ // An application can attach arbitrary data to an RTP packet using
+ // `additional_data`. The additional data does not affect WebRTC processing.
+ rtc::scoped_refptr<rtc::RefCountedBase> additional_data() const {
+ return additional_data_;
}
-
- void set_application_data(rtc::ArrayView<const uint8_t> data) {
- application_data_.assign(data.begin(), data.end());
+ void set_additional_data(rtc::scoped_refptr<rtc::RefCountedBase> data) {
+ additional_data_ = std::move(data);
}
void set_packetization_finish_time_ms(int64_t time) {
@@ -122,7 +125,7 @@
absl::optional<RtpPacketMediaType> packet_type_;
bool allow_retransmission_ = false;
absl::optional<uint16_t> retransmitted_sequence_number_;
- std::vector<uint8_t> application_data_;
+ rtc::scoped_refptr<rtc::RefCountedBase> additional_data_;
bool is_first_packet_of_frame_ = false;
bool is_key_frame_ = false;
bool fec_protect_packet_ = false;
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
index 69a64fe..fa4af1d 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
@@ -24,6 +24,7 @@
#include "modules/rtp_rtcp/source/rtp_rtcp_config.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
+#include "system_wrappers/include/ntp_time.h"
#ifdef _WIN32
// Disable warning C4355: 'this' : used in base member initializer list.
@@ -122,20 +123,18 @@
// processed RTT for at least |kRtpRtcpRttProcessTimeMs| milliseconds.
// Note that LastReceivedReportBlockMs() grabs a lock, so check
// |process_rtt| first.
- if (process_rtt &&
+ if (process_rtt && rtt_stats_ != nullptr &&
rtcp_receiver_.LastReceivedReportBlockMs() > last_rtt_process_time_) {
- std::vector<RTCPReportBlock> receive_blocks;
- rtcp_receiver_.StatisticsReceived(&receive_blocks);
- int64_t max_rtt = 0;
- for (std::vector<RTCPReportBlock>::iterator it = receive_blocks.begin();
- it != receive_blocks.end(); ++it) {
- int64_t rtt = 0;
- rtcp_receiver_.RTT(it->sender_ssrc, &rtt, NULL, NULL, NULL);
- max_rtt = (rtt > max_rtt) ? rtt : max_rtt;
+ int64_t max_rtt_ms = 0;
+ for (const auto& block : rtcp_receiver_.GetLatestReportBlockData()) {
+ if (block.last_rtt_ms() > max_rtt_ms) {
+ max_rtt_ms = block.last_rtt_ms();
+ }
}
// Report the rtt.
- if (rtt_stats_ && max_rtt != 0)
- rtt_stats_->OnRttUpdate(max_rtt);
+ if (max_rtt_ms > 0) {
+ rtt_stats_->OnRttUpdate(max_rtt_ms);
+ }
}
// Verify receiver reports are delivered and the reported sequence number
@@ -192,7 +191,7 @@
if (rtcp_sender_.TimeToSendRTCPReport())
rtcp_sender_.SendRTCP(GetFeedbackState(), kRtcpReport);
- if (TMMBR() && rtcp_receiver_.UpdateTmmbrTimers()) {
+ if (rtcp_sender_.TMMBR() && rtcp_receiver_.UpdateTmmbrTimers()) {
rtcp_receiver_.NotifyTmmbrUpdated();
}
}
@@ -312,8 +311,19 @@
}
state.receiver = &rtcp_receiver_;
- LastReceivedNTP(&state.last_rr_ntp_secs, &state.last_rr_ntp_frac,
- &state.remote_sr);
+ uint32_t received_ntp_secs = 0;
+ uint32_t received_ntp_frac = 0;
+ state.remote_sr = 0;
+ if (rtcp_receiver_.NTP(&received_ntp_secs, &received_ntp_frac,
+ /*rtcp_arrival_time_secs=*/&state.last_rr_ntp_secs,
+ /*rtcp_arrival_time_frac=*/&state.last_rr_ntp_frac,
+ /*rtcp_timestamp=*/nullptr,
+ /*remote_sender_packet_count=*/nullptr,
+ /*remote_sender_octet_count=*/nullptr,
+ /*remote_sender_reports_count=*/nullptr)) {
+ state.remote_sr = ((received_ntp_secs & 0x0000ffff) << 16) +
+ ((received_ntp_frac & 0xffff0000) >> 16);
+ }
state.last_xr_rtis = rtcp_receiver_.ConsumeReceivedXrReferenceTimeInfo();
@@ -467,19 +477,6 @@
return rtcp_sender_.SetCNAME(c_name);
}
-int32_t ModuleRtpRtcpImpl::AddMixedCNAME(uint32_t ssrc, const char* c_name) {
- return rtcp_sender_.AddMixedCNAME(ssrc, c_name);
-}
-
-int32_t ModuleRtpRtcpImpl::RemoveMixedCNAME(const uint32_t ssrc) {
- return rtcp_sender_.RemoveMixedCNAME(ssrc);
-}
-
-int32_t ModuleRtpRtcpImpl::RemoteCNAME(const uint32_t remote_ssrc,
- char c_name[RTCP_CNAME_SIZE]) const {
- return rtcp_receiver_.CNAME(remote_ssrc, c_name);
-}
-
int32_t ModuleRtpRtcpImpl::RemoteNTP(uint32_t* received_ntpsecs,
uint32_t* received_ntpfrac,
uint32_t* rtcp_arrival_time_secs,
@@ -487,7 +484,10 @@
uint32_t* rtcp_timestamp) const {
return rtcp_receiver_.NTP(received_ntpsecs, received_ntpfrac,
rtcp_arrival_time_secs, rtcp_arrival_time_frac,
- rtcp_timestamp)
+ rtcp_timestamp,
+ /*remote_sender_packet_count=*/nullptr,
+ /*remote_sender_octet_count=*/nullptr,
+ /*remote_sender_reports_count=*/nullptr)
? 0
: -1;
}
@@ -527,39 +527,6 @@
return rtcp_sender_.SendRTCP(GetFeedbackState(), packet_type);
}
-int32_t ModuleRtpRtcpImpl::SetRTCPApplicationSpecificData(
- const uint8_t sub_type,
- const uint32_t name,
- const uint8_t* data,
- const uint16_t length) {
- RTC_NOTREACHED() << "Not implemented";
- return -1;
-}
-
-// TODO(asapersson): Replace this method with the one below.
-int32_t ModuleRtpRtcpImpl::DataCountersRTP(size_t* bytes_sent,
- uint32_t* packets_sent) const {
- StreamDataCounters rtp_stats;
- StreamDataCounters rtx_stats;
- rtp_sender_->packet_sender.GetDataCounters(&rtp_stats, &rtx_stats);
-
- if (bytes_sent) {
- // TODO(http://crbug.com/webrtc/10525): Bytes sent should only include
- // payload bytes, not header and padding bytes.
- *bytes_sent = rtp_stats.transmitted.payload_bytes +
- rtp_stats.transmitted.padding_bytes +
- rtp_stats.transmitted.header_bytes +
- rtx_stats.transmitted.payload_bytes +
- rtx_stats.transmitted.padding_bytes +
- rtx_stats.transmitted.header_bytes;
- }
- if (packets_sent) {
- *packets_sent =
- rtp_stats.transmitted.packets + rtx_stats.transmitted.packets;
- }
- return 0;
-}
-
void ModuleRtpRtcpImpl::GetSendStreamDataCounters(
StreamDataCounters* rtp_counters,
StreamDataCounters* rtx_counters) const {
@@ -567,16 +534,31 @@
}
// Received RTCP report.
-int32_t ModuleRtpRtcpImpl::RemoteRTCPStat(
- std::vector<RTCPReportBlock>* receive_blocks) const {
- return rtcp_receiver_.StatisticsReceived(receive_blocks);
-}
-
std::vector<ReportBlockData> ModuleRtpRtcpImpl::GetLatestReportBlockData()
const {
return rtcp_receiver_.GetLatestReportBlockData();
}
+absl::optional<RtpRtcpInterface::SenderReportStats>
+ModuleRtpRtcpImpl::GetSenderReportStats() const {
+ SenderReportStats stats;
+ uint32_t remote_timestamp_secs;
+ uint32_t remote_timestamp_frac;
+ uint32_t arrival_timestamp_secs;
+ uint32_t arrival_timestamp_frac;
+ if (rtcp_receiver_.NTP(&remote_timestamp_secs, &remote_timestamp_frac,
+ &arrival_timestamp_secs, &arrival_timestamp_frac,
+ /*rtcp_timestamp=*/nullptr, &stats.packets_sent,
+ &stats.bytes_sent, &stats.reports_count)) {
+ stats.last_remote_timestamp.Set(remote_timestamp_secs,
+ remote_timestamp_frac);
+ stats.last_arrival_timestamp.Set(arrival_timestamp_secs,
+ arrival_timestamp_frac);
+ return stats;
+ }
+ return absl::nullopt;
+}
+
// (REMB) Receiver Estimated Max Bitrate.
void ModuleRtpRtcpImpl::SetRemb(int64_t bitrate_bps,
std::vector<uint32_t> ssrcs) {
@@ -591,12 +573,6 @@
rtp_sender_->packet_generator.SetExtmapAllowMixed(extmap_allow_mixed);
}
-int32_t ModuleRtpRtcpImpl::RegisterSendRtpHeaderExtension(
- const RTPExtensionType type,
- const uint8_t id) {
- return rtp_sender_->packet_generator.RegisterRtpHeaderExtension(type, id);
-}
-
void ModuleRtpRtcpImpl::RegisterRtpHeaderExtension(absl::string_view uri,
int id) {
bool registered =
@@ -613,15 +589,6 @@
rtp_sender_->packet_generator.DeregisterRtpHeaderExtension(uri);
}
-// (TMMBR) Temporary Max Media Bit Rate.
-bool ModuleRtpRtcpImpl::TMMBR() const {
- return rtcp_sender_.TMMBR();
-}
-
-void ModuleRtpRtcpImpl::SetTMMBRStatus(const bool enable) {
- rtcp_sender_.SetTMMBRStatus(enable);
-}
-
void ModuleRtpRtcpImpl::SetTmmbn(std::vector<rtcp::TmmbItem> bounding_set) {
rtcp_sender_.SetTmmbn(std::move(bounding_set));
}
@@ -763,23 +730,6 @@
}
}
-bool ModuleRtpRtcpImpl::LastReceivedNTP(
- uint32_t* rtcp_arrival_time_secs, // When we got the last report.
- uint32_t* rtcp_arrival_time_frac,
- uint32_t* remote_sr) const {
- // Remote SR: NTP inside the last received (mid 16 bits from sec and frac).
- uint32_t ntp_secs = 0;
- uint32_t ntp_frac = 0;
-
- if (!rtcp_receiver_.NTP(&ntp_secs, &ntp_frac, rtcp_arrival_time_secs,
- rtcp_arrival_time_frac, NULL)) {
- return false;
- }
- *remote_sr =
- ((ntp_secs & 0x0000ffff) << 16) + ((ntp_frac & 0xffff0000) >> 16);
- return true;
-}
-
void ModuleRtpRtcpImpl::set_rtt_ms(int64_t rtt_ms) {
{
MutexLock lock(&mutex_rtt_);
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/modules/rtp_rtcp/source/rtp_rtcp_impl.h
index e30f1cc..5bcabc5 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.h
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.h
@@ -73,8 +73,6 @@
void SetExtmapAllowMixed(bool extmap_allow_mixed) override;
// Register RTP header extension.
- int32_t RegisterSendRtpHeaderExtension(RTPExtensionType type,
- uint8_t id) override;
void RegisterRtpHeaderExtension(absl::string_view uri, int id) override;
int32_t DeregisterSendRtpHeaderExtension(RTPExtensionType type) override;
void DeregisterSendRtpHeaderExtension(absl::string_view uri) override;
@@ -166,10 +164,6 @@
// Set RTCP CName.
int32_t SetCNAME(const char* c_name) override;
- // Get remote CName.
- int32_t RemoteCNAME(uint32_t remote_ssrc,
- char c_name[RTCP_CNAME_SIZE]) const override;
-
// Get remote NTP.
int32_t RemoteNTP(uint32_t* received_ntp_secs,
uint32_t* received_ntp_frac,
@@ -177,10 +171,6 @@
uint32_t* rtcp_arrival_time_frac,
uint32_t* rtcp_timestamp) const override;
- int32_t AddMixedCNAME(uint32_t ssrc, const char* c_name) override;
-
- int32_t RemoveMixedCNAME(uint32_t ssrc) override;
-
// Get RoundTripTime.
int32_t RTT(uint32_t remote_ssrc,
int64_t* rtt,
@@ -194,32 +184,21 @@
// Normal SR and RR are triggered via the process function.
int32_t SendRTCP(RTCPPacketType rtcpPacketType) override;
- // Statistics of the amount of data sent and received.
- int32_t DataCountersRTP(size_t* bytes_sent,
- uint32_t* packets_sent) const override;
-
void GetSendStreamDataCounters(
StreamDataCounters* rtp_counters,
StreamDataCounters* rtx_counters) const override;
- // Get received RTCP report, report block.
- int32_t RemoteRTCPStat(
- std::vector<RTCPReportBlock>* receive_blocks) const override;
// A snapshot of the most recent Report Block with additional data of
// interest to statistics. Used to implement RTCRemoteInboundRtpStreamStats.
// Within this list, the ReportBlockData::RTCPReportBlock::source_ssrc(),
// which is the SSRC of the corresponding outbound RTP stream, is unique.
std::vector<ReportBlockData> GetLatestReportBlockData() const override;
+ absl::optional<SenderReportStats> GetSenderReportStats() const override;
// (REMB) Receiver Estimated Max Bitrate.
void SetRemb(int64_t bitrate_bps, std::vector<uint32_t> ssrcs) override;
void UnsetRemb() override;
- // (TMMBR) Temporary Max Media Bit Rate.
- bool TMMBR() const override;
-
- void SetTMMBRStatus(bool enable) override;
-
void SetTmmbn(std::vector<rtcp::TmmbItem> bounding_set) override;
size_t MaxRtpPacketSize() const override;
@@ -241,22 +220,12 @@
void SendCombinedRtcpPacket(
std::vector<std::unique_ptr<rtcp::RtcpPacket>> rtcp_packets) override;
- // (APP) Application specific data.
- int32_t SetRTCPApplicationSpecificData(uint8_t sub_type,
- uint32_t name,
- const uint8_t* data,
- uint16_t length) override;
-
// Video part.
int32_t SendLossNotification(uint16_t last_decoded_seq_num,
uint16_t last_received_seq_num,
bool decodability_flag,
bool buffering_allowed) override;
- bool LastReceivedNTP(uint32_t* NTPsecs,
- uint32_t* NTPfrac,
- uint32_t* remote_sr) const;
-
RtpSendRates GetSendRates() const override;
void OnReceivedNack(
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc
index 94dc297..78ccf99 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc
@@ -24,6 +24,7 @@
#include "modules/rtp_rtcp/source/rtp_rtcp_config.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
+#include "system_wrappers/include/ntp_time.h"
#ifdef _WIN32
// Disable warning C4355: 'this' : used in base member initializer list.
@@ -260,8 +261,19 @@
}
state.receiver = &rtcp_receiver_;
- LastReceivedNTP(&state.last_rr_ntp_secs, &state.last_rr_ntp_frac,
- &state.remote_sr);
+ uint32_t received_ntp_secs = 0;
+ uint32_t received_ntp_frac = 0;
+ state.remote_sr = 0;
+ if (rtcp_receiver_.NTP(&received_ntp_secs, &received_ntp_frac,
+ /*rtcp_arrival_time_secs=*/&state.last_rr_ntp_secs,
+ /*rtcp_arrival_time_frac=*/&state.last_rr_ntp_frac,
+ /*rtcp_timestamp=*/nullptr,
+ /*remote_sender_packet_count=*/nullptr,
+ /*remote_sender_octet_count=*/nullptr,
+ /*remote_sender_reports_count=*/nullptr)) {
+ state.remote_sr = ((received_ntp_secs & 0x0000ffff) << 16) +
+ ((received_ntp_frac & 0xffff0000) >> 16);
+ }
state.last_xr_rtis = rtcp_receiver_.ConsumeReceivedXrReferenceTimeInfo();
@@ -436,7 +448,10 @@
uint32_t* rtcp_timestamp) const {
return rtcp_receiver_.NTP(received_ntpsecs, received_ntpfrac,
rtcp_arrival_time_secs, rtcp_arrival_time_frac,
- rtcp_timestamp)
+ rtcp_timestamp,
+ /*remote_sender_packet_count=*/nullptr,
+ /*remote_sender_octet_count=*/nullptr,
+ /*remote_sender_reports_count=*/nullptr)
? 0
: -1;
}
@@ -486,16 +501,31 @@
}
// Received RTCP report.
-int32_t ModuleRtpRtcpImpl2::RemoteRTCPStat(
- std::vector<RTCPReportBlock>* receive_blocks) const {
- return rtcp_receiver_.StatisticsReceived(receive_blocks);
-}
-
std::vector<ReportBlockData> ModuleRtpRtcpImpl2::GetLatestReportBlockData()
const {
return rtcp_receiver_.GetLatestReportBlockData();
}
+absl::optional<RtpRtcpInterface::SenderReportStats>
+ModuleRtpRtcpImpl2::GetSenderReportStats() const {
+ SenderReportStats stats;
+ uint32_t remote_timestamp_secs;
+ uint32_t remote_timestamp_frac;
+ uint32_t arrival_timestamp_secs;
+ uint32_t arrival_timestamp_frac;
+ if (rtcp_receiver_.NTP(&remote_timestamp_secs, &remote_timestamp_frac,
+ &arrival_timestamp_secs, &arrival_timestamp_frac,
+ /*rtcp_timestamp=*/nullptr, &stats.packets_sent,
+ &stats.bytes_sent, &stats.reports_count)) {
+ stats.last_remote_timestamp.Set(remote_timestamp_secs,
+ remote_timestamp_frac);
+ stats.last_arrival_timestamp.Set(arrival_timestamp_secs,
+ arrival_timestamp_frac);
+ return stats;
+ }
+ return absl::nullopt;
+}
+
// (REMB) Receiver Estimated Max Bitrate.
void ModuleRtpRtcpImpl2::SetRemb(int64_t bitrate_bps,
std::vector<uint32_t> ssrcs) {
@@ -668,23 +698,6 @@
}
}
-bool ModuleRtpRtcpImpl2::LastReceivedNTP(
- uint32_t* rtcp_arrival_time_secs, // When we got the last report.
- uint32_t* rtcp_arrival_time_frac,
- uint32_t* remote_sr) const {
- // Remote SR: NTP inside the last received (mid 16 bits from sec and frac).
- uint32_t ntp_secs = 0;
- uint32_t ntp_frac = 0;
-
- if (!rtcp_receiver_.NTP(&ntp_secs, &ntp_frac, rtcp_arrival_time_secs,
- rtcp_arrival_time_frac, NULL)) {
- return false;
- }
- *remote_sr =
- ((ntp_secs & 0x0000ffff) << 16) + ((ntp_frac & 0xffff0000) >> 16);
- return true;
-}
-
void ModuleRtpRtcpImpl2::set_rtt_ms(int64_t rtt_ms) {
RTC_DCHECK_RUN_ON(worker_queue_);
{
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl2.h b/modules/rtp_rtcp/source/rtp_rtcp_impl2.h
index 9431e75..00f6ff1 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl2.h
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl2.h
@@ -21,6 +21,7 @@
#include "absl/types/optional.h"
#include "api/rtp_headers.h"
+#include "api/sequence_checker.h"
#include "api/task_queue/task_queue_base.h"
#include "api/video/video_bitrate_allocation.h"
#include "modules/include/module_fec_types.h"
@@ -36,7 +37,6 @@
#include "modules/rtp_rtcp/source/rtp_sender_egress.h"
#include "rtc_base/gtest_prod_util.h"
#include "rtc_base/synchronization/mutex.h"
-#include "rtc_base/synchronization/sequence_checker.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/task_utils/pending_task_safety_flag.h"
#include "rtc_base/task_utils/repeating_task.h"
@@ -200,14 +200,12 @@
StreamDataCounters* rtp_counters,
StreamDataCounters* rtx_counters) const override;
- // Get received RTCP report, report block.
- int32_t RemoteRTCPStat(
- std::vector<RTCPReportBlock>* receive_blocks) const override;
// A snapshot of the most recent Report Block with additional data of
// interest to statistics. Used to implement RTCRemoteInboundRtpStreamStats.
// Within this list, the ReportBlockData::RTCPReportBlock::source_ssrc(),
// which is the SSRC of the corresponding outbound RTP stream, is unique.
std::vector<ReportBlockData> GetLatestReportBlockData() const override;
+ absl::optional<SenderReportStats> GetSenderReportStats() const override;
// (REMB) Receiver Estimated Max Bitrate.
void SetRemb(int64_t bitrate_bps, std::vector<uint32_t> ssrcs) override;
@@ -240,10 +238,6 @@
bool decodability_flag,
bool buffering_allowed) override;
- bool LastReceivedNTP(uint32_t* NTPsecs,
- uint32_t* NTPfrac,
- uint32_t* remote_sr) const;
-
RtpSendRates GetSendRates() const override;
void OnReceivedNack(
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl2_unittest.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl2_unittest.cc
index 3b66642..193889f 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl2_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl2_unittest.cc
@@ -14,11 +14,13 @@
#include <memory>
#include <set>
+#include "absl/types/optional.h"
#include "api/transport/field_trial_based_config.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/rtcp_packet.h"
#include "modules/rtp_rtcp/source/rtcp_packet/nack.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
+#include "modules/rtp_rtcp/source/rtp_rtcp_interface.h"
#include "modules/rtp_rtcp/source/rtp_sender_video.h"
#include "rtc_base/rate_limiter.h"
#include "test/gmock.h"
@@ -29,6 +31,11 @@
#include "test/time_controller/simulated_time_controller.h"
using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::Field;
+using ::testing::Gt;
+using ::testing::Not;
+using ::testing::Optional;
namespace webrtc {
namespace {
@@ -631,4 +638,102 @@
/*is_last=*/1)));
}
+// Checks that the sender report stats are not available if no RTCP SR was sent.
+TEST_F(RtpRtcpImpl2Test, SenderReportStatsNotAvailable) {
+ EXPECT_THAT(receiver_.impl_->GetSenderReportStats(), Eq(absl::nullopt));
+}
+
+// Checks that the sender report stats are available if an RTCP SR was sent.
+TEST_F(RtpRtcpImpl2Test, SenderReportStatsAvailable) {
+ // Send a frame in order to send an SR.
+ SendFrame(&sender_, sender_video_.get(), kBaseLayerTid);
+ // Send an SR.
+ ASSERT_THAT(sender_.impl_->SendRTCP(kRtcpReport), Eq(0));
+ EXPECT_THAT(receiver_.impl_->GetSenderReportStats(), Not(Eq(absl::nullopt)));
+}
+
+// Checks that the sender report stats are not available if an RTCP SR with an
+// unexpected SSRC is received.
+TEST_F(RtpRtcpImpl2Test, SenderReportStatsNotUpdatedWithUnexpectedSsrc) {
+ constexpr uint32_t kUnexpectedSenderSsrc = 0x87654321;
+ static_assert(kUnexpectedSenderSsrc != kSenderSsrc, "");
+ // Forge a sender report and pass it to the receiver as if an RTCP SR were
+ // sent by an unexpected sender.
+ rtcp::SenderReport sr;
+ sr.SetSenderSsrc(kUnexpectedSenderSsrc);
+ sr.SetNtp({/*seconds=*/1u, /*fractions=*/1u << 31});
+ sr.SetPacketCount(123u);
+ sr.SetOctetCount(456u);
+ auto raw_packet = sr.Build();
+ receiver_.impl_->IncomingRtcpPacket(raw_packet.data(), raw_packet.size());
+ EXPECT_THAT(receiver_.impl_->GetSenderReportStats(), Eq(absl::nullopt));
+}
+
+// Checks the stats derived from the last received RTCP SR are set correctly.
+TEST_F(RtpRtcpImpl2Test, SenderReportStatsCheckStatsFromLastReport) {
+ using SenderReportStats = RtpRtcpInterface::SenderReportStats;
+ const NtpTime ntp(/*seconds=*/1u, /*fractions=*/1u << 31);
+ constexpr uint32_t kPacketCount = 123u;
+ constexpr uint32_t kOctetCount = 456u;
+ // Forge a sender report and pass it to the receiver as if an RTCP SR were
+ // sent by the sender.
+ rtcp::SenderReport sr;
+ sr.SetSenderSsrc(kSenderSsrc);
+ sr.SetNtp(ntp);
+ sr.SetPacketCount(kPacketCount);
+ sr.SetOctetCount(kOctetCount);
+ auto raw_packet = sr.Build();
+ receiver_.impl_->IncomingRtcpPacket(raw_packet.data(), raw_packet.size());
+
+ EXPECT_THAT(
+ receiver_.impl_->GetSenderReportStats(),
+ Optional(AllOf(Field(&SenderReportStats::last_remote_timestamp, Eq(ntp)),
+ Field(&SenderReportStats::packets_sent, Eq(kPacketCount)),
+ Field(&SenderReportStats::bytes_sent, Eq(kOctetCount)))));
+}
+
+// Checks that the sender report stats count equals the number of sent RTCP SRs.
+TEST_F(RtpRtcpImpl2Test, SenderReportStatsCount) {
+ using SenderReportStats = RtpRtcpInterface::SenderReportStats;
+ // Send a frame in order to send an SR.
+ SendFrame(&sender_, sender_video_.get(), kBaseLayerTid);
+ // Send the first SR.
+ ASSERT_THAT(sender_.impl_->SendRTCP(kRtcpReport), Eq(0));
+ EXPECT_THAT(receiver_.impl_->GetSenderReportStats(),
+ Optional(Field(&SenderReportStats::reports_count, Eq(1u))));
+ // Send the second SR.
+ ASSERT_THAT(sender_.impl_->SendRTCP(kRtcpReport), Eq(0));
+ EXPECT_THAT(receiver_.impl_->GetSenderReportStats(),
+ Optional(Field(&SenderReportStats::reports_count, Eq(2u))));
+}
+
+// Checks that the sender report stats include a valid arrival time if an RTCP
+// SR was sent.
+TEST_F(RtpRtcpImpl2Test, SenderReportStatsArrivalTimestampSet) {
+ // Send a frame in order to send an SR.
+ SendFrame(&sender_, sender_video_.get(), kBaseLayerTid);
+ // Send an SR.
+ ASSERT_THAT(sender_.impl_->SendRTCP(kRtcpReport), Eq(0));
+ auto stats = receiver_.impl_->GetSenderReportStats();
+ ASSERT_THAT(stats, Not(Eq(absl::nullopt)));
+ EXPECT_TRUE(stats->last_arrival_timestamp.Valid());
+}
+
+// Checks that the packet and byte counters from an RTCP SR are not zero once
+// a frame is sent.
+TEST_F(RtpRtcpImpl2Test, SenderReportStatsPacketByteCounters) {
+ using SenderReportStats = RtpRtcpInterface::SenderReportStats;
+ // Send a frame in order to send an SR.
+ SendFrame(&sender_, sender_video_.get(), kBaseLayerTid);
+ ASSERT_THAT(sender_.transport_.rtp_packets_sent_, Gt(0));
+ // Advance time otherwise the RTCP SR report will not include any packets
+ // generated by `SendFrame()`.
+ AdvanceTimeMs(1);
+ // Send an SR.
+ ASSERT_THAT(sender_.impl_->SendRTCP(kRtcpReport), Eq(0));
+ EXPECT_THAT(receiver_.impl_->GetSenderReportStats(),
+ Optional(AllOf(Field(&SenderReportStats::packets_sent, Gt(0u)),
+ Field(&SenderReportStats::bytes_sent, Gt(0u)))));
+}
+
} // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc
index 05c6ae1..cc6b76c 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc
@@ -27,6 +27,11 @@
#include "test/rtp_header_parser.h"
using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::Field;
+using ::testing::Gt;
+using ::testing::Not;
+using ::testing::Optional;
namespace webrtc {
namespace {
@@ -616,4 +621,102 @@
/*is_last=*/1)));
}
+// Checks that the sender report stats are not available if no RTCP SR was sent.
+TEST_F(RtpRtcpImplTest, SenderReportStatsNotAvailable) {
+ EXPECT_THAT(receiver_.impl_->GetSenderReportStats(), Eq(absl::nullopt));
+}
+
+// Checks that the sender report stats are available if an RTCP SR was sent.
+TEST_F(RtpRtcpImplTest, SenderReportStatsAvailable) {
+ // Send a frame in order to send an SR.
+ SendFrame(&sender_, sender_video_.get(), kBaseLayerTid);
+ // Send an SR.
+ ASSERT_THAT(sender_.impl_->SendRTCP(kRtcpReport), Eq(0));
+ EXPECT_THAT(receiver_.impl_->GetSenderReportStats(), Not(Eq(absl::nullopt)));
+}
+
+// Checks that the sender report stats are not available if an RTCP SR with an
+// unexpected SSRC is received.
+TEST_F(RtpRtcpImplTest, SenderReportStatsNotUpdatedWithUnexpectedSsrc) {
+ constexpr uint32_t kUnexpectedSenderSsrc = 0x87654321;
+ static_assert(kUnexpectedSenderSsrc != kSenderSsrc, "");
+ // Forge a sender report and pass it to the receiver as if an RTCP SR were
+ // sent by an unexpected sender.
+ rtcp::SenderReport sr;
+ sr.SetSenderSsrc(kUnexpectedSenderSsrc);
+ sr.SetNtp({/*seconds=*/1u, /*fractions=*/1u << 31});
+ sr.SetPacketCount(123u);
+ sr.SetOctetCount(456u);
+ auto raw_packet = sr.Build();
+ receiver_.impl_->IncomingRtcpPacket(raw_packet.data(), raw_packet.size());
+ EXPECT_THAT(receiver_.impl_->GetSenderReportStats(), Eq(absl::nullopt));
+}
+
+// Checks the stats derived from the last received RTCP SR are set correctly.
+TEST_F(RtpRtcpImplTest, SenderReportStatsCheckStatsFromLastReport) {
+ using SenderReportStats = RtpRtcpInterface::SenderReportStats;
+ const NtpTime ntp(/*seconds=*/1u, /*fractions=*/1u << 31);
+ constexpr uint32_t kPacketCount = 123u;
+ constexpr uint32_t kOctetCount = 456u;
+ // Forge a sender report and pass it to the receiver as if an RTCP SR were
+ // sent by the sender.
+ rtcp::SenderReport sr;
+ sr.SetSenderSsrc(kSenderSsrc);
+ sr.SetNtp(ntp);
+ sr.SetPacketCount(kPacketCount);
+ sr.SetOctetCount(kOctetCount);
+ auto raw_packet = sr.Build();
+ receiver_.impl_->IncomingRtcpPacket(raw_packet.data(), raw_packet.size());
+
+ EXPECT_THAT(
+ receiver_.impl_->GetSenderReportStats(),
+ Optional(AllOf(Field(&SenderReportStats::last_remote_timestamp, Eq(ntp)),
+ Field(&SenderReportStats::packets_sent, Eq(kPacketCount)),
+ Field(&SenderReportStats::bytes_sent, Eq(kOctetCount)))));
+}
+
+// Checks that the sender report stats count equals the number of sent RTCP SRs.
+TEST_F(RtpRtcpImplTest, SenderReportStatsCount) {
+ using SenderReportStats = RtpRtcpInterface::SenderReportStats;
+ // Send a frame in order to send an SR.
+ SendFrame(&sender_, sender_video_.get(), kBaseLayerTid);
+ // Send the first SR.
+ ASSERT_THAT(sender_.impl_->SendRTCP(kRtcpReport), Eq(0));
+ EXPECT_THAT(receiver_.impl_->GetSenderReportStats(),
+ Optional(Field(&SenderReportStats::reports_count, Eq(1u))));
+ // Send the second SR.
+ ASSERT_THAT(sender_.impl_->SendRTCP(kRtcpReport), Eq(0));
+ EXPECT_THAT(receiver_.impl_->GetSenderReportStats(),
+ Optional(Field(&SenderReportStats::reports_count, Eq(2u))));
+}
+
+// Checks that the sender report stats include a valid arrival time if an RTCP
+// SR was sent.
+TEST_F(RtpRtcpImplTest, SenderReportStatsArrivalTimestampSet) {
+ // Send a frame in order to send an SR.
+ SendFrame(&sender_, sender_video_.get(), kBaseLayerTid);
+ // Send an SR.
+ ASSERT_THAT(sender_.impl_->SendRTCP(kRtcpReport), Eq(0));
+ auto stats = receiver_.impl_->GetSenderReportStats();
+ ASSERT_THAT(stats, Not(Eq(absl::nullopt)));
+ EXPECT_TRUE(stats->last_arrival_timestamp.Valid());
+}
+
+// Checks that the packet and byte counters from an RTCP SR are not zero once
+// a frame is sent.
+TEST_F(RtpRtcpImplTest, SenderReportStatsPacketByteCounters) {
+ using SenderReportStats = RtpRtcpInterface::SenderReportStats;
+ // Send a frame in order to send an SR.
+ SendFrame(&sender_, sender_video_.get(), kBaseLayerTid);
+ ASSERT_THAT(sender_.transport_.rtp_packets_sent_, Gt(0));
+ // Advance time otherwise the RTCP SR report will not include any packets
+ // generated by `SendFrame()`.
+ clock_.AdvanceTimeMilliseconds(1);
+ // Send an SR.
+ ASSERT_THAT(sender_.impl_->SendRTCP(kRtcpReport), Eq(0));
+ EXPECT_THAT(receiver_.impl_->GetSenderReportStats(),
+ Optional(AllOf(Field(&SenderReportStats::packets_sent, Gt(0u)),
+ Field(&SenderReportStats::bytes_sent, Gt(0u)))));
+}
+
} // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_interface.h b/modules/rtp_rtcp/source/rtp_rtcp_interface.h
index 5bb3eb5..5ab48c9 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_interface.h
+++ b/modules/rtp_rtcp/source/rtp_rtcp_interface.h
@@ -28,6 +28,7 @@
#include "modules/rtp_rtcp/source/rtp_sequence_number_map.h"
#include "modules/rtp_rtcp/source/video_fec_generator.h"
#include "rtc_base/constructor_magic.h"
+#include "system_wrappers/include/ntp_time.h"
namespace webrtc {
@@ -152,6 +153,27 @@
RTC_DISALLOW_COPY_AND_ASSIGN(Configuration);
};
+ // Stats for RTCP sender reports (SR) for a specific SSRC.
+ // Refer to https://tools.ietf.org/html/rfc3550#section-6.4.1.
+ struct SenderReportStats {
+ // Arrival NPT timestamp for the last received RTCP SR.
+ NtpTime last_arrival_timestamp;
+ // Received (a.k.a., remote) NTP timestamp for the last received RTCP SR.
+ NtpTime last_remote_timestamp;
+ // Total number of RTP data packets transmitted by the sender since starting
+ // transmission up until the time this SR packet was generated. The count
+ // should be reset if the sender changes its SSRC identifier.
+ uint32_t packets_sent;
+ // Total number of payload octets (i.e., not including header or padding)
+ // transmitted in RTP data packets by the sender since starting transmission
+ // up until the time this SR packet was generated. The count should be reset
+ // if the sender changes its SSRC identifier.
+ uint64_t bytes_sent;
+ // Total number of RTCP SR blocks received.
+ // https://www.w3.org/TR/webrtc-stats/#dom-rtcremoteoutboundrtpstreamstats-reportssent.
+ uint64_t reports_count;
+ };
+
// **************************************************************************
// Receiver functions
// **************************************************************************
@@ -361,17 +383,13 @@
StreamDataCounters* rtp_counters,
StreamDataCounters* rtx_counters) const = 0;
- // Returns received RTCP report block.
- // Returns -1 on failure else 0.
- // TODO(https://crbug.com/webrtc/10678): Remove this in favor of
- // GetLatestReportBlockData().
- virtual int32_t RemoteRTCPStat(
- std::vector<RTCPReportBlock>* receive_blocks) const = 0;
// A snapshot of Report Blocks with additional data of interest to statistics.
// Within this list, the sender-source SSRC pair is unique and per-pair the
// ReportBlockData represents the latest Report Block that was received for
// that pair.
virtual std::vector<ReportBlockData> GetLatestReportBlockData() const = 0;
+ // Returns stats based on the received RTCP SRs.
+ virtual absl::optional<SenderReportStats> GetSenderReportStats() const = 0;
// (REMB) Receiver Estimated Max Bitrate.
// Schedules sending REMB on next and following sender/receiver reports.
diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc
index 584fced..c8ea999 100644
--- a/modules/rtp_rtcp/source/rtp_sender.cc
+++ b/modules/rtp_rtcp/source/rtp_sender.cc
@@ -42,7 +42,6 @@
constexpr size_t kMinAudioPaddingLength = 50;
constexpr size_t kRtpHeaderLength = 12;
constexpr uint16_t kMaxInitRtpSeqNumber = 32767; // 2^15 -1.
-constexpr uint32_t kTimestampTicksPerMs = 90;
// Min size needed to get payload padding from packet history.
constexpr int kMinPayloadPaddingBytes = 50;
@@ -170,28 +169,25 @@
paced_sender_(packet_sender),
sending_media_(true), // Default to sending media.
max_packet_size_(IP_PACKET_SIZE - 28), // Default is IP-v4/UDP.
- last_payload_type_(-1),
rtp_header_extension_map_(config.extmap_allow_mixed),
- max_media_packet_header_(kRtpHeaderSize),
- max_padding_fec_packet_header_(kRtpHeaderSize),
// RTP variables
- sequence_number_forced_(false),
+ sequencer_(config.local_media_ssrc,
+ config.rtx_send_ssrc.value_or(config.local_media_ssrc),
+ /*require_marker_before_media_padding_=*/!config.audio,
+ config.clock),
always_send_mid_and_rid_(config.always_send_mid_and_rid),
ssrc_has_acked_(false),
rtx_ssrc_has_acked_(false),
- last_rtp_timestamp_(0),
- capture_time_ms_(0),
- last_timestamp_time_ms_(0),
- last_packet_marker_bit_(false),
csrcs_(),
rtx_(kRtxOff),
supports_bwe_extension_(false),
retransmission_rate_limiter_(config.retransmission_rate_limiter) {
+ UpdateHeaderSizes();
// This random initialization is not intended to be cryptographic strong.
timestamp_offset_ = random_.Rand<uint32_t>();
// Random start, 16 bits. Can't be 0.
- sequence_number_rtx_ = random_.Rand(1, kMaxInitRtpSeqNumber);
- sequence_number_ = random_.Rand(1, kMaxInitRtpSeqNumber);
+ sequencer_.set_rtx_sequence_number(random_.Rand(1, kMaxInitRtpSeqNumber));
+ sequencer_.set_media_sequence_number(random_.Rand(1, kMaxInitRtpSeqNumber));
RTC_DCHECK(paced_sender_);
RTC_DCHECK(packet_history_);
@@ -229,15 +225,6 @@
rtp_header_extension_map_.SetExtmapAllowMixed(extmap_allow_mixed);
}
-int32_t RTPSender::RegisterRtpHeaderExtension(RTPExtensionType type,
- uint8_t id) {
- MutexLock lock(&send_mutex_);
- bool registered = rtp_header_extension_map_.RegisterByType(id, type);
- supports_bwe_extension_ = HasBweExtension(rtp_header_extension_map_);
- UpdateHeaderSizes();
- return registered ? 0 : -1;
-}
-
bool RTPSender::RegisterRtpHeaderExtension(absl::string_view uri, int id) {
MutexLock lock(&send_mutex_);
bool registered = rtp_header_extension_map_.RegisterByUri(id, uri);
@@ -360,7 +347,11 @@
void RTPSender::OnReceivedAckOnRtxSsrc(
int64_t extended_highest_sequence_number) {
MutexLock lock(&send_mutex_);
+ bool update_required = !rtx_ssrc_has_acked_;
rtx_ssrc_has_acked_ = true;
+ if (update_required) {
+ UpdateHeaderSizes();
+ }
}
void RTPSender::OnReceivedNack(
@@ -452,23 +443,11 @@
std::make_unique<RtpPacketToSend>(&rtp_header_extension_map_);
padding_packet->set_packet_type(RtpPacketMediaType::kPadding);
padding_packet->SetMarker(false);
- padding_packet->SetTimestamp(last_rtp_timestamp_);
- padding_packet->set_capture_time_ms(capture_time_ms_);
if (rtx_ == kRtxOff) {
- if (last_payload_type_ == -1) {
- break;
- }
- // Without RTX we can't send padding in the middle of frames.
- // For audio marker bits doesn't mark the end of a frame and frames
- // are usually a single packet, so for now we don't apply this rule
- // for audio.
- if (!audio_configured_ && !last_packet_marker_bit_) {
- break;
- }
-
padding_packet->SetSsrc(ssrc_);
- padding_packet->SetPayloadType(last_payload_type_);
- padding_packet->SetSequenceNumber(sequence_number_++);
+ if (!sequencer_.Sequence(*padding_packet)) {
+ break;
+ }
} else {
// Without abs-send-time or transport sequence number a media packet
// must be sent before padding so that the timestamps used for
@@ -479,24 +458,13 @@
TransportSequenceNumber::kId))) {
break;
}
- // Only change the timestamp of padding packets sent over RTX.
- // Padding only packets over RTP has to be sent as part of a media
- // frame (and therefore the same timestamp).
- int64_t now_ms = clock_->TimeInMilliseconds();
- if (last_timestamp_time_ms_ > 0) {
- padding_packet->SetTimestamp(padding_packet->Timestamp() +
- (now_ms - last_timestamp_time_ms_) *
- kTimestampTicksPerMs);
- if (padding_packet->capture_time_ms() > 0) {
- padding_packet->set_capture_time_ms(
- padding_packet->capture_time_ms() +
- (now_ms - last_timestamp_time_ms_));
- }
- }
+
RTC_DCHECK(rtx_ssrc_);
padding_packet->SetSsrc(*rtx_ssrc_);
- padding_packet->SetSequenceNumber(sequence_number_rtx_++);
padding_packet->SetPayloadType(rtx_payload_type_map_.begin()->second);
+ if (!sequencer_.Sequence(*padding_packet)) {
+ break;
+ }
}
if (rtp_header_extension_map_.IsRegistered(TransportSequenceNumber::kId)) {
@@ -561,13 +529,6 @@
return max_media_packet_header_;
}
-uint16_t RTPSender::AllocateSequenceNumber(uint16_t packets_to_send) {
- MutexLock lock(&send_mutex_);
- uint16_t first_allocated_sequence_number = sequence_number_;
- sequence_number_ += packets_to_send;
- return first_allocated_sequence_number;
-}
-
std::unique_ptr<RtpPacketToSend> RTPSender::AllocatePacket() const {
MutexLock lock(&send_mutex_);
// TODO(danilchap): Find better motivator and value for extra capacity.
@@ -614,18 +575,18 @@
MutexLock lock(&send_mutex_);
if (!sending_media_)
return false;
- RTC_DCHECK(packet->Ssrc() == ssrc_);
- packet->SetSequenceNumber(sequence_number_++);
+ return sequencer_.Sequence(*packet);
+}
- // Remember marker bit to determine if padding can be inserted with
- // sequence number following |packet|.
- last_packet_marker_bit_ = packet->Marker();
- // Remember payload type to use in the padding packet if rtx is disabled.
- last_payload_type_ = packet->PayloadType();
- // Save timestamps to generate timestamp field and extensions for the padding.
- last_rtp_timestamp_ = packet->Timestamp();
- last_timestamp_time_ms_ = clock_->TimeInMilliseconds();
- capture_time_ms_ = packet->capture_time_ms();
+bool RTPSender::AssignSequenceNumbersAndStoreLastPacketState(
+ rtc::ArrayView<std::unique_ptr<RtpPacketToSend>> packets) {
+ RTC_DCHECK(!packets.empty());
+ MutexLock lock(&send_mutex_);
+ if (!sending_media_)
+ return false;
+ for (auto& packet : packets) {
+ sequencer_.Sequence(*packet);
+ }
return true;
}
@@ -680,11 +641,10 @@
bool updated_sequence_number = false;
{
MutexLock lock(&send_mutex_);
- sequence_number_forced_ = true;
- if (sequence_number_ != seq) {
+ if (sequencer_.media_sequence_number() != seq) {
updated_sequence_number = true;
}
- sequence_number_ = seq;
+ sequencer_.set_media_sequence_number(seq);
}
if (updated_sequence_number) {
@@ -696,7 +656,7 @@
uint16_t RTPSender::SequenceNumber() const {
MutexLock lock(&send_mutex_);
- return sequence_number_;
+ return sequencer_.media_sequence_number();
}
static void CopyHeaderAndExtensionsToRtxPacket(const RtpPacketToSend& packet,
@@ -769,12 +729,12 @@
rtx_packet->SetPayloadType(kv->second);
- // Replace sequence number.
- rtx_packet->SetSequenceNumber(sequence_number_rtx_++);
-
// Replace SSRC.
rtx_packet->SetSsrc(*rtx_ssrc_);
+ // Replace sequence number.
+ sequencer_.Sequence(*rtx_packet);
+
CopyHeaderAndExtensionsToRtxPacket(packet, rtx_packet.get());
// RTX packets are sent on an SSRC different from the main media, so the
@@ -809,8 +769,8 @@
auto payload = packet.payload();
memcpy(rtx_payload + kRtxHeaderSize, payload.data(), payload.size());
- // Add original application data.
- rtx_packet->set_application_data(packet.application_data());
+ // Add original additional data.
+ rtx_packet->set_additional_data(packet.additional_data());
// Copy capture time so e.g. TransmissionOffset is correctly set.
rtx_packet->set_capture_time_ms(packet.capture_time_ms());
@@ -820,12 +780,9 @@
void RTPSender::SetRtpState(const RtpState& rtp_state) {
MutexLock lock(&send_mutex_);
- sequence_number_ = rtp_state.sequence_number;
- sequence_number_forced_ = true;
+
timestamp_offset_ = rtp_state.start_timestamp;
- last_rtp_timestamp_ = rtp_state.timestamp;
- capture_time_ms_ = rtp_state.capture_time_ms;
- last_timestamp_time_ms_ = rtp_state.last_timestamp_time_ms;
+ sequencer_.SetRtpState(rtp_state);
ssrc_has_acked_ = rtp_state.ssrc_has_acked;
UpdateHeaderSizes();
}
@@ -834,18 +791,15 @@
MutexLock lock(&send_mutex_);
RtpState state;
- state.sequence_number = sequence_number_;
state.start_timestamp = timestamp_offset_;
- state.timestamp = last_rtp_timestamp_;
- state.capture_time_ms = capture_time_ms_;
- state.last_timestamp_time_ms = last_timestamp_time_ms_;
state.ssrc_has_acked = ssrc_has_acked_;
+ sequencer_.PupulateRtpState(state);
return state;
}
void RTPSender::SetRtxRtpState(const RtpState& rtp_state) {
MutexLock lock(&send_mutex_);
- sequence_number_rtx_ = rtp_state.sequence_number;
+ sequencer_.set_rtx_sequence_number(rtp_state.sequence_number);
rtx_ssrc_has_acked_ = rtp_state.ssrc_has_acked;
}
@@ -853,18 +807,13 @@
MutexLock lock(&send_mutex_);
RtpState state;
- state.sequence_number = sequence_number_rtx_;
+ state.sequence_number = sequencer_.rtx_sequence_number();
state.start_timestamp = timestamp_offset_;
state.ssrc_has_acked = rtx_ssrc_has_acked_;
return state;
}
-int64_t RTPSender::LastTimestampTimeMs() const {
- MutexLock lock(&send_mutex_);
- return last_timestamp_time_ms_;
-}
-
void RTPSender::UpdateHeaderSizes() {
const size_t rtp_header_length =
kRtpHeaderLength + sizeof(uint32_t) * csrcs_.size();
@@ -874,10 +823,12 @@
rtp_header_extension_map_);
// RtpStreamId and Mid are treated specially in that we check if they
- // currently are being sent. RepairedRtpStreamId is still ignored since we
- // assume RTX will not make up large enough bitrate to treat overhead
- // differently.
- const bool send_mid_rid = always_send_mid_and_rid_ || !ssrc_has_acked_;
+ // currently are being sent. RepairedRtpStreamId is ignored because it is sent
+ // instead of RtpStreamId on rtx packets and require the same size.
+ const bool send_mid_rid_on_rtx =
+ rtx_ssrc_.has_value() && !rtx_ssrc_has_acked_;
+ const bool send_mid_rid =
+ always_send_mid_and_rid_ || !ssrc_has_acked_ || send_mid_rid_on_rtx;
std::vector<RtpExtensionSize> non_volatile_extensions;
for (auto& extension :
audio_configured_ ? AudioExtensionSizes() : VideoExtensionSizes()) {
@@ -901,5 +852,9 @@
max_media_packet_header_ =
rtp_header_length + RtpHeaderExtensionSize(non_volatile_extensions,
rtp_header_extension_map_);
+ // Reserve extra bytes if packet might be resent in an rtx packet.
+ if (rtx_ssrc_.has_value()) {
+ max_media_packet_header_ += kRtxHeaderSize;
+ }
}
} // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtp_sender.h b/modules/rtp_rtcp/source/rtp_sender.h
index 1580259..fbf1350 100644
--- a/modules/rtp_rtcp/source/rtp_sender.h
+++ b/modules/rtp_rtcp/source/rtp_sender.h
@@ -26,10 +26,10 @@
#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
#include "modules/rtp_rtcp/include/rtp_packet_sender.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
+#include "modules/rtp_rtcp/source/packet_sequencer.h"
#include "modules/rtp_rtcp/source/rtp_packet_history.h"
#include "modules/rtp_rtcp/source/rtp_rtcp_config.h"
#include "modules/rtp_rtcp/source/rtp_rtcp_interface.h"
-#include "rtc_base/deprecation.h"
#include "rtc_base/random.h"
#include "rtc_base/rate_statistics.h"
#include "rtc_base/synchronization/mutex.h"
@@ -78,8 +78,6 @@
RTC_LOCKS_EXCLUDED(send_mutex_);
// RTP header extension
- int32_t RegisterRtpHeaderExtension(RTPExtensionType type, uint8_t id)
- RTC_LOCKS_EXCLUDED(send_mutex_);
bool RegisterRtpHeaderExtension(absl::string_view uri, int id)
RTC_LOCKS_EXCLUDED(send_mutex_);
bool IsRtpHeaderExtensionRegistered(RTPExtensionType type) const
@@ -139,13 +137,16 @@
// Return false if sending was turned off.
bool AssignSequenceNumber(RtpPacketToSend* packet)
RTC_LOCKS_EXCLUDED(send_mutex_);
+ // Same as AssignSequenceNumber(), but applies sequence numbers atomically to
+ // a batch of packets.
+ bool AssignSequenceNumbersAndStoreLastPacketState(
+ rtc::ArrayView<std::unique_ptr<RtpPacketToSend>> packets)
+ RTC_LOCKS_EXCLUDED(send_mutex_);
// Maximum header overhead per fec/padding packet.
size_t FecOrPaddingPacketMaxRtpHeaderLength() const
RTC_LOCKS_EXCLUDED(send_mutex_);
// Expected header overhead per media packet.
size_t ExpectedPerPacketOverhead() const RTC_LOCKS_EXCLUDED(send_mutex_);
- uint16_t AllocateSequenceNumber(uint16_t packets_to_send)
- RTC_LOCKS_EXCLUDED(send_mutex_);
// Including RTP headers.
size_t MaxRtpPacketSize() const RTC_LOCKS_EXCLUDED(send_mutex_);
@@ -171,8 +172,6 @@
RTC_LOCKS_EXCLUDED(send_mutex_);
RtpState GetRtxRtpState() const RTC_LOCKS_EXCLUDED(send_mutex_);
- int64_t LastTimestampTimeMs() const RTC_LOCKS_EXCLUDED(send_mutex_);
-
private:
std::unique_ptr<RtpPacketToSend> BuildRtxPacket(
const RtpPacketToSend& packet);
@@ -181,6 +180,9 @@
void UpdateHeaderSizes() RTC_EXCLUSIVE_LOCKS_REQUIRED(send_mutex_);
+ void UpdateLastPacketState(const RtpPacketToSend& packet)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(send_mutex_);
+
Clock* const clock_;
Random random_ RTC_GUARDED_BY(send_mutex_);
@@ -201,17 +203,13 @@
bool sending_media_ RTC_GUARDED_BY(send_mutex_);
size_t max_packet_size_;
- int8_t last_payload_type_ RTC_GUARDED_BY(send_mutex_);
-
RtpHeaderExtensionMap rtp_header_extension_map_ RTC_GUARDED_BY(send_mutex_);
size_t max_media_packet_header_ RTC_GUARDED_BY(send_mutex_);
size_t max_padding_fec_packet_header_ RTC_GUARDED_BY(send_mutex_);
// RTP variables
uint32_t timestamp_offset_ RTC_GUARDED_BY(send_mutex_);
- bool sequence_number_forced_ RTC_GUARDED_BY(send_mutex_);
- uint16_t sequence_number_ RTC_GUARDED_BY(send_mutex_);
- uint16_t sequence_number_rtx_ RTC_GUARDED_BY(send_mutex_);
+ PacketSequencer sequencer_ RTC_GUARDED_BY(send_mutex_);
// RID value to send in the RID or RepairedRID header extension.
std::string rid_ RTC_GUARDED_BY(send_mutex_);
// MID value to send in the MID header extension.
@@ -222,10 +220,6 @@
// when to stop sending the MID and RID header extensions.
bool ssrc_has_acked_ RTC_GUARDED_BY(send_mutex_);
bool rtx_ssrc_has_acked_ RTC_GUARDED_BY(send_mutex_);
- uint32_t last_rtp_timestamp_ RTC_GUARDED_BY(send_mutex_);
- int64_t capture_time_ms_ RTC_GUARDED_BY(send_mutex_);
- int64_t last_timestamp_time_ms_ RTC_GUARDED_BY(send_mutex_);
- bool last_packet_marker_bit_ RTC_GUARDED_BY(send_mutex_);
std::vector<uint32_t> csrcs_ RTC_GUARDED_BY(send_mutex_);
int rtx_ RTC_GUARDED_BY(send_mutex_);
// Mapping rtx_payload_type_map_[associated] = rtx.
diff --git a/modules/rtp_rtcp/source/rtp_sender_egress.cc b/modules/rtp_rtcp/source/rtp_sender_egress.cc
index aba23dd..48f536c 100644
--- a/modules/rtp_rtcp/source/rtp_sender_egress.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_egress.cc
@@ -250,8 +250,7 @@
AddPacketToTransportFeedback(*packet_id, *packet, pacing_info);
}
- options.application_data.assign(packet->application_data().begin(),
- packet->application_data().end());
+ options.additional_data = packet->additional_data();
if (packet->packet_type() != RtpPacketMediaType::kPadding &&
packet->packet_type() != RtpPacketMediaType::kRetransmission) {
diff --git a/modules/rtp_rtcp/source/rtp_sender_egress.h b/modules/rtp_rtcp/source/rtp_sender_egress.h
index d7d71e2..c767a1f 100644
--- a/modules/rtp_rtcp/source/rtp_sender_egress.h
+++ b/modules/rtp_rtcp/source/rtp_sender_egress.h
@@ -19,6 +19,7 @@
#include "absl/types/optional.h"
#include "api/call/transport.h"
#include "api/rtc_event_log/rtc_event_log.h"
+#include "api/sequence_checker.h"
#include "api/task_queue/task_queue_base.h"
#include "api/units/data_rate.h"
#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
@@ -29,7 +30,6 @@
#include "modules/rtp_rtcp/source/rtp_sequence_number_map.h"
#include "rtc_base/rate_statistics.h"
#include "rtc_base/synchronization/mutex.h"
-#include "rtc_base/synchronization/sequence_checker.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/task_utils/pending_task_safety_flag.h"
#include "rtc_base/task_utils/repeating_task.h"
diff --git a/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
index 807d63d..cb4350d 100644
--- a/modules/rtp_rtcp/source/rtp_sender_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
@@ -34,6 +34,7 @@
#include "modules/rtp_rtcp/source/rtp_utility.h"
#include "modules/rtp_rtcp/source/video_fec_generator.h"
#include "rtc_base/arraysize.h"
+#include "rtc_base/logging.h"
#include "rtc_base/rate_limiter.h"
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/task_utils/to_queued_task.h"
@@ -415,7 +416,12 @@
std::unique_ptr<RtpPacketToSend> SendGenericPacket() {
const int64_t kCaptureTimeMs = clock_->TimeInMilliseconds();
- return SendPacket(kCaptureTimeMs, sizeof(kPayloadData));
+ // Use maximum allowed size to catch corner cases when packet is dropped
+ // because of lack of capacity for the media packet, or for an rtx packet
+ // containing the media packet.
+ return SendPacket(kCaptureTimeMs,
+ /*payload_length=*/rtp_sender()->MaxRtpPacketSize() -
+ rtp_sender()->ExpectedPerPacketOverhead());
}
size_t GenerateAndSendPadding(size_t target_size_bytes) {
@@ -445,16 +451,16 @@
// Enable sending of the MID header extension for both the primary SSRC and
// the RTX SSRC.
void EnableMidSending(const std::string& mid) {
- rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionMid, kMidExtensionId);
+ rtp_sender()->RegisterRtpHeaderExtension(RtpMid::kUri, kMidExtensionId);
rtp_sender()->SetMid(mid);
}
// Enable sending of the RSID header extension for the primary SSRC and the
// RRSID header extension for the RTX SSRC.
void EnableRidSending(const std::string& rid) {
- rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionRtpStreamId,
+ rtp_sender()->RegisterRtpHeaderExtension(RtpStreamId::kUri,
kRidExtensionId);
- rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionRepairedRtpStreamId,
+ rtp_sender()->RegisterRtpHeaderExtension(RepairedRtpStreamId::kUri,
kRepairedRidExtensionId);
rtp_sender()->SetRid(rid);
}
@@ -482,19 +488,16 @@
TEST_P(RtpSenderTestWithoutPacer, AllocatePacketReserveExtensions) {
// Configure rtp_sender with extensions.
- ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransmissionTimeOffset,
- kTransmissionTimeOffsetExtensionId));
- ASSERT_EQ(0,
- rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId));
- ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionAudioLevel, kAudioLevelExtensionId));
- ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransportSequenceNumber,
- kTransportSequenceNumberExtensionId));
- ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionVideoRotation, kVideoRotationExtensionId));
+ ASSERT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransmissionOffset::kUri, kTransmissionTimeOffsetExtensionId));
+ ASSERT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ AbsoluteSendTime::kUri, kAbsoluteSendTimeExtensionId));
+ ASSERT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(AudioLevel::kUri,
+ kAudioLevelExtensionId));
+ ASSERT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
+ ASSERT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ VideoOrientation::kUri, kVideoRotationExtensionId));
auto packet = rtp_sender()->AllocatePacket();
@@ -596,6 +599,7 @@
TEST_P(RtpSenderTestWithoutPacer,
TransportFeedbackObserverGetsCorrectByteCount) {
constexpr size_t kRtpOverheadBytesPerPacket = 12 + 8;
+ constexpr size_t kPayloadSize = 1400;
RtpRtcpInterface::Configuration config;
config.clock = clock_;
@@ -608,14 +612,12 @@
rtp_sender_context_ =
std::make_unique<RtpSenderContext>(config, &time_controller_);
- EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransportSequenceNumber,
- kTransportSequenceNumberExtensionId));
+ EXPECT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
- const size_t expected_bytes =
- GetParam().with_overhead
- ? sizeof(kPayloadData) + kRtpOverheadBytesPerPacket
- : sizeof(kPayloadData);
+ const size_t expected_bytes = GetParam().with_overhead
+ ? kPayloadSize + kRtpOverheadBytesPerPacket
+ : kPayloadSize;
EXPECT_CALL(feedback_observer_,
OnAddPacket(AllOf(
@@ -629,7 +631,7 @@
.Times(1);
EXPECT_EQ(rtp_sender()->ExpectedPerPacketOverhead(),
kRtpOverheadBytesPerPacket);
- SendGenericPacket();
+ SendPacket(clock_->TimeInMilliseconds(), kPayloadSize);
}
TEST_P(RtpSenderTestWithoutPacer, SendsPacketsWithTransportSequenceNumber) {
@@ -644,9 +646,8 @@
rtp_sender_context_ =
std::make_unique<RtpSenderContext>(config, &time_controller_);
- EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransportSequenceNumber,
- kTransportSequenceNumberExtensionId));
+ EXPECT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
EXPECT_CALL(send_packet_observer_,
OnSendPacket(kTransportSequenceNumber, _, _))
@@ -692,7 +693,7 @@
TEST_P(RtpSenderTestWithoutPacer,
SetsIncludedInFeedbackWhenTransportSequenceNumberExtensionIsRegistered) {
SetUpRtpSender(false, false, false);
- rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionTransportSequenceNumber,
+ rtp_sender()->RegisterRtpHeaderExtension(TransportSequenceNumber::kUri,
kTransportSequenceNumberExtensionId);
EXPECT_CALL(send_packet_observer_, OnSendPacket).Times(1);
SendGenericPacket();
@@ -703,7 +704,7 @@
RtpSenderTestWithoutPacer,
SetsIncludedInAllocationWhenTransportSequenceNumberExtensionIsRegistered) {
SetUpRtpSender(false, false, false);
- rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionTransportSequenceNumber,
+ rtp_sender()->RegisterRtpHeaderExtension(TransportSequenceNumber::kUri,
kTransportSequenceNumberExtensionId);
EXPECT_CALL(send_packet_observer_, OnSendPacket).Times(1);
SendGenericPacket();
@@ -806,9 +807,8 @@
}
TEST_P(RtpSenderTestWithoutPacer, OnSendPacketUpdated) {
- EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransportSequenceNumber,
- kTransportSequenceNumberExtensionId));
+ EXPECT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
EXPECT_CALL(send_packet_observer_,
OnSendPacket(kTransportSequenceNumber, _, _))
.Times(1);
@@ -832,9 +832,8 @@
rtp_sender()->SetSequenceNumber(kSeqNum);
rtp_sender_context_->packet_history_.SetStorePacketsStatus(
RtpPacketHistory::StorageMode::kStoreAndCull, 10);
- EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransportSequenceNumber,
- kTransportSequenceNumberExtensionId));
+ EXPECT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
EXPECT_CALL(send_packet_observer_,
OnSendPacket(kTransportSequenceNumber, _, _))
@@ -871,8 +870,8 @@
TEST_P(RtpSenderTest, WritesPacerExitToTimingExtension) {
rtp_sender_context_->packet_history_.SetStorePacketsStatus(
RtpPacketHistory::StorageMode::kStoreAndCull, 10);
- EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionVideoTiming, kVideoTimingExtensionId));
+ EXPECT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ VideoTimingExtension::kUri, kVideoTimingExtensionId));
int64_t capture_time_ms = clock_->TimeInMilliseconds();
auto packet = rtp_sender()->AllocatePacket();
packet->SetPayloadType(kPayload);
@@ -906,8 +905,8 @@
SetUpRtpSender(/*pacer=*/true, /*populate_network2=*/true, false);
rtp_sender_context_->packet_history_.SetStorePacketsStatus(
RtpPacketHistory::StorageMode::kStoreAndCull, 10);
- EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionVideoTiming, kVideoTimingExtensionId));
+ EXPECT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ VideoTimingExtension::kUri, kVideoTimingExtensionId));
int64_t capture_time_ms = clock_->TimeInMilliseconds();
auto packet = rtp_sender()->AllocatePacket();
packet->SetPayloadType(kPayload);
@@ -943,8 +942,8 @@
TEST_P(RtpSenderTest, WritesNetwork2ToTimingExtensionWithoutPacer) {
SetUpRtpSender(/*pacer=*/false, /*populate_network2=*/true, false);
- EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionVideoTiming, kVideoTimingExtensionId));
+ EXPECT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ VideoTimingExtension::kUri, kVideoTimingExtensionId));
auto packet = rtp_sender()->AllocatePacket();
packet->SetMarker(true);
packet->set_capture_time_ms(clock_->TimeInMilliseconds());
@@ -972,12 +971,10 @@
rtp_sender_context_->packet_history_.SetStorePacketsStatus(
RtpPacketHistory::StorageMode::kStoreAndCull, 10);
- EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransmissionTimeOffset,
- kTransmissionTimeOffsetExtensionId));
- EXPECT_EQ(0,
- rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId));
+ EXPECT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransmissionOffset::kUri, kTransmissionTimeOffsetExtensionId));
+ EXPECT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ AbsoluteSendTime::kUri, kAbsoluteSendTimeExtensionId));
int64_t capture_time_ms = clock_->TimeInMilliseconds();
auto packet =
BuildRtpPacket(kPayload, kMarkerBit, kTimestamp, capture_time_ms);
@@ -1017,12 +1014,10 @@
rtp_sender_context_->packet_history_.SetStorePacketsStatus(
RtpPacketHistory::StorageMode::kStoreAndCull, 10);
- EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransmissionTimeOffset,
- kTransmissionTimeOffsetExtensionId));
- EXPECT_EQ(0,
- rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId));
+ EXPECT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransmissionOffset::kUri, kTransmissionTimeOffsetExtensionId));
+ EXPECT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ AbsoluteSendTime::kUri, kAbsoluteSendTimeExtensionId));
int64_t capture_time_ms = clock_->TimeInMilliseconds();
auto packet =
BuildRtpPacket(kPayload, kMarkerBit, kTimestamp, capture_time_ms);
@@ -1088,13 +1083,11 @@
rtp_sender_context_->packet_history_.SetStorePacketsStatus(
RtpPacketHistory::StorageMode::kStoreAndCull, 10);
size_t rtp_header_len = kRtpHeaderSize;
- EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransmissionTimeOffset,
- kTransmissionTimeOffsetExtensionId));
+ EXPECT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransmissionOffset::kUri, kTransmissionTimeOffsetExtensionId));
rtp_header_len += 4; // 4 bytes extension.
- EXPECT_EQ(0,
- rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId));
+ EXPECT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ AbsoluteSendTime::kUri, kAbsoluteSendTimeExtensionId));
rtp_header_len += 4; // 4 bytes extension.
rtp_header_len += 4; // 4 extra bytes common to all extension headers.
@@ -1190,9 +1183,8 @@
}
TEST_P(RtpSenderTest, OnSendPacketUpdated) {
- EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransportSequenceNumber,
- kTransportSequenceNumberExtensionId));
+ EXPECT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
rtp_sender_context_->packet_history_.SetStorePacketsStatus(
RtpPacketHistory::StorageMode::kStoreAndCull, 10);
@@ -1214,9 +1206,8 @@
}
TEST_P(RtpSenderTest, OnSendPacketNotUpdatedForRetransmits) {
- EXPECT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransportSequenceNumber,
- kTransportSequenceNumberExtensionId));
+ EXPECT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
rtp_sender_context_->packet_history_.SetStorePacketsStatus(
RtpPacketHistory::StorageMode::kStoreAndCull, 10);
@@ -1968,12 +1959,12 @@
}
TEST_P(RtpSenderTestWithoutPacer, BytesReportedCorrectly) {
- // XXX const char* kPayloadName = "GENERIC";
const uint8_t kPayloadType = 127;
+ const size_t kPayloadSize = 1400;
rtp_sender()->SetRtxPayloadType(kPayloadType - 1, kPayloadType);
rtp_sender()->SetRtxStatus(kRtxRetransmitted | kRtxRedundantPayloads);
- SendGenericPacket();
+ SendPacket(clock_->TimeInMilliseconds(), kPayloadSize);
// Will send 2 full-size padding packets.
GenerateAndSendPadding(1);
GenerateAndSendPadding(1);
@@ -1984,7 +1975,7 @@
// Payload
EXPECT_GT(rtp_stats.first_packet_time_ms, -1);
- EXPECT_EQ(rtp_stats.transmitted.payload_bytes, sizeof(kPayloadData));
+ EXPECT_EQ(rtp_stats.transmitted.payload_bytes, kPayloadSize);
EXPECT_EQ(rtp_stats.transmitted.header_bytes, 12u);
EXPECT_EQ(rtp_stats.transmitted.padding_bytes, 0u);
EXPECT_EQ(rtx_stats.transmitted.payload_bytes, 0u);
@@ -2067,7 +2058,7 @@
// Base RTP overhead is 12B.
EXPECT_EQ(rtp_sender()->ExpectedPerPacketOverhead(), 12u);
- rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionTransmissionTimeOffset,
+ rtp_sender()->RegisterRtpHeaderExtension(TransmissionOffset::kUri,
kTransmissionTimeOffsetExtensionId);
// TransmissionTimeOffset extension has a size of 3B, but with the addition
@@ -2087,9 +2078,8 @@
// Base RTP overhead is 12B.
EXPECT_EQ(rtp_sender()->ExpectedPerPacketOverhead(), 12u);
- rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionMid, kMidExtensionId);
- rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionRtpStreamId,
- kRidExtensionId);
+ rtp_sender()->RegisterRtpHeaderExtension(RtpMid::kUri, kMidExtensionId);
+ rtp_sender()->RegisterRtpHeaderExtension(RtpStreamId::kUri, kRidExtensionId);
// Counted only if set.
EXPECT_EQ(rtp_sender()->ExpectedPerPacketOverhead(), 12u);
@@ -2115,14 +2105,16 @@
// Base RTP overhead is 12B.
EXPECT_EQ(rtp_sender()->ExpectedPerPacketOverhead(), 12u);
- rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionInbandComfortNoise, 1);
- rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionAbsoluteCaptureTime, 2);
- rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionVideoRotation, 3);
- rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionPlayoutDelay, 4);
- rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionVideoContentType, 5);
- rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionVideoTiming, 6);
- rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionRepairedRtpStreamId, 7);
- rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionColorSpace, 8);
+ rtp_sender()->RegisterRtpHeaderExtension(InbandComfortNoiseExtension::kUri,
+ 1);
+ rtp_sender()->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::kUri,
+ 2);
+ rtp_sender()->RegisterRtpHeaderExtension(VideoOrientation::kUri, 3);
+ rtp_sender()->RegisterRtpHeaderExtension(PlayoutDelayLimits::kUri, 4);
+ rtp_sender()->RegisterRtpHeaderExtension(VideoContentTypeExtension::kUri, 5);
+ rtp_sender()->RegisterRtpHeaderExtension(VideoTimingExtension::kUri, 6);
+ rtp_sender()->RegisterRtpHeaderExtension(RepairedRtpStreamId::kUri, 7);
+ rtp_sender()->RegisterRtpHeaderExtension(ColorSpaceExtension::kUri, 8);
// Still only 12B counted since can't count on above being sent.
EXPECT_EQ(rtp_sender()->ExpectedPerPacketOverhead(), 12u);
@@ -2262,16 +2254,12 @@
}
TEST_P(RtpSenderTest, SendPacketUpdatesExtensions) {
- ASSERT_EQ(rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransmissionTimeOffset,
- kTransmissionTimeOffsetExtensionId),
- 0);
- ASSERT_EQ(rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId),
- 0);
- ASSERT_EQ(rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionVideoTiming,
- kVideoTimingExtensionId),
- 0);
+ ASSERT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransmissionOffset::kUri, kTransmissionTimeOffsetExtensionId));
+ ASSERT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ AbsoluteSendTime::kUri, kAbsoluteSendTimeExtensionId));
+ ASSERT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ VideoTimingExtension::kUri, kVideoTimingExtensionId));
std::unique_ptr<RtpPacketToSend> packet =
BuildRtpPacket(kPayload, true, 0, clock_->TimeInMilliseconds());
@@ -2297,10 +2285,8 @@
TEST_P(RtpSenderTest, SendPacketSetsPacketOptions) {
const uint16_t kPacketId = 42;
- ASSERT_EQ(rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransportSequenceNumber,
- kTransportSequenceNumberExtensionId),
- 0);
+ ASSERT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
std::unique_ptr<RtpPacketToSend> packet =
BuildRtpPacket(kPayload, true, 0, clock_->TimeInMilliseconds());
packet->SetExtension<TransportSequenceNumber>(kPacketId);
@@ -2338,9 +2324,8 @@
config.send_packet_observer = &send_packet_observer_;
rtp_sender_context_ =
std::make_unique<RtpSenderContext>(config, &time_controller_);
- ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransportSequenceNumber,
- kTransportSequenceNumberExtensionId));
+ ASSERT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
const int64_t capture_time_ms = clock_->TimeInMilliseconds();
@@ -2402,15 +2387,12 @@
rtp_sender_context_->packet_history_.SetStorePacketsStatus(
RtpPacketHistory::StorageMode::kStoreAndCull, 1);
- ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransmissionTimeOffset,
- kTransmissionTimeOffsetExtensionId));
- ASSERT_EQ(0,
- rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId));
- ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransportSequenceNumber,
- kTransportSequenceNumberExtensionId));
+ ASSERT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransmissionOffset::kUri, kTransmissionTimeOffsetExtensionId));
+ ASSERT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ AbsoluteSendTime::kUri, kAbsoluteSendTimeExtensionId));
+ ASSERT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
// Send a payload packet first, to enable padding and populate the packet
// history.
@@ -2476,9 +2458,8 @@
rtp_sender_context_->packet_history_.SetStorePacketsStatus(
RtpPacketHistory::StorageMode::kStoreAndCull, 1);
- ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransportSequenceNumber,
- kTransportSequenceNumberExtensionId));
+ ASSERT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
const size_t kPayloadPacketSize = kMinPaddingSize;
std::unique_ptr<RtpPacketToSend> packet =
@@ -2530,9 +2511,8 @@
rtp_sender_context_->packet_history_.SetStorePacketsStatus(
RtpPacketHistory::StorageMode::kStoreAndCull, 1);
- ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransportSequenceNumber,
- kTransportSequenceNumberExtensionId));
+ ASSERT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
// Send a dummy video packet so it ends up in the packet history.
const size_t kPayloadPacketSize = 1234u;
@@ -2567,15 +2547,12 @@
TEST_P(RtpSenderTest, GeneratePaddingCreatesPurePaddingWithoutRtx) {
rtp_sender_context_->packet_history_.SetStorePacketsStatus(
RtpPacketHistory::StorageMode::kStoreAndCull, 1);
- ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransmissionTimeOffset,
- kTransmissionTimeOffsetExtensionId));
- ASSERT_EQ(0,
- rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId));
- ASSERT_EQ(0, rtp_sender()->RegisterRtpHeaderExtension(
- kRtpExtensionTransportSequenceNumber,
- kTransportSequenceNumberExtensionId));
+ ASSERT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransmissionOffset::kUri, kTransmissionTimeOffsetExtensionId));
+ ASSERT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ AbsoluteSendTime::kUri, kAbsoluteSendTimeExtensionId));
+ ASSERT_TRUE(rtp_sender()->RegisterRtpHeaderExtension(
+ TransportSequenceNumber::kUri, kTransportSequenceNumberExtensionId));
const size_t kPayloadPacketSize = 1234;
// Send a dummy video packet so it ends up in the packet history. Since we
@@ -2626,10 +2603,9 @@
TEST_P(RtpSenderTest, SupportsPadding) {
bool kSendingMediaStats[] = {true, false};
bool kEnableRedundantPayloads[] = {true, false};
- RTPExtensionType kBweExtensionTypes[] = {
- kRtpExtensionTransportSequenceNumber,
- kRtpExtensionTransportSequenceNumber02, kRtpExtensionAbsoluteSendTime,
- kRtpExtensionTransmissionTimeOffset};
+ absl::string_view kBweExtensionUris[] = {
+ TransportSequenceNumber::kUri, TransportSequenceNumberV2::kUri,
+ AbsoluteSendTime::kUri, TransmissionOffset::kUri};
const int kExtensionsId = 7;
for (bool sending_media : kSendingMediaStats) {
@@ -2641,9 +2617,9 @@
}
rtp_sender()->SetRtxStatus(rtx_mode);
- for (auto extension_type : kBweExtensionTypes) {
+ for (auto extension_uri : kBweExtensionUris) {
EXPECT_FALSE(rtp_sender()->SupportsPadding());
- rtp_sender()->RegisterRtpHeaderExtension(extension_type, kExtensionsId);
+ rtp_sender()->RegisterRtpHeaderExtension(extension_uri, kExtensionsId);
if (!sending_media) {
EXPECT_FALSE(rtp_sender()->SupportsPadding());
} else {
@@ -2654,7 +2630,7 @@
EXPECT_FALSE(rtp_sender()->SupportsRtxPayloadPadding());
}
}
- rtp_sender()->DeregisterRtpHeaderExtension(extension_type);
+ rtp_sender()->DeregisterRtpHeaderExtension(extension_uri);
EXPECT_FALSE(rtp_sender()->SupportsPadding());
}
}
@@ -2662,7 +2638,7 @@
}
TEST_P(RtpSenderTest, SetsCaptureTimeAndPopulatesTransmissionOffset) {
- rtp_sender()->RegisterRtpHeaderExtension(kRtpExtensionTransmissionTimeOffset,
+ rtp_sender()->RegisterRtpHeaderExtension(TransmissionOffset::kUri,
kTransmissionTimeOffsetExtensionId);
rtp_sender()->SetSendingMediaStatus(true);
diff --git a/modules/rtp_rtcp/source/rtp_sender_video.cc b/modules/rtp_rtcp/source/rtp_sender_video.cc
index 934be82..602cf9d 100644
--- a/modules/rtp_rtcp/source/rtp_sender_video.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_video.cc
@@ -648,8 +648,6 @@
if (!packetizer->NextPacket(packet.get()))
return false;
RTC_DCHECK_LE(packet->payload_size(), expected_payload_capacity);
- if (!rtp_sender_->AssignSequenceNumber(packet.get()))
- return false;
packet->set_allow_retransmission(allow_retransmission);
packet->set_is_key_frame(video_header.frame_type ==
@@ -670,7 +668,7 @@
red_packet->SetPayloadType(*red_payload_type_);
red_packet->set_is_red(true);
- // Send |red_packet| instead of |packet| for allocated sequence number.
+ // Append |red_packet| instead of |packet| to output.
red_packet->set_packet_type(RtpPacketMediaType::kVideo);
red_packet->set_allow_retransmission(packet->allow_retransmission());
rtp_packets.emplace_back(std::move(red_packet));
@@ -691,6 +689,11 @@
}
}
+ if (!rtp_sender_->AssignSequenceNumbersAndStoreLastPacketState(rtp_packets)) {
+ // Media not being sent.
+ return false;
+ }
+
LogAndSendToNetwork(std::move(rtp_packets), payload.size());
// Update details about the last sent frame.
diff --git a/modules/rtp_rtcp/source/rtp_sender_video.h b/modules/rtp_rtcp/source/rtp_sender_video.h
index 6e46990..06f3d20 100644
--- a/modules/rtp_rtcp/source/rtp_sender_video.h
+++ b/modules/rtp_rtcp/source/rtp_sender_video.h
@@ -20,6 +20,7 @@
#include "api/array_view.h"
#include "api/frame_transformer_interface.h"
#include "api/scoped_refptr.h"
+#include "api/sequence_checker.h"
#include "api/task_queue/task_queue_base.h"
#include "api/transport/rtp/dependency_descriptor.h"
#include "api/video/video_codec_type.h"
@@ -37,7 +38,6 @@
#include "rtc_base/race_checker.h"
#include "rtc_base/rate_statistics.h"
#include "rtc_base/synchronization/mutex.h"
-#include "rtc_base/synchronization/sequence_checker.h"
#include "rtc_base/thread_annotations.h"
namespace webrtc {
diff --git a/modules/rtp_rtcp/source/ulpfec_receiver_impl.cc b/modules/rtp_rtcp/source/ulpfec_receiver_impl.cc
index fee0b9c..16b87ba 100644
--- a/modules/rtp_rtcp/source/ulpfec_receiver_impl.cc
+++ b/modules/rtp_rtcp/source/ulpfec_receiver_impl.cc
@@ -37,12 +37,13 @@
fec_(ForwardErrorCorrection::CreateUlpfec(ssrc_)) {}
UlpfecReceiverImpl::~UlpfecReceiverImpl() {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
received_packets_.clear();
fec_->ResetState(&recovered_packets_);
}
FecPacketCounter UlpfecReceiverImpl::GetPacketCounter() const {
- MutexLock lock(&mutex_);
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
return packet_counter_;
}
@@ -77,6 +78,10 @@
bool UlpfecReceiverImpl::AddReceivedRedPacket(
const RtpPacketReceived& rtp_packet,
uint8_t ulpfec_payload_type) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ // TODO(bugs.webrtc.org/11993): We get here via Call::DeliverRtp, so should be
+ // moved to the network thread.
+
if (rtp_packet.Ssrc() != ssrc_) {
RTC_LOG(LS_WARNING)
<< "Received RED packet with different SSRC than expected; dropping.";
@@ -87,7 +92,6 @@
"packet size; dropping.";
return false;
}
- MutexLock lock(&mutex_);
static constexpr uint8_t kRedHeaderLength = 1;
@@ -151,7 +155,7 @@
// TODO(nisse): Drop always-zero return value.
int32_t UlpfecReceiverImpl::ProcessReceivedFec() {
- mutex_.Lock();
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
// If we iterate over |received_packets_| and it contains a packet that cause
// us to recurse back to this function (for example a RED packet encapsulating
@@ -168,10 +172,8 @@
// Send received media packet to VCM.
if (!received_packet->is_fec) {
ForwardErrorCorrection::Packet* packet = received_packet->pkt;
- mutex_.Unlock();
recovered_packet_callback_->OnRecoveredPacket(packet->data.data(),
packet->data.size());
- mutex_.Lock();
// Create a packet with the buffer to modify it.
RtpPacketReceived rtp_packet;
const uint8_t* const original_data = packet->data.cdata();
@@ -208,13 +210,10 @@
// Set this flag first; in case the recovered packet carries a RED
// header, OnRecoveredPacket will recurse back here.
recovered_packet->returned = true;
- mutex_.Unlock();
recovered_packet_callback_->OnRecoveredPacket(packet->data.data(),
packet->data.size());
- mutex_.Lock();
}
- mutex_.Unlock();
return 0;
}
diff --git a/modules/rtp_rtcp/source/ulpfec_receiver_impl.h b/modules/rtp_rtcp/source/ulpfec_receiver_impl.h
index 2bed042..f59251f 100644
--- a/modules/rtp_rtcp/source/ulpfec_receiver_impl.h
+++ b/modules/rtp_rtcp/source/ulpfec_receiver_impl.h
@@ -17,12 +17,13 @@
#include <memory>
#include <vector>
+#include "api/sequence_checker.h"
#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/include/ulpfec_receiver.h"
#include "modules/rtp_rtcp/source/forward_error_correction.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
-#include "rtc_base/synchronization/mutex.h"
+#include "rtc_base/system/no_unique_address.h"
namespace webrtc {
@@ -44,17 +45,18 @@
const uint32_t ssrc_;
const RtpHeaderExtensionMap extensions_;
- mutable Mutex mutex_;
- RecoveredPacketReceiver* recovered_packet_callback_;
- std::unique_ptr<ForwardErrorCorrection> fec_;
+ RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_;
+ RecoveredPacketReceiver* const recovered_packet_callback_;
+ const std::unique_ptr<ForwardErrorCorrection> fec_;
// TODO(nisse): The AddReceivedRedPacket method adds one or two packets to
// this list at a time, after which it is emptied by ProcessReceivedFec. It
// will make things simpler to merge AddReceivedRedPacket and
// ProcessReceivedFec into a single method, and we can then delete this list.
std::vector<std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>>
- received_packets_;
- ForwardErrorCorrection::RecoveredPacketList recovered_packets_;
- FecPacketCounter packet_counter_;
+ received_packets_ RTC_GUARDED_BY(&sequence_checker_);
+ ForwardErrorCorrection::RecoveredPacketList recovered_packets_
+ RTC_GUARDED_BY(&sequence_checker_);
+ FecPacketCounter packet_counter_ RTC_GUARDED_BY(&sequence_checker_);
};
} // namespace webrtc
diff --git a/modules/video_coding/codecs/vp8/include/vp8.h b/modules/video_coding/codecs/vp8/include/vp8.h
index 44efbee..d05c3a6 100644
--- a/modules/video_coding/codecs/vp8/include/vp8.h
+++ b/modules/video_coding/codecs/vp8/include/vp8.h
@@ -14,10 +14,10 @@
#include <memory>
#include <vector>
+#include "absl/base/attributes.h"
#include "api/video_codecs/video_encoder.h"
#include "api/video_codecs/vp8_frame_buffer_controller.h"
#include "modules/video_coding/include/video_codec_interface.h"
-#include "rtc_base/deprecation.h"
namespace webrtc {
@@ -40,7 +40,8 @@
static std::unique_ptr<VideoEncoder> Create();
static std::unique_ptr<VideoEncoder> Create(Settings settings);
- RTC_DEPRECATED static std::unique_ptr<VideoEncoder> Create(
+ ABSL_DEPRECATED("")
+ static std::unique_ptr<VideoEncoder> Create(
std::unique_ptr<Vp8FrameBufferControllerFactory>
frame_buffer_controller_factory);
};
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn
index 7181c23..32ef93e 100644
--- a/rtc_base/BUILD.gn
+++ b/rtc_base/BUILD.gn
@@ -8,6 +8,7 @@
import("//build/config/crypto.gni")
import("//build/config/ui.gni")
+import("//third_party/google_benchmark/buildconfig.gni")
import("../webrtc.gni")
if (is_android) {
@@ -15,7 +16,7 @@
import("//build/config/android/rules.gni")
}
-config("rtc_base_chromium_config") {
+config("threading_chromium_config") {
defines = [ "NO_MAIN_THREAD_WRAPPING" ]
}
@@ -74,18 +75,20 @@
":type_traits",
"../api:array_view",
"../api:scoped_refptr",
+ "../api:sequence_checker",
"synchronization:mutex",
"system:arch",
"system:no_unique_address",
"system:rtc_export",
- "system:unused",
"third_party/base64",
]
- absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/base:core_headers",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
public_deps = [] # no-presubmit-check TODO(webrtc:8603)
sources = [
- "bind.h",
"bit_buffer.cc",
"bit_buffer.h",
"buffer.h",
@@ -126,8 +129,6 @@
if (is_win) {
sources += [
- "win/create_direct3d_device.cc",
- "win/create_direct3d_device.h",
"win/get_activation_factory.cc",
"win/get_activation_factory.h",
"win/hstring.cc",
@@ -140,6 +141,14 @@
data_deps = [ "//build/win:runtime_libs" ]
}
+ # These files add a dependency on the Win10 SDK v10.0.10240.
+ if (rtc_enable_win_wgc) {
+ sources += [
+ "win/create_direct3d_device.cc",
+ "win/create_direct3d_device.h",
+ ]
+ }
+
if (is_nacl) {
public_deps += # no-presubmit-check TODO(webrtc:8603)
[ "//native_client_sdk/src/libraries/nacl_io" ]
@@ -160,9 +169,8 @@
":rtc_event",
":safe_conversions",
":stringutils",
- ":thread_checker",
":timeutils",
- "synchronization:sequence_checker",
+ "../api:sequence_checker",
]
}
@@ -215,8 +223,8 @@
":rtc_task_queue_libevent",
":rtc_task_queue_stdlib",
":rtc_task_queue_win",
+ "../api:sequence_checker",
"synchronization:mutex",
- "synchronization:sequence_checker",
]
sources = [
"platform_thread.cc",
@@ -228,8 +236,8 @@
":macromagic",
":platform_thread_types",
":rtc_event",
- ":thread_checker",
":timeutils",
+ "../api:sequence_checker",
]
absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
}
@@ -299,24 +307,12 @@
frameworks = [ "Foundation.framework" ]
}
- # logging.h needs the deprecation header while downstream projects are
- # removing code that depends on logging implementation details.
- deps += [ ":deprecation" ]
-
if (is_android) {
libs += [ "log" ]
}
}
}
-rtc_source_set("thread_checker") {
- sources = [ "thread_checker.h" ]
- deps = [
- ":deprecation",
- "synchronization:sequence_checker",
- ]
-}
-
rtc_source_set("atomicops") {
sources = [ "atomic_ops.h" ]
}
@@ -400,6 +396,8 @@
rtc_library("timeutils") {
visibility = [ "*" ]
sources = [
+ "system_time.cc",
+ "system_time.h",
"time_utils.cc",
"time_utils.h",
]
@@ -409,6 +407,10 @@
":stringutils",
"system:rtc_export",
]
+ if (rtc_exclude_system_time) {
+ defines = [ "WEBRTC_EXCLUDE_SYSTEM_TIME" ]
+ }
+
libs = []
if (is_win) {
libs += [ "winmm.lib" ]
@@ -455,10 +457,6 @@
sources = [ "type_traits.h" ]
}
-rtc_source_set("deprecation") {
- sources = [ "deprecation.h" ]
-}
-
rtc_library("rtc_task_queue") {
visibility = [ "*" ]
sources = [
@@ -485,7 +483,7 @@
":macromagic",
":refcount",
"../api:scoped_refptr",
- "synchronization:sequence_checker",
+ "../api:sequence_checker",
"system:no_unique_address",
]
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
@@ -589,7 +587,7 @@
deps = [
":refcount",
"../api:scoped_refptr",
- "synchronization:sequence_checker",
+ "../api:sequence_checker",
"system:no_unique_address",
]
}
@@ -667,140 +665,213 @@
}
}
-rtc_source_set("async_resolver") {
- # TODO(bugs.webrtc.org/9987): This build target will soon contain
- # async_resolver source files (see
- # https://webrtc-review.googlesource.com/c/src/+/196903).
- sources = [ "async_resolver.h" ]
+rtc_library("net_helpers") {
+ sources = [
+ "net_helpers.cc",
+ "net_helpers.h",
+ ]
+ deps = []
+ if (is_android) {
+ deps += [ ":ifaddrs_android" ]
+ }
+ if (is_win) {
+ deps += [ ":win32" ]
+ }
}
-rtc_source_set("net_helpers") {
- # TODO(bugs.webrtc.org/9987): This build target will soon contain
- # the following files:
- # sources = [
- # "net_helpers.cc",
- # "net_helpers.h",
- # ]
-}
-
-rtc_source_set("async_resolver_interface") {
+rtc_library("async_resolver_interface") {
visibility = [ "*" ]
- # TODO(bugs.webrtc.org/9987): This build target will soon contain
- # the following files:
- # sources = [
- # "async_resolver_interface.cc",
- # "async_resolver_interface.h",
- # ]
+ sources = [
+ "async_resolver_interface.cc",
+ "async_resolver_interface.h",
+ ]
+ deps = [
+ ":socket_address",
+ "system:rtc_export",
+ "third_party/sigslot",
+ ]
}
-rtc_source_set("ip_address") {
+rtc_library("ip_address") {
visibility = [ "*" ]
- # TODO(bugs.webrtc.org/9987): This build target will soon contain
- # the following files:
- # sources = [
- # "ip_address.cc",
- # "ip_address.h",
- # ]
+ sources = [
+ "ip_address.cc",
+ "ip_address.h",
+ ]
+ deps = [
+ ":net_helpers",
+ ":rtc_base_approved",
+ ":stringutils",
+ "system:rtc_export",
+ ]
+ if (is_win) {
+ deps += [ ":win32" ]
+ }
}
-rtc_source_set("socket_address") {
+rtc_library("socket_address") {
visibility = [ "*" ]
- # TODO(bugs.webrtc.org/9987): This build target will soon contain
- # the following files:
- # sources = [
- # "socket_address.cc",
- # "socket_address.h",
- # ]
+ sources = [
+ "socket_address.cc",
+ "socket_address.h",
+ ]
+ deps = [
+ ":checks",
+ ":ip_address",
+ ":logging",
+ ":net_helpers",
+ ":rtc_base_approved",
+ ":safe_conversions",
+ ":stringutils",
+ "system:rtc_export",
+ ]
+ if (is_win) {
+ deps += [ ":win32" ]
+ }
}
-rtc_source_set("null_socket_server") {
- # TODO(bugs.webrtc.org/9987): This build target will soon contain
- # the following files:
- # sources = [
- # "null_socket_server.cc",
- # "null_socket_server.h",
- # ]
+rtc_library("null_socket_server") {
+ sources = [
+ "null_socket_server.cc",
+ "null_socket_server.h",
+ ]
+ deps = [
+ ":async_socket",
+ ":checks",
+ ":rtc_event",
+ ":socket",
+ ":socket_server",
+ "system:rtc_export",
+ ]
}
rtc_source_set("socket_server") {
- # TODO(bugs.webrtc.org/9987): This build target will soon contain
- # the following files:
- # sources = [
- # "socket_server.h",
- # ]
+ sources = [ "socket_server.h" ]
+ deps = [ ":socket_factory" ]
}
-rtc_source_set("threading") {
+rtc_library("threading") {
visibility = [ "*" ]
- # TODO(bugs.webrtc.org/9987): This build target will soon contain
- # the following files:
- # sources = [
- # "asyncresolver.cc",
- # "asyncresolver.h",
- # "defaultsocketserver.cc",
- # "defaultsocketserver.h",
- # "message_handler.cc",
- # "message_handler.h",
- # "network_monitor.cc",
- # "network_monitor.h",
- # "network_monitor_factory.cc",
- # "network_monitor_factory.h",
- # "physical_socket_server.cc",
- # "physical_socket_server.h",
- # "signal_thread.cc",
- # "signal_thread.h",
- # "thread.cc",
- # "thread.h",
- # ]
+
+ if (build_with_chromium) {
+ public_configs = [ ":threading_chromium_config" ]
+ }
+
+ sources = [
+ "async_resolver.cc",
+ "async_resolver.h",
+ "internal/default_socket_server.cc",
+ "internal/default_socket_server.h",
+ "message_handler.cc",
+ "message_handler.h",
+ "network_monitor.cc",
+ "network_monitor.h",
+ "network_monitor_factory.cc",
+ "network_monitor_factory.h",
+ "physical_socket_server.cc",
+ "physical_socket_server.h",
+ "thread.cc",
+ "thread.h",
+ "thread_message.h",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container" ]
+ deps = [
+ ":async_resolver_interface",
+ ":atomicops",
+ ":checks",
+ ":criticalsection",
+ ":ip_address",
+ ":logging",
+ ":macromagic",
+ ":network_constants",
+ ":null_socket_server",
+ ":platform_thread_types",
+ ":rtc_base_approved",
+ ":rtc_event",
+ ":rtc_task_queue",
+ ":socket_address",
+ ":socket_server",
+ ":timeutils",
+ "../api:function_view",
+ "../api:scoped_refptr",
+ "../api:sequence_checker",
+ "../api/task_queue",
+ "synchronization:mutex",
+ "system:no_unique_address",
+ "system:rtc_export",
+ "task_utils:pending_task_safety_flag",
+ "task_utils:to_queued_task",
+ "third_party/sigslot",
+ ]
+ if (is_android) {
+ deps += [ ":ifaddrs_android" ]
+ }
+ if (is_win) {
+ deps += [ ":win32" ]
+ }
+ if (is_mac || is_ios) {
+ deps += [ "system:cocoa_threading" ]
+ }
}
rtc_source_set("socket_factory") {
- # TODO(bugs.webrtc.org/9987): This build target will soon contain
- # the following files:
- # sources = [
- # "socket_factory.h",
- # ]
+ sources = [ "socket_factory.h" ]
+ deps = [
+ ":async_socket",
+ ":socket",
+ ]
}
-rtc_source_set("async_socket") {
- # TODO(bugs.webrtc.org/9987): This build target will soon contain
- # the following files:
- # sources = [
- # "async_socket.cc",
- # "async_socket.h",
- # ]
+rtc_library("async_socket") {
+ sources = [
+ "async_socket.cc",
+ "async_socket.h",
+ ]
+ deps = [
+ ":checks",
+ ":socket",
+ ":socket_address",
+ "third_party/sigslot",
+ ]
}
-rtc_source_set("socket") {
- # TODO(bugs.webrtc.org/9987): This build target will soon contain
- # the following files:
- # sources = [
- # "socket.cc",
- # "socket.h",
- # ]
+rtc_library("socket") {
+ sources = [
+ "socket.cc",
+ "socket.h",
+ ]
+ deps = [
+ ":macromagic",
+ ":socket_address",
+ ]
+ if (is_win) {
+ deps += [ ":win32" ]
+ }
}
rtc_source_set("network_constants") {
- # TODO(bugs.webrtc.org/9987): This build target will soon contain
- # the following files:
- # sources = [
- # "network_constants.h",
- # ]
+ sources = [
+ "network_constants.cc",
+ "network_constants.h",
+ ]
+ deps = [ ":checks" ]
}
if (is_android) {
- rtc_source_set("ifaddrs_android") {
- # TODO(bugs.webrtc.org/9987): This build target will soon contain
- # the following files:
- # sources = [
- # "ifaddrs_android.cc",
- # "ifaddrs_android.h",
- # ]
+ rtc_library("ifaddrs_android") {
+ sources = [
+ "ifaddrs_android.cc",
+ "ifaddrs_android.h",
+ ]
+ libs = [
+ "log",
+ "GLESv2",
+ ]
}
}
if (is_win) {
- rtc_source_set("win32") {
+ rtc_library("win32") {
sources = [
"win32.cc",
"win32.h",
@@ -831,19 +902,28 @@
libs = []
defines = []
deps = [
+ ":async_resolver_interface",
+ ":async_socket",
":checks",
- ":deprecation",
+ ":ip_address",
+ ":network_constants",
+ ":null_socket_server",
":rtc_task_queue",
+ ":socket",
+ ":socket_address",
+ ":socket_factory",
+ ":socket_server",
":stringutils",
+ ":threading",
"../api:array_view",
"../api:function_view",
"../api:scoped_refptr",
+ "../api:sequence_checker",
"../api/numerics",
"../api/task_queue",
"../system_wrappers:field_trial",
"network:sent_packet",
"synchronization:mutex",
- "synchronization:sequence_checker",
"system:file_wrapper",
"system:inline",
"system:no_unique_address",
@@ -870,10 +950,6 @@
"async_invoker_inl.h",
"async_packet_socket.cc",
"async_packet_socket.h",
- "async_resolver_interface.cc",
- "async_resolver_interface.h",
- "async_socket.cc",
- "async_socket.h",
"async_tcp_socket.cc",
"async_tcp_socket.h",
"async_udp_socket.cc",
@@ -884,8 +960,6 @@
"crypt_string.h",
"data_rate_limiter.cc",
"data_rate_limiter.h",
- "deprecated/signal_thread.cc",
- "deprecated/signal_thread.h",
"dscp.h",
"file_rotating_stream.cc",
"file_rotating_stream.h",
@@ -893,30 +967,15 @@
"helpers.h",
"http_common.cc",
"http_common.h",
- "ip_address.cc",
- "ip_address.h",
- "keep_ref_until_done.h",
"mdns_responder_interface.h",
"message_digest.cc",
"message_digest.h",
- "message_handler.cc",
- "message_handler.h",
"net_helper.cc",
"net_helper.h",
- "net_helpers.cc",
- "net_helpers.h",
"network.cc",
"network.h",
- "network_constants.cc",
- "network_constants.h",
- "network_monitor.cc",
- "network_monitor.h",
- "network_monitor_factory.cc",
- "network_monitor_factory.h",
"network_route.cc",
"network_route.h",
- "null_socket_server.cc",
- "null_socket_server.h",
"openssl.h",
"openssl_adapter.cc",
"openssl_adapter.h",
@@ -930,26 +989,17 @@
"openssl_stream_adapter.h",
"openssl_utility.cc",
"openssl_utility.h",
- "physical_socket_server.cc",
- "physical_socket_server.h",
"proxy_info.cc",
"proxy_info.h",
"rtc_certificate.cc",
"rtc_certificate.h",
"rtc_certificate_generator.cc",
"rtc_certificate_generator.h",
- "signal_thread.h",
"sigslot_repeater.h",
- "socket.cc",
- "socket.h",
"socket_adapters.cc",
"socket_adapters.h",
- "socket_address.cc",
- "socket_address.h",
"socket_address_pair.cc",
"socket_address_pair.h",
- "socket_factory.h",
- "socket_server.h",
"ssl_adapter.cc",
"ssl_adapter.h",
"ssl_certificate.cc",
@@ -962,9 +1012,6 @@
"ssl_stream_adapter.h",
"stream.cc",
"stream.h",
- "thread.cc",
- "thread.h",
- "thread_message.h",
"unique_id_generator.cc",
"unique_id_generator.h",
]
@@ -988,10 +1035,8 @@
if (build_with_chromium) {
include_dirs = [ "../../boringssl/src/include" ]
- public_configs += [ ":rtc_base_chromium_config" ]
} else {
sources += [
- "callback.h",
"log_sinks.cc",
"log_sinks.h",
"rolling_accumulator.h",
@@ -1018,20 +1063,11 @@
}
if (is_android) {
- sources += [
- "ifaddrs_android.cc",
- "ifaddrs_android.h",
- ]
-
- libs += [
- "log",
- "GLESv2",
- ]
+ deps += [ ":ifaddrs_android" ]
}
if (is_ios || is_mac) {
sources += [ "mac_ifaddrs_converter.cc" ]
- deps += [ "system:cocoa_threading" ]
}
if (is_linux || is_chromeos) {
@@ -1086,6 +1122,7 @@
":rtc_base",
":rtc_base_tests_utils",
":stringutils",
+ ":threading",
"../test:test_support",
]
absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
@@ -1102,6 +1139,7 @@
":gunit_helpers",
":rtc_base",
":rtc_base_tests_utils",
+ ":threading",
":timeutils",
"synchronization:mutex",
]
@@ -1160,8 +1198,15 @@
"virtual_socket_server.h",
]
deps = [
+ ":async_socket",
":checks",
+ ":ip_address",
":rtc_base",
+ ":socket",
+ ":socket_address",
+ ":socket_factory",
+ ":socket_server",
+ ":threading",
"../api/units:time_delta",
"../api/units:timestamp",
"memory:fifo_buffer",
@@ -1216,138 +1261,6 @@
]
}
- rtc_library("rtc_base_nonparallel_tests") {
- testonly = true
-
- sources = [
- "cpu_time_unittest.cc",
- "file_rotating_stream_unittest.cc",
- "null_socket_server_unittest.cc",
- "physical_socket_server_unittest.cc",
- "socket_address_unittest.cc",
- "socket_unittest.cc",
- "socket_unittest.h",
- ]
- deps = [
- ":checks",
- ":gunit_helpers",
- ":rtc_base",
- ":rtc_base_tests_utils",
- ":testclient",
- "../system_wrappers",
- "../test:fileutils",
- "../test:test_main",
- "../test:test_support",
- "third_party/sigslot",
- "//testing/gtest",
- ]
- absl_deps = [ "//third_party/abseil-cpp/absl/memory" ]
- if (is_win) {
- sources += [ "win32_socket_server_unittest.cc" ]
- }
- }
-
- rtc_library("rtc_base_approved_unittests") {
- testonly = true
- sources = [
- "atomic_ops_unittest.cc",
- "base64_unittest.cc",
- "bind_unittest.cc",
- "bit_buffer_unittest.cc",
- "bounded_inline_vector_unittest.cc",
- "buffer_queue_unittest.cc",
- "buffer_unittest.cc",
- "byte_buffer_unittest.cc",
- "byte_order_unittest.cc",
- "checks_unittest.cc",
- "copy_on_write_buffer_unittest.cc",
- "deprecated/recursive_critical_section_unittest.cc",
- "event_tracer_unittest.cc",
- "event_unittest.cc",
- "logging_unittest.cc",
- "numerics/divide_round_unittest.cc",
- "numerics/histogram_percentile_counter_unittest.cc",
- "numerics/mod_ops_unittest.cc",
- "numerics/moving_max_counter_unittest.cc",
- "numerics/safe_compare_unittest.cc",
- "numerics/safe_minmax_unittest.cc",
- "numerics/sample_counter_unittest.cc",
- "one_time_event_unittest.cc",
- "platform_thread_unittest.cc",
- "random_unittest.cc",
- "rate_limiter_unittest.cc",
- "rate_statistics_unittest.cc",
- "rate_tracker_unittest.cc",
- "ref_counted_object_unittest.cc",
- "sanitizer_unittest.cc",
- "string_encode_unittest.cc",
- "string_to_number_unittest.cc",
- "string_utils_unittest.cc",
- "strings/string_builder_unittest.cc",
- "strings/string_format_unittest.cc",
- "swap_queue_unittest.cc",
- "thread_annotations_unittest.cc",
- "thread_checker_unittest.cc",
- "time_utils_unittest.cc",
- "timestamp_aligner_unittest.cc",
- "virtual_socket_unittest.cc",
- "zero_memory_unittest.cc",
- ]
- if (is_win) {
- sources += [ "win/windows_version_unittest.cc" ]
- }
- deps = [
- ":bounded_inline_vector",
- ":checks",
- ":divide_round",
- ":gunit_helpers",
- ":rate_limiter",
- ":rtc_base",
- ":rtc_base_approved",
- ":rtc_base_tests_utils",
- ":rtc_numerics",
- ":rtc_task_queue",
- ":safe_compare",
- ":safe_minmax",
- ":sanitizer",
- ":stringutils",
- ":testclient",
- "../api:array_view",
- "../api:scoped_refptr",
- "../api/numerics",
- "../api/units:time_delta",
- "../system_wrappers",
- "../test:fileutils",
- "../test:test_main",
- "../test:test_support",
- "memory:unittests",
- "synchronization:mutex",
- "task_utils:to_queued_task",
- "third_party/base64",
- "third_party/sigslot",
- ]
- absl_deps = [
- "//third_party/abseil-cpp/absl/base:core_headers",
- "//third_party/abseil-cpp/absl/memory",
- ]
- }
-
- rtc_library("rtc_task_queue_unittests") {
- testonly = true
-
- sources = [ "task_queue_unittest.cc" ]
- deps = [
- ":gunit_helpers",
- ":rtc_base_approved",
- ":rtc_base_tests_utils",
- ":rtc_task_queue",
- ":task_queue_for_test",
- "../test:test_main",
- "../test:test_support",
- ]
- absl_deps = [ "//third_party/abseil-cpp/absl/memory" ]
- }
-
rtc_library("rtc_operations_chain_unittests") {
testonly = true
@@ -1358,138 +1271,294 @@
":rtc_base_approved",
":rtc_event",
":rtc_operations_chain",
+ ":threading",
"../test:test_support",
]
}
- rtc_library("weak_ptr_unittests") {
- testonly = true
+ if (!build_with_chromium) {
+ rtc_library("rtc_base_nonparallel_tests") {
+ testonly = true
- sources = [ "weak_ptr_unittest.cc" ]
- deps = [
- ":gunit_helpers",
- ":rtc_base_approved",
- ":rtc_base_tests_utils",
- ":rtc_event",
- ":task_queue_for_test",
- ":weak_ptr",
- "../test:test_main",
- "../test:test_support",
- ]
- }
-
- rtc_library("rtc_numerics_unittests") {
- testonly = true
-
- sources = [
- "numerics/event_based_exponential_moving_average_unittest.cc",
- "numerics/exp_filter_unittest.cc",
- "numerics/moving_average_unittest.cc",
- "numerics/moving_median_filter_unittest.cc",
- "numerics/percentile_filter_unittest.cc",
- "numerics/running_statistics_unittest.cc",
- "numerics/sequence_number_util_unittest.cc",
- ]
- deps = [
- ":rtc_base_approved",
- ":rtc_numerics",
- "../test:test_main",
- "../test:test_support",
- ]
- absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container" ]
- }
-
- rtc_library("rtc_json_unittests") {
- testonly = true
-
- sources = [ "strings/json_unittest.cc" ]
- deps = [
- ":gunit_helpers",
- ":rtc_base_tests_utils",
- ":rtc_json",
- "../test:test_main",
- "../test:test_support",
- ]
- }
-
- rtc_library("rtc_base_unittests") {
- testonly = true
- defines = []
-
- sources = [
- "callback_unittest.cc",
- "crc32_unittest.cc",
- "data_rate_limiter_unittest.cc",
- "deprecated/signal_thread_unittest.cc",
- "fake_clock_unittest.cc",
- "helpers_unittest.cc",
- "ip_address_unittest.cc",
- "memory_usage_unittest.cc",
- "message_digest_unittest.cc",
- "nat_unittest.cc",
- "network_route_unittest.cc",
- "network_unittest.cc",
- "proxy_unittest.cc",
- "rolling_accumulator_unittest.cc",
- "rtc_certificate_generator_unittest.cc",
- "rtc_certificate_unittest.cc",
- "sigslot_tester_unittest.cc",
- "test_client_unittest.cc",
- "thread_unittest.cc",
- "unique_id_generator_unittest.cc",
- ]
- deps = [
- ":checks",
- ":gunit_helpers",
- ":rtc_base_tests_utils",
- ":stringutils",
- ":testclient",
- "../api:array_view",
- "../api/task_queue",
- "../api/task_queue:task_queue_test",
- "../test:field_trial",
- "../test:fileutils",
- "../test:rtc_expect_death",
- "../test:test_main",
- "../test:test_support",
- "memory:fifo_buffer",
- "synchronization:mutex",
- "synchronization:synchronization_unittests",
- "task_utils:pending_task_safety_flag",
- "task_utils:to_queued_task",
- "third_party/sigslot",
- ]
- if (is_win) {
- sources += [
- "win32_unittest.cc",
- "win32_window_unittest.cc",
+ sources = [
+ "cpu_time_unittest.cc",
+ "file_rotating_stream_unittest.cc",
+ "null_socket_server_unittest.cc",
+ "physical_socket_server_unittest.cc",
+ "socket_address_unittest.cc",
+ "socket_unittest.cc",
+ "socket_unittest.h",
]
- deps += [ ":win32" ]
+ deps = [
+ ":async_socket",
+ ":checks",
+ ":gunit_helpers",
+ ":ip_address",
+ ":net_helpers",
+ ":null_socket_server",
+ ":rtc_base",
+ ":rtc_base_tests_utils",
+ ":socket",
+ ":socket_address",
+ ":socket_server",
+ ":testclient",
+ ":threading",
+ "../system_wrappers",
+ "../test:fileutils",
+ "../test:test_main",
+ "../test:test_support",
+ "third_party/sigslot",
+ "//testing/gtest",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/memory" ]
+ if (is_win) {
+ sources += [ "win32_socket_server_unittest.cc" ]
+ }
}
- if (is_posix || is_fuchsia) {
- sources += [
- "openssl_adapter_unittest.cc",
- "openssl_session_cache_unittest.cc",
- "openssl_utility_unittest.cc",
- "ssl_adapter_unittest.cc",
- "ssl_identity_unittest.cc",
- "ssl_stream_adapter_unittest.cc",
+
+ rtc_library("rtc_base_approved_unittests") {
+ testonly = true
+ sources = [
+ "atomic_ops_unittest.cc",
+ "base64_unittest.cc",
+ "bit_buffer_unittest.cc",
+ "bounded_inline_vector_unittest.cc",
+ "buffer_queue_unittest.cc",
+ "buffer_unittest.cc",
+ "byte_buffer_unittest.cc",
+ "byte_order_unittest.cc",
+ "checks_unittest.cc",
+ "copy_on_write_buffer_unittest.cc",
+ "deprecated/recursive_critical_section_unittest.cc",
+ "event_tracer_unittest.cc",
+ "event_unittest.cc",
+ "logging_unittest.cc",
+ "numerics/divide_round_unittest.cc",
+ "numerics/histogram_percentile_counter_unittest.cc",
+ "numerics/mod_ops_unittest.cc",
+ "numerics/moving_max_counter_unittest.cc",
+ "numerics/safe_compare_unittest.cc",
+ "numerics/safe_minmax_unittest.cc",
+ "numerics/sample_counter_unittest.cc",
+ "one_time_event_unittest.cc",
+ "platform_thread_unittest.cc",
+ "random_unittest.cc",
+ "rate_limiter_unittest.cc",
+ "rate_statistics_unittest.cc",
+ "rate_tracker_unittest.cc",
+ "ref_counted_object_unittest.cc",
+ "sanitizer_unittest.cc",
+ "string_encode_unittest.cc",
+ "string_to_number_unittest.cc",
+ "string_utils_unittest.cc",
+ "strings/string_builder_unittest.cc",
+ "strings/string_format_unittest.cc",
+ "swap_queue_unittest.cc",
+ "thread_annotations_unittest.cc",
+ "time_utils_unittest.cc",
+ "timestamp_aligner_unittest.cc",
+ "virtual_socket_unittest.cc",
+ "zero_memory_unittest.cc",
+ ]
+ if (is_win) {
+ sources += [ "win/windows_version_unittest.cc" ]
+ }
+ deps = [
+ ":async_socket",
+ ":bounded_inline_vector",
+ ":checks",
+ ":divide_round",
+ ":gunit_helpers",
+ ":ip_address",
+ ":null_socket_server",
+ ":rate_limiter",
+ ":rtc_base",
+ ":rtc_base_approved",
+ ":rtc_base_tests_utils",
+ ":rtc_numerics",
+ ":rtc_task_queue",
+ ":safe_compare",
+ ":safe_minmax",
+ ":sanitizer",
+ ":socket",
+ ":socket_address",
+ ":socket_server",
+ ":stringutils",
+ ":testclient",
+ ":threading",
+ "../api:array_view",
+ "../api:scoped_refptr",
+ "../api/numerics",
+ "../api/units:time_delta",
+ "../system_wrappers",
+ "../test:fileutils",
+ "../test:test_main",
+ "../test:test_support",
+ "memory:unittests",
+ "synchronization:mutex",
+ "task_utils:to_queued_task",
+ "third_party/base64",
+ "third_party/sigslot",
+ ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/base:core_headers",
+ "//third_party/abseil-cpp/absl/memory",
]
}
- absl_deps = [
- "//third_party/abseil-cpp/absl/algorithm:container",
- "//third_party/abseil-cpp/absl/memory",
- "//third_party/abseil-cpp/absl/strings",
- "//third_party/abseil-cpp/absl/types:optional",
- ]
- public_deps = [ ":rtc_base" ] # no-presubmit-check TODO(webrtc:8603)
- if (build_with_chromium) {
- include_dirs = [ "../../boringssl/src/include" ]
+
+ rtc_library("rtc_task_queue_unittests") {
+ testonly = true
+
+ sources = [ "task_queue_unittest.cc" ]
+ deps = [
+ ":gunit_helpers",
+ ":rtc_base_approved",
+ ":rtc_base_tests_utils",
+ ":rtc_task_queue",
+ ":task_queue_for_test",
+ "../test:test_main",
+ "../test:test_support",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/memory" ]
}
- if (rtc_build_ssl) {
- deps += [ "//third_party/boringssl" ]
- } else {
- configs += [ ":external_ssl_library" ]
+
+ rtc_library("weak_ptr_unittests") {
+ testonly = true
+
+ sources = [ "weak_ptr_unittest.cc" ]
+ deps = [
+ ":gunit_helpers",
+ ":rtc_base_approved",
+ ":rtc_base_tests_utils",
+ ":rtc_event",
+ ":task_queue_for_test",
+ ":weak_ptr",
+ "../test:test_main",
+ "../test:test_support",
+ ]
+ }
+
+ rtc_library("rtc_numerics_unittests") {
+ testonly = true
+
+ sources = [
+ "numerics/event_based_exponential_moving_average_unittest.cc",
+ "numerics/exp_filter_unittest.cc",
+ "numerics/moving_average_unittest.cc",
+ "numerics/moving_median_filter_unittest.cc",
+ "numerics/percentile_filter_unittest.cc",
+ "numerics/running_statistics_unittest.cc",
+ "numerics/sequence_number_util_unittest.cc",
+ ]
+ deps = [
+ ":rtc_base_approved",
+ ":rtc_numerics",
+ "../test:test_main",
+ "../test:test_support",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container" ]
+ }
+
+ rtc_library("rtc_json_unittests") {
+ testonly = true
+
+ sources = [ "strings/json_unittest.cc" ]
+ deps = [
+ ":gunit_helpers",
+ ":rtc_base_tests_utils",
+ ":rtc_json",
+ "../test:test_main",
+ "../test:test_support",
+ ]
+ }
+
+ rtc_library("rtc_base_unittests") {
+ testonly = true
+ defines = []
+
+ sources = [
+ "crc32_unittest.cc",
+ "data_rate_limiter_unittest.cc",
+ "fake_clock_unittest.cc",
+ "helpers_unittest.cc",
+ "ip_address_unittest.cc",
+ "memory_usage_unittest.cc",
+ "message_digest_unittest.cc",
+ "nat_unittest.cc",
+ "network_route_unittest.cc",
+ "network_unittest.cc",
+ "proxy_unittest.cc",
+ "rolling_accumulator_unittest.cc",
+ "rtc_certificate_generator_unittest.cc",
+ "rtc_certificate_unittest.cc",
+ "sigslot_tester_unittest.cc",
+ "test_client_unittest.cc",
+ "thread_unittest.cc",
+ "unique_id_generator_unittest.cc",
+ ]
+ deps = [
+ ":async_socket",
+ ":checks",
+ ":gunit_helpers",
+ ":ip_address",
+ ":net_helpers",
+ ":null_socket_server",
+ ":rtc_base_tests_utils",
+ ":socket_address",
+ ":socket_factory",
+ ":socket_server",
+ ":stringutils",
+ ":testclient",
+ ":threading",
+ "../api:array_view",
+ "../api/task_queue",
+ "../api/task_queue:task_queue_test",
+ "../test:field_trial",
+ "../test:fileutils",
+ "../test:rtc_expect_death",
+ "../test:test_main",
+ "../test:test_support",
+ "memory:fifo_buffer",
+ "synchronization:mutex",
+ "task_utils:pending_task_safety_flag",
+ "task_utils:to_queued_task",
+ "third_party/sigslot",
+ ]
+ if (enable_google_benchmarks) {
+ deps += [ "synchronization:synchronization_unittests" ]
+ }
+ if (is_win) {
+ sources += [
+ "win32_unittest.cc",
+ "win32_window_unittest.cc",
+ ]
+ deps += [ ":win32" ]
+ }
+ if (is_posix || is_fuchsia) {
+ sources += [
+ "openssl_adapter_unittest.cc",
+ "openssl_session_cache_unittest.cc",
+ "openssl_utility_unittest.cc",
+ "ssl_adapter_unittest.cc",
+ "ssl_identity_unittest.cc",
+ "ssl_stream_adapter_unittest.cc",
+ ]
+ }
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/algorithm:container",
+ "//third_party/abseil-cpp/absl/memory",
+ "//third_party/abseil-cpp/absl/strings",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
+ public_deps = [ ":rtc_base" ] # no-presubmit-check TODO(webrtc:8603)
+ if (build_with_chromium) {
+ include_dirs = [ "../../boringssl/src/include" ]
+ }
+ if (rtc_build_ssl) {
+ deps += [ "//third_party/boringssl" ]
+ } else {
+ configs += [ ":external_ssl_library" ]
+ }
}
}
}
diff --git a/rtc_base/async_invoker.cc b/rtc_base/async_invoker.cc
index 8b410a4..995f443 100644
--- a/rtc_base/async_invoker.cc
+++ b/rtc_base/async_invoker.cc
@@ -55,7 +55,7 @@
// Run this on |thread| to reduce the number of context switches.
if (Thread::Current() != thread) {
thread->Invoke<void>(RTC_FROM_HERE,
- Bind(&AsyncInvoker::Flush, this, thread, id));
+ [this, thread, id] { Flush(thread, id); });
return;
}
diff --git a/rtc_base/async_invoker.h b/rtc_base/async_invoker.h
index 983e710..d3bb9a2 100644
--- a/rtc_base/async_invoker.h
+++ b/rtc_base/async_invoker.h
@@ -17,7 +17,6 @@
#include "api/scoped_refptr.h"
#include "rtc_base/async_invoker_inl.h"
-#include "rtc_base/bind.h"
#include "rtc_base/constructor_magic.h"
#include "rtc_base/event.h"
#include "rtc_base/ref_counted_object.h"
diff --git a/rtc_base/async_invoker_inl.h b/rtc_base/async_invoker_inl.h
index 6307afe..6151059 100644
--- a/rtc_base/async_invoker_inl.h
+++ b/rtc_base/async_invoker_inl.h
@@ -12,7 +12,6 @@
#define RTC_BASE_ASYNC_INVOKER_INL_H_
#include "api/scoped_refptr.h"
-#include "rtc_base/bind.h"
#include "rtc_base/event.h"
#include "rtc_base/message_handler.h"
#include "rtc_base/ref_counted_object.h"
diff --git a/rtc_base/async_resolver.cc b/rtc_base/async_resolver.cc
new file mode 100644
index 0000000..198b498
--- /dev/null
+++ b/rtc_base/async_resolver.cc
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2008 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 "rtc_base/async_resolver.h"
+
+#include <string>
+#include <utility>
+
+#if defined(WEBRTC_WIN)
+#include <ws2spi.h>
+#include <ws2tcpip.h>
+
+#include "rtc_base/win32.h"
+#endif
+#if defined(WEBRTC_POSIX) && !defined(__native_client__)
+#if defined(WEBRTC_ANDROID)
+#include "rtc_base/ifaddrs_android.h"
+#else
+#include <ifaddrs.h>
+#endif
+#endif // defined(WEBRTC_POSIX) && !defined(__native_client__)
+
+#include "api/task_queue/task_queue_base.h"
+#include "rtc_base/ip_address.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/task_queue.h"
+#include "rtc_base/task_utils/to_queued_task.h"
+#include "rtc_base/third_party/sigslot/sigslot.h" // for signal_with_thread...
+
+namespace rtc {
+
+int ResolveHostname(const std::string& hostname,
+ int family,
+ std::vector<IPAddress>* addresses) {
+#ifdef __native_client__
+ RTC_NOTREACHED();
+ RTC_LOG(LS_WARNING) << "ResolveHostname() is not implemented for NaCl";
+ return -1;
+#else // __native_client__
+ if (!addresses) {
+ return -1;
+ }
+ addresses->clear();
+ struct addrinfo* result = nullptr;
+ struct addrinfo hints = {0};
+ hints.ai_family = family;
+ // |family| here will almost always be AF_UNSPEC, because |family| comes from
+ // AsyncResolver::addr_.family(), which comes from a SocketAddress constructed
+ // with a hostname. When a SocketAddress is constructed with a hostname, its
+ // family is AF_UNSPEC. However, if someday in the future we construct
+ // a SocketAddress with both a hostname and a family other than AF_UNSPEC,
+ // then it would be possible to get a specific family value here.
+
+ // The behavior of AF_UNSPEC is roughly "get both ipv4 and ipv6", as
+ // documented by the various operating systems:
+ // Linux: http://man7.org/linux/man-pages/man3/getaddrinfo.3.html
+ // Windows: https://msdn.microsoft.com/en-us/library/windows/desktop/
+ // ms738520(v=vs.85).aspx
+ // Mac: https://developer.apple.com/legacy/library/documentation/Darwin/
+ // Reference/ManPages/man3/getaddrinfo.3.html
+ // Android (source code, not documentation):
+ // https://android.googlesource.com/platform/bionic/+/
+ // 7e0bfb511e85834d7c6cb9631206b62f82701d60/libc/netbsd/net/getaddrinfo.c#1657
+ hints.ai_flags = AI_ADDRCONFIG;
+ int ret = getaddrinfo(hostname.c_str(), nullptr, &hints, &result);
+ if (ret != 0) {
+ return ret;
+ }
+ struct addrinfo* cursor = result;
+ for (; cursor; cursor = cursor->ai_next) {
+ if (family == AF_UNSPEC || cursor->ai_family == family) {
+ IPAddress ip;
+ if (IPFromAddrInfo(cursor, &ip)) {
+ addresses->push_back(ip);
+ }
+ }
+ }
+ freeaddrinfo(result);
+ return 0;
+#endif // !__native_client__
+}
+
+AsyncResolver::AsyncResolver() : error_(-1) {}
+
+AsyncResolver::~AsyncResolver() {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+}
+
+void AsyncResolver::Start(const SocketAddress& addr) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ RTC_DCHECK(!destroy_called_);
+ addr_ = addr;
+ webrtc::TaskQueueBase* current_task_queue = webrtc::TaskQueueBase::Current();
+ popup_thread_ = Thread::Create();
+ popup_thread_->Start();
+ popup_thread_->PostTask(webrtc::ToQueuedTask(
+ [this, flag = safety_.flag(), addr, current_task_queue] {
+ std::vector<IPAddress> addresses;
+ int error =
+ ResolveHostname(addr.hostname().c_str(), addr.family(), &addresses);
+ current_task_queue->PostTask(webrtc::ToQueuedTask(
+ std::move(flag), [this, error, addresses = std::move(addresses)] {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ ResolveDone(std::move(addresses), error);
+ }));
+ }));
+}
+
+bool AsyncResolver::GetResolvedAddress(int family, SocketAddress* addr) const {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ RTC_DCHECK(!destroy_called_);
+ if (error_ != 0 || addresses_.empty())
+ return false;
+
+ *addr = addr_;
+ for (size_t i = 0; i < addresses_.size(); ++i) {
+ if (family == addresses_[i].family()) {
+ addr->SetResolvedIP(addresses_[i]);
+ return true;
+ }
+ }
+ return false;
+}
+
+int AsyncResolver::GetError() const {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ RTC_DCHECK(!destroy_called_);
+ return error_;
+}
+
+void AsyncResolver::Destroy(bool wait) {
+ // Some callers have trouble guaranteeing that Destroy is called on the
+ // sequence guarded by |sequence_checker_|.
+ // RTC_DCHECK_RUN_ON(&sequence_checker_);
+ RTC_DCHECK(!destroy_called_);
+ destroy_called_ = true;
+ MaybeSelfDestruct();
+}
+
+const std::vector<IPAddress>& AsyncResolver::addresses() const {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ RTC_DCHECK(!destroy_called_);
+ return addresses_;
+}
+
+void AsyncResolver::ResolveDone(std::vector<IPAddress> addresses, int error) {
+ addresses_ = addresses;
+ error_ = error;
+ recursion_check_ = true;
+ SignalDone(this);
+ MaybeSelfDestruct();
+}
+
+void AsyncResolver::MaybeSelfDestruct() {
+ if (!recursion_check_) {
+ delete this;
+ } else {
+ recursion_check_ = false;
+ }
+}
+
+} // namespace rtc
diff --git a/rtc_base/async_resolver.h b/rtc_base/async_resolver.h
index 3c3ad82..c43685a 100644
--- a/rtc_base/async_resolver.h
+++ b/rtc_base/async_resolver.h
@@ -11,7 +11,62 @@
#ifndef RTC_BASE_ASYNC_RESOLVER_H_
#define RTC_BASE_ASYNC_RESOLVER_H_
-// Placeholder header for the refactoring in:
-// https://webrtc-review.googlesource.com/c/src/+/196903
+#if defined(WEBRTC_POSIX)
+#include <sys/socket.h>
+#elif WEBRTC_WIN
+#include <winsock2.h> // NOLINT
+#endif
+
+#include <memory>
+#include <vector>
+
+#include "api/sequence_checker.h"
+#include "rtc_base/async_resolver_interface.h"
+#include "rtc_base/ip_address.h"
+#include "rtc_base/socket_address.h"
+#include "rtc_base/system/no_unique_address.h"
+#include "rtc_base/system/rtc_export.h"
+#include "rtc_base/task_utils/pending_task_safety_flag.h"
+#include "rtc_base/thread.h"
+#include "rtc_base/thread_annotations.h"
+
+namespace rtc {
+
+// AsyncResolver will perform async DNS resolution, signaling the result on
+// the SignalDone from AsyncResolverInterface when the operation completes.
+//
+// This class is thread-compatible, and all methods and destruction needs to
+// happen from the same rtc::Thread, except for Destroy which is allowed to
+// happen on another context provided it's not happening concurrently to another
+// public API call, and is the last access to the object.
+class RTC_EXPORT AsyncResolver : public AsyncResolverInterface {
+ public:
+ AsyncResolver();
+ ~AsyncResolver() override;
+
+ void Start(const SocketAddress& addr) override;
+ bool GetResolvedAddress(int family, SocketAddress* addr) const override;
+ int GetError() const override;
+ void Destroy(bool wait) override;
+
+ const std::vector<IPAddress>& addresses() const;
+
+ private:
+ void ResolveDone(std::vector<IPAddress> addresses, int error)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(sequence_checker_);
+ void MaybeSelfDestruct();
+
+ SocketAddress addr_ RTC_GUARDED_BY(sequence_checker_);
+ std::vector<IPAddress> addresses_ RTC_GUARDED_BY(sequence_checker_);
+ int error_ RTC_GUARDED_BY(sequence_checker_);
+ webrtc::ScopedTaskSafety safety_ RTC_GUARDED_BY(sequence_checker_);
+ std::unique_ptr<Thread> popup_thread_ RTC_GUARDED_BY(sequence_checker_);
+ bool recursion_check_ =
+ false; // Protects against SignalDone calling into Destroy.
+ bool destroy_called_ = false;
+ RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker sequence_checker_;
+};
+
+} // namespace rtc
#endif // RTC_BASE_ASYNC_RESOLVER_H_
diff --git a/rtc_base/bind.h b/rtc_base/bind.h
deleted file mode 100644
index b61d189..0000000
--- a/rtc_base/bind.h
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * Copyright 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.
- */
-
-// Bind() is an overloaded function that converts method calls into function
-// objects (aka functors). The method object is captured as a scoped_refptr<> if
-// possible, and as a raw pointer otherwise. Any arguments to the method are
-// captured by value. The return value of Bind is a stateful, nullary function
-// object. Care should be taken about the lifetime of objects captured by
-// Bind(); the returned functor knows nothing about the lifetime of a non
-// ref-counted method object or any arguments passed by pointer, and calling the
-// functor with a destroyed object will surely do bad things.
-//
-// To prevent the method object from being captured as a scoped_refptr<>, you
-// can use Unretained. But this should only be done when absolutely necessary,
-// and when the caller knows the extra reference isn't needed.
-//
-// Example usage:
-// struct Foo {
-// int Test1() { return 42; }
-// int Test2() const { return 52; }
-// int Test3(int x) { return x*x; }
-// float Test4(int x, float y) { return x + y; }
-// };
-//
-// int main() {
-// Foo foo;
-// cout << rtc::Bind(&Foo::Test1, &foo)() << endl;
-// cout << rtc::Bind(&Foo::Test2, &foo)() << endl;
-// cout << rtc::Bind(&Foo::Test3, &foo, 3)() << endl;
-// cout << rtc::Bind(&Foo::Test4, &foo, 7, 8.5f)() << endl;
-// }
-//
-// Example usage of ref counted objects:
-// struct Bar {
-// int AddRef();
-// int Release();
-//
-// void Test() {}
-// void BindThis() {
-// // The functor passed to AsyncInvoke() will keep this object alive.
-// invoker.AsyncInvoke(RTC_FROM_HERE,rtc::Bind(&Bar::Test, this));
-// }
-// };
-//
-// int main() {
-// rtc::scoped_refptr<Bar> bar = new rtc::RefCountedObject<Bar>();
-// auto functor = rtc::Bind(&Bar::Test, bar);
-// bar = nullptr;
-// // The functor stores an internal scoped_refptr<Bar>, so this is safe.
-// functor();
-// }
-//
-
-#ifndef RTC_BASE_BIND_H_
-#define RTC_BASE_BIND_H_
-
-#include <tuple>
-#include <type_traits>
-
-#include "api/scoped_refptr.h"
-
-#define NONAME
-
-namespace rtc {
-namespace detail {
-// This is needed because the template parameters in Bind can't be resolved
-// if they're used both as parameters of the function pointer type and as
-// parameters to Bind itself: the function pointer parameters are exact
-// matches to the function prototype, but the parameters to bind have
-// references stripped. This trick allows the compiler to dictate the Bind
-// parameter types rather than deduce them.
-template <class T>
-struct identity {
- typedef T type;
-};
-
-// IsRefCounted<T>::value will be true for types that can be used in
-// rtc::scoped_refptr<T>, i.e. types that implements nullary functions AddRef()
-// and Release(), regardless of their return types. AddRef() and Release() can
-// be defined in T or any superclass of T.
-template <typename T>
-class IsRefCounted {
- // This is a complex implementation detail done with SFINAE.
-
- // Define types such that sizeof(Yes) != sizeof(No).
- struct Yes {
- char dummy[1];
- };
- struct No {
- char dummy[2];
- };
- // Define two overloaded template functions with return types of different
- // size. This way, we can use sizeof() on the return type to determine which
- // function the compiler would have chosen. One function will be preferred
- // over the other if it is possible to create it without compiler errors,
- // otherwise the compiler will simply remove it, and default to the less
- // preferred function.
- template <typename R>
- static Yes test(R* r, decltype(r->AddRef(), r->Release(), 42));
- template <typename C>
- static No test(...);
-
- public:
- // Trick the compiler to tell if it's possible to call AddRef() and Release().
- static const bool value = sizeof(test<T>((T*)nullptr, 42)) == sizeof(Yes);
-};
-
-// TernaryTypeOperator is a helper class to select a type based on a static bool
-// value.
-template <bool condition, typename IfTrueT, typename IfFalseT>
-struct TernaryTypeOperator {};
-
-template <typename IfTrueT, typename IfFalseT>
-struct TernaryTypeOperator<true, IfTrueT, IfFalseT> {
- typedef IfTrueT type;
-};
-
-template <typename IfTrueT, typename IfFalseT>
-struct TernaryTypeOperator<false, IfTrueT, IfFalseT> {
- typedef IfFalseT type;
-};
-
-// PointerType<T>::type will be scoped_refptr<T> for ref counted types, and T*
-// otherwise.
-template <class T>
-struct PointerType {
- typedef typename TernaryTypeOperator<IsRefCounted<T>::value,
- scoped_refptr<T>,
- T*>::type type;
-};
-
-template <typename T>
-class UnretainedWrapper {
- public:
- explicit UnretainedWrapper(T* o) : ptr_(o) {}
- T* get() const { return ptr_; }
-
- private:
- T* ptr_;
-};
-
-} // namespace detail
-
-template <typename T>
-static inline detail::UnretainedWrapper<T> Unretained(T* o) {
- return detail::UnretainedWrapper<T>(o);
-}
-
-template <class ObjectT, class MethodT, class R, typename... Args>
-class MethodFunctor {
- public:
- MethodFunctor(MethodT method, ObjectT* object, Args... args)
- : method_(method), object_(object), args_(args...) {}
- R operator()() const {
- return CallMethod(std::index_sequence_for<Args...>());
- }
-
- private:
- template <size_t... S>
- R CallMethod(std::index_sequence<S...>) const {
- return (object_->*method_)(std::get<S>(args_)...);
- }
-
- MethodT method_;
- typename detail::PointerType<ObjectT>::type object_;
- typename std::tuple<typename std::remove_reference<Args>::type...> args_;
-};
-
-template <class ObjectT, class MethodT, class R, typename... Args>
-class UnretainedMethodFunctor {
- public:
- UnretainedMethodFunctor(MethodT method,
- detail::UnretainedWrapper<ObjectT> object,
- Args... args)
- : method_(method), object_(object.get()), args_(args...) {}
- R operator()() const {
- return CallMethod(std::index_sequence_for<Args...>());
- }
-
- private:
- template <size_t... S>
- R CallMethod(std::index_sequence<S...>) const {
- return (object_->*method_)(std::get<S>(args_)...);
- }
-
- MethodT method_;
- ObjectT* object_;
- typename std::tuple<typename std::remove_reference<Args>::type...> args_;
-};
-
-template <class FunctorT, class R, typename... Args>
-class Functor {
- public:
- Functor(const FunctorT& functor, Args... args)
- : functor_(functor), args_(args...) {}
- R operator()() const {
- return CallFunction(std::index_sequence_for<Args...>());
- }
-
- private:
- template <size_t... S>
- R CallFunction(std::index_sequence<S...>) const {
- return functor_(std::get<S>(args_)...);
- }
-
- FunctorT functor_;
- typename std::tuple<typename std::remove_reference<Args>::type...> args_;
-};
-
-#define FP_T(x) R (ObjectT::*x)(Args...)
-
-template <class ObjectT, class R, typename... Args>
-MethodFunctor<ObjectT, FP_T(NONAME), R, Args...> Bind(
- FP_T(method),
- ObjectT* object,
- typename detail::identity<Args>::type... args) {
- return MethodFunctor<ObjectT, FP_T(NONAME), R, Args...>(method, object,
- args...);
-}
-
-template <class ObjectT, class R, typename... Args>
-MethodFunctor<ObjectT, FP_T(NONAME), R, Args...> Bind(
- FP_T(method),
- const scoped_refptr<ObjectT>& object,
- typename detail::identity<Args>::type... args) {
- return MethodFunctor<ObjectT, FP_T(NONAME), R, Args...>(method, object.get(),
- args...);
-}
-
-template <class ObjectT, class R, typename... Args>
-UnretainedMethodFunctor<ObjectT, FP_T(NONAME), R, Args...> Bind(
- FP_T(method),
- detail::UnretainedWrapper<ObjectT> object,
- typename detail::identity<Args>::type... args) {
- return UnretainedMethodFunctor<ObjectT, FP_T(NONAME), R, Args...>(
- method, object, args...);
-}
-
-#undef FP_T
-#define FP_T(x) R (ObjectT::*x)(Args...) const
-
-template <class ObjectT, class R, typename... Args>
-MethodFunctor<const ObjectT, FP_T(NONAME), R, Args...> Bind(
- FP_T(method),
- const ObjectT* object,
- typename detail::identity<Args>::type... args) {
- return MethodFunctor<const ObjectT, FP_T(NONAME), R, Args...>(method, object,
- args...);
-}
-template <class ObjectT, class R, typename... Args>
-UnretainedMethodFunctor<const ObjectT, FP_T(NONAME), R, Args...> Bind(
- FP_T(method),
- detail::UnretainedWrapper<const ObjectT> object,
- typename detail::identity<Args>::type... args) {
- return UnretainedMethodFunctor<const ObjectT, FP_T(NONAME), R, Args...>(
- method, object, args...);
-}
-
-#undef FP_T
-#define FP_T(x) R (*x)(Args...)
-
-template <class R, typename... Args>
-Functor<FP_T(NONAME), R, Args...> Bind(
- FP_T(function),
- typename detail::identity<Args>::type... args) {
- return Functor<FP_T(NONAME), R, Args...>(function, args...);
-}
-
-#undef FP_T
-
-} // namespace rtc
-
-#undef NONAME
-
-#endif // RTC_BASE_BIND_H_
diff --git a/rtc_base/bind_unittest.cc b/rtc_base/bind_unittest.cc
deleted file mode 100644
index 664cb54..0000000
--- a/rtc_base/bind_unittest.cc
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright 2004 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 "rtc_base/bind.h"
-
-#include <string>
-
-#include "rtc_base/ref_count.h"
-#include "rtc_base/ref_counted_object.h"
-#include "test/gtest.h"
-
-namespace rtc {
-
-namespace {
-
-struct LifeTimeCheck;
-
-struct MethodBindTester {
- void NullaryVoid() { ++call_count; }
- int NullaryInt() {
- ++call_count;
- return 1;
- }
- int NullaryConst() const {
- ++call_count;
- return 2;
- }
- void UnaryVoid(int dummy) { ++call_count; }
- template <class T>
- T Identity(T value) {
- ++call_count;
- return value;
- }
- int UnaryByPointer(int* value) const {
- ++call_count;
- return ++(*value);
- }
- int UnaryByRef(const int& value) const {
- ++call_count;
- return ++const_cast<int&>(value);
- }
- int Multiply(int a, int b) const {
- ++call_count;
- return a * b;
- }
- void RefArgument(const scoped_refptr<LifeTimeCheck>& object) {
- EXPECT_TRUE(object.get() != nullptr);
- }
-
- mutable int call_count;
-};
-
-struct A {
- int dummy;
-};
-struct B : public RefCountInterface {
- int dummy;
-};
-struct C : public A, B {};
-struct D {
- int AddRef();
-};
-struct E : public D {
- int Release();
-};
-struct F {
- void AddRef();
- void Release();
-};
-
-struct LifeTimeCheck {
- LifeTimeCheck() : ref_count_(0) {}
- void AddRef() { ++ref_count_; }
- void Release() { --ref_count_; }
- void NullaryVoid() {}
- int ref_count_;
-};
-
-int Return42() {
- return 42;
-}
-int Negate(int a) {
- return -a;
-}
-int Multiply(int a, int b) {
- return a * b;
-}
-
-} // namespace
-
-// Try to catch any problem with scoped_refptr type deduction in rtc::Bind at
-// compile time.
-#define EXPECT_IS_CAPTURED_AS_PTR(T) \
- static_assert(std::is_same<detail::PointerType<T>::type, T*>::value, \
- "PointerType")
-#define EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(T) \
- static_assert( \
- std::is_same<detail::PointerType<T>::type, scoped_refptr<T>>::value, \
- "PointerType")
-
-EXPECT_IS_CAPTURED_AS_PTR(void);
-EXPECT_IS_CAPTURED_AS_PTR(int);
-EXPECT_IS_CAPTURED_AS_PTR(double);
-EXPECT_IS_CAPTURED_AS_PTR(A);
-EXPECT_IS_CAPTURED_AS_PTR(D);
-EXPECT_IS_CAPTURED_AS_PTR(RefCountInterface*);
-EXPECT_IS_CAPTURED_AS_PTR(
- decltype(Unretained<RefCountedObject<RefCountInterface>>));
-
-EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountInterface);
-EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(B);
-EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(C);
-EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(E);
-EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(F);
-EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<RefCountInterface>);
-EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<B>);
-EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<C>);
-EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(const RefCountedObject<RefCountInterface>);
-
-TEST(BindTest, BindToMethod) {
- MethodBindTester object = {0};
- EXPECT_EQ(0, object.call_count);
- Bind(&MethodBindTester::NullaryVoid, &object)();
- EXPECT_EQ(1, object.call_count);
- EXPECT_EQ(1, Bind(&MethodBindTester::NullaryInt, &object)());
- EXPECT_EQ(2, object.call_count);
- EXPECT_EQ(2, Bind(&MethodBindTester::NullaryConst,
- static_cast<const MethodBindTester*>(&object))());
- EXPECT_EQ(3, object.call_count);
- Bind(&MethodBindTester::UnaryVoid, &object, 5)();
- EXPECT_EQ(4, object.call_count);
- EXPECT_EQ(100, Bind(&MethodBindTester::Identity<int>, &object, 100)());
- EXPECT_EQ(5, object.call_count);
- const std::string string_value("test string");
- EXPECT_EQ(string_value, Bind(&MethodBindTester::Identity<std::string>,
- &object, string_value)());
- EXPECT_EQ(6, object.call_count);
- int value = 11;
- // Bind binds by value, even if the method signature is by reference, so
- // "reference" binds require pointers.
- EXPECT_EQ(12, Bind(&MethodBindTester::UnaryByPointer, &object, &value)());
- EXPECT_EQ(12, value);
- EXPECT_EQ(7, object.call_count);
- // It's possible to bind to a function that takes a const reference, though
- // the capture will be a copy. See UnaryByRef hackery above where it removes
- // the const to make sure the underlying storage is, in fact, a copy.
- EXPECT_EQ(13, Bind(&MethodBindTester::UnaryByRef, &object, value)());
- // But the original value is unmodified.
- EXPECT_EQ(12, value);
- EXPECT_EQ(8, object.call_count);
- EXPECT_EQ(56, Bind(&MethodBindTester::Multiply, &object, 7, 8)());
- EXPECT_EQ(9, object.call_count);
-}
-
-TEST(BindTest, BindToFunction) {
- EXPECT_EQ(42, Bind(&Return42)());
- EXPECT_EQ(3, Bind(&Negate, -3)());
- EXPECT_EQ(56, Bind(&Multiply, 8, 7)());
-}
-
-// Test Bind where method object implements RefCountInterface and is passed as a
-// pointer.
-TEST(BindTest, CapturePointerAsScopedRefPtr) {
- LifeTimeCheck object;
- EXPECT_EQ(object.ref_count_, 0);
- scoped_refptr<LifeTimeCheck> scoped_object(&object);
- EXPECT_EQ(object.ref_count_, 1);
- {
- auto functor = Bind(&LifeTimeCheck::NullaryVoid, &object);
- EXPECT_EQ(object.ref_count_, 2);
- scoped_object = nullptr;
- EXPECT_EQ(object.ref_count_, 1);
- }
- EXPECT_EQ(object.ref_count_, 0);
-}
-
-// Test Bind where method object implements RefCountInterface and is passed as a
-// scoped_refptr<>.
-TEST(BindTest, CaptureScopedRefPtrAsScopedRefPtr) {
- LifeTimeCheck object;
- EXPECT_EQ(object.ref_count_, 0);
- scoped_refptr<LifeTimeCheck> scoped_object(&object);
- EXPECT_EQ(object.ref_count_, 1);
- {
- auto functor = Bind(&LifeTimeCheck::NullaryVoid, scoped_object);
- EXPECT_EQ(object.ref_count_, 2);
- scoped_object = nullptr;
- EXPECT_EQ(object.ref_count_, 1);
- }
- EXPECT_EQ(object.ref_count_, 0);
-}
-
-// Test Bind where method object is captured as scoped_refptr<> and the functor
-// dies while there are references left.
-TEST(BindTest, FunctorReleasesObjectOnDestruction) {
- LifeTimeCheck object;
- EXPECT_EQ(object.ref_count_, 0);
- scoped_refptr<LifeTimeCheck> scoped_object(&object);
- EXPECT_EQ(object.ref_count_, 1);
- Bind(&LifeTimeCheck::NullaryVoid, &object)();
- EXPECT_EQ(object.ref_count_, 1);
- scoped_object = nullptr;
- EXPECT_EQ(object.ref_count_, 0);
-}
-
-// Test Bind with scoped_refptr<> argument.
-TEST(BindTest, ScopedRefPointerArgument) {
- LifeTimeCheck object;
- EXPECT_EQ(object.ref_count_, 0);
- scoped_refptr<LifeTimeCheck> scoped_object(&object);
- EXPECT_EQ(object.ref_count_, 1);
- {
- MethodBindTester bind_tester;
- auto functor =
- Bind(&MethodBindTester::RefArgument, &bind_tester, scoped_object);
- EXPECT_EQ(object.ref_count_, 2);
- }
- EXPECT_EQ(object.ref_count_, 1);
- scoped_object = nullptr;
- EXPECT_EQ(object.ref_count_, 0);
-}
-
-namespace {
-
-const int* Ref(const int& a) {
- return &a;
-}
-
-} // anonymous namespace
-
-// Test Bind with non-scoped_refptr<> reference argument, which should be
-// modified to a non-reference capture.
-TEST(BindTest, RefArgument) {
- const int x = 42;
- EXPECT_EQ(&x, Ref(x));
- // Bind() should make a copy of |x|, i.e. the pointers should be different.
- auto functor = Bind(&Ref, x);
- EXPECT_NE(&x, functor());
-}
-
-} // namespace rtc
diff --git a/rtc_base/boringssl_certificate.cc b/rtc_base/boringssl_certificate.cc
index 4e55cf3..bb14036 100644
--- a/rtc_base/boringssl_certificate.cc
+++ b/rtc_base/boringssl_certificate.cc
@@ -291,7 +291,7 @@
#define OID_MATCHES(oid, oid_other) \
(CBS_len(&oid) == sizeof(oid_other) && \
- 0 == memcmp(CBS_data(&oid), oid_other, sizeof(oid_other)))
+ 0 == memcmp(CBS_data(&oid), oid_other, sizeof(oid_other)))
bool BoringSSLCertificate::GetSignatureDigestAlgorithm(
std::string* algorithm) const {
diff --git a/rtc_base/buffer_queue.h b/rtc_base/buffer_queue.h
index 5895530..09c6c4f 100644
--- a/rtc_base/buffer_queue.h
+++ b/rtc_base/buffer_queue.h
@@ -16,9 +16,9 @@
#include <deque>
#include <vector>
+#include "api/sequence_checker.h"
#include "rtc_base/buffer.h"
#include "rtc_base/constructor_magic.h"
-#include "rtc_base/synchronization/sequence_checker.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/thread_annotations.h"
diff --git a/rtc_base/callback.h b/rtc_base/callback.h
deleted file mode 100644
index 4751221..0000000
--- a/rtc_base/callback.h
+++ /dev/null
@@ -1,250 +0,0 @@
-// This file was GENERATED by command:
-// pump.py callback.h.pump
-// DO NOT EDIT BY HAND!!!
-
-/*
- * Copyright 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.
- */
-
-// To generate callback.h from callback.h.pump, execute:
-// ../third_party/googletest/src/googletest/scripts/pump.py callback.h.pump
-
-// Callbacks are callable object containers. They can hold a function pointer
-// or a function object and behave like a value type. Internally, data is
-// reference-counted, making copies and pass-by-value inexpensive.
-//
-// Callbacks are typed using template arguments. The format is:
-// CallbackN<ReturnType, ParamType1, ..., ParamTypeN>
-// where N is the number of arguments supplied to the callable object.
-// Callbacks are invoked using operator(), just like a function or a function
-// object. Default-constructed callbacks are "empty," and executing an empty
-// callback does nothing. A callback can be made empty by assigning it from
-// a default-constructed callback.
-//
-// Callbacks are similar in purpose to std::function (which isn't available on
-// all platforms we support) and a lightweight alternative to sigslots. Since
-// they effectively hide the type of the object they call, they're useful in
-// breaking dependencies between objects that need to interact with one another.
-// Notably, they can hold the results of Bind(), std::bind*, etc, without
-// needing
-// to know the resulting object type of those calls.
-//
-// Sigslots, on the other hand, provide a fuller feature set, such as multiple
-// subscriptions to a signal, optional thread-safety, and lifetime tracking of
-// slots. When these features are needed, choose sigslots.
-//
-// Example:
-// int sqr(int x) { return x * x; }
-// struct AddK {
-// int k;
-// int operator()(int x) const { return x + k; }
-// } add_k = {5};
-//
-// Callback1<int, int> my_callback;
-// cout << my_callback.empty() << endl; // true
-//
-// my_callback = Callback1<int, int>(&sqr);
-// cout << my_callback.empty() << endl; // false
-// cout << my_callback(3) << endl; // 9
-//
-// my_callback = Callback1<int, int>(add_k);
-// cout << my_callback(10) << endl; // 15
-//
-// my_callback = Callback1<int, int>();
-// cout << my_callback.empty() << endl; // true
-
-#ifndef RTC_BASE_CALLBACK_H_
-#define RTC_BASE_CALLBACK_H_
-
-#include "api/scoped_refptr.h"
-#include "rtc_base/ref_count.h"
-#include "rtc_base/ref_counted_object.h"
-
-namespace rtc {
-
-template <class R>
-class Callback0 {
- public:
- // Default copy operations are appropriate for this class.
- Callback0() {}
- template <class T>
- Callback0(const T& functor)
- : helper_(new RefCountedObject<HelperImpl<T> >(functor)) {}
- R operator()() {
- if (empty())
- return R();
- return helper_->Run();
- }
- bool empty() const { return !helper_; }
-
- private:
- struct Helper : RefCountInterface {
- virtual ~Helper() {}
- virtual R Run() = 0;
- };
- template <class T>
- struct HelperImpl : Helper {
- explicit HelperImpl(const T& functor) : functor_(functor) {}
- virtual R Run() { return functor_(); }
- T functor_;
- };
- scoped_refptr<Helper> helper_;
-};
-
-template <class R, class P1>
-class Callback1 {
- public:
- // Default copy operations are appropriate for this class.
- Callback1() {}
- template <class T>
- Callback1(const T& functor)
- : helper_(new RefCountedObject<HelperImpl<T> >(functor)) {}
- R operator()(P1 p1) {
- if (empty())
- return R();
- return helper_->Run(p1);
- }
- bool empty() const { return !helper_; }
-
- private:
- struct Helper : RefCountInterface {
- virtual ~Helper() {}
- virtual R Run(P1 p1) = 0;
- };
- template <class T>
- struct HelperImpl : Helper {
- explicit HelperImpl(const T& functor) : functor_(functor) {}
- virtual R Run(P1 p1) { return functor_(p1); }
- T functor_;
- };
- scoped_refptr<Helper> helper_;
-};
-
-template <class R, class P1, class P2>
-class Callback2 {
- public:
- // Default copy operations are appropriate for this class.
- Callback2() {}
- template <class T>
- Callback2(const T& functor)
- : helper_(new RefCountedObject<HelperImpl<T> >(functor)) {}
- R operator()(P1 p1, P2 p2) {
- if (empty())
- return R();
- return helper_->Run(p1, p2);
- }
- bool empty() const { return !helper_; }
-
- private:
- struct Helper : RefCountInterface {
- virtual ~Helper() {}
- virtual R Run(P1 p1, P2 p2) = 0;
- };
- template <class T>
- struct HelperImpl : Helper {
- explicit HelperImpl(const T& functor) : functor_(functor) {}
- virtual R Run(P1 p1, P2 p2) { return functor_(p1, p2); }
- T functor_;
- };
- scoped_refptr<Helper> helper_;
-};
-
-template <class R, class P1, class P2, class P3>
-class Callback3 {
- public:
- // Default copy operations are appropriate for this class.
- Callback3() {}
- template <class T>
- Callback3(const T& functor)
- : helper_(new RefCountedObject<HelperImpl<T> >(functor)) {}
- R operator()(P1 p1, P2 p2, P3 p3) {
- if (empty())
- return R();
- return helper_->Run(p1, p2, p3);
- }
- bool empty() const { return !helper_; }
-
- private:
- struct Helper : RefCountInterface {
- virtual ~Helper() {}
- virtual R Run(P1 p1, P2 p2, P3 p3) = 0;
- };
- template <class T>
- struct HelperImpl : Helper {
- explicit HelperImpl(const T& functor) : functor_(functor) {}
- virtual R Run(P1 p1, P2 p2, P3 p3) { return functor_(p1, p2, p3); }
- T functor_;
- };
- scoped_refptr<Helper> helper_;
-};
-
-template <class R, class P1, class P2, class P3, class P4>
-class Callback4 {
- public:
- // Default copy operations are appropriate for this class.
- Callback4() {}
- template <class T>
- Callback4(const T& functor)
- : helper_(new RefCountedObject<HelperImpl<T> >(functor)) {}
- R operator()(P1 p1, P2 p2, P3 p3, P4 p4) {
- if (empty())
- return R();
- return helper_->Run(p1, p2, p3, p4);
- }
- bool empty() const { return !helper_; }
-
- private:
- struct Helper : RefCountInterface {
- virtual ~Helper() {}
- virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4) = 0;
- };
- template <class T>
- struct HelperImpl : Helper {
- explicit HelperImpl(const T& functor) : functor_(functor) {}
- virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4) {
- return functor_(p1, p2, p3, p4);
- }
- T functor_;
- };
- scoped_refptr<Helper> helper_;
-};
-
-template <class R, class P1, class P2, class P3, class P4, class P5>
-class Callback5 {
- public:
- // Default copy operations are appropriate for this class.
- Callback5() {}
- template <class T>
- Callback5(const T& functor)
- : helper_(new RefCountedObject<HelperImpl<T> >(functor)) {}
- R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) {
- if (empty())
- return R();
- return helper_->Run(p1, p2, p3, p4, p5);
- }
- bool empty() const { return !helper_; }
-
- private:
- struct Helper : RefCountInterface {
- virtual ~Helper() {}
- virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) = 0;
- };
- template <class T>
- struct HelperImpl : Helper {
- explicit HelperImpl(const T& functor) : functor_(functor) {}
- virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) {
- return functor_(p1, p2, p3, p4, p5);
- }
- T functor_;
- };
- scoped_refptr<Helper> helper_;
-};
-} // namespace rtc
-
-#endif // RTC_BASE_CALLBACK_H_
diff --git a/rtc_base/callback.h.pump b/rtc_base/callback.h.pump
deleted file mode 100644
index dc5fb3a..0000000
--- a/rtc_base/callback.h.pump
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright 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.
- */
-
-// To generate callback.h from callback.h.pump, execute:
-// ../third_party/googletest/src/googletest/scripts/pump.py callback.h.pump
-
-// Callbacks are callable object containers. They can hold a function pointer
-// or a function object and behave like a value type. Internally, data is
-// reference-counted, making copies and pass-by-value inexpensive.
-//
-// Callbacks are typed using template arguments. The format is:
-// CallbackN<ReturnType, ParamType1, ..., ParamTypeN>
-// where N is the number of arguments supplied to the callable object.
-// Callbacks are invoked using operator(), just like a function or a function
-// object. Default-constructed callbacks are "empty," and executing an empty
-// callback does nothing. A callback can be made empty by assigning it from
-// a default-constructed callback.
-//
-// Callbacks are similar in purpose to std::function (which isn't available on
-// all platforms we support) and a lightweight alternative to sigslots. Since
-// they effectively hide the type of the object they call, they're useful in
-// breaking dependencies between objects that need to interact with one another.
-// Notably, they can hold the results of Bind(), std::bind*, etc, without needing
-// to know the resulting object type of those calls.
-//
-// Sigslots, on the other hand, provide a fuller feature set, such as multiple
-// subscriptions to a signal, optional thread-safety, and lifetime tracking of
-// slots. When these features are needed, choose sigslots.
-//
-// Example:
-// int sqr(int x) { return x * x; }
-// struct AddK {
-// int k;
-// int operator()(int x) const { return x + k; }
-// } add_k = {5};
-//
-// Callback1<int, int> my_callback;
-// cout << my_callback.empty() << endl; // true
-//
-// my_callback = Callback1<int, int>(&sqr);
-// cout << my_callback.empty() << endl; // false
-// cout << my_callback(3) << endl; // 9
-//
-// my_callback = Callback1<int, int>(add_k);
-// cout << my_callback(10) << endl; // 15
-//
-// my_callback = Callback1<int, int>();
-// cout << my_callback.empty() << endl; // true
-
-#ifndef RTC_BASE_CALLBACK_H_
-#define RTC_BASE_CALLBACK_H_
-
-#include "rtc_base/ref_count.h"
-#include "rtc_base/ref_counted_object.h"
-#include "api/scoped_refptr.h"
-
-namespace rtc {
-
-$var n = 5
-$range i 0..n
-$for i [[
-$range j 1..i
-
-template <class R$for j [[,
- class P$j]]>
-class Callback$i {
- public:
- // Default copy operations are appropriate for this class.
- Callback$i() {}
- template <class T> Callback$i(const T& functor)
- : helper_(new RefCountedObject< HelperImpl<T> >(functor)) {}
- R operator()($for j , [[P$j p$j]]) {
- if (empty())
- return R();
- return helper_->Run($for j , [[p$j]]);
- }
- bool empty() const { return !helper_; }
-
- private:
- struct Helper : RefCountInterface {
- virtual ~Helper() {}
- virtual R Run($for j , [[P$j p$j]]) = 0;
- };
- template <class T> struct HelperImpl : Helper {
- explicit HelperImpl(const T& functor) : functor_(functor) {}
- virtual R Run($for j , [[P$j p$j]]) {
- return functor_($for j , [[p$j]]);
- }
- T functor_;
- };
- scoped_refptr<Helper> helper_;
-};
-
-]]
-} // namespace rtc
-
-#endif // RTC_BASE_CALLBACK_H_
diff --git a/rtc_base/callback_list_unittest.cc b/rtc_base/callback_list_unittest.cc
index 119f88f..665d779 100644
--- a/rtc_base/callback_list_unittest.cc
+++ b/rtc_base/callback_list_unittest.cc
@@ -11,7 +11,6 @@
#include <type_traits>
#include "api/function_view.h"
-#include "rtc_base/bind.h"
#include "rtc_base/callback_list.h"
#include "test/gtest.h"
@@ -209,8 +208,6 @@
}
// todo(glahiru): Add a test case to catch some error for Karl's first fix
-// todo(glahiru): Add a test for rtc::Bind
-// which used the following code in the Send
TEST(CallbackList, RemoveOneReceiver) {
int removal_tag[2];
diff --git a/rtc_base/callback_unittest.cc b/rtc_base/callback_unittest.cc
deleted file mode 100644
index 8767295..0000000
--- a/rtc_base/callback_unittest.cc
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright 2004 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 "rtc_base/callback.h"
-
-#include "rtc_base/bind.h"
-#include "rtc_base/keep_ref_until_done.h"
-#include "rtc_base/ref_count.h"
-#include "test/gtest.h"
-
-namespace rtc {
-
-namespace {
-
-void f() {}
-int g() {
- return 42;
-}
-int h(int x) {
- return x * x;
-}
-void i(int& x) {
- x *= x;
-} // NOLINT: Testing refs
-
-struct BindTester {
- int a() { return 24; }
- int b(int x) const { return x * x; }
-};
-
-class RefCountedBindTester : public RefCountInterface {
- public:
- RefCountedBindTester() : count_(0) {}
- void AddRef() const override { ++count_; }
- RefCountReleaseStatus Release() const override {
- --count_;
- return count_ == 0 ? RefCountReleaseStatus::kDroppedLastRef
- : RefCountReleaseStatus::kOtherRefsRemained;
- }
- int RefCount() const { return count_; }
-
- private:
- mutable int count_;
-};
-
-} // namespace
-
-TEST(CallbackTest, VoidReturn) {
- Callback0<void> cb;
- EXPECT_TRUE(cb.empty());
- cb(); // Executing an empty callback should not crash.
- cb = Callback0<void>(&f);
- EXPECT_FALSE(cb.empty());
- cb();
-}
-
-TEST(CallbackTest, IntReturn) {
- Callback0<int> cb;
- EXPECT_TRUE(cb.empty());
- cb = Callback0<int>(&g);
- EXPECT_FALSE(cb.empty());
- EXPECT_EQ(42, cb());
- EXPECT_EQ(42, cb());
-}
-
-TEST(CallbackTest, OneParam) {
- Callback1<int, int> cb1(&h);
- EXPECT_FALSE(cb1.empty());
- EXPECT_EQ(9, cb1(-3));
- EXPECT_EQ(100, cb1(10));
-
- // Try clearing a callback.
- cb1 = Callback1<int, int>();
- EXPECT_TRUE(cb1.empty());
-
- // Try a callback with a ref parameter.
- Callback1<void, int&> cb2(&i);
- int x = 3;
- cb2(x);
- EXPECT_EQ(9, x);
- cb2(x);
- EXPECT_EQ(81, x);
-}
-
-TEST(CallbackTest, WithBind) {
- BindTester t;
- Callback0<int> cb1 = Bind(&BindTester::a, &t);
- EXPECT_EQ(24, cb1());
- EXPECT_EQ(24, cb1());
- cb1 = Bind(&BindTester::b, &t, 10);
- EXPECT_EQ(100, cb1());
- EXPECT_EQ(100, cb1());
- cb1 = Bind(&BindTester::b, &t, 5);
- EXPECT_EQ(25, cb1());
- EXPECT_EQ(25, cb1());
-}
-
-TEST(KeepRefUntilDoneTest, simple) {
- RefCountedBindTester t;
- EXPECT_EQ(0, t.RefCount());
- {
- Callback0<void> cb = KeepRefUntilDone(&t);
- EXPECT_EQ(1, t.RefCount());
- cb();
- EXPECT_EQ(1, t.RefCount());
- cb();
- EXPECT_EQ(1, t.RefCount());
- }
- EXPECT_EQ(0, t.RefCount());
-}
-
-TEST(KeepRefUntilDoneTest, copy) {
- RefCountedBindTester t;
- EXPECT_EQ(0, t.RefCount());
- Callback0<void> cb2;
- {
- Callback0<void> cb = KeepRefUntilDone(&t);
- EXPECT_EQ(1, t.RefCount());
- cb2 = cb;
- }
- EXPECT_EQ(1, t.RefCount());
- cb2 = Callback0<void>();
- EXPECT_EQ(0, t.RefCount());
-}
-
-TEST(KeepRefUntilDoneTest, scopedref) {
- RefCountedBindTester t;
- EXPECT_EQ(0, t.RefCount());
- {
- scoped_refptr<RefCountedBindTester> t_scoped_ref(&t);
- Callback0<void> cb = KeepRefUntilDone(t_scoped_ref);
- t_scoped_ref = nullptr;
- EXPECT_EQ(1, t.RefCount());
- cb();
- EXPECT_EQ(1, t.RefCount());
- }
- EXPECT_EQ(0, t.RefCount());
-}
-
-} // namespace rtc
diff --git a/rtc_base/copy_on_write_buffer.cc b/rtc_base/copy_on_write_buffer.cc
index 73182a1..f3cc710 100644
--- a/rtc_base/copy_on_write_buffer.cc
+++ b/rtc_base/copy_on_write_buffer.cc
@@ -32,16 +32,15 @@
: CopyOnWriteBuffer(s.data(), s.length()) {}
CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size)
- : buffer_(size > 0 ? new RefCountedObject<Buffer>(size) : nullptr),
+ : buffer_(size > 0 ? new RefCountedBuffer(size) : nullptr),
offset_(0),
size_(size) {
RTC_DCHECK(IsConsistent());
}
CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size, size_t capacity)
- : buffer_(size > 0 || capacity > 0
- ? new RefCountedObject<Buffer>(size, capacity)
- : nullptr),
+ : buffer_(size > 0 || capacity > 0 ? new RefCountedBuffer(size, capacity)
+ : nullptr),
offset_(0),
size_(size) {
RTC_DCHECK(IsConsistent());
@@ -61,7 +60,7 @@
RTC_DCHECK(IsConsistent());
if (!buffer_) {
if (size > 0) {
- buffer_ = new RefCountedObject<Buffer>(size);
+ buffer_ = new RefCountedBuffer(size);
offset_ = 0;
size_ = size;
}
@@ -84,7 +83,7 @@
RTC_DCHECK(IsConsistent());
if (!buffer_) {
if (new_capacity > 0) {
- buffer_ = new RefCountedObject<Buffer>(0, new_capacity);
+ buffer_ = new RefCountedBuffer(0, new_capacity);
offset_ = 0;
size_ = 0;
}
@@ -105,7 +104,7 @@
if (buffer_->HasOneRef()) {
buffer_->Clear();
} else {
- buffer_ = new RefCountedObject<Buffer>(0, capacity());
+ buffer_ = new RefCountedBuffer(0, capacity());
}
offset_ = 0;
size_ = 0;
@@ -117,8 +116,8 @@
return;
}
- buffer_ = new RefCountedObject<Buffer>(buffer_->data() + offset_, size_,
- new_capacity);
+ buffer_ =
+ new RefCountedBuffer(buffer_->data() + offset_, size_, new_capacity);
offset_ = 0;
RTC_DCHECK(IsConsistent());
}
diff --git a/rtc_base/copy_on_write_buffer.h b/rtc_base/copy_on_write_buffer.h
index 87bf625..526cbe5 100644
--- a/rtc_base/copy_on_write_buffer.h
+++ b/rtc_base/copy_on_write_buffer.h
@@ -95,14 +95,6 @@
return buffer_->data<T>() + offset_;
}
- // TODO(bugs.webrtc.org/12334): Delete when all usage updated to MutableData()
- template <typename T = uint8_t,
- typename std::enable_if<
- internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
- T* data() {
- return MutableData<T>();
- }
-
// Get const pointer to the data. This will not create a copy of the
// underlying data if it is shared with other buffers.
template <typename T = uint8_t,
@@ -154,12 +146,6 @@
return !(*this == buf);
}
- // TODO(bugs.webrtc.org/12334): Delete when all usage updated to MutableData()
- uint8_t& operator[](size_t index) {
- RTC_DCHECK_LT(index, size());
- return MutableData()[index];
- }
-
uint8_t operator[](size_t index) const {
RTC_DCHECK_LT(index, size());
return cdata()[index];
@@ -173,9 +159,9 @@
void SetData(const T* data, size_t size) {
RTC_DCHECK(IsConsistent());
if (!buffer_) {
- buffer_ = size > 0 ? new RefCountedObject<Buffer>(data, size) : nullptr;
+ buffer_ = size > 0 ? new RefCountedBuffer(data, size) : nullptr;
} else if (!buffer_->HasOneRef()) {
- buffer_ = new RefCountedObject<Buffer>(data, size, capacity());
+ buffer_ = new RefCountedBuffer(data, size, capacity());
} else {
buffer_->SetData(data, size);
}
@@ -210,7 +196,7 @@
void AppendData(const T* data, size_t size) {
RTC_DCHECK(IsConsistent());
if (!buffer_) {
- buffer_ = new RefCountedObject<Buffer>(data, size);
+ buffer_ = new RefCountedBuffer(data, size);
offset_ = 0;
size_ = size;
RTC_DCHECK(IsConsistent());
@@ -256,7 +242,7 @@
// Swaps two buffers.
friend void swap(CopyOnWriteBuffer& a, CopyOnWriteBuffer& b) {
- std::swap(a.buffer_, b.buffer_);
+ a.buffer_.swap(b.buffer_);
std::swap(a.offset_, b.offset_);
std::swap(a.size_, b.size_);
}
@@ -271,6 +257,7 @@
}
private:
+ using RefCountedBuffer = FinalRefCountedObject<Buffer>;
// Create a copy of the underlying data if it is referenced from other Buffer
// objects or there is not enough capacity.
void UnshareAndEnsureCapacity(size_t new_capacity);
@@ -286,7 +273,7 @@
}
// buffer_ is either null, or points to an rtc::Buffer with capacity > 0.
- scoped_refptr<RefCountedObject<Buffer>> buffer_;
+ scoped_refptr<RefCountedBuffer> buffer_;
// This buffer may represent a slice of a original data.
size_t offset_; // Offset of a current slice in the original data in buffer_.
// Should be 0 if the buffer_ is empty.
diff --git a/rtc_base/copy_on_write_buffer_unittest.cc b/rtc_base/copy_on_write_buffer_unittest.cc
index 5c29c10..d397868 100644
--- a/rtc_base/copy_on_write_buffer_unittest.cc
+++ b/rtc_base/copy_on_write_buffer_unittest.cc
@@ -261,47 +261,34 @@
EXPECT_EQ(10u, buf2.capacity());
}
-TEST(CopyOnWriteBufferTest, TestConstDataAccessor) {
+TEST(CopyOnWriteBufferTest, DataAccessorDoesntCloneData) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
- // .cdata() doesn't clone data.
- const uint8_t* cdata1 = buf1.cdata();
- const uint8_t* cdata2 = buf2.cdata();
- EXPECT_EQ(cdata1, cdata2);
-
- // Non-const .data() clones data if shared.
- const uint8_t* data1 = buf1.data();
- const uint8_t* data2 = buf2.data();
- EXPECT_NE(data1, data2);
- // buf1 was cloned above.
- EXPECT_NE(data1, cdata1);
- // Therefore buf2 was no longer sharing data and was not cloned.
- EXPECT_EQ(data2, cdata1);
+ EXPECT_EQ(buf1.data(), buf2.data());
}
-// TODO(bugs.webrtc.org/12334): Delete when all reads become const
+TEST(CopyOnWriteBufferTest, MutableDataClonesDataWhenShared) {
+ CopyOnWriteBuffer buf1(kTestData, 3, 10);
+ CopyOnWriteBuffer buf2(buf1);
+ const uint8_t* cdata = buf1.data();
+
+ uint8_t* data1 = buf1.MutableData();
+ uint8_t* data2 = buf2.MutableData();
+ // buf1 was cloned above.
+ EXPECT_NE(data1, cdata);
+ // Therefore buf2 was no longer sharing data and was not cloned.
+ EXPECT_EQ(data2, cdata);
+}
+
TEST(CopyOnWriteBufferTest, SeveralReads) {
CopyOnWriteBuffer buf1(kTestData, 3, 10);
CopyOnWriteBuffer buf2(buf1);
EnsureBuffersShareData(buf1, buf2);
- // Non-const reads clone the data if shared.
for (size_t i = 0; i != 3u; ++i) {
EXPECT_EQ(buf1[i], kTestData[i]);
}
- EnsureBuffersDontShareData(buf1, buf2);
-}
-
-TEST(CopyOnWriteBufferTest, SeveralConstReads) {
- CopyOnWriteBuffer buf1(kTestData, 3, 10);
- CopyOnWriteBuffer buf2(buf1);
-
- EnsureBuffersShareData(buf1, buf2);
- const CopyOnWriteBuffer& cbuf1 = buf1;
- for (size_t i = 0; i != 3u; ++i) {
- EXPECT_EQ(cbuf1[i], kTestData[i]);
- }
EnsureBuffersShareData(buf1, buf2);
}
diff --git a/rtc_base/deprecated/signal_thread.cc b/rtc_base/deprecated/signal_thread.cc
deleted file mode 100644
index 96bdd65..0000000
--- a/rtc_base/deprecated/signal_thread.cc
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright 2004 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 "rtc_base/deprecated/signal_thread.h"
-
-#include <memory>
-
-#include "rtc_base/checks.h"
-#include "rtc_base/location.h"
-#include "rtc_base/null_socket_server.h"
-#include "rtc_base/socket_server.h"
-
-namespace rtc {
-
-///////////////////////////////////////////////////////////////////////////////
-// SignalThread
-///////////////////////////////////////////////////////////////////////////////
-
-DEPRECATED_SignalThread::DEPRECATED_SignalThread()
- : main_(Thread::Current()), worker_(this), state_(kInit), refcount_(1) {
- main_->SignalQueueDestroyed.connect(
- this, &DEPRECATED_SignalThread::OnMainThreadDestroyed);
- worker_.SetName("SignalThread", this);
-}
-
-DEPRECATED_SignalThread::~DEPRECATED_SignalThread() {
- rtc::CritScope lock(&cs_);
- RTC_DCHECK(refcount_ == 0);
-}
-
-bool DEPRECATED_SignalThread::SetName(const std::string& name,
- const void* obj) {
- EnterExit ee(this);
- RTC_DCHECK(!destroy_called_);
- RTC_DCHECK(main_->IsCurrent());
- RTC_DCHECK(kInit == state_);
- return worker_.SetName(name, obj);
-}
-
-void DEPRECATED_SignalThread::Start() {
- EnterExit ee(this);
- RTC_DCHECK(!destroy_called_);
- RTC_DCHECK(main_->IsCurrent());
- if (kInit == state_ || kComplete == state_) {
- state_ = kRunning;
- OnWorkStart();
- worker_.Start();
- } else {
- RTC_NOTREACHED();
- }
-}
-
-void DEPRECATED_SignalThread::Destroy(bool wait) {
- EnterExit ee(this);
- // Sometimes the caller can't guarantee which thread will call Destroy, only
- // that it will be the last thing it does.
- // RTC_DCHECK(main_->IsCurrent());
- RTC_DCHECK(!destroy_called_);
- destroy_called_ = true;
- if ((kInit == state_) || (kComplete == state_)) {
- refcount_--;
- } else if (kRunning == state_ || kReleasing == state_) {
- state_ = kStopping;
- // OnWorkStop() must follow Quit(), so that when the thread wakes up due to
- // OWS(), ContinueWork() will return false.
- worker_.Quit();
- OnWorkStop();
- if (wait) {
- // Release the thread's lock so that it can return from ::Run.
- cs_.Leave();
- worker_.Stop();
- cs_.Enter();
- refcount_--;
- }
- } else {
- RTC_NOTREACHED();
- }
-}
-
-void DEPRECATED_SignalThread::Release() {
- EnterExit ee(this);
- RTC_DCHECK(!destroy_called_);
- RTC_DCHECK(main_->IsCurrent());
- if (kComplete == state_) {
- refcount_--;
- } else if (kRunning == state_) {
- state_ = kReleasing;
- } else {
- // if (kInit == state_) use Destroy()
- RTC_NOTREACHED();
- }
-}
-
-bool DEPRECATED_SignalThread::ContinueWork() {
- EnterExit ee(this);
- RTC_DCHECK(!destroy_called_);
- RTC_DCHECK(worker_.IsCurrent());
- return worker_.ProcessMessages(0);
-}
-
-void DEPRECATED_SignalThread::OnMessage(Message* msg) {
- EnterExit ee(this);
- if (ST_MSG_WORKER_DONE == msg->message_id) {
- RTC_DCHECK(main_->IsCurrent());
- OnWorkDone();
- bool do_delete = false;
- if (kRunning == state_) {
- state_ = kComplete;
- } else {
- do_delete = true;
- }
- if (kStopping != state_) {
- // Before signaling that the work is done, make sure that the worker
- // thread actually is done. We got here because DoWork() finished and
- // Run() posted the ST_MSG_WORKER_DONE message. This means the worker
- // thread is about to go away anyway, but sometimes it doesn't actually
- // finish before SignalWorkDone is processed, and for a reusable
- // SignalThread this makes an assert in thread.cc fire.
- //
- // Calling Stop() on the worker ensures that the OS thread that underlies
- // the worker will finish, and will be set to null, enabling us to call
- // Start() again.
- worker_.Stop();
- SignalWorkDone(this);
- }
- if (do_delete) {
- refcount_--;
- }
- }
-}
-
-DEPRECATED_SignalThread::Worker::Worker(DEPRECATED_SignalThread* parent)
- : Thread(std::make_unique<NullSocketServer>(), /*do_init=*/false),
- parent_(parent) {
- DoInit();
-}
-
-DEPRECATED_SignalThread::Worker::~Worker() {
- Stop();
-}
-
-void DEPRECATED_SignalThread::Worker::Run() {
- parent_->Run();
-}
-
-void DEPRECATED_SignalThread::Run() {
- DoWork();
- {
- EnterExit ee(this);
- if (main_) {
- main_->Post(RTC_FROM_HERE, this, ST_MSG_WORKER_DONE);
- }
- }
-}
-
-void DEPRECATED_SignalThread::OnMainThreadDestroyed() {
- EnterExit ee(this);
- main_ = nullptr;
-}
-
-bool DEPRECATED_SignalThread::Worker::IsProcessingMessagesForTesting() {
- return false;
-}
-
-} // namespace rtc
diff --git a/rtc_base/deprecated/signal_thread.h b/rtc_base/deprecated/signal_thread.h
deleted file mode 100644
index 10805ad..0000000
--- a/rtc_base/deprecated/signal_thread.h
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright 2004 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 RTC_BASE_DEPRECATED_SIGNAL_THREAD_H_
-#define RTC_BASE_DEPRECATED_SIGNAL_THREAD_H_
-
-#include <string>
-
-#include "rtc_base/checks.h"
-#include "rtc_base/constructor_magic.h"
-#include "rtc_base/deprecated/recursive_critical_section.h"
-#include "rtc_base/deprecation.h"
-#include "rtc_base/message_handler.h"
-#include "rtc_base/third_party/sigslot/sigslot.h"
-#include "rtc_base/thread.h"
-#include "rtc_base/thread_annotations.h"
-
-namespace rtc {
-
-///////////////////////////////////////////////////////////////////////////////
-// NOTE: this class has been deprecated. Do not use for new code. New code
-// should use factilities exposed by api/task_queue/ instead.
-//
-// SignalThread - Base class for worker threads. The main thread should call
-// Start() to begin work, and then follow one of these models:
-// Normal: Wait for SignalWorkDone, and then call Release to destroy.
-// Cancellation: Call Release(true), to abort the worker thread.
-// Fire-and-forget: Call Release(false), which allows the thread to run to
-// completion, and then self-destruct without further notification.
-// Periodic tasks: Wait for SignalWorkDone, then eventually call Start()
-// again to repeat the task. When the instance isn't needed anymore,
-// call Release. DoWork, OnWorkStart and OnWorkStop are called again,
-// on a new thread.
-// The subclass should override DoWork() to perform the background task. By
-// periodically calling ContinueWork(), it can check for cancellation.
-// OnWorkStart and OnWorkDone can be overridden to do pre- or post-work
-// tasks in the context of the main thread.
-///////////////////////////////////////////////////////////////////////////////
-
-class DEPRECATED_SignalThread : public sigslot::has_slots<>,
- protected MessageHandlerAutoCleanup {
- public:
- DEPRECATED_SignalThread();
-
- // Context: Main Thread. Call before Start to change the worker's name.
- bool SetName(const std::string& name, const void* obj);
-
- // Context: Main Thread. Call to begin the worker thread.
- void Start();
-
- // Context: Main Thread. If the worker thread is not running, deletes the
- // object immediately. Otherwise, asks the worker thread to abort processing,
- // and schedules the object to be deleted once the worker exits.
- // SignalWorkDone will not be signalled. If wait is true, does not return
- // until the thread is deleted.
- void Destroy(bool wait);
-
- // Context: Main Thread. If the worker thread is complete, deletes the
- // object immediately. Otherwise, schedules the object to be deleted once
- // the worker thread completes. SignalWorkDone will be signalled.
- void Release();
-
- // Context: Main Thread. Signalled when work is complete.
- sigslot::signal1<DEPRECATED_SignalThread*> SignalWorkDone;
-
- enum { ST_MSG_WORKER_DONE, ST_MSG_FIRST_AVAILABLE };
-
- protected:
- ~DEPRECATED_SignalThread() override;
-
- Thread* worker() { return &worker_; }
-
- // Context: Main Thread. Subclass should override to do pre-work setup.
- virtual void OnWorkStart() {}
-
- // Context: Worker Thread. Subclass should override to do work.
- virtual void DoWork() = 0;
-
- // Context: Worker Thread. Subclass should call periodically to
- // dispatch messages and determine if the thread should terminate.
- bool ContinueWork();
-
- // Context: Worker Thread. Subclass should override when extra work is
- // needed to abort the worker thread.
- virtual void OnWorkStop() {}
-
- // Context: Main Thread. Subclass should override to do post-work cleanup.
- virtual void OnWorkDone() {}
-
- // Context: Any Thread. If subclass overrides, be sure to call the base
- // implementation. Do not use (message_id < ST_MSG_FIRST_AVAILABLE)
- void OnMessage(Message* msg) override;
-
- private:
- enum State {
- kInit, // Initialized, but not started
- kRunning, // Started and doing work
- kReleasing, // Same as running, but to be deleted when work is done
- kComplete, // Work is done
- kStopping, // Work is being interrupted
- };
-
- class Worker : public Thread {
- public:
- explicit Worker(DEPRECATED_SignalThread* parent);
-
- Worker() = delete;
- Worker(const Worker&) = delete;
- Worker& operator=(const Worker&) = delete;
-
- ~Worker() override;
- void Run() override;
- bool IsProcessingMessagesForTesting() override;
-
- private:
- DEPRECATED_SignalThread* parent_;
- };
-
- class RTC_SCOPED_LOCKABLE EnterExit {
- public:
- explicit EnterExit(DEPRECATED_SignalThread* t)
- RTC_EXCLUSIVE_LOCK_FUNCTION(t->cs_)
- : t_(t) {
- t_->cs_.Enter();
- // If refcount_ is zero then the object has already been deleted and we
- // will be double-deleting it in ~EnterExit()! (shouldn't happen)
- RTC_DCHECK_NE(0, t_->refcount_);
- ++t_->refcount_;
- }
-
- EnterExit() = delete;
- EnterExit(const EnterExit&) = delete;
- EnterExit& operator=(const EnterExit&) = delete;
-
- ~EnterExit() RTC_UNLOCK_FUNCTION() {
- bool d = (0 == --t_->refcount_);
- t_->cs_.Leave();
- if (d)
- delete t_;
- }
-
- private:
- DEPRECATED_SignalThread* t_;
- };
-
- void Run();
- void OnMainThreadDestroyed();
-
- Thread* main_;
- Worker worker_;
- RecursiveCriticalSection cs_;
- State state_ RTC_GUARDED_BY(cs_);
- int refcount_ RTC_GUARDED_BY(cs_);
- bool destroy_called_ RTC_GUARDED_BY(cs_) = false;
-
- RTC_DISALLOW_COPY_AND_ASSIGN(DEPRECATED_SignalThread);
-};
-
-typedef RTC_DEPRECATED DEPRECATED_SignalThread SignalThread;
-
-///////////////////////////////////////////////////////////////////////////////
-
-} // namespace rtc
-
-#endif // RTC_BASE_DEPRECATED_SIGNAL_THREAD_H_
diff --git a/rtc_base/deprecated/signal_thread_unittest.cc b/rtc_base/deprecated/signal_thread_unittest.cc
deleted file mode 100644
index f5a49aa..0000000
--- a/rtc_base/deprecated/signal_thread_unittest.cc
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright 2004 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 "rtc_base/signal_thread.h"
-
-#include <memory>
-
-#include "rtc_base/constructor_magic.h"
-#include "rtc_base/gunit.h"
-#include "rtc_base/null_socket_server.h"
-#include "rtc_base/synchronization/mutex.h"
-#include "rtc_base/thread.h"
-#include "rtc_base/thread_annotations.h"
-#include "test/gtest.h"
-
-namespace rtc {
-namespace {
-
-// 10 seconds.
-static const int kTimeout = 10000;
-
-class SignalThreadTest : public ::testing::Test, public sigslot::has_slots<> {
- public:
- class SlowSignalThread : public DEPRECATED_SignalThread {
- public:
- explicit SlowSignalThread(SignalThreadTest* harness) : harness_(harness) {}
-
- ~SlowSignalThread() override {
- EXPECT_EQ(harness_->main_thread_, Thread::Current());
- ++harness_->thread_deleted_;
- }
-
- const SignalThreadTest* harness() { return harness_; }
-
- protected:
- void OnWorkStart() override {
- ASSERT_TRUE(harness_ != nullptr);
- ++harness_->thread_started_;
- EXPECT_EQ(harness_->main_thread_, Thread::Current());
- EXPECT_FALSE(worker()->RunningForTest()); // not started yet
- }
-
- void OnWorkStop() override {
- ++harness_->thread_stopped_;
- EXPECT_EQ(harness_->main_thread_, Thread::Current());
- EXPECT_TRUE(worker()->RunningForTest()); // not stopped yet
- }
-
- void OnWorkDone() override {
- ++harness_->thread_done_;
- EXPECT_EQ(harness_->main_thread_, Thread::Current());
- EXPECT_TRUE(worker()->RunningForTest()); // not stopped yet
- }
-
- void DoWork() override {
- EXPECT_NE(harness_->main_thread_, Thread::Current());
- EXPECT_EQ(worker(), Thread::Current());
- Thread::Current()->socketserver()->Wait(250, false);
- }
-
- private:
- SignalThreadTest* harness_;
- RTC_DISALLOW_COPY_AND_ASSIGN(SlowSignalThread);
- };
-
- void OnWorkComplete(rtc::DEPRECATED_SignalThread* thread) {
- SlowSignalThread* t = static_cast<SlowSignalThread*>(thread);
- EXPECT_EQ(t->harness(), this);
- EXPECT_EQ(main_thread_, Thread::Current());
-
- ++thread_completed_;
- if (!called_release_) {
- thread->Release();
- }
- }
-
- void SetUp() override {
- main_thread_ = Thread::Current();
- thread_ = new SlowSignalThread(this);
- thread_->SignalWorkDone.connect(this, &SignalThreadTest::OnWorkComplete);
- called_release_ = false;
- thread_started_ = 0;
- thread_done_ = 0;
- thread_completed_ = 0;
- thread_stopped_ = 0;
- thread_deleted_ = 0;
- }
-
- void ExpectState(int started,
- int done,
- int completed,
- int stopped,
- int deleted) {
- EXPECT_EQ(started, thread_started_);
- EXPECT_EQ(done, thread_done_);
- EXPECT_EQ(completed, thread_completed_);
- EXPECT_EQ(stopped, thread_stopped_);
- EXPECT_EQ(deleted, thread_deleted_);
- }
-
- void ExpectStateWait(int started,
- int done,
- int completed,
- int stopped,
- int deleted,
- int timeout) {
- EXPECT_EQ_WAIT(started, thread_started_, timeout);
- EXPECT_EQ_WAIT(done, thread_done_, timeout);
- EXPECT_EQ_WAIT(completed, thread_completed_, timeout);
- EXPECT_EQ_WAIT(stopped, thread_stopped_, timeout);
- EXPECT_EQ_WAIT(deleted, thread_deleted_, timeout);
- }
-
- Thread* main_thread_;
- SlowSignalThread* thread_;
- bool called_release_;
-
- int thread_started_;
- int thread_done_;
- int thread_completed_;
- int thread_stopped_;
- int thread_deleted_;
-};
-
-class OwnerThread : public Thread, public sigslot::has_slots<> {
- public:
- explicit OwnerThread(SignalThreadTest* harness)
- : Thread(std::make_unique<NullSocketServer>()),
- harness_(harness),
- has_run_(false) {}
-
- ~OwnerThread() override { Stop(); }
-
- void Run() override {
- SignalThreadTest::SlowSignalThread* signal_thread =
- new SignalThreadTest::SlowSignalThread(harness_);
- signal_thread->SignalWorkDone.connect(this, &OwnerThread::OnWorkDone);
- signal_thread->Start();
- Thread::Current()->socketserver()->Wait(100, false);
- signal_thread->Release();
- // Delete |signal_thread|.
- signal_thread->Destroy(true);
- {
- webrtc::MutexLock lock(&mutex_);
- has_run_ = true;
- }
- }
-
- bool has_run() {
- webrtc::MutexLock lock(&mutex_);
- return has_run_;
- }
- void OnWorkDone(DEPRECATED_SignalThread* /*signal_thread*/) {
- FAIL() << " This shouldn't get called.";
- }
-
- private:
- webrtc::Mutex mutex_;
- SignalThreadTest* harness_;
- bool has_run_ RTC_GUARDED_BY(mutex_);
- RTC_DISALLOW_COPY_AND_ASSIGN(OwnerThread);
-};
-
-// Test for when the main thread goes away while the
-// signal thread is still working. This may happen
-// when shutting down the process.
-TEST_F(SignalThreadTest, OwnerThreadGoesAway) {
- // We don't use |thread_| for this test, so destroy it.
- thread_->Destroy(true);
-
- {
- std::unique_ptr<OwnerThread> owner(new OwnerThread(this));
- main_thread_ = owner.get();
- owner->Start();
- while (!owner->has_run()) {
- Thread::Current()->socketserver()->Wait(10, false);
- }
- }
- // At this point the main thread has gone away.
- // Give the SignalThread a little time to do its callback,
- // which will crash if the signal thread doesn't handle
- // this situation well.
- Thread::Current()->socketserver()->Wait(500, false);
-}
-
-TEST_F(SignalThreadTest, ThreadFinishes) {
- thread_->Start();
- ExpectState(1, 0, 0, 0, 0);
- ExpectStateWait(1, 1, 1, 0, 1, kTimeout);
-}
-
-TEST_F(SignalThreadTest, ReleasedThreadFinishes) {
- thread_->Start();
- ExpectState(1, 0, 0, 0, 0);
- thread_->Release();
- called_release_ = true;
- ExpectState(1, 0, 0, 0, 0);
- ExpectStateWait(1, 1, 1, 0, 1, kTimeout);
-}
-
-TEST_F(SignalThreadTest, DestroyedThreadCleansUp) {
- thread_->Start();
- ExpectState(1, 0, 0, 0, 0);
- thread_->Destroy(true);
- ExpectState(1, 0, 0, 1, 1);
- Thread::Current()->ProcessMessages(0);
- ExpectState(1, 0, 0, 1, 1);
-}
-
-TEST_F(SignalThreadTest, DeferredDestroyedThreadCleansUp) {
- thread_->Start();
- ExpectState(1, 0, 0, 0, 0);
- thread_->Destroy(false);
- ExpectState(1, 0, 0, 1, 0);
- ExpectStateWait(1, 1, 0, 1, 1, kTimeout);
-}
-
-} // namespace
-} // namespace rtc
diff --git a/rtc_base/deprecation.h b/rtc_base/deprecation.h
deleted file mode 100644
index f285ab0..0000000
--- a/rtc_base/deprecation.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef RTC_BASE_DEPRECATION_H_
-#define RTC_BASE_DEPRECATION_H_
-
-// Annotate the declarations of deprecated functions with this to cause a
-// compiler warning when they're used. Like so:
-//
-// RTC_DEPRECATED std::pony PonyPlz(const std::pony_spec& ps);
-//
-// NOTE 1: The annotation goes on the declaration in the .h file, not the
-// definition in the .cc file!
-//
-// NOTE 2: In order to keep unit testing the deprecated function without
-// getting warnings, do something like this:
-//
-// std::pony DEPRECATED_PonyPlz(const std::pony_spec& ps);
-// RTC_DEPRECATED inline std::pony PonyPlz(const std::pony_spec& ps) {
-// return DEPRECATED_PonyPlz(ps);
-// }
-//
-// In other words, rename the existing function, and provide an inline wrapper
-// using the original name that calls it. That way, callers who are willing to
-// call it using the DEPRECATED_-prefixed name don't get the warning.
-//
-// TODO(kwiberg): Remove this when we can use [[deprecated]] from C++14.
-#if defined(_MSC_VER)
-// Note: Deprecation warnings seem to fail to trigger on Windows
-// (https://bugs.chromium.org/p/webrtc/issues/detail?id=5368).
-#define RTC_DEPRECATED __declspec(deprecated)
-#elif defined(__GNUC__)
-#define RTC_DEPRECATED __attribute__((__deprecated__))
-#else
-#define RTC_DEPRECATED
-#endif
-
-#endif // RTC_BASE_DEPRECATION_H_
diff --git a/rtc_base/event_tracer.cc b/rtc_base/event_tracer.cc
index 3af8183..9e3ee60 100644
--- a/rtc_base/event_tracer.cc
+++ b/rtc_base/event_tracer.cc
@@ -17,6 +17,7 @@
#include <string>
#include <vector>
+#include "api/sequence_checker.h"
#include "rtc_base/atomic_ops.h"
#include "rtc_base/checks.h"
#include "rtc_base/event.h"
@@ -25,7 +26,6 @@
#include "rtc_base/platform_thread_types.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/thread_annotations.h"
-#include "rtc_base/thread_checker.h"
#include "rtc_base/time_utils.h"
#include "rtc_base/trace_event.h"
@@ -321,7 +321,7 @@
std::vector<TraceEvent> trace_events_ RTC_GUARDED_BY(mutex_);
rtc::PlatformThread logging_thread_;
rtc::Event shutdown_event_;
- rtc::ThreadChecker thread_checker_;
+ webrtc::SequenceChecker thread_checker_;
FILE* output_file_ = nullptr;
bool output_file_owned_ = false;
};
diff --git a/rtc_base/experiments/BUILD.gn b/rtc_base/experiments/BUILD.gn
index a40c9e0..b0a729a 100644
--- a/rtc_base/experiments/BUILD.gn
+++ b/rtc_base/experiments/BUILD.gn
@@ -130,6 +130,20 @@
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
+rtc_library("encoder_info_settings") {
+ sources = [
+ "encoder_info_settings.cc",
+ "encoder_info_settings.h",
+ ]
+ deps = [
+ ":field_trial_parser",
+ "../:rtc_base_approved",
+ "../../api/video_codecs:video_codecs_api",
+ "../../system_wrappers:field_trial",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
+}
+
rtc_library("rtt_mult_experiment") {
sources = [
"rtt_mult_experiment.cc",
@@ -217,13 +231,14 @@
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
-if (rtc_include_tests) {
+if (rtc_include_tests && !build_with_chromium) {
rtc_library("experiments_unittests") {
testonly = true
sources = [
"balanced_degradation_settings_unittest.cc",
"cpu_speed_experiment_unittest.cc",
+ "encoder_info_settings_unittest.cc",
"field_trial_list_unittest.cc",
"field_trial_parser_unittest.cc",
"field_trial_units_unittest.cc",
@@ -241,6 +256,7 @@
deps = [
":balanced_degradation_settings",
":cpu_speed_experiment",
+ ":encoder_info_settings",
":field_trial_parser",
":keyframe_interval_settings_experiment",
":min_video_bitrate_experiment",
diff --git a/rtc_base/experiments/balanced_degradation_settings.cc b/rtc_base/experiments/balanced_degradation_settings.cc
index d061597..90d44ef 100644
--- a/rtc_base/experiments/balanced_degradation_settings.cc
+++ b/rtc_base/experiments/balanced_degradation_settings.cc
@@ -93,7 +93,8 @@
bool IsValid(const std::vector<BalancedDegradationSettings::Config>& configs) {
if (configs.size() <= 1) {
- RTC_LOG(LS_WARNING) << "Unsupported size, value ignored.";
+ if (configs.size() == 1)
+ RTC_LOG(LS_WARNING) << "Unsupported size, value ignored.";
return false;
}
for (const auto& config : configs) {
diff --git a/rtc_base/experiments/cpu_speed_experiment.cc b/rtc_base/experiments/cpu_speed_experiment.cc
index 0f53320..7e61255 100644
--- a/rtc_base/experiments/cpu_speed_experiment.cc
+++ b/rtc_base/experiments/cpu_speed_experiment.cc
@@ -25,7 +25,6 @@
std::vector<CpuSpeedExperiment::Config> GetValidOrEmpty(
const std::vector<CpuSpeedExperiment::Config>& configs) {
if (configs.empty()) {
- RTC_LOG(LS_WARNING) << "Unsupported size, value ignored.";
return {};
}
diff --git a/rtc_base/experiments/encoder_info_settings.cc b/rtc_base/experiments/encoder_info_settings.cc
new file mode 100644
index 0000000..9e1a519
--- /dev/null
+++ b/rtc_base/experiments/encoder_info_settings.cc
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2021 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 "rtc_base/experiments/encoder_info_settings.h"
+
+#include <stdio.h>
+
+#include "rtc_base/experiments/field_trial_list.h"
+#include "rtc_base/logging.h"
+#include "system_wrappers/include/field_trial.h"
+
+namespace webrtc {
+namespace {
+
+std::vector<VideoEncoder::ResolutionBitrateLimits> ToResolutionBitrateLimits(
+ const std::vector<EncoderInfoSettings::BitrateLimit>& limits) {
+ std::vector<VideoEncoder::ResolutionBitrateLimits> result;
+ for (const auto& limit : limits) {
+ result.push_back(VideoEncoder::ResolutionBitrateLimits(
+ limit.frame_size_pixels, limit.min_start_bitrate_bps,
+ limit.min_bitrate_bps, limit.max_bitrate_bps));
+ }
+ return result;
+}
+
+} // namespace
+
+// Default bitrate limits for simulcast with one active stream:
+// {frame_size_pixels, min_start_bitrate_bps, min_bitrate_bps, max_bitrate_bps}.
+std::vector<VideoEncoder::ResolutionBitrateLimits>
+EncoderInfoSettings::GetDefaultSinglecastBitrateLimits(
+ VideoCodecType codec_type) {
+ // Specific limits for VP9. Other codecs use VP8 limits.
+ if (codec_type == kVideoCodecVP9) {
+ return {{320 * 180, 0, 30000, 150000},
+ {480 * 270, 120000, 30000, 300000},
+ {640 * 360, 190000, 30000, 420000},
+ {960 * 540, 350000, 30000, 1000000},
+ {1280 * 720, 480000, 30000, 1500000}};
+ }
+
+ return {{320 * 180, 0, 30000, 300000},
+ {480 * 270, 200000, 30000, 500000},
+ {640 * 360, 300000, 30000, 800000},
+ {960 * 540, 500000, 30000, 1500000},
+ {1280 * 720, 900000, 30000, 2500000}};
+}
+
+absl::optional<VideoEncoder::ResolutionBitrateLimits>
+EncoderInfoSettings::GetDefaultSinglecastBitrateLimitsForResolution(
+ VideoCodecType codec_type,
+ int frame_size_pixels) {
+ VideoEncoder::EncoderInfo info;
+ info.resolution_bitrate_limits =
+ GetDefaultSinglecastBitrateLimits(codec_type);
+ return info.GetEncoderBitrateLimitsForResolution(frame_size_pixels);
+}
+
+EncoderInfoSettings::EncoderInfoSettings(std::string name)
+ : requested_resolution_alignment_("requested_resolution_alignment"),
+ apply_alignment_to_all_simulcast_layers_(
+ "apply_alignment_to_all_simulcast_layers") {
+ FieldTrialStructList<BitrateLimit> bitrate_limits(
+ {FieldTrialStructMember(
+ "frame_size_pixels",
+ [](BitrateLimit* b) { return &b->frame_size_pixels; }),
+ FieldTrialStructMember(
+ "min_start_bitrate_bps",
+ [](BitrateLimit* b) { return &b->min_start_bitrate_bps; }),
+ FieldTrialStructMember(
+ "min_bitrate_bps",
+ [](BitrateLimit* b) { return &b->min_bitrate_bps; }),
+ FieldTrialStructMember(
+ "max_bitrate_bps",
+ [](BitrateLimit* b) { return &b->max_bitrate_bps; })},
+ {});
+
+ if (field_trial::FindFullName(name).empty()) {
+ // Encoder name not found, use common string applying to all encoders.
+ name = "WebRTC-GetEncoderInfoOverride";
+ }
+
+ ParseFieldTrial({&bitrate_limits, &requested_resolution_alignment_,
+ &apply_alignment_to_all_simulcast_layers_},
+ field_trial::FindFullName(name));
+
+ resolution_bitrate_limits_ = ToResolutionBitrateLimits(bitrate_limits.Get());
+}
+
+absl::optional<int> EncoderInfoSettings::requested_resolution_alignment()
+ const {
+ if (requested_resolution_alignment_ &&
+ requested_resolution_alignment_.Value() < 1) {
+ RTC_LOG(LS_WARNING) << "Unsupported alignment value, ignored.";
+ return absl::nullopt;
+ }
+ return requested_resolution_alignment_.GetOptional();
+}
+
+EncoderInfoSettings::~EncoderInfoSettings() {}
+
+SimulcastEncoderAdapterEncoderInfoSettings::
+ SimulcastEncoderAdapterEncoderInfoSettings()
+ : EncoderInfoSettings(
+ "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride") {}
+
+LibvpxVp8EncoderInfoSettings::LibvpxVp8EncoderInfoSettings()
+ : EncoderInfoSettings("WebRTC-VP8-GetEncoderInfoOverride") {}
+
+LibvpxVp9EncoderInfoSettings::LibvpxVp9EncoderInfoSettings()
+ : EncoderInfoSettings("WebRTC-VP9-GetEncoderInfoOverride") {}
+
+} // namespace webrtc
diff --git a/rtc_base/experiments/encoder_info_settings.h b/rtc_base/experiments/encoder_info_settings.h
new file mode 100644
index 0000000..9cbb587
--- /dev/null
+++ b/rtc_base/experiments/encoder_info_settings.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2021 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 RTC_BASE_EXPERIMENTS_ENCODER_INFO_SETTINGS_H_
+#define RTC_BASE_EXPERIMENTS_ENCODER_INFO_SETTINGS_H_
+
+#include <string>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/video_codecs/video_encoder.h"
+#include "rtc_base/experiments/field_trial_parser.h"
+
+namespace webrtc {
+
+class EncoderInfoSettings {
+ public:
+ virtual ~EncoderInfoSettings();
+
+ // Bitrate limits per resolution.
+ struct BitrateLimit {
+ int frame_size_pixels = 0; // The video frame size.
+ int min_start_bitrate_bps = 0; // The minimum bitrate to start encoding.
+ int min_bitrate_bps = 0; // The minimum bitrate.
+ int max_bitrate_bps = 0; // The maximum bitrate.
+ };
+
+ absl::optional<int> requested_resolution_alignment() const;
+ bool apply_alignment_to_all_simulcast_layers() const {
+ return apply_alignment_to_all_simulcast_layers_.Get();
+ }
+ std::vector<VideoEncoder::ResolutionBitrateLimits> resolution_bitrate_limits()
+ const {
+ return resolution_bitrate_limits_;
+ }
+
+ static std::vector<VideoEncoder::ResolutionBitrateLimits>
+ GetDefaultSinglecastBitrateLimits(VideoCodecType codec_type);
+
+ static absl::optional<VideoEncoder::ResolutionBitrateLimits>
+ GetDefaultSinglecastBitrateLimitsForResolution(VideoCodecType codec_type,
+ int frame_size_pixels);
+
+ protected:
+ explicit EncoderInfoSettings(std::string name);
+
+ private:
+ FieldTrialOptional<int> requested_resolution_alignment_;
+ FieldTrialFlag apply_alignment_to_all_simulcast_layers_;
+ std::vector<VideoEncoder::ResolutionBitrateLimits> resolution_bitrate_limits_;
+};
+
+// EncoderInfo settings for SimulcastEncoderAdapter.
+class SimulcastEncoderAdapterEncoderInfoSettings : public EncoderInfoSettings {
+ public:
+ SimulcastEncoderAdapterEncoderInfoSettings();
+ ~SimulcastEncoderAdapterEncoderInfoSettings() override {}
+};
+
+// EncoderInfo settings for LibvpxVp8Encoder.
+class LibvpxVp8EncoderInfoSettings : public EncoderInfoSettings {
+ public:
+ LibvpxVp8EncoderInfoSettings();
+ ~LibvpxVp8EncoderInfoSettings() override {}
+};
+
+// EncoderInfo settings for LibvpxVp9Encoder.
+class LibvpxVp9EncoderInfoSettings : public EncoderInfoSettings {
+ public:
+ LibvpxVp9EncoderInfoSettings();
+ ~LibvpxVp9EncoderInfoSettings() override {}
+};
+
+} // namespace webrtc
+
+#endif // RTC_BASE_EXPERIMENTS_ENCODER_INFO_SETTINGS_H_
diff --git a/rtc_base/experiments/encoder_info_settings_unittest.cc b/rtc_base/experiments/encoder_info_settings_unittest.cc
new file mode 100644
index 0000000..aabb687
--- /dev/null
+++ b/rtc_base/experiments/encoder_info_settings_unittest.cc
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2021 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 "rtc_base/experiments/encoder_info_settings.h"
+
+#include "rtc_base/gunit.h"
+#include "test/field_trial.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+
+TEST(SimulcastEncoderAdapterSettingsTest, NoValuesWithoutFieldTrial) {
+ SimulcastEncoderAdapterEncoderInfoSettings settings;
+ EXPECT_EQ(absl::nullopt, settings.requested_resolution_alignment());
+ EXPECT_FALSE(settings.apply_alignment_to_all_simulcast_layers());
+ EXPECT_TRUE(settings.resolution_bitrate_limits().empty());
+}
+
+TEST(SimulcastEncoderAdapterSettingsTest, NoValueForInvalidAlignment) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride/"
+ "requested_resolution_alignment:0/");
+
+ SimulcastEncoderAdapterEncoderInfoSettings settings;
+ EXPECT_EQ(absl::nullopt, settings.requested_resolution_alignment());
+}
+
+TEST(SimulcastEncoderAdapterSettingsTest, GetResolutionAlignment) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride/"
+ "requested_resolution_alignment:2/");
+
+ SimulcastEncoderAdapterEncoderInfoSettings settings;
+ EXPECT_EQ(2, settings.requested_resolution_alignment());
+ EXPECT_FALSE(settings.apply_alignment_to_all_simulcast_layers());
+ EXPECT_TRUE(settings.resolution_bitrate_limits().empty());
+}
+
+TEST(SimulcastEncoderAdapterSettingsTest, GetApplyAlignment) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride/"
+ "requested_resolution_alignment:3,"
+ "apply_alignment_to_all_simulcast_layers/");
+
+ SimulcastEncoderAdapterEncoderInfoSettings settings;
+ EXPECT_EQ(3, settings.requested_resolution_alignment());
+ EXPECT_TRUE(settings.apply_alignment_to_all_simulcast_layers());
+ EXPECT_TRUE(settings.resolution_bitrate_limits().empty());
+}
+
+TEST(SimulcastEncoderAdapterSettingsTest, GetResolutionBitrateLimits) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride/"
+ "frame_size_pixels:123,"
+ "min_start_bitrate_bps:11000,"
+ "min_bitrate_bps:44000,"
+ "max_bitrate_bps:77000/");
+
+ SimulcastEncoderAdapterEncoderInfoSettings settings;
+ EXPECT_EQ(absl::nullopt, settings.requested_resolution_alignment());
+ EXPECT_FALSE(settings.apply_alignment_to_all_simulcast_layers());
+ EXPECT_THAT(settings.resolution_bitrate_limits(),
+ ::testing::ElementsAre(VideoEncoder::ResolutionBitrateLimits{
+ 123, 11000, 44000, 77000}));
+}
+
+TEST(SimulcastEncoderAdapterSettingsTest, GetResolutionBitrateLimitsWithList) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride/"
+ "frame_size_pixels:123|456|789,"
+ "min_start_bitrate_bps:11000|22000|33000,"
+ "min_bitrate_bps:44000|55000|66000,"
+ "max_bitrate_bps:77000|88000|99000/");
+
+ SimulcastEncoderAdapterEncoderInfoSettings settings;
+ EXPECT_THAT(
+ settings.resolution_bitrate_limits(),
+ ::testing::ElementsAre(
+ VideoEncoder::ResolutionBitrateLimits{123, 11000, 44000, 77000},
+ VideoEncoder::ResolutionBitrateLimits{456, 22000, 55000, 88000},
+ VideoEncoder::ResolutionBitrateLimits{789, 33000, 66000, 99000}));
+}
+
+TEST(EncoderSettingsTest, CommonSettingsUsedIfEncoderNameUnspecified) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-VP8-GetEncoderInfoOverride/requested_resolution_alignment:2/"
+ "WebRTC-GetEncoderInfoOverride/requested_resolution_alignment:3/");
+
+ LibvpxVp8EncoderInfoSettings vp8_settings;
+ EXPECT_EQ(2, vp8_settings.requested_resolution_alignment());
+ LibvpxVp9EncoderInfoSettings vp9_settings;
+ EXPECT_EQ(3, vp9_settings.requested_resolution_alignment());
+}
+
+} // namespace webrtc
diff --git a/rtc_base/experiments/field_trial_parser.cc b/rtc_base/experiments/field_trial_parser.cc
index b88d0f9..8fc89ce 100644
--- a/rtc_base/experiments/field_trial_parser.cc
+++ b/rtc_base/experiments/field_trial_parser.cc
@@ -83,7 +83,10 @@
RTC_LOG(LS_WARNING) << "Failed to read empty key field with value '"
<< key << "' in trial: \"" << trial_string << "\"";
}
- } else {
+ } else if (key.empty() || key[0] != '_') {
+ // "_" is be used to prefix keys that are part of the string for
+ // debugging purposes but not neccessarily used.
+ // e.g. WebRTC-Experiment/param: value, _DebuggingString
RTC_LOG(LS_INFO) << "No field with key: '" << key
<< "' (found in trial: \"" << trial_string << "\")";
std::string valid_keys;
diff --git a/rtc_base/experiments/quality_scaling_experiment.cc b/rtc_base/experiments/quality_scaling_experiment.cc
index ca58ba8..7d5722b 100644
--- a/rtc_base/experiments/quality_scaling_experiment.cc
+++ b/rtc_base/experiments/quality_scaling_experiment.cc
@@ -25,6 +25,11 @@
constexpr int kMaxH264Qp = 51;
constexpr int kMaxGenericQp = 255;
+#if !defined(WEBRTC_IOS)
+constexpr char kDefaultQualityScalingSetttings[] =
+ "Enabled-29,95,149,205,24,37,26,36,0.9995,0.9999,1";
+#endif
+
absl::optional<VideoEncoder::QpThresholds> GetThresholds(int low,
int high,
int max) {
@@ -38,15 +43,22 @@
} // namespace
bool QualityScalingExperiment::Enabled() {
+#if defined(WEBRTC_IOS)
return webrtc::field_trial::IsEnabled(kFieldTrial);
+#else
+ return !webrtc::field_trial::IsDisabled(kFieldTrial);
+#endif
}
absl::optional<QualityScalingExperiment::Settings>
QualityScalingExperiment::ParseSettings() {
- const std::string group = webrtc::field_trial::FindFullName(kFieldTrial);
+ std::string group = webrtc::field_trial::FindFullName(kFieldTrial);
+ // TODO(http://crbug.com/webrtc/12401): Completely remove the experiment code
+ // after few releases.
+#if !defined(WEBRTC_IOS)
if (group.empty())
- return absl::nullopt;
-
+ group = kDefaultQualityScalingSetttings;
+#endif
Settings s;
if (sscanf(group.c_str(), "Enabled-%d,%d,%d,%d,%d,%d,%d,%d,%f,%f,%d",
&s.vp8_low, &s.vp8_high, &s.vp9_low, &s.vp9_high, &s.h264_low,
diff --git a/rtc_base/experiments/quality_scaling_experiment_unittest.cc b/rtc_base/experiments/quality_scaling_experiment_unittest.cc
index 7a345b6..4507f15 100644
--- a/rtc_base/experiments/quality_scaling_experiment_unittest.cc
+++ b/rtc_base/experiments/quality_scaling_experiment_unittest.cc
@@ -38,10 +38,18 @@
}
} // namespace
-TEST(QualityScalingExperimentTest, DisabledWithoutFieldTrial) {
+#if !defined(WEBRTC_IOS)
+// TODO(bugs.webrtc.org/12401): investigate why QualityScaler kicks in on iOS.
+TEST(QualityScalingExperimentTest, DefaultEnabledWithoutFieldTrial) {
+ webrtc::test::ScopedFieldTrials field_trials("");
+ EXPECT_TRUE(QualityScalingExperiment::Enabled());
+}
+#else
+TEST(QualityScalingExperimentTest, DefaultDisabledWithoutFieldTrialIOS) {
webrtc::test::ScopedFieldTrials field_trials("");
EXPECT_FALSE(QualityScalingExperiment::Enabled());
}
+#endif
TEST(QualityScalingExperimentTest, EnabledWithFieldTrial) {
webrtc::test::ScopedFieldTrials field_trials(
@@ -59,10 +67,19 @@
ExpectEqualSettings(kExpected, *settings);
}
+#if !defined(WEBRTC_IOS)
+// TODO(bugs.webrtc.org/12401): investigate why QualityScaler kicks in on iOS.
+TEST(QualityScalingExperimentTest, ParseSettingsUsesDefaultsWithoutFieldTrial) {
+ webrtc::test::ScopedFieldTrials field_trials("");
+ // Uses some default hard coded values.
+ EXPECT_TRUE(QualityScalingExperiment::ParseSettings());
+}
+#else
TEST(QualityScalingExperimentTest, ParseSettingsFailsWithoutFieldTrial) {
webrtc::test::ScopedFieldTrials field_trials("");
EXPECT_FALSE(QualityScalingExperiment::ParseSettings());
}
+#endif
TEST(QualityScalingExperimentTest, ParseSettingsFailsWithInvalidFieldTrial) {
webrtc::test::ScopedFieldTrials field_trials(
diff --git a/rtc_base/experiments/rate_control_settings.cc b/rtc_base/experiments/rate_control_settings.cc
index 6766db6..bed194e 100644
--- a/rtc_base/experiments/rate_control_settings.cc
+++ b/rtc_base/experiments/rate_control_settings.cc
@@ -24,10 +24,13 @@
namespace {
-const int kDefaultAcceptedQueueMs = 250;
+const int kDefaultAcceptedQueueMs = 350;
const int kDefaultMinPushbackTargetBitrateBps = 30000;
+const char kCongestionWindowDefaultFieldTrialString[] =
+ "QueueSize:350,MinBitrate:30000,DropFrame:true";
+
const char kUseBaseHeavyVp8Tl3RateAllocationFieldTrialName[] =
"WebRTC-UseBaseHeavyVP8TL3RateAllocation";
@@ -91,9 +94,13 @@
}
RateControlSettings::RateControlSettings(
- const WebRtcKeyValueConfig* const key_value_config)
- : congestion_window_config_(CongestionWindowConfig::Parse(
- key_value_config->Lookup(CongestionWindowConfig::kKey))) {
+ const WebRtcKeyValueConfig* const key_value_config) {
+ std::string congestion_window_config =
+ key_value_config->Lookup(CongestionWindowConfig::kKey).empty()
+ ? kCongestionWindowDefaultFieldTrialString
+ : key_value_config->Lookup(CongestionWindowConfig::kKey);
+ congestion_window_config_ =
+ CongestionWindowConfig::Parse(congestion_window_config);
video_config_.vp8_base_heavy_tl3_alloc = IsEnabled(
key_value_config, kUseBaseHeavyVp8Tl3RateAllocationFieldTrialName);
ParseHysteresisFactor(key_value_config, kVideoHysteresisFieldTrialname,
diff --git a/rtc_base/experiments/rate_control_settings.h b/rtc_base/experiments/rate_control_settings.h
index db7f1cd..1c38e92 100644
--- a/rtc_base/experiments/rate_control_settings.h
+++ b/rtc_base/experiments/rate_control_settings.h
@@ -96,7 +96,7 @@
explicit RateControlSettings(
const WebRtcKeyValueConfig* const key_value_config);
- const CongestionWindowConfig congestion_window_config_;
+ CongestionWindowConfig congestion_window_config_;
VideoRateControlConfig video_config_;
};
diff --git a/rtc_base/experiments/rate_control_settings_unittest.cc b/rtc_base/experiments/rate_control_settings_unittest.cc
index 8d72272..84e5825 100644
--- a/rtc_base/experiments/rate_control_settings_unittest.cc
+++ b/rtc_base/experiments/rate_control_settings_unittest.cc
@@ -20,7 +20,7 @@
namespace {
TEST(RateControlSettingsTest, CongestionWindow) {
- EXPECT_FALSE(
+ EXPECT_TRUE(
RateControlSettings::ParseFromFieldTrials().UseCongestionWindow());
test::ScopedFieldTrials field_trials(
@@ -32,8 +32,8 @@
}
TEST(RateControlSettingsTest, CongestionWindowPushback) {
- EXPECT_FALSE(RateControlSettings::ParseFromFieldTrials()
- .UseCongestionWindowPushback());
+ EXPECT_TRUE(RateControlSettings::ParseFromFieldTrials()
+ .UseCongestionWindowPushback());
test::ScopedFieldTrials field_trials(
"WebRTC-CongestionWindow/QueueSize:100,MinBitrate:100000/");
@@ -44,6 +44,29 @@
100000u);
}
+TEST(RateControlSettingsTest, CongestionWindowPushbackDropframe) {
+ EXPECT_TRUE(RateControlSettings::ParseFromFieldTrials()
+ .UseCongestionWindowPushback());
+
+ test::ScopedFieldTrials field_trials(
+ "WebRTC-CongestionWindow/"
+ "QueueSize:100,MinBitrate:100000,DropFrame:true/");
+ const RateControlSettings settings_after =
+ RateControlSettings::ParseFromFieldTrials();
+ EXPECT_TRUE(settings_after.UseCongestionWindowPushback());
+ EXPECT_EQ(settings_after.CongestionWindowMinPushbackTargetBitrateBps(),
+ 100000u);
+ EXPECT_TRUE(settings_after.UseCongestionWindowDropFrameOnly());
+}
+
+TEST(RateControlSettingsTest, CongestionWindowPushbackDefaultConfig) {
+ const RateControlSettings settings =
+ RateControlSettings::ParseFromFieldTrials();
+ EXPECT_TRUE(settings.UseCongestionWindowPushback());
+ EXPECT_EQ(settings.CongestionWindowMinPushbackTargetBitrateBps(), 30000u);
+ EXPECT_TRUE(settings.UseCongestionWindowDropFrameOnly());
+}
+
TEST(RateControlSettingsTest, PacingFactor) {
EXPECT_FALSE(RateControlSettings::ParseFromFieldTrials().GetPacingFactor());
diff --git a/rtc_base/experiments/struct_parameters_parser.cc b/rtc_base/experiments/struct_parameters_parser.cc
index 2605da8..d62eb6f 100644
--- a/rtc_base/experiments/struct_parameters_parser.cc
+++ b/rtc_base/experiments/struct_parameters_parser.cc
@@ -107,7 +107,10 @@
break;
}
}
- if (!found) {
+ // "_" is be used to prefix keys that are part of the string for
+ // debugging purposes but not neccessarily used.
+ // e.g. WebRTC-Experiment/param: value, _DebuggingString
+ if (!found && (key.empty() || key[0] != '_')) {
RTC_LOG(LS_INFO) << "No field with key: '" << key
<< "' (found in trial: \"" << src << "\")";
}
diff --git a/rtc_base/internal/default_socket_server.cc b/rtc_base/internal/default_socket_server.cc
new file mode 100644
index 0000000..5632b98
--- /dev/null
+++ b/rtc_base/internal/default_socket_server.cc
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020 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 "rtc_base/internal/default_socket_server.h"
+
+#include <memory>
+
+#include "rtc_base/socket_server.h"
+
+#if defined(__native_client__)
+#include "rtc_base/null_socket_server.h"
+#else
+#include "rtc_base/physical_socket_server.h"
+#endif
+
+namespace rtc {
+
+std::unique_ptr<SocketServer> CreateDefaultSocketServer() {
+#if defined(__native_client__)
+ return std::unique_ptr<SocketServer>(new rtc::NullSocketServer);
+#else
+ return std::unique_ptr<SocketServer>(new rtc::PhysicalSocketServer);
+#endif
+}
+
+} // namespace rtc
diff --git a/rtc_base/internal/default_socket_server.h b/rtc_base/internal/default_socket_server.h
new file mode 100644
index 0000000..5b3489f
--- /dev/null
+++ b/rtc_base/internal/default_socket_server.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2020 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 RTC_BASE_INTERNAL_DEFAULT_SOCKET_SERVER_H_
+#define RTC_BASE_INTERNAL_DEFAULT_SOCKET_SERVER_H_
+
+#include <memory>
+
+#include "rtc_base/socket_server.h"
+
+namespace rtc {
+
+std::unique_ptr<SocketServer> CreateDefaultSocketServer();
+
+} // namespace rtc
+
+#endif // RTC_BASE_INTERNAL_DEFAULT_SOCKET_SERVER_H_
diff --git a/rtc_base/ip_address.cc b/rtc_base/ip_address.cc
index 9dd534c..9f1df58 100644
--- a/rtc_base/ip_address.cc
+++ b/rtc_base/ip_address.cc
@@ -20,8 +20,9 @@
#include <netdb.h>
#endif
-#include "rtc_base/byte_order.h"
#include "rtc_base/ip_address.h"
+
+#include "rtc_base/byte_order.h"
#include "rtc_base/net_helpers.h"
#include "rtc_base/string_utils.h"
diff --git a/rtc_base/keep_ref_until_done.h b/rtc_base/keep_ref_until_done.h
deleted file mode 100644
index 5ae0ed1..0000000
--- a/rtc_base/keep_ref_until_done.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#ifndef RTC_BASE_KEEP_REF_UNTIL_DONE_H_
-#define RTC_BASE_KEEP_REF_UNTIL_DONE_H_
-
-#include "api/scoped_refptr.h"
-#include "rtc_base/callback.h"
-
-namespace rtc {
-
-// KeepRefUntilDone keeps a reference to |object| until the returned
-// callback goes out of scope. If the returned callback is copied, the
-// reference will be released when the last callback goes out of scope.
-template <class ObjectT>
-static inline Callback0<void> KeepRefUntilDone(ObjectT* object) {
- scoped_refptr<ObjectT> p(object);
- return [p] {};
-}
-
-template <class ObjectT>
-static inline Callback0<void> KeepRefUntilDone(
- const scoped_refptr<ObjectT>& object) {
- return [object] {};
-}
-
-} // namespace rtc
-
-#endif // RTC_BASE_KEEP_REF_UNTIL_DONE_H_
diff --git a/rtc_base/logging.h b/rtc_base/logging.h
index d2607c2..e21c30e 100644
--- a/rtc_base/logging.h
+++ b/rtc_base/logging.h
@@ -51,10 +51,10 @@
#include <string>
#include <utility>
+#include "absl/base/attributes.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h"
#include "rtc_base/constructor_magic.h"
-#include "rtc_base/deprecation.h"
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/system/inline.h"
@@ -434,7 +434,7 @@
// DEPRECATED - DO NOT USE - PLEASE USE THE MACROS INSTEAD OF THE CLASS.
// Android code should use the 'const char*' version since tags are static
// and we want to avoid allocating a std::string copy per log line.
- RTC_DEPRECATED
+ ABSL_DEPRECATED("Use RTC_LOG macros instead of accessing this class directly")
LogMessage(const char* file,
int line,
LoggingSeverity sev,
@@ -508,7 +508,7 @@
// DEPRECATED - DO NOT USE - PLEASE USE THE MACROS INSTEAD OF THE CLASS.
// Android code should use the 'const char*' version since tags are static
// and we want to avoid allocating a std::string copy per log line.
- RTC_DEPRECATED
+ ABSL_DEPRECATED("Use RTC_LOG macros instead of accessing this class directly")
LogMessage(const char* file,
int line,
LoggingSeverity sev,
diff --git a/rtc_base/memory/BUILD.gn b/rtc_base/memory/BUILD.gn
index 838fbc6..8fbb549 100644
--- a/rtc_base/memory/BUILD.gn
+++ b/rtc_base/memory/BUILD.gn
@@ -36,6 +36,7 @@
]
deps = [
"..:rtc_base",
+ "..:threading",
"../synchronization:mutex",
"../task_utils:pending_task_safety_flag",
"../task_utils:to_queued_task",
diff --git a/rtc_base/module.mk b/rtc_base/module.mk
index 1f3ce4b..9a3c969 100644
--- a/rtc_base/module.mk
+++ b/rtc_base/module.mk
@@ -22,12 +22,13 @@
rtc_base/rate_tracker.o \
rtc_base/string_to_number.o \
rtc_base/string_encode.o \
+ rtc_base/system_time.o \
rtc_base/timestamp_aligner.o \
rtc_base/time_utils.o \
rtc_base/zero_memory.o \
rtc_base/event.o \
rtc_base/logging.o \
- rtc_base/synchronization/sequence_checker.o \
+ rtc_base/synchronization/sequence_checker_internal.o \
rtc_base/synchronization/yield_policy.o
# requires libevent at link
diff --git a/rtc_base/net_helpers.cc b/rtc_base/net_helpers.cc
index c6685e2..e51a51a 100644
--- a/rtc_base/net_helpers.cc
+++ b/rtc_base/net_helpers.cc
@@ -10,6 +10,8 @@
#include "rtc_base/net_helpers.h"
+#include <memory>
+
#if defined(WEBRTC_WIN)
#include <ws2spi.h>
#include <ws2tcpip.h>
@@ -17,6 +19,7 @@
#include "rtc_base/win32.h"
#endif
#if defined(WEBRTC_POSIX) && !defined(__native_client__)
+#include <arpa/inet.h>
#if defined(WEBRTC_ANDROID)
#include "rtc_base/ifaddrs_android.h"
#else
@@ -24,145 +27,8 @@
#endif
#endif // defined(WEBRTC_POSIX) && !defined(__native_client__)
-#include "api/task_queue/task_queue_base.h"
-#include "rtc_base/logging.h"
-#include "rtc_base/signal_thread.h"
-#include "rtc_base/task_queue.h"
-#include "rtc_base/task_utils/to_queued_task.h"
-#include "rtc_base/third_party/sigslot/sigslot.h" // for signal_with_thread...
-
namespace rtc {
-int ResolveHostname(const std::string& hostname,
- int family,
- std::vector<IPAddress>* addresses) {
-#ifdef __native_client__
- RTC_NOTREACHED();
- RTC_LOG(LS_WARNING) << "ResolveHostname() is not implemented for NaCl";
- return -1;
-#else // __native_client__
- if (!addresses) {
- return -1;
- }
- addresses->clear();
- struct addrinfo* result = nullptr;
- struct addrinfo hints = {0};
- hints.ai_family = family;
- // |family| here will almost always be AF_UNSPEC, because |family| comes from
- // AsyncResolver::addr_.family(), which comes from a SocketAddress constructed
- // with a hostname. When a SocketAddress is constructed with a hostname, its
- // family is AF_UNSPEC. However, if someday in the future we construct
- // a SocketAddress with both a hostname and a family other than AF_UNSPEC,
- // then it would be possible to get a specific family value here.
-
- // The behavior of AF_UNSPEC is roughly "get both ipv4 and ipv6", as
- // documented by the various operating systems:
- // Linux: http://man7.org/linux/man-pages/man3/getaddrinfo.3.html
- // Windows: https://msdn.microsoft.com/en-us/library/windows/desktop/
- // ms738520(v=vs.85).aspx
- // Mac: https://developer.apple.com/legacy/library/documentation/Darwin/
- // Reference/ManPages/man3/getaddrinfo.3.html
- // Android (source code, not documentation):
- // https://android.googlesource.com/platform/bionic/+/
- // 7e0bfb511e85834d7c6cb9631206b62f82701d60/libc/netbsd/net/getaddrinfo.c#1657
- hints.ai_flags = AI_ADDRCONFIG;
- int ret = getaddrinfo(hostname.c_str(), nullptr, &hints, &result);
- if (ret != 0) {
- return ret;
- }
- struct addrinfo* cursor = result;
- for (; cursor; cursor = cursor->ai_next) {
- if (family == AF_UNSPEC || cursor->ai_family == family) {
- IPAddress ip;
- if (IPFromAddrInfo(cursor, &ip)) {
- addresses->push_back(ip);
- }
- }
- }
- freeaddrinfo(result);
- return 0;
-#endif // !__native_client__
-}
-
-AsyncResolver::AsyncResolver() : error_(-1) {}
-
-AsyncResolver::~AsyncResolver() {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
-}
-
-void AsyncResolver::Start(const SocketAddress& addr) {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- RTC_DCHECK(!destroy_called_);
- addr_ = addr;
- webrtc::TaskQueueBase* current_task_queue = webrtc::TaskQueueBase::Current();
- popup_thread_ = Thread::Create();
- popup_thread_->Start();
- popup_thread_->PostTask(webrtc::ToQueuedTask(
- [this, flag = safety_.flag(), addr, current_task_queue] {
- std::vector<IPAddress> addresses;
- int error =
- ResolveHostname(addr.hostname().c_str(), addr.family(), &addresses);
- current_task_queue->PostTask(webrtc::ToQueuedTask(
- std::move(flag), [this, error, addresses = std::move(addresses)] {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- ResolveDone(std::move(addresses), error);
- }));
- }));
-}
-
-bool AsyncResolver::GetResolvedAddress(int family, SocketAddress* addr) const {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- RTC_DCHECK(!destroy_called_);
- if (error_ != 0 || addresses_.empty())
- return false;
-
- *addr = addr_;
- for (size_t i = 0; i < addresses_.size(); ++i) {
- if (family == addresses_[i].family()) {
- addr->SetResolvedIP(addresses_[i]);
- return true;
- }
- }
- return false;
-}
-
-int AsyncResolver::GetError() const {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- RTC_DCHECK(!destroy_called_);
- return error_;
-}
-
-void AsyncResolver::Destroy(bool wait) {
- // Some callers have trouble guaranteeing that Destroy is called on the
- // sequence guarded by |sequence_checker_|.
- // RTC_DCHECK_RUN_ON(&sequence_checker_);
- RTC_DCHECK(!destroy_called_);
- destroy_called_ = true;
- MaybeSelfDestruct();
-}
-
-const std::vector<IPAddress>& AsyncResolver::addresses() const {
- RTC_DCHECK_RUN_ON(&sequence_checker_);
- RTC_DCHECK(!destroy_called_);
- return addresses_;
-}
-
-void AsyncResolver::ResolveDone(std::vector<IPAddress> addresses, int error) {
- addresses_ = addresses;
- error_ = error;
- recursion_check_ = true;
- SignalDone(this);
- MaybeSelfDestruct();
-}
-
-void AsyncResolver::MaybeSelfDestruct() {
- if (!recursion_check_) {
- delete this;
- } else {
- recursion_check_ = false;
- }
-}
-
const char* inet_ntop(int af, const void* src, char* dst, socklen_t size) {
#if defined(WEBRTC_WIN)
return win32_inet_ntop(af, src, dst, size);
diff --git a/rtc_base/net_helpers.h b/rtc_base/net_helpers.h
index 172a222..4ed8478 100644
--- a/rtc_base/net_helpers.h
+++ b/rtc_base/net_helpers.h
@@ -15,57 +15,12 @@
#include <sys/socket.h>
#elif WEBRTC_WIN
#include <winsock2.h> // NOLINT
+
+#include "rtc_base/win32.h"
#endif
-#include <vector>
-
-#include "rtc_base/async_resolver_interface.h"
-#include "rtc_base/ip_address.h"
-#include "rtc_base/socket_address.h"
-#include "rtc_base/synchronization/sequence_checker.h"
-#include "rtc_base/system/no_unique_address.h"
-#include "rtc_base/system/rtc_export.h"
-#include "rtc_base/task_utils/pending_task_safety_flag.h"
-#include "rtc_base/thread.h"
-#include "rtc_base/thread_annotations.h"
-
namespace rtc {
-// AsyncResolver will perform async DNS resolution, signaling the result on
-// the SignalDone from AsyncResolverInterface when the operation completes.
-//
-// This class is thread-compatible, and all methods and destruction needs to
-// happen from the same rtc::Thread, except for Destroy which is allowed to
-// happen on another context provided it's not happening concurrently to another
-// public API call, and is the last access to the object.
-class RTC_EXPORT AsyncResolver : public AsyncResolverInterface {
- public:
- AsyncResolver();
- ~AsyncResolver() override;
-
- void Start(const SocketAddress& addr) override;
- bool GetResolvedAddress(int family, SocketAddress* addr) const override;
- int GetError() const override;
- void Destroy(bool wait) override;
-
- const std::vector<IPAddress>& addresses() const;
-
- private:
- void ResolveDone(std::vector<IPAddress> addresses, int error)
- RTC_EXCLUSIVE_LOCKS_REQUIRED(sequence_checker_);
- void MaybeSelfDestruct();
-
- SocketAddress addr_ RTC_GUARDED_BY(sequence_checker_);
- std::vector<IPAddress> addresses_ RTC_GUARDED_BY(sequence_checker_);
- int error_ RTC_GUARDED_BY(sequence_checker_);
- webrtc::ScopedTaskSafety safety_ RTC_GUARDED_BY(sequence_checker_);
- std::unique_ptr<Thread> popup_thread_ RTC_GUARDED_BY(sequence_checker_);
- bool recursion_check_ =
- false; // Protects against SignalDone calling into Destroy.
- bool destroy_called_ = false;
- RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker sequence_checker_;
-};
-
// rtc namespaced wrappers for inet_ntop and inet_pton so we can avoid
// the windows-native versions of these.
const char* inet_ntop(int af, const void* src, char* dst, socklen_t size);
@@ -73,6 +28,7 @@
bool HasIPv4Enabled();
bool HasIPv6Enabled();
+
} // namespace rtc
#endif // RTC_BASE_NET_HELPERS_H_
diff --git a/rtc_base/network.cc b/rtc_base/network.cc
index 07c39ae..1b0ba36 100644
--- a/rtc_base/network.cc
+++ b/rtc_base/network.cc
@@ -212,7 +212,8 @@
return ADAPTER_TYPE_ETHERNET;
}
- if (MatchTypeNameWithIndexPattern(network_name, "wlan")) {
+ if (MatchTypeNameWithIndexPattern(network_name, "wlan") ||
+ MatchTypeNameWithIndexPattern(network_name, "v4-wlan")) {
return ADAPTER_TYPE_WIFI;
}
@@ -478,15 +479,15 @@
return nullptr;
}
-BasicNetworkManager::BasicNetworkManager()
- : allow_mac_based_ipv6_(
- webrtc::field_trial::IsEnabled("WebRTC-AllowMACBasedIPv6")) {}
+BasicNetworkManager::BasicNetworkManager() : BasicNetworkManager(nullptr) {}
BasicNetworkManager::BasicNetworkManager(
NetworkMonitorFactory* network_monitor_factory)
: network_monitor_factory_(network_monitor_factory),
allow_mac_based_ipv6_(
- webrtc::field_trial::IsEnabled("WebRTC-AllowMACBasedIPv6")) {}
+ webrtc::field_trial::IsEnabled("WebRTC-AllowMACBasedIPv6")),
+ bind_using_ifname_(
+ webrtc::field_trial::IsEnabled("WebRTC-BindUsingInterfaceName")) {}
BasicNetworkManager::~BasicNetworkManager() {}
@@ -865,6 +866,15 @@
network_monitor_->SignalNetworksChanged.connect(
this, &BasicNetworkManager::OnNetworksChanged);
}
+
+ if (network_monitor_->SupportsBindSocketToNetwork()) {
+ // Set NetworkBinder on SocketServer so that
+ // PhysicalSocket::Bind will call
+ // BasicNetworkManager::BindSocketToNetwork(), (that will lookup interface
+ // name and then call network_monitor_->BindSocketToNetwork()).
+ thread_->socketserver()->set_network_binder(this);
+ }
+
network_monitor_->Start();
}
@@ -873,6 +883,13 @@
return;
}
network_monitor_->Stop();
+
+ if (network_monitor_->SupportsBindSocketToNetwork()) {
+ // Reset NetworkBinder on SocketServer.
+ if (thread_->socketserver()->network_binder() == this) {
+ thread_->socketserver()->set_network_binder(nullptr);
+ }
+ }
}
void BasicNetworkManager::OnMessage(Message* msg) {
@@ -954,6 +971,20 @@
}
}
+NetworkBindingResult BasicNetworkManager::BindSocketToNetwork(
+ int socket_fd,
+ const IPAddress& address) {
+ RTC_DCHECK_RUN_ON(thread_);
+ std::string if_name;
+ if (bind_using_ifname_) {
+ Network* net = GetNetworkFromAddress(address);
+ if (net != nullptr) {
+ if_name = net->name();
+ }
+ }
+ return network_monitor_->BindSocketToNetwork(socket_fd, address, if_name);
+}
+
Network::Network(const std::string& name,
const std::string& desc,
const IPAddress& prefix,
diff --git a/rtc_base/network.h b/rtc_base/network.h
index 3107b72..8b6b623 100644
--- a/rtc_base/network.h
+++ b/rtc_base/network.h
@@ -19,12 +19,12 @@
#include <string>
#include <vector>
+#include "api/sequence_checker.h"
#include "rtc_base/ip_address.h"
#include "rtc_base/mdns_responder_interface.h"
#include "rtc_base/message_handler.h"
#include "rtc_base/network_monitor.h"
#include "rtc_base/network_monitor_factory.h"
-#include "rtc_base/synchronization/sequence_checker.h"
#include "rtc_base/system/rtc_export.h"
#include "rtc_base/third_party/sigslot/sigslot.h"
#include "rtc_base/thread_annotations.h"
@@ -194,11 +194,11 @@
void set_default_local_addresses(const IPAddress& ipv4,
const IPAddress& ipv6);
+ Network* GetNetworkFromAddress(const rtc::IPAddress& ip) const;
+
private:
friend class NetworkTest;
- Network* GetNetworkFromAddress(const rtc::IPAddress& ip) const;
-
EnumerationPermission enumeration_permission_;
NetworkList networks_;
@@ -225,6 +225,7 @@
// of networks using OS APIs.
class RTC_EXPORT BasicNetworkManager : public NetworkManagerBase,
public MessageHandlerAutoCleanup,
+ public NetworkBinderInterface,
public sigslot::has_slots<> {
public:
BasicNetworkManager();
@@ -248,6 +249,15 @@
network_ignore_list_ = list;
}
+ // Bind a socket to interface that ip address belong to.
+ // Implementation look up interface name and calls
+ // BindSocketToNetwork on NetworkMonitor.
+ // The interface name is needed as e.g ipv4 over ipv6 addresses
+ // are not exposed using Android functions, but it is possible
+ // bind an ipv4 address to the interface.
+ NetworkBindingResult BindSocketToNetwork(int socket_fd,
+ const IPAddress& address) override;
+
protected:
#if defined(WEBRTC_POSIX)
// Separated from CreateNetworks for tests.
@@ -293,7 +303,8 @@
nullptr;
std::unique_ptr<NetworkMonitorInterface> network_monitor_
RTC_GUARDED_BY(thread_);
- bool allow_mac_based_ipv6_ = false;
+ bool allow_mac_based_ipv6_ RTC_GUARDED_BY(thread_) = false;
+ bool bind_using_ifname_ RTC_GUARDED_BY(thread_) = false;
};
// Represents a Unix-type network interface, with a name and single address.
diff --git a/rtc_base/network_monitor.h b/rtc_base/network_monitor.h
index 4a3002f..dddc2f6 100644
--- a/rtc_base/network_monitor.h
+++ b/rtc_base/network_monitor.h
@@ -36,6 +36,8 @@
const char* NetworkPreferenceToString(NetworkPreference preference);
+// This interface is set onto a socket server,
+// where only the ip address is known at the time of binding.
class NetworkBinderInterface {
public:
// Binds a socket to the network that is attached to |address| so that all
@@ -83,6 +85,19 @@
virtual NetworkPreference GetNetworkPreference(
const std::string& interface_name) = 0;
+ // Does |this| NetworkMonitorInterface implement BindSocketToNetwork?
+ // Only Android returns true.
+ virtual bool SupportsBindSocketToNetwork() const { return false; }
+
+ // Bind a socket to an interface specified by ip address and/or interface
+ // name. Only implemented on Android.
+ virtual NetworkBindingResult BindSocketToNetwork(
+ int socket_fd,
+ const IPAddress& address,
+ const std::string& interface_name) {
+ return NetworkBindingResult::NOT_IMPLEMENTED;
+ }
+
// Is this interface available to use? WebRTC shouldn't attempt to use it if
// this returns false.
//
diff --git a/rtc_base/network_unittest.cc b/rtc_base/network_unittest.cc
index 73ddd81..abad479 100644
--- a/rtc_base/network_unittest.cc
+++ b/rtc_base/network_unittest.cc
@@ -76,9 +76,35 @@
unavailable_adapters_ = unavailable_adapters;
}
+ bool SupportsBindSocketToNetwork() const override { return true; }
+
+ NetworkBindingResult BindSocketToNetwork(
+ int socket_fd,
+ const IPAddress& address,
+ const std::string& if_name) override {
+ if (absl::c_count(addresses_, address) > 0) {
+ return NetworkBindingResult::SUCCESS;
+ }
+
+ for (auto const& iter : adapters_) {
+ if (if_name.find(iter) != std::string::npos) {
+ return NetworkBindingResult::SUCCESS;
+ }
+ }
+ return NetworkBindingResult::ADDRESS_NOT_FOUND;
+ }
+
+ void set_ip_addresses(std::vector<IPAddress> addresses) {
+ addresses_ = addresses;
+ }
+
+ void set_adapters(std::vector<std::string> adapters) { adapters_ = adapters; }
+
private:
bool started_ = false;
+ std::vector<std::string> adapters_;
std::vector<std::string> unavailable_adapters_;
+ std::vector<IPAddress> addresses_;
};
class FakeNetworkMonitorFactory : public NetworkMonitorFactory {
@@ -1279,4 +1305,48 @@
}
#endif
+#if defined(WEBRTC_POSIX)
+TEST_F(NetworkTest, WebRTC_BindUsingInterfaceName) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-BindUsingInterfaceName/Enabled/");
+
+ char if_name1[20] = "wlan0";
+ char if_name2[20] = "v4-wlan0";
+ ifaddrs* list = nullptr;
+ list = AddIpv6Address(list, if_name1, "1000:2000:3000:4000:0:0:0:1",
+ "FFFF:FFFF:FFFF:FFFF::", 0);
+ list = AddIpv4Address(list, if_name2, "192.168.0.2", "255.255.255.255");
+ NetworkManager::NetworkList result;
+
+ // Sanity check that both interfaces are included by default.
+ FakeNetworkMonitorFactory factory;
+ BasicNetworkManager manager(&factory);
+ manager.StartUpdating();
+ CallConvertIfAddrs(manager, list, /*include_ignored=*/false, &result);
+ EXPECT_EQ(2u, result.size());
+ ReleaseIfAddrs(list);
+ bool changed;
+ // This ensures we release the objects created in CallConvertIfAddrs.
+ MergeNetworkList(manager, result, &changed);
+ result.clear();
+
+ FakeNetworkMonitor* network_monitor = GetNetworkMonitor(manager);
+
+ IPAddress ipv6;
+ EXPECT_TRUE(IPFromString("1000:2000:3000:4000:0:0:0:1", &ipv6));
+ IPAddress ipv4;
+ EXPECT_TRUE(IPFromString("192.168.0.2", &ipv4));
+
+ // The network monitor only knwos about the ipv6 address, interface.
+ network_monitor->set_adapters({"wlan0"});
+ network_monitor->set_ip_addresses({ipv6});
+ EXPECT_EQ(manager.BindSocketToNetwork(/* fd */ 77, ipv6),
+ NetworkBindingResult::SUCCESS);
+
+ // But it will bind anyway using string matching...
+ EXPECT_EQ(manager.BindSocketToNetwork(/* fd */ 77, ipv4),
+ NetworkBindingResult::SUCCESS);
+}
+#endif
+
} // namespace rtc
diff --git a/rtc_base/operations_chain.h b/rtc_base/operations_chain.h
index a7252d4..3dc5995 100644
--- a/rtc_base/operations_chain.h
+++ b/rtc_base/operations_chain.h
@@ -20,11 +20,11 @@
#include "absl/types/optional.h"
#include "api/scoped_refptr.h"
+#include "api/sequence_checker.h"
#include "rtc_base/checks.h"
#include "rtc_base/constructor_magic.h"
#include "rtc_base/ref_count.h"
#include "rtc_base/ref_counted_object.h"
-#include "rtc_base/synchronization/sequence_checker.h"
#include "rtc_base/system/no_unique_address.h"
namespace rtc {
diff --git a/rtc_base/operations_chain_unittest.cc b/rtc_base/operations_chain_unittest.cc
index 5f183e4..792a2c7 100644
--- a/rtc_base/operations_chain_unittest.cc
+++ b/rtc_base/operations_chain_unittest.cc
@@ -16,7 +16,6 @@
#include <utility>
#include <vector>
-#include "rtc_base/bind.h"
#include "rtc_base/event.h"
#include "rtc_base/gunit.h"
#include "rtc_base/thread.h"
diff --git a/rtc_base/physical_socket_server.cc b/rtc_base/physical_socket_server.cc
index 3cb7c20..7904548 100644
--- a/rtc_base/physical_socket_server.cc
+++ b/rtc_base/physical_socket_server.cc
@@ -48,6 +48,7 @@
#include "rtc_base/logging.h"
#include "rtc_base/network_monitor.h"
#include "rtc_base/null_socket_server.h"
+#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/time_utils.h"
#if defined(WEBRTC_LINUX)
@@ -119,14 +120,6 @@
namespace rtc {
-std::unique_ptr<SocketServer> SocketServer::CreateDefault() {
-#if defined(__native_client__)
- return std::unique_ptr<SocketServer>(new rtc::NullSocketServer);
-#else
- return std::unique_ptr<SocketServer>(new rtc::PhysicalSocketServer);
-#endif
-}
-
PhysicalSocket::PhysicalSocket(PhysicalSocketServer* ss, SOCKET s)
: ss_(ss),
s_(s),
@@ -281,12 +274,12 @@
}
int PhysicalSocket::GetError() const {
- CritScope cs(&crit_);
+ webrtc::MutexLock lock(&mutex_);
return error_;
}
void PhysicalSocket::SetError(int error) {
- CritScope cs(&crit_);
+ webrtc::MutexLock lock(&mutex_);
error_ = error;
}
@@ -767,21 +760,14 @@
return enabled_events();
}
-void SocketDispatcher::OnPreEvent(uint32_t ff) {
- if ((ff & DE_CONNECT) != 0)
- state_ = CS_CONNECTED;
-
-#if defined(WEBRTC_WIN)
-// We set CS_CLOSED from CheckSignalClose.
-#elif defined(WEBRTC_POSIX)
- if ((ff & DE_CLOSE) != 0)
- state_ = CS_CLOSED;
-#endif
-}
-
#if defined(WEBRTC_WIN)
void SocketDispatcher::OnEvent(uint32_t ff, int err) {
+ if ((ff & DE_CONNECT) != 0)
+ state_ = CS_CONNECTED;
+
+ // We set CS_CLOSED from CheckSignalClose.
+
int cache_id = id_;
// Make sure we deliver connect/accept first. Otherwise, consumers may see
// something like a READ followed by a CONNECT, which would be odd.
@@ -816,6 +802,12 @@
#elif defined(WEBRTC_POSIX)
void SocketDispatcher::OnEvent(uint32_t ff, int err) {
+ if ((ff & DE_CONNECT) != 0)
+ state_ = CS_CONNECTED;
+
+ if ((ff & DE_CLOSE) != 0)
+ state_ = CS_CLOSED;
+
#if defined(WEBRTC_USE_EPOLL)
// Remember currently enabled events so we can combine multiple changes
// into one update call later.
@@ -927,22 +919,32 @@
}
#if defined(WEBRTC_POSIX)
-class EventDispatcher : public Dispatcher {
+// Sets the value of a boolean value to false when signaled.
+class Signaler : public Dispatcher {
public:
- EventDispatcher(PhysicalSocketServer* ss) : ss_(ss), fSignaled_(false) {
- if (pipe(afd_) < 0)
- RTC_LOG(LERROR) << "pipe failed";
+ Signaler(PhysicalSocketServer* ss, bool& flag_to_clear)
+ : ss_(ss),
+ afd_([] {
+ std::array<int, 2> afd = {-1, -1};
+
+ if (pipe(afd.data()) < 0) {
+ RTC_LOG(LERROR) << "pipe failed";
+ }
+ return afd;
+ }()),
+ fSignaled_(false),
+ flag_to_clear_(flag_to_clear) {
ss_->Add(this);
}
- ~EventDispatcher() override {
+ ~Signaler() override {
ss_->Remove(this);
close(afd_[0]);
close(afd_[1]);
}
virtual void Signal() {
- CritScope cs(&crit_);
+ webrtc::MutexLock lock(&mutex_);
if (!fSignaled_) {
const uint8_t b[1] = {0};
const ssize_t res = write(afd_[1], b, sizeof(b));
@@ -953,30 +955,30 @@
uint32_t GetRequestedEvents() override { return DE_READ; }
- void OnPreEvent(uint32_t ff) override {
+ void OnEvent(uint32_t ff, int err) override {
// It is not possible to perfectly emulate an auto-resetting event with
// pipes. This simulates it by resetting before the event is handled.
- CritScope cs(&crit_);
+ webrtc::MutexLock lock(&mutex_);
if (fSignaled_) {
uint8_t b[4]; // Allow for reading more than 1 byte, but expect 1.
const ssize_t res = read(afd_[0], b, sizeof(b));
RTC_DCHECK_EQ(1, res);
fSignaled_ = false;
}
+ flag_to_clear_ = false;
}
- void OnEvent(uint32_t ff, int err) override { RTC_NOTREACHED(); }
-
int GetDescriptor() override { return afd_[0]; }
bool IsDescriptorClosed() override { return false; }
private:
- PhysicalSocketServer* ss_;
- int afd_[2];
- bool fSignaled_;
- RecursiveCriticalSection crit_;
+ PhysicalSocketServer* const ss_;
+ const std::array<int, 2> afd_;
+ bool fSignaled_ RTC_GUARDED_BY(mutex_);
+ webrtc::Mutex mutex_;
+ bool& flag_to_clear_;
};
#endif // WEBRTC_POSIX
@@ -995,16 +997,18 @@
return ffFD;
}
-class EventDispatcher : public Dispatcher {
+// Sets the value of a boolean value to false when signaled.
+class Signaler : public Dispatcher {
public:
- EventDispatcher(PhysicalSocketServer* ss) : ss_(ss) {
+ Signaler(PhysicalSocketServer* ss, bool& flag_to_clear)
+ : ss_(ss), flag_to_clear_(flag_to_clear) {
hev_ = WSACreateEvent();
if (hev_) {
ss_->Add(this);
}
}
- ~EventDispatcher() override {
+ ~Signaler() override {
if (hev_ != nullptr) {
ss_->Remove(this);
WSACloseEvent(hev_);
@@ -1019,9 +1023,10 @@
uint32_t GetRequestedEvents() override { return 0; }
- void OnPreEvent(uint32_t ff) override { WSAResetEvent(hev_); }
-
- void OnEvent(uint32_t ff, int err) override {}
+ void OnEvent(uint32_t ff, int err) override {
+ WSAResetEvent(hev_);
+ flag_to_clear_ = false;
+ }
WSAEVENT GetWSAEvent() override { return hev_; }
@@ -1032,24 +1037,10 @@
private:
PhysicalSocketServer* ss_;
WSAEVENT hev_;
+ bool& flag_to_clear_;
};
#endif // WEBRTC_WIN
-// Sets the value of a boolean value to false when signaled.
-class Signaler : public EventDispatcher {
- public:
- Signaler(PhysicalSocketServer* ss, bool* pf) : EventDispatcher(ss), pf_(pf) {}
- ~Signaler() override {}
-
- void OnEvent(uint32_t ff, int err) override {
- if (pf_)
- *pf_ = false;
- }
-
- private:
- bool* pf_;
-};
-
PhysicalSocketServer::PhysicalSocketServer()
:
#if defined(WEBRTC_USE_EPOLL)
@@ -1069,7 +1060,8 @@
// Note that -1 == INVALID_SOCKET, the alias used by later checks.
}
#endif
- signal_wakeup_ = new Signaler(this, &fWait_);
+ // The `fWait_` flag to be cleared by the Signaler.
+ signal_wakeup_ = new Signaler(this, fWait_);
}
PhysicalSocketServer::~PhysicalSocketServer() {
@@ -1237,7 +1229,6 @@
// Tell the descriptor about the event.
if (ff != 0) {
- dispatcher->OnPreEvent(ff);
dispatcher->OnEvent(ff, errcode);
}
}
@@ -1641,7 +1632,6 @@
continue;
}
Dispatcher* disp = dispatcher_by_key_.at(key);
- disp->OnPreEvent(0);
disp->OnEvent(0, 0);
} else if (process_io) {
// Iterate only on the dispatchers whose sockets were passed into
@@ -1712,7 +1702,6 @@
errcode = wsaEvents.iErrorCode[FD_CLOSE_BIT];
}
if (ff != 0) {
- disp->OnPreEvent(ff);
disp->OnEvent(ff, errcode);
}
}
diff --git a/rtc_base/physical_socket_server.h b/rtc_base/physical_socket_server.h
index cc21a67..4b7957e 100644
--- a/rtc_base/physical_socket_server.h
+++ b/rtc_base/physical_socket_server.h
@@ -21,9 +21,11 @@
#include <unordered_map>
#include <vector>
+#include "rtc_base/async_resolver.h"
+#include "rtc_base/async_resolver_interface.h"
#include "rtc_base/deprecated/recursive_critical_section.h"
-#include "rtc_base/net_helpers.h"
#include "rtc_base/socket_server.h"
+#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/system/rtc_export.h"
#include "rtc_base/thread_annotations.h"
@@ -48,7 +50,6 @@
public:
virtual ~Dispatcher() {}
virtual uint32_t GetRequestedEvents() = 0;
- virtual void OnPreEvent(uint32_t ff) = 0;
virtual void OnEvent(uint32_t ff, int err) = 0;
#if defined(WEBRTC_WIN)
virtual WSAEVENT GetWSAEvent() = 0;
@@ -202,8 +203,8 @@
SOCKET s_;
bool udp_;
int family_ = 0;
- RecursiveCriticalSection crit_;
- int error_ RTC_GUARDED_BY(crit_);
+ mutable webrtc::Mutex mutex_;
+ int error_ RTC_GUARDED_BY(mutex_);
ConnState state_;
AsyncResolver* resolver_;
@@ -236,7 +237,6 @@
#endif
uint32_t GetRequestedEvents() override;
- void OnPreEvent(uint32_t ff) override;
void OnEvent(uint32_t ff, int err) override;
int Close() override;
diff --git a/rtc_base/physical_socket_server_unittest.cc b/rtc_base/physical_socket_server_unittest.cc
index 648f397..3762762 100644
--- a/rtc_base/physical_socket_server_unittest.cc
+++ b/rtc_base/physical_socket_server_unittest.cc
@@ -18,6 +18,7 @@
#include "rtc_base/gunit.h"
#include "rtc_base/ip_address.h"
#include "rtc_base/logging.h"
+#include "rtc_base/net_helpers.h"
#include "rtc_base/network_monitor.h"
#include "rtc_base/socket_unittest.h"
#include "rtc_base/test_utils.h"
diff --git a/rtc_base/platform_thread.h b/rtc_base/platform_thread.h
index 4968de9..64a74d8 100644
--- a/rtc_base/platform_thread.h
+++ b/rtc_base/platform_thread.h
@@ -17,9 +17,9 @@
#include <string>
#include "absl/strings/string_view.h"
+#include "api/sequence_checker.h"
#include "rtc_base/constructor_magic.h"
#include "rtc_base/platform_thread_types.h"
-#include "rtc_base/thread_checker.h"
namespace rtc {
@@ -84,8 +84,8 @@
// TODO(pbos): Make sure call sites use string literals and update to a const
// char* instead of a std::string.
const std::string name_;
- rtc::ThreadChecker thread_checker_;
- rtc::ThreadChecker spawned_thread_checker_;
+ webrtc::SequenceChecker thread_checker_;
+ webrtc::SequenceChecker spawned_thread_checker_;
#if defined(WEBRTC_WIN)
static DWORD WINAPI StartThread(void* param);
diff --git a/rtc_base/ref_counted_object.h b/rtc_base/ref_counted_object.h
index ce18379..e86a1fb 100644
--- a/rtc_base/ref_counted_object.h
+++ b/rtc_base/ref_counted_object.h
@@ -59,6 +59,37 @@
RTC_DISALLOW_COPY_AND_ASSIGN(RefCountedObject);
};
+template <class T>
+class FinalRefCountedObject final : public T {
+ public:
+ using T::T;
+ // Until c++17 compilers are allowed not to inherit the default constructor,
+ // and msvc doesn't. Thus the default constructor is forwarded explicitly.
+ FinalRefCountedObject() = default;
+ FinalRefCountedObject(const FinalRefCountedObject&) = delete;
+ FinalRefCountedObject& operator=(const FinalRefCountedObject&) = delete;
+
+ void AddRef() const { ref_count_.IncRef(); }
+ void Release() const {
+ if (ref_count_.DecRef() == RefCountReleaseStatus::kDroppedLastRef) {
+ delete this;
+ }
+ }
+ bool HasOneRef() const { return ref_count_.HasOneRef(); }
+
+ private:
+ ~FinalRefCountedObject() = default;
+
+ // gcc v7.1 requires default contructors for members of
+ // `FinalRefCountedObject` to be able to use inherited constructors.
+ // TODO(danilchap): Replace with simpler braced initialization when
+ // bot support for that version of gcc is dropped.
+ class ZeroBasedRefCounter : public webrtc::webrtc_impl::RefCounter {
+ public:
+ ZeroBasedRefCounter() : RefCounter(0) {}
+ } mutable ref_count_;
+};
+
} // namespace rtc
#endif // RTC_BASE_REF_COUNTED_OBJECT_H_
diff --git a/rtc_base/ref_counted_object_unittest.cc b/rtc_base/ref_counted_object_unittest.cc
index eacf731..05380b7 100644
--- a/rtc_base/ref_counted_object_unittest.cc
+++ b/rtc_base/ref_counted_object_unittest.cc
@@ -12,6 +12,7 @@
#include <memory>
#include <string>
+#include <type_traits>
#include <utility>
#include "api/scoped_refptr.h"
@@ -95,4 +96,19 @@
EXPECT_EQ(c, ref->c_);
}
+TEST(FinalRefCountedObject, CanWrapIntoScopedRefptr) {
+ using WrappedTyped = FinalRefCountedObject<A>;
+ static_assert(!std::is_polymorphic<WrappedTyped>::value, "");
+ scoped_refptr<WrappedTyped> ref(new WrappedTyped());
+ EXPECT_TRUE(ref.get());
+ EXPECT_TRUE(ref->HasOneRef());
+ // Test reference counter is updated on some simple operations.
+ scoped_refptr<WrappedTyped> ref2 = ref;
+ EXPECT_FALSE(ref->HasOneRef());
+ EXPECT_FALSE(ref2->HasOneRef());
+
+ ref = nullptr;
+ EXPECT_TRUE(ref2->HasOneRef());
+}
+
} // namespace rtc
diff --git a/rtc_base/signal_thread.h b/rtc_base/signal_thread.h
deleted file mode 100644
index b444d54..0000000
--- a/rtc_base/signal_thread.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2020 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 RTC_BASE_SIGNAL_THREAD_H_
-#define RTC_BASE_SIGNAL_THREAD_H_
-
-// The facilities in this file have been deprecated. Please do not use them
-// in new code. New code should use factilities exposed by api/task_queue/
-// instead.
-#include "rtc_base/deprecated/signal_thread.h"
-
-#endif // RTC_BASE_SIGNAL_THREAD_H_
diff --git a/rtc_base/socket.h b/rtc_base/socket.h
index c2d1e3d..6b3ad5e 100644
--- a/rtc_base/socket.h
+++ b/rtc_base/socket.h
@@ -59,6 +59,8 @@
#define ECONNREFUSED WSAECONNREFUSED
#undef EHOSTUNREACH
#define EHOSTUNREACH WSAEHOSTUNREACH
+#undef ENETUNREACH
+#define ENETUNREACH WSAENETUNREACH
#define SOCKET_EACCES WSAEACCES
#endif // WEBRTC_WIN
diff --git a/rtc_base/ssl_identity.h b/rtc_base/ssl_identity.h
index d078b04..a9167ef 100644
--- a/rtc_base/ssl_identity.h
+++ b/rtc_base/ssl_identity.h
@@ -18,7 +18,6 @@
#include <memory>
#include <string>
-#include "rtc_base/deprecation.h"
#include "rtc_base/system/rtc_export.h"
namespace rtc {
diff --git a/rtc_base/ssl_stream_adapter.h b/rtc_base/ssl_stream_adapter.h
index 7bff726..977768a 100644
--- a/rtc_base/ssl_stream_adapter.h
+++ b/rtc_base/ssl_stream_adapter.h
@@ -18,7 +18,6 @@
#include <vector>
#include "absl/memory/memory.h"
-#include "rtc_base/deprecation.h"
#include "rtc_base/ssl_certificate.h"
#include "rtc_base/ssl_identity.h"
#include "rtc_base/stream.h"
diff --git a/rtc_base/swap_queue.h b/rtc_base/swap_queue.h
index 9eac49a..3c8149c 100644
--- a/rtc_base/swap_queue.h
+++ b/rtc_base/swap_queue.h
@@ -17,8 +17,8 @@
#include <utility>
#include <vector>
+#include "absl/base/attributes.h"
#include "rtc_base/checks.h"
-#include "rtc_base/system/unused.h"
namespace webrtc {
@@ -127,7 +127,7 @@
// When specified, the T given in *input must pass the ItemVerifier() test.
// The contents of *input after the call are then also guaranteed to pass the
// ItemVerifier() test.
- bool Insert(T* input) RTC_WARN_UNUSED_RESULT {
+ ABSL_MUST_USE_RESULT bool Insert(T* input) {
RTC_DCHECK(input);
RTC_DCHECK(queue_item_verifier_(*input));
@@ -168,7 +168,7 @@
// empty). When specified, The T given in *output must pass the ItemVerifier()
// test and the contents of *output after the call are then also guaranteed to
// pass the ItemVerifier() test.
- bool Remove(T* output) RTC_WARN_UNUSED_RESULT {
+ ABSL_MUST_USE_RESULT bool Remove(T* output) {
RTC_DCHECK(output);
RTC_DCHECK(queue_item_verifier_(*output));
diff --git a/rtc_base/synchronization/BUILD.gn b/rtc_base/synchronization/BUILD.gn
index 618e224..73ff667 100644
--- a/rtc_base/synchronization/BUILD.gn
+++ b/rtc_base/synchronization/BUILD.gn
@@ -6,6 +6,7 @@
# in the file PATENTS. All contributing project authors may
# be found in the AUTHORS file in the root of the source tree.
+import("//third_party/google_benchmark/buildconfig.gni")
import("../../webrtc.gni")
if (is_android) {
import("//build/config/android/config.gni")
@@ -36,7 +37,6 @@
"..:checks",
"..:macromagic",
"..:platform_thread_types",
- "../system:unused",
]
absl_deps = [ "//third_party/abseil-cpp/absl/base:core_headers" ]
if (rtc_use_absl_mutex) {
@@ -44,15 +44,15 @@
}
}
-rtc_library("sequence_checker") {
+rtc_library("sequence_checker_internal") {
+ visibility = [ "../../api:sequence_checker" ]
sources = [
- "sequence_checker.cc",
- "sequence_checker.h",
+ "sequence_checker_internal.cc",
+ "sequence_checker_internal.h",
]
deps = [
":mutex",
"..:checks",
- "..:criticalsection",
"..:macromagic",
"..:platform_thread_types",
"..:stringutils",
@@ -74,47 +74,35 @@
}
if (rtc_include_tests) {
- rtc_library("synchronization_unittests") {
- testonly = true
- sources = [
- "mutex_unittest.cc",
- "yield_policy_unittest.cc",
- ]
- deps = [
- ":mutex",
- ":yield",
- ":yield_policy",
- "..:checks",
- "..:macromagic",
- "..:rtc_base",
- "..:rtc_event",
- "../../test:test_support",
- "//third_party/google_benchmark",
- ]
- }
+ if (enable_google_benchmarks) {
+ rtc_library("synchronization_unittests") {
+ testonly = true
+ sources = [
+ "mutex_unittest.cc",
+ "yield_policy_unittest.cc",
+ ]
+ deps = [
+ ":mutex",
+ ":yield",
+ ":yield_policy",
+ "..:checks",
+ "..:macromagic",
+ "..:rtc_base",
+ "..:rtc_event",
+ "..:threading",
+ "../../test:test_support",
+ "//third_party/google_benchmark",
+ ]
+ }
- rtc_library("mutex_benchmark") {
- testonly = true
- sources = [ "mutex_benchmark.cc" ]
- deps = [
- ":mutex",
- "../system:unused",
- "//third_party/google_benchmark",
- ]
- }
-
- rtc_library("sequence_checker_unittests") {
- testonly = true
-
- sources = [ "sequence_checker_unittest.cc" ]
- deps = [
- ":sequence_checker",
- "..:checks",
- "..:rtc_base_approved",
- "..:task_queue_for_test",
- "../../api:function_view",
- "../../test:test_main",
- "../../test:test_support",
- ]
+ rtc_library("mutex_benchmark") {
+ testonly = true
+ sources = [ "mutex_benchmark.cc" ]
+ deps = [
+ ":mutex",
+ "../system:unused",
+ "//third_party/google_benchmark",
+ ]
+ }
}
}
diff --git a/rtc_base/synchronization/mutex.h b/rtc_base/synchronization/mutex.h
index 620fe74..0023d90 100644
--- a/rtc_base/synchronization/mutex.h
+++ b/rtc_base/synchronization/mutex.h
@@ -13,9 +13,9 @@
#include <atomic>
+#include "absl/base/attributes.h"
#include "absl/base/const_init.h"
#include "rtc_base/checks.h"
-#include "rtc_base/system/unused.h"
#include "rtc_base/thread_annotations.h"
#if defined(WEBRTC_ABSL_MUTEX)
@@ -41,7 +41,7 @@
void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() {
impl_.Lock();
}
- RTC_WARN_UNUSED_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
+ ABSL_MUST_USE_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
return impl_.TryLock();
}
void Unlock() RTC_UNLOCK_FUNCTION() {
diff --git a/rtc_base/synchronization/mutex_abseil.h b/rtc_base/synchronization/mutex_abseil.h
index 4ad1d07..9247065 100644
--- a/rtc_base/synchronization/mutex_abseil.h
+++ b/rtc_base/synchronization/mutex_abseil.h
@@ -11,6 +11,7 @@
#ifndef RTC_BASE_SYNCHRONIZATION_MUTEX_ABSEIL_H_
#define RTC_BASE_SYNCHRONIZATION_MUTEX_ABSEIL_H_
+#include "absl/base/attributes.h"
#include "absl/synchronization/mutex.h"
#include "rtc_base/thread_annotations.h"
@@ -23,7 +24,7 @@
MutexImpl& operator=(const MutexImpl&) = delete;
void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { mutex_.Lock(); }
- RTC_WARN_UNUSED_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
+ ABSL_MUST_USE_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
return mutex_.TryLock();
}
void Unlock() RTC_UNLOCK_FUNCTION() { mutex_.Unlock(); }
diff --git a/rtc_base/synchronization/mutex_critical_section.h b/rtc_base/synchronization/mutex_critical_section.h
index d206794..cb3d6a0 100644
--- a/rtc_base/synchronization/mutex_critical_section.h
+++ b/rtc_base/synchronization/mutex_critical_section.h
@@ -23,6 +23,7 @@
#include <sal.h> // must come after windows headers.
// clang-format on
+#include "absl/base/attributes.h"
#include "rtc_base/thread_annotations.h"
namespace webrtc {
@@ -37,7 +38,7 @@
void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() {
EnterCriticalSection(&critical_section_);
}
- RTC_WARN_UNUSED_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
+ ABSL_MUST_USE_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
return TryEnterCriticalSection(&critical_section_) != FALSE;
}
void Unlock() RTC_UNLOCK_FUNCTION() {
diff --git a/rtc_base/synchronization/mutex_pthread.h b/rtc_base/synchronization/mutex_pthread.h
index c9496e7..8898ca5 100644
--- a/rtc_base/synchronization/mutex_pthread.h
+++ b/rtc_base/synchronization/mutex_pthread.h
@@ -18,6 +18,7 @@
#include <pthread_spis.h>
#endif
+#include "absl/base/attributes.h"
#include "rtc_base/thread_annotations.h"
namespace webrtc {
@@ -39,7 +40,7 @@
~MutexImpl() { pthread_mutex_destroy(&mutex_); }
void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { pthread_mutex_lock(&mutex_); }
- RTC_WARN_UNUSED_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
+ ABSL_MUST_USE_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
return pthread_mutex_trylock(&mutex_) == 0;
}
void Unlock() RTC_UNLOCK_FUNCTION() { pthread_mutex_unlock(&mutex_); }
diff --git a/rtc_base/synchronization/sequence_checker.h b/rtc_base/synchronization/sequence_checker.h
deleted file mode 100644
index ecf8490..0000000
--- a/rtc_base/synchronization/sequence_checker.h
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright 2019 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 RTC_BASE_SYNCHRONIZATION_SEQUENCE_CHECKER_H_
-#define RTC_BASE_SYNCHRONIZATION_SEQUENCE_CHECKER_H_
-
-#include <type_traits>
-
-#include "api/task_queue/task_queue_base.h"
-#include "rtc_base/platform_thread_types.h"
-#include "rtc_base/synchronization/mutex.h"
-#include "rtc_base/system/rtc_export.h"
-#include "rtc_base/thread_annotations.h"
-
-namespace webrtc {
-// Real implementation of SequenceChecker, for use in debug mode, or
-// for temporary use in release mode (e.g. to RTC_CHECK on a threading issue
-// seen only in the wild).
-//
-// Note: You should almost always use the SequenceChecker class to get the
-// right version for your build configuration.
-class RTC_EXPORT SequenceCheckerImpl {
- public:
- SequenceCheckerImpl();
- ~SequenceCheckerImpl();
-
- bool IsCurrent() const;
- // Changes the task queue or thread that is checked for in IsCurrent. This can
- // be useful when an object may be created on one task queue / thread and then
- // used exclusively on another thread.
- void Detach();
-
- // Returns a string that is formatted to match with the error string printed
- // by RTC_CHECK() when a condition is not met.
- // This is used in conjunction with the RTC_DCHECK_RUN_ON() macro.
- std::string ExpectationToString() const;
-
- private:
- mutable Mutex lock_;
- // These are mutable so that IsCurrent can set them.
- mutable bool attached_ RTC_GUARDED_BY(lock_);
- mutable rtc::PlatformThreadRef valid_thread_ RTC_GUARDED_BY(lock_);
- mutable const TaskQueueBase* valid_queue_ RTC_GUARDED_BY(lock_);
- mutable const void* valid_system_queue_ RTC_GUARDED_BY(lock_);
-};
-
-// Do nothing implementation, for use in release mode.
-//
-// Note: You should almost always use the SequenceChecker class to get the
-// right version for your build configuration.
-class SequenceCheckerDoNothing {
- public:
- bool IsCurrent() const { return true; }
- void Detach() {}
-};
-
-// SequenceChecker is a helper class used to help verify that some methods
-// of a class are called on the same task queue or thread. A
-// SequenceChecker is bound to a a task queue if the object is
-// created on a task queue, or a thread otherwise.
-//
-//
-// Example:
-// class MyClass {
-// public:
-// void Foo() {
-// RTC_DCHECK_RUN_ON(sequence_checker_);
-// ... (do stuff) ...
-// }
-//
-// private:
-// SequenceChecker sequence_checker_;
-// }
-//
-// In Release mode, IsCurrent will always return true.
-#if RTC_DCHECK_IS_ON
-class RTC_LOCKABLE SequenceChecker : public SequenceCheckerImpl {};
-#else
-class RTC_LOCKABLE SequenceChecker : public SequenceCheckerDoNothing {};
-#endif // RTC_ENABLE_THREAD_CHECKER
-
-namespace webrtc_seq_check_impl {
-// Helper class used by RTC_DCHECK_RUN_ON (see example usage below).
-class RTC_SCOPED_LOCKABLE SequenceCheckerScope {
- public:
- template <typename ThreadLikeObject>
- explicit SequenceCheckerScope(const ThreadLikeObject* thread_like_object)
- RTC_EXCLUSIVE_LOCK_FUNCTION(thread_like_object) {}
- SequenceCheckerScope(const SequenceCheckerScope&) = delete;
- SequenceCheckerScope& operator=(const SequenceCheckerScope&) = delete;
- ~SequenceCheckerScope() RTC_UNLOCK_FUNCTION() {}
-
- template <typename ThreadLikeObject>
- static bool IsCurrent(const ThreadLikeObject* thread_like_object) {
- return thread_like_object->IsCurrent();
- }
-};
-} // namespace webrtc_seq_check_impl
-} // namespace webrtc
-
-// RTC_RUN_ON/RTC_GUARDED_BY/RTC_DCHECK_RUN_ON macros allows to annotate
-// variables are accessed from same thread/task queue.
-// Using tools designed to check mutexes, it checks at compile time everywhere
-// variable is access, there is a run-time dcheck thread/task queue is correct.
-//
-// class ThreadExample {
-// public:
-// void NeedVar1() {
-// RTC_DCHECK_RUN_ON(network_thread_);
-// transport_->Send();
-// }
-//
-// private:
-// rtc::Thread* network_thread_;
-// int transport_ RTC_GUARDED_BY(network_thread_);
-// };
-//
-// class SequenceCheckerExample {
-// public:
-// int CalledFromPacer() RTC_RUN_ON(pacer_sequence_checker_) {
-// return var2_;
-// }
-//
-// void CallMeFromPacer() {
-// RTC_DCHECK_RUN_ON(&pacer_sequence_checker_)
-// << "Should be called from pacer";
-// CalledFromPacer();
-// }
-//
-// private:
-// int pacer_var_ RTC_GUARDED_BY(pacer_sequence_checker_);
-// SequenceChecker pacer_sequence_checker_;
-// };
-//
-// class TaskQueueExample {
-// public:
-// class Encoder {
-// public:
-// rtc::TaskQueue* Queue() { return encoder_queue_; }
-// void Encode() {
-// RTC_DCHECK_RUN_ON(encoder_queue_);
-// DoSomething(var_);
-// }
-//
-// private:
-// rtc::TaskQueue* const encoder_queue_;
-// Frame var_ RTC_GUARDED_BY(encoder_queue_);
-// };
-//
-// void Encode() {
-// // Will fail at runtime when DCHECK is enabled:
-// // encoder_->Encode();
-// // Will work:
-// rtc::scoped_refptr<Encoder> encoder = encoder_;
-// encoder_->Queue()->PostTask([encoder] { encoder->Encode(); });
-// }
-//
-// private:
-// rtc::scoped_refptr<Encoder> encoder_;
-// }
-
-// Document if a function expected to be called from same thread/task queue.
-#define RTC_RUN_ON(x) \
- RTC_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(x))
-
-namespace webrtc {
-std::string ExpectationToString(const webrtc::SequenceChecker* checker);
-
-// Catch-all implementation for types other than explicitly supported above.
-template <typename ThreadLikeObject>
-std::string ExpectationToString(const ThreadLikeObject*) {
- return std::string();
-}
-
-} // namespace webrtc
-
-#define RTC_DCHECK_RUN_ON(x) \
- webrtc::webrtc_seq_check_impl::SequenceCheckerScope seq_check_scope(x); \
- RTC_DCHECK((x)->IsCurrent()) << webrtc::ExpectationToString(x)
-
-#endif // RTC_BASE_SYNCHRONIZATION_SEQUENCE_CHECKER_H_
diff --git a/rtc_base/synchronization/sequence_checker.cc b/rtc_base/synchronization/sequence_checker_internal.cc
similarity index 92%
rename from rtc_base/synchronization/sequence_checker.cc
rename to rtc_base/synchronization/sequence_checker_internal.cc
index 1de26cf..7b66d80 100644
--- a/rtc_base/synchronization/sequence_checker.cc
+++ b/rtc_base/synchronization/sequence_checker_internal.cc
@@ -7,15 +7,19 @@
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
-#include "rtc_base/synchronization/sequence_checker.h"
+#include "rtc_base/synchronization/sequence_checker_internal.h"
+
+#include <string>
#if defined(WEBRTC_MAC)
#include <dispatch/dispatch.h>
#endif
+#include "rtc_base/checks.h"
#include "rtc_base/strings/string_builder.h"
namespace webrtc {
+namespace webrtc_sequence_checker_internal {
namespace {
// On Mac, returns the label of the current dispatch queue; elsewhere, return
// null.
@@ -29,21 +33,12 @@
} // namespace
-std::string ExpectationToString(const webrtc::SequenceChecker* checker) {
-#if RTC_DCHECK_IS_ON
- return checker->ExpectationToString();
-#endif
- return std::string();
-}
-
SequenceCheckerImpl::SequenceCheckerImpl()
: attached_(true),
valid_thread_(rtc::CurrentThreadRef()),
valid_queue_(TaskQueueBase::Current()),
valid_system_queue_(GetSystemQueueRef()) {}
-SequenceCheckerImpl::~SequenceCheckerImpl() = default;
-
bool SequenceCheckerImpl::IsCurrent() const {
const TaskQueueBase* const current_queue = TaskQueueBase::Current();
const rtc::PlatformThreadRef current_thread = rtc::CurrentThreadRef();
@@ -109,4 +104,12 @@
}
#endif // RTC_DCHECK_IS_ON
+std::string ExpectationToString(const SequenceCheckerImpl* checker) {
+#if RTC_DCHECK_IS_ON
+ return checker->ExpectationToString();
+#endif
+ return std::string();
+}
+
+} // namespace webrtc_sequence_checker_internal
} // namespace webrtc
diff --git a/rtc_base/synchronization/sequence_checker_internal.h b/rtc_base/synchronization/sequence_checker_internal.h
new file mode 100644
index 0000000..f7ac6de
--- /dev/null
+++ b/rtc_base/synchronization/sequence_checker_internal.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2020 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 RTC_BASE_SYNCHRONIZATION_SEQUENCE_CHECKER_INTERNAL_H_
+#define RTC_BASE_SYNCHRONIZATION_SEQUENCE_CHECKER_INTERNAL_H_
+
+#include <string>
+#include <type_traits>
+
+#include "api/task_queue/task_queue_base.h"
+#include "rtc_base/platform_thread_types.h"
+#include "rtc_base/synchronization/mutex.h"
+#include "rtc_base/system/rtc_export.h"
+#include "rtc_base/thread_annotations.h"
+
+namespace webrtc {
+namespace webrtc_sequence_checker_internal {
+
+// Real implementation of SequenceChecker, for use in debug mode, or
+// for temporary use in release mode (e.g. to RTC_CHECK on a threading issue
+// seen only in the wild).
+//
+// Note: You should almost always use the SequenceChecker class to get the
+// right version for your build configuration.
+class RTC_EXPORT SequenceCheckerImpl {
+ public:
+ SequenceCheckerImpl();
+ ~SequenceCheckerImpl() = default;
+
+ bool IsCurrent() const;
+ // Changes the task queue or thread that is checked for in IsCurrent. This can
+ // be useful when an object may be created on one task queue / thread and then
+ // used exclusively on another thread.
+ void Detach();
+
+ // Returns a string that is formatted to match with the error string printed
+ // by RTC_CHECK() when a condition is not met.
+ // This is used in conjunction with the RTC_DCHECK_RUN_ON() macro.
+ std::string ExpectationToString() const;
+
+ private:
+ mutable Mutex lock_;
+ // These are mutable so that IsCurrent can set them.
+ mutable bool attached_ RTC_GUARDED_BY(lock_);
+ mutable rtc::PlatformThreadRef valid_thread_ RTC_GUARDED_BY(lock_);
+ mutable const TaskQueueBase* valid_queue_ RTC_GUARDED_BY(lock_);
+ mutable const void* valid_system_queue_ RTC_GUARDED_BY(lock_);
+};
+
+// Do nothing implementation, for use in release mode.
+//
+// Note: You should almost always use the SequenceChecker class to get the
+// right version for your build configuration.
+class SequenceCheckerDoNothing {
+ public:
+ bool IsCurrent() const { return true; }
+ void Detach() {}
+};
+
+// Helper class used by RTC_DCHECK_RUN_ON (see example usage below).
+class RTC_SCOPED_LOCKABLE SequenceCheckerScope {
+ public:
+ template <typename ThreadLikeObject>
+ explicit SequenceCheckerScope(const ThreadLikeObject* thread_like_object)
+ RTC_EXCLUSIVE_LOCK_FUNCTION(thread_like_object) {}
+ SequenceCheckerScope(const SequenceCheckerScope&) = delete;
+ SequenceCheckerScope& operator=(const SequenceCheckerScope&) = delete;
+ ~SequenceCheckerScope() RTC_UNLOCK_FUNCTION() {}
+
+ template <typename ThreadLikeObject>
+ static bool IsCurrent(const ThreadLikeObject* thread_like_object) {
+ return thread_like_object->IsCurrent();
+ }
+};
+
+std::string ExpectationToString(const SequenceCheckerImpl* checker);
+
+// Catch-all implementation for types other than explicitly supported above.
+template <typename ThreadLikeObject>
+std::string ExpectationToString(const ThreadLikeObject*) {
+ return std::string();
+}
+
+} // namespace webrtc_sequence_checker_internal
+} // namespace webrtc
+
+#endif // RTC_BASE_SYNCHRONIZATION_SEQUENCE_CHECKER_INTERNAL_H_
diff --git a/rtc_base/system/BUILD.gn b/rtc_base/system/BUILD.gn
index 385f2e1..9f83c62 100644
--- a/rtc_base/system/BUILD.gn
+++ b/rtc_base/system/BUILD.gn
@@ -57,7 +57,6 @@
rtc_source_set("no_unique_address") {
sources = [ "no_unique_address.h" ]
- deps = [ "..:sanitizer" ]
}
if (is_mac || is_ios) {
diff --git a/rtc_base/system/no_unique_address.h b/rtc_base/system/no_unique_address.h
index eca349c..77e7a99 100644
--- a/rtc_base/system/no_unique_address.h
+++ b/rtc_base/system/no_unique_address.h
@@ -11,8 +11,6 @@
#ifndef RTC_BASE_SYSTEM_NO_UNIQUE_ADDRESS_H_
#define RTC_BASE_SYSTEM_NO_UNIQUE_ADDRESS_H_
-#include "rtc_base/sanitizer.h"
-
// RTC_NO_UNIQUE_ADDRESS is a portable annotation to tell the compiler that
// a data member need not have an address distinct from all other non-static
// data members of its class.
@@ -26,10 +24,7 @@
// should add support for it starting from C++20. Among clang compilers,
// clang-cl doesn't support it yet and support is unclear also when the target
// platform is iOS.
-//
-// TODO(bugs.webrtc.org/12218): Re-enable on MSan builds.
-#if !RTC_HAS_MSAN && \
- ((defined(__clang__) && !defined(_MSC_VER) && !defined(WEBRTC_IOS)) || \
+#if ((defined(__clang__) && !defined(_MSC_VER) && !defined(WEBRTC_IOS)) || \
__cplusplus > 201703L)
// NOLINTNEXTLINE(whitespace/braces)
#define RTC_NO_UNIQUE_ADDRESS [[no_unique_address]]
diff --git a/rtc_base/system/unused.h b/rtc_base/system/unused.h
index a0add4e..084c526 100644
--- a/rtc_base/system/unused.h
+++ b/rtc_base/system/unused.h
@@ -11,21 +11,6 @@
#ifndef RTC_BASE_SYSTEM_UNUSED_H_
#define RTC_BASE_SYSTEM_UNUSED_H_
-// Annotate a function indicating the caller must examine the return value.
-// Use like:
-// int foo() RTC_WARN_UNUSED_RESULT;
-// To explicitly ignore a result, cast to void.
-// TODO(kwiberg): Remove when we can use [[nodiscard]] from C++17.
-#if defined(__clang__)
-#define RTC_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))
-#elif defined(__GNUC__)
-// gcc has a __warn_unused_result__ attribute, but you can't quiet it by
-// casting to void, so we don't use it.
-#define RTC_WARN_UNUSED_RESULT
-#else
-#define RTC_WARN_UNUSED_RESULT
-#endif
-
// Prevent the compiler from warning about an unused variable. For example:
// int result = DoSomething();
// assert(result == 17);
diff --git a/rtc_base/system_time.cc b/rtc_base/system_time.cc
new file mode 100644
index 0000000..9efe76e
--- /dev/null
+++ b/rtc_base/system_time.cc
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2021 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.
+ */
+
+// If WEBRTC_EXCLUDE_SYSTEM_TIME is set, an implementation of
+// rtc::SystemTimeNanos() must be provided externally.
+#ifndef WEBRTC_EXCLUDE_SYSTEM_TIME
+
+#include <stdint.h>
+
+#include <limits>
+
+#if defined(WEBRTC_POSIX)
+#include <sys/time.h>
+#if defined(WEBRTC_MAC)
+#include <mach/mach_time.h>
+#endif
+#endif
+
+#if defined(WEBRTC_WIN)
+// clang-format off
+// clang formatting would put <windows.h> last,
+// which leads to compilation failure.
+#include <windows.h>
+#include <mmsystem.h>
+#include <sys/timeb.h>
+// clang-format on
+#endif
+
+#include "rtc_base/checks.h"
+#include "rtc_base/numerics/safe_conversions.h"
+#include "rtc_base/system_time.h"
+#include "rtc_base/time_utils.h"
+
+namespace rtc {
+
+int64_t SystemTimeNanos() {
+ int64_t ticks;
+#if defined(WEBRTC_MAC)
+ static mach_timebase_info_data_t timebase;
+ if (timebase.denom == 0) {
+ // Get the timebase if this is the first time we run.
+ // Recommended by Apple's QA1398.
+ if (mach_timebase_info(&timebase) != KERN_SUCCESS) {
+ RTC_NOTREACHED();
+ }
+ }
+ // Use timebase to convert absolute time tick units into nanoseconds.
+ const auto mul = [](uint64_t a, uint32_t b) -> int64_t {
+ RTC_DCHECK_NE(b, 0);
+ RTC_DCHECK_LE(a, std::numeric_limits<int64_t>::max() / b)
+ << "The multiplication " << a << " * " << b << " overflows";
+ return rtc::dchecked_cast<int64_t>(a * b);
+ };
+ ticks = mul(mach_absolute_time(), timebase.numer) / timebase.denom;
+#elif defined(WEBRTC_POSIX)
+ struct timespec ts;
+ // TODO(deadbeef): Do we need to handle the case when CLOCK_MONOTONIC is not
+ // supported?
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ ticks = kNumNanosecsPerSec * static_cast<int64_t>(ts.tv_sec) +
+ static_cast<int64_t>(ts.tv_nsec);
+#elif defined(WINUWP)
+ ticks = WinUwpSystemTimeNanos();
+#elif defined(WEBRTC_WIN)
+ static volatile LONG last_timegettime = 0;
+ static volatile int64_t num_wrap_timegettime = 0;
+ volatile LONG* last_timegettime_ptr = &last_timegettime;
+ DWORD now = timeGetTime();
+ // Atomically update the last gotten time
+ DWORD old = InterlockedExchange(last_timegettime_ptr, now);
+ if (now < old) {
+ // If now is earlier than old, there may have been a race between threads.
+ // 0x0fffffff ~3.1 days, the code will not take that long to execute
+ // so it must have been a wrap around.
+ if (old > 0xf0000000 && now < 0x0fffffff) {
+ num_wrap_timegettime++;
+ }
+ }
+ ticks = now + (num_wrap_timegettime << 32);
+ // TODO(deadbeef): Calculate with nanosecond precision. Otherwise, we're
+ // just wasting a multiply and divide when doing Time() on Windows.
+ ticks = ticks * kNumNanosecsPerMillisec;
+#else
+#error Unsupported platform.
+#endif
+ return ticks;
+}
+
+} // namespace rtc
+#endif // WEBRTC_EXCLUDE_SYSTEM_TIME
diff --git a/rtc_base/system_time.h b/rtc_base/system_time.h
new file mode 100644
index 0000000..d86e94a
--- /dev/null
+++ b/rtc_base/system_time.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2021 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 RTC_BASE_SYSTEM_TIME_H_
+#define RTC_BASE_SYSTEM_TIME_H_
+
+namespace rtc {
+
+// Returns the actual system time, even if a clock is set for testing.
+// Useful for timeouts while using a test clock, or for logging.
+int64_t SystemTimeNanos();
+
+} // namespace rtc
+
+#endif // RTC_BASE_SYSTEM_TIME_H_
diff --git a/rtc_base/task_queue_unittest.cc b/rtc_base/task_queue_unittest.cc
index a7148dc..0c79858 100644
--- a/rtc_base/task_queue_unittest.cc
+++ b/rtc_base/task_queue_unittest.cc
@@ -21,7 +21,6 @@
#include <vector>
#include "absl/memory/memory.h"
-#include "rtc_base/bind.h"
#include "rtc_base/event.h"
#include "rtc_base/task_queue_for_test.h"
#include "rtc_base/time_utils.h"
@@ -67,7 +66,7 @@
webrtc::TaskQueueForTest queue(kQueueName, TaskQueue::Priority::HIGH);
uint32_t start = Time();
- queue.PostDelayedTask(Bind(&CheckCurrent, &event, &queue), 3);
+ queue.PostDelayedTask([&event, &queue] { CheckCurrent(&event, &queue); }, 3);
EXPECT_TRUE(event.Wait(1000));
uint32_t end = TimeMillis();
// These tests are a little relaxed due to how "powerful" our test bots can
diff --git a/rtc_base/task_utils/BUILD.gn b/rtc_base/task_utils/BUILD.gn
index 018844f..39e4ba1 100644
--- a/rtc_base/task_utils/BUILD.gn
+++ b/rtc_base/task_utils/BUILD.gn
@@ -16,13 +16,12 @@
deps = [
":to_queued_task",
"..:logging",
- "..:thread_checker",
"..:timeutils",
+ "../../api:sequence_checker",
"../../api/task_queue",
"../../api/units:time_delta",
"../../api/units:timestamp",
"../../system_wrappers:system_wrappers",
- "../synchronization:sequence_checker",
]
absl_deps = [ "//third_party/abseil-cpp/absl/memory" ]
}
@@ -35,9 +34,8 @@
deps = [
"..:checks",
"..:refcount",
- "..:thread_checker",
"../../api:scoped_refptr",
- "../synchronization:sequence_checker",
+ "../../api:sequence_checker",
"../system:no_unique_address",
]
}
diff --git a/rtc_base/task_utils/pending_task_safety_flag.cc b/rtc_base/task_utils/pending_task_safety_flag.cc
index 4be2131..acdbc00 100644
--- a/rtc_base/task_utils/pending_task_safety_flag.cc
+++ b/rtc_base/task_utils/pending_task_safety_flag.cc
@@ -24,6 +24,11 @@
alive_ = false;
}
+void PendingTaskSafetyFlag::SetAlive() {
+ RTC_DCHECK_RUN_ON(&main_sequence_);
+ alive_ = true;
+}
+
bool PendingTaskSafetyFlag::alive() const {
RTC_DCHECK_RUN_ON(&main_sequence_);
return alive_;
diff --git a/rtc_base/task_utils/pending_task_safety_flag.h b/rtc_base/task_utils/pending_task_safety_flag.h
index 182db2c..4c9d348 100644
--- a/rtc_base/task_utils/pending_task_safety_flag.h
+++ b/rtc_base/task_utils/pending_task_safety_flag.h
@@ -12,23 +12,29 @@
#define RTC_BASE_TASK_UTILS_PENDING_TASK_SAFETY_FLAG_H_
#include "api/scoped_refptr.h"
+#include "api/sequence_checker.h"
#include "rtc_base/checks.h"
#include "rtc_base/ref_count.h"
-#include "rtc_base/synchronization/sequence_checker.h"
#include "rtc_base/system/no_unique_address.h"
namespace webrtc {
-// Use this flag to drop pending tasks that have been posted to the "main"
-// thread/TQ and end up running after the owning instance has been
-// deleted. The owning instance signals deletion by calling SetNotAlive() from
-// its destructor.
-//
+// The PendingTaskSafetyFlag and the ScopedTaskSafety are designed to address
+// the issue where you have a task to be executed later that has references,
+// but cannot guarantee that the referenced object is alive when the task is
+// executed.
+
+// This mechanism can be used with tasks that are created and destroyed
+// on a single thread / task queue, and with tasks posted to the same
+// thread/task queue, but tasks can be posted from any thread/TQ.
+
+// Typical usage:
// When posting a task, post a copy (capture by-value in a lambda) of the flag
-// instance and before performing the work, check the |alive()| state. Abort if
+// reference and before performing the work, check the |alive()| state. Abort if
// alive() returns |false|:
//
-// // Running outside of the main thread.
+// class ExampleClass {
+// ....
// my_task_queue_->PostTask(ToQueuedTask(
// [safety = pending_task_safety_flag_, this]() {
// // Now running on the main thread.
@@ -36,15 +42,19 @@
// return;
// MyMethod();
// }));
+// ....
+// ~ExampleClass() {
+// pending_task_safety_flag_->SetNotAlive();
+// }
+// scoped_refptr<PendingTaskSafetyFlag> pending_task_safety_flag_
+// = PendingTaskSafetyFlag::Create();
+// }
//
-// Or implicitly by letting ToQueuedTask do the checking:
+// ToQueuedTask has an overload that makes this check automatic:
//
-// // Running outside of the main thread.
// my_task_queue_->PostTask(ToQueuedTask(pending_task_safety_flag_,
// [this]() { MyMethod(); }));
//
-// Note that checking the state only works on the construction/destruction
-// thread of the ReceiveStatisticsProxy instance.
class PendingTaskSafetyFlag : public rtc::RefCountInterface {
public:
static rtc::scoped_refptr<PendingTaskSafetyFlag> Create();
@@ -52,6 +62,7 @@
~PendingTaskSafetyFlag() = default;
void SetNotAlive();
+ void SetAlive();
bool alive() const;
protected:
@@ -62,11 +73,22 @@
RTC_NO_UNIQUE_ADDRESS SequenceChecker main_sequence_;
};
-// Makes using PendingTaskSafetyFlag very simple. Automatic PTSF creation
-// and signalling of destruction when the ScopedTaskSafety instance goes out
-// of scope.
-// Should be used by the class that wants tasks dropped after destruction.
-// Requirements are that the instance be constructed and destructed on
+// The ScopedTaskSafety makes using PendingTaskSafetyFlag very simple.
+// It does automatic PTSF creation and signalling of destruction when the
+// ScopedTaskSafety instance goes out of scope.
+//
+// ToQueuedTask has an overload that takes a ScopedTaskSafety too, so there
+// is no need to explicitly call the "flag" method.
+//
+// Example usage:
+//
+// my_task_queue->PostTask(ToQueuedTask(scoped_task_safety,
+// [this]() {
+// // task goes here
+// }
+//
+// This should be used by the class that wants tasks dropped after destruction.
+// The requirement is that the instance has to be constructed and destructed on
// the same thread as the potentially dropped tasks would be running on.
class ScopedTaskSafety {
public:
diff --git a/rtc_base/task_utils/to_queued_task.h b/rtc_base/task_utils/to_queued_task.h
index 07ab0eb..b2e3aae 100644
--- a/rtc_base/task_utils/to_queued_task.h
+++ b/rtc_base/task_utils/to_queued_task.h
@@ -20,7 +20,7 @@
namespace webrtc {
namespace webrtc_new_closure_impl {
-// Simple implementation of QueuedTask for use with rtc::Bind and lambdas.
+// Simple implementation of QueuedTask for use with lambdas.
template <typename Closure>
class ClosureTask : public QueuedTask {
public:
diff --git a/rtc_base/thread.cc b/rtc_base/thread.cc
index 3244902..a047eed 100644
--- a/rtc_base/thread.cc
+++ b/rtc_base/thread.cc
@@ -29,13 +29,14 @@
#include <utility>
#include "absl/algorithm/container.h"
+#include "api/sequence_checker.h"
#include "rtc_base/atomic_ops.h"
#include "rtc_base/checks.h"
#include "rtc_base/deprecated/recursive_critical_section.h"
#include "rtc_base/event.h"
+#include "rtc_base/internal/default_socket_server.h"
#include "rtc_base/logging.h"
#include "rtc_base/null_socket_server.h"
-#include "rtc_base/synchronization/sequence_checker.h"
#include "rtc_base/task_utils/to_queued_task.h"
#include "rtc_base/time_utils.h"
#include "rtc_base/trace_event.h"
@@ -70,8 +71,6 @@
namespace rtc {
namespace {
-const int kSlowDispatchLoggingThreshold = 50; // 50 ms
-
class MessageHandlerWithTask final : public MessageHandler {
public:
MessageHandlerWithTask() {}
@@ -257,7 +256,7 @@
#ifndef NO_MAIN_THREAD_WRAPPING
// Only autowrap the thread which instantiated the ThreadManager.
if (!thread && manager->IsMainThread()) {
- thread = new Thread(SocketServer::CreateDefault());
+ thread = new Thread(CreateDefaultSocketServer());
thread->WrapCurrentWithThreadManager(manager, true);
}
#endif
@@ -326,7 +325,7 @@
Thread* ThreadManager::WrapCurrentThread() {
Thread* result = CurrentThread();
if (nullptr == result) {
- result = new Thread(SocketServer::CreateDefault());
+ result = new Thread(CreateDefaultSocketServer());
result->WrapCurrentWithThreadManager(this, true);
}
return result;
@@ -680,14 +679,18 @@
TRACE_EVENT2("webrtc", "Thread::Dispatch", "src_file",
pmsg->posted_from.file_name(), "src_func",
pmsg->posted_from.function_name());
+ RTC_DCHECK_RUN_ON(this);
int64_t start_time = TimeMillis();
pmsg->phandler->OnMessage(pmsg);
int64_t end_time = TimeMillis();
int64_t diff = TimeDiff(end_time, start_time);
- if (diff >= kSlowDispatchLoggingThreshold) {
- RTC_LOG(LS_INFO) << "Message took " << diff
+ if (diff >= dispatch_warning_ms_) {
+ RTC_LOG(LS_INFO) << "Message to " << name() << " took " << diff
<< "ms to dispatch. Posted from: "
<< pmsg->posted_from.ToString();
+ // To avoid log spew, move the warning limit to only give warning
+ // for delays that are larger than the one observed.
+ dispatch_warning_ms_ = diff + 1;
}
}
@@ -696,7 +699,7 @@
}
std::unique_ptr<Thread> Thread::CreateWithSocketServer() {
- return std::unique_ptr<Thread>(new Thread(SocketServer::CreateDefault()));
+ return std::unique_ptr<Thread>(new Thread(CreateDefaultSocketServer()));
}
std::unique_ptr<Thread> Thread::Create() {
@@ -739,6 +742,16 @@
return true;
}
+void Thread::SetDispatchWarningMs(int deadline) {
+ if (!IsCurrent()) {
+ PostTask(webrtc::ToQueuedTask(
+ [this, deadline]() { SetDispatchWarningMs(deadline); }));
+ return;
+ }
+ RTC_DCHECK_RUN_ON(this);
+ dispatch_warning_ms_ = deadline;
+}
+
bool Thread::Start() {
RTC_DCHECK(!IsRunning());
@@ -1137,7 +1150,7 @@
}
AutoThread::AutoThread()
- : Thread(SocketServer::CreateDefault(), /*do_init=*/false) {
+ : Thread(CreateDefaultSocketServer(), /*do_init=*/false) {
if (!ThreadManager::Instance()->CurrentThread()) {
// DoInit registers with ThreadManager. Do that only if we intend to
// be rtc::Thread::Current(), otherwise ProcessAllMessageQueuesInternal will
diff --git a/rtc_base/thread.h b/rtc_base/thread.h
index ed19e98..e16d5d1 100644
--- a/rtc_base/thread.h
+++ b/rtc_base/thread.h
@@ -290,6 +290,11 @@
const std::string& name() const { return name_; }
bool SetName(const std::string& name, const void* obj);
+ // Sets the expected processing time in ms. The thread will write
+ // log messages when Invoke() takes more time than this.
+ // Default is 50 ms.
+ void SetDispatchWarningMs(int deadline);
+
// Starts the execution of the thread.
bool Start();
@@ -525,6 +530,8 @@
RecursiveCriticalSection* CritForTest() { return &crit_; }
private:
+ static const int kSlowDispatchLoggingThreshold = 50; // 50 ms
+
class QueuedTaskHandler final : public MessageHandler {
public:
QueuedTaskHandler() {}
@@ -614,6 +621,8 @@
friend class ThreadManager;
+ int dispatch_warning_ms_ RTC_GUARDED_BY(this) = kSlowDispatchLoggingThreshold;
+
RTC_DISALLOW_COPY_AND_ASSIGN(Thread);
};
diff --git a/rtc_base/thread_checker.h b/rtc_base/thread_checker.h
deleted file mode 100644
index 876a08e..0000000
--- a/rtc_base/thread_checker.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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.
- */
-
-// Borrowed from Chromium's src/base/threading/thread_checker.h.
-
-#ifndef RTC_BASE_THREAD_CHECKER_H_
-#define RTC_BASE_THREAD_CHECKER_H_
-
-#include "rtc_base/deprecation.h"
-#include "rtc_base/synchronization/sequence_checker.h"
-
-namespace rtc {
-// TODO(srte): Replace usages of this with SequenceChecker.
-class ThreadChecker : public webrtc::SequenceChecker {
- public:
- RTC_DEPRECATED bool CalledOnValidThread() const { return IsCurrent(); }
- RTC_DEPRECATED void DetachFromThread() { Detach(); }
-};
-} // namespace rtc
-#endif // RTC_BASE_THREAD_CHECKER_H_
diff --git a/rtc_base/thread_checker_unittest.cc b/rtc_base/thread_checker_unittest.cc
deleted file mode 100644
index b592704..0000000
--- a/rtc_base/thread_checker_unittest.cc
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * 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.
- */
-
-// Borrowed from Chromium's src/base/threading/thread_checker_unittest.cc.
-
-#include "rtc_base/thread_checker.h"
-
-#include <memory>
-#include <utility>
-
-#include "rtc_base/checks.h"
-#include "rtc_base/constructor_magic.h"
-#include "rtc_base/null_socket_server.h"
-#include "rtc_base/socket_server.h"
-#include "rtc_base/task_queue.h"
-#include "rtc_base/thread.h"
-#include "test/gtest.h"
-
-// Duplicated from base/threading/thread_checker.h so that we can be
-// good citizens there and undef the macro.
-#define ENABLE_THREAD_CHECKER RTC_DCHECK_IS_ON
-
-namespace rtc {
-
-namespace {
-
-// Simple class to exercise the basics of ThreadChecker.
-// Both the destructor and DoStuff should verify that they were
-// called on the same thread as the constructor.
-class ThreadCheckerClass : public ThreadChecker {
- public:
- ThreadCheckerClass() {}
-
- // Verifies that it was called on the same thread as the constructor.
- void DoStuff() { RTC_DCHECK(IsCurrent()); }
-
- void Detach() { ThreadChecker::Detach(); }
-
- static void MethodOnDifferentThreadImpl();
- static void DetachThenCallFromDifferentThreadImpl();
-
- private:
- RTC_DISALLOW_COPY_AND_ASSIGN(ThreadCheckerClass);
-};
-
-// Calls ThreadCheckerClass::DoStuff on another thread.
-class CallDoStuffOnThread : public Thread {
- public:
- explicit CallDoStuffOnThread(ThreadCheckerClass* thread_checker_class)
- : Thread(std::unique_ptr<SocketServer>(new rtc::NullSocketServer())),
- thread_checker_class_(thread_checker_class) {
- SetName("call_do_stuff_on_thread", nullptr);
- }
-
- void Run() override { thread_checker_class_->DoStuff(); }
-
- // New method. Needed since Thread::Join is protected, and it is called by
- // the TEST.
- void Join() { Thread::Join(); }
-
- private:
- ThreadCheckerClass* thread_checker_class_;
-
- RTC_DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread);
-};
-
-// Deletes ThreadCheckerClass on a different thread.
-class DeleteThreadCheckerClassOnThread : public Thread {
- public:
- explicit DeleteThreadCheckerClassOnThread(
- std::unique_ptr<ThreadCheckerClass> thread_checker_class)
- : Thread(std::unique_ptr<SocketServer>(new rtc::NullSocketServer())),
- thread_checker_class_(std::move(thread_checker_class)) {
- SetName("delete_thread_checker_class_on_thread", nullptr);
- }
-
- void Run() override { thread_checker_class_.reset(); }
-
- // New method. Needed since Thread::Join is protected, and it is called by
- // the TEST.
- void Join() { Thread::Join(); }
-
- bool has_been_deleted() const { return !thread_checker_class_; }
-
- private:
- std::unique_ptr<ThreadCheckerClass> thread_checker_class_;
-
- RTC_DISALLOW_COPY_AND_ASSIGN(DeleteThreadCheckerClassOnThread);
-};
-
-} // namespace
-
-TEST(ThreadCheckerTest, CallsAllowedOnSameThread) {
- std::unique_ptr<ThreadCheckerClass> thread_checker_class(
- new ThreadCheckerClass);
-
- // Verify that DoStuff doesn't assert.
- thread_checker_class->DoStuff();
-
- // Verify that the destructor doesn't assert.
- thread_checker_class.reset();
-}
-
-TEST(ThreadCheckerTest, DestructorAllowedOnDifferentThread) {
- std::unique_ptr<ThreadCheckerClass> thread_checker_class(
- new ThreadCheckerClass);
-
- // Verify that the destructor doesn't assert
- // when called on a different thread.
- DeleteThreadCheckerClassOnThread delete_on_thread(
- std::move(thread_checker_class));
-
- EXPECT_FALSE(delete_on_thread.has_been_deleted());
-
- delete_on_thread.Start();
- delete_on_thread.Join();
-
- EXPECT_TRUE(delete_on_thread.has_been_deleted());
-}
-
-TEST(ThreadCheckerTest, Detach) {
- std::unique_ptr<ThreadCheckerClass> thread_checker_class(
- new ThreadCheckerClass);
-
- // Verify that DoStuff doesn't assert when called on a different thread after
- // a call to Detach.
- thread_checker_class->Detach();
- CallDoStuffOnThread call_on_thread(thread_checker_class.get());
-
- call_on_thread.Start();
- call_on_thread.Join();
-}
-
-#if GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER
-
-void ThreadCheckerClass::MethodOnDifferentThreadImpl() {
- std::unique_ptr<ThreadCheckerClass> thread_checker_class(
- new ThreadCheckerClass);
-
- // DoStuff should assert in debug builds only when called on a
- // different thread.
- CallDoStuffOnThread call_on_thread(thread_checker_class.get());
-
- call_on_thread.Start();
- call_on_thread.Join();
-}
-
-#if ENABLE_THREAD_CHECKER
-TEST(ThreadCheckerDeathTest, MethodNotAllowedOnDifferentThreadInDebug) {
- ASSERT_DEATH({ ThreadCheckerClass::MethodOnDifferentThreadImpl(); }, "");
-}
-#else
-TEST(ThreadCheckerTest, MethodAllowedOnDifferentThreadInRelease) {
- ThreadCheckerClass::MethodOnDifferentThreadImpl();
-}
-#endif // ENABLE_THREAD_CHECKER
-
-void ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl() {
- std::unique_ptr<ThreadCheckerClass> thread_checker_class(
- new ThreadCheckerClass);
-
- // DoStuff doesn't assert when called on a different thread
- // after a call to Detach.
- thread_checker_class->Detach();
- CallDoStuffOnThread call_on_thread(thread_checker_class.get());
-
- call_on_thread.Start();
- call_on_thread.Join();
-
- // DoStuff should assert in debug builds only after moving to
- // another thread.
- thread_checker_class->DoStuff();
-}
-
-#if ENABLE_THREAD_CHECKER
-TEST(ThreadCheckerDeathTest, DetachFromThreadInDebug) {
- ASSERT_DEATH({ ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl(); },
- "");
-}
-#else
-TEST(ThreadCheckerTest, DetachFromThreadInRelease) {
- ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
-}
-#endif // ENABLE_THREAD_CHECKER
-
-#endif // GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER
-
-class ThreadAnnotateTest {
- public:
- // Next two function should create warnings when compile (e.g. if used with
- // specific T).
- // TODO(danilchap): Find a way to test they do not compile when thread
- // annotation checks enabled.
- template <typename T>
- void access_var_no_annotate() {
- var_thread_ = 42;
- }
-
- template <typename T>
- void access_fun_no_annotate() {
- function();
- }
-
- // Functions below should be able to compile.
- void access_var_annotate_thread() {
- RTC_DCHECK_RUN_ON(thread_);
- var_thread_ = 42;
- }
-
- void access_var_annotate_checker() {
- RTC_DCHECK_RUN_ON(&checker_);
- var_checker_ = 44;
- }
-
- void access_var_annotate_queue() {
- RTC_DCHECK_RUN_ON(queue_);
- var_queue_ = 46;
- }
-
- void access_fun_annotate() {
- RTC_DCHECK_RUN_ON(thread_);
- function();
- }
-
- void access_fun_and_var() {
- RTC_DCHECK_RUN_ON(thread_);
- fun_acccess_var();
- }
-
- private:
- void function() RTC_RUN_ON(thread_) {}
- void fun_acccess_var() RTC_RUN_ON(thread_) { var_thread_ = 13; }
-
- rtc::Thread* thread_;
- rtc::ThreadChecker checker_;
- rtc::TaskQueue* queue_;
-
- int var_thread_ RTC_GUARDED_BY(thread_);
- int var_checker_ RTC_GUARDED_BY(checker_);
- int var_queue_ RTC_GUARDED_BY(queue_);
-};
-
-// Just in case we ever get lumped together with other compilation units.
-#undef ENABLE_THREAD_CHECKER
-
-} // namespace rtc
diff --git a/rtc_base/thread_unittest.cc b/rtc_base/thread_unittest.cc
index 5132198..eb3b4ec 100644
--- a/rtc_base/thread_unittest.cc
+++ b/rtc_base/thread_unittest.cc
@@ -19,6 +19,7 @@
#include "rtc_base/atomic_ops.h"
#include "rtc_base/event.h"
#include "rtc_base/gunit.h"
+#include "rtc_base/internal/default_socket_server.h"
#include "rtc_base/null_socket_server.h"
#include "rtc_base/physical_socket_server.h"
#include "rtc_base/socket_address.h"
@@ -432,7 +433,7 @@
struct LocalFuncs {
static void Set(LockedBool* out) { out->Set(true); }
static void InvokeSet(Thread* thread, LockedBool* out) {
- thread->Invoke<void>(RTC_FROM_HERE, Bind(&Set, out));
+ thread->Invoke<void>(RTC_FROM_HERE, [out] { Set(out); });
}
// Set |out| true and call InvokeSet on |thread|.
@@ -452,8 +453,9 @@
LockedBool async_invoked(false);
invoker->AsyncInvoke<void>(
- RTC_FROM_HERE, thread1,
- Bind(&SetAndInvokeSet, &async_invoked, thread2, out));
+ RTC_FROM_HERE, thread1, [&async_invoked, thread2, out] {
+ SetAndInvokeSet(&async_invoked, thread2, out);
+ });
EXPECT_TRUE_WAIT(async_invoked.Get(), 2000);
}
@@ -465,9 +467,12 @@
// Start the sequence A --(invoke)--> B --(async invoke)--> C --(invoke)--> A.
// Thread B returns when C receives the call and C should be blocked until A
// starts to process messages.
- thread_b->Invoke<void>(RTC_FROM_HERE,
- Bind(&LocalFuncs::AsyncInvokeSetAndWait, &invoker,
- thread_c.get(), thread_a, &thread_a_called));
+ Thread* thread_c_ptr = thread_c.get();
+ thread_b->Invoke<void>(
+ RTC_FROM_HERE, [&invoker, thread_c_ptr, thread_a, &thread_a_called] {
+ LocalFuncs::AsyncInvokeSetAndWait(&invoker, thread_c_ptr, thread_a,
+ &thread_a_called);
+ });
EXPECT_FALSE(thread_a_called.Get());
EXPECT_TRUE_WAIT(thread_a_called.Get(), 2000);
@@ -505,7 +510,7 @@
class ThreadQueueTest : public ::testing::Test, public Thread {
public:
- ThreadQueueTest() : Thread(SocketServer::CreateDefault(), true) {}
+ ThreadQueueTest() : Thread(CreateDefaultSocketServer(), true) {}
bool IsLocked_Worker() {
if (!CritForTest()->TryEnter()) {
return true;
@@ -518,8 +523,8 @@
// succeed, since our critical sections are reentrant.
std::unique_ptr<Thread> worker(Thread::CreateWithSocketServer());
worker->Start();
- return worker->Invoke<bool>(
- RTC_FROM_HERE, rtc::Bind(&ThreadQueueTest::IsLocked_Worker, this));
+ return worker->Invoke<bool>(RTC_FROM_HERE,
+ [this] { return IsLocked_Worker(); });
}
};
@@ -555,7 +560,7 @@
}
TEST_F(ThreadQueueTest, DelayedPostsWithIdenticalTimesAreProcessedInFifoOrder) {
- Thread q(SocketServer::CreateDefault(), true);
+ Thread q(CreateDefaultSocketServer(), true);
DelayedPostsWithIdenticalTimesAreProcessedInFifoOrder(&q);
NullSocketServer nullss;
@@ -839,11 +844,6 @@
EXPECT_TRUE(flag2.get());
}
-void ThreadIsCurrent(Thread* thread, bool* result, Event* event) {
- *result = thread->IsCurrent();
- event->Set();
-}
-
void WaitAndSetEvent(Event* wait_event, Event* set_event) {
wait_event->Wait(Event::kForever);
set_event->Set();
@@ -908,15 +908,6 @@
bool was_invoked_ = false;
};
-TEST(ThreadPostTaskTest, InvokesWithBind) {
- std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
- background_thread->Start();
-
- Event event;
- background_thread->PostTask(RTC_FROM_HERE, Bind(&Event::Set, &event));
- event.Wait(Event::kForever);
-}
-
TEST(ThreadPostTaskTest, InvokesWithLambda) {
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
background_thread->Start();
@@ -1019,9 +1010,13 @@
Event event;
bool was_invoked_on_background_thread = false;
- background_thread->PostTask(RTC_FROM_HERE,
- Bind(&ThreadIsCurrent, background_thread.get(),
- &was_invoked_on_background_thread, &event));
+ Thread* background_thread_ptr = background_thread.get();
+ background_thread->PostTask(
+ RTC_FROM_HERE,
+ [background_thread_ptr, &was_invoked_on_background_thread, &event] {
+ was_invoked_on_background_thread = background_thread_ptr->IsCurrent();
+ event.Set();
+ });
event.Wait(Event::kForever);
EXPECT_TRUE(was_invoked_on_background_thread);
@@ -1035,9 +1030,10 @@
// thread. The second event ensures that the message is processed.
Event event_set_by_test_thread;
Event event_set_by_background_thread;
- background_thread->PostTask(RTC_FROM_HERE,
- Bind(&WaitAndSetEvent, &event_set_by_test_thread,
- &event_set_by_background_thread));
+ background_thread->PostTask(RTC_FROM_HERE, [&event_set_by_test_thread,
+ &event_set_by_background_thread] {
+ WaitAndSetEvent(&event_set_by_test_thread, &event_set_by_background_thread);
+ });
event_set_by_test_thread.Set();
event_set_by_background_thread.Wait(Event::kForever);
}
@@ -1051,12 +1047,12 @@
Event third;
Event fourth;
- background_thread->PostTask(RTC_FROM_HERE,
- Bind(&WaitAndSetEvent, &first, &second));
- background_thread->PostTask(RTC_FROM_HERE,
- Bind(&WaitAndSetEvent, &second, &third));
- background_thread->PostTask(RTC_FROM_HERE,
- Bind(&WaitAndSetEvent, &third, &fourth));
+ background_thread->PostTask(
+ RTC_FROM_HERE, [&first, &second] { WaitAndSetEvent(&first, &second); });
+ background_thread->PostTask(
+ RTC_FROM_HERE, [&second, &third] { WaitAndSetEvent(&second, &third); });
+ background_thread->PostTask(
+ RTC_FROM_HERE, [&third, &fourth] { WaitAndSetEvent(&third, &fourth); });
// All tasks have been posted before the first one is unblocked.
first.Set();
@@ -1074,8 +1070,10 @@
Event event_set_by_background_thread;
background_thread->PostDelayedTask(
RTC_FROM_HERE,
- Bind(&WaitAndSetEvent, &event_set_by_test_thread,
- &event_set_by_background_thread),
+ [&event_set_by_test_thread, &event_set_by_background_thread] {
+ WaitAndSetEvent(&event_set_by_test_thread,
+ &event_set_by_background_thread);
+ },
/*milliseconds=*/10);
event_set_by_test_thread.Set();
event_set_by_background_thread.Wait(Event::kForever);
@@ -1091,15 +1089,15 @@
Event third;
Event fourth;
- background_thread->PostDelayedTask(RTC_FROM_HERE,
- Bind(&WaitAndSetEvent, &third, &fourth),
- /*milliseconds=*/11);
- background_thread->PostDelayedTask(RTC_FROM_HERE,
- Bind(&WaitAndSetEvent, &first, &second),
- /*milliseconds=*/9);
- background_thread->PostDelayedTask(RTC_FROM_HERE,
- Bind(&WaitAndSetEvent, &second, &third),
- /*milliseconds=*/10);
+ background_thread->PostDelayedTask(
+ RTC_FROM_HERE, [&third, &fourth] { WaitAndSetEvent(&third, &fourth); },
+ /*milliseconds=*/11);
+ background_thread->PostDelayedTask(
+ RTC_FROM_HERE, [&first, &second] { WaitAndSetEvent(&first, &second); },
+ /*milliseconds=*/9);
+ background_thread->PostDelayedTask(
+ RTC_FROM_HERE, [&second, &third] { WaitAndSetEvent(&second, &third); },
+ /*milliseconds=*/10);
// All tasks have been posted before the first one is unblocked.
first.Set();
diff --git a/rtc_base/time_utils.cc b/rtc_base/time_utils.cc
index 11c9d5a..fe63d3a 100644
--- a/rtc_base/time_utils.cc
+++ b/rtc_base/time_utils.cc
@@ -12,23 +12,15 @@
#if defined(WEBRTC_POSIX)
#include <sys/time.h>
-#if defined(WEBRTC_MAC)
-#include <mach/mach_time.h>
-#endif
#endif
#if defined(WEBRTC_WIN)
-// clang-format off
-// clang formatting would put <windows.h> last,
-// which leads to compilation failure.
-#include <windows.h>
-#include <mmsystem.h>
#include <sys/timeb.h>
-// clang-format on
#endif
#include "rtc_base/checks.h"
#include "rtc_base/numerics/safe_conversions.h"
+#include "rtc_base/system_time.h"
#include "rtc_base/time_utils.h"
namespace rtc {
@@ -141,61 +133,12 @@
TimeHelper::SyncWithNtp(time_from_ntp_server_ms);
}
-#endif // defined(WINUWP)
-
-int64_t SystemTimeNanos() {
- int64_t ticks;
-#if defined(WEBRTC_MAC)
- static mach_timebase_info_data_t timebase;
- if (timebase.denom == 0) {
- // Get the timebase if this is the first time we run.
- // Recommended by Apple's QA1398.
- if (mach_timebase_info(&timebase) != KERN_SUCCESS) {
- RTC_NOTREACHED();
- }
- }
- // Use timebase to convert absolute time tick units into nanoseconds.
- const auto mul = [](uint64_t a, uint32_t b) -> int64_t {
- RTC_DCHECK_NE(b, 0);
- RTC_DCHECK_LE(a, std::numeric_limits<int64_t>::max() / b)
- << "The multiplication " << a << " * " << b << " overflows";
- return rtc::dchecked_cast<int64_t>(a * b);
- };
- ticks = mul(mach_absolute_time(), timebase.numer) / timebase.denom;
-#elif defined(WEBRTC_POSIX)
- struct timespec ts;
- // TODO(deadbeef): Do we need to handle the case when CLOCK_MONOTONIC is not
- // supported?
- clock_gettime(CLOCK_MONOTONIC, &ts);
- ticks = kNumNanosecsPerSec * static_cast<int64_t>(ts.tv_sec) +
- static_cast<int64_t>(ts.tv_nsec);
-#elif defined(WINUWP)
- ticks = TimeHelper::TicksNs();
-#elif defined(WEBRTC_WIN)
- static volatile LONG last_timegettime = 0;
- static volatile int64_t num_wrap_timegettime = 0;
- volatile LONG* last_timegettime_ptr = &last_timegettime;
- DWORD now = timeGetTime();
- // Atomically update the last gotten time
- DWORD old = InterlockedExchange(last_timegettime_ptr, now);
- if (now < old) {
- // If now is earlier than old, there may have been a race between threads.
- // 0x0fffffff ~3.1 days, the code will not take that long to execute
- // so it must have been a wrap around.
- if (old > 0xf0000000 && now < 0x0fffffff) {
- num_wrap_timegettime++;
- }
- }
- ticks = now + (num_wrap_timegettime << 32);
- // TODO(deadbeef): Calculate with nanosecond precision. Otherwise, we're
- // just wasting a multiply and divide when doing Time() on Windows.
- ticks = ticks * kNumNanosecsPerMillisec;
-#else
-#error Unsupported platform.
-#endif
- return ticks;
+int64_t WinUwpSystemTimeNanos() {
+ return TimeHelper::TicksNs();
}
+#endif // defined(WINUWP)
+
int64_t SystemTimeMillis() {
return static_cast<int64_t>(SystemTimeNanos() / kNumNanosecsPerMillisec);
}
diff --git a/rtc_base/time_utils.h b/rtc_base/time_utils.h
index 147ab8d..de3c58c 100644
--- a/rtc_base/time_utils.h
+++ b/rtc_base/time_utils.h
@@ -16,6 +16,7 @@
#include "rtc_base/checks.h"
#include "rtc_base/system/rtc_export.h"
+#include "rtc_base/system_time.h"
namespace rtc {
@@ -61,11 +62,16 @@
// Synchronizes the current clock based upon an NTP server's epoch in
// milliseconds.
void SyncWithNtp(int64_t time_from_ntp_server_ms);
+
+// Returns the current time in nanoseconds. The clock is synchonized with the
+// system wall clock time upon instatiation. It may also be synchronized using
+// the SyncWithNtp() function above. Please note that the clock will most likely
+// drift away from the system wall clock time as time goes by.
+int64_t WinUwpSystemTimeNanos();
#endif // defined(WINUWP)
// Returns the actual system time, even if a clock is set for testing.
// Useful for timeouts while using a test clock, or for logging.
-int64_t SystemTimeNanos();
int64_t SystemTimeMillis();
// Returns the current time in milliseconds in 32 bits.
diff --git a/rtc_base/virtual_socket_server.cc b/rtc_base/virtual_socket_server.cc
index 804dc75..80d7f3c 100644
--- a/rtc_base/virtual_socket_server.cc
+++ b/rtc_base/virtual_socket_server.cc
@@ -54,7 +54,6 @@
enum {
MSG_ID_PACKET,
- MSG_ID_ADDRESS_BOUND,
MSG_ID_CONNECT,
MSG_ID_DISCONNECT,
MSG_ID_SIGNALREADEVENT,
@@ -149,9 +148,6 @@
} else {
bound_ = true;
was_any_ = addr.IsAnyIP();
- // Post a message here such that test case could have chance to
- // process the local address. (i.e. SetAlternativeLocalAddress).
- server_->msg_queue_->Post(RTC_FROM_HERE, this, MSG_ID_ADDRESS_BOUND);
}
return result;
}
@@ -174,7 +170,7 @@
SocketAddress addr = listen_queue_->front();
// Disconnect listening socket.
- server_->Disconnect(server_->LookupBinding(addr));
+ server_->Disconnect(addr);
listen_queue_->pop_front();
}
delete listen_queue_;
@@ -182,51 +178,14 @@
}
// Disconnect stream sockets
if (CS_CONNECTED == state_) {
- // Disconnect remote socket, check if it is a child of a server socket.
- VirtualSocket* socket =
- server_->LookupConnection(local_addr_, remote_addr_);
- if (!socket) {
- // Not a server socket child, then see if it is bound.
- // TODO(tbd): If this is indeed a server socket that has no
- // children this will cause the server socket to be
- // closed. This might lead to unexpected results, how to fix this?
- socket = server_->LookupBinding(remote_addr_);
- }
- server_->Disconnect(socket);
-
- // Remove mapping for both directions.
- server_->RemoveConnection(remote_addr_, local_addr_);
- server_->RemoveConnection(local_addr_, remote_addr_);
+ server_->Disconnect(local_addr_, remote_addr_);
}
// Cancel potential connects
- MessageList msgs;
- if (server_->msg_queue_) {
- server_->msg_queue_->Clear(this, MSG_ID_CONNECT, &msgs);
- }
- for (MessageList::iterator it = msgs.begin(); it != msgs.end(); ++it) {
- RTC_DCHECK(nullptr != it->pdata);
- MessageAddress* data = static_cast<MessageAddress*>(it->pdata);
-
- // Lookup remote side.
- VirtualSocket* socket =
- server_->LookupConnection(local_addr_, data->addr);
- if (socket) {
- // Server socket, remote side is a socket retreived by
- // accept. Accepted sockets are not bound so we will not
- // find it by looking in the bindings table.
- server_->Disconnect(socket);
- server_->RemoveConnection(local_addr_, data->addr);
- } else {
- server_->Disconnect(server_->LookupBinding(data->addr));
- }
- delete data;
- }
+ server_->CancelConnects(this);
}
// Clear incoming packets and disconnect messages
- if (server_->msg_queue_) {
- server_->msg_queue_->Clear(this);
- }
+ server_->Clear(this);
state_ = CS_CLOSED;
local_addr_.Clear();
@@ -279,9 +238,7 @@
return -1;
}
while (recv_buffer_.empty()) {
- Message msg;
- server_->msg_queue_->Get(&msg);
- server_->msg_queue_->Dispatch(&msg);
+ server_->ProcessOneMessage();
}
}
@@ -301,18 +258,14 @@
// To behave like a real socket, SignalReadEvent should fire in the next
// message loop pass if there's still data buffered.
if (!recv_buffer_.empty()) {
- // Clear the message so it doesn't end up posted multiple times.
- server_->msg_queue_->Clear(this, MSG_ID_SIGNALREADEVENT);
- server_->msg_queue_->Post(RTC_FROM_HERE, this, MSG_ID_SIGNALREADEVENT);
+ server_->PostSignalReadEvent(this);
}
if (SOCK_STREAM == type_) {
- bool was_full = (recv_buffer_size_ == server_->recv_buffer_capacity_);
+ bool was_full = (recv_buffer_size_ == server_->recv_buffer_capacity());
recv_buffer_size_ -= data_read;
if (was_full) {
- VirtualSocket* sender = server_->LookupBinding(remote_addr_);
- RTC_DCHECK(nullptr != sender);
- server_->SendTcp(sender);
+ server_->SendTcp(remote_addr_);
}
}
@@ -410,7 +363,7 @@
} else {
RTC_LOG(LS_VERBOSE) << "Socket at " << local_addr_.ToString()
<< " is not listening";
- server_->Disconnect(server_->LookupBinding(data->addr));
+ server_->Disconnect(data->addr);
}
delete data;
} else if (pmsg->message_id == MSG_ID_DISCONNECT) {
@@ -423,8 +376,6 @@
SignalCloseEvent(this, error);
}
}
- } else if (pmsg->message_id == MSG_ID_ADDRESS_BOUND) {
- SignalAddressReady(this, GetLocalAddress());
} else if (pmsg->message_id == MSG_ID_SIGNALREADEVENT) {
if (!recv_buffer_.empty()) {
SignalReadEvent(this);
@@ -494,7 +445,7 @@
}
int VirtualSocket::SendTcp(const void* pv, size_t cb) {
- size_t capacity = server_->send_buffer_capacity_ - send_buffer_.size();
+ size_t capacity = server_->send_buffer_capacity() - send_buffer_.size();
if (0 == capacity) {
ready_to_send_ = false;
error_ = EWOULDBLOCK;
@@ -523,6 +474,67 @@
}
}
+void VirtualSocket::SetToBlocked() {
+ CritScope cs(&crit_);
+ ready_to_send_ = false;
+ error_ = EWOULDBLOCK;
+}
+
+void VirtualSocket::UpdateRecv(size_t data_size) {
+ recv_buffer_size_ += data_size;
+}
+
+void VirtualSocket::UpdateSend(size_t data_size) {
+ size_t new_buffer_size = send_buffer_.size() - data_size;
+ // Avoid undefined access beyond the last element of the vector.
+ // This only happens when new_buffer_size is 0.
+ if (data_size < send_buffer_.size()) {
+ // memmove is required for potentially overlapping source/destination.
+ memmove(&send_buffer_[0], &send_buffer_[data_size], new_buffer_size);
+ }
+ send_buffer_.resize(new_buffer_size);
+}
+
+void VirtualSocket::MaybeSignalWriteEvent(size_t capacity) {
+ if (!ready_to_send_ && (send_buffer_.size() < capacity)) {
+ ready_to_send_ = true;
+ SignalWriteEvent(this);
+ }
+}
+
+uint32_t VirtualSocket::AddPacket(int64_t cur_time, size_t packet_size) {
+ network_size_ += packet_size;
+ uint32_t send_delay =
+ server_->SendDelay(static_cast<uint32_t>(network_size_));
+
+ NetworkEntry entry;
+ entry.size = packet_size;
+ entry.done_time = cur_time + send_delay;
+ network_.push_back(entry);
+
+ return send_delay;
+}
+
+int64_t VirtualSocket::UpdateOrderedDelivery(int64_t ts) {
+ // Ensure that new packets arrive after previous ones
+ ts = std::max(ts, last_delivery_time_);
+ // A socket should not have both ordered and unordered delivery, so its last
+ // delivery time only needs to be updated when it has ordered delivery.
+ last_delivery_time_ = ts;
+ return ts;
+}
+
+size_t VirtualSocket::PurgeNetworkPackets(int64_t cur_time) {
+ CritScope cs(&crit_);
+
+ while (!network_.empty() && (network_.front().done_time <= cur_time)) {
+ RTC_DCHECK(network_size_ >= network_.front().size);
+ network_size_ -= network_.front().size;
+ network_.pop_front();
+ }
+ return network_size_;
+}
+
VirtualSocketServer::VirtualSocketServer() : VirtualSocketServer(nullptr) {}
VirtualSocketServer::VirtualSocketServer(ThreadProcessingFakeClock* fake_clock)
@@ -596,9 +608,7 @@
}
VirtualSocket* VirtualSocketServer::CreateSocketInternal(int family, int type) {
- VirtualSocket* socket = new VirtualSocket(this, family, type, true);
- SignalSocketCreated(socket);
- return socket;
+ return new VirtualSocket(this, family, type, true);
}
void VirtualSocketServer::SetMessageQueue(Thread* msg_queue) {
@@ -814,19 +824,98 @@
return false;
}
+bool VirtualSocketServer::Disconnect(const SocketAddress& addr) {
+ return Disconnect(LookupBinding(addr));
+}
+
+bool VirtualSocketServer::Disconnect(const SocketAddress& local_addr,
+ const SocketAddress& remote_addr) {
+ // Disconnect remote socket, check if it is a child of a server socket.
+ VirtualSocket* socket = LookupConnection(local_addr, remote_addr);
+ if (!socket) {
+ // Not a server socket child, then see if it is bound.
+ // TODO(tbd): If this is indeed a server socket that has no
+ // children this will cause the server socket to be
+ // closed. This might lead to unexpected results, how to fix this?
+ socket = LookupBinding(remote_addr);
+ }
+ Disconnect(socket);
+
+ // Remove mapping for both directions.
+ RemoveConnection(remote_addr, local_addr);
+ RemoveConnection(local_addr, remote_addr);
+ return socket != nullptr;
+}
+
+void VirtualSocketServer::CancelConnects(VirtualSocket* socket) {
+ MessageList msgs;
+ if (msg_queue_) {
+ msg_queue_->Clear(socket, MSG_ID_CONNECT, &msgs);
+ }
+ for (MessageList::iterator it = msgs.begin(); it != msgs.end(); ++it) {
+ RTC_DCHECK(nullptr != it->pdata);
+ MessageAddress* data = static_cast<MessageAddress*>(it->pdata);
+ SocketAddress local_addr = socket->GetLocalAddress();
+ // Lookup remote side.
+ VirtualSocket* socket = LookupConnection(local_addr, data->addr);
+ if (socket) {
+ // Server socket, remote side is a socket retreived by
+ // accept. Accepted sockets are not bound so we will not
+ // find it by looking in the bindings table.
+ Disconnect(socket);
+ RemoveConnection(local_addr, data->addr);
+ } else {
+ Disconnect(data->addr);
+ }
+ delete data;
+ }
+}
+
+void VirtualSocketServer::Clear(VirtualSocket* socket) {
+ // Clear incoming packets and disconnect messages
+ if (msg_queue_) {
+ msg_queue_->Clear(socket);
+ }
+}
+
+void VirtualSocketServer::ProcessOneMessage() {
+ Message msg;
+ msg_queue_->Get(&msg);
+ msg_queue_->Dispatch(&msg);
+}
+
+void VirtualSocketServer::PostSignalReadEvent(VirtualSocket* socket) {
+ // Clear the message so it doesn't end up posted multiple times.
+ msg_queue_->Clear(socket, MSG_ID_SIGNALREADEVENT);
+ msg_queue_->Post(RTC_FROM_HERE, socket, MSG_ID_SIGNALREADEVENT);
+}
+
int VirtualSocketServer::SendUdp(VirtualSocket* socket,
const char* data,
size_t data_size,
const SocketAddress& remote_addr) {
++sent_packets_;
if (sending_blocked_) {
- CritScope cs(&socket->crit_);
- socket->ready_to_send_ = false;
- socket->error_ = EWOULDBLOCK;
+ socket->SetToBlocked();
return -1;
}
+ if (data_size > largest_seen_udp_payload_) {
+ if (data_size > 1000) {
+ RTC_LOG(LS_VERBOSE) << "Largest UDP seen is " << data_size;
+ }
+ largest_seen_udp_payload_ = data_size;
+ }
+
// See if we want to drop this packet.
+ if (data_size > max_udp_payload_) {
+ RTC_LOG(LS_VERBOSE) << "Dropping too large UDP payload of size "
+ << data_size << ", UDP payload limit is "
+ << max_udp_payload_;
+ // Return as if send was successful; packet disappears.
+ return data_size;
+ }
+
if (Random() < drop_prob_) {
RTC_LOG(LS_VERBOSE) << "Dropping packet: bad luck";
return static_cast<int>(data_size);
@@ -856,10 +945,8 @@
}
{
- CritScope cs(&socket->crit_);
-
int64_t cur_time = TimeMillis();
- PurgeNetworkPackets(socket, cur_time);
+ size_t network_size = socket->PurgeNetworkPackets(cur_time);
// Determine whether we have enough bandwidth to accept this packet. To do
// this, we need to update the send queue. Once we know it's current size,
@@ -870,7 +957,7 @@
// simulation of what a normal network would do.
size_t packet_size = data_size + UDP_HEADER_SIZE;
- if (socket->network_size_ + packet_size > network_capacity_) {
+ if (network_size + packet_size > network_capacity_) {
RTC_LOG(LS_VERBOSE) << "Dropping packet: network capacity exceeded";
return static_cast<int>(data_size);
}
@@ -898,45 +985,36 @@
// Lookup the local/remote pair in the connections table.
VirtualSocket* recipient =
- LookupConnection(socket->local_addr_, socket->remote_addr_);
+ LookupConnection(socket->GetLocalAddress(), socket->GetRemoteAddress());
if (!recipient) {
RTC_LOG(LS_VERBOSE) << "Sending data to no one.";
return;
}
- CritScope cs(&socket->crit_);
-
int64_t cur_time = TimeMillis();
- PurgeNetworkPackets(socket, cur_time);
+ socket->PurgeNetworkPackets(cur_time);
while (true) {
- size_t available = recv_buffer_capacity_ - recipient->recv_buffer_size_;
+ size_t available = recv_buffer_capacity_ - recipient->recv_buffer_size();
size_t max_data_size =
std::min<size_t>(available, TCP_MSS - TCP_HEADER_SIZE);
- size_t data_size = std::min(socket->send_buffer_.size(), max_data_size);
+ size_t data_size = std::min(socket->send_buffer_size(), max_data_size);
if (0 == data_size)
break;
- AddPacketToNetwork(socket, recipient, cur_time, &socket->send_buffer_[0],
+ AddPacketToNetwork(socket, recipient, cur_time, socket->send_buffer_data(),
data_size, TCP_HEADER_SIZE, true);
- recipient->recv_buffer_size_ += data_size;
-
- size_t new_buffer_size = socket->send_buffer_.size() - data_size;
- // Avoid undefined access beyond the last element of the vector.
- // This only happens when new_buffer_size is 0.
- if (data_size < socket->send_buffer_.size()) {
- // memmove is required for potentially overlapping source/destination.
- memmove(&socket->send_buffer_[0], &socket->send_buffer_[data_size],
- new_buffer_size);
- }
- socket->send_buffer_.resize(new_buffer_size);
+ recipient->UpdateRecv(data_size);
+ socket->UpdateSend(data_size);
}
- if (!socket->ready_to_send_ &&
- (socket->send_buffer_.size() < send_buffer_capacity_)) {
- socket->ready_to_send_ = true;
- socket->SignalWriteEvent(socket);
- }
+ socket->MaybeSignalWriteEvent(send_buffer_capacity_);
+}
+
+void VirtualSocketServer::SendTcp(const SocketAddress& addr) {
+ VirtualSocket* sender = LookupBinding(addr);
+ RTC_DCHECK(nullptr != sender);
+ SendTcp(sender);
}
void VirtualSocketServer::AddPacketToNetwork(VirtualSocket* sender,
@@ -946,13 +1024,7 @@
size_t data_size,
size_t header_size,
bool ordered) {
- VirtualSocket::NetworkEntry entry;
- entry.size = data_size + header_size;
-
- sender->network_size_ += entry.size;
- uint32_t send_delay = SendDelay(static_cast<uint32_t>(sender->network_size_));
- entry.done_time = cur_time + send_delay;
- sender->network_.push_back(entry);
+ uint32_t send_delay = sender->AddPacket(cur_time, data_size + header_size);
// Find the delay for crossing the many virtual hops of the network.
uint32_t transit_delay = GetTransitDelay(sender);
@@ -960,7 +1032,7 @@
// When the incoming packet is from a binding of the any address, translate it
// to the default route here such that the recipient will see the default
// route.
- SocketAddress sender_addr = sender->local_addr_;
+ SocketAddress sender_addr = sender->GetLocalAddress();
IPAddress default_ip = GetDefaultRoute(sender_addr.ipaddr().family());
if (sender_addr.IsAnyIP() && !IPIsUnspec(default_ip)) {
sender_addr.SetIP(default_ip);
@@ -971,25 +1043,11 @@
int64_t ts = TimeAfter(send_delay + transit_delay);
if (ordered) {
- // Ensure that new packets arrive after previous ones
- ts = std::max(ts, sender->last_delivery_time_);
- // A socket should not have both ordered and unordered delivery, so its last
- // delivery time only needs to be updated when it has ordered delivery.
- sender->last_delivery_time_ = ts;
+ ts = sender->UpdateOrderedDelivery(ts);
}
msg_queue_->PostAt(RTC_FROM_HERE, ts, recipient, MSG_ID_PACKET, p);
}
-void VirtualSocketServer::PurgeNetworkPackets(VirtualSocket* socket,
- int64_t cur_time) {
- while (!socket->network_.empty() &&
- (socket->network_.front().done_time <= cur_time)) {
- RTC_DCHECK(socket->network_size_ >= socket->network_.front().size);
- socket->network_size_ -= socket->network_.front().size;
- socket->network_.pop_front();
- }
-}
-
uint32_t VirtualSocketServer::SendDelay(uint32_t size) {
if (bandwidth_ == 0)
return 0;
@@ -1019,13 +1077,7 @@
#endif // <unused>
void VirtualSocketServer::UpdateDelayDistribution() {
- Function* dist =
- CreateDistribution(delay_mean_, delay_stddev_, delay_samples_);
- // We take a lock just to make sure we don't leak memory.
- {
- CritScope cs(&delay_crit_);
- delay_dist_.reset(dist);
- }
+ delay_dist_ = CreateDistribution(delay_mean_, delay_stddev_, delay_samples_);
}
static double PI = 4 * atan(1.0);
@@ -1044,11 +1096,11 @@
}
#endif
-VirtualSocketServer::Function* VirtualSocketServer::CreateDistribution(
- uint32_t mean,
- uint32_t stddev,
- uint32_t samples) {
- Function* f = new Function();
+std::unique_ptr<VirtualSocketServer::Function>
+VirtualSocketServer::CreateDistribution(uint32_t mean,
+ uint32_t stddev,
+ uint32_t samples) {
+ auto f = std::make_unique<Function>();
if (0 == stddev) {
f->push_back(Point(mean, 1.0));
@@ -1064,7 +1116,7 @@
f->push_back(Point(x, y));
}
}
- return Resample(Invert(Accumulate(f)), 0, 1, samples);
+ return Resample(Invert(Accumulate(std::move(f))), 0, 1, samples);
}
uint32_t VirtualSocketServer::GetTransitDelay(Socket* socket) {
@@ -1093,7 +1145,8 @@
}
};
-VirtualSocketServer::Function* VirtualSocketServer::Accumulate(Function* f) {
+std::unique_ptr<VirtualSocketServer::Function> VirtualSocketServer::Accumulate(
+ std::unique_ptr<Function> f) {
RTC_DCHECK(f->size() >= 1);
double v = 0;
for (Function::size_type i = 0; i < f->size() - 1; ++i) {
@@ -1106,7 +1159,8 @@
return f;
}
-VirtualSocketServer::Function* VirtualSocketServer::Invert(Function* f) {
+std::unique_ptr<VirtualSocketServer::Function> VirtualSocketServer::Invert(
+ std::unique_ptr<Function> f) {
for (Function::size_type i = 0; i < f->size(); ++i)
std::swap((*f)[i].first, (*f)[i].second);
@@ -1114,24 +1168,25 @@
return f;
}
-VirtualSocketServer::Function* VirtualSocketServer::Resample(Function* f,
- double x1,
- double x2,
- uint32_t samples) {
- Function* g = new Function();
+std::unique_ptr<VirtualSocketServer::Function> VirtualSocketServer::Resample(
+ std::unique_ptr<Function> f,
+ double x1,
+ double x2,
+ uint32_t samples) {
+ auto g = std::make_unique<Function>();
for (size_t i = 0; i < samples; i++) {
double x = x1 + (x2 - x1) * i / (samples - 1);
- double y = Evaluate(f, x);
+ double y = Evaluate(f.get(), x);
g->push_back(Point(x, y));
}
- delete f;
return g;
}
-double VirtualSocketServer::Evaluate(Function* f, double x) {
- Function::iterator iter = absl::c_lower_bound(*f, x, FunctionDomainCmp());
+double VirtualSocketServer::Evaluate(const Function* f, double x) {
+ Function::const_iterator iter =
+ absl::c_lower_bound(*f, x, FunctionDomainCmp());
if (iter == f->begin()) {
return (*f)[0].second;
} else if (iter == f->end()) {
diff --git a/rtc_base/virtual_socket_server.h b/rtc_base/virtual_socket_server.h
index 84f8fb1..54de578 100644
--- a/rtc_base/virtual_socket_server.h
+++ b/rtc_base/virtual_socket_server.h
@@ -94,6 +94,16 @@
drop_prob_ = drop_prob;
}
+ // Controls the maximum UDP payload for the networks simulated
+ // by this server. Any UDP payload sent that is larger than this will
+ // be dropped.
+ size_t max_udp_payload() { return max_udp_payload_; }
+ void set_max_udp_payload(size_t payload_size) {
+ max_udp_payload_ = payload_size;
+ }
+
+ size_t largest_seen_udp_payload() { return largest_seen_udp_payload_; }
+
// If |blocked| is true, subsequent attempts to send will result in -1 being
// returned, with the socket error set to EWOULDBLOCK.
//
@@ -130,9 +140,9 @@
typedef std::pair<double, double> Point;
typedef std::vector<Point> Function;
- static Function* CreateDistribution(uint32_t mean,
- uint32_t stddev,
- uint32_t samples);
+ static std::unique_ptr<Function> CreateDistribution(uint32_t mean,
+ uint32_t stddev,
+ uint32_t samples);
// Similar to Thread::ProcessMessages, but it only processes messages until
// there are no immediate messages or pending network traffic. Returns false
@@ -151,25 +161,12 @@
// socket server. Intended to be used for test assertions.
uint32_t sent_packets() const { return sent_packets_; }
- // For testing purpose only. Fired when a client socket is created.
- sigslot::signal1<VirtualSocket*> SignalSocketCreated;
-
- protected:
- // Returns a new IP not used before in this network.
- IPAddress GetNextIP(int family);
- uint16_t GetNextPort();
-
- VirtualSocket* CreateSocketInternal(int family, int type);
-
// Binds the given socket to addr, assigning and IP and Port if necessary
int Bind(VirtualSocket* socket, SocketAddress* addr);
// Binds the given socket to the given (fully-defined) address.
int Bind(VirtualSocket* socket, const SocketAddress& addr);
- // Find the socket bound to the given address
- VirtualSocket* LookupBinding(const SocketAddress& addr);
-
int Unbind(const SocketAddress& addr, VirtualSocket* socket);
// Adds a mapping between this socket pair and the socket.
@@ -177,13 +174,6 @@
const SocketAddress& server,
VirtualSocket* socket);
- // Find the socket pair corresponding to this server address.
- VirtualSocket* LookupConnection(const SocketAddress& client,
- const SocketAddress& server);
-
- void RemoveConnection(const SocketAddress& client,
- const SocketAddress& server);
-
// Connects the given socket to the socket at the given address
int Connect(VirtualSocket* socket,
const SocketAddress& remote_addr,
@@ -192,6 +182,13 @@
// Sends a disconnect message to the socket at the given address
bool Disconnect(VirtualSocket* socket);
+ // Lookup address, and disconnect corresponding socket.
+ bool Disconnect(const SocketAddress& addr);
+
+ // Lookup connection, close corresponding socket.
+ bool Disconnect(const SocketAddress& local_addr,
+ const SocketAddress& remote_addr);
+
// Sends the given packet to the socket at the given address (if one exists).
int SendUdp(VirtualSocket* socket,
const char* data,
@@ -201,6 +198,44 @@
// Moves as much data as possible from the sender's buffer to the network
void SendTcp(VirtualSocket* socket);
+ // Like above, but lookup sender by address.
+ void SendTcp(const SocketAddress& addr);
+
+ // Computes the number of milliseconds required to send a packet of this size.
+ uint32_t SendDelay(uint32_t size);
+
+ // Cancel attempts to connect to a socket that is being closed.
+ void CancelConnects(VirtualSocket* socket);
+
+ // Clear incoming messages for a socket that is being closed.
+ void Clear(VirtualSocket* socket);
+
+ void ProcessOneMessage();
+
+ void PostSignalReadEvent(VirtualSocket* socket);
+
+ // Sending was previously blocked, but now isn't.
+ sigslot::signal0<> SignalReadyToSend;
+
+ protected:
+ // Returns a new IP not used before in this network.
+ IPAddress GetNextIP(int family);
+
+ // Find the socket bound to the given address
+ VirtualSocket* LookupBinding(const SocketAddress& addr);
+
+ private:
+ uint16_t GetNextPort();
+
+ VirtualSocket* CreateSocketInternal(int family, int type);
+
+ // Find the socket pair corresponding to this server address.
+ VirtualSocket* LookupConnection(const SocketAddress& client,
+ const SocketAddress& server);
+
+ void RemoveConnection(const SocketAddress& client,
+ const SocketAddress& server);
+
// Places a packet on the network.
void AddPacketToNetwork(VirtualSocket* socket,
VirtualSocket* recipient,
@@ -210,26 +245,19 @@
size_t header_size,
bool ordered);
- // Removes stale packets from the network
- void PurgeNetworkPackets(VirtualSocket* socket, int64_t cur_time);
-
- // Computes the number of milliseconds required to send a packet of this size.
- uint32_t SendDelay(uint32_t size);
-
// If the delay has been set for the address of the socket, returns the set
// delay. Otherwise, returns a random transit delay chosen from the
// appropriate distribution.
uint32_t GetTransitDelay(Socket* socket);
- // Basic operations on functions. Those that return a function also take
- // ownership of the function given (and hence, may modify or delete it).
- static Function* Accumulate(Function* f);
- static Function* Invert(Function* f);
- static Function* Resample(Function* f,
- double x1,
- double x2,
- uint32_t samples);
- static double Evaluate(Function* f, double x);
+ // Basic operations on functions.
+ static std::unique_ptr<Function> Accumulate(std::unique_ptr<Function> f);
+ static std::unique_ptr<Function> Invert(std::unique_ptr<Function> f);
+ static std::unique_ptr<Function> Resample(std::unique_ptr<Function> f,
+ double x1,
+ double x2,
+ uint32_t samples);
+ static double Evaluate(const Function* f, double x);
// Null out our message queue if it goes away. Necessary in the case where
// our lifetime is greater than that of the thread we are using, since we
@@ -254,12 +282,6 @@
// NB: This scheme doesn't permit non-dualstack IPv6 sockets.
static bool CanInteractWith(VirtualSocket* local, VirtualSocket* remote);
- private:
- friend class VirtualSocket;
-
- // Sending was previously blocked, but now isn't.
- sigslot::signal0<> SignalReadyToSend;
-
typedef std::map<SocketAddress, VirtualSocket*> AddressMap;
typedef std::map<SocketAddressPair, VirtualSocket*> ConnectionMap;
@@ -295,9 +317,14 @@
std::map<rtc::IPAddress, rtc::IPAddress> alternative_address_mapping_;
std::unique_ptr<Function> delay_dist_;
- RecursiveCriticalSection delay_crit_;
-
double drop_prob_;
+ // The largest UDP payload permitted on this virtual socket server.
+ // The default is the max size of IPv4 fragmented UDP packet payload:
+ // 65535 bytes - 8 bytes UDP header - 20 bytes IP header.
+ size_t max_udp_payload_ = 65507;
+ // The largest UDP payload seen so far.
+ size_t largest_seen_udp_payload_ = 0;
+
bool sending_blocked_ = false;
RTC_DISALLOW_COPY_AND_ASSIGN(VirtualSocketServer);
};
@@ -334,11 +361,30 @@
int SetOption(Option opt, int value) override;
void OnMessage(Message* pmsg) override;
+ size_t recv_buffer_size() const { return recv_buffer_size_; }
+ size_t send_buffer_size() const { return send_buffer_.size(); }
+ const char* send_buffer_data() const { return send_buffer_.data(); }
+
+ // Used by server sockets to set the local address without binding.
+ void SetLocalAddress(const SocketAddress& addr);
+
bool was_any() { return was_any_; }
void set_was_any(bool was_any) { was_any_ = was_any; }
- // For testing purpose only. Fired when client socket is bound to an address.
- sigslot::signal2<VirtualSocket*, const SocketAddress&> SignalAddressReady;
+ void SetToBlocked();
+
+ void UpdateRecv(size_t data_size);
+ void UpdateSend(size_t data_size);
+
+ void MaybeSignalWriteEvent(size_t capacity);
+
+ // Adds a packet to be sent. Returns delay, based on network_size_.
+ uint32_t AddPacket(int64_t cur_time, size_t packet_size);
+
+ int64_t UpdateOrderedDelivery(int64_t ts);
+
+ // Removes stale packets from the network. Returns current size.
+ size_t PurgeNetworkPackets(int64_t cur_time);
private:
struct NetworkEntry {
@@ -357,9 +403,6 @@
int SendUdp(const void* pv, size_t cb, const SocketAddress& addr);
int SendTcp(const void* pv, size_t cb);
- // Used by server sockets to set the local address without binding.
- void SetLocalAddress(const SocketAddress& addr);
-
void OnSocketServerReadyToSend();
VirtualSocketServer* server_;
@@ -405,8 +448,6 @@
// Store the options that are set
OptionsMap options_map_;
-
- friend class VirtualSocketServer;
};
} // namespace rtc
diff --git a/rtc_base/virtual_socket_unittest.cc b/rtc_base/virtual_socket_unittest.cc
index 78003f5..96a359d 100644
--- a/rtc_base/virtual_socket_unittest.cc
+++ b/rtc_base/virtual_socket_unittest.cc
@@ -1117,10 +1117,10 @@
ASSERT_LT(0u, kTestSamples[sidx]);
const uint32_t kStdDev =
static_cast<uint32_t>(kTestDev[didx] * kTestMean[midx]);
- VirtualSocketServer::Function* f =
+ std::unique_ptr<VirtualSocketServer::Function> f =
VirtualSocketServer::CreateDistribution(kTestMean[midx], kStdDev,
kTestSamples[sidx]);
- ASSERT_TRUE(nullptr != f);
+ ASSERT_TRUE(nullptr != f.get());
ASSERT_EQ(kTestSamples[sidx], f->size());
double sum = 0;
for (uint32_t i = 0; i < f->size(); ++i) {
@@ -1139,7 +1139,6 @@
EXPECT_NEAR(kStdDev, stddev, 0.1 * kStdDev)
<< "M=" << kTestMean[midx] << " SD=" << kStdDev
<< " N=" << kTestSamples[sidx];
- delete f;
}
}
}
diff --git a/rtc_base/weak_ptr.h b/rtc_base/weak_ptr.h
index 68d57fc..a9e6b3a 100644
--- a/rtc_base/weak_ptr.h
+++ b/rtc_base/weak_ptr.h
@@ -15,9 +15,9 @@
#include <utility>
#include "api/scoped_refptr.h"
+#include "api/sequence_checker.h"
#include "rtc_base/ref_count.h"
#include "rtc_base/ref_counted_object.h"
-#include "rtc_base/synchronization/sequence_checker.h"
#include "rtc_base/system/no_unique_address.h"
// The implementation is borrowed from chromium except that it does not
diff --git a/rtc_base/win/create_direct3d_device.h b/rtc_base/win/create_direct3d_device.h
index 102f741..7c21f87 100644
--- a/rtc_base/win/create_direct3d_device.h
+++ b/rtc_base/win/create_direct3d_device.h
@@ -11,7 +11,7 @@
#ifndef RTC_BASE_WIN_CREATE_DIRECT3D_DEVICE_H_
#define RTC_BASE_WIN_CREATE_DIRECT3D_DEVICE_H_
-#include <windows.graphics.capture.interop.h>
+#include <windows.graphics.directX.direct3d11.h>
#include <windows.graphics.directX.direct3d11.interop.h>
#include <winerror.h>
#include <wrl/client.h>
diff --git a/system_wrappers/BUILD.gn b/system_wrappers/BUILD.gn
index f44ff5b..80088e0 100644
--- a/system_wrappers/BUILD.gn
+++ b/system_wrappers/BUILD.gn
@@ -108,7 +108,7 @@
]
}
-if (rtc_include_tests) {
+if (rtc_include_tests && !build_with_chromium) {
rtc_test("system_wrappers_unittests") {
testonly = true
sources = [
@@ -130,9 +130,10 @@
"../test:test_main",
"../test:test_support",
"//testing/gtest",
- "//third_party/abseil-cpp/absl/strings",
]
+ absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
+
if (is_android) {
deps += [ "//testing/android/native_test:native_test_support" ]
diff --git a/system_wrappers/source/clock.cc b/system_wrappers/source/clock.cc
index 0ae624d..8edffa6 100644
--- a/system_wrappers/source/clock.cc
+++ b/system_wrappers/source/clock.cc
@@ -90,10 +90,10 @@
protected:
timeval CurrentTimeVal() override {
- // The rtc::SystemTimeNanos() method is already time offset from a base
- // epoch value and might as be synchronized against an NTP time server as
- // an added bonus.
- auto nanos = rtc::SystemTimeNanos();
+ // The rtc::WinUwpSystemTimeNanos() method is already time offset from a
+ // base epoch value and might as be synchronized against an NTP time server
+ // as an added bonus.
+ auto nanos = rtc::WinUwpSystemTimeNanos();
struct timeval tv;
diff --git a/third_party/pffft/OWNERS b/third_party/pffft/OWNERS
index e97a539..b956a23 100644
--- a/third_party/pffft/OWNERS
+++ b/third_party/pffft/OWNERS
@@ -1,4 +1,2 @@
maxmorin@chromium.org
olka@chromium.org
-
-# COMPONENT: Blink>WebRTC>Audio
diff --git a/third_party/rnnoise/OWNERS b/third_party/rnnoise/OWNERS
index 5e862ce..b3f35ec 100644
--- a/third_party/rnnoise/OWNERS
+++ b/third_party/rnnoise/OWNERS
@@ -1,3 +1,2 @@
alessiob@chromium.org
aleloi@chromium.org
-# COMPONENT: Tools
diff --git a/webrtc_apm.cc b/webrtc_apm.cc
index 4a0ecbb..7961e5e 100644
--- a/webrtc_apm.cc
+++ b/webrtc_apm.cc
@@ -17,10 +17,25 @@
#include "webrtc_apm.h"
-webrtc_apm webrtc_apm_create(unsigned int num_channels,
- unsigned int frame_rate,
- dictionary *aec_ini,
- dictionary *apm_ini)
+
+webrtc_apm webrtc_apm_create(
+ unsigned int num_channels,
+ unsigned int frame_rate,
+ dictionary *aec_ini,
+ dictionary *apm_ini) {
+
+ return webrtc_apm_create_with_enforced_effects(num_channels, frame_rate,
+ aec_ini,apm_ini,0,0,0);
+}
+
+webrtc_apm webrtc_apm_create_with_enforced_effects(
+ unsigned int num_channels,
+ unsigned int frame_rate,
+ dictionary *aec_ini,
+ dictionary *apm_ini,
+ unsigned int enforce_aec_on,
+ unsigned int enforce_ns_on,
+ unsigned int enforce_agc_on)
{
int err;
webrtc::AudioProcessing *apm;
@@ -40,6 +55,7 @@
return NULL;
}
+ /* Set the AEC-tunings. */
if (aec_ini) {
aec_config_get(aec_ini, &aec3_config);
ec3_factory.reset(
@@ -51,8 +67,36 @@
apm_builder.SetEchoControlFactory(std::move(ec3_factory));
apm = apm_builder.Create();
- if (apm_ini)
- apm_config_apply(apm_ini, apm);
+ /* Set the rest of the all settings/tunings. */
+ webrtc::AudioProcessing::Config config = apm->GetConfig();
+ bool config_changed = false;
+ if (aec_ini || enforce_aec_on == 1) {
+ config.echo_canceller.enabled = true;
+ config_changed = true;
+ }
+
+ if (apm_ini) {
+ apm_config_set(apm_ini, &config);
+ config_changed = true;
+ }
+
+ if (enforce_ns_on == 1) {
+ config.noise_suppression.enabled = true;
+ config_changed = true;
+ }
+
+ if (enforce_agc_on == 1) {
+ config.gain_controller1.enabled = true;
+ config.gain_controller1.mode =
+ webrtc::AudioProcessing::Config::GainController1::Mode::kAdaptiveAnalog;
+ config.capture_level_adjustment.enabled = true;
+ config.capture_level_adjustment.analog_mic_gain_emulation.enabled = true;
+ config_changed = true;
+ }
+
+ if (config_changed) {
+ apm->ApplyConfig(config);
+ }
err = apm->Initialize(frame_rate, frame_rate, frame_rate,
channel_layout, channel_layout, channel_layout);
diff --git a/webrtc_apm.h b/webrtc_apm.h
index 39f850b..615fcc6 100644
--- a/webrtc_apm.h
+++ b/webrtc_apm.h
@@ -15,13 +15,38 @@
/* Pointer to a webrtc::AudioProcessing instance. */
typedef void* webrtc_apm;
-/* Creates a webrtc_apm for forward stream properties. In CRAS use case it
+/* Creates a webrtc_apm for forward stream properties using optional enforcement
+ * of the effects that are specified in the config files. In CRAS use case it
* is usually created for input stream.
* Args:
- * num_channels - Number of channels of the forward stream.
- * frame_rate - Frame rate used by forward stream.
- * aec_config - Pointer to aec config.
- * apm_config - Pointer to apm config.
+ * num_channels - Number of channels of the forward stream.
+ * frame_rate - Frame rate used by forward stream.
+ * aec_config - Pointer to aec config.
+ * apm_config - Pointer to apm config.
+ * enforce_aec_on - When set to 1, enforces the aec to be activated
+ * regardless of settings in apm.ini
+ * enforce_ns_on - When set to 1, enforces the ns to be activated
+ * regardless of settings in apm.ini
+ * enforce_agc_on - When set to 1, enforces the agc to be activated
+ * regardless of settings in apm.ini
+ */
+WEBRTC_APM_API webrtc_apm webrtc_apm_create_with_enforced_effects(
+ unsigned int num_channels,
+ unsigned int frame_rate,
+ dictionary *aec_ini,
+ dictionary *apm_ini,
+ unsigned int enforce_aec_on,
+ unsigned int enforce_ns_on,
+ unsigned int enforce_agc_on);
+
+/* Deprecated: Should be removed.
+ * Creates a webrtc_apm for forward stream properties using the parameters in
+ * the configs. In CRAS use case it is usually created for input stream.
+ * Args:
+ * num_channels - Number of channels of the forward stream.
+ * frame_rate - Frame rate used by forward stream.
+ * aec_config - Pointer to aec config.
+ * apm_config - Pointer to apm config.
*/
WEBRTC_APM_API webrtc_apm webrtc_apm_create(
unsigned int num_channels,